diff --git a/vendor/assets/ember/development/ember.js b/vendor/assets/ember/development/ember.js index 543351c..33998cb 100644 --- a/vendor/assets/ember/development/ember.js +++ b/vendor/assets/ember/development/ember.js @@ -1,5 +1,5 @@ -// Fetched from channel: tags/v1.5.1, with url http://builds.emberjs.com/tags/v1.5.1/ember.js -// Fetched on: 2014-04-23T09:05:37Z +// Fetched from channel: tags/v1.7.0, with url http://builds.emberjs.com/tags/v1.7.0/ember.js +// Fetched on: 2014-08-20T06:37:48Z /*! * @overview Ember - JavaScript Application Framework * @copyright Copyright 2011-2014 Tilde Inc. and contributors @@ -7,43397 +7,48215 @@ * 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.5.1 + * @version 1.7.0 */ - (function() { -/*global __fail__*/ - -/** -Ember Debug - -@module ember -@submodule ember-debug -*/ - -/** -@class Ember -*/ - -if ('undefined' === typeof Ember) { - Ember = {}; - - if ('undefined' !== typeof window) { - window.Em = window.Ember = Em = Ember; - } -} - -// This needs to be kept in sync with the logic in -// `packages/ember-metal/lib/core.js`. -// -// This is duplicated here to ensure that `Ember.ENV` -// is setup even if `Ember` is not loaded yet. -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 (!('MANDATORY_SETTER' in Ember.ENV)) { - Ember.ENV.MANDATORY_SETTER = true; // default to true for debug dist -} - -/** - Define an assertion that will throw an exception if the condition is not - met. Ember build tools will remove any calls to `Ember.assert()` when - doing a production build. Example: - - ```javascript - // Test for truthiness - Ember.assert('Must pass a valid object', obj); - // Fail unconditionally - Ember.assert('This code path should never be run') - ``` - - @method assert - @param {String} desc A description of the assertion. This will become - the text of the Error thrown if the assertion fails. - @param {Boolean} test Must be truthy for the assertion to pass. If - falsy, an exception will be thrown. -*/ -Ember.assert = function(desc, test) { - if (!test) { - throw new Ember.Error("Assertion Failed: " + desc); - } -}; - - -/** - Display a warning with the provided message. Ember build tools will - remove any calls to `Ember.warn()` when doing a production build. - - @method warn - @param {String} message A warning to display. - @param {Boolean} test An optional boolean. If falsy, the warning - will be displayed. -*/ -Ember.warn = function(message, test) { - if (!test) { - Ember.Logger.warn("WARNING: "+message); - if ('trace' in Ember.Logger) Ember.Logger.trace(); - } -}; - -/** - Display a debug notice. Ember build tools will remove any calls to - `Ember.debug()` when doing a production build. - - ```javascript - Ember.debug("I'm a debug notice!"); - ``` - - @method debug - @param {String} message A debug message to display. -*/ -Ember.debug = function(message) { - Ember.Logger.debug("DEBUG: "+message); -}; - -/** - Display a deprecation warning with the provided message and a stack trace - (Chrome and Firefox only). Ember build tools will remove any calls to - `Ember.deprecate()` when doing a production build. - - @method deprecate - @param {String} message A description of the deprecation. - @param {Boolean} test An optional boolean. If falsy, the deprecation - will be displayed. -*/ -Ember.deprecate = function(message, test) { - if (test) { return; } - - if (Ember.ENV.RAISE_ON_DEPRECATION) { throw new Ember.Error(message); } - - var error; - - // When using new Error, we can't do the arguments check for Chrome. Alternatives are welcome - try { __fail__.fail(); } catch (e) { error = e; } - - if (Ember.LOG_STACKTRACE_ON_DEPRECATION && error.stack) { - var stack, stackStr = ''; - if (error['arguments']) { - // Chrome - stack = error.stack.replace(/^\s+at\s+/gm, ''). - replace(/^([^\(]+?)([\n$])/gm, '{anonymous}($1)$2'). - replace(/^Object.\s*\(([^\)]+)\)/gm, '{anonymous}($1)').split('\n'); - stack.shift(); - } else { - // Firefox - stack = error.stack.replace(/(?:\n@:0)?\s+$/m, ''). - replace(/^\(/gm, '{anonymous}(').split('\n'); - } - - stackStr = "\n " + stack.slice(2).join("\n "); - message = message + stackStr; - } - - Ember.Logger.warn("DEPRECATION: "+message); -}; - - +var define, requireModule, require, requirejs, Ember; -/** - Alias an old, deprecated method with its new counterpart. - - Display a deprecation warning with the provided message and a stack trace - (Chrome and Firefox only) when the assigned method is called. +(function() { + Ember = this.Ember = this.Ember || {}; + if (typeof Ember === 'undefined') { Ember = {} }; - Ember build tools will not remove calls to `Ember.deprecateFunc()`, though - no warnings will be shown in production. + if (typeof Ember.__loader === 'undefined') { + var registry = {}, seen = {}; - ```javascript - Ember.oldMethod = Ember.deprecateFunc("Please use the new, updated method", Ember.newMethod); - ``` + define = function(name, deps, callback) { + registry[name] = { deps: deps, callback: callback }; + }; - @method deprecateFunc - @param {String} message A description of the deprecation. - @param {Function} func The new function called to replace its deprecated counterpart. - @return {Function} a new function that wrapped the original function with a deprecation warning -*/ -Ember.deprecateFunc = function(message, func) { - return function() { - Ember.deprecate(message); - return func.apply(this, arguments); - }; -}; + requirejs = require = requireModule = function(name) { + if (seen.hasOwnProperty(name)) { return seen[name]; } + seen[name] = {}; + if (!registry[name]) { + throw new Error("Could not find module " + name); + } -/** - Run a function meant for debugging. Ember build tools will remove any calls to - `Ember.runInDebug()` when doing a production build. + var mod = registry[name], + deps = mod.deps, + callback = mod.callback, + reified = [], + exports; - ```javascript - Ember.runInDebug( function() { - Ember.Handlebars.EachView.reopen({ - didInsertElement: function() { - console.log("I'm happy"); + for (var i=0, l=deps.length; i 3 ? slice.call(arguments, 3) : undefined; + if (!this.currentInstance) { createAutorun(this); } + return this.currentInstance.schedule(queueName, target, method, args, false, stack); + }, -/** - Standard environmental variables. You can define these in a global `EmberENV` - variable before loading Ember to control various configuration settings. + deferOnce: function(queueName, target, method /* , args */) { + if (!method) { + method = target; + target = null; + } - For backwards compatibility with earlier versions of Ember the global `ENV` - variable will be used if `EmberENV` is not defined. + if (isString(method)) { + method = target[method]; + } - @property ENV - @type Hash -*/ + var stack = this.DEBUG ? new Error() : undefined, + args = arguments.length > 3 ? slice.call(arguments, 3) : undefined; + if (!this.currentInstance) { createAutorun(this); } + return this.currentInstance.schedule(queueName, target, method, args, true, stack); + }, -// This needs to be kept in sync with the logic in -// `packages/ember-debug/lib/main.js`. -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 = {}; -} + setTimeout: function() { + var args = slice.call(arguments), + length = args.length, + method, wait, target, + methodOrTarget, methodOrWait, methodOrArgs; -Ember.config = Ember.config || {}; + if (length === 0) { + return; + } else if (length === 1) { + method = args.shift(); + wait = 0; + } else if (length === 2) { + methodOrTarget = args[0]; + methodOrWait = args[1]; -// We disable the RANGE API by default for performance reasons -if ('undefined' === typeof Ember.ENV.DISABLE_RANGE_API) { - Ember.ENV.DISABLE_RANGE_API = true; -} + if (isFunction(methodOrWait) || isFunction(methodOrTarget[methodOrWait])) { + target = args.shift(); + method = args.shift(); + wait = 0; + } else if (isCoercableNumber(methodOrWait)) { + method = args.shift(); + wait = args.shift(); + } else { + method = args.shift(); + wait = 0; + } + } else { + var last = args[args.length - 1]; -if ("undefined" === typeof MetamorphENV) { - exports.MetamorphENV = {}; -} + if (isCoercableNumber(last)) { + wait = args.pop(); + } else { + wait = 0; + } -MetamorphENV.DISABLE_RANGE_API = Ember.ENV.DISABLE_RANGE_API; + methodOrTarget = args[0]; + methodOrArgs = args[1]; -/** - Hash of enabled Canary features. Add to before creating your application. + if (isFunction(methodOrArgs) || (isString(methodOrArgs) && + methodOrTarget !== null && + methodOrArgs in methodOrTarget)) { + target = args.shift(); + method = args.shift(); + } else { + method = args.shift(); + } + } - You can also define `ENV.FEATURES` if you need to enable features flagged at runtime. + var executeAt = (+new Date()) + parseInt(wait, 10); - @property FEATURES - @type Hash -*/ + if (isString(method)) { + method = target[method]; + } -Ember.FEATURES = Ember.ENV.FEATURES || {}; + var onError = getOnError(this.options); -/** - Test that a feature is enabled. Parsed by Ember's build tools to leave - experimental features out of beta/stable builds. + function fn() { + if (onError) { + try { + method.apply(target, args); + } catch (e) { + onError(e); + } + } else { + method.apply(target, args); + } + } - You can define the following configuration options: + // find position to insert + var i = searchTimer(executeAt, timers); - * `ENV.ENABLE_ALL_FEATURES` - force all features to be enabled. - * `ENV.ENABLE_OPTIONAL_FEATURES` - enable any features that have not been explicitly - enabled/disabled. + timers.splice(i, 0, executeAt, fn); - @method isEnabled - @param {string} feature -*/ + updateLaterTimer(this, executeAt, wait); -Ember.FEATURES.isEnabled = function(feature) { - var featureValue = Ember.FEATURES[feature]; + return fn; + }, - if (Ember.ENV.ENABLE_ALL_FEATURES) { - return true; - } else if (featureValue === true || featureValue === false || featureValue === undefined) { - return featureValue; - } else if (Ember.ENV.ENABLE_OPTIONAL_FEATURES) { - return true; - } else { - return false; - } -}; - -// .......................................................... -// BOOTSTRAP -// - -/** - Determines whether Ember should enhances 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 -*/ -Ember.EXTEND_PROTOTYPES = Ember.ENV.EXTEND_PROTOTYPES; - -if (typeof Ember.EXTEND_PROTOTYPES === 'undefined') { - Ember.EXTEND_PROTOTYPES = true; -} - -/** - Determines whether Ember logs a full stack trace during deprecation warnings - - @property LOG_STACKTRACE_ON_DEPRECATION - @type Boolean - @default true -*/ -Ember.LOG_STACKTRACE_ON_DEPRECATION = (Ember.ENV.LOG_STACKTRACE_ON_DEPRECATION !== false); - -/** - Determines whether Ember should add ECMAScript 5 shims to older browsers. - - @property SHIM_ES5 - @type Boolean - @default Ember.EXTEND_PROTOTYPES -*/ -Ember.SHIM_ES5 = (Ember.ENV.SHIM_ES5 === false) ? false : Ember.EXTEND_PROTOTYPES; - -/** - Determines whether Ember logs info about version of used libraries - - @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`. - - @method K - @private - @return {Object} -*/ -Ember.K = function() { return this; }; - - -// Stub out the methods defined by the ember-debug package in case it's not loaded - -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; }; -} - -/** - Previously we used `Ember.$.uuid`, however `$.uuid` has been removed from - jQuery master. We'll just bootstrap our own uuid now. - - @property uuid - @type Number - @private -*/ -Ember.uuid = 0; - -/** - Merge the contents of two objects together into the first object. - - ```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 merge - @for Ember - @param {Object} original The object to merge into - @param {Object} updates The object to copy properties from - @return {Object} -*/ -Ember.merge = function(original, updates) { - for (var prop in updates) { - if (!updates.hasOwnProperty(prop)) { continue; } - original[prop] = updates[prop]; - } - return original; -}; - -/** - Returns true if the passed value is null or undefined. This avoids errors - from JSLint complaining about use of ==, which can be technically - confusing. - - ```javascript - Ember.isNone(); // true - Ember.isNone(null); // true - Ember.isNone(undefined); // true - Ember.isNone(''); // false - Ember.isNone([]); // false - Ember.isNone(function() {}); // false - ``` - - @method isNone - @for Ember - @param {Object} obj Value to test - @return {Boolean} -*/ -Ember.isNone = function(obj) { - return obj === null || obj === undefined; -}; -Ember.none = Ember.deprecateFunc("Ember.none is deprecated. Please use Ember.isNone instead.", Ember.isNone); - -/** - Verifies that a value is `null` or an empty string, empty array, - or empty function. - - Constrains the rules on `Ember.isNone` by returning false for empty - string and empty arrays. - - ```javascript - Ember.isEmpty(); // true - Ember.isEmpty(null); // true - Ember.isEmpty(undefined); // true - Ember.isEmpty(''); // true - Ember.isEmpty([]); // true - Ember.isEmpty('Adam Hawkins'); // false - Ember.isEmpty([0,1,2]); // false - ``` - - @method isEmpty - @for Ember - @param {Object} obj Value to test - @return {Boolean} -*/ -Ember.isEmpty = function(obj) { - return Ember.isNone(obj) || (obj.length === 0 && typeof obj !== 'function') || (typeof obj === 'object' && Ember.get(obj, 'length') === 0); -}; -Ember.empty = Ember.deprecateFunc("Ember.empty is deprecated. Please use Ember.isEmpty instead.", Ember.isEmpty); - - - /** - A value is blank if it is empty or a whitespace string. + throttle: function(target, method /* , args, wait, [immediate] */) { + var self = this, + args = arguments, + immediate = pop.call(args), + wait, + throttler, + index, + timer; - ```javascript - Ember.isBlank(); // true - Ember.isBlank(null); // true - Ember.isBlank(undefined); // true - Ember.isBlank(''); // true - Ember.isBlank([]); // true - Ember.isBlank('\n\t'); // true - Ember.isBlank(' '); // true - Ember.isBlank({}); // false - Ember.isBlank('\n\t Hello'); // false - Ember.isBlank('Hello world'); // false - Ember.isBlank([1,2,3]); // false - ``` + if (isNumber(immediate) || isString(immediate)) { + wait = immediate; + immediate = true; + } else { + wait = pop.call(args); + } - @method isBlank - @for Ember - @param {Object} obj Value to test - @return {Boolean} - */ - Ember.isBlank = function(obj) { - return Ember.isEmpty(obj) || (typeof obj === 'string' && obj.match(/\S/) === null); - }; + wait = parseInt(wait, 10); + index = findThrottler(target, method, this._throttlers); + if (index > -1) { return this._throttlers[index]; } // throttled -})(); + timer = global.setTimeout(function() { + if (!immediate) { + self.run.apply(self, args); + } + var index = findThrottler(target, method, self._throttlers); + if (index > -1) { + self._throttlers.splice(index, 1); + } + }, wait); + if (immediate) { + self.run.apply(self, args); + } + throttler = [target, method, timer]; -(function() { -/*globals Node */ -/** -@module ember-metal -*/ + this._throttlers.push(throttler); -/** - Platform specific methods and feature detectors needed by the framework. + return throttler; + }, - @class platform - @namespace Ember - @static -*/ -var platform = Ember.platform = {}; + debounce: function(target, method /* , args, wait, [immediate] */) { + var self = this, + args = arguments, + immediate = pop.call(args), + wait, + index, + debouncee, + timer; + if (isNumber(immediate) || isString(immediate)) { + wait = immediate; + immediate = false; + } else { + wait = pop.call(args); + } -/** - Identical to `Object.create()`. Implements if not available natively. + wait = parseInt(wait, 10); + // Remove debouncee + index = findDebouncee(target, method, this._debouncees); - @method create - @for Ember -*/ -Ember.create = Object.create; + if (index > -1) { + debouncee = this._debouncees[index]; + this._debouncees.splice(index, 1); + clearTimeout(debouncee[2]); + } -// IE8 has Object.create but it couldn't treat property descriptors. -if (Ember.create) { - if (Ember.create({a: 1}, {a: {value: 2}}).a !== 2) { - Ember.create = null; - } -} + timer = global.setTimeout(function() { + if (!immediate) { + self.run.apply(self, args); + } + var index = findDebouncee(target, method, self._debouncees); + if (index > -1) { + self._debouncees.splice(index, 1); + } + }, wait); -// STUB_OBJECT_CREATE allows us to override other libraries that stub -// Object.create different than we would prefer -if (!Ember.create || Ember.ENV.STUB_OBJECT_CREATE) { - var K = function() {}; + if (immediate && index === -1) { + self.run.apply(self, args); + } - Ember.create = function(obj, props) { - K.prototype = obj; - obj = new K(); - if (props) { - K.prototype = obj; - for (var prop in props) { - K.prototype[prop] = props[prop].value; - } - obj = new K(); - } - K.prototype = null; + debouncee = [target, method, timer]; - return obj; - }; + self._debouncees.push(debouncee); - Ember.create.isSimulated = true; -} + return debouncee; + }, -var defineProperty = Object.defineProperty; -var canRedefineProperties, canDefinePropertyOnDOM; + cancelTimers: function() { + var clearItems = function(item) { + clearTimeout(item[2]); + }; -// Catch IE8 where Object.defineProperty exists but only works on DOM elements -if (defineProperty) { - try { - defineProperty({}, 'a',{get:function() {}}); - } catch (e) { - defineProperty = null; - } -} + each(this._throttlers, clearItems); + this._throttlers = []; -if (defineProperty) { - // Detects a bug in Android <3.2 where you cannot redefine a property using - // Object.defineProperty once accessors have already been set. - canRedefineProperties = (function() { - var obj = {}; + each(this._debouncees, clearItems); + this._debouncees = []; - defineProperty(obj, 'a', { - configurable: true, - enumerable: true, - get: function() { }, - set: function() { } - }); + if (this._laterTimer) { + clearTimeout(this._laterTimer); + this._laterTimer = null; + } + timers = []; - defineProperty(obj, 'a', { - configurable: true, - enumerable: true, - writable: true, - value: true - }); - - return obj.a === true; - })(); - - // This is for Safari 5.0, which supports Object.defineProperty, but not - // on DOM nodes. - canDefinePropertyOnDOM = (function() { - try { - defineProperty(document.createElement('div'), 'definePropertyOnDOM', {}); - return true; - } catch(e) { } - - return false; - })(); - - if (!canRedefineProperties) { - defineProperty = null; - } else if (!canDefinePropertyOnDOM) { - defineProperty = function(obj, keyName, desc) { - var isNode; - - if (typeof Node === "object") { - isNode = obj instanceof Node; - } else { - isNode = typeof obj === "object" && typeof obj.nodeType === "number" && typeof obj.nodeName === "string"; - } - - if (isNode) { - // TODO: Should we have a warning here? - return (obj[keyName] = desc.value); - } else { - return Object.defineProperty(obj, keyName, desc); - } - }; - } -} - -/** -@class platform -@namespace Ember -*/ - -/** - Identical to `Object.defineProperty()`. Implements as much functionality - as possible if not available natively. + if (this._autorun) { + clearTimeout(this._autorun); + this._autorun = null; + } + }, - @method defineProperty - @param {Object} obj The object to modify - @param {String} keyName property name to modify - @param {Object} desc descriptor hash - @return {void} -*/ -platform.defineProperty = defineProperty; + hasTimers: function() { + return !!timers.length || !!this._debouncees.length || !!this._throttlers.length || this._autorun; + }, -/** - Set to true if the platform supports native getters and setters. + cancel: function(timer) { + var timerType = typeof timer; - @property hasPropertyAccessors - @final -*/ -platform.hasPropertyAccessors = true; + if (timer && timerType === 'object' && timer.queue && timer.method) { // we're cancelling a deferOnce + return timer.queue.cancel(timer); + } else if (timerType === 'function') { // we're cancelling a setTimeout + for (var i = 0, l = timers.length; i < l; i += 2) { + if (timers[i + 1] === timer) { + timers.splice(i, 2); // remove the two elements + return true; + } + } + } else if (Object.prototype.toString.call(timer) === "[object Array]"){ // we're cancelling a throttle or debounce + return this._cancelItem(findThrottler, this._throttlers, timer) || + this._cancelItem(findDebouncee, this._debouncees, timer); + } else { + return; // timer was null or not a timer + } + }, -if (!platform.defineProperty) { - platform.hasPropertyAccessors = false; + _cancelItem: function(findMethod, array, timer){ + var item, + index; - platform.defineProperty = function(obj, keyName, desc) { - if (!desc.get) { obj[keyName] = desc.value; } - }; + if (timer.length < 3) { return false; } - platform.defineProperty.isSimulated = true; -} + index = findMethod(timer[0], timer[1], array); -if (Ember.ENV.MANDATORY_SETTER && !platform.hasPropertyAccessors) { - Ember.ENV.MANDATORY_SETTER = false; -} + if(index > -1) { -})(); + item = array[index]; + if(item[2] === timer[2]){ + array.splice(index, 1); + clearTimeout(timer[2]); + return true; + } + } + return false; + } + }; -(function() { -/*jshint newcap:false*/ -/** -@module ember-metal -*/ - -// NOTE: There is a bug in jshint that doesn't recognize `Object()` without `new` -// as being ok unless both `newcap:false` and not `use strict`. -// https://github.com/jshint/jshint/issues/392 - -// 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; -}; - -// From: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/map -var arrayMap = isNativeFunc(Array.prototype.map) ? Array.prototype.map : function(fun /*, thisp */) { - //"use strict"; - - if (this === void 0 || this === null) { - throw new TypeError(); - } + Backburner.prototype.schedule = Backburner.prototype.defer; + Backburner.prototype.scheduleOnce = Backburner.prototype.deferOnce; + Backburner.prototype.later = Backburner.prototype.setTimeout; - var t = Object(this); - var len = t.length >>> 0; - if (typeof fun !== "function") { - throw new TypeError(); - } + if (needsIETryCatchFix) { + var originalRun = Backburner.prototype.run; + Backburner.prototype.run = wrapInTryCatch(originalRun); - var res = new Array(len); - var thisp = arguments[1]; - for (var i = 0; i < len; i++) { - if (i in t) { - res[i] = fun.call(thisp, t[i], i, t); + var originalEnd = Backburner.prototype.end; + Backburner.prototype.end = wrapInTryCatch(originalEnd); } - } - - return res; -}; - -// From: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/foreach -var arrayForEach = isNativeFunc(Array.prototype.forEach) ? Array.prototype.forEach : function(fun /*, thisp */) { - //"use strict"; - - if (this === void 0 || this === null) { - throw new TypeError(); - } - - var t = Object(this); - var len = t.length >>> 0; - if (typeof fun !== "function") { - throw new TypeError(); - } - var thisp = arguments[1]; - for (var i = 0; i < len; i++) { - if (i in t) { - fun.call(thisp, t[i], i, t); + function wrapInTryCatch(func) { + return function () { + try { + return func.apply(this, arguments); + } catch (e) { + throw e; + } + }; } - } -}; - -var arrayIndexOf = isNativeFunc(Array.prototype.indexOf) ? Array.prototype.indexOf : function (obj, fromIndex) { - if (fromIndex === null || fromIndex === undefined) { fromIndex = 0; } - else if (fromIndex < 0) { fromIndex = Math.max(0, this.length + fromIndex); } - for (var i = fromIndex, j = this.length; i < j; i++) { - if (this[i] === obj) { return i; } - } - return -1; -}; - -var arrayFilter = isNativeFunc(Array.prototype.filter) ? Array.prototype.filter : function (fn, context) { - var i, - value, - result = [], - 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); - } + function getOnError(options) { + return options.onError || (options.onErrorTarget && options.onErrorTarget[options.onErrorMethod]); } - } - return result; -}; - -/** - Array polyfills to support ES5 features in older browsers. - - @namespace Ember - @property ArrayPolyfills -*/ -Ember.ArrayPolyfills = { - map: arrayMap, - forEach: arrayForEach, - filter: arrayFilter, - indexOf: arrayIndexOf -}; - -if (Ember.SHIM_ES5) { - if (!Array.prototype.map) { - Array.prototype.map = arrayMap; - } - - if (!Array.prototype.forEach) { - Array.prototype.forEach = arrayForEach; - } - - if (!Array.prototype.filter) { - Array.prototype.filter = arrayFilter; - } - - if (!Array.prototype.indexOf) { - Array.prototype.indexOf = arrayIndexOf; - } -} - -})(); - - - -(function() { -var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; - -/** - A subclass of the JavaScript Error object for use in Ember. - - @class Error - @namespace Ember - @extends Error - @constructor -*/ -Ember.Error = function() { - var tmp = Error.apply(this, arguments); - - // Adds a `stack` property to the given error object that will yield the - // stack trace at the time captureStackTrace was called. - // When collecting the stack trace all frames above the topmost call - // to this function, including that call, will be left out of the - // stack trace. - // This is useful because we can hide Ember implementation details - // that are not very helpful for the user. - if (Error.captureStackTrace) { - Error.captureStackTrace(this, Ember.Error); - } - // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. - for (var idx = 0; idx < errorProps.length; idx++) { - this[errorProps[idx]] = tmp[errorProps[idx]]; - } -}; - -Ember.Error.prototype = Ember.create(Error.prototype); - -// .......................................................... -// ERROR HANDLING -// - -/** - 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. - - ```javascript - Ember.onerror = function(error) { - Em.$.ajax('/report-error', 'POST', { - stack: error.stack, - otherInformation: 'whatever app state you want to provide' - }); - }; - ``` - @event onerror - @for Ember - @param {Exception} error the error object -*/ -Ember.onerror = null; -})(); - - - -(function() { -/** -@module ember-metal -*/ - -/** - Prefix used for guids through out Ember. - @private -*/ -Ember.GUID_PREFIX = 'ember'; - - -var o_defineProperty = Ember.platform.defineProperty, - o_create = Ember.create, - // Used for guid generation... - GUID_KEY = '__ember'+ (+ new Date()), - numberCache = [], - stringCache = {}, - uuid = 0; - -var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER; - -/** - A unique key used to assign guids and other private metadata to objects. - If you inspect an object in your browser debugger you will often see these. - They can be safely ignored. - - On browsers that support it, these properties are added with enumeration - disabled so they won't show up when you iterate over your properties. - - @private - @property GUID_KEY - @for Ember - @type String - @final -*/ -Ember.GUID_KEY = GUID_KEY; - -var GUID_DESC = { - writable: false, - configurable: false, - enumerable: false, - value: null -}; - -/** - Generates a new guid, optionally saving the guid to the object that you - pass in. You will rarely need to use this method. Instead you should - call `Ember.guidFor(obj)`, which return an existing guid if available. - - @private - @method generateGuid - @for Ember - @param {Object} [obj] Object the guid will be used for. If passed in, the guid will - be saved on the object and reused whenever you pass the same object - again. - - If no object is passed, just generate a new guid. - @param {String} [prefix] Prefix to place in front of the guid. Useful when you want to - separate the guid into separate namespaces. - @return {String} the guid -*/ -Ember.generateGuid = function generateGuid(obj, prefix) { - if (!prefix) prefix = Ember.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); + function createAutorun(backburner) { + backburner.begin(); + backburner._autorun = global.setTimeout(function() { + backburner._autorun = null; + backburner.end(); + }); } - } - return ret; -}; - -/** - Returns a unique id for the object. If the object does not yet have a guid, - one will be assigned to it. You can call this on any object, - `Ember.Object`-based or not, but be aware that it will add a `_guid` - property. - - You can also use this method on DOM Element objects. - - @private - @method guidFor - @for Ember - @param {Object} obj any object, string, number, Element, or primitive - @return {String} the unique guid for this instance. -*/ -Ember.guidFor = function guidFor(obj) { - - // special cases where we don't want to add a key to object - if (obj === undefined) return "(undefined)"; - if (obj === null) return "(null)"; - - var ret; - var type = typeof obj; - - // Don't allow prototype changes to String etc. to change the guidFor - switch(type) { - case 'number': - ret = numberCache[obj]; - if (!ret) ret = numberCache[obj] = 'nu'+obj; - return ret; - case 'string': - ret = stringCache[obj]; - if (!ret) ret = stringCache[obj] = 'st'+(uuid++); - return ret; - - case 'boolean': - return obj ? '(true)' : '(false)'; - - default: - if (obj[GUID_KEY]) return obj[GUID_KEY]; - if (obj === Object) return '(Object)'; - if (obj === Array) return '(Array)'; - ret = 'ember' + (uuid++); - - if (obj[GUID_KEY] === null) { - obj[GUID_KEY] = ret; - } else { - GUID_DESC.value = ret; - o_defineProperty(obj, GUID_KEY, GUID_DESC); + function updateLaterTimer(self, executeAt, wait) { + if (!self._laterTimer || executeAt < self._laterTimerExpiresAt) { + self._laterTimer = global.setTimeout(function() { + self._laterTimer = null; + self._laterTimerExpiresAt = null; + executeTimers(self); + }, wait); + self._laterTimerExpiresAt = executeAt; } - return ret; - } -}; - -// .......................................................... -// META -// - -var META_DESC = Ember.META_DESC = { - writable: true, - configurable: false, - enumerable: false, - value: null -}; - -var META_KEY = Ember.GUID_KEY+'_meta'; - -/** - The key used to store meta information on object for property observing. - - @property META_KEY - @for Ember - @private - @final - @type String -*/ -Ember.META_KEY = META_KEY; - -var isDefinePropertySimulated = Ember.platform.defineProperty.isSimulated; - -function Meta(obj) { - this.descs = {}; - this.watching = {}; - this.cache = {}; - this.cacheMeta = {}; - this.source = obj; -} - -Meta.prototype = { - descs: null, - deps: null, - watching: null, - listeners: null, - cache: null, - cacheMeta: null, - source: null, - mixins: null, - bindings: null, - chains: null, - chainWatchers: null, - values: null, - proto: null -}; - -if (isDefinePropertySimulated) { - // on platforms that don't support enumerable false - // make meta fail jQuery.isPlainObject() to hide from - // jQuery.extend() by having a property that fails - // hasOwnProperty check. - Meta.prototype.__preventPlainObject__ = true; - - // 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 (MANDATORY_SETTER) { EMPTY_META.values = {}; } - -Ember.EMPTY_META = EMPTY_META; - -/** - Retrieves the meta hash for an object. If `writable` is true ensures the - hash is writable for this object as well. - - The meta object contains information about computed property descriptors as - well as any watched properties and other information. You generally will - not access this information directly but instead work with higher level - methods that manipulate this hash indirectly. - - @method meta - @for Ember - @private - - @param {Object} obj The object to retrieve meta for - @param {Boolean} [writable=true] Pass `false` if you do not intend to modify - the meta hash, allowing the method to avoid making an unnecessary copy. - @return {Object} the meta hash for an object -*/ -Ember.meta = function meta(obj, writable) { - - var ret = obj[META_KEY]; - if (writable===false) return ret || EMPTY_META; - - if (!ret) { - if (!isDefinePropertySimulated) o_defineProperty(obj, META_KEY, META_DESC); - - ret = new Meta(obj); - - if (MANDATORY_SETTER) { ret.values = {}; } - - obj[META_KEY] = ret; - - // make sure we don't accidentally try to create constructor like desc - ret.descs.constructor = null; - - } else if (ret.source !== obj) { - if (!isDefinePropertySimulated) o_defineProperty(obj, META_KEY, META_DESC); - - ret = o_create(ret); - ret.descs = o_create(ret.descs); - ret.watching = o_create(ret.watching); - ret.cache = {}; - ret.cacheMeta = {}; - ret.source = obj; - - if (MANDATORY_SETTER) { ret.values = o_create(ret.values); } - - obj[META_KEY] = ret; - } - return ret; -}; - -Ember.getMeta = function getMeta(obj, property) { - var meta = Ember.meta(obj, false); - return meta[property]; -}; - -Ember.setMeta = function setMeta(obj, property, value) { - var meta = Ember.meta(obj, true); - meta[property] = value; - return value; -}; - -/** - @deprecated - @private - - In order to store defaults for a class, a prototype may need to create - a default meta object, which will be inherited by any objects instantiated - from the class's constructor. - - However, the properties of that meta object are only shallow-cloned, - so if a property is a hash (like the event system's `listeners` hash), - it will by default be shared across all instances of that class. - - This method allows extensions to deeply clone a series of nested hashes or - other complex objects. For instance, the event system might pass - `['listeners', 'foo:change', 'ember157']` to `prepareMetaPath`, which will - walk down the keys provided. - - For each key, if the key does not exist, it is created. If it already - exists and it was inherited from its constructor, the constructor's - key is cloned. - - You can also pass false for `writable`, which will simply return - undefined if `prepareMetaPath` discovers any part of the path that - shared or undefined. - - @method metaPath - @for Ember - @param {Object} obj The object whose meta we are examining - @param {Array} path An array of keys to walk down - @param {Boolean} writable whether or not to create a new meta - (or meta property) if one does not already exist or if it's - shared with its constructor -*/ -Ember.metaPath = function metaPath(obj, path, writable) { - Ember.deprecate("Ember.metaPath is deprecated and will be removed from future releases."); - var meta = Ember.meta(obj, writable), keyName, value; - - for (var i=0, l=path.length; i= timers[middle]) { + start = middle + 2; + } else { + end = middle; + } + } - var v, ret = []; - for(var key in obj) { - if (obj.hasOwnProperty(key)) { - v = obj[key]; - if (v === 'toString') { continue; } // ignore useless items - if (Ember.typeOf(v) === 'function') { v = "function() { ... }"; } - ret.push(key + ": " + v); + return (time >= timers[start]) ? start + 2 : start; } - } - return "{" + ret.join(", ") + "}"; -}; - - - -})(); - - -(function() { -// Ember.tryCatchFinally - -/** - The purpose of the Ember Instrumentation module is - to provide efficient, general-purpose instrumentation - for Ember. - - Subscribe to a listener by using `Ember.subscribe`: + __exports__.Backburner = Backburner; + }); +define("backburner/deferred_action_queues", + ["backburner/utils","backburner/queue","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var Utils = __dependency1__["default"]; + var Queue = __dependency2__.Queue; - ```javascript - Ember.subscribe("render", { - before: function(name, timestamp, payload) { + var each = Utils.each, + isString = Utils.isString; - }, + function DeferredActionQueues(queueNames, options) { + var queues = this.queues = {}; + this.queueNames = queueNames = queueNames || []; - after: function(name, timestamp, payload) { + this.options = options; + each(queueNames, function(queueName) { + queues[queueName] = new Queue(this, queueName, options); + }); } - }); - ``` - If you return a value from the `before` callback, that same - value will be passed as a fourth parameter to the `after` - callback. + DeferredActionQueues.prototype = { + queueNames: null, + queues: null, + options: null, + + schedule: function(queueName, target, method, args, onceFlag, stack) { + var queues = this.queues, + queue = queues[queueName]; - Instrument a block of code by using `Ember.instrument`: + if (!queue) { throw new Error("You attempted to schedule an action in a queue (" + queueName + ") that doesn't exist"); } - ```javascript - Ember.instrument("render.handlebars", payload, function() { - // rendering logic - }, binding); - ``` + if (onceFlag) { + return queue.pushUnique(target, method, args, stack); + } else { + return queue.push(target, method, args, stack); + } + }, - 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. + invoke: function(target, method, args, _) { + if (args && args.length > 0) { + method.apply(target, args); + } else { + method.call(target); + } + }, - 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`. + invokeWithOnError: function(target, method, args, onError) { + try { + if (args && args.length > 0) { + method.apply(target, args); + } else { + method.call(target); + } + } catch(error) { + onError(error); + } + }, - @class Instrumentation - @namespace Ember - @static -*/ -Ember.Instrumentation = {}; + flush: function() { + var queues = this.queues, + queueNames = this.queueNames, + queueName, queue, queueItems, priorQueueNameIndex, + queueNameIndex = 0, numberOfQueues = queueNames.length, + options = this.options, + onError = options.onError || (options.onErrorTarget && options.onErrorTarget[options.onErrorMethod]), + invoke = onError ? this.invokeWithOnError : this.invoke; -var subscribers = [], cache = {}; + outerloop: + while (queueNameIndex < numberOfQueues) { + queueName = queueNames[queueNameIndex]; + queue = queues[queueName]; + queueItems = queue._queueBeingFlushed = queue._queue.slice(); + queue._queue = []; -var populateListeners = function(name) { - var listeners = [], subscriber; + var queueOptions = queue.options, // TODO: write a test for this + before = queueOptions && queueOptions.before, + after = queueOptions && queueOptions.after, + target, method, args, stack, + queueIndex = 0, numberOfQueueItems = queueItems.length; - for (var i=0, l=subscribers.length; i 0) { + if (onError) { + try { + method.apply(target, args); + } catch (e) { + onError(e); + } + } else { + method.apply(target, args); + } + } else { + if (onError) { + try { + method.call(target); + } catch(e) { + onError(e); + } + } else { + method.call(target); + } + } + } + if (l && after) { after(); } - regex = regex.join("\\."); - regex = regex + "(\\..*)?"; + // check if new items have been added + if (queue.length > l) { + this._queue = queue.slice(l); + this.flush(); + } else { + this._queue.length = 0; + } + }, - var subscriber = { - pattern: pattern, - regex: new RegExp("^" + regex + "$"), - object: object - }; + cancel: function(actionToCancel) { + var queue = this._queue, currentTarget, currentMethod, i, l; - subscribers.push(subscriber); - cache = {}; + for (i = 0, l = queue.length; i < l; i += 4) { + currentTarget = queue[i]; + currentMethod = queue[i+1]; - return subscriber; -}; + if (currentTarget === actionToCancel.target && currentMethod === actionToCancel.method) { + queue.splice(i, 4); + return true; + } + } -/** - Unsubscribes from a particular event or instrumented block of code. + // if not found in current queue + // could be in the queue that is being flushed + queue = this._queueBeingFlushed; + if (!queue) { + return; + } + for (i = 0, l = queue.length; i < l; i += 4) { + currentTarget = queue[i]; + currentMethod = queue[i+1]; - @method unsubscribe - @namespace Ember.Instrumentation + if (currentTarget === actionToCancel.target && currentMethod === actionToCancel.method) { + // don't mess with array during flush + // just nullify the method + queue[i+1] = null; + return true; + } + } + } + }; - @param {Object} [subscriber] -*/ -Ember.Instrumentation.unsubscribe = function(subscriber) { - var index; + __exports__.Queue = Queue; + }); +define("backburner/utils", + ["exports"], + function(__exports__) { + "use strict"; + __exports__["default"] = { + each: function(collection, callback) { + for (var i = 0; i < collection.length; i++) { + callback(collection[i]); + } + }, - for (var i=0, l=subscribers.length; i -1) { + try { + if (fs.existsSync(headFilePath)) { + var headFile = fs.readFileSync(headFilePath, {encoding: 'utf8'}); + var branchName = headFile.split('/').slice(-1)[0].trim(); + var refPath = headFile.split(' ')[1]; + var branchSHA; + + if (refPath) { + var branchPath = path.join(gitPath, refPath.trim()); + branchSHA = fs.readFileSync(branchPath); + } else { + branchSHA = branchName; + } -(function() { -var map, forEach, indexOf, splice, filter; -map = Array.prototype.map || Ember.ArrayPolyfills.map; -forEach = Array.prototype.forEach || Ember.ArrayPolyfills.forEach; -indexOf = Array.prototype.indexOf || Ember.ArrayPolyfills.indexOf; -filter = Array.prototype.filter || Ember.ArrayPolyfills.filter; -splice = Array.prototype.splice; - -/** - * Defines some convenience methods for working with Enumerables. - * `Ember.EnumerableUtils` uses `Ember.ArrayPolyfills` when necessary. - * - * @class EnumerableUtils - * @namespace Ember - * @static - * */ -var utils = Ember.EnumerableUtils = { - /** - * Calls the map function on the passed object with a specified callback. This - * uses `Ember.ArrayPolyfill`'s-map method when necessary. - * - * @method map - * @param {Object} obj The object that should be mapped - * @param {Function} callback The callback to execute - * @param {Object} thisArg Value to use as this when executing *callback* - * - * @return {Array} An array of mapped values. - */ - map: function(obj, callback, thisArg) { - return obj.map ? obj.map.call(obj, callback, thisArg) : map.call(obj, callback, thisArg); - }, - - /** - * Calls the forEach function on the passed object with a specified callback. This - * uses `Ember.ArrayPolyfill`'s-forEach method when necessary. - * - * @method forEach - * @param {Object} obj The object to call forEach on - * @param {Function} callback The callback to execute - * @param {Object} thisArg Value to use as this when executing *callback* - * - */ - forEach: function(obj, callback, thisArg) { - return obj.forEach ? obj.forEach.call(obj, callback, thisArg) : forEach.call(obj, callback, thisArg); - }, - - /** - * 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 - */ - filter: function(obj, callback, thisArg) { - return obj.filter ? obj.filter.call(obj, callback, thisArg) : filter.call(obj, callback, thisArg); - }, - - /** - * Calls the indexOf function on the passed object with a specified callback. This - * uses `Ember.ArrayPolyfill`'s-indexOf method when necessary. - * - * @method indexOf - * @param {Object} obj The object to call indexOn on - * @param {Function} callback The callback to execute - * @param {Object} index The index to start searching from - * - */ - indexOf: function(obj, element, index) { - return obj.indexOf ? obj.indexOf.call(obj, element, index) : indexOf.call(obj, element, index); - }, - - /** - * Returns an array of indexes of the first occurrences of the passed elements - * on the passed object. - * - * ```javascript - * var array = [1, 2, 3, 4, 5]; - * Ember.EnumerableUtils.indexesOf(array, [2, 5]); // [1, 4] - * - * var fubar = "Fubarr"; - * Ember.EnumerableUtils.indexesOf(fubar, ['b', 'r']); // [2, 4] - * ``` - * - * @method indexesOf - * @param {Object} obj The object to check for element indexes - * @param {Array} elements The elements to search for on *obj* - * - * @return {Array} An array of indexes. - * - */ - indexesOf: function(obj, elements) { - return elements === undefined ? [] : utils.map(elements, function(item) { - return utils.indexOf(obj, item); - }); - }, - - /** - * Adds an object to an array. If the array already includes the object this - * method has no effect. - * - * @method addObject - * @param {Array} array The array the passed item should be added to - * @param {Object} item The item to add to the passed array - * - * @return 'undefined' - */ - addObject: function(array, item) { - var index = utils.indexOf(array, item); - if (index === -1) { array.push(item); } - }, - - /** - * Removes an object from an array. If the array does not contain the passed - * object this method has no effect. - * - * @method removeObject - * @param {Array} array The array to remove the item from. - * @param {Object} item The item to remove from the passed array. - * - * @return 'undefined' - */ - removeObject: function(array, item) { - var index = utils.indexOf(array, item); - if (index !== -1) { array.splice(index, 1); } - }, - - _replace: function(array, idx, amt, objects) { - var args = [].concat(objects), chunk, ret = [], - // https://code.google.com/p/chromium/issues/detail?id=56588 - size = 60000, start = idx, ends = amt, count; - - while (args.length) { - count = ends > size ? size : ends; - if (count <= 0) { count = 0; } - - chunk = args.splice(0, size); - chunk = [start, count].concat(chunk); - - start += size; - ends -= count; - - ret = ret.concat(splice.apply(array, chunk)); - } - return ret; - }, - - /** - * Replaces objects in an array with the passed objects. - * - * ```javascript - * var array = [1,2,3]; - * Ember.EnumerableUtils.replace(array, 1, 2, [4, 5]); // [1, 4, 5] - * - * var array = [1,2,3]; - * Ember.EnumerableUtils.replace(array, 1, 1, [4, 5]); // [1, 4, 5, 3] - * - * var array = [1,2,3]; - * Ember.EnumerableUtils.replace(array, 10, 1, [4, 5]); // [1, 2, 3, 4, 5] - * ``` - * - * @method replace - * @param {Array} array The array the objects should be inserted into. - * @param {Number} idx Starting index in the array to replace. If *idx* >= - * length, then append to the end of the array. - * @param {Number} amt Number of elements that should be removed from the array, - * starting at *idx* - * @param {Array} objects An array of zero or more objects that should be - * inserted into the array at *idx* - * - * @return {Array} The modified array. - */ - replace: function(array, idx, amt, objects) { - if (array.replace) { - return array.replace(idx, amt, objects); - } else { - return utils._replace(array, idx, amt, objects); - } - }, - - /** - * Calculates the intersection of two arrays. This method returns a new array - * filled with the records that the two passed arrays share with each other. - * If there is no intersection, an empty array will be returned. - * - * ```javascript - * var array1 = [1, 2, 3, 4, 5]; - * var array2 = [1, 3, 5, 6, 7]; - * - * Ember.EnumerableUtils.intersection(array1, array2); // [1, 3, 5] - * - * var array1 = [1, 2, 3]; - * var array2 = [4, 5, 6]; - * - * Ember.EnumerableUtils.intersection(array1, array2); // [] - * ``` - * - * @method intersection - * @param {Array} array1 The first array - * @param {Array} array2 The second array - * - * @return {Array} The intersection of the two passed arrays. - */ - intersection: function(array1, array2) { - var intersection = []; - - utils.forEach(array1, function(element) { - if (utils.indexOf(array2, element) >= 0) { - intersection.push(element); + output.push(branchSHA.slice(0,10)); + } + } catch (err) { + console.error(err.stack); + } + return output.join('.'); + } else { + return packageVersion; } - }); + }; + }); +define("container", + ["container/container","exports"], + function(__dependency1__, __exports__) { + "use strict"; + /* + Public api for the container is still in flux. + The public api, specified on the application namespace should be considered the stable api. + // @module container + @private + */ - return intersection; - } -}; + /* + Flag to enable/disable model factory injections (disabled by default) + If model factory injections are enabled, models should not be + accessed globally (only through `container.lookupFactory('model:modelName'))`); + */ + Ember.MODEL_FACTORY_INJECTIONS = false; -})(); + if (Ember.ENV && typeof Ember.ENV.MODEL_FACTORY_INJECTIONS !== 'undefined') { + Ember.MODEL_FACTORY_INJECTIONS = !!Ember.ENV.MODEL_FACTORY_INJECTIONS; + } + var Container = __dependency1__["default"]; -(function() { -/** -@module ember-metal -*/ - -var META_KEY = Ember.META_KEY, get; - -var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER; - -var IS_GLOBAL_PATH = /^([A-Z$]|([0-9][A-Z$])).*[\.\*]/; -var HAS_THIS = /^this[\.\*]/; -var FIRST_KEY = /^([^\.\*]+)/; - -// .......................................................... -// 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. - -/** - 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. - - 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.) - - 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. - - Note that if the object itself is `undefined`, this method will throw - an error. - - @method get - @for Ember - @param {Object} obj The object to retrieve from. - @param {String} keyName The property key to retrieve - @return {Object} the property value or `null`. -*/ -get = function get(obj, keyName) { - // Helpers that operate with 'this' within an #each - if (keyName === '') { - return obj; - } + __exports__["default"] = Container; + }); +define("container/container", + ["container/inheriting_dict","ember-metal/core","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var InheritingDict = __dependency1__["default"]; + var Ember = __dependency2__["default"]; + // Ember.assert - if (!keyName && 'string'===typeof obj) { - keyName = obj; - obj = null; - } + // A lightweight container that helps to assemble and decouple components. + // Public api for the container is still in flux. + // The public api, specified on the application namespace should be considered the stable api. + function Container(parent) { + this.parent = parent; + this.children = []; - Ember.assert("Cannot call get with "+ keyName +" key.", !!keyName); - Ember.assert("Cannot call get with '"+ keyName +"' on an undefined object.", obj !== undefined); + this.resolver = parent && parent.resolver || function() {}; - if (obj === null || keyName.indexOf('.') !== -1) { - return getPath(obj, keyName); - } + this.registry = new InheritingDict(parent && parent.registry); + this.cache = new InheritingDict(parent && parent.cache); + this.factoryCache = new InheritingDict(parent && parent.factoryCache); + this.resolveCache = new InheritingDict(parent && parent.resolveCache); + this.typeInjections = new InheritingDict(parent && parent.typeInjections); + this.injections = {}; - var meta = obj[META_KEY], desc = meta && meta.descs[keyName], ret; - if (desc) { - return desc.get(obj, keyName); - } else { - if (MANDATORY_SETTER && meta && meta.watching[keyName] > 0) { - ret = meta.values[keyName]; - } else { - ret = obj[keyName]; - } + this.factoryTypeInjections = new InheritingDict(parent && parent.factoryTypeInjections); + this.factoryInjections = {}; - if (ret === undefined && - 'object' === typeof obj && !(keyName in obj) && 'function' === typeof obj.unknownProperty) { - return obj.unknownProperty(keyName); + this._options = new InheritingDict(parent && parent._options); + this._typeOptions = new InheritingDict(parent && parent._typeOptions); } - return ret; - } -}; - -// Currently used only by Ember Data tests -if (Ember.config.overrideAccessors) { - Ember.get = get; - Ember.config.overrideAccessors(); - get = Ember.get; -} - -/** - 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) and * separators. - - @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. -*/ -var normalizeTuple = Ember.normalizeTuple = function(target, path) { - var hasThis = HAS_THIS.test(path), - isGlobal = !hasThis && IS_GLOBAL_PATH.test(path), - key; - - if (!target || isGlobal) target = Ember.lookup; - if (hasThis) path = path.slice(5); - - if (target === Ember.lookup) { - key = path.match(FIRST_KEY)[0]; - target = get(target, key); - path = path.slice(key.length+1); - } - - // must return some kind of path to be valid else other things will break. - if (!path || path.length===0) throw new Ember.Error('Path cannot be empty'); + Container.prototype = { - return [ target, path ]; -}; + /** + @property parent + @type Container + @default null + */ + parent: null, -var getPath = Ember._getPath = function(root, path) { - var hasThis, parts, tuple, idx, len; + /** + @property children + @type Array + @default [] + */ + children: 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 && path.indexOf('.') === -1) { return get(Ember.lookup, path); } + /** + @property resolver + @type function + */ + resolver: null, - // detect complicated paths and normalize them - hasThis = HAS_THIS.test(path); + /** + @property registry + @type InheritingDict + */ + registry: null, - if (!root || hasThis) { - tuple = normalizeTuple(root, path); - root = tuple[0]; - path = tuple[1]; - tuple.length = 0; - } + /** + @property cache + @type InheritingDict + */ + cache: null, - 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; -}; + /** + @property typeInjections + @type InheritingDict + */ + typeInjections: null, -Ember.getWithDefault = function(root, key, defaultValue) { - var value = get(root, key); + /** + @property injections + @type Object + @default {} + */ + injections: null, - if (value === undefined) { return defaultValue; } - return value; -}; + /** + @private + @property _options + @type InheritingDict + @default null + */ + _options: null, -Ember.get = get; + /** + @private -})(); + @property _typeOptions + @type InheritingDict + */ + _typeOptions: null, + /** + Returns a new child of the current container. These children are configured + to correctly inherit from the current container. + @method child + @return {Container} + */ + child: function() { + var container = new Container(this); + this.children.push(container); + return container; + }, -(function() { -/** -@module ember-metal -*/ + /** + Sets a key-value pair on the current container. If a parent container, + has the same key, once set on a child, the parent and child will diverge + as expected. -var o_create = Ember.create, - metaFor = Ember.meta, - META_KEY = Ember.META_KEY, - a_slice = [].slice, - /* listener flags */ - ONCE = 1, SUSPENDED = 2; + @method set + @param {Object} object + @param {String} key + @param {any} value + */ + set: function(object, key, value) { + object[key] = value; + }, -/* - 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. + /** + Registers a factory for later injection. - The hashes are stored in the object's meta hash, and look like this: + Example: - // Object's meta hash - { - listeners: { // variable name: `listenerSet` - "foo:changed": [ // variable name: `actions` - target, method, flags - ] - } - } + ```javascript + var container = new Container(); -*/ + container.register('model:user', Person, {singleton: false }); + container.register('fruit:favorite', Orange); + container.register('communication:main', Email, {singleton: false}); + ``` -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; -} + @method register + @param {String} fullName + @param {Function} factory + @param {Object} options + */ + register: function(fullName, factory, options) { + Ember.assert('fullName must be a proper full name', validateFullName(fullName)); -function actionsFor(obj, eventName) { - var meta = metaFor(obj, true), - actions; + if (factory === undefined) { + throw new TypeError('Attempting to register an unknown factory: `' + fullName + '`'); + } - if (!meta.listeners) { meta.listeners = {}; } + var normalizedName = this.normalize(fullName); - if (!meta.hasOwnProperty('listeners')) { - // setup inherited copy of the listeners object - meta.listeners = o_create(meta.listeners); - } + if (this.cache.has(normalizedName)) { + throw new Error('Cannot re-register: `' + fullName +'`, as it has already been looked up.'); + } - actions = meta.listeners[eventName]; + this.registry.set(normalizedName, factory); + this._options.set(normalizedName, options || {}); + }, - // if there are actions, but the eventName doesn't exist in our listeners, then copy them from the prototype - if (actions && !meta.listeners.hasOwnProperty(eventName)) { - actions = meta.listeners[eventName] = meta.listeners[eventName].slice(); - } else if (!actions) { - actions = meta.listeners[eventName] = []; - } + /** + Unregister a fullName - return actions; -} + ```javascript + var container = new Container(); + container.register('model:user', User); -function actionsUnion(obj, eventName, otherActions) { - var meta = obj[META_KEY], - actions = meta && meta.listeners && meta.listeners[eventName]; + container.lookup('model:user') instanceof User //=> true - if (!actions) { return; } - for (var i = actions.length - 3; i >= 0; i -= 3) { - var target = actions[i], - method = actions[i+1], - flags = actions[i+2], - actionIndex = indexOf(otherActions, target, method); + container.unregister('model:user') + container.lookup('model:user') === undefined //=> true + ``` - if (actionIndex === -1) { - otherActions.push(target, method, flags); - } - } -} + @method unregister + @param {String} fullName + */ + unregister: function(fullName) { + Ember.assert('fullName must be a proper full name', validateFullName(fullName)); -function actionsDiff(obj, eventName, otherActions) { - var meta = obj[META_KEY], - actions = meta && meta.listeners && meta.listeners[eventName], - diffActions = []; + var normalizedName = this.normalize(fullName); - if (!actions) { return; } - for (var i = actions.length - 3; i >= 0; i -= 3) { - var target = actions[i], - method = actions[i+1], - flags = actions[i+2], - actionIndex = indexOf(otherActions, target, method); + this.registry.remove(normalizedName); + this.cache.remove(normalizedName); + this.factoryCache.remove(normalizedName); + this.resolveCache.remove(normalizedName); + this._options.remove(normalizedName); + }, - if (actionIndex !== -1) { continue; } + /** + Given a fullName return the corresponding factory. - otherActions.push(target, method, flags); - diffActions.push(target, method, flags); - } + By default `resolve` will retrieve the factory from + its container's registry. - return diffActions; -} - -/** - Add an event listener - - @method addListener - @for Ember - @param obj - @param {String} eventName - @param {Object|Function} targetOrMethod A target object or a function - @param {Function|String} method A function or the name of a function to be called on `target` - @param {Boolean} once A flag whether a function should only be called once -*/ -function addListener(obj, eventName, target, method, once) { - Ember.assert("You must pass at least an object and event name to Ember.addListener", !!obj && !!eventName); - - if (!method && 'function' === typeof target) { - method = target; - target = null; - } + ```javascript + var container = new Container(); + container.register('api:twitter', Twitter); - var actions = actionsFor(obj, eventName), - actionIndex = indexOf(actions, target, method), - flags = 0; + container.resolve('api:twitter') // => Twitter + ``` - if (once) flags |= ONCE; + Optionally the container can be provided with a custom resolver. + If provided, `resolve` will first provide the custom resolver + the opportunity to resolve the fullName, otherwise it will fallback + to the registry. - if (actionIndex !== -1) { return; } + ```javascript + var container = new Container(); + container.resolver = function(fullName) { + // lookup via the module system of choice + }; - actions.push(target, method, flags); + // the twitter factory is added to the module system + container.resolve('api:twitter') // => Twitter + ``` - if ('function' === typeof obj.didAddListener) { - obj.didAddListener(eventName, target, method); - } -} - -/** - Remove an event listener - - Arguments should match those passed to `Ember.addListener`. - - @method removeListener - @for Ember - @param obj - @param {String} eventName - @param {Object|Function} targetOrMethod A target object or a function - @param {Function|String} method A function or the name of a function to be called on `target` -*/ -function removeListener(obj, eventName, target, method) { - Ember.assert("You must pass at least an object and event name to Ember.removeListener", !!obj && !!eventName); - - if (!method && 'function' === typeof target) { - method = target; - target = null; - } + @method resolve + @param {String} fullName + @return {Function} fullName's factory + */ + resolve: function(fullName) { + Ember.assert('fullName must be a proper full name', validateFullName(fullName)); + return resolve(this, this.normalize(fullName)); + }, - function _removeListener(target, method) { - var actions = actionsFor(obj, eventName), - actionIndex = indexOf(actions, target, method); + /** + A hook that can be used to describe how the resolver will + attempt to find the factory. - // action doesn't exist, give up silently - if (actionIndex === -1) { return; } + For example, the default Ember `.describe` returns the full + class name (including namespace) where Ember's resolver expects + to find the `fullName`. - actions.splice(actionIndex, 3); + @method describe + @param {String} fullName + @return {string} described fullName + */ + describe: function(fullName) { + return fullName; + }, - if ('function' === typeof obj.didRemoveListener) { - obj.didRemoveListener(eventName, target, method); - } - } + /** + A hook to enable custom fullName normalization behaviour - if (method) { - _removeListener(target, method); - } else { - var meta = obj[META_KEY], - actions = meta && meta.listeners && meta.listeners[eventName]; + @method normalize + @param {String} fullName + @return {string} normalized fullName + */ + normalize: function(fullName) { + return fullName; + }, - if (!actions) { return; } - for (var i = actions.length - 3; i >= 0; i -= 3) { - _removeListener(actions[i], actions[i+1]); - } - } -} - -/** - Suspend listener during callback. - - 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 suspendListener - @for Ember - - @private - @param obj - @param {String} eventName - @param {Object|Function} targetOrMethod A target object or a function - @param {Function|String} method A function or the name of a function to be called on `target` - @param {Function} callback -*/ -function suspendListener(obj, eventName, target, method, callback) { - if (!method && 'function' === typeof target) { - method = target; - target = null; - } + /** + @method makeToString - var actions = actionsFor(obj, eventName), - actionIndex = indexOf(actions, target, method); + @param {any} factory + @param {string} fullName + @return {function} toString function + */ + makeToString: function(factory, fullName) { + return factory.toString(); + }, - if (actionIndex !== -1) { - actions[actionIndex+2] |= SUSPENDED; // mark the action as suspended - } + /** + Given a fullName return a corresponding instance. - function tryable() { return callback.call(target); } - function finalizer() { if (actionIndex !== -1) { actions[actionIndex+2] &= ~SUSPENDED; } } - - return Ember.tryFinally(tryable, finalizer); -} - -/** - Suspends multiple listeners during a callback. - - @method suspendListeners - @for Ember - - @private - @param obj - @param {Array} eventName Array of event names - @param {Object|Function} targetOrMethod A target object or a function - @param {Function|String} method A function or the name of a function to be called on `target` - @param {Function} callback -*/ -function suspendListeners(obj, eventNames, target, method, callback) { - if (!method && 'function' === typeof target) { - method = target; - target = null; - } + The default behaviour is for lookup to return a singleton instance. + The singleton is scoped to the container, allowing multiple containers + to all have their own locally scoped singletons. - var suspendedActions = [], - actionsList = [], - eventName, actions, i, l; + ```javascript + var container = new Container(); + container.register('api:twitter', Twitter); - for (i=0, l=eventNames.length; i true - function tryable() { return callback.call(target); } + // by default the container will return singletons + var twitter2 = container.lookup('api:twitter'); + twitter2 instanceof Twitter; // => true - function finalizer() { - for (var i = 0, l = suspendedActions.length; i < l; i++) { - var actionIndex = suspendedActions[i]; - actionsList[i][actionIndex+2] &= ~SUSPENDED; - } - } + twitter === twitter2; //=> true + ``` - return Ember.tryFinally(tryable, finalizer); -} + If singletons are not wanted an optional flag can be provided at lookup. -/** - Return a list of currently watched events + ```javascript + var container = new Container(); + container.register('api:twitter', Twitter); - @private - @method watchedEvents - @for Ember - @param obj -*/ -function watchedEvents(obj) { - var listeners = obj[META_KEY].listeners, ret = []; + var twitter = container.lookup('api:twitter', { singleton: false }); + var twitter2 = container.lookup('api:twitter', { singleton: false }); - if (listeners) { - for(var eventName in listeners) { - if (listeners[eventName]) { ret.push(eventName); } - } - } - return ret; -} - -/** - 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. - - @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); - } + twitter === twitter2; //=> false + ``` - if (!actions) { - var meta = obj[META_KEY]; - actions = meta && meta.listeners && meta.listeners[eventName]; - } + @method lookup + @param {String} fullName + @param {Object} options + @return {any} + */ + lookup: function(fullName, options) { + Ember.assert('fullName must be a proper full name', validateFullName(fullName)); + return lookup(this, this.normalize(fullName), options); + }, - if (!actions) { return; } - - 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) { method = target[method]; } - if (params) { - method.apply(target, params); - } else { - method.call(target); - } - } - return true; -} - -/** - @private - @method hasListeners - @for Ember - @param obj - @param {String} eventName -*/ -function hasListeners(obj, eventName) { - var meta = obj[META_KEY], - actions = meta && meta.listeners && meta.listeners[eventName]; - - return !!(actions && actions.length); -} - -/** - @private - @method listenersFor - @for Ember - @param obj - @param {String} eventName -*/ -function listenersFor(obj, eventName) { - var ret = []; - var meta = obj[META_KEY], - actions = meta && meta.listeners && meta.listeners[eventName]; - - if (!actions) { return ret; } - - for (var i = 0, l = actions.length; i < l; i += 3) { - var target = actions[i], - method = actions[i+1]; - ret.push([target, method]); - } + /** + Given a fullName return the corresponding factory. - return ret; -} + @method lookupFactory + @param {String} fullName + @return {any} + */ + lookupFactory: function(fullName) { + Ember.assert('fullName must be a proper full name', validateFullName(fullName)); + return factoryFor(this, this.normalize(fullName)); + }, -/** - Define a property as a function that should be executed when - a specified event or events are triggered. + /** + Given a fullName check if the container is aware of its factory + or singleton instance. + @method has + @param {String} fullName + @return {Boolean} + */ + has: function(fullName) { + Ember.assert('fullName must be a proper full name', validateFullName(fullName)); + return has(this, this.normalize(fullName)); + }, - ``` javascript - var Job = Ember.Object.extend({ - logCompleted: Ember.on('completed', function(){ - console.log('Job completed!'); - }) - }); - var job = Job.create(); - Ember.sendEvent(job, 'completed'); // Logs "Job completed!" - ``` - - @method on - @for Ember - @param {String} eventNames* - @param {Function} func - @return func -*/ -Ember.on = function(){ - var func = a_slice.call(arguments, -1)[0], - events = a_slice.call(arguments, 0, -1); - func.__ember_listens__ = events; - return func; -}; - -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.listenersDiff = actionsDiff; -Ember.listenersUnion = actionsUnion; + /** + Allow registering options for all factories of a type. -})(); + ```javascript + var container = new Container(); + // if all of type `connection` must not be singletons + container.optionsForType('connection', { singleton: false }); + container.register('connection:twitter', TwitterConnection); + container.register('connection:facebook', FacebookConnection); -(function() { -var guidFor = Ember.guidFor, - sendEvent = Ember.sendEvent; + var twitter = container.lookup('connection:twitter'); + var twitter2 = container.lookup('connection:twitter'); -/* - this.observerSet = { - [senderGuid]: { // variable name: `keySet` - [keyName]: listIndex - } - }, - this.observers = [ - { - sender: obj, - keyName: keyName, - eventName: eventName, - listeners: [ - [target, method, flags] - ] - }, - ... - ] -*/ -var ObserverSet = Ember._ObserverSet = function() { - this.clear(); -}; - -ObserverSet.prototype.add = function(sender, keyName, eventName) { - var observerSet = this.observerSet, - observers = this.observers, - senderGuid = guidFor(sender), - keySet = observerSet[senderGuid], - index; - - 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; -}; - -ObserverSet.prototype.flush = function() { - var observers = this.observers, i, len, observer, sender; - this.clear(); - for (i=0, len=observers.length; i < len; ++i) { - observer = observers[i]; - sender = observer.sender; - if (sender.isDestroying || sender.isDestroyed) { continue; } - sendEvent(sender, observer.eventName, [sender, observer.keyName], observer.listeners); - } -}; + twitter === twitter2; // => false -ObserverSet.prototype.clear = function() { - this.observerSet = {}; - this.observers = []; -}; -})(); + var facebook = container.lookup('connection:facebook'); + var facebook2 = container.lookup('connection:facebook'); + facebook === facebook2; // => false + ``` + @method optionsForType + @param {String} type + @param {Object} options + */ + optionsForType: function(type, options) { + if (this.parent) { illegalChildOperation('optionsForType'); } -(function() { -var META_KEY = Ember.META_KEY, - guidFor = Ember.guidFor, - tryFinally = Ember.tryFinally, - sendEvent = Ember.sendEvent, - listenersUnion = Ember.listenersUnion, - listenersDiff = Ember.listenersDiff, - ObserverSet = Ember._ObserverSet, - beforeObserverSet = new ObserverSet(), - observerSet = new ObserverSet(), - deferred = 0; - -// .......................................................... -// PROPERTY CHANGES -// - -/** - 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. - - @method propertyWillChange - @for Ember - @param {Object} obj The object with the property that will change - @param {String} keyName The property key (or path) that will change. - @return {void} -*/ -function propertyWillChange(obj, keyName) { - var m = obj[META_KEY], - watching = (m && m.watching[keyName] > 0) || keyName === 'length', - proto = m && m.proto, - desc = m && m.descs[keyName]; - - if (!watching) { return; } - if (proto === obj) { return; } - if (desc && desc.willChange) { desc.willChange(obj, keyName); } - dependentKeysWillChange(obj, keyName, m); - chainsWillChange(obj, keyName, m); - notifyBeforeObservers(obj, keyName); -} -Ember.propertyWillChange = propertyWillChange; - -/** - 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[META_KEY], - watching = (m && m.watching[keyName] > 0) || keyName === 'length', - proto = m && m.proto, - desc = m && m.descs[keyName]; - - if (proto === obj) { return; } - - // shouldn't this mean that we're watching this key? - if (desc && desc.didChange) { desc.didChange(obj, keyName); } - if (!watching && keyName !== 'length') { return; } - - dependentKeysDidChange(obj, keyName, m); - chainsDidChange(obj, keyName, m, false); - notifyObservers(obj, keyName); -} -Ember.propertyDidChange = propertyDidChange; - -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 seen = WILL_SEEN, top = !seen; - if (top) { seen = WILL_SEEN = {}; } - iterDeps(propertyWillChange, obj, depKey, seen, meta); - if (top) { WILL_SEEN = null; } -} - -// called whenever a property has just changed to update dependent keys -function dependentKeysDidChange(obj, depKey, meta) { - if (obj.isDestroying) { return; } - - var seen = DID_SEEN, top = !seen; - if (top) { seen = DID_SEEN = {}; } - iterDeps(propertyDidChange, obj, depKey, seen, meta); - if (top) { DID_SEEN = null; } -} - -function iterDeps(method, obj, depKey, seen, meta) { - var guid = guidFor(obj); - if (!seen[guid]) seen[guid] = {}; - if (seen[guid][depKey]) return; - seen[guid][depKey] = true; - - var deps = meta.deps; - deps = deps && deps[depKey]; - if (deps) { - for(var key in deps) { - var desc = meta.descs[key]; - if (desc && desc._suspended === obj) continue; - method(obj, key); - } - } -} + this._typeOptions.set(type, options); + }, -function chainsWillChange(obj, keyName, m) { - if (!(m.hasOwnProperty('chainWatchers') && - m.chainWatchers[keyName])) { - return; - } + /** + @method options + @param {String} type + @param {Object} options + */ + options: function(type, options) { + this.optionsForType(type, options); + }, - var nodes = m.chainWatchers[keyName], - events = [], - i, l; + /** + Used only via `injection`. - for(i = 0, l = nodes.length; i < l; i++) { - nodes[i].willChange(events); - } + Provides a specialized form of injection, specifically enabling + all objects of one type to be injected with a reference to another + object. - for (i = 0, l = events.length; i < l; i += 2) { - propertyWillChange(events[i], events[i+1]); - } -} + For example, provided each object of type `controller` needed a `router`. + one would do the following: -function chainsDidChange(obj, keyName, m, suppressEvents) { - if (!(m && m.hasOwnProperty('chainWatchers') && - m.chainWatchers[keyName])) { - return; - } + ```javascript + var container = new Container(); - var nodes = m.chainWatchers[keyName], - events = suppressEvents ? null : [], - i, l; + container.register('router:main', Router); + container.register('controller:user', UserController); + container.register('controller:post', PostController); - for(i = 0, l = nodes.length; i < l; i++) { - nodes[i].didChange(events); - } + container.typeInjection('controller', 'router', 'router:main'); - if (suppressEvents) { - return; - } + var user = container.lookup('controller:user'); + var post = container.lookup('controller:post'); - for (i = 0, l = events.length; i < l; i += 2) { - propertyDidChange(events[i], events[i+1]); - } -} - -Ember.overrideChains = function(obj, keyName, m) { - chainsDidChange(obj, keyName, m, true); -}; - -/** - @method beginPropertyChanges - @chainable - @private -*/ -function beginPropertyChanges() { - deferred++; -} - -Ember.beginPropertyChanges = beginPropertyChanges; - -/** - @method endPropertyChanges - @private -*/ -function endPropertyChanges() { - deferred--; - if (deferred<=0) { - beforeObserverSet.clear(); - observerSet.flush(); - } -} + user.router instanceof Router; //=> true + post.router instanceof Router; //=> true -Ember.endPropertyChanges = endPropertyChanges; + // both controllers share the same router + user.router === post.router; //=> true + ``` -/** - Make a series of property changes together in an - exception-safe way. + @private + @method typeInjection + @param {String} type + @param {String} property + @param {String} fullName + */ + typeInjection: function(type, property, fullName) { + Ember.assert('fullName must be a proper full name', validateFullName(fullName)); + if (this.parent) { illegalChildOperation('typeInjection'); } - ```javascript - Ember.changeProperties(function() { - obj1.set('foo', mayBlowUpWhenSet); - obj2.set('bar', baz); - }); - ``` - - @method changeProperties - @param {Function} callback - @param [binding] -*/ -Ember.changeProperties = function(cb, binding) { - beginPropertyChanges(); - tryFinally(cb, endPropertyChanges, binding); -}; - -function notifyBeforeObservers(obj, keyName) { - if (obj.isDestroying) { return; } - - var eventName = keyName + ':before', listeners, diff; - if (deferred) { - listeners = beforeObserverSet.add(obj, keyName, eventName); - diff = listenersDiff(obj, eventName, listeners); - sendEvent(obj, eventName, [obj, keyName], diff); - } else { - sendEvent(obj, eventName, [obj, keyName]); - } -} + var fullNameType = fullName.split(':')[0]; + if(fullNameType === type) { + throw new Error('Cannot inject a `' + fullName + '` on other ' + type + '(s). Register the `' + fullName + '` as a different type and perform the typeInjection.'); + } + addTypeInjection(this.typeInjections, type, property, fullName); + }, -function notifyObservers(obj, keyName) { - if (obj.isDestroying) { return; } + /** + Defines injection rules. - var eventName = keyName + ':change', listeners; - if (deferred) { - listeners = observerSet.add(obj, keyName, eventName); - listenersUnion(obj, eventName, listeners); - } else { - sendEvent(obj, eventName, [obj, keyName]); - } -} + These rules are used to inject dependencies onto objects when they + are instantiated. -})(); + Two forms of injections are possible: + * Injecting one fullName on another fullName + * Injecting one fullName on a type + Example: -(function() { -// META_KEY -// _getPath -// propertyWillChange, propertyDidChange - -var META_KEY = Ember.META_KEY, - MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER, - IS_GLOBAL = /^([A-Z$]|([0-9][A-Z$]))/, - getPath = Ember._getPath; - -/** - 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. - - @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; - } + ```javascript + var container = new Container(); - Ember.assert("Cannot call set with "+ keyName +" key.", !!keyName); + container.register('source:main', Source); + container.register('model:user', User); + container.register('model:post', Post); - if (!obj || keyName.indexOf('.') !== -1) { - return setPath(obj, keyName, value, tolerant); - } + // injecting one fullName on another fullName + // eg. each user model gets a post model + container.injection('model:user', 'post', 'model:post'); - Ember.assert("You need to provide an object and key to `set`.", !!obj && keyName !== undefined); - Ember.assert('calling set on destroyed object', !obj.isDestroyed); + // injecting one fullName on another type + container.injection('model', 'source', 'source:main'); - var meta = obj[META_KEY], desc = meta && meta.descs[keyName], - isUnknown, currentValue; - if (desc) { - desc.set(obj, keyName, value); - } else { - isUnknown = 'object' === typeof obj && !(keyName in obj); - - // setUnknownProperty is called if `obj` is an object, - // the property does not already exist, and the - // `setUnknownProperty` method exists on the object - if (isUnknown && 'function' === typeof obj.setUnknownProperty) { - obj.setUnknownProperty(keyName, value); - } else if (meta && meta.watching[keyName] > 0) { - if (MANDATORY_SETTER) { - currentValue = meta.values[keyName]; - } else { - currentValue = obj[keyName]; - } - // only trigger a change if the value has changed - if (value !== currentValue) { - Ember.propertyWillChange(obj, keyName); - if (MANDATORY_SETTER) { - if ((currentValue === undefined && !(keyName in obj)) || !obj.propertyIsEnumerable(keyName)) { - Ember.defineProperty(obj, keyName, null, value); // setup mandatory setter - } else { - meta.values[keyName] = value; - } - } else { - obj[keyName] = value; - } - Ember.propertyDidChange(obj, keyName); - } - } else { - obj[keyName] = value; - } - } - return value; -}; + var user = container.lookup('model:user'); + var post = container.lookup('model:post'); -// Currently used only by Ember Data tests -if (Ember.config.overrideAccessors) { - Ember.set = set; - Ember.config.overrideAccessors(); - set = Ember.set; -} + user.source instanceof Source; //=> true + post.source instanceof Source; //=> true -function setPath(root, path, value, tolerant) { - var keyName; + user.post instanceof Post; //=> true - // get the last part of the path - keyName = path.slice(path.lastIndexOf('.') + 1); + // and both models share the same source + user.source === post.source; //=> true + ``` - // get the first part of the part - path = (path === keyName) ? keyName : path.slice(0, path.length-(keyName.length+1)); + @method injection + @param {String} factoryName + @param {String} property + @param {String} injectionName + */ + injection: function(fullName, property, injectionName) { + if (this.parent) { illegalChildOperation('injection'); } - // unless the path is this, look up the first part to - // get the root - if (path !== 'this') { - root = getPath(root, path); - } + validateFullName(injectionName); + var normalizedInjectionName = this.normalize(injectionName); - if (!keyName || keyName.length === 0) { - throw new Ember.Error('Property set failed: You passed an empty path'); - } + if (fullName.indexOf(':') === -1) { + return this.typeInjection(fullName, property, normalizedInjectionName); + } - if (!root) { - if (tolerant) { return; } - else { throw new Ember.Error('Property set failed: object in path "'+path+'" could not be found or was destroyed.'); } - } + Ember.assert('fullName must be a proper full name', validateFullName(fullName)); + var normalizedName = this.normalize(fullName); - return set(root, keyName, value); -} + if (this.cache.has(normalizedName)) { + throw new Error("Attempted to register an injection for a type that has already been looked up. ('" + normalizedName + "', '" + property + "', '" + injectionName + "')"); + } + addInjection(this.injections, normalizedName, property, normalizedInjectionName); + }, -Ember.set = set; -/** - Error-tolerant form of `Ember.set`. Will not blow up if any part of the - chain is `undefined`, `null`, or destroyed. + /** + Used only via `factoryInjection`. - This is primarily used when syncing bindings, which may try to update after - an object has been destroyed. + Provides a specialized form of injection, specifically enabling + all factory of one type to be injected with a reference to another + object. - @method trySet - @for Ember - @param {Object} obj The object to modify. - @param {String} path The property path to set - @param {Object} value The value to set -*/ -Ember.trySet = function(root, path, value) { - return set(root, path, value, true); -}; + For example, provided each factory of type `model` needed a `store`. + one would do the following: -})(); + ```javascript + var container = new Container(); + container.register('store:main', SomeStore); + container.factoryTypeInjection('model', 'store', 'store:main'); -(function() { -/** -@module ember-metal -*/ - -/* - JavaScript (before ES6) does not have a Map implementation. Objects, - which are often used as dictionaries, may only have Strings as keys. - - 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. - - This implementation follows the current iteration of the ES6 proposal for - maps (http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets), - with two exceptions. First, because we need our implementation to be pleasant - on older browsers, we do not use the `delete` name (using `remove` instead). - Second, as we do not have the luxury of in-VM iteration, we implement a - forEach method for iteration. - - Map is mocked out to look like an Ember object, so you can do - `Ember.Map.create()` for symmetry with other Ember classes. -*/ -var set = Ember.set, - guidFor = Ember.guidFor, - indexOf = Ember.ArrayPolyfills.indexOf; - -var copy = function(obj) { - var output = {}; - - for (var prop in obj) { - if (obj.hasOwnProperty(prop)) { output[prop] = obj[prop]; } - } + var store = container.lookup('store:main'); + var UserFactory = container.lookupFactory('model:user'); - return output; -}; - -var copyMap = function(original, newObject) { - var keys = original.keys.copy(), - values = copy(original.values); - - newObject.keys = keys; - newObject.values = values; - newObject.length = original.length; - - return newObject; -}; - -/** - This class is used internally by Ember and Ember Data. - Please do not use it at this time. We plan to clean it up - and add many tests soon. - - @class OrderedSet - @namespace Ember - @constructor - @private -*/ -var OrderedSet = Ember.OrderedSet = function() { - this.clear(); -}; - -/** - @method create - @static - @return {Ember.OrderedSet} -*/ -OrderedSet.create = function() { - return new OrderedSet(); -}; - - -OrderedSet.prototype = { - /** - @method clear - */ - clear: function() { - this.presenceSet = {}; - this.list = []; - }, - - /** - @method add - @param obj - */ - add: function(obj) { - var guid = guidFor(obj), - presenceSet = this.presenceSet, - list = this.list; - - if (guid in presenceSet) { return; } - - presenceSet[guid] = true; - list.push(obj); - }, - - /** - @method remove - @param obj - */ - remove: function(obj) { - var guid = guidFor(obj), - presenceSet = this.presenceSet, - list = this.list; - - delete presenceSet[guid]; - - var index = indexOf.call(list, obj); - if (index > -1) { - list.splice(index, 1); - } - }, - - /** - @method isEmpty - @return {Boolean} - */ - isEmpty: function() { - return this.list.length === 0; - }, - - /** - @method has - @param obj - @return {Boolean} - */ - has: function(obj) { - var guid = guidFor(obj), - presenceSet = this.presenceSet; - - return guid in presenceSet; - }, - - /** - @method forEach - @param {Function} fn - @param self - */ - forEach: function(fn, self) { - // allow mutation during iteration - var list = this.toArray(); - - for (var i = 0, j = list.length; i < j; i++) { - fn.call(self, list[i]); - } - }, - - /** - @method toArray - @return {Array} - */ - toArray: function() { - return this.list.slice(); - }, - - /** - @method copy - @return {Ember.OrderedSet} - */ - copy: function() { - var set = new OrderedSet(); - - set.presenceSet = copy(this.presenceSet); - set.list = this.toArray(); - - return set; - } -}; - -/** - A Map stores values indexed by keys. Unlike JavaScript's - default Objects, the keys of a Map can be any JavaScript - object. - - 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)` - - 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`. - - @class Map - @namespace Ember - @private - @constructor -*/ -var Map = Ember.Map = function() { - this.keys = Ember.OrderedSet.create(); - this.values = {}; -}; - -/** - @method create - @static -*/ -Map.create = function() { - return new Map(); -}; - -Map.prototype = { - /** - This property will change as the number of objects in the map changes. - - @property length - @type number - @default 0 - */ - length: 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) { - var values = this.values, - 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 - */ - set: function(key, value) { - var keys = this.keys, - values = this.values, - guid = guidFor(key); - - keys.add(key); - values[guid] = value; - set(this, 'length', keys.list.length); - }, - - /** - Removes a value from the map for an associated key. - - @method remove - @param {*} key - @return {Boolean} true if an item was removed, false otherwise - */ - remove: function(key) { - // don't use ES6 "delete" because it will be annoying - // to use in browsers that are not ES6 friendly; - var keys = this.keys, - values = this.values, - guid = guidFor(key); - - if (values.hasOwnProperty(guid)) { - keys.remove(key); - delete values[guid]; - set(this, 'length', keys.list.length); - return true; - } else { - return false; - } - }, + UserFactory.store instanceof SomeStore; //=> true + ``` - /** - Check whether a key is present. + @private + @method factoryTypeInjection + @param {String} type + @param {String} property + @param {String} fullName + */ + factoryTypeInjection: function(type, property, fullName) { + if (this.parent) { illegalChildOperation('factoryTypeInjection'); } - @method has - @param {*} key - @return {Boolean} true if the item was present, false otherwise - */ - has: function(key) { - var values = this.values, - guid = guidFor(key); + addTypeInjection(this.factoryTypeInjections, type, property, this.normalize(fullName)); + }, - return values.hasOwnProperty(guid); - }, + /** + Defines factory injection rules. - /** - Iterate over all the keys and values. Calls the function once - for each key, passing in the key and value, in that order. + Similar to regular injection rules, but are run against factories, via + `Container#lookupFactory`. - The keys are guaranteed to be iterated over in insertion order. + These rules are used to inject objects onto factories when they + are looked up. - @method forEach - @param {Function} callback - @param {*} self if passed, the `this` value inside the - callback. By default, `this` is the map. - */ - forEach: function(callback, self) { - var keys = this.keys, - values = this.values; + Two forms of injections are possible: - keys.forEach(function(key) { - var guid = guidFor(key); - callback.call(self, key, values[guid]); - }); - }, - - /** - @method copy - @return {Ember.Map} - */ - copy: function() { - return copyMap(this, new Map()); - } -}; - -/** - @class MapWithDefault - @namespace Ember - @extends Ember.Map - @private - @constructor - @param [options] - @param {*} [options.defaultValue] -*/ -var MapWithDefault = Ember.MapWithDefault = function(options) { - Map.call(this); - 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(); - } -}; + * Injecting one fullName on another fullName + * Injecting one fullName on a type -MapWithDefault.prototype = Ember.create(Map.prototype); + Example: -/** - Retrieve the value associated with a given key. + ```javascript + var container = new Container(); - @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); + container.register('store:main', Store); + container.register('store:secondary', OtherStore); + container.register('model:user', User); + container.register('model:post', Post); - if (hasValue) { - return Map.prototype.get.call(this, key); - } else { - var defaultValue = this.defaultValue(key); - this.set(key, defaultValue); - return defaultValue; - } -}; - -/** - @method copy - @return {Ember.MapWithDefault} -*/ -MapWithDefault.prototype.copy = function() { - return copyMap(this, new MapWithDefault({ - defaultValue: this.defaultValue - })); -}; + // injecting one fullName on another type + container.factoryInjection('model', 'store', 'store:main'); -})(); + // injecting one fullName on another fullName + container.factoryInjection('model:post', 'secondaryStore', 'store:secondary'); + var UserFactory = container.lookupFactory('model:user'); + var PostFactory = container.lookupFactory('model:post'); + var store = container.lookup('store:main'); + UserFactory.store instanceof Store; //=> true + UserFactory.secondaryStore instanceof OtherStore; //=> false -(function() { -function consoleMethod(name) { - var consoleObj, logToConsole; - if (Ember.imports.console) { - consoleObj = Ember.imports.console; - } else if (typeof console !== 'undefined') { - consoleObj = console; - } + PostFactory.store instanceof Store; //=> true + PostFactory.secondaryStore instanceof OtherStore; //=> true - var method = typeof consoleObj === 'object' ? consoleObj[name] : null; + // and both models share the same source instance + UserFactory.store === PostFactory.store; //=> true + ``` - if (method) { - // Older IE doesn't support apply, but Chrome needs it - 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 assertPolyfill(test, message) { - if (!test) { - try { - // attempt to preserve the stack - throw new Ember.Error("assertion failed: " + message); - } catch(error) { - setTimeout(function() { - throw error; - }, 0); - } - } -} + @method factoryInjection + @param {String} factoryName + @param {String} property + @param {String} injectionName + */ + factoryInjection: function(fullName, property, injectionName) { + if (this.parent) { illegalChildOperation('injection'); } -/** - Inside Ember-Metal, simply uses the methods from `imports.console`. - Override this to provide more robust logging functionality. + var normalizedName = this.normalize(fullName); + var normalizedInjectionName = this.normalize(injectionName); - @class Logger - @namespace Ember -*/ -Ember.Logger = { - /** - Logs the arguments to the console. - You can pass as many arguments as you want and they will be joined together with a space. + validateFullName(injectionName); - ```javascript - var foo = 1; - Ember.Logger.log('log value of foo:', foo); - // "log value of foo: 1" will be printed to the console - ``` + if (fullName.indexOf(':') === -1) { + return this.factoryTypeInjection(normalizedName, property, normalizedInjectionName); + } - @method log - @for Ember.Logger - @param {*} arguments - */ - log: consoleMethod('log') || Ember.K, + Ember.assert('fullName must be a proper full name', validateFullName(fullName)); - /** - 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. + if (this.factoryCache.has(normalizedName)) { + throw new Error('Attempted to register a factoryInjection for a type that has already ' + + 'been looked up. (\'' + normalizedName + '\', \'' + property + '\', \'' + injectionName + '\')'); + } - ```javascript - Ember.Logger.warn('Something happened!'); - // "Something happened!" will be printed to the console with a warning icon. - ``` + addInjection(this.factoryInjections, normalizedName, property, normalizedInjectionName); + }, - @method warn - @for Ember.Logger - @param {*} arguments - */ - warn: consoleMethod('warn') || Ember.K, + /** + A depth first traversal, destroying the container, its descendant containers and all + their managed objects. - /** - 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. + @method destroy + */ + destroy: function() { + for (var i = 0, length = this.children.length; i < length; i++) { + this.children[i].destroy(); + } - ```javascript - Ember.Logger.error('Danger! Danger!'); - // "Danger! Danger!" will be printed to the console in red text. - ``` + this.children = []; - @method error - @for Ember.Logger - @param {*} arguments - */ - error: consoleMethod('error') || Ember.K, + eachDestroyable(this, function(item) { + item.destroy(); + }); - /** - Logs the arguments to the console. - You can pass as many arguments as you want and they will be joined together with a space. + this.parent = undefined; + this.isDestroyed = true; + }, - ```javascript - var foo = 1; - Ember.Logger.info('log value of foo:', foo); - // "log value of foo: 1" will be printed to the console - ``` + /** + @method reset + */ + reset: function() { + for (var i = 0, length = this.children.length; i < length; i++) { + resetCache(this.children[i]); + } - @method info - @for Ember.Logger - @param {*} arguments - */ - info: consoleMethod('info') || Ember.K, + resetCache(this); + } + }; - /** - 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. + function resolve(container, normalizedName) { + var cached = container.resolveCache.get(normalizedName); + if (cached) { return cached; } - ```javascript - var foo = 1; - Ember.Logger.debug('log value of foo:', foo); - // "log value of foo: 1" will be printed to the console - ``` + var resolved = container.resolver(normalizedName) || container.registry.get(normalizedName); + container.resolveCache.set(normalizedName, resolved); - @method debug - @for Ember.Logger - @param {*} arguments - */ - debug: consoleMethod('debug') || consoleMethod('info') || Ember.K, + return resolved; + } - /** - If the value passed into `Ember.Logger.assert` is not truthy it will throw an error with a stack trace. + function has(container, fullName){ + if (container.cache.has(fullName)) { + return true; + } - ```javascript - Ember.Logger.assert(true); // undefined - Ember.Logger.assert(true === false); // Throws an Assertion failed error. - ``` + return !!container.resolve(fullName); + } - @method assert - @for Ember.Logger - @param {Boolean} bool Value to test - */ - assert: consoleMethod('assert') || assertPolyfill -}; + function lookup(container, fullName, options) { + options = options || {}; + if (container.cache.has(fullName) && options.singleton !== false) { + return container.cache.get(fullName); + } -})(); + var value = instantiate(container, fullName); + if (value === undefined) { return; } + if (isSingleton(container, fullName) && options.singleton !== false) { + container.cache.set(fullName, value); + } -(function() { -/** -@module ember-metal -*/ - -var META_KEY = Ember.META_KEY, - metaFor = Ember.meta, - objectDefineProperty = Ember.platform.defineProperty; - -var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER; - -// .......................................................... -// 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 -*/ -Ember.Descriptor = function() {}; - -// .......................................................... -// DEFINING PROPERTIES API -// - -var MANDATORY_SETTER_FUNCTION = Ember.MANDATORY_SETTER_FUNCTION = function(value) { - Ember.assert("You must use Ember.set() to access this property (of " + this + ")", false); -}; - -var DEFAULT_GETTER_FUNCTION = Ember.DEFAULT_GETTER_FUNCTION = function(name) { - return function() { - var meta = this[META_KEY]; - return meta && meta.values[name]; - }; -}; - -/** - 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 - - ```javascript - // ES5 compatible mode - Ember.defineProperty(contact, 'firstName', { - writable: true, - configurable: false, - enumerable: true, - value: 'Charles' - }); + return value; + } - // define a simple property - Ember.defineProperty(contact, 'lastName', undefined, 'Jolley'); - - // define a computed property - Ember.defineProperty(contact, 'fullName', Ember.computed(function() { - return this.firstName+' '+this.lastName; - }).property('firstName', 'lastName')); - ``` - - @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. -*/ -Ember.defineProperty = function(obj, keyName, desc, data, meta) { - var descs, existingDesc, watching, value; - - if (!meta) meta = metaFor(obj); - descs = meta.descs; - existingDesc = meta.descs[keyName]; - watching = meta.watching[keyName] > 0; - - if (existingDesc instanceof Ember.Descriptor) { - existingDesc.teardown(obj, keyName); - } + function illegalChildOperation(operation) { + throw new Error(operation + ' is not currently supported on child containers'); + } - if (desc instanceof Ember.Descriptor) { - value = desc; + function isSingleton(container, fullName) { + var singleton = option(container, fullName, 'singleton'); - descs[keyName] = desc; - if (MANDATORY_SETTER && watching) { - objectDefineProperty(obj, keyName, { - configurable: true, - enumerable: true, - writable: true, - value: undefined // make enumerable - }); - } else { - obj[keyName] = undefined; // make enumerable + return singleton !== false; } - if (Ember.FEATURES.isEnabled('composable-computed-properties')) { - if (desc.func && desc._dependentCPs) { - addImplicitCPs(obj, desc._dependentCPs, meta); - } - } - } else { - descs[keyName] = undefined; // shadow descriptor in proto - if (desc == null) { - value = data; + function buildInjections(container, injections) { + var hash = {}; - if (MANDATORY_SETTER && watching) { - meta.values[keyName] = data; - objectDefineProperty(obj, keyName, { - configurable: true, - enumerable: true, - set: MANDATORY_SETTER_FUNCTION, - get: DEFAULT_GETTER_FUNCTION(keyName) - }); - } else { - obj[keyName] = data; - } - } else { - value = desc; + if (!injections) { return hash; } - // compatibility with ES5 - objectDefineProperty(obj, keyName, desc); - } - } + var injection, injectable; - // if key is being watched, override chains that - // were initialized with the prototype - if (watching) { Ember.overrideChains(obj, keyName, meta); } + for (var i = 0, length = injections.length; i < length; i++) { + injection = injections[i]; + injectable = lookup(container, injection.fullName); - // The `value` passed to the `didDefineProperty` hook is - // either the descriptor or data, whichever was passed. - if (obj.didDefineProperty) { obj.didDefineProperty(obj, keyName, value); } + if (injectable !== undefined) { + hash[injection.property] = injectable; + } else { + throw new Error('Attempting to inject an unknown injection: `' + injection.fullName + '`'); + } + } - return this; -}; + return hash; + } -if (Ember.FEATURES.isEnabled('composable-computed-properties')) { - var addImplicitCPs = function defineImplicitCPs(obj, implicitCPs, meta) { - var cp, key, length = implicitCPs.length; + function option(container, fullName, optionName) { + var options = container._options.get(fullName); - for (var i=0; i 1) { - watching[keyName]--; - } -}; -})(); + function addInjection(rules, factoryName, property, injectionName) { + var injections = rules[factoryName] = rules[factoryName] || []; + injections.push({ property: property, fullName: injectionName }); + } + __exports__["default"] = Container; + }); +define("container/inheriting_dict", + ["exports"], + function(__exports__) { + "use strict"; + // A safe and simple inheriting object. + function InheritingDict(parent) { + this.parent = parent; + this.dict = {}; + } + InheritingDict.prototype = { -(function() { -var metaFor = Ember.meta, // utils.js - get = Ember.get, // property_get.js - normalizeTuple = Ember.normalizeTuple, // property_get.js - forEach = Ember.ArrayPolyfills.forEach, // array.js - warn = Ember.warn, - watchKey = Ember.watchKey, - unwatchKey = Ember.unwatchKey, - FIRST_KEY = /^([^\.\*]+)/, - META_KEY = Ember.META_KEY; + /** + @property parent + @type InheritingDict + @default null + */ -function firstKey(path) { - return path.match(FIRST_KEY)[0]; -} + parent: null, -var pendingQueue = []; + /** + Object used to store the current nodes data. -// 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. -Ember.flushPendingChains = function() { - if (pendingQueue.length === 0) { return; } // nothing to do + @property dict + @type Object + @default Object + */ + dict: null, - var queue = pendingQueue; - pendingQueue = []; + /** + Retrieve the value given a key, if the value is present at the current + level use it, otherwise walk up the parent hierarchy and try again. If + no matching key is found, return undefined. - forEach.call(queue, function(q) { q[0].add(q[1]); }); + @method get + @param {String} key + @return {any} + */ + get: function(key) { + var dict = this.dict; - 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); -}; + if (dict.hasOwnProperty(key)) { + return dict[key]; + } + if (this.parent) { + return this.parent.get(key); + } + }, -function addChainWatcher(obj, keyName, node) { - if (!obj || ('object' !== typeof obj)) { return; } // nothing to do + /** + Set the given value for the given key, at the current level. - var m = metaFor(obj), nodes = m.chainWatchers; + @method set + @param {String} key + @param {Any} value + */ + set: function(key, value) { + this.dict[key] = value; + }, - if (!m.hasOwnProperty('chainWatchers')) { - nodes = m.chainWatchers = {}; - } + /** + Delete the given key - if (!nodes[keyName]) { nodes[keyName] = []; } - nodes[keyName].push(node); - watchKey(obj, keyName, m); -} + @method remove + @param {String} key + */ + remove: function(key) { + delete this.dict[key]; + }, -var removeChainWatcher = Ember.removeChainWatcher = function(obj, keyName, node) { - if (!obj || 'object' !== typeof obj) { return; } // nothing to do + /** + Check for the existence of given a key, if the key is present at the current + level return true, otherwise walk up the parent hierarchy and try again. If + no matching key is found, return false. - var m = obj[META_KEY]; - if (m && !m.hasOwnProperty('chainWatchers')) { return; } // nothing to do + @method has + @param {String} key + @return {Boolean} + */ + has: function(key) { + var dict = this.dict; - var nodes = m && m.chainWatchers; + if (dict.hasOwnProperty(key)) { + return true; + } - if (nodes && nodes[keyName]) { - nodes = nodes[keyName]; - for (var i = 0, l = nodes.length; i < l; i++) { - if (nodes[i] === node) { nodes.splice(i, 1); } - } - } - unwatchKey(obj, keyName, m); -}; - -// 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. -var ChainNode = Ember._ChainNode = function(parent, key, value) { - this._parent = parent; - this._key = key; - - // _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); } - } + if (this.parent) { + return this.parent.has(key); + } - // 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(); - } -}; + return false; + }, -var ChainNodePrototype = ChainNode.prototype; + /** + Iterate and invoke a callback for each local key-value pair. -function lazyGet(obj, key) { - if (!obj) return undefined; + @method eachLocal + @param {Function} callback + @param {Object} binding + */ + eachLocal: function(callback, binding) { + var dict = this.dict; - var meta = obj[META_KEY]; - // check if object meant only to be a prototype - if (meta && meta.proto === obj) return undefined; + for (var prop in dict) { + if (dict.hasOwnProperty(prop)) { + callback.call(binding, prop, dict[prop]); + } + } + } + }; - if (key === "@each") return get(obj, key); + __exports__["default"] = InheritingDict; + }); +define("ember-application", + ["ember-metal/core","ember-runtime/system/lazy_load","ember-application/system/dag","ember-application/system/resolver","ember-application/system/application","ember-application/ext/controller"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__) { + "use strict"; + var Ember = __dependency1__["default"]; + var runLoadHooks = __dependency2__.runLoadHooks; - // 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; - } - } + /** + Ember Application - return get(obj, key); -} + @module ember + @submodule ember-application + @requires ember-views, ember-routing + */ -ChainNodePrototype.value = function() { - if (this._value === undefined && this._watching) { - var obj = this._parent.value(); - this._value = lazyGet(obj, this._key); - } - return this._value; -}; - -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 - } -}; - -// copies a top level object only -ChainNodePrototype.copy = function(obj) { - var ret = new ChainNode(null, null, obj), - paths = this._paths, path; - for (path in paths) { - if (paths[path] <= 0) { continue; } // this check will also catch non-number vals. - ret.add(path); - } - return ret; -}; - -// 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; - - paths = this._paths; - paths[path] = (paths[path] || 0) + 1; - - obj = this.value(); - tuple = normalizeTuple(obj, path); - - // the path was a local path - if (tuple[0] && tuple[0] === obj) { - path = tuple[1]; - key = firstKey(path); - path = path.slice(key.length+1); - - // global path, but object does not exist yet. - // put into a queue and try to connect later. - } else if (!tuple[0]) { - pendingQueue.push([this, path]); - tuple.length = 0; - return; - - // global path, and object already exists - } else { - src = tuple[0]; - key = path.slice(0, 0-(tuple[1].length+1)); - path = tuple[1]; - } + var DAG = __dependency3__["default"]; + var Resolver = __dependency4__.Resolver; + var DefaultResolver = __dependency4__["default"]; + var Application = __dependency5__["default"]; + // side effect of extending ControllerMixin - tuple.length = 0; - this.chain(key, path, src); -}; + Ember.Application = Application; + Ember.DAG = DAG; + Ember.Resolver = Resolver; + Ember.DefaultResolver = DefaultResolver; -// 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; + runLoadHooks('Ember.Application', Application); + }); +define("ember-application/ext/controller", + ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/error","ember-metal/utils","ember-metal/computed","ember-runtime/mixins/controller","ember-routing/system/controller_for","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-application + */ - paths = this._paths; - if (paths[path] > 0) { paths[path]--; } + var Ember = __dependency1__["default"]; + // Ember.assert + var get = __dependency2__.get; + var set = __dependency3__.set; + var EmberError = __dependency4__["default"]; + var inspect = __dependency5__.inspect; + var computed = __dependency6__.computed; + var ControllerMixin = __dependency7__["default"]; + var meta = __dependency5__.meta; + var controllerFor = __dependency8__["default"]; - 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]; - } + function verifyNeedsDependencies(controller, container, needs) { + var dependency, i, l, missing = []; - tuple.length = 0; - this.unchain(key, path); -}; + for (i=0, l=needs.length; i 1 ? 'they' : 'it') + " could not be found"); + } + } - // chain rest of path if there is one - if (path && path.length>0) { - key = firstKey(path); - path = path.slice(key.length+1); - node.chain(key, path); // NOTE: no src means it will observe changes... - } -}; + var defaultControllersComputedProperty = computed(function() { + var controller = this; -ChainNodePrototype.unchain = function(key, path) { - var chains = this._chains, node = chains[key]; + return { + needs: get(controller, 'needs'), + container: get(controller, 'container'), + unknownProperty: function(controllerName) { + var needs = this.needs, + dependency, i, l; + for (i=0, l=needs.length; i1) { - key = firstKey(path); - path = path.slice(key.length+1); - node.unchain(key, path); - } + var errorMessage = inspect(controller) + '#needs does not include `' + controllerName + '`. To access the ' + controllerName + ' controller from ' + inspect(controller) + ', ' + inspect(controller) + ' should have a `needs` property that is an array of the controllers it has access to.'; + throw new ReferenceError(errorMessage); + }, + setUnknownProperty: function (key, value) { + throw new Error("You cannot overwrite the value of `controllers." + key + "` of " + inspect(controller)); + } + }; + }); - // delete node if needed. - node.count--; - if (node.count<=0) { - delete chains[node._key]; - node.destroy(); - } + /** + @class ControllerMixin + @namespace Ember + */ + ControllerMixin.reopen({ + concatenatedProperties: ['needs'], -}; + /** + An array of other controller objects available inside + instances of this controller via the `controllers` + property: -ChainNodePrototype.willChange = function(events) { - var chains = this._chains; - if (chains) { - for(var key in chains) { - if (!chains.hasOwnProperty(key)) { continue; } - chains[key].willChange(events); - } - } + For example, when you define a controller: - if (this._parent) { this._parent.chainWillChange(this, this._key, 1, events); } -}; + ```javascript + App.CommentsController = Ember.ArrayController.extend({ + needs: ['post'] + }); + ``` -ChainNodePrototype.chainWillChange = function(chain, path, depth, events) { - if (this._key) { path = this._key + '.' + path; } + The application's single instance of these other + controllers are accessible by name through the + `controllers` property: - 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); - } - } -}; + ```javascript + this.get('controllers.post'); // instance of App.PostController + ``` -ChainNodePrototype.chainDidChange = function(chain, path, depth, events) { - if (this._key) { path = this._key + '.' + path; } - if (this._parent) { - this._parent.chainDidChange(this, path, depth+1, events); - } else { - if (depth > 1) { - events.push(this.value(), path); - } - path = 'this.' + path; - if (this._paths[path] > 0) { - events.push(this.value(), path); - } - } -}; - -ChainNodePrototype.didChange = function(events) { - // invalidate my own value first. - if (this._watching) { - var obj = this._parent.value(); - if (obj !== this._object) { - removeChainWatcher(this._object, this._key, this); - this._object = obj; - addChainWatcher(obj, this._key, this); - } - this._value = undefined; - - // Special-case: the EachProxy relies on immediate evaluation to - // establish its observers. - if (this._parent && this._parent._key === '@each') - this.value(); - } + Given that you have a nested controller (nested resource): - // then notify chains... - var chains = this._chains; - if (chains) { - for(var key in chains) { - if (!chains.hasOwnProperty(key)) { continue; } - chains[key].didChange(events); - } - } + ```javascript + App.CommentsNewController = Ember.ObjectController.extend({ + }); + ``` - // if no events are passed in then we only care about the above wiring update - if (events === null) { return; } + When you define a controller that requires access to a nested one: - // and finally tell parent about my path changing... - if (this._parent) { this._parent.chainDidChange(this, this._key, 1, events); } -}; + ```javascript + App.IndexController = Ember.ObjectController.extend({ + needs: ['commentsNew'] + }); + ``` -Ember.finishChains = function(obj) { - // We only create meta if we really have to - var m = obj[META_KEY], chains = m && m.chains; - if (chains) { - if (chains.value() !== obj) { - metaFor(obj).chains = chains = chains.copy(obj); - } else { - chains.didChange(null); - } - } -}; + You will be able to get access to it: -})(); + ```javascript + this.get('controllers.commentsNew'); // instance of App.CommentsNewController + ``` + This is only available for singleton controllers. + @property {Array} needs + @default [] + */ + needs: [], -(function() { -/** - @module ember-metal - */ - -var forEach = Ember.EnumerableUtils.forEach, -BRACE_EXPANSION = /^((?:[^\.]*\.)*)\{(.*)\}$/; - -/** - Expands `pattern`, invoking `callback` for each expansion. - - The only pattern supported is brace-expansion, anything else will be passed - once to `callback` directly. Brace expansion can only appear at the end of a - pattern, for example as the last item in a chain. - - Example - ```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' - ``` - - @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. - */ -Ember.expandProperties = function (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); - } -}; + init: function() { + var needs = get(this, 'needs'); + var length = get(needs, 'length'); -})(); + if (length > 0) { + Ember.assert(' `' + inspect(this) + ' specifies `needs`, but does ' + + "not have a container. Please ensure this controller was " + + "instantiated with a container.", + this.container || meta(this, false).descs.controllers !== defaultControllersComputedProperty); + if (this.container) { + verifyNeedsDependencies(this, this.container, needs); + } + // if needs then initialize controllers proxy + get(this, 'controllers'); + } -(function() { -var metaFor = Ember.meta, // utils.js - typeOf = Ember.typeOf, // utils.js - ChainNode = Ember._ChainNode; // chains.js - -// get the chains for the current object. If the current object has -// chains inherited from the proto they will be cloned and reconfigured for -// the current object. -function chainsFor(obj, meta) { - var m = meta || metaFor(obj), ret = m.chains; - if (!ret) { - ret = m.chains = new ChainNode(null, null, obj); - } else if (ret.value() !== obj) { - ret = m.chains = ret.copy(obj); - } - return ret; -} + this._super.apply(this, arguments); + }, -Ember.watchPath = function(obj, keyPath, meta) { - // can't watch length on Array - it is special... - if (keyPath === 'length' && typeOf(obj) === 'array') { return; } + /** + @method controllerFor + @see {Ember.Route#controllerFor} + @deprecated Use `needs` instead + */ + controllerFor: function(controllerName) { + Ember.deprecate("Controller#controllerFor is deprecated, please use Controller#needs instead"); + return controllerFor(get(this, 'container'), controllerName); + }, - var m = meta || metaFor(obj), watching = m.watching; + /** + Stores the instances of other controllers available from within + this controller. Any controller listed by name in the `needs` + property will be accessible by name through this property. - if (!watching[keyPath]) { // activate watching first time - watching[keyPath] = 1; - chainsFor(obj, m).add(keyPath); - } else { - watching[keyPath] = (watching[keyPath] || 0) + 1; - } -}; + ```javascript + App.CommentsController = Ember.ArrayController.extend({ + needs: ['post'], + postTitle: function(){ + var currentPost = this.get('controllers.post'); // instance of App.PostController + return currentPost.get('title'); + }.property('controllers.post.title') + }); + ``` -Ember.unwatchPath = function(obj, keyPath, meta) { - var m = meta || metaFor(obj), watching = m.watching; + @see {Ember.ControllerMixin#needs} + @property {Object} controllers + @default null + */ + controllers: defaultControllersComputedProperty + }); - if (watching[keyPath] === 1) { - watching[keyPath] = 0; - chainsFor(obj, m).remove(keyPath); - } else if (watching[keyPath] > 1) { - watching[keyPath]--; - } -}; -})(); + __exports__["default"] = ControllerMixin; + }); +define("ember-application/system/application", + ["ember-metal","ember-metal/property_get","ember-metal/property_set","ember-runtime/system/lazy_load","ember-application/system/dag","ember-runtime/system/namespace","ember-runtime/mixins/deferred","ember-application/system/resolver","ember-metal/platform","ember-metal/run_loop","ember-metal/utils","container/container","ember-runtime/controllers/controller","ember-metal/enumerable_utils","ember-runtime/controllers/object_controller","ember-runtime/controllers/array_controller","ember-views/system/event_dispatcher","ember-views/system/jquery","ember-routing/system/route","ember-routing/system/router","ember-routing/location/hash_location","ember-routing/location/history_location","ember-routing/location/auto_location","ember-routing/location/none_location","ember-routing/system/cache","ember-metal/core","ember-handlebars-compiler","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __dependency19__, __dependency20__, __dependency21__, __dependency22__, __dependency23__, __dependency24__, __dependency25__, __dependency26__, __dependency27__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-application + */ + var Ember = __dependency1__["default"]; + // Ember.FEATURES, Ember.deprecate, Ember.assert, Ember.libraries, LOG_VERSION, Namespace, BOOTED + var get = __dependency2__.get; + var set = __dependency3__.set; + var runLoadHooks = __dependency4__.runLoadHooks; + var DAG = __dependency5__["default"]; + var Namespace = __dependency6__["default"]; + var DeferredMixin = __dependency7__["default"]; + var DefaultResolver = __dependency8__["default"]; + var create = __dependency9__.create; + var run = __dependency10__["default"]; + var canInvoke = __dependency11__.canInvoke; + var Container = __dependency12__["default"]; + var Controller = __dependency13__["default"]; + var EnumerableUtils = __dependency14__["default"]; + var ObjectController = __dependency15__["default"]; + var ArrayController = __dependency16__["default"]; + var EventDispatcher = __dependency17__["default"]; + //import ContainerDebugAdapter from "ember-extension-support/container_debug_adapter"; + var jQuery = __dependency18__["default"]; + var Route = __dependency19__["default"]; + var Router = __dependency20__["default"]; + var HashLocation = __dependency21__["default"]; + var HistoryLocation = __dependency22__["default"]; + var AutoLocation = __dependency23__["default"]; + var NoneLocation = __dependency24__["default"]; + var BucketCache = __dependency25__["default"]; + + var K = __dependency26__.K; + var EmberHandlebars = __dependency27__["default"]; + + var ContainerDebugAdapter; + /** + An instance of `Ember.Application` is the starting point for every Ember + application. It helps to instantiate, initialize and coordinate the many + objects that make up your app. -(function() { -/** -@module ember-metal -*/ - -var metaFor = Ember.meta, // utils.js - GUID_KEY = Ember.GUID_KEY, // utils.js - META_KEY = Ember.META_KEY, // utils.js - removeChainWatcher = Ember.removeChainWatcher, - watchKey = Ember.watchKey, // watch_key.js - unwatchKey = Ember.unwatchKey, - watchPath = Ember.watchPath, // watch_path.js - unwatchPath = Ember.unwatchPath, - typeOf = Ember.typeOf, // utils.js - generateGuid = Ember.generateGuid, - IS_PATH = /[\.\*]/; - -// returns true if the passed path is just a keyName -function isKeyName(path) { - return path==='*' || !IS_PATH.test(path); -} - -/** - Starts watching a property on an object. Whenever the property changes, - invokes `Ember.propertyWillChange` and `Ember.propertyDidChange`. This is the - primitive used by observers and dependent keys; usually you will never call - this method directly but instead use higher level methods like - `Ember.addObserver()` - - @private - @method watch - @for Ember - @param obj - @param {String} keyName -*/ -Ember.watch = function(obj, _keyPath, m) { - // can't watch length on Array - it is special... - if (_keyPath === 'length' && typeOf(obj) === 'array') { return; } - - if (isKeyName(_keyPath)) { - watchKey(obj, _keyPath, m); - } else { - watchPath(obj, _keyPath, m); - } -}; + Each Ember app has one and only one `Ember.Application` object. In fact, the + very first thing you should do in your application is create the instance: -Ember.isWatching = function isWatching(obj, key) { - var meta = obj[META_KEY]; - return (meta && meta.watching[key]) > 0; -}; + ```javascript + window.App = Ember.Application.create(); + ``` -Ember.watch.flushPending = Ember.flushPendingChains; + Typically, the application object is the only global variable. All other + classes in your app should be properties on the `Ember.Application` instance, + which highlights its first role: a global namespace. -Ember.unwatch = function(obj, _keyPath, m) { - // can't watch length on Array - it is special... - if (_keyPath === 'length' && typeOf(obj) === 'array') { return; } + For example, if you define a view class, it might look like this: - if (isKeyName(_keyPath)) { - unwatchKey(obj, _keyPath, m); - } else { - unwatchPath(obj, _keyPath, m); - } -}; - -/** - Call on an object when you first beget it from another object. This will - setup any chained watchers on the object instance as needed. This method is - safe to call multiple times. - - @private - @method rewatch - @for Ember - @param obj -*/ -Ember.rewatch = function(obj) { - var m = obj[META_KEY], chains = m && m.chains; - - // make sure the object has its own guid. - if (GUID_KEY in obj && !obj.hasOwnProperty(GUID_KEY)) { - generateGuid(obj); - } + ```javascript + App.MyView = Ember.View.extend(); + ``` - // make sure any chained watchers update. - if (chains && chains.value() !== obj) { - m.chains = chains.copy(obj); - } -}; - -var NODE_STACK = []; - -/** - Tears down the meta on an object so that it can be garbage collected. - Multiple calls will have no effect. - - @method destroy - @for Ember - @param {Object} obj the object to destroy - @return {void} -*/ -Ember.destroy = function (obj) { - var meta = obj[META_KEY], node, nodes, key, nodeObject; - if (meta) { - obj[META_KEY] = null; - // remove chainWatchers to remove circular references that would prevent GC - node = meta.chains; - if (node) { - NODE_STACK.push(node); - // process tree - while (NODE_STACK.length > 0) { - node = NODE_STACK.pop(); - // push children - nodes = node._chains; - if (nodes) { - for (key in nodes) { - if (nodes.hasOwnProperty(key)) { - NODE_STACK.push(nodes[key]); - } - } - } - // remove chainWatcher in node object - if (node._watching) { - nodeObject = node._object; - if (nodeObject) { - removeChainWatcher(nodeObject, node._key, node); - } - } - } - } - } -}; + By default, calling `Ember.Application.create()` will automatically initialize + your application by calling the `Ember.Application.initialize()` method. If + you need to delay initialization, you can call your app's `deferReadiness()` + method. When you are ready for your app to be initialized, call its + `advanceReadiness()` method. -})(); + You can define a `ready` method on the `Ember.Application` instance, which + will be run by Ember when the application is initialized. + Because `Ember.Application` inherits from `Ember.Namespace`, any classes + you create will have useful string representations when calling `toString()`. + See the `Ember.Namespace` documentation for more information. + While you can think of your `Ember.Application` as a container that holds the + other classes in your application, there are several other responsibilities + going on under-the-hood that you may want to understand. -(function() { -/** -@module ember-metal -*/ - -Ember.warn("The CP_DEFAULT_CACHEABLE flag has been removed and computed properties are always cached by default. Use `volatile` if you don't want caching.", Ember.ENV.CP_DEFAULT_CACHEABLE !== false); - - -var get = Ember.get, - set = Ember.set, - metaFor = Ember.meta, - a_slice = [].slice, - o_create = Ember.create, - META_KEY = Ember.META_KEY, - watch = Ember.watch, - unwatch = Ember.unwatch; - -var expandProperties = Ember.expandProperties; - - -// .......................................................... -// DEPENDENT KEYS -// - -// data structure: -// meta.deps = { -// 'depKey': { -// 'keyName': count, -// } -// } - -/* - 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; -} - -function metaForDeps(meta) { - return keysForDep(meta, 'deps'); -} - -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 removeDependentKeys(desc, obj, keyName, meta) { - // the descriptor has a list of dependent keys, so - // add all of its dependent keys. - var depKeys = desc._dependentKeys, depsMeta, idx, len, depKey, keys; - if (!depKeys) return; - - 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 - unwatch(obj, depKey, meta); - } -} - -// .......................................................... -// COMPUTED PROPERTY -// - -/** - A computed property transforms an objects function into a property. - - By default the function backing the computed property will only be called - once and the result will be cached. You can specify various properties - that your computed property is dependent on. This will force the cached - result to be recomputed if the dependencies are modified. - - In the following example we declare a computed property (by calling - `.property()` on the fullName function) and setup the properties - dependencies (depending on firstName and lastName). The fullName function - will be called once (regardless of how many times it is accessed) as long - as it's dependencies have not been changed. Once firstName or lastName are updated - any future calls (or anything bound) to fullName will incorporate the new - values. - - ```javascript - Person = Ember.Object.extend({ - // these will be supplied by `create` - firstName: null, - lastName: null, - - fullName: function() { - var firstName = this.get('firstName'); - var lastName = this.get('lastName'); - - return firstName + ' ' + lastName; - }.property('firstName', 'lastName') - }); + ### Event Delegation - var tom = Person.create({ - firstName: "Tom", - lastName: "Dale" - }); + Ember uses a technique called _event delegation_. This allows the framework + to set up a global, shared event listener instead of requiring each view to + do it manually. For example, instead of each view registering its own + `mousedown` listener on its associated element, Ember sets up a `mousedown` + listener on the `body`. - tom.get('fullName') // "Tom Dale" - ``` + If a `mousedown` event occurs, Ember will look at the target of the event and + start walking up the DOM node tree, finding corresponding views and invoking + their `mouseDown` method as it goes. - You can also define what Ember should do when setting a computed property. - If you try to set a computed property, it will be invoked with the key and - value you want to set it to. You can also accept the previous value as the - third parameter. + `Ember.Application` has a number of default events that it listens for, as + well as a mapping from lowercase events to camel-cased view method names. For + example, the `keypress` event causes the `keyPress` method on the view to be + called, the `dblclick` event causes `doubleClick` to be called, and so on. - ```javascript + If there is a bubbling browser event that Ember does not listen for by + default, you can specify custom events and their corresponding view method + names by setting the application's `customEvents` property: - Person = Ember.Object.extend({ - // these will be supplied by `create` - firstName: null, - lastName: null, + ```javascript + App = Ember.Application.create({ + customEvents: { + // add support for the paste event + paste: "paste" + } + }); + ``` - fullName: function(key, value, oldValue) { - // getter - if (arguments.length === 1) { - var firstName = this.get('firstName'); - var lastName = this.get('lastName'); + By default, the application sets up these event listeners on the document + body. However, in cases where you are embedding an Ember application inside + an existing page, you may want it to set up the listeners on an element + inside the body. - return firstName + ' ' + lastName; + For example, if only events inside a DOM element with the ID of `ember-app` + should be delegated, set your application's `rootElement` property: - // setter - } else { - var name = value.split(" "); + ```javascript + window.App = Ember.Application.create({ + rootElement: '#ember-app' + }); + ``` - this.set('firstName', name[0]); - this.set('lastName', name[1]); + The `rootElement` can be either a DOM element or a jQuery-compatible selector + string. Note that *views appended to the DOM outside the root element will + not receive events.* If you specify a custom root element, make sure you only + append views inside it! - return value; - } - }.property('firstName', 'lastName') - }); + To learn more about the advantages of event delegation and the Ember view + layer, and a list of the event listeners that are setup by default, visit the + [Ember View Layer guide](http://emberjs.com/guides/understanding-ember/the-view-layer/#toc_event-delegation). - var person = Person.create(); - person.set('fullName', "Peter Wagenet"); - person.get('firstName') // Peter - person.get('lastName') // Wagenet - ``` - - @class ComputedProperty - @namespace Ember - @extends Ember.Descriptor - @constructor -*/ -function ComputedProperty(func, opts) { - this.func = func; - if (Ember.FEATURES.isEnabled('composable-computed-properties')) { - setDependentKeys(this, opts && opts.dependentKeys); - } else { - this._dependentKeys = opts && opts.dependentKeys; - } + ### Initializers - this._cacheable = (opts && opts.cacheable !== undefined) ? opts.cacheable : true; - this._readOnly = opts && (opts.readOnly !== undefined || !!opts.readOnly); -} - -Ember.ComputedProperty = ComputedProperty; - -ComputedProperty.prototype = new Ember.Descriptor(); - -var ComputedPropertyPrototype = ComputedProperty.prototype; -ComputedPropertyPrototype._dependentKeys = undefined; -ComputedPropertyPrototype._suspended = undefined; -ComputedPropertyPrototype._meta = undefined; - -if (Ember.FEATURES.isEnabled('composable-computed-properties')) { - ComputedPropertyPrototype._dependentCPs = undefined; - ComputedPropertyPrototype.implicitCPKey = undefined; - - ComputedPropertyPrototype.toString = function() { - if (this.implicitCPKey) { - return this.implicitCPKey; - } - return Ember.Descriptor.prototype.toString.apply(this, arguments); - }; -} - -/** - 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 -*/ -ComputedPropertyPrototype.cacheable = function(aFlag) { - this._cacheable = aFlag !== false; - return this; -}; - -/** - 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. - - ```javascript - MyApp.outsideService = Ember.Object.extend({ - value: function() { - return OutsideService.getValue(); - }.property().volatile() - }).create(); - ``` - - @method volatile - @return {Ember.ComputedProperty} this - @chainable -*/ -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. - - ```javascript - MyApp.Person = Ember.Object.extend({ - guid: function() { - return 'guid-guid-guid'; - }.property().readOnly() - }); + Libraries on top of Ember can add initializers, like so: - MyApp.person = MyApp.Person.create(); + ```javascript + Ember.Application.initializer({ + name: 'api-adapter', - MyApp.person.set('guid', 'new-guid'); // will throw an exception - ``` + initialize: function(container, application) { + application.register('api-adapter:main', ApiAdapter); + } + }); + ``` - @method readOnly - @return {Ember.ComputedProperty} this - @chainable -*/ -ComputedPropertyPrototype.readOnly = function(readOnly) { - this._readOnly = readOnly === undefined || !!readOnly; - return this; -}; + Initializers provide an opportunity to access the container, which + organizes the different components of an Ember application. Additionally + they provide a chance to access the instantiated application. Beyond + being used for libraries, initializers are also a great way to organize + dependency injection or setup in your own application. -/** - Sets the dependent keys on this computed property. Pass any number of - arguments containing key paths that this computed property depends on. + ### Routing - ```javascript - MyApp.President = Ember.Object.extend({ - fullName: Ember.computed(function() { - return this.get('firstName') + ' ' + this.get('lastName'); + In addition to creating your application's router, `Ember.Application` is + also responsible for telling the router when to start routing. Transitions + between routes can be logged with the `LOG_TRANSITIONS` flag, and more + detailed intra-transition logging can be logged with + the `LOG_TRANSITIONS_INTERNAL` flag: - // Tell Ember that this computed property depends on firstName - // and lastName - }).property('firstName', 'lastName') - }); + ```javascript + window.App = Ember.Application.create({ + LOG_TRANSITIONS: true, // basic logging of successful transitions + LOG_TRANSITIONS_INTERNAL: true // detailed logging of all routing steps + }); + ``` - MyApp.president = MyApp.President.create({ - firstName: 'Barack', - lastName: 'Obama', - }); - MyApp.president.get('fullName'); // Barack Obama - ``` - - @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); - }; - - args = []; - for (var i = 0, l = arguments.length; i < l; i++) { - expandProperties(arguments[i], addArg); - } + By default, the router will begin trying to translate the current URL into + application state once the browser emits the `DOMContentReady` event. If you + need to defer routing, you can call the application's `deferReadiness()` + method. Once routing can begin, call the `advanceReadiness()` method. - if (Ember.FEATURES.isEnabled('composable-computed-properties')) { - setDependentKeys(this, args); - } else { - this._dependentKeys = args; - } + If there is any setup required before routing begins, you can implement a + `ready()` method on your app that will be invoked immediately before routing + begins. + ``` - return this; -}; - -/** - 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. - - You can pass a hash of these values to a computed property like this: - - ``` - person: function() { - var personId = this.get('personId'); - return App.Person.create({ id: personId }); - }.property().meta({ type: App.Person }) - ``` - - 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. - - @method meta - @param {Hash} meta - @chainable -*/ - -ComputedPropertyPrototype.meta = function(meta) { - if (arguments.length === 0) { - return this._meta || {}; - } else { - this._meta = meta; - return this; - } -}; - -/* 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 (keyName in meta.cache) { - delete meta.cache[keyName]; - removeDependentKeys(this, obj, keyName, meta); - } - } -}; + @class Application + @namespace Ember + @extends Ember.Namespace + */ -function finishChains(chainNodes) -{ - for (var i=0, l=chainNodes.length; i 1) { - args = a_slice.call(arguments, 0, -1); - func = a_slice.call(arguments, -1)[0]; - } + Also register a default application view in case the application + itself does not. - if (typeof func !== "function") { - throw new Ember.Error("Computed Property declared without a property function"); - } + @private + @method buildContainer + @return {Ember.Container} the configured container + */ + buildContainer: function() { + var container = this.__container__ = Application.buildContainer(this); - var cp = new ComputedProperty(func); + return container; + }, - if (args) { - cp.property.apply(cp, args); - } + /** + If the application has not opted out of routing and has not explicitly + defined a router, supply a default router for the application author + to configure. - return cp; -}; - -/** - Returns the cached value for a property, if one exists. - This can be useful for peeking at the value of a computed - property that is generated lazily, without accidentally causing - it to be created. - - @method 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 -*/ -Ember.cacheFor = function cacheFor(obj, key) { - var meta = obj[META_KEY], - cache = meta && meta.cache; - - if (cache && key in cache) { - return cache[key]; - } -}; + This allows application developers to do: -function getProperties(self, propertyNames) { - var ret = {}; - for(var i = 0; i < propertyNames.length; i++) { - ret[propertyNames[i]] = get(self, propertyNames[i]); - } - return ret; -} + ```javascript + var App = Ember.Application.create(); -var registerComputed, registerComputedWithProperties; + App.Router.map(function() { + this.resource('posts'); + }); + ``` -if (Ember.FEATURES.isEnabled('composable-computed-properties')) { - var guidFor = Ember.guidFor, - map = Ember.EnumerableUtils.map, - filter = Ember.EnumerableUtils.filter, - typeOf = Ember.typeOf; + @private + @method defaultRouter + @return {Ember.Router} the default router + */ - var implicitKey = function (cp) { - return [guidFor(cp)].concat(cp._dependentKeys).join('_').replace(/\./g, '_DOT_'); - }; + defaultRouter: function() { + if (this.Router === false) { return; } + var container = this.__container__; - var normalizeDependentKey = function (key) { - if (key instanceof Ember.ComputedProperty) { - return implicitKey(key); - } else { - return key; - } - }; + if (this.Router) { + container.unregister('router:main'); + container.register('router:main', this.Router); + } - var normalizeDependentKeys = function (keys) { - return map(keys, function (key) { - return normalizeDependentKey(key); - }); - }; + return container.lookupFactory('router:main'); + }, - var selectDependentCPs = function (keys) { - return filter(keys, function (key) { - return key instanceof Ember.ComputedProperty; - }); - }; + /** + Automatically initialize the application once the DOM has + become ready. - var setDependentKeys = function(cp, dependentKeys) { - if (dependentKeys) { - cp._dependentKeys = normalizeDependentKeys(dependentKeys); - cp._dependentCPs = selectDependentCPs(dependentKeys); - } else { - cp._dependentKeys = cp._dependentCPs = []; - } - cp.implicitCPKey = implicitKey(cp); - }; - // expose `normalizeDependentKey[s]` so user CP macros can easily support - // composition - Ember.computed.normalizeDependentKey = normalizeDependentKey; - Ember.computed.normalizeDependentKeys = normalizeDependentKeys; + The initialization itself is scheduled on the actions queue + which ensures that application loading finishes before + booting. - registerComputed = function (name, macro) { - Ember.computed[name] = function(dependentKey) { - var args = normalizeDependentKeys(a_slice.call(arguments)); - return Ember.computed(dependentKey, function() { - return macro.apply(this, args); - }); - }; - }; -} + If you are asynchronously loading code, you should call + `deferReadiness()` to defer booting, and then call + `advanceReadiness()` once all of your code has finished + loading. -if (Ember.FEATURES.isEnabled('composable-computed-properties')) { - registerComputedWithProperties = function(name, macro) { - Ember.computed[name] = function() { - var args = a_slice.call(arguments); - var properties = normalizeDependentKeys(args); + @private + @method scheduleInitialize + */ + scheduleInitialize: function() { + var self = this; - var computed = Ember.computed(function() { - return macro.apply(this, [getProperties(this, properties)]); - }); + if (!this.$ || this.$.isReady) { + run.schedule('actions', self, '_initialize'); + } else { + this.$().ready(function runInitialize() { + run(self, '_initialize'); + }); + } + }, - return computed.property.apply(computed, args); - }; - }; -} else { - registerComputed = function (name, macro) { - Ember.computed[name] = function(dependentKey) { - var args = a_slice.call(arguments); - return Ember.computed(dependentKey, function() { - return macro.apply(this, args); - }); - }; - }; + /** + Use this to defer readiness until some condition is true. - registerComputedWithProperties = function(name, macro) { - Ember.computed[name] = function() { - var properties = a_slice.call(arguments); + Example: - var computed = Ember.computed(function() { - return macro.apply(this, [getProperties(this, properties)]); - }); + ```javascript + App = Ember.Application.create(); + App.deferReadiness(); - return computed.property.apply(computed, properties); - }; - }; -} + jQuery.getJSON("/auth-token", function(token) { + App.token = token; + App.advanceReadiness(); + }); + ``` + This allows you to perform asynchronous setup logic and defer + booting your application until the setup has finished. -if (Ember.FEATURES.isEnabled('composable-computed-properties')) { - Ember.computed.literal = function (value) { - return Ember.computed(function () { - return value; - }); - }; -} + However, if the setup requires a loading UI, it might be better + to use the router for this purpose. + @method deferReadiness + */ + deferReadiness: function() { + Ember.assert("You must call deferReadiness on an instance of Ember.Application", this instanceof Application); + Ember.assert("You cannot defer readiness since the `ready()` hook has already been called.", this._readinessDeferrals > 0); + this._readinessDeferrals++; + }, - /** - A computed property that returns true if the value of the dependent - property is null, an empty string, empty array, or empty function. + /** + Call `advanceReadiness` after any asynchronous setup logic has completed. + Each call to `deferReadiness` must be matched by a call to `advanceReadiness` + or the application will never become ready and routing will not begin. - Note: When using `Ember.computed.empty` to watch an array make sure to - use the `array.[]` syntax so the computed can subscribe to transitions - from empty to non-empty states. + @method advanceReadiness + @see {Ember.Application#deferReadiness} + */ + advanceReadiness: function() { + Ember.assert("You must call advanceReadiness on an instance of Ember.Application", this instanceof Application); + this._readinessDeferrals--; - Example + if (this._readinessDeferrals === 0) { + run.once(this, this.didBecomeReady); + } + }, - ```javascript - var ToDoList = Ember.Object.extend({ - done: Ember.computed.empty('todos.[]') // detect array changes - }); - var todoList = ToDoList.create({todos: ['Unit Test', 'Documentation', 'Release']}); - todoList.get('done'); // false - todoList.get('todos').clear(); // [] - todoList.get('done'); // true - ``` + /** + Registers a factory that can be used for dependency injection (with + `App.inject`) or for service lookup. Each factory is registered with + a full name including two parts: `type:name`. - @method computed.empty - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computed property which negate - the original value for property - */ - registerComputed('empty', function(dependentKey) { - return Ember.isEmpty(get(this, dependentKey)); - }); + A simple example: + ```javascript + var App = Ember.Application.create(); + App.Orange = Ember.Object.extend(); + App.register('fruit:favorite', App.Orange); + ``` -/** - A computed property that returns true if the value of the dependent - property is NOT null, an empty string, empty array, or empty function. + Ember will resolve factories from the `App` namespace automatically. + For example `App.CarsController` will be discovered and returned if + an application requests `controller:cars`. - Note: When using `Ember.computed.notEmpty` to watch an array make sure to - use the `array.[]` syntax so the computed can subscribe to transitions - from empty to non-empty states. + An example of registering a controller with a non-standard name: - Example + ```javascript + var App = Ember.Application.create(), + Session = Ember.Controller.extend(); - ```javascript - var Hamster = Ember.Object.extend({ - hasStuff: Ember.computed.notEmpty('backpack.[]') - }); - var hamster = Hamster.create({backpack: ['Food', 'Sleeping Bag', 'Tent']}); - hamster.get('hasStuff'); // true - hamster.get('backpack').clear(); // [] - hamster.get('hasStuff'); // false - ``` - - @method computed.notEmpty - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computed property which returns true if - original value for property is not empty. -*/ -registerComputed('notEmpty', function(dependentKey) { - return !Ember.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. - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - isHungry: Ember.computed.none('food') - }); - var hamster = Hamster.create(); - hamster.get('isHungry'); // true - hamster.set('food', 'Banana'); - hamster.get('isHungry'); // false - hamster.set('food', null); - hamster.get('isHungry'); // true - ``` - - @method computed.none - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computed property which - returns true if original value for property is null or undefined. -*/ -registerComputed('none', function(dependentKey) { - return Ember.isNone(get(this, dependentKey)); -}); - -/** - 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 user = User.create({loggedIn: false}); - user.get('isAnonymous'); // true - user.set('loggedIn', true); - user.get('isAnonymous'); // false - ``` - - @method computed.not - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computed property which returns - inverse of the original value for property -*/ -registerComputed('not', function(dependentKey) { - return !get(this, dependentKey); -}); - -/** - A computed property that converts the provided dependent property - into a boolean value. - - ```javascript - var Hamster = Ember.Object.extend({ - hasBananas: Ember.computed.bool('numBananas') - }); - var hamster = Hamster.create(); - 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 - ``` - - @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); -}); - -/** - 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. - - Example - - ```javascript - var User = Ember.Object.extend({ - hasValidEmail: Ember.computed.match('email', /^.+@.+\..+$/) - }); - var user = User.create({loggedIn: false}); - user.get('hasValidEmail'); // false - user.set('email', ''); - user.get('hasValidEmail'); // false - user.set('email', 'ember_hamster@example.com'); - user.get('hasValidEmail'); // true - ``` - - @method computed.match - @for Ember - @param {String} dependentKey - @param {RegExp} regexp - @return {Ember.ComputedProperty} computed property which match - the original value for property against a given RegExp -*/ -registerComputed('match', function(dependentKey, regexp) { - var value = get(this, dependentKey); - return typeof value === 'string' ? regexp.test(value) : false; -}); - -/** - A computed property that returns true if the provided dependent property - is equal to the given value. - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - napTime: Ember.computed.equal('state', 'sleepy') - }); - var hamster = Hamster.create(); - hamster.get('napTime'); // false - hamster.set('state', 'sleepy'); - hamster.get('napTime'); // true - hamster.set('state', 'hungry'); - hamster.get('napTime'); // false - ``` - - @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; -}); - -/** - A computed property that returns true if the provied dependent property - is greater than the provided value. - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - hasTooManyBananas: Ember.computed.gt('numBananas', 10) - }); - var hamster = Hamster.create(); - hamster.get('hasTooManyBananas'); // false - hamster.set('numBananas', 3); - hamster.get('hasTooManyBananas'); // false - hamster.set('numBananas', 11); - hamster.get('hasTooManyBananas'); // true - ``` - - @method computed.gt - @for Ember - @param {String} dependentKey - @param {Number} value - @return {Ember.ComputedProperty} computed property which returns true if - the original value for property is greater then given value. -*/ -registerComputed('gt', function(dependentKey, value) { - return get(this, dependentKey) > value; -}); - -/** - A computed property that returns true if the provided dependent property - is greater than or equal to the provided value. - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - hasTooManyBananas: Ember.computed.gte('numBananas', 10) - }); - var hamster = Hamster.create(); - hamster.get('hasTooManyBananas'); // false - hamster.set('numBananas', 3); - hamster.get('hasTooManyBananas'); // false - hamster.set('numBananas', 10); - hamster.get('hasTooManyBananas'); // true - ``` - - @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; -}); - -/** - A computed property that returns true if the provided dependent property - is less than the provided value. - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - needsMoreBananas: Ember.computed.lt('numBananas', 3) - }); - var hamster = Hamster.create(); - hamster.get('needsMoreBananas'); // true - hamster.set('numBananas', 3); - hamster.get('needsMoreBananas'); // false - hamster.set('numBananas', 2); - hamster.get('needsMoreBananas'); // true - ``` - - @method computed.lt - @for Ember - @param {String} dependentKey - @param {Number} value - @return {Ember.ComputedProperty} computed property which returns true if - the original value for property is less then given value. -*/ -registerComputed('lt', function(dependentKey, value) { - return get(this, dependentKey) < value; -}); - -/** - A computed property that returns true if the provided dependent property - is less than or equal to the provided value. - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - needsMoreBananas: Ember.computed.lte('numBananas', 3) - }); - var hamster = Hamster.create(); - hamster.get('needsMoreBananas'); // true - hamster.set('numBananas', 5); - hamster.get('needsMoreBananas'); // false - hamster.set('numBananas', 3); - hamster.get('needsMoreBananas'); // true - ``` - - @method computed.lte - @for Ember - @param {String} dependentKey - @param {Number} value - @return {Ember.ComputedProperty} computed property which returns true if - the original value for property is less or equal then given value. -*/ -registerComputed('lte', function(dependentKey, value) { - return get(this, dependentKey) <= value; -}); - -/** - A computed property that performs a logical `and` on the - original values for the provided dependent properties. - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - readyForCamp: Ember.computed.and('hasTent', 'hasBackpack') - }); - var hamster = Hamster.create(); - 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; -}); + App.register('controller:session', Session); -/** - A computed property which performs a logical `or` on the - original values for the provided dependent properties. + // The Session controller can now be treated like a normal controller, + // despite its non-standard name. + App.ApplicationController = Ember.Controller.extend({ + needs: ['session'] + }); + ``` - Example + Registered factories are **instantiated** by having `create` + called on them. Additionally they are **singletons**, each time + they are looked up they return the same instance. - ```javascript - var Hamster = Ember.Object.extend({ - readyForRain: Ember.computed.or('hasJacket', 'hasUmbrella') - }); - var hamster = Hamster.create(); - hamster.get('readyForRain'); // false - hamster.set('hasJacket', true); - hamster.get('readyForRain'); // true - ``` - - @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; -}); + Some examples modifying that default behavior: -/** - A computed property that returns the first truthy value - from a list of dependent properties. + ```javascript + var App = Ember.Application.create(); - Example + App.Person = Ember.Object.extend(); + App.Orange = Ember.Object.extend(); + App.Email = Ember.Object.extend(); + App.session = Ember.Object.create(); - ```javascript - var Hamster = Ember.Object.extend({ - hasClothes: Ember.computed.any('hat', 'shirt') - }); - var hamster = Hamster.create(); - hamster.get('hasClothes'); // null - hamster.set('shirt', 'Hawaiian Shirt'); - hamster.get('hasClothes'); // 'Hawaiian Shirt' - ``` - - @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; -}); + App.register('model:user', App.Person, {singleton: false }); + App.register('fruit:favorite', App.Orange); + App.register('communication:main', App.Email, {singleton: false}); + App.register('session', App.session, {instantiate: false}); + ``` -/** - A computed property that returns the array of values - for the provided dependent properties. + @method register + @param fullName {String} type:name (e.g., 'model:user') + @param factory {Function} (e.g., App.Person) + @param options {Object} (optional) disable instantiation or singleton usage + **/ + register: function() { + var container = this.__container__; + container.register.apply(container, arguments); + }, - Example + /** + Define a dependency injection onto a specific factory or all factories + of a type. - ```javascript - var Hamster = Ember.Object.extend({ - clothes: Ember.computed.collect('hat', 'shirt') - }); - var hamster = Hamster.create(); - hamster.get('clothes'); // [null, null] - hamster.set('hat', 'Camp Hat'); - hamster.set('shirt', 'Camp Shirt'); - hamster.get('clothes'); // ['Camp Hat', 'Camp Shirt'] - ``` - - @method computed.collect - @for Ember - @param {String} dependentKey* - @return {Ember.ComputedProperty} computed property which maps - values of all passed properties in to an array. -*/ -registerComputedWithProperties('collect', function(properties) { - var res = []; - for (var key in properties) { - if (properties.hasOwnProperty(key)) { - if (Ember.isNone(properties[key])) { - res.push(null); - } else { - res.push(properties[key]); - } - } - } - return res; -}); - -/** - Creates a new property that is an alias for another property - on an object. Calls to `get` or `set` this property behave as - though they were called on the original property. - - ```javascript - Person = Ember.Object.extend({ - name: 'Alex Matchneer', - nomen: Ember.computed.alias('name') - }); + When Ember instantiates a controller, view, or other framework component + it can attach a dependency to that component. This is often used to + provide services to a set of framework components. - alex = Person.create(); - alex.get('nomen'); // 'Alex Matchneer' - alex.get('name'); // 'Alex Matchneer' - - alex.set('nomen', '@machty'); - alex.get('name'); // '@machty' - ``` - @method computed.alias - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computed property which creates an - alias to the original value for property. -*/ -Ember.computed.alias = function(dependentKey) { - return Ember.computed(dependentKey, function(key, value) { - if (arguments.length > 1) { - set(this, dependentKey, value); - return value; - } else { - return get(this, dependentKey); - } - }); -}; - -/** - 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 permentantly - diverge from the upstream property. - - Example - - ```javascript - User = Ember.Object.extend({ - firstName: null, - lastName: null, - nickName: Ember.computed.oneWay('firstName') - }); + An example of providing a session object to all controllers: - user = User.create({ - firstName: 'Teddy', - lastName: 'Zeenny' - }); + ```javascript + var App = Ember.Application.create(), + Session = Ember.Object.extend({ isAuthenticated: false }); - user.get('nickName'); - # 'Teddy' - - user.set('nickName', 'TeddyBear'); - # 'TeddyBear' - - user.get('firstName'); - # 'Teddy' - ``` - - @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.computed.oneWay = function(dependentKey) { - return Ember.computed(dependentKey, function() { - return get(this, dependentKey); - }); -}; + // A factory must be registered before it can be injected + App.register('session:main', Session); + // Inject 'session:main' onto all factories of the type 'controller' + // with the name 'session' + App.inject('controller', 'session', 'session:main'); + App.IndexController = Ember.Controller.extend({ + isLoggedIn: Ember.computed.alias('session.isAuthenticated') + }); + ``` -/** - Where `computed.oneWay` provides oneWay bindings, `computed.readOnly` provides - a readOnly one way binding. Very often when using `computed.oneWay` one does - not also want changes to propogate back up, as they will replace the value. + Injections can also be performed on specific factories. - This prevents the reverse flow, and also throws an exception when it occurs. + ```javascript + App.inject(, , ) + App.inject('route', 'source', 'source:main') + App.inject('route:application', 'email', 'model:email') + ``` - Example + It is important to note that injections can only be performed on + classes that are instantiated by Ember itself. Instantiating a class + directly (via `create` or `new`) bypasses the dependency injection + system. + + Ember-Data instantiates its models in a unique manner, and consequently + injections onto models (or all models) will not work as expected. Injections + on models can be enabled by setting `Ember.MODEL_FACTORY_INJECTIONS` + to `true`. + + @method inject + @param factoryNameOrType {String} + @param property {String} + @param injectionName {String} + **/ + inject: function() { + var container = this.__container__; + container.injection.apply(container, arguments); + }, - ```javascript - User = Ember.Object.extend({ - firstName: null, - lastName: null, - nickName: Ember.computed.readOnly('firstName') - }); + /** + Calling initialize manually is not supported. - user = User.create({ - firstName: 'Teddy', - lastName: 'Zeenny' - }); + Please see Ember.Application#advanceReadiness and + Ember.Application#deferReadiness. - user.get('nickName'); - # 'Teddy' - - user.set('nickName', 'TeddyBear'); - # throws Exception - # throw new Ember.Error('Cannot Set: nickName on: ' );` - - user.get('firstName'); - # 'Teddy' - ``` - - @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. -*/ -Ember.computed.readOnly = function(dependentKey) { - return Ember.computed(dependentKey, function() { - return get(this, dependentKey); - }).readOnly(); -}; - -/** - A computed property that acts like a standard getter and setter, - but returns the value at the provided `defaultPath` if the - property itself has not been set to a value - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - wishList: Ember.computed.defaultTo('favoriteFood') - }); - var hamster = Hamster.create({favoriteFood: 'Banana'}); - hamster.get('wishList'); // 'Banana' - hamster.set('wishList', 'More Unit Tests'); - hamster.get('wishList'); // 'More Unit Tests' - hamster.get('favoriteFood'); // 'Banana' - ``` - - @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`. -*/ -Ember.computed.defaultTo = function(defaultPath) { - return Ember.computed(function(key, newValue, cachedValue) { - if (arguments.length === 1) { - return cachedValue != null ? cachedValue : get(this, defaultPath); - } - return newValue != null ? newValue : get(this, defaultPath); - }); -}; + @private + @deprecated + @method initialize + **/ + initialize: function() { + Ember.deprecate('Calling initialize manually is not supported. Please see Ember.Application#advanceReadiness and Ember.Application#deferReadiness'); + }, + /** + Initialize the application. This happens automatically. -})(); + Run any initializers and run the application load hook. These hooks may + choose to defer readiness. For example, an authentication hook might want + to defer readiness until the auth token has been retrieved. + @private + @method _initialize + */ + _initialize: function() { + if (this.isDestroyed) { return; } + // At this point, the App.Router must already be assigned + if (this.Router) { + var container = this.__container__; + container.unregister('router:main'); + container.register('router:main', this.Router); + } -(function() { -// Ember.tryFinally -/** -@module ember-metal -*/ - -var AFTER_OBSERVERS = ':change', - BEFORE_OBSERVERS = ':before'; - -function changeEvent(keyName) { - return keyName+AFTER_OBSERVERS; -} - -function beforeEvent(keyName) { - return keyName+BEFORE_OBSERVERS; -} - -/** - @method addObserver - @for Ember - @param obj - @param {String} path - @param {Object|Function} targetOrMethod - @param {Function|String} [method] -*/ -Ember.addObserver = function(obj, _path, target, method) { - Ember.addListener(obj, changeEvent(_path), target, method); - Ember.watch(obj, _path); - - return this; -}; - -Ember.observersFor = function(obj, path) { - return Ember.listenersFor(obj, changeEvent(path)); -}; - -/** - @method removeObserver - @for Ember - @param obj - @param {String} path - @param {Object|Function} targetOrMethod - @param {Function|String} [method] -*/ -Ember.removeObserver = function(obj, _path, target, method) { - Ember.unwatch(obj, _path); - Ember.removeListener(obj, changeEvent(_path), target, method); - - return this; -}; - -/** - @method addBeforeObserver - @for Ember - @param obj - @param {String} path - @param {Object|Function} targetOrMethod - @param {Function|String} [method] -*/ -Ember.addBeforeObserver = function(obj, _path, target, method) { - Ember.addListener(obj, beforeEvent(_path), target, method); - Ember.watch(obj, _path); - - return this; -}; - -// Suspend observer during callback. -// -// This should only be used by the target of the observer -// while it is setting the observed path. -Ember._suspendBeforeObserver = function(obj, path, target, method, callback) { - return Ember._suspendListener(obj, beforeEvent(path), target, method, callback); -}; - -Ember._suspendObserver = function(obj, path, target, method, callback) { - return Ember._suspendListener(obj, changeEvent(path), target, method, callback); -}; - -var map = Ember.ArrayPolyfills.map; - -Ember._suspendBeforeObservers = function(obj, paths, target, method, callback) { - var events = map.call(paths, beforeEvent); - return Ember._suspendListeners(obj, events, target, method, callback); -}; - -Ember._suspendObservers = function(obj, paths, target, method, callback) { - var events = map.call(paths, changeEvent); - return Ember._suspendListeners(obj, events, target, method, callback); -}; - -Ember.beforeObserversFor = function(obj, path) { - return Ember.listenersFor(obj, beforeEvent(path)); -}; - -/** - @method removeBeforeObserver - @for Ember - @param obj - @param {String} path - @param {Object|Function} targetOrMethod - @param {Function|String} [method] -*/ -Ember.removeBeforeObserver = function(obj, _path, target, method) { - Ember.unwatch(obj, _path); - Ember.removeListener(obj, beforeEvent(_path), target, method); - - return this; -}; + this.runInitializers(); + runLoadHooks('application', this); -})(); + // At this point, any initializers or load hooks that would have wanted + // to defer readiness have fired. In general, advancing readiness here + // will proceed to didBecomeReady. + this.advanceReadiness(); + return this; + }, + /** + Reset the application. This is typically used only in tests. It cleans up + the application in the following order: -(function() { -define("backburner/queue", - ["exports"], - function(__exports__) { - "use strict"; - function Queue(daq, name, options) { - this.daq = daq; - this.name = name; - this.options = options; - this._queue = []; - } + 1. Deactivate existing routes + 2. Destroy all objects in the container + 3. Create a new application container + 4. Re-route to the existing url - Queue.prototype = { - daq: null, - name: null, - options: null, - _queue: null, + Typical Example: - push: function(target, method, args, stack) { - var queue = this._queue; - queue.push(target, method, args, stack); - return {queue: this, target: target, method: method}; - }, + ```javascript - pushUnique: function(target, method, args, stack) { - var queue = this._queue, currentTarget, currentMethod, i, l; + var App; - for (i = 0, l = queue.length; i < l; i += 4) { - currentTarget = queue[i]; - currentMethod = queue[i+1]; + run(function() { + App = Ember.Application.create(); + }); - if (currentTarget === target && currentMethod === method) { - queue[i+2] = args; // replace args - queue[i+3] = stack; // replace stack - return {queue: this, target: target, method: method}; // TODO: test this code path + module("acceptance test", { + setup: function() { + App.reset(); } - } + }); - this._queue.push(target, method, args, stack); - return {queue: this, target: target, method: method}; - }, + test("first test", function() { + // App is freshly reset + }); - // TODO: remove me, only being used for Ember.run.sync - flush: function() { - var queue = this._queue, - options = this.options, - before = options && options.before, - after = options && options.after, - target, method, args, stack, i, l = queue.length; + test("first test", function() { + // App is again freshly reset + }); + ``` - if (l && before) { before(); } - for (i = 0; i < l; i += 4) { - target = queue[i]; - method = queue[i+1]; - args = queue[i+2]; - stack = queue[i+3]; // Debugging assistance + Advanced Example: - // TODO: error handling - if (args && args.length > 0) { - method.apply(target, args); - } else { - method.call(target); - } - } - if (l && after) { after(); } + Occasionally you may want to prevent the app from initializing during + setup. This could enable extra configuration, or enable asserting prior + to the app becoming ready. - // check if new items have been added - if (queue.length > l) { - this._queue = queue.slice(l); - this.flush(); - } else { - this._queue.length = 0; - } - }, + ```javascript - cancel: function(actionToCancel) { - var queue = this._queue, currentTarget, currentMethod, i, l; + var App; - for (i = 0, l = queue.length; i < l; i += 4) { - currentTarget = queue[i]; - currentMethod = queue[i+1]; + run(function() { + App = Ember.Application.create(); + }); - if (currentTarget === actionToCancel.target && currentMethod === actionToCancel.method) { - queue.splice(i, 4); - return true; + module("acceptance test", { + setup: function() { + run(function() { + App.reset(); + App.deferReadiness(); + }); } - } + }); - // if not found in current queue - // could be in the queue that is being flushed - queue = this._queueBeingFlushed; - if (!queue) { - return; - } - for (i = 0, l = queue.length; i < l; i += 4) { - currentTarget = queue[i]; - currentMethod = queue[i+1]; + test("first test", function() { + ok(true, 'something before app is initialized'); - if (currentTarget === actionToCancel.target && currentMethod === actionToCancel.method) { - // don't mess with array during flush - // just nullify the method - queue[i+1] = null; - return true; - } - } - } - }; + run(function() { + App.advanceReadiness(); + }); + ok(true, 'something after app is initialized'); + }); + ``` - __exports__.Queue = Queue; - }); + @method reset + **/ + reset: function() { + this._readinessDeferrals = 1; -define("backburner/deferred_action_queues", - ["backburner/queue","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Queue = __dependency1__.Queue; + function handleReset() { + var router = this.__container__.lookup('router:main'); + router.reset(); - function DeferredActionQueues(queueNames, options) { - var queues = this.queues = {}; - this.queueNames = queueNames = queueNames || []; + run(this.__container__, 'destroy'); - var queueName; - for (var i = 0, l = queueNames.length; i < l; i++) { - queueName = queueNames[i]; - queues[queueName] = new Queue(this, queueName, options[queueName]); - } - } + this.buildContainer(); - DeferredActionQueues.prototype = { - queueNames: null, - queues: null, + run.schedule('actions', this, function() { + this._initialize(); + }); + } - schedule: function(queueName, target, method, args, onceFlag, stack) { - var queues = this.queues, - queue = queues[queueName]; + run.join(this, handleReset); + }, - if (!queue) { throw new Error("You attempted to schedule an action in a queue (" + queueName + ") that doesn't exist"); } + /** + @private + @method runInitializers + */ + runInitializers: function() { + var initializers = get(this.constructor, 'initializers'); + var container = this.__container__; + var graph = new DAG(); + var namespace = this; + var name, initializer; + + for (name in initializers) { + initializer = initializers[name]; + graph.addEdges(initializer.name, initializer.initialize, initializer.before, initializer.after); + } + + graph.topsort(function (vertex) { + var initializer = vertex.value; + Ember.assert("No application initializer named '"+vertex.name+"'", initializer); + initializer(container, namespace); + }); + }, - if (onceFlag) { - return queue.pushUnique(target, method, args, stack); - } else { - return queue.push(target, method, args, stack); + /** + @private + @method didBecomeReady + */ + didBecomeReady: function() { + this.setupEventDispatcher(); + this.ready(); // user hook + this.startRouting(); + + if (!Ember.testing) { + // Eagerly name all classes that are already loaded + Ember.Namespace.processAll(); + Ember.BOOTED = true; } + + this.resolve(this); }, - flush: function() { - var queues = this.queues, - queueNames = this.queueNames, - queueName, queue, queueItems, priorQueueNameIndex, - queueNameIndex = 0, numberOfQueues = queueNames.length; + /** + Setup up the event dispatcher to receive events on the + application's `rootElement` with any registered + `customEvents`. - outerloop: - while (queueNameIndex < numberOfQueues) { - queueName = queueNames[queueNameIndex]; - queue = queues[queueName]; - queueItems = queue._queueBeingFlushed = queue._queue.slice(); - queue._queue = []; + @private + @method setupEventDispatcher + */ + setupEventDispatcher: function() { + var customEvents = get(this, 'customEvents'); + var rootElement = get(this, 'rootElement'); + var dispatcher = this.__container__.lookup('event_dispatcher:main'); - var options = queue.options, - before = options && options.before, - after = options && options.after, - target, method, args, stack, - queueIndex = 0, numberOfQueueItems = queueItems.length; + set(this, 'eventDispatcher', dispatcher); + dispatcher.setup(customEvents, rootElement); + }, - if (numberOfQueueItems && before) { before(); } - while (queueIndex < numberOfQueueItems) { - target = queueItems[queueIndex]; - method = queueItems[queueIndex+1]; - args = queueItems[queueIndex+2]; - stack = queueItems[queueIndex+3]; // Debugging assistance + /** + If the application has a router, use it to route to the current URL, and + trigger a new call to `route` whenever the URL changes. - if (typeof method === 'string') { method = target[method]; } + @private + @method startRouting + @property router {Ember.Router} + */ + startRouting: function() { + var router = this.__container__.lookup('router:main'); + if (!router) { return; } - // method could have been nullified / canceled during flush - if (method) { - // TODO: error handling - if (args && args.length > 0) { - method.apply(target, args); - } else { - method.call(target); - } - } + router.startRouting(); + }, - queueIndex += 4; - } - queue._queueBeingFlushed = null; - if (numberOfQueueItems && after) { after(); } + handleURL: function(url) { + var router = this.__container__.lookup('router:main'); - if ((priorQueueNameIndex = indexOfPriorQueueWithActions(this, queueNameIndex)) !== -1) { - queueNameIndex = priorQueueNameIndex; - continue outerloop; - } + router.handleURL(url); + }, - queueNameIndex++; - } - } - }; + /** + Called when the Application has become ready. + The call will be delayed until the DOM has become ready. - function indexOfPriorQueueWithActions(daq, currentQueueIndex) { - var queueName, queue; + @event ready + */ + ready: K, - for (var i = 0, l = currentQueueIndex; i <= l; i++) { - queueName = daq.queueNames[i]; - queue = daq.queues[queueName]; - if (queue._queue.length) { return i; } - } + /** + @deprecated Use 'Resolver' instead + Set this to provide an alternate class to `Ember.DefaultResolver` - return -1; - } - __exports__.DeferredActionQueues = DeferredActionQueues; - }); + @property resolver + */ + resolver: null, -define("backburner", - ["backburner/deferred_action_queues","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var DeferredActionQueues = __dependency1__.DeferredActionQueues; + /** + Set this to provide an alternate class to `Ember.DefaultResolver` - var slice = [].slice, - pop = [].pop, - throttlers = [], - debouncees = [], - timers = [], - autorun, laterTimer, laterTimerExpiresAt, - global = this, - NUMBER = /\d+/; + @property resolver + */ + Resolver: null, - function isCoercableNumber(number) { - return typeof number === 'number' || NUMBER.test(number); - } + willDestroy: function() { + Ember.BOOTED = false; + // Ensure deactivation of routes before objects are destroyed + this.__container__.lookup('router:main').reset(); - function Backburner(queueNames, options) { - this.queueNames = queueNames; - this.options = options || {}; - if (!this.options.defaultQueue) { - this.options.defaultQueue = queueNames[0]; - } - this.instanceStack = []; - } - - Backburner.prototype = { - queueNames: null, - options: null, - currentInstance: null, - instanceStack: null, + this.__container__.destroy(); + }, - begin: function() { - var onBegin = this.options && this.options.onBegin, - previousInstance = this.currentInstance; + initializer: function(options) { + this.constructor.initializer(options); + }, - if (previousInstance) { - this.instanceStack.push(previousInstance); - } + /** + @method then + @private + @deprecated + */ + then: function() { + Ember.deprecate('Do not use `.then` on an instance of Ember.Application. Please use the `.ready` hook instead.'); - this.currentInstance = new DeferredActionQueues(this.queueNames, this.options); - if (onBegin) { - onBegin(this.currentInstance, previousInstance); - } - }, + this._super.apply(this, arguments); + } + }); - end: function() { - var onEnd = this.options && this.options.onEnd, - currentInstance = this.currentInstance, - nextInstance = null; + Application.reopenClass({ + initializers: {}, - try { - currentInstance.flush(); - } finally { - this.currentInstance = null; + /** + Initializer receives an object which has the following attributes: + `name`, `before`, `after`, `initialize`. The only required attribute is + `initialize, all others are optional. - if (this.instanceStack.length) { - nextInstance = this.instanceStack.pop(); - this.currentInstance = nextInstance; - } + * `name` allows you to specify under which name the initializer is registered. + This must be a unique name, as trying to register two initializers with the + same name will result in an error. - if (onEnd) { - onEnd(currentInstance, nextInstance); + ```javascript + Ember.Application.initializer({ + name: 'namedInitializer', + initialize: function(container, application) { + Ember.debug("Running namedInitializer!"); } - } - }, - - run: function(target, method /*, args */) { - var ret; - this.begin(); + }); + ``` - if (!method) { - method = target; - target = null; - } + * `before` and `after` are used to ensure that this initializer is ran prior + or after the one identified by the value. This value can be a single string + or an array of strings, referencing the `name` of other initializers. - if (typeof method === 'string') { - method = target[method]; - } + An example of ordering initializers, we create an initializer named `first`: - // Prevent Safari double-finally. - var finallyAlreadyCalled = false; - try { - if (arguments.length > 2) { - ret = method.apply(target, slice.call(arguments, 2)); - } else { - ret = method.call(target); - } - } finally { - if (!finallyAlreadyCalled) { - finallyAlreadyCalled = true; - this.end(); + ```javascript + Ember.Application.initializer({ + name: 'first', + initialize: function(container, application) { + Ember.debug("First initializer!"); } - } - return ret; - }, - - defer: function(queueName, target, method /* , args */) { - if (!method) { - method = target; - target = null; - } + }); - if (typeof method === 'string') { - method = target[method]; - } + // DEBUG: First initializer! + ``` - var stack = this.DEBUG ? new Error() : undefined, - args = arguments.length > 3 ? slice.call(arguments, 3) : undefined; - if (!this.currentInstance) { createAutorun(this); } - return this.currentInstance.schedule(queueName, target, method, args, false, stack); - }, + We add another initializer named `second`, specifying that it should run + after the initializer named `first`: - deferOnce: function(queueName, target, method /* , args */) { - if (!method) { - method = target; - target = null; - } + ```javascript + Ember.Application.initializer({ + name: 'second', + after: 'first', - if (typeof method === 'string') { - method = target[method]; - } + initialize: function(container, application) { + Ember.debug("Second initializer!"); + } + }); - var stack = this.DEBUG ? new Error() : undefined, - args = arguments.length > 3 ? slice.call(arguments, 3) : undefined; - if (!this.currentInstance) { createAutorun(this); } - return this.currentInstance.schedule(queueName, target, method, args, true, stack); - }, + // DEBUG: First initializer! + // DEBUG: Second initializer! + ``` - setTimeout: function() { - var args = slice.call(arguments); - var length = args.length; - var method, wait, target; - var self = this; - var methodOrTarget, methodOrWait, methodOrArgs; + Afterwards we add a further initializer named `pre`, this time specifying + that it should run before the initializer named `first`: - if (length === 0) { - return; - } else if (length === 1) { - method = args.shift(); - wait = 0; - } else if (length === 2) { - methodOrTarget = args[0]; - methodOrWait = args[1]; + ```javascript + Ember.Application.initializer({ + name: 'pre', + before: 'first', - if (typeof methodOrWait === 'function' || typeof methodOrTarget[methodOrWait] === 'function') { - target = args.shift(); - method = args.shift(); - wait = 0; - } else if (isCoercableNumber(methodOrWait)) { - method = args.shift(); - wait = args.shift(); - } else { - method = args.shift(); - wait = 0; + initialize: function(container, application) { + Ember.debug("Pre initializer!"); } - } else { - var last = args[args.length - 1]; + }); - if (isCoercableNumber(last)) { - wait = args.pop(); - } + // DEBUG: Pre initializer! + // DEBUG: First initializer! + // DEBUG: Second initializer! + ``` - methodOrTarget = args[0]; - methodOrArgs = args[1]; + Finally we add an initializer named `post`, specifying it should run after + both the `first` and the `second` initializers: - if (typeof methodOrArgs === 'function' || (typeof methodOrArgs === 'string' && - methodOrTarget !== null && - methodOrArgs in methodOrTarget)) { - target = args.shift(); - method = args.shift(); - } else { - method = args.shift(); + ```javascript + Ember.Application.initializer({ + name: 'post', + after: ['first', 'second'], + + initialize: function(container, application) { + Ember.debug("Post initializer!"); } - } + }); - var executeAt = (+new Date()) + parseInt(wait, 10); + // DEBUG: Pre initializer! + // DEBUG: First initializer! + // DEBUG: Second initializer! + // DEBUG: Post initializer! + ``` - if (typeof method === 'string') { - method = target[method]; - } + * `initialize` is a callback function that receives two arguments, `container` + and `application` on which you can operate. - function fn() { - method.apply(target, args); - } + Example of using `container` to preload data into the store: - // find position to insert - TODO: binary search - var i, l; - for (i = 0, l = timers.length; i < l; i += 2) { - if (executeAt < timers[i]) { break; } - } + ```javascript + Ember.Application.initializer({ + name: "preload-data", - timers.splice(i, 0, executeAt, fn); + initialize: function(container, application) { + var store = container.lookup('store:main'); + store.pushPayload(preloadedData); + } + }); + ``` - updateLaterTimer(self, executeAt, wait); + Example of using `application` to register an adapter: - return fn; - }, + ```javascript + Ember.Application.initializer({ + name: 'api-adapter', - throttle: function(target, method /* , args, wait, [immediate] */) { - var self = this, - args = arguments, - immediate = pop.call(args), - wait, - throttler, - index, - timer; + initialize: function(container, application) { + application.register('api-adapter:main', ApiAdapter); + } + }); + ``` - if (typeof immediate === "number" || typeof immediate === "string") { - wait = immediate; - immediate = true; - } else { - wait = pop.call(args); + @method initializer + @param initializer {Object} + */ + initializer: function(initializer) { + // If this is the first initializer being added to a subclass, we are going to reopen the class + // to make sure we have a new `initializers` object, which extends from the parent class' using + // prototypal inheritance. Without this, attempting to add initializers to the subclass would + // pollute the parent class as well as other subclasses. + if (this.superclass.initializers !== undefined && this.superclass.initializers === this.initializers) { + this.reopenClass({ + initializers: create(this.initializers) + }); } - wait = parseInt(wait, 10); + Ember.assert("The initializer '" + initializer.name + "' has already been registered", !this.initializers[initializer.name]); + Ember.assert("An initializer cannot be registered without an initialize function", canInvoke(initializer, 'initialize')); - index = findThrottler(target, method); - if (index > -1) { return throttlers[index]; } // throttled + this.initializers[initializer.name] = initializer; + }, - timer = global.setTimeout(function() { - if (!immediate) { - self.run.apply(self, args); - } - var index = findThrottler(target, method); - if (index > -1) { throttlers.splice(index, 1); } - }, wait); + /** + This creates a container with the default Ember naming conventions. + + It also configures the container: + + * registered views are created every time they are looked up (they are + not singletons) + * registered templates are not factories; the registered value is + returned directly. + * the router receives the application as its `namespace` property + * all controllers receive the router as their `target` and `controllers` + properties + * all controllers receive the application as their `namespace` property + * the application view receives the application controller as its + `controller` property + * the application view receives the application template as its + `defaultTemplate` property - if (immediate) { - self.run.apply(self, args); - } + @private + @method buildContainer + @static + @param {Ember.Application} namespace the application to build the + container for. + @return {Ember.Container} the built container + */ + buildContainer: function(namespace) { + var container = new Container(); - throttler = [target, method, timer]; + container.set = set; + container.resolver = resolverFor(namespace); + container.normalize = container.resolver.normalize; + container.describe = container.resolver.describe; + container.makeToString = container.resolver.makeToString; - throttlers.push(throttler); + container.optionsForType('component', { singleton: false }); + container.optionsForType('view', { singleton: false }); + container.optionsForType('template', { instantiate: false }); + container.optionsForType('helper', { instantiate: false }); - return throttler; - }, + container.register('application:main', namespace, { instantiate: false }); - debounce: function(target, method /* , args, wait, [immediate] */) { - var self = this, - args = arguments, - immediate = pop.call(args), - wait, - index, - debouncee, - timer; + container.register('controller:basic', Controller, { instantiate: false }); + container.register('controller:object', ObjectController, { instantiate: false }); + container.register('controller:array', ArrayController, { instantiate: false }); + container.register('route:basic', Route, { instantiate: false }); + container.register('event_dispatcher:main', EventDispatcher); - if (typeof immediate === "number" || typeof immediate === "string") { - wait = immediate; - immediate = false; - } else { - wait = pop.call(args); - } + container.register('router:main', Router); + container.injection('router:main', 'namespace', 'application:main'); - wait = parseInt(wait, 10); - // Remove debouncee - index = findDebouncee(target, method); + container.register('location:auto', AutoLocation); + container.register('location:hash', HashLocation); + container.register('location:history', HistoryLocation); + container.register('location:none', NoneLocation); - if (index > -1) { - debouncee = debouncees[index]; - debouncees.splice(index, 1); - clearTimeout(debouncee[2]); - } + container.injection('controller', 'target', 'router:main'); + container.injection('controller', 'namespace', 'application:main'); - timer = global.setTimeout(function() { - if (!immediate) { - self.run.apply(self, args); - } - var index = findDebouncee(target, method); - if (index > -1) { - debouncees.splice(index, 1); - } - }, wait); + container.register('-bucket-cache:main', BucketCache); + container.injection('router', '_bucketCache', '-bucket-cache:main'); + container.injection('route', '_bucketCache', '-bucket-cache:main'); + container.injection('controller', '_bucketCache', '-bucket-cache:main'); - if (immediate && index === -1) { - self.run.apply(self, args); - } + container.injection('route', 'router', 'router:main'); + container.injection('location', 'rootURL', '-location-setting:root-url'); - debouncee = [target, method, timer]; + // DEBUGGING + container.register('resolver-for-debugging:main', container.resolver.__resolver__, { instantiate: false }); + container.injection('container-debug-adapter:main', 'resolver', 'resolver-for-debugging:main'); + container.injection('data-adapter:main', 'containerDebugAdapter', 'container-debug-adapter:main'); + // Custom resolver authors may want to register their own ContainerDebugAdapter with this key - debouncees.push(debouncee); + // ES6TODO: resolve this via import once ember-application package is ES6'ed + if (!ContainerDebugAdapter) { ContainerDebugAdapter = requireModule('ember-extension-support/container_debug_adapter')['default']; } + container.register('container-debug-adapter:main', ContainerDebugAdapter); - return debouncee; - }, + return container; + } + }); - cancelTimers: function() { - var i, len; + /** + This function defines the default lookup rules for container lookups: - for (i = 0, len = throttlers.length; i < len; i++) { - clearTimeout(throttlers[i][2]); - } - throttlers = []; + * templates are looked up on `Ember.TEMPLATES` + * other names are looked up on the application after classifying the name. + For example, `controller:post` looks up `App.PostController` by default. + * if the default lookup fails, look for registered classes on the container - for (i = 0, len = debouncees.length; i < len; i++) { - clearTimeout(debouncees[i][2]); - } - debouncees = []; + This allows the application to register default injections in the container + that could be overridden by the normal naming convention. - if (laterTimer) { - clearTimeout(laterTimer); - laterTimer = null; - } - timers = []; + @private + @method resolverFor + @param {Ember.Namespace} namespace the namespace to look for classes + @return {*} the resolved value for a given lookup + */ + function resolverFor(namespace) { + if (namespace.get('resolver')) { + Ember.deprecate('Application.resolver is deprecated in favor of Application.Resolver', false); + } - if (autorun) { - clearTimeout(autorun); - autorun = null; - } - }, + var ResolverClass = namespace.get('resolver') || namespace.get('Resolver') || DefaultResolver; + var resolver = ResolverClass.create({ + namespace: namespace + }); - hasTimers: function() { - return !!timers.length || autorun; - }, + function resolve(fullName) { + return resolver.resolve(fullName); + } - cancel: function(timer) { - var timerType = typeof timer; + resolve.describe = function(fullName) { + return resolver.lookupDescription(fullName); + }; - if (timer && timerType === 'object' && timer.queue && timer.method) { // we're cancelling a deferOnce - return timer.queue.cancel(timer); - } else if (timerType === 'function') { // we're cancelling a setTimeout - for (var i = 0, l = timers.length; i < l; i += 2) { - if (timers[i + 1] === timer) { - timers.splice(i, 2); // remove the two elements - return true; - } - } - } else if (Object.prototype.toString.call(timer) === "[object Array]"){ // we're cancelling a throttle or debounce - return this._cancelItem(findThrottler, throttlers, timer) || - this._cancelItem(findDebouncee, debouncees, timer); + resolve.makeToString = function(factory, fullName) { + return resolver.makeToString(factory, fullName); + }; + + resolve.normalize = function(fullName) { + if (resolver.normalize) { + return resolver.normalize(fullName); } else { - return; // timer was null or not a timer + Ember.deprecate('The Resolver should now provide a \'normalize\' function', false); + return fullName; } - }, - - _cancelItem: function(findMethod, array, timer){ - var item, - index; + }; - if (timer.length < 3) { return false; } + resolve.__resolver__ = resolver; - index = findMethod(timer[0], timer[1]); + return resolve; + } - if(index > -1) { - - item = array[index]; + __exports__["default"] = Application; + }); +define("ember-application/system/dag", + ["ember-metal/error","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var EmberError = __dependency1__["default"]; - if(item[2] === timer[2]){ - array.splice(index, 1); - clearTimeout(timer[2]); - return true; - } - } + function visit(vertex, fn, visited, path) { + var name = vertex.name; + var vertices = vertex.incoming; + var names = vertex.incomingNames; + var len = names.length; + var i; - return false; + if (!visited) { + visited = {}; } + if (!path) { + path = []; + } + if (visited.hasOwnProperty(name)) { + return; + } + path.push(name); + visited[name] = true; + for (i = 0; i < len; i++) { + visit(vertices[names[i]], fn, visited, path); + } + fn(vertex, path); + path.pop(); + } - }; - - Backburner.prototype.schedule = Backburner.prototype.defer; - Backburner.prototype.scheduleOnce = Backburner.prototype.deferOnce; - Backburner.prototype.later = Backburner.prototype.setTimeout; - function createAutorun(backburner) { - backburner.begin(); - autorun = global.setTimeout(function() { - autorun = null; - backburner.end(); - }); + /** + * DAG stands for Directed acyclic graph. + * + * It is used to build a graph of dependencies checking that there isn't circular + * dependencies. p.e Registering initializers with a certain precedence order. + * + * @class DAG + * @constructor + */ + function DAG() { + this.names = []; + this.vertices = {}; } - function updateLaterTimer(self, executeAt, wait) { - if (!laterTimer || executeAt < laterTimerExpiresAt) { - if (laterTimer) { - clearTimeout(laterTimer); - } - laterTimer = global.setTimeout(function() { - laterTimer = null; - laterTimerExpiresAt = null; - executeTimers(self); - }, wait); - laterTimerExpiresAt = executeAt; + /** + * Adds a vertex entry to the graph unless it is already added. + * + * @private + * @method add + * @param {String} name The name of the vertex to add + */ + DAG.prototype.add = function(name) { + if (!name) { return; } + if (this.vertices.hasOwnProperty(name)) { + return this.vertices[name]; } - } + var vertex = { + name: name, incoming: {}, incomingNames: [], hasOutgoing: false, value: null + }; + this.vertices[name] = vertex; + this.names.push(name); + return vertex; + }; - function executeTimers(self) { - var now = +new Date(), - time, fns, i, l; + /** + * Adds a vertex to the graph and sets its value. + * + * @private + * @method map + * @param {String} name The name of the vertex. + * @param value The value to put in the vertex. + */ + DAG.prototype.map = function(name, value) { + this.add(name).value = value; + }; - self.run(function() { - // TODO: binary search - for (i = 0, l = timers.length; i < l; i += 2) { - time = timers[i]; - if (time > now) { break; } + /** + * Connects the vertices with the given names, adding them to the graph if + * necesary, only if this does not produce is any circular dependency. + * + * @private + * @method addEdge + * @param {String} fromName The name the vertex where the edge starts. + * @param {String} toName The name the vertex where the edge ends. + */ + DAG.prototype.addEdge = function(fromName, toName) { + if (!fromName || !toName || fromName === toName) { + return; + } + var from = this.add(fromName), to = this.add(toName); + if (to.incoming.hasOwnProperty(fromName)) { + return; + } + function checkCycle(vertex, path) { + if (vertex.name === toName) { + throw new EmberError("cycle detected: " + toName + " <- " + path.join(" <- ")); } + } + visit(from, checkCycle); + from.hasOutgoing = true; + to.incoming[fromName] = from; + to.incomingNames.push(fromName); + }; - fns = timers.splice(0, i); + /** + * Visits all the vertex of the graph calling the given function with each one, + * ensuring that the vertices are visited respecting their precedence. + * + * @method topsort + * @param {Function} fn The function to be invoked on each vertex. + */ + DAG.prototype.topsort = function(fn) { + var visited = {}; + var vertices = this.vertices; + var names = this.names; + var len = names.length; + var i, vertex; - for (i = 1, l = fns.length; i < l; i += 2) { - self.schedule(self.options.defaultQueue, null, fns[i]); + for (i = 0; i < len; i++) { + vertex = vertices[names[i]]; + if (!vertex.hasOutgoing) { + visit(vertex, fn, visited); } - }); - - if (timers.length) { - updateLaterTimer(self, timers[0], timers[0] - now); } - } - - function findDebouncee(target, method) { - var debouncee, - index = -1; + }; - for (var i = 0, l = debouncees.length; i < l; i++) { - debouncee = debouncees[i]; - if (debouncee[0] === target && debouncee[1] === method) { - index = i; - break; + /** + * Adds a vertex with the given name and value to the graph and joins it with the + * vertices referenced in _before_ and _after_. If there isn't vertices with those + * names, they are added too. + * + * If either _before_ or _after_ are falsy/empty, the added vertex will not have + * an incoming/outgoing edge. + * + * @method addEdges + * @param {String} name The name of the vertex to be added. + * @param value The value of that vertex. + * @param before An string or array of strings with the names of the vertices before + * which this vertex must be visited. + * @param after An string or array of strings with the names of the vertex after + * which this vertex must be visited. + * + */ + DAG.prototype.addEdges = function(name, value, before, after) { + var i; + this.map(name, value); + if (before) { + if (typeof before === 'string') { + this.addEdge(name, before); + } else { + for (i = 0; i < before.length; i++) { + this.addEdge(name, before[i]); + } } } - - return index; - } - - function findThrottler(target, method) { - var throttler, - index = -1; - - for (var i = 0, l = throttlers.length; i < l; i++) { - throttler = throttlers[i]; - if (throttler[0] === target && throttler[1] === method) { - index = i; - break; + if (after) { + if (typeof after === 'string') { + this.addEdge(after, name); + } else { + for (i = 0; i < after.length; i++) { + this.addEdge(after[i], name); + } } } + }; - return index; - } - - __exports__.Backburner = Backburner; + __exports__["default"] = DAG; }); -})(); - - +define("ember-application/system/resolver", + ["ember-metal/core","ember-metal/property_get","ember-metal/logger","ember-runtime/system/string","ember-runtime/system/object","ember-runtime/system/namespace","ember-handlebars","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-application + */ -(function() { -var onBegin = function(current) { - Ember.run.currentRunLoop = current; -}; + var Ember = __dependency1__["default"]; + // Ember.TEMPLATES, Ember.assert + var get = __dependency2__.get; + var Logger = __dependency3__["default"]; + var classify = __dependency4__.classify; + var capitalize = __dependency4__.capitalize; + var decamelize = __dependency4__.decamelize; + var EmberObject = __dependency5__["default"]; + var Namespace = __dependency6__["default"]; + var EmberHandlebars = __dependency7__["default"]; + + var Resolver = EmberObject.extend({ + /** + This will be set to the Application instance when it is + created. -var onEnd = function(current, next) { - Ember.run.currentRunLoop = next; -}; + @property namespace + */ + namespace: null, + normalize: Ember.required(Function), + resolve: Ember.required(Function), + parseName: Ember.required(Function), + lookupDescription: Ember.required(Function), + makeToString: Ember.required(Function), + resolveOther: Ember.required(Function), + _logLookup: Ember.required(Function) + }); + __exports__.Resolver = Resolver; + /** + The DefaultResolver defines the default lookup rules to resolve + container lookups before consulting the container for registered + items: -var Backburner = requireModule('backburner').Backburner, - backburner = new Backburner(['sync', 'actions', 'destroy'], { - sync: { - before: Ember.beginPropertyChanges, - after: Ember.endPropertyChanges - }, - defaultQueue: 'actions', - onBegin: onBegin, - onEnd: onEnd - }), - slice = [].slice, - concat = [].concat; - -// .......................................................... -// Ember.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. - - ```javascript - Ember.run(function() { - // code to be execute within a RunLoop - }); - ``` - - @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. -*/ -Ember.run = function() { - if (Ember.onerror) { - return onerror(arguments); - } else { - return backburner.run.apply(backburner, arguments); - } -}; + * templates are looked up on `Ember.TEMPLATES` + * other names are looked up on the application after converting + the name. For example, `controller:post` looks up + `App.PostController` by default. + * there are some nuances (see examples below) -function onerror(args) { - try { - return backburner.run.apply(backburner, args); - } catch(error) { - Ember.onerror(error); - } -} -/** - 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. + ### How Resolving Works - Please note: This is not for normal usage, and should be used sparingly. + The container calls this object's `resolve` method with the + `fullName` argument. - If invoked when not within a run loop: + It first parses the fullName into an object using `parseName`. - ```javascript - Ember.run.join(function() { - // creates a new run-loop - }); - ``` + Then it checks for the presence of a type-specific instance + method of the form `resolve[Type]` and calls it if it exists. + For example if it was resolving 'template:post', it would call + the `resolveTemplate` method. - Alternatively, if called within an existing run loop: + Its last resort is to call the `resolveOther` method. - ```javascript - Ember.run(function() { - // creates a new run-loop - Ember.run.join(function() { - // joins with the existing run-loop, and queues for invocation on - // the existing run-loops action queue. - }); - }); - ``` - - @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. -*/ -Ember.run.join = function(target, method /* args */) { - if (!Ember.run.currentRunLoop) { - return Ember.run.apply(Ember.run, arguments); - } + The methods of this object are designed to be easy to override + in a subclass. For example, you could enhance how a template + is resolved like so: - var args = slice.call(arguments); - args.unshift('actions'); - Ember.run.schedule.apply(Ember.run, args); -}; + ```javascript + App = Ember.Application.create({ + Resolver: Ember.DefaultResolver.extend({ + resolveTemplate: function(parsedName) { + var resolvedTemplate = this._super(parsedName); + if (resolvedTemplate) { return resolvedTemplate; } + return Ember.TEMPLATES['not_found']; + } + }) + }); + ``` -/** - Provides a useful utility for when integrating with non-Ember libraries - that provide asynchronous callbacks. + Some examples of how names are resolved: - Ember utilizes a run-loop to batch and coalesce changes. This works by - marking the start and end of Ember-related Javascript execution. + ``` + 'template:post' //=> Ember.TEMPLATES['post'] + 'template:posts/byline' //=> Ember.TEMPLATES['posts/byline'] + 'template:posts.byline' //=> Ember.TEMPLATES['posts/byline'] + 'template:blogPost' //=> Ember.TEMPLATES['blogPost'] + // OR + // Ember.TEMPLATES['blog_post'] + 'controller:post' //=> App.PostController + 'controller:posts.index' //=> App.PostsIndexController + 'controller:blog/post' //=> Blog.PostController + 'controller:basic' //=> Ember.Controller + 'route:post' //=> App.PostRoute + 'route:posts.index' //=> App.PostsIndexRoute + 'route:blog/post' //=> Blog.PostRoute + 'route:basic' //=> Ember.Route + 'view:post' //=> App.PostView + 'view:posts.index' //=> App.PostsIndexView + 'view:blog/post' //=> Blog.PostView + 'view:basic' //=> Ember.View + 'foo:post' //=> App.PostFoo + 'model:post' //=> App.Post + ``` - When using events such as a View's click handler, Ember wraps the event - handler in a run-loop, but when integrating with non-Ember libraries this - can be tedious. + @class DefaultResolver + @namespace Ember + @extends Ember.Object + */ - For example, the following is rather verbose but is the correct way to combine - third-party events and Ember code. + __exports__["default"] = EmberObject.extend({ + /** + This will be set to the Application instance when it is + created. - ```javascript - var that = this; - jQuery(window).on('resize', function(){ - Ember.run(function(){ - that.handleResize(); - }); - }); - ``` - - To reduce the boilerplate, the following can be used to construct a - run-loop-wrapped callback handler. - - ```javascript - jQuery(window).on('resize', Ember.run.bind(this, this.handleResize)); - ``` - - @method bind - @namespace Ember.run - @param {Object} [target] target of method to call - @param {Function|String} method Method to invoke. - May be a function or a string. If you pass a string - then it will be looked up on the passed target. - @param {Object} [args*] Any additional arguments you wish to pass to the method. - @return {Object} return value from invoking the passed function. Please note, - when called within an existing loop, no return value is possible. -*/ -Ember.run.bind = function(target, method /* args*/) { - var args = slice.call(arguments); - return function() { - return Ember.run.join.apply(Ember.run, args.concat(slice.call(arguments))); - }; -}; - -Ember.run.backburner = backburner; - -var run = Ember.run; - -Ember.run.currentRunLoop = null; - -Ember.run.queues = backburner.queueNames; - -/** - Begins a new RunLoop. Any deferred actions invoked after the begin will - be buffered until you invoke a matching call to `Ember.run.end()`. This is - a lower-level way to use a RunLoop instead of using `Ember.run()`. - - ```javascript - Ember.run.begin(); - // code to be execute within a RunLoop - Ember.run.end(); - ``` - - @method begin - @return {void} -*/ -Ember.run.begin = function() { - backburner.begin(); -}; - -/** - Ends a RunLoop. This must be called sometime after you call - `Ember.run.begin()` to flush any deferred actions. This is a lower-level way - to use a RunLoop instead of using `Ember.run()`. - - ```javascript - Ember.run.begin(); - // code to be execute within a RunLoop - Ember.run.end(); - ``` - - @method end - @return {void} -*/ -Ember.run.end = function() { - backburner.end(); -}; - -/** - 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'] -*/ - -/** - 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 `Ember.run.queues` property. - - ```javascript - Ember.run.schedule('sync', this, function() { - // this will be executed in the first RunLoop queue, when bindings are synced - console.log("scheduled on sync queue"); - }); + @property namespace + */ + namespace: null, - Ember.run.schedule('actions', this, function() { - // this will be executed in the 'actions' queue, after bindings have synced. - console.log("scheduled on actions queue"); - }); + normalize: function(fullName) { + var split = fullName.split(':', 2), + type = split[0], + name = split[1]; - // 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 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} -*/ -Ember.run.schedule = function(queue, target, method) { - checkAutoRun(); - backburner.schedule.apply(backburner, arguments); -}; - -// Used by global test teardown -Ember.run.hasScheduledTimers = function() { - return backburner.hasTimers(); -}; - -// Used by global test teardown -Ember.run.cancelTimers = function () { - backburner.cancelTimers(); -}; - -/** - 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. - - 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). - - ```javascript - Ember.run.sync(); - ``` - - @method sync - @return {void} -*/ -Ember.run.sync = function() { - if (backburner.currentInstance) { - backburner.currentInstance.queues.sync.flush(); - } -}; - -/** - Invokes the passed target/method and optional arguments after a specified - period if time. The last parameter of this method must always be a number - of milliseconds. - - 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.run.later(myContext, function() { - // code here will execute within a RunLoop in about 500ms with this == myContext - }, 500); - ``` - - @method later - @param {Object} [target] target of method to invoke - @param {Function|String} method The method to invoke. - If you pass a string it will be resolved on the - target at the time the method is invoked. - @param {Object} [args*] Optional arguments to pass to the timeout. - @param {Number} wait Number of milliseconds to wait. - @return {String} a string you can use to cancel the timer in - `Ember.run.cancel` later. -*/ -Ember.run.later = function(target, method) { - return backburner.later.apply(backburner, arguments); -}; - -/** - Schedule a function to run one time during the current RunLoop. This is equivalent - to calling `scheduleOnce` with the "actions" queue. - - @method once - @param {Object} [target] The target of the method to invoke. - @param {Function|String} method The method to invoke. - If you pass a string it will be resolved on the - target at the time the method is invoked. - @param {Object} [args*] Optional arguments to pass to the timeout. - @return {Object} Timer information for use in cancelling, see `Ember.run.cancel`. -*/ -Ember.run.once = function(target, method) { - checkAutoRun(); - var args = slice.call(arguments); - args.unshift('actions'); - return backburner.scheduleOnce.apply(backburner, args); -}; - -/** - Schedules a function to run one time in a given queue of the current RunLoop. - Calling this method with the same queue/target/method combination will have - no effect (past the initial call). - - Note that although you can pass optional arguments these will not be - considered when looking for duplicates. New arguments will replace previous - calls. - - ```javascript - Ember.run(function() { - var sayHi = function() { console.log('hi'); } - Ember.run.scheduleOnce('afterRender', myContext, sayHi); - Ember.run.scheduleOnce('afterRender', myContext, sayHi); - // sayHi will only be executed once, in the afterRender queue of the RunLoop - }); - ``` + Ember.assert("Tried to normalize a container name without a colon (:) in it. You probably tried to lookup a name that did not contain a type, a colon, and a name. A proper lookup name would be `view:post`.", split.length === 2); - Also note that passing an anonymous function to `Ember.run.scheduleOnce` will - not prevent additional calls with an identical anonymous function from - scheduling the items multiple times, e.g.: + if (type !== 'template') { + var result = name; - ```javascript - function scheduleIt() { - Ember.run.scheduleOnce('actions', myContext, function() { console.log("Closure"); }); - } - scheduleIt(); - scheduleIt(); - // "Closure" will print twice, even though we're using `Ember.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 `Ember.run.queues` - - @method scheduleOnce - @param {String} [queue] The name of the queue to schedule against. Default queues are 'sync' and 'actions'. - @param {Object} [target] The target of the method to invoke. - @param {Function|String} method The method to invoke. - If you pass a string it will be resolved on the - target at the time the method is invoked. - @param {Object} [args*] Optional arguments to pass to the timeout. - @return {Object} Timer information for use in cancelling, see `Ember.run.cancel`. -*/ -Ember.run.scheduleOnce = function(queue, target, method) { - checkAutoRun(); - return backburner.scheduleOnce.apply(backburner, arguments); -}; - -/** - Schedules an item to run from within a separate run loop, after - control has been returned to the system. This is equivalent to calling - `Ember.run.later` with a wait time of 1ms. - - ```javascript - Ember.run.next(myContext, function() { - // code to be executed in the next run loop, - // which will be scheduled after the current one - }); - ``` - - Multiple operations scheduled with `Ember.run.next` will coalesce - into the same later run loop, along with any other operations - scheduled by `Ember.run.later` that expire right around the same - time that `Ember.run.next` operations will fire. - - Note that there are often alternatives to using `Ember.run.next`. - For instance, if you'd like to schedule an operation to happen - after all DOM element operations have completed within the current - run loop, you can make use of the `afterRender` run loop queue (added - by the `ember-views` package, along with the preceding `render` queue - where all the DOM element operations happen). Example: - - ```javascript - App.MyCollectionView = Ember.CollectionView.extend({ - didInsertElement: function() { - Ember.run.scheduleOnce('afterRender', this, 'processChildElements'); - }, - processChildElements: function() { - // ... do something with collectionView's child view - // elements after they've finished rendering, which - // can't be done within the CollectionView's - // `didInsertElement` hook because that gets run - // before the child elements have been added to the DOM. - } - }); - ``` - - One benefit of the above approach compared to using `Ember.run.next` is - that you will be able to perform DOM/CSS operations before unprocessed - elements are rendered to the screen, which may prevent flickering or - other artifacts caused by delaying processing until after rendering. - - The other major benefit to the above approach is that `Ember.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 `Ember.run.next`. - - @method next - @param {Object} [target] target of method to invoke - @param {Function|String} method The method to invoke. - If you pass a string it will be resolved on the - target at the time the method is invoked. - @param {Object} [args*] Optional arguments to pass to the timeout. - @return {Object} Timer information for use in cancelling, see `Ember.run.cancel`. -*/ -Ember.run.next = function() { - var args = slice.call(arguments); - args.push(1); - return backburner.later.apply(backburner, args); -}; - -/** - Cancels a scheduled item. Must be a value returned by `Ember.run.later()`, - `Ember.run.once()`, `Ember.run.next()`, `Ember.run.debounce()`, or - `Ember.run.throttle()`. - - ```javascript - var runNext = Ember.run.next(myContext, function() { - // will not be executed - }); - Ember.run.cancel(runNext); + if (result.indexOf('.') > -1) { + result = result.replace(/\.(.)/g, function(m) { return m.charAt(1).toUpperCase(); }); + } - var runLater = Ember.run.later(myContext, function() { - // will not be executed - }, 500); - Ember.run.cancel(runLater); + if (name.indexOf('_') > -1) { + result = result.replace(/_(.)/g, function(m) { return m.charAt(1).toUpperCase(); }); + } - var runOnce = Ember.run.once(myContext, function() { - // will not be executed - }); - Ember.run.cancel(runOnce); - - var throttle = Ember.run.throttle(myContext, function() { - // will not be executed - }, 1, false); - Ember.run.cancel(throttle); - - var debounce = Ember.run.debounce(myContext, function() { - // will not be executed - }, 1); - Ember.run.cancel(debounce); - - var debounceImmediate = Ember.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 - Ember.run.cancel(debounceImmediate); - ``` - - @method cancel - @param {Object} timer Timer object to cancel - @return {Boolean} true if cancelled or false/undefined if it wasn't found -*/ -Ember.run.cancel = function(timer) { - return backburner.cancel(timer); -}; - -/** - Delay calling the target method until the debounce period has elapsed - with no additional debounce calls. If `debounce` is called again before - the specified time has elapsed, the timer is reset and the entire period - must pass again before the target method is called. - - This method should be used when an event may be called multiple times - but the action should only be called once when the event is done firing. - A common example is for scroll events where you only want updates to - happen once scrolling has ceased. - - ```javascript - var myFunc = function() { console.log(this.name + ' ran.'); }; - var myContext = {name: 'debounce'}; - - Ember.run.debounce(myContext, myFunc, 150); - - // less than 150ms passes - - Ember.run.debounce(myContext, myFunc, 150); - - // 150ms passes - // myFunc is invoked with context myContext - // console logs 'debounce ran.' one time. - ``` - - Immediate allows you to run the function immediately, but debounce - other calls for this function until the wait time has elapsed. If - `debounce` is called again before the specified time has elapsed, - the timer is reset and the entire period must pass again before - the method can be called again. - - ```javascript - var myFunc = function() { console.log(this.name + ' ran.'); }; - var myContext = {name: 'debounce'}; - - Ember.run.debounce(myContext, myFunc, 150, true); - - // console logs 'debounce ran.' one time immediately. - // 100ms passes - - Ember.run.debounce(myContext, myFunc, 150, true); - - // 150ms passes and nothing else is logged to the console and - // the debouncee is no longer being watched - - Ember.run.debounce(myContext, myFunc, 150, true); - - // console logs 'debounce ran.' one time immediately. - // 150ms passes and nothing else is logged tot he console and - // the debouncee is no longer being watched - - ``` - - @method debounce - @param {Object} [target] target of method to invoke - @param {Function|String} method The method to invoke. - May be a function or a string. If you pass a string - then it will be looked up on the passed target. - @param {Object} [args*] Optional arguments to pass to the timeout. - @param {Number} wait Number of milliseconds to wait. - @param {Boolean} immediate Trigger the function on the leading instead - of the trailing edge of the wait interval. Defaults to false. - @return {Array} Timer information for use in cancelling, see `Ember.run.cancel`. -*/ -Ember.run.debounce = function() { - return backburner.debounce.apply(backburner, arguments); -}; - -/** - Ensure that the target method is never called more frequently than - the specified spacing period. - - ```javascript - var myFunc = function() { console.log(this.name + ' ran.'); }; - var myContext = {name: 'throttle'}; - - Ember.run.throttle(myContext, myFunc, 150); - // myFunc is invoked with context myContext - - // 50ms passes - Ember.run.throttle(myContext, myFunc, 150); - - // 50ms passes - Ember.run.throttle(myContext, myFunc, 150); - - // 150ms passes - Ember.run.throttle(myContext, myFunc, 150); - // myFunc is invoked with context myContext - // console logs 'throttle ran.' twice, 250ms apart. - ``` - - @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. - @return {Array} Timer information for use in cancelling, see `Ember.run.cancel`. -*/ -Ember.run.throttle = function() { - return backburner.throttle.apply(backburner, arguments); -}; - -// Make sure it's not an autorun during testing -function checkAutoRun() { - if (!Ember.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 Ember.run", !Ember.testing); - } -} + return type + ':' + result; + } else { + return fullName; + } + }, -})(); + /** + This method is called via the container's resolver method. + It parses the provided `fullName` and then looks up and + returns the appropriate template or class. + @method resolve + @param {String} fullName the lookup string + @return {Object} the resolved factory + */ + resolve: function(fullName) { + var parsedName = this.parseName(fullName), + resolveMethodName = parsedName.resolveMethodName, + resolved; -(function() { -// Ember.Logger -// get -// set -// guidFor, meta -// addObserver, removeObserver -// Ember.run.schedule -/** -@module ember-metal -*/ - -// .......................................................... -// CONSTANTS -// - -/** - Debug parameter you can turn on. This will log all bindings that fire to - the console. This should be disabled in production code. Note that you - can also enable this from the console or temporarily. - - @property LOG_BINDINGS - @for Ember - @type Boolean - @default false -*/ -Ember.LOG_BINDINGS = false || !!Ember.ENV.LOG_BINDINGS; - -var get = Ember.get, - set = Ember.set, - guidFor = Ember.guidFor, - IS_GLOBAL = /^([A-Z$]|([0-9][A-Z$]))/; - -/** - Returns true if the provided path is global (e.g., `MyApp.fooController.bar`) - instead of local (`foo.bar.baz`). - - @method isGlobalPath - @for Ember - @private - @param {String} path - @return Boolean -*/ -var isGlobalPath = Ember.isGlobalPath = function(path) { - return IS_GLOBAL.test(path); -}; - -function getWithGlobals(obj, path) { - return get(isGlobalPath(path) ? Ember.lookup : obj, path); -} - -// .......................................................... -// BINDING -// - -var Binding = function(toPath, fromPath) { - this._direction = 'fwd'; - this._from = fromPath; - this._to = toPath; - this._directionMap = Ember.Map.create(); -}; - -/** -@class Binding -@namespace Ember -*/ - -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 - // - - /** - 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 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. - - @method from - @param {String} path the property path to connect to - @return {Ember.Binding} `this` - */ - from: function(path) { - this._from = path; - return this; - }, - - /** - 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. - - 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. - - @method to - @param {String|Tuple} path A property path or tuple - @return {Ember.Binding} `this` - */ - to: function(path) { - this._to = path; - return this; - }, - - /** - 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. - - @method oneWay - @return {Ember.Binding} `this` - */ - oneWay: function() { - this._oneWay = true; - return this; - }, - - /** - @method toString - @return {String} string representation of binding - */ - toString: function() { - var oneWay = this._oneWay ? '[oneWay]' : ''; - return "Ember.Binding<" + guidFor(this) + ">(" + this._from + " -> " + this._to + ")" + oneWay; - }, - - // .......................................................... - // CONNECT AND SYNC - // - - /** - 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. - - @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); - - var fromPath = this._from, toPath = this._to; - Ember.trySet(obj, toPath, getWithGlobals(obj, fromPath)); - - // add an observer on the object to be notified when the binding should be updated - Ember.addObserver(obj, fromPath, this, this.fromDidChange); - - // if the binding is a two-way binding, also set up an observer on the target - if (!this._oneWay) { Ember.addObserver(obj, toPath, this, this.toDidChange); } - - this._readyToSync = true; - - return this; - }, - - /** - Disconnects the binding instance. Changes will no longer be relayed. You - will not usually need to call this method. - - @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); - - var twoWay = !this._oneWay; - - // remove an observer on the object so we're no longer notified of - // changes that should update bindings. - Ember.removeObserver(obj, this._from, this, this.fromDidChange); - - // if the binding is two-way, remove the observer from the target as well - if (twoWay) { Ember.removeObserver(obj, this._to, this, this.toDidChange); } - - this._readyToSync = false; // disable scheduled syncs... - return this; - }, - - // .......................................................... - // PRIVATE - // - - /* called when the from side changes */ - fromDidChange: function(target) { - this._scheduleSync(target, 'fwd'); - }, - - /* called when the to side changes */ - toDidChange: function(target) { - this._scheduleSync(target, 'back'); - }, - - _scheduleSync: function(obj, dir) { - var directionMap = this._directionMap; - var existingDir = directionMap.get(obj); - - // if we haven't scheduled the binding yet, schedule it - if (!existingDir) { - Ember.run.schedule('sync', this, this._sync, obj); - directionMap.set(obj, dir); - } - - // If both a 'back' and 'fwd' sync have been scheduled on the same object, - // default to a 'fwd' sync so that it remains deterministic. - if (existingDir === 'back' && dir === 'fwd') { - directionMap.set(obj, 'fwd'); - } - }, - - _sync: function(obj) { - var log = Ember.LOG_BINDINGS; + if (!(parsedName.name && parsedName.type)) { + throw new TypeError('Invalid fullName: `' + fullName + '`, must be of the form `type:name` '); + } - // don't synchronize destroyed objects or disconnected bindings - if (obj.isDestroyed || !this._readyToSync) { return; } + if (this[resolveMethodName]) { + resolved = this[resolveMethodName](parsedName); + } - // get the direction of the binding for the object we are - // synchronizing from - var directionMap = this._directionMap; - var direction = directionMap.get(obj); + if (!resolved) { + resolved = this.resolveOther(parsedName); + } - var fromPath = this._from, toPath = this._to; + if (parsedName.root && parsedName.root.LOG_RESOLVER) { + this._logLookup(resolved, parsedName); + } - directionMap.remove(obj); + return resolved; + }, + /** + Convert the string name of the form 'type:name' to + a Javascript object with the parsed aspects of the name + broken out. - // 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) { - Ember.trySet(obj, toPath, fromValue); - } else { - Ember._suspendObserver(obj, toPath, this, this.toDidChange, function () { - Ember.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); - } - Ember._suspendObserver(obj, fromPath, this, this.fromDidChange, function () { - Ember.trySet(Ember.isGlobalPath(fromPath) ? Ember.lookup : obj, fromPath, toValue); - }); - } - } + @protected + @param {String} fullName the lookup string + @method parseName + */ + parseName: function(fullName) { + var nameParts = fullName.split(':'), + type = nameParts[0], fullNameWithoutType = nameParts[1], + name = fullNameWithoutType, + namespace = get(this, 'namespace'), + root = namespace; + + if (type !== 'template' && name.indexOf('/') !== -1) { + var parts = name.split('/'); + name = parts[parts.length - 1]; + var namespaceName = capitalize(parts.slice(0, -1).join('.')); + root = Namespace.byName(namespaceName); + + Ember.assert('You are looking for a ' + name + ' ' + type + ' in the ' + namespaceName + ' namespace, but the namespace could not be found', root); + } + + return { + fullName: fullName, + type: type, + fullNameWithoutType: fullNameWithoutType, + name: name, + root: root, + resolveMethodName: 'resolve' + classify(type) + }; + }, -}; + /** + Returns a human-readable description for a fullName. Used by the + Application namespace in assertions to describe the + precise name of the class that Ember is looking for, rather than + container keys. + + @protected + @param {String} fullName the lookup string + @method lookupDescription + */ + lookupDescription: function(fullName) { + var parsedName = this.parseName(fullName); -function mixinProperties(to, from) { - for (var key in from) { - if (from.hasOwnProperty(key)) { - to[key] = from[key]; - } - } -} - -mixinProperties(Binding, { - - /* - See `Ember.Binding.from`. - - @method from - @static - */ - from: function() { - var C = this, binding = new C(); - return binding.from.apply(binding, arguments); - }, - - /* - See `Ember.Binding.to`. - - @method to - @static - */ - to: function() { - var C = this, binding = new C(); - return binding.to.apply(binding, arguments); - }, - - /** - Creates a new Binding instance and makes it apply in a single direction. - A one-way binding will relay changes on the `from` side object (supplied - as the `from` argument) the `to` side, but not the other way around. - This means that if you change the "to" side directly, the "from" side may have - a different value. - - See `Binding.oneWay`. - - @method oneWay - @param {String} from from path. - @param {Boolean} [flag] (Optional) passing nothing here will make the - binding `oneWay`. You can instead pass `false` to disable `oneWay`, making the - binding two way again. - @return {Ember.Binding} `this` - */ - oneWay: function(from, flag) { - var C = this, binding = new C(null, from); - return binding.oneWay(flag); - } + if (parsedName.type === 'template') { + return 'template at ' + parsedName.fullNameWithoutType.replace(/\./g, '/'); + } -}); + var description = parsedName.root + '.' + classify(parsedName.name); + if (parsedName.type !== 'model') { description += classify(parsedName.type); } -/** - 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. + return description; + }, - ## Automatic Creation of Bindings with `/^*Binding/`-named Properties + makeToString: function(factory, fullName) { + return factory.toString(); + }, + /** + Given a parseName object (output from `parseName`), apply + the conventions expected by `Ember.Router` - You do not usually create Binding objects directly but instead describe - bindings in your class or object definition using automatic binding - detection. + @protected + @param {Object} parsedName a parseName object with the parsed + fullName lookup string + @method useRouterNaming + */ + useRouterNaming: function(parsedName) { + parsedName.name = parsedName.name.replace(/\./g, '_'); + if (parsedName.name === 'basic') { + parsedName.name = ''; + } + }, + /** + Look up the template in Ember.TEMPLATES - Properties ending in a `Binding` suffix will be converted to `Ember.Binding` - instances. The value of this property should be a string representing a path - to another object or a custom binding instanced created using Binding helpers - (see "One Way Bindings"): + @protected + @param {Object} parsedName a parseName object with the parsed + fullName lookup string + @method resolveTemplate + */ + resolveTemplate: function(parsedName) { + var templateName = parsedName.fullNameWithoutType.replace(/\./g, '/'); - ``` - valueBinding: "MyApp.someController.title" - ``` + if (Ember.TEMPLATES[templateName]) { + return Ember.TEMPLATES[templateName]; + } - 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. + templateName = decamelize(templateName); + if (Ember.TEMPLATES[templateName]) { + return Ember.TEMPLATES[templateName]; + } + }, + /** + Lookup the view using `resolveOther` - ## One Way Bindings + @protected + @param {Object} parsedName a parseName object with the parsed + fullName lookup string + @method resolveView + */ + resolveView: function(parsedName) { + this.useRouterNaming(parsedName); + return this.resolveOther(parsedName); + }, + /** + Lookup the controller using `resolveOther` - 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: + @protected + @param {Object} parsedName a parseName object with the parsed + fullName lookup string + @method resolveController + */ + resolveController: function(parsedName) { + this.useRouterNaming(parsedName); + return this.resolveOther(parsedName); + }, + /** + Lookup the route using `resolveOther` - ``` - bigTitlesBinding: Ember.Binding.oneWay("MyApp.preferencesController.bigTitles") - ``` + @protected + @param {Object} parsedName a parseName object with the parsed + fullName lookup string + @method resolveRoute + */ + resolveRoute: function(parsedName) { + this.useRouterNaming(parsedName); + return this.resolveOther(parsedName); + }, - 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`. + /** + Lookup the model on the Application namespace - 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. + @protected + @param {Object} parsedName a parseName object with the parsed + fullName lookup string + @method resolveModel + */ + resolveModel: function(parsedName) { + var className = classify(parsedName.name); + var factory = get(parsedName.root, className); - 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). + if (factory) { return factory; } + }, + /** + Look up the specified object (from parsedName) on the appropriate + namespace (usually on the Application) - ## Adding Bindings Manually + @protected + @param {Object} parsedName a parseName object with the parsed + fullName lookup string + @method resolveHelper + */ + resolveHelper: function(parsedName) { + return this.resolveOther(parsedName) || EmberHandlebars.helpers[parsedName.fullNameWithoutType]; + }, + /** + Look up the specified object (from parsedName) on the appropriate + namespace (usually on the Application) - 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. + @protected + @param {Object} parsedName a parseName object with the parsed + fullName lookup string + @method resolveOther + */ + resolveOther: function(parsedName) { + var className = classify(parsedName.name) + classify(parsedName.type); + var factory = get(parsedName.root, className); + if (factory) { return factory; } + }, - 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. + /** + @method _logLookup + @param {Boolean} found + @param {Object} parsedName + @private + */ + _logLookup: function(found, parsedName) { + var symbol, padding; - When you define a custom binding, you are usually describing the property - you want to bind from (such as `MyApp.someController.value` in the examples - above). When your object is created, it will automatically assign the value - you want to bind `to` based on the name of your binding key. In the - examples above, during init, Ember objects will effectively call - something like this on your binding: + if (found) { symbol = '[✓]'; } + else { symbol = '[ ]'; } - ```javascript - binding = Ember.Binding.from(this.valueBinding).to("value"); - ``` + if (parsedName.fullName.length > 60) { + padding = '.'; + } else { + padding = new Array(60 - parsedName.fullName.length).join('.'); + } - 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: + Logger.info(symbol, parsedName.fullName, padding, this.lookupDescription(parsedName.fullName)); + } + }); + }); +define("ember-debug", + ["ember-metal/core","ember-metal/error","ember-metal/logger"], + function(__dependency1__, __dependency2__, __dependency3__) { + "use strict"; + /*global __fail__*/ - ```javascript - binding.connect(this); - ``` + var Ember = __dependency1__["default"]; + var EmberError = __dependency2__["default"]; + var Logger = __dependency3__["default"]; - 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. + /** + Ember Debug - Now that the binding is connected, it will observe both the from and to side - and relay changes. + @module ember + @submodule ember-debug + */ - 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): + /** + @class Ember + */ - ```javascript - Ember.bind(MyApp.anotherObject, "value", "MyApp.someController.value"); - ``` + /** + Define an assertion that will throw an exception if the condition is not + met. Ember build tools will remove any calls to `Ember.assert()` when + doing a production build. Example: - Both of these code fragments have the same effect as doing the most friendly - form of binding creation like so: + ```javascript + // Test for truthiness + Ember.assert('Must pass a valid object', obj); - ```javascript - MyApp.anotherObject = Ember.Object.create({ - valueBinding: "MyApp.someController.value", + // Fail unconditionally + Ember.assert('This code path should never be run'); + ``` - // OTHER CODE FOR THIS OBJECT... - }); - ``` - - Ember's built in binding creation method makes it easy to automatically - create bindings for you. You should always use the highest-level APIs - available, even if you understand how it works underneath. - - @class Binding - @namespace Ember - @since Ember 0.9 -*/ -Ember.Binding = Binding; - - -/** - 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. - - @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 -*/ -Ember.bind = function(obj, to, from) { - return new Ember.Binding(to, from).connect(obj); -}; - -/** - @method oneWay - @for Ember - @param {Object} obj The root object of the transform. - @param {String} to The path to the 'to' side of the binding. - Must be relative to obj. - @param {String} from The path to the 'from' side of the binding. - Must be relative to obj or a global path. - @return {Ember.Binding} binding instance -*/ -Ember.oneWay = function(obj, to, from) { - return new Ember.Binding(to, from).oneWay().connect(obj); -}; + @method assert + @param {String} desc A description of the assertion. This will become + the text of the Error thrown if the assertion fails. + @param {Boolean} test Must be truthy for the assertion to pass. If + falsy, an exception will be thrown. + */ + Ember.assert = function(desc, test) { + if (!test) { + throw new EmberError("Assertion Failed: " + desc); + } + }; -})(); + /** + Display a warning with the provided message. Ember build tools will + remove any calls to `Ember.warn()` when doing a production build. + @method warn + @param {String} message A warning to display. + @param {Boolean} test An optional boolean. If falsy, the warning + will be displayed. + */ + Ember.warn = function(message, test) { + if (!test) { + Logger.warn("WARNING: "+message); + if ('trace' in Logger) Logger.trace(); + } + }; -(function() { -/** -@module ember -@submodule ember-metal -*/ - -var Mixin, REQUIRED, Alias, - a_map = Ember.ArrayPolyfills.map, - a_indexOf = Ember.ArrayPolyfills.indexOf, - a_forEach = Ember.ArrayPolyfills.forEach, - a_slice = [].slice, - o_create = Ember.create, - defineProperty = Ember.defineProperty, - guidFor = Ember.guidFor, - metaFor = Ember.meta, - META_KEY = Ember.META_KEY; - -var expandProperties = Ember.expandProperties; - -function superFunction(){ - var ret, func = this.__nextSuper; - if (func) { - this.__nextSuper = null; - ret = func.apply(this, arguments); - this.__nextSuper = func; - } - return ret; -} - -function mixinsMeta(obj) { - var m = metaFor(obj, true), ret = m.mixins; - if (!ret) { - ret = m.mixins = {}; - } else if (!m.hasOwnProperty('mixins')) { - ret = m.mixins = o_create(ret); - } - return ret; -} - -function initMixin(mixin, args) { - if (args && args.length > 0) { - mixin.mixins = a_map.call(args, function(x) { - if (x instanceof Mixin) { return x; } - - // Note: Manually setup a primitive mixin here. This is the only - // way to actually get a primitive mixin. This way normal creation - // of mixins will give you combined mixins... - var mixin = new Mixin(); - mixin.properties = x; - return mixin; - }); - } - return mixin; -} + /** + Display a debug notice. Ember build tools will remove any calls to + `Ember.debug()` when doing a production build. -function isMethod(obj) { - return 'function' === typeof obj && - obj.isMethod !== false && - obj !== Boolean && obj !== Object && obj !== Number && obj !== Array && obj !== Date && obj !== String; -} + ```javascript + Ember.debug('I\'m a debug notice!'); + ``` -var CONTINUE = {}; + @method debug + @param {String} message A debug message to display. + */ + Ember.debug = function(message) { + Logger.debug("DEBUG: "+message); + }; -function mixinProperties(mixinsMeta, mixin) { - var guid; + /** + Display a deprecation warning with the provided message and a stack trace + (Chrome and Firefox only). Ember build tools will remove any calls to + `Ember.deprecate()` when doing a production build. + + @method deprecate + @param {String} message A description of the deprecation. + @param {Boolean} test An optional boolean. If falsy, the deprecation + will be displayed. + */ + Ember.deprecate = function(message, test) { + if (test) { return; } - 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 (Ember.ENV.RAISE_ON_DEPRECATION) { throw new EmberError(message); } -function concatenatedMixinProperties(concatProp, props, values, base) { - var concats; + var error; - // 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]; - } + // When using new Error, we can't do the arguments check for Chrome. Alternatives are welcome + try { __fail__.fail(); } catch (e) { error = e; } - return concats; -} + if (Ember.LOG_STACKTRACE_ON_DEPRECATION && error.stack) { + var stack, stackStr = ''; + if (error['arguments']) { + // Chrome + stack = error.stack.replace(/^\s+at\s+/gm, ''). + replace(/^([^\(]+?)([\n$])/gm, '{anonymous}($1)$2'). + replace(/^Object.\s*\(([^\)]+)\)/gm, '{anonymous}($1)').split('\n'); + stack.shift(); + } else { + // Firefox + stack = error.stack.replace(/(?:\n@:0)?\s+$/m, ''). + replace(/^\(/gm, '{anonymous}(').split('\n'); + } -function giveDescriptorSuper(meta, key, property, values, descs) { - var superProperty; + stackStr = "\n " + stack.slice(2).join("\n "); + message = message + stackStr; + } - // 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]; - } + Logger.warn("DEPRECATION: "+message); + }; - // If we didn't find the original descriptor in a parent mixin, find - // it on the original object. - superProperty = superProperty || meta.descs[key]; - if (!superProperty || !(superProperty instanceof Ember.ComputedProperty)) { - return property; - } - // Since multiple mixins may inherit from the same parent, we need - // to clone the computed property so that other mixins do not receive - // the wrapped version. - property = o_create(property); - property.func = Ember.wrap(property.func, superProperty.func); + /** + Alias an old, deprecated method with its new counterpart. - return property; -} + Display a deprecation warning with the provided message and a stack trace + (Chrome and Firefox only) when the assigned method is called. -function giveMethodSuper(obj, key, method, values, descs) { - var superMethod; + Ember build tools will not remove calls to `Ember.deprecateFunc()`, though + no warnings will be shown in production. - // 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]; - } + ```javascript + Ember.oldMethod = Ember.deprecateFunc('Please use the new, updated method', Ember.newMethod); + ``` - // If we didn't find the original value in a parent mixin, find it in - // the original object - superMethod = superMethod || obj[key]; + @method deprecateFunc + @param {String} message A description of the deprecation. + @param {Function} func The new function called to replace its deprecated counterpart. + @return {Function} a new function that wrapped the original function with a deprecation warning + */ + Ember.deprecateFunc = function(message, func) { + return function() { + Ember.deprecate(message); + return func.apply(this, arguments); + }; + }; - // Only wrap the new method if the original method was a function - if ('function' !== typeof superMethod) { - return method; - } - return Ember.wrap(method, superMethod); -} + /** + Run a function meant for debugging. Ember build tools will remove any calls to + `Ember.runInDebug()` when doing a production build. -function applyConcatenatedProperties(obj, key, value, values) { - var baseValue = values[key] || obj[key]; + ```javascript + Ember.runInDebug(function() { + Ember.Handlebars.EachView.reopen({ + didInsertElement: function() { + console.log('I\'m happy'); + } + }); + }); + ``` - if (baseValue) { - if ('function' === typeof baseValue.concat) { - return baseValue.concat(value); - } else { - return Ember.makeArray(baseValue).concat(value); - } - } else { - return Ember.makeArray(value); - } -} + @method runInDebug + @param {Function} func The function to be executed. + @since 1.5.0 + */ + Ember.runInDebug = function(func) { + func(); + }; -function applyMergedProperties(obj, key, value, values) { - var baseValue = values[key] || obj[key]; + // Inform the developer about the Ember Inspector if not installed. + if (!Ember.testing) { + var isFirefox = typeof InstallTrigger !== 'undefined'; + var isChrome = !!window.chrome && !window.opera; + + if (typeof window !== 'undefined' && (isFirefox || isChrome) && window.addEventListener) { + window.addEventListener("load", function() { + if (document.documentElement && document.documentElement.dataset && !document.documentElement.dataset.emberExtension) { + var downloadURL; + + if(isChrome) { + downloadURL = 'https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi'; + } else if(isFirefox) { + downloadURL = 'https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/'; + } - if (!baseValue) { return value; } + Ember.debug('For more advanced debugging, install the Ember Inspector from ' + downloadURL); + } + }, false); + } + } + }); +define("ember-extension-support", + ["ember-metal/core","ember-extension-support/data_adapter","ember-extension-support/container_debug_adapter"], + function(__dependency1__, __dependency2__, __dependency3__) { + "use strict"; + /** + Ember Extension Support - var newBase = Ember.merge({}, baseValue), - hasFunction = false; + @module ember + @submodule ember-extension-support + @requires ember-application + */ - for (var prop in value) { - if (!value.hasOwnProperty(prop)) { continue; } + var Ember = __dependency1__["default"]; + var DataAdapter = __dependency2__["default"]; + var ContainerDebugAdapter = __dependency3__["default"]; - 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; - } - } + Ember.DataAdapter = DataAdapter; + Ember.ContainerDebugAdapter = ContainerDebugAdapter; + }); +define("ember-extension-support/container_debug_adapter", + ["ember-metal/core","ember-runtime/system/native_array","ember-metal/utils","ember-runtime/system/string","ember-runtime/system/namespace","ember-runtime/system/object","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + var emberA = __dependency2__.A; + var typeOf = __dependency3__.typeOf; + var dasherize = __dependency4__.dasherize; + var classify = __dependency4__.classify; + var Namespace = __dependency5__["default"]; + var EmberObject = __dependency6__["default"]; - if (hasFunction) { - newBase._super = superFunction; - } + /** + @module ember + @submodule ember-extension-support + */ - return newBase; -} + /** + The `ContainerDebugAdapter` helps the container and resolver interface + with tools that debug Ember such as the + [Ember Extension](https://github.com/tildeio/ember-extension) + for Chrome and Firefox. -function addNormalizedProperty(base, key, value, meta, descs, values, concats, mergings) { - if (value instanceof Ember.Descriptor) { - if (value === REQUIRED && descs[key]) { return CONTINUE; } + This class can be extended by a custom resolver implementer + to override some of the methods with library-specific code. - // Wrap descriptor function to implement - // __nextSuper() if needed - if (value.func) { - value = giveDescriptorSuper(meta, key, value, values, descs); - } + The methods likely to be overridden are: - 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); - } - - descs[key] = undefined; - values[key] = value; - } -} + * `canCatalogEntriesByType` + * `catalogEntriesByType` -function mergeMixins(mixins, m, descs, values, base, keys) { - var mixin, props, key, concats, mergings, meta; + The adapter will need to be registered + in the application's container as `container-debug-adapter:main` - function removeKeys(keyName) { - delete descs[keyName]; - delete values[keyName]; - } + Example: - for(var i=0, l=mixins.length; i= 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[META_KEY], - mixins = m && m.mixins; - if (mixins) { - return !!mixins[guidFor(this)]; - } - return false; -}; - -MixinPrototype.without = function() { - var ret = new Mixin(this); - ret._without = a_slice.call(arguments); - return ret; -}; - -function _keys(ret, mixin, seen) { - if (seen[guidFor(mixin)]) { return; } - seen[guidFor(mixin)] = true; - - 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); }); - } -} + @return {Function} Method to call to remove all observers + */ + watchRecords: function(type, recordsAdded, recordsUpdated, recordsRemoved) { + var self = this, releaseMethods = emberA(), records = this.getRecords(type), release; -MixinPrototype.keys = function() { - var keys = {}, seen = {}, ret = []; - _keys(keys, this, seen); - for(var key in keys) { - if (keys.hasOwnProperty(key)) { ret.push(key); } - } - return ret; -}; + var recordUpdated = function(updatedRecord) { + recordsUpdated([updatedRecord]); + }; -// returns the mixins currently applied to the specified object -// TODO: Make Ember.mixin -Mixin.mixins = function(obj) { - var m = obj[META_KEY], - mixins = m && m.mixins, ret = []; + var recordsToSend = records.map(function(record) { + releaseMethods.push(self.observeRecord(record, recordUpdated)); + return self.wrapRecord(record); + }); - if (!mixins) { return ret; } - for (var key in mixins) { - var mixin = mixins[key]; + var contentDidChange = function(array, idx, removedCount, addedCount) { + for (var i = idx; i < idx + addedCount; i++) { + var record = array.objectAt(i); + var wrapped = self.wrapRecord(record); + releaseMethods.push(self.observeRecord(record, recordUpdated)); + recordsAdded([wrapped]); + } - // skip primitive mixins since these are always anonymous - if (!mixin.properties) { ret.push(mixin); } - } + if (removedCount) { + recordsRemoved(idx, removedCount); + } + }; - return ret; -}; + var observer = { didChange: contentDidChange, willChange: Ember.K }; + records.addArrayObserver(self, observer); -REQUIRED = new Ember.Descriptor(); -REQUIRED.toString = function() { return '(Required Property)'; }; + release = function() { + releaseMethods.forEach(function(fn) { fn(); }); + records.removeArrayObserver(self, observer); + self.releaseMethods.removeObject(release); + }; -/** - Denotes a required property for a mixin + recordsAdded(recordsToSend); - @method required - @for Ember -*/ -Ember.required = function() { - return REQUIRED; -}; + this.releaseMethods.pushObject(release); + return release; + }, -Alias = function(methodName) { - this.methodName = methodName; -}; -Alias.prototype = new Ember.Descriptor(); + /** + Clear all observers before destruction + @private + @method willDestroy + */ + willDestroy: function() { + this._super(); + this.releaseMethods.forEach(function(fn) { + fn(); + }); + }, -/** - Makes a method available via an additional name. + /** + Detect whether a class is a model. - ```javascript - App.Person = Ember.Object.extend({ - name: function() { - return 'Tomhuda Katzdale'; - }, - moniker: Ember.aliasMethod('name') - }); + Test that against the model class + of your persistence library - var goodGuy = App.Person.create() - ``` - - @method aliasMethod - @for Ember - @param {String} methodName name of the method to alias - @return {Ember.Descriptor} -*/ -Ember.aliasMethod = function(methodName) { - return new Alias(methodName); -}; - -// .......................................................... -// OBSERVER HELPER -// - -/** - Specify a method that observes property changes. - - ```javascript - Ember.Object.extend({ - valueObserver: Ember.observer('value', function() { - // Executes whenever the "value" property changes - }) - }); - ``` + @private + @method detect + @param {Class} klass The class to test + @return boolean Whether the class is a model class or not + */ + detect: function(klass) { + return false; + }, - In the future this method may become asynchronous. If you want to ensure - synchronous behavior, use `immediateObserver`. + /** + Get the columns for a given model type. - Also available as `Function.prototype.observes` if prototype extensions are - enabled. + @private + @method columnsForType + @param {Class} type The model type + @return {Array} An array of columns of the following format: + name: {String} name of the column + desc: {String} Humanized description (what would show in a table column name) + */ + columnsForType: function(type) { + return emberA(); + }, - @method observer - @for Ember - @param {String} propertyNames* - @param {Function} func - @return func -*/ -Ember.observer = function() { - var func = a_slice.call(arguments, -1)[0]; - var paths; + /** + Adds observers to a model type class. - var addWatchedProperty = function (path) { paths.push(path); }; - var _paths = a_slice.call(arguments, 0, -1); + @private + @method observeModelType + @param {Class} type The model type class + @param {Function} typesUpdated Called when a type is modified. + @return {Function} The function to call to remove observers + */ - if (typeof func !== "function") { - // revert to old, soft-deprecated argument ordering + observeModelType: function(type, typesUpdated) { + var self = this, records = this.getRecords(type); - func = arguments[0]; - _paths = a_slice.call(arguments, 1); - } + var onChange = function() { + typesUpdated([self.wrapModelType(type)]); + }; + var observer = { + didChange: function() { + run.scheduleOnce('actions', this, onChange); + }, + willChange: Ember.K + }; - paths = []; + records.addArrayObserver(this, observer); - for (var i=0; i<_paths.length; ++i) { - expandProperties(_paths[i], addWatchedProperty); - } + var release = function() { + records.removeArrayObserver(self, observer); + }; - if (typeof func !== "function") { - throw new Ember.Error("Ember.observer called without a function"); - } + return release; + }, - func.__ember_observes__ = paths; - return func; -}; -/** - Specify a method that observes property changes. + /** + Wraps a given model type and observes changes to it. - ```javascript - Ember.Object.extend({ - valueObserver: Ember.immediateObserver('value', function() { - // Executes whenever the "value" property changes - }) - }); - ``` - - In the future, `Ember.observer` may become asynchronous. In this event, - `Ember.immediateObserver` will maintain the synchronous behavior. - - Also available as `Function.prototype.observesImmediately` if prototype extensions are - enabled. - - @method immediateObserver - @for Ember - @param {String} propertyNames* - @param {Function} func - @return func -*/ -Ember.immediateObserver = function() { - for (var i=0, l=arguments.length; i this.changingFrom ? 'green' : 'red'; - // logic - } - }), + /** + Loops over all namespaces and all objects + attached to them - friendsDidChange: Ember.observer('friends.@each.name', function(obj, keyName) { - // some logic - // obj.get(keyName) returns friends array - }) - }); - ``` + @private + @method _getObjectsOnNamespaces + @return {Array} Array of model type strings + */ + _getObjectsOnNamespaces: function() { + var namespaces = emberA(Namespace.NAMESPACES), + types = emberA(), + self = this; + + namespaces.forEach(function(namespace) { + for (var key in namespace) { + if (!namespace.hasOwnProperty(key)) { continue; } + // Even though we will filter again in `getModelTypes`, + // we should not call `lookupContainer` on non-models + // (especially when `Ember.MODEL_FACTORY_INJECTIONS` is `true`) + if (!self.detect(namespace[key])) { continue; } + var name = dasherize(key); + if (!(namespace instanceof Application) && namespace.toString()) { + name = namespace + '/' + name; + } + types.push(name); + } + }); + return types; + }, - Also available as `Function.prototype.observesBefore` if prototype extensions are - enabled. + /** + Fetches all loaded records for a given type. - @method beforeObserver - @for Ember - @param {String} propertyNames* - @param {Function} func - @return func -*/ -Ember.beforeObserver = function() { - var func = a_slice.call(arguments, -1)[0]; - var paths; + @private + @method getRecords + @return {Array} An array of records. + This array will be observed for changes, + so it should update when new records are added/removed. + */ + getRecords: function(type) { + return emberA(); + }, - var addWatchedProperty = function(path) { paths.push(path); }; + /** + Wraps a record and observers changes to it. - var _paths = a_slice.call(arguments, 0, -1); + @private + @method wrapRecord + @param {Object} record The record instance. + @return {Object} The wrapped record. Format: + columnValues: {Array} + searchKeywords: {Array} + */ + wrapRecord: function(record) { + var recordToSend = { object: record }, columnValues = {}, self = this; - if (typeof func !== "function") { - // revert to old, soft-deprecated argument ordering + recordToSend.columnValues = this.getRecordColumnValues(record); + recordToSend.searchKeywords = this.getRecordKeywords(record); + recordToSend.filterValues = this.getRecordFilterValues(record); + recordToSend.color = this.getRecordColor(record); - func = arguments[0]; - _paths = a_slice.call(arguments, 1); - } + return recordToSend; + }, - paths = []; + /** + Gets the values for each column. - for (var i=0; i<_paths.length; ++i) { - expandProperties(_paths[i], addWatchedProperty); - } + @private + @method getRecordColumnValues + @return {Object} Keys should match column names defined + by the model type. + */ + getRecordColumnValues: function(record) { + return {}; + }, - if (typeof func !== "function") { - throw new Ember.Error("Ember.beforeObserver called without a function"); - } + /** + Returns keywords to match when searching records. - func.__ember_observesBefore__ = paths; - return func; -}; + @private + @method getRecordKeywords + @return {Array} Relevant keywords for search. + */ + getRecordKeywords: function(record) { + return emberA(); + }, -})(); + /** + Returns the values of filters defined by `getFilters`. + @private + @method getRecordFilterValues + @param {Object} record The record instance + @return {Object} The filter values + */ + getRecordFilterValues: function(record) { + return {}; + }, + /** + Each record can have a color that represents its state. -(function() { -// Provides a way to register library versions with ember. -var forEach = Ember.EnumerableUtils.forEach, - indexOf = Ember.EnumerableUtils.indexOf; + @private + @method getRecordColor + @param {Object} record The record instance + @return {String} The record's color + Possible options: black, red, blue, green + */ + getRecordColor: function(record) { + return null; + }, -Ember.libraries = function() { - var libraries = []; - var coreLibIndex = 0; + /** + Observes all relevant properties and re-sends the wrapped record + when a change occurs. - var getLibrary = function(name) { - for (var i = 0; i < libraries.length; i++) { - if (libraries[i].name === name) { - return libraries[i]; + @private + @method observerRecord + @param {Object} record The record instance + @param {Function} recordUpdated The callback to call when a record is updated. + @return {Function} The function to call to remove all observers. + */ + observeRecord: function(record, recordUpdated) { + return function(){}; } - } - }; + }); + }); +define("ember-extension-support/initializers", + [], + function() { + "use strict"; - libraries.register = function(name, version) { - if (!getLibrary(name)) { - libraries.push({name: name, version: version}); - } - }; + }); +define("ember-handlebars-compiler", + ["ember-metal/core","exports"], + function(__dependency1__, __exports__) { + "use strict"; + /* global Handlebars:true */ - libraries.registerCoreLibrary = function(name, version) { - if (!getLibrary(name)) { - libraries.splice(coreLibIndex++, 0, {name: name, version: version}); - } - }; + /** + @module ember + @submodule ember-handlebars-compiler + */ - libraries.deRegister = function(name) { - var lib = getLibrary(name); - if (lib) libraries.splice(indexOf(libraries, lib), 1); - }; + var Ember = __dependency1__["default"]; - libraries.each = function (callback) { - forEach(libraries, function(lib) { - callback(lib.name, lib.version); - }); - }; + // ES6Todo: you'll need to import debugger once debugger is es6'd. + if (typeof Ember.assert === 'undefined') { Ember.assert = function(){}; } + if (typeof Ember.FEATURES === 'undefined') { Ember.FEATURES = { isEnabled: function(){} }; } - return libraries; -}(); + var objectCreate = Object.create || function(parent) { + function F() {} + F.prototype = parent; + return new F(); + }; -Ember.libraries.registerCoreLibrary('Ember', Ember.VERSION); + // set up for circular references later + var View, Component; -})(); + // ES6Todo: when ember-debug is es6'ed import this. + // var emberAssert = Ember.assert; + var Handlebars = (Ember.imports && Ember.imports.Handlebars) || (this && this.Handlebars); + if (!Handlebars && typeof require === 'function') { + Handlebars = require('handlebars'); + } + Ember.assert("Ember Handlebars requires Handlebars version 1.0 or 1.1. Include " + + "a SCRIPT tag in the HTML HEAD linking to the Handlebars file " + + "before you link to Ember.", Handlebars); + Ember.assert("Ember Handlebars requires Handlebars version 1.0 or 1.1, " + + "COMPILER_REVISION expected: 4, got: " + Handlebars.COMPILER_REVISION + + " - Please note: Builds of master may have other COMPILER_REVISION values.", + Handlebars.COMPILER_REVISION === 4); -(function() { -/** -Ember Metal + /** + Prepares the Handlebars templating library for use inside Ember's view + system. -@module ember -@submodule ember-metal -*/ + The `Ember.Handlebars` object is the standard Handlebars library, extended to + use Ember's `get()` method instead of direct property access, which allows + computed properties to be used inside templates. -})(); + To create an `Ember.Handlebars` template, call `Ember.Handlebars.compile()`. + This will return a function that can be used by `Ember.View` for rendering. -(function() { -/** - @class RSVP - @module RSVP - */ -define("rsvp/all", - ["./promise","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Promise = __dependency1__["default"]; + @class Handlebars + @namespace Ember + */ + var EmberHandlebars = Ember.Handlebars = objectCreate(Handlebars); /** - This is a convenient alias for `RSVP.Promise.all`. - - @method all - @for RSVP - @param {Array} array Array of promises. - @param {String} label An optional label. This is useful - for tooling. - @static - */ - __exports__["default"] = function all(array, label) { - return Promise.all(array, label); - }; - }); -define("rsvp/all_settled", - ["./promise","./utils","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var Promise = __dependency1__["default"]; - var isArray = __dependency2__.isArray; - var isNonThenable = __dependency2__.isNonThenable; + Register a bound helper or custom view helper. - /** - `RSVP.allSettled` is similar to `RSVP.all`, but instead of implementing - a fail-fast method, it waits until all the promises have returned and - shows you all the results. This is useful if you want to handle multiple - promises' failure states together as a set. + ## Simple bound helper example - Returns a promise that is fulfilled when all the given promises have been - settled. The return promise is fulfilled with an array of the states of - the promises passed into the `promises` array argument. + ```javascript + Ember.Handlebars.helper('capitalize', function(value) { + return value.toUpperCase(); + }); + ``` - Each state object will either indicate fulfillment or rejection, and - provide the corresponding value or reason. The states will take one of - the following formats: + The above bound helper can be used inside of templates as follows: - ```javascript - { state: 'fulfilled', value: value } - or - { state: 'rejected', reason: reason } + ```handlebars + {{capitalize name}} ``` - Example: + 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. - ```javascript - var promise1 = RSVP.Promise.resolve(1); - var promise2 = RSVP.Promise.reject(new Error('2')); - var promise3 = RSVP.Promise.reject(new Error('3')); - var promises = [ promise1, promise2, promise3 ]; + For more examples of bound helpers, see documentation for + `Ember.Handlebars.registerBoundHelper`. - RSVP.allSettled(promises).then(function(array){ - // array == [ - // { state: 'fulfilled', value: 1 }, - // { state: 'rejected', reason: Error }, - // { state: 'rejected', reason: Error } - // ] - // Note that for the second item, reason.message will be "2", and for the - // third item, reason.message will be "3". - }, function(error) { - // Not run. (This block would only be called if allSettled had failed, - // for instance if passed an incorrect argument type.) - }); - ``` + ## Custom view helper example - @method allSettled - @for RSVP - @param {Array} promises - @param {String} label - optional string that describes the promise. - Useful for tooling. - @return {Promise} promise that is fulfilled with an array of the settled - states of the constituent promises. - @static - */ + Assuming a view subclass named `App.CalendarView` were defined, a helper + for rendering instances of this view could be registered as follows: - __exports__["default"] = function allSettled(entries, label) { - return new Promise(function(resolve, reject) { - if (!isArray(entries)) { - throw new TypeError('You must pass an array to allSettled.'); - } + ```javascript + Ember.Handlebars.helper('calendar', App.CalendarView): + ``` - var remaining = entries.length; - var entry; + The above bound helper can be used inside of templates as follows: - if (remaining === 0) { - resolve([]); - return; - } + ```handlebars + {{calendar}} + ``` - var results = new Array(remaining); + Which is functionally equivalent to: - function fulfilledResolver(index) { - return function(value) { - resolveAll(index, fulfilled(value)); - }; - } + ```handlebars + {{view App.CalendarView}} + ``` - function rejectedResolver(index) { - return function(reason) { - resolveAll(index, rejected(reason)); - }; - } + Options in the helper will be passed to the view in exactly the same + manner as with the `view` helper. - function resolveAll(index, value) { - results[index] = value; - if (--remaining === 0) { - resolve(results); - } - } + @method helper + @for Ember.Handlebars + @param {String} name + @param {Function|Ember.View} function or view class constructor + @param {String} dependentKeys* + */ + EmberHandlebars.helper = function(name, value) { + if (!View) { View = requireModule('ember-views/views/view')['default']; } // ES6TODO: stupid circular dep + if (!Component) { Component = requireModule('ember-views/views/component')['default']; } // ES6TODO: stupid circular dep - for (var index = 0; index < entries.length; index++) { - entry = entries[index]; + Ember.assert("You tried to register a component named '" + name + "', but component names must include a '-'", !Component.detect(value) || name.match(/-/)); - if (isNonThenable(entry)) { - resolveAll(index, fulfilled(entry)); - } else { - Promise.cast(entry).then(fulfilledResolver(index), rejectedResolver(index)); - } - } - }, label); + if (View.detect(value)) { + EmberHandlebars.registerHelper(name, EmberHandlebars.makeViewHelper(value)); + } else { + EmberHandlebars.registerBoundHelper.apply(null, arguments); + } }; - function fulfilled(value) { - return { state: 'fulfilled', value: value }; - } + /** + Returns a helper function that renders the provided ViewClass. - function rejected(reason) { - return { state: 'rejected', reason: reason }; - } - }); -define("rsvp/config", - ["./events","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var EventTarget = __dependency1__["default"]; + Used internally by Ember.Handlebars.helper and other methods + involving helper/component registration. - var config = { - instrument: false + @private + @method makeViewHelper + @for Ember.Handlebars + @param {Function} ViewClass view class constructor + @since 1.2.0 + */ + EmberHandlebars.makeViewHelper = function(ViewClass) { + return function(options) { + Ember.assert("You can only pass attributes (such as name=value) not bare values to a helper for a View found in '" + ViewClass.toString() + "'", arguments.length < 2); + return EmberHandlebars.helpers.view.call(this, ViewClass, options); + }; }; - EventTarget.mixin(config); + /** + @class helpers + @namespace Ember.Handlebars + */ + EmberHandlebars.helpers = objectCreate(Handlebars.helpers); - function configure(name, value) { - if (name === 'onerror') { - // handle for legacy users that expect the actual - // error to be passed to their function added via - // `RSVP.configure('onerror', someFunctionHere);` - config.on('error', value); - return; - } + /** + Override the the opcode compiler and JavaScript compiler for Handlebars. - if (arguments.length === 2) { - config[name] = value; - } else { - return config[name]; - } + @class Compiler + @namespace Ember.Handlebars + @private + @constructor + */ + EmberHandlebars.Compiler = function() {}; + + // Handlebars.Compiler doesn't exist in runtime-only + if (Handlebars.Compiler) { + EmberHandlebars.Compiler.prototype = objectCreate(Handlebars.Compiler.prototype); } - __exports__.config = config; - __exports__.configure = configure; - }); -define("rsvp/defer", - ["./promise","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Promise = __dependency1__["default"]; + EmberHandlebars.Compiler.prototype.compiler = EmberHandlebars.Compiler; /** - `RSVP.defer` returns an object similar to jQuery's `$.Deferred`. - `RSVP.defer` should be used when porting over code reliant on `$.Deferred`'s - interface. New code should use the `RSVP.Promise` constructor instead. - - The object returned from `RSVP.defer` is a plain object with three properties: + @class JavaScriptCompiler + @namespace Ember.Handlebars + @private + @constructor + */ + EmberHandlebars.JavaScriptCompiler = function() {}; - * promise - an `RSVP.Promise`. - * reject - a function that causes the `promise` property on this object to - become rejected - * resolve - a function that causes the `promise` property on this object to - become fulfilled. + // Handlebars.JavaScriptCompiler doesn't exist in runtime-only + if (Handlebars.JavaScriptCompiler) { + EmberHandlebars.JavaScriptCompiler.prototype = objectCreate(Handlebars.JavaScriptCompiler.prototype); + EmberHandlebars.JavaScriptCompiler.prototype.compiler = EmberHandlebars.JavaScriptCompiler; + } - Example: - ```javascript - var deferred = RSVP.defer(); + EmberHandlebars.JavaScriptCompiler.prototype.namespace = "Ember.Handlebars"; - deferred.resolve("Success!"); + EmberHandlebars.JavaScriptCompiler.prototype.initializeBuffer = function() { + return "''"; + }; - defered.promise.then(function(value){ - // value here is "Success!" - }); - ``` + /** + Override the default buffer for Ember Handlebars. By default, Handlebars + creates an empty String at the beginning of each invocation and appends to + it. Ember's Handlebars overrides this to append to a single shared buffer. - @method defer - @for RSVP - @param {String} label optional string for labeling the promise. - Useful for tooling. - @return {Object} - */ + @private + @method appendToBuffer + @param string {String} + */ + EmberHandlebars.JavaScriptCompiler.prototype.appendToBuffer = function(string) { + return "data.buffer.push("+string+");"; + }; - __exports__["default"] = function defer(label) { - var deferred = { }; + // Hacks ahead: + // Handlebars presently has a bug where the `blockHelperMissing` hook + // doesn't get passed the name of the missing helper name, but rather + // gets passed the value of that missing helper evaluated on the current + // context, which is most likely `undefined` and totally useless. + // + // So we alter the compiled template function to pass the name of the helper + // instead, as expected. + // + // This can go away once the following is closed: + // https://github.com/wycats/handlebars.js/issues/634 - deferred.promise = new Promise(function(resolve, reject) { - deferred.resolve = resolve; - deferred.reject = reject; - }, label); + var DOT_LOOKUP_REGEX = /helpers\.(.*?)\)/, + BRACKET_STRING_LOOKUP_REGEX = /helpers\['(.*?)'/, + INVOCATION_SPLITTING_REGEX = /(.*blockHelperMissing\.call\(.*)(stack[0-9]+)(,.*)/; - return deferred; - }; - }); -define("rsvp/events", - ["exports"], - function(__exports__) { - "use strict"; - var indexOf = function(callbacks, callback) { - for (var i=0, l=callbacks.length; i` 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. + + ## 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: + + + + + + + + + + + + +
`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` 
+ + + 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 value="http://www.facebook.com"}} + ``` - object.trigger('stuff'); // callback1 and callback2 will be executed. - object.off('stuff'); - object.trigger('stuff'); // callback1 and callback2 will not be executed! - ``` + ```html + + ``` - @method off - @param {String} eventName event to stop listening to - @param {Function} callback optional argument. If given, only the function - given will be removed from the event's callback queue. If no `callback` - argument is given, all callbacks will be removed from the event's callback - queue. - @private + ## Bound: - */ - off: function(eventName, callback) { - var allCallbacks = callbacksFor(this), callbacks, index; + ```javascript + App.ApplicationController = Ember.Controller.extend({ + firstName: "Stanley", + entryNotAllowed: true + }); + ``` - if (!callback) { - allCallbacks[eventName] = []; - return; - } - callbacks = allCallbacks[eventName]; + ```handlebars + {{input type="text" value=firstName disabled=entryNotAllowed size="50"}} + ``` - index = indexOf(callbacks, callback); - if (index !== -1) { callbacks.splice(index, 1); } - }, + ```html + + ``` - /** - Use `trigger` to fire custom events. For example: + ## Actions - ```javascript - object.on('foo', function(){ - console.log('foo event happened!'); - }); - object.trigger('foo'); - // 'foo event happened!' logged to the console - ``` + The helper can send multiple actions based on user events. - You can also pass a value as a second argument to `trigger` that will be - passed as an argument to all event listeners for the event: + The action property defines the action which is send when + the user presses the return key. - ```javascript - object.on('foo', function(value){ - console.log(value.name); - }); + ```handlebars + {{input action="submit"}} + ``` - object.trigger('foo', { name: 'bar' }); - // 'bar' logged to the console - ``` + The helper allows some user events to send actions. - @method trigger - @param {String} eventName name of the event to be triggered - @param {Any} options optional value to be passed to any event handlers for - the given `eventName` - @private - */ - trigger: function(eventName, options) { - var allCallbacks = callbacksFor(this), - callbacks, callbackTuple, callback, binding; + * `enter` + * `insert-newline` + * `escape-press` + * `focus-in` + * `focus-out` + * `key-press` - if (callbacks = allCallbacks[eventName]) { - // Don't cache the callbacks.length since it may grow - for (var i=0; i 1; - }; + See more about [Ember components](/api/classes/Ember.Component.html) - RSVP.filter(promises, filterFn).then(function(result){ - // result is [ 2, 3 ] - }); + + ## Use as checkbox + + An `{{input}}` with a `type` of `checkbox` will render an HTML checkbox input. + The following HTML attributes can be set via the helper: + + * `checked` + * `disabled` + * `tabindex` + * `indeterminate` + * `name` + * `autofocus` + * `form` + + + When set to a quoted string, these values will be directly applied to the HTML + element. When left unquoted, these values will be bound to a property on the + template's current rendering context (most typically a controller instance). + + ## Unbound: + + ```handlebars + {{input type="checkbox" name="isAdmin"}} ``` - If any of the `promises` given to `RSVP.filter` are rejected, the first promise - that is rejected will be given as an argument to the returned promise's - rejection handler. For example: + ```html + + ``` + + ## Bound: ```javascript - var promise1 = RSVP.resolve(1); - var promise2 = RSVP.reject(new Error("2")); - var promise3 = RSVP.reject(new Error("3")); - var promises = [ promise1, promise2, promise3 ]; + App.ApplicationController = Ember.Controller.extend({ + isAdmin: true + }); + ``` - var filterFn = function(item){ - return item > 1; - }; - RSVP.filter(promises, filterFn).then(function(array){ - // Code here never runs because there are rejected promises! - }, function(reason) { - // reason.message === "2" - }); + ```handlebars + {{input type="checkbox" checked=isAdmin }} ``` - `RSVP.filter` will also wait for any promises returned from `filterFn`. - For instance, you may want to fetch a list of users then return a subset - of those users based on some asynchronous operation: - ```javascript + ```html + + ``` - var alice = { name: 'alice' }; - var bob = { name: 'bob' }; - var users = [ alice, bob ]; + ## Extension - var promises = users.map(function(user){ - return RSVP.resolve(user); - }); + 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: - var filterFn = function(user){ - // Here, Alice has permissions to create a blog post, but Bob does not. - return getPrivilegesForUser(user).then(function(privs){ - return privs.can_create_blog_post === true; - }); - }; - RSVP.filter(promises, filterFn).then(function(users){ - // true, because the server told us only Alice can create a blog post. - users.length === 1; - // false, because Alice is the only user present in `users` - users[0] === bob; + + ```javascript + Ember.Checkbox.reopen({ + classNames: ['my-app-checkbox'] }); ``` - @method filter - @for RSVP - @param {Array} promises - @param {Function} filterFn - function to be called on each resolved value to - filter the final results. - @param {String} label optional string describing the promise. Useful for - tooling. - @return {Promise} + + @method input + @for Ember.Handlebars.helpers + @param {Hash} options */ - function filter(promises, filterFn, label) { - return all(promises, label).then(function(values){ - if (!isArray(promises)) { - throw new TypeError('You must pass an array to filter.'); - } + function inputHelper(options) { + Ember.assert('You can only pass attributes to the `input` helper, not arguments', arguments.length < 2); - if (!isFunction(filterFn)){ - throw new TypeError("You must pass a function to filter's second argument."); - } + var hash = options.hash, + types = options.hashTypes, + inputType = _resolveOption(this, options, 'type'), + onEvent = hash.on; - return map(promises, filterFn, label).then(function(filterResults){ - var i, - valuesLen = values.length, - filtered = []; + delete hash.type; + delete hash.on; - for (i = 0; i < valuesLen; i++){ - if(filterResults[i]) filtered.push(values[i]); - } - return filtered; - }); - }); - } + if (inputType === 'checkbox') { + Ember.assert("{{input type='checkbox'}} does not support setting `value=someBooleanValue`; you must use `checked=someBooleanValue` instead.", options.hashTypes.value !== 'ID'); + return helpers.view.call(this, Checkbox, options); + } else { + if (inputType) { hash.type = inputType; } + hash.onEvent = onEvent || 'enter'; + return helpers.view.call(this, TextField, options); + } + } + + __exports__.inputHelper = inputHelper;/** + `{{textarea}}` inserts a new instance of ` + ``` - Returns a promise that is fulfilled when all the given promises have been - fulfilled, or rejected if any of them become rejected. The returned promise - is fulfilled with a hash that has the same key names as the `promises` object - argument. If any of the values in the object are not promises, they will - simply be copied over to the fulfilled object. + Bound: - Example: + In the following example, the `writtenWords` property on `App.ApplicationController` + will be updated live as the user types 'Lots of text that IS bound' into + the text area of their browser's window. ```javascript - var promises = { - myPromise: RSVP.resolve(1), - yourPromise: RSVP.resolve(2), - theirPromise: RSVP.resolve(3), - notAPromise: 4 - }; - - RSVP.hash(promises).then(function(hash){ - // hash here is an object that looks like: - // { - // myPromise: 1, - // yourPromise: 2, - // theirPromise: 3, - // notAPromise: 4 - // } + App.ApplicationController = Ember.Controller.extend({ + writtenWords: "Lots of text that IS bound" }); - ```` + ``` - If any of the `promises` given to `RSVP.hash` are rejected, the first promise - that is rejected will be given as the reason to the rejection handler. + ```handlebars + {{textarea value=writtenWords}} + ``` - Example: + Would result in the following HTML: - ```javascript - var promises = { - myPromise: RSVP.resolve(1), - rejectedPromise: RSVP.reject(new Error("rejectedPromise")), - anotherRejectedPromise: RSVP.reject(new Error("anotherRejectedPromise")), - }; + ```html + + ``` - RSVP.hash(promises).then(function(hash){ - // Code here never runs because there are rejected promises! - }, function(reason) { - // reason.message === "rejectedPromise" + 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`: + + ```javascript + App.ApplicationController = Ember.Controller.extend({ + writtenWords: "Lots of text that IS bound", + outputWrittenWords: Ember.computed.oneWay("writtenWords") }); ``` - An important note: `RSVP.hash` is intended for plain JavaScript objects that - are just a set of keys and values. `RSVP.hash` will NOT preserve prototype - chains. + ```handlebars + {{textarea value=writtenWords}} - Example: +
+ {{outputWrittenWords}} +
+ ``` - ```javascript - function MyConstructor(){ - this.example = RSVP.resolve("Example"); - } + Would result in the following HTML: - MyConstructor.prototype = { - protoProperty: RSVP.resolve("Proto Property") - }; + ```html + - var myObject = new MyConstructor(); + <-- the following div will be updated in real time as you type --> - RSVP.hash(myObject).then(function(hash){ - // protoProperty will not be present, instead you will just have an - // object that looks like: - // { - // example: "Example" - // } - // - // hash.hasOwnProperty('protoProperty'); // false - // 'undefined' === typeof hash.protoProperty +
+ Lots of text that IS bound +
+ ``` + + Finally, this example really shows the power and ease of Ember when two + properties are bound to eachother via `Ember.computed.alias`. Type into + either text area box and they'll both stay in sync. Note that + `Ember.computed.alias` costs more in terms of performance, so only use it when + your really binding in both directions: + + ```javascript + App.ApplicationController = Ember.Controller.extend({ + writtenWords: "Lots of text that IS bound", + twoWayWrittenWords: Ember.computed.alias("writtenWords") }); ``` - @method hash - @for RSVP - @param {Object} promises - @param {String} label optional string that describes the promise. - Useful for tooling. - @return {Promise} promise that is fulfilled when all properties of `promises` - have been fulfilled, or rejected if any of them become rejected. - @static - */ - __exports__["default"] = function hash(object, label) { - return new Promise(function(resolve, reject){ - var results = {}; - var keys = keysOf(object); - var remaining = keys.length; - var entry, property; - - if (remaining === 0) { - resolve(results); - return; - } + ```handlebars + {{textarea value=writtenWords}} + {{textarea value=twoWayWrittenWords}} + ``` - function fulfilledTo(property) { - return function(value) { - results[property] = value; - if (--remaining === 0) { - resolve(results); - } - }; - } + ```html + - function onRejection(reason) { - remaining = 0; - reject(reason); - } + <-- both updated in real time --> - for (var i = 0; i < keys.length; i++) { - property = keys[i]; - entry = object[property]; + + ``` - if (isNonThenable(entry)) { - results[property] = entry; - if (--remaining === 0) { - resolve(results); - } - } else { - Promise.cast(entry).then(fulfilledTo(property), onRejection); - } - } - }); - }; - }); -define("rsvp/instrument", - ["./config","./utils","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var config = __dependency1__.config; - var now = __dependency2__.now; + ## Actions - __exports__["default"] = function instrument(eventName, promise, child) { - // instrumentation should not disrupt normal usage. - try { - config.trigger(eventName, { - guid: promise._guidKey + promise._id, - eventName: eventName, - detail: promise._detail, - childGuid: child && promise._guidKey + child._id, - label: promise._label, - timeStamp: now(), - stack: new Error(promise._label).stack - }); - } catch(error) { - setTimeout(function(){ - throw error; - }, 0); - } - }; - }); -define("rsvp/map", - ["./promise","./all","./utils","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var Promise = __dependency1__["default"]; - var all = __dependency2__["default"]; - var isArray = __dependency3__.isArray; - var isFunction = __dependency3__.isFunction; + The helper can send multiple actions based on user events. - /** - `RSVP.map` is similar to JavaScript's native `map` method, except that it - waits for all promises to become fulfilled before running the `mapFn` on - each item in given to `promises`. `RSVP.map` returns a promise that will - become fulfilled with the result of running `mapFn` on the values the promises - become fulfilled with. + The action property defines the action which is send when + the user presses the return key. - For example: + ```handlebars + {{input action="submit"}} + ``` - ```javascript + The helper allows some user events to send actions. - var promise1 = RSVP.resolve(1); - var promise2 = RSVP.resolve(2); - var promise3 = RSVP.resolve(3); - var promises = [ promise1, promise2, promise3 ]; + * `enter` + * `insert-newline` + * `escape-press` + * `focus-in` + * `focus-out` + * `key-press` - var mapFn = function(item){ - return item + 1; - }; + 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. - RSVP.map(promises, mapFn).then(function(result){ - // result is [ 2, 3, 4 ] - }); + ```handlebars + {{textarea focus-in="alertMessage"}} ``` - If any of the `promises` given to `RSVP.map` are rejected, the first promise - that is rejected will be given as an argument to the returned promise's - rejection handler. For example: + See more about [Text Support Actions](/api/classes/Ember.TextArea.html) - ```javascript - var promise1 = RSVP.resolve(1); - var promise2 = RSVP.reject(new Error("2")); - var promise3 = RSVP.reject(new Error("3")); - var promises = [ promise1, promise2, promise3 ]; + ## Extension - var mapFn = function(item){ - return item + 1; - }; + Internally, `{{textarea}}` creates an instance of `Ember.TextArea`, passing + arguments from the helper to `Ember.TextArea`'s `create` method. You can + extend the capabilities of text areas in your application by reopening this + class. For example, if you are building a Bootstrap project where `data-*` + attributes are used, you can globally add support for a `data-*` attribute + on all `{{textarea}}`s' in your app by reopening `Ember.TextArea` or + `Ember.TextSupport` and adding it to the `attributeBindings` concatenated + property: - RSVP.map(promises, mapFn).then(function(array){ - // Code here never runs because there are rejected promises! - }, function(reason) { - // reason.message === "2" + ```javascript + Ember.TextArea.reopen({ + attributeBindings: ['data-error'] }); ``` - `RSVP.map` will also wait if a promise is returned from `mapFn`. For example, - say you want to get all comments from a set of blog posts, but you need - the blog posts first becuase they contain a url to those comments. + 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. - ```javscript + See more about [Ember components](/api/classes/Ember.Component.html) - var mapFn = function(blogPost){ - // getComments does some ajax and returns an RSVP.Promise that is fulfilled - // with some comments data - return getComments(blogPost.comments_url); - }; + @method textarea + @for Ember.Handlebars.helpers + @param {Hash} options + */ + function textareaHelper(options) { + Ember.assert('You can only pass attributes to the `textarea` helper, not arguments', arguments.length < 2); - // getBlogPosts does some ajax and returns an RSVP.Promise that is fulfilled - // with some blog post data - RSVP.map(getBlogPosts(), mapFn).then(function(comments){ - // comments is the result of asking the server for the comments - // of all blog posts returned from getBlogPosts() - }); - ``` + var hash = options.hash, + types = options.hashTypes; - @method map - @for RSVP - @param {Array} promises - @param {Function} mapFn function to be called on each fulfilled promise. - @param {String} label optional string for labeling the promise. - Useful for tooling. - @return {Promise} promise that is fulfilled with the result of calling - `mapFn` on each fulfilled promise or value when they become fulfilled. - The promise will be rejected if any of the given `promises` become rejected. - @static + return helpers.view.call(this, TextArea, options); + } + + __exports__.textareaHelper = textareaHelper; + }); +define("ember-handlebars/controls/checkbox", + ["ember-metal/property_get","ember-metal/property_set","ember-views/views/view","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var get = __dependency1__.get; + var set = __dependency2__.set; + var View = __dependency3__["default"]; + + /** + @module ember + @submodule ember-handlebars */ - __exports__["default"] = function map(promises, mapFn, label) { - return all(promises, label).then(function(results){ - if (!isArray(promises)) { - throw new TypeError('You must pass an array to map.'); - } - if (!isFunction(mapFn)){ - throw new TypeError("You must pass a function to map's second argument."); - } + /** + The internal class used to create text inputs when the `{{input}}` + helper is used with `type` of `checkbox`. + See [handlebars.helpers.input](/api/classes/Ember.Handlebars.helpers.html#method_input) for usage details. - var resultLen = results.length, - mappedResults = [], - i; + ## Direct manipulation of `checked` - for (i = 0; i < resultLen; i++){ - mappedResults.push(mapFn(results[i])); - } + 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. - return all(mappedResults, label); - }); - }; + ## 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'], + + tagName: 'input', + + attributeBindings: [ + 'type', + 'checked', + 'indeterminate', + 'disabled', + 'tabindex', + 'name', + 'autofocus', + 'required', + 'form' + ], + + type: 'checkbox', + checked: false, + disabled: false, + indeterminate: false, + + init: function() { + this._super(); + this.on('change', this, this._updateElementValue); + }, + + didInsertElement: function() { + this._super(); + get(this, 'element').indeterminate = !!get(this, 'indeterminate'); + }, + + _updateElementValue: function() { + set(this, 'checked', this.$().prop('checked')); + } + }); }); -define("rsvp/node", - ["./promise","exports"], - function(__dependency1__, __exports__) { +define("ember-handlebars/controls/select", + ["ember-handlebars-compiler","ember-metal/enumerable_utils","ember-metal/property_get","ember-metal/property_set","ember-views/views/view","ember-views/views/collection_view","ember-metal/utils","ember-metal/is_none","ember-metal/computed","ember-runtime/system/native_array","ember-metal/mixin","ember-metal/properties","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __exports__) { "use strict"; - var Promise = __dependency1__["default"]; + /** + @module ember + @submodule ember-handlebars + */ - var slice = Array.prototype.slice; + var EmberHandlebars = __dependency1__["default"]; + + var forEach = __dependency2__.forEach; + var indexOf = __dependency2__.indexOf; + var indexesOf = __dependency2__.indexesOf; + var replace = __dependency2__.replace; + + 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; + + var precompileTemplate = EmberHandlebars.compile; + + var SelectOption = View.extend({ + instrumentDisplay: 'Ember.SelectOption', + + tagName: 'option', + attributeBindings: ['value', 'selected'], + + defaultTemplate: function(context, options) { + options = { data: options.data, hash: {} }; + EmberHandlebars.helpers.bind.call(context, "view.label", options); + }, - function makeNodeCallbackFor(resolve, reject) { - return function (error, value) { - if (error) { - reject(error); - } else if (arguments.length > 2) { - resolve(slice.call(arguments, 1)); + init: function() { + this.labelPathDidChange(); + this.valuePathDidChange(); + + this._super(); + }, + + selected: computed(function() { + var content = get(this, 'content'), + selection = get(this, 'parentView.selection'); + if (get(this, 'parentView.multiple')) { + return selection && indexOf(selection, content.valueOf()) > -1; } else { - resolve(value); + // 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'), + + labelPathDidChange: observer('parentView.optionLabelPath', function() { + var labelPath = get(this, 'parentView.optionLabelPath'); + + if (!labelPath) { return; } + + defineProperty(this, 'label', computed(function() { + return get(this, labelPath); + }).property(labelPath)); + }), + + valuePathDidChange: observer('parentView.optionValuePath', function() { + var valuePath = get(this, 'parentView.optionValuePath'); + + if (!valuePath) { return; } + + defineProperty(this, 'value', computed(function() { + return get(this, valuePath); + }).property(valuePath)); + }) + }); + + var SelectOptgroup = CollectionView.extend({ + instrumentDisplay: 'Ember.SelectOptgroup', + + tagName: 'optgroup', + attributeBindings: ['label'], + + selectionBinding: 'parentView.selection', + multipleBinding: 'parentView.multiple', + optionLabelPathBinding: 'parentView.optionLabelPath', + optionValuePathBinding: 'parentView.optionValuePath', + + itemViewClassBinding: 'parentView.optionView' + }); /** - `RSVP.denodeify` takes a "node-style" function and returns a function that - will return an `RSVP.Promise`. You can use `denodeify` in Node.js or the - browser when you'd prefer to use promises over using callbacks. For example, - `denodeify` transforms the following: + 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. - ```javascript - var fs = require('fs'); + The text and `value` property of each ` + + + ``` - A promise can be in one of three states: pending, fulfilled, or rejected. + The `value` attribute of the selected `"); + return buffer; } - function rejectPromise(reason) { - reject(promise, reason); + function program3(depth0,data) { + + var stack1; + stack1 = helpers.each.call(depth0, "view.groupedContent", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(4, program4, data),contexts:[depth0],types:["ID"],data:data}); + if(stack1 || stack1 === 0) { data.buffer.push(stack1); } + else { data.buffer.push(''); } + } + function program4(depth0,data) { + + + data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.groupView", {hash:{ + 'content': ("content"), + 'label': ("label") + },hashTypes:{'content': "ID",'label': "ID"},hashContexts:{'content': depth0,'label': depth0},contexts:[depth0],types:["ID"],data:data}))); } - try { - resolver(resolvePromise, rejectPromise); - } catch(e) { - rejectPromise(e); + function program6(depth0,data) { + + var stack1; + stack1 = helpers.each.call(depth0, "view.content", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(7, program7, data),contexts:[depth0],types:["ID"],data:data}); + if(stack1 || stack1 === 0) { data.buffer.push(stack1); } + else { data.buffer.push(''); } + } + function program7(depth0,data) { + + + data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.optionView", {hash:{ + 'content': ("") + },hashTypes:{'content': "ID"},hashContexts:{'content': depth0},contexts:[depth0],types:["ID"],data:data}))); } - } - Promise.cast = cast; - Promise.all = all; - Promise.race = race; - Promise.resolve = Resolve; - Promise.reject = Reject; + stack1 = helpers['if'].call(depth0, "view.prompt", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(1, program1, data),contexts:[depth0],types:["ID"],data:data}); + if(stack1 || stack1 === 0) { data.buffer.push(stack1); } + stack1 = helpers['if'].call(depth0, "view.optionGroupPath", {hash:{},hashTypes:{},hashContexts:{},inverse:self.program(6, program6, data),fn:self.program(3, program3, data),contexts:[depth0],types:["ID"],data:data}); + if(stack1 || stack1 === 0) { data.buffer.push(stack1); } + return buffer; + + }), + attributeBindings: ['multiple', 'disabled', 'tabindex', 'name', 'required', 'autofocus', + 'form', 'size'], - var PENDING = void 0; - var SEALED = 0; - var FULFILLED = 1; - var REJECTED = 2; + /** + The `multiple` attribute of the select element. Indicates whether multiple + options can be selected. - function subscribe(parent, child, onFulfillment, onRejection) { - var subscribers = parent._subscribers; - var length = subscribers.length; + @property multiple + @type Boolean + @default false + */ + multiple: false, - subscribers[length] = child; - subscribers[length + FULFILLED] = onFulfillment; - subscribers[length + REJECTED] = onRejection; - } + /** + The `disabled` attribute of the select element. Indicates whether + the element is disabled from interactions. - function publish(promise, settled) { - var child, callback, subscribers = promise._subscribers, detail = promise._detail; + @property disabled + @type Boolean + @default false + */ + disabled: false, - if (config.instrument) { - instrument(settled === FULFILLED ? 'fulfilled' : 'rejected', promise); - } + /** + The `required` attribute of the select element. Indicates whether + a selected option is required for form validation. - for (var i = 0; i < subscribers.length; i += 3) { - child = subscribers[i]; - callback = subscribers[i + settled]; + @property required + @type Boolean + @default false + @since 1.5.0 + */ + required: false, - invokeCallback(settled, child, callback, detail); - } + /** + The list of options. - promise._subscribers = null; - } + If `optionLabelPath` and `optionValuePath` are not overridden, this should + be a list of strings, which will serve simultaneously as labels and values. - Promise.prototype = { - constructor: Promise, + Otherwise, this should be a list of objects. For instance: - _id: undefined, - _guidKey: guidKey, - _label: undefined, + ```javascript + Ember.Select.create({ + content: Ember.A([ + { id: 1, firstName: 'Yehuda' }, + { id: 2, firstName: 'Tom' } + ]), + optionLabelPath: 'content.firstName', + optionValuePath: 'content.id' + }); + ``` - _state: undefined, - _detail: undefined, - _subscribers: undefined, + @property content + @type Array + @default null + */ + content: null, - _onerror: function (reason) { - config.trigger('error', reason); - }, + /** + When `multiple` is `false`, the element of `content` that is currently + selected, if any. - /** - The primary way of interacting with a promise is through its `then` method, - which registers callbacks to receive either a promise's eventual value or the - reason why the promise cannot be fulfilled. + When `multiple` is `true`, an array of such elements. - ```js - findUser().then(function(user){ - // user is available - }, function(reason){ - // user is unavailable, and you are given the reason why - }); - ``` + @property selection + @type Object or Array + @default null + */ + selection: null, - Chaining - -------- + /** + 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. - The return value of `then` is itself a promise. This second, "downstream" - promise is resolved with the return value of the first promise's fulfillment - or rejection handler, or rejected if the handler throws an exception. + It is not currently supported in multiple selection mode. - ```js - findUser().then(function (user) { - return user.name; - }, function (reason) { - return "default name"; - }).then(function (userName) { - // If `findUser` fulfilled, `userName` will be the user's name, otherwise it - // will be `"default name"` - }); + @property 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'), - findUser().then(function (user) { - throw new Error("Found user, but still unhappy"); - }, function (reason) { - throw new Error("`findUser` rejected and we're unhappy"); - }).then(function (value) { - // never reached - }, function (reason) { - // if `findUser` fulfilled, `reason` will be "Found user, but still unhappy". - // If `findUser` rejected, `reason` will be "`findUser` rejected and we're unhappy". - }); - ``` - If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. + /** + If given, a top-most dummy option will be rendered to serve as a user + prompt. - ```js - findUser().then(function (user) { - throw new PedagogicalException("Upstream error"); - }).then(function (value) { - // never reached - }).then(function (value) { - // never reached - }, function (reason) { - // The `PedgagocialException` is propagated all the way down to here - }); - ``` + @property prompt + @type String + @default null + */ + prompt: null, - Assimilation - ------------ + /** + The path of the option labels. See [content](/api/classes/Ember.Select.html#property_content). - Sometimes the value you want to propagate to a downstream promise can only be - retrieved asynchronously. This can be achieved by returning a promise in the - fulfillment or rejection handler. The downstream promise will then be pending - until the returned promise is settled. This is called *assimilation*. + @property optionLabelPath + @type String + @default 'content' + */ + optionLabelPath: 'content', - ```js - findUser().then(function (user) { - return findCommentsByAuthor(user); - }).then(function (comments) { - // The user's comments are now available - }); - ``` + /** + The path of the option values. See [content](/api/classes/Ember.Select.html#property_content). - If the assimliated promise rejects, then the downstream promise will also reject. + @property optionValuePath + @type String + @default 'content' + */ + optionValuePath: 'content', - ```js - findUser().then(function (user) { - return findCommentsByAuthor(user); - }).then(function (comments) { - // If `findCommentsByAuthor` fulfills, we'll have the value here - }, function (reason) { - // If `findCommentsByAuthor` rejects, we'll have the reason here - }); - ``` + /** + The path of the option group. + When this property is used, `content` should be sorted by `optionGroupPath`. - Simple Example - -------------- + @property optionGroupPath + @type String + @default null + */ + optionGroupPath: null, - Synchronous Example + /** + The view class for optgroup. - ```javascript - var result; + @property groupView + @type Ember.View + @default Ember.SelectOptgroup + */ + groupView: SelectOptgroup, - try { - result = findResult(); - // success - } catch(reason) { - // failure - } - ``` + groupedContent: computed(function() { + var groupPath = get(this, 'optionGroupPath'); + var groupedContent = emberA(); + var content = get(this, 'content') || []; - Errback Example + forEach(content, function(item) { + var label = get(item, groupPath); - ```js - findResult(function(result, err){ - if (err) { - // failure - } else { - // success - } - }); - ``` + if (get(groupedContent, 'lastObject.label') !== label) { + groupedContent.pushObject({ + label: label, + content: emberA() + }); + } - Promise Example; + get(groupedContent, 'lastObject.content').push(item); + }); - ```javascript - findResult().then(function(result){ - // success - }, function(reason){ - // failure - }); - ``` + return groupedContent; + }).property('optionGroupPath', 'content.@each'), - Advanced Example - -------------- + /** + The view class for option. - Synchronous Example + @property optionView + @type Ember.View + @default Ember.SelectOption + */ + optionView: SelectOption, - ```javascript - var author, books; + _change: function() { + if (get(this, 'multiple')) { + this._changeMultiple(); + } else { + this._changeSingle(); + } + }, - try { - author = findAuthor(); - books = findBooksByAuthor(author); - // success - } catch(reason) { - // failure - } - ``` + 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(); + } + }), - Errback Example + valueDidChange: observer('value', function() { + var content = get(this, 'content'), + value = get(this, 'value'), + valuePath = get(this, 'optionValuePath').replace(/^content\.?/, ''), + selectedValue = (valuePath ? get(this, 'selection.' + valuePath) : get(this, 'selection')), + selection; - ```js + if (value !== selectedValue) { + selection = content ? content.find(function(obj) { + return value === (valuePath ? get(obj, valuePath) : obj); + }) : null; - function foundBooks(books) { + this.set('selection', selection); + } + }), - } - function failure(reason) { + _triggerChange: function() { + var selection = get(this, 'selection'); + var value = get(this, 'value'); - } + if (!isNone(selection)) { this.selectionDidChange(); } + if (!isNone(value)) { this.valueDidChange(); } - findAuthor(function(author, err){ - if (err) { - failure(err); - // failure - } else { - try { - findBoooksByAuthor(author, function(books, err) { - if (err) { - failure(err); - } else { - try { - foundBooks(books); - } catch(reason) { - failure(reason); - } - } - }); - } catch(error) { - failure(err); - } - // success - } - }); - ``` + this._change(); + }, - Promise Example; + _changeSingle: function() { + var selectedIndex = this.$()[0].selectedIndex, + content = get(this, 'content'), + prompt = get(this, 'prompt'); - ```javascript - findAuthor(). - then(findBooksByAuthor). - then(function(books){ - // found books - }).catch(function(reason){ - // something went wrong - }); - ``` + if (!content || !get(content, 'length')) { return; } + if (prompt && selectedIndex === 0) { set(this, 'selection', null); return; } - @method then - @param {Function} onFulfilled - @param {Function} onRejected - @param {String} label optional string for labeling the promise. - Useful for tooling. - @return {Promise} - */ - then: function(onFulfillment, onRejection, label) { - var promise = this; - this._onerror = null; + if (prompt) { selectedIndex -= 1; } + set(this, 'selection', content.objectAt(selectedIndex)); + }, - var thenPromise = new this.constructor(noop, label); - if (this._state) { - var callbacks = arguments; - config.async(function invokePromiseCallback() { - invokeCallback(promise._state, thenPromise, callbacks[promise._state - 1], promise._detail); - }); - } else { - subscribe(this, thenPromise, onFulfillment, onRejection); - } + _changeMultiple: function() { + var options = this.$('option:selected'), + prompt = get(this, 'prompt'), + offset = prompt ? 1 : 0, + content = get(this, 'content'), + selection = get(this, 'selection'); - if (config.instrument) { - instrument('chained', promise, thenPromise); - } + if (!content) { return; } + if (options) { + var selectedIndexes = options.map(function() { + return this.index - offset; + }).toArray(); + var newSelection = content.objectsAt(selectedIndexes); - return thenPromise; + if (isArray(selection)) { + replace(selection, 0, get(selection, 'length'), newSelection); + } else { + set(this, 'selection', newSelection); + } + } }, - /** - `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same - as the catch block of a try/catch statement. + _selectionDidChangeSingle: function() { + var el = this.get('element'); + if (!el) { return; } - ```js - function findAuthor(){ - throw new Error("couldn't find that author"); - } + var content = get(this, 'content'), + selection = get(this, 'selection'), + selectionIndex = content ? indexOf(content, selection) : -1, + prompt = get(this, 'prompt'); - // synchronous - try { - findAuthor(); - } catch(reason) { - // something went wrong + if (prompt) { selectionIndex += 1; } + if (el) { el.selectedIndex = selectionIndex; } + }, + + _selectionDidChangeMultiple: function() { + var content = get(this, 'content'), + selection = get(this, 'selection'), + selectedIndexes = content ? indexesOf(content, selection) : [-1], + prompt = get(this, 'prompt'), + offset = prompt ? 1 : 0, + options = this.$('option'), + adjusted; + + if (options) { + options.each(function() { + adjusted = this.index > -1 ? this.index - offset : -1; + this.selected = indexOf(selectedIndexes, adjusted) > -1; + }); + } + }, + + init: function() { + this._super(); + this.on("didInsertElement", this, this._triggerChange); + this.on("change", this, this._change); } + }); - // async with promises - findAuthor().catch(function(reason){ - // something went wrong - }); - ``` + __exports__["default"] = Select; + __exports__.Select = Select; + __exports__.SelectOption = SelectOption; + __exports__.SelectOptgroup = SelectOptgroup; + }); +define("ember-handlebars/controls/text_area", + ["ember-metal/property_get","ember-views/views/component","ember-handlebars/controls/text_support","ember-metal/mixin","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; - @method catch - @param {Function} onRejection - @param {String} label optional string for labeling the promise. - Useful for tooling. - @return {Promise} + /** + @module ember + @submodule ember-handlebars */ - 'catch': function(onRejection, label) { - return this.then(null, onRejection, label); - }, + var get = __dependency1__.get; + var Component = __dependency2__["default"]; + var TextSupport = __dependency3__["default"]; + var observer = __dependency4__.observer; /** - `finally` will be invoked regardless of the promise's fate just as native - try/catch/finally behaves - - Synchronous example: - - ```js - findAuthor() { - if (Math.random() > 0.5) { - throw new Error(); - } - return new Author(); - } + The internal class used to create textarea element when the `{{textarea}}` + helper is used. - try { - return findAuthor(); // succeed or fail - } catch(error) { - return findOtherAuther(); - } finally { - // always runs - // doesn't affect the return value - } - ``` + See [handlebars.helpers.textarea](/api/classes/Ember.Handlebars.helpers.html#method_textarea) for usage details. - Asynchronous example: + ## Layout and LayoutName properties - ```js - findAuthor().catch(function(reason){ - return findOtherAuther(); - }).finally(function(){ - // author was either found, or not - }); - ``` + 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. - @method finally - @param {Function} callback - @param {String} label optional string for labeling the promise. - Useful for tooling. - @return {Promise} + @class TextArea + @namespace Ember + @extends Ember.Component + @uses Ember.TextSupport */ - 'finally': function(callback, label) { - var constructor = this.constructor; + __exports__["default"] = Component.extend(TextSupport, { + instrumentDisplay: '{{textarea}}', - return this.then(function(value) { - return constructor.cast(callback()).then(function(){ - return value; - }); - }, function(reason) { - return constructor.cast(callback()).then(function(){ - throw reason; - }); - }, label); - } - }; + classNames: ['ember-text-area'], - function invokeCallback(settled, promise, callback, detail) { - var hasCallback = isFunction(callback), - value, error, succeeded, failed; + tagName: "textarea", + attributeBindings: ['rows', 'cols', 'name', 'selectionEnd', 'selectionStart', 'wrap'], + rows: null, + cols: null, - if (hasCallback) { - try { - value = callback(detail); - succeeded = true; - } catch(e) { - failed = true; - error = e; + _updateElementValue: observer('value', function() { + // We do this check so cursor position doesn't get affected in IE + var value = get(this, 'value'), + $el = this.$(); + if ($el && value !== $el.val()) { + $el.val(value); } - } else { - value = detail; - succeeded = true; - } + }), - if (handleThenable(promise, value)) { - return; - } else if (hasCallback && succeeded) { - resolve(promise, value); - } else if (failed) { - reject(promise, error); - } else if (settled === FULFILLED) { - resolve(promise, value); - } else if (settled === REJECTED) { - reject(promise, value); + init: function() { + this._super(); + this.on("didInsertElement", this, this._updateElementValue); } - } + }); + }); +define("ember-handlebars/controls/text_field", + ["ember-metal/property_get","ember-metal/property_set","ember-views/views/component","ember-handlebars/controls/text_support","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-handlebars + */ - function handleThenable(promise, value) { - var then = null, - resolved; + var get = __dependency1__.get; + var set = __dependency2__.set; + var Component = __dependency3__["default"]; + var TextSupport = __dependency4__["default"]; - try { - if (promise === value) { - throw new TypeError("A promises callback cannot return that same promise."); - } + /** - if (objectOrFunction(value)) { - then = value.then; + The internal class used to create text inputs when the `{{input}}` + helper is used with `type` of `text`. - if (isFunction(then)) { - then.call(value, function(val) { - if (resolved) { return true; } - resolved = true; + See [Handlebars.helpers.input](/api/classes/Ember.Handlebars.helpers.html#method_input) for usage details. - if (value !== val) { - resolve(promise, val); - } else { - fulfill(promise, val); - } - }, function(val) { - if (resolved) { return true; } - resolved = true; + ## Layout and LayoutName properties - reject(promise, val); - }, 'derived from: ' + (promise._label || ' unknown promise')); + 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. - return true; - } - } - } catch (error) { - if (resolved) { return true; } - reject(promise, error); - return true; - } + @class TextField + @namespace Ember + @extends Ember.Component + @uses Ember.TextSupport + */ + __exports__["default"] = Component.extend(TextSupport, { + instrumentDisplay: '{{input type="text"}}', - return false; - } + classNames: ['ember-text-field'], + tagName: "input", + attributeBindings: ['type', 'value', 'size', 'pattern', 'name', 'min', 'max', + 'accept', 'autocomplete', 'autosave', 'formaction', + 'formenctype', 'formmethod', 'formnovalidate', 'formtarget', + 'height', 'inputmode', 'list', 'multiple', 'step', + 'width'], - function resolve(promise, value) { - if (promise === value) { - fulfill(promise, value); - } else if (!handleThenable(promise, value)) { - fulfill(promise, value); - } - } + /** + The `value` attribute of the input element. As the user inputs text, this + property is updated live. - function fulfill(promise, value) { - if (promise._state !== PENDING) { return; } - promise._state = SEALED; - promise._detail = value; + @property value + @type String + @default "" + */ + value: "", - config.async(publishFulfillment, promise); - } + /** + The `type` attribute of the input element. - function reject(promise, reason) { - if (promise._state !== PENDING) { return; } - promise._state = SEALED; - promise._detail = reason; + @property type + @type String + @default "text" + */ + type: "text", - config.async(publishRejection, promise); - } + /** + The `size` of the text field in characters. - function publishFulfillment(promise) { - publish(promise, promise._state = FULFILLED); - } + @property size + @type String + @default null + */ + size: null, - function publishRejection(promise) { - if (promise._onerror) { - promise._onerror(promise._detail); - } + /** + The `pattern` attribute of input element. - publish(promise, promise._state = REJECTED); - } + @property pattern + @type String + @default null + */ + pattern: null, + + /** + The `min` attribute of input element used with `type="number"` or `type="range"`. + + @property min + @type String + @default null + @since 1.4.0 + */ + min: null, + + /** + The `max` attribute of input element used with `type="number"` or `type="range"`. + + @property max + @type String + @default null + @since 1.4.0 + */ + max: null + }); }); -define("rsvp/promise/all", - ["../utils","exports"], - function(__dependency1__, __exports__) { +define("ember-handlebars/controls/text_support", + ["ember-metal/property_get","ember-metal/property_set","ember-metal/mixin","ember-runtime/mixins/target_action_support","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { "use strict"; - var isArray = __dependency1__.isArray; - var isNonThenable = __dependency1__.isNonThenable; + /** + @module ember + @submodule ember-handlebars + */ + + var get = __dependency1__.get; + var set = __dependency2__.set; + var Mixin = __dependency3__.Mixin; + var TargetActionSupport = __dependency4__["default"]; /** - `RSVP.Promise.all` accepts an array of promises, and returns a new promise which - is fulfilled with an array of fulfillment values for the passed promises, or - rejected with the reason of the first passed promise to be rejected. It casts all - elements of the passed iterable to promises as it runs this algorithm. + Shared mixin used by `Ember.TextField` and `Ember.TextArea`. - Example: + @class TextSupport + @namespace Ember + @uses Ember.TargetActionSupport + @extends Ember.Mixin + @private + */ + var TextSupport = Mixin.create(TargetActionSupport, { + value: "", - ```javascript - var promise1 = RSVP.resolve(1); - var promise2 = RSVP.resolve(2); - var promise3 = RSVP.resolve(3); - var promises = [ promise1, promise2, promise3 ]; - - RSVP.Promise.all(promises).then(function(array){ - // The array here would be [ 1, 2, 3 ]; - }); - ``` + attributeBindings: ['placeholder', 'disabled', 'maxlength', 'tabindex', 'readonly', + 'autofocus', 'form', 'selectionDirection', 'spellcheck', 'required', + 'title', 'autocapitalize', 'autocorrect'], + placeholder: null, + disabled: false, + maxlength: null, - If any of the `promises` given to `RSVP.all` are rejected, the first promise - that is rejected will be given as an argument to the returned promises's - rejection handler. For example: + init: function() { + this._super(); + this.on("focusOut", this, this._elementValueDidChange); + this.on("change", this, this._elementValueDidChange); + this.on("paste", this, this._elementValueDidChange); + this.on("cut", this, this._elementValueDidChange); + this.on("input", this, this._elementValueDidChange); + this.on("keyUp", this, this.interpretKeyEvents); + }, - Example: + /** + The action to be sent when the user presses the return key. - ```javascript - var promise1 = RSVP.resolve(1); - var promise2 = RSVP.reject(new Error("2")); - var promise3 = RSVP.reject(new Error("3")); - var promises = [ promise1, promise2, promise3 ]; + 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. - RSVP.Promise.all(promises).then(function(array){ - // Code here never runs because there are rejected promises! - }, function(error) { - // error.message === "2" - }); - ``` + @property action + @type String + @default null + */ + action: null, - @method all - @for Ember.RSVP.Promise - @param {Array} entries array of promises - @param {String} label optional string for labeling the promise. - Useful for tooling. - @return {Promise} promise that is fulfilled when all `promises` have been - fulfilled, or rejected if any of them become rejected. - @static - */ - __exports__["default"] = function all(entries, label) { + /** + The event that should send the action. - /*jshint validthis:true */ - var Constructor = this; + Options are: - return new Constructor(function(resolve, reject) { - if (!isArray(entries)) { - throw new TypeError('You must pass an array to all.'); - } + * `enter`: the user pressed enter + * `keyPress`: the user pressed a key - var remaining = entries.length; - var results = new Array(remaining); - var entry, pending = true; + @property onEvent + @type String + @default enter + */ + onEvent: 'enter', - if (remaining === 0) { - resolve(results); - return; - } + /** + Whether they `keyUp` event that triggers an `action` to be sent continues + propagating to other views. - function fulfillmentAt(index) { - return function(value) { - results[index] = value; - if (--remaining === 0) { - resolve(results); - } - }; - } + 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. - function onRejection(reason) { - remaining = 0; - reject(reason); - } + If you would like parent views to receive the `keyUp` event even after an + action has been dispatched, set `bubbles` to true. - for (var index = 0; index < entries.length; index++) { - entry = entries[index]; - if (isNonThenable(entry)) { - results[index] = entry; - if (--remaining === 0) { - resolve(results); - } - } else { - Constructor.cast(entry).then(fulfillmentAt(index), onRejection); - } - } - }, label); - }; - }); -define("rsvp/promise/cast", - ["exports"], - function(__exports__) { - "use strict"; - /** - `RSVP.Promise.cast` coerces its argument to a promise, or returns the - argument if it is already a promise which shares a constructor with the caster. + @property bubbles + @type Boolean + @default false + */ + bubbles: false, - Example: + interpretKeyEvents: function(event) { + var map = TextSupport.KEY_EVENTS; + var method = map[event.keyCode]; - ```javascript - var promise = RSVP.Promise.resolve(1); - var casted = RSVP.Promise.cast(promise); + this._elementValueDidChange(); + if (method) { return this[method](event); } + }, - console.log(promise === casted); // true - ``` + _elementValueDidChange: function() { + set(this, 'value', this.$().val()); + }, - In the case of a promise whose constructor does not match, it is assimilated. - The resulting promise will fulfill or reject based on the outcome of the - promise being casted. + /** + Called when the user inserts a new line. - Example: + Called by the `Ember.TextSupport` mixin on keyUp if keycode matches 13. + Uses sendAction to send the `enter` action. - ```javascript - var thennable = $.getJSON('/api/foo'); - var casted = RSVP.Promise.cast(thennable); + @method insertNewline + @param {Event} event + */ + insertNewline: function(event) { + sendAction('enter', this, event); + sendAction('insert-newline', this, event); + }, - console.log(thennable === casted); // false - console.log(casted instanceof RSVP.Promise) // true + /** + Called when the user hits escape. - casted.then(function(data) { - // data is the value getJSON fulfills with - }); - ``` + Called by the `Ember.TextSupport` mixin on keyUp if keycode matches 27. + Uses sendAction to send the `escape-press` action. - In the case of a non-promise, a promise which will fulfill with that value is - returned. + @method cancel + @param {Event} event + */ + cancel: function(event) { + sendAction('escape-press', this, event); + }, - Example: + /** + Called when the text area is focused. - ```javascript - var value = 1; // could be a number, boolean, string, undefined... - var casted = RSVP.Promise.cast(value); + Uses sendAction to send the `focus-in` action. - console.log(value === casted); // false - console.log(casted instanceof RSVP.Promise) // true + @method focusIn + @param {Event} event + */ + focusIn: function(event) { + sendAction('focus-in', this, event); + }, - casted.then(function(val) { - val === value // => true - }); - ``` + /** + Called when the text area is blurred. - `RSVP.Promise.cast` is similar to `RSVP.Promise.resolve`, but `RSVP.Promise.cast` differs in the - following ways: + Uses sendAction to send the `focus-out` action. - * `RSVP.Promise.cast` serves as a memory-efficient way of getting a promise, when you - have something that could either be a promise or a value. RSVP.resolve - will have the same effect but will create a new promise wrapper if the - argument is a promise. - * `RSVP.Promise.cast` is a way of casting incoming thenables or promise subclasses to - promises of the exact class specified, so that the resulting object's `then` is - ensured to have the behavior of the constructor you are calling cast on (i.e., RSVP.Promise). + @method focusOut + @param {Event} event + */ + focusOut: function(event) { + sendAction('focus-out', this, event); + }, - @method cast - @param {Object} object to be casted - @param {String} label optional string for labeling the promise. - Useful for tooling. - @return {Promise} promise - @static - */ + /** + Called when the user presses a key. Enabled by setting + the `onEvent` property to `keyPress`. - __exports__["default"] = function cast(object, label) { - /*jshint validthis:true */ - var Constructor = this; + Uses sendAction to send the `key-press` action. - if (object && typeof object === 'object' && object.constructor === Constructor) { - return object; + @method keyPress + @param {Event} event + */ + keyPress: function(event) { + sendAction('key-press', this, event); } - return new Constructor(function(resolve) { - resolve(object); - }, label); + }); + + TextSupport.KEY_EVENTS = { + 13: 'insertNewline', + 27: 'cancel' }; - }); -define("rsvp/promise/race", - ["../utils","exports"], - function(__dependency1__, __exports__) { - "use strict"; - /* global toString */ - var isArray = __dependency1__.isArray; - var isFunction = __dependency1__.isFunction; - var isNonThenable = __dependency1__.isNonThenable; + // In principle, this shouldn't be necessary, but the legacy + // sendAction semantics for TextField are different from + // the component semantics so this method normalizes them. + function sendAction(eventName, view, event) { + var action = get(view, eventName), + on = get(view, 'onEvent'), + value = get(view, 'value'); - /** - `RSVP.Promise.race` returns a new promise which is settled in the same way as the - first passed promise to settle. + // 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); + } - Example: + view.sendAction(eventName, value); - ```javascript - var promise1 = new RSVP.Promise(function(resolve, reject){ - setTimeout(function(){ - resolve("promise 1"); - }, 200); - }); + if (action || on === eventName) { + if(!get(view, 'bubbles')) { + event.stopPropagation(); + } + } + } - var promise2 = new RSVP.Promise(function(resolve, reject){ - setTimeout(function(){ - resolve("promise 2"); - }, 100); - }); + __exports__["default"] = TextSupport; + }); +define("ember-handlebars/ext", + ["ember-metal/core","ember-runtime/system/string","ember-handlebars-compiler","ember-metal/property_get","ember-metal/binding","ember-metal/error","ember-metal/mixin","ember-metal/is_empty","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // Ember.FEATURES, Ember.assert, Ember.Handlebars, Ember.lookup + // var emberAssert = Ember.assert; - RSVP.Promise.race([promise1, promise2]).then(function(result){ - // result === "promise 2" because it was resolved before promise1 - // was resolved. - }); - ``` + var fmt = __dependency2__.fmt; - `RSVP.Promise.race` is deterministic in that only the state of the first - settled promise matters. For example, even if other promises given to the - `promises` array argument are resolved, but the first settled promise has - become rejected before the other promises became fulfilled, the returned - promise will become rejected: + var EmberHandlebars = __dependency3__["default"]; + var helpers = EmberHandlebars.helpers; - ```javascript - var promise1 = new RSVP.Promise(function(resolve, reject){ - setTimeout(function(){ - resolve("promise 1"); - }, 200); - }); + var get = __dependency4__.get; + var isGlobalPath = __dependency5__.isGlobalPath; + var EmberError = __dependency6__["default"]; + var IS_BINDING = __dependency7__.IS_BINDING; - var promise2 = new RSVP.Promise(function(resolve, reject){ - setTimeout(function(){ - reject(new Error("promise 2")); - }, 100); - }); + // late bound via requireModule because of circular dependencies. + var resolveHelper, + SimpleHandlebarsView; - RSVP.Promise.race([promise1, promise2]).then(function(result){ - // Code here never runs - }, function(reason){ - // reason.message === "promise2" because promise 2 became rejected before - // promise 1 became fulfilled - }); - ``` + var isEmpty = __dependency8__["default"]; - An example real-world use case is implementing timeouts: + var slice = [].slice, originalTemplate = EmberHandlebars.template; - ```javascript - RSVP.Promise.race([ajax('foo.json'), timeout(5000)]) - ``` + /** + If a path starts with a reserved keyword, returns the root + that should be used. - @method race - @param {Array} promises array of promises to observe - @param {String} label optional string for describing the promise returned. - Useful for tooling. - @return {Promise} a promise which settles in the same way as the first passed - promise to settle. - @static + @private + @method normalizePath + @for Ember + @param root {Object} + @param path {String} + @param data {Hash} */ - __exports__["default"] = function race(entries, label) { - /*jshint validthis:true */ - var Constructor = this, entry; - - return new Constructor(function(resolve, reject) { - if (!isArray(entries)) { - throw new TypeError('You must pass an array to race.'); + function normalizePath(root, path, data) { + var keywords = (data && data.keywords) || {}, + keyword, isKeyword; + + // Get the first segment of the path. For example, if the + // path is "foo.bar.baz", returns "foo". + keyword = path.split('.', 1)[0]; + + // Test to see if the first path is a keyword that has been + // passed along in the view's data hash. If so, we will treat + // that object as the new root. + if (keywords.hasOwnProperty(keyword)) { + // Look up the value in the template's data hash. + root = keywords[keyword]; + isKeyword = true; + + // Handle cases where the entire path is the reserved + // word. In that case, return the object itself. + if (path === keyword) { + path = ''; + } else { + // Strip the keyword from the path and look up + // the remainder from the newly found root. + path = path.substr(keyword.length+1); } + } - var pending = true; + return { root: root, path: path, isKeyword: isKeyword }; + } - function onFulfillment(value) { if (pending) { pending = false; resolve(value); } } - function onRejection(reason) { if (pending) { pending = false; reject(reason); } } - for (var i = 0; i < entries.length; i++) { - entry = entries[i]; - if (isNonThenable(entry)) { - pending = false; - resolve(entry); - return; - } else { - Constructor.cast(entry).then(onFulfillment, onRejection); - } - } - }, label); - }; - }); -define("rsvp/promise/reject", - ["exports"], - function(__exports__) { - "use strict"; /** - `RSVP.Promise.reject` returns a promise rejected with the passed `reason`. - It is shorthand for the following: + Lookup both on root and on window. If the path starts with + a keyword, the corresponding object will be looked up in the + template's data hash and used to resolve the path. + + @method get + @for Ember.Handlebars + @param {Object} root The object to look up the property on + @param {String} path The path to be lookedup + @param {Object} options The template's option hash + */ + function handlebarsGet(root, path, options) { + var data = options && options.data, + normalizedPath = normalizePath(root, path, data), + value; - ```javascript - var promise = new RSVP.Promise(function(resolve, reject){ - reject(new Error('WHOOPS')); - }); + + root = normalizedPath.root; + path = normalizedPath.path; - promise.then(function(value){ - // Code here doesn't run because the promise is rejected! - }, function(reason){ - // reason.message === 'WHOOPS' - }); - ``` + value = get(root, path); - Instead of writing the above, your code now simply becomes the following: + if (value === undefined && root !== Ember.lookup && isGlobalPath(path)) { + value = get(Ember.lookup, path); + } + - ```javascript - var promise = RSVP.Promise.reject(new Error('WHOOPS')); + return value; + } - promise.then(function(value){ - // Code here doesn't run because the promise is rejected! - }, function(reason){ - // reason.message === 'WHOOPS' - }); - ``` + /** + This method uses `Ember.Handlebars.get` to lookup a value, then ensures + that the value is escaped properly. - @method reject - @param {Any} reason value that the returned promise will be rejected with. - @param {String} label optional string for identifying the returned promise. - Useful for tooling. - @return {Promise} a promise rejected with the given `reason`. - @static + If `unescaped` is a truthy value then the escaping will not be performed. + + @method getEscaped + @for Ember.Handlebars + @param {Object} root The object to look up the property on + @param {String} path The path to be lookedup + @param {Object} options The template's option hash + @since 1.4.0 */ - __exports__["default"] = function reject(reason, label) { - /*jshint validthis:true */ - var Constructor = this; + function getEscaped(root, path, options) { + var result = handlebarsGet(root, path, options); - return new Constructor(function (resolve, reject) { - reject(reason); - }, label); - }; - }); -define("rsvp/promise/resolve", - ["exports"], - function(__exports__) { - "use strict"; - /** - `RSVP.Promise.resolve` returns a promise that will become resolved with the - passed `value`. It is shorthand for the following: + if (result === null || result === undefined) { + result = ""; + } else if (!(result instanceof Handlebars.SafeString)) { + result = String(result); + } + if (!options.hash.unescaped){ + result = Handlebars.Utils.escapeExpression(result); + } - ```javascript - var promise = new RSVP.Promise(function(resolve, reject){ - resolve(1); - }); + return result; + } - promise.then(function(value){ - // value === 1 - }); - ``` + __exports__.getEscaped = getEscaped;function resolveParams(context, params, options) { + var resolvedParams = [], types = options.types, param, type; - Instead of writing the above, your code now simply becomes the following: + for (var i=0, l=params.length; i true + view.appendChild(bindView); - container.unregister('model:user') - container.lookup('model:user') === undefined //=> true - ``` + // Assemble list of watched properties that'll re-render this helper. + for (boundOption in boundOptions) { + if (boundOptions.hasOwnProperty(boundOption)) { + watchedProperties.push(normalizePath(currentContext, boundOptions[boundOption], data)); + } + } - @method unregister - @param {String} fullName - */ - unregister: function(fullName) { - validateFullName(fullName); + // Observe each property. + for (loc = 0, len = watchedProperties.length; loc < len; ++loc) { + property = watchedProperties[loc]; + view.registerObserver(property.root, property.path, bindView, bindView.rerender); + } - var normalizedName = this.normalize(fullName); + if (types[0] !== 'ID' || normalizedProperties.length === 0) { + return; + } - this.registry.remove(normalizedName); - this.cache.remove(normalizedName); - this.factoryCache.remove(normalizedName); - this.resolveCache.remove(normalizedName); - this._options.remove(normalizedName); - }, + // Add dependent key observers to the first param + var normalized = normalizedProperties[0], + pathRoot = normalized.root, + path = normalized.path; - /** - Given a fullName return the corresponding factory. + if(!isEmpty(path)) { + prefixPathForDependentKeys = path + '.'; + } + for (var i=0, l=dependentKeys.length; i Twitter - ``` + @private + @method evaluateUnboundHelper + @param {Function} fn + @param {Object} context + @param {Array} normalizedProperties + @param {String} options + */ + function evaluateUnboundHelper(context, fn, normalizedProperties, options) { + var args = [], + hash = options.hash, + boundOptions = hash.boundOptions, + types = slice.call(options.types, 1), + loc, + len, + property, + propertyType, + boundOption; - Optionally the container can be provided with a custom resolver. - If provided, `resolve` will first provide the custom resolver - the oppertunity to resolve the fullName, otherwise it will fallback - to the registry. + for (boundOption in boundOptions) { + if (!boundOptions.hasOwnProperty(boundOption)) { continue; } + hash[boundOption] = handlebarsGet(context, boundOptions[boundOption], options); + } - ```javascript - var container = new Container(); - container.resolver = function(fullName) { - // lookup via the module system of choice - }; + for(loc = 0, len = normalizedProperties.length; loc < len; ++loc) { + property = normalizedProperties[loc]; + propertyType = types[loc]; + if(propertyType === "ID") { + args.push(handlebarsGet(property.root, property.path, options)); + } else { + args.push(property.path); + } + } + args.push(options); + return fn.apply(context, args); + } - // the twitter factory is added to the module system - container.resolve('api:twitter') // => Twitter - ``` + /** + Overrides Handlebars.template so that we can distinguish + user-created, top-level templates from inner contexts. - @method resolve - @param {String} fullName - @return {Function} fullName's factory - */ - resolve: function(fullName) { - validateFullName(fullName); + @private + @method template + @for Ember.Handlebars + @param {String} spec + */ + function template(spec) { + var t = originalTemplate(spec); + t.isTop = true; + return t; + } - var normalizedName = this.normalize(fullName); - var cached = this.resolveCache.get(normalizedName); + __exports__.template = template;__exports__.normalizePath = normalizePath; + __exports__.makeBoundHelper = makeBoundHelper; + __exports__.handlebarsGet = handlebarsGet; + __exports__.evaluateUnboundHelper = evaluateUnboundHelper; + }); +define("ember-handlebars/helpers/binding", + ["ember-metal/core","ember-handlebars-compiler","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-runtime/system/string","ember-metal/platform","ember-metal/is_none","ember-metal/enumerable_utils","ember-metal/array","ember-views/views/view","ember-metal/run_loop","ember-metal/observer","ember-metal/binding","ember-views/system/jquery","ember-handlebars/ext","ember-runtime/keys","ember-handlebars/views/handlebars_bound_view","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-handlebars + */ - if (cached) { return cached; } + var Ember = __dependency1__["default"]; + // Ember.assert, Ember.warn, uuid + // var emberAssert = Ember.assert, Ember.warn = Ember.warn; + + var EmberHandlebars = __dependency2__["default"]; + var get = __dependency3__.get; + var set = __dependency4__.set; + var apply = __dependency5__.apply; + var uuid = __dependency5__.uuid; + var fmt = __dependency6__.fmt; + var o_create = __dependency7__.create; + var isNone = __dependency8__["default"]; + var EnumerableUtils = __dependency9__["default"]; + var forEach = __dependency10__.forEach; + var View = __dependency11__["default"]; + var run = __dependency12__["default"]; + var removeObserver = __dependency13__.removeObserver; + var isGlobalPath = __dependency14__.isGlobalPath; + var emberBind = __dependency14__.bind; + var jQuery = __dependency15__["default"]; + var isArray = __dependency5__.isArray; + var handlebarsGetEscaped = __dependency16__.getEscaped; + var keys = __dependency17__["default"]; + + var _HandlebarsBoundView = __dependency18__._HandlebarsBoundView; + var SimpleHandlebarsView = __dependency18__.SimpleHandlebarsView; + + var normalizePath = __dependency16__.normalizePath; + var handlebarsGet = __dependency16__.handlebarsGet; + var getEscaped = __dependency16__.getEscaped; + + var guidFor = __dependency5__.guidFor; + var typeOf = __dependency5__.typeOf; + + var helpers = EmberHandlebars.helpers; + var SafeString = EmberHandlebars.SafeString; + + function exists(value) { + return !isNone(value); + } + + var WithView = _HandlebarsBoundView.extend({ + init: function() { + var controller; - var resolved = this.resolver(normalizedName) || this.registry.get(normalizedName); + apply(this, this._super, arguments); - this.resolveCache.set(normalizedName, resolved); + var keywords = this.templateData.keywords; + var keywordName = this.templateHash.keywordName; + var keywordPath = this.templateHash.keywordPath; + var controllerName = this.templateHash.controller; + var preserveContext = this.preserveContext; - return resolved; - }, + if (controllerName) { + var previousContext = this.previousContext; + controller = this.container.lookupFactory('controller:'+controllerName).create({ + parentController: previousContext, + target: previousContext + }); - /** - A hook that can be used to describe how the resolver will - attempt to find the factory. + this._generatedController = controller; - For example, the default Ember `.describe` returns the full - class name (including namespace) where Ember's resolver expects - to find the `fullName`. + if (!preserveContext) { + this.set('controller', controller); - @method describe - @param {String} fullName - @return {string} described fullName - */ - describe: function(fullName) { - return fullName; - }, + this.valueNormalizerFunc = function(result) { + controller.set('model', result); + return controller; + }; + } else { + var controllerPath = jQuery.expando + guidFor(controller); + keywords[controllerPath] = controller; + emberBind(keywords, controllerPath + '.model', keywordPath); + keywordPath = controllerPath; + } + } - /** - A hook to enable custom fullName normalization behaviour + if (preserveContext) { + emberBind(keywords, keywordName, keywordPath); + } - @method normalize - @param {String} fullName - @return {string} normalized fullName - */ - normalize: function(fullName) { - return fullName; }, + willDestroy: function() { + this._super(); - /** - @method makeToString + if (this._generatedController) { + this._generatedController.destroy(); + } + } + }); - @param {any} factory - @param {string} fullName - @return {function} toString function - */ - makeToString: function(factory, fullName) { - return factory.toString(); - }, + // 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) { + var data = options.data, + fn = options.fn, + inverse = options.inverse, + view = data.view, + normalized, observer, i; + + // 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; + + normalized = normalizePath(currentContext, property, data); + + // Set up observers for observable objects + if ('object' === typeof this) { + if (data.insideGroup) { + observer = function() { + while (view._contextView) { + view = view._contextView; + } + run.once(view, 'rerender'); + }; - /** - Given a fullName return a corresponding instance. + var template, context, result = handlebarsGet(currentContext, property, options); - The default behaviour is for lookup to return a singleton instance. - The singleton is scoped to the container, allowing multiple containers - to all have their own locally scoped singletons. + result = valueNormalizer ? valueNormalizer(result) : result; - ```javascript - var container = new Container(); - container.register('api:twitter', Twitter); + context = preserveContext ? currentContext : result; + if (shouldDisplay(result)) { + template = fn; + } else if (inverse) { + template = inverse; + } - var twitter = container.lookup('api:twitter'); + template(context, { data: options.data }); + } else { + var viewClass = _HandlebarsBoundView; + var viewOptions = { + preserveContext: preserveContext, + shouldDisplayFunc: shouldDisplay, + valueNormalizerFunc: valueNormalizer, + displayTemplate: fn, + inverseTemplate: inverse, + path: property, + pathRoot: currentContext, + previousContext: currentContext, + isEscaped: !options.hash.unescaped, + templateData: options.data, + templateHash: options.hash, + helperName: options.helperName + }; - twitter instanceof Twitter; // => true + if (options.isWithHelper) { + viewClass = WithView; + } - // by default the container will return singletons - var twitter2 = container.lookup('api:twitter'); - twitter instanceof Twitter; // => true + // 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); - twitter === twitter2; //=> true - ``` + view.appendChild(bindView); - If singletons are not wanted an optional flag can be provided at lookup. + observer = function() { + run.scheduleOnce('render', bindView, 'rerenderIfNeeded'); + }; + } - ```javascript - var container = new Container(); - container.register('api:twitter', Twitter); + // Observes the given property on the context and + // tells the Ember._HandlebarsBoundView to re-render. If property + // is an empty string, we are printing the current context + // object ({{this}}) so updating it is not our responsibility. + if (normalized.path !== '') { + view.registerObserver(normalized.root, normalized.path, observer); + if (childProperties) { + for (i=0; i false - ``` + normalized = normalizePath(currentContext, property, data); + pathRoot = normalized.root; - @method lookup - @param {String} fullName - @param {Object} options - @return {any} - */ - lookup: function(fullName, options) { - validateFullName(fullName); - return lookup(this, this.normalize(fullName), options); - }, + // Set up observers for observable objects + if (pathRoot && ('object' === typeof pathRoot)) { + if (data.insideGroup) { + observer = function() { + while (view._contextView) { + view = view._contextView; + } + run.once(view, 'rerender'); + }; - /** - Given a fullName return the corresponding factory. + output = handlebarsGetEscaped(currentContext, property, options); - @method lookupFactory - @param {String} fullName - @return {any} - */ - lookupFactory: function(fullName) { - validateFullName(fullName); - return factoryFor(this, this.normalize(fullName)); - }, + data.buffer.push(output); + } else { + var bindView = new SimpleHandlebarsView( + property, currentContext, !options.hash.unescaped, options.data + ); - /** - Given a fullName check if the container is aware of its factory - or singleton instance. + bindView._parentView = view; + view.appendChild(bindView); - @method has - @param {String} fullName - @return {Boolean} - */ - has: function(fullName) { - validateFullName(fullName); - return has(this, this.normalize(fullName)); - }, - - /** - Allow registering options for all factories of a type. + observer = function() { + run.scheduleOnce('render', bindView, 'rerender'); + }; + } - ```javascript - var container = new Container(); + // Observes the given property on the context and + // tells the Ember._HandlebarsBoundView to re-render. If property + // is an empty string, we are printing the current context + // object ({{this}}) so updating it is not our responsibility. + if (normalized.path !== '') { + view.registerObserver(normalized.root, normalized.path, observer); + } + } else { + // The object is not observable, so just render it out and + // be done with it. + output = handlebarsGetEscaped(currentContext, property, options); + data.buffer.push(output); + } + } - // if all of type `connection` must not be singletons - container.optionsForType('connection', { singleton: false }); + function shouldDisplayIfHelperContent(result) { + var truthy = result && get(result, 'isTruthy'); + if (typeof truthy === 'boolean') { return truthy; } - container.register('connection:twitter', TwitterConnection); - container.register('connection:facebook', FacebookConnection); + if (isArray(result)) { + return get(result, 'length') !== 0; + } else { + return !!result; + } + } - var twitter = container.lookup('connection:twitter'); - var twitter2 = container.lookup('connection:twitter'); + /** + '_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. - twitter === twitter2; // => false + This would not be typically invoked by directly. - var facebook = container.lookup('connection:facebook'); - var facebook2 = container.lookup('connection:facebook'); + @private + @method _triageMustache + @for Ember.Handlebars.helpers + @param {String} property Property/helperID to triage + @param {Object} options hash of template/rendering 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); - facebook === facebook2; // => false - ``` + var helper = EmberHandlebars.resolveHelper(options.data.view.container, property); + if (helper) { + return helper.call(this, options); + } - @method optionsForType - @param {String} type - @param {Object} options - */ - optionsForType: function(type, options) { - if (this.parent) { illegalChildOperation('optionsForType'); } + return helpers.bind.call(this, property, options); + } - this._typeOptions.set(type, options); - }, + /** + Used to lookup/resolve handlebars helpers. The lookup order is: - /** - @method options - @param {String} type - @param {Object} options - */ - options: function(type, options) { - this.optionsForType(type, options); - }, + * 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 - /** - Used only via `injection`. + @private + @method resolveHelper + @param {Container} container + @param {String} name the name of the helper to lookup + @return {Handlebars Helper} + */ + function resolveHelper(container, name) { + if (helpers[name]) { + return helpers[name]; + } - Provides a specialized form of injection, specifically enabling - all objects of one type to be injected with a reference to another - object. + if (!container || name.indexOf('-') === -1) { + return; + } - For example, provided each object of type `controller` needed a `router`. - one would do the following: + 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); - ```javascript - var container = new Container(); + var Component = componentLookup.lookupFactory(name, container); + if (Component) { + helper = EmberHandlebars.makeViewHelper(Component); + container.register('helper:' + name, helper); + } + } + return helper; + } - container.register('router:main', Router); - container.register('controller:user', UserController); - container.register('controller:post', PostController); - container.typeInjection('controller', 'router', 'router:main'); + /** + `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`: - var user = container.lookup('controller:user'); - var post = container.lookup('controller:post'); + ```handlebars + {{bind "content.title"}} + ``` - user.router instanceof Router; //=> true - post.router instanceof Router; //=> true + 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. - // both controllers share the same router - user.router === post.router; //=> true - ``` + @private + @method bind + @for Ember.Handlebars.helpers + @param {String} property Property to bind + @param {Function} fn Context to provide for rendering + @return {String} HTML string + */ + function bindHelper(property, options) { + Ember.assert("You cannot pass more than one argument to the bind helper", arguments.length <= 2); - @private - @method typeInjection - @param {String} type - @param {String} property - @param {String} fullName - */ - typeInjection: function(type, property, fullName) { - validateFullName(fullName); - if (this.parent) { illegalChildOperation('typeInjection'); } + var context = (options.contexts && options.contexts.length) ? options.contexts[0] : this; - addTypeInjection(this.typeInjections, type, property, fullName); - }, + if (!options.fn) { + return simpleBind(context, property, options); + } - /** - Defines injection rules. + options.helperName = 'bind'; - These rules are used to inject dependencies onto objects when they - are instantiated. + return bind.call(context, property, options, false, exists); + } - Two forms of injections are possible: + /** + Use the `boundIf` helper to create a conditional that re-evaluates + whenever the truthiness of the bound value changes. - * Injecting one fullName on another fullName - * Injecting one fullName on a type + ```handlebars + {{#boundIf "content.shouldDisplayTitle"}} + {{content.title}} + {{/boundIf}} + ``` - Example: + @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; - ```javascript - var container = new Container(); + fn.helperName = fn.helperName || 'boundIf'; - container.register('source:main', Source); - container.register('model:user', User); - container.register('model:post', Post); + return bind.call(context, property, fn, true, shouldDisplayIfHelperContent, shouldDisplayIfHelperContent, ['isTruthy', 'length']); + } - // injecting one fullName on another fullName - // eg. each user model gets a post model - container.injection('model:user', 'post', 'model:post'); - // injecting one fullName on another type - container.injection('model', 'source', 'source:main'); + /** + @private - var user = container.lookup('model:user'); - var post = container.lookup('model:post'); + Use the `unboundIf` helper to create a conditional that evaluates once. - user.source instanceof Source; //=> true - post.source instanceof Source; //=> true + ```handlebars + {{#unboundIf "content.shouldDisplayTitle"}} + {{content.title}} + {{/unboundIf}} + ``` - user.post instanceof Post; //=> true + @method unboundIf + @for Ember.Handlebars.helpers + @param {String} property Property to bind + @param {Function} fn Context to provide for rendering + @return {String} HTML string + @since 1.4.0 + */ + function unboundIfHelper(property, fn) { + var context = (fn.contexts && fn.contexts.length) ? fn.contexts[0] : this, + data = fn.data, + template = fn.fn, + inverse = fn.inverse, + normalized, propertyValue, result; - // and both models share the same source - user.source === post.source; //=> true - ``` + normalized = normalizePath(context, property, data); + propertyValue = handlebarsGet(context, property, fn); - @method injection - @param {String} factoryName - @param {String} property - @param {String} injectionName - */ - injection: function(fullName, property, injectionName) { - if (this.parent) { illegalChildOperation('injection'); } + if (!shouldDisplayIfHelperContent(propertyValue)) { + template = inverse; + } - validateFullName(injectionName); - var normalizedInjectionName = this.normalize(injectionName); + template(context, { data: data }); + } - if (fullName.indexOf(':') === -1) { - return this.typeInjection(fullName, property, normalizedInjectionName); - } + /** + Use the `{{with}}` helper when you want to scope context. Take the following code as an example: - validateFullName(fullName); - var normalizedName = this.normalize(fullName); + ```handlebars +
{{user.name}}
- addInjection(this.injections, normalizedName, property, normalizedInjectionName); - }, +
+
{{user.role.label}}
+ {{user.role.id}} +

{{user.role.description}}

+
+ ``` - /** - Used only via `factoryInjection`. + `{{with}}` can be our best friend in these cases, + instead of writing `user.role.*` over and over, we use `{{#with user.role}}`. + Now the context within the `{{#with}} .. {{/with}}` block is `user.role` so you can do the following: - Provides a specialized form of injection, specifically enabling - all factory of one type to be injected with a reference to another - object. + ```handlebars +
{{user.name}}
- For example, provided each factory of type `model` needed a `store`. - one would do the following: +
+ {{#with user.role}} +
{{label}}
+ {{id}} - ```javascript - var container = new Container(); +

{{description}}

+ {{/with}} +
+ ``` - container.register('store:main', SomeStore); + ### `as` operator - container.factoryTypeInjection('model', 'store', 'store:main'); + This operator aliases the scope to a new name. It's helpful for semantic clarity and to retain + default scope or to reference from another `{{with}}` block. - var store = container.lookup('store:main'); - var UserFactory = container.lookupFactory('model:user'); + ```handlebars + // posts might not be + {{#with user.posts as blogPosts}} +
+ There are {{blogPosts.length}} blog posts written by {{user.name}}. +
- UserFactory.store instanceof SomeStore; //=> true - ``` + {{#each post in blogPosts}} +
  • {{post.title}}
  • + {{/each}} + {{/with}} + ``` - @private - @method factoryTypeInjection - @param {String} type - @param {String} property - @param {String} fullName - */ - factoryTypeInjection: function(type, property, fullName) { - if (this.parent) { illegalChildOperation('factoryTypeInjection'); } + Without the `as` operator, it would be impossible to reference `user.name` in the example above. - addTypeInjection(this.factoryTypeInjections, type, property, this.normalize(fullName)); - }, + 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}}`. - /** - Defines factory injection rules. + ### `controller` option - Similar to regular injection rules, but are run against factories, via - `Container#lookupFactory`. + Adding `controller='something'` instructs the `{{with}}` helper to create and use an instance of + the specified controller with the new context as its content. - These rules are used to inject objects onto factories when they - are looked up. + This is very similar to using an `itemController` option with the `{{each}}` helper. - Two forms of injections are possible: + ```handlebars + {{#with users.posts controller='userBlogPosts'}} + {{!- The current context is wrapped in our controller instance }} + {{/with}} + ``` - * Injecting one fullName on another fullName - * Injecting one fullName on a type + In the above example, the template provided to the `{{with}}` block is now wrapped in the + `userBlogPost` controller, which provides a very elegant way to decorate the context with custom + functions/properties. - Example: + @method with + @for Ember.Handlebars.helpers + @param {Function} context + @param {Hash} options + @return {String} HTML string + */ + function withHelper(context, options) { + var bindContext, preserveContext, controller, helperName = 'with'; - ```javascript - var container = new Container(); + if (arguments.length === 4) { + var keywordName, path, rootPath, normalized, contextPath; - container.register('store:main', Store); - container.register('store:secondary', OtherStore); - container.register('model:user', User); - container.register('model:post', Post); + Ember.assert("If you pass more than one argument to the with helper, it must be in the form #with foo as bar", arguments[1] === "as"); + options = arguments[3]; + keywordName = arguments[2]; + path = arguments[0]; - // injecting one fullName on another type - container.factoryInjection('model', 'store', 'store:main'); + if (path) { + helperName += ' ' + path + ' as ' + keywordName; + } - // injecting one fullName on another fullName - container.factoryInjection('model:post', 'secondaryStore', 'store:secondary'); + Ember.assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop); - var UserFactory = container.lookupFactory('model:user'); - var PostFactory = container.lookupFactory('model:post'); - var store = container.lookup('store:main'); + var localizedOptions = o_create(options); + localizedOptions.data = o_create(options.data); + localizedOptions.data.keywords = o_create(options.data.keywords || {}); - UserFactory.store instanceof Store; //=> true - UserFactory.secondaryStore instanceof OtherStore; //=> false + if (isGlobalPath(path)) { + contextPath = path; + } else { + normalized = normalizePath(this, path, options.data); + path = normalized.path; + rootPath = normalized.root; - PostFactory.store instanceof Store; //=> true - PostFactory.secondaryStore instanceof OtherStore; //=> true + // This is a workaround for the fact that you cannot bind separate objects + // together. When we implement that functionality, we should use it here. + var contextKey = jQuery.expando + guidFor(rootPath); + localizedOptions.data.keywords[contextKey] = rootPath; + // if the path is '' ("this"), just bind directly to the current context + contextPath = path ? contextKey + '.' + path : contextKey; + } - // and both models share the same source instance - UserFactory.store === PostFactory.store; //=> true - ``` + localizedOptions.hash.keywordName = keywordName; + localizedOptions.hash.keywordPath = contextPath; - @method factoryInjection - @param {String} factoryName - @param {String} property - @param {String} injectionName - */ - factoryInjection: function(fullName, property, injectionName) { - if (this.parent) { illegalChildOperation('injection'); } + bindContext = this; + context = contextPath; + options = localizedOptions; + preserveContext = true; + } else { + Ember.assert("You must pass exactly one argument to the with helper", arguments.length === 2); + Ember.assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop); - var normalizedName = this.normalize(fullName); - var normalizedInjectionName = this.normalize(injectionName); + helperName += ' ' + context; + bindContext = options.contexts[0]; + preserveContext = false; + } - validateFullName(injectionName); + options.helperName = helperName; + options.isWithHelper = true; - if (fullName.indexOf(':') === -1) { - return this.factoryTypeInjection(normalizedName, property, normalizedInjectionName); - } + return bind.call(bindContext, context, options, preserveContext, exists); + } + /** + See [boundIf](/api/classes/Ember.Handlebars.helpers.html#method_boundIf) + and [unboundIf](/api/classes/Ember.Handlebars.helpers.html#method_unboundIf) + + @method if + @for Ember.Handlebars.helpers + @param {Function} context + @param {Hash} options + @return {String} HTML string + */ + function ifHelper(context, options) { + Ember.assert("You must pass exactly one argument to the if helper", arguments.length === 2); + Ember.assert("You must pass a block to the if helper", options.fn && options.fn !== Handlebars.VM.noop); - validateFullName(fullName); + options.helperName = options.helperName || ('if ' + context); - addInjection(this.factoryInjections, normalizedName, property, normalizedInjectionName); - }, + if (options.data.isUnbound) { + return helpers.unboundIf.call(options.contexts[0], context, options); + } else { + return helpers.boundIf.call(options.contexts[0], context, options); + } + } - /** - A depth first traversal, destroying the container, its descendant containers and all - their managed objects. + /** + @method unless + @for Ember.Handlebars.helpers + @param {Function} context + @param {Hash} options + @return {String} HTML string + */ + function unlessHelper(context, options) { + Ember.assert("You must pass exactly one argument to the unless helper", arguments.length === 2); + Ember.assert("You must pass a block to the unless helper", options.fn && options.fn !== Handlebars.VM.noop); - @method destroy - */ - destroy: function() { - for (var i=0, l=this.children.length; i + ``` - var value = instantiate(container, fullName); + 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"`. - if (value === undefined) { return; } + If the rendering context of this template is the following object: - if (isSingleton(container, fullName) && options.singleton !== false) { - container.cache.set(fullName, value); + ```javascript + { + imageUrl: 'http://lolcats.info/haz-a-funny', + imageTitle: 'A humorous image of a cat' } + ``` - return value; - } + The resulting HTML output will be: - function illegalChildOperation(operation) { - throw new Error(operation + " is not currently supported on child containers"); - } + ```html + A humorous image of a cat + ``` - function isSingleton(container, fullName) { - var singleton = option(container, fullName, 'singleton'); + `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: - return singleton !== false; - } + ```handlebars + imageTitle + ``` - function buildInjections(container, injections) { - var hash = {}; + ### `bind-attr` and the `class` attribute - if (!injections) { return hash; } + `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: - var injection, injectable; + * a string return value of an object's property. + * a boolean return value of an object's property + * a hard-coded value - for (var i=0, l=injections.length; i + ``` - var type = fullName.split(":")[0]; - options = container._typeOptions.get(type); + 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`. - if (options) { - return options[optionName]; - } - } + A class name is provided via the syntax + `somePropertyName:class-name-if-true`. - function factoryFor(container, fullName) { - var name = fullName; - var factory = container.resolve(name); - var injectedFactory; - var cache = container.factoryCache; - var type = fullName.split(":")[0]; + ```javascript + AView = View.extend({ + someBool: true + }) + ``` - if (factory === undefined) { return; } + ```handlebars + + ``` - if (cache.has(fullName)) { - return cache.get(fullName); - } + Result in the following rendered output: - if (!factory || typeof factory.extend !== 'function' || (!Ember.MODEL_FACTORY_INJECTIONS && type === 'model')) { - // TODO: think about a 'safe' merge style extension - // for now just fallback to create time injection - return factory; - } else { + ```html + + ``` - var injections = injectionsFor(container, fullName); - var factoryInjections = factoryInjectionsFor(container, fullName); + 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: - factoryInjections._toString = container.makeToString(factory, fullName); + ```handlebars + + ``` - injectedFactory = factory.extend(injections); - injectedFactory.reopenClass(factoryInjections); + A hard-coded value can be used by prepending `:` to the desired + class name: `:class-name-to-always-apply`. - cache.set(fullName, injectedFactory); + ```handlebars + + ``` - return injectedFactory; - } - } + Results in the following rendered output: - function injectionsFor(container, fullName) { - var splitName = fullName.split(":"), - type = splitName[0], - injections = []; + ```html + + ``` - injections = injections.concat(container.typeInjections.get(type) || []); - injections = injections.concat(container.injections[fullName] || []); + All three strategies - string return value, boolean return value, and + hard-coded value – can be combined in a single declaration: - injections = buildInjections(container, injections); - injections._debugContainerKey = fullName; - injections.container = container; + ```handlebars + + ``` - return injections; - } + @method bind-attr + @for Ember.Handlebars.helpers + @param {Hash} options + @return {String} HTML string + */ + function bindAttrHelper(options) { + var attrs = options.hash; - function factoryInjectionsFor(container, fullName) { - var splitName = fullName.split(":"), - type = splitName[0], - factoryInjections = []; + Ember.assert("You must specify at least one hash argument to bind-attr", !!keys(attrs).length); - factoryInjections = factoryInjections.concat(container.factoryTypeInjections.get(type) || []); - factoryInjections = factoryInjections.concat(container.factoryInjections[fullName] || []); + var view = options.data.view; + var ret = []; - factoryInjections = buildInjections(container, factoryInjections); - factoryInjections._debugContainerKey = fullName; + // 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; - return factoryInjections; - } + // 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(); - function instantiate(container, fullName) { - var factory = factoryFor(container, fullName); + // Handle classes differently, as we can bind multiple classes + var classBindings = attrs['class']; + if (classBindings != null) { + var classResults = bindClasses(ctx, classBindings, view, dataId, options); - if (option(container, fullName, 'instantiate') === false) { - return factory; + ret.push('class="' + Handlebars.Utils.escapeExpression(classResults.join(' ')) + '"'); + delete attrs['class']; } - if (factory) { - if (typeof factory.extend === 'function') { - // assume the factory was extendable and is already injected - return factory.create(); - } else { - // assume the factory was extendable - // to create time injections - // TODO: support new'ing for instantiation and merge injections for pure JS Functions - return factory.create(injectionsFor(container, fullName)); - } - } - } + var attrKeys = keys(attrs); - function eachDestroyable(container, callback) { - container.cache.eachLocal(function(key, value) { - if (option(container, key, 'instantiate') === false) { return; } - callback(value); - }); - } + // For each attribute passed, create an observer and emit the + // current value of the property as an attribute. + forEach.call(attrKeys, function(attr) { + var path = attrs[attr], + normalized; - function resetCache(container) { - container.cache.eachLocal(function(key, value) { - if (option(container, key, 'instantiate') === false) { return; } - value.destroy(); - }); - container.cache.dict = {}; - } + Ember.assert(fmt("You must provide an expression as the value of bound attribute. You specified: %@=%@", [attr, path]), typeof path === 'string'); - function addTypeInjection(rules, type, property, fullName) { - var injections = rules.get(type); + normalized = normalizePath(ctx, path, options.data); - if (!injections) { - injections = []; - rules.set(type, injections); - } + var value = (path === 'this') ? normalized.root : handlebarsGet(ctx, path, options), + type = typeOf(value); - injections.push({ - property: property, - fullName: fullName - }); - } + Ember.assert(fmt("Attributes must be numbers, strings or booleans, not %@", [value]), value === null || value === undefined || type === 'number' || type === 'string' || type === 'boolean'); - var VALID_FULL_NAME_REGEXP = /^[^:]+.+:[^:]+$/; - function validateFullName(fullName) { - if (!VALID_FULL_NAME_REGEXP.test(fullName)) { - throw new TypeError('Invalid Fullname, expected: `type:name` got: ' + fullName); - } - } + var observer; - function addInjection(rules, factoryName, property, injectionName) { - var injections = rules[factoryName] = rules[factoryName] || []; - injections.push({ property: property, fullName: injectionName }); - } + observer = function observer() { + var result = handlebarsGet(ctx, path, options); - __exports__["default"] = Container; - }); -define("container/inheriting_dict", - ["exports"], - function(__exports__) { - "use strict"; - // A safe and simple inheriting object. - function InheritingDict(parent) { - this.parent = parent; - this.dict = {}; - } + 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'); - InheritingDict.prototype = { + var elem = view.$("[data-bindattr-" + dataId + "='" + dataId + "']"); - /** - @property parent - @type InheritingDict - @default null - */ + // If we aren't able to find the element, it means the element + // to which we were bound has been removed from the view. + // In that case, we can assume the template has been re-rendered + // and we need to clean up the observer. + if (!elem || elem.length === 0) { + removeObserver(normalized.root, normalized.path, observer); + return; + } - parent: null, + View.applyAttributeBindings(elem, attr, result); + }; - /** - Object used to store the current nodes data. + // Add an observer to the view for when the property changes. + // When the observer fires, find the element using the + // unique data id and update the attribute to the new value. + // Note: don't add observer when path is 'this' or path + // is whole keyword e.g. {{#each x in list}} ... {{bind-attr attr="x"}} + if (path !== 'this' && !(normalized.isKeyword && normalized.path === '' )) { + view.registerObserver(normalized.root, normalized.path, observer); + } - @property dict - @type Object - @default Object - */ - dict: null, + // if this changes, also change the logic in ember-views/lib/views/view.js + if ((type === 'string' || (type === 'number' && !isNaN(value)))) { + ret.push(attr + '="' + Handlebars.Utils.escapeExpression(value) + '"'); + } else if (value && type === 'boolean') { + // The developer controls the attr name, so it should always be safe + ret.push(attr + '="' + attr + '"'); + } + }, this); - /** - Retrieve the value given a key, if the value is present at the current - level use it, otherwise walk up the parent hierarchy and try again. If - no matching key is found, return undefined. + // 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(' ')); + } - @method get - @param {String} key - @return {any} - */ - get: function(key) { - var dict = this.dict; + /** + See `bind-attr` + + @method bindAttr + @for Ember.Handlebars.helpers + @deprecated + @param {Function} context + @param {Hash} options + @return {String} HTML string + */ + function bindAttrHelperDeprecated() { + Ember.warn("The 'bindAttr' view helper is deprecated in favor of 'bind-attr'"); + return helpers['bind-attr'].apply(this, arguments); + } - if (dict.hasOwnProperty(key)) { - return dict[key]; - } + /** + 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. - if (this.parent) { - return this.parent.get(key); + 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. + + @private + @method bindClasses + @for Ember.Handlebars + @param {Ember.Object} context The context from which to lookup properties + @param {String} classBindings A string, space-separated, of class bindings + to use + @param {View} view The view in which observers should look for the + element to update + @param {Srting} bindAttrId Optional bindAttr id used to lookup elements + @return {Array} An array of class names to add + */ + function bindClasses(context, classBindings, view, bindAttrId, options) { + var ret = [], newClass, value, elem; + + // Helper method to retrieve the property from the context and + // determine which class string to return, based on whether it is + // a Boolean or not. + var classStringForPath = function(root, parsedPath, options) { + var val, + path = parsedPath.path; + + if (path === 'this') { + val = root; + } else if (path === '') { + val = true; + } else { + val = handlebarsGet(root, path, options); } - }, - /** - Set the given value for the given key, at the current level. + return View._classStringForValue(path, val, parsedPath.className, parsedPath.falsyClassName); + }; - @method set - @param {String} key - @param {Any} value - */ - set: function(key, value) { - this.dict[key] = value; - }, + // For each property passed, loop through and setup + // an observer. + forEach.call(classBindings.split(' '), function(binding) { - /** - Delete the given key + // 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; - @method remove - @param {String} key - */ - remove: function(key) { - delete this.dict[key]; - }, + var observer; - /** - Check for the existence of given a key, if the key is present at the current - level return true, otherwise walk up the parent hierarchy and try again. If - no matching key is found, return false. + var parsedPath = View._parsePropertyPath(binding), + path = parsedPath.path, + pathRoot = context, + normalized; - @method has - @param {String} key - @return {Boolean} - */ - has: function(key) { - var dict = this.dict; + if (path !== '' && path !== 'this') { + normalized = normalizePath(context, path, options.data); - if (dict.hasOwnProperty(key)) { - return true; + pathRoot = normalized.root; + path = normalized.path; } - if (this.parent) { - return this.parent.has(key); - } + // Set up an observer on the context. If the property changes, toggle the + // class name. + observer = function() { + // Get the current value of the property + newClass = classStringForPath(context, parsedPath, options); + elem = bindAttrId ? view.$("[data-bindattr-" + bindAttrId + "='" + bindAttrId + "']") : view.$(); - return false; - }, + // If we can't find the element anymore, a parent template has been + // re-rendered and we've been nuked. Remove the observer. + if (!elem || elem.length === 0) { + removeObserver(pathRoot, path, observer); + } else { + // If we had previously added a class to the element, remove it. + if (oldClass) { + elem.removeClass(oldClass); + } - /** - Iterate and invoke a callback for each local key-value pair. + // 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; + } + } + }; - @method eachLocal - @param {Function} callback - @param {Object} binding - */ - eachLocal: function(callback, binding) { - var dict = this.dict; + if (path !== '' && path !== 'this') { + view.registerObserver(pathRoot, path, observer); + } - for (var prop in dict) { - if (dict.hasOwnProperty(prop)) { - callback.call(binding, prop, dict[prop]); - } + // We've already setup the observer; now we just need to figure out the + // correct behavior right now on the first pass through. + value = classStringForPath(context, parsedPath, options); + + if (value) { + ret.push(value); + + // Make sure we save the current value so that it can be removed if the + // observer fires. + oldClass = value; } - } - }; + }); - __exports__["default"] = InheritingDict; + return ret; + } + + __exports__.bind = bind; + __exports__._triageMustacheHelper = _triageMustacheHelper; + __exports__.resolveHelper = resolveHelper; + __exports__.bindHelper = bindHelper; + __exports__.boundIfHelper = boundIfHelper; + __exports__.unboundIfHelper = unboundIfHelper; + __exports__.withHelper = withHelper; + __exports__.ifHelper = ifHelper; + __exports__.unlessHelper = unlessHelper; + __exports__.bindAttrHelper = bindAttrHelper; + __exports__.bindAttrHelperDeprecated = bindAttrHelperDeprecated; + __exports__.bindClasses = bindClasses; }); -define("container", - ["container/container","exports"], - function(__dependency1__, __exports__) { +define("ember-handlebars/helpers/collection", + ["ember-metal/core","ember-metal/utils","ember-handlebars-compiler","ember-runtime/system/string","ember-metal/property_get","ember-handlebars/ext","ember-handlebars/helpers/view","ember-metal/computed","ember-views/views/collection_view","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __exports__) { "use strict"; /** - Public api for the container is still in flux. - The public api, specified on the application namespace should be considered the stable api. - // @module container - @private - */ - - /* - Flag to enable/disable model factory injections (disabled by default) - If model factory injections are enabled, models should not be - accessed globally (only through `container.lookupFactory('model:modelName'))`); + @module ember + @submodule ember-handlebars */ - Ember.MODEL_FACTORY_INJECTIONS = false || !!Ember.ENV.MODEL_FACTORY_INJECTIONS; - - var Container = __dependency1__["default"]; - __exports__["default"] = Container; - }); -})(); + var Ember = __dependency1__["default"]; + // Ember.assert, Ember.deprecate + var inspect = __dependency2__.inspect; -(function() { -/*globals ENV */ -/** -@module ember -@submodule ember-runtime -*/ + // var emberAssert = Ember.assert; + // emberDeprecate = Ember.deprecate; -var indexOf = Ember.EnumerableUtils.indexOf; + var EmberHandlebars = __dependency3__["default"]; + var helpers = EmberHandlebars.helpers; -/** - This will compare two javascript values of possibly different types. - It will tell you which one is greater than the other by returning: + var fmt = __dependency4__.fmt; + var get = __dependency5__.get; + var handlebarsGet = __dependency6__.handlebarsGet; + var ViewHelper = __dependency7__.ViewHelper; + var computed = __dependency8__.computed; + var CollectionView = __dependency9__["default"]; - - -1 if the first is smaller than the second, - - 0 if both are equal, - - 1 if the first is greater than the second. + var alias = computed.alias; + /** + `{{collection}}` is a `Ember.Handlebars` helper for adding instances of + `Ember.CollectionView` to a template. See [Ember.CollectionView](/api/classes/Ember.CollectionView.html) + for additional information on how a `CollectionView` functions. - 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. + `{{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. - ```javascript - Ember.compare('hello', 'hello'); // 0 - Ember.compare('abc', 'dfg'); // -1 - Ember.compare(2, 1); // 1 - ``` + The provided block will be applied as the template for each item's view. - @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. -*/ -Ember.compare = function compare(v, w) { - if (v === w) { return 0; } + Given an empty `` the following template: - var type1 = Ember.typeOf(v); - var type2 = Ember.typeOf(w); + ```handlebars + {{#collection contentBinding="App.items"}} + Hi {{view.content.name}} + {{/collection}} + ``` - var Comparable = Ember.Comparable; - if (Comparable) { - if (type1==='instance' && Comparable.detect(v.constructor)) { - return v.constructor.compare(v, w); - } + And the following application code - if (type2 === 'instance' && Comparable.detect(w.constructor)) { - return 1-w.constructor.compare(w, v); - } - } + ```javascript + App = Ember.Application.create() + App.items = [ + Ember.Object.create({name: 'Dave'}), + Ember.Object.create({name: 'Mary'}), + Ember.Object.create({name: 'Sara'}) + ] + ``` - // If we haven't yet generated a reverse-mapping of Ember.ORDER_DEFINITION, - // do so now. - var mapping = Ember.ORDER_DEFINITION_MAPPING; - if (!mapping) { - var order = Ember.ORDER_DEFINITION; - mapping = Ember.ORDER_DEFINITION_MAPPING = {}; - var idx, len; - for (idx = 0, len = order.length; idx < len; ++idx) { - mapping[order[idx]] = idx; - } + Will result in the HTML structure below - // We no longer need Ember.ORDER_DEFINITION. - delete Ember.ORDER_DEFINITION; - } + ```html +
    +
    Hi Dave
    +
    Hi Mary
    +
    Hi Sara
    +
    + ``` - var type1Index = mapping[type1]; - var type2Index = mapping[type2]; - - if (type1Index < type2Index) { return -1; } - if (type1Index > type2Index) { return 1; } - - // types are equal - so we have to check values now - switch (type1) { - case 'boolean': - case 'number': - if (v < w) { return -1; } - if (v > w) { return 1; } - return 0; - - case 'string': - var comp = v.localeCompare(w); - if (comp < 0) { return -1; } - if (comp > 0) { return 1; } - return 0; - - case 'array': - var vLen = v.length; - var wLen = w.length; - var l = Math.min(vLen, wLen); - var r = 0; - var i = 0; - while (r === 0 && i < l) { - r = compare(v[i],w[i]); - i++; - } - if (r !== 0) { return r; } - - // all elements are equal now - // shorter array should be ordered first - if (vLen < wLen) { return -1; } - if (vLen > wLen) { return 1; } - // arrays are equal now - return 0; - - case 'instance': - if (Ember.Comparable && Ember.Comparable.detect(v)) { - return v.compare(v, w); - } - return 0; - - case 'date': - var vNum = v.getTime(); - var wNum = w.getTime(); - if (vNum < wNum) { return -1; } - if (vNum > wNum) { return 1; } - return 0; - - default: - return 0; - } -}; + ### Blockless use in a collection -function _copy(obj, deep, seen, copies) { - var ret, loc, key; + If you provide an `itemViewClass` option that has its own `template` you can + omit the block. - // primitive data types are immutable, just return them. - if ('object' !== typeof obj || obj===null) return obj; + The following template: - // avoid cyclical loops - if (deep && (loc=indexOf(seen, obj))>=0) return copies[loc]; + ```handlebars + {{collection contentBinding="App.items" itemViewClass="App.AnItemView"}} + ``` - Ember.assert('Cannot clone an Ember.Object that does not implement Ember.Copyable', !(obj instanceof Ember.Object) || (Ember.Copyable && Ember.Copyable.detect(obj))); + And application code - // IMPORTANT: this specific test will detect a native array only. Any other - // object will need to implement Copyable. - if (Ember.typeOf(obj) === 'array') { - ret = obj.slice(); - if (deep) { - loc = ret.length; - while(--loc>=0) ret[loc] = _copy(ret[loc], deep, seen, copies); - } - } else if (Ember.Copyable && Ember.Copyable.detect(obj)) { - ret = obj.copy(deep, seen, copies); - } else if (obj instanceof Date) { - ret = new Date(obj.getTime()); - } else { - ret = {}; - for(key in obj) { - if (!obj.hasOwnProperty(key)) continue; + ```javascript + App = Ember.Application.create(); + App.items = [ + Ember.Object.create({name: 'Dave'}), + Ember.Object.create({name: 'Mary'}), + Ember.Object.create({name: 'Sara'}) + ]; - // Prevents browsers that don't respect non-enumerability from - // copying internal Ember properties - if (key.substring(0,2) === '__') continue; + App.AnItemView = Ember.View.extend({ + template: Ember.Handlebars.compile("Greetings {{view.content.name}}") + }); + ``` - ret[key] = deep ? _copy(obj[key], deep, seen, copies) : obj[key]; - } - } + Will result in the HTML structure below - if (deep) { - seen.push(obj); - copies.push(ret); - } + ```html +
    +
    Greetings Dave
    +
    Greetings Mary
    +
    Greetings Sara
    +
    + ``` - return ret; -} - -/** - Creates a clone of the passed object. This function can take just about - any type of object and create a clone of it, including primitive values - (which are not actually cloned because they are immutable). - - If the passed object implements the `clone()` method, then this function - will simply call that method and return the result. - - @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 -*/ -Ember.copy = function(obj, deep) { - // fast paths - if ('object' !== typeof obj || obj===null) return obj; // can't copy primitives - if (Ember.Copyable && Ember.Copyable.detect(obj)) return obj.copy(deep); - return _copy(obj, deep, deep ? [] : null, deep ? [] : null); -}; - -/** - Compares two objects, returning true if they are logically equal. This is - a deeper comparison than a simple triple equal. For sets it will compare the - internal objects. For any other object that implements `isEqual()` it will - respect that method. - - ```javascript - Ember.isEqual('hello', 'hello'); // true - Ember.isEqual(1, 2); // false - Ember.isEqual([4,2], [4,2]); // false - ``` - - @method isEqual - @for Ember - @param {Object} a first object to compare - @param {Object} b second object to compare - @return {Boolean} -*/ -Ember.isEqual = function(a, b) { - if (a && 'function'===typeof a.isEqual) return a.isEqual(b); - return a === b; -}; - -// Used by Ember.compare -Ember.ORDER_DEFINITION = Ember.ENV.ORDER_DEFINITION || [ - 'undefined', - 'null', - 'boolean', - 'number', - 'string', - 'array', - 'object', - 'instance', - 'function', - 'class', - 'date' -]; - -/** - Returns all of the keys defined on an object or hash. This is useful - when inspecting objects for debugging. On browsers that support it, this - uses the native `Object.keys` implementation. - - @method keys - @for Ember - @param {Object} obj - @return {Array} Array containing keys of obj -*/ -Ember.keys = Object.keys; - -if (!Ember.keys || Ember.create.isSimulated) { - var prototypeProperties = [ - 'constructor', - 'hasOwnProperty', - 'isPrototypeOf', - 'propertyIsEnumerable', - 'valueOf', - 'toLocaleString', - 'toString' - ], - pushPropertyName = function(obj, array, key) { - // Prevents browsers that don't respect non-enumerability from - // copying internal Ember properties - if (key.substring(0,2) === '__') return; - if (key === '_super') return; - if (indexOf(array, key) >= 0) return; - if (!obj.hasOwnProperty(key)) return; - - array.push(key); - }; - - Ember.keys = function(obj) { - var ret = [], key; - for (key in obj) { - pushPropertyName(obj, ret, key); - } - - // IE8 doesn't enumerate property that named the same as prototype properties. - for (var i = 0, l = prototypeProperties.length; i < l; i++) { - key = prototypeProperties[i]; - - pushPropertyName(obj, ret, key); - } - - return ret; - }; -} + ### Specifying a CollectionView subclass -})(); + 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 App.MyCustomCollectionClass contentBinding="App.items"}} + Hi {{view.content.name}} + {{/collection}} + ``` + ### Forwarded `item.*`-named Options -(function() { -/** -@module ember -@submodule ember-runtime -*/ - -var STRING_DASHERIZE_REGEXP = (/[ _]/g); -var STRING_DASHERIZE_CACHE = {}; -var STRING_DECAMELIZE_REGEXP = (/([a-z\d])([A-Z])/g); -var STRING_CAMELIZE_REGEXP = (/(\-|_|\.|\s)+(.)?/g); -var STRING_UNDERSCORE_REGEXP_1 = (/([a-z\d])([A-Z]+)/g); -var STRING_UNDERSCORE_REGEXP_2 = (/\-|\s+/g); -var STRING_PARAMETERIZE_REGEXP_1 = (/[_|\/|\s]+/g); -var STRING_PARAMETERIZE_REGEXP_2 = (/[^a-z0-9\-]+/gi); -var STRING_PARAMETERIZE_REGEXP_3 = (/[\-]+/g); -var STRING_PARAMETERIZE_REGEXP_4 = (/^-+|-+$/g); - -/** - 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 -*/ -Ember.String = { - - /** - 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. + 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): - ```javascript - "Hello %@ %@".fmt('John', 'Doe'); // "Hello John Doe" - "Hello %@2, %@1".fmt('John', 'Doe'); // "Hello Doe, John" - ``` + ```handlebars + {{#collection contentBinding="App.items" + itemTagName="p" + itemClassNames="greeting"}} + Howdy {{view.content.name}} + {{/collection}} + ``` - @method fmt - @param {String} str The string to format - @param {Array} formats An array of parameters to interpolate into string. - @return {String} formatted string - */ - fmt: function(str, formats) { - // first, replace any ORDERED replacements. - var idx = 0; // the current index for non-numerical replacements - return str.replace(/%@([0-9]+)?/g, function(s, argIndex) { - argIndex = (argIndex) ? parseInt(argIndex, 10) - 1 : idx++; - s = formats[argIndex]; - return (s === null) ? '(null)' : (s === undefined) ? '' : Ember.inspect(s); - }) ; - }, - - /** - 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. - - 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. + Will result in the following HTML structure: - ```javascript - Ember.STRINGS = { - '_Hello World': 'Bonjour le monde', - '_Hello %@ %@': 'Bonjour %@ %@' - }; + ```html +
    +

    Howdy Dave

    +

    Howdy Mary

    +

    Howdy Sara

    +
    + ``` - Ember.String.loc("_Hello World"); // 'Bonjour le monde'; - Ember.String.loc("_Hello %@ %@", ["John", "Smith"]); // "Bonjour John Smith"; - ``` + @method collection + @for Ember.Handlebars.helpers + @param {String} path + @param {Hash} options + @return {String} HTML string + @deprecated Use `{{each}}` helper instead. + */ + function collectionHelper(path, options) { + Ember.deprecate("Using the {{collection}} helper without specifying a class has been deprecated as the {{each}} helper now supports the same functionality.", path !== 'collection'); + + // If 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); + } + + var fn = options.fn; + var data = options.data; + var inverse = options.inverse; + var view = options.data.view; + + + var controller, container; + // If passed a path string, convert that into an object. + // Otherwise, just default to the standard class. + var collectionClass; + if (path) { + controller = data.keywords.controller; + container = controller && controller.container; + collectionClass = handlebarsGet(this, path, options) || container.lookupFactory('view:' + path); + Ember.assert(fmt("%@ #collection: Could not find collection class %@", [data.view, path]), !!collectionClass); + } + else { + collectionClass = CollectionView; + } + + var hash = options.hash, itemHash = {}, match; + + // Extract item view class if provided else default to the standard class + var collectionPrototype = collectionClass.proto(), itemViewClass; + + if (hash.itemView) { + controller = data.keywords.controller; + Ember.assert('You specified an itemView, but the current context has no ' + + 'container to look the itemView up in. This probably means ' + + 'that you created a view manually, instead of through the ' + + 'container. Instead, use container.lookup("view:viewName"), ' + + 'which will properly instantiate your view.', + controller && controller.container); + container = controller.container; + itemViewClass = container.lookupFactory('view:' + hash.itemView); + Ember.assert('You specified the itemView ' + hash.itemView + ", but it was " + + "not found at " + container.describe("view:" + hash.itemView) + + " (and it was not registered in the container)", !!itemViewClass); + } else if (hash.itemViewClass) { + itemViewClass = handlebarsGet(collectionPrototype, hash.itemViewClass, options); + } else { + itemViewClass = collectionPrototype.itemViewClass; + } - @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: function(str, formats) { - str = Ember.STRINGS[str] || str; - return Ember.String.fmt(str, formats) ; - }, - - /** - 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`. + Ember.assert(fmt("%@ #collection: Could not find itemViewClass %@", [data.view, itemViewClass]), !!itemViewClass); - ```javascript - Ember.String.w("alpha beta gamma").forEach(function(key) { - console.log(key); - }); + delete hash.itemViewClass; + delete hash.itemView; - // > alpha - // > beta - // > gamma - ``` + // Go through options passed to the {{collection}} helper and extract options + // that configure item views instead of the collection itself. + for (var prop in hash) { + if (hash.hasOwnProperty(prop)) { + match = prop.match(/^item(.)(.*)$/); - @method w - @param {String} str The string to split - @return {String} split string - */ - w: function(str) { return str.split(/\s+/); }, + if (match && prop !== 'itemController') { + // Convert itemShouldFoo -> shouldFoo + itemHash[match[1].toLowerCase() + match[2]] = hash[prop]; + // Delete from hash as this will end up getting passed to the + // {{view}} helper method. + delete hash[prop]; + } + } + } - /** - Converts a camelized string into all lower case separated by underscores. + if (fn) { + itemHash.template = fn; + delete options.fn; + } - ```javascript - 'innerHTML'.decamelize(); // 'inner_html' - 'action_name'.decamelize(); // 'action_name' - 'css-class-name'.decamelize(); // 'css-class-name' - 'my favorite items'.decamelize(); // 'my favorite items' - ``` + var emptyViewClass; + if (inverse && inverse !== EmberHandlebars.VM.noop) { + emptyViewClass = get(collectionPrototype, 'emptyViewClass'); + emptyViewClass = emptyViewClass.extend({ + template: inverse, + tagName: itemHash.tagName + }); + } else if (hash.emptyViewClass) { + emptyViewClass = handlebarsGet(this, hash.emptyViewClass, options); + } + if (emptyViewClass) { hash.emptyView = emptyViewClass; } - @method decamelize - @param {String} str The string to decamelize. - @return {String} the decamelized string. - */ - decamelize: function(str) { - return str.replace(STRING_DECAMELIZE_REGEXP, '$1_$2').toLowerCase(); - }, + if (hash.keyword) { + itemHash._context = this; + } else { + itemHash._context = alias('content'); + } - /** - Replaces underscores, spaces, or camelCase with dashes. + var viewOptions = ViewHelper.propertiesFromHTMLOptions({ data: data, hash: itemHash }, this); + hash.itemViewClass = itemViewClass.extend(viewOptions); - ```javascript - 'innerHTML'.dasherize(); // 'inner-html' - 'action_name'.dasherize(); // 'action-name' - 'css-class-name'.dasherize(); // 'css-class-name' - 'my favorite items'.dasherize(); // 'my-favorite-items' - ``` + options.helperName = options.helperName || 'collection'; - @method dasherize - @param {String} str The string to dasherize. - @return {String} the dasherized string. - */ - dasherize: function(str) { - var cache = STRING_DASHERIZE_CACHE, - hit = cache.hasOwnProperty(str), - ret; - - if (hit) { - return cache[str]; - } else { - ret = Ember.String.decamelize(str).replace(STRING_DASHERIZE_REGEXP,'-'); - cache[str] = ret; + return helpers.view.call(this, collectionClass, options); } - return ret; - }, + __exports__["default"] = collectionHelper; + }); +define("ember-handlebars/helpers/debug", + ["ember-metal/core","ember-metal/utils","ember-metal/logger","ember-metal/property_get","ember-handlebars/ext","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { + "use strict"; + /*jshint debug:true*/ - /** - Returns the lowerCamelCase form of a string. + /** + @module ember + @submodule ember-handlebars + */ + var Ember = __dependency1__["default"]; + // Ember.FEATURES, + var inspect = __dependency2__.inspect; + var Logger = __dependency3__["default"]; - ```javascript - 'innerHTML'.camelize(); // 'innerHTML' - 'action_name'.camelize(); // 'actionName' - 'css-class-name'.camelize(); // 'cssClassName' - 'my favorite items'.camelize(); // 'myFavoriteItems' - 'My Favorite Items'.camelize(); // 'myFavoriteItems' - ``` + var get = __dependency4__.get; + var normalizePath = __dependency5__.normalizePath; + var handlebarsGet = __dependency5__.handlebarsGet; - @method camelize - @param {String} str The string to camelize. - @return {String} the camelized string. - */ - camelize: function(str) { - return str.replace(STRING_CAMELIZE_REGEXP, function(match, separator, chr) { - return chr ? chr.toUpperCase() : ''; - }).replace(/^([A-Z])/, function(match, separator, chr) { - return match.toLowerCase(); - }); - }, + var a_slice = [].slice; - /** - Returns the UpperCamelCase form of a string. + /** + `log` allows you to output the value of variables in the current rendering + context. `log` also accepts primitive types such as strings or numbers. - ```javascript - 'innerHTML'.classify(); // 'InnerHTML' - 'action_name'.classify(); // 'ActionName' - 'css-class-name'.classify(); // 'CssClassName' - 'my favorite items'.classify(); // 'MyFavoriteItems' - ``` + ```handlebars + {{log "myVariable:" myVariable }} + ``` - @method classify - @param {String} str the string to classify - @return {String} the classified string - */ - classify: function(str) { - var parts = str.split("."), - out = []; + @method log + @for Ember.Handlebars.helpers + @param {String} property + */ + function logHelper() { + var params = a_slice.call(arguments, 0, -1), + options = arguments[arguments.length - 1], + logger = Logger.log, + values = [], + allowPrimitives = true; + + for (var i = 0; i < params.length; i++) { + var type = options.types[i]; + + if (type === 'ID' || !allowPrimitives) { + var context = (options.contexts && options.contexts[i]) || this, + normalized = normalizePath(context, params[i], options.data); + + if (normalized.path === 'this') { + values.push(normalized.root); + } else { + values.push(handlebarsGet(normalized.root, normalized.path, options)); + } + } else { + values.push(params[i]); + } + } - for (var i=0, l=parts.length; i templateContext.get('foo') // -> "" + ``` - /** - Returns the Capitalized form of a string + @method debugger + @for Ember.Handlebars.helpers + @param {String} property + */ + function debuggerHelper(options) { - ```javascript - 'innerHTML'.capitalize() // 'InnerHTML' - 'action_name'.capitalize() // 'Action_name' - 'css-class-name'.capitalize() // 'Css-class-name' - 'my favorite items'.capitalize() // 'My favorite items' - ``` + // These are helpful values you can inspect while debugging. + var templateContext = this; + var typeOfTemplateContext = inspect(templateContext); + Ember.Logger.info('Use `this` to access the context of the calling template.'); - @method capitalize - @param {String} str The string to capitalize. - @return {String} The capitalized string. - */ - capitalize: function(str) { - return str.charAt(0).toUpperCase() + str.substr(1); - } -}; + debugger; + } -})(); + __exports__.logHelper = logHelper; + __exports__.debuggerHelper = debuggerHelper; + }); +define("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-runtime/copy","ember-metal/run_loop","ember-metal/events","ember-handlebars/ext","ember-metal/computed","ember-metal/observer","ember-handlebars/views/metamorph_view","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-handlebars + */ + var Ember = __dependency1__["default"]; + // Ember.assert;, Ember.K + // var emberAssert = Ember.assert, + var K = Ember.K; + + var EmberHandlebars = __dependency2__["default"]; + var helpers = EmberHandlebars.helpers; + + var 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 copy = __dependency11__["default"]; + var run = __dependency12__["default"]; + var on = __dependency13__.on; + var handlebarsGet = __dependency14__.handlebarsGet; + var computed = __dependency15__.computed; + + var addObserver = __dependency16__.addObserver; + var removeObserver = __dependency16__.removeObserver; + var addBeforeObserver = __dependency16__.addBeforeObserver; + var removeBeforeObserver = __dependency16__.removeBeforeObserver; + + var _Metamorph = __dependency17__._Metamorph; + var _MetamorphView = __dependency17__._MetamorphView; + + var EachView = CollectionView.extend(_Metamorph, { + init: function() { + var itemController = get(this, 'itemController'); + var binding; + + 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 + }); -(function() { -/** -@module ember -@submodule ember-runtime -*/ - - - -var fmt = Ember.String.fmt, - w = Ember.String.w, - loc = Ember.String.loc, - camelize = Ember.String.camelize, - decamelize = Ember.String.decamelize, - dasherize = Ember.String.dasherize, - underscore = Ember.String.underscore, - capitalize = Ember.String.capitalize, - classify = Ember.String.classify; - -if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { - - /** - See [Ember.String.fmt](/api/classes/Ember.String.html#method_fmt). - - @method fmt - @for String - */ - String.prototype.fmt = function() { - return fmt(this, arguments); - }; - - /** - See [Ember.String.w](/api/classes/Ember.String.html#method_w). - - @method w - @for String - */ - String.prototype.w = function() { - return w(this); - }; - - /** - See [Ember.String.loc](/api/classes/Ember.String.html#method_loc). - - @method loc - @for String - */ - String.prototype.loc = function() { - return loc(this, arguments); - }; - - /** - See [Ember.String.camelize](/api/classes/Ember.String.html#method_camelize). - - @method camelize - @for String - */ - String.prototype.camelize = function() { - return camelize(this); - }; - - /** - See [Ember.String.decamelize](/api/classes/Ember.String.html#method_decamelize). - - @method decamelize - @for String - */ - String.prototype.decamelize = function() { - return decamelize(this); - }; - - /** - See [Ember.String.dasherize](/api/classes/Ember.String.html#method_dasherize). - - @method dasherize - @for String - */ - String.prototype.dasherize = function() { - return dasherize(this); - }; - - /** - See [Ember.String.underscore](/api/classes/Ember.String.html#method_underscore). - - @method underscore - @for String - */ - String.prototype.underscore = function() { - return underscore(this); - }; - - /** - See [Ember.String.classify](/api/classes/Ember.String.html#method_classify). - - @method classify - @for String - */ - String.prototype.classify = function() { - return classify(this); - }; - - /** - See [Ember.String.capitalize](/api/classes/Ember.String.html#method_capitalize). - - @method capitalize - @for String - */ - String.prototype.capitalize = function() { - return capitalize(this); - }; -} + this.disableContentObservers(function() { + set(this, 'content', controller); + binding = new Binding('content', '_eachView.dataSource').oneWay(); + binding.connect(controller); + }); -})(); + set(this, '_arrayController', controller); + } else { + this.disableContentObservers(function() { + binding = new Binding('content', 'dataSource').oneWay(); + binding.connect(this); + }); + } + return this._super(); + }, + _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)); + }, -(function() { -/** -@module ember -@submodule ember-runtime -*/ + disableContentObservers: function(callback) { + removeBeforeObserver(this, 'content', null, '_contentWillChange'); + removeObserver(this, 'content', null, '_contentDidChange'); -var get = Ember.get, - set = Ember.set, - slice = Array.prototype.slice, - getProperties = Ember.getProperties; + callback.call(this); -/** - ## Overview + addBeforeObserver(this, 'content', null, '_contentWillChange'); + addObserver(this, 'content', null, '_contentDidChange'); + }, - This mixin provides properties and property observing functionality, core - features of the Ember object model. + itemViewClass: _MetamorphView, + emptyViewClass: _MetamorphView, - 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. + createChildView: function(view, attrs) { + view = this._super(view, attrs); - 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. + // At the moment, if a container view subclass wants + // to insert keywords, it is responsible for cloning + // the keywords hash. This will be fixed momentarily. + var keyword = get(this, 'keyword'); + var content = get(view, 'content'); - Note that you will not generally apply this mixin to classes yourself, - but you will use the features provided by this module frequently, so it - is important to understand how to use it. + if (keyword) { + var data = get(view, 'templateData'); - ## Using `get()` and `set()` + data = copy(data); + data.keywords = view.cloneKeywords(); + set(view, 'templateData', data); - 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. + // In this case, we do not bind, because the `content` of + // a #each item cannot change. + data.keywords[keyword] = content; + } - More documentation about `get` and `set` are below. + // 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); + } - ## Observing Property Changes + return view; + }, - 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: + destroy: function() { + if (!this._super()) { return; } - ```javascript - Ember.Object.extend({ - valueObserver: function() { - // Executes whenever the "value" property changes - }.observes('value') - }); - ``` + var arrayController = get(this, '_arrayController'); - 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. + if (arrayController) { + arrayController.destroy(); + } - To add an observer for a property, call: + return this; + } + }); - ```javascript - object.addObserver('propertyKey', targetObject, targetAction) - ``` + // Defeatureify doesn't seem to like nested functions that need to be removed + function _addMetamorphCheck() { + EachView.reopen({ + _checkMetamorph: on('didInsertElement', function() { + Ember.assert("The metamorph tags, " + + this.morph.start + " and " + this.morph.end + + ", have different parents.\nThe browser has fixed your template to output valid HTML (for example, check that you have properly closed all tags and have used a TBODY tag when creating a table with '{{#each}}')", + document.getElementById( this.morph.start ).parentNode === + document.getElementById( this.morph.end ).parentNode + ); + }) + }); + } - This will call the `targetAction` method on the `targetObject` whenever - the value of the `propertyKey` changes. + // until ember-debug is es6ed + var runInDebug = function(f){ f(); }; + runInDebug( function() { + _addMetamorphCheck(); + }); - 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 GroupedEach = EmberHandlebars.GroupedEach = function(context, path, options) { + var self = this, + normalized = EmberHandlebars.normalizePath(context, path, options.data); - @class Observable - @namespace Ember -*/ -Ember.Observable = Ember.Mixin.create({ + this.context = context; + this.path = path; + this.options = options; + this.template = options.fn; + this.containingView = options.data.view; + this.normalizedRoot = normalized.root; + this.normalizedPath = normalized.path; + this.content = this.lookupContent(); - /** - Retrieves the value of a property from the object. + this.addContentObservers(); + this.addArrayObservers(); - This method is usually similar to using `object[keyName]` or `object.keyName`, - however it supports both computed properties and the unknownProperty - handler. + this.containingView.on('willClearRender', function() { + self.destroy(); + }); + }; - 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. + GroupedEach.prototype = { + contentWillChange: function() { + this.removeArrayObservers(); + }, - ### Computed Properties + contentDidChange: function() { + this.content = this.lookupContent(); + this.addArrayObservers(); + this.rerenderContainingView(); + }, - Computed properties are methods defined with the `property` modifier - declared at the end, such as: + contentArrayWillChange: K, - ```javascript - fullName: function() { - return this.get('firstName') + ' ' + this.get('lastName'); - }.property('firstName', 'lastName') - ``` + contentArrayDidChange: function() { + this.rerenderContainingView(); + }, - 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. + lookupContent: function() { + return handlebarsGet(this.normalizedRoot, this.normalizedPath, this.options); + }, - ### Unknown Properties + addArrayObservers: function() { + if (!this.content) { return; } - 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. + this.content.addArrayObserver(this, { + willChange: 'contentArrayWillChange', + didChange: 'contentArrayDidChange' + }); + }, - @method get - @param {String} keyName The property to retrieve - @return {Object} The property value or undefined. - */ - get: function(keyName) { - return get(this, keyName); - }, + removeArrayObservers: function() { + if (!this.content) { return; } - /** - To get multiple properties at once, call `getProperties` - with a list of strings or an array: + this.content.removeArrayObserver(this, { + willChange: 'contentArrayWillChange', + didChange: 'contentArrayDidChange' + }); + }, - ```javascript - record.getProperties('firstName', 'lastName', 'zipCode'); // { firstName: 'John', lastName: 'Doe', zipCode: '10011' } - ``` + addContentObservers: function() { + addBeforeObserver(this.normalizedRoot, this.normalizedPath, this, this.contentWillChange); + addObserver(this.normalizedRoot, this.normalizedPath, this, this.contentDidChange); + }, - is equivalent to: + removeContentObservers: function() { + removeBeforeObserver(this.normalizedRoot, this.normalizedPath, this.contentWillChange); + removeObserver(this.normalizedRoot, this.normalizedPath, this.contentDidChange); + }, - ```javascript - record.getProperties(['firstName', 'lastName', 'zipCode']); // { firstName: 'John', lastName: 'Doe', zipCode: '10011' } - ``` + render: function() { + if (!this.content) { return; } - @method getProperties - @param {String...|Array} list of keys to get - @return {Hash} - */ - getProperties: function() { - return getProperties.apply(null, [this].concat(slice.call(arguments))); - }, + var content = this.content, + contentLength = get(content, 'length'), + options = this.options, + data = options.data, + template = this.template; - /** - Sets the provided key or path to the value. + data.insideEach = true; + for (var i = 0; i < contentLength; i++) { + var context = content.objectAt(i); + options.data.keywords[options.hash.keyword] = context; + template(context, { data: data }); + } + }, - 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. + rerenderContainingView: function() { + var self = this; + run.scheduleOnce('render', this, function() { + // It's possible it's been destroyed after we enqueued a re-render call. + if (!self.destroyed) { + self.containingView.rerender(); + } + }); + }, - ### Computed Properties + destroy: function() { + this.removeContentObservers(); + if (this.content) { + this.removeArrayObservers(); + } + this.destroyed = true; + } + }; - 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. + /** + The `{{#each}}` helper loops over elements in a collection, rendering its + block once for each item. It is an extension of the base Handlebars `{{#each}}` + helper: - ### Unknown Properties + ```javascript + Developers = [{name: 'Yehuda'},{name: 'Tom'}, {name: 'Paul'}]; + ``` - 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. + ```handlebars + {{#each Developers}} + {{name}} + {{/each}} + ``` - ### Property Observers + `{{each}}` supports an alternative syntax with element naming: - 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. + ```handlebars + {{#each person in Developers}} + {{person.name}} + {{/each}} + ``` - ### Chaining + When looping over objects that do not have properties, `{{this}}` can be used + to render the object: - In addition to property changes, `set()` returns the value of the object - itself so you can do chaining like this: + ```javascript + DeveloperNames = ['Yehuda', 'Tom', 'Paul'] + ``` - ```javascript - record.set('firstName', 'Charles').set('lastName', 'Jolley'); - ``` + ```handlebars + {{#each DeveloperNames}} + {{this}} + {{/each}} + ``` + ### {{else}} condition + `{{#each}}` can have a matching `{{else}}`. The contents of this block will render + if the collection is empty. - @method set - @param {String} keyName The property to set - @param {Object} value The value to set or `null`. - @return {Ember.Observable} - */ - set: function(keyName, value) { - set(this, keyName, value); - return this; - }, + ``` + {{#each person in Developers}} + {{person.name}} + {{else}} +

    Sorry, nobody is available for this task.

    + {{/each}} + ``` + ### Specifying a View class for items + If you provide an `itemViewClass` option that references a view class + with its own `template` you can omit the block. + The following template: - /** - Sets a list of properties at once. These properties are set inside - a single `beginPropertyChanges` and `endPropertyChanges` batch, so - observers will be buffered. + ```handlebars + {{#view App.MyView }} + {{each view.items itemViewClass="App.AnItemView"}} + {{/view}} + ``` - ```javascript - record.setProperties({ firstName: 'Charles', lastName: 'Jolley' }); - ``` + And application code - @method setProperties - @param {Hash} hash the hash of keys and values to set - @return {Ember.Observable} - */ - setProperties: function(hash) { - return Ember.setProperties(this, hash); - }, - - /** - Begins a grouping of property changes. - - You can use this method to group property changes so that notifications - will not be sent until the changes are finished. If you plan to make a - large number of changes to an object at one time, you should call this - method at the beginning of the changes to begin deferring change - notifications. When you are done making changes, call - `endPropertyChanges()` to deliver the deferred change notifications and end - deferring. - - @method beginPropertyChanges - @return {Ember.Observable} - */ - beginPropertyChanges: function() { - Ember.beginPropertyChanges(); - return this; - }, - - /** - Ends a grouping of property changes. - - 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} - */ - endPropertyChanges: function() { - Ember.endPropertyChanges(); - return this; - }, - - /** - Notify the observer system that a property is about to change. - - Sometimes you need to change a value directly or indirectly without - actually calling `get()` or `set()` on it. In this case, you can use this - method and `propertyDidChange()` instead. Calling these two methods - together will notify all observers that the property has potentially - changed value. - - Note that you must always call `propertyWillChange` and `propertyDidChange` - as a pair. If you do not, it may get the property change groups out of - order and cause notifications to be delivered more often than you would - like. - - @method propertyWillChange - @param {String} keyName The property key that is about to change. - @return {Ember.Observable} - */ - propertyWillChange: function(keyName) { - Ember.propertyWillChange(this, keyName); - return this; - }, - - /** - Notify the observer system that a property has just changed. - - 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. - - 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) { - Ember.propertyDidChange(this, keyName); - 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; - }, - - addBeforeObserver: function(key, target, method) { - Ember.addBeforeObserver(this, key, target, method); - }, - - /** - 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 - - Observer methods you pass should generally have the following signature if - you do not pass a `context` parameter: + ```javascript + App = Ember.Application.create({ + MyView: Ember.View.extend({ + items: [ + Ember.Object.create({name: 'Dave'}), + Ember.Object.create({name: 'Mary'}), + Ember.Object.create({name: 'Sara'}) + ] + }) + }); - ```javascript - fooDidChange: function(sender, key, value, rev) { }; - ``` + App.AnItemView = Ember.View.extend({ + template: Ember.Handlebars.compile("Greetings {{name}}") + }); + ``` - 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. + Will result in the HTML structure below - If you pass a `context` parameter, the context will be passed before the - revision like so: + ```html +
    +
    Greetings Dave
    +
    Greetings Mary
    +
    Greetings Sara
    +
    + ``` - ```javascript - fooDidChange: function(sender, key, value, context, rev) { }; - ``` + If an `itemViewClass` is defined on the helper, and therefore the helper is not + being used as a block, an `emptyViewClass` can also be provided optionally. + The `emptyViewClass` will match the behavior of the `{{else}}` condition + described above. That is, the `emptyViewClass` will render if the collection + is empty. - 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. - @return {Ember.Object} self - */ - addObserver: function(key, target, method) { - Ember.addObserver(this, key, target, 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 removeObserver - @param {String} key The key to observer - @param {Object} target The target object to invoke - @param {String|Function} method The method to invoke. - @return {Ember.Observable} receiver - */ - removeObserver: function(key, target, method) { - Ember.removeObserver(this, key, target, 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 hasObserverFor - @param {String} key Key to check - @return {Boolean} - */ - hasObserverFor: function(key) { - return Ember.hasListeners(this, key+':change'); - }, - - /** - Retrieves the value of a property, or a default value in the case that the - property returns `undefined`. + ### Representing each item with a Controller. + By default the controller lookup within an `{{#each}}` block will be + the controller of the template where the `{{#each}}` was used. If each + item needs to be presented by a custom controller you can provide a + `itemController` option which references a controller by lookup name. + Each item in the loop will be wrapped in an instance of this controller + and the item itself will be set to the `model` property of that controller. - ```javascript - person.getWithDefault('lastName', 'Doe'); - ``` + This is useful in cases where properties of model objects need transformation + or synthesis for display: - @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 Ember.getWithDefault(this, keyName, defaultValue); - }, + ```javascript + App.DeveloperController = Ember.ObjectController.extend({ + isAvailableForHire: function() { + return !this.get('model.isEmployed') && this.get('model.isSeekingWork'); + }.property('isEmployed', 'isSeekingWork') + }) + ``` - /** - Set the value of a property to the current value plus some amount. + ```handlebars + {{#each person in developers itemController="developer"}} + {{person.name}} {{#if person.isAvailableForHire}}Hire me!{{/if}} + {{/each}} + ``` - ```javascript - person.incrementProperty('age'); - team.incrementProperty('score', 2); - ``` + Each itemController will receive a reference to the current controller as + a `parentController` property. - @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 (Ember.isNone(increment)) { increment = 1; } - Ember.assert("Must pass a numeric value to incrementProperty", (!isNaN(parseFloat(increment)) && isFinite(increment))); - set(this, keyName, (get(this, keyName) || 0) + increment); - return get(this, keyName); - }, - - /** - Set the value of a property to the current value minus some amount. + ### (Experimental) Grouped Each - ```javascript - player.decrementProperty('lives'); - orc.decrementProperty('health', 5); - ``` + When used in conjunction with the experimental [group helper](https://github.com/emberjs/group-helper), + you can inform Handlebars to re-render an entire group of items instead of + re-rendering them one at a time (in the event that they are changed en masse + or an item is added/removed). - @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 (Ember.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); - }, - - /** - Set the value of a boolean property to the opposite of it's - current value. + ```handlebars + {{#group}} + {{#each people}} + {{firstName}} {{lastName}} + {{/each}} + {{/group}} + ``` - ```javascript - starship.toggleProperty('warpDriveEngaged'); - ``` + This can be faster than the normal way that Handlebars re-renders items + in some cases. - @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); - }, - - /** - 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 Ember.cacheFor(this, keyName); - }, - - // intended for debugging purposes - observersForKey: function(keyName) { - return Ember.observersFor(this, keyName); - } -}); + If for some reason you have a group with more than one `#each`, you can make + one of the collections be updated in normal (non-grouped) fashion by setting + the option `groupedRows=true` (counter-intuitive, I know). -})(); + For example, + ```handlebars + {{dealershipName}} + {{#group}} + {{#each dealers}} + {{firstName}} {{lastName}} + {{/each}} -(function() { -/** - @module ember - @submodule ember-runtime -*/ - - -// 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 set = Ember.set, get = Ember.get, - o_create = Ember.create, - o_defineProperty = Ember.platform.defineProperty, - GUID_KEY = Ember.GUID_KEY, - guidFor = Ember.guidFor, - generateGuid = Ember.generateGuid, - meta = Ember.meta, - META_KEY = Ember.META_KEY, - rewatch = Ember.rewatch, - finishChains = Ember.finishChains, - sendEvent = Ember.sendEvent, - destroy = Ember.destroy, - schedule = Ember.run.schedule, - Mixin = Ember.Mixin, - applyMixin = Mixin._apply, - finishPartial = Mixin.finishPartial, - reopen = Mixin.prototype.reopen, - MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER, - indexOf = Ember.EnumerableUtils.indexOf; - -var undefinedDescriptor = { - configurable: true, - writable: true, - enumerable: false, - value: undefined -}; - -var nullDescriptor = { - configurable: true, - writable: true, - enumerable: false, - value: null -}; - -function makeCtor() { - - // 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 wasApplied = false, 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), proto = m.proto; - m.proto = this; - if (initMixins) { - // capture locally so we can clear the closed over variable - var mixins = initMixins; - initMixins = null; - this.reopen.apply(this, mixins); - } - if (initProperties) { - // capture locally so we can clear the closed over variable - var props = initProperties; - initProperties = null; - - var concatenatedProperties = this.concatenatedProperties; - - 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 Ember.Mixin)); - - if (typeof properties !== 'object' && properties !== undefined) { - throw new Ember.Error("Ember.Object.create only accepts objects."); - } - - if (!properties) { continue; } - - var keyNames = Ember.keys(properties); - - for (var j = 0, ll = keyNames.length; j < ll; j++) { - var keyName = keyNames[j]; - if (!properties.hasOwnProperty(keyName)) { continue; } - - var value = properties[keyName], - IS_BINDING = Ember.IS_BINDING; - - 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; - } + {{#each car in cars groupedRows=true}} + {{car.make}} {{car.model}} {{car.color}} + {{/each}} + {{/group}} + ``` + Any change to `dealershipName` or the `dealers` collection will cause the + entire group to be re-rendered. However, changes to the `cars` collection + will be re-rendered individually (as normal). + + Note that `group` behavior is also disabled by specifying an `itemViewClass`. + + @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.itemController] {String} name of a controller to be created for each item + @param [options.groupedRows] {boolean} enable normal item-by-item rendering when inside a `#group` helper + */ + function eachHelper(path, options) { + var ctx, helperName = 'each'; - var desc = m.descs[keyName]; + 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"); - Ember.assert("Ember.Object.create no longer supports defining computed properties. Define computed properties using extend() or reopen() before calling create().", !(value instanceof Ember.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') && Ember.ActionHandler.detect(this))); + var keywordName = arguments[0]; - if (concatenatedProperties && indexOf(concatenatedProperties, keyName) >= 0) { - var baseValue = this[keyName]; - if (baseValue) { - if ('function' === typeof baseValue.concat) { - value = baseValue.concat(value); - } else { - value = Ember.makeArray(baseValue).concat(value); - } - } else { - value = Ember.makeArray(value); - } - } + options = arguments[3]; + path = arguments[2]; - if (desc) { - desc.set(this, keyName, value); - } else { - if (typeof this.setUnknownProperty === 'function' && !(keyName in this)) { - this.setUnknownProperty(keyName, value); - } else if (MANDATORY_SETTER) { - Ember.defineProperty(this, keyName, null, value); // setup mandatory setter - } else { - this[keyName] = value; - } - } - } - } - } - finishPartial(this, m); - this.init.apply(this, arguments); - m.proto = proto; - finishChains(this); - sendEvent(this, "init"); - }; - - Class.toString = Mixin.prototype.toString; - Class.willReopen = function() { - if (wasApplied) { - Class.PrototypeMixin = Mixin.create(Class.PrototypeMixin); - } - - 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); - rewatch(Class.prototype); - } - - return this.prototype; - }; + helperName += ' ' + keywordName + ' in ' + path; - return Class; + if (path === '') { path = "this"; } -} + options.hash.keyword = keywordName; -/** - @class CoreObject - @namespace Ember -*/ -var CoreObject = makeCtor(); -CoreObject.toString = function() { return "Ember.CoreObject"; }; + } else if (arguments.length === 1) { + options = path; + path = 'this'; + } else { + helperName += ' ' + path; + } -CoreObject.PrototypeMixin = Mixin.create({ - reopen: function() { - applyMixin(this, arguments, true); - return this; - }, + options.hash.dataSourceBinding = path; + // Set up emptyView as a metamorph with no tag + //options.hash.emptyViewClass = Ember._MetamorphView; - /** - An overridable method called when objects are instantiated. By default, - does nothing unless it is overridden during class definition. + // can't rely on this default behavior when use strict + ctx = this || window; - Example: + options.helperName = options.helperName || helperName; - ```javascript - App.Person = Ember.Object.extend({ - init: function() { - alert('Name is ' + this.get('name')); + if (options.data.insideGroup && !options.hash.groupedRows && !options.hash.itemViewClass) { + new GroupedEach(ctx, path, options).render(); + } else { + // ES6TODO: figure out how to do this without global lookup. + return helpers.collection.call(ctx, 'Ember.Handlebars.EachView', options); } - }); + } - var steve = App.Person.create({ - name: "Steve" - }); + __exports__.EachView = EachView; + __exports__.GroupedEach = GroupedEach; + __exports__.eachHelper = eachHelper; + }); +define("ember-handlebars/helpers/loc", + ["ember-runtime/system/string","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var loc = __dependency1__.loc; - // alerts 'Name is Steve'. - ``` + /** + @module ember + @submodule ember-handlebars + */ - 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. + // ES6TODO: + // Pretty sure this can be expressed as + // var locHelper EmberStringUtils.loc ? - @method init - */ - init: function() {}, + /** + Calls [Ember.String.loc](/api/classes/Ember.String.html#method_loc) with the + provided string. - /** - Defines the properties that will be concatenated from the superclass - (instead of overridden). + This is a convenient way to localize text. For example: - 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`. + ```html + + ``` - Here is some sample code showing the difference between a concatenated - property and a normal one: + Take note that `"welcome"` is a string and not an object + reference. - ```javascript - App.BarView = Ember.View.extend({ - someNonConcatenatedProperty: ['bar'], - classNames: ['bar'] - }); + See [Ember.String.loc](/api/classes/Ember.String.html#method_loc) for how to + set up localized string references. - App.FooBarView = App.BarView.extend({ - someNonConcatenatedProperty: ['foo'], - classNames: ['foo'], - }); + @method loc + @for Ember.Handlebars.helpers + @param {String} str The string to format + @see {Ember.String#loc} + */ + __exports__["default"] = function locHelper(str) { + return loc(str); + } + }); +define("ember-handlebars/helpers/partial", + ["ember-metal/core","ember-metal/is_none","ember-handlebars/ext","ember-handlebars/helpers/binding","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // Ember.assert + // var emberAssert = Ember.assert; - var fooBarView = App.FooBarView.create(); - fooBarView.get('someNonConcatenatedProperty'); // ['foo'] - fooBarView.get('classNames'); // ['ember-view', 'bar', 'foo'] - ``` + var isNone = __dependency2__.isNone; + var handlebarsGet = __dependency3__.handlebarsGet; + var bind = __dependency4__.bind; - This behavior extends to object creation as well. Continuing the - above example: + /** + @module ember + @submodule ember-handlebars + */ - ```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: + /** + The `partial` helper renders another template without + changing the template context: - ```javascript - var view = App.FooBarView.create({ - classNames: 'baz' - }) - view.get('classNames'); // ['ember-view', 'bar', 'foo', 'baz'] - ``` + ```handlebars + {{foo}} + {{partial "nav"}} + ``` - 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`. - - This feature is available for you to use throughout the Ember object model, - although typical app developers are likely to use it infrequently. Since - it changes expectations about behavior of properties, you should properly - document its usage in each individual concatenated property (to not - mislead your users to think they can override the property in a subclass). - - @property concatenatedProperties - @type Array - @default null - */ - concatenatedProperties: null, - - /** - Destroyed object property flag. - - if this property is `true` the observers and bindings were already - removed by the effect of calling the `destroy()` method. - - @property isDestroyed - @default false - */ - isDestroyed: false, - - /** - Destruction scheduled flag. The `destroy()` method has been called. - - The object stays intact until the end of the run loop at which point - the `isDestroyed` flag is set. - - @property isDestroying - @default false - */ - isDestroying: false, - - /** - 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. - - 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 destroy - @return {Ember.Object} receiver - */ - destroy: function() { - if (this.isDestroying) { return; } - this.isDestroying = true; - - schedule('actions', this, this.willDestroy); - schedule('destroy', this, this._scheduledDestroy); - return this; - }, - - /** - Override to implement teardown. - - @method willDestroy - */ - willDestroy: Ember.K, - - /** - 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; - }, - - bind: function(to, from) { - if (!(from instanceof Ember.Binding)) { from = Ember.Binding.from(from); } - from.to(to).connect(this); - return from; - }, - - /** - Returns a string representation which attempts to provide more information - than Javascript's `toString` typically does, in a generic way for all Ember - objects. + 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. - ```javascript - App.Person = Em.Object.extend() - person = App.Person.create() - person.toString() //=> "" - ``` + If a "_nav" template isn't found, the `partial` helper will + fall back to a template named "nav". - If the object's class is not defined on an Ember namespace, it will - indicate it is a subclass of the registered superclass: + ## Bound template names - ```javascript - Student = App.Person.extend() - student = Student.create() - student.toString() //=> "<(subclass of App.Person):ember1025>" - ``` + The parameter supplied to `partial` can also be a path + to a property containing a template name, e.g.: - If the method `toStringExtension` is defined, its return value will be - included in the output. + ```handlebars + {{partial someTemplateName}} + ``` - ```javascript - App.Teacher = App.Person.extend({ - toStringExtension: function() { - return this.get('fullName'); - } - }); - teacher = App.Teacher.create() - teacher.toString(); //=> "" - ``` + 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. - @method toString - @return {String} string representation - */ - toString: function toString() { - var hasToStringExtension = typeof this.toStringExtension === 'function', - extension = hasToStringExtension ? ":" + this.toStringExtension() : ''; - var ret = '<'+this.constructor.toString()+':'+guidFor(this)+extension+'>'; - this.toString = makeToString(ret); - return ret; - } -}); + ## Setting the partial's context with `with` -CoreObject.PrototypeMixin.ownerConstructor = CoreObject; + The `partial` helper can be used in conjunction with the `with` + helper to set a context that will be used by the partial: -function makeToString(ret) { - return function() { return ret; }; -} + ```handlebars + {{#with currentUser}} + {{partial "user_info"}} + {{/with}} + ``` -if (Ember.config.overridePrototypeMixin) { - Ember.config.overridePrototypeMixin(CoreObject.PrototypeMixin); -} + @method partial + @for Ember.Handlebars.helpers + @param {String} partialName the name of the template to render minus the leading underscore + */ -CoreObject.__super__ = null; + __exports__["default"] = function partialHelper(name, options) { -var ClassMixin = Mixin.create({ + var context = (options.contexts && options.contexts.length) ? options.contexts[0] : this; - ClassMixin: Ember.required(), + options.helperName = options.helperName || 'partial'; - PrototypeMixin: Ember.required(), + if (options.types[0] === "ID") { + // Helper was passed a property path; we need to + // create a binding that will re-render whenever + // this property changes. + options.fn = function(context, fnOptions) { + var partialName = handlebarsGet(context, name, fnOptions); + renderPartial(context, partialName, fnOptions); + }; - isClass: true, + return bind.call(context, name, options, true, exists); + } else { + // Render the partial right into parent template. + renderPartial(context, name, options); + } + } - isMethod: false, + function exists(value) { + return !isNone(value); + } - /** - Creates a new subclass. + function renderPartial(context, name, options) { + var nameParts = name.split("/"); + var lastPart = nameParts[nameParts.length - 1]; - ```javascript - App.Person = Ember.Object.extend({ - say: function(thing) { - alert(thing); - } - }); - ``` + nameParts[nameParts.length - 1] = "_" + lastPart; - This defines a new subclass of Ember.Object: `App.Person`. It contains one method: `say()`. + var view = options.data.view; + var underscoredName = nameParts.join("/"); + var template = view.templateForName(underscoredName); + var deprecatedTemplate = !template && view.templateForName(name); - 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: + Ember.assert("Unable to find partial with name '"+name+"'.", template || deprecatedTemplate); - ```javascript - App.PersonView = Ember.View.extend({ - tagName: 'li', - classNameBindings: ['isAdministrator'] - }); - ``` + template = template || deprecatedTemplate; - When defining a subclass, you can override methods but still access the implementation of your parent class by calling the special `_super()` method: + template(context, { data: options.data }); + } + }); +define("ember-handlebars/helpers/shared", + ["ember-handlebars/ext","exports"], + function(__dependency1__, __exports__) { + "use strict"; + var handlebarsGet = __dependency1__.handlebarsGet; - ```javascript - App.Person = Ember.Object.extend({ - say: function(thing) { - var name = this.get('name'); - alert(name + ' says: ' + thing); - } - }); + __exports__["default"] = function resolvePaths(options) { + var ret = [], + contexts = options.contexts, + roots = options.roots, + data = options.data; - App.Soldier = App.Person.extend({ - say: function(thing) { - this._super(thing + ", sir!"); - }, - march: function(numberOfHours) { - alert(this.get('name') + ' marches for ' + numberOfHours + ' hours.') + for (var i=0, l=contexts.length; i + {{#with loggedInUser}} + Last Login: {{lastLogin}} + User Info: {{template "user_info"}} + {{/with}} + + ``` - You can also pass `Ember.Mixin` classes to add additional properties to the subclass. + ```html + + ``` - ```javascript - App.Person = Ember.Object.extend({ - say: function(thing) { - alert(this.get('name') + ' says: ' + thing); - } - }); + ```handlebars + {{#if isUser}} + {{template "user_info"}} + {{else}} + {{template "unlogged_user_info"}} + {{/if}} + ``` - App.SingingMixin = Ember.Mixin.create({ - sing: function(thing){ - alert(this.get('name') + ' sings: la la la ' + thing); - } - }); + This helper looks for templates in the global `Ember.TEMPLATES` hash. If you + add `"; - return testEl.firstChild.innerHTML === ''; -})(); + @class Descriptor + @namespace Ember + @private + @constructor + */ + function Descriptor() {} -// IE 8 (and likely earlier) likes to move whitespace preceeding -// a script tag to appear after it. This means that we can -// accidentally remove whitespace when updating a morph. -var movesWhitespace = typeof document !== 'undefined' && (function() { - var testEl = document.createElement('div'); - testEl.innerHTML = "Test: Value"; - return testEl.childNodes[0].nodeValue === 'Test:' && - testEl.childNodes[2].nodeValue === ' Value'; -})(); + __exports__.Descriptor = Descriptor;// .......................................................... + // DEFINING PROPERTIES API + // -// Use this to find children by ID instead of using jQuery -var findChildById = function(element, id) { - if (element.getAttribute('id') === id) { return element; } + var MANDATORY_SETTER_FUNCTION = Ember.MANDATORY_SETTER_FUNCTION = function(value) { + Ember.assert("You must use Ember.set() to access this property (of " + this + ")", false); + }; - var len = element.childNodes.length, idx, node, found; - for (idx=0; idx 0) { - var len = matches.length, idx; - for (idx=0; idx 0; - // IE 8 and earlier don't allow us to do innerHTML on select - if (tagName.toLowerCase() === 'select') { - var el = document.createElement('select'); - setInnerHTMLWithoutFix(el, ''); - canSet = el.options.length === 1; - } + if (existingDesc instanceof Descriptor) { + existingDesc.teardown(obj, keyName); + } - innerHTMLTags[tagName] = canSet; + if (desc instanceof Descriptor) { + value = desc; - return canSet; -}; + descs[keyName] = desc; + if (MANDATORY_SETTER && watching) { + objectDefineProperty(obj, keyName, { + configurable: true, + enumerable: true, + writable: true, + value: undefined // make enumerable + }); + } else { + obj[keyName] = undefined; // make enumerable + } + if (desc.setup) { desc.setup(obj, keyName); } + } else { + descs[keyName] = undefined; // shadow descriptor in proto + if (desc == null) { + value = data; + + if (MANDATORY_SETTER && watching) { + meta.values[keyName] = data; + objectDefineProperty(obj, keyName, { + configurable: true, + enumerable: true, + set: MANDATORY_SETTER_FUNCTION, + get: DEFAULT_GETTER_FUNCTION(keyName) + }); + } else { + obj[keyName] = data; + } + } else { + value = desc; -var setInnerHTML = function(element, html) { - var tagName = element.tagName; + // compatibility with ES5 + objectDefineProperty(obj, keyName, desc); + } + } - if (canSetInnerHTML(tagName)) { - setInnerHTMLWithoutFix(element, html); - } else { - // Firefox versions < 11 do not have support for element.outerHTML. - var outerHTML = element.outerHTML || new XMLSerializer().serializeToString(element); - Ember.assert("Can't set innerHTML on "+element.tagName+" in this browser", outerHTML); + // if key is being watched, override chains that + // were initialized with the prototype + if (watching) { overrideChains(obj, keyName, meta); } - var startTag = outerHTML.match(new RegExp("<"+tagName+"([^>]*)>", 'i'))[0], - endTag = ''; + // The `value` passed to the `didDefineProperty` hook is + // either the descriptor or data, whichever was passed. + if (obj.didDefineProperty) { obj.didDefineProperty(obj, keyName, value); } - var wrapper = document.createElement('div'); - setInnerHTMLWithoutFix(wrapper, startTag + html + endTag); - element = wrapper.firstChild; - while (element.tagName !== tagName) { - element = element.nextSibling; + return this; } - } - - return element; -}; -function isSimpleClick(event) { - var modifier = event.shiftKey || event.metaKey || event.altKey || event.ctrlKey, - secondaryClick = event.which > 1; // IE9 may return undefined + __exports__.defineProperty = defineProperty;/** + Used internally to allow changing properties in a backwards compatible way, and print a helpful + deprecation warning. - return !modifier && !secondaryClick; -} + @method deprecateProperty + @param {Object} object The object to add the deprecated property to. + @param {String} deprecatedKey The property to add (and print deprecation warnings upon accessing). + @param {String} newKey The property that will be aliased. + @private + @since 1.7.0 + */ -Ember.ViewUtils = { - setInnerHTML: setInnerHTML, - isSimpleClick: isSimpleClick -}; + function deprecateProperty(object, deprecatedKey, newKey) { + function deprecate() { + Ember.deprecate('Usage of `' + deprecatedKey + '` is deprecated, use `' + newKey + '` instead.'); + } -})(); + if (platform.hasPropertyAccessors) { + defineProperty(object, deprecatedKey, { + configurable: true, + enumerable: false, + set: function(value) { deprecate(); set(object, newKey, value); }, + get: function() { deprecate(); return get(object, newKey); } + }); + } + } + __exports__.deprecateProperty = deprecateProperty; + }); +define("ember-metal/property_events", + ["ember-metal/utils","ember-metal/events","ember-metal/observer_set","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var META_KEY = __dependency1__.META_KEY; + var guidFor = __dependency1__.guidFor; + var tryFinally = __dependency1__.tryFinally; + var sendEvent = __dependency2__.sendEvent; + var listenersUnion = __dependency2__.listenersUnion; + var listenersDiff = __dependency2__.listenersDiff; + var ObserverSet = __dependency3__["default"]; + + var beforeObserverSet = new ObserverSet(); + var observerSet = new ObserverSet(); + var deferred = 0; + + // .......................................................... + // PROPERTY CHANGES + // + /** + 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. + + @method propertyWillChange + @for Ember + @param {Object} obj The object with the property that will change + @param {String} keyName The property key (or path) that will change. + @return {void} + */ + function propertyWillChange(obj, keyName) { + var m = obj[META_KEY], + watching = (m && m.watching[keyName] > 0) || keyName === 'length', + proto = m && m.proto, + desc = m && m.descs[keyName]; -(function() { -/** -@module ember -@submodule ember-views -*/ + if (!watching) { return; } + if (proto === obj) { return; } + if (desc && desc.willChange) { desc.willChange(obj, keyName); } + dependentKeysWillChange(obj, keyName, m); + chainsWillChange(obj, keyName, m); + notifyBeforeObservers(obj, keyName); + } -var get = Ember.get, set = Ember.set; + /** + 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[META_KEY], + watching = (m && m.watching[keyName] > 0) || keyName === 'length', + proto = m && m.proto, + desc = m && m.descs[keyName]; -var ClassSet = function() { - this.seen = {}; - this.list = []; -}; + if (proto === obj) { return; } -ClassSet.prototype = { - add: function(string) { - if (string in this.seen) { return; } - this.seen[string] = true; + // shouldn't this mean that we're watching this key? + if (desc && desc.didChange) { desc.didChange(obj, keyName); } + if (!watching && keyName !== 'length') { return; } - this.list.push(string); - }, + dependentKeysDidChange(obj, keyName, m); + chainsDidChange(obj, keyName, m, false); + notifyObservers(obj, keyName); + } - toDOM: function() { - return this.list.join(" "); - } -}; + var WILL_SEEN, DID_SEEN; -var BAD_TAG_NAME_TEST_REGEXP = /[^a-zA-Z0-9\-]/; -var BAD_TAG_NAME_REPLACE_REGEXP = /[^a-zA-Z0-9\-]/g; + // 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; } -function stripTagName(tagName) { - if (!tagName) { - return tagName; - } + var seen = WILL_SEEN, top = !seen; + if (top) { seen = WILL_SEEN = {}; } + iterDeps(propertyWillChange, obj, depKey, seen, meta); + if (top) { WILL_SEEN = null; } + } - if (!BAD_TAG_NAME_TEST_REGEXP.test(tagName)) { - return tagName; - } + // called whenever a property has just changed to update dependent keys + function dependentKeysDidChange(obj, depKey, meta) { + if (obj.isDestroying) { return; } - return tagName.replace(BAD_TAG_NAME_REPLACE_REGEXP, ''); -} + var seen = DID_SEEN, top = !seen; + if (top) { seen = DID_SEEN = {}; } + iterDeps(propertyDidChange, obj, depKey, seen, meta); + if (top) { DID_SEEN = null; } + } -var BAD_CHARS_REGEXP = /&(?!\w+;)|[<>"'`]/g; -var POSSIBLE_CHARS_REGEXP = /[&<>"'`]/; + function iterDeps(method, obj, depKey, seen, meta) { + var guid = guidFor(obj); + if (!seen[guid]) seen[guid] = {}; + if (seen[guid][depKey]) return; + seen[guid][depKey] = true; -function escapeAttribute(value) { - // Stolen shamelessly from Handlebars + var deps = meta.deps; + deps = deps && deps[depKey]; + if (deps) { + for(var key in deps) { + var desc = meta.descs[key]; + if (desc && desc._suspended === obj) continue; + method(obj, key); + } + } + } - var escape = { - "<": "<", - ">": ">", - '"': """, - "'": "'", - "`": "`" - }; + function chainsWillChange(obj, keyName, m) { + if (!(m.hasOwnProperty('chainWatchers') && + m.chainWatchers[keyName])) { + return; + } - var escapeChar = function(chr) { - return escape[chr] || "&"; - }; + var nodes = m.chainWatchers[keyName], + events = [], + i, l; - var string = value.toString(); + for(i = 0, l = nodes.length; i < l; i++) { + nodes[i].willChange(events); + } - if(!POSSIBLE_CHARS_REGEXP.test(string)) { return string; } - return string.replace(BAD_CHARS_REGEXP, escapeChar); -} + for (i = 0, l = events.length; i < l; i += 2) { + propertyWillChange(events[i], events[i+1]); + } + } -// IE 6/7 have bugs around setting names on inputs during creation. -// From http://msdn.microsoft.com/en-us/library/ie/ms536389(v=vs.85).aspx: -// "To include the NAME attribute at run time on objects created with the createElement method, use the eTag." -var canSetNameOnInputs = (function() { - var div = document.createElement('div'), - el = document.createElement('input'); + function chainsDidChange(obj, keyName, m, suppressEvents) { + if (!(m && m.hasOwnProperty('chainWatchers') && + m.chainWatchers[keyName])) { + return; + } - el.setAttribute('name', 'foo'); - div.appendChild(el); + var nodes = m.chainWatchers[keyName], + events = suppressEvents ? null : [], + i, l; - return !!div.innerHTML.match('foo'); -})(); + for(i = 0, l = nodes.length; i < l; i++) { + nodes[i].didChange(events); + } -/** - `Ember.RenderBuffer` gathers information regarding the a view and generates the - final representation. `Ember.RenderBuffer` will generate HTML which can be pushed - to the DOM. - - ```javascript - var buffer = Ember.RenderBuffer('div'); - ``` - - @class RenderBuffer - @namespace Ember - @constructor - @param {String} tagName tag name (such as 'div' or 'p') used for the buffer -*/ -Ember.RenderBuffer = function(tagName) { - return new Ember._RenderBuffer(tagName); -}; - -Ember._RenderBuffer = function(tagName) { - this.tagNames = [tagName || null]; - this.buffer = ""; -}; - -Ember._RenderBuffer.prototype = { - - // The root view's element - _element: null, - - _hasElement: true, - - /** - An internal set used to de-dupe class names when `addClass()` is - used. After each call to `addClass()`, the `classes` property - will be updated. - - @private - @property elementClasses - @type Array - @default [] - */ - elementClasses: null, - - /** - Array of class names which will be applied in the class attribute. - - You can use `setClasses()` to set this property directly. If you - use `addClass()`, it will be maintained for you. - - @property classes - @type Array - @default [] - */ - classes: null, - - /** - The id in of the element, to be applied in the id attribute. - - You should not set this property yourself, rather, you should use - the `id()` method of `Ember.RenderBuffer`. - - @property elementId - @type String - @default null - */ - elementId: null, - - /** - A hash keyed on the name of the attribute and whose value will be - applied to that attribute. For example, if you wanted to apply a - `data-view="Foo.bar"` property to an element, you would set the - elementAttributes hash to `{'data-view':'Foo.bar'}`. - - You should not maintain this hash yourself, rather, you should use - the `attr()` method of `Ember.RenderBuffer`. - - @property elementAttributes - @type Hash - @default {} - */ - elementAttributes: null, - - /** - A hash keyed on the name of the properties and whose value will be - applied to that property. For example, if you wanted to apply a - `checked=true` property to an element, you would set the - elementProperties hash to `{'checked':true}`. - - You should not maintain this hash yourself, rather, you should use - the `prop()` method of `Ember.RenderBuffer`. - - @property elementProperties - @type Hash - @default {} - */ - elementProperties: null, - - /** - The tagname of the element an instance of `Ember.RenderBuffer` represents. - - Usually, this gets set as the first parameter to `Ember.RenderBuffer`. For - example, if you wanted to create a `p` tag, then you would call + if (suppressEvents) { + return; + } - ```javascript - Ember.RenderBuffer('p') - ``` + for (i = 0, l = events.length; i < l; i += 2) { + propertyDidChange(events[i], events[i+1]); + } + } - @property elementTag - @type String - @default null - */ - elementTag: null, - - /** - A hash keyed on the name of the style attribute and whose value will - be applied to that attribute. For example, if you wanted to apply a - `background-color:black;` style to an element, you would set the - elementStyle hash to `{'background-color':'black'}`. - - You should not maintain this hash yourself, rather, you should use - the `style()` method of `Ember.RenderBuffer`. - - @property elementStyle - @type Hash - @default {} - */ - elementStyle: null, - - /** - Nested `RenderBuffers` will set this to their parent `RenderBuffer` - instance. - - @property parentBuffer - @type Ember._RenderBuffer - */ - parentBuffer: null, - - /** - Adds a string of HTML to the `RenderBuffer`. - - @method push - @param {String} string HTML to push into the buffer - @chainable - */ - push: function(string) { - this.buffer += string; - return this; - }, - - /** - Adds a class to the buffer, which will be rendered to the class attribute. - - @method addClass - @param {String} className Class name to add to the buffer - @chainable - */ - addClass: function(className) { - // lazily create elementClasses - this.elementClasses = (this.elementClasses || new ClassSet()); - this.elementClasses.add(className); - this.classes = this.elementClasses.list; - - return this; - }, - - setClasses: function(classNames) { - this.elementClasses = null; - var len = classNames.length, i; - for (i = 0; i < len; i++) { - this.addClass(classNames[i]); - } - }, - - /** - Sets the elementID to be used for the element. - - @method id - @param {String} id - @chainable - */ - id: function(id) { - this.elementId = id; - return this; - }, - - // duck type attribute functionality like jQuery so a render buffer - // can be used like a jQuery object in attribute binding scenarios. - - /** - Adds an attribute which will be rendered to the element. - - @method attr - @param {String} name The name of the attribute - @param {String} value The value to add to the attribute - @chainable - @return {Ember.RenderBuffer|String} this or the current attribute value - */ - attr: function(name, value) { - var attributes = this.elementAttributes = (this.elementAttributes || {}); - - if (arguments.length === 1) { - return attributes[name]; - } else { - attributes[name] = value; + function overrideChains(obj, keyName, m) { + chainsDidChange(obj, keyName, m, true); } - return this; - }, + /** + @method beginPropertyChanges + @chainable + @private + */ + function beginPropertyChanges() { + deferred++; + } - /** - Remove an attribute from the list of attributes to render. + /** + @method endPropertyChanges + @private + */ + function endPropertyChanges() { + deferred--; + if (deferred<=0) { + beforeObserverSet.clear(); + observerSet.flush(); + } + } - @method removeAttr - @param {String} name The name of the attribute - @chainable - */ - removeAttr: function(name) { - var attributes = this.elementAttributes; - if (attributes) { delete attributes[name]; } + /** + Make a series of property changes together in an + exception-safe way. - return this; - }, + ```javascript + Ember.changeProperties(function() { + obj1.set('foo', mayBlowUpWhenSet); + obj2.set('bar', baz); + }); + ``` - /** - Adds a property which will be rendered to the element. + @method changeProperties + @param {Function} callback + @param [binding] + */ + function changeProperties(cb, binding) { + beginPropertyChanges(); + tryFinally(cb, endPropertyChanges, binding); + } - @method prop - @param {String} name The name of the property - @param {String} value The value to add to the property - @chainable - @return {Ember.RenderBuffer|String} this or the current property value - */ - prop: function(name, value) { - var properties = this.elementProperties = (this.elementProperties || {}); + function notifyBeforeObservers(obj, keyName) { + if (obj.isDestroying) { return; } - if (arguments.length === 1) { - return properties[name]; - } else { - properties[name] = value; + var eventName = keyName + ':before', listeners, diff; + if (deferred) { + listeners = beforeObserverSet.add(obj, keyName, eventName); + diff = listenersDiff(obj, eventName, listeners); + sendEvent(obj, eventName, [obj, keyName], diff); + } else { + sendEvent(obj, eventName, [obj, keyName]); + } } - return this; - }, + function notifyObservers(obj, keyName) { + if (obj.isDestroying) { return; } - /** - Remove an property from the list of properties to render. - - @method removeProp - @param {String} name The name of the property - @chainable - */ - removeProp: function(name) { - var properties = this.elementProperties; - if (properties) { delete properties[name]; } + var eventName = keyName + ':change', listeners; + if (deferred) { + listeners = observerSet.add(obj, keyName, eventName); + listenersUnion(obj, eventName, listeners); + } else { + sendEvent(obj, eventName, [obj, keyName]); + } + } - return this; - }, + __exports__.propertyWillChange = propertyWillChange; + __exports__.propertyDidChange = propertyDidChange; + __exports__.overrideChains = overrideChains; + __exports__.beginPropertyChanges = beginPropertyChanges; + __exports__.endPropertyChanges = endPropertyChanges; + __exports__.changeProperties = changeProperties; + }); +define("ember-metal/property_get", + ["ember-metal/core","ember-metal/utils","ember-metal/error","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + /** + @module ember-metal + */ - /** - Adds a style to the style attribute which will be rendered to the element. + var Ember = __dependency1__["default"]; + var META_KEY = __dependency2__.META_KEY; + var EmberError = __dependency3__["default"]; - @method style - @param {String} name Name of the style - @param {String} value - @chainable - */ - style: function(name, value) { - this.elementStyle = (this.elementStyle || {}); + var get; - this.elementStyle[name] = value; - return this; - }, + var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER; - begin: function(tagName) { - this.tagNames.push(tagName || null); - return this; - }, + var IS_GLOBAL_PATH = /^([A-Z$]|([0-9][A-Z$])).*[\.]/; + var HAS_THIS = 'this.'; + var FIRST_KEY = /^([^\.]+)/; - pushOpeningTag: function() { - var tagName = this.currentTagName(); - if (!tagName) { return; } + // .......................................................... + // 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. - if (this._hasElement && !this._element && this.buffer.length === 0) { - this._element = this.generateElement(); - return; - } + /** + 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. + + 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.) + + 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. + + Note that if the object itself is `undefined`, this method will throw + an error. + + @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; + } - var buffer = this.buffer, - id = this.elementId, - classes = this.classes, - attrs = this.elementAttributes, - props = this.elementProperties, - style = this.elementStyle, - attr, prop; + if (!keyName && 'string'===typeof obj) { + keyName = obj; + obj = null; + } - buffer += '<' + stripTagName(tagName); + Ember.assert("Cannot call get with "+ keyName +" key.", !!keyName); + Ember.assert("Cannot call get with '"+ keyName +"' on an undefined object.", obj !== undefined); - if (id) { - buffer += ' id="' + escapeAttribute(id) + '"'; - this.elementId = null; - } - if (classes) { - buffer += ' class="' + escapeAttribute(classes.join(' ')) + '"'; - this.classes = null; - this.elementClasses = null; - } + if (obj === null) { return _getPath(obj, keyName); } - if (style) { - buffer += ' style="'; + var meta = obj[META_KEY], desc = meta && meta.descs[keyName], ret; - for (prop in style) { - if (style.hasOwnProperty(prop)) { - buffer += prop + ':' + escapeAttribute(style[prop]) + ';'; - } + if (desc === undefined && keyName.indexOf('.') !== -1) { + return _getPath(obj, keyName); } - buffer += '"'; - - this.elementStyle = null; - } - - if (attrs) { - for (attr in attrs) { - if (attrs.hasOwnProperty(attr)) { - buffer += ' ' + attr + '="' + escapeAttribute(attrs[attr]) + '"'; + if (desc) { + return desc.get(obj, keyName); + } else { + if (MANDATORY_SETTER && meta && meta.watching[keyName] > 0) { + ret = meta.values[keyName]; + } else { + ret = obj[keyName]; } - } - - this.elementAttributes = null; - } - if (props) { - for (prop in props) { - if (props.hasOwnProperty(prop)) { - var value = props[prop]; - if (value || typeof(value) === 'number') { - if (value === true) { - buffer += ' ' + prop + '="' + prop + '"'; - } else { - buffer += ' ' + prop + '="' + escapeAttribute(props[prop]) + '"'; - } - } + if (ret === undefined && + 'object' === typeof obj && !(keyName in obj) && 'function' === typeof obj.unknownProperty) { + return obj.unknownProperty(keyName); } + + return ret; } + }; - this.elementProperties = null; + // Currently used only by Ember Data tests + if (Ember.config.overrideAccessors) { + Ember.get = get; + Ember.config.overrideAccessors(); + get = Ember.get; } - buffer += '>'; - this.buffer = buffer; - }, - - pushClosingTag: function() { - var tagName = this.tagNames.pop(); - if (tagName) { this.buffer += ''; } - }, + /** + 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). - currentTagName: function() { - return this.tagNames[this.tagNames.length-1]; - }, + @private + @method normalizeTuple + @for Ember + @param {Object} target The current target. May be `null`. + @param {String} path A path on the target or a global property path. + @return {Array} a temporary array with the normalized target/path pair. + */ + function normalizeTuple(target, path) { + var hasThis = path.indexOf(HAS_THIS) === 0, + isGlobal = !hasThis && IS_GLOBAL_PATH.test(path), + key; - generateElement: function() { - var tagName = this.tagNames.pop(), // pop since we don't need to close - id = this.elementId, - classes = this.classes, - attrs = this.elementAttributes, - props = this.elementProperties, - style = this.elementStyle, - styleBuffer = '', attr, prop, tagString; + if (!target || isGlobal) target = Ember.lookup; + if (hasThis) path = path.slice(5); - if (attrs && attrs.name && !canSetNameOnInputs) { - // IE allows passing a tag to createElement. See note on `canSetNameOnInputs` above as well. - tagString = '<'+stripTagName(tagName)+' name="'+escapeAttribute(attrs.name)+'">'; - } else { - tagString = tagName; - } + if (target === Ember.lookup) { + key = path.match(FIRST_KEY)[0]; + target = get(target, key); + path = path.slice(key.length+1); + } - var element = document.createElement(tagString), - $element = Ember.$(element); + // 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 (id) { - $element.attr('id', id); - this.elementId = null; - } - if (classes) { - $element.attr('class', classes.join(' ')); - this.classes = null; - this.elementClasses = null; + return [ target, path ]; } - if (style) { - for (prop in style) { - if (style.hasOwnProperty(prop)) { - styleBuffer += (prop + ':' + style[prop] + ';'); - } - } + function _getPath(root, path) { + var hasThis, parts, tuple, idx, len; - $element.attr('style', styleBuffer); + // If there is no root and path is a key name, return that + // property from the global object. + // E.g. get('Ember') -> Ember + if (root === null && path.indexOf('.') === -1) { return get(Ember.lookup, path); } - this.elementStyle = null; - } + // detect complicated paths and normalize them + hasThis = path.indexOf(HAS_THIS) === 0; - if (attrs) { - for (attr in attrs) { - if (attrs.hasOwnProperty(attr)) { - $element.attr(attr, attrs[attr]); - } + if (!root || hasThis) { + tuple = normalizeTuple(root, path); + root = tuple[0]; + path = tuple[1]; + tuple.length = 0; } - this.elementAttributes = null; - } - - if (props) { - for (prop in props) { - if (props.hasOwnProperty(prop)) { - $element.prop(prop, props[prop]); - } + 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; } } - - this.elementProperties = null; + return root; } - return element; - }, - - /** - @method element - @return {DOMElement} The element corresponding to the generated HTML - of this buffer - */ - element: function() { - var html = this.innerString(); + function getWithDefault(root, key, defaultValue) { + var value = get(root, key); - if (html) { - this._element = Ember.ViewUtils.setInnerHTML(this._element, html); + if (value === undefined) { return defaultValue; } + return value; } - return this._element; - }, + __exports__.getWithDefault = getWithDefault;__exports__["default"] = get; + __exports__.get = get; + __exports__.normalizeTuple = normalizeTuple; + __exports__._getPath = _getPath; + }); +define("ember-metal/property_set", + ["ember-metal/core","ember-metal/property_get","ember-metal/utils","ember-metal/property_events","ember-metal/properties","ember-metal/error","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + var getPath = __dependency2__._getPath; + var META_KEY = __dependency3__.META_KEY; + var propertyWillChange = __dependency4__.propertyWillChange; + var propertyDidChange = __dependency4__.propertyDidChange; + var defineProperty = __dependency5__.defineProperty; + var EmberError = __dependency6__["default"]; - /** - Generates the HTML content for this buffer. + var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER; + var IS_GLOBAL = /^([A-Z$]|([0-9][A-Z$]))/; - @method string - @return {String} The generated HTML - */ - string: function() { - if (this._hasElement && this._element) { - // Firefox versions < 11 do not have support for element.outerHTML. - var thisElement = this.element(), outerHTML = thisElement.outerHTML; - if (typeof outerHTML === 'undefined') { - return Ember.$('
    ').append(thisElement).html(); + /** + 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. + + @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; } - return outerHTML; - } else { - return this.innerString(); - } - }, - - innerString: function() { - return this.buffer; - } -}; - -})(); - + Ember.assert("Cannot call set with "+ keyName +" key.", !!keyName); -(function() { -/** -@module ember -@submodule ember-views -*/ - -var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt; - -/** - `Ember.EventDispatcher` handles delegating browser events to their - corresponding `Ember.Views.` For example, when you click on a view, - `Ember.EventDispatcher` ensures that that view's `mouseDown` method gets - called. - - @class EventDispatcher - @namespace Ember - @private - @extends Ember.Object -*/ -Ember.EventDispatcher = Ember.Object.extend({ - - /** - 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. - - This set will be modified by `setup` to also include any events added at that time. - - @property events - @type Object - */ - events: { - touchstart : 'touchStart', - touchmove : 'touchMove', - touchend : 'touchEnd', - touchcancel : 'touchCancel', - keydown : 'keyDown', - keyup : 'keyUp', - keypress : 'keyPress', - mousedown : 'mouseDown', - mouseup : 'mouseUp', - contextmenu : 'contextMenu', - click : 'click', - dblclick : 'doubleClick', - mousemove : 'mouseMove', - focusin : 'focusIn', - focusout : 'focusOut', - mouseenter : 'mouseEnter', - mouseleave : 'mouseLeave', - submit : 'submit', - input : 'input', - change : 'change', - dragstart : 'dragStart', - drag : 'drag', - dragenter : 'dragEnter', - dragleave : 'dragLeave', - dragover : 'dragOver', - drop : 'drop', - dragend : 'dragEnd' - }, - - /** - The root DOM element to which event listeners should be attached. Event - listeners will be attached to the document unless this is overridden. - - Can be specified as a DOMElement or a selector string. - - The default body is a string since this may be evaluated before document.body - exists in the DOM. - - @private - @property rootElement - @type DOMElement - @default 'body' - */ - rootElement: 'body', - - /** - 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'); - - Ember.$.extend(events, addedEvents || {}); - - - if (!Ember.isNone(rootElement)) { - set(this, 'rootElement', rootElement); - } - - rootElement = Ember.$(get(this, 'rootElement')); - - Ember.assert(fmt('You cannot use the same root element (%@) multiple times in an Ember.Application', [rootElement.selector || rootElement[0].tagName]), !rootElement.is('.ember-application')); - Ember.assert('You cannot make a new Ember.Application using a root element that is a descendent of an existing Ember.Application', !rootElement.closest('.ember-application').length); - Ember.assert('You cannot make a new Ember.Application using a root element that is an ancestor of an existing Ember.Application', !rootElement.find('.ember-application').length); - - rootElement.addClass('ember-application'); - - Ember.assert('Unable to add "ember-application" class to rootElement. Make sure you set rootElement to the body or an element in the body.', rootElement.is('.ember-application')); - - for (event in events) { - if (events.hasOwnProperty(event)) { - this.setupHandler(rootElement, event, events[event]); - } - } - }, - - /** - Registers an event listener on the document. If the given event is - triggered, the provided event handler will be triggered on the target view. - - If the target view does not implement the event handler, or if the handler - returns `false`, the parent view will be called. The event will continue to - bubble to each successive parent view until it reaches the top. - - For example, to have the `mouseDown` method called on the target view when - a `mousedown` event is received from the browser, do the following: - - ```javascript - setupHandler('mousedown', 'mouseDown'); - ``` - - @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 = Ember.View.views[this.id], - result = true, manager = null; - - manager = self._findNearestEventManager(view, eventName); - - if (manager && manager !== triggeringManager) { - result = self._dispatchEvent(manager, evt, eventName, view); - } else if (view) { - result = self._bubbleEvent(view, evt, eventName); - } else { - evt.stopPropagation(); + if (!obj) { + return setPath(obj, keyName, value, tolerant); } - return result; - }); - - rootElement.on(event + '.ember', '[data-ember-action]', function(evt) { - var actionId = Ember.$(evt.currentTarget).attr('data-ember-action'), - action = Ember.Handlebars.ActionHelper.registeredActions[actionId]; + var meta = obj[META_KEY], desc = meta && meta.descs[keyName], + isUnknown, currentValue; - // We have to check for action here since in some cases, jQuery will trigger - // an event on `removeChild` (i.e. focusout) after we've already torn down the - // action handlers for the view. - if (action && action.eventName === eventName) { - return action.handler(evt); + if (desc === undefined && keyName.indexOf('.') !== -1) { + return setPath(obj, keyName, value, tolerant); } - }); - }, - _findNearestEventManager: function(view, eventName) { - var manager = null; + Ember.assert("You need to provide an object and key to `set`.", !!obj && keyName !== undefined); + Ember.assert('calling set on destroyed object', !obj.isDestroyed); - while (view) { - manager = get(view, 'eventManager'); - if (manager && manager[eventName]) { break; } + if (desc !== undefined) { + desc.set(obj, keyName, value); + } else { - view = get(view, 'parentView'); - } + if (typeof obj === 'object' && obj !== null && value !== undefined && obj[keyName] === value) { + return value; + } - return manager; - }, + isUnknown = 'object' === typeof obj && !(keyName in obj); - _dispatchEvent: function(object, evt, eventName, view) { - var result = true; + // setUnknownProperty is called if `obj` is an object, + // the property does not already exist, and the + // `setUnknownProperty` method exists on the object + if (isUnknown && 'function' === typeof obj.setUnknownProperty) { + obj.setUnknownProperty(keyName, value); + } else if (meta && meta.watching[keyName] > 0) { + if (MANDATORY_SETTER) { + currentValue = meta.values[keyName]; + } else { + currentValue = obj[keyName]; + } + // only trigger a change if the value has changed + if (value !== currentValue) { + propertyWillChange(obj, keyName); + if (MANDATORY_SETTER) { + if ((currentValue === undefined && !(keyName in obj)) || !obj.propertyIsEnumerable(keyName)) { + defineProperty(obj, keyName, null, value); // setup mandatory setter + } else { + meta.values[keyName] = value; + } + } else { + obj[keyName] = value; + } + propertyDidChange(obj, keyName); + } + } else { + obj[keyName] = value; + } + } + return value; + }; - var handler = object[eventName]; - if (Ember.typeOf(handler) === 'function') { - result = Ember.run(object, handler, evt, view); - // Do not preventDefault in eventManagers. - evt.stopPropagation(); + // Currently used only by Ember Data tests + // ES6TODO: Verify still true + if (Ember.config.overrideAccessors) { + Ember.set = set; + Ember.config.overrideAccessors(); + set = Ember.set; } - else { - result = this._bubbleEvent(view, evt, eventName); - } - - return result; - }, - _bubbleEvent: function(view, evt, eventName) { - return Ember.run(view, view.handleEvent, eventName, evt); - }, + function setPath(root, path, value, tolerant) { + var keyName; - destroy: function() { - var rootElement = get(this, 'rootElement'); - Ember.$(rootElement).off('.ember', '**').removeClass('ember-application'); - return this._super(); - } -}); + // get the last part of the path + keyName = path.slice(path.lastIndexOf('.') + 1); -})(); + // get the first part of the part + path = (path === keyName) ? keyName : path.slice(0, path.length-(keyName.length+1)); + // unless the path is this, look up the first part to + // get the root + if (path !== 'this') { + root = getPath(root, path); + } + if (!keyName || keyName.length === 0) { + throw new EmberError('Property set failed: You passed an empty path'); + } -(function() { -/** -@module ember -@submodule ember-views -*/ - -// Add a new named queue for rendering views that happens -// after bindings have synced, and a queue for scheduling actions -// that that should occur after view rendering. -var queues = Ember.run.queues, - indexOf = Ember.ArrayPolyfills.indexOf; -queues.splice(indexOf.call(queues, 'actions')+1, 0, 'render', 'afterRender'); + if (!root) { + if (tolerant) { return; } + else { throw new EmberError('Property set failed: object in path "'+path+'" could not be found or was destroyed.'); } + } -})(); + return set(root, keyName, value); + } + /** + 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. -(function() { + @method trySet + @for Ember + @param {Object} obj The object to modify. + @param {String} path The property path to set + @param {Object} value The value to set + */ + function trySet(root, path, value) { + return set(root, path, value, true); + } -})(); + __exports__.trySet = trySet;__exports__.set = set; + }); +define("ember-metal/run_loop", + ["ember-metal/core","ember-metal/utils","ember-metal/array","ember-metal/property_events","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + var apply = __dependency2__.apply; + var indexOf = __dependency3__.indexOf; + var beginPropertyChanges = __dependency4__.beginPropertyChanges; + var endPropertyChanges = __dependency4__.endPropertyChanges; + function onBegin(current) { + run.currentRunLoop = current; + } + function onEnd(current, next) { + run.currentRunLoop = next; + } -(function() { -var states = {}; + // ES6TODO: should Backburner become es6? + var Backburner = requireModule('backburner').Backburner; + var backburner = new Backburner(['sync', 'actions', 'destroy'], { + sync: { + before: beginPropertyChanges, + after: endPropertyChanges + }, + defaultQueue: 'actions', + onBegin: onBegin, + onEnd: onEnd, + onErrorTarget: Ember, + onErrorMethod: 'onerror' + }); + var slice = [].slice; + var concat = [].concat; -/** -@module ember -@submodule ember-views -*/ + // .......................................................... + // run - this is ideally the only public API the dev sees + // -var get = Ember.get, set = Ember.set, - guidFor = Ember.guidFor, - a_forEach = Ember.EnumerableUtils.forEach, - a_addObject = Ember.EnumerableUtils.addObject, - meta = Ember.meta, - defineProperty = Ember.defineProperty; + /** + Runs the passed target and method inside of a RunLoop, ensuring any + deferred actions including bindings and views updates are flushed at the + end. -function nullViewsBuffer(view) { - view.buffer = null; -} + Normally you should not need to invoke this method yourself. However if + you are implementing raw event handlers when interfacing with other + libraries or plugins, you should probably wrap all of your code inside this + call. -var childViewsProperty = Ember.computed(function() { - var childViews = this._childViews, ret = Ember.A(), view = this; + ```javascript + run(function() { + // code to be execute within a RunLoop + }); + ``` - a_forEach(childViews, function(view) { - var currentChildViews; - if (view.isVirtual) { - if (currentChildViews = get(view, 'childViews')) { - ret.pushObjects(currentChildViews); - } - } else { - ret.push(view); + @class run + @namespace Ember + @static + @constructor + @param {Object} [target] target of method to call + @param {Function|String} method Method to invoke. + May be a function or a string. If you pass a string + then it will be looked up on the passed target. + @param {Object} [args*] Any additional arguments you wish to pass to the method. + @return {Object} return value from invoking the passed function. + */ + __exports__["default"] = run; + function run() { + return apply(backburner, backburner.run, arguments); } - }); - ret.replace = function (idx, removedCount, addedViews) { - if (view instanceof Ember.ContainerView) { - Ember.deprecate("Manipulating an Ember.ContainerView through its childViews property is deprecated. Please use the ContainerView instance itself as an Ember.MutableArray."); - return view.replace(idx, removedCount, addedViews); - } - throw new Ember.Error("childViews is immutable"); - }; - - return ret; -}); - -Ember.warn("The VIEW_PRESERVES_CONTEXT flag has been removed and the functionality can no longer be disabled.", Ember.ENV.VIEW_PRESERVES_CONTEXT !== false); - -/** - Global hash of shared templates. This will automatically be populated - by the build tools so that you can store your Handlebars templates in - separate files that get loaded into JavaScript at buildtime. - - @property TEMPLATES - @for Ember - @type Hash -*/ -Ember.TEMPLATES = {}; - -/** - `Ember.CoreView` is an abstract class that exists to give view-like behavior - to both Ember's main view class `Ember.View` and other classes like - `Ember._SimpleMetamorphView` that don't need the fully functionaltiy of - `Ember.View`. - - Unless you have specific needs for `CoreView`, you will use `Ember.View` - in your applications. - - @class CoreView - @namespace Ember - @extends Ember.Object - @uses Ember.Evented - @uses Ember.ActionHandler -*/ - -Ember.CoreView = Ember.Object.extend(Ember.Evented, Ember.ActionHandler, { - isView: true, - - states: states, - - init: function() { - this._super(); - this.transitionTo('preRender'); - this._isVisible = get(this, 'isVisible'); - }, - - /** - If the view is currently inserted into the DOM of a parent view, this - property will point to the parent of the view. - - @property parentView - @type Ember.View - @default null - */ - parentView: Ember.computed('_parentView', function() { - var parent = this._parentView; - - if (parent && parent.isVirtual) { - return get(parent, 'parentView'); - } else { - return parent; - } - }), - - state: null, - - _parentView: null, - - // return the current view, not including virtual views - concreteView: Ember.computed('parentView', function() { - if (!this.isVirtual) { return this; } - else { return get(this, 'parentView'); } - }), - - instrumentName: 'core_view', - - instrumentDetails: function(hash) { - hash.object = this.toString(); - }, - - /** - Invoked by the view system when this view needs to produce an HTML - representation. This method will create a new render buffer, if needed, - then apply any default attributes, such as class names and visibility. - Finally, the `render()` method is invoked, which is responsible for - doing the bulk of the rendering. + /** + If 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. - You should not need to override this method; instead, implement the - `template` property, or if you need more control, override the `render` - method. + Please note: This is not for normal usage, and should be used sparingly. - @method renderToBuffer - @param {Ember.RenderBuffer} buffer the render buffer. If no buffer is - passed, a default buffer, using the current view's `tagName`, will - be used. - @private - */ - renderToBuffer: function(parentBuffer, bufferOperation) { - var name = 'render.' + this.instrumentName, - details = {}; - - this.instrumentDetails(details); - - return Ember.instrument(name, details, function instrumentRenderToBuffer() { - return this._renderToBuffer(parentBuffer, bufferOperation); - }, this); - }, - - _renderToBuffer: function(parentBuffer, bufferOperation) { - // If this is the top-most view, start a new buffer. Otherwise, - // create a new buffer relative to the original using the - // provided buffer operation (for example, `insertAfter` will - // insert a new buffer after the "parent buffer"). - var tagName = this.tagName; + If invoked when not within a run loop: - if (tagName === null || tagName === undefined) { - tagName = 'div'; - } + ```javascript + run.join(function() { + // creates a new run-loop + }); + ``` - var buffer = this.buffer = parentBuffer && parentBuffer.begin(tagName) || Ember.RenderBuffer(tagName); - this.transitionTo('inBuffer', false); + Alternatively, if called within an existing run loop: - this.beforeRender(buffer); - this.render(buffer); - this.afterRender(buffer); + ```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. + }); + }); + ``` - return buffer; - }, + @method join + @namespace Ember + @param {Object} [target] target of method to call + @param {Function|String} method Method to invoke. + May be a function or a string. If you pass a string + then it will be looked up on the passed target. + @param {Object} [args*] Any additional arguments you wish to pass to the method. + @return {Object} Return value from invoking the passed function. Please note, + when called within an existing loop, no return value is possible. + */ + run.join = function(target, method /* args */) { + if (!run.currentRunLoop) { + return apply(Ember, run, arguments); + } - /** - Override the default event firing from `Ember.Evented` to - also call methods with the given name. + var args = slice.call(arguments); + args.unshift('actions'); + apply(run, run.schedule, args); + }; - @method trigger - @param name {String} - @private - */ - trigger: function(name) { - this._super.apply(this, arguments); - var method = this[name]; - if (method) { - var args = [], i, l; - for (i = 1, l = arguments.length; i < l; i++) { - args.push(arguments[i]); - } - return method.apply(this, args); - } - }, - - deprecatedSendHandles: function(actionName) { - return !!this[actionName]; - }, - - deprecatedSend: function(actionName) { - var args = [].slice.call(arguments, 1); - Ember.assert('' + this + " has the action " + actionName + " but it is not a function", typeof this[actionName] === 'function'); - Ember.deprecate('Action handlers implemented directly on views are deprecated in favor of action handlers on an `actions` object ( action: `' + actionName + '` on ' + this + ')', false); - this[actionName].apply(this, args); - return; - }, - - has: function(name) { - return Ember.typeOf(this[name]) === 'function' || this._super(name); - }, - - destroy: function() { - var parent = this._parentView; - - if (!this._super()) { return; } - - // destroy the element -- this will avoid each child view destroying - // the element over and over again... - if (!this.removedFromDOM) { this.destroyElement(); } - - // remove from parent if found. Don't call removeFromParent, - // as removeFromParent will try to remove the element from - // the DOM again. - if (parent) { parent.removeChild(this); } - - this.transitionTo('destroying', false); - - return this; - }, - - clearRenderedChildren: Ember.K, - triggerRecursively: Ember.K, - invokeRecursively: Ember.K, - transitionTo: Ember.K, - destroyElement: Ember.K -}); - -var ViewCollection = Ember._ViewCollection = function(initialViews) { - var views = this.views = initialViews || []; - this.length = views.length; -}; - -ViewCollection.prototype = { - length: 0, + /** + Provides a useful utility for when integrating with non-Ember libraries + that provide asynchronous callbacks. - trigger: function(eventName) { - var views = this.views, view; - for (var i = 0, l = views.length; i < l; i++) { - view = views[i]; - if (view.trigger) { view.trigger(eventName); } - } - }, + Ember utilizes a run-loop to batch and coalesce changes. This works by + marking the start and end of Ember-related Javascript execution. - triggerRecursively: function(eventName) { - var views = this.views; - for (var i = 0, l = views.length; i < l; i++) { - views[i].triggerRecursively(eventName); - } - }, + When using events such as a View's click handler, Ember wraps the event + handler in a run-loop, but when integrating with non-Ember libraries this + can be tedious. - invokeRecursively: function(fn) { - var views = this.views, view; + For example, the following is rather verbose but is the correct way to combine + third-party events and Ember code. - for (var i = 0, l = views.length; i < l; i++) { - view = views[i]; - fn(view); - } - }, - - transitionTo: function(state, children) { - var views = this.views; - for (var i = 0, l = views.length; i < l; i++) { - views[i].transitionTo(state, children); - } - }, - - push: function() { - this.length += arguments.length; - var views = this.views; - return views.push.apply(views, arguments); - }, - - objectAt: function(idx) { - return this.views[idx]; - }, + ```javascript + var that = this; + jQuery(window).on('resize', function(){ + run(function(){ + that.handleResize(); + }); + }); + ``` - forEach: function(callback) { - var views = this.views; - return a_forEach(views, callback); - }, + To reduce the boilerplate, the following can be used to construct a + run-loop-wrapped callback handler. - clear: function() { - this.length = 0; - this.views.length = 0; - } -}; + ```javascript + jQuery(window).on('resize', run.bind(this, this.handleResize)); + ``` -var EMPTY_ARRAY = []; + @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 + */ + run.bind = function(target, method /* args*/) { + var args = slice.call(arguments); + return function() { + return apply(run, run.join, args.concat(slice.call(arguments))); + }; + }; -/** - `Ember.View` is the class in Ember responsible for encapsulating templates of - HTML content, combining templates with data to render as sections of a page's - DOM, and registering and responding to user-initiated events. + run.backburner = backburner; + run.currentRunLoop = null; + run.queues = backburner.queueNames; - ## HTML Tag + /** + 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()`. - The default HTML tag name used for a view's DOM representation is `div`. This - can be customized by setting the `tagName` property. The following view - class: + ```javascript + run.begin(); + // code to be execute within a RunLoop + run.end(); + ``` - ```javascript - ParagraphView = Ember.View.extend({ - tagName: 'em' - }); - ``` + @method begin + @return {void} + */ + run.begin = function() { + backburner.begin(); + }; - Would result in instances with the following HTML: + /** + 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()`. - ```html - - ``` + ```javascript + run.begin(); + // code to be execute within a RunLoop + run.end(); + ``` - ## HTML `class` Attribute + @method end + @return {void} + */ + run.end = function() { + backburner.end(); + }; - The HTML `class` attribute of a view's tag can be set by providing a - `classNames` property that is set to an array of strings: + /** + Array of named queues. This array determines the order in which queues + are flushed at the end of the RunLoop. You can define your own queues by + simply adding the queue name to this array. Normally you should not need + to inspect or modify this property. - ```javascript - MyView = Ember.View.extend({ - classNames: ['my-class', 'my-other-class'] - }); - ``` - - Will result in view instances with an HTML representation of: - - ```html -
    - ``` - - `class` attribute values can also be set by providing a `classNameBindings` - property set to an array of properties names for the view. The return value - of these properties will be added as part of the value for the view's `class` - attribute. These properties can be computed properties: - - ```javascript - MyView = Ember.View.extend({ - classNameBindings: ['propertyA', 'propertyB'], - propertyA: 'from-a', - propertyB: function() { - if (someLogic) { return 'from-b'; } - }.property() - }); - ``` + @property queues + @type Array + @default ['sync', 'actions', 'destroy'] + */ - Will result in view instances with an HTML representation of: + /** + 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. - ```html -
    - ``` + 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. - If the value of a class name binding returns a boolean the property name - itself will be used as the class name if the property is true. The class name - will not be added if the value is `false` or `undefined`. + ```javascript + run.schedule('sync', this, function() { + // this will be executed in the first RunLoop queue, when bindings are synced + console.log("scheduled on sync queue"); + }); - ```javascript - MyView = Ember.View.extend({ - classNameBindings: ['hovered'], - hovered: true - }); - ``` + run.schedule('actions', this, function() { + // this will be executed in the 'actions' queue, after bindings have synced. + console.log("scheduled on actions queue"); + }); - Will result in view instances with an HTML representation of: + // 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 + ``` - ```html -
    - ``` + @method schedule + @param {String} queue The name of the queue to schedule against. + Default queues are 'sync' and 'actions' + @param {Object} [target] target object to use as the context when invoking a method. + @param {String|Function} method The method to invoke. If you pass a string it + will be resolved on the target object at the time the scheduled item is + invoked allowing you to change the target function. + @param {Object} [arguments*] Optional arguments to be passed to the queued method. + @return {void} + */ + run.schedule = function(queue, target, method) { + checkAutoRun(); + apply(backburner, backburner.schedule, arguments); + }; - When using boolean class name bindings you can supply a string value other - than the property name for use as the `class` HTML attribute by appending the - preferred value after a ":" character when defining the binding: + // Used by global test teardown + run.hasScheduledTimers = function() { + return backburner.hasTimers(); + }; - ```javascript - MyView = Ember.View.extend({ - classNameBindings: ['awesome:so-very-cool'], - awesome: true - }); - ``` + // Used by global test teardown + run.cancelTimers = function () { + backburner.cancelTimers(); + }; - Will result in view instances with an HTML representation of: + /** + 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. - ```html -
    - ``` + 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). - Boolean value class name bindings whose property names are in a - camelCase-style format will be converted to a dasherized format: + ```javascript + run.sync(); + ``` - ```javascript - MyView = Ember.View.extend({ - classNameBindings: ['isUrgent'], - isUrgent: true - }); - ``` + @method sync + @return {void} + */ + run.sync = function() { + if (backburner.currentInstance) { + backburner.currentInstance.queues.sync.flush(); + } + }; - Will result in view instances with an HTML representation of: + /** + Invokes the passed target/method and optional arguments after a specified + period if time. The last parameter of this method must always be a number + of milliseconds. - ```html -
    - ``` + 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. - Class name bindings can also refer to object values that are found by - traversing a path relative to the view itself: + ```javascript + run.later(myContext, function() { + // code here will execute within a RunLoop in about 500ms with this == myContext + }, 500); + ``` - ```javascript - MyView = Ember.View.extend({ - classNameBindings: ['messages.empty'] - messages: Ember.Object.create({ - empty: true - }) - }); - ``` + @method later + @param {Object} [target] target of method to invoke + @param {Function|String} method The method to invoke. + If you pass a string it will be resolved on the + target at the time the method is invoked. + @param {Object} [args*] Optional arguments to pass to the timeout. + @param {Number} wait Number of milliseconds to wait. + @return {String} a string you can use to cancel the timer in + `run.cancel` later. + */ + run.later = function(target, method) { + return apply(backburner, backburner.later, arguments); + }; - Will result in view instances with an HTML representation of: + /** + Schedule a function to run one time during the current RunLoop. This is equivalent + to calling `scheduleOnce` with the "actions" queue. + + @method once + @param {Object} [target] The target of the method to invoke. + @param {Function|String} method The method to invoke. + If you pass a string it will be resolved on the + target at the time the method is invoked. + @param {Object} [args*] Optional arguments to pass to the timeout. + @return {Object} Timer information for use in cancelling, see `run.cancel`. + */ + run.once = function(target, method) { + checkAutoRun(); + var args = slice.call(arguments); + args.unshift('actions'); + return apply(backburner, backburner.scheduleOnce, args); + }; - ```html -
    - ``` + /** + 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). - If you want to add a class name for a property which evaluates to true and - and a different class name if it evaluates to false, you can pass a binding - like this: + Note that although you can pass optional arguments these will not be + considered when looking for duplicates. New arguments will replace previous + calls. - ```javascript - // Applies 'enabled' class when isEnabled is true and 'disabled' when isEnabled is false - Ember.View.extend({ - classNameBindings: ['isEnabled:enabled:disabled'] - isEnabled: true - }); - ``` + ```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 + }); + ``` - Will result in view instances with an HTML representation of: + 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.: - ```html -
    - ``` + ```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. + ``` - When isEnabled is `false`, the resulting HTML reprensentation looks like - this: + Available queues, and their order, can be found at `run.queues` - ```html -
    - ``` + @method scheduleOnce + @param {String} [queue] The name of the queue to schedule against. Default queues are 'sync' and 'actions'. + @param {Object} [target] The target of the method to invoke. + @param {Function|String} method The method to invoke. + If you pass a string it will be resolved on the + target at the time the method is invoked. + @param {Object} [args*] Optional arguments to pass to the timeout. + @return {Object} Timer information for use in cancelling, see `run.cancel`. + */ + run.scheduleOnce = function(queue, target, method) { + checkAutoRun(); + return apply(backburner, backburner.scheduleOnce, arguments); + }; - This syntax offers the convenience to add a class if a property is `false`: + /** + Schedules an item to run from within a separate run loop, after + control has been returned to the system. This is equivalent to calling + `run.later` with a wait time of 1ms. - ```javascript - // Applies no class when isEnabled is true and class 'disabled' when isEnabled is false - Ember.View.extend({ - classNameBindings: ['isEnabled::disabled'] - isEnabled: true - }); - ``` + ```javascript + run.next(myContext, function() { + // code to be executed in the next run loop, + // which will be scheduled after the current one + }); + ``` - Will result in view instances with an HTML representation of: + 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. - ```html -
    - ``` + 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: - When the `isEnabled` property on the view is set to `false`, it will result - in view instances with an HTML representation of: + ```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. + } + }); + ``` - ```html -
    - ``` + One benefit of the above approach compared to using `run.next` is + that you will be able to perform DOM/CSS operations before unprocessed + elements are rendered to the screen, which may prevent flickering or + other artifacts caused by delaying processing until after rendering. + + The other major benefit to the above approach is that `run.next` + introduces an element of non-determinism, which can make things much + harder to test, due to its reliance on `setTimeout`; it's much harder + to guarantee the order of scheduled operations when they are scheduled + outside of the current run loop, i.e. with `run.next`. + + @method next + @param {Object} [target] target of method to invoke + @param {Function|String} method The method to invoke. + If you pass a string it will be resolved on the + target at the time the method is invoked. + @param {Object} [args*] Optional arguments to pass to the timeout. + @return {Object} Timer information for use in cancelling, see `run.cancel`. + */ + run.next = function() { + var args = slice.call(arguments); + args.push(1); + return apply(backburner, backburner.later, args); + }; - Updates to the the value of a class name binding will result in automatic - update of the HTML `class` attribute in the view's rendered HTML - representation. If the value becomes `false` or `undefined` the class name - will be removed. + /** + Cancels a scheduled item. Must be a value returned by `run.later()`, + `run.once()`, `run.next()`, `run.debounce()`, or + `run.throttle()`. - Both `classNames` and `classNameBindings` are concatenated properties. See - [Ember.Object](/api/classes/Ember.Object.html) documentation for more - information about concatenated properties. + ```javascript + var runNext = run.next(myContext, function() { + // will not be executed + }); + run.cancel(runNext); - ## HTML Attributes + var runLater = run.later(myContext, function() { + // will not be executed + }, 500); + run.cancel(runLater); - The HTML attribute section of a view's tag can be set by providing an - `attributeBindings` property set to an array of property names on the view. - The return value of these properties will be used as the value of the view's - HTML associated attribute: + var runOnce = run.once(myContext, function() { + // will not be executed + }); + run.cancel(runOnce); + + var throttle = run.throttle(myContext, function() { + // will not be executed + }, 1, false); + run.cancel(throttle); + + var debounce = run.debounce(myContext, function() { + // will not be executed + }, 1); + run.cancel(debounce); + + var debounceImmediate = run.debounce(myContext, function() { + // will be executed since we passed in true (immediate) + }, 100, true); + // the 100ms delay until this method can be called again will be cancelled + run.cancel(debounceImmediate); + ``` - ```javascript - AnchorView = Ember.View.extend({ - tagName: 'a', - attributeBindings: ['href'], - href: 'http://google.com' - }); - ``` + @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); + }; - Will result in view instances with an HTML representation of: + /** + 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. - ```html - - ``` + 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. - If the return value of an `attributeBindings` monitored property is a boolean - the property will follow HTML's pattern of repeating the attribute's name as - its value: + ```javascript + var myFunc = function() { console.log(this.name + ' ran.'); }; + var myContext = {name: 'debounce'}; - ```javascript - MyTextInput = Ember.View.extend({ - tagName: 'input', - attributeBindings: ['disabled'], - disabled: true - }); - ``` + run.debounce(myContext, myFunc, 150); - Will result in view instances with an HTML representation of: + // less than 150ms passes - ```html - - ``` + run.debounce(myContext, myFunc, 150); - `attributeBindings` can refer to computed properties: + // 150ms passes + // myFunc is invoked with context myContext + // console logs 'debounce ran.' one time. + ``` - ```javascript - MyTextInput = Ember.View.extend({ - tagName: 'input', - attributeBindings: ['disabled'], - disabled: function() { - if (someLogic) { - return true; - } else { - return false; - } - }.property() - }); - ``` + 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. - Updates to the the property of an attribute binding will result in automatic - update of the HTML attribute in the view's rendered HTML representation. + ```javascript + var myFunc = function() { console.log(this.name + ' ran.'); }; + var myContext = {name: 'debounce'}; - `attributeBindings` is a concatenated property. See [Ember.Object](/api/classes/Ember.Object.html) - documentation for more information about concatenated properties. + run.debounce(myContext, myFunc, 150, true); - ## Templates + // console logs 'debounce ran.' one time immediately. + // 100ms passes - The HTML contents of a view's rendered representation are determined by its - template. Templates can be any function that accepts an optional context - parameter and returns a string of HTML that will be inserted within the - view's tag. Most typically in Ember this function will be a compiled - `Ember.Handlebars` template. + run.debounce(myContext, myFunc, 150, true); - ```javascript - AView = Ember.View.extend({ - template: Ember.Handlebars.compile('I am the template') - }); - ``` + // 150ms passes and nothing else is logged to the console and + // the debouncee is no longer being watched - Will result in view instances with an HTML representation of: + run.debounce(myContext, myFunc, 150, true); - ```html -
    I am the template
    - ``` + // 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 - Within an Ember application is more common to define a Handlebars templates as - part of a page: + ``` - ```html - - ``` + @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 apply(backburner, backburner.debounce, arguments); + }; - And associate it by name using a view's `templateName` property: + /** + Ensure that the target method is never called more frequently than + the specified spacing period. The target method is called immediately. - ```javascript - AView = Ember.View.extend({ - templateName: 'some-template' - }); - ``` + ```javascript + var myFunc = function() { console.log(this.name + ' ran.'); }; + var myContext = {name: 'throttle'}; - If you have nested resources, your Handlebars template will look like this: + run.throttle(myContext, myFunc, 150); + // myFunc is invoked with context myContext + // console logs 'throttle ran.' - ```html - - ``` + // 50ms passes + run.throttle(myContext, myFunc, 150); - And `templateName` property: + // 50ms passes + run.throttle(myContext, myFunc, 150); - ```javascript - AView = Ember.View.extend({ - templateName: 'posts/new' - }); - ``` - - Using a value for `templateName` that does not have a Handlebars template - with a matching `data-template-name` attribute will throw an error. - - For views classes that may have a template later defined (e.g. as the block - portion of a `{{view}}` Handlebars helper call in another template or in - a subclass), you can provide a `defaultTemplate` property set to compiled - template function. If a template is not later provided for the view instance - the `defaultTemplate` value will be used: - - ```javascript - AView = Ember.View.extend({ - defaultTemplate: Ember.Handlebars.compile('I was the default'), - template: null, - templateName: null - }); - ``` + // 150ms passes + run.throttle(myContext, myFunc, 150); + // myFunc is invoked with context myContext + // console logs 'throttle ran.' + ``` - Will result in instances with an HTML representation of: + @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 apply(backburner, backburner.throttle, arguments); + }; - ```html -
    I was the default
    - ``` + // 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 a `template` or `templateName` is provided it will take precedence over - `defaultTemplate`: + /** + Add a new named queue after the specified queue. - ```javascript - AView = Ember.View.extend({ - defaultTemplate: Ember.Handlebars.compile('I was the default') - }); + The queue to add will only be added once. - aView = AView.create({ - template: Ember.Handlebars.compile('I was the template, not default') + @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); + } + }; }); - ``` +define("ember-metal/set_properties", + ["ember-metal/property_events","ember-metal/property_set","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var changeProperties = __dependency1__.changeProperties; + var set = __dependency2__.set; - Will result in the following HTML representation when rendered: + /** + Set a list of properties on an object. These properties are set inside + a single `beginPropertyChanges` and `endPropertyChanges` batch, so + observers will be buffered. - ```html -
    I was the template, not default
    - ``` + ```javascript + var anObject = Ember.Object.create(); - ## View Context + anObject.setProperties({ + firstName: 'Stanley', + lastName: 'Stuart', + age: 21 + }); + ``` - The default context of the compiled template is the view's controller: - - ```javascript - AView = Ember.View.extend({ - template: Ember.Handlebars.compile('Hello {{excitedGreeting}}') + @method setProperties + @param self + @param {Object} hash + @return self + */ + __exports__["default"] = function setProperties(self, hash) { + changeProperties(function() { + for(var prop in hash) { + if (hash.hasOwnProperty(prop)) { set(self, prop, hash[prop]); } + } + }); + return self; + } }); +define("ember-metal/utils", + ["ember-metal/core","ember-metal/platform","ember-metal/array","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + var platform = __dependency2__.platform; + var create = __dependency2__.create; - aController = Ember.Object.create({ - firstName: 'Barry', - excitedGreeting: function() { - return this.get("content.firstName") + "!!!" - }.property() - }); + var forEach = __dependency3__.forEach; - aView = AView.create({ - controller: aController, - }); - ``` + /** + @module ember-metal + */ - Will result in an HTML representation of: + /** + Previously we used `Ember.$.uuid`, however `$.uuid` has been removed from + jQuery master. We'll just bootstrap our own uuid now. - ```html -
    Hello Barry!!!
    - ``` + @private + @return {Number} the uuid + */ + var _uuid = 0; - A context can also be explicitly supplied through the view's `context` - property. If the view has neither `context` nor `controller` properties, the - `parentView`'s context will be used. + /** + 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. - ## Layouts + @public + @return {Number} [description] + */ + function uuid() { + return ++_uuid; + } - Views can have a secondary template that wraps their main template. Like - primary templates, layouts can be any function that accepts an optional - context parameter and returns a string of HTML that will be inserted inside - view's tag. Views whose HTML element is self closing (e.g. ``) - cannot have a layout and this property will be ignored. + __exports__.uuid = uuid;/** + Prefix used for guids through out Ember. + @private + @property GUID_PREFIX + @for Ember + @type String + @final + */ + var GUID_PREFIX = 'ember'; - Most typically in Ember a layout will be a compiled `Ember.Handlebars` - template. + var o_defineProperty = platform.defineProperty; + var o_create = create; + // Used for guid generation... + var numberCache = []; + var stringCache = {}; + var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER; - A view's layout can be set directly with the `layout` property or reference - an existing Handlebars template by name with the `layoutName` property. + /** + 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. - A template used as a layout must contain a single use of the Handlebars - `{{yield}}` helper. The HTML contents of a view's rendered `template` will be - inserted at this location: + 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 - AViewWithLayout = Ember.View.extend({ - layout: Ember.Handlebars.compile("
    {{yield}}
    ") - template: Ember.Handlebars.compile("I got wrapped"), - }); - ``` + @private + @property GUID_KEY + @for Ember + @type String + @final + */ + var GUID_KEY = '__ember' + (+ new Date()); - Will result in view instances with an HTML representation of: + var GUID_DESC = { + writable: false, + configurable: false, + enumerable: false, + value: null + }; - ```html -
    -
    - I got wrapped -
    -
    - ``` + /** + 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. - See [Ember.Handlebars.helpers.yield](/api/classes/Ember.Handlebars.helpers.html#method_yield) - for more information. + @private + @method generateGuid + @for Ember + @param {Object} [obj] Object the guid will be used for. If passed in, the guid will + be saved on the object and reused whenever you pass the same object + again. + + If no object is passed, just generate a new guid. + @param {String} [prefix] Prefix to place in front of the guid. Useful when you want to + separate the guid into separate namespaces. + @return {String} the guid + */ + function generateGuid(obj, prefix) { + if (!prefix) prefix = GUID_PREFIX; + var ret = (prefix + uuid()); + if (obj) { + if (obj[GUID_KEY] === null) { + obj[GUID_KEY] = ret; + } else { + GUID_DESC.value = ret; + o_defineProperty(obj, GUID_KEY, GUID_DESC); + } + } + return ret; + } - ## Responding to Browser Events + __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. - Views can respond to user-initiated events in one of three ways: method - implementation, through an event manager, and through `{{action}}` helper use - in their template or layout. + You can also use this method on DOM Element objects. - ### Method Implementation + @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) { - Views can respond to user-initiated events by implementing a method that - matches the event name. A `jQuery.Event` object will be passed as the - argument to this method. + // special cases where we don't want to add a key to object + if (obj === undefined) return "(undefined)"; + if (obj === null) return "(null)"; - ```javascript - AView = Ember.View.extend({ - click: function(event) { - // will be called when when an instance's - // rendered element is clicked + var ret; + var type = typeof obj; + + // Don't allow prototype changes to String etc. to change the guidFor + switch(type) { + case 'number': + ret = numberCache[obj]; + if (!ret) ret = numberCache[obj] = 'nu'+obj; + return ret; + + case 'string': + ret = stringCache[obj]; + if (!ret) ret = stringCache[obj] = 'st' + uuid(); + return ret; + + case 'boolean': + return obj ? '(true)' : '(false)'; + + default: + if (obj[GUID_KEY]) return obj[GUID_KEY]; + if (obj === Object) return '(Object)'; + if (obj === Array) return '(Array)'; + ret = 'ember' + uuid(); + + if (obj[GUID_KEY] === null) { + obj[GUID_KEY] = ret; + } else { + GUID_DESC.value = ret; + o_defineProperty(obj, GUID_KEY, GUID_DESC); + } + return ret; + } } - }); - ``` - - ### Event Managers - - Views can define an object as their `eventManager` property. This object can - then implement methods that match the desired event names. Matching events - that occur on the view's rendered HTML or the rendered HTML of any of its DOM - descendants will trigger this method. A `jQuery.Event` object will be passed - as the first argument to the method and an `Ember.View` object as the - second. The `Ember.View` will be the view whose rendered HTML was interacted - with. This may be the view with the `eventManager` property or one of its - descendent views. - - ```javascript - AView = Ember.View.extend({ - eventManager: Ember.Object.create({ - doubleClick: function(event, view) { - // will be called when when an instance's - // rendered element or any rendering - // of this views's descendent - // elements is clicked - } - }) - }); - ``` - An event defined for an event manager takes precedence over events of the - same name handled through methods on the view. - - ```javascript - AView = Ember.View.extend({ - mouseEnter: function(event) { - // will never trigger. - }, - eventManager: Ember.Object.create({ - mouseEnter: function(event, view) { - // takes precedence over AView#mouseEnter - } - }) - }); - ``` - - Similarly a view's event manager will take precedence for events of any views - rendered as a descendent. A method name that matches an event name will not - be called if the view instance was rendered inside the HTML representation of - a view that has an `eventManager` property defined that handles events of the - name. Events not handled by the event manager will still trigger method calls - on the descendent. - - ```javascript - OuterView = Ember.View.extend({ - template: Ember.Handlebars.compile("outer {{#view InnerView}}inner{{/view}} outer"), - eventManager: Ember.Object.create({ - mouseEnter: function(event, view) { - // view might be instance of either - // OuterView or InnerView depending on - // where on the page the user interaction occured - } - }) - }); + __exports__.guidFor = guidFor;// .......................................................... + // META + // - InnerView = Ember.View.extend({ - click: function(event) { - // will be called if rendered inside - // an OuterView because OuterView's - // eventManager doesn't handle click events - }, - mouseEnter: function(event) { - // will never be called if rendered inside - // an OuterView. - } - }); - ``` + var META_DESC = { + writable: true, + configurable: false, + enumerable: false, + value: null + }; - ### Handlebars `{{action}}` Helper + /** + The key used to store meta information on object for property observing. - See [Handlebars.helpers.action](/api/classes/Ember.Handlebars.helpers.html#method_action). + @property META_KEY + @for Ember + @private + @final + @type String + */ + var META_KEY = '__ember_meta__'; - ### Event Names + var isDefinePropertySimulated = platform.defineProperty.isSimulated; - All of the event handling approaches described above respond to the same set - of events. The names of the built-in events are listed below. (The hash of - built-in events exists in `Ember.EventDispatcher`.) Additional, custom events - can be registered by using `Ember.Application.customEvents`. + function Meta(obj) { + this.descs = {}; + this.watching = {}; + this.cache = {}; + this.cacheMeta = {}; + this.source = obj; + } - Touch events: + Meta.prototype = { + descs: null, + deps: null, + watching: null, + listeners: null, + cache: null, + cacheMeta: null, + source: null, + mixins: null, + bindings: null, + chains: null, + chainWatchers: null, + values: null, + proto: null + }; - * `touchStart` - * `touchMove` - * `touchEnd` - * `touchCancel` + if (isDefinePropertySimulated) { + // on platforms that don't support enumerable false + // make meta fail jQuery.isPlainObject() to hide from + // jQuery.extend() by having a property that fails + // hasOwnProperty check. + Meta.prototype.__preventPlainObject__ = true; - Keyboard events + // Without non-enumerable properties, meta objects will be output in JSON + // unless explicitly suppressed + Meta.prototype.toJSON = function () { }; + } - * `keyDown` - * `keyUp` - * `keyPress` + // Placeholder for non-writable metas. + var EMPTY_META = new Meta(null); - Mouse events + if (MANDATORY_SETTER) { EMPTY_META.values = {}; } - * `mouseDown` - * `mouseUp` - * `contextMenu` - * `click` - * `doubleClick` - * `mouseMove` - * `focusIn` - * `focusOut` - * `mouseEnter` - * `mouseLeave` + /** + Retrieves the meta hash for an object. If `writable` is true ensures the + hash is writable for this object as well. - Form events: + 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. - * `submit` - * `change` - * `focusIn` - * `focusOut` - * `input` + @method meta + @for Ember + @private - HTML5 drag and drop events: - - * `dragStart` - * `drag` - * `dragEnter` - * `dragLeave` - * `drop` - * `dragEnd` - - ## Handlebars `{{view}}` Helper - - Other `Ember.View` instances can be included as part of a view's template by - using the `{{view}}` Handlebars helper. See [Ember.Handlebars.helpers.view](/api/classes/Ember.Handlebars.helpers.html#method_view) - for additional information. - - @class View - @namespace Ember - @extends Ember.CoreView -*/ -Ember.View = Ember.CoreView.extend({ - - concatenatedProperties: ['classNames', 'classNameBindings', 'attributeBindings'], - - /** - @property isView - @type Boolean - @default true - @static - */ - isView: true, - - // .......................................................... - // TEMPLATE SUPPORT - // - - /** - The name of the template to lookup if no template is provided. - - By default `Ember.View` will lookup a template with this name in - `Ember.TEMPLATES` (a shared global object). - - @property templateName - @type String - @default null - */ - templateName: null, - - /** - The name of the layout to lookup if no layout is provided. - - By default `Ember.View` will lookup a template with this name in - `Ember.TEMPLATES` (a shared global object). - - @property layoutName - @type String - @default null - */ - layoutName: null, - - /** - The template used to render the view. This should be a function that - accepts an optional context parameter and returns a string of HTML that - will be inserted into the DOM relative to its parent view. - - In general, you should set the `templateName` property instead of setting - the template yourself. - - @property template - @type Function - */ - template: Ember.computed('templateName', function(key, value) { - if (value !== undefined) { return value; } - - var templateName = get(this, 'templateName'), - template = this.templateForName(templateName, 'template'); - - Ember.assert("You specified the templateName " + templateName + " for " + this + ", but it did not exist.", !templateName || template); - - return template || get(this, 'defaultTemplate'); - }), - - /** - The controller managing this view. If this property is set, it will be - made available for use by the template. - - @property controller - @type Object - */ - controller: Ember.computed('_parentView', function(key) { - var parentView = get(this, '_parentView'); - return parentView ? get(parentView, 'controller') : null; - }), - - /** - A view may contain a layout. A layout is a regular template but - supersedes the `template` property during rendering. It is the - responsibility of the layout template to retrieve the `template` - property from the view (or alternatively, call `Handlebars.helpers.yield`, - `{{yield}}`) to render it in the correct location. - - This is useful for a view that has a shared wrapper, but which delegates - the rendering of the contents of the wrapper to the `template` property - on a subclass. - - @property layout - @type Function - */ - layout: Ember.computed(function(key) { - var layoutName = get(this, 'layoutName'), - layout = this.templateForName(layoutName, 'layout'); - - Ember.assert("You specified the layoutName " + layoutName + " for " + this + ", but it did not exist.", !layoutName || layout); - - return layout || get(this, 'defaultLayout'); - }).property('layoutName'), - - _yield: function(context, options) { - var template = get(this, 'template'); - if (template) { template(context, options); } - }, - - templateForName: function(name, type) { - if (!name) { return; } - Ember.assert("templateNames are not allowed to contain periods: "+name, name.indexOf('.') === -1); - - // the defaultContainer is deprecated - var container = this.container || (Ember.Container && Ember.Container.defaultContainer); - return container && container.lookup('template:' + name); - }, - - /** - The object from which templates should access properties. - - This object will be passed to the template function each time the render - method is called, but it is up to the individual function to decide what - to do with it. - - By default, this will be the view's controller. - - @property context - @type Object - */ - context: Ember.computed(function(key, value) { - if (arguments.length === 2) { - set(this, '_context', value); - return value; - } else { - return get(this, '_context'); - } - }).volatile(), - - /** - Private copy of the view's template context. This can be set directly - by Handlebars without triggering the observer that causes the view - to be re-rendered. + @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) { - The context of a view is looked up as follows: + var ret = obj[META_KEY]; + if (writable===false) return ret || EMPTY_META; - 1. Supplied context (usually by Handlebars) - 2. Specified controller - 3. `parentView`'s context (for a child of a ContainerView) + if (!ret) { + if (!isDefinePropertySimulated) o_defineProperty(obj, META_KEY, META_DESC); - The code in Handlebars that overrides the `_context` property first - checks to see whether the view has a specified controller. This is - something of a hack and should be revisited. - - @property _context - @private - */ - _context: Ember.computed(function(key) { - var parentView, controller; - - if (controller = get(this, 'controller')) { - return controller; - } - - parentView = this._parentView; - if (parentView) { - return get(parentView, '_context'); - } - - return null; - }), - - /** - If a value that affects template rendering changes, the view should be - re-rendered to reflect the new value. - - @method _contextDidChange - @private - */ - _contextDidChange: Ember.observer('context', function() { - this.rerender(); - }), - - /** - If `false`, the view will appear hidden in DOM. - - @property isVisible - @type Boolean - @default null - */ - isVisible: true, - - /** - Array of child views. You should never edit this array directly. - Instead, use `appendChild` and `removeFromParent`. - - @property childViews - @type Array - @default [] - @private - */ - childViews: childViewsProperty, - - _childViews: EMPTY_ARRAY, - - // When it's a virtual view, we need to notify the parent that their - // childViews will change. - _childViewsWillChange: Ember.beforeObserver('childViews', function() { - if (this.isVirtual) { - var parentView = get(this, 'parentView'); - if (parentView) { Ember.propertyWillChange(parentView, 'childViews'); } - } - }), + ret = new Meta(obj); - // When it's a virtual view, we need to notify the parent that their - // childViews did change. - _childViewsDidChange: Ember.observer('childViews', function() { - if (this.isVirtual) { - var parentView = get(this, 'parentView'); - if (parentView) { Ember.propertyDidChange(parentView, 'childViews'); } - } - }), + if (MANDATORY_SETTER) { ret.values = {}; } - /** - Return the nearest ancestor that is an instance of the provided - class. + obj[META_KEY] = ret; - @method nearestInstanceOf - @param {Class} klass Subclass of Ember.View (or Ember.View itself) - @return Ember.View - @deprecated - */ - nearestInstanceOf: function(klass) { - Ember.deprecate("nearestInstanceOf is deprecated and will be removed from future releases. Use nearestOfType."); - var view = get(this, 'parentView'); + // make sure we don't accidentally try to create constructor like desc + ret.descs.constructor = null; - while (view) { - if (view instanceof klass) { return view; } - view = get(view, 'parentView'); - } - }, + } else if (ret.source !== obj) { + if (!isDefinePropertySimulated) o_defineProperty(obj, META_KEY, META_DESC); - /** - Return the nearest ancestor that is an instance of the provided - class or mixin. + ret = o_create(ret); + ret.descs = o_create(ret.descs); + ret.watching = o_create(ret.watching); + ret.cache = {}; + ret.cacheMeta = {}; + ret.source = obj; - @method nearestOfType - @param {Class,Mixin} klass Subclass of Ember.View (or Ember.View itself), - or an instance of Ember.Mixin. - @return Ember.View - */ - nearestOfType: function(klass) { - var view = get(this, 'parentView'), - isOfType = klass instanceof Ember.Mixin ? - function(view) { return klass.detect(view); } : - function(view) { return klass.detect(view.constructor); }; + if (MANDATORY_SETTER) { ret.values = o_create(ret.values); } - while (view) { - if (isOfType(view)) { return view; } - view = get(view, 'parentView'); + obj[META_KEY] = ret; + } + return ret; } - }, - /** - Return the nearest ancestor that has a given property. - - @function nearestWithProperty - @param {String} property A property name - @return Ember.View - */ - nearestWithProperty: function(property) { - var view = get(this, 'parentView'); - - while (view) { - if (property in view) { return view; } - view = get(view, 'parentView'); - } - }, - - /** - Return the nearest ancestor whose parent is an instance of - `klass`. - - @method nearestChildOf - @param {Class} klass Subclass of Ember.View (or Ember.View itself) - @return Ember.View - */ - nearestChildOf: function(klass) { - var view = get(this, 'parentView'); - - while (view) { - if (get(view, 'parentView') instanceof klass) { return view; } - view = get(view, 'parentView'); - } - }, - - /** - When the parent view changes, recursively invalidate `controller` - - @method _parentViewDidChange - @private - */ - _parentViewDidChange: Ember.observer('_parentView', function() { - if (this.isDestroying) { return; } + function getMeta(obj, property) { + var _meta = meta(obj, false); + return _meta[property]; + } - this.trigger('parentViewDidChange'); - - if (get(this, 'parentView.controller') && !get(this, 'controller')) { - this.notifyPropertyChange('controller'); + __exports__.getMeta = getMeta;function setMeta(obj, property, value) { + var _meta = meta(obj, true); + _meta[property] = value; + return value; } - }), - _controllerDidChange: Ember.observer('controller', function() { - if (this.isDestroying) { return; } - - this.rerender(); + __exports__.setMeta = setMeta;/** + @deprecated + @private - this.forEachChildView(function(view) { - view.propertyDidChange('controller'); - }); - }), - - cloneKeywords: function() { - var templateData = get(this, 'templateData'); - - var keywords = templateData ? Ember.copy(templateData.keywords) : {}; - set(keywords, 'view', get(this, 'concreteView')); - set(keywords, '_view', this); - set(keywords, 'controller', get(this, 'controller')); - - return keywords; - }, - - /** - Called on your view when it should push strings of HTML into a - `Ember.RenderBuffer`. Most users will want to override the `template` - or `templateName` properties instead of this method. - - By default, `Ember.View` will look for a function in the `template` - property and invoke it with the value of `context`. The value of - `context` will be the view's controller unless you override it. - - @method render - @param {Ember.RenderBuffer} buffer The render buffer - */ - render: function(buffer) { - // If this view has a layout, it is the responsibility of the - // the layout to render the view's template. Otherwise, render the template - // directly. - var template = get(this, 'layout') || get(this, 'template'); - - if (template) { - var context = get(this, 'context'); - var keywords = this.cloneKeywords(); - var output; - - var data = { - view: this, - buffer: buffer, - isRenderData: true, - keywords: keywords, - insideGroup: get(this, 'templateData.insideGroup') - }; + In order to store defaults for a class, a prototype may need to create + a default meta object, which will be inherited by any objects instantiated + from the class's constructor. + + However, the properties of that meta object are only shallow-cloned, + so if a property is a hash (like the event system's `listeners` hash), + it will by default be shared across all instances of that class. + + This method allows extensions to deeply clone a series of nested hashes or + other complex objects. For instance, the event system might pass + `['listeners', 'foo:change', 'ember157']` to `prepareMetaPath`, which will + walk down the keys provided. + + For each key, if the key does not exist, it is created. If it already + exists and it was inherited from its constructor, the constructor's + key is cloned. + + You can also pass false for `writable`, which will simply return + undefined if `prepareMetaPath` discovers any part of the path that + shared or undefined. + + @method metaPath + @for Ember + @param {Object} obj The object whose meta we are examining + @param {Array} path An array of keys to walk down + @param {Boolean} writable whether or not to create a new meta + (or meta property) if one does not already exist or if it's + shared with its constructor + */ + function metaPath(obj, path, writable) { + Ember.deprecate("Ember.metaPath is deprecated and will be removed from future releases."); + var _meta = meta(obj, writable), keyName, value; - // Invoke the template with the provided template context, which - // is the view's controller by default. A hash of data is also passed that provides - // the template with access to the view and render buffer. - - Ember.assert('template must be a function. Did you mean to call Ember.Handlebars.compile("...") or specify templateName instead?', typeof template === 'function'); - // The template should write directly to the render buffer instead - // of returning a string. - output = template(context, { data: data }); - - // If the template returned a string instead of writing to the buffer, - // push the string onto the buffer. - if (output !== undefined) { buffer.push(output); } - } - }, - - /** - Renders the view again. This will work regardless of whether the - view is already in the DOM or not. If the view is in the DOM, the - rendering process will be deferred to give bindings a chance - to synchronize. - - If children were added during the rendering process using `appendChild`, - `rerender` will remove them, because they will be added again - if needed by the next `render`. - - In general, if the display of your view changes, you should modify - the DOM element directly instead of manually calling `rerender`, which can - be slow. - - @method rerender - */ - rerender: function() { - return this.currentState.rerender(this); - }, - - clearRenderedChildren: function() { - var lengthBefore = this.lengthBeforeRender, - lengthAfter = this.lengthAfterRender; - - // If there were child views created during the last call to render(), - // remove them under the assumption that they will be re-created when - // we re-render. - - // VIEW-TODO: Unit test this path. - var childViews = this._childViews; - for (var i=lengthAfter-1; i>=lengthBefore; i--) { - if (childViews[i]) { childViews[i].destroy(); } - } - }, - - /** - Iterates over the view's `classNameBindings` array, inserts the value - of the specified property into the `classNames` array, then creates an - observer to update the view's element if the bound property ever changes - in the future. - - @method _applyClassNameBindings - @private - */ - _applyClassNameBindings: function(classBindings) { - var classNames = this.classNames, - elem, newClass, dasherizedClass; - - // Loop through all of the configured bindings. These will be either - // property names ('isUrgent') or property paths relative to the view - // ('content.isUrgent') - a_forEach(classBindings, function(binding) { - - Ember.assert("classNameBindings must not have spaces in them. Multiple class name bindings can be provided as elements of an array, e.g. ['foo', ':bar']", binding.indexOf(' ') === -1); - - // Variable in which the old class value is saved. The observer function - // closes over this variable, so it knows which string to remove when - // the property changes. - var oldClass; - // Extract just the property name from bindings like 'foo:bar' - var parsedPath = Ember.View._parsePropertyPath(binding); - - // Set up an observer on the context. If the property changes, toggle the - // class name. - var observer = function() { - // Get the current value of the property - newClass = this._classStringForProperty(binding); - elem = this.$(); - - // If we had previously added a class to the element, remove it. - if (oldClass) { - elem.removeClass(oldClass); - // Also remove from classNames so that if the view gets rerendered, - // the class doesn't get added back to the DOM. - classNames.removeObject(oldClass); - } - - // If necessary, add a new class. Make sure we keep track of it so - // it can be removed in the future. - if (newClass) { - elem.addClass(newClass); - oldClass = newClass; - } else { - oldClass = null; + for (var i=0, l=path.length; i= 0) { - view = childViews[idx]; - callback(this, view, idx); - } + try { + result = tryable.call(binding); + } finally { + try { + finalResult = finalizer.call(binding); + } catch (e) { + finalError = e; + } + } - return this; - }, + if (finalError) { throw finalError; } - forEachChildView: function(callback) { - var childViews = this._childViews; + return (finalResult === undefined) ? result : finalResult; + }; + } else { + tryFinally = function(tryable, finalizer, binding) { + var result, finalResult; - if (!childViews) { return this; } + binding = binding || this; - var len = childViews.length, - view, idx; + try { + result = tryable.call(binding); + } finally { + finalResult = finalizer.call(binding); + } - for (idx = 0; idx < len; idx++) { - view = childViews[idx]; - callback(view); + return (finalResult === undefined) ? result : finalResult; + }; } - return this; - }, + /** + Provides try/catch/finally functionality, while working + around Safari's double finally bug. + + ```javascript + var tryable = function() { + for (i = 0, l = listeners.length; i < l; i++) { + listener = listeners[i]; + beforeValues[i] = listener.before(name, time(), payload); + } + + return callback.call(binding); + }; - /** - Appends the view's element to the specified parent element. + var catchable = function(e) { + payload = payload || {}; + payload.exception = e; + }; - If the view does not have an HTML representation yet, `createElement()` - will be called automatically. + var finalizer = function() { + for (i = 0, l = listeners.length; i < l; i++) { + listener = listeners[i]; + listener.after(name, time(), payload, beforeValues[i]); + } + }; - Note that this method just schedules the view to be appended; the DOM - element will not be appended to the given element until all bindings have - finished synchronizing. + Ember.tryCatchFinally(tryable, catchable, finalizer); + ``` - This is not typically a function that you will need to call directly when - building your application. You might consider using `Ember.ContainerView` - instead. If you do need to use `appendTo`, be sure that the target element - you are providing is associated with an `Ember.Application` and does not - have an ancestor element that is associated with an Ember view. + @method 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; - @method appendTo - @param {String|DOMElement|jQuery} A selector, element, HTML string, or jQuery object - @return {Ember.View} receiver - */ - appendTo: function(target) { - // Schedule the DOM element to be created and appended to the given - // element after bindings have synchronized. - this._insertElementLater(function() { - Ember.assert("You tried to append to (" + target + ") but that isn't in the DOM", Ember.$(target).length > 0); - Ember.assert("You cannot append to an existing Ember.View. Consider using Ember.ContainerView instead.", !Ember.$(target).is('.ember-view') && !Ember.$(target).parents().is('.ember-view')); - this.$().appendTo(target); - }); + binding = binding || this; - return this; - }, - - /** - Replaces the content of the specified parent element with this view's - element. If the view does not have an HTML representation yet, - `createElement()` will be called automatically. - - Note that this method just schedules the view to be appended; the DOM - element will not be appended to the given element until all bindings have - finished synchronizing - - @method replaceIn - @param {String|DOMElement|jQuery} target A selector, element, HTML string, or jQuery object - @return {Ember.View} received - */ - replaceIn: function(target) { - Ember.assert("You tried to replace in (" + target + ") but that isn't in the DOM", Ember.$(target).length > 0); - Ember.assert("You cannot replace an existing Ember.View. Consider using Ember.ContainerView instead.", !Ember.$(target).is('.ember-view') && !Ember.$(target).parents().is('.ember-view')); - - this._insertElementLater(function() { - Ember.$(target).empty(); - this.$().appendTo(target); - }); + try { + result = tryable.call(binding); + } catch(error) { + result = catchable.call(binding, error); + } finally { + try { + finalResult = finalizer.call(binding); + } catch (e) { + finalError = e; + } + } - return this; - }, + if (finalError) { throw finalError; } - /** - Schedules a DOM operation to occur during the next render phase. This - ensures that all bindings have finished synchronizing before the view is - rendered. + return (finalResult === undefined) ? result : finalResult; + }; + } else { + tryCatchFinally = function(tryable, catchable, finalizer, binding) { + var result, finalResult; - To use, pass a function that performs a DOM operation. + binding = binding || this; - Before your function is called, this view and all child views will receive - the `willInsertElement` event. After your function is invoked, this view - and all of its child views will receive the `didInsertElement` event. + try { + result = tryable.call(binding); + } catch(error) { + result = catchable.call(binding, error); + } finally { + finalResult = finalizer.call(binding); + } - ```javascript - view._insertElementLater(function() { - this.createElement(); - this.$().appendTo('body'); - }); - ``` + return (finalResult === undefined) ? result : finalResult; + }; + } + + // ........................................ + // TYPING & ARRAY MESSAGING + // - @method _insertElementLater - @param {Function} fn the function that inserts the element into the DOM - @private - */ - _insertElementLater: function(fn) { - this._scheduledInsert = Ember.run.scheduleOnce('render', this, '_insertElement', fn); - }, - - _insertElement: function (fn) { - this._scheduledInsert = null; - this.currentState.insertElement(this, fn); - }, - - /** - Appends the view's element to the document body. If the view does - not have an HTML representation yet, `createElement()` will be called - automatically. - - If your application uses the `rootElement` property, you must append - the view within that element. Rendering views outside of the `rootElement` - is not supported. - - Note that this method just schedules the view to be appended; the DOM - element will not be appended to the document body until all bindings have - finished synchronizing. - - @method append - @return {Ember.View} receiver - */ - append: function() { - return this.appendTo(document.body); - }, - - /** - Removes the view's element from the element to which it is attached. - - @method remove - @return {Ember.View} receiver - */ - remove: function() { - // What we should really do here is wait until the end of the run loop - // to determine if the element has been re-appended to a different - // element. - // In the interim, we will just re-render if that happens. It is more - // important than elements get garbage collected. - if (!this.removedFromDOM) { this.destroyElement(); } - this.invokeRecursively(function(view) { - if (view.clearRenderedChildren) { view.clearRenderedChildren(); } + 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(); }); - }, - - elementId: null, - - /** - Attempts to discover the element in the parent element. The default - implementation looks for an element with an ID of `elementId` (or the - view's guid if `elementId` is null). You can override this method to - provide your own form of lookup. For example, if you want to discover your - element using a CSS class name instead of an ID. - @method findElementInParentElement - @param {DOMElement} parentElement The parent's DOM element - @return {DOMElement} The discovered element - */ - findElementInParentElement: function(parentElem) { - var id = "#" + this.elementId; - return Ember.$(id)[0] || Ember.$(id, parentElem)[0]; - }, + var toString = Object.prototype.toString; + + var EmberObject; + + /** + 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: + + | 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 | + + Examples: + + ```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' + + // 'normal' JavaScript object + Ember.typeOf({ a: 'b' }); // 'object' + ``` - /** - Creates a DOM representation of the view and all of its - child views by recursively calling the `render()` method. + @method typeOf + @for Ember + @param {Object} item the item to check + @return {String} the type + */ + function typeOf(item) { + var ret, modulePath; - After the element has been created, `didInsertElement` will - be called on this view and all of its child views. + // 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']; + } + } - @method createElement - @return {Ember.View} receiver - */ - createElement: function() { - if (get(this, 'element')) { return this; } + ret = (item === null || item === undefined) ? String(item) : TYPE_MAP[toString.call(item)] || 'object'; - var buffer = this.renderToBuffer(); - set(this, 'element', buffer.element()); + 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 this; - }, + return ret; + } - /** - Called when a view is going to insert an element into the DOM. + /** + Convenience method to inspect an object. This method will attempt to + convert the object into a useful string description. - @event willInsertElement - */ - willInsertElement: Ember.K, + It is a pretty simple implementation. If you want something more robust, + use something like JSDump: https://github.com/NV/jsDump - /** - Called when the element of the view has been inserted into the DOM - or after the view was re-rendered. Override this function to do any - set up that requires an element in the document body. + @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 + ''; + } + + var v, ret = []; + for(var key in obj) { + if (obj.hasOwnProperty(key)) { + v = obj[key]; + if (v === 'toString') { continue; } // ignore useless items + if (typeOf(v) === 'function') { v = "function() { ... }"; } + ret.push(key + ": " + v); + } + } + return "{" + ret.join(", ") + "}"; + } + + __exports__.inspect = inspect;// The following functions are intentionally minified to keep the functions + // below Chrome's function body size inlining limit of 600 chars. + + 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); + } + } + + __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); + } + } + + __exports__.applyStr = applyStr;__exports__.GUID_KEY = GUID_KEY; + __exports__.GUID_PREFIX = GUID_PREFIX; + __exports__.META_DESC = META_DESC; + __exports__.EMPTY_META = EMPTY_META; + __exports__.META_KEY = META_KEY; + __exports__.meta = meta; + __exports__.typeOf = typeOf; + __exports__.tryCatchFinally = tryCatchFinally; + __exports__.isArray = isArray; + __exports__.canInvoke = canInvoke; + __exports__.tryFinally = tryFinally; + }); +define("ember-metal/watch_key", + ["ember-metal/core","ember-metal/utils","ember-metal/platform","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + var meta = __dependency2__.meta; + var typeOf = __dependency2__.typeOf; + var platform = __dependency3__.platform; - @event didInsertElement - */ - didInsertElement: Ember.K, + var metaFor = meta; // utils.js + var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER; + var o_defineProperty = platform.defineProperty; - /** - Called when the view is about to rerender, but before anything has - been torn down. This is a good opportunity to tear down any manual - observers you have installed based on the DOM state + function watchKey(obj, keyName, meta) { + // can't watch length on Array - it is special... + if (keyName === 'length' && typeOf(obj) === 'array') { return; } - @event willClearRender - */ - willClearRender: Ember.K, + var m = meta || metaFor(obj), watching = m.watching; - /** - Run this callback on the current view (unless includeSelf is false) and recursively on child views. + // activate watching first time + if (!watching[keyName]) { + watching[keyName] = 1; - @method invokeRecursively - @param fn {Function} - @param includeSelf {Boolean} Includes itself if true. - @private - */ - invokeRecursively: function(fn, includeSelf) { - var childViews = (includeSelf === false) ? this._childViews : [this]; - var currentViews, view, currentChildViews; + var desc = m.descs[keyName]; + if (desc && desc.willWatch) { desc.willWatch(obj, keyName); } - while (childViews.length) { - currentViews = childViews.slice(); - childViews = []; + if ('function' === typeof obj.willWatchProperty) { + obj.willWatchProperty(keyName); + } - for (var i=0, l=currentViews.length; i 1) { + watching[keyName]--; } } - return viewCollection; - }, - - /** - Destroys any existing element along with the element for any child views - as well. If the view does not currently have a element, then this method - will do nothing. + __exports__.unwatchKey = unwatchKey; + }); +define("ember-metal/watch_path", + ["ember-metal/utils","ember-metal/chains","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + var meta = __dependency1__.meta; + var typeOf = __dependency1__.typeOf; + var ChainNode = __dependency2__.ChainNode; + + var metaFor = meta; + + // get the chains for the current object. If the current object has + // chains inherited from the proto they will be cloned and reconfigured for + // the current object. + function chainsFor(obj, meta) { + var m = meta || metaFor(obj), ret = m.chains; + if (!ret) { + ret = m.chains = new ChainNode(null, null, obj); + } else if (ret.value() !== obj) { + ret = m.chains = ret.copy(obj); + } + return ret; + } - If you implement `willDestroyElement()` on your view, then this method will - be invoked on your view before your element is destroyed to give you a - chance to clean up any event handlers, etc. + function watchPath(obj, keyPath, meta) { + // can't watch length on Array - it is special... + if (keyPath === 'length' && typeOf(obj) === 'array') { return; } - If you write a `willDestroyElement()` handler, you can assume that your - `didInsertElement()` handler was called earlier for the same element. + var m = meta || metaFor(obj), watching = m.watching; - You should not call or override this method yourself, but you may - want to implement the above callbacks. + if (!watching[keyPath]) { // activate watching first time + watching[keyPath] = 1; + chainsFor(obj, m).add(keyPath); + } else { + watching[keyPath] = (watching[keyPath] || 0) + 1; + } + } - @method destroyElement - @return {Ember.View} receiver - */ - destroyElement: function() { - return this.currentState.destroyElement(this); - }, + __exports__.watchPath = watchPath;function unwatchPath(obj, keyPath, meta) { + var m = meta || metaFor(obj), watching = m.watching; - /** - Called when the element of the view is going to be destroyed. Override - this function to do any teardown that requires an element, like removing - event listeners. + if (watching[keyPath] === 1) { + watching[keyPath] = 0; + chainsFor(obj, m).remove(keyPath); + } else if (watching[keyPath] > 1) { + watching[keyPath]--; + } + } - @event willDestroyElement - */ - willDestroyElement: Ember.K, + __exports__.unwatchPath = unwatchPath; + }); +define("ember-metal/watching", + ["ember-metal/utils","ember-metal/chains","ember-metal/watch_key","ember-metal/watch_path","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + "use strict"; + /** + @module ember-metal + */ - /** - Triggers the `willDestroyElement` event (which invokes the - `willDestroyElement()` method if it exists) on this view and all child - views. + var meta = __dependency1__.meta; + var META_KEY = __dependency1__.META_KEY; + var GUID_KEY = __dependency1__.GUID_KEY; + var typeOf = __dependency1__.typeOf; + var generateGuid = __dependency1__.generateGuid; + var removeChainWatcher = __dependency2__.removeChainWatcher; + var flushPendingChains = __dependency2__.flushPendingChains; + var watchKey = __dependency3__.watchKey; + var unwatchKey = __dependency3__.unwatchKey; + var watchPath = __dependency4__.watchPath; + var unwatchPath = __dependency4__.unwatchPath; - Before triggering `willDestroyElement`, it first triggers the - `willClearRender` event recursively. + var metaFor = meta; // utils.js - @method _notifyWillDestroyElement - @private - */ - _notifyWillDestroyElement: function() { - var viewCollection = this.viewHierarchyCollection(); - viewCollection.trigger('willClearRender'); - viewCollection.trigger('willDestroyElement'); - return viewCollection; - }, + // returns true if the passed path is just a keyName + function isKeyName(path) { + return path.indexOf('.') === -1; + } - /** - If this view's element changes, we need to invalidate the caches of our - child views so that we do not retain references to DOM elements that are - no longer needed. + /** + 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 _elementDidChange - @private - */ - _elementDidChange: Ember.observer('element', function() { - this.forEachChildView(function(view) { - delete meta(view).cache.element; - }); - }), + @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; } - /** - Called when the parentView property has changed. + if (isKeyName(_keyPath)) { + watchKey(obj, _keyPath, m); + } else { + watchPath(obj, _keyPath, m); + } + } - @event parentViewDidChange - */ - parentViewDidChange: Ember.K, + __exports__.watch = watch; - instrumentName: 'view', + function isWatching(obj, key) { + var meta = obj[META_KEY]; + return (meta && meta.watching[key]) > 0; + } - instrumentDetails: function(hash) { - hash.template = get(this, 'templateName'); - this._super(hash); - }, + __exports__.isWatching = isWatching;watch.flushPending = flushPendingChains; - _renderToBuffer: function(parentBuffer, bufferOperation) { - this.lengthBeforeRender = this._childViews.length; - var buffer = this._super(parentBuffer, bufferOperation); - this.lengthAfterRender = this._childViews.length; + function unwatch(obj, _keyPath, m) { + // can't watch length on Array - it is special... + if (_keyPath === 'length' && typeOf(obj) === 'array') { return; } - return buffer; - }, + if (isKeyName(_keyPath)) { + unwatchKey(obj, _keyPath, m); + } else { + unwatchPath(obj, _keyPath, m); + } + } - renderToBufferIfNeeded: function (buffer) { - return this.currentState.renderToBufferIfNeeded(this, buffer); - }, + __exports__.unwatch = unwatch;/** + Call on an object when you first beget it from another object. This will + setup any chained watchers on the object instance as needed. This method is + safe to call multiple times. - beforeRender: function(buffer) { - this.applyAttributesToBuffer(buffer); - buffer.pushOpeningTag(); - }, + @private + @method rewatch + @for Ember + @param obj + */ + function rewatch(obj) { + var m = obj[META_KEY], chains = m && m.chains; - afterRender: function(buffer) { - buffer.pushClosingTag(); - }, + // make sure the object has its own guid. + if (GUID_KEY in obj && !obj.hasOwnProperty(GUID_KEY)) { + generateGuid(obj); + } - applyAttributesToBuffer: function(buffer) { - // Creates observers for all registered class name and attribute bindings, - // then adds them to the element. - var classNameBindings = get(this, 'classNameBindings'); - if (classNameBindings.length) { - this._applyClassNameBindings(classNameBindings); + // make sure any chained watchers update. + if (chains && chains.value() !== obj) { + m.chains = chains.copy(obj); + } } - // Pass the render buffer so the method can apply attributes directly. - // This isn't needed for class name bindings because they use the - // existing classNames infrastructure. - var attributeBindings = get(this, 'attributeBindings'); - if (attributeBindings.length) { - this._applyAttributeBindings(buffer, attributeBindings); - } + __exports__.rewatch = rewatch;var NODE_STACK = []; - buffer.setClasses(this.classNames); - buffer.id(this.elementId); + /** + Tears down the meta on an object so that it can be garbage collected. + Multiple calls will have no effect. - var role = get(this, 'ariaRole'); - if (role) { - buffer.attr('role', role); + @method destroy + @for Ember + @param {Object} obj the object to destroy + @return {void} + */ + function destroy(obj) { + var meta = obj[META_KEY], node, nodes, key, nodeObject; + if (meta) { + obj[META_KEY] = null; + // remove chainWatchers to remove circular references that would prevent GC + node = meta.chains; + if (node) { + NODE_STACK.push(node); + // process tree + while (NODE_STACK.length > 0) { + node = NODE_STACK.pop(); + // push children + nodes = node._chains; + if (nodes) { + for (key in nodes) { + if (nodes.hasOwnProperty(key)) { + NODE_STACK.push(nodes[key]); + } + } + } + // remove chainWatcher in node object + if (node._watching) { + nodeObject = node._object; + if (nodeObject) { + removeChainWatcher(nodeObject, node._key, node); + } + } + } + } + } } - if (get(this, 'isVisible') === false) { - buffer.style('display', 'none'); - } - }, + __exports__.destroy = destroy; + }); +define("ember-routing-handlebars", + ["ember-metal/core","ember-handlebars","ember-routing/system/router","ember-routing-handlebars/helpers/shared","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__, __dependency7__, __dependency8__, __exports__) { + "use strict"; + /** + Ember Routing Handlebars - // .......................................................... - // STANDARD RENDER PROPERTIES - // + @module ember + @submodule ember-routing-handlebars + @requires ember-views + */ - /** - Tag name for the view's outer element. The tag name is only used when an - element is first created. If you change the `tagName` for an element, you - must destroy and recreate the view element. + var Ember = __dependency1__["default"]; + var EmberHandlebars = __dependency2__["default"]; + var Router = __dependency3__["default"]; - By default, the render buffer will use a `
    ` tag for views. + var resolvePaths = __dependency4__.resolvePaths; + var resolveParams = __dependency4__.resolveParams; - @property tagName - @type String - @default null - */ + var deprecatedLinkToHelper = __dependency5__.deprecatedLinkToHelper; + var linkToHelper = __dependency5__.linkToHelper; + var LinkView = __dependency5__.LinkView; - // We leave this null by default so we can tell the difference between - // the default case and a user-specified tag. - tagName: null, + var outletHelper = __dependency6__.outletHelper; + var OutletView = __dependency6__.OutletView; - /** - The WAI-ARIA role of the control represented by this view. For example, a - button may have a role of type 'button', or a pane may have a role of - type 'alertdialog'. This property is used by assistive software to help - visually challenged users navigate rich web applications. + var renderHelper = __dependency7__["default"]; - The full list of valid WAI-ARIA roles is available at: - [http://www.w3.org/TR/wai-aria/roles#roles_categorization](http://www.w3.org/TR/wai-aria/roles#roles_categorization) + var ActionHelper = __dependency8__.ActionHelper; + var actionHelper = __dependency8__.actionHelper; - @property ariaRole - @type String - @default null - */ - ariaRole: null, + Router.resolveParams = resolveParams; + Router.resolvePaths = resolvePaths; - /** - Standard CSS class names to apply to the view's outer element. This - property automatically inherits any class names defined by the view's - superclasses as well. + Ember.LinkView = LinkView; + EmberHandlebars.ActionHelper = ActionHelper; + EmberHandlebars.OutletView = OutletView; - @property classNames - @type Array - @default ['ember-view'] - */ - classNames: ['ember-view'], + EmberHandlebars.registerHelper('render', renderHelper); + EmberHandlebars.registerHelper('action', actionHelper); + EmberHandlebars.registerHelper('outlet', outletHelper); + EmberHandlebars.registerHelper('link-to', linkToHelper); + EmberHandlebars.registerHelper('linkTo', deprecatedLinkToHelper); - /** - A list of properties of the view to apply as class names. If the property - is a string value, the value of that string will be applied as a class - name. + __exports__["default"] = Ember; + }); +define("ember-routing-handlebars/helpers/action", + ["ember-metal/core","ember-metal/property_get","ember-metal/array","ember-metal/utils","ember-metal/run_loop","ember-views/system/utils","ember-routing/system/router","ember-handlebars","ember-handlebars/ext","ember-handlebars/helpers/view","ember-routing-handlebars/helpers/shared","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // Handlebars, uuid, FEATURES, assert, deprecate + var get = __dependency2__.get; + var forEach = __dependency3__.forEach; + var uuid = __dependency4__.uuid; + var run = __dependency5__["default"]; - ```javascript - // Applies the 'high' class to the view element - Ember.View.extend({ - classNameBindings: ['priority'] - priority: 'high' - }); - ``` + var isSimpleClick = __dependency6__.isSimpleClick; + var EmberRouter = __dependency7__["default"]; - If the value of the property is a Boolean, the name of that property is - added as a dasherized class name. + var EmberHandlebars = __dependency8__["default"]; + var handlebarsGet = __dependency9__.handlebarsGet; + var viewHelper = __dependency10__.viewHelper; + var resolveParams = __dependency11__.resolveParams; + var resolvePath = __dependency11__.resolvePath; - ```javascript - // Applies the 'is-urgent' class to the view element - Ember.View.extend({ - classNameBindings: ['isUrgent'] - isUrgent: true - }); - ``` - - If you would prefer to use a custom value instead of the dasherized - property name, you can pass a binding like this: + /** + @module ember + @submodule ember-routing + */ - ```javascript - // Applies the 'urgent' class to the view element - Ember.View.extend({ - classNameBindings: ['isUrgent:urgent'] - isUrgent: true - }); - ``` + var SafeString = EmberHandlebars.SafeString; + var a_slice = Array.prototype.slice; - This list of properties is inherited from the view's superclasses as well. + function args(options, actionName) { + var ret = []; + if (actionName) { ret.push(actionName); } - @property classNameBindings - @type Array - @default [] - */ - classNameBindings: EMPTY_ARRAY, + var types = options.options.types.slice(1), + data = options.options.data; - /** - A list of properties of the view to apply as attributes. If the property is - a string value, the value of that string will be applied as the attribute. + return ret.concat(resolveParams(options.context, options.params, { types: types, data: data })); + } - ```javascript - // Applies the type attribute to the element - // with the value "button", like
    - Ember.View.extend({ - attributeBindings: ['type'], - type: 'button' - }); - ``` + var ActionHelper = { + registeredActions: {} + }; - If the value of the property is a Boolean, the name of that property is - added as an attribute. + __exports__.ActionHelper = ActionHelper; - ```javascript - // Renders something like
    - Ember.View.extend({ - attributeBindings: ['enabled'], - enabled: true - }); - ``` + var keys = ["alt", "shift", "meta", "ctrl"]; - @property attributeBindings - */ - attributeBindings: EMPTY_ARRAY, + var POINTER_EVENT_TYPE_REGEX = /^click|mouse|touch/; - // ....................................................... - // CORE DISPLAY METHODS - // + var isAllowedEvent = function(event, allowedKeys) { + if (typeof allowedKeys === "undefined") { + if (POINTER_EVENT_TYPE_REGEX.test(event.type)) { + return isSimpleClick(event); + } else { + allowedKeys = ''; + } + } - /** - Setup a view, but do not finish waking it up. + if (allowedKeys.indexOf("any") >= 0) { + return true; + } - * configure `childViews` - * register the view with the global views hash, which is used for event - dispatch + var allowed = true; - @method init - @private - */ - init: function() { - this.elementId = this.elementId || guidFor(this); + forEach.call(keys, function(key) { + if (event[key + "Key"] && allowedKeys.indexOf(key) === -1) { + allowed = false; + } + }); - this._super(); + return allowed; + }; - // setup child views. be sure to clone the child views array first - this._childViews = this._childViews.slice(); + ActionHelper.registerAction = function(actionNameOrPath, options, allowedKeys) { + var actionId = uuid(); - Ember.assert("Only arrays are allowed for 'classNameBindings'", Ember.typeOf(this.classNameBindings) === 'array'); - this.classNameBindings = Ember.A(this.classNameBindings.slice()); + ActionHelper.registeredActions[actionId] = { + eventName: options.eventName, + handler: function handleRegisteredAction(event) { + if (!isAllowedEvent(event, allowedKeys)) { return true; } - Ember.assert("Only arrays are allowed for 'classNames'", Ember.typeOf(this.classNames) === 'array'); - this.classNames = Ember.A(this.classNames.slice()); - }, + if (options.preventDefault !== false) { + event.preventDefault(); + } - appendChild: function(view, options) { - return this.currentState.appendChild(this, view, options); - }, + if (options.bubbles === false) { + event.stopPropagation(); + } - /** - Removes the child view from the parent view. + var target = options.target, + parameters = options.parameters, + actionName; - @method removeChild - @param {Ember.View} view - @return {Ember.View} receiver - */ - removeChild: function(view) { - // If we're destroying, the entire subtree will be - // freed, and the DOM will be handled separately, - // so no need to mess with childViews. - if (this.isDestroying) { return; } + if (target.target) { + target = handlebarsGet(target.root, target.target, target.options); + } else { + target = target.root; + } - // update parent node - set(view, '_parentView', null); + if (options.boundProperty) { + actionName = resolveParams(parameters.context, [actionNameOrPath], { types: ['ID'], data: parameters.options.data })[0]; - // remove view from childViews array. - var childViews = this._childViews; + if (typeof actionName === 'undefined' || typeof actionName === 'function') { + Ember.deprecate("You specified a quoteless path to the {{action}} helper '" + actionNameOrPath + "' which did not resolve to an actionName. Perhaps you meant to use a quoted actionName? (e.g. {{action '" + actionNameOrPath + "'}})."); + actionName = actionNameOrPath; + } + } - Ember.EnumerableUtils.removeObject(childViews, view); + if (!actionName) { + actionName = actionNameOrPath; + } - this.propertyDidChange('childViews'); // HUH?! what happened to will change? + run(function runRegisteredAction() { + if (target.send) { + target.send.apply(target, args(parameters, actionName)); + } else { + Ember.assert("The action '" + actionName + "' did not exist on " + target, typeof target[actionName] === 'function'); + target[actionName].apply(target, args(parameters)); + } + }); + } + }; - return this; - }, + options.view.on('willClearRender', function() { + delete ActionHelper.registeredActions[actionId]; + }); - /** - Removes all children from the `parentView`. + return actionId; + }; - @method removeAllChildren - @return {Ember.View} receiver - */ - removeAllChildren: function() { - return this.mutateChildViews(function(parentView, view) { - parentView.removeChild(view); - }); - }, + /** + The `{{action}}` helper registers an HTML element within a template for DOM + event handling and forwards that interaction to the templates's controller + or supplied `target` option (see 'Specifying a Target'). - destroyAllChildren: function() { - return this.mutateChildViews(function(parentView, view) { - view.destroy(); - }); - }, + If the controller does not implement the event, the event is sent + to the current route, and it bubbles up the route hierarchy from there. - /** - Removes the view from its `parentView`, if one is found. Otherwise - does nothing. + User interaction with that element will invoke the supplied action name on + the appropriate target. Specifying a non-quoted action name will result in + a bound property lookup at the time the event will be triggered. - @method removeFromParent - @return {Ember.View} receiver - */ - removeFromParent: function() { - var parent = this._parentView; + Given the following application Handlebars template on the page - // Remove DOM element from parent - this.remove(); + ```handlebars +
    + click me +
    + ``` - if (parent) { parent.removeChild(this); } - return this; - }, + And application code - /** - You must call `destroy` on a view to destroy the view (and all of its - child views). This will remove the view from any parent node, then make - sure that the DOM element managed by the view can be released by the - memory manager. + ```javascript + App.ApplicationController = Ember.Controller.extend({ + actions: { + anActionName: function() { + } + } + }); + ``` - @method destroy - */ - destroy: function() { - var childViews = this._childViews, - // get parentView before calling super because it'll be destroyed - nonVirtualParentView = get(this, 'parentView'), - viewName = this.viewName, - childLen, i; + Will result in the following rendered HTML - if (!this._super()) { return; } + ```html +
    +
    + click me +
    +
    + ``` - childLen = childViews.length; - for (i=childLen-1; i>=0; i--) { - childViews[i].removedFromDOM = true; - } + Clicking "click me" will trigger the `anActionName` action of the + `App.ApplicationController`. In this case, no additional parameters will be passed. - // remove from non-virtual parent view if viewName was specified - if (viewName && nonVirtualParentView) { - nonVirtualParentView.set(viewName, null); - } + If you provide additional parameters to the helper: - childLen = childViews.length; - for (i=childLen-1; i>=0; i--) { - childViews[i].destroy(); - } + ```handlebars + + ``` - return this; - }, + Those parameters will be passed along as arguments to the JavaScript + function implementing the action. - /** - Instantiates a view to be added to the childViews array during view - initialization. You generally will not call this method directly unless - you are overriding `createChildViews()`. Note that this method will - automatically configure the correct settings on the new view instance to - act as a child of the parent. + ### Event Propagation - @method createChildView - @param {Class|String} viewClass - @param {Hash} [attrs] Attributes to add - @return {Ember.View} new instance - */ - createChildView: function(view, attrs) { - if (!view) { - throw new TypeError("createChildViews first argument must exist"); - } + 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 (view.isView && view._parentView === this && view.container === this.container) { - return view; - } + ```handlebars +
    + + +
    + ``` - attrs = attrs || {}; - attrs._parentView = this; + To disable bubbling, pass `bubbles=false` to the helper: - if (Ember.CoreView.detect(view)) { - attrs.templateData = attrs.templateData || get(this, 'templateData'); + ```handlebars + + ``` - attrs.container = this.container; - view = view.create(attrs); + 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. - // don't set the property on a virtual view, as they are invisible to - // consumers of the view API - if (view.viewName) { - set(get(this, 'concreteView'), view.viewName, view); - } - } else if ('string' === typeof view) { - var fullName = 'view:' + view; - var View = this.container.lookupFactory(fullName); + ### Specifying DOM event type - Ember.assert("Could not find view: '" + fullName + "'", !!View); + 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: - attrs.templateData = get(this, 'templateData'); - view = View.create(attrs); - } else { - Ember.assert('You must pass instance or subclass of View', view.isView); - attrs.container = this.container; + ```handlebars +
    + click me +
    + ``` - if (!get(view, 'templateData')) { - attrs.templateData = get(this, 'templateData'); - } + See `Ember.View` 'Responding to Browser Events' for a list of + acceptable DOM event names. - Ember.setProperties(view, attrs); + NOTE: Because `{{action}}` depends on Ember's event dispatch system it will + only function if an `Ember.EventDispatcher` instance is available. An + `Ember.EventDispatcher` instance will be created when a new `Ember.Application` + is created. Having an instance of `Ember.Application` will satisfy this + requirement. - } + ### Specifying whitelisted modifier keys - return view; - }, + 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. - becameVisible: Ember.K, - becameHidden: Ember.K, + ```handlebars +
    + click me +
    + ``` - /** - When the view's `isVisible` property changes, toggle the visibility - element of the actual DOM element. + This way the `{{action}}` will fire when clicking with the alt key pressed down. - @method _isVisibleDidChange - @private - */ - _isVisibleDidChange: Ember.observer('isVisible', function() { - if (this._isVisible === get(this, 'isVisible')) { return ; } - Ember.run.scheduleOnce('render', this, this._toggleVisibility); - }), + Alternatively, supply "any" to the `allowedKeys` option to accept any combination of modifier keys. - _toggleVisibility: function() { - var $el = this.$(); - if (!$el) { return; } + ```handlebars +
    + click me with any key pressed +
    + ``` - var isVisible = get(this, 'isVisible'); + ### Specifying a Target - if (this._isVisible === isVisible) { return ; } + There are several possible target objects for `{{action}}` helpers: - $el.toggle(isVisible); + In a typical Ember application, where views 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. - this._isVisible = isVisible; + 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: - if (this._isAncestorHidden()) { return; } + ```handlebars + {{! the application template }} +
    + click me +
    + ``` - if (isVisible) { - this._notifyBecameVisible(); - } else { - this._notifyBecameHidden(); - } - }, + ```javascript + App.ApplicationView = Ember.View.extend({ + actions: { + anActionName: function(){} + } + }); - _notifyBecameVisible: function() { - this.trigger('becameVisible'); + ``` - this.forEachChildView(function(view) { - var isVisible = get(view, 'isVisible'); + ### Additional Parameters - if (isVisible || isVisible === null) { - view._notifyBecameVisible(); - } - }); - }, + You may specify additional parameters to the `{{action}}` helper. These + parameters are passed along as the arguments to the JavaScript function + implementing the action. - _notifyBecameHidden: function() { - this.trigger('becameHidden'); - this.forEachChildView(function(view) { - var isVisible = get(view, 'isVisible'); + ```handlebars + {{#each person in people}} +
    + click me +
    + {{/each}} + ``` - if (isVisible || isVisible === null) { - view._notifyBecameHidden(); - } - }); - }, + Clicking "click me" will trigger the `edit` method on the current controller + with the value of `person` as a parameter. - _isAncestorHidden: function() { - var parent = get(this, 'parentView'); + @method action + @for Ember.Handlebars.helpers + @param {String} actionName + @param {Object} [context]* + @param {Hash} options + */ + function actionHelper(actionName) { + var options = arguments[arguments.length - 1], + contexts = a_slice.call(arguments, 1, -1); + + var hash = options.hash, + controller = options.data.keywords.controller; + + // create a hash to pass along to registerAction + var action = { + eventName: hash.on || "click", + parameters: { + context: this, + options: options, + params: contexts + }, + view: options.data.view, + bubbles: hash.bubbles, + preventDefault: hash.preventDefault, + target: { options: options }, + boundProperty: options.types[0] === "ID" + }; - while (parent) { - if (get(parent, 'isVisible') === false) { return true; } + if (hash.target) { + action.target.root = this; + action.target.target = hash.target; + } else if (controller) { + action.target.root = controller; + } - parent = get(parent, 'parentView'); + var actionId = ActionHelper.registerAction(actionName, action, hash.allowedKeys); + return new SafeString('data-ember-action="' + actionId + '"'); } - return false; - }, + __exports__.actionHelper = actionHelper; + }); +define("ember-routing-handlebars/helpers/link_to", + ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/merge","ember-metal/run_loop","ember-metal/computed","ember-runtime/system/lazy_load","ember-runtime/system/string","ember-runtime/system/object","ember-runtime/keys","ember-views/system/utils","ember-views/views/component","ember-handlebars","ember-handlebars/helpers/view","ember-routing/system/router","ember-routing-handlebars/helpers/shared","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // FEATURES, Logger, Handlebars, warn, assert + var get = __dependency2__.get; + var set = __dependency3__.set; + var merge = __dependency4__["default"]; + var run = __dependency5__["default"]; + var computed = __dependency6__.computed; + + var onLoad = __dependency7__.onLoad; + var fmt = __dependency8__.fmt; + var EmberObject = __dependency9__["default"]; + var keys = __dependency10__["default"]; + var isSimpleClick = __dependency11__.isSimpleClick; + var EmberComponent = __dependency12__["default"]; + var EmberHandlebars = __dependency13__["default"]; + var viewHelper = __dependency14__.viewHelper; + var EmberRouter = __dependency15__["default"]; + var resolveParams = __dependency16__.resolveParams; + var resolvePaths = __dependency16__.resolvePaths; + var routeArgs = __dependency16__.routeArgs; - clearBuffer: function() { - this.invokeRecursively(nullViewsBuffer); - }, + /** + @module ember + @submodule ember-routing + */ - transitionTo: function(state, children) { - var priorState = this.currentState, - currentState = this.currentState = this.states[state]; - this.state = state; + var slice = [].slice; - if (priorState && priorState.exit) { priorState.exit(this); } - if (currentState.enter) { currentState.enter(this); } - if (state === 'inDOM') { delete Ember.meta(this).cache.element; } + requireModule('ember-handlebars'); - if (children !== false) { - this.forEachChildView(function(view) { - view.transitionTo(state); - }); - } - }, + 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; + } - // ....................................................... - // EVENT HANDLING - // + return req; + }; - /** - Handle events from `Ember.EventDispatcher` + var QueryParams = EmberObject.extend({ + values: null + }); - @method handleEvent - @param eventName {String} - @param evt {Event} - @private - */ - handleEvent: function(eventName, evt) { - return this.currentState.handleEvent(this, eventName, evt); - }, + function getResolvedPaths(options) { - registerObserver: function(root, path, target, observer) { - if (!observer && 'function' === typeof target) { - observer = target; - target = null; - } + var types = options.options.types, + data = options.options.data; - if (!root || typeof root !== 'object') { - return; + return resolvePaths(options.context, options.params, { types: types, data: data }); } - var view = this, - stateCheckedObserver = function() { - view.currentState.invokeObserver(this, observer); - }, - scheduledObserver = function() { - Ember.run.scheduleOnce('render', this, stateCheckedObserver); - }; + /** + `Ember.LinkView` renders an element whose `click` event triggers a + transition of the application's instance of `Ember.Router` to + a supplied route by name. + + Instances of `LinkView` will most likely be created through + the `link-to` Handlebars helper, but properties of this class + can be overridden to customize application-wide behavior. + + @class LinkView + @namespace Ember + @extends Ember.View + @see {Handlebars.helpers.link-to} + **/ + var LinkView = Ember.LinkView = EmberComponent.extend({ + tagName: 'a', + currentWhen: null, - Ember.addObserver(root, path, target, scheduledObserver); + /** + Sets the `title` attribute of the `LinkView`'s HTML element. - this.one('willClearRender', function() { - Ember.removeObserver(root, path, target, scheduledObserver); - }); - } + @property title + @default null + **/ + title: null, -}); + /** + Sets the `rel` attribute of the `LinkView`'s HTML element. -/* - Describe how the specified actions should behave in the various - states that a view can exist in. Possible states: + @property rel + @default null + **/ + rel: null, - * preRender: when a view is first instantiated, and after its - element was destroyed, it is in the preRender state - * inBuffer: once a view has been rendered, but before it has - been inserted into the DOM, it is in the inBuffer state - * hasElement: the DOM representation of the view is created, - and is ready to be inserted - * inDOM: once a view has been inserted into the DOM it is in - the inDOM state. A view spends the vast majority of its - existence in this state. - * destroyed: once a view has been destroyed (using the destroy - method), it is in this state. No further actions can be invoked - on a destroyed view. -*/ + /** + The CSS class to apply to `LinkView`'s element when its `active` + property is `true`. - // in the destroyed state, everything is illegal + @property activeClass + @type String + @default active + **/ + activeClass: 'active', - // before rendering has begun, all legal manipulations are noops. + /** + The CSS class to apply to `LinkView`'s element when its `loading` + property is `true`. - // inside the buffer, legal manipulations are done on the buffer + @property loadingClass + @type String + @default loading + **/ + loadingClass: 'loading', - // once the view has been inserted into the DOM, legal manipulations - // are done on the DOM element. + /** + The CSS class to apply to a `LinkView`'s element when its `disabled` + property is `true`. -function notifyMutationListeners() { - Ember.run.once(Ember.View, 'notifyMutationListeners'); -} + @property disabledClass + @type String + @default disabled + **/ + disabledClass: 'disabled', + _isDisabled: false, -var DOMManager = { - prepend: function(view, html) { - view.$().prepend(html); - notifyMutationListeners(); - }, + /** + Determines whether the `LinkView` will trigger routing via + the `replaceWith` routing strategy. - after: function(view, html) { - view.$().after(html); - notifyMutationListeners(); - }, + @property replace + @type Boolean + @default false + **/ + replace: false, - html: function(view, html) { - view.$().html(html); - notifyMutationListeners(); - }, + /** + 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. - replace: function(view) { - var element = get(view, 'element'); + @property attributeBindings + @type Array | String + @default ['href', 'title', 'rel'] + **/ + attributeBindings: ['href', 'title', 'rel', 'tabindex'], - set(view, 'element', null); + /** + By default the `{{link-to}}` helper will bind to the `active`, `loading`, and + `disabled` classes. It is discouraged to override these directly. - view._insertElementLater(function() { - Ember.$(element).replaceWith(get(view, 'element')); - notifyMutationListeners(); - }); - }, + @property classNameBindings + @type Array + @default ['active', 'loading', 'disabled'] + **/ + classNameBindings: ['active', 'loading', 'disabled'], - remove: function(view) { - view.$().remove(); - notifyMutationListeners(); - }, + /** + 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. - empty: function(view) { - view.$().empty(); - notifyMutationListeners(); - } -}; + This is particularly useful on mobile when one wants to avoid the 300ms + click delay using some sort of custom `tap` event. -Ember.View.reopen({ - domManager: DOMManager -}); + @property eventName + @type String + @default click + */ + eventName: 'click', -Ember.View.reopenClass({ + // 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. - /** - Parse a path and return an object which holds the parsed properties. + @event click + **/ - For example a path like "content.isEnabled:enabled:disabled" will return the - following object: + /** + An overridable method called when LinkView objects are instantiated. - ```javascript - { - path: "content.isEnabled", - className: "enabled", - falsyClassName: "disabled", - classNames: ":enabled:disabled" - } - ``` + Example: - @method _parsePropertyPath - @static - @private - */ - _parsePropertyPath: function(path) { - var split = path.split(':'), - propertyPath = split[0], - classNames = "", - className, - falsyClassName; - - // check if the property is defined as prop:class or prop:trueClass:falseClass - if (split.length > 1) { - className = split[1]; - if (split.length === 3) { falsyClassName = split[2]; } - - classNames = ':' + className; - if (falsyClassName) { classNames += ":" + falsyClassName; } - } - - return { - path: propertyPath, - classNames: classNames, - className: (className === '') ? undefined : className, - falsyClassName: falsyClassName - }; - }, - - /** - Get the class name for a given value, based on the path, optional - `className` and optional `falsyClassName`. - - - 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 - */ - _classStringForValue: function(path, val, className, falsyClassName) { - // 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; - - } else if (falsyClassName && !val) { - return falsyClassName; + ```javascript + App.MyLinkView = Ember.LinkView.extend({ + init: function() { + this._super(); + Ember.Logger.log('Event is ' + this.get('eventName')); + } + }); + ``` - } else { - return null; - } + NOTE: If you do override `init` for a framework class like `Ember.View` or + `Ember.ArrayController`, be sure to call `this._super()` in your + `init` declaration! If you don't, Ember may not have an opportunity to + do important setup work, and you'll see strange behavior in your + application. - // If 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 Ember.String.dasherize(parts[parts.length-1]); + @method init + */ + init: function() { + this._super.apply(this, arguments); - // If the value is not false, undefined, or null, return the current - // value of the property. - } else if (val !== false && val != null) { - return val; + // Map desired event name to invoke function + var eventName = get(this, 'eventName'); + this.on(eventName, this, this._invoke); + }, - // Nothing to display. Return null so that the old class is removed - // but no new class is added. - } else { - return null; - } - } -}); - -var mutation = Ember.Object.extend(Ember.Evented).create(); - -Ember.View.addMutationListener = function(callback) { - mutation.on('change', callback); -}; - -Ember.View.removeMutationListener = function(callback) { - mutation.off('change', callback); -}; - -Ember.View.notifyMutationListeners = function() { - mutation.trigger('change'); -}; - -/** - Global views hash - - @property views - @static - @type Hash -*/ -Ember.View.views = {}; - -// If someone overrides the child views computed property when -// defining their class, we want to be able to process the user's -// supplied childViews and then restore the original computed property -// at view initialization time. This happens in Ember.ContainerView's init -// method. -Ember.View.childViewsProperty = childViewsProperty; - -Ember.View.applyAttributeBindings = function(elem, name, value) { - var type = Ember.typeOf(value); - - // if this changes, also change the logic in ember-handlebars/lib/helpers/binding.js - if (name !== 'value' && (type === 'string' || (type === 'number' && !isNaN(value)))) { - if (value !== elem.attr(name)) { - elem.attr(name, value); - } - } else if (name === 'value' || type === 'boolean') { - if (Ember.isNone(value) || value === false) { - // `null`, `undefined` or `false` should remove attribute - elem.removeAttr(name); - elem.prop(name, ''); - } else if (value !== elem.prop(name)) { - // value should always be properties - elem.prop(name, value); - } - } else if (!value) { - elem.removeAttr(name); - } -}; + /** + This method is invoked by observers installed during `init` that fire + whenever the params change -Ember.View.states = states; + @private + @method _paramsChanged + @since 1.3.0 + */ + _paramsChanged: function() { + this.notifyPropertyChange('resolvedParams'); + }, -})(); + /** + This is called to setup observers that will trigger a rerender. + + @private + @method _setupPathObservers + @since 1.3.0 + **/ + _setupPathObservers: function(){ + var helperParameters = this.parameters, + linkTextPath = helperParameters.options.linkTextPath, + paths = getResolvedPaths(helperParameters), + length = paths.length, + path, i, normalizedPath; + + if (linkTextPath) { + normalizedPath = getNormalizedPath(linkTextPath, helperParameters); + this.registerObserver(normalizedPath.root, normalizedPath.path, this, this.rerender); + } + + for(i=0; i < length; i++) { + path = paths[i]; + if (null === path) { + // A literal value was provided, not a path, so nothing to observe. + continue; + } + normalizedPath = getNormalizedPath(path, helperParameters); + this.registerObserver(normalizedPath.root, normalizedPath.path, this, this._paramsChanged); + } + var queryParamsObject = this.queryParamsObject; + if (queryParamsObject) { + var values = queryParamsObject.values; -(function() { -/** -@module ember -@submodule ember-views -*/ + // Install observers for all of the hash options + // provided in the (query-params) subexpression. + for (var k in values) { + if (!values.hasOwnProperty(k)) { continue; } -var get = Ember.get, set = Ember.set; + if (queryParamsObject.types[k] === 'ID') { + normalizedPath = getNormalizedPath(values[k], helperParameters); + this.registerObserver(normalizedPath.root, normalizedPath.path, this, this._paramsChanged); + } + } + } + }, -Ember.View.states._default = { - // appendChild is only legal while rendering the buffer. - appendChild: function() { - throw "You can't use appendChild outside of the rendering process"; - }, + afterRender: function(){ + this._super.apply(this, arguments); + this._setupPathObservers(); + }, - $: function() { - return undefined; - }, + /** - getElement: function() { - return null; - }, + Accessed as a classname binding to apply the `LinkView`'s `disabledClass` + CSS `class` to the element when the link is disabled. - // Handle events from `Ember.EventDispatcher` - handleEvent: function() { - return true; // continue event propagation - }, + 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); } - destroyElement: function(view) { - set(view, 'element', null); - if (view._scheduledInsert) { - Ember.run.cancel(view._scheduledInsert); - view._scheduledInsert = null; - } - return view; - }, + return value ? get(this, 'disabledClass') : false; + }), - renderToBufferIfNeeded: function () { - return false; - }, + /** + Accessed as a classname binding to apply the `LinkView`'s `activeClass` + CSS `class` to the element when the link is active. + + A `LinkView` is considered active when its `currentWhen` property is `true` + or the application's current route is the route the `LinkView` would trigger + transitions into. + + @property active + **/ + active: computed('loadedParams', function computeLinkViewActive() { + if (get(this, 'loading')) { return false; } + + var router = get(this, 'router'); + var loadedParams = get(this, 'loadedParams'); + var contexts = loadedParams.models; + var currentWhen = this.currentWhen; + var isCurrentWhenSpecified = Boolean(currentWhen); + currentWhen = currentWhen || loadedParams.targetRouteName; + + var handlers = router.router.recognizer.handlersFor(currentWhen); + var leafName = handlers[handlers.length-1].handler; + var maximumContexts = numberOfContextsAcceptedByHandler(currentWhen, handlers); + + // NOTE: any ugliness in the calculation of activeness is largely + // due to the fact that we support automatic normalizing of + // `resource` -> `resource.index`, even though there might be + // dynamic segments / query params defined on `resource.index` + // which complicates (and makes somewhat ambiguous) the calculation + // of activeness for links that link to `resource` instead of + // directly to `resource.index`. + + // if we don't have enough contexts revert back to full route name + // this is because the leaf route will use one of the contexts + if (contexts.length > maximumContexts) { + currentWhen = leafName; + } + + var args = routeArgs(currentWhen, contexts, null); + var isActive = router.isActive.apply(router, args); + if (!isActive) { return false; } - rerender: Ember.K, - invokeObserver: Ember.K -}; + + var emptyQueryParams = Ember.isEmpty(Ember.keys(loadedParams.queryParams)); -})(); + if (!isCurrentWhenSpecified && !emptyQueryParams && isActive) { + var visibleQueryParams = {}; + merge(visibleQueryParams, loadedParams.queryParams); + router._prepareQueryParams(loadedParams.targetRouteName, loadedParams.models, visibleQueryParams); + isActive = shallowEqual(visibleQueryParams, router.router.state.queryParams); + } + + if (isActive) { return get(this, 'activeClass'); } + }), + /** + Accessed as a classname binding to apply the `LinkView`'s `loadingClass` + CSS `class` to the element when the link is loading. -(function() { -/** -@module ember -@submodule ember-views -*/ + 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. -var preRender = Ember.View.states.preRender = Ember.create(Ember.View.states._default); + @property loading + **/ + loading: computed('loadedParams', function computeLinkViewLoading() { + if (!get(this, 'loadedParams')) { return get(this, 'loadingClass'); } + }), -Ember.merge(preRender, { - // a view leaves the preRender state once its element has been - // created (createElement). - insertElement: function(view, fn) { - view.createElement(); - var viewCollection = view.viewHierarchyCollection(); + /** + Returns the application's main router from the container. - viewCollection.trigger('willInsertElement'); + @private + @property router + **/ + router: computed(function() { + var controller = get(this, 'controller'); + if (controller && controller.container) { + return controller.container.lookup('router:main'); + } + }), - fn.call(view); + /** + Event handler that invokes the link, activating the associated route. - // We transition to `inDOM` if the element exists in the DOM - var element = view.get('element'); - if (document.body.contains(element)) { - viewCollection.transitionTo('inDOM', false); - viewCollection.trigger('didInsertElement'); - } - }, + @private + @method _invoke + @param {Event} event + */ + _invoke: function(event) { + if (!isSimpleClick(event)) { return true; } - renderToBufferIfNeeded: function(view, buffer) { - view.renderToBuffer(buffer); - return true; - }, + if (this.preventDefault !== false) { + + event.preventDefault(); + + } - empty: Ember.K, + if (this.bubbles === false) { event.stopPropagation(); } - setElement: function(view, value) { - if (value !== null) { - view.transitionTo('hasElement'); - } - return value; - } -}); + if (get(this, '_isDisabled')) { return false; } -})(); + if (get(this, 'loading')) { + Ember.Logger.warn("This link-to is in an inactive loading state because at least one of its parameters presently has a null/undefined value, or the provided route name is invalid."); + return false; + } + var router = get(this, 'router'), + loadedParams = get(this, 'loadedParams'); + var transition = router._doTransition(loadedParams.targetRouteName, loadedParams.models, loadedParams.queryParams); + if (get(this, 'replace')) { + transition.method('replace'); + } -(function() { -/** -@module ember -@submodule ember-views -*/ - -var get = Ember.get, set = Ember.set; - -var inBuffer = Ember.View.states.inBuffer = Ember.create(Ember.View.states._default); - -Ember.merge(inBuffer, { - $: function(view, sel) { - // if we don't have an element yet, someone calling this.$() is - // trying to update an element that isn't in the DOM. Instead, - // rerender the view to allow the render method to reflect the - // changes. - view.rerender(); - return Ember.$(); - }, - - // when a view is rendered in a buffer, rerendering it simply - // replaces the existing buffer with a new one - rerender: function(view) { - throw new Ember.Error("Something you did caused a view to re-render after it rendered but before it was inserted into the DOM."); - }, - - // when a view is rendered in a buffer, appending a child - // view will render that view and append the resulting - // buffer into its buffer. - appendChild: function(view, childView, options) { - var buffer = view.buffer, _childViews = view._childViews; - - childView = view.createChildView(childView, options); - if (!_childViews.length) { _childViews = view._childViews = _childViews.slice(); } - _childViews.push(childView); - - childView.renderToBuffer(buffer); - - view.propertyDidChange('childViews'); - - return childView; - }, - - // when a view is rendered in a buffer, destroying the - // element will simply destroy the buffer and put the - // state back into the preRender state. - destroyElement: function(view) { - view.clearBuffer(); - var viewCollection = view._notifyWillDestroyElement(); - viewCollection.transitionTo('preRender', false); - - return view; - }, - - empty: function() { - Ember.assert("Emptying a view in the inBuffer state is not allowed and " + - "should not happen under normal circumstances. Most likely " + - "there is a bug in your application. This may be due to " + - "excessive property change notifications."); - }, - - renderToBufferIfNeeded: function (view, buffer) { - return false; - }, - - // It should be impossible for a rendered view to be scheduled for - // insertion. - insertElement: function() { - throw "You can't insert an element that has already been rendered"; - }, - - setElement: function(view, value) { - if (value === null) { - view.transitionTo('preRender'); - } else { - view.clearBuffer(); - view.transitionTo('hasElement'); - } + // 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); - return value; - }, + run.scheduleOnce('routerTransitions', this, this._eagerUpdateUrl, transition, url); + }, - invokeObserver: function(target, observer) { - observer.call(target); - } -}); + /** + @private + @method _eagerUpdateUrl + @param transition + @param href + */ + _eagerUpdateUrl: function(transition, href) { + if (!transition.isActive || !transition.urlMethod) { + // transition was aborted, already ran to completion, + // or it has a null url-updated method. + return; + } + if (href.indexOf('#') === 0) { + href = href.slice(1); + } -})(); + // Re-use the routerjs hooks set up by the Ember router. + var routerjs = get(this, 'router.router'); + if (transition.urlMethod === 'update') { + routerjs.updateURL(href); + } else if (transition.urlMethod === 'replace') { + routerjs.replaceURL(href); + } + // Prevent later update url refire. + transition.method(null); + }, + /** + Computed property that returns an array of the + resolved parameters passed to the `link-to` helper, + e.g.: -(function() { -/** -@module ember -@submodule ember-views -*/ - -var get = Ember.get, set = Ember.set; - -var hasElement = Ember.View.states.hasElement = Ember.create(Ember.View.states._default); - -Ember.merge(hasElement, { - $: function(view, sel) { - var elem = get(view, 'element'); - return sel ? Ember.$(sel, elem) : Ember.$(elem); - }, - - getElement: function(view) { - var parent = get(view, 'parentView'); - if (parent) { parent = get(parent, 'element'); } - if (parent) { return view.findElementInParentElement(parent); } - return Ember.$("#" + get(view, 'elementId'))[0]; - }, - - setElement: function(view, value) { - if (value === null) { - view.transitionTo('preRender'); - } else { - throw "You cannot set an element to a non-null value when the element is already in the DOM."; - } + ```hbs + {{link-to a b '123' c}} + ``` - return value; - }, + will generate a `resolvedParams` of: - // once the view has been inserted into the DOM, rerendering is - // deferred to allow bindings to synchronize. - rerender: function(view) { - view.triggerRecursively('willClearRender'); + ```js + [aObject, bObject, '123', cObject] + ``` - view.clearRenderedChildren(); + @private + @property + @return {Array} + */ + resolvedParams: computed('router.url', function() { + var parameters = this.parameters, + options = parameters.options, + types = options.types, + data = options.data, + targetRouteName, models; + + var onlyQueryParamsSupplied = (parameters.params.length === 0); + if (onlyQueryParamsSupplied) { + var appController = this.container.lookup('controller:application'); + targetRouteName = get(appController, 'currentRouteName'); + models = []; + } else { + models = resolveParams(parameters.context, parameters.params, { types: types, data: data }); + targetRouteName = models.shift(); + } - view.domManager.replace(view); - return view; - }, + var suppliedQueryParams = getResolvedQueryParams(this, targetRouteName); - // once the view is already in the DOM, destroying it removes it - // from the DOM, nukes its element, and puts it back into the - // preRender state if inDOM. + return { + targetRouteName: targetRouteName, + models: models, + queryParams: suppliedQueryParams + }; + }), - destroyElement: function(view) { - view._notifyWillDestroyElement(); - view.domManager.remove(view); - set(view, 'element', null); - if (view._scheduledInsert) { - Ember.run.cancel(view._scheduledInsert); - view._scheduledInsert = null; - } - return view; - }, + /** + 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. - empty: function(view) { - var _childViews = view._childViews, len, idx; - if (_childViews) { - len = _childViews.length; - for (idx = 0; idx < len; idx++) { - _childViews[idx]._notifyWillDestroyElement(); - } - } - view.domManager.empty(view); - }, + @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; } - // Handle events from `Ember.EventDispatcher` - handleEvent: function(view, eventName, evt) { - if (view.has(eventName)) { - // Handler should be able to re-dispatch events, so we don't - // preventDefault or stopPropagation. - return view.trigger(eventName, evt); - } else { - return true; // continue event propagation - } - }, + var resolvedParams = get(this, 'resolvedParams'), + namedRoute = resolvedParams.targetRouteName; - invokeObserver: function(target, observer) { - observer.call(target); - } -}); -})(); + if (!namedRoute) { return; } + Ember.assert(fmt("The attempt to link-to route '%@' failed. " + + "The router did not find '%@' in its possible routes: '%@'", + [namedRoute, namedRoute, keys(router.router.recognizer.names).join("', '")]), + router.hasRoute(namedRoute)); + if (!paramsAreLoaded(resolvedParams.models)) { return; } -(function() { -/** -@module ember -@submodule ember-views -*/ + return resolvedParams; + }), -var hasElement = Ember.View.states.hasElement; -var inDOM = Ember.View.states.inDOM = Ember.create(hasElement); + queryParamsObject: null, -Ember.merge(inDOM, { - enter: function(view) { - // Register the view for event handling. This hash is used by - // Ember.EventDispatcher to dispatch incoming events. - if (!view.isVirtual) { - Ember.assert("Attempted to register a view with an id already in use: "+view.elementId, !Ember.View.views[view.elementId]); - Ember.View.views[view.elementId] = view; - } + /** + Sets the element's `href` attribute to the url for + the `LinkView`'s targeted route. - view.addBeforeObserver('elementId', function() { - throw new Ember.Error("Changing a view's elementId after creation is not allowed"); - }); - }, + If the `LinkView`'s `tagName` is changed to a value other + than `a`, this property will be ignored. - exit: function(view) { - if (!this.isVirtual) delete Ember.View.views[view.elementId]; - }, + @property href + **/ + href: computed('loadedParams', function computeLinkViewHref() { + if (get(this, 'tagName') !== 'a') { return; } - insertElement: function(view, fn) { - throw "You can't insert an element into the DOM that has already been inserted"; - } -}); + var router = get(this, 'router'), + loadedParams = get(this, 'loadedParams'); -})(); + if (!loadedParams) { + return get(this, 'loadingHref'); + } + var visibleQueryParams = {}; + + merge(visibleQueryParams, loadedParams.queryParams); + router._prepareQueryParams(loadedParams.targetRouteName, loadedParams.models, visibleQueryParams); + + var args = routeArgs(loadedParams.targetRouteName, loadedParams.models, visibleQueryParams); + var result = router.generate.apply(router, args); + return result; + }), -(function() { -/** -@module ember -@submodule ember-views -*/ - -var destroyingError = "You can't call %@ on a view being destroyed", fmt = Ember.String.fmt; - -var destroying = Ember.View.states.destroying = Ember.create(Ember.View.states._default); - -Ember.merge(destroying, { - appendChild: function() { - throw fmt(destroyingError, ['appendChild']); - }, - rerender: function() { - throw fmt(destroyingError, ['rerender']); - }, - destroyElement: function() { - throw fmt(destroyingError, ['destroyElement']); - }, - empty: function() { - throw fmt(destroyingError, ['empty']); - }, - - setElement: function() { - throw fmt(destroyingError, ["set('element', ...)"]); - }, - - renderToBufferIfNeeded: function() { - return false; - }, - - // Since element insertion is scheduled, don't do anything if - // the view has been destroyed between scheduling and execution - insertElement: Ember.K -}); + /** + The default href value to use while a link-to is loading. + Only applies when tagName is 'a' + @property loadingHref + @type String + @default # + */ + loadingHref: '#' + }); -})(); + LinkView.toString = function() { return "LinkView"; }; + + /** + The `{{link-to}}` helper renders a link to the supplied + `routeName` passing an optionally supplied model to the + route as its `model` context of the route. The block + for `{{link-to}}` becomes the innerHTML of the rendered + element: + + ```handlebars + {{#link-to 'photoGallery'}} + Great Hamster Photos + {{/link-to}} + ``` + ```html + + Great Hamster Photos + + ``` -(function() { -Ember.View.cloneStates = function(from) { - var into = {}; - - into._default = {}; - into.preRender = Ember.create(into._default); - into.destroying = Ember.create(into._default); - into.inBuffer = Ember.create(into._default); - into.hasElement = Ember.create(into._default); - into.inDOM = Ember.create(into.hasElement); - - for (var stateName in from) { - if (!from.hasOwnProperty(stateName)) { continue; } - Ember.merge(into[stateName], from[stateName]); - } + ### 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: - return into; -}; + ```handlebars + {{#link-to 'photoGallery' tagName="li"}} + Great Hamster Photos + {{/link-to}} + ``` -})(); + ```html +
  • + Great Hamster Photos +
  • + ``` + To override this option for your entire application, see + "Overriding Application-wide Defaults". + ### Disabling the `link-to` helper + By default `{{link-to}}` is enabled. + any passed value to `disabled` helper property will disable the `link-to` helper. -(function() { -var states = Ember.View.cloneStates(Ember.View.states); + static use: the `disabled` option: -/** -@module ember -@submodule ember-views -*/ + ```handlebars + {{#link-to 'photoGallery' disabled=true}} + Great Hamster Photos + {{/link-to}} + ``` -var get = Ember.get, set = Ember.set; -var forEach = Ember.EnumerableUtils.forEach; -var ViewCollection = Ember._ViewCollection; + dynamic use: the `disabledWhen` option: -/** - A `ContainerView` is an `Ember.View` subclass that implements `Ember.MutableArray` - allowing programmatic management of its child views. - - ## Setting Initial Child Views - - The initial array of child views can be set in one of two ways. You can - provide a `childViews` property at creation time that contains instance of - `Ember.View`: + ```handlebars + {{#link-to 'photoGallery' disabledWhen=controller.someProperty}} + Great Hamster Photos + {{/link-to}} + ``` - ```javascript - aContainer = Ember.ContainerView.create({ - childViews: [Ember.View.create(), Ember.View.create()] - }); - ``` + 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`. - You can also provide a list of property names whose values are instances of - `Ember.View`: + ```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; + }) + }); + ``` - ```javascript - aContainer = Ember.ContainerView.create({ - childViews: ['aView', 'bView', 'cView'], - aView: Ember.View.create(), - bView: Ember.View.create(), - cView: Ember.View.create() - }); - ``` + see "Overriding Application-wide Defaults" for more. + + ### Handling `href` + `{{link-to}}` will use your application's Router to + fill the element's `href` property with a url that + matches the path to the supplied `routeName` for your + routers's configured `Location` scheme, which defaults + to Ember.HashLocation. + + ### Handling current route + `{{link-to}}` will apply a CSS class name of 'active' + when the application's current route matches + the supplied routeName. For example, if the application's + current route is 'photoGallery.recent' the following + use of `{{link-to}}`: + + ```handlebars + {{#link-to 'photoGallery.recent'}} + Great Hamster Photos from the last week + {{/link-to}} + ``` - The two strategies can be combined: + will result in - ```javascript - aContainer = Ember.ContainerView.create({ - childViews: ['aView', Ember.View.create()], - aView: Ember.View.create() - }); - ``` + ```html +
    + Great Hamster Photos + + ``` - Each child view's rendering will be inserted into the container's rendered - HTML in the same order as its position in the `childViews` property. + The CSS class name used for active classes can be customized + for a single use of `{{link-to}}` by passing an `activeClass` + option: - ## Adding and Removing Child Views + ```handlebars + {{#link-to 'photoGallery.recent' activeClass="current-url"}} + Great Hamster Photos from the last week + {{/link-to}} + ``` - The container view implements `Ember.MutableArray` allowing programmatic management of its child views. + ```html + + Great Hamster Photos + + ``` - To remove a view, pass that view into a `removeObject` call on the container view. + To override this option for your entire application, see + "Overriding Application-wide Defaults". - Given an empty `` the following code + ### Supplying a model + An optional model argument can be used for routes whose + paths contain dynamic segments. This argument will become + the model context of the linked route: - ```javascript - aContainer = Ember.ContainerView.create({ - classNames: ['the-container'], - childViews: ['aView', 'bView'], - aView: Ember.View.create({ - template: Ember.Handlebars.compile("A") - }), - bView: Ember.View.create({ - template: Ember.Handlebars.compile("B") - }) - }); + ```javascript + App.Router.map(function() { + this.resource("photoGallery", {path: "hamster-photos/:photo_id"}); + }); + ``` - aContainer.appendTo('body'); - ``` + ```handlebars + {{#link-to 'photoGallery' aPhoto}} + {{aPhoto.title}} + {{/link-to}} + ``` - Results in the HTML + ```html + + Tomster + + ``` - ```html -
    -
    A
    -
    B
    -
    - ``` + ### 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: - Removing a view + ```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: - ```javascript - aContainer.toArray(); // [aContainer.aView, aContainer.bView] - aContainer.removeObject(aContainer.get('bView')); - aContainer.toArray(); // [aContainer.aView] - ``` + ```handlebars + {{#link-to 'photoGallery.comment' aPhoto comment}} + {{comment.body}} + {{/link-to}} + ``` - Will result in the following HTML + ```html + + A+++ would snuggle again. + + ``` - ```html -
    -
    A
    -
    - ``` + ### 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: - Similarly, adding a child view is accomplished by adding `Ember.View` instances to the - container view. + ```javascript + App.Router.map(function() { + this.resource("photoGallery", {path: "hamster-photos/:photo_id"}); + }); + ``` - Given an empty `` the following code + ```handlebars + {{#link-to 'photoGallery' aPhotoId}} + {{aPhoto.title}} + {{/link-to}} + ``` - ```javascript - aContainer = Ember.ContainerView.create({ - classNames: ['the-container'], - childViews: ['aView', 'bView'], - aView: Ember.View.create({ - template: Ember.Handlebars.compile("A") - }), - bView: Ember.View.create({ - template: Ember.Handlebars.compile("B") - }) - }); + ```html + + Tomster + + ``` - aContainer.appendTo('body'); - ``` + When transitioning into the linked route, the `model` hook will + be triggered with parameters including this passed identifier. - Results in the HTML + ### Allowing Default Action - ```html -
    -
    A
    -
    B
    -
    - ``` + 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). - Adding a view + If you need to override this behavior specify `preventDefault=false` in + your template: - ```javascript - AnotherViewClass = Ember.View.extend({ - template: Ember.Handlebars.compile("Another view") - }); + ```handlebars + {{#link-to 'photoGallery' aPhotoId preventDefault=false}} + {{aPhotoId.title}} + {{/link-to}} + ``` - aContainer.toArray(); // [aContainer.aView, aContainer.bView] - aContainer.pushObject(AnotherViewClass.create()); - aContainer.toArray(); // [aContainer.aView, aContainer.bView, ] - ``` + ### 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: - Will result in the following HTML + ```handlebars + {{#link-to aPhoto tagName='li' title='Following this link will change your life' classNames='pic sweet'}} + Uh-mazing! + {{/link-to}} + ``` - ```html -
    -
    A
    -
    B
    -
    Another view
    -
    - ``` + 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`. - ## Templates and Layout + ### 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: - A `template`, `templateName`, `defaultTemplate`, `layout`, `layoutName` or - `defaultLayout` property on a container view will not result in the template - or layout being rendered. The HTML contents of a `Ember.ContainerView`'s DOM - representation will only be the rendered HTML of its child views. + ``` javascript + Ember.LinkView.reopen({ + activeClass: "is-active", + tagName: 'li' + }) + ``` - @class ContainerView - @namespace Ember - @extends Ember.View -*/ -Ember.ContainerView = Ember.View.extend(Ember.MutableArray, { - states: states, + It is also possible to override the default event in + this manner: - init: function() { - this._super(); + ``` javascript + Ember.LinkView.reopen({ + eventName: 'customEventName' + }); + ``` - var childViews = get(this, 'childViews'); + @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], + params = slice.call(arguments, 0, -1), + hash = options.hash; - // redefine view's childViews property that was obliterated - Ember.defineProperty(this, 'childViews', Ember.View.childViewsProperty); + Ember.assert("You must provide one or more parameters to the link-to helper.", params.length); - var _childViews = this._childViews; + if (params[params.length - 1] instanceof QueryParams) { + hash.queryParamsObject = params.pop(); + } - forEach(childViews, function(viewName, idx) { - var view; + hash.disabledBinding = hash.disabledWhen; - if ('string' === typeof viewName) { - view = get(this, viewName); - view = this.createChildView(view); - set(this, viewName, view); - } else { - view = this.createChildView(viewName); + if (!options.fn) { + var linkTitle = params.shift(); + var linkType = options.types.shift(); + var context = this; + if (linkType === 'ID') { + options.linkTextPath = linkTitle; + options.fn = function() { + return EmberHandlebars.getEscaped(context, linkTitle, options); + }; + } else { + options.fn = function() { + return linkTitle; + }; + } } - _childViews[idx] = view; - }, this); + hash.parameters = { + context: this, + options: options, + params: params + }; - var currentView = get(this, 'currentView'); - if (currentView) { - if (!_childViews.length) { _childViews = this._childViews = this._childViews.slice(); } - _childViews.push(this.createChildView(currentView)); + options.helperName = options.helperName || 'link-to'; + + return viewHelper.call(this, LinkView, options); } - }, - replace: function(idx, removedCount, addedViews) { - var addedCount = addedViews ? get(addedViews, 'length') : 0; - var self = this; - Ember.assert("You can't add a child to a container that is already a child of another view", Ember.A(addedViews).every(function(item) { return !get(item, '_parentView') || get(item, '_parentView') === self; })); - this.arrayContentWillChange(idx, removedCount, addedCount); - this.childViewsWillChange(this._childViews, idx, removedCount); + + EmberHandlebars.registerHelper('query-params', 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); - if (addedCount === 0) { - this._childViews.splice(idx, removedCount) ; - } else { - var args = [idx, removedCount].concat(addedViews); - if (addedViews.length && !this._childViews.length) { this._childViews = this._childViews.slice(); } - this._childViews.splice.apply(this._childViews, args); - } + return QueryParams.create({ + values: options.hash, + types: options.hashTypes + }); + }); + - this.arrayContentDidChange(idx, removedCount, addedCount); - this.childViewsDidChange(this._childViews, idx, removedCount, addedCount); + /** + See [link-to](/api/classes/Ember.Handlebars.helpers.html#method_link-to) + + @method linkTo + @for Ember.Handlebars.helpers + @deprecated + @param {String} routeName + @param {Object} [context]* + @return {String} HTML string + */ + function deprecatedLinkToHelper() { + Ember.warn("The 'linkTo' view helper is deprecated in favor of 'link-to'"); + return linkToHelper.apply(this, arguments); + } - return this; - }, + function getResolvedQueryParams(linkView, targetRouteName) { + var helperParameters = linkView.parameters, + queryParamsObject = linkView.queryParamsObject, + resolvedQueryParams = {}; - objectAt: function(idx) { - return this._childViews[idx]; - }, + if (!queryParamsObject) { return resolvedQueryParams; } + var rawParams = queryParamsObject.values; - length: Ember.computed(function () { - return this._childViews.length; - }).volatile(), + for (var key in rawParams) { + if (!rawParams.hasOwnProperty(key)) { continue; } - /** - Instructs each child view to render to the passed render buffer. + var value = rawParams[key], + type = queryParamsObject.types[key]; - @private - @method render - @param {Ember.RenderBuffer} buffer the buffer to render to - */ - render: function(buffer) { - this.forEachChildView(function(view) { - view.renderToBuffer(buffer); - }); - }, - - instrumentName: 'container', - - /** - When a child view is removed, destroy its element so that - it is removed from the DOM. - - The array observer that triggers this action is set up in the - `renderToBuffer` method. - - @private - @method childViewsWillChange - @param {Ember.Array} views the child views array before mutation - @param {Number} start the start position of the mutation - @param {Number} removed the number of child views removed - **/ - childViewsWillChange: function(views, start, removed) { - this.propertyWillChange('childViews'); - - if (removed > 0) { - var changedViews = views.slice(start, start+removed); - // transition to preRender before clearing parentView - this.currentState.childViewsWillChange(this, views, start, removed); - this.initializeViews(changedViews, null, null); - } - }, - - removeChild: function(child) { - this.removeObject(child); - return this; - }, - - /** - When a child view is added, make sure the DOM gets updated appropriately. - - If the view has already rendered an element, we tell the child view to - create an element and insert it into the DOM. If the enclosing container - view has already written to a buffer, but not yet converted that buffer - into an element, we insert the string representation of the child into the - appropriate place in the buffer. - - @private - @method childViewsDidChange - @param {Ember.Array} views the array of child views afte the mutation has occurred - @param {Number} start the start position of the mutation - @param {Number} removed the number of child views removed - @param {Number} the number of child views added - */ - childViewsDidChange: function(views, start, removed, added) { - if (added > 0) { - var changedViews = views.slice(start, start+added); - this.initializeViews(changedViews, this, get(this, 'templateData')); - this.currentState.childViewsDidChange(this, views, start, added); - } - this.propertyDidChange('childViews'); - }, - - initializeViews: function(views, parentView, templateData) { - forEach(views, function(view) { - set(view, '_parentView', parentView); - - if (!view.container && parentView) { - set(view, 'container', parentView.container); - } - - if (!get(view, 'templateData')) { - set(view, 'templateData', templateData); + if (type === 'ID') { + var normalizedPath = getNormalizedPath(value, helperParameters); + value = EmberHandlebars.get(normalizedPath.root, normalizedPath.path, helperParameters.options); + } + resolvedQueryParams[key] = value; } - }); - }, - - currentView: null, + return resolvedQueryParams; + } - _currentViewWillChange: Ember.beforeObserver('currentView', function() { - var currentView = get(this, 'currentView'); - if (currentView) { - currentView.destroy(); + function getNormalizedPath(path, helperParameters) { + return EmberHandlebars.normalizePath(helperParameters.context, path, helperParameters.options.data); } - }), - _currentViewDidChange: Ember.observer('currentView', function() { - var currentView = get(this, 'currentView'); - if (currentView) { - Ember.assert("You tried to set a current view that already has a parent. Make sure you don't have multiple outlets in the same view.", !get(currentView, '_parentView')); - this.pushObject(currentView); + 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; } - }), - _ensureChildrenAreInDOM: function () { - this.currentState.ensureChildrenAreInDOM(this); - } -}); + 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; + } -Ember.merge(states._default, { - childViewsWillChange: Ember.K, - childViewsDidChange: Ember.K, - ensureChildrenAreInDOM: Ember.K -}); + __exports__.LinkView = LinkView; + __exports__.deprecatedLinkToHelper = deprecatedLinkToHelper; + __exports__.linkToHelper = linkToHelper; + }); +define("ember-routing-handlebars/helpers/outlet", + ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-runtime/system/lazy_load","ember-views/views/container_view","ember-handlebars/views/metamorph_view","ember-handlebars/helpers/view","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // assert + var get = __dependency2__.get; + var set = __dependency3__.set; + var onLoad = __dependency4__.onLoad; + var ContainerView = __dependency5__["default"]; + var _Metamorph = __dependency6__._Metamorph; + var viewHelper = __dependency7__.viewHelper; -Ember.merge(states.inBuffer, { - childViewsDidChange: function(parentView, views, start, added) { - throw new Ember.Error('You cannot modify child views while in the inBuffer state'); - } -}); + /** + @module ember + @submodule ember-routing + */ -Ember.merge(states.hasElement, { - childViewsWillChange: function(view, views, start, removed) { - for (var i=start; i` and the following code: + return viewHelper.call(this, viewClass, options); + } - ```javascript - someItemsView = Ember.CollectionView.create({ - classNames: ['a-collection'], - content: ['A','B','C'], - itemViewClass: Ember.View.extend({ - template: Ember.Handlebars.compile("the letter: {{view.content}}") - }) + __exports__.outletHelper = outletHelper; }); +define("ember-routing-handlebars/helpers/render", + ["ember-metal/core","ember-metal/error","ember-metal/property_get","ember-metal/property_set","ember-runtime/system/string","ember-routing/system/generate_controller","ember-handlebars/ext","ember-handlebars/helpers/view","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // assert, deprecate + var EmberError = __dependency2__["default"]; + var get = __dependency3__.get; + var set = __dependency4__.set; + var camelize = __dependency5__.camelize; + var generateControllerFactory = __dependency6__.generateControllerFactory; + var generateController = __dependency6__["default"]; + var handlebarsGet = __dependency7__.handlebarsGet; + var viewHelper = __dependency8__.viewHelper; - someItemsView.appendTo('body'); - ``` - Will result in the following HTML structure + /** + @module ember + @submodule ember-routing + */ - ```html -
    -
    the letter: A
    -
    the letter: B
    -
    the letter: C
    -
    - ``` + /** + 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). - ## Automatic matching of parent/child tagNames + If a view class with the same name exists, the view class also will be used. - Setting the `tagName` property of a `CollectionView` to any of - "ul", "ol", "table", "thead", "tbody", "tfoot", "tr", or "select" will result - in the item views receiving an appropriately matched `tagName` property. + 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. - Given an empty `` and the following code: + Example: - ```javascript - anUnorderedListView = Ember.CollectionView.create({ - tagName: 'ul', - content: ['A','B','C'], - itemViewClass: Ember.View.extend({ - template: Ember.Handlebars.compile("the letter: {{view.content}}") - }) - }); + ```javascript + App.NavigationController = Ember.Controller.extend({ + who: "world" + }); + ``` + + ```handlebars + + Hello, {{who}}. + ``` + + ```handelbars + +

    My great app

    + {{render "navigation"}} + ``` - anUnorderedListView.appendTo('body'); - ``` + ```html +

    My great app

    +
    + Hello, world. +
    + ``` - Will result in the following HTML structure + Optionally you may provide a second argument: a property path + that will be bound to the `model` property of the controller. - ```html -
      -
    • the letter: A
    • -
    • the letter: B
    • -
    • the letter: C
    • -
    - ``` + 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. - Additional `tagName` pairs can be provided by adding to - `Ember.CollectionView.CONTAINER_MAP ` + For example if you had this `author` template. - ```javascript - Ember.CollectionView.CONTAINER_MAP['article'] = 'section' - ``` + ```handlebars +
    + Written by {{firstName}} {{lastName}}. + Total Posts: {{postCount}} +
    + ``` - ## Programmatic creation of child views + You could render it inside the `post` template using the `render` helper. - For cases where additional customization beyond the use of a single - `itemViewClass` or `tagName` matching is required CollectionView's - `createChildView` method can be overidden: + ```handlebars +
    +

    {{title}}

    +
    {{body}}
    + {{render "author" author}} +
    + ``` - ```javascript - CustomCollectionView = Ember.CollectionView.extend({ - createChildView: function(viewClass, attrs) { - if (attrs.content.kind == 'album') { - viewClass = App.AlbumView; + @method render + @for Ember.Handlebars.helpers + @param {String} name + @param {Object?} contextString + @param {Hash} options + @return {String} HTML string + */ + __exports__["default"] = function renderHelper(name, contextString, options) { + var length = arguments.length; + + var contextProvided = length === 3, + container, router, controller, view, context, lookupOptions; + + container = (options || contextString).data.keywords.controller.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 + context = handlebarsGet(options.contexts[1], contextString, options); } else { - viewClass = App.SongView; + throw new EmberError("You must pass a templateName to render"); } - return this._super(viewClass, attrs); - } - }); - ``` - - ## Empty View - - You can provide an `Ember.View` subclass to the `Ember.CollectionView` - instance as its `emptyView` property. If the `content` property of a - `CollectionView` is set to `null` or an empty array, an instance of this view - will be the `CollectionView`s only child. - - ```javascript - aListWithNothing = Ember.CollectionView.create({ - classNames: ['nothing'] - content: null, - emptyView: Ember.View.extend({ - template: Ember.Handlebars.compile("The collection is empty") - }) - }); - aListWithNothing.appendTo('body'); - ``` + Ember.deprecate("Using a quoteless parameter with {{render}} is deprecated. Please update to quoted usage '{{render \"" + name + "\"}}.", options.types[0] !== 'ID'); - Will result in the following HTML structure - - ```html -
    -
    - The collection is empty -
    -
    - ``` - - ## Adding and Removing items - - The `childViews` property of a `CollectionView` should not be directly - manipulated. Instead, add, remove, replace items from its `content` property. - This will trigger appropriate changes to its rendered HTML. - - - @class CollectionView - @namespace Ember - @extends Ember.ContainerView - @since Ember 0.9 -*/ -Ember.CollectionView = Ember.ContainerView.extend({ - - /** - A list of items to be displayed by the `Ember.CollectionView`. - - @property content - @type Ember.Array - @default null - */ - content: null, - - /** - This provides metadata about what kind of empty view class this - collection would like if it is being instantiated from another - system (like Handlebars) - - @private - @property emptyViewClass - */ - emptyViewClass: Ember.View, - - /** - An optional view to display if content is set to an empty array. - - @property emptyView - @type Ember.View - @default null - */ - emptyView: null, - - /** - @property itemViewClass - @type Ember.View - @default Ember.View - */ - itemViewClass: Ember.View, - - /** - Setup a CollectionView - - @method init - */ - init: function() { - var ret = this._super(); - this._contentDidChange(); - return ret; - }, - - /** - Invoked when the content property is about to change. Notifies observers that the - entire array content will change. - - @private - @method _contentWillChange - */ - _contentWillChange: Ember.beforeObserver('content', function() { - var content = this.get('content'); + // # legacy namespace + name = name.replace(/\//g, '.'); + // \ legacy slash as namespace support - if (content) { content.removeArrayObserver(this); } - var len = content ? get(content, 'length') : 0; - this.arrayWillChange(content, 0, len); - }), - /** - Check to make sure that the content has changed, and if so, - update the children directly. This is always scheduled - asynchronously, to allow the element to be created before - bindings have synchronized and vice versa. - - @private - @method _contentDidChange - */ - _contentDidChange: Ember.observer('content', function() { - var content = get(this, 'content'); + view = container.lookup('view:' + name) || container.lookup('view:default'); - if (content) { - this._assertArrayLike(content); - content.addArrayObserver(this); - } + // provide controller override + var controllerName = options.hash.controller || name; + var controllerFullName = 'controller:' + controllerName; - var len = content ? get(content, 'length') : 0; - this.arrayDidChange(content, 0, null, len); - }), + if (options.hash.controller) { + Ember.assert("The controller name you supplied '" + controllerName + "' did not resolve to a controller.", container.has(controllerFullName)); + } - /** - Ensure that the content implements Ember.Array + var parentController = options.data.keywords.controller; - @private - @method _assertArrayLike - */ - _assertArrayLike: function(content) { - Ember.assert(fmt("an Ember.CollectionView's content must implement Ember.Array. You passed %@", [content]), Ember.Array.detect(content)); - }, + // choose name + if (length > 2) { + var factory = container.lookupFactory(controllerFullName) || + generateControllerFactory(container, controllerName, context); - /** - Removes the content and content observers. - - @method destroy - */ - destroy: function() { - if (!this._super()) { return; } + controller = factory.create({ + model: context, + parentController: parentController, + target: parentController + }); - var content = get(this, 'content'); - if (content) { content.removeArrayObserver(this); } + view.one('willDestroyElement', function() { + controller.destroy(); + }); + } else { + controller = container.lookup(controllerFullName) || + generateController(container, controllerName); - if (this._createdEmptyView) { - this._createdEmptyView.destroy(); - } + controller.setProperties({ + target: parentController, + parentController: parentController + }); + } - return this; - }, + var root = options.contexts[1]; - /** - Called when a mutation to the underlying content array will occur. + if (root) { + view.registerObserver(root, contextString, function() { + controller.set('model', handlebarsGet(root, contextString, options)); + }); + } - This method will remove any views that are no longer in the underlying - content array. + options.hash.viewName = camelize(name); - Invokes whenever the content array itself will change. + 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); - @method arrayWillChange - @param {Array} content the managed collection of objects - @param {Number} start the index at which the changes will occurr - @param {Number} removed number of object to be removed from content - */ - arrayWillChange: function(content, start, removedCount) { - // If the contents were empty before and this template collection has an - // empty view remove it now. - var emptyView = get(this, 'emptyView'); - if (emptyView && emptyView instanceof Ember.View) { - emptyView.removeFromParent(); - } + options.hash.controller = controller; - // Loop through child views that correspond with the removed items. - // Note that we loop from the end of the array to the beginning because - // we are mutating it as we go. - var childViews = this._childViews, childView, idx, len; + if (router && !context) { + router._connectActiveView(name, view); + } - len = this._childViews.length; + options.helperName = options.helperName || ('render "' + name + '"'); - var removingAll = removedCount === len; + viewHelper.call(this, view, options); + } + }); +define("ember-routing-handlebars/helpers/shared", + ["ember-metal/property_get","ember-metal/array","ember-runtime/mixins/controller","ember-handlebars/ext","ember-metal/utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { + "use strict"; + var get = __dependency1__.get; + var map = __dependency2__.map; + var ControllerMixin = __dependency3__["default"]; + var handlebarsResolve = __dependency4__.resolveParams; + var handlebarsGet = __dependency4__.handlebarsGet; + var typeOf = __dependency5__.typeOf; + var get = __dependency1__.get; - if (removingAll) { - this.currentState.empty(this); - this.invokeRecursively(function(view) { - view.removedFromDOM = true; - }, false); + function routeArgs(targetRouteName, models, queryParams) { + var args = []; + if (typeOf(targetRouteName) === 'string') { + args.push('' + targetRouteName); + } + args.push.apply(args, models); + args.push({ queryParams: queryParams }); + return args; } - for (idx = start + removedCount - 1; idx >= start; idx--) { - childView = childViews[idx]; - childView.destroy(); + __exports__.routeArgs = routeArgs;function getActiveTargetName(router) { + var handlerInfos = router.activeTransition ? + router.activeTransition.state.handlerInfos : + router.state.handlerInfos; + return handlerInfos[handlerInfos.length - 1].name; } - }, - /** - Called when a mutation to the underlying content array occurs. + __exports__.getActiveTargetName = getActiveTargetName;function resolveParams(context, params, options) { + return map.call(resolvePaths(context, params, options), function(path, i) { + if (null === path) { + // Param was string/number, not a path, so just return raw string/number. + return params[i]; + } else { + return handlebarsGet(context, path, options); + } + }); + } - This method will replay that mutation against the views that compose the - `Ember.CollectionView`, ensuring that the view reflects the model. + __exports__.resolveParams = resolveParams;function stashParamNames(router, handlerInfos) { + if (handlerInfos._namesStashed) { return; } - This array observer is added in `contentDidChange`. + // 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; - @method arrayDidChange - @param {Array} content the managed collection of objects - @param {Number} start the index at which the changes occurred - @param {Number} removed number of object removed from content - @param {Number} added number of object added to content - */ - arrayDidChange: function(content, start, removed, added) { - var addedViews = [], view, item, idx, len, itemViewClass, - emptyView; + for (var i = 0, len = handlerInfos.length; i < len; ++i) { + var handlerInfo = handlerInfos[i]; + var names = recogHandlers[i].names; - len = content ? get(content, 'length') : 0; + if (names.length) { + dynamicParent = handlerInfo; + } - if (len) { - itemViewClass = get(this, 'itemViewClass'); + handlerInfo._names = names; - if ('string' === typeof itemViewClass) { - itemViewClass = get(itemViewClass) || itemViewClass; + var route = handlerInfo.handler; + route._stashNames(handlerInfo, dynamicParent); } - Ember.assert(fmt("itemViewClass must be a subclass of Ember.View, not %@", - [itemViewClass]), - 'string' === typeof itemViewClass || Ember.View.detect(itemViewClass)); - - for (idx = start; idx < start+added; idx++) { - item = content.objectAt(idx); - - view = this.createChildView(itemViewClass, { - content: item, - contentIndex: idx - }); - - addedViews.push(view); - } - } else { - emptyView = get(this, 'emptyView'); + handlerInfos._namesStashed = true; + } - if (!emptyView) { return; } + __exports__.stashParamNames = stashParamNames;function resolvePaths(context, params, options) { + var resolved = handlebarsResolve(context, params, options), + types = options.types; - if ('string' === typeof emptyView) { - emptyView = get(emptyView) || emptyView; - } + return map.call(resolved, function(object, i) { + if (types[i] === 'ID') { + return unwrap(object, params[i]); + } else { + return null; + } + }); - emptyView = this.createChildView(emptyView); - addedViews.push(emptyView); - set(this, 'emptyView', emptyView); + function unwrap(object, path) { + if (path === 'controller') { return path; } - if (Ember.CoreView.detect(emptyView)) { - this._createdEmptyView = emptyView; + if (ControllerMixin.detect(object)) { + return unwrap(get(object, 'model'), path ? path + '.model' : 'model'); + } else { + return path; + } } } - this.replace(start, 0, addedViews); - }, + __exports__.resolvePaths = resolvePaths; + }); +define("ember-routing", + ["ember-handlebars","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__, __dependency15__, __exports__) { + "use strict"; + /** + Ember Routing - /** - Instantiates a view to be added to the childViews array during view - initialization. You generally will not call this method directly unless - you are overriding `createChildViews()`. Note that this method will - automatically configure the correct settings on the new view instance to - act as a child of the parent. + @module ember + @submodule ember-routing + @requires ember-views + */ - The tag name for the view will be set to the tagName of the viewClass - passed in. + var EmberHandlebars = __dependency1__["default"]; + var Ember = __dependency2__["default"]; + + // ES6TODO: Cleanup modules with side-effects below + + var EmberLocation = __dependency6__["default"]; + var NoneLocation = __dependency7__["default"]; + var HashLocation = __dependency8__["default"]; + var HistoryLocation = __dependency9__["default"]; + var AutoLocation = __dependency10__["default"]; + + var generateControllerFactory = __dependency11__.generateControllerFactory; + var generateController = __dependency11__["default"]; + var controllerFor = __dependency12__["default"]; + var RouterDSL = __dependency13__["default"]; + var Router = __dependency14__["default"]; + var Route = __dependency15__["default"]; + + Ember.Location = EmberLocation; + Ember.AutoLocation = AutoLocation; + Ember.HashLocation = HashLocation; + Ember.HistoryLocation = HistoryLocation; + Ember.NoneLocation = NoneLocation; + + Ember.controllerFor = controllerFor; + Ember.generateControllerFactory = generateControllerFactory; + Ember.generateController = generateController; + Ember.RouterDSL = RouterDSL; + Ember.Router = Router; + Ember.Route = Route; + + __exports__["default"] = Ember; + }); +define("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-metal/enumerable_utils","ember-runtime/mixins/controller","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __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 map = __dependency7__.map; + + var ControllerMixin = __dependency8__["default"]; - @method createChildView - @param {Class} viewClass - @param {Hash} [attrs] Attributes to add - @return {Ember.View} new instance - */ - createChildView: function(view, attrs) { - view = this._super(view, attrs); + /** + @module ember + @submodule ember-routing + */ - var itemTagName = get(view, 'tagName'); + ControllerMixin.reopen({ + /** + Transition the application into another route. The route may + be either a single route or route path: - if (itemTagName === null || itemTagName === undefined) { - itemTagName = Ember.CollectionView.CONTAINER_MAP[get(this, 'tagName')]; - set(view, 'tagName', itemTagName); - } + ```javascript + aController.transitionToRoute('blogPosts'); + aController.transitionToRoute('blogPosts.recentEntries'); + ``` - return view; - } -}); - -/** - A map of parent tags to their default child tags. You can add - additional parent tags if you want collection views that use - a particular parent tag to default to a child tag. - - @property CONTAINER_MAP - @type Hash - @static - @final -*/ -Ember.CollectionView.CONTAINER_MAP = { - ul: 'li', - ol: 'li', - table: 'tr', - thead: 'tr', - tbody: 'tr', - tfoot: 'tr', - tr: 'td', - select: 'option' -}; + Optionally supply a model for the route in question. The model + will be serialized into the URL using the `serialize` hook of + the route: -})(); + ```javascript + aController.transitionToRoute('blogPost', aPost); + ``` + If a literal is passed (such as a number or a string), it will + be treated as an identifier instead. In this case, the `model` + hook of the route will be triggered: + ```javascript + aController.transitionToRoute('blogPost', 1); + ``` -(function() { -var get = Ember.get; - -/** - 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 -*/ -Ember.ComponentTemplateDeprecation = Ember.Mixin.create({ - /** - @private - - Moves `templateName` to `layoutName` and `template` to `layout` at extend - time if a layout is not also specified. - - 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 willMergeMixin - */ - 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); + Multiple models will be applied last to first recursively up the + resource tree. - var deprecatedProperty, replacementProperty, - layoutSpecified = (props.layoutName || props.layout || get(this, 'layoutName')); + ```javascript + App.Router.map(function() { + this.resource('blogPost', {path:':blogPostId'}, function(){ + this.resource('blogComment', {path: ':blogCommentId'}); + }); + }); - if (props.templateName && !layoutSpecified) { - deprecatedProperty = 'templateName'; - replacementProperty = 'layoutName'; + aController.transitionToRoute('blogComment', aPost, aComment); + aController.transitionToRoute('blogComment', 1, 13); + ``` - props.layoutName = props.templateName; - delete props['templateName']; - } + It is also possible to pass a URL (a string that starts with a + `/`). This is intended for testing and debugging purposes and + should rarely be used in production code. - if (props.template && !layoutSpecified) { - deprecatedProperty = 'template'; - replacementProperty = 'layout'; + ```javascript + aController.transitionToRoute('/'); + aController.transitionToRoute('/blog/post/1/comment/13'); + ``` - props.layout = props.template; - delete props['template']; - } + See also [replaceRoute](/api/classes/Ember.ControllerMixin.html#method_replaceRoute). - if (deprecatedProperty) { - Ember.deprecate('Do not specify ' + deprecatedProperty + ' on a Component, use ' + replacementProperty + ' instead.', false); - } - } -}); + @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 + */ + transitionToRoute: function() { + // target may be either another controller or a router + var target = get(this, 'target'); + var method = target.transitionToRoute || target.transitionTo; + return method.apply(target, arguments); + }, + /** + @deprecated + @for Ember.ControllerMixin + @method transitionTo + */ + transitionTo: function() { + Ember.deprecate("transitionTo is deprecated. Please use transitionToRoute."); + return this.transitionToRoute.apply(this, arguments); + }, -})(); + /** + Transition into another route while replacing the current URL, if possible. + This will replace the current history entry instead of adding a new one. + Beside that, it is identical to `transitionToRoute` in all other respects. + ```javascript + aController.replaceRoute('blogPosts'); + aController.replaceRoute('blogPosts.recentEntries'); + ``` + Optionally supply a model for the route in question. The model + will be serialized into the URL using the `serialize` hook of + the route: -(function() { -var get = Ember.get, set = Ember.set, isNone = Ember.isNone, - a_slice = Array.prototype.slice; - - -/** -@module ember -@submodule ember-views -*/ - -/** - An `Ember.Component` is a view that is completely - isolated. Property access in its templates go - to the view object and actions are targeted at - the view object. There is no access to the - surrounding context or outer controller; all - contextual information must be passed in. - - The easiest way to create an `Ember.Component` is via - a template. If you name a template - `components/my-foo`, you will be able to use - `{{my-foo}}` in other templates, which will make - an instance of the isolated component. - - ```handlebars - {{app-profile person=currentUser}} - ``` - - ```handlebars - -

    {{person.title}}

    - -

    {{person.signature}}

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

    Admin mode

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

    {{person.title}}

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

    {{person.title}}

    - {{yield}} - - - ``` - - Components must have a `-` in their name to avoid - conflicts with built-in controls that wrap HTML - elements. This is consistent with the same - requirement in web components. - - @class Component - @namespace Ember - @extends Ember.View -*/ -Ember.Component = Ember.View.extend(Ember.TargetActionSupport, Ember.ComponentTemplateDeprecation, { - init: function() { - this._super(); - set(this, 'context', this); - set(this, 'controller', this); - }, - - defaultLayout: function(context, options){ - Ember.Handlebars.helpers['yield'].call(context, options); - }, - - /** - A components template property is set by passing a block - during its invocation. It is executed within the parent context. - - Example: - - ```handlebars - {{#my-component}} - // something that is run in the context - // of the parent context - {{/my-component}} - ``` - - Specifying a template directly to a component is deprecated without - also specifying the layout property. - - @deprecated - @property template - */ - template: Ember.computed(function(key, value) { - if (value !== undefined) { return value; } - - var templateName = get(this, 'templateName'), - template = this.templateForName(templateName, 'template'); - - Ember.assert("You specified the templateName " + templateName + " for " + this + ", but it did not exist.", !templateName || template); - - return template || get(this, 'defaultTemplate'); - }).property('templateName'), - - /** - Specifying a components `templateName` is deprecated without also - providing the `layout` or `layoutName` properties. - - @deprecated - @property templateName - */ - templateName: null, - - // during render, isolate keywords - cloneKeywords: function() { - return { - view: this, - controller: this - }; - }, + ```javascript + aController.replaceRoute('blogPost', aPost); + ``` - _yield: function(context, options) { - var view = options.data.view, - parentView = this._parentView, - template = get(this, 'template'); + If a literal is passed (such as a number or a string), it will + be treated as an identifier instead. In this case, the `model` + hook of the route will be triggered: - if (template) { - Ember.assert("A Component must have a parent view in order to yield.", parentView); + ```javascript + aController.replaceRoute('blogPost', 1); + ``` - view.appendChild(Ember.View, { - isVirtual: true, - tagName: '', - _contextView: parentView, - template: template, - context: get(parentView, 'context'), - controller: get(parentView, 'controller'), - templateData: { keywords: parentView.cloneKeywords() } - }); - } - }, + Multiple models will be applied last to first recursively up the + resource tree. - /** - If the component is currently inserted into the DOM of a parent view, this - property will point to the controller of the parent view. + ```javascript + App.Router.map(function() { + this.resource('blogPost', {path:':blogPostId'}, function(){ + this.resource('blogComment', {path: ':blogCommentId'}); + }); + }); - @property targetObject - @type Ember.Controller - @default null - */ - targetObject: Ember.computed(function(key) { - var parentView = get(this, '_parentView'); - return parentView ? get(parentView, 'controller') : null; - }).property('_parentView'), + aController.replaceRoute('blogComment', aPost, aComment); + aController.replaceRoute('blogComment', 1, 13); + ``` - /** - Triggers a named action on the controller context where the component is used if - this controller has registered for notifications of the action. + 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. - For example a component for playing or pausing music may translate click events - into action notifications of "play" or "stop" depending on some internal state - of the component: + ```javascript + aController.replaceRoute('/'); + aController.replaceRoute('/blog/post/1/comment/13'); + ``` + @param {String} name the name of the route or a URL + @param {...Object} models the model(s) or identifier(s) to be used + while transitioning to the route. + @for Ember.ControllerMixin + @method replaceRoute + */ + replaceRoute: function() { + // target may be either another controller or a router + var target = get(this, 'target'); + var method = target.replaceRoute || target.replaceWith; + return method.apply(target, arguments); + }, - ```javascript - App.PlayButtonComponent = Ember.Component.extend({ - click: function(){ - if (this.get('isPlaying')) { - this.sendAction('play'); - } else { - this.sendAction('stop'); - } + /** + @deprecated + @for Ember.ControllerMixin + @method replaceWith + */ + replaceWith: function() { + Ember.deprecate("replaceWith is deprecated. Please use replaceRoute."); + return this.replaceRoute.apply(this, arguments); } }); - ``` - When used inside a template these component actions are configured to - trigger actions in the outer application context: + var ALL_PERIODS_REGEX = /\./g; - ```handlebars - {{! application.hbs }} - {{play-button play="musicStarted" stop="musicStopped"}} - ``` + + ControllerMixin.reopen({ + init: function() { + this._super.apply(this, arguments); + listenForQueryParamChanges(this); + }, - When the component receives a browser `click` event it translate this - interaction into application-specific semantics ("play" or "stop") and - triggers the specified action name on the controller for the template - where the component is used: + concatenatedProperties: ['queryParams', '_pCacheMeta'], + queryParams: null, + _qpDelegate: null, + _normalizedQueryParams: computed(function() { + var m = meta(this); + if (m.proto !== this) { + return get(m.proto, '_normalizedQueryParams'); + } - ```javascript - App.ApplicationController = Ember.Controller.extend({ - actions: { - musicStarted: function(){ - // called when the play button is clicked - // and the music started playing - }, - musicStopped: function(){ - // called when the play button is clicked - // and the music stopped playing - } - } - }); - ``` + var queryParams = get(this, 'queryParams'); + if (queryParams._qpMap) { + return queryParams._qpMap; + } - If no action name is passed to `sendAction` a default name of "action" - is assumed. + var qpMap = queryParams._qpMap = {}; - ```javascript - App.NextButtonComponent = Ember.Component.extend({ - click: function(){ - this.sendAction(); - } - }); - ``` + for (var i = 0, len = queryParams.length; i < len; ++i) { + accumulateQueryParamDescriptors(queryParams[i], qpMap); + } - ```handlebars - {{! application.hbs }} - {{next-button action="playNextSongInAlbum"}} - ``` + return qpMap; + }), - ```javascript - App.ApplicationController = Ember.Controller.extend({ - actions: { - playNextSongInAlbum: function(){ - ... - } - } - }); - ``` + _cacheMeta: computed(function() { + var m = meta(this); + if (m.proto !== this) { + return get(m.proto, '_cacheMeta'); + } - @method sendAction - @param [action] {String} the action to trigger - @param [context] {*} a context to send with the action - */ - sendAction: function(action) { - var actionName, - contexts = a_slice.call(arguments, 1); - - // Send the default action - if (action === undefined) { - actionName = get(this, 'action'); - Ember.assert("The default action was triggered on the component " + this.toString() + - ", but the action name (" + actionName + ") was not a string.", - isNone(actionName) || typeof actionName === 'string'); - } else { - actionName = get(this, action); - Ember.assert("The " + action + " action was triggered on the component " + - this.toString() + ", but the action name (" + actionName + - ") was not a string.", - isNone(actionName) || typeof actionName === 'string'); - } + var cacheMeta = {}; + var qpMap = get(this, '_normalizedQueryParams'); + for (var prop in qpMap) { + if (!qpMap.hasOwnProperty(prop)) { continue; } - // If no action name for that action could be found, just abort. - if (actionName === undefined) { return; } + var qp = qpMap[prop]; + var scope = qp.scope; + var parts; - this.triggerAction({ - action: actionName, - actionContext: contexts - }); - } -}); + if (scope === 'controller') { + parts = []; + } -})(); + cacheMeta[prop] = { + parts: parts, // provided by route if 'model' scope + values: null, // provided by route + scope: scope, + prefix: "", + def: get(this, prop) + }; + } + return cacheMeta; + }), + _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; -(function() { + 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); + } + } + }, + _qpChanged: function(controller, _prop) { + var prop = _prop.substr(0, _prop.length-3); + var cacheMeta = get(controller, '_cacheMeta'); + var propCache = cacheMeta[prop]; + var cacheKey = controller._calculateCacheKey(propCache.prefix || "", propCache.parts, propCache.values); + var value = get(controller, prop); + + // 1. Update model-dep cache + var cache = this._bucketCache; + if (cache) { + controller._bucketCache.stash(cacheKey, prop, value); + } + // 2. Notify a delegate (e.g. to fire a qp transition) + var delegate = controller._qpDelegate; + if (delegate) { + delegate(controller, prop); + } + }, -(function() { -/** -`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. - -Note: In normal Ember usage, the `{{action}}` helper is usually the best -choice. This mixin is most often useful when you are doing more complex -event handling in custom View subclasses. - -For example: - -```javascript -App.SaveButtonView = Ember.View.extend(Ember.ViewTargetActionSupport, { - action: 'save', - click: function() { - this.triggerAction(); // Sends the `save` action, along with the current context - // to the current controller - } -}); -``` - -The `action` can be provided as properties of an optional object argument -to `triggerAction` as well. - -```javascript -App.SaveButtonView = Ember.View.extend(Ember.ViewTargetActionSupport, { - click: function() { - this.triggerAction({ - action: 'save' - }); // Sends the `save` action, along with the current context - // to the current controller - } -}); -``` - -@class ViewTargetActionSupport -@namespace Ember -@extends Ember.TargetActionSupport -*/ -Ember.ViewTargetActionSupport = Ember.Mixin.create(Ember.TargetActionSupport, { - /** - @property target - */ - target: Ember.computed.alias('controller'), - /** - @property actionContext - */ - actionContext: Ember.computed.alias('context') -}); + _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, '-'); + } + }); + -})(); + function accumulateQueryParamDescriptors(_desc, accum) { + var desc = _desc, tmp; + if (typeOf(desc) === 'string') { + tmp = {}; + tmp[desc] = { as: null }; + desc = tmp; + } + for (var key in desc) { + if (!desc.hasOwnProperty(key)) { return; } + var singleDesc = desc[key]; + if (typeOf(singleDesc) === 'string') { + singleDesc = { as: singleDesc }; + } -(function() { + tmp = accum[key] || { as: null, scope: 'model' }; + merge(tmp, singleDesc); -})(); + accum[key] = tmp; + } + } + function listenForQueryParamChanges(controller) { + var qpMap = get(controller, '_normalizedQueryParams'); + for (var prop in qpMap) { + if (!qpMap.hasOwnProperty(prop)) { continue; } + controller.addObserver(prop + '.[]', controller, controller._qpChanged); + } + } -(function() { -/** -Ember Views + __exports__["default"] = ControllerMixin; + }); +define("ember-routing/ext/run_loop", + ["ember-metal/run_loop"], + function(__dependency1__) { + "use strict"; + var run = __dependency1__["default"]; -@module ember -@submodule ember-views -@requires ember-runtime -@main ember-views -*/ + /** + @module ember + @submodule ember-views + */ -})(); + // Add a new named queue after the 'actions' queue (where RSVP promises + // resolve), which is used in router transitions to prevent unnecessary + // loading state entry if all context promises resolve on the + // 'actions' queue first. -(function() { -define("metamorph", - [], - function() { + var queues = run.queues; + run._addQueue('routerTransitions', 'actions'); + }); +define("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"; - // ========================================================================== - // Project: metamorph - // Copyright: ©2014 Tilde, Inc. All rights reserved. - // ========================================================================== + var get = __dependency1__.get; + var set = __dependency2__.set; + var run = __dependency3__["default"]; + var EmberView = __dependency4__["default"]; - var K = function() {}, - guid = 0, - disableRange = (function(){ - if ('undefined' !== typeof MetamorphENV) { - return MetamorphENV.DISABLE_RANGE_API; - } else if ('undefined' !== ENV) { - return ENV.DISABLE_RANGE_API; - } else { - return false; - } - })(), + /** + @module ember + @submodule ember-routing + */ - // Feature-detect the W3C range API, the extended check is for IE9 which only partially supports ranges - supportsRange = (!disableRange) && typeof document !== 'undefined' && ('createRange' in document) && (typeof Range !== 'undefined') && Range.prototype.createContextualFragment, + EmberView.reopen({ - // Internet Explorer prior to 9 does not allow setting innerHTML if the first element - // is a "zero-scope" element. This problem can be worked around by making - // the first node an invisible text node. We, like Modernizr, use ­ - needsShy = typeof document !== 'undefined' && (function() { - var testEl = document.createElement('div'); - testEl.innerHTML = "
    "; - testEl.firstChild.innerHTML = ""; - return testEl.firstChild.innerHTML === ''; - })(), + /** + Sets the private `_outlets` object on the view. + @method init + */ + init: function() { + set(this, '_outlets', {}); + this._super(); + }, - // IE 8 (and likely earlier) likes to move whitespace preceeding - // a script tag to appear after it. This means that we can - // accidentally remove whitespace when updating a morph. - movesWhitespace = document && (function() { - var testEl = document.createElement('div'); - testEl.innerHTML = "Test: Value"; - return testEl.childNodes[0].nodeValue === 'Test:' && - testEl.childNodes[2].nodeValue === ' Value'; - })(); + /** + Manually fill any of a view's `{{outlet}}` areas with the + supplied view. - // Constructor that supports either Metamorph('foo') or new - // Metamorph('foo'); - // - // Takes a string of HTML as the argument. + Example - var Metamorph = function(html) { - var self; + ```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 (this instanceof Metamorph) { - self = this; - } else { - self = new K(); - } + 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]; + } - self.innerHTML = html; - var myGuid = 'metamorph-'+(guid++); - self.start = myGuid + '-start'; - self.end = myGuid + '-end'; + if (this._hasEquivalentView(outletName, view)) { + view.destroy(); + return; + } - return self; - }; + var outlets = get(this, '_outlets'); + var container = get(this, 'container'); + var router = container && container.lookup('router:main'); + var renderedName = get(view, 'renderedName'); - K.prototype = Metamorph.prototype; + set(outlets, outletName, view); - var rangeFor, htmlFunc, removeFunc, outerHTMLFunc, appendToFunc, afterFunc, prependFunc, startTagFunc, endTagFunc; + if (router && renderedName) { + router._connectActiveView(renderedName, view); + } + }, - outerHTMLFunc = function() { - return this.startTag() + this.innerHTML + this.endTag(); - }; + /** + 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. - startTagFunc = function() { - /* - * We replace chevron by its hex code in order to prevent escaping problems. - * Check this thread for more explaination: - * http://stackoverflow.com/questions/8231048/why-use-x3c-instead-of-when-generating-html-from-javascript + @private + @method _hasEquivalentView + @param {String} outletName The name of the outlet we are checking + @param {Object} view An Ember.View + @return {Boolean} */ - return "hi"; - * div.firstChild.firstChild.tagName //=> "" - * - * If our script markers are inside such a node, we need to find that - * node and use *it* as the marker. - */ - var realNode = function(start) { - while (start.parentNode.tagName === "") { - start = start.parentNode; + function superClassString(mixin) { + var superclass = mixin.superclass; + if (superclass) { + if (superclass[NAME_KEY]) { return superclass[NAME_KEY]; } + else { return superClassString(superclass); } + } else { + return; + } + } + + function classToString() { + if (!Ember.BOOTED && !this[NAME_KEY]) { + processAllNamespaces(); + } + + var ret; + + if (this[NAME_KEY]) { + ret = this[NAME_KEY]; + } else if (this._toString) { + ret = this._toString; + } else { + var str = superClassString(this); + if (str) { + ret = "(subclass of " + str + ")"; + } else { + ret = "(unknown mixin)"; } + this.toString = makeToString(ret); + } - return start; - }; + return ret; + } - /* - * When automatically adding a tbody, Internet Explorer inserts the - * tbody immediately before the first . Other browsers create it - * before the first node, no matter what. - * - * This means the the following code: - * - * div = document.createElement("div"); - * div.innerHTML = "
    hi
    - * - * Generates the following DOM in IE: - * - * + div - * + table - * - script id='first' - * + tbody - * + tr - * + td - * - "hi" - * - script id='last' - * - * Which means that the two script tags, even though they were - * inserted at the same point in the hierarchy in the original - * HTML, now have different parents. - * - * This code reparents the first script tag by making it the tbody's - * first child. - * - */ - var fixParentage = function(start, end) { - if (start.parentNode !== end.parentNode) { - end.parentNode.insertBefore(start, end.parentNode.firstChild); + function processAllNamespaces() { + var unprocessedNamespaces = !Namespace.PROCESSED, + unprocessedMixins = Ember.anyUnprocessedMixins; + + if (unprocessedNamespaces) { + findNamespaces(); + Namespace.PROCESSED = true; + } + + if (unprocessedNamespaces || unprocessedMixins) { + var namespaces = Namespace.NAMESPACES, namespace; + for (var i=0, l=namespaces.length; i=0;idx--) { + if (this[idx] === object) return idx ; + } + return -1; + }, + + copy: function(deep) { + if (deep) { + return this.map(function(item) { return copy(item, true); }); } - }; - prependFunc = function(html) { - var start = document.getElementById(this.start); - var parentNode = start.parentNode; - var nextSibling; - var node; + return this.slice(); + } + }); - node = firstNodeFor(parentNode, html); - var insertBefore = start.nextSibling; + // 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); + }); - while (node) { - nextSibling = node.nextSibling; - parentNode.insertBefore(node, insertBefore); - node = nextSibling; - } - }; + if (ignore.length>0) { + NativeArray = NativeArray.without.apply(NativeArray, ignore); } - Metamorph.prototype.html = function(html) { - this.checkRemoved(); - if (html === undefined) { return this.innerHTML; } + /** + Creates an `Ember.NativeArray` from an Array like object. + Does not modify the original object. Ember.A is not needed if + `Ember.EXTEND_PROTOTYPES` is `true` (the default value). However, + it is recommended that you use Ember.A when creating addons for + ember or when you can not guarantee that `Ember.EXTEND_PROTOTYPES` + will be `true`. - htmlFunc.call(this, html); + Example - this.innerHTML = html; - }; + ```js + var Pagination = Ember.CollectionView.extend({ + tagName: 'ul', + classNames: ['pagination'], - Metamorph.prototype.replaceWith = function(html) { - this.checkRemoved(); - htmlFunc.call(this, html, true); - }; + init: function() { + this._super(); + if (!this.get('content')) { + this.set('content', Ember.A()); + } + } + }); + ``` - Metamorph.prototype.remove = removeFunc; - Metamorph.prototype.outerHTML = outerHTMLFunc; - Metamorph.prototype.appendTo = appendToFunc; - Metamorph.prototype.after = afterFunc; - Metamorph.prototype.prepend = prependFunc; - Metamorph.prototype.startTag = startTagFunc; - Metamorph.prototype.endTag = endTagFunc; + @method A + @for Ember + @return {Ember.NativeArray} + */ + var A = function(arr) { + if (arr === undefined) { arr = []; } + return EmberArray.detect(arr) ? arr : NativeArray.apply(arr); + }; - Metamorph.prototype.isRemoved = function() { - var before = document.getElementById(this.start); - var after = document.getElementById(this.end); + /** + 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`. - return !before || !after; - }; + Example - Metamorph.prototype.checkRemoved = function() { - if (this.isRemoved()) { - throw new Error("Cannot perform operations on a Metamorph that is not in the DOM."); + ```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 || []; }; }; - return Metamorph; + 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; }); +define("ember-runtime/system/object", + ["ember-runtime/system/core_object","ember-runtime/mixins/observable","exports"], + function(__dependency1__, __dependency2__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-runtime + */ -})(); + var CoreObject = __dependency1__["default"]; + var Observable = __dependency2__["default"]; -(function() { -/** -@module ember -@submodule ember-handlebars-compiler -*/ - -// Eliminate dependency on any Ember to simplify precompilation workflow -var objectCreate = Object.create || function(parent) { - function F() {} - F.prototype = parent; - return new F(); -}; - -var Handlebars = (Ember.imports && Ember.imports.Handlebars) || (this && this.Handlebars); -if (!Handlebars && typeof require === 'function') { - Handlebars = require('handlebars'); -} - -Ember.assert("Ember Handlebars requires Handlebars version 1.0 or 1.1. Include " + - "a SCRIPT tag in the HTML HEAD linking to the Handlebars file " + - "before you link to Ember.", Handlebars); - -Ember.assert("Ember Handlebars requires Handlebars version 1.0 or 1.1, " + - "COMPILER_REVISION expected: 4, got: " + Handlebars.COMPILER_REVISION + - " - Please note: Builds of master may have other COMPILER_REVISION values.", - Handlebars.COMPILER_REVISION === 4); - -/** - Prepares the Handlebars templating library for use inside Ember's view - system. - - The `Ember.Handlebars` object is the standard Handlebars library, extended to - use Ember's `get()` method instead of direct property access, which allows - computed properties to be used inside templates. - - To create an `Ember.Handlebars` template, call `Ember.Handlebars.compile()`. - This will return a function that can be used by `Ember.View` for rendering. - - @class Handlebars - @namespace Ember -*/ -Ember.Handlebars = objectCreate(Handlebars); - -/** - Register a bound helper or custom view helper. - - ## Simple bound helper example - - ```javascript - Ember.Handlebars.helper('capitalize', function(value) { - return value.toUpperCase(); + /** + `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"; + }; + + __exports__["default"] = EmberObject; }); - ``` +define("ember-runtime/system/object_proxy", + ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/observer","ember-metal/property_events","ember-metal/computed","ember-metal/properties","ember-metal/mixin","ember-runtime/system/string","ember-runtime/system/object","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-runtime + */ + var Ember = __dependency1__["default"]; + // Ember.assert + var get = __dependency2__.get; + var set = __dependency3__.set; + var meta = __dependency4__.meta; + var addObserver = __dependency5__.addObserver; + var removeObserver = __dependency5__.removeObserver; + var addBeforeObserver = __dependency5__.addBeforeObserver; + var removeBeforeObserver = __dependency5__.removeBeforeObserver; + var propertyWillChange = __dependency6__.propertyWillChange; + var propertyDidChange = __dependency6__.propertyDidChange; + var computed = __dependency7__.computed; + var defineProperty = __dependency8__.defineProperty; + var observer = __dependency9__.observer; + var fmt = __dependency10__.fmt; + var EmberObject = __dependency11__["default"]; + + function contentPropertyWillChange(content, contentKey) { + var key = contentKey.slice(8); // remove "content." + if (key in this) { return; } // if shadowed in proxy + propertyWillChange(this, key); + } + + function contentPropertyDidChange(content, contentKey) { + var key = contentKey.slice(8); // remove "content." + if (key in this) { return; } // if shadowed in proxy + propertyDidChange(this, key); + } - The above bound helper can be used inside of templates as follows: + /** + `Ember.ObjectProxy` forwards all properties not defined by the proxy itself + to a proxied `content` object. - ```handlebars - {{capitalize name}} - ``` + ```javascript + object = Ember.Object.create({ + name: 'Foo' + }); - 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. + proxy = Ember.ObjectProxy.create({ + content: object + }); - For more examples of bound helpers, see documentation for - `Ember.Handlebars.registerBoundHelper`. + // Access and change existing properties + proxy.get('name') // 'Foo' + proxy.set('name', 'Bar'); + object.get('name') // 'Bar' - ## Custom view helper example + // Create new 'description' property on `object` + proxy.set('description', 'Foo is a whizboo baz'); + object.get('description') // 'Foo is a whizboo baz' + ``` - Assuming a view subclass named `App.CalendarView` were defined, a helper - for rendering instances of this view could be registered as follows: + While `content` is unset, setting a property to be delegated will throw an + Error. - ```javascript - Ember.Handlebars.helper('calendar', App.CalendarView): - ``` + ```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 + ``` - The above bound helper can be used inside of templates as follows: + Delegated properties can be bound to and will change when content is updated. - ```handlebars - {{calendar}} - ``` + Computed properties on the proxy itself can depend on delegated properties. - Which is functionally equivalent to: + ```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') + }); - ```handlebars - {{view App.CalendarView}} - ``` + proxy = ProxyWithComputedProperty.create(); - Options in the helper will be passed to the view in exactly the same - manner as with the `view` helper. + proxy.get('fullName'); // undefined + proxy.set('content', { + firstName: 'Tom', lastName: 'Dale' + }); // triggers property change for fullName on proxy - @method helper - @for Ember.Handlebars - @param {String} name - @param {Function|Ember.View} function or view class constructor - @param {String} dependentKeys* -*/ -Ember.Handlebars.helper = function(name, value) { - Ember.assert("You tried to register a component named '" + name + "', but component names must include a '-'", !Ember.Component.detect(value) || name.match(/-/)); + proxy.get('fullName'); // 'Tom Dale' + ``` - if (Ember.View.detect(value)) { - Ember.Handlebars.registerHelper(name, Ember.Handlebars.makeViewHelper(value)); - } else { - Ember.Handlebars.registerBoundHelper.apply(null, arguments); - } -}; - -/** - Returns a helper function that renders the provided ViewClass. - - Used internally by Ember.Handlebars.helper and other methods - involving helper/component registration. - - @private - @method makeViewHelper - @for Ember.Handlebars - @param {Function} ViewClass view class constructor -*/ -Ember.Handlebars.makeViewHelper = function(ViewClass) { - return function(options) { - Ember.assert("You can only pass attributes (such as name=value) not bare values to a helper for a View found in '" + ViewClass.toString() + "'", arguments.length < 2); - return Ember.Handlebars.helpers.view.call(this, ViewClass, options); - }; -}; - -/** -@class helpers -@namespace Ember.Handlebars -*/ -Ember.Handlebars.helpers = objectCreate(Handlebars.helpers); - -/** - Override the the opcode compiler and JavaScript compiler for Handlebars. - - @class Compiler - @namespace Ember.Handlebars - @private - @constructor -*/ -Ember.Handlebars.Compiler = function() {}; - -// Handlebars.Compiler doesn't exist in runtime-only -if (Handlebars.Compiler) { - Ember.Handlebars.Compiler.prototype = objectCreate(Handlebars.Compiler.prototype); -} - -Ember.Handlebars.Compiler.prototype.compiler = Ember.Handlebars.Compiler; - -/** - @class JavaScriptCompiler - @namespace Ember.Handlebars - @private - @constructor -*/ -Ember.Handlebars.JavaScriptCompiler = function() {}; - -// Handlebars.JavaScriptCompiler doesn't exist in runtime-only -if (Handlebars.JavaScriptCompiler) { - Ember.Handlebars.JavaScriptCompiler.prototype = objectCreate(Handlebars.JavaScriptCompiler.prototype); - Ember.Handlebars.JavaScriptCompiler.prototype.compiler = Ember.Handlebars.JavaScriptCompiler; -} - - -Ember.Handlebars.JavaScriptCompiler.prototype.namespace = "Ember.Handlebars"; - -Ember.Handlebars.JavaScriptCompiler.prototype.initializeBuffer = function() { - return "''"; -}; - -/** - Override the default buffer for Ember Handlebars. By default, Handlebars - creates an empty String at the beginning of each invocation and appends to - it. Ember's Handlebars overrides this to append to a single shared buffer. - - @private - @method appendToBuffer - @param string {String} -*/ -Ember.Handlebars.JavaScriptCompiler.prototype.appendToBuffer = function(string) { - return "data.buffer.push("+string+");"; -}; - -// Hacks ahead: -// Handlebars presently has a bug where the `blockHelperMissing` hook -// doesn't get passed the name of the missing helper name, but rather -// gets passed the value of that missing helper evaluated on the current -// context, which is most likely `undefined` and totally useless. -// -// So we alter the compiled template function to pass the name of the helper -// instead, as expected. -// -// This can go away once the following is closed: -// https://github.com/wycats/handlebars.js/issues/634 - -var DOT_LOOKUP_REGEX = /helpers\.(.*?)\)/, - BRACKET_STRING_LOOKUP_REGEX = /helpers\['(.*?)'/, - INVOCATION_SPLITTING_REGEX = /(.*blockHelperMissing\.call\(.*)(stack[0-9]+)(,.*)/; - -Ember.Handlebars.JavaScriptCompiler.stringifyLastBlockHelperMissingInvocation = function(source) { - var helperInvocation = source[source.length - 1], - helperName = (DOT_LOOKUP_REGEX.exec(helperInvocation) || BRACKET_STRING_LOOKUP_REGEX.exec(helperInvocation))[1], - matches = INVOCATION_SPLITTING_REGEX.exec(helperInvocation); - - source[source.length - 1] = matches[1] + "'" + helperName + "'" + matches[3]; -}; -var stringifyBlockHelperMissing = Ember.Handlebars.JavaScriptCompiler.stringifyLastBlockHelperMissingInvocation; - -var originalBlockValue = Ember.Handlebars.JavaScriptCompiler.prototype.blockValue; -Ember.Handlebars.JavaScriptCompiler.prototype.blockValue = function() { - originalBlockValue.apply(this, arguments); - stringifyBlockHelperMissing(this.source); -}; - -var originalAmbiguousBlockValue = Ember.Handlebars.JavaScriptCompiler.prototype.ambiguousBlockValue; -Ember.Handlebars.JavaScriptCompiler.prototype.ambiguousBlockValue = function() { - originalAmbiguousBlockValue.apply(this, arguments); - stringifyBlockHelperMissing(this.source); -}; - -/** - Rewrite simple mustaches from `{{foo}}` to `{{bind "foo"}}`. This means that - all simple mustaches in Ember's Handlebars will also set up an observer to - keep the DOM up to date when the underlying property changes. - - @private - @method mustache - @for Ember.Handlebars.Compiler - @param mustache -*/ -Ember.Handlebars.Compiler.prototype.mustache = function(mustache) { - if (!(mustache.params.length || mustache.hash)) { - var id = new Handlebars.AST.IdNode([{ part: '_triageMustache' }]); - - // Update the mustache node to include a hash value indicating whether the original node - // was escaped. This will allow us to properly escape values when the underlying value - // changes and we need to re-render the value. - if (!mustache.escaped) { - mustache.hash = mustache.hash || new Handlebars.AST.HashNode([]); - mustache.hash.pairs.push(["unescaped", new Handlebars.AST.StringNode("true")]); - } - mustache = new Handlebars.AST.MustacheNode([id].concat([mustache.id]), mustache.hash, !mustache.escaped); - } + @class ObjectProxy + @namespace Ember + @extends Ember.Object + */ + var ObjectProxy = EmberObject.extend({ + /** + The object whose properties will be forwarded. - return Handlebars.Compiler.prototype.mustache.call(this, mustache); -}; - -/** - Used for precompilation of Ember Handlebars templates. This will not be used - during normal app execution. - - @method precompile - @for Ember.Handlebars - @static - @param {String} string The template to precompile -*/ -Ember.Handlebars.precompile = function(string) { - var ast = Handlebars.parse(string); - - var options = { - knownHelpers: { - action: true, - unbound: true, - 'bind-attr': true, - template: true, - view: true, - _triageMustache: true - }, - data: true, - stringParams: true - }; - - var environment = new Ember.Handlebars.Compiler().compile(ast, options); - return new Ember.Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true); -}; - -// We don't support this for Handlebars runtime-only -if (Handlebars.compile) { - /** - The entry point for Ember Handlebars. This replaces the default - `Handlebars.compile` and turns on template-local data and String - parameters. - - @method compile - @for Ember.Handlebars - @static - @param {String} string The template to compile - @return {Function} - */ - Ember.Handlebars.compile = function(string) { - var ast = Handlebars.parse(string); - var options = { data: true, stringParams: true }; - var environment = new Ember.Handlebars.Compiler().compile(ast, options); - var templateSpec = new Ember.Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true); - - var template = Ember.Handlebars.template(templateSpec); - template.isMethod = false; //Make sure we don't wrap templates with ._super - - return template; - }; -} + @property content + @type Ember.Object + @default null + */ + content: null, + _contentDidChange: observer('content', function() { + Ember.assert("Can't set ObjectProxy's content to itself", get(this, 'content') !== this); + }), + isTruthy: computed.bool('content'), -})(); + _debugContainerKey: null, -(function() { -var slice = Array.prototype.slice, - originalTemplate = Ember.Handlebars.template; - -/** - If a path starts with a reserved keyword, returns the root - that should be used. - - @private - @method normalizePath - @for Ember - @param root {Object} - @param path {String} - @param data {Hash} -*/ -var normalizePath = Ember.Handlebars.normalizePath = function(root, path, data) { - var keywords = (data && data.keywords) || {}, - keyword, isKeyword; - - // Get the first segment of the path. For example, if the - // path is "foo.bar.baz", returns "foo". - keyword = path.split('.', 1)[0]; - - // Test to see if the first path is a keyword that has been - // passed along in the view's data hash. If so, we will treat - // that object as the new root. - if (keywords.hasOwnProperty(keyword)) { - // Look up the value in the template's data hash. - root = keywords[keyword]; - isKeyword = true; - - // Handle cases where the entire path is the reserved - // word. In that case, return the object itself. - if (path === keyword) { - path = ''; - } else { - // Strip the keyword from the path and look up - // the remainder from the newly found root. - path = path.substr(keyword.length+1); - } - } + willWatchProperty: function (key) { + var contentKey = 'content.' + key; + addBeforeObserver(this, contentKey, null, contentPropertyWillChange); + addObserver(this, contentKey, null, contentPropertyDidChange); + }, - return { root: root, path: path, isKeyword: isKeyword }; -}; + didUnwatchProperty: function (key) { + var contentKey = 'content.' + key; + removeBeforeObserver(this, contentKey, null, contentPropertyWillChange); + removeObserver(this, contentKey, null, contentPropertyDidChange); + }, + + unknownProperty: function (key) { + var content = get(this, 'content'); + if (content) { + return get(content, key); + } + }, + + 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; + } + + 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); + } + }); -/** - 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. + __exports__["default"] = ObjectProxy; + }); +define("ember-runtime/system/set", + ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/is_none","ember-runtime/system/string","ember-runtime/system/core_object","ember-runtime/mixins/mutable_enumerable","ember-runtime/mixins/enumerable","ember-runtime/mixins/copyable","ember-runtime/mixins/freezable","ember-metal/error","ember-metal/property_events","ember-metal/mixin","ember-metal/computed","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-runtime + */ + var Ember = __dependency1__["default"]; + // Ember.isNone + + var get = __dependency2__.get; + var set = __dependency3__.set; + var guidFor = __dependency4__.guidFor; + var isNone = __dependency5__.isNone; + var fmt = __dependency6__.fmt; + var CoreObject = __dependency7__["default"]; + var MutableEnumerable = __dependency8__["default"]; + var Enumerable = __dependency9__["default"]; + var Copyable = __dependency10__["default"]; + var Freezable = __dependency11__.Freezable; + var FROZEN_ERROR = __dependency11__.FROZEN_ERROR; + var EmberError = __dependency12__["default"]; + var propertyWillChange = __dependency13__.propertyWillChange; + var propertyDidChange = __dependency13__.propertyDidChange; + var aliasMethod = __dependency14__.aliasMethod; + var computed = __dependency15__.computed; - @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 -*/ -var handlebarsGet = Ember.Handlebars.get = function(root, path, options) { - var data = options && options.data, - normalizedPath = normalizePath(root, path, data), - value; + /** + An unordered collection of objects. - - root = normalizedPath.root; - path = normalizedPath.path; + 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. - value = Ember.get(root, path); + All Sets are observable via the Enumerable Observer API - which works + on any enumerable object including both Sets and Arrays. - if (value === undefined && root !== Ember.lookup && Ember.isGlobalPath(path)) { - value = Ember.get(Ember.lookup, path); - } - + ## Creating a Set - return value; -}; + 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. -/** - This method uses `Ember.Handlebars.get` to lookup a value, then ensures - that the value is escaped properly. + 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()`. - If `unescaped` is a truthy value then the escaping will not be performed. + ```javascript + // creates a new empty set + var foundNames = new Ember.Set(); - @method getEscaped - @for Ember.Handlebars - @param {Object} root The object to look up the property on - @param {String} path The path to be lookedup - @param {Object} options The template's option hash -*/ -Ember.Handlebars.getEscaped = function(root, path, options) { - var result = handlebarsGet(root, path, options); + // creates a set with four names in it. + var names = new Ember.Set(["Charles", "Tom", "Juan", "Alex"]); // :P - if (result === null || result === undefined) { - result = ""; - } else if (!(result instanceof Handlebars.SafeString)) { - result = String(result); - } - if (!options.hash.unescaped){ - result = Handlebars.Utils.escapeExpression(result); - } + // creates a copy of the names set. + var namesCopy = new Ember.Set(names); - return result; -}; + // same as above. + var anotherNamesCopy = names.copy(); + ``` -Ember.Handlebars.resolveParams = function(context, params, options) { - var resolvedParams = [], types = options.types, param, type; + ## 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.CoreObject + @uses Ember.MutableEnumerable + @uses Ember.Copyable + @uses Ember.Freezable + @since Ember 0.9 + */ + __exports__["default"] = CoreObject.extend(MutableEnumerable, Copyable, Freezable, { - for (var i=0, l=params.length; i= 0) { + if (!obj.contains(this[loc])) return false; + } - Ember.assert("`blockHelperMissing` was invoked without a helper name, which " + - "is most likely due to a mismatch between the version of " + - "Ember.js you're running now and the one used to precompile your " + - "templates. Please make sure the version of " + - "`ember-handlebars-compiler` you're using is up to date.", path); + return true; + }, - var helper = Ember.Handlebars.resolveHelper(options.data.view.container, path); + /** + 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. - if (helper) { - return helper.apply(this, slice.call(arguments, 1)); - } else { - return Handlebars.helpers.helperMissing.call(this, path); - } + This is an alias for `Ember.MutableEnumerable.addObject()`. - return Handlebars.helpers.blockHelperMissing.apply(this, arguments); -}); + ```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"] + ``` -/** - Register a bound handlebars helper. Bound helpers behave similarly to regular - handlebars helpers, with the added ability to re-render when the underlying data - changes. + @method add + @param {Object} obj The object to add. + @return {Ember.Set} The set itself. + */ + add: aliasMethod('addObject'), - ## Simple example + /** + 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()`. - ```javascript - Ember.Handlebars.registerBoundHelper('capitalize', function(value) { - return value.toUpperCase(); - }); - ``` + ```javascript + var colors = new Ember.Set(["red", "green", "blue"]); + colors.remove("red"); // ["blue", "green"] + colors.remove("purple"); // ["blue", "green"] + colors.remove(null); // ["blue", "green"] + ``` - The above bound helper can be used inside of templates as follows: + @method remove + @param {Object} obj The object to remove + @return {Ember.Set} The set itself. + */ + remove: aliasMethod('removeObject'), - ```handlebars - {{capitalize name}} - ``` + /** + Removes the last element from the set and returns it, or `null` if it's empty. - 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. + ```javascript + var colors = new Ember.Set(["green", "blue"]); + colors.pop(); // "blue" + colors.pop(); // "green" + colors.pop(); // null + ``` - ## Example with options + @method pop + @return {Object} The removed object from the set or null. + */ + pop: function() { + if (get(this, 'isFrozen')) throw new EmberError(FROZEN_ERROR); + var obj = this.length > 0 ? this[this.length-1] : null; + this.remove(obj); + return obj; + }, - Like normal handlebars helpers, bound helpers have access to the options - passed into the helper call. + /** + Inserts the given object on to the end of the set. It returns + the set itself. - ```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(''); - }); - ``` + This is an alias for `Ember.MutableEnumerable.addObject()`. - This helper could be used in a template as follows: + ```javascript + var colors = new Ember.Set(); + colors.push("red"); // ["red"] + colors.push("green"); // ["red", "green"] + colors.push("blue"); // ["red", "green", "blue"] + ``` - ```handlebars - {{repeat text count=3}} - ``` + @method push + @return {Ember.Set} The set itself. + */ + push: aliasMethod('addObject'), - ## Example with bound options + /** + Removes the last element from the set and returns it, or `null` if it's empty. - Bound hash options are also supported. Example: + This is an alias for `Ember.Set.pop()`. - ```handlebars - {{repeat text countBinding="numRepeats"}} - ``` + ```javascript + var colors = new Ember.Set(["green", "blue"]); + colors.shift(); // "blue" + colors.shift(); // "green" + colors.shift(); // null + ``` - 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. + @method shift + @return {Object} The removed object from the set or null. + */ + shift: aliasMethod('pop'), - ## Example with extra dependencies + /** + Inserts the given object on to the end of the set. It returns + the set itself. - The `Ember.Handlebars.registerBoundHelper` method takes a variable length - third parameter which indicates extra dependencies on the passed in value. - This allows the handlebars helper to update when these dependencies change. + This is an alias of `Ember.Set.push()` - ```javascript - Ember.Handlebars.registerBoundHelper('capitalizeName', function(value) { - return value.get('name').toUpperCase(); - }, 'name'); - ``` + ```javascript + var colors = new Ember.Set(); + colors.unshift("red"); // ["red"] + colors.unshift("green"); // ["red", "green"] + colors.unshift("blue"); // ["red", "green", "blue"] + ``` - ## Example with multiple bound properties + @method unshift + @return {Ember.Set} The set itself. + */ + unshift: aliasMethod('push'), - `Ember.Handlebars.registerBoundHelper` supports binding to - multiple properties, e.g.: + /** + Adds each object in the passed enumerable to the set. - ```javascript - Ember.Handlebars.registerBoundHelper('concatenate', function() { - var values = Array.prototype.slice.call(arguments, 0, -1); - return values.join('||'); - }); - ``` - - Which allows for template syntax such as `{{concatenate prop1 prop2}}` or - `{{concatenate prop1 prop2 prop3}}`. If any of the properties change, - the helpr will re-render. Note that dependency keys cannot be - using in conjunction with multi-property helpers, since it is ambiguous - which property the dependent keys would belong to. - - ## Use with unbound helper - - The `{{unbound}}` helper can be used with bound helper invocations - to render them in their unbound form, e.g. - - ```handlebars - {{unbound capitalize name}} - ``` - - In this example, if the name property changes, the helper - will not re-render. - - ## Use with blocks not supported - - Bound helpers do not support use with Handlebars blocks or - the addition of child views of any kind. - - @method registerBoundHelper - @for Ember.Handlebars - @param {String} name - @param {Function} function - @param {String} dependentKeys* -*/ -Ember.Handlebars.registerBoundHelper = function(name, fn) { - var boundHelperArgs = slice.call(arguments, 1), - boundFn = Ember.Handlebars.makeBoundHelper.apply(this, boundHelperArgs); - Ember.Handlebars.registerHelper(name, boundFn); -}; - -/** - A (mostly) private helper function to `registerBoundHelper`. Takes the - provided Handlebars helper function fn and returns it in wrapped - bound helper form. - - The main use case for using this outside of `registerBoundHelper` - is for registering helpers on the container: - - ```js - var boundHelperFn = Ember.Handlebars.makeBoundHelper(function(word) { - return word.toUpperCase(); - }); + This is an alias of `Ember.MutableEnumerable.addObjects()` - container.register('helper:my-bound-helper', boundHelperFn); - ``` - - In the above example, if the helper function hadn't been wrapped in - `makeBoundHelper`, the registered helper would be unbound. - - @private - @method makeBoundHelper - @for Ember.Handlebars - @param {Function} function - @param {String} dependentKeys* -*/ -Ember.Handlebars.makeBoundHelper = function(fn) { - var dependentKeys = slice.call(arguments, 1); - - function helper() { - var properties = slice.call(arguments, 0, -1), - numProperties = properties.length, - options = arguments[arguments.length - 1], - normalizedProperties = [], - data = options.data, - types = data.isUnbound ? slice.call(options.types, 1) : options.types, - hash = options.hash, - view = data.view, - contexts = options.contexts, - currentContext = (contexts && contexts.length) ? contexts[0] : this, - prefixPathForDependentKeys = '', - loc, len, hashOption, - boundOption, property, - normalizedValue = Ember._SimpleHandlebarsView.prototype.normalizedValue; - - Ember.assert("registerBoundHelper-generated helpers do not support use with Handlebars blocks.", !options.fn); - - // Detect bound options (e.g. countBinding="otherCount") - var boundOptions = hash.boundOptions = {}; - for (hashOption in hash) { - if (Ember.IS_BINDING.test(hashOption)) { - // Lop off 'Binding' suffix. - boundOptions[hashOption.slice(0, -7)] = hash[hashOption]; - } - } - - // Expose property names on data.properties object. - var watchedProperties = []; - data.properties = []; - for (loc = 0; loc < numProperties; ++loc) { - data.properties.push(properties[loc]); - if (types[loc] === 'ID') { - var normalizedProp = normalizePath(currentContext, properties[loc], data); - normalizedProperties.push(normalizedProp); - watchedProperties.push(normalizedProp); - } else { - if(data.isUnbound) { - normalizedProperties.push({path: properties[loc]}); - }else { - normalizedProperties.push(null); - } - } - } + ```javascript + var colors = new Ember.Set(); + colors.addEach(["red", "green", "blue"]); // ["red", "green", "blue"] + ``` - // Handle case when helper invocation is preceded by `unbound`, e.g. - // {{unbound myHelper foo}} - if (data.isUnbound) { - return evaluateUnboundHelper(this, fn, normalizedProperties, options); - } + @method addEach + @param {Ember.Enumerable} objects the objects to add. + @return {Ember.Set} The set itself. + */ + addEach: aliasMethod('addObjects'), - var bindView = new Ember._SimpleHandlebarsView(null, null, !options.hash.unescaped, options.data); + /** + Removes each object in the passed enumerable to the set. - // Override SimpleHandlebarsView's method for generating the view's content. - bindView.normalizedValue = function() { - var args = [], boundOption; + This is an alias of `Ember.MutableEnumerable.removeObjects()` - // Copy over bound hash options. - for (boundOption in boundOptions) { - if (!boundOptions.hasOwnProperty(boundOption)) { continue; } - property = normalizePath(currentContext, boundOptions[boundOption], data); - bindView.path = property.path; - bindView.pathRoot = property.root; - hash[boundOption] = normalizedValue.call(bindView); - } + ```javascript + var colors = new Ember.Set(["red", "green", "blue"]); + colors.removeEach(["red", "blue"]); // ["green"] + ``` - for (loc = 0; loc < numProperties; ++loc) { - property = normalizedProperties[loc]; - if (property) { - bindView.path = property.path; - bindView.pathRoot = property.root; - args.push(normalizedValue.call(bindView)); - } else { - args.push(properties[loc]); - } - } - args.push(options); + @method removeEach + @param {Ember.Enumerable} objects the objects to remove. + @return {Ember.Set} The set itself. + */ + removeEach: aliasMethod('removeObjects'), - // Run the supplied helper function. - return fn.apply(currentContext, args); - }; + // .......................................................... + // PRIVATE ENUMERABLE SUPPORT + // + + init: function(items) { + this._super(); + if (items) this.addObjects(items); + }, - view.appendChild(bindView); + // implement Ember.Enumerable + nextObject: function(idx) { + return this[idx]; + }, - // Assemble list of watched properties that'll re-render this helper. - for (boundOption in boundOptions) { - if (boundOptions.hasOwnProperty(boundOption)) { - watchedProperties.push(normalizePath(currentContext, boundOptions[boundOption], data)); - } - } + // more optimized version + firstObject: computed(function() { + return this.length > 0 ? this[0] : undefined; + }), - // Observe each property. - for (loc = 0, len = watchedProperties.length; loc < len; ++loc) { - property = watchedProperties[loc]; - view.registerObserver(property.root, property.path, bindView, bindView.rerender); - } + // more optimized version + lastObject: computed(function() { + return this.length > 0 ? this[this.length-1] : undefined; + }), - if (types[0] !== 'ID' || normalizedProperties.length === 0) { - return; - } + // implements Ember.MutableEnumerable + addObject: function(obj) { + if (get(this, 'isFrozen')) throw new EmberError(FROZEN_ERROR); + if (isNone(obj)) return this; // nothing to do - // Add dependent key observers to the first param - var normalized = normalizedProperties[0], - pathRoot = normalized.root, - path = normalized.path; + var guid = guidFor(obj), + idx = this[guid], + len = get(this, 'length'), + added ; - if(!Ember.isEmpty(path)) { - prefixPathForDependentKeys = path + '.'; - } - for (var i=0, l=dependentKeys.length; i=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; + } + return ret; + }, + toString: function() { + var len = this.length, idx, array = []; + for(idx = 0; idx < len; idx++) { + array[idx] = this[idx]; + } + return fmt("Ember.Set<%@>", [array.join(',')]); + } + }); + }); +define("ember-runtime/system/string", + ["ember-metal/core","ember-metal/utils","exports"], + function(__dependency1__, __dependency2__, __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 STRING_DASHERIZE_REGEXP = (/[ _]/g); + var STRING_DASHERIZE_CACHE = {}; + var STRING_DECAMELIZE_REGEXP = (/([a-z\d])([A-Z])/g); + var STRING_CAMELIZE_REGEXP = (/(\-|_|\.|\s)+(.)?/g); + var STRING_UNDERSCORE_REGEXP_1 = (/([a-z\d])([A-Z]+)/g); + var STRING_UNDERSCORE_REGEXP_2 = (/\-|\s+/g); + + function fmt(str, formats) { + if (!isArray(formats) || arguments.length > 2) { + formats = Array.prototype.slice.call(arguments, 1); + } + + // first, replace any ORDERED replacements. + var idx = 0; // the current index for non-numerical replacements + return str.replace(/%@([0-9]+)?/g, function(s, argIndex) { + argIndex = (argIndex) ? parseInt(argIndex, 10) - 1 : idx++; + s = formats[argIndex]; + return (s === null) ? '(null)' : (s === undefined) ? '' : emberInspect(s); + }); + } -(function() { -/** - 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. + function loc(str, formats) { + if (!isArray(formats) || arguments.length > 2) { + formats = Array.prototype.slice.call(arguments, 1); + } - ```javascript - Ember.String.htmlSafe('
    someString
    ') - ``` + str = Ember.STRINGS[str] || str; + return fmt(str, formats); + } - @method htmlSafe - @for Ember.String - @static - @return {Handlebars.SafeString} a string that will not be html escaped by Handlebars -*/ -Ember.String.htmlSafe = function(str) { - return new Handlebars.SafeString(str); -}; + function w(str) { + return str.split(/\s+/); + } -var htmlSafe = Ember.String.htmlSafe; + function decamelize(str) { + return str.replace(STRING_DECAMELIZE_REGEXP, '$1_$2').toLowerCase(); + } -if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { + function dasherize(str) { + var cache = STRING_DASHERIZE_CACHE, + hit = cache.hasOwnProperty(str), + ret; - /** - Mark a string as being safe for unescaped output with Handlebars. + if (hit) { + return cache[str]; + } else { + ret = decamelize(str).replace(STRING_DASHERIZE_REGEXP,'-'); + cache[str] = ret; + } - ```javascript - '
    someString
    '.htmlSafe() - ``` + return ret; + } - See [Ember.String.htmlSafe](/api/classes/Ember.String.html#method_htmlSafe). + function camelize(str) { + return str.replace(STRING_CAMELIZE_REGEXP, function(match, separator, chr) { + return chr ? chr.toUpperCase() : ''; + }).replace(/^([A-Z])/, function(match, separator, chr) { + return match.toLowerCase(); + }); + } - @method htmlSafe - @for String - @return {Handlebars.SafeString} a string that will not be html escaped by Handlebars - */ - String.prototype.htmlSafe = function() { - return htmlSafe(this); - }; -} + function classify(str) { + var parts = str.split("."), + out = []; -})(); + for (var i=0, l=parts.length; i alpha + // > beta + // > gamma + ``` - view.invokeRecursively(function(view) { - view.propertyWillChange('element'); - }); - view.triggerRecursively('willInsertElement'); + @method w + @param {String} str The string to split + @return {Array} array containing the split strings + */ + w: w, - morph.replaceWith(buffer.string()); - view.transitionTo('inDOM'); + /** + Converts a camelized string into all lower case separated by underscores. - view.invokeRecursively(function(view) { - view.propertyDidChange('element'); - }); - view.triggerRecursively('didInsertElement'); + ```javascript + 'innerHTML'.decamelize(); // 'inner_html' + 'action_name'.decamelize(); // 'action_name' + 'css-class-name'.decamelize(); // 'css-class-name' + 'my favorite items'.decamelize(); // 'my favorite items' + ``` - notifyMutationListeners(); - }); - }, + @method decamelize + @param {String} str The string to decamelize. + @return {String} the decamelized string. + */ + decamelize: decamelize, - empty: function(view) { - view.morph.html(""); - notifyMutationListeners(); - } -}; - -// The `morph` and `outerHTML` properties are internal only -// and not observable. - -/** - @class _Metamorph - @namespace Ember - @private -*/ -Ember._Metamorph = Ember.Mixin.create({ - isVirtual: true, - tagName: '', - - instrumentName: 'metamorph', - - init: function() { - this._super(); - this.morph = Metamorph(); - Ember.deprecate('Supplying a tagName to Metamorph views is unreliable and is deprecated. You may be setting the tagName on a Handlebars helper that creates a Metamorph.', !this.tagName); - }, - - beforeRender: function(buffer) { - buffer.push(this.morph.startTag()); - buffer.pushOpeningTag(); - }, - - afterRender: function(buffer) { - buffer.pushClosingTag(); - buffer.push(this.morph.endTag()); - }, - - createElement: function() { - var buffer = this.renderToBuffer(); - this.outerHTML = buffer.string(); - this.clearBuffer(); - }, - - domManager: DOMManager -}); - -/** - @class _MetamorphView - @namespace Ember - @extends Ember.View - @uses Ember._Metamorph - @private -*/ -Ember._MetamorphView = Ember.View.extend(Ember._Metamorph); - -/** - @class _SimpleMetamorphView - @namespace Ember - @extends Ember.CoreView - @uses Ember._Metamorph - @private -*/ -Ember._SimpleMetamorphView = Ember.CoreView.extend(Ember._Metamorph); + /** + 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' + ``` -})(); + @method dasherize + @param {String} str The string to dasherize. + @return {String} the dasherized string. + */ + dasherize: dasherize, + /** + Returns the lowerCamelCase form of a string. + ```javascript + 'innerHTML'.camelize(); // 'innerHTML' + 'action_name'.camelize(); // 'actionName' + 'css-class-name'.camelize(); // 'cssClassName' + 'my favorite items'.camelize(); // 'myFavoriteItems' + 'My Favorite Items'.camelize(); // 'myFavoriteItems' + ``` -(function() { -/*globals Handlebars */ -/*jshint newcap:false*/ -/** -@module ember -@submodule ember-handlebars -*/ - -var get = Ember.get, set = Ember.set, handlebarsGet = Ember.Handlebars.get; -var Metamorph = requireModule('metamorph'); -function SimpleHandlebarsView(path, pathRoot, isEscaped, templateData) { - this.path = path; - this.pathRoot = pathRoot; - this.isEscaped = isEscaped; - this.templateData = templateData; - - this.morph = Metamorph(); - this.state = 'preRender'; - this.updateId = null; - this._parentView = null; - this.buffer = null; -} - -Ember._SimpleHandlebarsView = SimpleHandlebarsView; - -SimpleHandlebarsView.prototype = { - isVirtual: true, - isView: true, - - destroy: function () { - if (this.updateId) { - Ember.run.cancel(this.updateId); - this.updateId = null; - } - if (this._parentView) { - this._parentView.removeChild(this); - } - this.morph = null; - this.state = 'destroyed'; - }, + @method camelize + @param {String} str The string to camelize. + @return {String} the camelized string. + */ + camelize: camelize, - propertyWillChange: Ember.K, + /** + Returns the UpperCamelCase form of a string. - propertyDidChange: Ember.K, + ```javascript + 'innerHTML'.classify(); // 'InnerHTML' + 'action_name'.classify(); // 'ActionName' + 'css-class-name'.classify(); // 'CssClassName' + 'my favorite items'.classify(); // 'MyFavoriteItems' + ``` - normalizedValue: function() { - var path = this.path, - pathRoot = this.pathRoot, - result, templateData; + @method classify + @param {String} str the string to classify + @return {String} the classified string + */ + classify: classify, - // Use the pathRoot as the result if no path is provided. This - // happens if the path is `this`, which gets normalized into - // a `pathRoot` of the current Handlebars context and a path - // of `''`. - if (path === '') { - result = pathRoot; - } else { - templateData = this.templateData; - result = handlebarsGet(pathRoot, path, { data: templateData }); - } + /** + More general than decamelize. Returns the lower\_case\_and\_underscored + form of a string. - return result; - }, + ```javascript + 'innerHTML'.underscore(); // 'inner_html' + 'action_name'.underscore(); // 'action_name' + 'css-class-name'.underscore(); // 'css_class_name' + 'my favorite items'.underscore(); // 'my_favorite_items' + ``` - renderToBuffer: function(buffer) { - var string = ''; + @method underscore + @param {String} str The string to underscore. + @return {String} the underscored string. + */ + underscore: underscore, - string += this.morph.startTag(); - string += this.render(); - string += this.morph.endTag(); + /** + Returns the Capitalized form of a string - buffer.push(string); - }, + ```javascript + 'innerHTML'.capitalize() // 'InnerHTML' + 'action_name'.capitalize() // 'Action_name' + 'css-class-name'.capitalize() // 'Css-class-name' + 'my favorite items'.capitalize() // 'My favorite items' + ``` - render: function() { - // If not invoked via a triple-mustache ({{{foo}}}), escape - // the content of the template. - var escape = this.isEscaped; - var result = this.normalizedValue(); + @method capitalize + @param {String} str The string to capitalize. + @return {String} The capitalized string. + */ + capitalize: capitalize + }; - if (result === null || result === undefined) { - result = ""; - } else if (!(result instanceof Handlebars.SafeString)) { - result = String(result); - } + __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; + }); +define("ember-runtime/system/subarray", + ["ember-metal/property_get","ember-metal/error","ember-metal/enumerable_utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var get = __dependency1__.get; + var EmberError = __dependency2__["default"]; + var EnumerableUtils = __dependency3__["default"]; - if (escape) { result = Handlebars.Utils.escapeExpression(result); } - return result; - }, + var RETAIN = 'r'; + var FILTER = 'f'; - rerender: function() { - switch(this.state) { - case 'preRender': - case 'destroyed': - break; - case 'inBuffer': - throw new Ember.Error("Something you did tried to replace an {{expression}} before it was inserted into the DOM."); - case 'hasElement': - case 'inDOM': - this.updateId = Ember.run.scheduleOnce('render', this, 'update'); - break; + function Operation(type, count) { + this.type = type; + this.count = count; } - return this; - }, + __exports__["default"] = SubArray; - update: function () { - this.updateId = null; - this.morph.html(this.render()); - }, - - transitionTo: function(state) { - this.state = state; - } -}; - -var states = Ember.View.cloneStates(Ember.View.states), merge = Ember.merge; + /** + 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. -merge(states._default, { - rerenderIfNeeded: Ember.K -}); + @class SubArray + @namespace Ember + */ + function SubArray (length) { + if (arguments.length < 1) { length = 0; } -merge(states.inDOM, { - rerenderIfNeeded: function(view) { - if (view.normalizedValue() !== view._lastNormalizedValue) { - view.rerender(); - } - } -}); - -/** - `Ember._HandlebarsBoundView` is a private view created by the Handlebars - `{{bind}}` helpers that is used to keep track of bound properties. - - 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. - - @class _HandlebarsBoundView - @namespace Ember - @extends Ember._MetamorphView - @private -*/ -Ember._HandlebarsBoundView = Ember._MetamorphView.extend({ - instrumentName: 'boundHandlebars', - states: states, - - /** - 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. - - @property shouldDisplayFunc - @type Function - @default null - */ - shouldDisplayFunc: null, - - /** - 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`. - - 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`. - - @property preserveContext - @type Boolean - @default false - */ - preserveContext: false, - - /** - If `preserveContext` is true, this is the object that will be used - to render the template. - - @property previousContext - @type Object - */ - previousContext: null, - - /** - The template to render when `shouldDisplayFunc` evaluates to `true`. - - @property displayTemplate - @type Function - @default null - */ - displayTemplate: null, - - /** - The template to render when `shouldDisplayFunc` evaluates to `false`. - - @property inverseTemplate - @type Function - @default null - */ - inverseTemplate: null, - - - /** - The path to look up on `pathRoot` that is passed to - `shouldDisplayFunc` to determine which template to render. - - In addition, if `preserveContext` is `false,` the object at this path will - be passed to the template when rendering. - - @property path - @type String - @default null - */ - path: null, - - /** - The object from which the `path` will be looked up. Sometimes this is the - same as the `previousContext`, but in cases where this view has been - generated for paths that start with a keyword such as `view` or - `controller`, the path root will be that resolved object. - - @property pathRoot - @type Object - */ - pathRoot: null, - - normalizedValue: function() { - var path = get(this, 'path'), - pathRoot = get(this, 'pathRoot'), - valueNormalizer = get(this, 'valueNormalizerFunc'), - result, templateData; - - // Use the pathRoot as the result if no path is provided. This - // happens if the path is `this`, which gets normalized into - // a `pathRoot` of the current Handlebars context and a path - // of `''`. - if (path === '') { - result = pathRoot; - } else { - templateData = get(this, 'templateData'); - result = handlebarsGet(pathRoot, path, { data: templateData }); + if (length > 0) { + this._operations = [new Operation(RETAIN, length)]; + } else { + this._operations = []; + } } - return valueNormalizer ? valueNormalizer(result) : result; - }, - - rerenderIfNeeded: function() { - this.currentState.rerenderIfNeeded(this); - }, - - /** - Determines which template to invoke, sets up the correct state based on - that logic, then invokes the default `Ember.View` `render` implementation. - 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. - - 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`. - - @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'); - - var shouldDisplay = get(this, 'shouldDisplayFunc'), - preserveContext = get(this, 'preserveContext'), - context = get(this, 'previousContext'); + SubArray.prototype = { + /** + Track that an item was added to the tracked array. - var _contextController = get(this, '_contextController'); + @method addItem - var inverseTemplate = get(this, 'inverseTemplate'), - displayTemplate = get(this, 'displayTemplate'); + @param {number} index The index of the item in the tracked array. + @param {boolean} match `true` iff the item is included in the subarray. - var result = this.normalizedValue(); - this._lastNormalizedValue = result; + @return {number} The index of the item in the subarray. + */ + addItem: function(index, match) { + var returnValue = -1, + itemType = match ? RETAIN : FILTER, + 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; - // First, test the conditional to see if we should - // render the template or not. - if (shouldDisplay(result)) { - set(this, 'template', displayTemplate); + self._operations.splice(operationIndex + 1, 0, newOperation, splitOperation); + } - // 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) { - if (_contextController) { - set(_contextController, 'content', result); - result = _contextController; - } - 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 Handlebars.SafeString)) { - result = String(result); + if (match) { + if (operation.type === RETAIN) { + returnValue = seenInSubArray + (index - rangeStart); + } else { + returnValue = seenInSubArray; + } } - if (escape) { result = Handlebars.Utils.escapeExpression(result); } - buffer.push(result); - return; - } - } - } else if (inverseTemplate) { - set(this, 'template', inverseTemplate); + self._composeAt(operationIndex); + }, function(seenInSubArray) { + self._operations.push(new Operation(itemType, 1)); - if (preserveContext) { - set(this, '_context', context); - } else { - set(this, '_context', result); - } - } else { - set(this, 'template', function() { return ''; }); - } + if (match) { + returnValue = seenInSubArray; + } - return this._super(buffer); - } -}); + self._composeAt(self._operations.length-1); + }); -})(); + return returnValue; + }, + /** + Track that an item was removed from the tracked array. + @method removeItem -(function() { -/** -@module ember -@submodule ember-handlebars -*/ - -var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt; -var handlebarsGet = Ember.Handlebars.get, normalizePath = Ember.Handlebars.normalizePath; -var handlebarsGetEscaped = Ember.Handlebars.getEscaped; -var forEach = Ember.ArrayPolyfills.forEach; -var o_create = Ember.create; - -var EmberHandlebars = Ember.Handlebars, helpers = EmberHandlebars.helpers; - -function exists(value) { - return !Ember.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) { - var data = options.data, - fn = options.fn, - inverse = options.inverse, - view = data.view, - currentContext = this, - normalized, observer, i; - - normalized = normalizePath(currentContext, property, data); - - // Set up observers for observable objects - if ('object' === typeof this) { - if (data.insideGroup) { - observer = function() { - Ember.run.once(view, 'rerender'); - }; + @param {number} index The index of the item in the tracked array. - var template, context, result = handlebarsGet(currentContext, property, options); + @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, + self = this; - result = valueNormalizer ? valueNormalizer(result) : result; + this._findOperation(index, function (operation, operationIndex, rangeStart, rangeEnd, seenInSubArray) { + if (operation.type === RETAIN) { + returnValue = seenInSubArray + (index - rangeStart); + } - context = preserveContext ? currentContext : result; - if (shouldDisplay(result)) { - template = fn; - } else if (inverse) { - template = inverse; - } + 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."); + }); - template(context, { data: options.data }); - } else { - // 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(Ember._HandlebarsBoundView, { - preserveContext: preserveContext, - shouldDisplayFunc: shouldDisplay, - valueNormalizerFunc: valueNormalizer, - displayTemplate: fn, - inverseTemplate: inverse, - path: property, - pathRoot: currentContext, - previousContext: currentContext, - isEscaped: !options.hash.unescaped, - templateData: options.data - }); + return returnValue; + }, - if (options.hash.controller) { - bindView.set('_contextController', this.container.lookupFactory('controller:'+options.hash.controller).create({ - container: currentContext.container, - parentController: currentContext, - target: currentContext - })); - } - view.appendChild(bindView); + _findOperation: function (index, foundCallback, notFoundCallback) { + var operationIndex, + len, + operation, + rangeStart, + rangeEnd, + seenInSubArray = 0; - observer = function() { - Ember.run.scheduleOnce('render', bindView, 'rerenderIfNeeded'); - }; - } + // 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; - // Observes the given property on the context and - // tells the Ember._HandlebarsBoundView to re-render. If property - // is an empty string, we are printing the current context - // object ({{this}}) so updating it is not our responsibility. - if (normalized.path !== '') { - view.registerObserver(normalized.root, normalized.path, observer); - if (childProperties) { - for (i=0; i= rangeStart && index <= rangeEnd) { + foundCallback(operation, operationIndex, rangeStart, rangeEnd, seenInSubArray); + return; + } else if (operation.type === RETAIN) { + seenInSubArray += operation.count; + } } - } - } - } else { - // The object is not observable, so just render it out and - // be done with it. - data.buffer.push(handlebarsGetEscaped(currentContext, property, options)); - } -} -EmberHandlebars.bind = bind; - -function simpleBind(currentContext, property, options) { - var data = options.data, - view = data.view, - normalized, observer, pathRoot, output; - - normalized = normalizePath(currentContext, property, data); - pathRoot = normalized.root; - - // Set up observers for observable objects - if (pathRoot && ('object' === typeof pathRoot)) { - if (data.insideGroup) { - observer = function() { - Ember.run.once(view, 'rerender'); - }; - - output = handlebarsGetEscaped(currentContext, property, options); - - data.buffer.push(output); - } else { - var bindView = new Ember._SimpleHandlebarsView( - property, currentContext, !options.hash.unescaped, options.data - ); - - bindView._parentView = view; - view.appendChild(bindView); + notFoundCallback(seenInSubArray); + }, - observer = function() { - Ember.run.scheduleOnce('render', bindView, 'rerender'); - }; - } + _composeAt: function(index) { + var op = this._operations[index], + otherOp; - // Observes the given property on the context and - // tells the Ember._HandlebarsBoundView to re-render. If property - // is an empty string, we are printing the current context - // object ({{this}}) so updating it is not our responsibility. - if (normalized.path !== '') { - view.registerObserver(normalized.root, normalized.path, observer); - } - } else { - // The object is not observable, so just render it out and - // be done with it. - output = handlebarsGetEscaped(currentContext, property, options); - data.buffer.push(output); - } -} + if (!op) { + // Composing out of bounds is a no-op, as when removing the last operation + // in the list. + return; + } -function shouldDisplayIfHelperContent(result) { - var truthy = result && get(result, 'isTruthy'); - if (typeof truthy === 'boolean') { return truthy; } + if (index > 0) { + otherOp = this._operations[index-1]; + if (otherOp.type === op.type) { + op.count += otherOp.count; + this._operations.splice(index-1, 1); + --index; + } + } - if (Ember.isArray(result)) { - return get(result, 'length') !== 0; - } else { - return !!result; - } -} - -/** - '_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. - - This would not be typically invoked by directly. - - @private - @method _triageMustache - @for Ember.Handlebars.helpers - @param {String} property Property/helperID to triage - @param {Object} options hash of template/rendering options - @return {String} HTML string -*/ -EmberHandlebars.registerHelper('_triageMustache', function(property, options) { - Ember.assert("You cannot pass more than one argument to the _triageMustache helper", arguments.length <= 2); - - if (helpers[property]) { - return helpers[property].call(this, options); - } + 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); + } + } + }, - var helper = Ember.Handlebars.resolveHelper(options.data.view.container, property); - if (helper) { - return helper.call(this, options); - } + toString: function () { + var str = ""; + EnumerableUtils.forEach(this._operations, function (operation) { + str += " " + operation.type + ":" + operation.count; + }); + return str.substring(1); + } + }; + }); +define("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; - return helpers.bind.call(this, property, options); -}); + var RETAIN = 'r'; + var INSERT = 'i'; + var DELETE = 'd'; -Ember.Handlebars.resolveHelper = function(container, name) { + __exports__["default"] = TrackedArray; - if (!container || name.indexOf('-') === -1) { - return; - } + /** + 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. + + @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 = []; } - 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 length = get(items, 'length'); - var Component = componentLookup.lookupFactory(name, container); - if (Component) { - helper = EmberHandlebars.makeViewHelper(Component); - container.register('helper:' + name, helper); + if (length) { + this._operations = [new ArrayOperation(RETAIN, length, items)]; + } else { + this._operations = []; + } } - } - return helper; -}; - -/** - `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 - {{bind "content.title"}} - ``` - - 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. - - @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 -*/ -EmberHandlebars.registerHelper('bind', 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; - - if (!options.fn) { - return simpleBind(context, property, options); - } - - return bind.call(context, property, options, false, exists); -}); - -/** - Use the `boundIf` helper to create a conditional that re-evaluates - whenever the truthiness of the bound value changes. - - ```handlebars - {{#boundIf "content.shouldDisplayTitle"}} - {{content.title}} - {{/boundIf}} - ``` - - @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 -*/ -EmberHandlebars.registerHelper('boundIf', function boundIfHelper(property, fn) { - var context = (fn.contexts && fn.contexts.length) ? fn.contexts[0] : this; - - return bind.call(context, property, fn, true, shouldDisplayIfHelperContent, shouldDisplayIfHelperContent, ['isTruthy', 'length']); -}); - - -/** - @private - - Use the `unboundIf` helper to create a conditional that evaluates once. - - ```handlebars - {{#unboundIf "content.shouldDisplayTitle"}} - {{content.title}} - {{/unboundIf}} - ``` - - @method unboundIf - @for Ember.Handlebars.helpers - @param {String} property Property to bind - @param {Function} fn Context to provide for rendering - @return {String} HTML string -*/ -EmberHandlebars.registerHelper('unboundIf', function unboundIfHelper(property, fn) { - var context = (fn.contexts && fn.contexts.length) ? fn.contexts[0] : this, - data = fn.data, - template = fn.fn, - inverse = fn.inverse, - normalized, propertyValue, result; - - normalized = normalizePath(context, property, data); - propertyValue = handlebarsGet(context, property, fn); - - if (!shouldDisplayIfHelperContent(propertyValue)) { - template = inverse; - } - template(context, { data: data }); -}); + TrackedArray.RETAIN = RETAIN; + TrackedArray.INSERT = INSERT; + TrackedArray.DELETE = DELETE; -/** - Use the `{{with}}` helper when you want to scope context. Take the following code as an example: + TrackedArray.prototype = { - ```handlebars -
    {{user.name}}
    + /** + Track that `newItems` were added to the tracked array at `index`. -
    -
    {{user.role.label}}
    - {{user.role.id}} + @method addItems + @param index + @param newItems + */ + addItems: function (index, newItems) { + var count = get(newItems, 'length'); + if (count < 1) { return; } + + var match = this._findArrayOperation(index), + arrayOperation = match.operation, + arrayOperationIndex = match.index, + arrayOperationRangeStart = match.rangeStart, + composeIndex, + splitIndex, + splitItems, + splitArrayOperation, + newArrayOperation; + + newArrayOperation = new ArrayOperation(INSERT, count, newItems); + + 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; + } -

    {{user.role.description}}

    -
    - ``` + this._composeInsert(composeIndex); + }, - `{{with}}` can be our best friend in these cases, - instead of writing `user.role.*` over and over, we use `{{#with user.role}}`. - Now the context within the `{{#with}} .. {{/with}}` block is `user.role` so you can do the following: + /** + Track that `count` items were removed at `index`. - ```handlebars -
    {{user.name}}
    + @method removeItems + @param index + @param count + */ + removeItems: function (index, count) { + if (count < 1) { return; } + + var match = this._findArrayOperation(index), + arrayOperation = match.operation, + arrayOperationIndex = match.index, + arrayOperationRangeStart = match.rangeStart, + 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; + } -
    - {{#with user.role}} -
    {{label}}
    - {{id}} + return this._composeDelete(composeIndex); + }, -

    {{description}}

    - {{/with}} -
    - ``` + /** + Apply all operations, reducing them to retain:n, for `n`, the number of + items in the array. - ### `as` operator + `callback` will be called for each operation and will be passed the following arguments: - This operator aliases the scope to a new name. It's helpful for semantic clarity and to retain - default scope or to reference from another `{{with}}` block. + * {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}` - ```handlebars - // posts might not be - {{#with user.posts as blogPosts}} -
    - There are {{blogPosts.length}} blog posts written by {{user.name}}. -
    + @method apply + @param {function} callback + */ + apply: function (callback) { + var items = [], + offset = 0; - {{#each post in blogPosts}} -
  • {{post.title}}
  • - {{/each}} - {{/with}} - ``` + forEach(this._operations, function (arrayOperation, operationIndex) { + callback(arrayOperation.items, offset, arrayOperation.type, operationIndex); - Without the `as` operator, it would be impossible to reference `user.name` in the example above. + if (arrayOperation.type !== DELETE) { + offset += arrayOperation.count; + items = items.concat(arrayOperation.items); + } + }); - 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}}`. + this._operations = [new ArrayOperation(RETAIN, items.length, items)]; + }, - ### `controller` option + /** + Return an `ArrayOperationMatch` for the operation that contains the item at `index`. - Adding `controller='something'` instructs the `{{with}}` helper to create and use an instance of - the specified controller with the new context as its content. + @method _findArrayOperation - This is very similar to using an `itemController` option with the `{{each}}` helper. + @param {number} index the index of the item whose operation information + should be returned. + @private + */ + _findArrayOperation: function (index) { + var arrayOperationIndex, + len, + split = false, + arrayOperation, + arrayOperationRangeStart, + arrayOperationRangeEnd; - ```handlebars - {{#with users.posts controller='userBlogPosts'}} - {{!- The current context is wrapped in our controller instance }} - {{/with}} - ``` + // 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]; - In the above example, the template provided to the `{{with}}` block is now wrapped in the - `userBlogPost` controller, which provides a very elegant way to decorate the context with custom - functions/properties. + if (arrayOperation.type === DELETE) { continue; } - @method with - @for Ember.Handlebars.helpers - @param {Function} context - @param {Hash} options - @return {String} HTML string -*/ -EmberHandlebars.registerHelper('with', function withHelper(context, options) { - if (arguments.length === 4) { - var keywordName, path, rootPath, normalized, contextPath; + arrayOperationRangeEnd = arrayOperationRangeStart + arrayOperation.count - 1; - Ember.assert("If you pass more than one argument to the with helper, it must be in the form #with foo as bar", arguments[1] === "as"); - options = arguments[3]; - keywordName = arguments[2]; - path = arguments[0]; + if (index === arrayOperationRangeStart) { + break; + } else if (index > arrayOperationRangeStart && index <= arrayOperationRangeEnd) { + split = true; + break; + } else { + arrayOperationRangeStart = arrayOperationRangeEnd + 1; + } + } - Ember.assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop); + return new ArrayOperationMatch(arrayOperation, arrayOperationIndex, split, arrayOperationRangeStart); + }, - var localizedOptions = o_create(options); - localizedOptions.data = o_create(options.data); - localizedOptions.data.keywords = o_create(options.data.keywords || {}); + _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); - if (Ember.isGlobalPath(path)) { - contextPath = path; - } else { - normalized = normalizePath(this, path, options.data); - path = normalized.path; - rootPath = normalized.root; + // truncate LHS + arrayOperation.count = splitIndex; + arrayOperation.items = arrayOperation.items.slice(0, splitIndex); - // This is a workaround for the fact that you cannot bind separate objects - // together. When we implement that functionality, we should use it here. - var contextKey = Ember.$.expando + Ember.guidFor(rootPath); - localizedOptions.data.keywords[contextKey] = rootPath; - // if the path is '' ("this"), just bind directly to the current context - contextPath = path ? contextKey + '.' + path : contextKey; - } + this._operations.splice(arrayOperationIndex + 1, 0, newArrayOperation, splitArrayOperation); + }, - Ember.bind(localizedOptions.data.keywords, keywordName, contextPath); + // see SubArray for a better implementation. + _composeInsert: function (index) { + var newArrayOperation = this._operations[index], + leftArrayOperation = this._operations[index-1], // may be undefined + rightArrayOperation = this._operations[index+1], // may be undefined + leftOp = leftArrayOperation && leftArrayOperation.type, + rightOp = rightArrayOperation && rightArrayOperation.type; + + if (leftOp === INSERT) { + // merge left + leftArrayOperation.count += newArrayOperation.count; + leftArrayOperation.items = leftArrayOperation.items.concat(newArrayOperation.items); + + 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); + } + }, - return bind.call(this, path, localizedOptions, true, exists); - } else { - Ember.assert("You must pass exactly one argument to the with helper", arguments.length === 2); - Ember.assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop); - return helpers.bind.call(options.contexts[0], context, options); - } -}); - - -/** - See [boundIf](/api/classes/Ember.Handlebars.helpers.html#method_boundIf) - and [unboundIf](/api/classes/Ember.Handlebars.helpers.html#method_unboundIf) - - @method if - @for Ember.Handlebars.helpers - @param {Function} context - @param {Hash} options - @return {String} HTML string -*/ -EmberHandlebars.registerHelper('if', function ifHelper(context, options) { - Ember.assert("You must pass exactly one argument to the if helper", arguments.length === 2); - Ember.assert("You must pass a block to the if helper", options.fn && options.fn !== Handlebars.VM.noop); - if (options.data.isUnbound) { - return helpers.unboundIf.call(options.contexts[0], context, options); - } else { - return helpers.boundIf.call(options.contexts[0], context, options); - } -}); - -/** - @method unless - @for Ember.Handlebars.helpers - @param {Function} context - @param {Hash} options - @return {String} HTML string -*/ -EmberHandlebars.registerHelper('unless', function unlessHelper(context, options) { - Ember.assert("You must pass exactly one argument to the unless helper", arguments.length === 2); - Ember.assert("You must pass a block to the unless helper", options.fn && options.fn !== Handlebars.VM.noop); - - var fn = options.fn, inverse = options.inverse; - - options.fn = inverse; - options.inverse = fn; - - if (options.data.isUnbound) { - return helpers.unboundIf.call(options.contexts[0], context, options); - } else { - return helpers.boundIf.call(options.contexts[0], context, options); - } -}); + _composeDelete: function (index) { + var arrayOperation = this._operations[index], + deletesToGo = arrayOperation.count, + leftArrayOperation = this._operations[index-1], // may be undefined + leftOp = leftArrayOperation && leftArrayOperation.type, + nextArrayOperation, + nextOp, + nextCount, + removeNewAndNextOp = false, + removedItems = []; + + if (leftOp === DELETE) { + arrayOperation = leftArrayOperation; + index -= 1; + } + + for (var i = index + 1; deletesToGo > 0; ++i) { + nextArrayOperation = this._operations[i]; + nextOp = nextArrayOperation.type; + nextCount = nextArrayOperation.count; + + if (nextOp === DELETE) { + arrayOperation.count += nextCount; + continue; + } -/** - `bind-attr` allows you to create a binding between DOM element attributes and - Ember objects. For example: + 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; - ```handlebars - imageTitle - ``` + // In the case where we truncate the last arrayOperation, we don't need to + // remove it; also the deletesToGo reduction is not the entirety of + // nextCount + i -= 1; + nextCount = deletesToGo; - The above handlebars template will fill the ``'s `src` attribute will - the value of the property referenced with `"imageUrl"` and its `alt` - attribute with the value of the property referenced with `"imageTitle"`. + 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 the rendering context of this template is the following object: + if (nextOp === INSERT) { + // d:2 i:3 will result in delete going away + arrayOperation.count -= nextCount; + } + } - ```javascript - { - imageUrl: 'http://lolcats.info/haz-a-funny', - imageTitle: 'A humorous image of a cat' - } - ``` + 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); + } - The resulting HTML output will be: + return removedItems; + }, - ```html - A humorous image of a cat - ``` + toString: function () { + var str = ""; + forEach(this._operations, function (operation) { + str += " " + operation.type + ":" + operation.count; + }); + return str.substring(1); + } + }; - `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: + /** + Internal data structure to represent an array operation. - ```handlebars - imageTitle - ``` + @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; + } - ### `bind-attr` and the `class` attribute + /** + Internal data structure used to include information when looking up operations + by item index. - `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: + @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; + } + }); +define("ember-testing", + ["ember-metal/core","ember-testing/initializers","ember-testing/support","ember-testing/setup_for_testing","ember-testing/test","ember-testing/adapters/adapter","ember-testing/adapters/qunit","ember-testing/helpers"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__) { + "use strict"; + var Ember = __dependency1__["default"]; - * a string return value of an object's property. - * a boolean return value of an object's property - * a hard-coded value + // to setup initializer + // to handle various edge cases - 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: + var setupForTesting = __dependency4__["default"]; + var Test = __dependency5__["default"]; + var Adapter = __dependency6__["default"]; + var QUnitAdapter = __dependency7__["default"]; + // adds helpers to helpers object in Test - ```javascript - AView = Ember.View.extend({ - someProperty: function() { - return "aValue"; - }.property() - }) - ``` + /** + Ember Testing - ```handlebars - - ``` + /** + @module ember + @submodule ember-testing + */ - 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`. + /** + The primary purpose of this class is to create hooks that can be implemented + by an adapter for various test frameworks. - A class name is provided via the syntax - `somePropertyName:class-name-if-true`. + @class Adapter + @namespace Ember.Test + */ + var Adapter = EmberObject.extend({ + /** + This callback will be called whenever an async operation is about to start. - ```javascript - AView = Ember.View.extend({ - someBool: true - }) - ``` + Override this to call your framework's methods that handle async + operations. - ```handlebars - - ``` + @public + @method asyncStart + */ + asyncStart: Ember.K, - Result in the following rendered output: + /** + This callback will be called whenever an async operation has completed. - ```html - - ``` + @public + @method asyncEnd + */ + asyncEnd: Ember.K, - 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: + /** + Override this method with your testing framework's false assertion. + This function is called whenever an exception occurs causing the testing + promise to fail. - ```handlebars - - ``` + QUnit example: - A hard-coded value can be used by prepending `:` to the desired - class name: `:class-name-to-always-apply`. + ```javascript + exception: function(error) { + ok(false, error); + }; + ``` - ```handlebars - - ``` + @public + @method exception + @param {String} error The exception to be raised. + */ + exception: function(error) { + throw error; + } + }); - Results in the following rendered output: + __exports__["default"] = Adapter; + }); +define("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; - ```html - - ``` + /** + This class implements the methods defined by Ember.Test.Adapter for the + QUnit testing framework. - All three strategies - string return value, boolean return value, and - hard-coded value – can be combined in a single declaration: + @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)); + } + }); + }); +define("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"]; - ```handlebars - - ``` + /** + * @module ember + * @submodule ember-testing + */ - @method bind-attr - @for Ember.Handlebars.helpers - @param {Hash} options - @return {String} HTML string -*/ -EmberHandlebars.registerHelper('bind-attr', function bindAttrHelper(options) { + var helper = Test.registerHelper; + var asyncHelper = Test.registerAsyncHelper; + var countAsync = 0; - var attrs = options.hash; + function currentRouteName(app){ + var appController = app.__container__.lookup('controller:application'); - Ember.assert("You must specify at least one hash argument to bind-attr", !!Ember.keys(attrs).length); + return get(appController, 'currentRouteName'); + } - var view = options.data.view; - var ret = []; - var ctx = this; + function currentPath(app){ + var appController = app.__container__.lookup('controller:application'); - // Generate a unique id for this element. This will be added as a - // data attribute to the element so it can be looked up when - // the bound property changes. - var dataId = ++Ember.uuid; + return get(appController, 'currentPath'); + } - // Handle classes differently, as we can bind multiple classes - var classBindings = attrs['class']; - if (classBindings != null) { - var classResults = EmberHandlebars.bindClasses(this, classBindings, view, dataId, options); + function currentURL(app){ + var router = app.__container__.lookup('router:main'); - ret.push('class="' + Handlebars.Utils.escapeExpression(classResults.join(' ')) + '"'); - delete attrs['class']; - } + return get(router, 'location').getURL(); + } - var attrKeys = Ember.keys(attrs); + function visit(app, url) { + var router = app.__container__.lookup('router:main'); + router.location.setURL(url); - // For each attribute passed, create an observer and emit the - // current value of the property as an attribute. - forEach.call(attrKeys, function(attr) { - var path = attrs[attr], - normalized; + if (app._readinessDeferrals > 0) { + router['initialURL'] = url; + run(app, 'advanceReadiness'); + delete router['initialURL']; + } else { + run(app, app.handleURL, url); + } - Ember.assert(fmt("You must provide an expression as the value of bound attribute. You specified: %@=%@", [attr, path]), typeof path === 'string'); + return app.testHelpers.wait(); + } - normalized = normalizePath(ctx, path, options.data); + function click(app, selector, context) { + var $el = app.testHelpers.findWithAssert(selector, context); + run($el, 'mousedown'); - var value = (path === 'this') ? normalized.root : handlebarsGet(ctx, path, options), - type = Ember.typeOf(value); + 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'); + } + }); + } + } - Ember.assert(fmt("Attributes must be numbers, strings or booleans, not %@", [value]), value === null || value === undefined || type === 'number' || type === 'string' || type === 'boolean'); + run($el, 'mouseup'); + run($el, 'click'); - var observer, invoker; + return app.testHelpers.wait(); + } - observer = function observer() { - var result = handlebarsGet(ctx, path, options); + function triggerEvent(app, selector, context, type, options){ + if (arguments.length === 3) { + // context and options are optional, so this is + // app, selector, type + type = context; + context = null; + options = {}; + } - 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'); + if (arguments.length === 4) { + // context and options are optional, so this is + if (typeof type === "object") { // either + // app, selector, type, options + options = type; + type = context; + context = null; + } else { // or + // app, selector, context, type + options = {}; + } + } - var elem = view.$("[data-bindattr-" + dataId + "='" + dataId + "']"); + var $el = app.testHelpers.findWithAssert(selector, context); - // If we aren't able to find the element, it means the element - // to which we were bound has been removed from the view. - // In that case, we can assume the template has been re-rendered - // and we need to clean up the observer. - if (!elem || elem.length === 0) { - Ember.removeObserver(normalized.root, normalized.path, invoker); - return; - } + var event = jQuery.Event(type, options); - Ember.View.applyAttributeBindings(elem, attr, result); - }; + run($el, 'trigger', event); - // Add an observer to the view for when the property changes. - // When the observer fires, find the element using the - // unique data id and update the attribute to the new value. - // Note: don't add observer when path is 'this' or path - // is whole keyword e.g. {{#each x in list}} ... {{bind-attr attr="x"}} - if (path !== 'this' && !(normalized.isKeyword && normalized.path === '' )) { - view.registerObserver(normalized.root, normalized.path, observer); - } - - // if this changes, also change the logic in ember-views/lib/views/view.js - if ((type === 'string' || (type === 'number' && !isNaN(value)))) { - ret.push(attr + '="' + Handlebars.Utils.escapeExpression(value) + '"'); - } else if (value && type === 'boolean') { - // The developer controls the attr name, so it should always be safe - ret.push(attr + '="' + attr + '"'); - } - }, this); - - // Add the unique identifier - // NOTE: We use all lower-case since Firefox has problems with mixed case in SVG - ret.push('data-bindattr-' + dataId + '="' + dataId + '"'); - return new EmberHandlebars.SafeString(ret.join(' ')); -}); - -/** - See `bind-attr` - - @method bindAttr - @for Ember.Handlebars.helpers - @deprecated - @param {Function} context - @param {Hash} options - @return {String} HTML string -*/ -EmberHandlebars.registerHelper('bindAttr', function bindAttrHelper() { - Ember.warn("The 'bindAttr' view helper is deprecated in favor of 'bind-attr'"); - return EmberHandlebars.helpers['bind-attr'].apply(this, arguments); -}); - -/** - 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. - - 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. - - @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 {Ember.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 -*/ -EmberHandlebars.bindClasses = function(context, classBindings, view, bindAttrId, options) { - var ret = [], newClass, value, elem; - - // Helper method to retrieve the property from the context and - // determine which class string to return, based on whether it is - // a Boolean or not. - var classStringForPath = function(root, parsedPath, options) { - var val, - path = parsedPath.path; - - if (path === 'this') { - val = root; - } else if (path === '') { - val = true; - } else { - val = handlebarsGet(root, path, options); + return app.testHelpers.wait(); } - return Ember.View._classStringForValue(path, val, parsedPath.className, parsedPath.falsyClassName); - }; - - // For each property passed, loop through and setup - // an observer. - forEach.call(classBindings.split(' '), function(binding) { + function keyEvent(app, selector, context, type, keyCode) { + if (typeof keyCode === 'undefined') { + keyCode = type; + type = context; + context = null; + } - // 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; + return app.testHelpers.triggerEvent(selector, context, type, { keyCode: keyCode, which: keyCode }); + } - var observer, invoker; + function fillIn(app, selector, context, text) { + var $el; + if (typeof text === 'undefined') { + text = context; + context = null; + } + $el = app.testHelpers.findWithAssert(selector, context); + run(function() { + $el.val(text).change(); + }); + return app.testHelpers.wait(); + } - var parsedPath = Ember.View._parsePropertyPath(binding), - path = parsedPath.path, - pathRoot = context, - normalized; + function findWithAssert(app, selector, context) { + var $el = app.testHelpers.find(selector, context); + if ($el.length === 0) { + throw new EmberError("Element " + selector + " not found."); + } + return $el; + } - if (path !== '' && path !== 'this') { - normalized = normalizePath(context, path, options.data); + function find(app, selector, context) { + var $el; + context = context || get(app, 'rootElement'); + $el = app.$(selector, context); - pathRoot = normalized.root; - path = normalized.path; + return $el; } - // Set up an observer on the context. If the property changes, toggle the - // class name. - observer = function() { - // Get the current value of the property - newClass = classStringForPath(context, parsedPath, options); - elem = bindAttrId ? view.$("[data-bindattr-" + bindAttrId + "='" + bindAttrId + "']") : view.$(); + function andThen(app, callback) { + return app.testHelpers.wait(callback(app)); + } - // If we can't find the element anymore, a parent template has been - // re-rendered and we've been nuked. Remove the observer. - if (!elem || elem.length === 0) { - Ember.removeObserver(pathRoot, path, invoker); - } else { - // If we had previously added a class to the element, remove it. - if (oldClass) { - elem.removeClass(oldClass); + 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(); } - // 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; - } - } - }; + // 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; } - if (path !== '' && path !== 'this') { - view.registerObserver(pathRoot, path, observer); - } + // 2. If there are pending Ajax requests, keep polling + if (Test.pendingAjaxRequests) { return; } - // We've already setup the observer; now we just need to figure out the - // correct behavior right now on the first pass through. - value = classStringForPath(context, parsedPath, options); + // 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(); + } - if (value) { - ret.push(value); + // Synchronously resolve the promise + run(null, resolve, value); + }, 10); + }); - // Make sure we save the current value so that it can be removed if the - // observer fires. - oldClass = value; } - }); - return ret; -}; + /** + * 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); -(function() { -/*globals Handlebars */ + /** + * 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); -// TODO: Don't require the entire module -/** -@module ember -@submodule ember-handlebars -*/ + /** + * 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); -var get = Ember.get, set = Ember.set; -var EmberHandlebars = Ember.Handlebars; -var LOWERCASE_A_Z = /^[a-z]/; -var VIEW_PREFIX = /^view\./; + /** + 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. -function makeBindings(thisContext, options) { - var hash = options.hash, - hashType = options.hashTypes; + This is most often used as the return value for the helper functions (see 'click', + 'fillIn','visit',etc). - for (var prop in hash) { - if (hashType[prop] === 'ID') { + Example: - var value = hash[prop]; + ```javascript + Ember.Test.registerAsyncHelper('loginUser', function(app, username, password) { + visit('secured/path/here') + .fillIn('#username', username) + .fillIn('#password', username) + .click('.submit') - if (Ember.IS_BINDING.test(prop)) { - 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 + "."); - } else { - hash[prop + 'Binding'] = value; - hashType[prop + 'Binding'] = 'STRING'; - delete hash[prop]; - delete hashType[prop]; - } - } - } + return app.testHelpers.wait(); + }); - if (hash.hasOwnProperty('idBinding')) { - // id can't be bound, so just perform one-time lookup. - hash.id = EmberHandlebars.get(thisContext, hash.idBinding, options); - hashType.id = 'STRING'; - delete hash.idBinding; - delete hashType.idBinding; - } -} + @method wait + @param {Object} value The value to be returned. + @return {RSVP.Promise} + */ + asyncHelper('wait', wait); + asyncHelper('andThen', andThen); -EmberHandlebars.ViewHelper = Ember.Object.create({ - propertiesFromHTMLOptions: function(options) { - var hash = options.hash, data = options.data; - var extensions = {}, - classes = hash['class'], - dup = false; + /** + Returns the currently active route name. - if (hash.id) { - extensions.elementId = hash.id; - dup = true; - } + Example: - if (hash.tag) { - extensions.tagName = hash.tag; - dup = true; + ```javascript + function validateRouteName(){ + equal(currentRouteName(), 'some.path', "correct route was transitioned into."); } - if (classes) { - classes = classes.split(' '); - extensions.classNames = classes; - dup = true; - } + visit('/some/path').then(validateRouteName) + ``` - if (hash.classBinding) { - extensions.classNameBindings = hash.classBinding.split(' '); - dup = true; - } + @method currentRouteName + @return {Object} The name of the currently active route. + @since 1.5.0 + */ + helper('currentRouteName', currentRouteName); - if (hash.classNameBindings) { - if (extensions.classNameBindings === undefined) extensions.classNameBindings = []; - extensions.classNameBindings = extensions.classNameBindings.concat(hash.classNameBindings.split(' ')); - dup = true; - } + /** + Returns the current path. - if (hash.attributeBindings) { - Ember.assert("Setting 'attributeBindings' via Handlebars is not allowed. Please subclass Ember.View and set it there instead."); - extensions.attributeBindings = null; - dup = true; - } + Example: - if (dup) { - hash = Ember.$.extend({}, hash); - delete hash.id; - delete hash.tag; - delete hash['class']; - delete hash.classBinding; + ```javascript + function validateURL(){ + equal(currentPath(), 'some.path.index', "correct path was transitioned into."); } - // 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 path; - - // Evaluate the context of regular attribute bindings: - for (var prop in hash) { - if (!hash.hasOwnProperty(prop)) { continue; } + click('#some-link-id').then(validateURL); + ``` - // Test if the property ends in "Binding" - if (Ember.IS_BINDING.test(prop) && typeof hash[prop] === 'string') { - path = this.contextualizeBindingPath(hash[prop], data); - if (path) { hash[prop] = path; } - } - } + @method currentPath + @return {Object} The currently active path. + @since 1.5.0 + */ + helper('currentPath', currentPath); - // Evaluate the context of class name bindings: - if (extensions.classNameBindings) { - for (var b in extensions.classNameBindings) { - var full = extensions.classNameBindings[b]; - if (typeof full === 'string') { - // Contextualize the path of classNameBinding so this: - // - // classNameBinding="isGreen:green" - // - // is converted to this: - // - // classNameBinding="_parentView.context.isGreen:green" - var parsedPath = Ember.View._parsePropertyPath(full); - path = this.contextualizeBindingPath(parsedPath.path, data); - if (path) { extensions.classNameBindings[b] = path + parsedPath.classNames; } - } - } - } + /** + Returns the current URL. - return Ember.$.extend(hash, extensions); - }, + Example: - // Transform bindings from the current context to a context that can be evaluated within the view. - // Returns null if the path shouldn't be changed. - // - // TODO: consider the addition of a prefix that would allow this method to return `path`. - contextualizeBindingPath: function(path, data) { - var normalized = Ember.Handlebars.normalizePath(null, path, data); - if (normalized.isKeyword) { - return 'templateData.keywords.' + path; - } else if (Ember.isGlobalPath(path)) { - return null; - } else if (path === 'this' || path === '') { - return '_parentView.context'; - } else { - return '_parentView.context.' + path; + ```javascript + function validateURL(){ + equal(currentURL(), '/some/path', "correct URL was transitioned into."); } - }, - helper: function(thisContext, path, options) { - var data = options.data, - fn = options.fn, - newView; - - makeBindings(thisContext, options); + click('#some-link-id').then(validateURL); + ``` - if ('string' === typeof path) { + @method currentURL + @return {Object} The currently active URL. + @since 1.5.0 + */ + helper('currentURL', currentURL); - // TODO: this is a lame conditional, this should likely change - // but something along these lines will likely need to be added - // as deprecation warnings - // - if (options.types[0] === 'STRING' && LOWERCASE_A_Z.test(path) && !VIEW_PREFIX.test(path)) { - Ember.assert("View requires a container", !!data.view.container); - newView = data.view.container.lookupFactory('view:' + path); - } else { - newView = EmberHandlebars.get(thisContext, path, options); - } + /** + Triggers the given DOM event on the element identified by the provided selector. - Ember.assert("Unable to find view at path '" + path + "'", !!newView); - } else { - newView = path; - } + Example: - Ember.assert(Ember.String.fmt('You must pass a view to the #view helper, not %@ (%@)', [path, newView]), Ember.View.detect(newView) || Ember.View.detectInstance(newView)); + ```javascript + triggerEvent('#some-elem-id', 'blur'); + ``` - var viewOptions = this.propertiesFromHTMLOptions(options, thisContext); - var currentView = data.view; - viewOptions.templateData = data; - var newViewProto = newView.proto ? newView.proto() : newView; + This is actually used internally by the `keyEvent` helper like so: - 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; - } + ```javascript + triggerEvent('#some-elem-id', 'keypress', { keyCode: 13 }); + ``` - // 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; - } + @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); + }); +define("ember-testing/initializers", + ["ember-runtime/system/lazy_load"], + function(__dependency1__) { + "use strict"; + var onLoad = __dependency1__.onLoad; - currentView.appendChild(newView, viewOptions); - } -}); + var name = 'deferReadiness in `testing` mode'; -/** - `{{view}}` inserts a new instance of `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. + onLoad('Ember.Application', function(Application) { + if (!Application.initializers[name]) { + Application.initializer({ + name: name, - An empty `` and the following template: + initialize: function(container, application){ + if (application.testing) { + application.deferReadiness(); + } + } + }); + } + }); + }); +define("ember-testing/setup_for_testing", + ["ember-metal/core","ember-testing/adapters/qunit","ember-views/system/jquery","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // import Test from "ember-testing/test"; // ES6TODO: fix when cycles are supported + var QUnitAdapter = __dependency2__["default"]; + var jQuery = __dependency3__["default"]; - ```handlebars - A span: - {{#view tagName="span"}} - hello. - {{/view}} - ``` + var Test, requests; - Will result in HTML structure: + function incrementAjaxPendingRequests(_, xhr){ + requests.push(xhr); + Test.pendingAjaxRequests = requests.length; + } - ```html - - + function decrementAjaxPendingRequests(_, xhr){ + for (var i=0;i - A span: - - Hello. - -
    - - ``` + /** + Sets Ember up for testing. This is useful to perform + basic setup steps in order to unit test. - ### `parentView` setting + Use `App.setupForTesting` to perform integration tests (full + application testing). - 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. + @method setupForTesting + @namespace Ember + @since 1.5.0 + */ + __exports__["default"] = function setupForTesting() { + if (!Test) { Test = requireModule('ember-testing/test')['default']; } - ```javascript - aView = Ember.View.create({ - template: Ember.Handlebars.compile("{{#view}} my parent: {{parentView.elementId}} {{/view}}") - }); + Ember.testing = true; - aView.appendTo('body'); - ``` + // if adapter is not manually set default to QUnit + if (!Test.adapter) { + Test.adapter = QUnitAdapter.create(); + } - Will result in HTML structure: + requests = []; + Test.pendingAjaxRequests = requests.length; - ```html -
    -
    - my parent: ember1 -
    -
    - ``` - - ### Setting CSS id and class attributes - - 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}} - ``` - - Results in the following HTML structure: - - ```html -
    - - hello. - -
    - ``` - - 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 HTML structure: - - ```html -
    - - hello. - -
    - ``` - - ### 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 "MyApp.CustomView"}} - hello. - {{/view}} - ``` - - The first argument can also be a relative path accessible from the current - context. - - ```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}}') + jQuery(document).off('ajaxSend', incrementAjaxPendingRequests); + jQuery(document).off('ajaxComplete', decrementAjaxPendingRequests); + jQuery(document).on('ajaxSend', incrementAjaxPendingRequests); + jQuery(document).on('ajaxComplete', decrementAjaxPendingRequests); + } }); +define("ember-testing/support", + ["ember-metal/core","ember-views/system/jquery"], + function(__dependency1__, __dependency2__) { + "use strict"; + var Ember = __dependency1__["default"]; + var jQuery = __dependency2__["default"]; - MyApp.OuterView.create().appendTo('body'); - ``` + /** + @module ember + @submodule ember-testing + */ - Will result in the following HTML: + var $ = jQuery; - ```html -
    -
    - hi -
    -
    - ``` + /** + This method creates a checkbox and triggers the click event to fire the + passed in handler. It is used to correct for a bug in older versions + of jQuery (e.g 1.8.3). - ### Blockless use + @private + @method testCheckboxClick + */ + function testCheckboxClick(handler) { + $('') + .css({ position: 'absolute', left: '-1000px', top: '-1000px' }) + .appendTo('body') + .on('click', handler) + .trigger('click') + .remove(); + } - 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. + $(function() { + /* + Determine whether a checkbox checked using jQuery's "click" method will have + the correct value for its checked property. - ```handlebars - {{view "MyApp.ViewWithATemplateDefined"}} - ``` + 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; + } + } + }; + } + }); - ### `viewName` property + // 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); + }); + }); + }); +define("ember-testing/test", + ["ember-metal/core","ember-metal/run_loop","ember-metal/platform","ember-runtime/compare","ember-runtime/ext/rsvp","ember-testing/setup_for_testing","ember-application/system/application","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + var emberRun = __dependency2__["default"]; + var create = __dependency3__.create; + var compare = __dependency4__["default"]; + var RSVP = __dependency5__["default"]; + var setupForTesting = __dependency6__["default"]; + var EmberApplication = __dependency7__["default"]; - 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. + /** + @module ember + @submodule ember-testing + */ + var slice = [].slice; + var helpers = {}; + var injectHelpersCallbacks = []; - ```javascript - aView = Ember.View.create({ - template: Ember.Handlebars.compile('{{#view viewName="aChildByName"}} hi {{/view}}') - }); + /** + This is a container for an assortment of testing related functionality: - aView.appendTo('body'); - aView.get('aChildByName') // the instance of Ember.View created by {{view}} helper - ``` - - @method view - @for Ember.Handlebars.helpers - @param {String} path - @param {Hash} options - @return {String} HTML string -*/ -EmberHandlebars.registerHelper('view', function viewHelper(path, options) { - Ember.assert("The view helper only takes a single argument", arguments.length <= 2); - - // If no path is provided, treat path param as options. - if (path && path.data && path.data.isRenderData) { - options = path; - path = "Ember.View"; - } + * 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. - return EmberHandlebars.ViewHelper.helper(this, path, options); -}); + @class Test + @namespace Ember + */ + var Test = { + /** + Hash containing all known test helpers. + @property _helpers + @private + @since 1.7.0 + */ + _helpers: helpers, -})(); + /** + `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: -(function() { -// TODO: Don't require all of this module -/** -@module ember -@submodule ember-handlebars -*/ - -var get = Ember.get, handlebarsGet = Ember.Handlebars.get, fmt = Ember.String.fmt; - -/** - `{{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 - {{#collection contentBinding="App.items"}} - Hi {{view.content.name}} - {{/collection}} - ``` - - And the following application code - - ```javascript - App = Ember.Application.create() - App.items = [ - Ember.Object.create({name: 'Dave'}), - Ember.Object.create({name: 'Mary'}), - Ember.Object.create({name: 'Sara'}) - ] - ``` - - Will result in the HTML structure below - - ```html -
    -
    Hi Dave
    -
    Hi Mary
    -
    Hi Sara
    -
    - ``` - - ### Blockless use in a collection - - If you provide an `itemViewClass` option that has its own `template` you can - omit the block. - - The following template: - - ```handlebars - {{collection contentBinding="App.items" itemViewClass="App.AnItemView"}} - ``` - - And application code - - ```javascript - App = Ember.Application.create(); - App.items = [ - Ember.Object.create({name: 'Dave'}), - Ember.Object.create({name: 'Mary'}), - Ember.Object.create({name: 'Sara'}) - ]; - - App.AnItemView = Ember.View.extend({ - template: Ember.Handlebars.compile("Greetings {{view.content.name}}") - }); - ``` - - Will result in the HTML structure below - - ```html -
    -
    Greetings Dave
    -
    Greetings Mary
    -
    Greetings Sara
    -
    - ``` - - ### Specifying a CollectionView subclass - - 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 App.MyCustomCollectionClass contentBinding="App.items"}} - Hi {{view.content.name}} - {{/collection}} - ``` - - ### 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 contentBinding="App.items" - itemTagName="p" - itemClassNames="greeting"}} - Howdy {{view.content.name}} - {{/collection}} - ``` - - Will result in the following HTML structure: - - ```html -
    -

    Howdy Dave

    -

    Howdy Mary

    -

    Howdy Sara

    -
    - ``` - - @method collection - @for Ember.Handlebars.helpers - @param {String} path - @param {Hash} options - @return {String} HTML string - @deprecated Use `{{each}}` helper instead. -*/ -Ember.Handlebars.registerHelper('collection', 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); - } + ```javascript + Ember.Test.registerHelper('boot', function(app) { + Ember.run(app, app.advanceReadiness); + }); + ``` - var fn = options.fn; - var data = options.data; - var inverse = options.inverse; - var view = options.data.view; - - - var controller, container; - // If passed a path string, convert that into an object. - // Otherwise, just default to the standard class. - var collectionClass; - if (path) { - controller = data.keywords.controller; - container = controller && controller.container; - collectionClass = handlebarsGet(this, path, options) || container.lookupFactory('view:' + path); - Ember.assert(fmt("%@ #collection: Could not find collection class %@", [data.view, path]), !!collectionClass); - } - else { - collectionClass = Ember.CollectionView; - } + This helper can later be called without arguments because it will be + called with `app` as the first parameter. - var hash = options.hash, itemHash = {}, match; - - // Extract item view class if provided else default to the standard class - var collectionPrototype = collectionClass.proto(), - itemViewClass; - - if (hash.itemView) { - controller = data.keywords.controller; - Ember.assert('You specified an itemView, but the current context has no ' + - 'container to look the itemView up in. This probably means ' + - 'that you created a view manually, instead of through the ' + - 'container. Instead, use container.lookup("view:viewName"), ' + - 'which will properly instantiate your view.', - controller && controller.container); - container = controller.container; - itemViewClass = container.lookupFactory('view:' + hash.itemView); - Ember.assert('You specified the itemView ' + hash.itemView + ", but it was " + - "not found at " + container.describe("view:" + hash.itemView) + - " (and it was not registered in the container)", !!itemViewClass); - } else if (hash.itemViewClass) { - itemViewClass = handlebarsGet(collectionPrototype, hash.itemViewClass, options); - } else { - itemViewClass = collectionPrototype.itemViewClass; - } + ```javascript + App = Ember.Application.create(); + App.injectTestHelpers(); + boot(); + ``` - Ember.assert(fmt("%@ #collection: Could not find itemViewClass %@", [data.view, itemViewClass]), !!itemViewClass); + @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 } + }; + }, - delete hash.itemViewClass; - delete hash.itemView; + /** + `registerAsyncHelper` is used to register an async test helper that will be injected + when `App.injectTestHelpers` is called. - // Go through options passed to the {{collection}} helper and extract options - // that configure item views instead of the collection itself. - for (var prop in hash) { - if (hash.hasOwnProperty(prop)) { - match = prop.match(/^item(.)(.*)$/); + The helper method will always be called with the current Application as + the first parameter. - if (match && prop !== 'itemController') { - // Convert itemShouldFoo -> shouldFoo - itemHash[match[1].toLowerCase() + match[2]] = hash[prop]; - // Delete from hash as this will end up getting passed to the - // {{view}} helper method. - delete hash[prop]; - } - } - } + For example: - if (fn) { - itemHash.template = fn; - delete options.fn; - } + ```javascript + Ember.Test.registerAsyncHelper('boot', function(app) { + Ember.run(app, app.advanceReadiness); + }); + ``` - var emptyViewClass; - if (inverse && inverse !== Ember.Handlebars.VM.noop) { - emptyViewClass = get(collectionPrototype, 'emptyViewClass'); - emptyViewClass = emptyViewClass.extend({ - template: inverse, - tagName: itemHash.tagName - }); - } else if (hash.emptyViewClass) { - emptyViewClass = handlebarsGet(this, hash.emptyViewClass, options); - } - if (emptyViewClass) { hash.emptyView = emptyViewClass; } + The advantage of an async helper is that it will not run + until the last async helper has completed. All async helpers + after it will wait for it complete before running. - if (!hash.keyword) { - itemHash._context = Ember.computed.alias('content'); - } - var viewOptions = Ember.Handlebars.ViewHelper.propertiesFromHTMLOptions({ data: data, hash: itemHash }, this); - hash.itemViewClass = itemViewClass.extend(viewOptions); + For example: - return Ember.Handlebars.helpers.view.call(this, collectionClass, options); -}); + ```javascript + Ember.Test.registerAsyncHelper('deletePost', function(app, postId) { + click('.delete-' + postId); + }); + // ... in your test + visit('/post/2'); + deletePost(2); + visit('/post/3'); + deletePost(3); + ``` -})(); + @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 } + }; + }, + /** + Remove a previously added helper method. + Example: -(function() { -/*globals Handlebars */ -/** -@module ember -@submodule ember-handlebars -*/ - -var handlebarsGet = Ember.Handlebars.get; - -/** - `unbound` allows you to output a property without binding. *Important:* The - output will not be updated if the property changes. Use with caution. - - ```handlebars -
    {{unbound somePropertyThatDoesntChange}}
    - ``` - - `unbound` can also be used in conjunction with a bound helper to - render it in its unbound form: - - ```handlebars -
    {{unbound helperName somePropertyThatDoesntChange}}
    - ``` - - @method unbound - @for Ember.Handlebars.helpers - @param {String} property - @return {String} HTML string -*/ -Ember.Handlebars.registerHelper('unbound', function unboundHelper(property, fn) { - var options = arguments[arguments.length - 1], helper, context, out; - - if (arguments.length > 2) { - // Unbound helper call. - options.data.isUnbound = true; - helper = Ember.Handlebars.helpers[arguments[0]] || Ember.Handlebars.helpers.helperMissing; - out = helper.apply(this, Array.prototype.slice.call(arguments, 1)); - delete options.data.isUnbound; - return out; - } + ```javascript + Ember.Test.unregisterHelper('wait'); + ``` - context = (fn.contexts && fn.contexts.length) ? fn.contexts[0] : this; - return handlebarsGet(context, property, fn); -}); + @public + @method unregisterHelper + @param {String} name The helper to remove. + */ + unregisterHelper: function(name) { + delete helpers[name]; + delete Test.Promise.prototype[name]; + }, -})(); + /** + Used to register callbacks to be fired whenever `App.injectTestHelpers` + is called. + The callback will receive the current application as an argument. + Example: -(function() { -/*jshint debug:true*/ -/** -@module ember -@submodule ember-handlebars -*/ - -var get = Ember.get; -var handlebarsGet = Ember.Handlebars.get, normalizePath = Ember.Handlebars.normalizePath; -var a_slice = [].slice; - -/** - `log` allows you to output the value of variables in the current rendering - context. `log` also accepts primitive types such as strings or numbers. - - ```handlebars - {{log "myVariable:" myVariable }} - ``` - - @method log - @for Ember.Handlebars.helpers - @param {String} property -*/ -Ember.Handlebars.registerHelper('log', function logHelper() { - var params = a_slice.call(arguments, 0, -1), - options = arguments[arguments.length - 1], - logger = Ember.Logger.log, - values = [], - allowPrimitives = false; - - - allowPrimitives = true; - - - for (var i = 0; i < params.length; i++) { - var type = options.types[i]; - - if (type === 'ID' || !allowPrimitives) { - var context = (options.contexts && options.contexts[i]) || this, - normalized = normalizePath(context, params[i], options.data); - - if (normalized.path === 'this') { - values.push(normalized.root); - } else { - values.push(handlebarsGet(normalized.root, normalized.path, options)); - } - } else { - values.push(params[i]); - } - } + ```javascript + Ember.Test.onInjectHelpers(function() { + Ember.$(document).ajaxSend(function() { + Test.pendingAjaxRequests++; + }); - logger.apply(logger, values); -}); + Ember.$(document).ajaxComplete(function() { + Test.pendingAjaxRequests--; + }); + }); + ``` -/** - Execute the `debugger` statement in the current context. + @public + @method onInjectHelpers + @param {Function} callback The function to be called. + */ + onInjectHelpers: function(callback) { + injectHelpersCallbacks.push(callback); + }, - ```handlebars - {{debugger}} - ``` + /** + 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. - 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: + This method should be returned by async helpers such as `wait`. - - templateContext: this is most likely a controller - from which this template looks up / displays properties - - typeOfTemplateContext: a string description of - what the templateContext is + @public + @method promise + @param {Function} resolver The function used to resolve the promise. + */ + promise: function(resolver) { + return new Test.Promise(resolver); + }, - 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`: + /** + Used to allow ember-testing to communicate with a specific testing + framework. - ``` - > templateContext.get('foo') // -> "" - ``` + You can manually set it before calling `App.setupForTesting()`. - @method debugger - @for Ember.Handlebars.helpers - @param {String} property -*/ -Ember.Handlebars.registerHelper('debugger', function debuggerHelper(options) { + Example: - // These are helpful values you can inspect while debugging. - var templateContext = this; - var typeOfTemplateContext = Ember.inspect(templateContext); + ```javascript + Ember.Test.adapter = MyCustomAdapter.create() + ``` - debugger; -}); + If you do not set it, ember-testing will default to `Ember.Test.QUnitAdapter`. + @public + @property adapter + @type {Class} The adapter to be used. + @default Ember.Test.QUnitAdapter + */ + adapter: null, + /** + Replacement for `Ember.RSVP.resolve` + The only difference is this uses + an instance of `Ember.Test.Promise` -})(); + @public + @method resolve + @param {Mixed} The value to resolve + @since 1.2.0 + */ + resolve: function(val) { + return Test.promise(function(resolve) { + return resolve(val); + }); + }, + /** + This allows ember-testing to play nicely with other asynchronous + events, such as an application that is waiting for a CSS3 + transition or an IndexDB transaction. + + For example: + + ```javascript + Ember.Test.registerWaiter(function() { + return myPendingTransactions() == 0; + }); + ``` + The `context` argument allows you to optionally specify the `this` + with which your callback will be invoked. + + For example: + + ```javascript + Ember.Test.registerWaiter(MyDB, MyDB.hasPendingTransactions); + ``` + + @public + @method registerWaiter + @param {Object} context (optional) + @param {Function} callback + @since 1.2.0 + */ + registerWaiter: function(context, callback) { + if (arguments.length === 1) { + callback = context; + context = null; + } + if (!this.waiters) { + this.waiters = Ember.A(); + } + this.waiters.push([context, callback]); + }, + /** + `unregisterWaiter` is used to unregister a callback that was + registered with `registerWaiter`. + + @public + @method unregisterWaiter + @param {Object} context (optional) + @param {Function} callback + @since 1.2.0 + */ + unregisterWaiter: function(context, callback) { + var pair; + if (!this.waiters) { return; } + if (arguments.length === 1) { + callback = context; + context = null; + } + pair = [context, callback]; + this.waiters = Ember.A(this.waiters.filter(function(elt) { + return compare(elt, pair)!==0; + })); + } + }; + function helper(app, name) { + var fn = helpers[name].method, + meta = helpers[name].meta; -(function() { -/** -@module ember -@submodule ember-handlebars -*/ + return function() { + var args = slice.call(arguments), + lastPromise = Test.lastPromise; -var get = Ember.get, set = Ember.set; -var fmt = Ember.String.fmt; + args.unshift(app); -Ember.Handlebars.EachView = Ember.CollectionView.extend(Ember._Metamorph, { - init: function() { - var itemController = get(this, 'itemController'); - var binding; + // some helpers are not async and + // need to return a value immediately. + // example: `find` + if (!meta.wait) { + return fn.apply(app, args); + } - 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 - }); + 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); + }); + }); + } - this.disableContentObservers(function() { - set(this, 'content', controller); - binding = new Ember.Binding('content', '_eachView.dataSource').oneWay(); - binding.connect(controller); - }); + return lastPromise; + }; + } - set(this, '_arrayController', controller); - } else { - this.disableContentObservers(function() { - binding = new Ember.Binding('content', 'dataSource').oneWay(); - binding.connect(this); - }); + function run(fn) { + if (!emberRun.currentRunLoop) { + emberRun(fn); + } else { + fn(); + } } - return this._super(); - }, + 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. - _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]), - !Ember.ControllerMixin.detect(content) || - (content && content.isGenerated) || - content instanceof Ember.ArrayController); - Ember.assert(fmt("The value that #each loops over must be an Array. You passed %@", [(Ember.ControllerMixin.detect(content) && content.get('model') !== undefined) ? fmt("'%@' (wrapped in %@)", [content.get('model'), content]) : content]), Ember.Array.detect(content)); - }, + @property testHelpers + @type {Object} + @default {} + */ + testHelpers: {}, - disableContentObservers: function(callback) { - Ember.removeBeforeObserver(this, 'content', null, '_contentWillChange'); - Ember.removeObserver(this, 'content', null, '_contentDidChange'); + /** + This property will contain the original methods that were registered + on the `helperContainer` before `injectTestHelpers` is called. - callback.call(this); + When `removeTestHelpers` is called, these methods are restored to the + `helperContainer`. - Ember.addBeforeObserver(this, 'content', null, '_contentWillChange'); - Ember.addObserver(this, 'content', null, '_contentDidChange'); - }, + @property originalMethods + @type {Object} + @default {} + @private + @since 1.3.0 + */ + originalMethods: {}, - itemViewClass: Ember._MetamorphView, - emptyViewClass: Ember._MetamorphView, - createChildView: function(view, attrs) { - view = this._super(view, attrs); + /** + This property indicates whether or not this application is currently in + testing mode. This is set when `setupForTesting` is called on the current + application. - // At the moment, if a container view subclass wants - // to insert keywords, it is responsible for cloning - // the keywords hash. This will be fixed momentarily. - var keyword = get(this, 'keyword'); - var content = get(view, 'content'); + @property testing + @type {Boolean} + @default false + @since 1.3.0 + */ + testing: false, - if (keyword) { - var data = get(view, 'templateData'); + /** + 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). - data = Ember.copy(data); - data.keywords = view.cloneKeywords(); - set(view, 'templateData', data); + Example: - // In this case, we do not bind, because the `content` of - // a #each item cannot change. - data.keywords[keyword] = content; - } + ``` + App.setupForTesting(); + ``` - // 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); - } + @method setupForTesting + */ + setupForTesting: function() { + setupForTesting(); - return view; - }, + this.testing = true; - destroy: function() { - if (!this._super()) { return; } + this.Router.reopen({ + location: 'none' + }); + }, - var arrayController = get(this, '_arrayController'); + /** + This will be used as the container to inject the test helpers into. By + default the helpers are injected into `window`. - if (arrayController) { - arrayController.destroy(); - } + @property helperContainer + @type {Object} The object to be used for test helpers. + @default window + @since 1.2.0 + */ + helperContainer: window, - return this; - } -}); - -// Defeatureify doesn't seem to like nested functions that need to be removed -function _addMetamorphCheck() { - Ember.Handlebars.EachView.reopen({ - _checkMetamorph: Ember.on('didInsertElement', function() { - Ember.assert("The metamorph tags, " + - this.morph.start + " and " + this.morph.end + - ", have different parents.\nThe browser has fixed your template to output valid HTML (for example, check that you have properly closed all tags and have used a TBODY tag when creating a table with '{{#each}}')", - document.getElementById( this.morph.start ).parentNode === - document.getElementById( this.morph.end ).parentNode - ); - }) - }); -} - -Ember.runInDebug( function() { - _addMetamorphCheck(); -}); - -var GroupedEach = Ember.Handlebars.GroupedEach = function(context, path, options) { - var self = this, - normalized = Ember.Handlebars.normalizePath(context, path, options.data); - - this.context = context; - this.path = path; - this.options = options; - this.template = options.fn; - this.containingView = options.data.view; - this.normalizedRoot = normalized.root; - this.normalizedPath = normalized.path; - this.content = this.lookupContent(); - - this.addContentObservers(); - this.addArrayObservers(); - - this.containingView.on('willClearRender', function() { - self.destroy(); - }); -}; + /** + 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`). -GroupedEach.prototype = { - contentWillChange: function() { - this.removeArrayObservers(); - }, + Any callbacks registered with `onInjectHelpers` will be called once the + helpers have been injected. - contentDidChange: function() { - this.content = this.lookupContent(); - this.addArrayObservers(); - this.rerenderContainingView(); - }, + Example: + ``` + App.injectTestHelpers(); + ``` - contentArrayWillChange: Ember.K, + @method injectTestHelpers + */ + injectTestHelpers: function(helperContainer) { + if (helperContainer) { this.helperContainer = helperContainer; } - contentArrayDidChange: function() { - this.rerenderContainingView(); - }, + 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); + } - lookupContent: function() { - return Ember.Handlebars.get(this.normalizedRoot, this.normalizedPath, this.options); - }, + for(var i = 0, l = injectHelpersCallbacks.length; i < l; i++) { + injectHelpersCallbacks[i](this); + } + }, - addArrayObservers: function() { - if (!this.content) { return; } + /** + This removes all helpers that have been registered, and resets and functions + that were overridden by the helpers. - this.content.addArrayObserver(this, { - willChange: 'contentArrayWillChange', - didChange: 'contentArrayDidChange' - }); - }, + Example: - removeArrayObservers: function() { - if (!this.content) { return; } + ```javascript + App.removeTestHelpers(); + ``` - this.content.removeArrayObserver(this, { - willChange: 'contentArrayWillChange', - didChange: 'contentArrayDidChange' + @public + @method removeTestHelpers + */ + removeTestHelpers: function() { + for (var name in helpers) { + this.helperContainer[name] = this.originalMethods[name]; + delete this.testHelpers[name]; + delete this.originalMethods[name]; + } + } }); - }, - - addContentObservers: function() { - Ember.addBeforeObserver(this.normalizedRoot, this.normalizedPath, this, this.contentWillChange); - Ember.addObserver(this.normalizedRoot, this.normalizedPath, this, this.contentDidChange); - }, - removeContentObservers: function() { - Ember.removeBeforeObserver(this.normalizedRoot, this.normalizedPath, this.contentWillChange); - Ember.removeObserver(this.normalizedRoot, this.normalizedPath, this.contentDidChange); - }, + // 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); + }); + } + }; + } - render: function() { - if (!this.content) { return; } + Test.Promise = function() { + RSVP.Promise.apply(this, arguments); + Test.lastPromise = this; + }; - var content = this.content, - contentLength = get(content, 'length'), - data = this.options.data, - template = this.template; + Test.Promise.prototype = create(RSVP.Promise.prototype); + Test.Promise.prototype.constructor = Test.Promise; - data.insideEach = true; - for (var i = 0; i < contentLength; i++) { - template(content.objectAt(i), { data: data }); - } - }, + // 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); + }; - rerenderContainingView: function() { - var self = this; - Ember.run.scheduleOnce('render', this, function() { - // It's possible it's been destroyed after we enqueued a re-render call. - if (!self.destroyed) { - self.containingView.rerender(); - } - }); - }, + // 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; - destroy: function() { - this.removeContentObservers(); - if (this.content) { - this.removeArrayObservers(); - } - this.destroyed = true; - } -}; - -/** - The `{{#each}}` helper loops over elements in a collection, rendering its - block once for each item. It is an extension of the base Handlebars `{{#each}}` - helper: - - ```javascript - Developers = [{name: 'Yehuda'},{name: 'Tom'}, {name: 'Paul'}]; - ``` - - ```handlebars - {{#each Developers}} - {{name}} - {{/each}} - ``` - - `{{each}}` supports an alternative syntax with element naming: - - ```handlebars - {{#each person in Developers}} - {{person.name}} - {{/each}} - ``` - - When looping over objects that do not have properties, `{{this}}` can be used - to render the object: - - ```javascript - DeveloperNames = ['Yehuda', 'Tom', 'Paul'] - ``` - - ```handlebars - {{#each DeveloperNames}} - {{this}} - {{/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 a View class for items - If you provide an `itemViewClass` option that references a view class - with its own `template` you can omit the block. - - The following template: - - ```handlebars - {{#view App.MyView }} - {{each view.items itemViewClass="App.AnItemView"}} - {{/view}} - ``` - - And application code - - ```javascript - App = Ember.Application.create({ - MyView: Ember.View.extend({ - items: [ - Ember.Object.create({name: 'Dave'}), - Ember.Object.create({name: 'Mary'}), - Ember.Object.create({name: 'Sara'}) - ] - }) - }); + // Reset lastPromise for nested helpers + Test.lastPromise = null; - App.AnItemView = Ember.View.extend({ - template: Ember.Handlebars.compile("Greetings {{name}}") - }); - ``` - - Will result in the HTML structure below - - ```html -
    -
    Greetings Dave
    -
    Greetings Mary
    -
    Greetings Sara
    -
    - ``` - - If an `itemViewClass` is defined on the helper, and therefore the helper is not - being used as a block, an `emptyViewClass` can also be provided optionally. - The `emptyViewClass` will match the behavior of the `{{else}}` condition - described above. That is, the `emptyViewClass` will render if the collection - is empty. - - ### Representing each item with a Controller. - By default the controller lookup within an `{{#each}}` block will be - the controller of the template where the `{{#each}}` was used. If each - item needs to be presented by a custom controller you can provide a - `itemController` option which references a controller by lookup name. - Each item in the loop will be wrapped in an instance of this controller - and the item itself will be set to the `content` property of that controller. - - This is useful in cases where properties of model objects need transformation - or synthesis for display: - - ```javascript - App.DeveloperController = Ember.ObjectController.extend({ - isAvailableForHire: function() { - return !this.get('content.isEmployed') && this.get('content.isSeekingWork'); - }.property('isEmployed', 'isSeekingWork') - }) - ``` - - ```handlebars - {{#each person in developers itemController="developer"}} - {{person.name}} {{#if person.isAvailableForHire}}Hire me!{{/if}} - {{/each}} - ``` - - Each itemController will receive a reference to the current controller as - a `parentController` property. - - ### (Experimental) Grouped Each - - When used in conjunction with the experimental [group helper](https://github.com/emberjs/group-helper), - you can inform Handlebars to re-render an entire group of items instead of - re-rendering them one at a time (in the event that they are changed en masse - or an item is added/removed). - - ```handlebars - {{#group}} - {{#each people}} - {{firstName}} {{lastName}} - {{/each}} - {{/group}} - ``` - - This can be faster than the normal way that Handlebars re-renders items - in some cases. - - If for some reason you have a group with more than one `#each`, you can make - one of the collections be updated in normal (non-grouped) fashion by setting - the option `groupedRows=true` (counter-intuitive, I know). - - For example, - - ```handlebars - {{dealershipName}} - - {{#group}} - {{#each dealers}} - {{firstName}} {{lastName}} - {{/each}} - - {{#each car in cars groupedRows=true}} - {{car.make}} {{car.model}} {{car.color}} - {{/each}} - {{/group}} - ``` - Any change to `dealershipName` or the `dealers` collection will cause the - entire group to be re-rendered. However, changes to the `cars` collection - will be re-rendered individually (as normal). - - Note that `group` behavior is also disabled by specifying an `itemViewClass`. - - @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.itemController] {String} name of a controller to be created for each item - @param [options.groupedRows] {boolean} enable normal item-by-item rendering when inside a `#group` helper -*/ -Ember.Handlebars.registerHelper('each', function eachHelper(path, options) { - 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"); - - var keywordName = arguments[0]; - - options = arguments[3]; - path = arguments[2]; - if (path === '') { path = "this"; } - - options.hash.keyword = keywordName; - } + value = fn(val); - if (arguments.length === 1) { - options = path; - path = 'this'; - } + lastPromise = Test.lastPromise; - options.hash.dataSourceBinding = path; - // Set up emptyView as a metamorph with no tag - //options.hash.emptyViewClass = Ember._MetamorphView; + // If the method returned a promise + // return that promise. If not, + // return the last async helper's promise + if ((value && (value instanceof Test.Promise)) || !lastPromise) { + return value; + } else { + run(function() { + lastPromise = Test.resolve(lastPromise).then(function() { + return value; + }); + }); + return lastPromise; + } + } - if (options.data.insideGroup && !options.hash.groupedRows && !options.hash.itemViewClass) { - new Ember.Handlebars.GroupedEach(this, path, options).render(); - } else { - return Ember.Handlebars.helpers.collection.call(this, 'Ember.Handlebars.EachView', options); - } -}); + __exports__["default"] = Test; + }); +define("ember-views", + ["ember-runtime","ember-views/system/jquery","ember-views/system/utils","ember-views/system/render_buffer","ember-views/system/ext","ember-views/views/states","ember-views/views/core_view","ember-views/views/view","ember-views/views/view_collection","ember-views/views/container_view","ember-views/views/collection_view","ember-views/views/component","ember-views/system/event_dispatcher","ember-views/mixins/view_target_action_support","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __exports__) { + "use strict"; + /** + Ember Views -})(); + @module ember + @submodule ember-views + @requires ember-runtime + @main ember-views + */ + // BEGIN IMPORTS + var Ember = __dependency1__["default"]; + var jQuery = __dependency2__["default"]; + var setInnerHTML = __dependency3__.setInnerHTML; + var isSimpleClick = __dependency3__.isSimpleClick; + var RenderBuffer = __dependency4__["default"]; + // for the side effect of extending Ember.run.queues + var cloneStates = __dependency6__.cloneStates; + var states = __dependency6__.states; + + var CoreView = __dependency7__["default"]; + var View = __dependency8__["default"]; + var ViewCollection = __dependency9__["default"]; + var ContainerView = __dependency10__["default"]; + var CollectionView = __dependency11__["default"]; + var Component = __dependency12__["default"]; + + var EventDispatcher = __dependency13__["default"]; + var ViewTargetActionSupport = __dependency14__["default"]; + // END IMPORTS + /** + Alias for jQuery -(function() { -/** -@module ember -@submodule ember-handlebars -*/ - -/** - `template` allows you to render a template from inside another template. - This allows you to re-use the same template in multiple places. For example: - - ```html - - ``` - - ```html - - ``` - - ```handlebars - {{#if isUser}} - {{template "user_info"}} - {{else}} - {{template "unlogged_user_info"}} - {{/if}} - ``` - - This helper looks for templates in the global `Ember.TEMPLATES` hash. If you - add ` - ``` + /** + The root DOM element to which event listeners should be attached. Event + listeners will be attached to the document unless this is overridden. - Take note that `"welcome"` is a string and not an object - reference. + Can be specified as a DOMElement or a selector string. - @method loc - @for Ember.Handlebars.helpers - @param {String} str The string to format -*/ + The default body is a string since this may be evaluated before document.body + exists in the DOM. -Ember.Handlebars.registerHelper('loc', function locHelper(str) { - return Ember.String.loc(str); -}); + @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); + ``` -(function() { + @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'); -(function() { + jQuery.extend(events, addedEvents || {}); -})(); + if (!isNone(rootElement)) { + set(this, 'rootElement', rootElement); + } + rootElement = jQuery(get(this, 'rootElement')); + Ember.assert(fmt('You cannot use the same root element (%@) multiple times in an Ember.Application', [rootElement.selector || rootElement[0].tagName]), !rootElement.is('.ember-application')); + Ember.assert('You cannot make a new Ember.Application using a root element that is a descendent of an existing Ember.Application', !rootElement.closest('.ember-application').length); + Ember.assert('You cannot make a new Ember.Application using a root element that is an ancestor of an existing Ember.Application', !rootElement.find('.ember-application').length); -(function() { -/** -@module ember -@submodule ember-handlebars -*/ + rootElement.addClass('ember-application'); -var set = Ember.set, get = Ember.get; + 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')); -/** - The internal class used to create text inputs when the `{{input}}` - helper is used with `type` of `checkbox`. + for (event in events) { + if (events.hasOwnProperty(event)) { + this.setupHandler(rootElement, event, events[event]); + } + } + }, - See [handlebars.helpers.input](/api/classes/Ember.Handlebars.helpers.html#method_input) for usage details. + /** + Registers an event listener on the rootElement. If the given event is + triggered, the provided event handler will be triggered on the target view. - ## Direct manipulation of `checked` + 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. - 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. + @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; - ## Layout and LayoutName properties + rootElement.on(event + '.ember', '.ember-view', function(evt, triggeringManager) { + var view = View.views[this.id], + result = true; - 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. + var manager = self.canDispatchToEventManager ? self._findNearestEventManager(view, eventName) : null; - @class Checkbox - @namespace Ember - @extends Ember.View -*/ -Ember.Checkbox = Ember.View.extend({ - classNames: ['ember-checkbox'], + if (manager && manager !== triggeringManager) { + result = self._dispatchEvent(manager, evt, eventName, view); + } else if (view) { + result = self._bubbleEvent(view, evt, eventName); + } - tagName: 'input', + return result; + }); - attributeBindings: ['type', 'checked', 'indeterminate', 'disabled', 'tabindex', 'name', - 'autofocus', 'form'], + rootElement.on(event + '.ember', '[data-ember-action]', function(evt) { + //ES6TODO: Needed for ActionHelper (generally not available in ember-views test suite) + if (!ActionHelper) { ActionHelper = requireModule("ember-routing-handlebars/helpers/action")["ActionHelper"]; } - type: "checkbox", - checked: false, - disabled: false, - indeterminate: false, + var actionId = jQuery(evt.currentTarget).attr('data-ember-action'), + action = ActionHelper.registeredActions[actionId]; - init: function() { - this._super(); - this.on("change", this, this._updateElementValue); - }, + // 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); + } + }); + }, - didInsertElement: function() { - this._super(); - this.get('element').indeterminate = !!this.get('indeterminate'); - }, + _findNearestEventManager: function(view, eventName) { + var manager = null; - _updateElementValue: function() { - set(this, 'checked', this.$().prop('checked')); - } -}); + while (view) { + manager = get(view, 'eventManager'); + if (manager && manager[eventName]) { break; } -})(); + view = get(view, 'parentView'); + } + return manager; + }, + _dispatchEvent: function(object, evt, eventName, view) { + var result = true; -(function() { -/** -@module ember -@submodule ember-handlebars -*/ - -var get = Ember.get, set = Ember.set; - -/** - Shared mixin used by `Ember.TextField` and `Ember.TextArea`. - - @class TextSupport - @namespace Ember - @uses Ember.TargetActionSupport - @extends Ember.Mixin - @private -*/ -Ember.TextSupport = Ember.Mixin.create(Ember.TargetActionSupport, { - value: "", - - attributeBindings: ['placeholder', 'disabled', 'maxlength', 'tabindex', 'readonly', - 'autofocus', 'form', 'selectionDirection', 'spellcheck', 'required'], - placeholder: null, - disabled: false, - maxlength: null, - - init: function() { - this._super(); - this.on("focusOut", this, this._elementValueDidChange); - this.on("change", this, this._elementValueDidChange); - this.on("paste", this, this._elementValueDidChange); - this.on("cut", this, this._elementValueDidChange); - this.on("input", this, this._elementValueDidChange); - this.on("keyUp", this, this.interpretKeyEvents); - }, - - /** - The action to be sent when the user presses the return key. - - This is similar to the `{{action}}` helper, but is fired when - the user presses the return key when editing a text field, and sends - the value of the field as the context. - - @property action - @type String - @default null - */ - action: null, - - /** - The event that should send the action. - - Options are: - - * `enter`: the user pressed enter - * `keyPress`: the user pressed a key - - @property onEvent - @type String - @default enter - */ - onEvent: 'enter', - - /** - Whether they `keyUp` event that triggers an `action` to be sent continues - propagating to other views. - - 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 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 = Ember.TextSupport.KEY_EVENTS; - var method = map[event.keyCode]; - - this._elementValueDidChange(); - if (method) { return this[method](event); } - }, - - _elementValueDidChange: function() { - set(this, 'value', this.$().val()); - }, - - /** - The action to be sent when the user inserts a new line. - - Called by the `Ember.TextSupport` mixin on keyUp if keycode matches 13. - Uses sendAction to send the `enter` action to the controller. - - @method insertNewline - @param {Event} event - */ - insertNewline: function(event) { - sendAction('enter', this, event); - sendAction('insert-newline', this, event); - }, - - /** - Called when the user hits escape. - - Called by the `Ember.TextSupport` mixin on keyUp if keycode matches 27. - Uses sendAction to send the `escape-press` action to the controller. - - @method cancel - @param {Event} event - */ - cancel: function(event) { - sendAction('escape-press', this, event); - }, - - /** - Called when the text area is focused. - - @method focusIn - @param {Event} event - */ - focusIn: function(event) { - sendAction('focus-in', this, event); - }, - - /** - Called when the text area is blurred. - - @method focusOut - @param {Event} event - */ - focusOut: function(event) { - sendAction('focus-out', this, event); - }, - - /** - The action to be sent when the user presses a key. Enabled by setting - the `onEvent` property to `keyPress`. - - Uses sendAction to send the `keyPress` action to the controller. - - @method keyPress - @param {Event} event - */ - keyPress: function(event) { - sendAction('key-press', this, event); - } + 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); + } -}); - -Ember.TextSupport.KEY_EVENTS = { - 13: 'insertNewline', - 27: 'cancel' -}; - -// In principle, this shouldn't be necessary, but the legacy -// sectionAction semantics for TextField are different from -// the component semantics so this method normalizes them. -function sendAction(eventName, view, event) { - var action = get(view, eventName), - on = get(view, 'onEvent'), - value = get(view, 'value'); - - // 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); - } + return result; + }, - view.sendAction(eventName, value); + _bubbleEvent: function(view, evt, eventName) { + return run(view, view.handleEvent, eventName, evt); + }, - if (action || on === eventName) { - if(!get(view, 'bubbles')) { - event.stopPropagation(); - } - } -} + destroy: function() { + var rootElement = get(this, 'rootElement'); + jQuery(rootElement).off('.ember', '**').removeClass('ember-application'); + return this._super(); + }, -})(); + toString: function() { + return '(EventDisptacher)'; + } + }); + }); +define("ember-views/system/ext", + ["ember-metal/run_loop"], + function(__dependency1__) { + "use strict"; + /** + @module ember + @submodule ember-views + */ + var run = __dependency1__["default"]; + // Add a new named queue for rendering views that happens + // after bindings have synced, and a queue for scheduling actions + // that that should occur after view rendering. + var queues = run.queues; + run._addQueue('render', 'actions'); + run._addQueue('afterRender', 'render'); + }); +define("ember-views/system/jquery", + ["ember-metal/core","ember-runtime/system/string","ember-metal/enumerable_utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var Ember = __dependency1__["default"]; + // Ember.assert + var w = __dependency2__.w; -(function() { -/** -@module ember -@submodule ember-handlebars -*/ - -var get = Ember.get, set = Ember.set; - -/** - - 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 -*/ -Ember.TextField = Ember.Component.extend(Ember.TextSupport, { - - classNames: ['ember-text-field'], - tagName: "input", - attributeBindings: ['type', 'value', 'size', 'pattern', 'name', 'min', 'max', - 'accept', 'autocomplete', 'autosave', 'formaction', - 'formenctype', 'formmethod', 'formnovalidate', 'formtarget', - 'height', 'inputmode', 'list', 'multiple', 'pattern', 'step', - 'width'], - - /** - The `value` attribute of the input element. As the user inputs text, this - property is updated live. - - @property value - @type String - @default "" - */ - value: "", - - /** - The `type` attribute of the input element. - - @property type - @type String - @default "text" - */ - type: "text", - - /** - The `size` of the text field in characters. - - @property size - @type String - @default null - */ - size: null, - - /** - The `pattern` attribute of input element. - - @property pattern - @type String - @default null - */ - pattern: null, - - /** - The `min` attribute of input element used with `type="number"` or `type="range"`. - - @property min - @type String - @default null - */ - min: null, - - /** - The `max` attribute of input element used with `type="number"` or `type="range"`. - - @property max - @type String - @default null - */ - max: null -}); + // ES6TODO: the functions on EnumerableUtils need their own exports + var forEach = __dependency3__.forEach; -})(); + /** + Ember Views + @module ember + @submodule ember-views + @requires ember-runtime + @main ember-views + */ + var jQuery = (Ember.imports && Ember.imports.jQuery) || (this && this.jQuery); + if (!jQuery && typeof require === 'function') { + jQuery = require('jquery'); + } -(function() { -/** -@module ember -@submodule ember-handlebars -*/ - -var get = Ember.get, set = Ember.set; - -/** - The internal class used to create textarea element when the `{{textarea}}` - helper is used. - - See [handlebars.helpers.textarea](/api/classes/Ember.Handlebars.helpers.html#method_textarea) for usage details. - - ## Layout and LayoutName properties - - 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. - - @class TextArea - @namespace Ember - @extends Ember.Component - @uses Ember.TextSupport -*/ -Ember.TextArea = Ember.Component.extend(Ember.TextSupport, { - classNames: ['ember-text-area'], - - tagName: "textarea", - attributeBindings: ['rows', 'cols', 'name', 'selectionEnd', 'selectionStart', 'wrap'], - rows: null, - cols: null, - - _updateElementValue: Ember.observer('value', function() { - // We do this check so cursor position doesn't get affected in IE - var value = get(this, 'value'), - $el = this.$(); - if ($el && value !== $el.val()) { - $el.val(value); - } - }), - - init: function() { - this._super(); - this.on("didInsertElement", this, this._updateElementValue); - } + 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)); -}); + /** + @module ember + @submodule ember-views + */ + if (jQuery) { + // http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#dndevents + var dragEvents = w('dragstart drag dragenter dragleave dragover drop dragend'); + + // 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'] }; + }); + } -})(); + __exports__["default"] = jQuery; + }); +define("ember-views/system/render_buffer", + ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-views/system/utils","ember-views/system/jquery","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { + "use strict"; + /** + @module ember + @submodule ember-views + */ + var Ember = __dependency1__["default"]; + // jQuery + var get = __dependency2__.get; + var set = __dependency3__.set; + var setInnerHTML = __dependency4__.setInnerHTML; + var jQuery = __dependency5__["default"]; -(function() { -/*jshint eqeqeq:false */ - -/** -@module ember -@submodule ember-handlebars -*/ - -var set = Ember.set, - get = Ember.get, - indexOf = Ember.EnumerableUtils.indexOf, - indexesOf = Ember.EnumerableUtils.indexesOf, - forEach = Ember.EnumerableUtils.forEach, - replace = Ember.EnumerableUtils.replace, - isArray = Ember.isArray, - precompileTemplate = Ember.Handlebars.compile; - -Ember.SelectOption = Ember.View.extend({ - tagName: 'option', - attributeBindings: ['value', 'selected'], - - defaultTemplate: function(context, options) { - options = { data: options.data, hash: {} }; - Ember.Handlebars.helpers.bind.call(context, "view.label", options); - }, - - init: function() { - this.labelPathDidChange(); - this.valuePathDidChange(); - - this._super(); - }, - - selected: Ember.computed(function() { - var content = get(this, 'content'), - selection = get(this, 'parentView.selection'); - if (get(this, 'parentView.multiple')) { - return selection && indexOf(selection, content.valueOf()) > -1; - } else { - // Primitives get passed through bindings as objects... since - // `new Number(4) !== 4`, we use `==` below - return content == selection; + function ClassSet() { + this.seen = {}; + this.list = []; } - }).property('content', 'parentView.selection'), - labelPathDidChange: Ember.observer('parentView.optionLabelPath', function() { - var labelPath = get(this, 'parentView.optionLabelPath'); + ClassSet.prototype = { + add: function(string) { + if (string in this.seen) { return; } + this.seen[string] = true; - if (!labelPath) { return; } + this.list.push(string); + }, - Ember.defineProperty(this, 'label', Ember.computed(function() { - return get(this, labelPath); - }).property(labelPath)); - }), + toDOM: function() { + return this.list.join(" "); + } + }; - valuePathDidChange: Ember.observer('parentView.optionValuePath', function() { - var valuePath = get(this, 'parentView.optionValuePath'); + var BAD_TAG_NAME_TEST_REGEXP = /[^a-zA-Z0-9\-]/; + var BAD_TAG_NAME_REPLACE_REGEXP = /[^a-zA-Z0-9\-]/g; - if (!valuePath) { return; } + function stripTagName(tagName) { + if (!tagName) { + return tagName; + } - Ember.defineProperty(this, 'value', Ember.computed(function() { - return get(this, valuePath); - }).property(valuePath)); - }) -}); + if (!BAD_TAG_NAME_TEST_REGEXP.test(tagName)) { + return tagName; + } -Ember.SelectOptgroup = Ember.CollectionView.extend({ - tagName: 'optgroup', - attributeBindings: ['label'], + return tagName.replace(BAD_TAG_NAME_REPLACE_REGEXP, ''); + } - selectionBinding: 'parentView.selection', - multipleBinding: 'parentView.multiple', - optionLabelPathBinding: 'parentView.optionLabelPath', - optionValuePathBinding: 'parentView.optionValuePath', + var BAD_CHARS_REGEXP = /&(?!\w+;)|[<>"'`]/g; + var POSSIBLE_CHARS_REGEXP = /[&<>"'`]/; - itemViewClassBinding: 'parentView.optionView' -}); + function escapeAttribute(value) { + // Stolen shamelessly from Handlebars -/** - 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. + var escape = { + "<": "<", + ">": ">", + '"': """, + "'": "'", + "`": "`" + }; - The text and `value` property of each ` - - - ``` - - The `value` attribute of the selected `"); - return buffer; - } -function program3(depth0,data) { - - var stack1; - stack1 = helpers.each.call(depth0, "view.groupedContent", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(4, program4, data),contexts:[depth0],types:["ID"],data:data}); - if(stack1 || stack1 === 0) { data.buffer.push(stack1); } - else { data.buffer.push(''); } - } -function program4(depth0,data) { - - - data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.groupView", {hash:{ - 'content': ("content"), - 'label': ("label") - },hashTypes:{'content': "ID",'label': "ID"},hashContexts:{'content': depth0,'label': depth0},contexts:[depth0],types:["ID"],data:data}))); - } + _RenderBuffer.prototype = { -function program6(depth0,data) { - - var stack1; - stack1 = helpers.each.call(depth0, "view.content", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(7, program7, data),contexts:[depth0],types:["ID"],data:data}); - if(stack1 || stack1 === 0) { data.buffer.push(stack1); } - else { data.buffer.push(''); } - } -function program7(depth0,data) { - - - data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.optionView", {hash:{ - 'content': ("") - },hashTypes:{'content': "ID"},hashContexts:{'content': depth0},contexts:[depth0],types:["ID"],data:data}))); - } + // The root view's element + _element: null, - stack1 = helpers['if'].call(depth0, "view.prompt", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(1, program1, data),contexts:[depth0],types:["ID"],data:data}); - if(stack1 || stack1 === 0) { data.buffer.push(stack1); } - stack1 = helpers['if'].call(depth0, "view.optionGroupPath", {hash:{},hashTypes:{},hashContexts:{},inverse:self.program(6, program6, data),fn:self.program(3, program3, data),contexts:[depth0],types:["ID"],data:data}); - if(stack1 || stack1 === 0) { data.buffer.push(stack1); } - return buffer; - -}), - attributeBindings: ['multiple', 'disabled', 'tabindex', 'name', 'required', 'autofocus', - 'form', 'size'], - - /** - The `multiple` attribute of the select element. Indicates whether multiple - options can be selected. - - @property multiple - @type Boolean - @default false - */ - multiple: false, - - /** - The `disabled` attribute of the select element. Indicates whether - the element is disabled from interactions. - - @property disabled - @type Boolean - @default false - */ - disabled: false, - - /** - The `required` attribute of the select element. Indicates whether - a selected option is required for form validation. - - @property required - @type Boolean - @default false - */ - required: false, - - /** - The list of options. - - If `optionLabelPath` and `optionValuePath` are not overridden, this should - be a list of strings, which will serve simultaneously as labels and values. - - Otherwise, this should be a list of objects. For instance: + _hasElement: true, - ```javascript - Ember.Select.create({ - content: Ember.A([ - { id: 1, firstName: 'Yehuda' }, - { id: 2, firstName: 'Tom' } - ]), - optionLabelPath: 'content.firstName', - optionValuePath: 'content.id' - }); - ``` + /** + An internal set used to de-dupe class names when `addClass()` is + used. After each call to `addClass()`, the `classes` property + will be updated. - @property content - @type Array - @default null - */ - content: null, - - /** - When `multiple` is `false`, the element of `content` that is currently - selected, if any. - - When `multiple` is `true`, an array of such elements. - - @property selection - @type Object or Array - @default null - */ - selection: null, - - /** - In single selection mode (when `multiple` is `false`), value can be used to - get the current selection's value or set the selection by it's value. - - It is not currently supported in multiple selection mode. - - @property value - @type String - @default null - */ - value: Ember.computed(function(key, value) { - if (arguments.length === 2) { return value; } - var valuePath = get(this, 'optionValuePath').replace(/^content\.?/, ''); - return valuePath ? get(this, 'selection.' + valuePath) : get(this, 'selection'); - }).property('selection'), - - /** - If given, a top-most dummy option will be rendered to serve as a user - prompt. - - @property prompt - @type String - @default null - */ - prompt: null, - - /** - The path of the option labels. See [content](/api/classes/Ember.Select.html#property_content). - - @property optionLabelPath - @type String - @default 'content' - */ - optionLabelPath: 'content', - - /** - The path of the option values. See [content](/api/classes/Ember.Select.html#property_content). - - @property optionValuePath - @type String - @default 'content' - */ - optionValuePath: 'content', - - /** - The path of the option group. - When this property is used, `content` should be sorted by `optionGroupPath`. - - @property optionGroupPath - @type String - @default null - */ - optionGroupPath: null, - - /** - The view class for optgroup. - - @property groupView - @type Ember.View - @default Ember.SelectOptgroup - */ - groupView: Ember.SelectOptgroup, - - groupedContent: Ember.computed(function() { - var groupPath = get(this, 'optionGroupPath'); - var groupedContent = Ember.A(); - var content = get(this, 'content') || []; - - forEach(content, function(item) { - var label = get(item, groupPath); - - if (get(groupedContent, 'lastObject.label') !== label) { - groupedContent.pushObject({ - label: label, - content: Ember.A() - }); - } + @private + @property elementClasses + @type Array + @default null + */ + elementClasses: null, - get(groupedContent, 'lastObject.content').push(item); - }); + /** + Array of class names which will be applied in the class attribute. - return groupedContent; - }).property('optionGroupPath', 'content.@each'), + You can use `setClasses()` to set this property directly. If you + use `addClass()`, it will be maintained for you. - /** - The view class for option. + @property classes + @type Array + @default null + */ + classes: null, - @property optionView - @type Ember.View - @default Ember.SelectOption - */ - optionView: Ember.SelectOption, + /** + The id in of the element, to be applied in the id attribute. - _change: function() { - if (get(this, 'multiple')) { - this._changeMultiple(); - } else { - this._changeSingle(); - } - }, + You should not set this property yourself, rather, you should use + the `id()` method of `Ember.RenderBuffer`. - selectionDidChange: Ember.observer('selection.@each', function() { - var selection = get(this, 'selection'); - if (get(this, 'multiple')) { - if (!isArray(selection)) { - set(this, 'selection', Ember.A([selection])); - return; - } - this._selectionDidChangeMultiple(); - } else { - this._selectionDidChangeSingle(); - } - }), + @property elementId + @type String + @default null + */ + elementId: null, - valueDidChange: Ember.observer('value', function() { - var content = get(this, 'content'), - value = get(this, 'value'), - valuePath = get(this, 'optionValuePath').replace(/^content\.?/, ''), - selectedValue = (valuePath ? get(this, 'selection.' + valuePath) : get(this, 'selection')), - selection; + /** + A hash keyed on the name of the attribute and whose value will be + applied to that attribute. For example, if you wanted to apply a + `data-view="Foo.bar"` property to an element, you would set the + elementAttributes hash to `{'data-view':'Foo.bar'}`. - if (value !== selectedValue) { - selection = content ? content.find(function(obj) { - return value === (valuePath ? get(obj, valuePath) : obj); - }) : null; + You should not maintain this hash yourself, rather, you should use + the `attr()` method of `Ember.RenderBuffer`. - this.set('selection', selection); - } - }), + @property elementAttributes + @type Hash + @default {} + */ + elementAttributes: null, + /** + A hash keyed on the name of the properties and whose value will be + applied to that property. For example, if you wanted to apply a + `checked=true` property to an element, you would set the + elementProperties hash to `{'checked':true}`. - _triggerChange: function() { - var selection = get(this, 'selection'); - var value = get(this, 'value'); + You should not maintain this hash yourself, rather, you should use + the `prop()` method of `Ember.RenderBuffer`. - if (!Ember.isNone(selection)) { this.selectionDidChange(); } - if (!Ember.isNone(value)) { this.valueDidChange(); } + @property elementProperties + @type Hash + @default {} + */ + elementProperties: null, - this._change(); - }, + /** + The tagname of the element an instance of `Ember.RenderBuffer` represents. - _changeSingle: function() { - var selectedIndex = this.$()[0].selectedIndex, - content = get(this, 'content'), - prompt = get(this, 'prompt'); + Usually, this gets set as the first parameter to `Ember.RenderBuffer`. For + example, if you wanted to create a `p` tag, then you would call - if (!content || !get(content, 'length')) { return; } - if (prompt && selectedIndex === 0) { set(this, 'selection', null); return; } + ```javascript + Ember.RenderBuffer('p') + ``` - if (prompt) { selectedIndex -= 1; } - set(this, 'selection', content.objectAt(selectedIndex)); - }, + @property elementTag + @type String + @default null + */ + elementTag: null, + /** + A hash keyed on the name of the style attribute and whose value will + be applied to that attribute. For example, if you wanted to apply a + `background-color:black;` style to an element, you would set the + elementStyle hash to `{'background-color':'black'}`. - _changeMultiple: function() { - var options = this.$('option:selected'), - prompt = get(this, 'prompt'), - offset = prompt ? 1 : 0, - content = get(this, 'content'), - selection = get(this, 'selection'); + You should not maintain this hash yourself, rather, you should use + the `style()` method of `Ember.RenderBuffer`. - if (!content) { return; } - if (options) { - var selectedIndexes = options.map(function() { - return this.index - offset; - }).toArray(); - var newSelection = content.objectsAt(selectedIndexes); + @property elementStyle + @type Hash + @default {} + */ + elementStyle: null, - if (isArray(selection)) { - replace(selection, 0, get(selection, 'length'), newSelection); - } else { - set(this, 'selection', newSelection); - } - } - }, + /** + Adds a string of HTML to the `RenderBuffer`. - _selectionDidChangeSingle: function() { - var el = this.get('element'); - if (!el) { return; } + @method push + @param {String} string HTML to push into the buffer + @chainable + */ + push: function(string) { + this.buffer += string; + return this; + }, - var content = get(this, 'content'), - selection = get(this, 'selection'), - selectionIndex = content ? indexOf(content, selection) : -1, - prompt = get(this, 'prompt'); + /** + Adds a class to the buffer, which will be rendered to the class attribute. - if (prompt) { selectionIndex += 1; } - if (el) { el.selectedIndex = selectionIndex; } - }, + @method addClass + @param {String} className Class name to add to the buffer + @chainable + */ + addClass: function(className) { + // lazily create elementClasses + this.elementClasses = (this.elementClasses || new ClassSet()); + this.elementClasses.add(className); + this.classes = this.elementClasses.list; - _selectionDidChangeMultiple: function() { - var content = get(this, 'content'), - selection = get(this, 'selection'), - selectedIndexes = content ? indexesOf(content, selection) : [-1], - prompt = get(this, 'prompt'), - offset = prompt ? 1 : 0, - options = this.$('option'), - adjusted; + return this; + }, - if (options) { - options.each(function() { - adjusted = this.index > -1 ? this.index - offset : -1; - this.selected = indexOf(selectedIndexes, adjusted) > -1; - }); - } - }, + setClasses: function(classNames) { + this.elementClasses = null; + var len = classNames.length, i; + for (i = 0; i < len; i++) { + this.addClass(classNames[i]); + } + }, - init: function() { - this._super(); - this.on("didInsertElement", this, this._triggerChange); - this.on("change", this, this._change); - } -}); + /** + Sets the elementID to be used for the element. -})(); + @method id + @param {String} id + @chainable + */ + id: function(id) { + this.elementId = id; + return this; + }, + // duck type attribute functionality like jQuery so a render buffer + // can be used like a jQuery object in attribute binding scenarios. + /** + Adds an attribute which will be rendered to the element. -(function() { -/** -@module ember -@submodule ember-handlebars-compiler -*/ + @method attr + @param {String} name The name of the attribute + @param {String} value The value to add to the attribute + @chainable + @return {Ember.RenderBuffer|String} this or the current attribute value + */ + attr: function(name, value) { + var attributes = this.elementAttributes = (this.elementAttributes || {}); -/** + if (arguments.length === 1) { + return attributes[name]; + } else { + attributes[name] = value; + } - 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. + return this; + }, - ## 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: + /** + Remove an attribute from the list of attributes to render. - - - - - - - - - - - -
    `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` 
    + @method removeAttr + @param {String} name The name of the attribute + @chainable + */ + removeAttr: function(name) { + var attributes = this.elementAttributes; + if (attributes) { delete attributes[name]; } + return this; + }, - When set to a quoted string, these values will be directly applied to the HTML - element. When left unquoted, these values will be bound to a property on the - template's current rendering context (most typically a controller instance). + /** + Adds a property which will be rendered to the element. - ## Unbound: + @method prop + @param {String} name The name of the property + @param {String} value The value to add to the property + @chainable + @return {Ember.RenderBuffer|String} this or the current property value + */ + prop: function(name, value) { + var properties = this.elementProperties = (this.elementProperties || {}); - ```handlebars - {{input value="http://www.facebook.com"}} - ``` + if (arguments.length === 1) { + return properties[name]; + } else { + properties[name] = value; + } + return this; + }, - ```html - - ``` + /** + Remove an property from the list of properties to render. - ## Bound: + @method removeProp + @param {String} name The name of the property + @chainable + */ + removeProp: function(name) { + var properties = this.elementProperties; + if (properties) { delete properties[name]; } - ```javascript - App.ApplicationController = Ember.Controller.extend({ - firstName: "Stanley", - entryNotAllowed: true - }); - ``` + return this; + }, + /** + Adds a style to the style attribute which will be rendered to the element. - ```handlebars - {{input type="text" value=firstName disabled=entryNotAllowed size="50"}} - ``` + @method style + @param {String} name Name of the style + @param {String} value + @chainable + */ + style: function(name, value) { + this.elementStyle = (this.elementStyle || {}); + this.elementStyle[name] = value; + return this; + }, - ```html - - ``` + begin: function(tagName) { + this.tagNames.push(tagName || null); + return this; + }, - ## Extension + pushOpeningTag: function() { + var tagName = this.currentTagName(); + if (!tagName) { return; } - 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 - capablilties of text inputs in your applications by reopening this class. For example, - if you are deploying to browsers where the `required` attribute is used, you - can add this to the `TextField`'s `attributeBindings` property: + if (this._hasElement && !this._element && this.buffer.length === 0) { + this._element = this.generateElement(); + return; + } + var buffer = this.buffer, + id = this.elementId, + classes = this.classes, + attrs = this.elementAttributes, + props = this.elementProperties, + style = this.elementStyle, + attr, prop; - ```javascript - Ember.TextField.reopen({ - attributeBindings: ['required'] - }); - ``` + buffer += '<' + stripTagName(tagName); - 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. + if (id) { + buffer += ' id="' + escapeAttribute(id) + '"'; + this.elementId = null; + } + if (classes) { + buffer += ' class="' + escapeAttribute(classes.join(' ')) + '"'; + this.classes = null; + this.elementClasses = null; + } - See more about [Ember components](api/classes/Ember.Component.html) + if (style) { + buffer += ' style="'; + for (prop in style) { + if (style.hasOwnProperty(prop)) { + buffer += prop + ':' + escapeAttribute(style[prop]) + ';'; + } + } - ## Use as checkbox + buffer += '"'; - An `{{input}}` with a `type` of `checkbox` will render an HTML checkbox input. - The following HTML attributes can be set via the helper: + this.elementStyle = null; + } -* `checked` -* `disabled` -* `tabindex` -* `indeterminate` -* `name` -* `autofocus` -* `form` + if (attrs) { + for (attr in attrs) { + if (attrs.hasOwnProperty(attr)) { + buffer += ' ' + attr + '="' + escapeAttribute(attrs[attr]) + '"'; + } + } + this.elementAttributes = null; + } - When set to a quoted string, these values will be directly applied to the HTML - element. When left unquoted, these values will be bound to a property on the - template's current rendering context (most typically a controller instance). + if (props) { + for (prop in props) { + if (props.hasOwnProperty(prop)) { + var value = props[prop]; + if (value || typeof(value) === 'number') { + if (value === true) { + buffer += ' ' + prop + '="' + prop + '"'; + } else { + buffer += ' ' + prop + '="' + escapeAttribute(props[prop]) + '"'; + } + } + } + } - ## Unbound: + this.elementProperties = null; + } - ```handlebars - {{input type="checkbox" name="isAdmin"}} - ``` + buffer += '>'; + this.buffer = buffer; + }, - ```html - - ``` + pushClosingTag: function() { + var tagName = this.tagNames.pop(); + if (tagName) { this.buffer += ''; } + }, - ## Bound: + currentTagName: function() { + return this.tagNames[this.tagNames.length-1]; + }, - ```javascript - App.ApplicationController = Ember.Controller.extend({ - isAdmin: true - }); - ``` + generateElement: function() { + var tagName = this.tagNames.pop(), // pop since we don't need to close + id = this.elementId, + classes = this.classes, + attrs = this.elementAttributes, + props = this.elementProperties, + style = this.elementStyle, + styleBuffer = '', attr, prop, tagString; + + if (attrs && attrs.name && !canSetNameOnInputs) { + // IE allows passing a tag to createElement. See note on `canSetNameOnInputs` above as well. + tagString = '<'+stripTagName(tagName)+' name="'+escapeAttribute(attrs.name)+'">'; + } else { + tagString = tagName; + } + var element = document.createElement(tagString), + $element = jQuery(element); - ```handlebars - {{input type="checkbox" checked=isAdmin }} - ``` + if (id) { + $element.attr('id', id); + this.elementId = null; + } + if (classes) { + $element.attr('class', classes.join(' ')); + this.classes = null; + this.elementClasses = null; + } + if (style) { + for (prop in style) { + if (style.hasOwnProperty(prop)) { + styleBuffer += (prop + ':' + style[prop] + ';'); + } + } - ```html - - ``` + $element.attr('style', styleBuffer); - ## Extension + this.elementStyle = null; + } - 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: + if (attrs) { + for (attr in attrs) { + if (attrs.hasOwnProperty(attr)) { + $element.attr(attr, attrs[attr]); + } + } + this.elementAttributes = null; + } - ```javascript - Ember.Checkbox.reopen({ - classNames: ['my-app-checkbox'] - }); - ``` + if (props) { + for (prop in props) { + if (props.hasOwnProperty(prop)) { + $element.prop(prop, props[prop]); + } + } + this.elementProperties = null; + } - @method input - @for Ember.Handlebars.helpers - @param {Hash} options -*/ -Ember.Handlebars.registerHelper('input', function(options) { - Ember.assert('You can only pass attributes to the `input` helper, not arguments', arguments.length < 2); + return element; + }, - var hash = options.hash, - types = options.hashTypes, - inputType = hash.type, - onEvent = hash.on; + /** + @method element + @return {DOMElement} The element corresponding to the generated HTML + of this buffer + */ + element: function() { + var html = this.innerString(); - delete hash.type; - delete hash.on; + if (html) { + this._element = setInnerHTML(this._element, html); + } - if (inputType === 'checkbox') { - Ember.assert("{{input type='checkbox'}} does not support setting `value=someBooleanValue`; you must use `checked=someBooleanValue` instead.", options.hashTypes.value !== 'ID'); - return Ember.Handlebars.helpers.view.call(this, Ember.Checkbox, options); - } else { - if (inputType) { hash.type = inputType; } - hash.onEvent = onEvent || 'enter'; - return Ember.Handlebars.helpers.view.call(this, Ember.TextField, options); - } -}); + return this._element; + }, -/** - `{{textarea}}` inserts a new instance of ` - ``` + var matches = []; + if (movesWhitespace) { + // Right now we only check for script tags with ids with the + // goal of targeting morphs. + html = html.replace(/(\s+)( + ``` + + And associate it by name using a view's `templateName` property: + + ```javascript + AView = Ember.View.extend({ + templateName: 'some-template' + }); + ``` - if (!isIntermediate) { - this.invalidateNonDynamicHandlers(newState.handlerInfos, nonDynamicIndexes, invalidateIndex); - } + If you have nested resources, your Handlebars template will look like this: - merge(newState.queryParams, oldState.queryParams); - merge(newState.queryParams, this.queryParams || {}); + ```html + + ``` - return newState; - }; + And `templateName` property: - NamedTransitionIntent.prototype.invalidateNonDynamicHandlers = function(handlerInfos, indexes, invalidateIndex) { - forEach(indexes, function(i) { - if (i >= invalidateIndex) { - var handlerInfo = handlerInfos[i]; - handlerInfos[i] = new UnresolvedHandlerInfoByParam({ - name: handlerInfo.name, - handler: handlerInfo.handler, - params: {} - }); - } + ```javascript + AView = Ember.View.extend({ + templateName: 'posts/new' }); - }; + ``` - NamedTransitionIntent.prototype.getHandlerInfoForDynamicSegment = function(name, handler, names, objects, oldHandlerInfo, targetRouteName) { + Using a value for `templateName` that does not have a Handlebars template + with a matching `data-template-name` attribute will throw an error. - var numNames = names.length; - var objectToUse; - if (objects.length > 0) { + For views classes that may have a template later defined (e.g. as the block + portion of a `{{view}}` Handlebars helper call in another template or in + a subclass), you can provide a `defaultTemplate` property set to compiled + template function. If a template is not later provided for the view instance + the `defaultTemplate` value will be used: - // Use the objects provided for this transition. - objectToUse = objects[objects.length - 1]; - if (isParam(objectToUse)) { - return this.createParamHandlerInfo(name, handler, names, objects, oldHandlerInfo); - } else { - objects.pop(); - } - } else if (oldHandlerInfo && oldHandlerInfo.name === name) { - // Reuse the matching oldHandlerInfo - return oldHandlerInfo; - } else { - // Ideally we should throw this error to provide maximal - // information to the user that not enough context objects - // were provided, but this proves too cumbersome in Ember - // in cases where inner template helpers are evaluated - // before parent helpers un-render, in which cases this - // error somewhat prematurely fires. - //throw new Error("Not enough context objects were provided to complete a transition to " + targetRouteName + ". Specifically, the " + name + " route needs an object that can be serialized into its dynamic URL segments [" + names.join(', ') + "]"); - return oldHandlerInfo; - } - - return new UnresolvedHandlerInfoByObject({ - name: name, - handler: handler, - context: objectToUse, - names: names + ```javascript + AView = Ember.View.extend({ + defaultTemplate: Ember.Handlebars.compile('I was the default'), + template: null, + templateName: null }); - }; + ``` - NamedTransitionIntent.prototype.createParamHandlerInfo = function(name, handler, names, objects, oldHandlerInfo) { - var params = {}; + Will result in instances with an HTML representation of: - // Soak up all the provided string/numbers - var numNames = names.length; - while (numNames--) { + ```html +
    I was the default
    + ``` - // Only use old params if the names match with the new handler - var oldParams = (oldHandlerInfo && name === oldHandlerInfo.name && oldHandlerInfo.params) || {}; + If a `template` or `templateName` is provided it will take precedence over + `defaultTemplate`: - var peek = objects[objects.length - 1]; - var paramName = names[numNames]; - if (isParam(peek)) { - params[paramName] = "" + objects.pop(); - } else { - // If we're here, this means only some of the params - // were string/number params, so try and use a param - // value from a previous handler. - if (oldParams.hasOwnProperty(paramName)) { - params[paramName] = oldParams[paramName]; - } else { - throw new Error("You didn't provide enough string/numeric parameters to satisfy all of the dynamic segments for route " + name); - } - } - } + ```javascript + AView = Ember.View.extend({ + defaultTemplate: Ember.Handlebars.compile('I was the default') + }); - return new UnresolvedHandlerInfoByParam({ - name: name, - handler: handler, - params: params + aView = AView.create({ + template: Ember.Handlebars.compile('I was the template, not default') }); - }; + ``` - __exports__.NamedTransitionIntent = NamedTransitionIntent; - }); -define("router/transition-intent/url-transition-intent", - ["../transition-intent","../transition-state","../handler-info","../utils","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; - var TransitionIntent = __dependency1__.TransitionIntent; - var TransitionState = __dependency2__.TransitionState; - var UnresolvedHandlerInfoByParam = __dependency3__.UnresolvedHandlerInfoByParam; - var oCreate = __dependency4__.oCreate; - var merge = __dependency4__.merge; + Will result in the following HTML representation when rendered: - function URLTransitionIntent(props) { - TransitionIntent.call(this, props); - } + ```html +
    I was the template, not default
    + ``` - URLTransitionIntent.prototype = oCreate(TransitionIntent.prototype); - URLTransitionIntent.prototype.applyToState = function(oldState, recognizer, getHandler) { - var newState = new TransitionState(); + ## View Context - var results = recognizer.recognize(this.url), - queryParams = {}, - i, len; + The default context of the compiled template is the view's controller: - if (!results) { - throw new UnrecognizedURLError(this.url); - } + ```javascript + AView = Ember.View.extend({ + template: Ember.Handlebars.compile('Hello {{excitedGreeting}}') + }); - var statesDiffer = false; + aController = Ember.Object.create({ + firstName: 'Barry', + excitedGreeting: function() { + return this.get("content.firstName") + "!!!" + }.property() + }); - for (i = 0, len = results.length; i < len; ++i) { - var result = results[i]; - var name = result.handler; - var handler = getHandler(name); + aView = AView.create({ + controller: aController + }); + ``` - if (handler.inaccessibleByURL) { - throw new UnrecognizedURLError(this.url); - } + Will result in an HTML representation of: - var newHandlerInfo = new UnresolvedHandlerInfoByParam({ - name: name, - handler: handler, - params: result.params - }); + ```html +
    Hello Barry!!!
    + ``` - var oldHandlerInfo = oldState.handlerInfos[i]; - if (statesDiffer || newHandlerInfo.shouldSupercede(oldHandlerInfo)) { - statesDiffer = true; - newState.handlerInfos[i] = newHandlerInfo; - } else { - newState.handlerInfos[i] = oldHandlerInfo; - } - } + A context can also be explicitly supplied through the view's `context` + property. If the view has neither `context` nor `controller` properties, the + `parentView`'s context will be used. - merge(newState.queryParams, results.queryParams); + ## Layouts - return newState; - }; + Views can have a secondary template that wraps their main template. Like + primary templates, layouts can be any function that accepts an optional + context parameter and returns a string of HTML that will be inserted inside + view's tag. Views whose HTML element is self closing (e.g. ``) + cannot have a layout and this property will be ignored. - /** - Promise reject reasons passed to promise rejection - handlers for failed transitions. - */ - function UnrecognizedURLError(message) { - this.message = (message || "UnrecognizedURLError"); - this.name = "UnrecognizedURLError"; - } + Most typically in Ember a layout will be a compiled `Ember.Handlebars` + template. - __exports__.URLTransitionIntent = URLTransitionIntent; - }); -define("router/transition-state", - ["./handler-info","./utils","rsvp","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var ResolvedHandlerInfo = __dependency1__.ResolvedHandlerInfo; - var forEach = __dependency2__.forEach; - var promiseLabel = __dependency2__.promiseLabel; - var resolve = __dependency3__.resolve; - var reject = __dependency3__.reject; + A view's layout can be set directly with the `layout` property or reference + an existing Handlebars template by name with the `layoutName` property. - function TransitionState(other) { - this.handlerInfos = []; - this.queryParams = {}; - this.params = {}; - } + A template used as a layout must contain a single use of the Handlebars + `{{yield}}` helper. The HTML contents of a view's rendered `template` will be + inserted at this location: - TransitionState.prototype = { - handlerInfos: null, - queryParams: null, - params: null, + ```javascript + AViewWithLayout = Ember.View.extend({ + layout: Ember.Handlebars.compile("
    {{yield}}
    "), + template: Ember.Handlebars.compile("I got wrapped") + }); + ``` - promiseLabel: function(label) { - var targetName = ''; - forEach(this.handlerInfos, function(handlerInfo) { - if (targetName !== '') { - targetName += '.'; - } - targetName += handlerInfo.name; - }); - return promiseLabel("'" + targetName + "': " + label); - }, + Will result in view instances with an HTML representation of: - resolve: function(async, shouldContinue, payload) { - var self = this; - // First, calculate params for this state. This is useful - // information to provide to the various route hooks. - var params = this.params; - forEach(this.handlerInfos, function(handlerInfo) { - params[handlerInfo.name] = handlerInfo.params || {}; - }); + ```html +
    +
    + I got wrapped +
    +
    + ``` - payload = payload || {}; - payload.resolveIndex = 0; + See [Ember.Handlebars.helpers.yield](/api/classes/Ember.Handlebars.helpers.html#method_yield) + for more information. - var currentState = this; - var wasAborted = false; + ## Responding to Browser Events - // The prelude RSVP.resolve() asyncs us into the promise land. - return resolve(null, this.promiseLabel("Start transition")) - .then(resolveOneHandlerInfo, null, this.promiseLabel('Resolve handler'))['catch'](handleError, this.promiseLabel('Handle error')); + Views can respond to user-initiated events in one of three ways: method + implementation, through an event manager, and through `{{action}}` helper use + in their template or layout. - function innerShouldContinue() { - return resolve(shouldContinue(), promiseLabel("Check if should continue"))['catch'](function(reason) { - // We distinguish between errors that occurred - // during resolution (e.g. beforeModel/model/afterModel), - // and aborts due to a rejecting promise from shouldContinue(). - wasAborted = true; - return reject(reason); - }, promiseLabel("Handle abort")); - } + ### Method Implementation - function handleError(error) { - // This is the only possible - // reject value of TransitionState#resolve - var handlerInfos = currentState.handlerInfos; - var errorHandlerIndex = payload.resolveIndex >= handlerInfos.length ? - handlerInfos.length - 1 : payload.resolveIndex; - return reject({ - error: error, - handlerWithError: currentState.handlerInfos[errorHandlerIndex].handler, - wasAborted: wasAborted, - state: currentState - }); - } + Views can respond to user-initiated events by implementing a method that + matches the event name. A `jQuery.Event` object will be passed as the + argument to this method. - function proceed(resolvedHandlerInfo) { - // Swap the previously unresolved handlerInfo with - // the resolved handlerInfo - currentState.handlerInfos[payload.resolveIndex++] = resolvedHandlerInfo; + ```javascript + AView = Ember.View.extend({ + click: function(event) { + // will be called when when an instance's + // rendered element is clicked + } + }); + ``` - // Call the redirect hook. The reason we call it here - // vs. afterModel is so that redirects into child - // routes don't re-run the model hooks for this - // already-resolved route. - var handler = resolvedHandlerInfo.handler; - if (handler && handler.redirect) { - handler.redirect(resolvedHandlerInfo.context, payload); - } + ### Event Managers - // Proceed after ensuring that the redirect hook - // didn't abort this transition by transitioning elsewhere. - return innerShouldContinue().then(resolveOneHandlerInfo, null, promiseLabel('Resolve handler')); - } + Views can define an object as their `eventManager` property. This object can + then implement methods that match the desired event names. Matching events + that occur on the view's rendered HTML or the rendered HTML of any of its DOM + descendants will trigger this method. A `jQuery.Event` object will be passed + as the first argument to the method and an `Ember.View` object as the + second. The `Ember.View` will be the view whose rendered HTML was interacted + with. This may be the view with the `eventManager` property or one of its + descendent views. - function resolveOneHandlerInfo() { - if (payload.resolveIndex === currentState.handlerInfos.length) { - // This is is the only possible - // fulfill value of TransitionState#resolve - return { - error: null, - state: currentState - }; + ```javascript + AView = Ember.View.extend({ + eventManager: Ember.Object.create({ + doubleClick: function(event, view) { + // will be called when when an instance's + // rendered element or any rendering + // of this views's descendent + // elements is clicked } + }) + }); + ``` - var handlerInfo = currentState.handlerInfos[payload.resolveIndex]; + An event defined for an event manager takes precedence over events of the + same name handled through methods on the view. - return handlerInfo.resolve(async, innerShouldContinue, payload) - .then(proceed, null, promiseLabel('Proceed')); - } - } - }; + ```javascript + AView = Ember.View.extend({ + mouseEnter: function(event) { + // will never trigger. + }, + eventManager: Ember.Object.create({ + mouseEnter: function(event, view) { + // takes precedence over AView#mouseEnter + } + }) + }); + ``` - __exports__.TransitionState = TransitionState; - }); -define("router/transition", - ["rsvp","./handler-info","./utils","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var reject = __dependency1__.reject; - var resolve = __dependency1__.resolve; - var ResolvedHandlerInfo = __dependency2__.ResolvedHandlerInfo; - var trigger = __dependency3__.trigger; - var slice = __dependency3__.slice; - var log = __dependency3__.log; - var promiseLabel = __dependency3__.promiseLabel; + Similarly a view's event manager will take precedence for events of any views + rendered as a descendent. A method name that matches an event name will not + be called if the view instance was rendered inside the HTML representation of + a view that has an `eventManager` property defined that handles events of the + name. Events not handled by the event manager will still trigger method calls + on the descendent. - /** - @private + ```javascript + OuterView = Ember.View.extend({ + template: Ember.Handlebars.compile("outer {{#view InnerView}}inner{{/view}} outer"), + eventManager: Ember.Object.create({ + mouseEnter: function(event, view) { + // view might be instance of either + // OuterView or InnerView depending on + // where on the page the user interaction occured + } + }) + }); - A Transition is a thennable (a promise-like object) that represents - an attempt to transition to another route. It can be aborted, either - explicitly via `abort` or by attempting another transition while a - previous one is still underway. An aborted transition can also - be `retry()`d later. - */ - function Transition(router, intent, state, error) { - var transition = this; - this.state = state || router.state; - this.intent = intent; - this.router = router; - this.data = this.intent && this.intent.data || {}; - this.resolvedModels = {}; - this.queryParams = {}; + InnerView = Ember.View.extend({ + click: function(event) { + // will be called if rendered inside + // an OuterView because OuterView's + // eventManager doesn't handle click events + }, + mouseEnter: function(event) { + // will never be called if rendered inside + // an OuterView. + } + }); + ``` - if (error) { - this.promise = reject(error); - return; - } + ### Handlebars `{{action}}` Helper - if (state) { - this.params = state.params; - this.queryParams = state.queryParams; + See [Handlebars.helpers.action](/api/classes/Ember.Handlebars.helpers.html#method_action). - var len = state.handlerInfos.length; - if (len) { - this.targetName = state.handlerInfos[state.handlerInfos.length-1].name; - } + ### Event Names - for (var i = 0; i < len; ++i) { - var handlerInfo = state.handlerInfos[i]; - if (!(handlerInfo instanceof ResolvedHandlerInfo)) { - break; - } - this.pivotHandler = handlerInfo.handler; - } + All of the event handling approaches described above respond to the same set + of events. The names of the built-in events are listed below. (The hash of + built-in events exists in `Ember.EventDispatcher`.) Additional, custom events + can be registered by using `Ember.Application.customEvents`. - this.sequence = Transition.currentSequence++; - this.promise = state.resolve(router.async, checkForAbort, this)['catch'](function(result) { - if (result.wasAborted) { - return reject(logAbort(transition)); - } else { - transition.trigger('error', result.error, transition, result.handlerWithError); - transition.abort(); - return reject(result.error); - } - }, promiseLabel('Handle Abort')); - } else { - this.promise = resolve(this.state); - this.params = {}; - } + Touch events: - function checkForAbort() { - if (transition.isAborted) { - return reject(undefined, promiseLabel("Transition aborted - reject")); - } - } - } + * `touchStart` + * `touchMove` + * `touchEnd` + * `touchCancel` - Transition.currentSequence = 0; + Keyboard events - Transition.prototype = { - targetName: null, - urlMethod: 'update', - intent: null, - params: null, - pivotHandler: null, - resolveIndex: 0, - handlerInfos: null, - resolvedModels: null, - isActive: true, - state: null, + * `keyDown` + * `keyUp` + * `keyPress` - /** - @public + Mouse events - The Transition's internal promise. Calling `.then` on this property - is that same as calling `.then` on the Transition object itself, but - this property is exposed for when you want to pass around a - Transition's promise, but not the Transition object itself, since - Transition object can be externally `abort`ed, while the promise - cannot. - */ - promise: null, + * `mouseDown` + * `mouseUp` + * `contextMenu` + * `click` + * `doubleClick` + * `mouseMove` + * `focusIn` + * `focusOut` + * `mouseEnter` + * `mouseLeave` - /** - @public + Form events: - Custom state can be stored on a Transition's `data` object. - This can be useful for decorating a Transition within an earlier - hook and shared with a later hook. Properties set on `data` will - be copied to new transitions generated by calling `retry` on this - transition. - */ - data: null, + * `submit` + * `change` + * `focusIn` + * `focusOut` + * `input` - /** - @public + HTML5 drag and drop events: - A standard promise hook that resolves if the transition - succeeds and rejects if it fails/redirects/aborts. + * `dragStart` + * `drag` + * `dragEnter` + * `dragLeave` + * `dragOver` + * `dragEnd` + * `drop` - Forwards to the internal `promise` property which you can - use in situations where you want to pass around a thennable, - but not the Transition itself. + ## Handlebars `{{view}}` Helper - @param {Function} success - @param {Function} failure - */ - then: function(success, failure) { - return this.promise.then(success, failure); - }, + Other `Ember.View` instances can be included as part of a view's template by + using the `{{view}}` Handlebars helper. See [Ember.Handlebars.helpers.view](/api/classes/Ember.Handlebars.helpers.html#method_view) + for additional information. - /** - @public + @class View + @namespace Ember + @extends Ember.CoreView + */ + var View = CoreView.extend({ - Aborts the Transition. Note you can also implicitly abort a transition - by initiating another transition while a previous one is underway. - */ - abort: function() { - if (this.isAborted) { return this; } - log(this.router, this.sequence, this.targetName + ": transition was aborted"); - this.isAborted = true; - this.isActive = false; - this.router.activeTransition = null; - return this; - }, + concatenatedProperties: ['classNames', 'classNameBindings', 'attributeBindings'], /** - @public + @property isView + @type Boolean + @default true + @static + */ + isView: true, - Retries a previously-aborted transition (making sure to abort the - transition if it's still active). Returns a new transition that - represents the new attempt to transition. - */ - retry: function() { - // TODO: add tests for merged state retry()s - this.abort(); - return this.router.transitionByIntent(this.intent, false); - }, + // .......................................................... + // TEMPLATE SUPPORT + // /** - @public - - Sets the URL-changing method to be employed at the end of a - successful transition. By default, a new Transition will just - use `updateURL`, but passing 'replace' to this method will - cause the URL to update using 'replaceWith' instead. Omitting - a parameter will disable the URL change, allowing for transitions - that don't update the URL at completion (this is also used for - handleURL, since the URL has already changed before the - transition took place). + The name of the template to lookup if no template is provided. - @param {String} method the type of URL-changing method to use - at the end of a transition. Accepted values are 'replace', - falsy values, or any other non-falsy value (which is - interpreted as an updateURL transition). + By default `Ember.View` will lookup a template with this name in + `Ember.TEMPLATES` (a shared global object). - @return {Transition} this transition - */ - method: function(method) { - this.urlMethod = method; - return this; - }, + @property templateName + @type String + @default null + */ + templateName: null, /** - @public + The name of the layout to lookup if no layout is provided. - Fires an event on the current list of resolved/resolving - handlers within this transition. Useful for firing events - on route hierarchies that haven't fully been entered yet. + By default `Ember.View` will lookup a template with this name in + `Ember.TEMPLATES` (a shared global object). - Note: This method is also aliased as `send` + @property layoutName + @type String + @default null + */ + layoutName: null, - @param {Boolean} [ignoreFailure=false] a boolean specifying whether unhandled events throw an error - @param {String} name the name of the event to fire - */ - trigger: function (ignoreFailure) { - var args = slice.call(arguments); - if (typeof ignoreFailure === 'boolean') { - args.shift(); - } else { - // Throw errors on unhandled trigger events by default - ignoreFailure = false; + /** + Used to identify this view during debugging + + @property instrumentDisplay + @type String + */ + instrumentDisplay: computed(function() { + if (this.helperName) { + return '{{' + this.helperName + '}}'; } - trigger(this.router, this.state.handlerInfos.slice(0, this.resolveIndex + 1), ignoreFailure, args); - }, + }), /** - @public + The template used to render the view. This should be a function that + accepts an optional context parameter and returns a string of HTML that + will be inserted into the DOM relative to its parent view. - Transitions are aborted and their promises rejected - when redirects occur; this method returns a promise - that will follow any redirects that occur and fulfill - with the value fulfilled by any redirecting transitions - that occur. + In general, you should set the `templateName` property instead of setting + the template yourself. - @return {Promise} a promise that fulfills with the same - value that the final redirecting transition fulfills with - */ - followRedirects: function() { - var router = this.router; - return this.promise['catch'](function(reason) { - if (router.activeTransition) { - return router.activeTransition.followRedirects(); - } - return reject(reason); - }); - }, + @property template + @type Function + */ + template: computed('templateName', function(key, value) { + if (value !== undefined) { return value; } - toString: function() { - return "Transition (sequence " + this.sequence + ")"; - }, + var templateName = get(this, 'templateName'), + template = this.templateForName(templateName, 'template'); - /** - @private - */ - log: function(message) { - log(this.router, this.sequence, message); - } - }; + Ember.assert("You specified the templateName " + templateName + " for " + this + ", but it did not exist.", !templateName || template); - // Alias 'trigger' as 'send' - Transition.prototype.send = Transition.prototype.trigger; + return template || get(this, 'defaultTemplate'); + }), - /** - @private + /** + The controller managing this view. If this property is set, it will be + made available for use by the template. - Logs and returns a TransitionAborted error. - */ - function logAbort(transition) { - log(transition.router, transition.sequence, "detected abort."); - return new TransitionAborted(); - } + @property controller + @type Object + */ + controller: computed('_parentView', function(key) { + var parentView = get(this, '_parentView'); + return parentView ? get(parentView, 'controller') : null; + }), - function TransitionAborted(message) { - this.message = (message || "TransitionAborted"); - this.name = "TransitionAborted"; - } + /** + A view may contain a layout. A layout is a regular template but + supersedes the `template` property during rendering. It is the + responsibility of the layout template to retrieve the `template` + property from the view (or alternatively, call `Handlebars.helpers.yield`, + `{{yield}}`) to render it in the correct location. + + This is useful for a view that has a shared wrapper, but which delegates + the rendering of the contents of the wrapper to the `template` property + on a subclass. + + @property layout + @type Function + */ + layout: computed(function(key) { + var layoutName = get(this, 'layoutName'), + layout = this.templateForName(layoutName, 'layout'); - __exports__.Transition = Transition; - __exports__.logAbort = logAbort; - __exports__.TransitionAborted = TransitionAborted; - }); -define("router/utils", - ["exports"], - function(__exports__) { - "use strict"; - var slice = Array.prototype.slice; + Ember.assert("You specified the layoutName " + layoutName + " for " + this + ", but it did not exist.", !layoutName || layout); - function isArray(test) { - return Object.prototype.toString.call(test) === "[object Array]"; - } + return layout || get(this, 'defaultLayout'); + }).property('layoutName'), - function merge(hash, other) { - for (var prop in other) { - if (other.hasOwnProperty(prop)) { hash[prop] = other[prop]; } - } - } + _yield: function(context, options) { + var template = get(this, 'template'); + if (template) { template(context, options); } + }, - var oCreate = Object.create || function(proto) { - function F() {} - F.prototype = proto; - return new F(); - }; + templateForName: function(name, type) { + if (!name) { return; } + Ember.assert("templateNames are not allowed to contain periods: "+name, name.indexOf('.') === -1); - /** - @private + if (!this.container) { + throw new EmberError('Container was not found when looking up a views template. ' + + 'This is most likely due to manually instantiating an Ember.View. ' + + 'See: http://git.io/EKPpnA'); + } - Extracts query params from the end of an array - **/ - function extractQueryParams(array) { - var len = (array && array.length), head, queryParams; + return this.container.lookup('template:' + name); + }, - if(len && len > 0 && array[len - 1] && array[len - 1].hasOwnProperty('queryParams')) { - queryParams = array[len - 1].queryParams; - head = slice.call(array, 0, len - 1); - return [head, queryParams]; - } else { - return [array, null]; - } - } + /** + The object from which templates should access properties. - /** - @private + This object will be passed to the template function each time the render + method is called, but it is up to the individual function to decide what + to do with it. - Coerces query param properties and array elements into strings. - **/ - function coerceQueryParamsToString(queryParams) { - for (var key in queryParams) { - if (typeof queryParams[key] === 'number') { - queryParams[key] = '' + queryParams[key]; - } else if (isArray(queryParams[key])) { - for (var i = 0, l = queryParams[key].length; i < l; i++) { - queryParams[key][i] = '' + queryParams[key][i]; - } - } - } - } - /** - @private - */ - function log(router, sequence, msg) { - if (!router.log) { return; } + By default, this will be the view's controller. - if (arguments.length === 3) { - router.log("Transition #" + sequence + ": " + msg); - } else { - msg = sequence; - router.log(msg); - } - } + @property context + @type Object + */ + context: computed(function(key, value) { + if (arguments.length === 2) { + set(this, '_context', value); + return value; + } else { + return get(this, '_context'); + } + })["volatile"](), - function bind(fn, context) { - var boundArgs = arguments; - return function(value) { - var args = slice.call(boundArgs, 2); - args.push(value); - return fn.apply(context, args); - }; - } + /** + Private copy of the view's template context. This can be set directly + by Handlebars without triggering the observer that causes the view + to be re-rendered. - function isParam(object) { - return (typeof object === "string" || object instanceof String || typeof object === "number" || object instanceof Number); - } + The context of a view is looked up as follows: + 1. Supplied context (usually by Handlebars) + 2. Specified controller + 3. `parentView`'s context (for a child of a ContainerView) - function forEach(array, callback) { - for (var i=0, l=array.length; i