diff --git a/app/index.html b/app/index.html
index 402fefd..9d3eeac 100644
--- a/app/index.html
+++ b/app/index.html
@@ -35,8 +35,8 @@
-
-
+
+
diff --git a/app/scripts/vendor/ember-1.3.2.js b/app/scripts/vendor/ember-1.4.0.js
similarity index 92%
rename from app/scripts/vendor/ember-1.3.2.js
rename to app/scripts/vendor/ember-1.4.0.js
index 7354389..740a41b 100644
--- a/app/scripts/vendor/ember-1.3.2.js
+++ b/app/scripts/vendor/ember-1.4.0.js
@@ -5,7 +5,7 @@
* Portions Copyright 2008-2011 Apple Inc. All rights reserved.
* @license Licensed under MIT license
* See https://raw.github.com/emberjs/ember.js/master/LICENSE
- * @version 1.3.2
+ * @version 1.4.0
*/
@@ -70,11 +70,6 @@ if (!('MANDATORY_SETTER' in Ember.ENV)) {
*/
Ember.assert = function(desc, test) {
if (!test) {
- Ember.Logger.assert(test, desc);
- }
-
- if (Ember.testing && !test) {
- // when testing, ensure test failures when assertions fail
throw new Ember.Error("Assertion Failed: " + desc);
}
};
@@ -185,10 +180,21 @@ Ember.deprecateFunc = function(message, func) {
// Inform the developer about the Ember Inspector if not installed.
if (!Ember.testing) {
- if (typeof window !== 'undefined' && window.chrome && window.addEventListener) {
+ var isFirefox = typeof InstallTrigger !== 'undefined';
+ var isChrome = !!window.chrome && !window.opera;
+
+ if (typeof window !== 'undefined' && (isFirefox || isChrome) && window.addEventListener) {
window.addEventListener("load", function() {
if (document.body && document.body.dataset && !document.body.dataset.emberExtension) {
- Ember.debug('For more advanced debugging, install the Ember Inspector from https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi');
+ var downloadURL;
+
+ if(isChrome) {
+ downloadURL = 'https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi';
+ } else if(isFirefox) {
+ downloadURL = 'https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/'
+ }
+
+ Ember.debug('For more advanced debugging, install the Ember Inspector from ' + downloadURL);
}
}, false);
}
@@ -203,7 +209,7 @@ if (!Ember.testing) {
* 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.3.2
+ * @version 1.4.0
*/
@@ -286,7 +292,7 @@ var define, requireModule, require, requirejs;
@class Ember
@static
- @version 1.3.2
+ @version 1.4.0
*/
if ('undefined' === typeof Ember) {
@@ -313,10 +319,10 @@ Ember.toString = function() { return "Ember"; };
/**
@property VERSION
@type String
- @default '1.3.2'
+ @default '1.4.0'
@static
*/
-Ember.VERSION = '1.3.2';
+Ember.VERSION = '1.4.0';
/**
Standard environmental variables. You can define these in a global `EmberENV`
@@ -791,17 +797,19 @@ var arrayIndexOf = isNativeFunc(Array.prototype.indexOf) ? Array.prototype.index
return -1;
};
-/**
- Array polyfills to support ES5 features in older browsers.
- @namespace Ember
- @property ArrayPolyfills
-*/
-Ember.ArrayPolyfills = {
- map: arrayMap,
- forEach: arrayForEach,
- indexOf: arrayIndexOf
-};
+ /**
+ Array polyfills to support ES5 features in older browsers.
+
+ @namespace Ember
+ @property ArrayPolyfills
+ */
+ Ember.ArrayPolyfills = {
+ map: arrayMap,
+ forEach: arrayForEach,
+ indexOf: arrayIndexOf
+ };
+
if (Ember.SHIM_ES5) {
if (!Array.prototype.map) {
@@ -1029,7 +1037,7 @@ Ember.guidFor = function guidFor(obj) {
// META
//
-var META_DESC = {
+var META_DESC = Ember.META_DESC = {
writable: true,
configurable: false,
enumerable: false,
@@ -1069,7 +1077,8 @@ Meta.prototype = {
bindings: null,
chains: null,
chainWatchers: null,
- values: null
+ values: null,
+ proto: null
};
if (isDefinePropertySimulated) {
@@ -1576,6 +1585,41 @@ Ember.typeOf = function(item) {
return ret;
};
+/**
+ Convenience method to inspect an object. This method will attempt to
+ convert the object into a useful string description.
+
+ It is a pretty simple implementation. If you want something more robust,
+ use something like JSDump: https://github.com/NV/jsDump
+
+ @method inspect
+ @for Ember
+ @param {Object} obj The object you want to inspect.
+ @return {String} A description of the object
+*/
+Ember.inspect = function(obj) {
+ var type = Ember.typeOf(obj);
+ if (type === 'array') {
+ return '[' + obj + ']';
+ }
+ if (type !== 'object') {
+ return obj + '';
+ }
+
+ var v, ret = [];
+ for(var key in obj) {
+ if (obj.hasOwnProperty(key)) {
+ v = obj[key];
+ if (v === 'toString') { continue; } // ignore useless items
+ if (Ember.typeOf(v) === 'function') { v = "function() { ... }"; }
+ ret.push(key + ": " + v);
+ }
+ }
+ return "{" + ret.join(", ") + "}";
+};
+
+
+
})();
@@ -1790,36 +1834,130 @@ Ember.subscribe = Ember.Instrumentation.subscribe;
(function() {
-var map, forEach, indexOf, splice;
+var map, forEach, indexOf, splice, filter;
map = Array.prototype.map || Ember.ArrayPolyfills.map;
forEach = Array.prototype.forEach || Ember.ArrayPolyfills.forEach;
indexOf = Array.prototype.indexOf || Ember.ArrayPolyfills.indexOf;
+filter = Array.prototype.filter || Ember.ArrayPolyfills.filter;
splice = Array.prototype.splice;
+/**
+ * Defines some convenience methods for working with Enumerables.
+ * `Ember.EnumerableUtils` uses `Ember.ArrayPolyfills` when necessary.
+ *
+ * @class EnumerableUtils
+ * @namespace Ember
+ * @static
+ * */
var utils = Ember.EnumerableUtils = {
+ /**
+ * Calls the map function on the passed object with a specified callback. This
+ * uses `Ember.ArrayPolyfill`'s-map method when necessary.
+ *
+ * @method map
+ * @param {Object} obj The object that should be mapped
+ * @param {Function} callback The callback to execute
+ * @param {Object} thisArg Value to use as this when executing *callback*
+ *
+ * @return {Array} An array of mapped values.
+ */
map: function(obj, callback, thisArg) {
return obj.map ? obj.map.call(obj, callback, thisArg) : map.call(obj, callback, thisArg);
},
+ /**
+ * Calls the forEach function on the passed object with a specified callback. This
+ * uses `Ember.ArrayPolyfill`'s-forEach method when necessary.
+ *
+ * @method forEach
+ * @param {Object} obj The object to call forEach on
+ * @param {Function} callback The callback to execute
+ * @param {Object} thisArg Value to use as this when executing *callback*
+ *
+ */
forEach: function(obj, callback, thisArg) {
return obj.forEach ? obj.forEach.call(obj, callback, thisArg) : forEach.call(obj, callback, thisArg);
},
+ /**
+ * Calls the filter function on the passed object with a specified callback. This
+ * uses `Ember.ArrayPolyfill`'s-filter method when necessary.
+ *
+ * @method filter
+ * @param {Object} obj The object to call filter on
+ * @param {Function} callback The callback to execute
+ * @param {Object} thisArg Value to use as this when executing *callback*
+ *
+ * @return {Array} An array containing the filtered values
+ */
+ filter: function(obj, callback, thisArg) {
+ return obj.filter ? obj.filter.call(obj, callback, thisArg) : filter.call(obj, callback, thisArg);
+ },
+
+ /**
+ * Calls the indexOf function on the passed object with a specified callback. This
+ * uses `Ember.ArrayPolyfill`'s-indexOf method when necessary.
+ *
+ * @method indexOf
+ * @param {Object} obj The object to call indexOn on
+ * @param {Function} callback The callback to execute
+ * @param {Object} index The index to start searching from
+ *
+ */
indexOf: function(obj, element, index) {
return obj.indexOf ? obj.indexOf.call(obj, element, index) : indexOf.call(obj, element, index);
},
+ /**
+ * Returns an array of indexes of the first occurrences of the passed elements
+ * on the passed object.
+ *
+ * ```javascript
+ * var array = [1, 2, 3, 4, 5];
+ * Ember.EnumerableUtils.indexesOf(array, [2, 5]); // [1, 4]
+ *
+ * var fubar = "Fubarr";
+ * Ember.EnumerableUtils.indexesOf(fubar, ['b', 'r']); // [2, 4]
+ * ```
+ *
+ * @method indexesOf
+ * @param {Object} obj The object to check for element indexes
+ * @param {Array} elements The elements to search for on *obj*
+ *
+ * @return {Array} An array of indexes.
+ *
+ */
indexesOf: function(obj, elements) {
return elements === undefined ? [] : utils.map(elements, function(item) {
return utils.indexOf(obj, item);
});
},
+ /**
+ * Adds an object to an array. If the array already includes the object this
+ * method has no effect.
+ *
+ * @method addObject
+ * @param {Array} array The array the passed item should be added to
+ * @param {Object} item The item to add to the passed array
+ *
+ * @return 'undefined'
+ */
addObject: function(array, item) {
var index = utils.indexOf(array, item);
if (index === -1) { array.push(item); }
},
+ /**
+ * Removes an object from an array. If the array does not contain the passed
+ * object this method has no effect.
+ *
+ * @method removeObject
+ * @param {Array} array The array to remove the item from.
+ * @param {Object} item The item to remove from the passed array.
+ *
+ * @return 'undefined'
+ */
removeObject: function(array, item) {
var index = utils.indexOf(array, item);
if (index !== -1) { array.splice(index, 1); }
@@ -1845,6 +1983,31 @@ var utils = Ember.EnumerableUtils = {
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);
@@ -1853,6 +2016,29 @@ var utils = Ember.EnumerableUtils = {
}
},
+ /**
+ * 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 = [];
@@ -1986,7 +2172,7 @@ var normalizeTuple = Ember.normalizeTuple = function(target, path) {
}
// must return some kind of path to be valid else other things will break.
- if (!path || path.length===0) throw new Ember.Error('Invalid Path');
+ if (!path || path.length===0) throw new Ember.Error('Path cannot be empty');
return [ target, path ];
};
@@ -2505,7 +2691,7 @@ ObserverSet.prototype.clear = function() {
(function() {
-var metaFor = Ember.meta,
+var META_KEY = Ember.META_KEY,
guidFor = Ember.guidFor,
tryFinally = Ember.tryFinally,
sendEvent = Ember.sendEvent,
@@ -2536,10 +2722,10 @@ var metaFor = Ember.meta,
@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; }
@@ -2566,10 +2752,10 @@ Ember.propertyWillChange = propertyWillChange;
@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; }
@@ -2642,7 +2828,7 @@ function chainsWillChange(obj, keyName, m) {
}
function chainsDidChange(obj, keyName, m, suppressEvents) {
- if (!(m.hasOwnProperty('chainWatchers') &&
+ if (!(m && m.hasOwnProperty('chainWatchers') &&
m.chainWatchers[keyName])) {
return;
}
@@ -2758,16 +2944,6 @@ var META_KEY = Ember.META_KEY,
property is not defined but the object implements the `setUnknownProperty`
method then that will be invoked as well.
- If you plan to run on IE8 and older browsers then you should use this
- method anytime you want to set a property on an object that you don't
- know for sure is private. (Properties beginning with an underscore '_'
- are considered private.)
-
- On all newer browsers, you only need to use this method to set
- properties if the property might not be defined on the object and you want
- to respect the `setUnknownProperty` handler. Otherwise you can ignore this
- method.
-
@method set
@for Ember
@param {Object} obj The object to modify.
@@ -3523,7 +3699,8 @@ Ember.defineProperty = function(obj, keyName, desc, data, meta) {
} else {
obj[keyName] = undefined; // make enumerable
}
- } else {
+
+ } else {
descs[keyName] = undefined; // shadow descriptor in proto
if (desc == null) {
value = data;
@@ -3645,11 +3822,11 @@ var metaFor = Ember.meta, // 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]) {
@@ -3674,8 +3851,8 @@ Ember.watchKey = function(obj, keyName) {
};
-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;
@@ -3718,7 +3895,8 @@ var metaFor = Ember.meta, // utils.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];
@@ -3752,24 +3930,24 @@ function addChainWatcher(obj, keyName, node) {
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
@@ -3809,14 +3987,14 @@ 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];
@@ -4028,12 +4206,14 @@ ChainNodePrototype.didChange = function(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);
}
};
@@ -4042,6 +4222,50 @@ Ember.finishChains = function(obj) {
(function() {
+/**
+ @module ember-metal
+ */
+
+var forEach = Ember.EnumerableUtils.forEach,
+BRACE_EXPANSION = /^((?:[^\.]*\.)*)\{(.*)\}$/;
+
+/**
+ Expands `pattern`, invoking `callback` for each expansion.
+
+ The only pattern supported is brace-expansion, anything else will be passed
+ once to `callback` directly. Brace expansion can only appear at the end of a
+ pattern, for example as the last item in a chain.
+
+ Example
+ ```js
+ function echo(arg){ console.log(arg); }
+
+ Ember.expandProperties('foo.bar', echo); //=> 'foo.bar'
+ Ember.expandProperties('{foo,bar}', echo); //=> 'foo', 'bar'
+ Ember.expandProperties('foo.{bar,baz}', echo); //=> 'foo.bar', 'foo.baz'
+ Ember.expandProperties('{foo,bar}.baz', echo); //=> '{foo,bar}.baz'
+ ```
+
+ @method
+ @private
+ @param {string} pattern The property pattern to expand.
+ @param {function} callback The callback to invoke. It is invoked once per
+ expansion, and is passed the expansion.
+ */
+Ember.expandProperties = function (pattern, callback) {
+ var match, prefix, list;
+
+ if (match = BRACE_EXPANSION.exec(pattern)) {
+ prefix = match[1];
+ list = match[2];
+
+ forEach(list.split(','), function (suffix) {
+ callback(prefix + suffix);
+ });
+ } else {
+ callback(pattern);
+ }
+};
})();
@@ -4055,8 +4279,8 @@ var metaFor = Ember.meta, // utils.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) {
@@ -4065,26 +4289,26 @@ function chainsFor(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]--;
}
@@ -4128,14 +4352,14 @@ function isKeyName(path) {
@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);
}
};
@@ -4146,14 +4370,14 @@ Ember.isWatching = function isWatching(obj, key) {
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);
}
};
@@ -4168,7 +4392,7 @@ Ember.unwatch = function(obj, _keyPath) {
@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)) {
@@ -4245,6 +4469,7 @@ var get = Ember.get,
watch = Ember.watch,
unwatch = Ember.unwatch;
+var expandProperties = Ember.expandProperties;
// ..........................................................
// DEPENDENT KEYS
@@ -4294,7 +4519,7 @@ function addDependentKeys(desc, obj, keyName, meta) {
// 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);
}
}
@@ -4313,7 +4538,7 @@ function removeDependentKeys(desc, obj, keyName, meta) {
// 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);
}
}
@@ -4404,9 +4629,11 @@ function removeDependentKeys(desc, obj, keyName, meta) {
*/
function ComputedProperty(func, opts) {
this.func = func;
+
+ this._dependentKeys = opts && opts.dependentKeys;
+
this._cacheable = (opts && opts.cacheable !== undefined) ? opts.cacheable : true;
- this._dependentKeys = opts && opts.dependentKeys;
this._readOnly = opts && (opts.readOnly !== undefined || !!opts.readOnly);
}
@@ -4415,6 +4642,7 @@ ComputedProperty.prototype = new Ember.Descriptor();
var ComputedPropertyPrototype = ComputedProperty.prototype;
+
/**
Properties are cacheable by default. Computed property will automatically
cache the return value of your function until one of the dependent keys changes.
@@ -4509,11 +4737,19 @@ ComputedPropertyPrototype.readOnly = function(readOnly) {
ComputedPropertyPrototype.property = function() {
var args;
+ var addArg = function (property) {
+ args.push(property);
+ };
+
+ args = [];
+ for (var i = 0, l = arguments.length; i < l; i++) {
+ expandProperties(arguments[i], addArg);
+ }
+
- args = a_slice.call(arguments);
+ this._dependentKeys = args;
- this._dependentKeys = args;
return this;
};
@@ -4640,7 +4876,7 @@ ComputedPropertyPrototype.set = function(obj, keyName, value) {
funcArgLength, cachedValue, ret;
if (this._readOnly) {
- throw new Ember.Error('Cannot Set: ' + keyName + ' on: ' + obj.toString() );
+ throw new Ember.Error('Cannot Set: ' + keyName + ' on: ' + Ember.inspect(obj));
}
this._suspended = obj;
@@ -4756,7 +4992,8 @@ Ember.computed = function(func) {
@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];
@@ -4771,26 +5008,33 @@ function getProperties(self, propertyNames) {
return ret;
}
-function registerComputed(name, macro) {
- Ember.computed[name] = function(dependentKey) {
- var args = a_slice.call(arguments);
- return Ember.computed(dependentKey, function() {
- return macro.apply(this, args);
- });
+var registerComputed, registerComputedWithProperties;
+
+
+
+ registerComputed = function (name, macro) {
+ Ember.computed[name] = function(dependentKey) {
+ var args = a_slice.call(arguments);
+ return Ember.computed(dependentKey, function() {
+ return macro.apply(this, args);
+ });
+ };
};
-}
-function registerComputedWithProperties(name, macro) {
- Ember.computed[name] = function() {
- var properties = a_slice.call(arguments);
+ registerComputedWithProperties = function(name, macro) {
+ Ember.computed[name] = function() {
+ var properties = a_slice.call(arguments);
- var computed = Ember.computed(function() {
- return macro.apply(this, [getProperties(this, properties)]);
- });
+ var computed = Ember.computed(function() {
+ return macro.apply(this, [getProperties(this, properties)]);
+ });
- return computed.property.apply(computed, properties);
+ return computed.property.apply(computed, properties);
+ };
};
-}
+
+
+
/**
A computed property that returns true if the value of the dependent
@@ -5321,7 +5565,6 @@ Ember.computed.oneWay = function(dependentKey) {
});
};
-
/**
A computed property that acts like a standard getter and setter,
but returns the value at the provided `defaultPath` if the
@@ -5998,7 +6241,7 @@ define("backburner",
return true;
}
}
- } else if (window.toString.call(timer) === "[object Array]"){ // we're cancelling a throttle or debounce
+ } else if (Object.prototype.toString.call(timer) === "[object Array]"){ // we're cancelling a throttle or debounce
return this._cancelItem(findThrottler, throttlers, timer) ||
this._cancelItem(findDebouncee, debouncees, timer);
} else {
@@ -6112,6 +6355,7 @@ define("backburner",
__exports__.Backburner = Backburner;
});
+
})();
@@ -6135,7 +6379,8 @@ var Backburner = requireModule('backburner').Backburner,
onBegin: onBegin,
onEnd: onEnd
}),
- slice = [].slice;
+ slice = [].slice,
+ concat = [].concat;
// ..........................................................
// Ember.run - this is ideally the only public API the dev sees
@@ -6221,7 +6466,7 @@ Ember.run = function(target, method) {
@return {Object} Return value from invoking the passed function. Please note,
when called within an existing loop, no return value is possible.
*/
-Ember.run.join = function(target, method) {
+Ember.run.join = function(target, method /* args */) {
if (!Ember.run.currentRunLoop) {
return Ember.run.apply(Ember.run, arguments);
}
@@ -6231,6 +6476,53 @@ Ember.run.join = function(target, method) {
Ember.run.schedule.apply(Ember.run, args);
};
+/**
+ Provides a useful utility for when integrating with non-Ember libraries
+ that provide asynchronous callbacks.
+
+ Ember utilizes a run-loop to batch and coalesce changes. This works by
+ marking the start and end of Ember-related Javascript execution.
+
+ When using events such as a View's click handler, Ember wraps the event
+ handler in a run-loop, but when integrating with non-Ember libraries this
+ can be tedious.
+
+ For example, the following is rather verbose but is the correct way to combine
+ third-party events and Ember code.
+
+ ```javascript
+ var that = this;
+ jQuery(window).on('resize', function(){
+ Ember.run(function(){
+ that.handleResize();
+ });
+ });
+ ```
+
+ To reduce the boilerplate, the following can be used to construct a
+ run-loop-wrapped callback handler.
+
+ ```javascript
+ jQuery(window).on('resize', Ember.run.bind(this, this.triggerResize));
+ ```
+
+ @method bind
+ @namespace Ember.run
+ @param {Object} [target] target of method to call
+ @param {Function|String} method Method to invoke.
+ May be a function or a string. If you pass a string
+ then it will be looked up on the passed target.
+ @param {Object} [args*] Any additional arguments you wish to pass to the method.
+ @return {Object} return value from invoking the passed function. Please note,
+ when called within an existing loop, no return value is possible.
+*/
+Ember.run.bind = function(target, method /* args*/) {
+ var args = arguments;
+ return function() {
+ return Ember.run.join.apply(Ember.run, args);
+ };
+};
+
Ember.run.backburner = backburner;
var run = Ember.run;
@@ -6400,7 +6692,7 @@ Ember.run.later = function(target, method) {
If you pass a string it will be resolved on the
target at the time the method is invoked.
@param {Object} [args*] Optional arguments to pass to the timeout.
- @return {Object} timer
+ @return {Object} Timer information for use in cancelling, see `Ember.run.cancel`.
*/
Ember.run.once = function(target, method) {
checkAutoRun();
@@ -6451,7 +6743,7 @@ Ember.run.once = function(target, method) {
If you pass a string it will be resolved on the
target at the time the method is invoked.
@param {Object} [args*] Optional arguments to pass to the timeout.
- @return {Object} timer
+ @return {Object} Timer information for use in cancelling, see `Ember.run.cancel`.
*/
Ember.run.scheduleOnce = function(queue, target, method) {
checkAutoRun();
@@ -6514,7 +6806,7 @@ Ember.run.scheduleOnce = function(queue, target, method) {
If you pass a string it will be resolved on the
target at the time the method is invoked.
@param {Object} [args*] Optional arguments to pass to the timeout.
- @return {Object} timer
+ @return {Object} Timer information for use in cancelling, see `Ember.run.cancel`.
*/
Ember.run.next = function() {
var args = slice.call(arguments);
@@ -6524,7 +6816,8 @@ Ember.run.next = function() {
/**
Cancels a scheduled item. Must be a value returned by `Ember.run.later()`,
- `Ember.run.once()`, or `Ember.run.next()`.
+ `Ember.run.once()`, `Ember.run.next()`, `Ember.run.debounce()`, or
+ `Ember.run.throttle()`.
```javascript
var runNext = Ember.run.next(myContext, function() {
@@ -6541,11 +6834,29 @@ Ember.run.next = function() {
// will not be executed
});
Ember.run.cancel(runOnce);
+
+ var throttle = Ember.run.throttle(myContext, function() {
+ // will not be executed
+ }, 1);
+ Ember.run.cancel(throttle);
+
+ var debounce = Ember.run.debounce(myContext, function() {
+ // will not be executed
+ }, 1);
+ Ember.run.cancel(debounce);
+
+ var debounceImmediate = Ember.run.debounce(myContext, function() {
+ // will be executed since we passed in true (immediate)
+ }, 100, true);
+ // the 100ms delay until this method can be called again will be cancelled
+ Ember.run.cancel(debounceImmediate);
+ ```
+ ```
```
@method cancel
@param {Object} timer Timer object to cancel
- @return {void}
+ @return {Boolean} true if cancelled or false/undefined if it wasn't found
*/
Ember.run.cancel = function(timer) {
return backburner.cancel(timer);
@@ -6577,6 +6888,34 @@ Ember.run.cancel = function(timer) {
// console logs 'debounce ran.' one time.
```
+ Immediate allows you to run the function immediately, but debounce
+ other calls for this function until the wait time has elapsed. If
+ `debounce` is called again before the specified time has elapsed,
+ the timer is reset and the entire period msut pass again before
+ the method can be called again.
+
+ ```javascript
+ var myFunc = function() { console.log(this.name + ' ran.'); };
+ var myContext = {name: 'debounce'};
+
+ Ember.run.debounce(myContext, myFunc, 150, true);
+
+ // console logs 'debounce ran.' one time immediately.
+ // 100ms passes
+
+ Ember.run.debounce(myContext, myFunc, 150, true);
+
+ // 150ms passes and nothing else is logged to the console and
+ // the debouncee is no longer being watched
+
+ Ember.run.debounce(myContext, myFunc, 150, true);
+
+ // console logs 'debounce ran.' one time immediately.
+ // 150ms passes and nothing else is logged tot he console and
+ // the debouncee is no longer being watched
+
+ ```
+
@method debounce
@param {Object} [target] target of method to invoke
@param {Function|String} method The method to invoke.
@@ -6585,7 +6924,7 @@ Ember.run.cancel = function(timer) {
@param {Object} [args*] Optional arguments to pass to the timeout.
@param {Number} wait Number of milliseconds to wait.
@param {Boolean} immediate Trigger the function on the leading instead of the trailing edge of the wait interval.
- @return {void}
+ @return {Array} Timer information for use in cancelling, see `Ember.run.cancel`.
*/
Ember.run.debounce = function() {
return backburner.debounce.apply(backburner, arguments);
@@ -6622,7 +6961,7 @@ Ember.run.debounce = function() {
then it will be looked up on the passed target.
@param {Object} [args*] Optional arguments to pass to the timeout.
@param {Number} spacing Number of milliseconds to space out requests.
- @return {void}
+ @return {Array} Timer information for use in cancelling, see `Ember.run.cancel`.
*/
Ember.run.throttle = function() {
return backburner.throttle.apply(backburner, arguments);
@@ -7130,11 +7469,14 @@ var Mixin, REQUIRED, Alias,
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')) {
@@ -7319,7 +7661,7 @@ function mergeMixins(mixins, m, descs, values, base, keys) {
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);
@@ -7377,7 +7719,7 @@ function connectBindings(obj, m) {
}
function finishPartial(obj, m) {
- connectBindings(obj, m || Ember.meta(obj));
+ connectBindings(obj, m || metaFor(obj));
return obj;
}
@@ -7424,7 +7766,7 @@ function replaceObserversAndListeners(obj, key, observerOrListener) {
}
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:
@@ -7506,7 +7848,7 @@ Ember.mixin = function(obj) {
Note that mixins extend a constructor's prototype so arrays and object literals
defined as properties will be shared amongst objects that implement the mixin.
- If you want to define an property in a mixin that is not shared, you can define
+ If you want to define a property in a mixin that is not shared, you can define
it either as a computed property or have it be created on initialization of the object.
```javascript
@@ -7636,7 +7978,8 @@ function _detect(curMixin, targetMixin, seen) {
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)];
}
@@ -7675,7 +8018,8 @@ MixinPrototype.keys = function() {
// 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; }
@@ -7707,35 +8051,6 @@ Alias = function(methodName) {
};
Alias.prototype = new Ember.Descriptor();
-/**
- Makes a property or method available via an additional name.
-
- ```javascript
- App.PaintSample = Ember.Object.extend({
- color: 'red',
- colour: Ember.alias('color'),
- name: function() {
- return "Zed";
- },
- moniker: Ember.alias("name")
- });
-
- var paintSample = App.PaintSample.create()
- paintSample.get('colour'); // 'red'
- paintSample.moniker(); // 'Zed'
- ```
-
- @method alias
- @for Ember
- @param {String} methodName name of the method or property to alias
- @return {Ember.Descriptor}
- @deprecated Use `Ember.aliasMethod` or `Ember.computed.alias` instead
-*/
-Ember.alias = function(methodName) {
- Ember.deprecate("Ember.alias is deprecated. Please use Ember.aliasMethod or Ember.computed.alias instead.");
- return new Alias(methodName);
-};
-
/**
Makes a method available via an additional name.
@@ -7790,16 +8105,21 @@ Ember.observer = function() {
var func = a_slice.call(arguments, -1)[0];
var paths;
-
- paths = a_slice.call(arguments, 0, -1);
+ var addWatchedProperty = function (path) { paths.push(path); };
+ var _paths = a_slice.call(arguments, 0, -1);
- if (typeof func !== "function") {
- // revert to old, soft-deprecated argument ordering
+ if (typeof func !== "function") {
+ // revert to old, soft-deprecated argument ordering
- func = arguments[0];
- paths = a_slice.call(arguments, 1);
- }
-
+ func = arguments[0];
+ _paths = a_slice.call(arguments, 1);
+ }
+
+ paths = [];
+
+ for (var i=0; i<_paths.length; ++i) {
+ expandProperties(_paths[i], addWatchedProperty);
+ }
if (typeof func !== "function") {
throw new Ember.Error("Ember.observer called without a function");
@@ -7888,16 +8208,22 @@ Ember.beforeObserver = function() {
var func = a_slice.call(arguments, -1)[0];
var paths;
-
- paths = a_slice.call(arguments, 0, -1);
+ var addWatchedProperty = function(path) { paths.push(path); };
- if (typeof func !== "function") {
- // revert to old, soft-deprecated argument ordering
+ var _paths = a_slice.call(arguments, 0, -1);
- func = arguments[0];
- paths = a_slice.call(arguments, 1);
- }
-
+ if (typeof func !== "function") {
+ // revert to old, soft-deprecated argument ordering
+
+ func = arguments[0];
+ _paths = a_slice.call(arguments, 1);
+ }
+
+ paths = [];
+
+ for (var i=0; i<_paths.length; ++i) {
+ expandProperties(_paths[i], addWatchedProperty);
+ }
if (typeof func !== "function") {
throw new Ember.Error("Ember.beforeObserver called without a function");
@@ -9571,7 +9897,7 @@ define("rsvp/promise/all",
```
@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.
@@ -10135,6 +10461,7 @@ Ember.MODEL_FACTORY_INJECTIONS = false || !!Ember.ENV.MODEL_FACTORY_INJECTIONS;
define("container",
[],
function() {
+ "use strict";
// A safe and simple inheriting object.
function InheritingDict(parent) {
@@ -10256,7 +10583,8 @@ define("container",
this.registry = new InheritingDict(parent && parent.registry);
this.cache = new InheritingDict(parent && parent.cache);
- this.factoryCache = new InheritingDict(parent && parent.cache);
+ this.factoryCache = new InheritingDict(parent && parent.factoryCache);
+ this.resolveCache = new InheritingDict(parent && parent.resolveCache);
this.typeInjections = new InheritingDict(parent && parent.typeInjections);
this.injections = {};
@@ -10377,9 +10705,7 @@ define("container",
@param {Object} options
*/
register: function(fullName, factory, options) {
- if (fullName.indexOf(':') === -1) {
- throw new TypeError("malformed fullName, expected: `type:name` got: " + fullName + "");
- }
+ validateFullName(fullName);
if (factory === undefined) {
throw new TypeError('Attempting to register an unknown factory: `' + fullName + '`');
@@ -10412,11 +10738,14 @@ define("container",
@param {String} fullName
*/
unregister: function(fullName) {
+ validateFullName(fullName);
+
var normalizedName = this.normalize(fullName);
this.registry.remove(normalizedName);
this.cache.remove(normalizedName);
this.factoryCache.remove(normalizedName);
+ this.resolveCache.remove(normalizedName);
this._options.remove(normalizedName);
},
@@ -10453,7 +10782,18 @@ define("container",
@return {Function} fullName's factory
*/
resolve: function(fullName) {
- return this.resolver(fullName) || this.registry.get(fullName);
+ validateFullName(fullName);
+
+ var normalizedName = this.normalize(fullName);
+ var cached = this.resolveCache.get(normalizedName);
+
+ if (cached) { return cached; }
+
+ var resolved = this.resolver(normalizedName) || this.registry.get(normalizedName);
+
+ this.resolveCache.set(normalizedName, resolved);
+
+ return resolved;
},
/**
@@ -10534,23 +10874,8 @@ define("container",
@return {any}
*/
lookup: function(fullName, options) {
- fullName = this.normalize(fullName);
-
- options = options || {};
-
- if (this.cache.has(fullName) && options.singleton !== false) {
- return this.cache.get(fullName);
- }
-
- var value = instantiate(this, fullName);
-
- if (value === undefined) { return; }
-
- if (isSingleton(this, fullName) && options.singleton !== false) {
- this.cache.set(fullName, value);
- }
-
- return value;
+ validateFullName(fullName);
+ return lookup(this, this.normalize(fullName), options);
},
/**
@@ -10561,7 +10886,8 @@ define("container",
@return {any}
*/
lookupFactory: function(fullName) {
- return factoryFor(this, fullName);
+ validateFullName(fullName);
+ return factoryFor(this, this.normalize(fullName));
},
/**
@@ -10573,11 +10899,8 @@ define("container",
@return {Boolean}
*/
has: function(fullName) {
- if (this.cache.has(fullName)) {
- return true;
- }
-
- return !!this.resolve(fullName);
+ validateFullName(fullName);
+ return has(this, this.normalize(fullName));
},
/**
@@ -10658,6 +10981,7 @@ define("container",
@param {String} fullName
*/
typeInjection: function(type, property, fullName) {
+ validateFullName(fullName);
if (this.parent) { illegalChildOperation('typeInjection'); }
addTypeInjection(this.typeInjections, type, property, fullName);
@@ -10707,14 +11031,20 @@ define("container",
@param {String} property
@param {String} injectionName
*/
- injection: function(factoryName, property, injectionName) {
+ injection: function(fullName, property, injectionName) {
if (this.parent) { illegalChildOperation('injection'); }
- if (factoryName.indexOf(':') === -1) {
- return this.typeInjection(factoryName, property, injectionName);
+ validateFullName(injectionName);
+ var normalizedInjectionName = this.normalize(injectionName);
+
+ if (fullName.indexOf(':') === -1) {
+ return this.typeInjection(fullName, property, normalizedInjectionName);
}
- addInjection(this.injections, factoryName, property, injectionName);
+ validateFullName(fullName);
+ var normalizedName = this.normalize(fullName);
+
+ addInjection(this.injections, normalizedName, property, normalizedInjectionName);
},
@@ -10750,7 +11080,7 @@ define("container",
factoryTypeInjection: function(type, property, fullName) {
if (this.parent) { illegalChildOperation('factoryTypeInjection'); }
- addTypeInjection(this.factoryTypeInjections, type, property, fullName);
+ addTypeInjection(this.factoryTypeInjections, type, property, this.normalize(fullName));
},
/**
@@ -10802,14 +11132,21 @@ define("container",
@param {String} property
@param {String} injectionName
*/
- factoryInjection: function(factoryName, property, injectionName) {
+ factoryInjection: function(fullName, property, injectionName) {
if (this.parent) { illegalChildOperation('injection'); }
- if (factoryName.indexOf(':') === -1) {
- return this.factoryTypeInjection(factoryName, property, injectionName);
+ var normalizedName = this.normalize(fullName);
+ var normalizedInjectionName = this.normalize(injectionName);
+
+ validateFullName(injectionName);
+
+ if (fullName.indexOf(':') === -1) {
+ return this.factoryTypeInjection(normalizedName, property, normalizedInjectionName);
}
- addInjection(this.factoryInjections, factoryName, property, injectionName);
+ validateFullName(fullName);
+
+ addInjection(this.factoryInjections, normalizedName, property, normalizedInjectionName);
},
/**
@@ -10819,7 +11156,6 @@ define("container",
@method destroy
*/
destroy: function() {
-
for (var i=0, l=this.children.length; i` and the following code:
```javascript
- anUndorderedListView = Ember.CollectionView.create({
+ anUnorderedListView = Ember.CollectionView.create({
tagName: 'ul',
content: ['A','B','C'],
itemViewClass: Ember.View.extend({
@@ -24942,7 +25392,7 @@ var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt;
})
});
- anUndorderedListView.appendTo('body');
+ anUnorderedListView.appendTo('body');
```
Will result in the following HTML structure
@@ -25291,6 +25741,70 @@ Ember.CollectionView.CONTAINER_MAP = {
+(function() {
+/**
+ The ComponentTemplateDeprecation mixin is used to provide a useful
+ deprecation warning when using either `template` or `templateName` with
+ a component. The `template` and `templateName` properties specified at
+ extend time are moved to `layout` and `layoutName` respectively.
+
+ `Ember.ComponentTemplateDeprecation` is used internally by Ember in
+ `Ember.Component`.
+
+ @class ComponentTemplateDeprecation
+ @namespace Ember
+*/
+Ember.ComponentTemplateDeprecation = Ember.Mixin.create({
+ /**
+ @private
+
+ Moves `templateName` to `layoutName` and `template` to `layout` at extend
+ time if a layout is not also specified.
+
+ Note that this currently modifies the mixin themselves, which is technically
+ dubious but is practically of little consequence. This may change in the
+ future.
+
+ @method willMergeMixin
+ */
+ willMergeMixin: function(props) {
+ // must call _super here to ensure that the ActionHandler
+ // mixin is setup properly (moves actions -> _actions)
+ //
+ // Calling super is only OK here since we KNOW that
+ // there is another Mixin loaded first.
+ this._super.apply(this, arguments);
+
+ var deprecatedProperty, replacementProperty,
+ layoutSpecified = (props.layoutName || props.layout);
+
+ if (props.templateName && !layoutSpecified) {
+ deprecatedProperty = 'templateName';
+ replacementProperty = 'layoutName';
+
+ props.layoutName = props.templateName;
+ delete props['templateName'];
+ }
+
+ if (props.template && !layoutSpecified) {
+ deprecatedProperty = 'template';
+ replacementProperty = 'layout';
+
+ props.layout = props.template;
+ delete props['template'];
+ }
+
+ if (deprecatedProperty) {
+ Ember.deprecate('Do not specify ' + deprecatedProperty + ' on a Component, use ' + replacementProperty + ' instead.', false);
+ }
+ }
+});
+
+
+})();
+
+
+
(function() {
var get = Ember.get, set = Ember.set, isNone = Ember.isNone,
a_slice = Array.prototype.slice;
@@ -25315,11 +25829,11 @@ var get = Ember.get, set = Ember.set, isNone = Ember.isNone,
`{{my-foo}}` in other templates, which will make
an instance of the isolated component.
- ```html
+ ```handlebars
{{app-profile person=currentUser}}
```
- ```html
+ ```handlebars
{{person.title}}
@@ -25334,7 +25848,7 @@ var get = Ember.get, set = Ember.set, isNone = Ember.isNone,
```handlebars
{{#app-profile person=currentUser}}
Admin mode
- {{! Executed in the controllers context. }}
+ {{! Executed in the controller's context. }}
{{/app-profile}}
```
@@ -25366,7 +25880,7 @@ var get = Ember.get, set = Ember.set, isNone = Ember.isNone,
And then use it in the component's template:
- ```html
+ ```handlebars
{{person.title}}
@@ -25386,18 +25900,56 @@ var get = Ember.get, set = Ember.set, isNone = Ember.isNone,
@namespace Ember
@extends Ember.View
*/
-Ember.Component = Ember.View.extend(Ember.TargetActionSupport, {
+Ember.Component = Ember.View.extend(Ember.TargetActionSupport, Ember.ComponentTemplateDeprecation, {
init: function() {
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.
+
+ Example:
+
+ ```handlebars
+ {{#my-component}}
+ // something that is run in the context
+ // of the parent context
+ {{/my-component}}
+ ```
+
+ Specifying a template directly to a component is deprecated without
+ also specifying the layout property.
+
+ @deprecated
+ @property template
+ */
+ template: Ember.computed(function(key, value) {
+ if (value !== undefined) { return value; }
+
+ var templateName = get(this, 'templateName'),
+ template = this.templateForName(templateName, 'template');
+
+ Ember.assert("You specified the templateName " + templateName + " for " + this + ", but it did not exist.", !templateName || template);
+
+ return template || get(this, 'defaultTemplate');
+ }).property('templateName'),
+
+ /**
+ Specifying a components `templateName` is deprecated without also
+ providing the `layout` or `layoutName` properties.
+
+ @deprecated
+ @property templateName
+ */
+ templateName: null,
+
// during render, isolate keywords
cloneKeywords: function() {
return {
@@ -25452,9 +26004,9 @@ Ember.Component = Ember.View.extend(Ember.TargetActionSupport, {
App.PlayButtonComponent = Ember.Component.extend({
click: function(){
if (this.get('isPlaying')) {
- this.triggerAction('play');
+ this.sendAction('play');
} else {
- this.triggerAction('stop');
+ this.sendAction('stop');
}
}
});
@@ -25655,12 +26207,12 @@ define("metamorph",
})(),
// Feature-detect the W3C range API, the extended check is for IE9 which only partially supports ranges
- supportsRange = (!disableRange) && document && ('createRange' in document) && (typeof Range !== 'undefined') && Range.prototype.createContextualFragment,
+ supportsRange = (!disableRange) && typeof document !== 'undefined' && ('createRange' in document) && (typeof Range !== 'undefined') && Range.prototype.createContextualFragment,
// Internet Explorer prior to 9 does not allow setting innerHTML if the first element
// is a "zero-scope" element. This problem can be worked around by making
// the first node an invisible text node. We, like Modernizr, use
- needsShy = document && (function() {
+ needsShy = typeof document !== 'undefined' && (function() {
var testEl = document.createElement('div');
testEl.innerHTML = "";
testEl.firstChild.innerHTML = "";
@@ -27423,6 +27975,8 @@ Ember._HandlebarsBoundView = Ember._MetamorphView.extend({
preserveContext = get(this, 'preserveContext'),
context = get(this, 'previousContext');
+ var _contextController = get(this, '_contextController');
+
var inverseTemplate = get(this, 'inverseTemplate'),
displayTemplate = get(this, 'displayTemplate');
@@ -27442,6 +27996,10 @@ Ember._HandlebarsBoundView = Ember._MetamorphView.extend({
// Otherwise, determine if this is a block bind or not.
// If so, pass the specified object to the template
if (displayTemplate) {
+ if (_contextController) {
+ set(_contextController, 'content', result);
+ result = _contextController;
+ }
set(this, '_context', result);
} else {
// This is not a bind block, just push the result of the
@@ -27543,6 +28101,14 @@ function bind(property, options, preserveContext, shouldDisplay, valueNormalizer
templateData: options.data
});
+ if (options.hash.controller) {
+ bindView.set('_contextController', this.container.lookupFactory('controller:'+options.hash.controller).create({
+ container: currentContext.container,
+ parentController: currentContext,
+ target: currentContext
+ }));
+ }
+
view.appendChild(bindView);
observer = function() {
@@ -27617,6 +28183,17 @@ function simpleBind(currentContext, property, options) {
}
}
+function shouldDisplayIfHelperContent(result) {
+ var truthy = result && get(result, 'isTruthy');
+ if (typeof truthy === 'boolean') { return truthy; }
+
+ if (Ember.isArray(result)) {
+ return get(result, 'length') !== 0;
+ } else {
+ return !!result;
+ }
+}
+
/**
'_triageMustache' is used internally select between a binding, helper, or component for
the given context. Until this point, it would be hard to determine if the
@@ -27721,18 +28298,43 @@ EmberHandlebars.registerHelper('bind', function bindHelper(property, options) {
*/
EmberHandlebars.registerHelper('boundIf', function boundIfHelper(property, fn) {
var context = (fn.contexts && fn.contexts.length) ? fn.contexts[0] : this;
- var func = function(result) {
- var truthy = result && get(result, 'isTruthy');
- if (typeof truthy === 'boolean') { return truthy; }
- if (Ember.isArray(result)) {
- return get(result, 'length') !== 0;
- } else {
- return !!result;
- }
- };
+ return bind.call(context, property, fn, true, shouldDisplayIfHelperContent, shouldDisplayIfHelperContent, ['isTruthy', 'length']);
+});
- return bind.call(context, property, fn, true, func, func, ['isTruthy', 'length']);
+
+/**
+ @private
+
+ Use the `unboundIf` helper to create a conditional that evaluates once.
+
+ ```handlebars
+ {{#unboundIf "content.shouldDisplayTitle"}}
+ {{content.title}}
+ {{/unboundIf}}
+ ```
+
+ @method unboundIf
+ @for Ember.Handlebars.helpers
+ @param {String} property Property to bind
+ @param {Function} fn Context to provide for rendering
+ @return {String} HTML string
+*/
+EmberHandlebars.registerHelper('unboundIf', function unboundIfHelper(property, fn) {
+ var context = (fn.contexts && fn.contexts.length) ? fn.contexts[0] : this,
+ data = fn.data,
+ template = fn.fn,
+ inverse = fn.inverse,
+ normalized, propertyValue, result;
+
+ normalized = normalizePath(context, property, data);
+ propertyValue = handlebarsGet(context, property, fn);
+
+ if (!shouldDisplayIfHelperContent(propertyValue)) {
+ template = inverse;
+ }
+
+ template(context, { data: data });
});
/**
@@ -27786,6 +28388,23 @@ EmberHandlebars.registerHelper('boundIf', function boundIfHelper(property, fn) {
Without the `as` operator, it would be impossible to reference `user.name` in the example above.
+ ### `controller` option
+
+ Adding `controller='something'` instructs the `{{with}}` helper to create and use an instance of
+ the specified controller with the new context as its content.
+
+ This is very similar to using an `itemController` option with the `{{each}}` helper.
+
+ ```handlebars
+ {{#with users.posts controller='userBlogPosts'}}
+ {{!- The current context is wrapped in our controller instance }}
+ {{/with}}
+ ```
+
+ In the above example, the template provided to the `{{with}}` block is now wrapped in the
+ `userBlogPost` controller, which provides a very elegant way to decorate the context with custom
+ functions/properties.
+
@method with
@for Ember.Handlebars.helpers
@param {Function} context
@@ -27835,6 +28454,7 @@ EmberHandlebars.registerHelper('with', function withHelper(context, options) {
/**
See [boundIf](/api/classes/Ember.Handlebars.helpers.html#method_boundIf)
+ and [unboundIf](/api/classes/Ember.Handlebars.helpers.html#method_unboundIf)
@method if
@for Ember.Handlebars.helpers
@@ -27845,8 +28465,11 @@ EmberHandlebars.registerHelper('with', function withHelper(context, options) {
EmberHandlebars.registerHelper('if', function ifHelper(context, options) {
Ember.assert("You must pass exactly one argument to the if helper", arguments.length === 2);
Ember.assert("You must pass a block to the if helper", options.fn && options.fn !== Handlebars.VM.noop);
-
- return helpers.boundIf.call(options.contexts[0], context, options);
+ if (options.data.isUnbound) {
+ return helpers.unboundIf.call(options.contexts[0], context, options);
+ } else {
+ return helpers.boundIf.call(options.contexts[0], context, options);
+ }
});
/**
@@ -27865,7 +28488,11 @@ EmberHandlebars.registerHelper('unless', function unlessHelper(context, options)
options.fn = inverse;
options.inverse = fn;
- return helpers.boundIf.call(options.contexts[0], context, options);
+ if (options.data.isUnbound) {
+ return helpers.unboundIf.call(options.contexts[0], context, options);
+ } else {
+ return helpers.boundIf.call(options.contexts[0], context, options);
+ }
});
/**
@@ -28641,7 +29268,7 @@ var get = Ember.get, handlebarsGet = Ember.Handlebars.get, fmt = Ember.String.fm
```
- ### Blockless Use
+ ### Blockless use in a collection
If you provide an `itemViewClass` option that has its own `template` you can
omit the block.
@@ -28738,11 +29365,20 @@ Ember.Handlebars.registerHelper('collection', function collectionHelper(path, op
var inverse = options.inverse;
var view = options.data.view;
+
+ var controller, container;
// If passed a path string, convert that into an object.
// Otherwise, just default to the standard class.
var collectionClass;
- collectionClass = path ? handlebarsGet(this, path, options) : Ember.CollectionView;
- Ember.assert(fmt("%@ #collection: Could not find collection class %@", [data.view, path]), !!collectionClass);
+ if (path) {
+ controller = data.keywords.controller;
+ container = controller && controller.container;
+ collectionClass = handlebarsGet(this, path, options) || container.lookupFactory('view:' + path);
+ Ember.assert(fmt("%@ #collection: Could not find collection class %@", [data.view, path]), !!collectionClass);
+ }
+ else {
+ collectionClass = Ember.CollectionView;
+ }
var hash = options.hash, itemHash = {}, match;
@@ -28751,15 +29387,15 @@ Ember.Handlebars.registerHelper('collection', function collectionHelper(path, op
itemViewClass;
if (hash.itemView) {
- var controller = data.keywords.controller;
+ controller = data.keywords.controller;
Ember.assert('You specified an itemView, but the current context has no ' +
'container to look the itemView up in. This probably means ' +
'that you created a view manually, instead of through the ' +
'container. Instead, use container.lookup("view:viewName"), ' +
'which will properly instantiate your view.',
controller && controller.container);
- var container = controller.container;
- itemViewClass = container.resolve('view:' + hash.itemView);
+ container = controller.container;
+ itemViewClass = container.lookupFactory('view:' + hash.itemView);
Ember.assert('You specified the itemView ' + hash.itemView + ", but it was " +
"not found at " + container.describe("view:" + hash.itemView) +
" (and it was not registered in the container)", !!itemViewClass);
@@ -28957,6 +29593,7 @@ Ember.Handlebars.registerHelper('debugger', function debuggerHelper(options) {
*/
var get = Ember.get, set = Ember.set;
+var fmt = Ember.String.fmt;
Ember.Handlebars.EachView = Ember.CollectionView.extend(Ember._Metamorph, {
init: function() {
@@ -28965,6 +29602,7 @@ Ember.Handlebars.EachView = Ember.CollectionView.extend(Ember._Metamorph, {
if (itemController) {
var controller = get(this, 'controller.container').lookupFactory('controller:array').create({
+ _isVirtual: true,
parentController: get(this, 'controller'),
itemController: itemController,
target: get(this, 'controller'),
@@ -28989,8 +29627,13 @@ Ember.Handlebars.EachView = Ember.CollectionView.extend(Ember._Metamorph, {
},
_assertArrayLike: function(content) {
- Ember.assert("The value that #each loops over must be an Array. You passed " + content.constructor + ", but it should have been an ArrayController", !Ember.ControllerMixin.detect(content) || (content && content.isGenerated) || content instanceof Ember.ArrayController);
- Ember.assert("The value that #each loops over must be an Array. You passed " + ((Ember.ControllerMixin.detect(content) && content.get('model') !== undefined) ? ("" + content.get('model') + " (wrapped in " + content + ")") : ("" + content)), Ember.Array.detect(content));
+ Ember.assert(fmt("The value that #each loops over must be an Array. You " +
+ "passed %@, but it should have been an ArrayController",
+ [content.constructor]),
+ !Ember.ControllerMixin.detect(content) ||
+ (content && content.isGenerated) ||
+ content instanceof Ember.ArrayController);
+ Ember.assert(fmt("The value that #each loops over must be an Array. You passed %@", [(Ember.ControllerMixin.detect(content) && content.get('model') !== undefined) ? fmt("'%@' (wrapped in %@)", [content.get('model'), content]) : content]), Ember.Array.detect(content));
},
disableContentObservers: function(callback) {
@@ -29949,7 +30592,7 @@ Ember.TextField = Ember.Component.extend(Ember.TextSupport, {
classNames: ['ember-text-field'],
tagName: "input",
- attributeBindings: ['type', 'value', 'size', 'pattern', 'name'],
+ attributeBindings: ['type', 'value', 'size', 'pattern', 'name', 'min', 'max'],
/**
The `value` attribute of the input element. As the user inputs text, this
@@ -29980,141 +30623,31 @@ Ember.TextField = Ember.Component.extend(Ember.TextSupport, {
size: null,
/**
- The `pattern` the pattern attribute of input element.
+ The `pattern` attribute of input element.
@property pattern
@type String
@default null
*/
- pattern: null
-});
+ pattern: null,
-})();
+ /**
+ The `min` attribute of input element used with `type="number"` or `type="range"`.
-
-
-(function() {
-/*
-@module ember
-@submodule ember-handlebars
-*/
-
-var get = Ember.get, set = Ember.set;
-
-/*
- @class Button
- @namespace Ember
- @extends Ember.View
- @uses Ember.TargetActionSupport
- @deprecated
-*/
-Ember.Button = Ember.View.extend(Ember.TargetActionSupport, {
- classNames: ['ember-button'],
- classNameBindings: ['isActive'],
-
- tagName: 'button',
-
- propagateEvents: false,
-
- attributeBindings: ['type', 'disabled', 'href', 'tabindex'],
-
- /*
- @private
-
- Overrides `TargetActionSupport`'s `targetObject` computed
- property to use Handlebars-specific path resolution.
-
- @property targetObject
+ @property min
+ @type String
+ @default null
*/
- targetObject: Ember.computed(function() {
- var target = get(this, 'target'),
- root = get(this, 'context'),
- data = get(this, 'templateData');
+ min: null,
- if (typeof target !== 'string') { return target; }
+ /**
+ The `max` attribute of input element used with `type="number"` or `type="range"`.
- return Ember.Handlebars.get(root, target, { data: data });
- }).property('target'),
-
- // Defaults to 'button' if tagName is 'input' or 'button'
- type: Ember.computed(function(key) {
- var tagName = this.tagName;
- if (tagName === 'input' || tagName === 'button') { return 'button'; }
- }),
-
- disabled: false,
-
- // Allow 'a' tags to act like buttons
- href: Ember.computed(function() {
- return this.tagName === 'a' ? '#' : null;
- }),
-
- mouseDown: function() {
- if (!get(this, 'disabled')) {
- set(this, 'isActive', true);
- this._mouseDown = true;
- this._mouseEntered = true;
- }
- return get(this, 'propagateEvents');
- },
-
- mouseLeave: function() {
- if (this._mouseDown) {
- set(this, 'isActive', false);
- this._mouseEntered = false;
- }
- },
-
- mouseEnter: function() {
- if (this._mouseDown) {
- set(this, 'isActive', true);
- this._mouseEntered = true;
- }
- },
-
- mouseUp: function(event) {
- if (get(this, 'isActive')) {
- // Actually invoke the button's target and action.
- // This method comes from the Ember.TargetActionSupport mixin.
- this.triggerAction();
- set(this, 'isActive', false);
- }
-
- this._mouseDown = false;
- this._mouseEntered = false;
- return get(this, 'propagateEvents');
- },
-
- keyDown: function(event) {
- // Handle space or enter
- if (event.keyCode === 13 || event.keyCode === 32) {
- this.mouseDown();
- }
- },
-
- keyUp: function(event) {
- // Handle space or enter
- if (event.keyCode === 13 || event.keyCode === 32) {
- this.mouseUp();
- }
- },
-
- // TODO: Handle proper touch behavior. Including should make inactive when
- // finger moves more than 20x outside of the edge of the button (vs mouse
- // which goes inactive as soon as mouse goes out of edges.)
-
- touchStart: function(touch) {
- return this.mouseDown(touch);
- },
-
- touchEnd: function(touch) {
- return this.mouseUp(touch);
- },
-
- init: function() {
- Ember.deprecate("Ember.Button is deprecated and will be removed from future releases. Consider using the `{{action}}` helper.");
- this._super();
- }
+ @property max
+ @type String
+ @default null
+ */
+ max: null
});
})();
@@ -30506,15 +31039,13 @@ Ember.Select = Ember.View.extend({
defaultTemplate: Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) {
this.compilerInfo = [4,'>= 1.0.0'];
helpers = this.merge(helpers, Ember.Handlebars.helpers); data = data || {};
- var buffer = '', stack1, hashTypes, hashContexts, escapeExpression=this.escapeExpression, self=this;
+ var buffer = '', stack1, escapeExpression=this.escapeExpression, self=this;
function program1(depth0,data) {
- var buffer = '', stack1, hashTypes, hashContexts;
+ var buffer = '', stack1;
data.buffer.push("");
return buffer;
@@ -30522,50 +31053,38 @@ function program1(depth0,data) {
function program3(depth0,data) {
- var stack1, hashTypes, hashContexts;
- hashTypes = {};
- hashContexts = {};
- stack1 = helpers.each.call(depth0, "view.groupedContent", {hash:{},inverse:self.noop,fn:self.program(4, program4, data),contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data});
+ var stack1;
+ stack1 = helpers.each.call(depth0, "view.groupedContent", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(4, program4, data),contexts:[depth0],types:["ID"],data:data});
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
else { data.buffer.push(''); }
}
function program4(depth0,data) {
- var hashContexts, hashTypes;
- hashContexts = {'content': depth0,'label': depth0};
- hashTypes = {'content': "ID",'label': "ID"};
+
data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.groupView", {hash:{
'content': ("content"),
'label': ("label")
- },contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
+ },hashTypes:{'content': "ID",'label': "ID"},hashContexts:{'content': depth0,'label': depth0},contexts:[depth0],types:["ID"],data:data})));
}
function program6(depth0,data) {
- var stack1, hashTypes, hashContexts;
- hashTypes = {};
- hashContexts = {};
- stack1 = helpers.each.call(depth0, "view.content", {hash:{},inverse:self.noop,fn:self.program(7, program7, data),contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data});
+ var stack1;
+ stack1 = helpers.each.call(depth0, "view.content", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(7, program7, data),contexts:[depth0],types:["ID"],data:data});
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
else { data.buffer.push(''); }
}
function program7(depth0,data) {
- var hashContexts, hashTypes;
- hashContexts = {'content': depth0};
- hashTypes = {'content': "ID"};
+
data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.optionView", {hash:{
'content': ("")
- },contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
+ },hashTypes:{'content': "ID"},hashContexts:{'content': depth0},contexts:[depth0],types:["ID"],data:data})));
}
- hashTypes = {};
- hashContexts = {};
- stack1 = helpers['if'].call(depth0, "view.prompt", {hash:{},inverse:self.noop,fn:self.program(1, program1, data),contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data});
+ stack1 = helpers['if'].call(depth0, "view.prompt", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(1, program1, data),contexts:[depth0],types:["ID"],data:data});
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
- hashTypes = {};
- hashContexts = {};
- stack1 = helpers['if'].call(depth0, "view.optionGroupPath", {hash:{},inverse:self.program(6, program6, data),fn:self.program(3, program3, data),contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data});
+ stack1 = helpers['if'].call(depth0, "view.optionGroupPath", {hash:{},hashTypes:{},hashContexts:{},inverse:self.program(6, program6, data),fn:self.program(3, program3, data),contexts:[depth0],types:["ID"],data:data});
if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
return buffer;
@@ -31004,6 +31523,7 @@ Ember.Handlebars.registerHelper('input', function(options) {
delete hash.on;
if (inputType === 'checkbox') {
+ Ember.assert("{{input type='checkbox'}} does not support setting `value=someBooleanValue`; you must use `checked=someBooleanValue` instead.", options.hashTypes.value !== 'ID');
return Ember.Handlebars.helpers.view.call(this, Ember.Checkbox, options);
} else {
if (inputType) { hash.type = inputType; }
@@ -31307,9 +31827,9 @@ Ember.runLoadHooks('Ember.Handlebars', Ember.Handlebars);
})();
(function() {
-define("route-recognizer",
- [],
- function() {
+define("route-recognizer",
+ ["exports"],
+ function(__exports__) {
"use strict";
var specials = [
'/', '.', '*', '+', '?', '|',
@@ -31338,11 +31858,11 @@ define("route-recognizer",
function StaticSegment(string) { this.string = string; }
StaticSegment.prototype = {
eachChar: function(callback) {
- var string = this.string, char;
+ var string = this.string, ch;
for (var i=0, l=string.length; i 0) {
- currentResult.queryParams = activeQueryParams;
- }
- result.push(currentResult);
+
+ result.push({ handler: handler.handler, params: params, isDynamic: !!names.length });
}
return result;
}
function addSegment(currentState, segment) {
- segment.eachChar(function(char) {
+ segment.eachChar(function(ch) {
var state;
- currentState = currentState.put(char);
+ currentState = currentState.put(ch);
});
return currentState;
@@ -31639,9 +32164,6 @@ define("route-recognizer",
}
var handler = { handler: route.handler, names: names };
- if(route.queryParams) {
- handler.queryParams = route.queryParams;
- }
handlers.push(handler);
}
@@ -31702,24 +32224,26 @@ define("route-recognizer",
},
generateQueryString: function(params, handlers) {
- var pairs = [], allowedParams = [];
- for(var i=0; i < handlers.length; i++) {
- var currentParamList = handlers[i].queryParams;
- if(currentParamList) {
- allowedParams.push.apply(allowedParams, currentParamList);
- }
- }
+ var pairs = [];
for(var key in params) {
if (params.hasOwnProperty(key)) {
- if(allowedParams.indexOf(key) === -1) {
- throw 'Query param "' + key + '" is not specified as a valid param for this route';
- }
var value = params[key];
- var pair = encodeURIComponent(key);
- if(value !== true) {
- pair += "=" + encodeURIComponent(value);
+ if (value === false || value == null) {
+ continue;
+ }
+ var pair = key;
+ if (Array.isArray(value)) {
+ for (var i = 0, l = value.length; i < l; i++) {
+ var arrayPair = key + '[]' + '=' + encodeURIComponent(value[i]);
+ pairs.push(arrayPair);
+ }
+ }
+ else if (value !== true) {
+ pair += "=" + encodeURIComponent(value);
+ pairs.push(pair);
+ } else {
+ pairs.push(pair);
}
- pairs.push(pair);
}
}
@@ -31733,15 +32257,36 @@ define("route-recognizer",
for(var i=0; i < pairs.length; i++) {
var pair = pairs[i].split('='),
key = decodeURIComponent(pair[0]),
- value = pair[1] ? decodeURIComponent(pair[1]) : true;
- queryParams[key] = value;
+ keyLength = key.length,
+ isArray = false,
+ value;
+ if (pair.length === 1) {
+ value = true;
+ } else {
+ //Handle arrays
+ if (keyLength > 2 && key.slice(keyLength -2) === '[]') {
+ isArray = true;
+ key = key.slice(0, keyLength - 2);
+ if(!queryParams[key]) {
+ queryParams[key] = [];
+ }
+ }
+ value = pair[1] ? decodeURIComponent(pair[1]) : '';
+ }
+ if (isArray) {
+ queryParams[key].push(value);
+ } else {
+ queryParams[key] = value;
+ }
+
}
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) {
@@ -31757,6 +32302,7 @@ define("route-recognizer",
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 0) {
- var err = 'You supplied the params ';
- err += missingParams.map(function(param) {
- return '"' + param + "=" + queryParams[param] + '"';
- }).join(' and ');
-
- err += ' which are not valid for the "' + handlerName + '" handler or its parents';
-
- throw new Error(err);
+ for (var i = 0, len = state.handlerInfos.length; i < len; ++i) {
+ var handlerInfo = state.handlerInfos[i];
+ var handlerParams = handlerInfo.params ||
+ serialize(handlerInfo.handler, handlerInfo.context, handlerInfo.names);
+ merge(params, handlerParams);
}
+ params.queryParams = queryParams;
return this.recognizer.generate(handlerName, params);
},
isActive: function(handlerName) {
+
var partitionedArgs = extractQueryParams(slice.call(arguments, 1)),
contexts = partitionedArgs[0],
queryParams = partitionedArgs[1],
- activeQueryParams = {},
- effectiveQueryParams = {};
+ activeQueryParams = this.state.queryParams;
- var targetHandlerInfos = this.targetHandlerInfos,
- found = false, names, object, handlerInfo, handlerObj;
+ var targetHandlerInfos = this.state.handlerInfos,
+ found = false, names, object, handlerInfo, handlerObj, i, len;
- if (!targetHandlerInfos) { return false; }
+ if (!targetHandlerInfos.length) { return false; }
- var recogHandlers = this.recognizer.handlersFor(targetHandlerInfos[targetHandlerInfos.length - 1].name);
- for (var i=targetHandlerInfos.length-1; i>=0; i--) {
- handlerInfo = targetHandlerInfos[i];
- if (handlerInfo.name === handlerName) { found = true; }
+ var targetHandler = targetHandlerInfos[targetHandlerInfos.length - 1].name;
+ var recogHandlers = this.recognizer.handlersFor(targetHandler);
- if (found) {
- var recogHandler = recogHandlers[i];
-
- merge(activeQueryParams, handlerInfo.queryParams);
- if (queryParams !== false) {
- merge(effectiveQueryParams, handlerInfo.queryParams);
- mergeSomeKeys(effectiveQueryParams, queryParams, recogHandler.queryParams);
- }
-
- if (handlerInfo.isDynamic && contexts.length > 0) {
- object = contexts.pop();
-
- if (isParam(object)) {
- var name = recogHandler.names[0];
- if (!this.currentParams || "" + object !== this.currentParams[name]) { return false; }
- } else if (handlerInfo.context !== object) {
- return false;
- }
- }
- }
+ var index = 0;
+ for (len = recogHandlers.length; index < len; ++index) {
+ handlerInfo = targetHandlerInfos[index];
+ if (handlerInfo.name === handlerName) { break; }
}
+ if (index === recogHandlers.length) {
+ // The provided route name isn't even in the route hierarchy.
+ return false;
+ }
- return contexts.length === 0 && found && queryParamsEqual(activeQueryParams, effectiveQueryParams);
+ var state = new TransitionState();
+ state.handlerInfos = targetHandlerInfos.slice(0, index + 1);
+ recogHandlers = recogHandlers.slice(0, index + 1);
+
+ var intent = new NamedTransitionIntent({
+ name: targetHandler,
+ contexts: contexts
+ });
+
+ var newState = intent.applyToHandlers(state, recogHandlers, this.getHandler, targetHandler, true, true);
+
+ return handlerInfosEqual(newState.handlerInfos, state.handlerInfos) &&
+ !getChangelist(activeQueryParams, queryParams);
},
trigger: function(name) {
@@ -32331,6 +32983,24 @@ define("router",
trigger(this, this.currentHandlerInfos, false, args);
},
+ /**
+ @private
+
+ Pluggable hook for possibly running route hooks
+ in a try-catch escaping manner.
+
+ @param {Function} callback the callback that will
+ be asynchronously called
+
+ @return {Promise} a promise that fulfills with the
+ value returned from the callback
+ */
+ async: function(callback) {
+ return new Promise(function(resolve) {
+ resolve(callback());
+ });
+ },
+
/**
Hook point for logging transition status updates.
@@ -32339,299 +33009,6 @@ define("router",
log: null
};
- /**
- @private
-
- Used internally for both URL and named transition to determine
- a shared pivot parent route and other data necessary to perform
- a transition.
- */
- function getMatchPoint(router, handlers, objects, inputParams, queryParams) {
-
- var matchPoint = handlers.length,
- providedModels = {}, i,
- currentHandlerInfos = router.currentHandlerInfos || [],
- params = {},
- oldParams = router.currentParams || {},
- activeTransition = router.activeTransition,
- handlerParams = {},
- obj;
-
- objects = slice.call(objects);
- merge(params, inputParams);
-
- for (i = handlers.length - 1; i >= 0; i--) {
- var handlerObj = handlers[i],
- handlerName = handlerObj.handler,
- oldHandlerInfo = currentHandlerInfos[i],
- hasChanged = false;
-
- // Check if handler names have changed.
- if (!oldHandlerInfo || oldHandlerInfo.name !== handlerObj.handler) { hasChanged = true; }
-
- if (handlerObj.isDynamic) {
- // URL transition.
-
- if (obj = getMatchPointObject(objects, handlerName, activeTransition, true, params)) {
- hasChanged = true;
- providedModels[handlerName] = obj;
- } else {
- handlerParams[handlerName] = {};
- for (var prop in handlerObj.params) {
- if (!handlerObj.params.hasOwnProperty(prop)) { continue; }
- var newParam = handlerObj.params[prop];
- if (oldParams[prop] !== newParam) { hasChanged = true; }
- handlerParams[handlerName][prop] = params[prop] = newParam;
- }
- }
- } else if (handlerObj.hasOwnProperty('names')) {
- // Named transition.
-
- if (objects.length) { hasChanged = true; }
-
- if (obj = getMatchPointObject(objects, handlerName, activeTransition, handlerObj.names[0], params)) {
- providedModels[handlerName] = obj;
- } else {
- var names = handlerObj.names;
- handlerParams[handlerName] = {};
- for (var j = 0, len = names.length; j < len; ++j) {
- var name = names[j];
- handlerParams[handlerName][name] = params[name] = params[name] || oldParams[name];
- }
- }
- }
-
- // If there is an old handler, see if query params are the same. If there isn't an old handler,
- // hasChanged will already be true here
- if(oldHandlerInfo && !queryParamsEqual(oldHandlerInfo.queryParams, handlerObj.queryParams)) {
- hasChanged = true;
- }
-
- if (hasChanged) { matchPoint = i; }
- }
-
- if (objects.length > 0) {
- throw new Error("More context objects were passed than there are dynamic segments for the route: " + handlers[handlers.length - 1].handler);
- }
-
- var pivotHandlerInfo = currentHandlerInfos[matchPoint - 1],
- pivotHandler = pivotHandlerInfo && pivotHandlerInfo.handler;
-
- return { matchPoint: matchPoint, providedModels: providedModels, params: params, handlerParams: handlerParams, pivotHandler: pivotHandler };
- }
-
- function getMatchPointObject(objects, handlerName, activeTransition, paramName, params) {
-
- if (objects.length && paramName) {
-
- var object = objects.pop();
-
- // If provided object is string or number, treat as param.
- if (isParam(object)) {
- params[paramName] = object.toString();
- } else {
- return object;
- }
- } else if (activeTransition) {
- // Use model from previous transition attempt, preferably the resolved one.
- return activeTransition.resolvedModels[handlerName] ||
- (paramName && activeTransition.providedModels[handlerName]);
- }
- }
-
- function isParam(object) {
- return (typeof object === "string" || object instanceof String || typeof object === "number" || object instanceof Number);
- }
-
-
-
- /**
- @private
-
- This method takes a handler name and returns a list of query params
- that are valid to pass to the handler or its parents
-
- @param {Router} router
- @param {String} handlerName
- @return {Array[String]} a list of query parameters
- */
- function queryParamsForHandler(router, handlerName) {
- var handlers = router.recognizer.handlersFor(handlerName),
- queryParams = [];
-
- for (var i = 0; i < handlers.length; i++) {
- queryParams.push.apply(queryParams, handlers[i].queryParams || []);
- }
-
- return queryParams;
- }
- /**
- @private
-
- This method takes a handler name and a list of contexts and returns
- a serialized parameter hash suitable to pass to `recognizer.generate()`.
-
- @param {Router} router
- @param {String} handlerName
- @param {Array[Object]} objects
- @return {Object} a serialized parameter hash
- */
- function paramsForHandler(router, handlerName, objects, queryParams) {
-
- var handlers = router.recognizer.handlersFor(handlerName),
- params = {},
- handlerInfos = generateHandlerInfosWithQueryParams(router, handlers, queryParams),
- matchPoint = getMatchPoint(router, handlerInfos, objects).matchPoint,
- mergedQueryParams = {},
- object, handlerObj, handler, names, i;
-
- params.queryParams = {};
-
- for (i=0; i= matchPoint) {
- object = objects.shift();
- // Otherwise use existing context
- } else {
- object = handler.context;
- }
-
- // Serialize to generate params
- merge(params, serialize(handler, object, names));
- }
- if (queryParams !== false) {
- mergeSomeKeys(params.queryParams, router.currentQueryParams, handlerObj.queryParams);
- mergeSomeKeys(params.queryParams, queryParams, handlerObj.queryParams);
- }
- }
-
- if (queryParamsEqual(params.queryParams, {})) { delete params.queryParams; }
- return params;
- }
-
- function merge(hash, other) {
- for (var prop in other) {
- if (other.hasOwnProperty(prop)) { hash[prop] = other[prop]; }
- }
- }
-
- function mergeSomeKeys(hash, other, keys) {
- if (!other || !keys) { return; }
- for(var i = 0; i < keys.length; i++) {
- var key = keys[i], value;
- if(other.hasOwnProperty(key)) {
- value = other[key];
- if(value === null || value === false || typeof value === "undefined") {
- delete hash[key];
- } else {
- hash[key] = other[key];
- }
- }
- }
- }
-
- /**
- @private
- */
-
- function generateHandlerInfosWithQueryParams(router, handlers, queryParams) {
- var handlerInfos = [];
-
- for (var i = 0; i < handlers.length; i++) {
- var handler = handlers[i],
- handlerInfo = { handler: handler.handler, names: handler.names, context: handler.context, isDynamic: handler.isDynamic },
- activeQueryParams = {};
-
- if (queryParams !== false) {
- mergeSomeKeys(activeQueryParams, router.currentQueryParams, handler.queryParams);
- mergeSomeKeys(activeQueryParams, queryParams, handler.queryParams);
- }
-
- if (handler.queryParams && handler.queryParams.length > 0) {
- handlerInfo.queryParams = activeQueryParams;
- }
-
- handlerInfos.push(handlerInfo);
- }
-
- return handlerInfos;
- }
-
- /**
- @private
- */
- function createQueryParamTransition(router, queryParams, isIntermediate) {
- var currentHandlers = router.currentHandlerInfos,
- currentHandler = currentHandlers[currentHandlers.length - 1],
- name = currentHandler.name;
-
- log(router, "Attempting query param transition");
-
- return createNamedTransition(router, [name, queryParams], isIntermediate);
- }
-
- /**
- @private
- */
- function createNamedTransition(router, args, isIntermediate) {
- var partitionedArgs = extractQueryParams(args),
- pureArgs = partitionedArgs[0],
- queryParams = partitionedArgs[1],
- handlers = router.recognizer.handlersFor(pureArgs[0]),
- handlerInfos = generateHandlerInfosWithQueryParams(router, handlers, queryParams);
-
-
- log(router, "Attempting transition to " + pureArgs[0]);
-
- return performTransition(router,
- handlerInfos,
- slice.call(pureArgs, 1),
- router.currentParams,
- queryParams,
- null,
- isIntermediate);
- }
-
- /**
- @private
- */
- function createURLTransition(router, url, isIntermediate) {
- var results = router.recognizer.recognize(url),
- currentHandlerInfos = router.currentHandlerInfos,
- queryParams = {},
- i, len;
-
- log(router, "Attempting URL transition to " + url);
-
- if (results) {
- // Make sure this route is actually accessible by URL.
- for (i = 0, len = results.length; i < len; ++i) {
-
- if (router.getHandler(results[i].handler).inaccessibleByURL) {
- results = null;
- break;
- }
- }
- }
-
- if (!results) {
- return errorTransition(router, new Router.UnrecognizedURLError(url));
- }
-
- for(i = 0, len = results.length; i < len; i++) {
- merge(queryParams, results[i].queryParams);
- }
-
- return performTransition(router, results, [], {}, queryParams, null, isIntermediate);
- }
-
-
/**
@private
@@ -32670,104 +33047,70 @@ define("router",
3. Triggers the `enter` callback on `about`
4. Triggers the `setup` callback on `about`
- @param {Transition} transition
- @param {Array[HandlerInfo]} handlerInfos
+ @param {Router} transition
+ @param {TransitionState} newState
*/
- function setupContexts(transition, handlerInfos) {
- var router = transition.router,
- partition = partitionHandlers(router.currentHandlerInfos || [], handlerInfos);
+ function setupContexts(router, newState, transition) {
+ var partition = partitionHandlers(router.state, newState);
- router.targetHandlerInfos = handlerInfos;
-
- eachHandler(partition.exited, function(handlerInfo) {
+ forEach(partition.exited, function(handlerInfo) {
var handler = handlerInfo.handler;
delete handler.context;
if (handler.exit) { handler.exit(); }
});
- var currentHandlerInfos = partition.unchanged.slice();
- router.currentHandlerInfos = currentHandlerInfos;
+ var oldState = router.oldState = router.state;
+ router.state = newState;
+ var currentHandlerInfos = router.currentHandlerInfos = partition.unchanged.slice();
- eachHandler(partition.updatedContext, function(handlerInfo) {
- handlerEnteredOrUpdated(transition, currentHandlerInfos, handlerInfo, false);
- });
+ try {
+ forEach(partition.updatedContext, function(handlerInfo) {
+ return handlerEnteredOrUpdated(currentHandlerInfos, handlerInfo, false, transition);
+ });
- eachHandler(partition.entered, function(handlerInfo) {
- handlerEnteredOrUpdated(transition, currentHandlerInfos, handlerInfo, true);
- });
+ forEach(partition.entered, function(handlerInfo) {
+ return handlerEnteredOrUpdated(currentHandlerInfos, handlerInfo, true, transition);
+ });
+ } catch(e) {
+ router.state = oldState;
+ router.currentHandlerInfos = oldState.handlerInfos;
+ throw e;
+ }
+
+ router.state.queryParams = finalizeQueryParamChange(router, currentHandlerInfos, newState.queryParams);
}
+
/**
@private
Helper method used by setupContexts. Handles errors or redirects
that may happen in enter/setup.
*/
- function handlerEnteredOrUpdated(transition, currentHandlerInfos, handlerInfo, enter) {
+ function handlerEnteredOrUpdated(currentHandlerInfos, handlerInfo, enter, transition) {
+
var handler = handlerInfo.handler,
context = handlerInfo.context;
- try {
- if (enter && handler.enter) { handler.enter(); }
- checkAbort(transition);
+ if (enter && handler.enter) { handler.enter(transition); }
+ if (transition && transition.isAborted) {
+ throw new TransitionAborted();
+ }
- setContext(handler, context);
- setQueryParams(handler, handlerInfo.queryParams);
+ handler.context = context;
+ if (handler.contextDidChange) { handler.contextDidChange(); }
- if (handler.setup) { handler.setup(context, handlerInfo.queryParams); }
- checkAbort(transition);
- } catch(e) {
- if (!(e instanceof Router.TransitionAborted)) {
- // Trigger the `error` event starting from this failed handler.
- transition.trigger(true, 'error', e, transition, handler);
- }
-
- // Propagate the error so that the transition promise will reject.
- throw e;
+ if (handler.setup) { handler.setup(context, transition); }
+ if (transition && transition.isAborted) {
+ throw new TransitionAborted();
}
currentHandlerInfos.push(handlerInfo);
- }
-
- /**
- @private
-
- Iterates over an array of `HandlerInfo`s, passing the handler
- and context into the callback.
-
- @param {Array[HandlerInfo]} handlerInfos
- @param {Function(Object, Object)} callback
- */
- function eachHandler(handlerInfos, callback) {
- for (var i=0, l=handlerInfos.length; i= 0; --i) {
+ var handlerInfo = handlerInfos[i];
+ merge(params, handlerInfo.params);
+ if (handlerInfo.handler.inaccessibleByURL) {
+ urlMethod = null;
+ }
+ }
+
+ if (urlMethod) {
+ params.queryParams = state.queryParams;
+ var url = router.recognizer.generate(handlerName, params);
+
+ if (urlMethod === 'replaceQuery') {
+ if (url !== inputUrl) {
+ router.replaceURL(url);
+ }
+ } else if (urlMethod === 'replace') {
+ router.replaceURL(url);
+ } else {
+ router.updateURL(url);
+ }
+ }
+ }
+
+ /**
+ @private
+
+ Updates the URL (if necessary) and calls `setupContexts`
+ to update the router's array of `currentHandlerInfos`.
+ */
+ function finalizeTransition(transition, newState) {
+
+ try {
+ log(transition.router, transition.sequence, "Resolved all models on destination route; finalizing transition.");
+
+ var router = transition.router,
+ handlerInfos = newState.handlerInfos,
+ seq = transition.sequence;
+
+ // Run all the necessary enter/setup/exit hooks
+ setupContexts(router, newState, transition);
+
+ // Check if a redirect occurred in enter/setup
+ if (transition.isAborted) {
+ // TODO: cleaner way? distinguish b/w targetHandlerInfos?
+ router.state.handlerInfos = router.currentHandlerInfos;
+ return reject(logAbort(transition));
+ }
+
+ updateURL(transition, newState, transition.intent.url);
+
+ transition.isActive = false;
+ router.activeTransition = null;
+
+ trigger(router, router.currentHandlerInfos, true, ['didTransition']);
+
+ if (router.didTransition) {
+ router.didTransition(router.currentHandlerInfos);
+ }
+
+ log(router, transition.sequence, "TRANSITION COMPLETE.");
+
+ // Resolve with the final handler.
+ return handlerInfos[handlerInfos.length - 1].handler;
+ } catch(e) {
+ if (!(e instanceof TransitionAborted)) {
+ //var erroneousHandler = handlerInfos.pop();
+ var infos = transition.state.handlerInfos;
+ transition.trigger(true, 'error', e, transition, infos[infos.length-1]);
+ transition.abort();
+ }
+
+ throw e;
+ }
+ }
+
+ /**
+ @private
+
+ Begins and returns a Transition based on the provided
+ arguments. Accepts arguments in the form of both URL
+ transitions and named transitions.
+
+ @param {Router} router
+ @param {Array[Object]} args arguments passed to transitionTo,
+ replaceWith, or handleURL
+ */
+ function doTransition(router, args, isIntermediate) {
+ // Normalize blank transitions to root URL transitions.
+ var name = args[0] || '/';
+
+ var lastArg = args[args.length-1];
+ var queryParams = {};
+ if (lastArg && lastArg.hasOwnProperty('queryParams')) {
+ queryParams = pop.call(args).queryParams;
+ }
+
+ var intent;
+ if (args.length === 0) {
+
+ log(router, "Updating query params");
+
+ // A query param update is really just a transition
+ // into the route you're already on.
+ var handlerInfos = router.state.handlerInfos;
+ intent = new NamedTransitionIntent({
+ name: handlerInfos[handlerInfos.length - 1].name,
+ contexts: [],
+ queryParams: queryParams
+ });
+
+ } else if (name.charAt(0) === '/') {
+
+ log(router, "Attempting URL transition to " + name);
+ intent = new URLTransitionIntent({ url: name });
+
+ } else {
+
+ log(router, "Attempting transition to " + name);
+ intent = new NamedTransitionIntent({
+ name: args[0],
+ contexts: slice.call(args, 1),
+ queryParams: queryParams
+ });
+ }
+
+ return router.transitionByIntent(intent, isIntermediate);
+ }
+
+ function handlerInfosEqual(handlerInfos, otherHandlerInfos) {
+ if (handlerInfos.length !== otherHandlerInfos.length) {
+ return false;
+ }
+
+ for (var i = 0, len = handlerInfos.length; i < len; ++i) {
+ if (handlerInfos[i] !== otherHandlerInfos[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ function finalizeQueryParamChange(router, resolvedHandlers, newQueryParams) {
+ // We fire a finalizeQueryParamChange event which
+ // gives the new route hierarchy a chance to tell
+ // us which query params it's consuming and what
+ // their final values are. If a query param is
+ // no longer consumed in the final route hierarchy,
+ // its serialized segment will be removed
+ // from the URL.
+ var finalQueryParamsArray = [];
+ trigger(router, resolvedHandlers, true, ['finalizeQueryParamChange', newQueryParams, finalQueryParamsArray]);
+
+ var finalQueryParams = {};
+ for (var i = 0, len = finalQueryParamsArray.length; i < len; ++i) {
+ var qp = finalQueryParamsArray[i];
+ finalQueryParams[qp.key] = qp.value;
+ }
+ return finalQueryParams;
+ }
+
+ __exports__.Router = Router;
+ });
+define("router/transition-intent",
+ ["./utils","exports"],
+ function(__dependency1__, __exports__) {
+ "use strict";
+ var merge = __dependency1__.merge;
+
+ function TransitionIntent(props) {
+ if (props) {
+ merge(this, props);
+ }
+ this.data = this.data || {};
+ }
+
+ TransitionIntent.prototype.applyToState = function(oldState) {
+ // Default TransitionIntent is a no-op.
+ return oldState;
+ };
+
+ __exports__.TransitionIntent = TransitionIntent;
+ });
+define("router/transition-intent/named-transition-intent",
+ ["../transition-intent","../transition-state","../handler-info","../utils","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) {
+ "use strict";
+ var TransitionIntent = __dependency1__.TransitionIntent;
+ var TransitionState = __dependency2__.TransitionState;
+ var UnresolvedHandlerInfoByParam = __dependency3__.UnresolvedHandlerInfoByParam;
+ var UnresolvedHandlerInfoByObject = __dependency3__.UnresolvedHandlerInfoByObject;
+ var isParam = __dependency4__.isParam;
+ var forEach = __dependency4__.forEach;
+ var extractQueryParams = __dependency4__.extractQueryParams;
+ var oCreate = __dependency4__.oCreate;
+ var merge = __dependency4__.merge;
+
+ function NamedTransitionIntent(props) {
+ TransitionIntent.call(this, props);
+ }
+
+ NamedTransitionIntent.prototype = oCreate(TransitionIntent.prototype);
+ NamedTransitionIntent.prototype.applyToState = function(oldState, recognizer, getHandler, isIntermediate) {
+
+ var partitionedArgs = extractQueryParams([this.name].concat(this.contexts)),
+ pureArgs = partitionedArgs[0],
+ queryParams = partitionedArgs[1],
+ handlers = recognizer.handlersFor(pureArgs[0]);
+
+ var targetRouteName = handlers[handlers.length-1].handler;
+
+ return this.applyToHandlers(oldState, handlers, getHandler, targetRouteName, isIntermediate);
+ };
+
+ NamedTransitionIntent.prototype.applyToHandlers = function(oldState, handlers, getHandler, targetRouteName, isIntermediate, checkingIfActive) {
+
+ var i;
+ var newState = new TransitionState();
+ var objects = this.contexts.slice(0);
+
+ var invalidateIndex = handlers.length;
+ var nonDynamicIndexes = [];
+
+ // Pivot handlers are provided for refresh transitions
+ if (this.pivotHandler) {
+ for (i = 0; i < handlers.length; ++i) {
+ if (getHandler(handlers[i].handler) === this.pivotHandler) {
+ invalidateIndex = i;
+ break;
+ }
+ }
+ }
+
+ var pivotHandlerFound = !this.pivotHandler;
+
+ for (i = handlers.length - 1; i >= 0; --i) {
+ var result = handlers[i];
+ var name = result.handler;
+ var handler = getHandler(name);
+
+ var oldHandlerInfo = oldState.handlerInfos[i];
+ var newHandlerInfo = null;
+
+ if (result.names.length > 0) {
+ if (i >= invalidateIndex) {
+ newHandlerInfo = this.createParamHandlerInfo(name, handler, result.names, objects, oldHandlerInfo);
+ } else {
+ newHandlerInfo = this.getHandlerInfoForDynamicSegment(name, handler, result.names, objects, oldHandlerInfo, targetRouteName);
+ }
+ } else {
+ // This route has no dynamic segment.
+ // Therefore treat as a param-based handlerInfo
+ // with empty params. This will cause the `model`
+ // hook to be called with empty params, which is desirable.
+ newHandlerInfo = this.createParamHandlerInfo(name, handler, result.names, objects, oldHandlerInfo);
+ nonDynamicIndexes.unshift(i);
+ }
+
+ if (checkingIfActive) {
+ // If we're performing an isActive check, we want to
+ // serialize URL params with the provided context, but
+ // ignore mismatches between old and new context.
+ newHandlerInfo = newHandlerInfo.becomeResolved(null, newHandlerInfo.context);
+ var oldContext = oldHandlerInfo && oldHandlerInfo.context;
+ if (result.names.length > 0 && newHandlerInfo.context === oldContext) {
+ // If contexts match in isActive test, assume params also match.
+ // This allows for flexibility in not requiring that every last
+ // handler provide a `serialize` method
+ newHandlerInfo.params = oldHandlerInfo && oldHandlerInfo.params;
+ }
+ newHandlerInfo.context = oldContext;
+ }
+
+ var handlerToUse = oldHandlerInfo;
+ if (i >= invalidateIndex || newHandlerInfo.shouldSupercede(oldHandlerInfo)) {
+ invalidateIndex = Math.min(i, invalidateIndex);
+ handlerToUse = newHandlerInfo;
+ }
+
+ if (isIntermediate && !checkingIfActive) {
+ handlerToUse = handlerToUse.becomeResolved(null, handlerToUse.context);
+ }
+
+ newState.handlerInfos.unshift(handlerToUse);
+ }
+
+ if (objects.length > 0) {
+ throw new Error("More context objects were passed than there are dynamic segments for the route: " + targetRouteName);
+ }
+
+ if (!isIntermediate) {
+ this.invalidateNonDynamicHandlers(newState.handlerInfos, nonDynamicIndexes, invalidateIndex);
+ }
+
+ merge(newState.queryParams, oldState.queryParams);
+ merge(newState.queryParams, this.queryParams || {});
+
+ return newState;
+ };
+
+ NamedTransitionIntent.prototype.invalidateNonDynamicHandlers = function(handlerInfos, indexes, invalidateIndex) {
+ forEach(indexes, function(i) {
+ if (i >= invalidateIndex) {
+ var handlerInfo = handlerInfos[i];
+ handlerInfos[i] = new UnresolvedHandlerInfoByParam({
+ name: handlerInfo.name,
+ handler: handlerInfo.handler,
+ params: {}
+ });
+ }
+ });
+ };
+
+ NamedTransitionIntent.prototype.getHandlerInfoForDynamicSegment = function(name, handler, names, objects, oldHandlerInfo, targetRouteName) {
+
+ var numNames = names.length;
+ var objectToUse;
+ if (objects.length > 0) {
+
+ // Use the objects provided for this transition.
+ objectToUse = objects[objects.length - 1];
+ if (isParam(objectToUse)) {
+ return this.createParamHandlerInfo(name, handler, names, objects, oldHandlerInfo);
+ } else {
+ objects.pop();
+ }
+ } else if (oldHandlerInfo && oldHandlerInfo.name === name) {
+ // Reuse the matching oldHandlerInfo
+ return oldHandlerInfo;
+ } else {
+ // Ideally we should throw this error to provide maximal
+ // information to the user that not enough context objects
+ // were provided, but this proves too cumbersome in Ember
+ // in cases where inner template helpers are evaluated
+ // before parent helpers un-render, in which cases this
+ // error somewhat prematurely fires.
+ //throw new Error("Not enough context objects were provided to complete a transition to " + targetRouteName + ". Specifically, the " + name + " route needs an object that can be serialized into its dynamic URL segments [" + names.join(', ') + "]");
+ return oldHandlerInfo;
+ }
+
+ return new UnresolvedHandlerInfoByObject({
+ name: name,
+ handler: handler,
+ context: objectToUse,
+ names: names
+ });
+ };
+
+ NamedTransitionIntent.prototype.createParamHandlerInfo = function(name, handler, names, objects, oldHandlerInfo) {
+ var params = {};
+
+ // Soak up all the provided string/numbers
+ var numNames = names.length;
+ while (numNames--) {
+
+ // Only use old params if the names match with the new handler
+ var oldParams = (oldHandlerInfo && name === oldHandlerInfo.name && oldHandlerInfo.params) || {};
+
+ var peek = objects[objects.length - 1];
+ var paramName = names[numNames];
+ if (isParam(peek)) {
+ params[paramName] = "" + objects.pop();
+ } else {
+ // If we're here, this means only some of the params
+ // were string/number params, so try and use a param
+ // value from a previous handler.
+ if (oldParams.hasOwnProperty(paramName)) {
+ params[paramName] = oldParams[paramName];
+ } else {
+ throw new Error("You didn't provide enough string/numeric parameters to satisfy all of the dynamic segments for route " + name);
+ }
+ }
+ }
+
+ return new UnresolvedHandlerInfoByParam({
+ name: name,
+ handler: handler,
+ params: params
+ });
+ };
+
+ __exports__.NamedTransitionIntent = NamedTransitionIntent;
+ });
+define("router/transition-intent/url-transition-intent",
+ ["../transition-intent","../transition-state","../handler-info","../utils","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) {
+ "use strict";
+ var TransitionIntent = __dependency1__.TransitionIntent;
+ var TransitionState = __dependency2__.TransitionState;
+ var UnresolvedHandlerInfoByParam = __dependency3__.UnresolvedHandlerInfoByParam;
+ var oCreate = __dependency4__.oCreate;
+ var merge = __dependency4__.merge;
+
+ function URLTransitionIntent(props) {
+ TransitionIntent.call(this, props);
+ }
+
+ URLTransitionIntent.prototype = oCreate(TransitionIntent.prototype);
+ URLTransitionIntent.prototype.applyToState = function(oldState, recognizer, getHandler) {
+ var newState = new TransitionState();
+
+ var results = recognizer.recognize(this.url),
+ queryParams = {},
+ i, len;
+
+ if (!results) {
+ throw new UnrecognizedURLError(this.url);
+ }
+
+ var statesDiffer = false;
+
+ for (i = 0, len = results.length; i < len; ++i) {
+ var result = results[i];
+ var name = result.handler;
+ var handler = getHandler(name);
+
+ if (handler.inaccessibleByURL) {
+ throw new UnrecognizedURLError(this.url);
+ }
+
+ var newHandlerInfo = new UnresolvedHandlerInfoByParam({
+ name: name,
+ handler: handler,
+ params: result.params
+ });
+
+ var oldHandlerInfo = oldState.handlerInfos[i];
+ if (statesDiffer || newHandlerInfo.shouldSupercede(oldHandlerInfo)) {
+ statesDiffer = true;
+ newState.handlerInfos[i] = newHandlerInfo;
+ } else {
+ newState.handlerInfos[i] = oldHandlerInfo;
+ }
+ }
+
+ merge(newState.queryParams, results.queryParams);
+
+ return newState;
+ };
+
+ /**
+ Promise reject reasons passed to promise rejection
+ handlers for failed transitions.
+ */
+ function UnrecognizedURLError(message) {
+ this.message = (message || "UnrecognizedURLError");
+ this.name = "UnrecognizedURLError";
+ }
+
+ __exports__.URLTransitionIntent = URLTransitionIntent;
+ });
+define("router/transition-state",
+ ["./handler-info","./utils","rsvp","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
+ "use strict";
+ var ResolvedHandlerInfo = __dependency1__.ResolvedHandlerInfo;
+ var forEach = __dependency2__.forEach;
+ var resolve = __dependency3__.resolve;
+
+ function TransitionState(other) {
+ this.handlerInfos = [];
+ this.queryParams = {};
+ this.params = {};
+ }
+
+ TransitionState.prototype = {
+ handlerInfos: null,
+ queryParams: null,
+ params: null,
+
+ resolve: function(async, shouldContinue, payload) {
+
+ // First, calculate params for this state. This is useful
+ // information to provide to the various route hooks.
+ var params = this.params;
+ forEach(this.handlerInfos, function(handlerInfo) {
+ params[handlerInfo.name] = handlerInfo.params || {};
+ });
+
+ payload = payload || {};
+ payload.resolveIndex = 0;
+
+ var currentState = this;
+ var wasAborted = false;
+
+ // The prelude RSVP.resolve() asyncs us into the promise land.
+ return resolve().then(resolveOneHandlerInfo)['catch'](handleError);
+
+ function innerShouldContinue() {
+ return resolve(shouldContinue())['catch'](function(reason) {
+ // We distinguish between errors that occurred
+ // during resolution (e.g. beforeModel/model/afterModel),
+ // and aborts due to a rejecting promise from shouldContinue().
+ wasAborted = true;
+ throw reason;
+ });
+ }
+
+ function handleError(error) {
+ // This is the only possible
+ // reject value of TransitionState#resolve
+ throw {
+ error: error,
+ handlerWithError: currentState.handlerInfos[payload.resolveIndex].handler,
+ wasAborted: wasAborted,
+ state: currentState
+ };
+ }
+
+ function proceed(resolvedHandlerInfo) {
+ // Swap the previously unresolved handlerInfo with
+ // the resolved handlerInfo
+ currentState.handlerInfos[payload.resolveIndex++] = resolvedHandlerInfo;
+
+ // Call the redirect hook. The reason we call it here
+ // vs. afterModel is so that redirects into child
+ // routes don't re-run the model hooks for this
+ // already-resolved route.
+ var handler = resolvedHandlerInfo.handler;
+ if (handler && handler.redirect) {
+ handler.redirect(resolvedHandlerInfo.context, payload);
+ }
+
+ // Proceed after ensuring that the redirect hook
+ // didn't abort this transition by transitioning elsewhere.
+ return innerShouldContinue().then(resolveOneHandlerInfo);
+ }
+
+ function resolveOneHandlerInfo() {
+ if (payload.resolveIndex === currentState.handlerInfos.length) {
+ // This is is the only possible
+ // fulfill value of TransitionState#resolve
+ return {
+ error: null,
+ state: currentState
+ };
+ }
+
+ var handlerInfo = currentState.handlerInfos[payload.resolveIndex];
+
+ return handlerInfo.resolve(async, innerShouldContinue, payload)
+ .then(proceed);
+ }
+ }
+ };
+
+ __exports__.TransitionState = TransitionState;
+ });
+define("router/transition",
+ ["rsvp","./handler-info","./utils","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
+ "use strict";
+ var reject = __dependency1__.reject;
+ var resolve = __dependency1__.resolve;
+ var ResolvedHandlerInfo = __dependency2__.ResolvedHandlerInfo;
+ var trigger = __dependency3__.trigger;
+ var slice = __dependency3__.slice;
+ var log = __dependency3__.log;
+
+ /**
+ @private
+
+ A Transition is a thennable (a promise-like object) that represents
+ an attempt to transition to another route. It can be aborted, either
+ explicitly via `abort` or by attempting another transition while a
+ previous one is still underway. An aborted transition can also
+ be `retry()`d later.
+ */
+ function Transition(router, intent, state, error) {
+ var transition = this;
+ this.state = state || router.state;
+ this.intent = intent;
+ this.router = router;
+ this.data = this.intent && this.intent.data || {};
+ this.resolvedModels = {};
+ this.queryParams = {};
+
+ if (error) {
+ this.promise = reject(error);
+ return;
+ }
+
+ if (state) {
+ this.params = state.params;
+ this.queryParams = state.queryParams;
+
+ var len = state.handlerInfos.length;
+ if (len) {
+ this.targetName = state.handlerInfos[state.handlerInfos.length-1].name;
+ }
+
+ for (var i = 0; i < len; ++i) {
+ var handlerInfo = state.handlerInfos[i];
+ if (!(handlerInfo instanceof ResolvedHandlerInfo)) {
+ break;
+ }
+ this.pivotHandler = handlerInfo.handler;
+ }
+
+ this.sequence = Transition.currentSequence++;
+ this.promise = state.resolve(router.async, checkForAbort, this)['catch'](function(result) {
+ if (result.wasAborted) {
+ throw logAbort(transition);
+ } else {
+ transition.trigger('error', result.error, transition, result.handlerWithError);
+ transition.abort();
+ throw result.error;
+ }
+ });
+ } else {
+ this.promise = resolve(this.state);
+ this.params = {};
+ }
+
+ function checkForAbort() {
+ if (transition.isAborted) {
+ return reject();
+ }
+ }
+ }
+
+ Transition.currentSequence = 0;
+
+ Transition.prototype = {
+ targetName: null,
+ urlMethod: 'update',
+ intent: null,
+ params: null,
+ pivotHandler: null,
+ resolveIndex: 0,
+ handlerInfos: null,
+ resolvedModels: null,
+ isActive: true,
+ state: null,
+
+ /**
+ @public
+
+ The Transition's internal promise. Calling `.then` on this property
+ is that same as calling `.then` on the Transition object itself, but
+ this property is exposed for when you want to pass around a
+ Transition's promise, but not the Transition object itself, since
+ Transition object can be externally `abort`ed, while the promise
+ cannot.
+ */
+ promise: null,
+
+ /**
+ @public
+
+ Custom state can be stored on a Transition's `data` object.
+ This can be useful for decorating a Transition within an earlier
+ hook and shared with a later hook. Properties set on `data` will
+ be copied to new transitions generated by calling `retry` on this
+ transition.
+ */
+ data: null,
+
+ /**
+ @public
+
+ A standard promise hook that resolves if the transition
+ succeeds and rejects if it fails/redirects/aborts.
+
+ Forwards to the internal `promise` property which you can
+ use in situations where you want to pass around a thennable,
+ but not the Transition itself.
+
+ @param {Function} success
+ @param {Function} failure
+ */
+ then: function(success, failure) {
+ return this.promise.then(success, failure);
+ },
+
+ /**
+ @public
+
+ Aborts the Transition. Note you can also implicitly abort a transition
+ by initiating another transition while a previous one is underway.
+ */
+ abort: function() {
+ if (this.isAborted) { return this; }
+ log(this.router, this.sequence, this.targetName + ": transition was aborted");
+ this.isAborted = true;
+ this.isActive = false;
+ this.router.activeTransition = null;
+ return this;
+ },
+
+ /**
+ @public
+
+ Retries a previously-aborted transition (making sure to abort the
+ transition if it's still active). Returns a new transition that
+ represents the new attempt to transition.
+ */
+ retry: function() {
+ // TODO: add tests for merged state retry()s
+ this.abort();
+ return this.router.transitionByIntent(this.intent, false);
+ },
+
+ /**
+ @public
+
+ Sets the URL-changing method to be employed at the end of a
+ successful transition. By default, a new Transition will just
+ use `updateURL`, but passing 'replace' to this method will
+ cause the URL to update using 'replaceWith' instead. Omitting
+ a parameter will disable the URL change, allowing for transitions
+ that don't update the URL at completion (this is also used for
+ handleURL, since the URL has already changed before the
+ transition took place).
+
+ @param {String} method the type of URL-changing method to use
+ at the end of a transition. Accepted values are 'replace',
+ falsy values, or any other non-falsy value (which is
+ interpreted as an updateURL transition).
+
+ @return {Transition} this transition
+ */
+ method: function(method) {
+ this.urlMethod = method;
+ return this;
+ },
+
+ /**
+ @public
+
+ Fires an event on the current list of resolved/resolving
+ handlers within this transition. Useful for firing events
+ on route hierarchies that haven't fully been entered yet.
+
+ Note: This method is also aliased as `send`
+
+ @param {Boolean} ignoreFailure the name of the event to fire
+ @param {String} name the name of the event to fire
+ */
+ trigger: function (ignoreFailure) {
+ var args = slice.call(arguments);
+ if (typeof ignoreFailure === 'boolean') {
+ args.shift();
+ } else {
+ // Throw errors on unhandled trigger events by default
+ ignoreFailure = false;
+ }
+ trigger(this.router, this.state.handlerInfos.slice(0, this.resolveIndex + 1), ignoreFailure, args);
+ },
+
+ /**
+ @public
+
+ Transitions are aborted and their promises rejected
+ when redirects occur; this method returns a promise
+ that will follow any redirects that occur and fulfill
+ with the value fulfilled by any redirecting transitions
+ that occur.
+
+ @return {Promise} a promise that fulfills with the same
+ value that the final redirecting transition fulfills with
+ */
+ followRedirects: function() {
+ var router = this.router;
+ return this.promise['catch'](function(reason) {
+ if (router.activeTransition) {
+ return router.activeTransition.followRedirects();
+ }
+ throw reason;
+ });
+ },
+
+ toString: function() {
+ return "Transition (sequence " + this.sequence + ")";
+ },
+
+ /**
+ @private
+ */
+ log: function(message) {
+ log(this.router, this.sequence, message);
+ }
+ };
+
+ // Alias 'trigger' as 'send'
+ Transition.prototype.send = Transition.prototype.trigger;
+
+ /**
+ @private
+
+ Logs and returns a TransitionAborted error.
+ */
+ function logAbort(transition) {
+ log(transition.router, transition.sequence, "detected abort.");
+ return new TransitionAborted();
+ }
+
+ function TransitionAborted(message) {
+ this.message = (message || "TransitionAborted");
+ this.name = "TransitionAborted";
+ }
+
+ __exports__.Transition = Transition;
+ __exports__.logAbort = logAbort;
+ __exports__.TransitionAborted = TransitionAborted;
+ });
+define("router/utils",
+ ["exports"],
+ function(__exports__) {
+ "use strict";
+ var slice = Array.prototype.slice;
+
+ function merge(hash, other) {
+ for (var prop in other) {
+ if (other.hasOwnProperty(prop)) { hash[prop] = other[prop]; }
+ }
+ }
+
+ var oCreate = Object.create || function(proto) {
+ function F() {}
+ F.prototype = proto;
+ return new F();
+ };
+
+ /**
+ @private
+
+ Extracts query params from the end of an array
+ **/
+ function extractQueryParams(array) {
+ var len = (array && array.length), head, queryParams;
+
+ if(len && len > 0 && array[len - 1] && array[len - 1].hasOwnProperty('queryParams')) {
+ queryParams = array[len - 1].queryParams;
+ head = slice.call(array, 0, len - 1);
+ return [head, queryParams];
+ } else {
+ return [array, null];
+ }
+ }
+
+ /**
+ @private
+ */
+ function log(router, sequence, msg) {
+ if (!router.log) { return; }
+
+ if (arguments.length === 3) {
+ router.log("Transition #" + sequence + ": " + msg);
+ } else {
+ msg = sequence;
+ router.log(msg);
+ }
+ }
+
+ function bind(fn, context) {
+ var boundArgs = arguments;
+ return function(value) {
+ var args = slice.call(boundArgs, 2);
+ args.push(value);
+ return fn.apply(context, args);
+ };
+ }
+
+ function isParam(object) {
+ return (typeof object === "string" || object instanceof String || typeof object === "number" || object instanceof Number);
+ }
+
+
+ function forEach(array, callback) {
+ for (var i=0, l=array.length; i 0 && array[len - 1] && array[len - 1].hasOwnProperty('queryParams')) {
- queryParams = array[len - 1].queryParams;
- head = slice.call(array, 0, len - 1);
- return [head, queryParams];
- } else {
- return [array, null];
- }
- }
-
- function performIntermediateTransition(router, recogHandlers, matchPointResults) {
-
- var handlerInfos = generateHandlerInfos(router, recogHandlers);
- for (var i = 0; i < handlerInfos.length; ++i) {
- var handlerInfo = handlerInfos[i];
- handlerInfo.context = matchPointResults.providedModels[handlerInfo.name];
- }
-
- var stubbedTransition = {
- router: router,
- isAborted: false
+ function getChangelist(oldObject, newObject) {
+ var key;
+ var results = {
+ all: {},
+ changed: {},
+ removed: {}
};
- setupContexts(stubbedTransition, handlerInfos);
- }
+ merge(results.all, newObject);
- /**
- @private
+ var didChange = false;
- Creates, begins, and returns a Transition.
- */
- function performTransition(router, recogHandlers, providedModelsArray, params, queryParams, data, isIntermediate) {
-
- var matchPointResults = getMatchPoint(router, recogHandlers, providedModelsArray, params, queryParams),
- targetName = recogHandlers[recogHandlers.length - 1].handler,
- wasTransitioning = false,
- currentHandlerInfos = router.currentHandlerInfos;
-
- if (isIntermediate) {
- return performIntermediateTransition(router, recogHandlers, matchPointResults);
- }
-
- // Check if there's already a transition underway.
- if (router.activeTransition) {
- if (transitionsIdentical(router.activeTransition, targetName, providedModelsArray, queryParams)) {
- return router.activeTransition;
- }
- router.activeTransition.abort();
- wasTransitioning = true;
- }
-
- var deferred = RSVP.defer(),
- transition = new Transition(router, deferred.promise);
-
- transition.targetName = targetName;
- transition.providedModels = matchPointResults.providedModels;
- transition.providedModelsArray = providedModelsArray;
- transition.params = matchPointResults.params;
- transition.data = data || {};
- transition.queryParams = queryParams;
- transition.pivotHandler = matchPointResults.pivotHandler;
- router.activeTransition = transition;
-
- var handlerInfos = generateHandlerInfos(router, recogHandlers);
- transition.handlerInfos = handlerInfos;
-
- // Fire 'willTransition' event on current handlers, but don't fire it
- // if a transition was already underway.
- if (!wasTransitioning) {
- trigger(router, currentHandlerInfos, true, ['willTransition', transition]);
- }
-
- log(router, transition.sequence, "Beginning validation for transition to " + transition.targetName);
- validateEntry(transition, matchPointResults.matchPoint, matchPointResults.handlerParams)
- .then(transitionSuccess, transitionFailure);
-
- return transition;
-
- function transitionSuccess() {
- checkAbort(transition);
-
- try {
- finalizeTransition(transition, handlerInfos);
-
- // currentHandlerInfos was updated in finalizeTransition
- trigger(router, router.currentHandlerInfos, true, ['didTransition']);
-
- if (router.didTransition) {
- router.didTransition(handlerInfos);
+ // Calculate removals
+ for (key in oldObject) {
+ if (oldObject.hasOwnProperty(key)) {
+ if (!newObject.hasOwnProperty(key)) {
+ didChange = true;
+ results.removed[key] = oldObject[key];
}
-
- log(router, transition.sequence, "TRANSITION COMPLETE.");
-
- // Resolve with the final handler.
- transition.isActive = false;
- deferred.resolve(handlerInfos[handlerInfos.length - 1].handler);
- } catch(e) {
- deferred.reject(e);
- }
-
- // Don't nullify if another transition is underway (meaning
- // there was a transition initiated with enter/setup).
- if (!transition.isAborted) {
- router.activeTransition = null;
}
}
- function transitionFailure(reason) {
- deferred.reject(reason);
- }
- }
-
- /**
- @private
-
- Accepts handlers in Recognizer format, either returned from
- recognize() or handlersFor(), and returns unified
- `HandlerInfo`s.
- */
- function generateHandlerInfos(router, recogHandlers) {
- var handlerInfos = [];
- for (var i = 0, len = recogHandlers.length; i < len; ++i) {
- var handlerObj = recogHandlers[i],
- isDynamic = handlerObj.isDynamic || (handlerObj.names && handlerObj.names.length);
-
- var handlerInfo = {
- isDynamic: !!isDynamic,
- name: handlerObj.handler,
- handler: router.getHandler(handlerObj.handler)
- };
- if(handlerObj.queryParams) {
- handlerInfo.queryParams = handlerObj.queryParams;
- }
- handlerInfos.push(handlerInfo);
- }
- return handlerInfos;
- }
-
- /**
- @private
- */
- function transitionsIdentical(oldTransition, targetName, providedModelsArray, queryParams) {
-
- if (oldTransition.targetName !== targetName) { return false; }
-
- var oldModels = oldTransition.providedModelsArray;
- if (oldModels.length !== providedModelsArray.length) { return false; }
-
- for (var i = 0, len = oldModels.length; i < len; ++i) {
- if (oldModels[i] !== providedModelsArray[i]) { return false; }
- }
-
- if(!queryParamsEqual(oldTransition.queryParams, queryParams)) {
- return false;
- }
-
- return true;
- }
-
- /**
- @private
-
- Updates the URL (if necessary) and calls `setupContexts`
- to update the router's array of `currentHandlerInfos`.
- */
- function finalizeTransition(transition, handlerInfos) {
-
- log(transition.router, transition.sequence, "Validation succeeded, finalizing transition;");
-
- var router = transition.router,
- seq = transition.sequence,
- handlerName = handlerInfos[handlerInfos.length - 1].name,
- urlMethod = transition.urlMethod,
- i;
-
- // Collect params for URL.
- var objects = [], providedModels = transition.providedModelsArray.slice();
- for (i = handlerInfos.length - 1; i>=0; --i) {
- var handlerInfo = handlerInfos[i];
- if (handlerInfo.isDynamic) {
- var providedModel = providedModels.pop();
- objects.unshift(isParam(providedModel) ? providedModel.toString() : handlerInfo.context);
- }
-
- if (handlerInfo.handler.inaccessibleByURL) {
- urlMethod = null;
+ // Calculate changes
+ for (key in newObject) {
+ if (newObject.hasOwnProperty(key)) {
+ if (oldObject[key] !== newObject[key]) {
+ results.changed[key] = newObject[key];
+ didChange = true;
+ }
}
}
- var newQueryParams = {};
- for (i = handlerInfos.length - 1; i>=0; --i) {
- merge(newQueryParams, handlerInfos[i].queryParams);
- }
- router.currentQueryParams = newQueryParams;
-
-
- var params = paramsForHandler(router, handlerName, objects, transition.queryParams);
-
- router.currentParams = params;
-
- if (urlMethod) {
- var url = router.recognizer.generate(handlerName, params);
-
- if (urlMethod === 'replace') {
- router.replaceURL(url);
- } else {
- // Assume everything else is just a URL update for now.
- router.updateURL(url);
- }
- }
-
- setupContexts(transition, handlerInfos);
+ return didChange && results;
}
- /**
- @private
-
- Internal function used to construct the chain of promises used
- to validate a transition. Wraps calls to `beforeModel`, `model`,
- and `afterModel` in promises, and checks for redirects/aborts
- between each.
- */
- function validateEntry(transition, matchPoint, handlerParams) {
-
- var handlerInfos = transition.handlerInfos,
- index = transition.resolveIndex;
-
- if (index === handlerInfos.length) {
- // No more contexts to resolve.
- return RSVP.resolve(transition.resolvedModels);
- }
-
- var router = transition.router,
- handlerInfo = handlerInfos[index],
- handler = handlerInfo.handler,
- handlerName = handlerInfo.name,
- seq = transition.sequence;
-
- if (index < matchPoint) {
- log(router, seq, handlerName + ": using context from already-active handler");
-
- // We're before the match point, so don't run any hooks,
- // just use the already resolved context from the handler.
- transition.resolvedModels[handlerInfo.name] =
- transition.providedModels[handlerInfo.name] ||
- handlerInfo.handler.context;
- return proceed();
- }
-
- transition.trigger(true, 'willResolveModel', transition, handler);
-
- return RSVP.resolve().then(handleAbort)
- .then(beforeModel)
- .then(handleAbort)
- .then(model)
- .then(handleAbort)
- .then(afterModel)
- .then(handleAbort)
- .then(null, handleError)
- .then(proceed);
-
- function handleAbort(result) {
- if (transition.isAborted) {
- log(transition.router, transition.sequence, "detected abort.");
- return RSVP.reject(new Router.TransitionAborted());
- }
-
- return result;
- }
-
- function handleError(reason) {
- if (reason instanceof Router.TransitionAborted || transition.isAborted) {
- // if the transition was aborted and *no additional* error was thrown,
- // reject with the Router.TransitionAborted instance
- return RSVP.reject(reason);
- }
-
- // otherwise, we're here because of a different error
- transition.abort();
-
- log(router, seq, handlerName + ": handling error: " + reason);
-
- // An error was thrown / promise rejected, so fire an
- // `error` event from this handler info up to root.
- transition.trigger(true, 'error', reason, transition, handlerInfo.handler);
-
- // Propagate the original error.
- return RSVP.reject(reason);
- }
-
- function beforeModel() {
-
- log(router, seq, handlerName + ": calling beforeModel hook");
-
- var args;
-
- if (handlerInfo.queryParams) {
- args = [handlerInfo.queryParams, transition];
- } else {
- args = [transition];
- }
-
- var p = handler.beforeModel && handler.beforeModel.apply(handler, args);
- return (p instanceof Transition) ? null : p;
- }
-
- function model() {
- log(router, seq, handlerName + ": resolving model");
- var p = getModel(handlerInfo, transition, handlerParams[handlerName], index >= matchPoint);
- return (p instanceof Transition) ? null : p;
- }
-
- function afterModel(context) {
-
- log(router, seq, handlerName + ": calling afterModel hook");
-
- // Pass the context and resolved parent contexts to afterModel, but we don't
- // want to use the value returned from `afterModel` in any way, but rather
- // always resolve with the original `context` object.
-
- transition.resolvedModels[handlerInfo.name] = context;
-
- var args;
-
- if (handlerInfo.queryParams) {
- args = [context, handlerInfo.queryParams, transition];
- } else {
- args = [context, transition];
- }
-
- var p = handler.afterModel && handler.afterModel.apply(handler, args);
- return (p instanceof Transition) ? null : p;
- }
-
- function proceed() {
- log(router, seq, handlerName + ": validation succeeded, proceeding");
-
- handlerInfo.context = transition.resolvedModels[handlerInfo.name];
- transition.resolveIndex++;
- return validateEntry(transition, matchPoint, handlerParams);
- }
- }
-
- /**
- @private
-
- Throws a TransitionAborted if the provided transition has been aborted.
- */
- function checkAbort(transition) {
- if (transition.isAborted) {
- log(transition.router, transition.sequence, "detected abort.");
- throw new Router.TransitionAborted();
- }
- }
-
- /**
- @private
-
- Encapsulates the logic for whether to call `model` on a route,
- or use one of the models provided to `transitionTo`.
- */
- function getModel(handlerInfo, transition, handlerParams, needsUpdate) {
- var handler = handlerInfo.handler,
- handlerName = handlerInfo.name, args;
-
- if (!needsUpdate && handler.hasOwnProperty('context')) {
- return handler.context;
- }
-
- if (transition.providedModels.hasOwnProperty(handlerName)) {
- var providedModel = transition.providedModels[handlerName];
- return typeof providedModel === 'function' ? providedModel() : providedModel;
- }
-
- if (handlerInfo.queryParams) {
- args = [handlerParams || {}, handlerInfo.queryParams, transition];
- } else {
- args = [handlerParams || {}, transition, handlerInfo.queryParams];
- }
-
- return handler.model && handler.model.apply(handler, args);
- }
-
- /**
- @private
- */
- function log(router, sequence, msg) {
-
- if (!router.log) { return; }
-
- if (arguments.length === 3) {
- router.log("Transition #" + sequence + ": " + msg);
- } else {
- msg = sequence;
- router.log(msg);
- }
- }
-
- /**
- @private
-
- Begins and returns a Transition based on the provided
- arguments. Accepts arguments in the form of both URL
- transitions and named transitions.
-
- @param {Router} router
- @param {Array[Object]} args arguments passed to transitionTo,
- replaceWith, or handleURL
- */
- function doTransition(router, args, isIntermediate) {
- // Normalize blank transitions to root URL transitions.
- var name = args[0] || '/';
-
- if(args.length === 1 && args[0].hasOwnProperty('queryParams')) {
- return createQueryParamTransition(router, args[0], isIntermediate);
- } else if (name.charAt(0) === '/') {
- return createURLTransition(router, name, isIntermediate);
- } else {
- return createNamedTransition(router, slice.call(args), isIntermediate);
- }
- }
-
- /**
- @private
-
- Serializes a handler using its custom `serialize` method or
- by a default that looks up the expected property name from
- the dynamic segment.
-
- @param {Object} handler a router handler
- @param {Object} model the model to be serialized for this handler
- @param {Array[Object]} names the names array attached to an
- handler object returned from router.recognizer.handlersFor()
- */
- function serialize(handler, model, names) {
-
- var object = {};
- if (isParam(model)) {
- object[names[0]] = model;
- return object;
- }
-
- // Use custom serialize if it exists.
- if (handler.serialize) {
- return handler.serialize(model, names);
- }
-
- if (names.length !== 1) { return; }
-
- var name = names[0];
-
- if (/_id$/.test(name)) {
- object[name] = model.id;
- } else {
- object[name] = model;
- }
- return object;
- }
+ __exports__.trigger = trigger;
+ __exports__.log = log;
+ __exports__.oCreate = oCreate;
+ __exports__.merge = merge;
+ __exports__.extractQueryParams = extractQueryParams;
+ __exports__.bind = bind;
+ __exports__.isParam = isParam;
+ __exports__.forEach = forEach;
+ __exports__.slice = slice;
+ __exports__.serialize = serialize;
+ __exports__.getChangelist = getChangelist;
});
+define("router",
+ ["./router/router","exports"],
+ function(__dependency1__, __exports__) {
+ "use strict";
+ var Router = __dependency1__.Router;
+ __exports__.Router = Router;
+ });
})();
@@ -33423,7 +34265,7 @@ DSL.prototype = {
for (var i=0, l=dslMatches.length; i 1) {
+ urlKeyName = parts[1];
+ } else {
+ // TODO: use _queryParamScope here?
+ if (controllerName !== 'application') {
+ urlKeyName = controllerName + '[' + propName + ']';
+ } else {
+ urlKeyName = propName;
+ }
+ }
+
+ var controllerFullname = controllerName + ':' + propName;
+
+ result.queryParams[controllerFullname] = urlKeyName;
+ result.translations[parts[0]] = controllerFullname;
+ });
+ }
+}
+
/**
Helper function for iterating root-ward, starting
from (but not including) the provided `originRoute`.
@@ -33893,7 +34852,7 @@ Ember.Router = Ember.Object.extend(Ember.Evented, {
@private
*/
function forEachRouteAbove(originRoute, transition, callback) {
- var handlerInfos = transition.handlerInfos,
+ var handlerInfos = transition.state.handlerInfos,
originRouteFound = false;
for (var i = handlerInfos.length - 1; i >= 0; --i) {
@@ -34113,11 +35072,22 @@ Ember.Router.reopenClass({
}
return path.join(".");
+ },
+
+ _translateQueryParams: function(queryParams, translations, routeName) {
+ for (var name in queryParams) {
+ if (!queryParams.hasOwnProperty(name)) { continue; }
+
+ if (name in translations) {
+ queryParams[translations[name]] = queryParams[name];
+ delete queryParams[name];
+ } else {
+ Ember.assert(fmt("You supplied an unknown query param controller property '%@' for route '%@'. Only the following query param properties can be set for this route: %@", [name, routeName, Ember.keys(translations)]), name in queryParams);
+ }
+ }
}
});
-Router.Transition.prototype.send = Router.Transition.prototype.trigger;
-
})();
@@ -34155,7 +35125,7 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
@method exit
*/
exit: function() {
- this.deactivate();
+ this.deactivate();
this.teardownViews();
},
@@ -34168,6 +35138,74 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
this.activate();
},
+ /**
+ The name of the view to use by default when rendering this routes template.
+
+ When rendering a template, the route will, by default, determine the
+ template and view to use from the name of the route itself. If you need to
+ define a specific view, set this property.
+
+ This is useful when multiple routes would benefit from using the same view
+ because it doesn't require a custom `renderTemplate` method. For example,
+ the following routes will all render using the `App.PostsListView` view:
+
+ ```js
+ var PostsList = Ember.Route.extend({
+ viewName: 'postsList',
+ });
+
+ App.PostsIndexRoute = PostsList.extend();
+ App.PostsArchivedRoute = PostsList.extend();
+ ```
+
+ @property viewName
+ @type String
+ @default null
+ */
+ viewName: null,
+
+ /**
+ The name of the template to use by default when rendering this routes
+ template.
+
+ This is similar with `viewName`, but is useful when you just want a custom
+ template without a view.
+
+ ```js
+ var PostsList = Ember.Route.extend({
+ templateName: 'posts/list'
+ });
+
+ App.PostsIndexRoute = PostsList.extend();
+ App.PostsArchivedRoute = PostsList.extend();
+ ```
+
+ @property templateName
+ @type String
+ @default null
+ */
+ templateName: null,
+
+ /**
+ The name of the controller to associate with this route.
+
+ By default, Ember will lookup a route's controller that matches the name
+ of the route (i.e. `App.PostController` for `App.PostRoute`). However,
+ if you would like to define a specific controller to use, you can do so
+ using this property.
+
+ This is useful in many ways, as the controller specified will be:
+
+ * passed to the `setupController` method.
+ * used as the controller for the view being rendered by the route.
+ * returned from a call to `controllerFor` for the route.
+
+ @property controllerName
+ @type String
+ @default null
+ */
+ controllerName: null,
+
/**
The collection of functions, keyed by name, available on this route as
action targets.
@@ -34477,6 +35515,8 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
@param {String} name the name of the route
@param {...Object} models the model(s) to be used while transitioning
to the route.
+ @return {Transition} the transition object associated with this
+ attempted transition
*/
transitionTo: function(name, context) {
var router = this.router;
@@ -34484,7 +35524,7 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
},
/**
- Perform a synchronous transition into another route with out attempting
+ Perform a synchronous transition into another route without attempting
to resolve promises, update the URL, or abort any currently active
asynchronous transitions (i.e. regular transitions caused by
`transitionTo` or URL changes).
@@ -34503,6 +35543,30 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
router.intermediateTransitionTo.apply(router, arguments);
},
+ /**
+ Refresh the model on this route and any child routes, firing the
+ `beforeModel`, `model`, and `afterModel` hooks in a similar fashion
+ to how routes are entered when transitioning in from other route.
+ The current route params (e.g. `article_id`) will be passed in
+ to the respective model hooks, and if a different model is returned,
+ `setupController` and associated route hooks will re-fire as well.
+
+ An example usage of this method is re-querying the server for the
+ latest information using the same parameters as when the route
+ was first entered.
+
+ Note that this will cause `model` hooks to fire even on routes
+ that were provided a model object when the route was initially
+ entered.
+
+ @method refresh
+ @return {Transition} the transition object associated with this
+ attempted transition
+ */
+ refresh: function() {
+ return this.router.router.refresh(this).method('replace');
+ },
+
/**
Transition into another route while replacing the current URL, if possible.
This will replace the current history entry instead of adding a new one.
@@ -34530,6 +35594,8 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
@param {String} name the name of the route
@param {...Object} models the model(s) to be used while transitioning
to the route.
+ @return {Transition} the transition object associated with this
+ attempted transition
*/
replaceWith: function() {
var router = this.router;
@@ -34580,7 +35646,7 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
@private
@method setup
*/
- setup: function(context, queryParams) {
+ setup: function(context, transition) {
var controllerName = this.controllerName || this.routeName,
controller = this.controllerFor(controllerName, true);
if (!controller) {
@@ -34591,40 +35657,25 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
// referenced in action handlers
this.controller = controller;
- var args = [controller, context];
-
if (this.setupControllers) {
Ember.deprecate("Ember.Route.setupControllers is deprecated. Please use Ember.Route.setupController(controller, model) instead.");
this.setupControllers(controller, context);
} else {
- this.setupController.apply(this, args);
+
+
+ this.setupController(controller, context);
+
}
if (this.renderTemplates) {
Ember.deprecate("Ember.Route.renderTemplates is deprecated. Please use Ember.Route.renderTemplate(controller, model) instead.");
this.renderTemplates(context);
} else {
- this.renderTemplate.apply(this, args);
+ this.renderTemplate(controller, context);
}
},
- /**
- A hook you can implement to optionally redirect to another route.
-
- If you call `this.transitionTo` from inside of this hook, this route
- will not be entered in favor of the other hook.
-
- Note that this hook is called by the default implementation of
- `afterModel`, so if you override `afterModel`, you must either
- explicitly call `redirect` or just put your redirecting
- `this.transitionTo()` call within `afterModel`.
-
- @method redirect
- @param {Object} model the model for this route
- */
- redirect: Ember.K,
-
/**
This hook is the first of the route entry validation hooks
called when an attempt is made to transition into a route
@@ -34686,8 +35737,6 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
// error so that it'd be handled by the `error`
// hook, you would have to either
return Ember.RSVP.reject(e);
- // or
- throw e;
});
}
}
@@ -34736,10 +35785,32 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
resolves. Otherwise, non-promise return values are not
utilized in any way.
*/
- afterModel: function(resolvedModel, transition, queryParams) {
- this.redirect(resolvedModel, transition);
- },
+ afterModel: Ember.K,
+ /**
+ A hook you can implement to optionally redirect to another route.
+
+ If you call `this.transitionTo` from inside of this hook, this route
+ will not be entered in favor of the other hook.
+
+ `redirect` and `afterModel` behave very similarly and are
+ called almost at the same time, but they have an important
+ distinction in the case that, from one of these hooks, a
+ redirect into a child route of this route occurs: redirects
+ from `afterModel` essentially invalidate the current attempt
+ to enter this route, and will result in this route's `beforeModel`,
+ `model`, and `afterModel` hooks being fired again within
+ the new, redirecting transition. Redirects that occur within
+ the `redirect` hook, on the other hand, will _not_ cause
+ these hooks to be fired again the second time around; in
+ other words, by the time the `redirect` hook has been called,
+ both the resolved model and attempted entry into this route
+ are considered to be fully validated.
+
+ @method redirect
+ @param {Object} model the model for this route
+ */
+ redirect: Ember.K,
/**
Called when the context is changed by router.js.
@@ -34805,6 +35876,8 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
var match, name, sawParams, value;
for (var prop in params) {
+ if (prop === 'queryParams') { continue; }
+
if (match = prop.match(/^(.*)_id$/)) {
name = match[1];
value = params[prop];
@@ -34818,6 +35891,17 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
return this.findModel(name, value);
},
+ /**
+ @private
+
+ Router.js hook.
+ */
+ deserialize: function(params, transition) {
+
+ return this.model(params, transition);
+
+ },
+
/**
@method findModel
@@ -34856,6 +35940,8 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
" did not exist and you did not override your route's `model` " +
"hook.", modelClass);
+ if (!modelClass) { return; }
+
return modelClass.find(value);
}
};
@@ -34960,7 +36046,7 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
@param {Controller} controller instance
@param {Object} model
*/
- setupController: function(controller, context) {
+ setupController: function(controller, context, transition) {
if (controller && (context !== undefined)) {
set(controller, 'model', context);
}
@@ -35255,7 +36341,7 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
options.outlet = options.outlet || 'main';
var parentView = this.router._lookupActiveView(options.parentView);
- parentView.disconnectOutlet(options.outlet);
+ if (parentView) { parentView.disconnectOutlet(options.outlet); }
},
willDestroy: function() {
@@ -35283,8 +36369,10 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
}
});
+
+
function parentRoute(route) {
- var handlerInfos = route.router.router.targetHandlerInfos;
+ var handlerInfos = route.router.router.state.handlerInfos;
if (!handlerInfos) { return; }
@@ -35330,7 +36418,11 @@ function normalizeOptions(route, name, template, options) {
}
if (typeof controller === 'string') {
- controller = route.container.lookup('controller:' + controller);
+ var controllerName = controller;
+ controller = route.container.lookup('controller:' + controllerName);
+ if (!controller) {
+ throw new Ember.Error("You passed `controller: '" + controllerName + "'` into the `render` method, but no such controller could be found.");
+ }
}
options.controller = controller;
@@ -35456,9 +36548,16 @@ Ember.onLoad('Ember.Handlebars', function() {
*/
var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt;
+
+var slice = Array.prototype.slice;
Ember.onLoad('Ember.Handlebars', function(Handlebars) {
+ var QueryParams = Ember.Object.extend({
+ values: null
+ });
+
var resolveParams = Ember.Router.resolveParams,
+ translateQueryParams = Ember.Router._translateQueryParams,
resolvePaths = Ember.Router.resolvePaths,
isSimpleClick = Ember.ViewUtils.isSimpleClick;
@@ -35634,8 +36733,7 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
// Map desired event name to invoke function
var eventName = get(this, 'eventName'), i;
this.on(eventName, this, this._invoke);
-
- },
+ },
/**
This method is invoked by observers installed during `init` that fire
@@ -35676,6 +36774,22 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
normalizedPath = Ember.Handlebars.normalizePath(helperParameters.context, path, helperParameters.options.data);
this.registerObserver(normalizedPath.root, normalizedPath.path, this, this._paramsChanged);
}
+
+ var queryParamsObject = this.queryParamsObject;
+ if (queryParamsObject) {
+ var values = queryParamsObject.values;
+
+ // Install observers for all of the hash options
+ // provided in the (query-params) subexpression.
+ for (var k in values) {
+ if (!values.hasOwnProperty(k)) { continue; }
+
+ if (queryParamsObject.types[k] === 'ID') {
+ normalizedPath = Ember.Handlebars.normalizePath(helperParameters.context, values[k], helperParameters.options.data);
+ this.registerObserver(normalizedPath.root, normalizedPath.path, this, this._paramsChanged);
+ }
+ }
+ }
},
afterRender: function(){
@@ -35683,16 +36797,6 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
this._setupPathObservers();
},
- /**
- This method is invoked by observers installed during `init` that fire
- whenever the query params change
- @private
- */
- _queryParamsChanged: function (object, path) {
- this.notifyPropertyChange('queryParams');
- },
-
-
/**
Even though this isn't a virtual view, we want to treat it as if it is
so that you can access the parent with {{view.prop}}
@@ -35741,7 +36845,7 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
router.isActive.apply(router, [currentWithIndex].concat(contexts));
if (isActive) { return get(this, 'activeClass'); }
- }).property('resolvedParams', 'routeArgs', 'router.url'),
+ }).property('resolvedParams', 'routeArgs'),
/**
Accessed as a classname binding to apply the `LinkView`'s `loadingClass`
@@ -35799,7 +36903,19 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
},
/**
- Computed property that returns the resolved parameters.
+ Computed property that returns an array of the
+ resolved parameters passed to the `link-to` helper,
+ e.g.:
+
+ ```hbs
+ {{link-to a b '123' c}}
+ ```
+
+ will generate a `resolvedParams` of:
+
+ ```js
+ [aObject, bObject, '123', cObject]
+ ```
@private
@property
@@ -35811,10 +36927,16 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
types = options.types,
data = options.data;
-
+ if (parameters.params.length === 0) {
+ var appController = this.container.lookup('controller:application');
+ return [get(appController, 'currentRouteName')];
+ } else {
+ return resolveParams(parameters.context, parameters.params, { types: types, data: data });
+ }
+
// Original implementation if query params not enabled
return resolveParams(parameters.context, parameters.params, { types: types, data: data });
- }).property(),
+ }).property('router.url'),
/**
Computed property that returns the current route name and
@@ -35844,36 +36966,53 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
return resolvedParams;
- }).property('resolvedParams', 'queryParams', 'router.url'),
+ }).property('resolvedParams', 'queryParams'),
+ queryParamsObject: null,
+ queryParams: Ember.computed(function computeLinkViewQueryParams() {
- _potentialQueryParams: Ember.computed(function () {
- var namedRoute = get(this, 'resolvedParams')[0];
- if (!namedRoute) { return null; }
- var router = get(this, 'router');
+ var queryParamsObject = get(this, 'queryParamsObject'),
+ suppliedParams = {};
- namedRoute = fullRouteName(router, namedRoute);
+ if (queryParamsObject) {
+ Ember.merge(suppliedParams, queryParamsObject.values);
+ }
- return router.router.queryParamsForHandler(namedRoute);
- }).property('resolvedParams'),
+ var resolvedParams = get(this, 'resolvedParams'),
+ router = get(this, 'router'),
+ routeName = resolvedParams[0],
+ paramsForRoute = router._queryParamNamesFor(routeName),
+ queryParams = paramsForRoute.queryParams,
+ translations = paramsForRoute.translations,
+ paramsForRecognizer = {};
- queryParams: Ember.computed(function () {
- var self = this,
- queryParams = null,
- allowedQueryParams = get(this, '_potentialQueryParams');
+ // Normalize supplied params into their long-form name
+ // e.g. 'foo' -> 'controllername:foo'
+ translateQueryParams(suppliedParams, translations, routeName);
- if (!allowedQueryParams) { return null; }
- allowedQueryParams.forEach(function (param) {
- var value = get(self, param);
- if (typeof value !== 'undefined') {
- queryParams = queryParams || {};
- queryParams[param] = value;
+ var helperParameters = this.parameters;
+ router._queryParamOverrides(paramsForRecognizer, queryParams, function(name, resultsName) {
+ if (!(name in suppliedParams)) { return; }
+
+ var parts = name.split(':');
+
+ var type = queryParamsObject.types[parts[1]];
+
+ var value;
+ if (type === 'ID') {
+ var normalizedPath = Ember.Handlebars.normalizePath(helperParameters.context, suppliedParams[name], helperParameters.options.data);
+ value = Ember.Handlebars.get(normalizedPath.root, normalizedPath.path, helperParameters.options);
+ } else {
+ value = suppliedParams[name];
}
+
+ delete suppliedParams[name];
+
+ paramsForRecognizer[resultsName] = value;
});
-
- return queryParams;
- }).property('_potentialQueryParams.[]'),
+ return paramsForRecognizer;
+ }).property('resolvedParams.[]'),
/**
Sets the element's `href` attribute to the url for
@@ -36169,10 +37308,14 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
@see {Ember.LinkView}
*/
Ember.Handlebars.registerHelper('link-to', function linkToHelper(name) {
- var options = [].slice.call(arguments, -1)[0],
- params = [].slice.call(arguments, 0, -1),
+ var options = slice.call(arguments, -1)[0],
+ params = slice.call(arguments, 0, -1),
hash = options.hash;
+ if (params[params.length - 1] instanceof QueryParams) {
+ hash.queryParamsObject = params.pop();
+ }
+
hash.disabledBinding = hash.disabledWhen;
if (!options.fn) {
@@ -36200,6 +37343,8 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
return Ember.Handlebars.helpers.view.call(this, LinkView, options);
});
+
+
/**
See [link-to](/api/classes/Ember.Handlebars.helpers.html#method_link-to)
@@ -36378,7 +37523,7 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
```handelbars
My great app
- {{render navigation}}
+ {{render "navigation"}}
```
```html
@@ -36423,7 +37568,7 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
*/
Ember.Handlebars.registerHelper('render', function renderHelper(name, contextString, options) {
var length = arguments.length;
- Ember.assert("You must pass a template to render", length >= 2);
+
var contextProvided = length === 3,
container, router, controller, view, context, lookupOptions;
@@ -36442,6 +37587,8 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
throw Ember.Error("You must pass a templateName to render");
}
+ Ember.deprecate("Using a quoteless parameter with {{render}} is deprecated. Please update to quoted usage '{{render \"" + name + "\"}}.", options.types[0] !== 'ID');
+
// # legacy namespace
name = name.replace(/\//g, '.');
// \ legacy slash as namespace support
@@ -36491,7 +37638,7 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
options.hash.viewName = Ember.String.camelize(name);
var templateName = 'template:' + name;
- Ember.assert("You used `{{render '" + name + "'}}`, but '" + name + "' can not be found as either a template or a view.", container.has("view:" + name) || container.has(templateName));
+ Ember.assert("You used `{{render '" + name + "'}}`, but '" + name + "' can not be found as either a template or a view.", container.has("view:" + name) || container.has(templateName) || options.fn);
options.hash.template = container.lookup(templateName);
options.hash.controller = controller;
@@ -36591,6 +37738,10 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
target = target.root;
}
+ if (options.boundProperty) {
+ Ember.deprecate("Using a quoteless parameter with {{action}} is deprecated. Please update to quoted usage '{{action \"" + actionName + "\"}}.", false);
+ }
+
Ember.run(function runRegisteredAction() {
if (target.send) {
target.send.apply(target, args(options.parameters, actionName));
@@ -36809,6 +37960,7 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
action.target = { root: root, target: target, options: options };
action.bubbles = hash.bubbles;
action.preventDefault = hash.preventDefault;
+ action.boundProperty = options.types[0] === "ID";
var actionId = ActionHelper.registerAction(actionName, action, hash.allowedKeys);
return new SafeString('data-ember-action="' + actionId + '"');
@@ -36832,7 +37984,10 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
@submodule ember-routing
*/
-var get = Ember.get, set = Ember.set;
+var get = Ember.get, set = Ember.set,
+ map = Ember.EnumerableUtils.map;
+
+var queuedQueryParamChanges = {};
Ember.ControllerMixin.reopen({
/**
@@ -36890,7 +38045,7 @@ Ember.ControllerMixin.reopen({
/**
Transition into another route while replacing the current URL, if possible.
- This will replace the current history entry instead of adding a new one.
+ This will replace the current history entry instead of adding a new one.
Beside that, it is identical to `transitionToRoute` in all other respects.
```javascript
@@ -36941,6 +38096,7 @@ Ember.ControllerMixin.reopen({
}
});
+
})();
@@ -37083,6 +38239,7 @@ Ember.View.reopen({
@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;
@@ -37253,10 +38410,32 @@ Ember.Location = {
container directly.
*/
registerImplementation: function(name, implementation) {
+ Ember.deprecate('Using the Ember.Location.registerImplementation is no longer supported. Register your custom location implementation with the container instead.', false);
+
this.implementations[name] = implementation;
},
- implementations: {}
+ implementations: {},
+
+ /**
+ Returns the current `location.hash` by parsing location.href since browsers
+ inconsistently URL-decode `location.hash`.
+
+ https://bugzilla.mozilla.org/show_bug.cgi?id=483304
+
+ @private
+ @method getHash
+ */
+ getHash: function () {
+ var href = window.location.href,
+ hashIndex = href.indexOf('#');
+
+ if (hashIndex === -1) {
+ return '';
+ } else {
+ return href.substr(hashIndex);
+ }
+ }
};
})();
@@ -37282,6 +38461,7 @@ var get = Ember.get, set = Ember.set;
@extends Ember.Object
*/
Ember.NoneLocation = Ember.Object.extend({
+ implementation: 'none',
path: '',
/**
@@ -37352,8 +38532,6 @@ Ember.NoneLocation = Ember.Object.extend({
}
});
-Ember.Location.registerImplementation('none', Ember.NoneLocation);
-
})();
@@ -37364,7 +38542,8 @@ Ember.Location.registerImplementation('none', Ember.NoneLocation);
@submodule ember-routing
*/
-var get = Ember.get, set = Ember.set;
+var get = Ember.get, set = Ember.set,
+ getHash = Ember.Location.getHash;
/**
`Ember.HashLocation` implements the location API using the browser's
@@ -37376,6 +38555,7 @@ var get = Ember.get, set = Ember.set;
@extends Ember.Object
*/
Ember.HashLocation = Ember.Object.extend({
+ implementation: 'hash',
init: function() {
set(this, 'location', get(this, 'location') || window.location);
@@ -37388,8 +38568,7 @@ Ember.HashLocation = Ember.Object.extend({
@method getURL
*/
getURL: function() {
- // Default implementation without feature flag enabled
- return get(this, 'location').hash.substr(1);
+ return getHash().substr(1);
},
/**
@@ -37416,6 +38595,7 @@ Ember.HashLocation = Ember.Object.extend({
*/
replaceURL: function(path) {
get(this, 'location').replace('#' + path);
+ set(this, 'lastSetURL', path);
},
/**
@@ -37433,7 +38613,7 @@ Ember.HashLocation = Ember.Object.extend({
Ember.$(window).on('hashchange.ember-location-'+guid, function() {
Ember.run(function() {
- var path = location.hash.substr(1);
+ var path = self.getURL();
if (get(self, 'lastSetURL') === path) { return; }
set(self, 'lastSetURL', null);
@@ -37471,8 +38651,6 @@ Ember.HashLocation = Ember.Object.extend({
}
});
-Ember.Location.registerImplementation('hash', Ember.HashLocation);
-
})();
@@ -37496,9 +38674,11 @@ var supportsHistoryState = window.history && 'state' in window.history;
@extends Ember.Object
*/
Ember.HistoryLocation = Ember.Object.extend({
+ implementation: 'history',
init: function() {
set(this, 'location', get(this, 'location') || window.location);
+ set(this, 'baseURL', Ember.$('base').attr('href') || '');
},
/**
@@ -37530,10 +38710,12 @@ Ember.HistoryLocation = Ember.Object.extend({
getURL: function() {
var rootURL = get(this, 'rootURL'),
location = get(this, 'location'),
- path = location.pathname;
+ path = location.pathname,
+ baseURL = get(this, 'baseURL');
rootURL = rootURL.replace(/\/$/, '');
- var url = path.replace(rootURL, '');
+ baseURL = baseURL.replace(/\/$/, '');
+ var url = path.replace(baseURL, '').replace(rootURL, '');
return url;
@@ -37658,13 +38840,17 @@ Ember.HistoryLocation = Ember.Object.extend({
@return formatted url {String}
*/
formatURL: function(url) {
- var rootURL = get(this, 'rootURL');
+ var rootURL = get(this, 'rootURL'),
+ baseURL = get(this, 'baseURL');
if (url !== '') {
rootURL = rootURL.replace(/\/$/, '');
+ baseURL = baseURL.replace(/\/$/, '');
+ } else if(baseURL.match(/^\//) && rootURL.match(/^\//)) {
+ baseURL = baseURL.replace(/\/$/, '');
}
- return rootURL + url;
+ return baseURL + rootURL + url;
},
/**
@@ -37680,8 +38866,6 @@ Ember.HistoryLocation = Ember.Object.extend({
}
});
-Ember.Location.registerImplementation('history', Ember.HistoryLocation);
-
})();
@@ -38536,9 +39720,15 @@ var Application = Ember.Application = Ember.Namespace.extend(Ember.DeferredMixin
```javascript
App.inject(, , )
- App.inject('model:user', 'email', 'model:email')
- App.inject('model', 'source', 'source:main')
+ App.inject('controller:application', 'email', 'model:email')
+ App.inject('controller', 'source', 'source:main')
```
+ Please note that injections on models are currently disabled.
+ This was done because ember-data was not ready for fully a container aware ecosystem.
+
+ You can enable injections on models by setting `Ember.MODEL_FACTORY_INJECTIONS` flag to `true`
+ If model factory injections are enabled, models should not be
+ accessed globally (only through `container.lookupFactory('model:modelName'))`);
@method inject
@param factoryNameOrType {String}
@@ -38787,6 +39977,8 @@ var Application = Ember.Application = Ember.Namespace.extend(Ember.DeferredMixin
willDestroy: function() {
Ember.BOOTED = false;
+ // Ensure deactivation of routes before objects are destroyed
+ this.__container__.lookup('router:main').reset();
this.__container__.destroy();
},
@@ -38868,6 +40060,10 @@ Ember.Application.reopenClass({
container.register('router:main', Ember.Router);
container.injection('router:main', 'namespace', 'application:main');
+ container.register('location:hash', Ember.HashLocation);
+ container.register('location:history', Ember.HistoryLocation);
+ container.register('location:none', Ember.NoneLocation);
+
container.injection('controller', 'target', 'router:main');
container.injection('controller', 'namespace', 'application:main');
@@ -39671,7 +40867,7 @@ Ember.Test = {
Example:
- ```
+ ```javascript
Ember.Test.unregisterHelper('wait');
```
@@ -39691,7 +40887,8 @@ Ember.Test = {
The callback will receive the current application as an argument.
Example:
- ```
+
+ ```javascript
Ember.Test.onInjectHelpers(function() {
Ember.$(document).ajaxStart(function() {
Test.pendingAjaxRequests++;
@@ -39734,7 +40931,7 @@ Ember.Test = {
Example:
- ```
+ ```javascript
Ember.Test.adapter = MyCustomAdapter.create()
```
@@ -39768,18 +40965,21 @@ Ember.Test = {
transition or an IndexDB transaction.
For example:
+
```javascript
Ember.Test.registerWaiter(function() {
- return myPendingTransactions() == 0;
+ return myPendingTransactions() == 0;
});
```
The `context` argument allows you to optionally specify the `this`
with which your callback will be invoked.
For example:
+
```javascript
Ember.Test.registerWaiter(MyDB, MyDB.hasPendingTransactions);
```
+
@public
@method registerWaiter
@param {Object} context (optional)
@@ -40040,7 +41240,7 @@ function isolate(fn, val) {
// Reset lastPromise for nested helpers
Ember.Test.lastPromise = null;
- value = fn.call(null, val);
+ value = fn(val);
lastPromise = Ember.Test.lastPromise;
@@ -40267,10 +41467,17 @@ function currentURL(app){
}
function visit(app, url) {
- Ember.run(app, 'advanceReadiness');
+ var router = app.__container__.lookup('router:main');
+ router.location.setURL(url);
+
+ if (app._readinessDeferrals > 0) {
+ router['initialURL'] = url;
+ Ember.run(app, 'advanceReadiness');
+ delete router['initialURL'];
+ } else {
+ Ember.run(app, app.handleURL, url);
+ }
- app.__container__.lookup('router:main').location.setURL(url);
- Ember.run(app, app.handleURL, url);
return wait(app);
}
@@ -40369,7 +41576,7 @@ function wait(app, value) {
// Every 10ms, poll for the async thing to have finished
var watcher = setInterval(function() {
// 1. If the router is loading, keep polling
- var routerIsLoading = app.__container__.lookup('router:main').router.isLoading;
+ var routerIsLoading = !!app.__container__.lookup('router:main').router.activeTransition;
if (routerIsLoading) { return; }
// 2. If there are pending Ajax requests, keep polling
@@ -40425,7 +41632,7 @@ asyncHelper('visit', visit);
*
* ```javascript
* click('.some-jQuery-selector').then(function() {
-* // assert something
+* // assert something
* });
* ```
*
@@ -40480,7 +41687,7 @@ asyncHelper('fillIn', fillIn);
* Example:
*
* ```javascript
-* var $el = find('.my-selector);
+* var $el = find('.my-selector');
* ```
*
* @method find
diff --git a/app/scripts/vendor/handlebars-1.3.0.js b/app/scripts/vendor/handlebars-1.2.1.js
similarity index 80%
rename from app/scripts/vendor/handlebars-1.3.0.js
rename to app/scripts/vendor/handlebars-1.2.1.js
index bec7085..c472c1b 100644
--- a/app/scripts/vendor/handlebars-1.3.0.js
+++ b/app/scripts/vendor/handlebars-1.2.1.js
@@ -1,6 +1,6 @@
/*!
- handlebars v1.3.0
+ handlebars v1.2.1
Copyright (C) 2011 by Yehuda Katz
@@ -132,25 +132,13 @@ var __module5__ = (function() {
var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
- function Exception(message, node) {
- var line;
- if (node && node.firstLine) {
- line = node.firstLine;
-
- message += ' - ' + line + ':' + node.firstColumn;
- }
-
- var tmp = Error.prototype.constructor.call(this, message);
+ function Exception(/* message */) {
+ var tmp = Error.prototype.constructor.apply(this, arguments);
// Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
for (var idx = 0; idx < errorProps.length; idx++) {
this[errorProps[idx]] = tmp[errorProps[idx]];
}
-
- if (line) {
- this.lineNumber = line;
- this.column = node.firstColumn;
- }
}
Exception.prototype = new Error();
@@ -166,7 +154,7 @@ var __module2__ = (function(__dependency1__, __dependency2__) {
var Utils = __dependency1__;
var Exception = __dependency2__;
- var VERSION = "1.3.0";
+ var VERSION = "1.2.1";
__exports__.VERSION = VERSION;var COMPILER_REVISION = 4;
__exports__.COMPILER_REVISION = COMPILER_REVISION;
var REVISION_CHANGES = {
@@ -218,7 +206,7 @@ var __module2__ = (function(__dependency1__, __dependency2__) {
if(arguments.length === 2) {
return undefined;
} else {
- throw new Exception("Missing helper: '" + arg + "'");
+ throw new Error("Missing helper: '" + arg + "'");
}
});
@@ -362,11 +350,11 @@ var __module6__ = (function(__dependency1__, __dependency2__, __dependency3__) {
if (compilerRevision < currentRevision) {
var runtimeVersions = REVISION_CHANGES[currentRevision],
compilerVersions = REVISION_CHANGES[compilerRevision];
- throw new Exception("Template was precompiled with an older version of Handlebars than the current runtime. "+
+ throw new Error("Template was precompiled with an older version of Handlebars than the current runtime. "+
"Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+").");
} else {
// Use the embedded version info since the runtime doesn't know about this revision yet
- throw new Exception("Template was precompiled with a newer version of Handlebars than the current runtime. "+
+ throw new Error("Template was precompiled with a newer version of Handlebars than the current runtime. "+
"Please update your runtime to a newer version ("+compilerInfo[1]+").");
}
}
@@ -376,7 +364,7 @@ var __module6__ = (function(__dependency1__, __dependency2__, __dependency3__) {
function template(templateSpec, env) {
if (!env) {
- throw new Exception("No environment passed to template");
+ throw new Error("No environment passed to template");
}
// Note: Using env.VM references rather than local var references throughout this section to allow
@@ -532,52 +520,23 @@ var __module7__ = (function(__dependency1__) {
var __exports__;
var Exception = __dependency1__;
- function LocationInfo(locInfo){
- locInfo = locInfo || {};
- this.firstLine = locInfo.first_line;
- this.firstColumn = locInfo.first_column;
- this.lastColumn = locInfo.last_column;
- this.lastLine = locInfo.last_line;
- }
-
var AST = {
- ProgramNode: function(statements, inverseStrip, inverse, locInfo) {
- var inverseLocationInfo, firstInverseNode;
- if (arguments.length === 3) {
- locInfo = inverse;
- inverse = null;
- } else if (arguments.length === 2) {
- locInfo = inverseStrip;
- inverseStrip = null;
- }
-
- LocationInfo.call(this, locInfo);
+ ProgramNode: function(statements, inverseStrip, inverse) {
this.type = "program";
this.statements = statements;
this.strip = {};
if(inverse) {
- firstInverseNode = inverse[0];
- if (firstInverseNode) {
- inverseLocationInfo = {
- first_line: firstInverseNode.firstLine,
- last_line: firstInverseNode.lastLine,
- last_column: firstInverseNode.lastColumn,
- first_column: firstInverseNode.firstColumn
- };
- this.inverse = new AST.ProgramNode(inverse, inverseStrip, inverseLocationInfo);
- } else {
- this.inverse = new AST.ProgramNode(inverse, inverseStrip);
- }
+ this.inverse = new AST.ProgramNode(inverse, inverseStrip);
this.strip.right = inverseStrip.left;
} else if (inverseStrip) {
this.strip.left = inverseStrip.right;
}
},
- MustacheNode: function(rawParams, hash, open, strip, locInfo) {
- LocationInfo.call(this, locInfo);
+ MustacheNode: function(rawParams, hash, open, strip) {
this.type = "mustache";
+ this.hash = hash;
this.strip = strip;
// Open may be a string parsed from the parser or a passed boolean flag
@@ -589,29 +548,6 @@ var __module7__ = (function(__dependency1__) {
this.escaped = !!open;
}
- if (rawParams instanceof AST.SexprNode) {
- this.sexpr = rawParams;
- } else {
- // Support old AST API
- this.sexpr = new AST.SexprNode(rawParams, hash);
- }
-
- this.sexpr.isRoot = true;
-
- // Support old AST API that stored this info in MustacheNode
- this.id = this.sexpr.id;
- this.params = this.sexpr.params;
- this.hash = this.sexpr.hash;
- this.eligibleHelper = this.sexpr.eligibleHelper;
- this.isHelper = this.sexpr.isHelper;
- },
-
- SexprNode: function(rawParams, hash, locInfo) {
- LocationInfo.call(this, locInfo);
-
- this.type = "sexpr";
- this.hash = hash;
-
var id = this.id = rawParams[0];
var params = this.params = rawParams.slice(1);
@@ -629,22 +565,19 @@ var __module7__ = (function(__dependency1__) {
// pass or at runtime.
},
- PartialNode: function(partialName, context, strip, locInfo) {
- LocationInfo.call(this, locInfo);
+ PartialNode: function(partialName, context, strip) {
this.type = "partial";
this.partialName = partialName;
this.context = context;
this.strip = strip;
},
- BlockNode: function(mustache, program, inverse, close, locInfo) {
- LocationInfo.call(this, locInfo);
-
- if(mustache.sexpr.id.original !== close.path.original) {
- throw new Exception(mustache.sexpr.id.original + " doesn't match " + close.path.original, this);
+ BlockNode: function(mustache, program, inverse, close) {
+ if(mustache.id.original !== close.path.original) {
+ throw new Exception(mustache.id.original + " doesn't match " + close.path.original);
}
- this.type = 'block';
+ this.type = "block";
this.mustache = mustache;
this.program = program;
this.inverse = inverse;
@@ -662,20 +595,17 @@ var __module7__ = (function(__dependency1__) {
}
},
- ContentNode: function(string, locInfo) {
- LocationInfo.call(this, locInfo);
+ ContentNode: function(string) {
this.type = "content";
this.string = string;
},
- HashNode: function(pairs, locInfo) {
- LocationInfo.call(this, locInfo);
+ HashNode: function(pairs) {
this.type = "hash";
this.pairs = pairs;
},
- IdNode: function(parts, locInfo) {
- LocationInfo.call(this, locInfo);
+ IdNode: function(parts) {
this.type = "ID";
var original = "",
@@ -687,16 +617,11 @@ var __module7__ = (function(__dependency1__) {
original += (parts[i].separator || '') + part;
if (part === ".." || part === "." || part === "this") {
- if (dig.length > 0) {
- throw new Exception("Invalid path: " + original, this);
- } else if (part === "..") {
- depth++;
- } else {
- this.isScoped = true;
- }
- } else {
- dig.push(part);
+ if (dig.length > 0) { throw new Exception("Invalid path: " + original); }
+ else if (part === "..") { depth++; }
+ else { this.isScoped = true; }
}
+ else { dig.push(part); }
}
this.original = original;
@@ -711,43 +636,37 @@ var __module7__ = (function(__dependency1__) {
this.stringModeValue = this.string;
},
- PartialNameNode: function(name, locInfo) {
- LocationInfo.call(this, locInfo);
+ PartialNameNode: function(name) {
this.type = "PARTIAL_NAME";
this.name = name.original;
},
- DataNode: function(id, locInfo) {
- LocationInfo.call(this, locInfo);
+ DataNode: function(id) {
this.type = "DATA";
this.id = id;
},
- StringNode: function(string, locInfo) {
- LocationInfo.call(this, locInfo);
+ StringNode: function(string) {
this.type = "STRING";
this.original =
this.string =
this.stringModeValue = string;
},
- IntegerNode: function(integer, locInfo) {
- LocationInfo.call(this, locInfo);
+ IntegerNode: function(integer) {
this.type = "INTEGER";
this.original =
this.integer = integer;
this.stringModeValue = Number(integer);
},
- BooleanNode: function(bool, locInfo) {
- LocationInfo.call(this, locInfo);
+ BooleanNode: function(bool) {
this.type = "BOOLEAN";
this.bool = bool;
this.stringModeValue = bool === "true";
},
- CommentNode: function(comment, locInfo) {
- LocationInfo.call(this, locInfo);
+ CommentNode: function(comment) {
this.type = "comment";
this.comment = comment;
}
@@ -768,105 +687,103 @@ var __module9__ = (function() {
var handlebars = (function(){
var parser = {trace: function trace() { },
yy: {},
- symbols_: {"error":2,"root":3,"statements":4,"EOF":5,"program":6,"simpleInverse":7,"statement":8,"openInverse":9,"closeBlock":10,"openBlock":11,"mustache":12,"partial":13,"CONTENT":14,"COMMENT":15,"OPEN_BLOCK":16,"sexpr":17,"CLOSE":18,"OPEN_INVERSE":19,"OPEN_ENDBLOCK":20,"path":21,"OPEN":22,"OPEN_UNESCAPED":23,"CLOSE_UNESCAPED":24,"OPEN_PARTIAL":25,"partialName":26,"partial_option0":27,"sexpr_repetition0":28,"sexpr_option0":29,"dataName":30,"param":31,"STRING":32,"INTEGER":33,"BOOLEAN":34,"OPEN_SEXPR":35,"CLOSE_SEXPR":36,"hash":37,"hash_repetition_plus0":38,"hashSegment":39,"ID":40,"EQUALS":41,"DATA":42,"pathSegments":43,"SEP":44,"$accept":0,"$end":1},
- terminals_: {2:"error",5:"EOF",14:"CONTENT",15:"COMMENT",16:"OPEN_BLOCK",18:"CLOSE",19:"OPEN_INVERSE",20:"OPEN_ENDBLOCK",22:"OPEN",23:"OPEN_UNESCAPED",24:"CLOSE_UNESCAPED",25:"OPEN_PARTIAL",32:"STRING",33:"INTEGER",34:"BOOLEAN",35:"OPEN_SEXPR",36:"CLOSE_SEXPR",40:"ID",41:"EQUALS",42:"DATA",44:"SEP"},
- productions_: [0,[3,2],[3,1],[6,2],[6,3],[6,2],[6,1],[6,1],[6,0],[4,1],[4,2],[8,3],[8,3],[8,1],[8,1],[8,1],[8,1],[11,3],[9,3],[10,3],[12,3],[12,3],[13,4],[7,2],[17,3],[17,1],[31,1],[31,1],[31,1],[31,1],[31,1],[31,3],[37,1],[39,3],[26,1],[26,1],[26,1],[30,2],[21,1],[43,3],[43,1],[27,0],[27,1],[28,0],[28,2],[29,0],[29,1],[38,1],[38,2]],
+ symbols_: {"error":2,"root":3,"statements":4,"EOF":5,"program":6,"simpleInverse":7,"statement":8,"openInverse":9,"closeBlock":10,"openBlock":11,"mustache":12,"partial":13,"CONTENT":14,"COMMENT":15,"OPEN_BLOCK":16,"inMustache":17,"CLOSE":18,"OPEN_INVERSE":19,"OPEN_ENDBLOCK":20,"path":21,"OPEN":22,"OPEN_UNESCAPED":23,"CLOSE_UNESCAPED":24,"OPEN_PARTIAL":25,"partialName":26,"partial_option0":27,"inMustache_repetition0":28,"inMustache_option0":29,"dataName":30,"param":31,"STRING":32,"INTEGER":33,"BOOLEAN":34,"hash":35,"hash_repetition_plus0":36,"hashSegment":37,"ID":38,"EQUALS":39,"DATA":40,"pathSegments":41,"SEP":42,"$accept":0,"$end":1},
+ terminals_: {2:"error",5:"EOF",14:"CONTENT",15:"COMMENT",16:"OPEN_BLOCK",18:"CLOSE",19:"OPEN_INVERSE",20:"OPEN_ENDBLOCK",22:"OPEN",23:"OPEN_UNESCAPED",24:"CLOSE_UNESCAPED",25:"OPEN_PARTIAL",32:"STRING",33:"INTEGER",34:"BOOLEAN",38:"ID",39:"EQUALS",40:"DATA",42:"SEP"},
+ productions_: [0,[3,2],[3,1],[6,2],[6,3],[6,2],[6,1],[6,1],[6,0],[4,1],[4,2],[8,3],[8,3],[8,1],[8,1],[8,1],[8,1],[11,3],[9,3],[10,3],[12,3],[12,3],[13,4],[7,2],[17,3],[17,1],[31,1],[31,1],[31,1],[31,1],[31,1],[35,1],[37,3],[26,1],[26,1],[26,1],[30,2],[21,1],[41,3],[41,1],[27,0],[27,1],[28,0],[28,2],[29,0],[29,1],[36,1],[36,2]],
performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
var $0 = $$.length - 1;
switch (yystate) {
- case 1: return new yy.ProgramNode($$[$0-1], this._$);
+ case 1: return new yy.ProgramNode($$[$0-1]);
break;
- case 2: return new yy.ProgramNode([], this._$);
+ case 2: return new yy.ProgramNode([]);
break;
- case 3:this.$ = new yy.ProgramNode([], $$[$0-1], $$[$0], this._$);
+ case 3:this.$ = new yy.ProgramNode([], $$[$0-1], $$[$0]);
break;
- case 4:this.$ = new yy.ProgramNode($$[$0-2], $$[$0-1], $$[$0], this._$);
+ case 4:this.$ = new yy.ProgramNode($$[$0-2], $$[$0-1], $$[$0]);
break;
- case 5:this.$ = new yy.ProgramNode($$[$0-1], $$[$0], [], this._$);
+ case 5:this.$ = new yy.ProgramNode($$[$0-1], $$[$0], []);
break;
- case 6:this.$ = new yy.ProgramNode($$[$0], this._$);
+ case 6:this.$ = new yy.ProgramNode($$[$0]);
break;
- case 7:this.$ = new yy.ProgramNode([], this._$);
+ case 7:this.$ = new yy.ProgramNode([]);
break;
- case 8:this.$ = new yy.ProgramNode([], this._$);
+ case 8:this.$ = new yy.ProgramNode([]);
break;
case 9:this.$ = [$$[$0]];
break;
case 10: $$[$0-1].push($$[$0]); this.$ = $$[$0-1];
break;
- case 11:this.$ = new yy.BlockNode($$[$0-2], $$[$0-1].inverse, $$[$0-1], $$[$0], this._$);
+ case 11:this.$ = new yy.BlockNode($$[$0-2], $$[$0-1].inverse, $$[$0-1], $$[$0]);
break;
- case 12:this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0-1].inverse, $$[$0], this._$);
+ case 12:this.$ = new yy.BlockNode($$[$0-2], $$[$0-1], $$[$0-1].inverse, $$[$0]);
break;
case 13:this.$ = $$[$0];
break;
case 14:this.$ = $$[$0];
break;
- case 15:this.$ = new yy.ContentNode($$[$0], this._$);
+ case 15:this.$ = new yy.ContentNode($$[$0]);
break;
- case 16:this.$ = new yy.CommentNode($$[$0], this._$);
+ case 16:this.$ = new yy.CommentNode($$[$0]);
break;
- case 17:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], stripFlags($$[$0-2], $$[$0]), this._$);
+ case 17:this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2], stripFlags($$[$0-2], $$[$0]));
break;
- case 18:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], stripFlags($$[$0-2], $$[$0]), this._$);
+ case 18:this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2], stripFlags($$[$0-2], $$[$0]));
break;
case 19:this.$ = {path: $$[$0-1], strip: stripFlags($$[$0-2], $$[$0])};
break;
- case 20:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], stripFlags($$[$0-2], $$[$0]), this._$);
+ case 20:this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2], stripFlags($$[$0-2], $$[$0]));
break;
- case 21:this.$ = new yy.MustacheNode($$[$0-1], null, $$[$0-2], stripFlags($$[$0-2], $$[$0]), this._$);
+ case 21:this.$ = new yy.MustacheNode($$[$0-1][0], $$[$0-1][1], $$[$0-2], stripFlags($$[$0-2], $$[$0]));
break;
- case 22:this.$ = new yy.PartialNode($$[$0-2], $$[$0-1], stripFlags($$[$0-3], $$[$0]), this._$);
+ case 22:this.$ = new yy.PartialNode($$[$0-2], $$[$0-1], stripFlags($$[$0-3], $$[$0]));
break;
case 23:this.$ = stripFlags($$[$0-1], $$[$0]);
break;
- case 24:this.$ = new yy.SexprNode([$$[$0-2]].concat($$[$0-1]), $$[$0], this._$);
+ case 24:this.$ = [[$$[$0-2]].concat($$[$0-1]), $$[$0]];
break;
- case 25:this.$ = new yy.SexprNode([$$[$0]], null, this._$);
+ case 25:this.$ = [[$$[$0]], null];
break;
case 26:this.$ = $$[$0];
break;
- case 27:this.$ = new yy.StringNode($$[$0], this._$);
+ case 27:this.$ = new yy.StringNode($$[$0]);
break;
- case 28:this.$ = new yy.IntegerNode($$[$0], this._$);
+ case 28:this.$ = new yy.IntegerNode($$[$0]);
break;
- case 29:this.$ = new yy.BooleanNode($$[$0], this._$);
+ case 29:this.$ = new yy.BooleanNode($$[$0]);
break;
case 30:this.$ = $$[$0];
break;
- case 31:$$[$0-1].isHelper = true; this.$ = $$[$0-1];
+ case 31:this.$ = new yy.HashNode($$[$0]);
break;
- case 32:this.$ = new yy.HashNode($$[$0], this._$);
+ case 32:this.$ = [$$[$0-2], $$[$0]];
break;
- case 33:this.$ = [$$[$0-2], $$[$0]];
+ case 33:this.$ = new yy.PartialNameNode($$[$0]);
break;
- case 34:this.$ = new yy.PartialNameNode($$[$0], this._$);
+ case 34:this.$ = new yy.PartialNameNode(new yy.StringNode($$[$0]));
break;
- case 35:this.$ = new yy.PartialNameNode(new yy.StringNode($$[$0], this._$), this._$);
+ case 35:this.$ = new yy.PartialNameNode(new yy.IntegerNode($$[$0]));
break;
- case 36:this.$ = new yy.PartialNameNode(new yy.IntegerNode($$[$0], this._$));
+ case 36:this.$ = new yy.DataNode($$[$0]);
break;
- case 37:this.$ = new yy.DataNode($$[$0], this._$);
+ case 37:this.$ = new yy.IdNode($$[$0]);
break;
- case 38:this.$ = new yy.IdNode($$[$0], this._$);
+ case 38: $$[$0-2].push({part: $$[$0], separator: $$[$0-1]}); this.$ = $$[$0-2];
break;
- case 39: $$[$0-2].push({part: $$[$0], separator: $$[$0-1]}); this.$ = $$[$0-2];
+ case 39:this.$ = [{part: $$[$0]}];
break;
- case 40:this.$ = [{part: $$[$0]}];
+ case 42:this.$ = [];
break;
- case 43:this.$ = [];
+ case 43:$$[$0-1].push($$[$0]);
break;
- case 44:$$[$0-1].push($$[$0]);
+ case 46:this.$ = [$$[$0]];
break;
- case 47:this.$ = [$$[$0]];
- break;
- case 48:$$[$0-1].push($$[$0]);
+ case 47:$$[$0-1].push($$[$0]);
break;
}
},
- table: [{3:1,4:2,5:[1,3],8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],25:[1,15]},{1:[3]},{5:[1,16],8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],25:[1,15]},{1:[2,2]},{5:[2,9],14:[2,9],15:[2,9],16:[2,9],19:[2,9],20:[2,9],22:[2,9],23:[2,9],25:[2,9]},{4:20,6:18,7:19,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,8],22:[1,13],23:[1,14],25:[1,15]},{4:20,6:22,7:19,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,8],22:[1,13],23:[1,14],25:[1,15]},{5:[2,13],14:[2,13],15:[2,13],16:[2,13],19:[2,13],20:[2,13],22:[2,13],23:[2,13],25:[2,13]},{5:[2,14],14:[2,14],15:[2,14],16:[2,14],19:[2,14],20:[2,14],22:[2,14],23:[2,14],25:[2,14]},{5:[2,15],14:[2,15],15:[2,15],16:[2,15],19:[2,15],20:[2,15],22:[2,15],23:[2,15],25:[2,15]},{5:[2,16],14:[2,16],15:[2,16],16:[2,16],19:[2,16],20:[2,16],22:[2,16],23:[2,16],25:[2,16]},{17:23,21:24,30:25,40:[1,28],42:[1,27],43:26},{17:29,21:24,30:25,40:[1,28],42:[1,27],43:26},{17:30,21:24,30:25,40:[1,28],42:[1,27],43:26},{17:31,21:24,30:25,40:[1,28],42:[1,27],43:26},{21:33,26:32,32:[1,34],33:[1,35],40:[1,28],43:26},{1:[2,1]},{5:[2,10],14:[2,10],15:[2,10],16:[2,10],19:[2,10],20:[2,10],22:[2,10],23:[2,10],25:[2,10]},{10:36,20:[1,37]},{4:38,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,7],22:[1,13],23:[1,14],25:[1,15]},{7:39,8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,6],22:[1,13],23:[1,14],25:[1,15]},{17:23,18:[1,40],21:24,30:25,40:[1,28],42:[1,27],43:26},{10:41,20:[1,37]},{18:[1,42]},{18:[2,43],24:[2,43],28:43,32:[2,43],33:[2,43],34:[2,43],35:[2,43],36:[2,43],40:[2,43],42:[2,43]},{18:[2,25],24:[2,25],36:[2,25]},{18:[2,38],24:[2,38],32:[2,38],33:[2,38],34:[2,38],35:[2,38],36:[2,38],40:[2,38],42:[2,38],44:[1,44]},{21:45,40:[1,28],43:26},{18:[2,40],24:[2,40],32:[2,40],33:[2,40],34:[2,40],35:[2,40],36:[2,40],40:[2,40],42:[2,40],44:[2,40]},{18:[1,46]},{18:[1,47]},{24:[1,48]},{18:[2,41],21:50,27:49,40:[1,28],43:26},{18:[2,34],40:[2,34]},{18:[2,35],40:[2,35]},{18:[2,36],40:[2,36]},{5:[2,11],14:[2,11],15:[2,11],16:[2,11],19:[2,11],20:[2,11],22:[2,11],23:[2,11],25:[2,11]},{21:51,40:[1,28],43:26},{8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,3],22:[1,13],23:[1,14],25:[1,15]},{4:52,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,5],22:[1,13],23:[1,14],25:[1,15]},{14:[2,23],15:[2,23],16:[2,23],19:[2,23],20:[2,23],22:[2,23],23:[2,23],25:[2,23]},{5:[2,12],14:[2,12],15:[2,12],16:[2,12],19:[2,12],20:[2,12],22:[2,12],23:[2,12],25:[2,12]},{14:[2,18],15:[2,18],16:[2,18],19:[2,18],20:[2,18],22:[2,18],23:[2,18],25:[2,18]},{18:[2,45],21:56,24:[2,45],29:53,30:60,31:54,32:[1,57],33:[1,58],34:[1,59],35:[1,61],36:[2,45],37:55,38:62,39:63,40:[1,64],42:[1,27],43:26},{40:[1,65]},{18:[2,37],24:[2,37],32:[2,37],33:[2,37],34:[2,37],35:[2,37],36:[2,37],40:[2,37],42:[2,37]},{14:[2,17],15:[2,17],16:[2,17],19:[2,17],20:[2,17],22:[2,17],23:[2,17],25:[2,17]},{5:[2,20],14:[2,20],15:[2,20],16:[2,20],19:[2,20],20:[2,20],22:[2,20],23:[2,20],25:[2,20]},{5:[2,21],14:[2,21],15:[2,21],16:[2,21],19:[2,21],20:[2,21],22:[2,21],23:[2,21],25:[2,21]},{18:[1,66]},{18:[2,42]},{18:[1,67]},{8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,4],22:[1,13],23:[1,14],25:[1,15]},{18:[2,24],24:[2,24],36:[2,24]},{18:[2,44],24:[2,44],32:[2,44],33:[2,44],34:[2,44],35:[2,44],36:[2,44],40:[2,44],42:[2,44]},{18:[2,46],24:[2,46],36:[2,46]},{18:[2,26],24:[2,26],32:[2,26],33:[2,26],34:[2,26],35:[2,26],36:[2,26],40:[2,26],42:[2,26]},{18:[2,27],24:[2,27],32:[2,27],33:[2,27],34:[2,27],35:[2,27],36:[2,27],40:[2,27],42:[2,27]},{18:[2,28],24:[2,28],32:[2,28],33:[2,28],34:[2,28],35:[2,28],36:[2,28],40:[2,28],42:[2,28]},{18:[2,29],24:[2,29],32:[2,29],33:[2,29],34:[2,29],35:[2,29],36:[2,29],40:[2,29],42:[2,29]},{18:[2,30],24:[2,30],32:[2,30],33:[2,30],34:[2,30],35:[2,30],36:[2,30],40:[2,30],42:[2,30]},{17:68,21:24,30:25,40:[1,28],42:[1,27],43:26},{18:[2,32],24:[2,32],36:[2,32],39:69,40:[1,70]},{18:[2,47],24:[2,47],36:[2,47],40:[2,47]},{18:[2,40],24:[2,40],32:[2,40],33:[2,40],34:[2,40],35:[2,40],36:[2,40],40:[2,40],41:[1,71],42:[2,40],44:[2,40]},{18:[2,39],24:[2,39],32:[2,39],33:[2,39],34:[2,39],35:[2,39],36:[2,39],40:[2,39],42:[2,39],44:[2,39]},{5:[2,22],14:[2,22],15:[2,22],16:[2,22],19:[2,22],20:[2,22],22:[2,22],23:[2,22],25:[2,22]},{5:[2,19],14:[2,19],15:[2,19],16:[2,19],19:[2,19],20:[2,19],22:[2,19],23:[2,19],25:[2,19]},{36:[1,72]},{18:[2,48],24:[2,48],36:[2,48],40:[2,48]},{41:[1,71]},{21:56,30:60,31:73,32:[1,57],33:[1,58],34:[1,59],35:[1,61],40:[1,28],42:[1,27],43:26},{18:[2,31],24:[2,31],32:[2,31],33:[2,31],34:[2,31],35:[2,31],36:[2,31],40:[2,31],42:[2,31]},{18:[2,33],24:[2,33],36:[2,33],40:[2,33]}],
- defaultActions: {3:[2,2],16:[2,1],50:[2,42]},
+ table: [{3:1,4:2,5:[1,3],8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],25:[1,15]},{1:[3]},{5:[1,16],8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],22:[1,13],23:[1,14],25:[1,15]},{1:[2,2]},{5:[2,9],14:[2,9],15:[2,9],16:[2,9],19:[2,9],20:[2,9],22:[2,9],23:[2,9],25:[2,9]},{4:20,6:18,7:19,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,8],22:[1,13],23:[1,14],25:[1,15]},{4:20,6:22,7:19,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,8],22:[1,13],23:[1,14],25:[1,15]},{5:[2,13],14:[2,13],15:[2,13],16:[2,13],19:[2,13],20:[2,13],22:[2,13],23:[2,13],25:[2,13]},{5:[2,14],14:[2,14],15:[2,14],16:[2,14],19:[2,14],20:[2,14],22:[2,14],23:[2,14],25:[2,14]},{5:[2,15],14:[2,15],15:[2,15],16:[2,15],19:[2,15],20:[2,15],22:[2,15],23:[2,15],25:[2,15]},{5:[2,16],14:[2,16],15:[2,16],16:[2,16],19:[2,16],20:[2,16],22:[2,16],23:[2,16],25:[2,16]},{17:23,21:24,30:25,38:[1,28],40:[1,27],41:26},{17:29,21:24,30:25,38:[1,28],40:[1,27],41:26},{17:30,21:24,30:25,38:[1,28],40:[1,27],41:26},{17:31,21:24,30:25,38:[1,28],40:[1,27],41:26},{21:33,26:32,32:[1,34],33:[1,35],38:[1,28],41:26},{1:[2,1]},{5:[2,10],14:[2,10],15:[2,10],16:[2,10],19:[2,10],20:[2,10],22:[2,10],23:[2,10],25:[2,10]},{10:36,20:[1,37]},{4:38,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,7],22:[1,13],23:[1,14],25:[1,15]},{7:39,8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,21],20:[2,6],22:[1,13],23:[1,14],25:[1,15]},{17:23,18:[1,40],21:24,30:25,38:[1,28],40:[1,27],41:26},{10:41,20:[1,37]},{18:[1,42]},{18:[2,42],24:[2,42],28:43,32:[2,42],33:[2,42],34:[2,42],38:[2,42],40:[2,42]},{18:[2,25],24:[2,25]},{18:[2,37],24:[2,37],32:[2,37],33:[2,37],34:[2,37],38:[2,37],40:[2,37],42:[1,44]},{21:45,38:[1,28],41:26},{18:[2,39],24:[2,39],32:[2,39],33:[2,39],34:[2,39],38:[2,39],40:[2,39],42:[2,39]},{18:[1,46]},{18:[1,47]},{24:[1,48]},{18:[2,40],21:50,27:49,38:[1,28],41:26},{18:[2,33],38:[2,33]},{18:[2,34],38:[2,34]},{18:[2,35],38:[2,35]},{5:[2,11],14:[2,11],15:[2,11],16:[2,11],19:[2,11],20:[2,11],22:[2,11],23:[2,11],25:[2,11]},{21:51,38:[1,28],41:26},{8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,3],22:[1,13],23:[1,14],25:[1,15]},{4:52,8:4,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,5],22:[1,13],23:[1,14],25:[1,15]},{14:[2,23],15:[2,23],16:[2,23],19:[2,23],20:[2,23],22:[2,23],23:[2,23],25:[2,23]},{5:[2,12],14:[2,12],15:[2,12],16:[2,12],19:[2,12],20:[2,12],22:[2,12],23:[2,12],25:[2,12]},{14:[2,18],15:[2,18],16:[2,18],19:[2,18],20:[2,18],22:[2,18],23:[2,18],25:[2,18]},{18:[2,44],21:56,24:[2,44],29:53,30:60,31:54,32:[1,57],33:[1,58],34:[1,59],35:55,36:61,37:62,38:[1,63],40:[1,27],41:26},{38:[1,64]},{18:[2,36],24:[2,36],32:[2,36],33:[2,36],34:[2,36],38:[2,36],40:[2,36]},{14:[2,17],15:[2,17],16:[2,17],19:[2,17],20:[2,17],22:[2,17],23:[2,17],25:[2,17]},{5:[2,20],14:[2,20],15:[2,20],16:[2,20],19:[2,20],20:[2,20],22:[2,20],23:[2,20],25:[2,20]},{5:[2,21],14:[2,21],15:[2,21],16:[2,21],19:[2,21],20:[2,21],22:[2,21],23:[2,21],25:[2,21]},{18:[1,65]},{18:[2,41]},{18:[1,66]},{8:17,9:5,11:6,12:7,13:8,14:[1,9],15:[1,10],16:[1,12],19:[1,11],20:[2,4],22:[1,13],23:[1,14],25:[1,15]},{18:[2,24],24:[2,24]},{18:[2,43],24:[2,43],32:[2,43],33:[2,43],34:[2,43],38:[2,43],40:[2,43]},{18:[2,45],24:[2,45]},{18:[2,26],24:[2,26],32:[2,26],33:[2,26],34:[2,26],38:[2,26],40:[2,26]},{18:[2,27],24:[2,27],32:[2,27],33:[2,27],34:[2,27],38:[2,27],40:[2,27]},{18:[2,28],24:[2,28],32:[2,28],33:[2,28],34:[2,28],38:[2,28],40:[2,28]},{18:[2,29],24:[2,29],32:[2,29],33:[2,29],34:[2,29],38:[2,29],40:[2,29]},{18:[2,30],24:[2,30],32:[2,30],33:[2,30],34:[2,30],38:[2,30],40:[2,30]},{18:[2,31],24:[2,31],37:67,38:[1,68]},{18:[2,46],24:[2,46],38:[2,46]},{18:[2,39],24:[2,39],32:[2,39],33:[2,39],34:[2,39],38:[2,39],39:[1,69],40:[2,39],42:[2,39]},{18:[2,38],24:[2,38],32:[2,38],33:[2,38],34:[2,38],38:[2,38],40:[2,38],42:[2,38]},{5:[2,22],14:[2,22],15:[2,22],16:[2,22],19:[2,22],20:[2,22],22:[2,22],23:[2,22],25:[2,22]},{5:[2,19],14:[2,19],15:[2,19],16:[2,19],19:[2,19],20:[2,19],22:[2,19],23:[2,19],25:[2,19]},{18:[2,47],24:[2,47],38:[2,47]},{39:[1,69]},{21:56,30:60,31:70,32:[1,57],33:[1,58],34:[1,59],38:[1,28],40:[1,27],41:26},{18:[2,32],24:[2,32],38:[2,32]}],
+ defaultActions: {3:[2,2],16:[2,1],50:[2,41]},
parseError: function parseError(str, hash) {
throw new Error(str);
},
@@ -1184,68 +1101,64 @@ var __module9__ = (function() {
break;
case 3:strip(0,4); this.popState(); return 15;
break;
- case 4:return 35;
+ case 4:return 25;
break;
- case 5:return 36;
+ case 5:return 16;
break;
- case 6:return 25;
+ case 6:return 20;
break;
- case 7:return 16;
+ case 7:return 19;
break;
- case 8:return 20;
+ case 8:return 19;
break;
- case 9:return 19;
+ case 9:return 23;
break;
- case 10:return 19;
+ case 10:return 22;
break;
- case 11:return 23;
+ case 11:this.popState(); this.begin('com');
break;
- case 12:return 22;
+ case 12:strip(3,5); this.popState(); return 15;
break;
- case 13:this.popState(); this.begin('com');
+ case 13:return 22;
break;
- case 14:strip(3,5); this.popState(); return 15;
+ case 14:return 39;
break;
- case 15:return 22;
+ case 15:return 38;
break;
- case 16:return 41;
+ case 16:return 38;
break;
- case 17:return 40;
+ case 17:return 42;
break;
- case 18:return 40;
+ case 18:// ignore whitespace
break;
- case 19:return 44;
+ case 19:this.popState(); return 24;
break;
- case 20:// ignore whitespace
+ case 20:this.popState(); return 18;
break;
- case 21:this.popState(); return 24;
+ case 21:yy_.yytext = strip(1,2).replace(/\\"/g,'"'); return 32;
break;
- case 22:this.popState(); return 18;
+ case 22:yy_.yytext = strip(1,2).replace(/\\'/g,"'"); return 32;
break;
- case 23:yy_.yytext = strip(1,2).replace(/\\"/g,'"'); return 32;
+ case 23:return 40;
break;
- case 24:yy_.yytext = strip(1,2).replace(/\\'/g,"'"); return 32;
+ case 24:return 34;
break;
- case 25:return 42;
+ case 25:return 34;
break;
- case 26:return 34;
+ case 26:return 33;
break;
- case 27:return 34;
+ case 27:return 38;
break;
- case 28:return 33;
+ case 28:yy_.yytext = strip(1,2); return 38;
break;
- case 29:return 40;
+ case 29:return 'INVALID';
break;
- case 30:yy_.yytext = strip(1,2); return 40;
- break;
- case 31:return 'INVALID';
- break;
- case 32:return 5;
+ case 30:return 5;
break;
}
};
- lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\()/,/^(?:\))/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.)])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s)])))/,/^(?:false(?=([~}\s)])))/,/^(?:-?[0-9]+(?=([~}\s)])))/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.)]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/];
- lexer.conditions = {"mu":{"rules":[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[3],"inclusive":false},"INITIAL":{"rules":[0,1,32],"inclusive":true}};
+ lexer.rules = [/^(?:[^\x00]*?(?=(\{\{)))/,/^(?:[^\x00]+)/,/^(?:[^\x00]{2,}?(?=(\{\{|\\\{\{|\\\\\{\{|$)))/,/^(?:[\s\S]*?--\}\})/,/^(?:\{\{(~)?>)/,/^(?:\{\{(~)?#)/,/^(?:\{\{(~)?\/)/,/^(?:\{\{(~)?\^)/,/^(?:\{\{(~)?\s*else\b)/,/^(?:\{\{(~)?\{)/,/^(?:\{\{(~)?&)/,/^(?:\{\{!--)/,/^(?:\{\{![\s\S]*?\}\})/,/^(?:\{\{(~)?)/,/^(?:=)/,/^(?:\.\.)/,/^(?:\.(?=([=~}\s\/.])))/,/^(?:[\/.])/,/^(?:\s+)/,/^(?:\}(~)?\}\})/,/^(?:(~)?\}\})/,/^(?:"(\\["]|[^"])*")/,/^(?:'(\\[']|[^'])*')/,/^(?:@)/,/^(?:true(?=([~}\s])))/,/^(?:false(?=([~}\s])))/,/^(?:-?[0-9]+(?=([~}\s])))/,/^(?:([^\s!"#%-,\.\/;->@\[-\^`\{-~]+(?=([=~}\s\/.]))))/,/^(?:\[[^\]]*\])/,/^(?:.)/,/^(?:$)/];
+ lexer.conditions = {"mu":{"rules":[4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30],"inclusive":false},"emu":{"rules":[2],"inclusive":false},"com":{"rules":[3],"inclusive":false},"INITIAL":{"rules":[0,1,30],"inclusive":true}};
return lexer;})()
parser.lexer = lexer;
function Parser () { this.yy = {}; }Parser.prototype = parser;parser.Parser = Parser;
@@ -1276,11 +1189,914 @@ var __module8__ = (function(__dependency1__, __dependency2__) {
return __exports__;
})(__module9__, __module7__);
+// handlebars/compiler/javascript-compiler.js
+var __module11__ = (function(__dependency1__) {
+ "use strict";
+ var __exports__;
+ var COMPILER_REVISION = __dependency1__.COMPILER_REVISION;
+ var REVISION_CHANGES = __dependency1__.REVISION_CHANGES;
+ var log = __dependency1__.log;
+
+ function Literal(value) {
+ this.value = value;
+ }
+
+ function JavaScriptCompiler() {}
+
+ JavaScriptCompiler.prototype = {
+ // PUBLIC API: You can override these methods in a subclass to provide
+ // alternative compiled forms for name lookup and buffering semantics
+ nameLookup: function(parent, name /* , type*/) {
+ var wrap,
+ ret;
+ if (parent.indexOf('depth') === 0) {
+ wrap = true;
+ }
+
+ if (/^[0-9]+$/.test(name)) {
+ ret = parent + "[" + name + "]";
+ } else if (JavaScriptCompiler.isValidJavaScriptVariableName(name)) {
+ ret = parent + "." + name;
+ }
+ else {
+ ret = parent + "['" + name + "']";
+ }
+
+ if (wrap) {
+ return '(' + parent + ' && ' + ret + ')';
+ } else {
+ return ret;
+ }
+ },
+
+ compilerInfo: function() {
+ var revision = COMPILER_REVISION,
+ versions = REVISION_CHANGES[revision];
+ return "this.compilerInfo = ["+revision+",'"+versions+"'];\n";
+ },
+
+ appendToBuffer: function(string) {
+ if (this.environment.isSimple) {
+ return "return " + string + ";";
+ } else {
+ return {
+ appendToBuffer: true,
+ content: string,
+ toString: function() { return "buffer += " + string + ";"; }
+ };
+ }
+ },
+
+ initializeBuffer: function() {
+ return this.quotedString("");
+ },
+
+ namespace: "Handlebars",
+ // END PUBLIC API
+
+ compile: function(environment, options, context, asObject) {
+ this.environment = environment;
+ this.options = options || {};
+
+ log('debug', this.environment.disassemble() + "\n\n");
+
+ this.name = this.environment.name;
+ this.isChild = !!context;
+ this.context = context || {
+ programs: [],
+ environments: [],
+ aliases: { }
+ };
+
+ this.preamble();
+
+ this.stackSlot = 0;
+ this.stackVars = [];
+ this.registers = { list: [] };
+ this.compileStack = [];
+ this.inlineStack = [];
+
+ this.compileChildren(environment, options);
+
+ var opcodes = environment.opcodes, opcode;
+
+ this.i = 0;
+
+ for(var l=opcodes.length; this.i 0) {
+ this.source[1] = this.source[1] + ", " + locals.join(", ");
+ }
+
+ // Generate minimizer alias mappings
+ if (!this.isChild) {
+ for (var alias in this.context.aliases) {
+ if (this.context.aliases.hasOwnProperty(alias)) {
+ this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
+ }
+ }
+ }
+
+ if (this.source[1]) {
+ this.source[1] = "var " + this.source[1].substring(2) + ";";
+ }
+
+ // Merge children
+ if (!this.isChild) {
+ this.source[1] += '\n' + this.context.programs.join('\n') + '\n';
+ }
+
+ if (!this.environment.isSimple) {
+ this.pushSource("return buffer;");
+ }
+
+ var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"];
+
+ for(var i=0, l=this.environment.depths.list.length; i this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
+ return this.topStackName();
+ },
+ topStackName: function() {
+ return "stack" + this.stackSlot;
+ },
+ flushInline: function() {
+ var inlineStack = this.inlineStack;
+ if (inlineStack.length) {
+ this.inlineStack = [];
+ for (var i = 0, len = inlineStack.length; i < len; i++) {
+ var entry = inlineStack[i];
+ if (entry instanceof Literal) {
+ this.compileStack.push(entry);
+ } else {
+ this.pushStack(entry);
+ }
+ }
+ }
+ },
+ isInline: function() {
+ return this.inlineStack.length;
+ },
+
+ popStack: function(wrapped) {
+ var inline = this.isInline(),
+ item = (inline ? this.inlineStack : this.compileStack).pop();
+
+ if (!wrapped && (item instanceof Literal)) {
+ return item.value;
+ } else {
+ if (!inline) {
+ this.stackSlot--;
+ }
+ return item;
+ }
+ },
+
+ topStack: function(wrapped) {
+ var stack = (this.isInline() ? this.inlineStack : this.compileStack),
+ item = stack[stack.length - 1];
+
+ if (!wrapped && (item instanceof Literal)) {
+ return item.value;
+ } else {
+ return item;
+ }
+ },
+
+ quotedString: function(str) {
+ return '"' + str
+ .replace(/\\/g, '\\\\')
+ .replace(/"/g, '\\"')
+ .replace(/\n/g, '\\n')
+ .replace(/\r/g, '\\r')
+ .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4
+ .replace(/\u2029/g, '\\u2029') + '"';
+ },
+
+ setupHelper: function(paramSize, name, missingParams) {
+ var params = [];
+ this.setupParams(paramSize, params, missingParams);
+ var foundHelper = this.nameLookup('helpers', name, 'helper');
+
+ return {
+ params: params,
+ name: foundHelper,
+ callParams: ["depth0"].concat(params).join(", "),
+ helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ")
+ };
+ },
+
+ // the params and contexts arguments are passed in arrays
+ // to fill in
+ setupParams: function(paramSize, params, useRegister) {
+ var options = [], contexts = [], types = [], param, inverse, program;
+
+ options.push("hash:" + this.popStack());
+
+ inverse = this.popStack();
+ program = this.popStack();
+
+ // Avoid setting fn and inverse if neither are set. This allows
+ // helpers to do a check for `if (options.fn)`
+ if (program || inverse) {
+ if (!program) {
+ this.context.aliases.self = "this";
+ program = "self.noop";
+ }
+
+ if (!inverse) {
+ this.context.aliases.self = "this";
+ inverse = "self.noop";
+ }
+
+ options.push("inverse:" + inverse);
+ options.push("fn:" + program);
+ }
+
+ for(var i=0; i 0) {
- this.source[1] = this.source[1] + ", " + locals.join(", ");
- }
-
- // Generate minimizer alias mappings
- if (!this.isChild) {
- for (var alias in this.context.aliases) {
- if (this.context.aliases.hasOwnProperty(alias)) {
- this.source[1] = this.source[1] + ', ' + alias + '=' + this.context.aliases[alias];
- }
- }
- }
-
- if (this.source[1]) {
- this.source[1] = "var " + this.source[1].substring(2) + ";";
- }
-
- // Merge children
- if (!this.isChild) {
- this.source[1] += '\n' + this.context.programs.join('\n') + '\n';
- }
-
- if (!this.environment.isSimple) {
- this.pushSource("return buffer;");
- }
-
- var params = this.isChild ? ["depth0", "data"] : ["Handlebars", "depth0", "helpers", "partials", "data"];
-
- for(var i=0, l=this.environment.depths.list.length; i this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
- return this.topStackName();
- },
- topStackName: function() {
- return "stack" + this.stackSlot;
- },
- flushInline: function() {
- var inlineStack = this.inlineStack;
- if (inlineStack.length) {
- this.inlineStack = [];
- for (var i = 0, len = inlineStack.length; i < len; i++) {
- var entry = inlineStack[i];
- if (entry instanceof Literal) {
- this.compileStack.push(entry);
- } else {
- this.pushStack(entry);
- }
- }
- }
- },
- isInline: function() {
- return this.inlineStack.length;
- },
-
- popStack: function(wrapped) {
- var inline = this.isInline(),
- item = (inline ? this.inlineStack : this.compileStack).pop();
-
- if (!wrapped && (item instanceof Literal)) {
- return item.value;
- } else {
- if (!inline) {
- if (!this.stackSlot) {
- throw new Exception('Invalid stack pop');
- }
- this.stackSlot--;
- }
- return item;
- }
- },
-
- topStack: function(wrapped) {
- var stack = (this.isInline() ? this.inlineStack : this.compileStack),
- item = stack[stack.length - 1];
-
- if (!wrapped && (item instanceof Literal)) {
- return item.value;
- } else {
- return item;
- }
- },
-
- quotedString: function(str) {
- return '"' + str
- .replace(/\\/g, '\\\\')
- .replace(/"/g, '\\"')
- .replace(/\n/g, '\\n')
- .replace(/\r/g, '\\r')
- .replace(/\u2028/g, '\\u2028') // Per Ecma-262 7.3 + 7.8.4
- .replace(/\u2029/g, '\\u2029') + '"';
- },
-
- setupHelper: function(paramSize, name, missingParams) {
- var params = [],
- paramsInit = this.setupParams(paramSize, params, missingParams);
- var foundHelper = this.nameLookup('helpers', name, 'helper');
-
- return {
- params: params,
- paramsInit: paramsInit,
- name: foundHelper,
- callParams: ["depth0"].concat(params).join(", "),
- helperMissingParams: missingParams && ["depth0", this.quotedString(name)].concat(params).join(", ")
- };
- },
-
- setupOptions: function(paramSize, params) {
- var options = [], contexts = [], types = [], param, inverse, program;
-
- options.push("hash:" + this.popStack());
-
- if (this.options.stringParams) {
- options.push("hashTypes:" + this.popStack());
- options.push("hashContexts:" + this.popStack());
- }
-
- inverse = this.popStack();
- program = this.popStack();
-
- // Avoid setting fn and inverse if neither are set. This allows
- // helpers to do a check for `if (options.fn)`
- if (program || inverse) {
- if (!program) {
- this.context.aliases.self = "this";
- program = "self.noop";
- }
-
- if (!inverse) {
- this.context.aliases.self = "this";
- inverse = "self.noop";
- }
-
- options.push("inverse:" + inverse);
- options.push("fn:" + program);
- }
-
- for(var i=0; i