dist/ember.js in ember-source-1.4.0.beta.1 vs dist/ember.js in ember-source-1.4.0.beta.2

- old
+ new

@@ -3,11 +3,11 @@ * @copyright Copyright 2011-2014 Tilde Inc. and contributors * Portions Copyright 2006-2011 Strobe Inc. * 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.4.0-beta.1 + * @version 1.4.0-beta.2 */ (function() { /*global __fail__*/ @@ -196,11 +196,11 @@ * @copyright Copyright 2011-2014 Tilde Inc. and contributors * Portions Copyright 2006-2011 Strobe Inc. * 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.4.0-beta.1 + * @version 1.4.0-beta.2 */ (function() { var define, requireModule, require, requirejs; @@ -279,11 +279,11 @@ The core Runtime framework is based on the jQuery API with a number of performance optimizations. @class Ember @static - @version 1.4.0-beta.1+canary.64fee6ed + @version 1.4.0-beta.2 */ if ('undefined' === typeof Ember) { // Create core object. Make it act like an instance of Ember.Namespace so that // objects assigned to it are given a sane string representation. @@ -306,14 +306,14 @@ /** @property VERSION @type String - @default '1.4.0-beta.1+canary.64fee6ed' + @default '1.4.0-beta.2' @static */ -Ember.VERSION = '1.4.0-beta.1+canary.64fee6ed'; +Ember.VERSION = '1.4.0-beta.2'; /** Standard environmental variables. You can define these in a global `EmberENV` variable before loading Ember to control various configuration settings. @@ -1024,11 +1024,11 @@ // .......................................................... // META // -var META_DESC = { +var META_DESC = Ember.META_DESC = { writable: true, configurable: false, enumerable: false, value: null }; @@ -1828,38 +1828,127 @@ 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); } }, @@ -1881,18 +1970,66 @@ 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 remove 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 changed 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) { @@ -2541,11 +2678,11 @@ })(); (function() { -var metaFor = Ember.meta, +var META_KEY = Ember.META_KEY, guidFor = Ember.guidFor, tryFinally = Ember.tryFinally, sendEvent = Ember.sendEvent, listenersUnion = Ember.listenersUnion, listenersDiff = Ember.listenersDiff, @@ -2572,14 +2709,14 @@ @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 = metaFor(obj, false), - watching = m.watching[keyName] > 0 || keyName === 'length', - proto = m.proto, - desc = m.descs[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); @@ -2602,14 +2739,14 @@ @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 = metaFor(obj, false), - watching = m.watching[keyName] > 0 || keyName === 'length', - proto = m.proto, - desc = m.descs[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); } @@ -2678,11 +2815,11 @@ propertyWillChange(events[i], events[i+1]); } } function chainsDidChange(obj, keyName, m, suppressEvents) { - if (!(m.hasOwnProperty('chainWatchers') && + if (!(m && m.hasOwnProperty('chainWatchers') && m.chainWatchers[keyName])) { return; } var nodes = m.chainWatchers[keyName], @@ -3703,15 +3840,15 @@ var metaFor = Ember.meta, // utils.js typeOf = Ember.typeOf, // utils.js MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER, o_defineProperty = Ember.platform.defineProperty; -Ember.watchKey = function(obj, keyName) { +Ember.watchKey = function(obj, keyName, meta) { // can't watch length on Array - it is special... if (keyName === 'length' && typeOf(obj) === 'array') { return; } - var m = metaFor(obj), watching = m.watching; + var m = meta || metaFor(obj), watching = m.watching; // activate watching first time if (!watching[keyName]) { watching[keyName] = 1; @@ -3732,12 +3869,12 @@ watching[keyName] = (watching[keyName] || 0) + 1; } }; -Ember.unwatchKey = function(obj, keyName) { - var m = metaFor(obj), watching = m.watching; +Ember.unwatchKey = function(obj, keyName, meta) { + var m = meta || metaFor(obj), watching = m.watching; if (watching[keyName] === 1) { watching[keyName] = 0; if ('function' === typeof obj.didUnwatchProperty) { @@ -3776,11 +3913,12 @@ normalizeTuple = Ember.normalizeTuple, // property_get.js forEach = Ember.ArrayPolyfills.forEach, // array.js warn = Ember.warn, watchKey = Ember.watchKey, unwatchKey = Ember.unwatchKey, - FIRST_KEY = /^([^\.\*]+)/; + FIRST_KEY = /^([^\.\*]+)/, + META_KEY = Ember.META_KEY; function firstKey(path) { return path.match(FIRST_KEY)[0]; } @@ -3810,28 +3948,28 @@ nodes = m.chainWatchers = {}; } if (!nodes[keyName]) { nodes[keyName] = []; } nodes[keyName].push(node); - watchKey(obj, keyName); + watchKey(obj, keyName, m); } var removeChainWatcher = Ember.removeChainWatcher = function(obj, keyName, node) { if (!obj || 'object' !== typeof obj) { return; } // nothing to do - var m = metaFor(obj, false); - if (!m.hasOwnProperty('chainWatchers')) { return; } // nothing to do + var m = obj[META_KEY]; + if (m && !m.hasOwnProperty('chainWatchers')) { return; } // nothing to do - var nodes = m.chainWatchers; + var nodes = m && m.chainWatchers; - if (nodes[keyName]) { + 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); + 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. @@ -3867,18 +4005,18 @@ var ChainNodePrototype = ChainNode.prototype; function lazyGet(obj, key) { if (!obj) return undefined; - var meta = metaFor(obj, false); + var meta = obj[META_KEY]; // check if object meant only to be a prototype - if (meta.proto === obj) return undefined; + if (meta && meta.proto === obj) return undefined; if (key === "@each") return get(obj, key); // if a CP only return cached value - var desc = meta.descs[key]; + var desc = meta && meta.descs[key]; if (desc && desc._cacheable) { if (key in meta.cache) { return meta.cache[key]; } else { return undefined; @@ -4086,16 +4224,18 @@ // and finally tell parent about my path changing... if (this._parent) { this._parent.chainDidChange(this, this._key, 1, events); } }; Ember.finishChains = function(obj) { - var m = metaFor(obj, false), chains = m.chains; + // We only create meta if we really have to + var m = obj[META_KEY], chains = m && m.chains; if (chains) { if (chains.value() !== obj) { - m.chains = chains = chains.copy(obj); + metaFor(obj).chains = chains = chains.copy(obj); + } else { + chains.didChange(null); } - chains.didChange(null); } }; })(); @@ -4159,40 +4299,40 @@ 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) { - var m = metaFor(obj), ret = m.chains; +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; } -Ember.watchPath = function(obj, keyPath) { +Ember.watchPath = function(obj, keyPath, meta) { // can't watch length on Array - it is special... if (keyPath === 'length' && typeOf(obj) === 'array') { return; } - var m = metaFor(obj), watching = m.watching; + var m = meta || metaFor(obj), watching = m.watching; if (!watching[keyPath]) { // activate watching first time watching[keyPath] = 1; - chainsFor(obj).add(keyPath); + chainsFor(obj, m).add(keyPath); } else { watching[keyPath] = (watching[keyPath] || 0) + 1; } }; -Ember.unwatchPath = function(obj, keyPath) { - var m = metaFor(obj), watching = m.watching; +Ember.unwatchPath = function(obj, keyPath, meta) { + var m = meta || metaFor(obj), watching = m.watching; if (watching[keyPath] === 1) { watching[keyPath] = 0; - chainsFor(obj).remove(keyPath); + chainsFor(obj, m).remove(keyPath); } else if (watching[keyPath] > 1) { watching[keyPath]--; } }; })(); @@ -4232,36 +4372,36 @@ @method watch @for Ember @param obj @param {String} keyName */ -Ember.watch = function(obj, _keyPath) { +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); + watchKey(obj, _keyPath, m); } else { - watchPath(obj, _keyPath); + watchPath(obj, _keyPath, m); } }; Ember.isWatching = function isWatching(obj, key) { var meta = obj[META_KEY]; return (meta && meta.watching[key]) > 0; }; Ember.watch.flushPending = Ember.flushPendingChains; -Ember.unwatch = function(obj, _keyPath) { +Ember.unwatch = function(obj, _keyPath, m) { // can't watch length on Array - it is special... if (_keyPath === 'length' && typeOf(obj) === 'array') { return; } if (isKeyName(_keyPath)) { - unwatchKey(obj, _keyPath); + unwatchKey(obj, _keyPath, m); } else { - unwatchPath(obj, _keyPath); + unwatchPath(obj, _keyPath, m); } }; /** Call on an object when you first beget it from another object. This will @@ -4272,11 +4412,11 @@ @method rewatch @for Ember @param obj */ Ember.rewatch = function(obj) { - var m = metaFor(obj, false), chains = m.chains; + 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); } @@ -4405,11 +4545,11 @@ // 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); + watch(obj, depKey, meta); } } function removeDependentKeys(desc, obj, keyName, meta) { // the descriptor has a list of dependent keys, so @@ -4424,11 +4564,11 @@ // 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); + unwatch(obj, depKey, meta); } } // .......................................................... // COMPUTED PROPERTY @@ -4632,11 +4772,11 @@ ComputedPropertyPrototype.property = function() { var args; var addArg = function (property) { - args.push(property); + args.push(property); }; args = []; for (var i = 0, l = arguments.length; i < l; i++) { expandProperties(arguments[i], addArg); @@ -4885,11 +5025,12 @@ @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 cache = metaFor(obj, false).cache; + var meta = obj[META_KEY], + cache = meta && meta.cache; if (cache && key in cache) { return cache[key]; } }; @@ -7461,18 +7602,20 @@ a_indexOf = Ember.ArrayPolyfills.indexOf, a_forEach = Ember.ArrayPolyfills.forEach, a_slice = [].slice, o_create = Ember.create, defineProperty = Ember.defineProperty, - guidFor = Ember.guidFor; + guidFor = Ember.guidFor, + metaFor = Ember.meta, + META_KEY = Ember.META_KEY; var expandProperties = Ember.expandProperties; function mixinsMeta(obj) { - var m = Ember.meta(obj, true), ret = m.mixins; + var m = metaFor(obj, true), ret = m.mixins; if (!ret) { ret = m.mixins = {}; } else if (!m.hasOwnProperty('mixins')) { ret = m.mixins = o_create(ret); } @@ -7653,11 +7796,11 @@ props = mixinProperties(m, mixin); if (props === CONTINUE) { continue; } if (props) { - meta = Ember.meta(base); + meta = metaFor(base); if (base.willMergeMixin) { base.willMergeMixin(props); } concats = concatenatedMixinProperties('concatenatedProperties', props, values, base); mergings = concatenatedMixinProperties('mergedProperties', props, values, base); for (key in props) { @@ -7711,11 +7854,11 @@ m.bindings = {}; } } function finishPartial(obj, m) { - connectBindings(obj, m || Ember.meta(obj)); + connectBindings(obj, m || metaFor(obj)); return obj; } function followAlias(obj, desc, m, descs, values) { var altKey = desc.methodName, value; @@ -7758,11 +7901,11 @@ updateObserversAndListeners(obj, key, observerOrListener, '__ember_listens__', 'addListener'); } } function applyMixin(obj, mixins, partial) { - var descs = {}, values = {}, m = Ember.meta(obj), + var descs = {}, values = {}, m = metaFor(obj), key, value, desc, keys = []; // Go through all mixins and hashes passed in, and: // // * Handle concatenated properties @@ -7970,11 +8113,12 @@ @return {Boolean} */ MixinPrototype.detect = function(obj) { if (!obj) { return false; } if (obj instanceof Mixin) { return _detect(obj, this, {}); } - var mixins = Ember.meta(obj, false).mixins; + var m = obj[META_KEY], + mixins = m && m.mixins; if (mixins) { return !!mixins[guidFor(this)]; } return false; }; @@ -8009,11 +8153,12 @@ }; // returns the mixins currently applied to the specified object // TODO: Make Ember.mixin Mixin.mixins = function(obj) { - var mixins = Ember.meta(obj, false).mixins, ret = []; + var m = obj[META_KEY], + mixins = m && m.mixins, ret = []; if (!mixins) { return ret; } for (var key in mixins) { var mixin = mixins[key]; @@ -9889,11 +10034,11 @@ // error.message === "2" }); ``` @method all - @for RSVP.Promise + @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. @@ -12617,10 +12762,11 @@ 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, @@ -13310,11 +13456,12 @@ @method metaForProperty @param key {String} property name */ metaForProperty: function(key) { - var desc = meta(this.proto(), false).descs[key]; + var meta = this.proto()[META_KEY], + desc = meta && meta.descs[key]; Ember.assert("metaForProperty() could not find a computed property with key '"+key+"'.", !!desc && desc instanceof Ember.ComputedProperty); return desc._meta || {}; }, @@ -20458,11 +20605,11 @@ }, deprecatedSend: function(actionName) { var args = [].slice.call(arguments, 1); Ember.assert('' + this + " has the action " + actionName + " but it is not a function", typeof this[actionName] === 'function'); - Ember.deprecate('Action handlers implemented directly on controllers are deprecated in favor of action handlers on an `actions` object (' + actionName + ' on ' + this + ')', false); + Ember.deprecate('Action handlers implemented directly on controllers are deprecated in favor of action handlers on an `actions` object ( action: `' + actionName + '` on ' + this + ')', false); this[actionName].apply(this, args); return; } }); @@ -22237,11 +22384,11 @@ }, 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 (' + actionName + ' on ' + this + ')', false); + 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) { @@ -24529,19 +24676,16 @@ if (name !== 'value' && (type === 'string' || (type === 'number' && !isNaN(value)))) { if (value !== elem.attr(name)) { elem.attr(name, value); } } else if (name === 'value' || type === 'boolean') { - // We can't set properties to undefined or null - if (Ember.isNone(value)) { value = ''; } - - if (!value) { + if (Ember.isNone(value) || value === false) { + // `null`, `undefined` or `false` should remove attribute elem.removeAttr(name); - } - - if (value !== elem.prop(name)) { - // value and booleans should always be properties + elem.prop(name, ''); + } else if (value !== elem.prop(name)) { + // value should always be properties elem.prop(name, value); } } else if (!value) { elem.removeAttr(name); } @@ -25922,13 +26066,12 @@ this._super(); set(this, 'context', this); set(this, 'controller', this); }, - defaultLayout: function(options){ - options.data = {view: options._context}; - Ember.Handlebars.helpers['yield'].apply(this, [options]); + 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. @@ -28061,10 +28204,24 @@ function exists(value) { return !Ember.isNone(value); } +function sanitizedHandlebarsGet(currentContext, property, options) { + var result = handlebarsGet(currentContext, property, options); + if (result === null || result === undefined) { + result = ""; + } else if (!(result instanceof Handlebars.SafeString)) { + result = String(result); + } + if (!options.hash.unescaped){ + result = Handlebars.Utils.escapeExpression(result); + } + + return result; +} + // 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, @@ -28162,13 +28319,13 @@ if (data.insideGroup) { observer = function() { Ember.run.once(view, 'rerender'); }; - var result = handlebarsGet(currentContext, property, options); - if (result === null || result === undefined) { result = ""; } - data.buffer.push(result); + output = sanitizedHandlebarsGet(currentContext, property, options); + + data.buffer.push(output); } else { var bindView = new Ember._SimpleHandlebarsView( property, currentContext, !options.hash.unescaped, options.data ); @@ -28188,12 +28345,13 @@ view.registerObserver(normalized.root, normalized.path, observer); } } else { // The object is not observable, so just render it out and // be done with it. - output = handlebarsGet(currentContext, property, options); - data.buffer.push((output === null || typeof output === 'undefined') ? '' : output); + output = sanitizedHandlebarsGet(currentContext, property, options); + + data.buffer.push(output); } } function shouldDisplayIfHelperContent(result) { var truthy = result && get(result, 'isTruthy'); @@ -32276,11 +32434,12 @@ return queryParams; }, recognize: function(path) { var states = [ this.rootState ], - pathLen, i, l, queryStart, queryParams = {}; + pathLen, i, l, queryStart, queryParams = {}, + isSlashDropped = false; queryStart = path.indexOf('?'); if (queryStart !== -1) { var queryString = path.substr(queryStart + 1, path.length); path = path.substr(0, queryStart); @@ -32292,10 +32451,11 @@ if (path.charAt(0) !== "/") { path = "/" + path; } pathLen = path.length; if (pathLen > 1 && path.charAt(pathLen - 1) === "/") { path = path.substr(0, pathLen - 1); + isSlashDropped = true; } for (i=0, l=path.length; i<l; i++) { states = recognizeChar(states, path.charAt(i)); if (!states.length) { break; } @@ -32311,10 +32471,15 @@ states = sortSolutions(solutions); var state = solutions[0]; if (state && state.handlers) { + // if a trailing slash was dropped and a star segment is the last segment + // specified, put the trailing slash back + if (isSlashDropped && state.regex.source.slice(-5) === "(.+)$") { + path = path + "/"; + } return findHandler(state, path, queryParams); } } }; @@ -32561,12 +32726,15 @@ function ResolvedHandlerInfo(props) { HandlerInfo.call(this, props); } ResolvedHandlerInfo.prototype = oCreate(HandlerInfo.prototype); - ResolvedHandlerInfo.prototype.resolve = function() { + ResolvedHandlerInfo.prototype.resolve = function(async, shouldContinue, payload) { // A ResolvedHandlerInfo just resolved with itself. + if (payload && payload.resolvedModels) { + payload.resolvedModels[this.name] = this.context; + } return resolve(this); }; // These are generated by URL transitions and // named transitions for non-dynamic route segments. @@ -33726,23 +33894,10 @@ var handlerInfo = currentState.handlerInfos[payload.resolveIndex]; return handlerInfo.resolve(async, innerShouldContinue, payload) .then(proceed); } - }, - - getResolvedHandlerInfos: function() { - var resolvedHandlerInfos = []; - var handlerInfos = this.handlerInfos; - for (var i = 0, len = handlerInfos.length; i < len; ++i) { - var handlerInfo = handlerInfos[i]; - if (!(handlerInfo instanceof ResolvedHandlerInfo)) { - break; - } - resolvedHandlerInfos.push(handlerInfo); - } - return resolvedHandlerInfos; } }; __exports__.TransitionState = TransitionState; }); @@ -36357,11 +36512,11 @@ options = options || {}; options.parentView = options.parentView ? options.parentView.replace(/\//g, '.') : parentTemplate(this); options.outlet = options.outlet || 'main'; var parentView = this.router._lookupActiveView(options.parentView); - parentView.disconnectOutlet(options.outlet); + if (parentView) { parentView.disconnectOutlet(options.outlet); } }, willDestroy: function() { this.teardownViews(); }, @@ -38386,9 +38541,10 @@ @private @method _finishDisconnections */ _finishDisconnections: function() { + if (this.isDestroyed) return; // _outlets will be gone anyway var outlets = get(this, '_outlets'); var pendingDisconnections = this._pendingDisconnections; this._pendingDisconnections = null; for (var outletName in pendingDisconnections) {