dist/ember.js in ember-source-1.8.0.beta.2 vs dist/ember.js in ember-source-1.8.0.beta.3

- 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.8.0-beta.2 + * @version 1.8.0-beta.3 */ (function() { var define, requireModule, require, requirejs, Ember; @@ -6334,11 +6334,11 @@ instrumentDisplay: '{{textarea}}', classNames: ['ember-text-area'], tagName: "textarea", - attributeBindings: ['rows', 'cols', 'name', 'selectionEnd', 'selectionStart', 'wrap'], + attributeBindings: ['rows', 'cols', 'name', 'selectionEnd', 'selectionStart', 'wrap', 'lang', 'dir'], rows: null, cols: null, _updateElementValue: observer('value', function() { // We do this check so cursor position doesn't get affected in IE @@ -6393,11 +6393,11 @@ 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', + 'height', 'inputmode', 'list', 'multiple', 'step', 'lang', 'dir', 'width'], /** The `value` attribute of the input element. As the user inputs text, this property is updated live. @@ -8511,12 +8511,10 @@ } var viewOptions = ViewHelper.propertiesFromHTMLOptions({ data: data, hash: itemHash }, this); hash.itemViewClass = itemViewClass.extend(viewOptions); - options.helperName = options.helperName || 'collection'; - return helpers.view.call(this, collectionClass, options); } __exports__["default"] = collectionHelper; }); @@ -9411,11 +9409,11 @@ context = (fn.contexts && fn.contexts.length) ? fn.contexts[0] : ctx; return handlebarsGet(context, property, fn); } }); define("ember-handlebars/helpers/view", - ["ember-metal/core","ember-runtime/system/object","ember-metal/property_get","ember-metal/property_set","ember-metal/mixin","ember-views/system/jquery","ember-views/views/view","ember-metal/binding","ember-metal/merge","ember-handlebars/ext","ember-runtime/system/string","exports"], + ["ember-metal/core","ember-runtime/system/object","ember-metal/property_get","ember-metal/property_set","ember-metal/mixin","ember-views/system/jquery","ember-views/views/view","ember-metal/binding","ember-metal/keys","ember-handlebars/ext","ember-runtime/system/string","exports"], function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __exports__) { "use strict"; /*globals Handlebars */ /** @@ -9432,11 +9430,11 @@ var set = __dependency4__.set; var IS_BINDING = __dependency5__.IS_BINDING; var jQuery = __dependency6__["default"]; var View = __dependency7__["default"]; var isGlobalPath = __dependency8__.isGlobalPath; - var merge = __dependency9__["default"]; + var keys = __dependency9__["default"]; var normalizePath = __dependency10__.normalizePath; var handlebarsGet = __dependency10__.handlebarsGet; var handlebarsGetView = __dependency10__.handlebarsGetView; var EmberString = __dependency11__["default"]; @@ -9472,96 +9470,94 @@ delete hashType.idBinding; } } var ViewHelper = EmberObject.create({ - propertiesFromHTMLOptions: function(options) { - var hash = options.hash, data = options.data; - var extensions = {}; + var hash = options.hash; + var data = options.data; + var extensions = { + classNameBindings: [], + helperName: options.helperName || '' + }; var classes = hash['class']; - var dup = false; if (hash.id) { extensions.elementId = hash.id; - dup = true; } if (hash.tag) { extensions.tagName = hash.tag; - dup = true; } if (classes) { classes = classes.split(' '); extensions.classNames = classes; - dup = true; } if (hash.classBinding) { extensions.classNameBindings = hash.classBinding.split(' '); - dup = true; } if (hash.classNameBindings) { - if (extensions.classNameBindings === undefined) extensions.classNameBindings = []; extensions.classNameBindings = extensions.classNameBindings.concat(hash.classNameBindings.split(' ')); - dup = true; } 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; } - if (dup) { - hash = merge({}, hash); - delete hash.id; - delete hash.tag; - delete hash['class']; - delete hash.classBinding; - } - // 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; + var hashKeys = keys(hash); - // Evaluate the context of regular attribute bindings: - for (var prop in hash) { - if (!hash.hasOwnProperty(prop)) { continue; } + for (var i = 0, l = hashKeys.length; i < l; i++) { + var prop = hashKeys[i]; + var isBinding = IS_BINDING.test(prop); + if (prop !== 'classNameBindings') { + extensions[prop] = hash[prop]; + } + // Test if the property ends in "Binding" - if (IS_BINDING.test(prop) && typeof hash[prop] === 'string') { + if (isBinding && typeof extensions[prop] === 'string') { path = this.contextualizeBindingPath(hash[prop], data); - if (path) { hash[prop] = path; } + if (path) { + extensions[prop] = path; + } } } // 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 = View._parsePropertyPath(full); - if(parsedPath.path !== '') { - path = this.contextualizeBindingPath(parsedPath.path, data); - if (path) { extensions.classNameBindings[b] = path + parsedPath.classNames; } + var classNameBindingsKeys = keys(extensions.classNameBindings); + + for (var j = 0, k = classNameBindingsKeys.length; j < k; j++) { + var classKey = classNameBindingsKeys[j]; + var full = extensions.classNameBindings[classKey]; + + 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 = View._parsePropertyPath(full); + if (parsedPath.path !== '') { + path = this.contextualizeBindingPath(parsedPath.path, data); + if (path) { + extensions.classNameBindings[classKey] = path + parsedPath.classNames; } } } } - return merge(hash, extensions); + return extensions; }, // 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. // @@ -9579,11 +9575,11 @@ } }, helper: function(thisContext, path, options) { var data = options.data; - var fn = options.fn; + var fn = options.fn; var newView; makeBindings(thisContext, options); var container = this.container || (data && data.view && data.view.container); @@ -9603,21 +9599,16 @@ // no specified controller. See View#_context for more information. if (!newViewProto.controller && !newViewProto.controllerBinding && !viewOptions.controller && !viewOptions.controllerBinding) { viewOptions._context = thisContext; } - // for instrumentation - if (options.helperName) { - viewOptions.helperName = options.helperName; - } - currentView.appendChild(newView, viewOptions); }, instanceHelper: function(thisContext, newView, options) { - var data = options.data, - fn = options.fn; + var data = options.data; + var fn = options.fn; makeBindings(thisContext, options); Ember.assert( 'Only a instance of a view may be passed to the ViewHelper.instanceHelper', @@ -9637,15 +9628,10 @@ // no specified controller. See View#_context for more information. if (!newView.controller && !newView.controllerBinding && !viewOptions.controller && !viewOptions.controllerBinding) { viewOptions._context = thisContext; } - // for instrumentation - if (options.helperName) { - viewOptions.helperName = options.helperName; - } - currentView.appendChild(newView, viewOptions); } }); __exports__.ViewHelper = ViewHelper; /** @@ -10557,13 +10543,14 @@ queue[0] = 0; var length = 1; var parentIndex = -1; var parents = this._parents; - var parent = null; + var parent = _parentView || null; var elements = this._elements; var element = null; + var contextualElement = null; var level = 0; var view = _view; var children, i, l, child; while (length) { @@ -10578,12 +10565,23 @@ if (view._elementCreated) { this.remove(view, false, true); } this.willCreateElement(view); - element = this.createElement(view); + contextualElement = view._morph && view._morph.contextualElement; + if (!contextualElement && parent && parent._childViewsMorph) { + contextualElement = parent._childViewsMorph.contextualElement; + } + if (!contextualElement && view._didCreateElementWithoutMorph) { + // This code path is only used by createElement and rerender when createElement + // was previously called on a view. + contextualElement = document.body; + } + Ember.assert("Required contextualElement for view "+_view+" is missing", contextualElement); + element = this.createElement(view, contextualElement); + parents[level++] = parentIndex; parentIndex = index; parent = view; // enqueue for end @@ -10615,11 +10613,11 @@ length--; break; } parentIndex = parents[level]; - parent = views[parentIndex]; + parent = parentIndex === -1 ? _parentView : views[parentIndex]; this.insertElement(view, parent, element, -1); index = queue[--length]; view = views[index]; element = elements[level]; elements[level] = null; @@ -10648,12 +10646,13 @@ }; Renderer.prototype.scheduleInsert = function Renderer_scheduleInsert(view, morph) { if (view._morph || view._elementCreated) { - throw new Error("You can't insert a View that has already been rendered"); + throw new Error("You cannot insert a View that has already been rendered"); } + Ember.assert("You cannot insert a View without a morph", morph); view._morph = morph; var viewId = this.uuid(view); this._inserts[viewId] = this.scheduleRender(this, function() { this._inserts[viewId] = null; this.renderTree(view); @@ -12232,16 +12231,30 @@ if (this._parent) { this._parent.chainDidChange(this, this._key, 1, events); } }; function finishChains(obj) { // We only create meta if we really have to - var m = obj['__ember_meta__'], chains = m && m.chains; - if (chains) { - if (chains.value() !== obj) { + var m = obj['__ember_meta__'], + chains, chainWatchers, chainNodes; + if (m) { + // finish any current chains node watchers that reference obj + chainWatchers = m.chainWatchers; + if (chainWatchers) { + for(var key in chainWatchers) { + if (!chainWatchers.hasOwnProperty(key)) { continue; } + chainNodes = chainWatchers[key]; + if (chainNodes) { + for (var i=0,l=chainNodes.length;i<l;i++) { + chainNodes[i].didChange(null); + } + } + } + } + // copy chains from prototype + chains = m.chains; + if (chains && chains.value() !== obj) { metaFor(obj).chains = chains = chains.copy(obj); - } else { - chains.didChange(null); } } } __exports__.finishChains = finishChains;__exports__.removeChainWatcher = removeChainWatcher; @@ -13587,11 +13600,11 @@ The core Runtime framework is based on the jQuery API with a number of performance optimizations. @class Ember @static - @version 1.8.0-beta.2 + @version 1.8.0-beta.3 */ if ('undefined' === typeof Ember) { // Create core object. Make it act like an instance of Ember.Namespace so that // objects assigned to it are given a sane string representation. @@ -13614,14 +13627,14 @@ /** @property VERSION @type String - @default '1.8.0-beta.2' + @default '1.8.0-beta.3' @static */ - Ember.VERSION = '1.8.0-beta.2'; + Ember.VERSION = '1.8.0-beta.3'; /** Standard environmental variables. You can define these in a global `EmberENV` variable before loading Ember to control various configuration settings. @@ -15143,15 +15156,14 @@ __exports__["default"] = isPresent; }); define("ember-metal/keys", - ["ember-metal/array","ember-metal/platform","exports"], - function(__dependency1__, __dependency2__, __exports__) { + ["ember-metal/platform","exports"], + function(__dependency1__, __exports__) { "use strict"; - var indexOf = __dependency1__.indexOf; - var canDefineNonEnumerableProperties = __dependency2__.canDefineNonEnumerableProperties; + var canDefineNonEnumerableProperties = __dependency1__.canDefineNonEnumerableProperties; /** 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. @@ -15162,58 +15174,51 @@ @return {Array} Array containing keys of obj */ var keys = Object.keys; if (!keys || !canDefineNonEnumerableProperties) { - var prototypeProperties = [ - 'constructor', - 'hasOwnProperty', - 'isPrototypeOf', - 'propertyIsEnumerable', - 'valueOf', - 'toLocaleString', - 'toString' - ]; - var pushPropertyName = function(obj, array, key) { - // Prevents browsers that don't respect non-enumerability from - // copying internal Ember properties - if (key.substring(0, 2) === '__') { - return; - } + // modified from + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys + keys = (function () { + var hasOwnProperty = Object.prototype.hasOwnProperty, + hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString'), + dontEnums = [ + 'toString', + 'toLocaleString', + 'valueOf', + 'hasOwnProperty', + 'isPrototypeOf', + 'propertyIsEnumerable', + 'constructor' + ], + dontEnumsLength = dontEnums.length; - if (key === '_super') { - return; - } + return function keys(obj) { + if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { + throw new TypeError('Object.keys called on non-object'); + } - if (indexOf(array, key) >= 0) { - return; - } + var result = [], prop, i; - if (!Object.prototype.hasOwnProperty.call(obj, key)) { - return; - } + for (prop in obj) { + if (prop !== '_super' && + prop.lastIndexOf('__',0) !== 0 && + hasOwnProperty.call(obj, prop)) { + result.push(prop); + } + } - array.push(key); - }; - - keys = function keys(obj) { - var ret = []; - var 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; - }; + if (hasDontEnumBug) { + for (i = 0; i < dontEnumsLength; i++) { + if (hasOwnProperty.call(obj, dontEnums[i])) { + result.push(dontEnums[i]); + } + } + } + return result; + }; + }()); } __exports__["default"] = keys; }); define("ember-metal/libraries", @@ -15411,12 +15416,12 @@ */ assert: consoleMethod('assert') || assertPolyfill }; }); define("ember-metal/map", - ["ember-metal/property_set","ember-metal/utils","ember-metal/array","ember-metal/platform","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { + ["ember-metal/utils","ember-metal/array","ember-metal/platform","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; /** @module ember-metal */ @@ -15438,33 +15443,40 @@ 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 = __dependency1__.set; - var guidFor = __dependency2__.guidFor; - var indexOf = __dependency3__.indexOf; - var create = __dependency4__.create; + var guidFor = __dependency1__.guidFor; + var indexOf = __dependency2__.indexOf; + var create = __dependency3__.create; - function copy(obj) { - var output = {}; + function missingFunction(fn) { + throw new TypeError('' + Object.prototype.toString.call(fn) + " is not a function"); + } + function missingNew(name) { + throw new TypeError("Constructor " + name + "requires 'new'"); + } + + function copyNull(obj) { + var output = Object.create(null); + for (var prop in obj) { // hasOwnPropery is not needed because obj is Object.create(null); output[prop] = obj[prop]; } return output; } function copyMap(original, newObject) { var keys = original.keys.copy(); - var values = copy(original.values); + var values = copyNull(original.values); newObject.keys = keys; newObject.values = values; - newObject.length = original.length; + newObject.size = original.size; return newObject; } /** @@ -15476,69 +15488,104 @@ @namespace Ember @constructor @private */ function OrderedSet() { - this.clear(); + + if (this instanceof OrderedSet) { + this.clear(); + } else { + missingNew("OrderedSet"); + } } /** @method create @static @return {Ember.OrderedSet} */ OrderedSet.create = function() { - return new OrderedSet(); + var Constructor = this; + + return new Constructor(); }; OrderedSet.prototype = { + constructor: OrderedSet, /** @method clear */ clear: function() { this.presenceSet = Object.create(null); this.list = []; + this.size = 0; }, /** @method add @param obj + @param guid (optional, and for internal use) + @return {Ember.OrderedSet} */ - add: function(obj) { - var guid = guidFor(obj); + add: function(obj, _guid) { + var guid = _guid || guidFor(obj); var presenceSet = this.presenceSet; var list = this.list; - if (presenceSet[guid]) { return; } + if (presenceSet[guid]) { + return; + } presenceSet[guid] = true; list.push(obj); + this.size++; + + return this; }, /** + @deprecated + @method remove @param obj + @param _guid (optional and for internal use only) + @return {Boolean} */ - remove: function(obj) { - var guid = guidFor(obj); + remove: function(obj, _guid) { + return this['delete'](obj, _guid); + }, + + /** + @method delete + @param obj + @param _guid (optional and for internal use only) + @return {Boolean} + */ + 'delete': function(obj, _guid) { + var guid = _guid || guidFor(obj); var presenceSet = this.presenceSet; var list = this.list; - delete presenceSet[guid]; - - var index = indexOf.call(list, obj); - if (index > -1) { - list.splice(index, 1); + if (presenceSet[guid] !== undefined) { + delete presenceSet[guid]; + var index = indexOf.call(list, obj); + if (index > -1) { + list.splice(index, 1); + } + this.size--; + return true; + } else { + return false; } }, /** @method isEmpty @return {Boolean} */ isEmpty: function() { - return this.list.length === 0; + return this.size === 0; }, /** @method has @param obj @@ -15546,24 +15593,31 @@ */ has: function(obj) { var guid = guidFor(obj); var presenceSet = this.presenceSet; - return presenceSet[guid]; + return !!presenceSet[guid]; }, /** @method forEach @param {Function} fn @param self */ - forEach: function(fn, self) { - // allow mutation during iteration - var list = this.toArray(); + forEach: function(fn, thisArg) { + var list = this.list; + var length = arguments.length; + var i; - for (var i = 0, j = list.length; i < j; i++) { - fn.call(self, list[i]); + if (length === 2) { + for (i = 0; i < list.length; i++) { + fn.call(thisArg, list[i]); + } + } else { + for (i = 0; i < list.length; i++) { + fn(list[i]); + } } }, /** @method toArray @@ -15576,13 +15630,14 @@ /** @method copy @return {Ember.OrderedSet} */ copy: function() { - var set = new OrderedSet(); + var Constructor = this.constructor; + var set = new Constructor(); - set.presenceSet = copy(this.presenceSet); + set.presenceSet = copyNull(this.presenceSet); set.list = this.toArray(); return set; } }; @@ -15606,33 +15661,41 @@ @namespace Ember @private @constructor */ function Map() { - this.keys = OrderedSet.create(); - this.values = Object.create(null); + if (this instanceof this.constructor) { + this.keys = OrderedSet.create(); + this.values = Object.create(null); + this.size = 0; + } else { + missingNew("OrderedSet"); + } } Ember.Map = Map; /** @method create @static */ Map.create = function() { - return new Map(); + var Constructor = this; + return new Constructor(); }; Map.prototype = { + constructor: Map, + /** This property will change as the number of objects in the map changes. - @property length + @property size @type number @default 0 */ - length: 0, + size: 0, /** Retrieve the value associated with a given key. @method get @@ -15651,39 +15714,55 @@ provided, the new value will replace the old value. @method set @param {*} key @param {*} value + @return {Ember.Map} */ set: function(key, value) { var keys = this.keys; var values = this.values; var guid = guidFor(key); - keys.add(key); + keys.add(key, guid); values[guid] = value; - set(this, 'length', keys.list.length); + + this.size = keys.size; + + return this; }, /** + @deprecated see delete 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) { + return this['delete'](key); + }, + + /** + Removes a value from the map for an associated key. + + @method delete + @param {*} key + @return {Boolean} true if an item was removed, false otherwise + */ + 'delete': 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; var values = this.values; var guid = guidFor(key); if (values[guid]) { - keys.remove(key); + keys.remove(key, guid); delete values[guid]; - set(this, 'length', keys.list.length); + this.size = keys.size; return true; } else { return false; } }, @@ -15694,14 +15773,11 @@ @method has @param {*} key @return {Boolean} true if the item was present, false otherwise */ has: function(key) { - var values = this.values; - var guid = guidFor(key); - - return !!values[guid]; + return this.keys.has(key); }, /** Iterate over all the keys and values. Calls the function once for each key, passing in the key and value, in that order. @@ -15711,18 +15787,30 @@ @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; - var values = this.values; + forEach: function(callback, thisArg) { + if (typeof callback !== 'function') { + missingFunction(callback); + } - keys.forEach(function(key) { - var guid = guidFor(key); - callback.call(self, key, values[guid]); - }); + var length = arguments.length; + var map = this; + var cb; + + if (length === 2) { + cb = function(key) { + callback.call(thisArg, map.get(key), key); + }; + } else { + cb = function(key) { + callback(map.get(key), key); + }; + } + + this.keys.forEach(cb); }, /** @method copy @return {Ember.Map} @@ -15789,23 +15877,26 @@ /** @method copy @return {Ember.MapWithDefault} */ MapWithDefault.prototype.copy = function() { - return copyMap(this, new MapWithDefault({ + var Constructor = this.constructor; + return copyMap(this, new Constructor({ defaultValue: this.defaultValue })); }; __exports__.OrderedSet = OrderedSet; __exports__.Map = Map; __exports__.MapWithDefault = MapWithDefault; }); define("ember-metal/merge", - ["exports"], - function(__exports__) { + ["ember-metal/keys","exports"], + function(__dependency1__, __exports__) { "use strict"; + var keys = __dependency1__["default"]; + /** Merge the contents of two objects together into the first object. ```javascript Ember.merge({first: 'Tom'}, {last: 'Dale'}); // {first: 'Tom', last: 'Dale'} @@ -15818,14 +15909,23 @@ @param {Object} original The object to merge into @param {Object} updates The object to copy properties from @return {Object} */ __exports__["default"] = function merge(original, updates) { - for (var prop in updates) { - if (!updates.hasOwnProperty(prop)) { continue; } + if (!updates || typeof updates !== 'object') { + return original; + } + + var props = keys(updates); + var prop; + var length = props.length; + + for (var i = 0; i < length; i++) { + prop = props[i]; original[prop] = updates[prop]; } + return original; } }); define("ember-metal/mixin", ["ember-metal/core","ember-metal/merge","ember-metal/array","ember-metal/platform","ember-metal/utils","ember-metal/expand_properties","ember-metal/properties","ember-metal/computed","ember-metal/binding","ember-metal/observer","ember-metal/events","exports"], @@ -29601,12 +29701,12 @@ }); __exports__["default"] = ActionHandler; }); define("ember-runtime/mixins/array", - ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/computed","ember-metal/is_none","ember-runtime/mixins/enumerable","ember-metal/enumerable_utils","ember-metal/mixin","ember-metal/property_events","ember-metal/events","ember-metal/watching","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __exports__) { + ["ember-metal/core","ember-metal/property_get","ember-metal/computed","ember-metal/is_none","ember-runtime/mixins/enumerable","ember-metal/enumerable_utils","ember-metal/mixin","ember-metal/property_events","ember-metal/events","ember-metal/watching","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __exports__) { "use strict"; /** @module ember @submodule ember-runtime */ @@ -29614,28 +29714,26 @@ // .......................................................... // HELPERS // var Ember = __dependency1__["default"]; // ES6TODO: Ember.A - var get = __dependency2__.get; - var set = __dependency3__.set; - var computed = __dependency4__.computed; - var cacheFor = __dependency4__.cacheFor; - var isNone = __dependency5__.isNone; - var none = __dependency5__.none; - var Enumerable = __dependency6__["default"]; - var map = __dependency7__.map; - var Mixin = __dependency8__.Mixin; - var required = __dependency8__.required; - var propertyWillChange = __dependency9__.propertyWillChange; - var propertyDidChange = __dependency9__.propertyDidChange; - var addListener = __dependency10__.addListener; - var removeListener = __dependency10__.removeListener; - var sendEvent = __dependency10__.sendEvent; - var hasListeners = __dependency10__.hasListeners; - var isWatching = __dependency11__.isWatching; + var computed = __dependency3__.computed; + var cacheFor = __dependency3__.cacheFor; + var isNone = __dependency4__.isNone; + var none = __dependency4__.none; + var Enumerable = __dependency5__["default"]; + var map = __dependency6__.map; + var Mixin = __dependency7__.Mixin; + var required = __dependency7__.required; + var propertyWillChange = __dependency8__.propertyWillChange; + var propertyDidChange = __dependency8__.propertyDidChange; + var addListener = __dependency9__.addListener; + var removeListener = __dependency9__.removeListener; + var sendEvent = __dependency9__.sendEvent; + var hasListeners = __dependency9__.hasListeners; + var isWatching = __dependency10__.isWatching; function arrayObserversHelper(obj, target, opts, operation, notify) { var willChange = (opts && opts.willChange) || 'arrayWillChange'; var didChange = (opts && opts.didChange) || 'arrayDidChange'; var hasObservers = get(obj, 'hasArrayObservers'); @@ -29648,10 +29746,11 @@ operation(obj, '@array:change', target, didChange); if (hasObservers === notify) { propertyDidChange(obj, 'hasArrayObservers'); } + return obj; } // .......................................................... // ARRAY @@ -29711,42 +29810,50 @@ (i.e. `myArray.get(0)`), then you do not need to implement this method yourself. ```javascript var arr = ['a', 'b', 'c', 'd']; - arr.objectAt(0); // "a" - arr.objectAt(3); // "d" + + arr.objectAt(0); // 'a' + arr.objectAt(3); // 'd' arr.objectAt(-1); // undefined arr.objectAt(4); // undefined arr.objectAt(5); // undefined ``` @method objectAt @param {Number} idx The index of the item to return. @return {*} item at index or undefined */ objectAt: function(idx) { - if (idx < 0 || idx >= get(this, 'length')) return undefined; + if (idx < 0 || idx >= get(this, 'length')) { + return undefined; + } + return get(this, idx); }, /** This returns the objects at the specified indexes, using `objectAt`. ```javascript var arr = ['a', 'b', 'c', 'd']; - arr.objectsAt([0, 1, 2]); // ["a", "b", "c"] - arr.objectsAt([2, 3, 4]); // ["c", "d", undefined] + + arr.objectsAt([0, 1, 2]); // ['a', 'b', 'c'] + arr.objectsAt([2, 3, 4]); // ['c', 'd', undefined] ``` @method objectsAt @param {Array} indexes An array of indexes of items to return. @return {Array} */ objectsAt: function(indexes) { var self = this; - return map(indexes, function(idx) { return self.objectAt(idx); }); + + return map(indexes, function(idx) { + return self.objectAt(idx); + }); }, // overrides Ember.Enumerable version nextObject: function(idx) { return this.objectAt(idx); @@ -29761,20 +29868,23 @@ @property [] @return this */ '[]': computed(function(key, value) { - if (value !== undefined) this.replace(0, get(this, 'length'), value) ; + if (value !== undefined) { + this.replace(0, get(this, 'length'), value); + } + return this; }), firstObject: computed(function() { return this.objectAt(0); }), lastObject: computed(function() { - return this.objectAt(get(this, 'length')-1); + return this.objectAt(get(this, 'length') - 1); }), // optimized version from Enumerable contains: function(obj) { return this.indexOf(obj) >= 0; @@ -29786,10 +29896,11 @@ uses the observable array methods to retrieve the objects for the new slice. ```javascript var arr = ['red', 'green', 'blue']; + arr.slice(0); // ['red', 'green', 'blue'] arr.slice(0, 2); // ['red', 'green'] arr.slice(1, 100); // ['green', 'blue'] ``` @@ -29798,86 +29909,118 @@ @param {Integer} endIndex (Optional) index to end the slice at (but not included). @return {Array} New array with specified slice */ slice: function(beginIndex, endIndex) { var ret = Ember.A(); - var length = get(this, 'length') ; - if (isNone(beginIndex)) beginIndex = 0 ; - if (isNone(endIndex) || (endIndex > length)) endIndex = length ; + var length = get(this, 'length'); - if (beginIndex < 0) beginIndex = length + beginIndex; - if (endIndex < 0) endIndex = length + endIndex; + if (isNone(beginIndex)) { + beginIndex = 0; + } - while(beginIndex < endIndex) { - ret[ret.length] = this.objectAt(beginIndex++) ; + if (isNone(endIndex) || (endIndex > length)) { + endIndex = length; } - return ret ; + + if (beginIndex < 0) { + beginIndex = length + beginIndex; + } + + if (endIndex < 0) { + endIndex = length + endIndex; + } + + while (beginIndex < endIndex) { + ret[ret.length] = this.objectAt(beginIndex++); + } + + return ret; }, /** Returns the index of the given object's first occurrence. If no `startAt` argument is given, the starting location to search is 0. If it's negative, will count backward from the end of the array. Returns -1 if no match is found. ```javascript - var arr = ["a", "b", "c", "d", "a"]; - arr.indexOf("a"); // 0 - arr.indexOf("z"); // -1 - arr.indexOf("a", 2); // 4 - arr.indexOf("a", -1); // 4 - arr.indexOf("b", 3); // -1 - arr.indexOf("a", 100); // -1 + var arr = ['a', 'b', 'c', 'd', 'a']; + + arr.indexOf('a'); // 0 + arr.indexOf('z'); // -1 + arr.indexOf('a', 2); // 4 + arr.indexOf('a', -1); // 4 + arr.indexOf('b', 3); // -1 + arr.indexOf('a', 100); // -1 ``` @method indexOf @param {Object} object the item to search for @param {Number} startAt optional starting location to search, default 0 @return {Number} index or -1 if not found */ indexOf: function(object, startAt) { - var idx, len = get(this, 'length'); + var len = get(this, 'length'); + var idx; - if (startAt === undefined) startAt = 0; - if (startAt < 0) startAt += len; + if (startAt === undefined) { + startAt = 0; + } - for(idx = startAt; idx < len; idx++) { - if (this.objectAt(idx) === object) return idx; + if (startAt < 0) { + startAt += len; } + + for (idx = startAt; idx < len; idx++) { + if (this.objectAt(idx) === object) { + return idx; + } + } + return -1; }, /** Returns the index of the given object's last occurrence. If no `startAt` argument is given, the search starts from the last position. If it's negative, will count backward from the end of the array. Returns -1 if no match is found. ```javascript - var arr = ["a", "b", "c", "d", "a"]; - arr.lastIndexOf("a"); // 4 - arr.lastIndexOf("z"); // -1 - arr.lastIndexOf("a", 2); // 0 - arr.lastIndexOf("a", -1); // 4 - arr.lastIndexOf("b", 3); // 1 - arr.lastIndexOf("a", 100); // 4 + var arr = ['a', 'b', 'c', 'd', 'a']; + + arr.lastIndexOf('a'); // 4 + arr.lastIndexOf('z'); // -1 + arr.lastIndexOf('a', 2); // 0 + arr.lastIndexOf('a', -1); // 4 + arr.lastIndexOf('b', 3); // 1 + arr.lastIndexOf('a', 100); // 4 ``` @method lastIndexOf @param {Object} object the item to search for @param {Number} startAt optional starting location to search, default 0 @return {Number} index or -1 if not found */ lastIndexOf: function(object, startAt) { - var idx, len = get(this, 'length'); + var len = get(this, 'length'); + var idx; - if (startAt === undefined || startAt >= len) startAt = len-1; - if (startAt < 0) startAt += len; + if (startAt === undefined || startAt >= len) { + startAt = len-1; + } - for(idx = startAt; idx >= 0; idx--) { - if (this.objectAt(idx) === object) return idx; + if (startAt < 0) { + startAt += len; } + + for (idx = startAt; idx >= 0; idx--) { + if (this.objectAt(idx) === object) { + return idx; + } + } + return -1; }, // .......................................................... // ARRAY OBSERVERS @@ -29950,30 +30093,40 @@ @param {Number} addAmt The number of items that will be added. If you pass `null` assumes 0. @return {Ember.Array} receiver */ arrayContentWillChange: function(startIdx, removeAmt, addAmt) { + var removing, lim; // if no args are passed assume everything changes - if (startIdx===undefined) { + if (startIdx === undefined) { startIdx = 0; removeAmt = addAmt = -1; } else { - if (removeAmt === undefined) removeAmt=-1; - if (addAmt === undefined) addAmt=-1; + if (removeAmt === undefined) { + removeAmt = -1; + } + + if (addAmt === undefined) { + addAmt = -1; + } } // Make sure the @each proxy is set up if anyone is observing @each - if (isWatching(this, '@each')) { get(this, '@each'); } + if (isWatching(this, '@each')) { + get(this, '@each'); + } sendEvent(this, '@array:before', [this, startIdx, removeAmt, addAmt]); - var removing, lim; - if (startIdx>=0 && removeAmt>=0 && get(this, 'hasEnumerableObservers')) { + if (startIdx >= 0 && removeAmt >= 0 && get(this, 'hasEnumerableObservers')) { removing = []; - lim = startIdx+removeAmt; - for(var idx=startIdx;idx<lim;idx++) removing.push(this.objectAt(idx)); + lim = startIdx + removeAmt; + + for (var idx = startIdx; idx < lim; idx++) { + removing.push(this.objectAt(idx)); + } } else { removing = removeAmt; } this.enumerableContentWillChange(removing, addAmt); @@ -29994,39 +30147,49 @@ @param {Number} addAmt The number of items that were added. If you pass `null` assumes 0. @return {Ember.Array} receiver */ arrayContentDidChange: function(startIdx, removeAmt, addAmt) { + var adding, lim; // if no args are passed assume everything changes - if (startIdx===undefined) { + if (startIdx === undefined) { startIdx = 0; removeAmt = addAmt = -1; } else { - if (removeAmt === undefined) removeAmt=-1; - if (addAmt === undefined) addAmt=-1; + if (removeAmt === undefined) { + removeAmt = -1; + } + + if (addAmt === undefined) { + addAmt = -1; + } } - var adding, lim; - if (startIdx>=0 && addAmt>=0 && get(this, 'hasEnumerableObservers')) { + if (startIdx >= 0 && addAmt >= 0 && get(this, 'hasEnumerableObservers')) { adding = []; - lim = startIdx+addAmt; - for(var idx=startIdx;idx<lim;idx++) adding.push(this.objectAt(idx)); + lim = startIdx + addAmt; + + for (var idx = startIdx; idx < lim; idx++) { + adding.push(this.objectAt(idx)); + } } else { adding = addAmt; } this.enumerableContentDidChange(removeAmt, adding); sendEvent(this, '@array:change', [this, startIdx, removeAmt, addAmt]); - var length = get(this, 'length'); + var length = get(this, 'length'); var cachedFirst = cacheFor(this, 'firstObject'); - var cachedLast = cacheFor(this, 'lastObject'); + var cachedLast = cacheFor(this, 'lastObject'); + if (this.objectAt(0) !== cachedFirst) { propertyWillChange(this, 'firstObject'); propertyDidChange(this, 'firstObject'); } + if (this.objectAt(length-1) !== cachedLast) { propertyWillChange(this, 'lastObject'); propertyDidChange(this, 'lastObject'); } @@ -37942,11 +38105,10 @@ */ // 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; @@ -37973,11 +38135,10 @@ Ember.ViewTargetActionSupport = ViewTargetActionSupport; Ember.RenderBuffer = RenderBuffer; var ViewUtils = Ember.ViewUtils = {}; - ViewUtils.setInnerHTML = setInnerHTML; ViewUtils.isSimpleClick = isSimpleClick; Ember.CoreView = CoreView; Ember.View = View; Ember.View.states = states; @@ -38457,27 +38618,68 @@ } __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","morph","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) { + ["ember-views/system/jquery","morph","ember-metal/core","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; /** @module ember @submodule ember-views */ - var Ember = __dependency1__["default"]; - // jQuery + var jQuery = __dependency1__["default"]; + var DOMHelper = __dependency2__.DOMHelper; + var Ember = __dependency3__["default"]; - var get = __dependency2__.get; - var set = __dependency3__.set; - var setInnerHTML = __dependency4__.setInnerHTML; - var jQuery = __dependency5__["default"]; - var DOMHelper = __dependency6__.DOMHelper; + // The HTML spec allows for "omitted start tags". These tags are optional + // when their intended child is the first thing in the parent tag. For + // example, this is a tbody start tag: + // + // <table> + // <tbody> + // <tr> + // + // The tbody may be omitted, and the browser will accept and render: + // + // <table> + // <tr> + // + // However, the omitted start tag will still be added to the DOM. Here + // we test the string and context to see if the browser is about to + // perform this cleanup, but with a special allowance for disregarding + // <script tags. This disregarding of <script being the first child item + // may bend the offical spec a bit, and is only needed for Handlebars + // templates. + // + // http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#optional-tags + // describes which tags are omittable. The spec for tbody and colgroup + // explains this behavior: + // + // http://www.whatwg.org/specs/web-apps/current-work/multipage/tables.html#the-tbody-element + // http://www.whatwg.org/specs/web-apps/current-work/multipage/tables.html#the-colgroup-element + // + var omittedStartTagChildren = { + tr: document.createElement('tbody'), + col: document.createElement('colgroup') + }; + var omittedStartTagChildTest = /(?:<script)*.*?<([\w:]+)/i; + + function detectOmittedStartTag(string, contextualElement){ + // Omitted start tags are only inside table tags. + if (contextualElement.tagName === 'TABLE') { + var omittedStartTagChildMatch = omittedStartTagChildTest.exec(string); + if (omittedStartTagChildMatch) { + // It is already asserted that the contextual element is a table + // and not the proper start tag. Just look up the start tag. + return omittedStartTagChildren[omittedStartTagChildMatch[1].toLowerCase()]; + } + } + } + function ClassSet() { this.seen = {}; this.list = []; } @@ -38550,34 +38752,36 @@ `Ember.renderBuffer` gathers information regarding the view and generates the final representation. `Ember.renderBuffer` will generate HTML which can be pushed to the DOM. ```javascript - var buffer = Ember.renderBuffer('div'); + var buffer = Ember.renderBuffer('div', contextualElement); ``` @method renderBuffer @namespace Ember @param {String} tagName tag name (such as 'div' or 'p') used for the buffer */ - __exports__["default"] = function renderBuffer(tagName) { - return new _RenderBuffer(tagName); // jshint ignore:line + __exports__["default"] = function renderBuffer(tagName, contextualElement) { + return new _RenderBuffer(tagName, contextualElement); // jshint ignore:line } - function _RenderBuffer(tagName) { + function _RenderBuffer(tagName, contextualElement) { this.tagName = tagName; + this._contextualElement = contextualElement; this.buffer = null; this.childViews = []; this.dom = new DOMHelper(); } _RenderBuffer.prototype = { - reset: function(tagName) { + reset: function(tagName, contextualElement) { this.tagName = tagName; this.buffer = null; this._element = null; + this._contextualElement = contextualElement; this.elementClasses = null; this.elementId = null; this.elementAttributes = null; this.elementProperties = null; this.elementTag = null; @@ -38586,10 +38790,13 @@ }, // The root view's element _element: null, + // The root view's contextualElement + _contextualElement: null, + /** An internal set used to de-dupe class names when `addClass()` is used. After each call to `addClass()`, the `classes` property will be updated. @@ -38659,11 +38866,11 @@ Usually, this gets set as the first parameter to `Ember.RenderBuffer`. For example, if you wanted to create a `p` tag, then you would call ```javascript - Ember.RenderBuffer('p') + Ember.RenderBuffer('p', contextualElement) ``` @property elementTag @type String @default null @@ -38689,19 +38896,23 @@ var index = this.childViews.length; this.childViews[index] = view; this.push("<script id='morph-"+index+"' type='text/x-placeholder'>\x3C/script>"); }, - hydrateMorphs: function () { + hydrateMorphs: function (contextualElement) { var childViews = this.childViews; var el = this._element; for (var i=0,l=childViews.length; i<l; i++) { var childView = childViews[i]; var ref = el.querySelector('#morph-'+i); var parent = ref.parentNode; - childView._morph = this.dom.insertMorphBefore(parent, ref); + childView._morph = this.dom.insertMorphBefore( + parent, + ref, + parent.nodeType === 1 ? parent : contextualElement + ); parent.removeChild(ref); } }, /** @@ -38859,11 +39070,11 @@ tagString = '<'+stripTagName(tagName)+' name="'+escapeAttribute(attrs.name)+'">'; } else { tagString = tagName; } - var element = document.createElement(tagString); + var element = this.dom.createElement(tagString); var $element = jQuery(element); if (id) { $element.attr('id', id); this.elementId = null; @@ -38913,25 +39124,35 @@ @method element @return {DOMElement} The element corresponding to the generated HTML of this buffer */ element: function() { + if (!this._contextualElement) { + Ember.deprecate("buffer.element expects a contextualElement to exist. This ensures DOM that requires context is correctly generated (tr, SVG tags). Defaulting to document.body, but this will be removed in the future"); + this._contextualElement = document.body; + } var html = this.innerString(); + var nodes; if (this._element) { if (html) { - this._element = setInnerHTML(this._element, html); - this.hydrateMorphs(); + nodes = this.dom.parseHTML(html, this._element); + while (nodes[0]) { + this._element.appendChild(nodes[0]); + } + this.hydrateMorphs(this._element); } } else { if (html) { + var omittedStartTag = detectOmittedStartTag(html, this._contextualElement); + var contextualElement = omittedStartTag || this._contextualElement; + nodes = this.dom.parseHTML(html, contextualElement); var frag = this._element = document.createDocumentFragment(); - var parsed = jQuery.parseHTML(html); - for (var i=0,l=parsed.length; i<l; i++) { - frag.appendChild(parsed[i]); + while (nodes[0]) { + frag.appendChild(nodes[0]); } - this.hydrateMorphs(); + this.hydrateMorphs(contextualElement); } else if (html === '') { this._element = html; } } return this._element; @@ -39024,22 +39245,22 @@ } return element; }; EmberRenderer.prototype.createElement = - function EmberRenderer_createElement(view) { + function EmberRenderer_createElement(view, contextualElement) { // If this is the top-most view, start a new buffer. Otherwise, // create a new buffer relative to the original using the // provided buffer operation (for example, `insertAfter` will // insert a new buffer after the "parent buffer"). var tagName = view.tagName; if (tagName === null || tagName === undefined) { tagName = 'div'; } var buffer = view.buffer = this.buffer; - buffer.reset(tagName); + buffer.reset(tagName, contextualElement); if (view.beforeRender) { view.beforeRender(buffer); } @@ -39127,166 +39348,19 @@ }; // element destroyed so view.destroy shouldn't try to remove it removedFromDOM __exports__["default"] = EmberRenderer; }); define("ember-views/system/utils", - ["ember-metal/core","ember-views/system/jquery","exports"], - function(__dependency1__, __dependency2__, __exports__) { + ["exports"], + function(__exports__) { "use strict"; - /* globals XMLSerializer */ - - var Ember = __dependency1__["default"]; - // Ember.assert - var jQuery = __dependency2__["default"]; - /** @module ember @submodule ember-views */ - /* BEGIN METAMORPH HELPERS */ - - // 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 &shy; - - var needsShy = typeof document !== 'undefined' && (function() { - var testEl = document.createElement('div'); - testEl.innerHTML = "<div></div>"; - testEl.firstChild.innerHTML = "<script></script>"; - return testEl.firstChild.innerHTML === ''; - })(); - - // IE 8 (and likely earlier) likes to move whitespace preceeding - // a script tag to appear after it. This means that we can - // accidentally remove whitespace when updating a morph. - var movesWhitespace = typeof document !== 'undefined' && (function() { - var testEl = document.createElement('div'); - testEl.innerHTML = "Test: <script type='text/x-placeholder'></script>Value"; - return testEl.childNodes[0].nodeValue === 'Test:' && - testEl.childNodes[2].nodeValue === ' Value'; - })(); - - // Use this to find children by ID instead of using jQuery - var findChildById = function(element, id) { - if (element.getAttribute('id') === id) { return element; } - - var len = element.childNodes.length; - var idx, node, found; - for (idx=0; idx<len; idx++) { - node = element.childNodes[idx]; - found = node.nodeType === 1 && findChildById(node, id); - if (found) { return found; } - } - }; - - var setInnerHTMLWithoutFix = function(element, html) { - if (needsShy) { - html = '&shy;' + html; - } - - var matches = []; - if (movesWhitespace) { - // Right now we only check for script tags with ids with the - // goal of targeting morphs. - html = html.replace(/(\s+)(<script id='([^']+)')/g, function(match, spaces, tag, id) { - matches.push([id, spaces]); - return tag; - }); - } - - element.innerHTML = html; - - // If we have to do any whitespace adjustments do them now - if (matches.length > 0) { - var len = matches.length; - var idx; - for (idx=0; idx<len; idx++) { - var script = findChildById(element, matches[idx][0]); - var node = document.createTextNode(matches[idx][1]); - script.parentNode.insertBefore(node, script); - } - } - - if (needsShy) { - var shyElement = element.firstChild; - while (shyElement.nodeType === 1 && !shyElement.nodeName) { - shyElement = shyElement.firstChild; - } - if (shyElement.nodeType === 3 && shyElement.nodeValue.charAt(0) === "\u00AD") { - shyElement.nodeValue = shyElement.nodeValue.slice(1); - } - } - }; - - /* END METAMORPH HELPERS */ - - function setInnerHTMLTestFactory(tagName, childTagName, ChildConstructor) { - return function() { - var el = document.createElement(tagName); - setInnerHTMLWithoutFix(el, '<' + childTagName + '>Content</' + childTagName + '>'); - return el.firstChild instanceof ChildConstructor; - }; - } - - - var innerHTMLTags = { - // IE 8 and earlier don't allow us to do innerHTML on select - select: function() { - var el = document.createElement('select'); - setInnerHTMLWithoutFix(el, '<option value="test">Test</option>'); - return el.options.length === 1; - }, - - // IE 9 and earlier don't allow us to set innerHTML on col, colgroup, frameset, - // html, style, table, tbody, tfoot, thead, title, tr. - col: setInnerHTMLTestFactory('col', 'span', window.HTMLSpanElement), - colgroup: setInnerHTMLTestFactory('colgroup', 'col', window.HTMLTableColElement), - frameset: setInnerHTMLTestFactory('frameset', 'frame', window.HTMLFrameElement), - table: setInnerHTMLTestFactory('table', 'tbody', window.HTMLTableSectionElement), - tbody: setInnerHTMLTestFactory('tbody', 'tr', window.HTMLTableRowElement), - tfoot: setInnerHTMLTestFactory('tfoot', 'tr', window.HTMLTableRowElement), - thead: setInnerHTMLTestFactory('thead', 'tr', window.HTMLTableRowElement), - tr: setInnerHTMLTestFactory('tr', 'td', window.HTMLTableCellElement) - }; - - var canSetInnerHTML = function(tagName) { - tagName = tagName.toLowerCase(); - var canSet = innerHTMLTags[tagName]; - - if (typeof canSet === 'function') { - canSet = innerHTMLTags[tagName] = canSet(); - } - - return canSet === undefined ? true : canSet; - }; - - function setInnerHTML(element, html) { - var tagName = element.tagName; - - if (canSetInnerHTML(tagName)) { - setInnerHTMLWithoutFix(element, html); - } else { - // Firefox versions < 11 do not have support for element.outerHTML. - var outerHTML = element.outerHTML || new XMLSerializer().serializeToString(element); - Ember.assert("Can't set innerHTML on "+element.tagName+" in this browser", outerHTML); - - var startTag = outerHTML.match(new RegExp("<"+tagName+"([^>]*)>", 'i'))[0]; - var endTag = '</'+tagName+'>'; - - var wrapper = document.createElement('div'); - jQuery(startTag + html + endTag).appendTo(wrapper); - element = wrapper.firstChild; - while (element.tagName !== tagName) { - element = element.nextSibling; - } - } - - return element; - } - - __exports__.setInnerHTML = setInnerHTML;function isSimpleClick(event) { + function isSimpleClick(event) { var modifier = event.shiftKey || event.metaKey || event.altKey || event.ctrlKey; var secondaryClick = event.which > 1; // IE9 may return undefined return !modifier && !secondaryClick; } @@ -42351,10 +42425,11 @@ @return {Ember.View} receiver */ createElement: function() { if (this.element) { return this; } + this._didCreateElementWithoutMorph = true; this.constructor.renderer.renderTree(this); return this; }, @@ -42368,10 +42443,13 @@ /** Called when the element of the view has been inserted into the DOM or after the view was re-rendered. Override this function to do any set up that requires an element in the document body. + When a view has children, didInsertElement will be called on the + child view(s) first, bubbling upwards through the hierarchy. + @event didInsertElement */ didInsertElement: Ember.K, /** @@ -43120,17 +43198,16 @@ var DOMHelper = __dependency2__["default"]; var DOMHelper; __exports__.DOMHelper = DOMHelper; }); define("morph/dom-helper", - ["../morph/morph","exports"], - function(__dependency1__, __exports__) { + ["../morph/morph","./dom-helper/build-html-dom","exports"], + function(__dependency1__, __dependency2__, __exports__) { "use strict"; var Morph = __dependency1__["default"]; + var buildHTMLDOM = __dependency2__.buildHTMLDOM; - var emptyString = ''; - var deletesBlankTextNodes = (function(){ var element = document.createElement('div'); element.appendChild( document.createTextNode('') ); var clonedElement = element.cloneNode(true); return clonedElement.childNodes.length === 0; @@ -43142,11 +43219,11 @@ var clonedElement = element.cloneNode(false); return !clonedElement.checked; })(); var svgNamespace = 'http://www.w3.org/2000/svg', - svgHTMLIntegrationPoints = ['foreignObject', 'desc', 'title']; + svgHTMLIntegrationPoints = {foreignObject: 1, desc: 1, title: 1}; function isSVG(ns){ return ns === svgNamespace; } @@ -43154,18 +43231,64 @@ // the elements inside that elements. function interiorNamespace(element){ if ( element && element.namespaceURI === svgNamespace && - svgHTMLIntegrationPoints.indexOf(element.tagName) === -1 + !svgHTMLIntegrationPoints[element.tagName] ) { return svgNamespace; } else { return null; } } + // The HTML spec allows for "omitted start tags". These tags are optional + // when their intended child is the first thing in the parent tag. For + // example, this is a tbody start tag: + // + // <table> + // <tbody> + // <tr> + // + // The tbody may be omitted, and the browser will accept and render: + // + // <table> + // <tr> + // + // However, the omitted start tag will still be added to the DOM. Here + // we test the string and context to see if the browser is about to + // perform this cleanup. + // + // http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#optional-tags + // describes which tags are omittable. The spec for tbody and colgroup + // explains this behavior: + // + // http://www.whatwg.org/specs/web-apps/current-work/multipage/tables.html#the-tbody-element + // http://www.whatwg.org/specs/web-apps/current-work/multipage/tables.html#the-colgroup-element + // + + var omittedStartTagChildTest = /<([\w:]+)/; + function detectOmittedStartTag(string, contextualElement){ + // Omitted start tags are only inside table tags. + if (contextualElement.tagName === 'TABLE') { + var omittedStartTagChildMatch = omittedStartTagChildTest.exec(string); + if (omittedStartTagChildMatch) { + var omittedStartTagChild = omittedStartTagChildMatch[1]; + // It is already asserted that the contextual element is a table + // and not the proper start tag. Just see if a tag was omitted. + return omittedStartTagChild === 'tr' || + omittedStartTagChild === 'col'; + } + } + } + + function buildSVGDOM(html, dom){ + var div = dom.document.createElement('div'); + div.innerHTML = '<svg>'+html+'</svg>'; + return div.firstChild.childNodes; + } + /* * A class wrapping DOM functions to address environment compatibility, * namespaces, contextual elements for morph un-escaped content * insertion. * @@ -43203,17 +43326,23 @@ prototype.setAttribute = function(element, name, value) { element.setAttribute(name, value); }; - prototype.createElement = function(tagName) { - if (this.namespace) { - return this.document.createElementNS(this.namespace, tagName); - } else { + if (document.createElementNS) { + prototype.createElement = function(tagName) { + if (this.namespace) { + return this.document.createElementNS(this.namespace, tagName); + } else { + return this.document.createElement(tagName); + } + }; + } else { + prototype.createElement = function(tagName) { return this.document.createElement(tagName); - } - }; + }; + } prototype.setNamespace = function(ns) { this.namespace = ns; }; @@ -43230,11 +43359,11 @@ }; prototype.repairClonedNode = function(element, blankChildTextNodes, isChecked){ if (deletesBlankTextNodes && blankChildTextNodes.length > 0) { for (var i=0, len=blankChildTextNodes.length;i<len;i++){ - var textNode = document.createTextNode(emptyString), + var textNode = this.document.createTextNode(''), offset = blankChildTextNodes[i], before = element.childNodes[offset]; if (before) { element.insertBefore(textNode, before); } else { @@ -43251,16 +43380,13 @@ var clone = element.cloneNode(!!deep); return clone; }; prototype.createMorph = function(parent, start, end, contextualElement){ - if (!contextualElement && parent.nodeType === Node.ELEMENT_NODE) { + if (!contextualElement && parent.nodeType === 1) { contextualElement = parent; } - if (!contextualElement) { - contextualElement = this.document.body; - } return new Morph(parent, start, end, this, contextualElement); }; // This helper is just to keep the templates good looking, // passing integers instead of element references. @@ -43270,43 +43396,247 @@ end = endIndex === -1 ? null : childNodes[endIndex]; return this.createMorph(parent, start, end, contextualElement); }; prototype.insertMorphBefore = function(element, referenceChild, contextualElement) { - var start = document.createTextNode(''); - var end = document.createTextNode(''); + var start = this.document.createTextNode(''); + var end = this.document.createTextNode(''); element.insertBefore(start, referenceChild); element.insertBefore(end, referenceChild); return this.createMorph(element, start, end, contextualElement); }; prototype.appendMorph = function(element, contextualElement) { - var start = document.createTextNode(''); - var end = document.createTextNode(''); + var start = this.document.createTextNode(''); + var end = this.document.createTextNode(''); element.appendChild(start); element.appendChild(end); return this.createMorph(element, start, end, contextualElement); }; - prototype.parseHTML = function(html, contextualElement){ - var element; - if (isSVG(this.namespace) && svgHTMLIntegrationPoints.indexOf(contextualElement.tagName) === -1) { - html = '<svg>'+html+'</svg>'; - element = document.createElement('div'); + prototype.parseHTML = function(html, contextualElement) { + var isSVGContent = ( + isSVG(this.namespace) && + !svgHTMLIntegrationPoints[contextualElement.tagName] + ); + + if (isSVGContent) { + return buildSVGDOM(html, this); } else { - element = this.cloneNode(contextualElement, false); + var nodes = buildHTMLDOM(html, contextualElement, this); + if (detectOmittedStartTag(html, contextualElement)) { + var node = nodes[0]; + while (node && node.nodeType !== 1) { + node = node.nextSibling; + } + return node.childNodes; + } else { + return nodes; + } } - element.innerHTML = html; - if (isSVG(this.namespace)) { - return element.firstChild.childNodes; - } else { - return element.childNodes; - } }; __exports__["default"] = DOMHelper; }); +define("morph/dom-helper/build-html-dom", + ["exports"], + function(__exports__) { + "use strict"; + // 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 &shy; + var needsShy = (function() { + var testEl = document.createElement('div'); + testEl.innerHTML = "<div></div>"; + testEl.firstChild.innerHTML = "<script><\/script>"; + return testEl.firstChild.innerHTML === ''; + })(); + + // IE 8 (and likely earlier) likes to move whitespace preceeding + // a script tag to appear after it. This means that we can + // accidentally remove whitespace when updating a morph. + var movesWhitespace = document && (function() { + var testEl = document.createElement('div'); + testEl.innerHTML = "Test: <script type='text/x-placeholder'><\/script>Value"; + return testEl.childNodes[0].nodeValue === 'Test:' && + testEl.childNodes[2].nodeValue === ' Value'; + })(); + + // IE 9 and earlier don't allow us to set innerHTML on col, colgroup, frameset, + // html, style, table, tbody, tfoot, thead, title, tr. Detect this and add + // them to an initial list of corrected tags. + // + // Here we are only dealing with the ones which can have child nodes. + // + var tagNamesRequiringInnerHTMLFix, tableNeedsInnerHTMLFix; + var tableInnerHTMLTestElement = document.createElement('table'); + try { + tableInnerHTMLTestElement.innerHTML = '<tbody></tbody>'; + } catch (e) { + } finally { + tableNeedsInnerHTMLFix = (tableInnerHTMLTestElement.childNodes.length === 0); + } + if (tableNeedsInnerHTMLFix) { + tagNamesRequiringInnerHTMLFix = { + colgroup: ['table'], + table: [], + tbody: ['table'], + tfoot: ['table'], + thead: ['table'], + tr: ['table', 'tbody'] + }; + } else { + tagNamesRequiringInnerHTMLFix = {}; + } + + // IE 8 doesn't allow setting innerHTML on a select tag. Detect this and + // add it to the list of corrected tags. + // + var selectInnerHTMLTestElement = document.createElement('select'); + selectInnerHTMLTestElement.innerHTML = '<option></option>'; + if (selectInnerHTMLTestElement) { + tagNamesRequiringInnerHTMLFix.select = []; + } + + function scriptSafeInnerHTML(element, html) { + // without a leading text node, IE will drop a leading script tag. + html = '&shy;'+html; + + element.innerHTML = html; + + var nodes = element.childNodes; + + // Look for &shy; to remove it. + var shyElement = nodes[0]; + while (shyElement.nodeType === 1 && !shyElement.nodeName) { + shyElement = shyElement.firstChild; + } + // At this point it's the actual unicode character. + if (shyElement.nodeType === 3 && shyElement.nodeValue.charAt(0) === "\u00AD") { + var newValue = shyElement.nodeValue.slice(1); + if (newValue.length) { + shyElement.nodeValue = shyElement.nodeValue.slice(1); + } else { + shyElement.parentNode.removeChild(shyElement); + } + } + + return nodes; + } + + function buildDOMWithFix(html, contextualElement){ + var tagName = contextualElement.tagName; + + // Firefox versions < 11 do not have support for element.outerHTML. + var outerHTML = contextualElement.outerHTML || new XMLSerializer().serializeToString(contextualElement); + if (!outerHTML) { + throw "Can't set innerHTML on "+tagName+" in this browser"; + } + + var wrappingTags = tagNamesRequiringInnerHTMLFix[tagName.toLowerCase()]; + var startTag = outerHTML.match(new RegExp("<"+tagName+"([^>]*)>", 'i'))[0]; + var endTag = '</'+tagName+'>'; + + var wrappedHTML = [startTag, html, endTag]; + + var i = wrappingTags.length; + var wrappedDepth = 1 + i; + while(i--) { + wrappedHTML.unshift('<'+wrappingTags[i]+'>'); + wrappedHTML.push('</'+wrappingTags[i]+'>'); + } + + var wrapper = document.createElement('div'); + scriptSafeInnerHTML(wrapper, wrappedHTML.join('')); + var element = wrapper; + while (wrappedDepth--) { + element = element.firstChild; + while (element && element.nodeType !== 1) { + element = element.nextSibling; + } + } + while (element && element.tagName !== tagName) { + element = element.nextSibling; + } + return element ? element.childNodes : []; + } + + function buildDOM(html, contextualElement, dom){ + contextualElement = dom.cloneNode(contextualElement, false); + scriptSafeInnerHTML(contextualElement, html); + return contextualElement.childNodes; + } + + var buildHTMLDOM; + // Really, this just means IE8 and IE9 get a slower buildHTMLDOM + if (tagNamesRequiringInnerHTMLFix.length > 0 || needsShy || movesWhitespace) { + buildHTMLDOM = function buildHTMLDOM(html, contextualElement, dom) { + // Make a list of the leading text on script nodes. Include + // script tags without any whitespace for easier processing later. + var spacesBefore = []; + var spacesAfter = []; + html = html.replace(/(\s*)(<script)/g, function(match, spaces, tag) { + spacesBefore.push(spaces); + return tag; + }); + + html = html.replace(/(<\/script>)(\s*)/g, function(match, tag, spaces) { + spacesAfter.push(spaces); + return tag; + }); + + // Fetch nodes + var nodes; + if (tagNamesRequiringInnerHTMLFix[contextualElement.tagName.toLowerCase()]) { + // buildDOMWithFix uses string wrappers for problematic innerHTML. + nodes = buildDOMWithFix(html, contextualElement); + } else { + nodes = buildDOM(html, contextualElement, dom); + } + + // Build a list of script tags, the nodes themselves will be + // mutated as we add test nodes. + var i, j, node, nodeScriptNodes; + var scriptNodes = []; + for (i=0;node=nodes[i];i++) { + if (node.nodeType !== 1) { + continue; + } + if (node.tagName === 'SCRIPT') { + scriptNodes.push(node); + } else { + nodeScriptNodes = node.getElementsByTagName('script'); + for (j=0;j<nodeScriptNodes.length;j++) { + scriptNodes.push(nodeScriptNodes[j]); + } + } + } + + // Walk the script tags and put back their leading text nodes. + var textNode, spaceBefore, spaceAfter; + for (i=0;scriptNode=scriptNodes[i];i++) { + spaceBefore = spacesBefore[i]; + if (spaceBefore && spaceBefore.length > 0) { + textNode = dom.document.createTextNode(spaceBefore); + scriptNode.parentNode.insertBefore(textNode, scriptNode); + } + + spaceAfter = spacesAfter[i]; + if (spaceAfter && spaceAfter.length > 0) { + textNode = dom.document.createTextNode(spaceAfter); + scriptNode.parentNode.insertBefore(textNode, scriptNode.nextSibling); + } + } + + return nodes; + }; + } else { + buildHTMLDOM = buildDOM; + } + + __exports__.buildHTMLDOM = buildHTMLDOM; + }); define("morph/morph", ["exports"], function(__exports__) { "use strict"; var splice = Array.prototype.splice; @@ -43316,11 +43646,11 @@ throw new Error('a fragment parent must have boundary nodes in order to detect insertion'); } } function ensureContext(contextualElement) { - if (!contextualElement || contextualElement.nodeType !== Node.ELEMENT_NODE) { + if (!contextualElement || contextualElement.nodeType !== 1) { throw new Error('An element node must be provided for a contextualElement, you provided ' + (contextualElement ? 'nodeType ' + contextualElement.nodeType : 'nothing')); } } @@ -44587,10 +44917,62 @@ function Router() { this.recognizer = new RouteRecognizer(); this.reset(); } + function getTransitionByIntent(intent, isIntermediate) { + var wasTransitioning = !!this.activeTransition; + var oldState = wasTransitioning ? this.activeTransition.state : this.state; + var newTransition; + + var newState = intent.applyToState(oldState, this.recognizer, this.getHandler, isIntermediate); + var queryParamChangelist = getChangelist(oldState.queryParams, newState.queryParams); + + if (handlerInfosEqual(newState.handlerInfos, oldState.handlerInfos)) { + + // This is a no-op transition. See if query params changed. + if (queryParamChangelist) { + newTransition = this.queryParamsTransition(queryParamChangelist, wasTransitioning, oldState, newState); + if (newTransition) { + return newTransition; + } + } + + // No-op. No need to create a new transition. + return new Transition(this); + } + + if (isIntermediate) { + setupContexts(this, newState); + return; + } + + // Create a new transition to the destination route. + newTransition = new Transition(this, intent, newState); + + // Abort and usurp any previously active transition. + if (this.activeTransition) { + this.activeTransition.abort(); + } + this.activeTransition = newTransition; + + // Transition promises by default resolve with resolved state. + // For our purposes, swap out the promise to resolve + // after the transition has been finalized. + newTransition.promise = newTransition.promise.then(function(result) { + return finalizeTransition(newTransition, result.state); + }, null, promiseLabel("Settle transition promise when transition is finalized")); + + if (!wasTransitioning) { + notifyExistingHandlers(this, newState, newTransition); + } + + fireQueryParamDidChange(this, newState, queryParamChangelist); + + return newTransition; + } + Router.prototype = { /** The main entry point into the router. The API is essentially the same as the `map` method in `route-recognizer`. @@ -44651,62 +45033,12 @@ // NOTE: this doesn't really belong here, but here // it shall remain until our ES6 transpiler can // handle cyclical deps. transitionByIntent: function(intent, isIntermediate) { - - var wasTransitioning = !!this.activeTransition; - var oldState = wasTransitioning ? this.activeTransition.state : this.state; - var newTransition; - var router = this; - try { - var newState = intent.applyToState(oldState, this.recognizer, this.getHandler, isIntermediate); - var queryParamChangelist = getChangelist(oldState.queryParams, newState.queryParams); - - if (handlerInfosEqual(newState.handlerInfos, oldState.handlerInfos)) { - - // This is a no-op transition. See if query params changed. - if (queryParamChangelist) { - newTransition = this.queryParamsTransition(queryParamChangelist, wasTransitioning, oldState, newState); - if (newTransition) { - return newTransition; - } - } - - // No-op. No need to create a new transition. - return new Transition(this); - } - - if (isIntermediate) { - setupContexts(this, newState); - return; - } - - // Create a new transition to the destination route. - newTransition = new Transition(this, intent, newState); - - // Abort and usurp any previously active transition. - if (this.activeTransition) { - this.activeTransition.abort(); - } - this.activeTransition = newTransition; - - // Transition promises by default resolve with resolved state. - // For our purposes, swap out the promise to resolve - // after the transition has been finalized. - newTransition.promise = newTransition.promise.then(function(result) { - return finalizeTransition(newTransition, result.state); - }, null, promiseLabel("Settle transition promise when transition is finalized")); - - if (!wasTransitioning) { - notifyExistingHandlers(this, newState, newTransition); - } - - fireQueryParamDidChange(this, newState, queryParamChangelist); - - return newTransition; + return getTransitionByIntent.apply(this, arguments); } catch(e) { return new Transition(this, intent, null, e); } }, @@ -45044,10 +45376,12 @@ function handlerEnteredOrUpdated(currentHandlerInfos, handlerInfo, enter, transition) { var handler = handlerInfo.handler, context = handlerInfo.context; - callHook(handler, 'enter', transition); + if (enter) { + callHook(handler, 'enter', transition); + } if (transition && transition.isAborted) { throw new TransitionAborted(); } handler.context = context; \ No newline at end of file