ember.js • TodoMVC
-
+
+
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/examples/emberjs/bower_components/ember/ember.js b/examples/emberjs/node_modules/components-ember/ember.js
similarity index 61%
rename from examples/emberjs/bower_components/ember/ember.js
rename to examples/emberjs/node_modules/components-ember/ember.js
index 647ee01560..627610b21d 100644
--- a/examples/emberjs/bower_components/ember/ember.js
+++ b/examples/emberjs/node_modules/components-ember/ember.js
@@ -5,20280 +5,26903 @@
* 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.6.1
+ * @version 1.10.0-beta.3
*/
-
(function() {
-var define, requireModule, require, requirejs, Ember;
+var enifed, requireModule, eriuqer, requirejs, Ember;
(function() {
Ember = this.Ember = this.Ember || {};
- if (typeof Ember === 'undefined') { Ember = {} };
+ if (typeof Ember === 'undefined') { Ember = {}; };
+ function UNDEFINED() { }
if (typeof Ember.__loader === 'undefined') {
var registry = {}, seen = {};
- define = function(name, deps, callback) {
+ enifed = function(name, deps, callback) {
registry[name] = { deps: deps, callback: callback };
};
- requirejs = require = requireModule = function(name) {
- if (seen.hasOwnProperty(name)) { return seen[name]; }
+ requirejs = eriuqer = requireModule = function(name) {
+ var s = seen[name];
+
+ if (s !== undefined) { return seen[name]; }
+ if (s === UNDEFINED) { return undefined; }
+
seen[name] = {};
if (!registry[name]) {
- throw new Error("Could not find module " + name);
+ throw new Error('Could not find module ' + name);
}
- var mod = registry[name],
- deps = mod.deps,
- callback = mod.callback,
- reified = [],
- exports;
+ var mod = registry[name];
+ var deps = mod.deps;
+ var callback = mod.callback;
+ var reified = [];
+ var exports;
+ var length = deps.length;
- for (var i=0, l=deps.length; i\s*\(([^\)]+)\)/gm, '{anonymous}($1)').split('\n');
- stack.shift();
+ if (onError) {
+ try {
+ return method.apply(target, args);
+ } catch(error) {
+ onError(error);
+ } finally {
+ if (!didFinally) {
+ didFinally = true;
+ this.end();
+ }
+ }
} else {
- // Firefox
- stack = error.stack.replace(/(?:\n@:0)?\s+$/m, '').
- replace(/^\(/gm, '{anonymous}(').split('\n');
+ try {
+ return method.apply(target, args);
+ } finally {
+ if (!didFinally) {
+ didFinally = true;
+ this.end();
+ }
+ }
}
+ },
- stackStr = "\n " + stack.slice(2).join("\n ");
- message = message + stackStr;
- }
+ join: function(target, method /*, args */) {
+ if (this.currentInstance) {
+ if (!method) {
+ method = target;
+ target = null;
+ }
- Logger.warn("DEPRECATION: "+message);
- };
+ if (isString(method)) {
+ method = target[method];
+ }
+ return method.apply(target, slice.call(arguments, 2));
+ } else {
+ return this.run.apply(this, arguments);
+ }
+ },
+ defer: function(queueName, target, method /* , args */) {
+ if (!method) {
+ method = target;
+ target = null;
+ }
- /**
- Alias an old, deprecated method with its new counterpart.
+ if (isString(method)) {
+ method = target[method];
+ }
- Display a deprecation warning with the provided message and a stack trace
- (Chrome and Firefox only) when the assigned method is called.
+ var stack = this.DEBUG ? new Error() : undefined;
+ var length = arguments.length;
+ var args;
- Ember build tools will not remove calls to `Ember.deprecateFunc()`, though
- no warnings will be shown in production.
+ if (length > 3) {
+ args = new Array(length - 3);
+ for (var i = 3; i < length; i++) {
+ args[i-3] = arguments[i];
+ }
+ } else {
+ args = undefined;
+ }
- ```javascript
- Ember.oldMethod = Ember.deprecateFunc('Please use the new, updated method', Ember.newMethod);
- ```
+ if (!this.currentInstance) { createAutorun(this); }
+ return this.currentInstance.schedule(queueName, target, method, args, false, stack);
+ },
- @method deprecateFunc
- @param {String} message A description of the deprecation.
- @param {Function} func The new function called to replace its deprecated counterpart.
- @return {Function} a new function that wrapped the original function with a deprecation warning
- */
- Ember.deprecateFunc = function(message, func) {
- return function() {
- Ember.deprecate(message);
- return func.apply(this, arguments);
- };
- };
+ deferOnce: function(queueName, target, method /* , args */) {
+ if (!method) {
+ method = target;
+ target = null;
+ }
+ if (isString(method)) {
+ method = target[method];
+ }
- /**
- Run a function meant for debugging. Ember build tools will remove any calls to
- `Ember.runInDebug()` when doing a production build.
+ var stack = this.DEBUG ? new Error() : undefined;
+ var length = arguments.length;
+ var args;
- ```javascript
- Ember.runInDebug(function() {
- Ember.Handlebars.EachView.reopen({
- didInsertElement: function() {
- console.log('I\'m happy');
+ if (length > 3) {
+ args = new Array(length - 3);
+ for (var i = 3; i < length; i++) {
+ args[i-3] = arguments[i];
}
- });
- });
- ```
+ } else {
+ args = undefined;
+ }
- @method runInDebug
- @param {Function} func The function to be executed.
- @since 1.5.0
- */
- Ember.runInDebug = function(func) {
- func()
- };
+ if (!this.currentInstance) {
+ createAutorun(this);
+ }
+ return this.currentInstance.schedule(queueName, target, method, args, true, stack);
+ },
- // Inform the developer about the Ember Inspector if not installed.
- if (!Ember.testing) {
- var isFirefox = typeof InstallTrigger !== 'undefined';
- var isChrome = !!window.chrome && !window.opera;
+ setTimeout: function() {
+ var l = arguments.length;
+ var args = new Array(l);
- if (typeof window !== 'undefined' && (isFirefox || isChrome) && window.addEventListener) {
- window.addEventListener("load", function() {
- if (document.documentElement && document.documentElement.dataset && !document.documentElement.dataset.emberExtension) {
- var downloadURL;
+ for (var x = 0; x < l; x++) {
+ args[x] = arguments[x];
+ }
- 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/';
- }
+ var length = args.length,
+ method, wait, target,
+ methodOrTarget, methodOrWait, methodOrArgs;
- Ember.debug('For more advanced debugging, install the Ember Inspector from ' + downloadURL);
- }
- }, false);
- }
- }
- });
-})();
+ if (length === 0) {
+ return;
+ } else if (length === 1) {
+ method = args.shift();
+ wait = 0;
+ } else if (length === 2) {
+ methodOrTarget = args[0];
+ methodOrWait = args[1];
-(function() {
-define("ember-metal/array",
- ["exports"],
- function(__exports__) {
- "use strict";
- /*jshint newcap:false*/
- /**
- @module ember-metal
- */
+ if (isFunction(methodOrWait) || isFunction(methodOrTarget[methodOrWait])) {
+ target = args.shift();
+ method = args.shift();
+ wait = 0;
+ } else if (isCoercableNumber(methodOrWait)) {
+ method = args.shift();
+ wait = args.shift();
+ } else {
+ method = args.shift();
+ wait = 0;
+ }
+ } else {
+ var last = args[args.length - 1];
- var ArrayPrototype = Array.prototype;
+ if (isCoercableNumber(last)) {
+ wait = args.pop();
+ } else {
+ wait = 0;
+ }
- // NOTE: There is a bug in jshint that doesn't recognize `Object()` without `new`
- // as being ok unless both `newcap:false` and not `use strict`.
- // https://github.com/jshint/jshint/issues/392
+ methodOrTarget = args[0];
+ methodOrArgs = args[1];
- // Testing this is not ideal, but we want to use native functions
- // if available, but not to use versions created by libraries like Prototype
- var isNativeFunc = function(func) {
- // This should probably work in all browsers likely to have ES5 array methods
- return func && Function.prototype.toString.call(func).indexOf('[native code]') > -1;
- };
+ if (isFunction(methodOrArgs) || (isString(methodOrArgs) &&
+ methodOrTarget !== null &&
+ methodOrArgs in methodOrTarget)) {
+ target = args.shift();
+ method = args.shift();
+ } else {
+ method = args.shift();
+ }
+ }
- // From: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/map
- var map = isNativeFunc(ArrayPrototype.map) ? ArrayPrototype.map : function(fun /*, thisp */) {
- //"use strict";
+ var executeAt = now() + parseInt(wait, 10);
- if (this === void 0 || this === null) {
- throw new TypeError();
- }
+ if (isString(method)) {
+ method = target[method];
+ }
- var t = Object(this);
- var len = t.length >>> 0;
- if (typeof fun !== "function") {
- throw new TypeError();
- }
+ var onError = getOnError(this.options);
- var res = new Array(len);
- var thisp = arguments[1];
- for (var i = 0; i < len; i++) {
- if (i in t) {
- res[i] = fun.call(thisp, t[i], i, t);
+ function fn() {
+ if (onError) {
+ try {
+ method.apply(target, args);
+ } catch (e) {
+ onError(e);
+ }
+ } else {
+ method.apply(target, args);
+ }
}
- }
- return res;
- };
+ // find position to insert
+ var i = searchTimer(executeAt, this._timers);
- // From: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/foreach
- var forEach = isNativeFunc(ArrayPrototype.forEach) ? ArrayPrototype.forEach : function(fun /*, thisp */) {
- //"use strict";
+ this._timers.splice(i, 0, executeAt, fn);
- if (this === void 0 || this === null) {
- throw new TypeError();
- }
+ updateLaterTimer(this, executeAt, wait);
- var t = Object(this);
- var len = t.length >>> 0;
- if (typeof fun !== "function") {
- throw new TypeError();
- }
+ return fn;
+ },
- var thisp = arguments[1];
- for (var i = 0; i < len; i++) {
- if (i in t) {
- fun.call(thisp, t[i], i, t);
+ throttle: function(target, method /* , args, wait, [immediate] */) {
+ var backburner = this;
+ var args = arguments;
+ var immediate = pop.call(args);
+ var wait, throttler, index, timer;
+
+ if (isNumber(immediate) || isString(immediate)) {
+ wait = immediate;
+ immediate = true;
+ } else {
+ wait = pop.call(args);
}
- }
- };
- var indexOf = isNativeFunc(ArrayPrototype.indexOf) ? ArrayPrototype.indexOf : function (obj, fromIndex) {
- if (fromIndex === null || fromIndex === undefined) { fromIndex = 0; }
- else if (fromIndex < 0) { fromIndex = Math.max(0, this.length + fromIndex); }
- for (var i = fromIndex, j = this.length; i < j; i++) {
- if (this[i] === obj) { return i; }
- }
- return -1;
- };
+ wait = parseInt(wait, 10);
- var filter = isNativeFunc(ArrayPrototype.filter) ? ArrayPrototype.filter : function (fn, context) {
- var i,
- value,
- result = [],
- length = this.length;
+ index = findThrottler(target, method, this._throttlers);
+ if (index > -1) { return this._throttlers[index]; } // throttled
- for (i = 0; i < length; i++) {
- if (this.hasOwnProperty(i)) {
- value = this[i];
- if (fn.call(context, value, i, this)) {
- result.push(value);
+ timer = global.setTimeout(function() {
+ if (!immediate) {
+ backburner.run.apply(backburner, args);
+ }
+ var index = findThrottler(target, method, backburner._throttlers);
+ if (index > -1) {
+ backburner._throttlers.splice(index, 1);
}
+ }, wait);
+
+ if (immediate) {
+ this.run.apply(this, args);
}
- }
- return result;
- };
+ throttler = [target, method, timer];
- if (Ember.SHIM_ES5) {
- if (!ArrayPrototype.map) {
- ArrayPrototype.map = map;
- }
+ this._throttlers.push(throttler);
- if (!ArrayPrototype.forEach) {
- ArrayPrototype.forEach = forEach;
- }
+ return throttler;
+ },
- if (!ArrayPrototype.filter) {
- ArrayPrototype.filter = filter;
- }
+ debounce: function(target, method /* , args, wait, [immediate] */) {
+ var backburner = this;
+ var args = arguments;
+ var immediate = pop.call(args);
+ var wait, index, debouncee, timer;
- if (!ArrayPrototype.indexOf) {
- ArrayPrototype.indexOf = indexOf;
- }
- }
+ if (isNumber(immediate) || isString(immediate)) {
+ wait = immediate;
+ immediate = false;
+ } else {
+ wait = pop.call(args);
+ }
- /**
- Array polyfills to support ES5 features in older browsers.
+ wait = parseInt(wait, 10);
+ // Remove debouncee
+ index = findDebouncee(target, method, this._debouncees);
- @namespace Ember
- @property ArrayPolyfills
- */
- __exports__.map = map;
- __exports__.forEach = forEach;
- __exports__.filter = filter;
- __exports__.indexOf = indexOf;
- });
-define("ember-metal/binding",
- ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/map","ember-metal/observer","ember-metal/run_loop","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) {
- "use strict";
- var Ember = __dependency1__["default"];
- // Ember.Logger, Ember.LOG_BINDINGS, assert
- var get = __dependency2__.get;
- var set = __dependency3__.set;
- var trySet = __dependency3__.trySet;
- var guidFor = __dependency4__.guidFor;
- var Map = __dependency5__.Map;
- var addObserver = __dependency6__.addObserver;
- var removeObserver = __dependency6__.removeObserver;
- var _suspendObserver = __dependency6__._suspendObserver;
- var run = __dependency7__["default"];
+ if (index > -1) {
+ debouncee = this._debouncees[index];
+ this._debouncees.splice(index, 1);
+ clearTimeout(debouncee[2]);
+ }
- // ES6TODO: where is Ember.lookup defined?
- /**
- @module ember-metal
- */
+ timer = global.setTimeout(function() {
+ if (!immediate) {
+ backburner.run.apply(backburner, args);
+ }
+ var index = findDebouncee(target, method, backburner._debouncees);
+ if (index > -1) {
+ backburner._debouncees.splice(index, 1);
+ }
+ }, wait);
- // ..........................................................
- // CONSTANTS
- //
+ if (immediate && index === -1) {
+ backburner.run.apply(backburner, args);
+ }
- /**
- Debug parameter you can turn on. This will log all bindings that fire to
- the console. This should be disabled in production code. Note that you
- can also enable this from the console or temporarily.
+ debouncee = [
+ target,
+ method,
+ timer
+ ];
- @property LOG_BINDINGS
- @for Ember
- @type Boolean
- @default false
- */
- Ember.LOG_BINDINGS = false || !!Ember.ENV.LOG_BINDINGS;
+ backburner._debouncees.push(debouncee);
- var IS_GLOBAL = /^([A-Z$]|([0-9][A-Z$]))/;
+ return debouncee;
+ },
- /**
- Returns true if the provided path is global (e.g., `MyApp.fooController.bar`)
- instead of local (`foo.bar.baz`).
+ cancelTimers: function() {
+ var clearItems = function(item) {
+ clearTimeout(item[2]);
+ };
- @method isGlobalPath
- @for Ember
- @private
- @param {String} path
- @return Boolean
- */
- function isGlobalPath(path) {
- return IS_GLOBAL.test(path);
- };
+ each(this._throttlers, clearItems);
+ this._throttlers = [];
- function getWithGlobals(obj, path) {
- return get(isGlobalPath(path) ? Ember.lookup : obj, path);
- }
+ each(this._debouncees, clearItems);
+ this._debouncees = [];
- // ..........................................................
- // BINDING
- //
+ if (this._laterTimer) {
+ clearTimeout(this._laterTimer);
+ this._laterTimer = null;
+ }
+ this._timers = [];
- var Binding = function(toPath, fromPath) {
- this._direction = 'fwd';
- this._from = fromPath;
- this._to = toPath;
- this._directionMap = Map.create();
- };
+ if (this._autorun) {
+ clearTimeout(this._autorun);
+ this._autorun = null;
+ }
+ },
- /**
- @class Binding
- @namespace Ember
- */
+ hasTimers: function() {
+ return !!this._timers.length || !!this._debouncees.length || !!this._throttlers.length || this._autorun;
+ },
- Binding.prototype = {
- /**
- This copies the Binding so it can be connected to another object.
+ cancel: function(timer) {
+ var timerType = typeof timer;
- @method copy
- @return {Ember.Binding} `this`
- */
- copy: function () {
- var copy = new Binding(this._to, this._from);
- if (this._oneWay) { copy._oneWay = true; }
- return copy;
+ if (timer && timerType === 'object' && timer.queue && timer.method) { // we're cancelling a deferOnce
+ return timer.queue.cancel(timer);
+ } else if (timerType === 'function') { // we're cancelling a setTimeout
+ for (var i = 0, l = this._timers.length; i < l; i += 2) {
+ if (this._timers[i + 1] === timer) {
+ this._timers.splice(i, 2); // remove the two elements
+ if (i === 0) {
+ if (this._laterTimer) { // Active timer? Then clear timer and reset for future timer
+ clearTimeout(this._laterTimer);
+ this._laterTimer = null;
+ }
+ if (this._timers.length > 0) { // Update to next available timer when available
+ updateLaterTimer(this, this._timers[0], this._timers[0] - now());
+ }
+ }
+ return true;
+ }
+ }
+ } else if (Object.prototype.toString.call(timer) === "[object Array]"){ // we're cancelling a throttle or debounce
+ return this._cancelItem(findThrottler, this._throttlers, timer) ||
+ this._cancelItem(findDebouncee, this._debouncees, timer);
+ } else {
+ return; // timer was null or not a timer
+ }
},
- // ..........................................................
- // CONFIG
- //
+ _cancelItem: function(findMethod, array, timer){
+ var item, index;
- /**
- This will set `from` property path to the specified value. It will not
- attempt to resolve this property path to an actual object until you
- connect the binding.
+ if (timer.length < 3) { return false; }
- The binding will search for the property path starting at the root object
- you pass when you `connect()` the binding. It follows the same rules as
- `get()` - see that method for more information.
+ index = findMethod(timer[0], timer[1], array);
- @method from
- @param {String} path the property path to connect to
- @return {Ember.Binding} `this`
- */
- from: function(path) {
- this._from = path;
- return this;
- },
+ if (index > -1) {
- /**
- This will set the `to` property path to the specified value. It will not
- attempt to resolve this property path to an actual object until you
- connect the binding.
+ item = array[index];
- The binding will search for the property path starting at the root object
- you pass when you `connect()` the binding. It follows the same rules as
- `get()` - see that method for more information.
+ if (item[2] === timer[2]) {
+ array.splice(index, 1);
+ clearTimeout(timer[2]);
+ return true;
+ }
+ }
- @method to
- @param {String|Tuple} path A property path or tuple
- @return {Ember.Binding} `this`
- */
- to: function(path) {
- this._to = path;
- return this;
- },
+ return false;
+ }
+ };
- /**
- Configures the binding as one way. A one-way binding will relay changes
- on the `from` side to the `to` side, but not the other way around. This
- means that if you change the `to` side directly, the `from` side may have
- a different value.
+ Backburner.prototype.schedule = Backburner.prototype.defer;
+ Backburner.prototype.scheduleOnce = Backburner.prototype.deferOnce;
+ Backburner.prototype.later = Backburner.prototype.setTimeout;
- @method oneWay
- @return {Ember.Binding} `this`
- */
- oneWay: function() {
- this._oneWay = true;
- return this;
- },
+ if (needsIETryCatchFix) {
+ var originalRun = Backburner.prototype.run;
+ Backburner.prototype.run = wrapInTryCatch(originalRun);
- /**
- @method toString
- @return {String} string representation of binding
- */
- toString: function() {
- var oneWay = this._oneWay ? '[oneWay]' : '';
- return "Ember.Binding<" + guidFor(this) + ">(" + this._from + " -> " + this._to + ")" + oneWay;
- },
+ var originalEnd = Backburner.prototype.end;
+ Backburner.prototype.end = wrapInTryCatch(originalEnd);
+ }
- // ..........................................................
- // CONNECT AND SYNC
- //
+ function getOnError(options) {
+ return options.onError || (options.onErrorTarget && options.onErrorTarget[options.onErrorMethod]);
+ }
- /**
- Attempts to connect this binding instance so that it can receive and relay
- changes. This method will raise an exception if you have not set the
- from/to properties yet.
+ function createAutorun(backburner) {
+ backburner.begin();
+ backburner._autorun = global.setTimeout(function() {
+ backburner._autorun = null;
+ backburner.end();
+ });
+ }
- @method connect
- @param {Object} obj The root object for this binding.
- @return {Ember.Binding} `this`
- */
- connect: function(obj) {
- Ember.assert('Must pass a valid object to Ember.Binding.connect()', !!obj);
+ function updateLaterTimer(backburner, executeAt, wait) {
+ var n = now();
+ if (!backburner._laterTimer || executeAt < backburner._laterTimerExpiresAt || backburner._laterTimerExpiresAt < n) {
- var fromPath = this._from, toPath = this._to;
- trySet(obj, toPath, getWithGlobals(obj, fromPath));
+ if (backburner._laterTimer) {
+ // Clear when:
+ // - Already expired
+ // - New timer is earlier
+ clearTimeout(backburner._laterTimer);
- // add an observer on the object to be notified when the binding should be updated
- addObserver(obj, fromPath, this, this.fromDidChange);
+ if (backburner._laterTimerExpiresAt < n) { // If timer was never triggered
+ // Calculate the left-over wait-time
+ wait = Math.max(0, executeAt - n);
+ }
+ }
- // if the binding is a two-way binding, also set up an observer on the target
- if (!this._oneWay) { addObserver(obj, toPath, this, this.toDidChange); }
+ backburner._laterTimer = global.setTimeout(function() {
+ backburner._laterTimer = null;
+ backburner._laterTimerExpiresAt = null;
+ executeTimers(backburner);
+ }, wait);
- this._readyToSync = true;
+ backburner._laterTimerExpiresAt = n + wait;
+ }
+ }
- return this;
- },
+ function executeTimers(backburner) {
+ var n = now();
+ var fns, i, l;
- /**
- Disconnects the binding instance. Changes will no longer be relayed. You
- will not usually need to call this method.
+ backburner.run(function() {
+ i = searchTimer(n, backburner._timers);
- @method disconnect
- @param {Object} obj The root object you passed when connecting the binding.
- @return {Ember.Binding} `this`
- */
- disconnect: function(obj) {
- Ember.assert('Must pass a valid object to Ember.Binding.disconnect()', !!obj);
+ fns = backburner._timers.splice(0, i);
- var twoWay = !this._oneWay;
+ for (i = 1, l = fns.length; i < l; i += 2) {
+ backburner.schedule(backburner.options.defaultQueue, null, fns[i]);
+ }
+ });
- // remove an observer on the object so we're no longer notified of
- // changes that should update bindings.
- removeObserver(obj, this._from, this, this.fromDidChange);
+ if (backburner._timers.length) {
+ updateLaterTimer(backburner, backburner._timers[0], backburner._timers[0] - n);
+ }
+ }
- // if the binding is two-way, remove the observer from the target as well
- if (twoWay) { removeObserver(obj, this._to, this, this.toDidChange); }
+ function findDebouncee(target, method, debouncees) {
+ return findItem(target, method, debouncees);
+ }
- this._readyToSync = false; // disable scheduled syncs...
- return this;
- },
+ function findThrottler(target, method, throttlers) {
+ return findItem(target, method, throttlers);
+ }
- // ..........................................................
- // PRIVATE
- //
+ function findItem(target, method, collection) {
+ var item;
+ var index = -1;
- /* called when the from side changes */
- fromDidChange: function(target) {
- this._scheduleSync(target, 'fwd');
- },
+ for (var i = 0, l = collection.length; i < l; i++) {
+ item = collection[i];
+ if (item[0] === target && item[1] === method) {
+ index = i;
+ break;
+ }
+ }
- /* called when the to side changes */
- toDidChange: function(target) {
- this._scheduleSync(target, 'back');
- },
+ return index;
+ }
- _scheduleSync: function(obj, dir) {
- var directionMap = this._directionMap;
- var existingDir = directionMap.get(obj);
+ __exports__["default"] = Backburner;
+ });
+enifed("backburner.umd",
+ ["./backburner"],
+ function(__dependency1__) {
+ "use strict";
+ var Backburner = __dependency1__["default"];
- // if we haven't scheduled the binding yet, schedule it
- if (!existingDir) {
- run.schedule('sync', this, this._sync, obj);
- directionMap.set(obj, dir);
- }
+ /* global define:true module:true window: true */
+ if (typeof enifed === 'function' && enifed.amd) {
+ enifed(function() { return Backburner; });
+ } else if (typeof module !== 'undefined' && module.exports) {
+ module.exports = Backburner;
+ } else if (typeof this !== 'undefined') {
+ this['Backburner'] = Backburner;
+ }
+ });
+enifed("backburner/binary-search",
+ ["exports"],
+ function(__exports__) {
+ "use strict";
+ __exports__["default"] = function binarySearch(time, timers) {
+ var start = 0;
+ var end = timers.length - 2;
+ var middle, l;
- // If both a 'back' and 'fwd' sync have been scheduled on the same object,
- // default to a 'fwd' sync so that it remains deterministic.
- if (existingDir === 'back' && dir === 'fwd') {
- directionMap.set(obj, 'fwd');
+ while (start < end) {
+ // since timers is an array of pairs 'l' will always
+ // be an integer
+ l = (end - start) / 2;
+
+ // compensate for the index in case even number
+ // of pairs inside timers
+ middle = start + l - (l % 2);
+
+ if (time >= timers[middle]) {
+ start = middle + 2;
+ } else {
+ end = middle;
}
- },
+ }
- _sync: function(obj) {
- var log = Ember.LOG_BINDINGS;
+ return (time >= timers[start]) ? start + 2 : start;
+ }
+ });
+enifed("backburner/deferred-action-queues",
+ ["./utils","./queue","exports"],
+ function(__dependency1__, __dependency2__, __exports__) {
+ "use strict";
+ var each = __dependency1__.each;
+ var Queue = __dependency2__["default"];
- // don't synchronize destroyed objects or disconnected bindings
- if (obj.isDestroyed || !this._readyToSync) { return; }
+ function DeferredActionQueues(queueNames, options) {
+ var queues = this.queues = Object.create(null);
+ this.queueNames = queueNames = queueNames || [];
- // get the direction of the binding for the object we are
- // synchronizing from
- var directionMap = this._directionMap;
- var direction = directionMap.get(obj);
+ this.options = options;
+
+ each(queueNames, function(queueName) {
+ queues[queueName] = new Queue(queueName, options[queueName], options);
+ });
+ }
- var fromPath = this._from, toPath = this._to;
+ function noSuchQueue(name) {
+ throw new Error("You attempted to schedule an action in a queue (" + name + ") that doesn't exist");
+ }
- directionMap.remove(obj);
+ DeferredActionQueues.prototype = {
+ schedule: function(name, target, method, args, onceFlag, stack) {
+ var queues = this.queues;
+ var queue = queues[name];
- // if we're synchronizing from the remote object...
- if (direction === 'fwd') {
- var fromValue = getWithGlobals(obj, this._from);
- if (log) {
- Ember.Logger.log(' ', this.toString(), '->', fromValue, obj);
- }
- if (this._oneWay) {
- trySet(obj, toPath, fromValue);
+ if (!queue) {
+ noSuchQueue(name);
+ }
+
+ if (onceFlag) {
+ return queue.pushUnique(target, method, args, stack);
+ } else {
+ return queue.push(target, method, args, stack);
+ }
+ },
+
+ flush: function() {
+ var queues = this.queues;
+ var queueNames = this.queueNames;
+ var queueName, queue, queueItems, priorQueueNameIndex;
+ var queueNameIndex = 0;
+ var numberOfQueues = queueNames.length;
+ var options = this.options;
+
+ while (queueNameIndex < numberOfQueues) {
+ queueName = queueNames[queueNameIndex];
+ queue = queues[queueName];
+
+ var numberOfQueueItems = queue._queue.length;
+
+ if (numberOfQueueItems === 0) {
+ queueNameIndex++;
} else {
- _suspendObserver(obj, toPath, this, this.toDidChange, function () {
- trySet(obj, toPath, fromValue);
- });
+ queue.flush(false /* async */);
+ queueNameIndex = 0;
}
- // if we're synchronizing *to* the remote object
- } else if (direction === 'back') {
- var toValue = get(obj, this._to);
- if (log) {
- Ember.Logger.log(' ', this.toString(), '<-', toValue, obj);
- }
- _suspendObserver(obj, fromPath, this, this.fromDidChange, function () {
- trySet(isGlobalPath(fromPath) ? Ember.lookup : obj, fromPath, toValue);
- });
}
}
-
};
- function mixinProperties(to, from) {
- for (var key in from) {
- if (from.hasOwnProperty(key)) {
- to[key] = from[key];
- }
- }
- }
+ __exports__["default"] = DeferredActionQueues;
+ });
+enifed("backburner/platform",
+ ["exports"],
+ function(__exports__) {
+ "use strict";
+ // In IE 6-8, try/finally doesn't work without a catch.
+ // Unfortunately, this is impossible to test for since wrapping it in a parent try/catch doesn't trigger the bug.
+ // This tests for another broken try/catch behavior that only exhibits in the same versions of IE.
+ var needsIETryCatchFix = (function(e,x){
+ try{ x(); }
+ catch(e) { } // jshint ignore:line
+ return !!e;
+ })();
+ __exports__.needsIETryCatchFix = needsIETryCatchFix;
+ });
+enifed("backburner/queue",
+ ["./utils","exports"],
+ function(__dependency1__, __exports__) {
+ "use strict";
+ var isString = __dependency1__.isString;
- mixinProperties(Binding, {
+ function Queue(name, options, globalOptions) {
+ this.name = name;
+ this.globalOptions = globalOptions || {};
+ this.options = options;
+ this._queue = [];
+ this.targetQueues = Object.create(null);
+ this._queueBeingFlushed = undefined;
+ }
- /*
- See `Ember.Binding.from`.
+ Queue.prototype = {
+ push: function(target, method, args, stack) {
+ var queue = this._queue;
+ queue.push(target, method, args, stack);
- @method from
- @static
- */
- from: function() {
- var C = this, binding = new C();
- return binding.from.apply(binding, arguments);
+ return {
+ queue: this,
+ target: target,
+ method: method
+ };
},
- /*
- See `Ember.Binding.to`.
+ pushUniqueWithoutGuid: function(target, method, args, stack) {
+ var queue = this._queue;
- @method to
- @static
- */
- to: function() {
- var C = this, binding = new C();
- return binding.to.apply(binding, arguments);
- },
+ for (var i = 0, l = queue.length; i < l; i += 4) {
+ var currentTarget = queue[i];
+ var currentMethod = queue[i+1];
- /**
- Creates a new Binding instance and makes it apply in a single direction.
- A one-way binding will relay changes on the `from` side object (supplied
- as the `from` argument) the `to` side, but not the other way around.
- This means that if you change the "to" side directly, the "from" side may have
- a different value.
+ if (currentTarget === target && currentMethod === method) {
+ queue[i+2] = args; // replace args
+ queue[i+3] = stack; // replace stack
+ return;
+ }
+ }
- See `Binding.oneWay`.
+ queue.push(target, method, args, stack);
+ },
- @method oneWay
- @param {String} from from path.
- @param {Boolean} [flag] (Optional) passing nothing here will make the
- binding `oneWay`. You can instead pass `false` to disable `oneWay`, making the
- binding two way again.
- @return {Ember.Binding} `this`
- */
- oneWay: function(from, flag) {
- var C = this, binding = new C(null, from);
- return binding.oneWay(flag);
- }
+ targetQueue: function(targetQueue, target, method, args, stack) {
+ var queue = this._queue;
- });
+ for (var i = 0, l = targetQueue.length; i < l; i += 4) {
+ var currentMethod = targetQueue[i];
+ var currentIndex = targetQueue[i + 1];
- /**
- An `Ember.Binding` connects the properties of two objects so that whenever
- the value of one property changes, the other property will be changed also.
+ if (currentMethod === method) {
+ queue[currentIndex + 2] = args; // replace args
+ queue[currentIndex + 3] = stack; // replace stack
+ return;
+ }
+ }
- ## Automatic Creation of Bindings with `/^*Binding/`-named Properties
+ targetQueue.push(
+ method,
+ queue.push(target, method, args, stack) - 4
+ );
+ },
- You do not usually create Binding objects directly but instead describe
- bindings in your class or object definition using automatic binding
- detection.
+ pushUniqueWithGuid: function(guid, target, method, args, stack) {
+ var hasLocalQueue = this.targetQueues[guid];
- Properties ending in a `Binding` suffix will be converted to `Ember.Binding`
- instances. The value of this property should be a string representing a path
- to another object or a custom binding instanced created using Binding helpers
- (see "One Way Bindings"):
+ if (hasLocalQueue) {
+ this.targetQueue(hasLocalQueue, target, method, args, stack);
+ } else {
+ this.targetQueues[guid] = [
+ method,
+ this._queue.push(target, method, args, stack) - 4
+ ];
+ }
- ```
- valueBinding: "MyApp.someController.title"
- ```
+ return {
+ queue: this,
+ target: target,
+ method: method
+ };
+ },
- This will create a binding from `MyApp.someController.title` to the `value`
- property of your object instance automatically. Now the two values will be
- kept in sync.
+ pushUnique: function(target, method, args, stack) {
+ var queue = this._queue, currentTarget, currentMethod, i, l;
+ var KEY = this.globalOptions.GUID_KEY;
- ## One Way Bindings
+ if (target && KEY) {
+ var guid = target[KEY];
+ if (guid) {
+ return this.pushUniqueWithGuid(guid, target, method, args, stack);
+ }
+ }
- One especially useful binding customization you can use is the `oneWay()`
- helper. This helper tells Ember that you are only interested in
- receiving changes on the object you are binding from. For example, if you
- are binding to a preference and you want to be notified if the preference
- has changed, but your object will not be changing the preference itself, you
- could do:
+ this.pushUniqueWithoutGuid(target, method, args, stack);
- ```
- bigTitlesBinding: Ember.Binding.oneWay("MyApp.preferencesController.bigTitles")
- ```
+ return {
+ queue: this,
+ target: target,
+ method: method
+ };
+ },
- This way if the value of `MyApp.preferencesController.bigTitles` changes the
- `bigTitles` property of your object will change also. However, if you
- change the value of your `bigTitles` property, it will not update the
- `preferencesController`.
+ invoke: function(target, method, args, _, _errorRecordedForStack) {
+ if (args && args.length > 0) {
+ method.apply(target, args);
+ } else {
+ method.call(target);
+ }
+ },
- One way bindings are almost twice as fast to setup and twice as fast to
- execute because the binding only has to worry about changes to one side.
+ invokeWithOnError: function(target, method, args, onError, errorRecordedForStack) {
+ try {
+ if (args && args.length > 0) {
+ method.apply(target, args);
+ } else {
+ method.call(target);
+ }
+ } catch(error) {
+ onError(error, errorRecordedForStack);
+ }
+ },
- You should consider using one way bindings anytime you have an object that
- may be created frequently and you do not intend to change a property; only
- to monitor it for changes (such as in the example above).
+ flush: function(sync) {
+ var queue = this._queue;
+ var length = queue.length;
- ## Adding Bindings Manually
+ if (length === 0) {
+ return;
+ }
- All of the examples above show you how to configure a custom binding, but the
- result of these customizations will be a binding template, not a fully active
- Binding instance. The binding will actually become active only when you
- instantiate the object the binding belongs to. It is useful however, to
- understand what actually happens when the binding is activated.
+ var globalOptions = this.globalOptions;
+ var options = this.options;
+ var before = options && options.before;
+ var after = options && options.after;
+ var onError = globalOptions.onError || (globalOptions.onErrorTarget &&
+ globalOptions.onErrorTarget[globalOptions.onErrorMethod]);
+ var target, method, args, errorRecordedForStack;
+ var invoke = onError ? this.invokeWithOnError : this.invoke;
- For a binding to function it must have at least a `from` property and a `to`
- property. The `from` property path points to the object/key that you want to
- bind from while the `to` path points to the object/key you want to bind to.
+ this.targetQueues = Object.create(null);
+ var queueItems = this._queueBeingFlushed = this._queue.slice();
+ this._queue = [];
- When you define a custom binding, you are usually describing the property
- you want to bind from (such as `MyApp.someController.value` in the examples
- above). When your object is created, it will automatically assign the value
- you want to bind `to` based on the name of your binding key. In the
- examples above, during init, Ember objects will effectively call
- something like this on your binding:
+ if (before) {
+ before();
+ }
- ```javascript
- binding = Ember.Binding.from(this.valueBinding).to("value");
- ```
+ for (var i = 0; i < length; i += 4) {
+ target = queueItems[i];
+ method = queueItems[i+1];
+ args = queueItems[i+2];
+ errorRecordedForStack = queueItems[i+3]; // Debugging assistance
- This creates a new binding instance based on the template you provide, and
- sets the to path to the `value` property of the new object. Now that the
- binding is fully configured with a `from` and a `to`, it simply needs to be
- connected to become active. This is done through the `connect()` method:
+ if (isString(method)) {
+ method = target[method];
+ }
- ```javascript
- binding.connect(this);
- ```
+ // method could have been nullified / canceled during flush
+ if (method) {
+ //
+ // ** Attention intrepid developer **
+ //
+ // To find out the stack of this task when it was scheduled onto
+ // the run loop, add the following to your app.js:
+ //
+ // Ember.run.backburner.DEBUG = true; // NOTE: This slows your app, don't leave it on in production.
+ //
+ // Once that is in place, when you are at a breakpoint and navigate
+ // here in the stack explorer, you can look at `errorRecordedForStack.stack`,
+ // which will be the captured stack when this job was scheduled.
+ //
+ invoke(target, method, args, onError, errorRecordedForStack);
+ }
+ }
- Note that when you connect a binding you pass the object you want it to be
- connected to. This object will be used as the root for both the from and
- to side of the binding when inspecting relative paths. This allows the
- binding to be automatically inherited by subclassed objects as well.
+ if (after) {
+ after();
+ }
- Now that the binding is connected, it will observe both the from and to side
- and relay changes.
+ this._queueBeingFlushed = undefined;
- If you ever needed to do so (you almost never will, but it is useful to
- understand this anyway), you could manually create an active binding by
- using the `Ember.bind()` helper method. (This is the same method used by
- to setup your bindings on objects):
+ if (sync !== false &&
+ this._queue.length > 0) {
+ // check if new items have been added
+ this.flush(true);
+ }
+ },
- ```javascript
- Ember.bind(MyApp.anotherObject, "value", "MyApp.someController.value");
- ```
+ cancel: function(actionToCancel) {
+ var queue = this._queue, currentTarget, currentMethod, i, l;
+ var target = actionToCancel.target;
+ var method = actionToCancel.method;
+ var GUID_KEY = this.globalOptions.GUID_KEY;
- Both of these code fragments have the same effect as doing the most friendly
- form of binding creation like so:
+ if (GUID_KEY && this.targetQueues && target) {
+ var targetQueue = this.targetQueues[target[GUID_KEY]];
- ```javascript
- MyApp.anotherObject = Ember.Object.create({
- valueBinding: "MyApp.someController.value",
+ if (targetQueue) {
+ for (i = 0, l = targetQueue.length; i < l; i++) {
+ if (targetQueue[i] === method) {
+ targetQueue.splice(i, 1);
+ }
+ }
+ }
+ }
- // OTHER CODE FOR THIS OBJECT...
- });
- ```
+ for (i = 0, l = queue.length; i < l; i += 4) {
+ currentTarget = queue[i];
+ currentMethod = queue[i+1];
- Ember's built in binding creation method makes it easy to automatically
- create bindings for you. You should always use the highest-level APIs
- available, even if you understand how it works underneath.
+ if (currentTarget === target &&
+ currentMethod === method) {
+ queue.splice(i, 4);
+ return true;
+ }
+ }
- @class Binding
- @namespace Ember
- @since Ember 0.9
- */
- // Ember.Binding = Binding; ES6TODO: where to put this?
+ // if not found in current queue
+ // could be in the queue that is being flushed
+ queue = this._queueBeingFlushed;
+ if (!queue) {
+ return;
+ }
- /**
- Global helper method to create a new binding. Just pass the root object
- along with a `to` and `from` path to create and connect the binding.
+ for (i = 0, l = queue.length; i < l; i += 4) {
+ currentTarget = queue[i];
+ currentMethod = queue[i+1];
- @method bind
- @for Ember
- @param {Object} obj The root object of the transform.
- @param {String} to The path to the 'to' side of the binding.
- Must be relative to obj.
- @param {String} from The path to the 'from' side of the binding.
- Must be relative to obj or a global path.
- @return {Ember.Binding} binding instance
- */
- function bind(obj, to, from) {
- return new Binding(to, from).connect(obj);
- };
-
- /**
- @method oneWay
- @for Ember
- @param {Object} obj The root object of the transform.
- @param {String} to The path to the 'to' side of the binding.
- Must be relative to obj.
- @param {String} from The path to the 'from' side of the binding.
- Must be relative to obj or a global path.
- @return {Ember.Binding} binding instance
- */
- function oneWay(obj, to, from) {
- return new Binding(to, from).oneWay().connect(obj);
+ if (currentTarget === target &&
+ currentMethod === method) {
+ // don't mess with array during flush
+ // just nullify the method
+ queue[i+1] = null;
+ return true;
+ }
+ }
+ }
};
- __exports__.Binding = Binding;
- __exports__.bind = bind;
- __exports__.oneWay = oneWay;
- __exports__.isGlobalPath = isGlobalPath;
+ __exports__["default"] = Queue;
});
-define("ember-metal/chains",
- ["ember-metal/core","ember-metal/property_get","ember-metal/utils","ember-metal/array","ember-metal/watch_key","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) {
+enifed("backburner/utils",
+ ["exports"],
+ function(__exports__) {
"use strict";
- var Ember = __dependency1__["default"];
- // warn, assert, etc;
- var get = __dependency2__.get;
- var normalizeTuple = __dependency2__.normalizeTuple;
- var meta = __dependency3__.meta;
- var META_KEY = __dependency3__.META_KEY;
- var forEach = __dependency4__.forEach;
- var watchKey = __dependency5__.watchKey;
- var unwatchKey = __dependency5__.unwatchKey;
+ var NUMBER = /\d+/;
- var metaFor = meta,
- warn = Ember.warn,
- FIRST_KEY = /^([^\.]+)/;
+ function each(collection, callback) {
+ for (var i = 0; i < collection.length; i++) {
+ callback(collection[i]);
+ }
+ }
- function firstKey(path) {
- return path.match(FIRST_KEY)[0];
+ __exports__.each = each;// Date.now is not available in browsers < IE9
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now#Compatibility
+ var now = Date.now || function() { return new Date().getTime(); };
+ __exports__.now = now;
+ function isString(suspect) {
+ return typeof suspect === 'string';
}
- var pendingQueue = [];
+ __exports__.isString = isString;function isFunction(suspect) {
+ return typeof suspect === 'function';
+ }
- // attempts to add the pendingQueue chains again. If some of them end up
- // back in the queue and reschedule is true, schedules a timeout to try
- // again.
- function flushPendingChains() {
- if (pendingQueue.length === 0) { return; } // nothing to do
+ __exports__.isFunction = isFunction;function isNumber(suspect) {
+ return typeof suspect === 'number';
+ }
- var queue = pendingQueue;
- pendingQueue = [];
+ __exports__.isNumber = isNumber;function isCoercableNumber(number) {
+ return isNumber(number) || NUMBER.test(number);
+ }
- forEach.call(queue, function(q) { q[0].add(q[1]); });
+ __exports__.isCoercableNumber = isCoercableNumber;function wrapInTryCatch(func) {
+ return function () {
+ try {
+ return func.apply(this, arguments);
+ } catch (e) {
+ throw e;
+ }
+ };
+ }
- warn('Watching an undefined global, Ember expects watched globals to be setup by the time the run loop is flushed, check for typos', pendingQueue.length === 0);
- };
+ __exports__.wrapInTryCatch = wrapInTryCatch;
+ });
+enifed("calculateVersion",
+ [],
+ function() {
+ "use strict";
+ 'use strict';
+ var fs = eriuqer('fs');
+ var path = eriuqer('path');
- function addChainWatcher(obj, keyName, node) {
- if (!obj || ('object' !== typeof obj)) { return; } // nothing to do
+ module.exports = function () {
+ var packageVersion = eriuqer('../package.json').version;
+ var output = [packageVersion];
+ var gitPath = path.join(__dirname,'..','.git');
+ var headFilePath = path.join(gitPath, 'HEAD');
- var m = metaFor(obj), nodes = m.chainWatchers;
+ if (packageVersion.indexOf('+') > -1) {
+ try {
+ if (fs.existsSync(headFilePath)) {
+ var headFile = fs.readFileSync(headFilePath, {encoding: 'utf8'});
+ var branchName = headFile.split('/').slice(-1)[0].trim();
+ var refPath = headFile.split(' ')[1];
+ var branchSHA;
+
+ if (refPath) {
+ var branchPath = path.join(gitPath, refPath.trim());
+ branchSHA = fs.readFileSync(branchPath);
+ } else {
+ branchSHA = branchName;
+ }
- if (!m.hasOwnProperty('chainWatchers')) {
- nodes = m.chainWatchers = {};
+ output.push(branchSHA.slice(0,10));
+ }
+ } catch (err) {
+ console.error(err.stack);
+ }
+ return output.join('.');
+ } else {
+ return packageVersion;
}
+ };
+ });
+enifed("container",
+ ["container/container","exports"],
+ function(__dependency1__, __exports__) {
+ "use strict";
+ /*
+ Public api for the container is still in flux.
+ The public api, specified on the application namespace should be considered the stable api.
+ // @module container
+ @private
+ */
- if (!nodes[keyName]) { nodes[keyName] = []; }
- nodes[keyName].push(node);
- watchKey(obj, keyName, m);
- }
+ /*
+ Flag to enable/disable model factory injections (disabled by default)
+ If model factory injections are enabled, models should not be
+ accessed globally (only through `container.lookupFactory('model:modelName'))`);
+ */
+ Ember.MODEL_FACTORY_INJECTIONS = false;
- function removeChainWatcher(obj, keyName, node) {
- if (!obj || 'object' !== typeof obj) { return; } // nothing to do
+ if (Ember.ENV && typeof Ember.ENV.MODEL_FACTORY_INJECTIONS !== 'undefined') {
+ Ember.MODEL_FACTORY_INJECTIONS = !!Ember.ENV.MODEL_FACTORY_INJECTIONS;
+ }
- var m = obj[META_KEY];
- if (m && !m.hasOwnProperty('chainWatchers')) { return; } // nothing to do
- var nodes = m && m.chainWatchers;
+ var Container = __dependency1__["default"];
- if (nodes && nodes[keyName]) {
- nodes = nodes[keyName];
- for (var i = 0, l = nodes.length; i < l; i++) {
- if (nodes[i] === node) { nodes.splice(i, 1); }
- }
- }
- unwatchKey(obj, keyName, m);
- };
+ __exports__["default"] = Container;
+ });
+enifed("container/container",
+ ["ember-metal/core","ember-metal/keys","ember-metal/dictionary","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
+ "use strict";
+ var Ember = __dependency1__["default"];
+ // Ember.assert
+ var emberKeys = __dependency2__["default"];
+ var dictionary = __dependency3__["default"];
- // A ChainNode watches a single key on an object. If you provide a starting
- // value for the key then the node won't actually watch it. For a root node
- // pass null for parent and key and object for value.
- function ChainNode(parent, key, value) {
- this._parent = parent;
- this._key = key;
+ // A lightweight container that helps to assemble and decouple components.
+ // Public api for the container is still in flux.
+ // The public api, specified on the application namespace should be considered the stable api.
+ function Container(parent) {
+ this.parent = parent;
+ this.children = [];
- // _watching is true when calling get(this._parent, this._key) will
- // return the value of this node.
- //
- // It is false for the root of a chain (because we have no parent)
- // and for global paths (because the parent node is the object with
- // the observer on it)
- this._watching = value===undefined;
+ this.resolver = parent && parent.resolver || function() {};
- this._value = value;
- this._paths = {};
- if (this._watching) {
- this._object = parent.value();
- if (this._object) { addChainWatcher(this._object, this._key, this); }
- }
+ this.registry = dictionary(parent ? parent.registry : null);
+ this.cache = dictionary(parent ? parent.cache : null);
+ this.factoryCache = dictionary(parent ? parent.factoryCache : null);
+ this.resolveCache = dictionary(parent ? parent.resolveCache : null);
+ this.typeInjections = dictionary(parent ? parent.typeInjections : null);
+ this.injections = dictionary(null);
+ this.normalizeCache = dictionary(null);
+
+ this.validationCache = dictionary(parent ? parent.validationCache : null);
+
- // Special-case: the EachProxy relies on immediate evaluation to
- // establish its observers.
- //
- // TODO: Replace this with an efficient callback that the EachProxy
- // can implement.
- if (this._parent && this._parent._key === '@each') {
- this.value();
- }
- };
+ this.factoryTypeInjections = dictionary(parent ? parent.factoryTypeInjections : null);
+ this.factoryInjections = dictionary(null);
- var ChainNodePrototype = ChainNode.prototype;
+ this._options = dictionary(parent ? parent._options : null);
+ this._typeOptions = dictionary(parent ? parent._typeOptions : null);
+ }
- function lazyGet(obj, key) {
- if (!obj) return undefined;
+ Container.prototype = {
- var meta = obj[META_KEY];
- // check if object meant only to be a prototype
- if (meta && meta.proto === obj) return undefined;
+ /**
+ @property parent
+ @type Container
+ @default null
+ */
+ parent: null,
- if (key === "@each") return get(obj, key);
+ /**
+ @property children
+ @type Array
+ @default []
+ */
+ children: null,
- // if a CP only return cached value
- var desc = meta && meta.descs[key];
- if (desc && desc._cacheable) {
- if (key in meta.cache) {
- return meta.cache[key];
- } else {
- return undefined;
- }
- }
+ /**
+ @property resolver
+ @type function
+ */
+ resolver: null,
- return get(obj, key);
- }
+ /**
+ @property registry
+ @type InheritingDict
+ */
+ registry: null,
- ChainNodePrototype.value = function() {
- if (this._value === undefined && this._watching) {
- var obj = this._parent.value();
- this._value = lazyGet(obj, this._key);
- }
- return this._value;
- };
+ /**
+ @property cache
+ @type InheritingDict
+ */
+ cache: null,
- ChainNodePrototype.destroy = function() {
- if (this._watching) {
- var obj = this._object;
- if (obj) { removeChainWatcher(obj, this._key, this); }
- this._watching = false; // so future calls do nothing
- }
- };
+ /**
+ @property typeInjections
+ @type InheritingDict
+ */
+ typeInjections: null,
- // copies a top level object only
- ChainNodePrototype.copy = function(obj) {
- var ret = new ChainNode(null, null, obj),
- paths = this._paths, path;
- for (path in paths) {
- if (paths[path] <= 0) { continue; } // this check will also catch non-number vals.
- ret.add(path);
- }
- return ret;
- };
+ /**
+ @property injections
+ @type Object
+ @default {}
+ */
+ injections: null,
- // called on the root node of a chain to setup watchers on the specified
- // path.
- ChainNodePrototype.add = function(path) {
- var obj, tuple, key, src, paths;
+ /**
+ @private
- paths = this._paths;
- paths[path] = (paths[path] || 0) + 1;
+ @property _options
+ @type InheritingDict
+ @default null
+ */
+ _options: null,
- obj = this.value();
- tuple = normalizeTuple(obj, path);
+ /**
+ @private
- // the path was a local path
- if (tuple[0] && tuple[0] === obj) {
- path = tuple[1];
- key = firstKey(path);
- path = path.slice(key.length+1);
+ @property _typeOptions
+ @type InheritingDict
+ */
+ _typeOptions: null,
- // global path, but object does not exist yet.
- // put into a queue and try to connect later.
- } else if (!tuple[0]) {
- pendingQueue.push([this, path]);
- tuple.length = 0;
- return;
+ /**
+ Returns a new child of the current container. These children are configured
+ to correctly inherit from the current container.
- // global path, and object already exists
- } else {
- src = tuple[0];
- key = path.slice(0, 0-(tuple[1].length+1));
- path = tuple[1];
- }
+ @method child
+ @return {Container}
+ */
+ child: function() {
+ var container = new Container(this);
+ this.children.push(container);
+ return container;
+ },
- tuple.length = 0;
- this.chain(key, path, src);
- };
+ /**
+ Registers a factory for later injection.
- // called on the root node of a chain to teardown watcher on the specified
- // path
- ChainNodePrototype.remove = function(path) {
- var obj, tuple, key, src, paths;
+ Example:
- paths = this._paths;
- if (paths[path] > 0) { paths[path]--; }
+ ```javascript
+ var container = new Container();
- obj = this.value();
- tuple = normalizeTuple(obj, path);
- if (tuple[0] === obj) {
- path = tuple[1];
- key = firstKey(path);
- path = path.slice(key.length+1);
- } else {
- src = tuple[0];
- key = path.slice(0, 0-(tuple[1].length+1));
- path = tuple[1];
- }
+ container.register('model:user', Person, {singleton: false });
+ container.register('fruit:favorite', Orange);
+ container.register('communication:main', Email, {singleton: false});
+ ```
- tuple.length = 0;
- this.unchain(key, path);
- };
+ @method register
+ @param {String} fullName
+ @param {Function} factory
+ @param {Object} options
+ */
+ register: function(fullName, factory, options) {
+ Ember.assert('fullName must be a proper full name', validateFullName(fullName));
- ChainNodePrototype.count = 0;
+ if (factory === undefined) {
+ throw new TypeError('Attempting to register an unknown factory: `' + fullName + '`');
+ }
- ChainNodePrototype.chain = function(key, path, src) {
- var chains = this._chains, node;
- if (!chains) { chains = this._chains = {}; }
+ var normalizedName = this.normalize(fullName);
- node = chains[key];
- if (!node) { node = chains[key] = new ChainNode(this, key, src); }
- node.count++; // count chains...
+ if (normalizedName in this.cache) {
+ throw new Error('Cannot re-register: `' + fullName +'`, as it has already been looked up.');
+ }
- // chain rest of path if there is one
- if (path && path.length>0) {
- key = firstKey(path);
- path = path.slice(key.length+1);
- node.chain(key, path); // NOTE: no src means it will observe changes...
- }
- };
+ this.registry[normalizedName] = factory;
+ this._options[normalizedName] = (options || {});
+ },
- ChainNodePrototype.unchain = function(key, path) {
- var chains = this._chains, node = chains[key];
+ /**
+ Unregister a fullName
- // unchain rest of path first...
- if (path && path.length>1) {
- key = firstKey(path);
- path = path.slice(key.length+1);
- node.unchain(key, path);
- }
+ ```javascript
+ var container = new Container();
+ container.register('model:user', User);
- // delete node if needed.
- node.count--;
- if (node.count<=0) {
- delete chains[node._key];
- node.destroy();
- }
+ container.lookup('model:user') instanceof User //=> true
- };
+ container.unregister('model:user')
+ container.lookup('model:user') === undefined //=> true
+ ```
- ChainNodePrototype.willChange = function(events) {
- var chains = this._chains;
- if (chains) {
- for(var key in chains) {
- if (!chains.hasOwnProperty(key)) { continue; }
- chains[key].willChange(events);
- }
- }
+ @method unregister
+ @param {String} fullName
+ */
+ unregister: function(fullName) {
+ Ember.assert('fullName must be a proper full name', validateFullName(fullName));
- if (this._parent) { this._parent.chainWillChange(this, this._key, 1, events); }
- };
+ var normalizedName = this.normalize(fullName);
- ChainNodePrototype.chainWillChange = function(chain, path, depth, events) {
- if (this._key) { path = this._key + '.' + path; }
+ delete this.registry[normalizedName];
+ delete this.cache[normalizedName];
+ delete this.factoryCache[normalizedName];
+ delete this.resolveCache[normalizedName];
+ delete this._options[normalizedName];
+
+ delete this.validationCache[normalizedName];
+
+ },
- if (this._parent) {
- this._parent.chainWillChange(this, path, depth+1, events);
- } else {
- if (depth > 1) {
- events.push(this.value(), path);
- }
- path = 'this.' + path;
- if (this._paths[path] > 0) {
- events.push(this.value(), path);
- }
- }
- };
+ /**
+ Given a fullName return the corresponding factory.
- ChainNodePrototype.chainDidChange = function(chain, path, depth, events) {
- if (this._key) { path = this._key + '.' + path; }
- if (this._parent) {
- this._parent.chainDidChange(this, path, depth+1, events);
- } else {
- if (depth > 1) {
- events.push(this.value(), path);
- }
- path = 'this.' + path;
- if (this._paths[path] > 0) {
- events.push(this.value(), path);
- }
- }
- };
+ By default `resolve` will retrieve the factory from
+ its container's registry.
- ChainNodePrototype.didChange = function(events) {
- // invalidate my own value first.
- if (this._watching) {
- var obj = this._parent.value();
- if (obj !== this._object) {
- removeChainWatcher(this._object, this._key, this);
- this._object = obj;
- addChainWatcher(obj, this._key, this);
- }
- this._value = undefined;
+ ```javascript
+ var container = new Container();
+ container.register('api:twitter', Twitter);
- // Special-case: the EachProxy relies on immediate evaluation to
- // establish its observers.
- if (this._parent && this._parent._key === '@each')
- this.value();
- }
+ container.resolve('api:twitter') // => Twitter
+ ```
- // then notify chains...
- var chains = this._chains;
- if (chains) {
- for(var key in chains) {
- if (!chains.hasOwnProperty(key)) { continue; }
- chains[key].didChange(events);
- }
- }
+ Optionally the container can be provided with a custom resolver.
+ If provided, `resolve` will first provide the custom resolver
+ the opportunity to resolve the fullName, otherwise it will fallback
+ to the registry.
- // if no events are passed in then we only care about the above wiring update
- if (events === null) { return; }
+ ```javascript
+ var container = new Container();
+ container.resolver = function(fullName) {
+ // lookup via the module system of choice
+ };
- // and finally tell parent about my path changing...
- if (this._parent) { this._parent.chainDidChange(this, this._key, 1, events); }
- };
+ // the twitter factory is added to the module system
+ container.resolve('api:twitter') // => Twitter
+ ```
- function finishChains(obj) {
- // We only create meta if we really have to
- var m = obj[META_KEY], chains = m && m.chains;
- if (chains) {
- if (chains.value() !== obj) {
- metaFor(obj).chains = chains = chains.copy(obj);
- } else {
- chains.didChange(null);
- }
- }
- };
+ @method resolve
+ @param {String} fullName
+ @return {Function} fullName's factory
+ */
+ resolve: function(fullName) {
+ Ember.assert('fullName must be a proper full name', validateFullName(fullName));
+ return resolve(this, this.normalize(fullName));
+ },
- __exports__.flushPendingChains = flushPendingChains;
- __exports__.removeChainWatcher = removeChainWatcher;
- __exports__.ChainNode = ChainNode;
- __exports__.finishChains = finishChains;
- });
-define("ember-metal/computed",
- ["ember-metal/core","ember-metal/property_get","ember-metal/property_set","ember-metal/utils","ember-metal/enumerable_utils","ember-metal/platform","ember-metal/watching","ember-metal/expand_properties","ember-metal/error","ember-metal/properties","ember-metal/property_events","ember-metal/is_empty","ember-metal/is_none","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __exports__) {
- "use strict";
- var Ember = __dependency1__["default"];
- var get = __dependency2__.get;
- var set = __dependency3__.set;
- var meta = __dependency4__.meta;
- var META_KEY = __dependency4__.META_KEY;
- var guidFor = __dependency4__.guidFor;
- var typeOf = __dependency4__.typeOf;
- var inspect = __dependency4__.inspect;
- var EnumerableUtils = __dependency5__["default"];
- var create = __dependency6__.create;
- var watch = __dependency7__.watch;
- var unwatch = __dependency7__.unwatch;
- var expandProperties = __dependency8__["default"];
- var EmberError = __dependency9__["default"];
- var Descriptor = __dependency10__.Descriptor;
- var defineProperty = __dependency10__.defineProperty;
- var propertyWillChange = __dependency11__.propertyWillChange;
- var propertyDidChange = __dependency11__.propertyDidChange;
- var isEmpty = __dependency12__["default"];
- var isNone = __dependency13__.isNone;
+ /**
+ A hook that can be used to describe how the resolver will
+ attempt to find the factory.
- /**
- @module ember-metal
- */
+ For example, the default Ember `.describe` returns the full
+ class name (including namespace) where Ember's resolver expects
+ to find the `fullName`.
- Ember.warn("The CP_DEFAULT_CACHEABLE flag has been removed and computed properties are always cached by default. Use `volatile` if you don't want caching.", Ember.ENV.CP_DEFAULT_CACHEABLE !== false);
+ @method describe
+ @param {String} fullName
+ @return {string} described fullName
+ */
+ describe: function(fullName) {
+ return fullName;
+ },
+ /**
+ A hook to enable custom fullName normalization behaviour
- var metaFor = meta,
- a_slice = [].slice,
- o_create = create;
+ @method normalizeFullName
+ @param {String} fullName
+ @return {string} normalized fullName
+ */
+ normalizeFullName: function(fullName) {
+ return fullName;
+ },
- function UNDEFINED() { }
+ /**
+ normalize a fullName based on the applications conventions
- var lengthPattern = /\.(length|\[\])$/;
+ @method normalize
+ @param {String} fullName
+ @return {string} normalized fullName
+ */
+ normalize: function(fullName) {
+ return this.normalizeCache[fullName] || (
+ this.normalizeCache[fullName] = this.normalizeFullName(fullName)
+ );
+ },
- // ..........................................................
- // DEPENDENT KEYS
- //
+ /**
+ @method makeToString
- // data structure:
- // meta.deps = {
- // 'depKey': {
- // 'keyName': count,
- // }
- // }
+ @param {any} factory
+ @param {string} fullName
+ @return {function} toString function
+ */
+ makeToString: function(factory, fullName) {
+ return factory.toString();
+ },
- /*
- This function returns a map of unique dependencies for a
- given object and key.
- */
- function keysForDep(depsMeta, depKey) {
- var keys = depsMeta[depKey];
- if (!keys) {
- // if there are no dependencies yet for a the given key
- // create a new empty list of dependencies for the key
- keys = depsMeta[depKey] = {};
- } else if (!depsMeta.hasOwnProperty(depKey)) {
- // otherwise if the dependency list is inherited from
- // a superclass, clone the hash
- keys = depsMeta[depKey] = o_create(keys);
- }
- return keys;
- }
+ /**
+ Given a fullName return a corresponding instance.
- function metaForDeps(meta) {
- return keysForDep(meta, 'deps');
- }
+ The default behaviour is for lookup to return a singleton instance.
+ The singleton is scoped to the container, allowing multiple containers
+ to all have their own locally scoped singletons.
- function addDependentKeys(desc, obj, keyName, meta) {
- // the descriptor has a list of dependent keys, so
- // add all of its dependent keys.
- var depKeys = desc._dependentKeys, depsMeta, idx, len, depKey, keys;
- if (!depKeys) return;
+ ```javascript
+ var container = new Container();
+ container.register('api:twitter', Twitter);
- depsMeta = metaForDeps(meta);
+ var twitter = container.lookup('api:twitter');
- for(idx = 0, len = depKeys.length; idx < len; idx++) {
- depKey = depKeys[idx];
- // Lookup keys meta for depKey
- keys = keysForDep(depsMeta, depKey);
- // Increment the number of times depKey depends on keyName.
- keys[keyName] = (keys[keyName] || 0) + 1;
- // Watch the depKey
- watch(obj, depKey, meta);
- }
- }
+ twitter instanceof Twitter; // => true
- function removeDependentKeys(desc, obj, keyName, meta) {
- // the descriptor has a list of dependent keys, so
- // add all of its dependent keys.
- var depKeys = desc._dependentKeys, depsMeta, idx, len, depKey, keys;
- if (!depKeys) return;
+ // by default the container will return singletons
+ var twitter2 = container.lookup('api:twitter');
+ twitter2 instanceof Twitter; // => true
- depsMeta = metaForDeps(meta);
+ twitter === twitter2; //=> true
+ ```
- for(idx = 0, len = depKeys.length; idx < len; idx++) {
- depKey = depKeys[idx];
- // Lookup keys meta for depKey
- keys = keysForDep(depsMeta, depKey);
- // Increment the number of times depKey depends on keyName.
- keys[keyName] = (keys[keyName] || 0) - 1;
- // Watch the depKey
- unwatch(obj, depKey, meta);
- }
- }
+ If singletons are not wanted an optional flag can be provided at lookup.
- // ..........................................................
- // COMPUTED PROPERTY
- //
+ ```javascript
+ var container = new Container();
+ container.register('api:twitter', Twitter);
- /**
- A computed property transforms an objects function into a property.
+ var twitter = container.lookup('api:twitter', { singleton: false });
+ var twitter2 = container.lookup('api:twitter', { singleton: false });
- By default the function backing the computed property will only be called
- once and the result will be cached. You can specify various properties
- that your computed property is dependent on. This will force the cached
- result to be recomputed if the dependencies are modified.
+ twitter === twitter2; //=> false
+ ```
- In the following example we declare a computed property (by calling
- `.property()` on the fullName function) and setup the properties
- dependencies (depending on firstName and lastName). The fullName function
- will be called once (regardless of how many times it is accessed) as long
- as it's dependencies have not been changed. Once firstName or lastName are updated
- any future calls (or anything bound) to fullName will incorporate the new
- values.
+ @method lookup
+ @param {String} fullName
+ @param {Object} options
+ @return {any}
+ */
+ lookup: function(fullName, options) {
+ Ember.assert('fullName must be a proper full name', validateFullName(fullName));
+ return lookup(this, this.normalize(fullName), options);
+ },
- ```javascript
- var Person = Ember.Object.extend({
- // these will be supplied by `create`
- firstName: null,
- lastName: null,
+ /**
+ Given a fullName return the corresponding factory.
- fullName: function() {
- var firstName = this.get('firstName');
- var lastName = this.get('lastName');
+ @method lookupFactory
+ @param {String} fullName
+ @return {any}
+ */
+ lookupFactory: function(fullName) {
+ Ember.assert('fullName must be a proper full name', validateFullName(fullName));
+ return factoryFor(this, this.normalize(fullName));
+ },
- return firstName + ' ' + lastName;
- }.property('firstName', 'lastName')
- });
+ /**
+ Given a fullName check if the container is aware of its factory
+ or singleton instance.
- var tom = Person.create({
- firstName: 'Tom',
- lastName: 'Dale'
- });
+ @method has
+ @param {String} fullName
+ @return {Boolean}
+ */
+ has: function(fullName) {
+ Ember.assert('fullName must be a proper full name', validateFullName(fullName));
+ return has(this, this.normalize(fullName));
+ },
- tom.get('fullName') // 'Tom Dale'
- ```
+ /**
+ Allow registering options for all factories of a type.
- You can also define what Ember should do when setting a computed property.
- If you try to set a computed property, it will be invoked with the key and
- value you want to set it to. You can also accept the previous value as the
- third parameter.
+ ```javascript
+ var container = new Container();
- ```javascript
- var Person = Ember.Object.extend({
- // these will be supplied by `create`
- firstName: null,
- lastName: null,
+ // if all of type `connection` must not be singletons
+ container.optionsForType('connection', { singleton: false });
- fullName: function(key, value, oldValue) {
- // getter
- if (arguments.length === 1) {
- var firstName = this.get('firstName');
- var lastName = this.get('lastName');
+ container.register('connection:twitter', TwitterConnection);
+ container.register('connection:facebook', FacebookConnection);
- return firstName + ' ' + lastName;
+ var twitter = container.lookup('connection:twitter');
+ var twitter2 = container.lookup('connection:twitter');
- // setter
- } else {
- var name = value.split(' ');
+ twitter === twitter2; // => false
- this.set('firstName', name[0]);
- this.set('lastName', name[1]);
+ var facebook = container.lookup('connection:facebook');
+ var facebook2 = container.lookup('connection:facebook');
- return value;
- }
- }.property('firstName', 'lastName')
- });
+ facebook === facebook2; // => false
+ ```
- var person = Person.create();
+ @method optionsForType
+ @param {String} type
+ @param {Object} options
+ */
+ optionsForType: function(type, options) {
+ if (this.parent) { illegalChildOperation('optionsForType'); }
- person.set('fullName', 'Peter Wagenet');
- person.get('firstName'); // 'Peter'
- person.get('lastName'); // 'Wagenet'
- ```
+ this._typeOptions[type] = options;
+ },
- @class ComputedProperty
- @namespace Ember
- @extends Ember.Descriptor
- @constructor
- */
- function ComputedProperty(func, opts) {
- func.__ember_arity__ = func.length;
- this.func = func;
+ /**
+ @method options
+ @param {String} fullName
+ @param {Object} options
+ */
+ options: function(fullName, options) {
+ options = options || {};
+ var normalizedName = this.normalize(fullName);
+ this._options[normalizedName] = options;
+ },
- this._cacheable = (opts && opts.cacheable !== undefined) ? opts.cacheable : true;
- this._dependentKeys = opts && opts.dependentKeys;
- this._readOnly = opts && (opts.readOnly !== undefined || !!opts.readOnly) || false;
- }
+ /**
+ Used only via `injection`.
- ComputedProperty.prototype = new Descriptor();
+ Provides a specialized form of injection, specifically enabling
+ all objects of one type to be injected with a reference to another
+ object.
- var ComputedPropertyPrototype = ComputedProperty.prototype;
- ComputedPropertyPrototype._dependentKeys = undefined;
- ComputedPropertyPrototype._suspended = undefined;
- ComputedPropertyPrototype._meta = undefined;
+ For example, provided each object of type `controller` needed a `router`.
+ one would do the following:
- /**
- Properties are cacheable by default. Computed property will automatically
- cache the return value of your function until one of the dependent keys changes.
+ ```javascript
+ var container = new Container();
- Call `volatile()` to set it into non-cached mode. When in this mode
- the computed property will not automatically cache the return value.
+ container.register('router:main', Router);
+ container.register('controller:user', UserController);
+ container.register('controller:post', PostController);
- However, if a property is properly observable, there is no reason to disable
- caching.
+ container.typeInjection('controller', 'router', 'router:main');
- @method cacheable
- @param {Boolean} aFlag optional set to `false` to disable caching
- @return {Ember.ComputedProperty} this
- @chainable
- */
- ComputedPropertyPrototype.cacheable = function(aFlag) {
- this._cacheable = aFlag !== false;
- return this;
- };
+ var user = container.lookup('controller:user');
+ var post = container.lookup('controller:post');
- /**
- Call on a computed property to set it into non-cached mode. When in this
- mode the computed property will not automatically cache the return value.
+ user.router instanceof Router; //=> true
+ post.router instanceof Router; //=> true
- ```javascript
- var outsideService = Ember.Object.extend({
- value: function() {
- return OutsideService.getValue();
- }.property().volatile()
- }).create();
- ```
+ // both controllers share the same router
+ user.router === post.router; //=> true
+ ```
- @method volatile
- @return {Ember.ComputedProperty} this
- @chainable
- */
- ComputedPropertyPrototype.volatile = function() {
- return this.cacheable(false);
- };
+ @private
+ @method typeInjection
+ @param {String} type
+ @param {String} property
+ @param {String} fullName
+ */
+ typeInjection: function(type, property, fullName) {
+ Ember.assert('fullName must be a proper full name', validateFullName(fullName));
- /**
- Call on a computed property to set it into read-only mode. When in this
- mode the computed property will throw an error when set.
+ if (this.parent) { illegalChildOperation('typeInjection'); }
- ```javascript
- var Person = Ember.Object.extend({
- guid: function() {
- return 'guid-guid-guid';
- }.property().readOnly()
- });
+ var fullNameType = fullName.split(':')[0];
+ if (fullNameType === type) {
+ throw new Error('Cannot inject a `' + fullName +
+ '` on other ' + type +
+ '(s). Register the `' + fullName +
+ '` as a different type and perform the typeInjection.');
+ }
- var person = Person.create();
+ addTypeInjection(this.typeInjections, type, property, fullName);
+ },
- person.set('guid', 'new-guid'); // will throw an exception
- ```
+ /**
+ Defines injection rules.
- @method readOnly
- @return {Ember.ComputedProperty} this
- @chainable
- */
- ComputedPropertyPrototype.readOnly = function(readOnly) {
- this._readOnly = readOnly === undefined || !!readOnly;
- return this;
- };
+ These rules are used to inject dependencies onto objects when they
+ are instantiated.
- /**
- Sets the dependent keys on this computed property. Pass any number of
- arguments containing key paths that this computed property depends on.
+ Two forms of injections are possible:
- ```javascript
- var President = Ember.Object.extend({
- fullName: computed(function() {
- return this.get('firstName') + ' ' + this.get('lastName');
+ * Injecting one fullName on another fullName
+ * Injecting one fullName on a type
- // Tell Ember that this computed property depends on firstName
- // and lastName
- }).property('firstName', 'lastName')
- });
+ Example:
- var president = President.create({
- firstName: 'Barack',
- lastName: 'Obama',
- });
+ ```javascript
+ var container = new Container();
- president.get('fullName'); // 'Barack Obama'
- ```
+ container.register('source:main', Source);
+ container.register('model:user', User);
+ container.register('model:post', Post);
- @method property
- @param {String} path* zero or more property paths
- @return {Ember.ComputedProperty} this
- @chainable
- */
- ComputedPropertyPrototype.property = function() {
- var args;
+ // injecting one fullName on another fullName
+ // eg. each user model gets a post model
+ container.injection('model:user', 'post', 'model:post');
- var addArg = function (property) {
- args.push(property);
- };
+ // injecting one fullName on another type
+ container.injection('model', 'source', 'source:main');
- args = [];
- for (var i = 0, l = arguments.length; i < l; i++) {
- expandProperties(arguments[i], addArg);
- }
+ var user = container.lookup('model:user');
+ var post = container.lookup('model:post');
- this._dependentKeys = args;
- return this;
- };
+ user.source instanceof Source; //=> true
+ post.source instanceof Source; //=> true
- /**
- In some cases, you may want to annotate computed properties with additional
- metadata about how they function or what values they operate on. For example,
- computed property functions may close over variables that are then no longer
- available for introspection.
+ user.post instanceof Post; //=> true
- You can pass a hash of these values to a computed property like this:
+ // and both models share the same source
+ user.source === post.source; //=> true
+ ```
- ```
- person: function() {
- var personId = this.get('personId');
- return App.Person.create({ id: personId });
- }.property().meta({ type: App.Person })
- ```
+ @method injection
+ @param {String} factoryName
+ @param {String} property
+ @param {String} injectionName
+ */
+ injection: function(fullName, property, injectionName) {
+ if (this.parent) { illegalChildOperation('injection'); }
- The hash that you pass to the `meta()` function will be saved on the
- computed property descriptor under the `_meta` key. Ember runtime
- exposes a public API for retrieving these values from classes,
- via the `metaForProperty()` function.
+ validateFullName(injectionName);
+ var normalizedInjectionName = this.normalize(injectionName);
- @method meta
- @param {Hash} meta
- @chainable
- */
+ if (fullName.indexOf(':') === -1) {
+ return this.typeInjection(fullName, property, normalizedInjectionName);
+ }
- ComputedPropertyPrototype.meta = function(meta) {
- if (arguments.length === 0) {
- return this._meta || {};
- } else {
- this._meta = meta;
- return this;
- }
- };
+ Ember.assert('fullName must be a proper full name', validateFullName(fullName));
+ var normalizedName = this.normalize(fullName);
- /* impl descriptor API */
- ComputedPropertyPrototype.didChange = function(obj, keyName) {
- // _suspended is set via a CP.set to ensure we don't clear
- // the cached value set by the setter
- if (this._cacheable && this._suspended !== obj) {
- var meta = metaFor(obj);
- if (meta.cache[keyName] !== undefined) {
- meta.cache[keyName] = undefined;
- removeDependentKeys(this, obj, keyName, meta);
+ if (this.cache[normalizedName]) {
+ throw new Error("Attempted to register an injection for a type that has already been looked up. ('" +
+ normalizedName + "', '" +
+ property + "', '" +
+ injectionName + "')");
}
- }
- };
- function finishChains(chainNodes)
- {
- for (var i=0, l=chainNodes.length; i true
+ ```
- chainNodes = meta.chainWatchers && meta.chainWatchers[keyName];
- if (chainNodes) { finishChains(chainNodes); }
- addDependentKeys(this, obj, keyName, meta);
- } else {
- ret = this.func.call(obj, keyName);
- }
- return ret;
- };
+ @private
+ @method factoryTypeInjection
+ @param {String} type
+ @param {String} property
+ @param {String} fullName
+ */
+ factoryTypeInjection: function(type, property, fullName) {
+ if (this.parent) { illegalChildOperation('factoryTypeInjection'); }
- /**
- Set the value of a computed property. If the function that backs your
- computed property does not accept arguments then the default action for
- setting would be to define the property on the current object, and set
- the value of the property to the value being set.
+ addTypeInjection(this.factoryTypeInjections, type, property, this.normalize(fullName));
+ },
- Generally speaking if you intend for your computed property to be set
- your backing function should accept either two or three arguments.
+ /**
+ Defines factory injection rules.
- @method set
- @param {String} keyName The key being accessed.
- @param {Object} newValue The new value being assigned.
- @param {String} oldValue The old value being replaced.
- @return {Object} The return value of the function backing the CP.
- */
- ComputedPropertyPrototype.set = function(obj, keyName, value) {
- var cacheable = this._cacheable,
- func = this.func,
- meta = metaFor(obj, cacheable),
- oldSuspended = this._suspended,
- hadCachedValue = false,
- cache = meta.cache,
- funcArgLength, cachedValue, ret;
+ Similar to regular injection rules, but are run against factories, via
+ `Container#lookupFactory`.
- if (this._readOnly) {
- throw new EmberError('Cannot set read-only property "' + keyName + '" on object: ' + inspect(obj));
- }
+ These rules are used to inject objects onto factories when they
+ are looked up.
- this._suspended = obj;
+ Two forms of injections are possible:
- try {
+ * Injecting one fullName on another fullName
+ * Injecting one fullName on a type
- if (cacheable && cache[keyName] !== undefined) {
- cachedValue = cache[keyName];
- hadCachedValue = true;
- }
-
- // Check if the CP has been wrapped. If it has, use the
- // length from the wrapped function.
-
- funcArgLength = func.wrappedFunction ? func.wrappedFunction.__ember_arity__ : func.__ember_arity__;
-
- // For backwards-compatibility with computed properties
- // that check for arguments.length === 2 to determine if
- // they are being get or set, only pass the old cached
- // value if the computed property opts into a third
- // argument.
- if (funcArgLength === 3) {
- ret = func.call(obj, keyName, value, cachedValue);
- } else if (funcArgLength === 2) {
- ret = func.call(obj, keyName, value);
- } else {
- defineProperty(obj, keyName, null, cachedValue);
- set(obj, keyName, value);
- return;
- }
+ Example:
- if (hadCachedValue && cachedValue === ret) { return; }
+ ```javascript
+ var container = new Container();
- var watched = meta.watching[keyName];
- if (watched) { propertyWillChange(obj, keyName); }
+ container.register('store:main', Store);
+ container.register('store:secondary', OtherStore);
+ container.register('model:user', User);
+ container.register('model:post', Post);
- if (hadCachedValue) {
- cache[keyName] = undefined;
- }
+ // injecting one fullName on another type
+ container.factoryInjection('model', 'store', 'store:main');
- if (cacheable) {
- if (!hadCachedValue) {
- addDependentKeys(this, obj, keyName, meta);
- }
- if (ret === undefined) {
- cache[keyName] = UNDEFINED;
- } else {
- cache[keyName] = ret;
- }
- }
+ // injecting one fullName on another fullName
+ container.factoryInjection('model:post', 'secondaryStore', 'store:secondary');
- if (watched) { propertyDidChange(obj, keyName); }
- } finally {
- this._suspended = oldSuspended;
- }
- return ret;
- };
+ var UserFactory = container.lookupFactory('model:user');
+ var PostFactory = container.lookupFactory('model:post');
+ var store = container.lookup('store:main');
- /* called before property is overridden */
- ComputedPropertyPrototype.teardown = function(obj, keyName) {
- var meta = metaFor(obj);
+ UserFactory.store instanceof Store; //=> true
+ UserFactory.secondaryStore instanceof OtherStore; //=> false
- if (keyName in meta.cache) {
- removeDependentKeys(this, obj, keyName, meta);
- }
+ PostFactory.store instanceof Store; //=> true
+ PostFactory.secondaryStore instanceof OtherStore; //=> true
- if (this._cacheable) { delete meta.cache[keyName]; }
+ // and both models share the same source instance
+ UserFactory.store === PostFactory.store; //=> true
+ ```
- return null; // no value to restore
- };
+ @method factoryInjection
+ @param {String} factoryName
+ @param {String} property
+ @param {String} injectionName
+ */
+ factoryInjection: function(fullName, property, injectionName) {
+ if (this.parent) { illegalChildOperation('injection'); }
+ var normalizedName = this.normalize(fullName);
+ var normalizedInjectionName = this.normalize(injectionName);
- /**
- This helper returns a new property descriptor that wraps the passed
- computed property function. You can use this helper to define properties
- with mixins or via `Ember.defineProperty()`.
+ validateFullName(injectionName);
- The function you pass will be used to both get and set property values.
- The function should accept two parameters, key and value. If value is not
- undefined you should set the value first. In either case return the
- current value of the property.
+ if (fullName.indexOf(':') === -1) {
+ return this.factoryTypeInjection(normalizedName, property, normalizedInjectionName);
+ }
- @method computed
- @for Ember
- @param {Function} func The computed property function.
- @return {Ember.ComputedProperty} property descriptor instance
- */
- function computed(func) {
- var args;
+ Ember.assert('fullName must be a proper full name', validateFullName(fullName));
- if (arguments.length > 1) {
- args = a_slice.call(arguments, 0, -1);
- func = a_slice.call(arguments, -1)[0];
- }
+ if (this.factoryCache[normalizedName]) {
+ throw new Error('Attempted to register a factoryInjection for a type that has already ' +
+ 'been looked up. (\'' + normalizedName + '\', \'' + property + '\', \'' + injectionName + '\')');
+ }
- if (typeof func !== "function") {
- throw new EmberError("Computed Property declared without a property function");
- }
+ addInjection(initRules(this.factoryInjections, normalizedName), property, normalizedInjectionName);
+ },
- var cp = new ComputedProperty(func);
+ /**
+ A depth first traversal, destroying the container, its descendant containers and all
+ their managed objects.
- if (args) {
- cp.property.apply(cp, args);
- }
+ @method destroy
+ */
+ destroy: function() {
+ for (var i = 0, length = this.children.length; i < length; i++) {
+ this.children[i].destroy();
+ }
- return cp;
- };
+ this.children = [];
- /**
- Returns the cached value for a property, if one exists.
- This can be useful for peeking at the value of a computed
- property that is generated lazily, without accidentally causing
- it to be created.
+ eachDestroyable(this, function(item) {
+ item.destroy();
+ });
- @method cacheFor
- @for Ember
- @param {Object} obj the object whose property you want to check
- @param {String} key the name of the property whose cached value you want
- to return
- @return {Object} the cached value
- */
- function cacheFor(obj, key) {
- var meta = obj[META_KEY],
- cache = meta && meta.cache,
- ret = cache && cache[key];
+ this.parent = undefined;
+ this.isDestroyed = true;
+ },
- if (ret === UNDEFINED) { return undefined; }
- return ret;
- };
+ /**
+ @method reset
+ */
+ reset: function() {
+ for (var i = 0, length = this.children.length; i < length; i++) {
+ resetCache(this.children[i]);
+ }
- cacheFor.set = function(cache, key, value) {
- if (value === undefined) {
- cache[key] = UNDEFINED;
- } else {
- cache[key] = value;
+ resetCache(this);
}
};
- cacheFor.get = function(cache, key) {
- var ret = cache[key];
- if (ret === UNDEFINED) { return undefined; }
- return ret;
- };
+ function resolve(container, normalizedName) {
+ var cached = container.resolveCache[normalizedName];
+ if (cached) { return cached; }
- cacheFor.remove = function(cache, key) {
- cache[key] = undefined;
- };
+ var resolved = container.resolver(normalizedName) || container.registry[normalizedName];
+ container.resolveCache[normalizedName] = resolved;
- function getProperties(self, propertyNames) {
- var ret = {};
- for(var i = 0; i < propertyNames.length; i++) {
- ret[propertyNames[i]] = get(self, propertyNames[i]);
- }
- return ret;
+ return resolved;
}
- function registerComputed(name, macro) {
- computed[name] = function(dependentKey) {
- var args = a_slice.call(arguments);
- return computed(dependentKey, function() {
- return macro.apply(this, args);
- });
- };
- };
+ function has(container, fullName){
+ if (container.cache[fullName]) {
+ return true;
+ }
- function registerComputedWithProperties(name, macro) {
- computed[name] = function() {
- var properties = a_slice.call(arguments);
+ return container.resolve(fullName) !== undefined;
+ }
- var computedFunc = computed(function() {
- return macro.apply(this, [getProperties(this, properties)]);
- });
+ function lookup(container, fullName, options) {
+ options = options || {};
- return computedFunc.property.apply(computedFunc, properties);
- };
- };
+ if (container.cache[fullName] && options.singleton !== false) {
+ return container.cache[fullName];
+ }
- /**
- A computed property that returns true if the value of the dependent
- property is null, an empty string, empty array, or empty function.
+ var value = instantiate(container, fullName);
- Example
+ if (value === undefined) { return; }
- ```javascript
- var ToDoList = Ember.Object.extend({
- done: Ember.computed.empty('todos')
- });
+ if (isSingleton(container, fullName) && options.singleton !== false) {
+ container.cache[fullName] = value;
+ }
- var todoList = ToDoList.create({
- todos: ['Unit Test', 'Documentation', 'Release']
- });
+ return value;
+ }
- todoList.get('done'); // false
- todoList.get('todos').clear();
- todoList.get('done'); // true
- ```
+ function illegalChildOperation(operation) {
+ throw new Error(operation + ' is not currently supported on child containers');
+ }
- @since 1.6.0
- @method computed.empty
- @for Ember
- @param {String} dependentKey
- @return {Ember.ComputedProperty} computed property which negate
- the original value for property
- */
- computed.empty = function (dependentKey) {
- return computed(dependentKey + '.length', function () {
- return isEmpty(get(this, dependentKey));
- });
- };
+ function isSingleton(container, fullName) {
+ var singleton = option(container, fullName, 'singleton');
- /**
- A computed property that returns true if the value of the dependent
- property is NOT null, an empty string, empty array, or empty function.
+ return singleton !== false;
+ }
- Note: When using `computed.notEmpty` to watch an array make sure to
- use the `array.[]` syntax so the computed can subscribe to transitions
- from empty to non-empty states.
+ function buildInjections(container, injections) {
+ var hash = {};
- Example
+ if (!injections) { return hash; }
- ```javascript
- var Hamster = Ember.Object.extend({
- hasStuff: Ember.computed.notEmpty('backpack.[]')
- });
+ validateInjections(container, injections);
- var hamster = Hamster.create({ backpack: ['Food', 'Sleeping Bag', 'Tent'] });
+ var injection;
- hamster.get('hasStuff'); // true
- hamster.get('backpack').clear(); // []
- hamster.get('hasStuff'); // false
- ```
+ for (var i = 0, length = injections.length; i < length; i++) {
+ injection = injections[i];
+ hash[injection.property] = lookup(container, injection.fullName);
+ }
- @method computed.notEmpty
- @for Ember
- @param {String} dependentKey
- @return {Ember.ComputedProperty} computed property which returns true if
- original value for property is not empty.
- */
- registerComputed('notEmpty', function(dependentKey) {
- return !isEmpty(get(this, dependentKey));
- });
+ return hash;
+ }
- /**
- A computed property that returns true if the value of the dependent
- property is null or undefined. This avoids errors from JSLint complaining
- about use of ==, which can be technically confusing.
+ function validateInjections(container, injections) {
+ if (!injections) { return; }
- Example
+ var fullName;
- ```javascript
- var Hamster = Ember.Object.extend({
- isHungry: Ember.computed.none('food')
- });
+ for (var i = 0, length = injections.length; i < length; i++) {
+ fullName = injections[i].fullName;
- var hamster = Hamster.create();
+ if (!container.has(fullName)) {
+ throw new Error('Attempting to inject an unknown injection: `' + fullName + '`');
+ }
+ }
+ }
- hamster.get('isHungry'); // true
- hamster.set('food', 'Banana');
- hamster.get('isHungry'); // false
- hamster.set('food', null);
- hamster.get('isHungry'); // true
- ```
+ function option(container, fullName, optionName) {
+ var options = container._options[fullName];
- @method computed.none
- @for Ember
- @param {String} dependentKey
- @return {Ember.ComputedProperty} computed property which
- returns true if original value for property is null or undefined.
- */
- registerComputed('none', function(dependentKey) {
- return isNone(get(this, dependentKey));
- });
+ if (options && options[optionName] !== undefined) {
+ return options[optionName];
+ }
- /**
- A computed property that returns the inverse boolean value
- of the original value for the dependent property.
+ var type = fullName.split(':')[0];
+ options = container._typeOptions[type];
- Example
+ if (options) {
+ return options[optionName];
+ }
+ }
- ```javascript
- var User = Ember.Object.extend({
- isAnonymous: Ember.computed.not('loggedIn')
- });
+ function factoryFor(container, fullName) {
+ var cache = container.factoryCache;
+ if (cache[fullName]) {
+ return cache[fullName];
+ }
+ var factory = container.resolve(fullName);
+ if (factory === undefined) { return; }
- var user = User.create({loggedIn: false});
+ var type = fullName.split(':')[0];
+ if (!factory || typeof factory.extend !== 'function' || (!Ember.MODEL_FACTORY_INJECTIONS && type === 'model')) {
+ if (factory && typeof factory._onLookup === 'function') {
+ factory._onLookup(fullName);
+ }
- user.get('isAnonymous'); // true
- user.set('loggedIn', true);
- user.get('isAnonymous'); // false
- ```
+ // TODO: think about a 'safe' merge style extension
+ // for now just fallback to create time injection
+ cache[fullName] = factory;
+ return factory;
+ } else {
+ var injections = injectionsFor(container, fullName);
+ var factoryInjections = factoryInjectionsFor(container, fullName);
- @method computed.not
- @for Ember
- @param {String} dependentKey
- @return {Ember.ComputedProperty} computed property which returns
- inverse of the original value for property
- */
- registerComputed('not', function(dependentKey) {
- return !get(this, dependentKey);
- });
+ factoryInjections._toString = container.makeToString(factory, fullName);
- /**
- A computed property that converts the provided dependent property
- into a boolean value.
+ var injectedFactory = factory.extend(injections);
+ injectedFactory.reopenClass(factoryInjections);
- ```javascript
- var Hamster = Ember.Object.extend({
- hasBananas: Ember.computed.bool('numBananas')
- });
+ if (factory && typeof factory._onLookup === 'function') {
+ factory._onLookup(fullName);
+ }
- var hamster = Hamster.create();
+ cache[fullName] = injectedFactory;
- hamster.get('hasBananas'); // false
- hamster.set('numBananas', 0);
- hamster.get('hasBananas'); // false
- hamster.set('numBananas', 1);
- hamster.get('hasBananas'); // true
- hamster.set('numBananas', null);
- hamster.get('hasBananas'); // false
- ```
+ return injectedFactory;
+ }
+ }
- @method computed.bool
- @for Ember
- @param {String} dependentKey
- @return {Ember.ComputedProperty} computed property which converts
- to boolean the original value for property
- */
- registerComputed('bool', function(dependentKey) {
- return !!get(this, dependentKey);
- });
+ function injectionsFor(container, fullName) {
+ var splitName = fullName.split(':');
+ var type = splitName[0];
+ var injections = [];
- /**
- A computed property which matches the original value for the
- dependent property against a given RegExp, returning `true`
- if they values matches the RegExp and `false` if it does not.
+ injections = injections.concat(container.typeInjections[type] || []);
+ injections = injections.concat(container.injections[fullName] || []);
- Example
+ injections = buildInjections(container, injections);
+ injections._debugContainerKey = fullName;
+ injections.container = container;
- ```javascript
- var User = Ember.Object.extend({
- hasValidEmail: Ember.computed.match('email', /^.+@.+\..+$/)
- });
+ return injections;
+ }
- var user = User.create({loggedIn: false});
+ function factoryInjectionsFor(container, fullName) {
+ var splitName = fullName.split(':');
+ var type = splitName[0];
+ var factoryInjections = [];
- user.get('hasValidEmail'); // false
- user.set('email', '');
- user.get('hasValidEmail'); // false
- user.set('email', 'ember_hamster@example.com');
- user.get('hasValidEmail'); // true
- ```
+ factoryInjections = factoryInjections.concat(container.factoryTypeInjections[type] || []);
+ factoryInjections = factoryInjections.concat(container.factoryInjections[fullName] || []);
- @method computed.match
- @for Ember
- @param {String} dependentKey
- @param {RegExp} regexp
- @return {Ember.ComputedProperty} computed property which match
- the original value for property against a given RegExp
- */
- registerComputed('match', function(dependentKey, regexp) {
- var value = get(this, dependentKey);
- return typeof value === 'string' ? regexp.test(value) : false;
- });
+ factoryInjections = buildInjections(container, factoryInjections);
+ factoryInjections._debugContainerKey = fullName;
- /**
- A computed property that returns true if the provided dependent property
- is equal to the given value.
+ return factoryInjections;
+ }
- Example
+ function normalizeInjectionsHash(hash) {
+ var injections = [];
- ```javascript
- var Hamster = Ember.Object.extend({
- napTime: Ember.computed.equal('state', 'sleepy')
- });
+ for (var key in hash) {
+ if (hash.hasOwnProperty(key)) {
+ Ember.assert("Expected a proper full name, given '" + hash[key] + "'", validateFullName(hash[key]));
- var hamster = Hamster.create();
+ addInjection(injections, key, hash[key]);
+ }
+ }
- hamster.get('napTime'); // false
- hamster.set('state', 'sleepy');
- hamster.get('napTime'); // true
- hamster.set('state', 'hungry');
- hamster.get('napTime'); // false
- ```
+ return injections;
+ }
- @method computed.equal
- @for Ember
- @param {String} dependentKey
- @param {String|Number|Object} value
- @return {Ember.ComputedProperty} computed property which returns true if
- the original value for property is equal to the given value.
- */
- registerComputed('equal', function(dependentKey, value) {
- return get(this, dependentKey) === value;
- });
+ function instantiate(container, fullName) {
+ var factory = factoryFor(container, fullName);
+ var lazyInjections, validationCache;
- /**
- A computed property that returns true if the provied dependent property
- is greater than the provided value.
+ if (option(container, fullName, 'instantiate') === false) {
+ return factory;
+ }
- Example
+ if (factory) {
+ if (typeof factory.create !== 'function') {
+ throw new Error('Failed to create an instance of \'' + fullName + '\'. ' +
+ 'Most likely an improperly defined class or an invalid module export.');
+ }
- ```javascript
- var Hamster = Ember.Object.extend({
- hasTooManyBananas: Ember.computed.gt('numBananas', 10)
- });
+
+ validationCache = container.validationCache;
- var hamster = Hamster.create();
+ // Ensure that all lazy injections are valid at instantiation time
+ if (!validationCache[fullName] && typeof factory._lazyInjections === 'function') {
+ lazyInjections = factory._lazyInjections();
- hamster.get('hasTooManyBananas'); // false
- hamster.set('numBananas', 3);
- hamster.get('hasTooManyBananas'); // false
- hamster.set('numBananas', 11);
- hamster.get('hasTooManyBananas'); // true
- ```
+ validateInjections(container, normalizeInjectionsHash(lazyInjections));
+ }
- @method computed.gt
- @for Ember
- @param {String} dependentKey
- @param {Number} value
- @return {Ember.ComputedProperty} computed property which returns true if
- the original value for property is greater then given value.
- */
- registerComputed('gt', function(dependentKey, value) {
- return get(this, dependentKey) > value;
- });
+ validationCache[fullName] = true;
+
- /**
- A computed property that returns true if the provided dependent property
- is greater than or equal to the provided value.
+ if (typeof factory.extend === 'function') {
+ // assume the factory was extendable and is already injected
+ return factory.create();
+ } else {
+ // assume the factory was extendable
+ // to create time injections
+ // TODO: support new'ing for instantiation and merge injections for pure JS Functions
+ return factory.create(injectionsFor(container, fullName));
+ }
+ }
+ }
- Example
+ function eachDestroyable(container, callback) {
+ var cache = container.cache;
+ var keys = emberKeys(cache);
+ var key, value;
- ```javascript
- var Hamster = Ember.Object.extend({
- hasTooManyBananas: Ember.computed.gte('numBananas', 10)
- });
+ for (var i = 0, l = keys.length; i < l; i++) {
+ key = keys[i];
+ value = cache[key];
- var hamster = Hamster.create();
+ if (option(container, key, 'instantiate') !== false) {
+ callback(value);
+ }
+ }
+ }
- hamster.get('hasTooManyBananas'); // false
- hamster.set('numBananas', 3);
- hamster.get('hasTooManyBananas'); // false
- hamster.set('numBananas', 10);
- hamster.get('hasTooManyBananas'); // true
- ```
+ function resetCache(container) {
+ eachDestroyable(container, function(value) {
+ value.destroy();
+ });
- @method computed.gte
- @for Ember
- @param {String} dependentKey
- @param {Number} value
- @return {Ember.ComputedProperty} computed property which returns true if
- the original value for property is greater or equal then given value.
- */
- registerComputed('gte', function(dependentKey, value) {
- return get(this, dependentKey) >= value;
- });
+ container.cache.dict = dictionary(null);
+ }
- /**
- A computed property that returns true if the provided dependent property
- is less than the provided value.
+ function addTypeInjection(rules, type, property, fullName) {
+ var injections = rules[type];
- Example
+ if (!injections) {
+ injections = [];
+ rules[type] = injections;
+ }
- ```javascript
- var Hamster = Ember.Object.extend({
- needsMoreBananas: Ember.computed.lt('numBananas', 3)
+ injections.push({
+ property: property,
+ fullName: fullName
});
+ }
- var hamster = Hamster.create();
+ var VALID_FULL_NAME_REGEXP = /^[^:]+.+:[^:]+$/;
+ function validateFullName(fullName) {
+ if (!VALID_FULL_NAME_REGEXP.test(fullName)) {
+ throw new TypeError('Invalid Fullname, expected: `type:name` got: ' + fullName);
+ }
+ return true;
+ }
- hamster.get('needsMoreBananas'); // true
- hamster.set('numBananas', 3);
- hamster.get('needsMoreBananas'); // false
- hamster.set('numBananas', 2);
- hamster.get('needsMoreBananas'); // true
- ```
+ function initRules(rules, factoryName) {
+ return rules[factoryName] || (rules[factoryName] = []);
+ }
- @method computed.lt
- @for Ember
- @param {String} dependentKey
- @param {Number} value
- @return {Ember.ComputedProperty} computed property which returns true if
- the original value for property is less then given value.
- */
- registerComputed('lt', function(dependentKey, value) {
- return get(this, dependentKey) < value;
- });
-
- /**
- A computed property that returns true if the provided dependent property
- is less than or equal to the provided value.
-
- Example
-
- ```javascript
- var Hamster = Ember.Object.extend({
- needsMoreBananas: Ember.computed.lte('numBananas', 3)
+ function addInjection(injections, property, injectionName) {
+ injections.push({
+ property: property,
+ fullName: injectionName
});
+ }
- var hamster = Hamster.create();
+ __exports__["default"] = Container;
+ });
+enifed("dag-map",
+ ["exports"],
+ function(__exports__) {
+ "use strict";
+ function visit(vertex, fn, visited, path) {
+ var name = vertex.name;
+ var vertices = vertex.incoming;
+ var names = vertex.incomingNames;
+ var len = names.length;
+ var i;
- hamster.get('needsMoreBananas'); // true
- hamster.set('numBananas', 5);
- hamster.get('needsMoreBananas'); // false
- hamster.set('numBananas', 3);
- hamster.get('needsMoreBananas'); // true
- ```
+ if (!visited) {
+ visited = {};
+ }
+ if (!path) {
+ path = [];
+ }
+ if (visited.hasOwnProperty(name)) {
+ return;
+ }
+ path.push(name);
+ visited[name] = true;
+ for (i = 0; i < len; i++) {
+ visit(vertices[names[i]], fn, visited, path);
+ }
+ fn(vertex, path);
+ path.pop();
+ }
- @method computed.lte
- @for Ember
- @param {String} dependentKey
- @param {Number} value
- @return {Ember.ComputedProperty} computed property which returns true if
- the original value for property is less or equal then given value.
- */
- registerComputed('lte', function(dependentKey, value) {
- return get(this, dependentKey) <= value;
- });
/**
- A computed property that performs a logical `and` on the
- original values for the provided dependent properties.
-
- Example
-
- ```javascript
- var Hamster = Ember.Object.extend({
- readyForCamp: Ember.computed.and('hasTent', 'hasBackpack')
- });
+ * DAG stands for Directed acyclic graph.
+ *
+ * It is used to build a graph of dependencies checking that there isn't circular
+ * dependencies. p.e Registering initializers with a certain precedence order.
+ *
+ * @class DAG
+ * @constructor
+ */
+ function DAG() {
+ this.names = [];
+ this.vertices = Object.create(null);
+ }
- var hamster = Hamster.create();
+ /**
+ * DAG Vertex
+ *
+ * @class Vertex
+ * @constructor
+ */
- hamster.get('readyForCamp'); // false
- hamster.set('hasTent', true);
- hamster.get('readyForCamp'); // false
- hamster.set('hasBackpack', true);
- hamster.get('readyForCamp'); // true
- ```
+ function Vertex(name) {
+ this.name = name;
+ this.incoming = {};
+ this.incomingNames = [];
+ this.hasOutgoing = false;
+ this.value = null;
+ }
- @method computed.and
- @for Ember
- @param {String} dependentKey*
- @return {Ember.ComputedProperty} computed property which performs
- a logical `and` on the values of all the original values for properties.
- */
- registerComputedWithProperties('and', function(properties) {
- for (var key in properties) {
- if (properties.hasOwnProperty(key) && !properties[key]) {
- return false;
- }
+ /**
+ * Adds a vertex entry to the graph unless it is already added.
+ *
+ * @private
+ * @method add
+ * @param {String} name The name of the vertex to add
+ */
+ DAG.prototype.add = function(name) {
+ if (!name) {
+ throw new Error("Can't add Vertex without name");
}
- return true;
- });
+ if (this.vertices[name] !== undefined) {
+ return this.vertices[name];
+ }
+ var vertex = new Vertex(name);
+ this.vertices[name] = vertex;
+ this.names.push(name);
+ return vertex;
+ };
/**
- A computed property which performs a logical `or` on the
- original values for the provided dependent properties.
-
- Example
-
- ```javascript
- var Hamster = Ember.Object.extend({
- readyForRain: Ember.computed.or('hasJacket', 'hasUmbrella')
- });
-
- var hamster = Hamster.create();
-
- hamster.get('readyForRain'); // false
- hamster.set('hasJacket', true);
- hamster.get('readyForRain'); // true
- ```
+ * Adds a vertex to the graph and sets its value.
+ *
+ * @private
+ * @method map
+ * @param {String} name The name of the vertex.
+ * @param value The value to put in the vertex.
+ */
+ DAG.prototype.map = function(name, value) {
+ this.add(name).value = value;
+ };
- @method computed.or
- @for Ember
- @param {String} dependentKey*
- @return {Ember.ComputedProperty} computed property which performs
- a logical `or` on the values of all the original values for properties.
- */
- registerComputedWithProperties('or', function(properties) {
- for (var key in properties) {
- if (properties.hasOwnProperty(key) && properties[key]) {
- return true;
+ /**
+ * Connects the vertices with the given names, adding them to the graph if
+ * necessary, only if this does not produce is any circular dependency.
+ *
+ * @private
+ * @method addEdge
+ * @param {String} fromName The name the vertex where the edge starts.
+ * @param {String} toName The name the vertex where the edge ends.
+ */
+ DAG.prototype.addEdge = function(fromName, toName) {
+ if (!fromName || !toName || fromName === toName) {
+ return;
+ }
+ var from = this.add(fromName);
+ var to = this.add(toName);
+ if (to.incoming.hasOwnProperty(fromName)) {
+ return;
+ }
+ function checkCycle(vertex, path) {
+ if (vertex.name === toName) {
+ throw new Error("cycle detected: " + toName + " <- " + path.join(" <- "));
}
}
- return false;
- });
+ visit(from, checkCycle);
+ from.hasOutgoing = true;
+ to.incoming[fromName] = from;
+ to.incomingNames.push(fromName);
+ };
/**
- A computed property that returns the first truthy value
- from a list of dependent properties.
-
- Example
-
- ```javascript
- var Hamster = Ember.Object.extend({
- hasClothes: Ember.computed.any('hat', 'shirt')
- });
-
- var hamster = Hamster.create();
-
- hamster.get('hasClothes'); // null
- hamster.set('shirt', 'Hawaiian Shirt');
- hamster.get('hasClothes'); // 'Hawaiian Shirt'
- ```
+ * Visits all the vertex of the graph calling the given function with each one,
+ * ensuring that the vertices are visited respecting their precedence.
+ *
+ * @method topsort
+ * @param {Function} fn The function to be invoked on each vertex.
+ */
+ DAG.prototype.topsort = function(fn) {
+ var visited = {};
+ var vertices = this.vertices;
+ var names = this.names;
+ var len = names.length;
+ var i, vertex;
- @method computed.any
- @for Ember
- @param {String} dependentKey*
- @return {Ember.ComputedProperty} computed property which returns
- the first truthy value of given list of properties.
- */
- registerComputedWithProperties('any', function(properties) {
- for (var key in properties) {
- if (properties.hasOwnProperty(key) && properties[key]) {
- return properties[key];
+ for (i = 0; i < len; i++) {
+ vertex = vertices[names[i]];
+ if (!vertex.hasOutgoing) {
+ visit(vertex, fn, visited);
}
}
- return null;
- });
+ };
/**
- A computed property that returns the array of values
- for the provided dependent properties.
-
- Example
-
- ```javascript
- var Hamster = Ember.Object.extend({
- clothes: Ember.computed.collect('hat', 'shirt')
- });
-
- var hamster = Hamster.create();
-
- hamster.get('clothes'); // [null, null]
- hamster.set('hat', 'Camp Hat');
- hamster.set('shirt', 'Camp Shirt');
- hamster.get('clothes'); // ['Camp Hat', 'Camp Shirt']
- ```
-
- @method computed.collect
- @for Ember
- @param {String} dependentKey*
- @return {Ember.ComputedProperty} computed property which maps
- values of all passed properties in to an array.
- */
- registerComputedWithProperties('collect', function(properties) {
- var res = [];
- for (var key in properties) {
- if (properties.hasOwnProperty(key)) {
- if (isNone(properties[key])) {
- res.push(null);
- } else {
- res.push(properties[key]);
+ * Adds a vertex with the given name and value to the graph and joins it with the
+ * vertices referenced in _before_ and _after_. If there isn't vertices with those
+ * names, they are added too.
+ *
+ * If either _before_ or _after_ are falsy/empty, the added vertex will not have
+ * an incoming/outgoing edge.
+ *
+ * @method addEdges
+ * @param {String} name The name of the vertex to be added.
+ * @param value The value of that vertex.
+ * @param before An string or array of strings with the names of the vertices before
+ * which this vertex must be visited.
+ * @param after An string or array of strings with the names of the vertex after
+ * which this vertex must be visited.
+ *
+ */
+ DAG.prototype.addEdges = function(name, value, before, after) {
+ var i;
+ this.map(name, value);
+ if (before) {
+ if (typeof before === 'string') {
+ this.addEdge(name, before);
+ } else {
+ for (i = 0; i < before.length; i++) {
+ this.addEdge(name, before[i]);
}
}
}
- return res;
- });
-
- /**
- Creates a new property that is an alias for another property
- on an object. Calls to `get` or `set` this property behave as
- though they were called on the original property.
-
- ```javascript
- var Person = Ember.Object.extend({
- name: 'Alex Matchneer',
- nomen: Ember.computed.alias('name')
- });
-
- var alex = Person.create();
-
- alex.get('nomen'); // 'Alex Matchneer'
- alex.get('name'); // 'Alex Matchneer'
-
- alex.set('nomen', '@machty');
- alex.get('name'); // '@machty'
- ```
-
- @method computed.alias
- @for Ember
- @param {String} dependentKey
- @return {Ember.ComputedProperty} computed property which creates an
- alias to the original value for property.
- */
- computed.alias = function(dependentKey) {
- return computed(dependentKey, function(key, value) {
- if (arguments.length > 1) {
- set(this, dependentKey, value);
- return get(this, dependentKey);
+ if (after) {
+ if (typeof after === 'string') {
+ this.addEdge(after, name);
} else {
- return get(this, dependentKey);
+ for (i = 0; i < after.length; i++) {
+ this.addEdge(after[i], name);
+ }
}
- });
+ }
};
- /**
- Where `computed.alias` aliases `get` and `set`, and allows for bidirectional
- data flow, `computed.oneWay` only provides an aliased `get`. The `set` will
- not mutate the upstream property, rather causes the current property to
- become the value set. This causes the downstream property to permanently
- diverge from the upstream property.
+ __exports__["default"] = DAG;
+ });
+enifed("dag-map.umd",
+ ["./dag-map"],
+ function(__dependency1__) {
+ "use strict";
+ var DAG = __dependency1__["default"];
- Example
+ /* global define:true module:true window: true */
+ if (typeof enifed === 'function' && enifed.amd) {
+ enifed(function() { return DAG; });
+ } else if (typeof module !== 'undefined' && module.exports) {
+ module.exports = DAG;
+ } else if (typeof this !== 'undefined') {
+ this['DAG'] = DAG;
+ }
+ });
+enifed("ember-application",
+ ["ember-metal/core","ember-runtime/system/lazy_load","ember-application/system/resolver","ember-application/system/application","ember-application/ext/controller"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__) {
+ "use strict";
+ var Ember = __dependency1__["default"];
+ var runLoadHooks = __dependency2__.runLoadHooks;
- ```javascript
- var User = Ember.Object.extend({
- firstName: null,
- lastName: null,
- nickName: Ember.computed.oneWay('firstName')
- });
+ /**
+ Ember Application
- var teddy = User.create({
- firstName: 'Teddy',
- lastName: 'Zeenny'
- });
+ @module ember
+ @submodule ember-application
+ @requires ember-views, ember-routing
+ */
- teddy.get('nickName'); // 'Teddy'
- teddy.set('nickName', 'TeddyBear'); // 'TeddyBear'
- teddy.get('firstName'); // 'Teddy'
- ```
+ var Resolver = __dependency3__.Resolver;
+ var DefaultResolver = __dependency3__["default"];
+ var Application = __dependency4__["default"];
+ // side effect of extending ControllerMixin
- @method computed.oneWay
- @for Ember
- @param {String} dependentKey
- @return {Ember.ComputedProperty} computed property which creates a
- one way computed property to the original value for property.
- */
- computed.oneWay = function(dependentKey) {
- return computed(dependentKey, function() {
- return get(this, dependentKey);
- });
- };
+ Ember.Application = Application;
+ Ember.Resolver = Resolver;
+ Ember.DefaultResolver = DefaultResolver;
-
+ runLoadHooks('Ember.Application', Application);
+ });
+enifed("ember-application/ext/controller",
+ ["ember-metal/core","ember-metal/property_get","ember-metal/error","ember-metal/utils","ember-metal/computed","ember-runtime/mixins/controller","ember-routing/system/controller_for","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) {
+ "use strict";
/**
- Where `computed.oneWay` provides oneWay bindings, `computed.readOnly` provides
- a readOnly one way binding. Very often when using `computed.oneWay` one does
- not also want changes to propogate back up, as they will replace the value.
-
- This prevents the reverse flow, and also throws an exception when it occurs.
+ @module ember
+ @submodule ember-application
+ */
- Example
+ var Ember = __dependency1__["default"];
+ // Ember.assert
+ var get = __dependency2__.get;
+ var EmberError = __dependency3__["default"];
+ var inspect = __dependency4__.inspect;
+ var computed = __dependency5__.computed;
+ var ControllerMixin = __dependency6__["default"];
+ var meta = __dependency4__.meta;
+ var controllerFor = __dependency7__["default"];
- ```javascript
- var User = Ember.Object.extend({
- firstName: null,
- lastName: null,
- nickName: Ember.computed.readOnly('firstName')
- });
+ function verifyNeedsDependencies(controller, container, needs) {
+ var dependency, i, l;
+ var missing = [];
- var teddy = User.create({
- firstName: 'Teddy',
- lastName: 'Zeenny'
- });
+ for (i=0, l=needs.length; i' );`
- teddy.get('firstName'); // 'Teddy'
- ```
+ Ember.assert(inspect(controller) + "#needs must not specify dependencies with periods in their names (" +
+ dependency + ")", dependency.indexOf('.') === -1);
- @method computed.readOnly
- @for Ember
- @param {String} dependentKey
- @return {Ember.ComputedProperty} computed property which creates a
- one way computed property to the original value for property.
- @since 1.5.0
- */
- computed.readOnly = function(dependentKey) {
- return computed(dependentKey, function() {
- return get(this, dependentKey);
- }).readOnly();
- };
- /**
- A computed property that acts like a standard getter and setter,
- but returns the value at the provided `defaultPath` if the
- property itself has not been set to a value
+ if (dependency.indexOf(':') === -1) {
+ dependency = "controller:" + dependency;
+ }
- Example
+ // Structure assert to still do verification but not string concat in production
+ if (!container.has(dependency)) {
+ missing.push(dependency);
+ }
+ }
+ if (missing.length) {
+ throw new EmberError(inspect(controller) + " needs [ " + missing.join(', ') +
+ " ] but " + (missing.length > 1 ? 'they' : 'it') + " could not be found");
+ }
+ }
- ```javascript
- var Hamster = Ember.Object.extend({
- wishList: Ember.computed.defaultTo('favoriteFood')
- });
+ var defaultControllersComputedProperty = computed(function() {
+ var controller = this;
- var hamster = Hamster.create({ favoriteFood: 'Banana' });
+ return {
+ needs: get(controller, 'needs'),
+ container: get(controller, 'container'),
+ unknownProperty: function(controllerName) {
+ var needs = this.needs;
+ var dependency, i, l;
- hamster.get('wishList'); // 'Banana'
- hamster.set('wishList', 'More Unit Tests');
- hamster.get('wishList'); // 'More Unit Tests'
- hamster.get('favoriteFood'); // 'Banana'
- ```
+ for (i=0, l=needs.length; i 0) {
+ Ember.assert(' `' + inspect(this) + ' specifies `needs`, but does ' +
+ "not have a container. Please ensure this controller was " +
+ "instantiated with a container.",
+ this.container || meta(this, false).descs.controllers !== defaultControllersComputedProperty);
- if (Ember.ENV) {
- // do nothing if Ember.ENV is already setup
- } else if ('undefined' !== typeof EmberENV) {
- Ember.ENV = EmberENV;
- } else if('undefined' !== typeof ENV) {
- Ember.ENV = ENV;
- } else {
- Ember.ENV = {};
- }
+ if (this.container) {
+ verifyNeedsDependencies(this, this.container, needs);
+ }
- Ember.config = Ember.config || {};
+ // if needs then initialize controllers proxy
+ get(this, 'controllers');
+ }
- // We disable the RANGE API by default for performance reasons
- if ('undefined' === typeof Ember.ENV.DISABLE_RANGE_API) {
- Ember.ENV.DISABLE_RANGE_API = true;
- }
+ this._super.apply(this, arguments);
+ },
- if ("undefined" === typeof MetamorphENV) {
- exports.MetamorphENV = {};
- }
+ /**
+ @method controllerFor
+ @see {Ember.Route#controllerFor}
+ @deprecated Use `needs` instead
+ */
+ controllerFor: function(controllerName) {
+ Ember.deprecate("Controller#controllerFor is deprecated, please use Controller#needs instead");
+ return controllerFor(get(this, 'container'), controllerName);
+ },
- MetamorphENV.DISABLE_RANGE_API = Ember.ENV.DISABLE_RANGE_API;
+ /**
+ Stores the instances of other controllers available from within
+ this controller. Any controller listed by name in the `needs`
+ property will be accessible by name through this property.
- /**
- Hash of enabled Canary features. Add to before creating your application.
+ ```javascript
+ App.CommentsController = Ember.ArrayController.extend({
+ needs: ['post'],
+ postTitle: function(){
+ var currentPost = this.get('controllers.post'); // instance of App.PostController
+ return currentPost.get('title');
+ }.property('controllers.post.title')
+ });
+ ```
- You can also define `ENV.FEATURES` if you need to enable features flagged at runtime.
+ @see {Ember.ControllerMixin#needs}
+ @property {Object} controllers
+ @default null
+ */
+ controllers: defaultControllersComputedProperty
+ });
- @class FEATURES
- @namespace Ember
- @static
- @since 1.1.0
+ __exports__["default"] = ControllerMixin;
+ });
+enifed("ember-application/system/application",
+ ["dag-map","container/container","ember-metal","ember-metal/property_get","ember-metal/property_set","ember-runtime/system/lazy_load","ember-runtime/system/namespace","ember-runtime/mixins/deferred","ember-application/system/resolver","ember-metal/platform","ember-metal/run_loop","ember-metal/utils","ember-runtime/controllers/controller","ember-metal/enumerable_utils","ember-runtime/controllers/object_controller","ember-runtime/controllers/array_controller","ember-views/views/select","ember-views/system/event_dispatcher","ember-views/system/jquery","ember-routing/system/route","ember-routing/system/router","ember-routing/location/hash_location","ember-routing/location/history_location","ember-routing/location/auto_location","ember-routing/location/none_location","ember-routing/system/cache","ember-extension-support/container_debug_adapter","ember-metal/core","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __dependency19__, __dependency20__, __dependency21__, __dependency22__, __dependency23__, __dependency24__, __dependency25__, __dependency26__, __dependency27__, __dependency28__, __exports__) {
+ "use strict";
+ /**
+ @module ember
+ @submodule ember-application
*/
+ var DAG = __dependency1__["default"];
+ var Container = __dependency2__["default"];
- Ember.FEATURES = Ember.ENV.FEATURES || {};
- /**
- Test that a feature is enabled. Parsed by Ember's build tools to leave
- experimental features out of beta/stable builds.
+ var Ember = __dependency3__["default"];
+ // Ember.FEATURES, Ember.deprecate, Ember.assert, Ember.libraries, LOG_VERSION, Namespace, BOOTED
+ var get = __dependency4__.get;
+ var set = __dependency5__.set;
+ var runLoadHooks = __dependency6__.runLoadHooks;
+ var Namespace = __dependency7__["default"];
+ var DeferredMixin = __dependency8__["default"];
+ var DefaultResolver = __dependency9__["default"];
+ var create = __dependency10__.create;
+ var run = __dependency11__["default"];
+ var canInvoke = __dependency12__.canInvoke;
+ var Controller = __dependency13__["default"];
+ var EnumerableUtils = __dependency14__["default"];
+ var ObjectController = __dependency15__["default"];
+ var ArrayController = __dependency16__["default"];
+ var SelectView = __dependency17__["default"];
+ var EventDispatcher = __dependency18__["default"];
+ var jQuery = __dependency19__["default"];
+ var Route = __dependency20__["default"];
+ var Router = __dependency21__["default"];
+ var HashLocation = __dependency22__["default"];
+ var HistoryLocation = __dependency23__["default"];
+ var AutoLocation = __dependency24__["default"];
+ var NoneLocation = __dependency25__["default"];
+ var BucketCache = __dependency26__["default"];
- You can define the following configuration options:
+ // this is technically incorrect (per @wycats)
+ // it should work properly with:
+ // `import ContainerDebugAdapter from 'ember-extension-support/container_debug_adapter';` but
+ // es6-module-transpiler 0.4.0 eagerly grabs the module (which is undefined)
- * `ENV.ENABLE_ALL_FEATURES` - force all features to be enabled.
- * `ENV.ENABLE_OPTIONAL_FEATURES` - enable any features that have not been explicitly
- enabled/disabled.
+ var ContainerDebugAdapter = __dependency27__["default"];
- @method isEnabled
- @param {String} feature
- @return {Boolean}
- @for Ember.FEATURES
- @since 1.1.0
- */
+ var K = __dependency28__.K;
- Ember.FEATURES.isEnabled = function(feature) {
- var featureValue = Ember.FEATURES[feature];
+ function props(obj) {
+ var properties = [];
- if (Ember.ENV.ENABLE_ALL_FEATURES) {
- return true;
- } else if (featureValue === true || featureValue === false || featureValue === undefined) {
- return featureValue;
- } else if (Ember.ENV.ENABLE_OPTIONAL_FEATURES) {
- return true;
- } else {
- return false;
+ for (var key in obj) {
+ properties.push(key);
}
- };
-
- // ..........................................................
- // BOOTSTRAP
- //
-
- /**
- Determines whether Ember should enhance some built-in object prototypes to
- provide a more friendly API. If enabled, a few methods will be added to
- `Function`, `String`, and `Array`. `Object.prototype` will not be enhanced,
- which is the one that causes most trouble for people.
-
- In general we recommend leaving this option set to true since it rarely
- conflicts with other code. If you need to turn it off however, you can
- define an `ENV.EXTEND_PROTOTYPES` config to disable it.
-
- @property EXTEND_PROTOTYPES
- @type Boolean
- @default true
- @for Ember
- */
- Ember.EXTEND_PROTOTYPES = Ember.ENV.EXTEND_PROTOTYPES;
- if (typeof Ember.EXTEND_PROTOTYPES === 'undefined') {
- Ember.EXTEND_PROTOTYPES = true;
+ return properties;
}
- /**
- Determines whether Ember logs a full stack trace during deprecation warnings
-
- @property LOG_STACKTRACE_ON_DEPRECATION
- @type Boolean
- @default true
- */
- Ember.LOG_STACKTRACE_ON_DEPRECATION = (Ember.ENV.LOG_STACKTRACE_ON_DEPRECATION !== false);
-
- /**
- Determines whether Ember should add ECMAScript 5 shims to older browsers.
-
- @property SHIM_ES5
- @type Boolean
- @default Ember.EXTEND_PROTOTYPES
- */
- Ember.SHIM_ES5 = (Ember.ENV.SHIM_ES5 === false) ? false : Ember.EXTEND_PROTOTYPES;
+ var librariesRegistered = false;
/**
- Determines whether Ember logs info about version of used libraries
+ An instance of `Ember.Application` is the starting point for every Ember
+ application. It helps to instantiate, initialize and coordinate the many
+ objects that make up your app.
- @property LOG_VERSION
- @type Boolean
- @default true
- */
- Ember.LOG_VERSION = (Ember.ENV.LOG_VERSION === false) ? false : true;
+ Each Ember app has one and only one `Ember.Application` object. In fact, the
+ very first thing you should do in your application is create the instance:
- /**
- Empty function. Useful for some operations. Always returns `this`.
+ ```javascript
+ window.App = Ember.Application.create();
+ ```
- @method K
- @private
- @return {Object}
- */
- Ember.K = function() { return this; };
+ Typically, the application object is the only global variable. All other
+ classes in your app should be properties on the `Ember.Application` instance,
+ which highlights its first role: a global namespace.
+ For example, if you define a view class, it might look like this:
- // Stub out the methods defined by the ember-debug package in case it's not loaded
+ ```javascript
+ App.MyView = Ember.View.extend();
+ ```
- if ('undefined' === typeof Ember.assert) { Ember.assert = Ember.K; }
- if ('undefined' === typeof Ember.warn) { Ember.warn = Ember.K; }
- if ('undefined' === typeof Ember.debug) { Ember.debug = Ember.K; }
- if ('undefined' === typeof Ember.runInDebug) { Ember.runInDebug = Ember.K; }
- if ('undefined' === typeof Ember.deprecate) { Ember.deprecate = Ember.K; }
- if ('undefined' === typeof Ember.deprecateFunc) {
- Ember.deprecateFunc = function(_, func) { return func; };
- }
+ By default, calling `Ember.Application.create()` will automatically initialize
+ your application by calling the `Ember.Application.initialize()` method. If
+ you need to delay initialization, you can call your app's `deferReadiness()`
+ method. When you are ready for your app to be initialized, call its
+ `advanceReadiness()` method.
- /**
- Previously we used `Ember.$.uuid`, however `$.uuid` has been removed from
- jQuery master. We'll just bootstrap our own uuid now.
+ You can define a `ready` method on the `Ember.Application` instance, which
+ will be run by Ember when the application is initialized.
- @property uuid
- @type Number
- @private
- */
- Ember.uuid = 0;
+ Because `Ember.Application` inherits from `Ember.Namespace`, any classes
+ you create will have useful string representations when calling `toString()`.
+ See the `Ember.Namespace` documentation for more information.
- __exports__["default"] = Ember;
- });
-define("ember-metal/enumerable_utils",
- ["ember-metal/array","exports"],
- function(__dependency1__, __exports__) {
- "use strict";
- var map, forEach, indexOf, splice, filter;
+ While you can think of your `Ember.Application` as a container that holds the
+ other classes in your application, there are several other responsibilities
+ going on under-the-hood that you may want to understand.
- var map = __dependency1__.map;
- var forEach = __dependency1__.forEach;
- var indexOf = __dependency1__.indexOf;
- var filter = __dependency1__.filter;
+ ### Event Delegation
- // ES6TODO: doesn't array polyfills already do this?
- map = Array.prototype.map || map;
- forEach = Array.prototype.forEach || forEach;
- indexOf = Array.prototype.indexOf || indexOf;
- filter = Array.prototype.filter || filter;
- splice = Array.prototype.splice;
+ Ember uses a technique called _event delegation_. This allows the framework
+ to set up a global, shared event listener instead of requiring each view to
+ do it manually. For example, instead of each view registering its own
+ `mousedown` listener on its associated element, Ember sets up a `mousedown`
+ listener on the `body`.
- /**
- * Defines some convenience methods for working with Enumerables.
- * `Ember.EnumerableUtils` uses `Ember.ArrayPolyfills` when necessary.
- *
- * @class EnumerableUtils
- * @namespace Ember
- * @static
- * */
- var utils = {
- /**
- * 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);
- },
+ If a `mousedown` event occurs, Ember will look at the target of the event and
+ start walking up the DOM node tree, finding corresponding views and invoking
+ their `mouseDown` method as it goes.
- /**
- * 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);
- },
+ `Ember.Application` has a number of default events that it listens for, as
+ well as a mapping from lowercase events to camel-cased view method names. For
+ example, the `keypress` event causes the `keyPress` method on the view to be
+ called, the `dblclick` event causes `doubleClick` to be called, and so on.
- /**
- * Calls the filter function on the passed object with a specified callback. This
- * uses `Ember.ArrayPolyfill`'s-filter method when necessary.
- *
- * @method filter
- * @param {Object} obj The object to call filter on
- * @param {Function} callback The callback to execute
- * @param {Object} thisArg Value to use as this when executing *callback*
- *
- * @return {Array} An array containing the filtered values
- * @since 1.4.0
- */
- filter: function(obj, callback, thisArg) {
- return obj.filter ? obj.filter.call(obj, callback, thisArg) : filter.call(obj, callback, thisArg);
- },
+ If there is a bubbling browser event that Ember does not listen for by
+ default, you can specify custom events and their corresponding view method
+ names by setting the application's `customEvents` property:
- /**
- * 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);
- },
+ ```javascript
+ var App = Ember.Application.create({
+ customEvents: {
+ // add support for the paste event
+ paste: 'paste'
+ }
+ });
+ ```
- /**
- * 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);
- });
- },
+ By default, the application sets up these event listeners on the document
+ body. However, in cases where you are embedding an Ember application inside
+ an existing page, you may want it to set up the listeners on an element
+ inside the body.
- /**
- * 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); }
- },
+ For example, if only events inside a DOM element with the ID of `ember-app`
+ should be delegated, set your application's `rootElement` property:
- /**
- * 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); }
- },
+ ```javascript
+ var App = Ember.Application.create({
+ rootElement: '#ember-app'
+ });
+ ```
- _replace: function(array, idx, amt, objects) {
- var args = [].concat(objects), chunk, ret = [],
- // https://code.google.com/p/chromium/issues/detail?id=56588
- size = 60000, start = idx, ends = amt, count;
+ The `rootElement` can be either a DOM element or a jQuery-compatible selector
+ string. Note that *views appended to the DOM outside the root element will
+ not receive events.* If you specify a custom root element, make sure you only
+ append views inside it!
- while (args.length) {
- count = ends > size ? size : ends;
- if (count <= 0) { count = 0; }
+ To learn more about the advantages of event delegation and the Ember view
+ layer, and a list of the event listeners that are setup by default, visit the
+ [Ember View Layer guide](http://emberjs.com/guides/understanding-ember/the-view-layer/#toc_event-delegation).
- chunk = args.splice(0, size);
- chunk = [start, count].concat(chunk);
+ ### Initializers
- start += size;
- ends -= count;
+ Libraries on top of Ember can add initializers, like so:
- ret = ret.concat(splice.apply(array, chunk));
- }
- return ret;
- },
+ ```javascript
+ Ember.Application.initializer({
+ name: 'api-adapter',
- /**
- * Replaces objects in an array with the passed objects.
- *
- * ```javascript
- * var array = [1,2,3];
- * Ember.EnumerableUtils.replace(array, 1, 2, [4, 5]); // [1, 4, 5]
- *
- * var array = [1,2,3];
- * Ember.EnumerableUtils.replace(array, 1, 1, [4, 5]); // [1, 4, 5, 3]
- *
- * var array = [1,2,3];
- * Ember.EnumerableUtils.replace(array, 10, 1, [4, 5]); // [1, 2, 3, 4, 5]
- * ```
- *
- * @method replace
- * @param {Array} array The array the objects should be inserted into.
- * @param {Number} idx Starting index in the array to replace. If *idx* >=
- * length, then append to the end of the array.
- * @param {Number} amt Number of elements that should be removed from the array,
- * starting at *idx*
- * @param {Array} objects An array of zero or more objects that should be
- * inserted into the array at *idx*
- *
- * @return {Array} The modified array.
- */
- replace: function(array, idx, amt, objects) {
- if (array.replace) {
- return array.replace(idx, amt, objects);
- } else {
- return utils._replace(array, idx, amt, objects);
+ initialize: function(container, application) {
+ application.register('api-adapter:main', ApiAdapter);
}
- },
+ });
+ ```
- /**
- * 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 = [];
+ Initializers provide an opportunity to access the container, which
+ organizes the different components of an Ember application. Additionally
+ they provide a chance to access the instantiated application. Beyond
+ being used for libraries, initializers are also a great way to organize
+ dependency injection or setup in your own application.
- utils.forEach(array1, function(element) {
- if (utils.indexOf(array2, element) >= 0) {
- intersection.push(element);
- }
- });
+ ### Routing
- return intersection;
- }
- };
+ In addition to creating your application's router, `Ember.Application` is
+ also responsible for telling the router when to start routing. Transitions
+ between routes can be logged with the `LOG_TRANSITIONS` flag, and more
+ detailed intra-transition logging can be logged with
+ the `LOG_TRANSITIONS_INTERNAL` flag:
- __exports__["default"] = utils;
- });
-define("ember-metal/error",
- ["ember-metal/platform","exports"],
- function(__dependency1__, __exports__) {
- "use strict";
- var create = __dependency1__.create;
+ ```javascript
+ var App = Ember.Application.create({
+ LOG_TRANSITIONS: true, // basic logging of successful transitions
+ LOG_TRANSITIONS_INTERNAL: true // detailed logging of all routing steps
+ });
+ ```
- var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
+ By default, the router will begin trying to translate the current URL into
+ application state once the browser emits the `DOMContentReady` event. If you
+ need to defer routing, you can call the application's `deferReadiness()`
+ method. Once routing can begin, call the `advanceReadiness()` method.
- /**
- A subclass of the JavaScript Error object for use in Ember.
+ If there is any setup required before routing begins, you can implement a
+ `ready()` method on your app that will be invoked immediately before routing
+ begins.
+ ```
- @class Error
+ @class Application
@namespace Ember
- @extends Error
- @constructor
+ @extends Ember.Namespace
*/
- var EmberError = function() {
- var tmp = Error.apply(this, arguments);
- // Adds a `stack` property to the given error object that will yield the
- // stack trace at the time captureStackTrace was called.
- // When collecting the stack trace all frames above the topmost call
- // to this function, including that call, will be left out of the
- // stack trace.
- // This is useful because we can hide Ember implementation details
- // that are not very helpful for the user.
- if (Error.captureStackTrace) {
- Error.captureStackTrace(this, Ember.Error);
- }
- // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
- for (var idx = 0; idx < errorProps.length; idx++) {
- this[errorProps[idx]] = tmp[errorProps[idx]];
- }
- };
+ var Application = Namespace.extend(DeferredMixin, {
+ _suppressDeferredDeprecation: true,
- EmberError.prototype = create(Error.prototype);
+ /**
+ The root DOM element of the Application. This can be specified as an
+ element or a
+ [jQuery-compatible selector string](http://api.jquery.com/category/selectors/).
- __exports__["default"] = EmberError;
- });
-define("ember-metal/events",
- ["ember-metal/core","ember-metal/utils","ember-metal/platform","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
- "use strict";
- /**
- @module ember-metal
- */
- var Ember = __dependency1__["default"];
- var meta = __dependency2__.meta;
- var META_KEY = __dependency2__.META_KEY;
- var tryFinally = __dependency2__.tryFinally;
- var apply = __dependency2__.apply;
- var applyStr = __dependency2__.applyStr;
- var create = __dependency3__.create;
+ This is the element that will be passed to the Application's,
+ `eventDispatcher`, which sets up the listeners for event delegation. Every
+ view in your application should be a child of the element you specify here.
- var a_slice = [].slice,
- metaFor = meta,
- /* listener flags */
- ONCE = 1, SUSPENDED = 2;
+ @property rootElement
+ @type DOMElement
+ @default 'body'
+ */
+ rootElement: 'body',
+ /**
+ The `Ember.EventDispatcher` responsible for delegating events to this
+ application's views.
- /*
- The event system uses a series of nested hashes to store listeners on an
- object. When a listener is registered, or when an event arrives, these
- hashes are consulted to determine which target and action pair to invoke.
+ The event dispatcher is created by the application at initialization time
+ and sets up event listeners on the DOM element described by the
+ application's `rootElement` property.
- The hashes are stored in the object's meta hash, and look like this:
+ See the documentation for `Ember.EventDispatcher` for more information.
- // Object's meta hash
- {
- listeners: { // variable name: `listenerSet`
- "foo:changed": [ // variable name: `actions`
- target, method, flags
- ]
- }
- }
+ @property eventDispatcher
+ @type Ember.EventDispatcher
+ @default null
+ */
+ eventDispatcher: null,
- */
+ /**
+ The DOM events for which the event dispatcher should listen.
- function indexOf(array, target, method) {
- var index = -1;
- // hashes are added to the end of the event array
- // so it makes sense to start searching at the end
- // of the array and search in reverse
- for (var i = array.length - 3 ; i >=0; i -= 3) {
- if (target === array[i] && method === array[i + 1]) {
- index = i; break;
- }
- }
- return index;
- }
+ By default, the application's `Ember.EventDispatcher` listens
+ for a set of standard DOM events, such as `mousedown` and
+ `keyup`, and delegates them to your application's `Ember.View`
+ instances.
- function actionsFor(obj, eventName) {
- var meta = metaFor(obj, true),
- actions;
+ If you would like additional bubbling events to be delegated to your
+ views, set your `Ember.Application`'s `customEvents` property
+ to a hash containing the DOM event name as the key and the
+ corresponding view method name as the value. For example:
- if (!meta.listeners) { meta.listeners = {}; }
+ ```javascript
+ var App = Ember.Application.create({
+ customEvents: {
+ // add support for the paste event
+ paste: 'paste'
+ }
+ });
+ ```
- if (!meta.hasOwnProperty('listeners')) {
- // setup inherited copy of the listeners object
- meta.listeners = create(meta.listeners);
- }
+ @property customEvents
+ @type Object
+ @default null
+ */
+ customEvents: null,
- actions = meta.listeners[eventName];
+ init: function() {
+ // Start off the number of deferrals at 1. This will be
+ // decremented by the Application's own `initialize` method.
+ this._readinessDeferrals = 1;
- // if there are actions, but the eventName doesn't exist in our listeners, then copy them from the prototype
- if (actions && !meta.listeners.hasOwnProperty(eventName)) {
- actions = meta.listeners[eventName] = meta.listeners[eventName].slice();
- } else if (!actions) {
- actions = meta.listeners[eventName] = [];
- }
+ if (!this.$) {
+ this.$ = jQuery;
+ }
+ this.__container__ = this.buildContainer();
- return actions;
- }
+ this.Router = this.defaultRouter();
- function listenersUnion(obj, eventName, otherActions) {
- var meta = obj[META_KEY],
- actions = meta && meta.listeners && meta.listeners[eventName];
+ this._super();
- if (!actions) { return; }
- for (var i = actions.length - 3; i >= 0; i -= 3) {
- var target = actions[i],
- method = actions[i+1],
- flags = actions[i+2],
- actionIndex = indexOf(otherActions, target, method);
+ this.scheduleInitialize();
- if (actionIndex === -1) {
- otherActions.push(target, method, flags);
+ if (!librariesRegistered) {
+ librariesRegistered = true;
+ Ember.libraries.registerCoreLibrary('jQuery', jQuery().jquery);
}
- }
- }
-
- function listenersDiff(obj, eventName, otherActions) {
- var meta = obj[META_KEY],
- actions = meta && meta.listeners && meta.listeners[eventName],
- diffActions = [];
- if (!actions) { return; }
- for (var i = actions.length - 3; i >= 0; i -= 3) {
- var target = actions[i],
- method = actions[i+1],
- flags = actions[i+2],
- actionIndex = indexOf(otherActions, target, method);
+ if (Ember.LOG_VERSION) {
+ // we only need to see this once per Application#init
+ Ember.LOG_VERSION = false;
+ var libs = Ember.libraries._registry;
- if (actionIndex !== -1) { continue; }
-
- otherActions.push(target, method, flags);
- diffActions.push(target, method, flags);
- }
+ var nameLengths = EnumerableUtils.map(libs, function(item) {
+ return get(item, 'name.length');
+ });
- return diffActions;
- }
+ var maxNameLength = Math.max.apply(this, nameLengths);
- /**
- Add an event listener
+ Ember.debug('-------------------------------');
+ for (var i = 0, l = libs.length; i < l; i++) {
+ var lib = libs[i];
+ var spaces = new Array(maxNameLength - lib.name.length + 1).join(' ');
+ Ember.debug([lib.name, spaces, ' : ', lib.version].join(''));
+ }
+ Ember.debug('-------------------------------');
+ }
+ },
- @method addListener
- @for Ember
- @param obj
- @param {String} eventName
- @param {Object|Function} targetOrMethod A target object or a function
- @param {Function|String} method A function or the name of a function to be called on `target`
- @param {Boolean} once A flag whether a function should only be called once
- */
- function addListener(obj, eventName, target, method, once) {
- Ember.assert("You must pass at least an object and event name to Ember.addListener", !!obj && !!eventName);
+ /**
+ Build the container for the current application.
- if (!method && 'function' === typeof target) {
- method = target;
- target = null;
- }
+ Also register a default application view in case the application
+ itself does not.
- var actions = actionsFor(obj, eventName),
- actionIndex = indexOf(actions, target, method),
- flags = 0;
+ @private
+ @method buildContainer
+ @return {Ember.Container} the configured container
+ */
+ buildContainer: function() {
+ var container = this.__container__ = Application.buildContainer(this);
- if (once) flags |= ONCE;
+ return container;
+ },
- if (actionIndex !== -1) { return; }
+ /**
+ If the application has not opted out of routing and has not explicitly
+ defined a router, supply a default router for the application author
+ to configure.
- actions.push(target, method, flags);
+ This allows application developers to do:
- if ('function' === typeof obj.didAddListener) {
- obj.didAddListener(eventName, target, method);
- }
- }
+ ```javascript
+ var App = Ember.Application.create();
- /**
- Remove an event listener
+ App.Router.map(function() {
+ this.resource('posts');
+ });
+ ```
- Arguments should match those passed to `Ember.addListener`.
+ @private
+ @method defaultRouter
+ @return {Ember.Router} the default router
+ */
- @method removeListener
- @for Ember
- @param obj
- @param {String} eventName
- @param {Object|Function} targetOrMethod A target object or a function
- @param {Function|String} method A function or the name of a function to be called on `target`
- */
- function removeListener(obj, eventName, target, method) {
- Ember.assert("You must pass at least an object and event name to Ember.removeListener", !!obj && !!eventName);
+ defaultRouter: function() {
+ if (this.Router === false) { return; }
+ var container = this.__container__;
- if (!method && 'function' === typeof target) {
- method = target;
- target = null;
- }
+ if (this.Router) {
+ container.unregister('router:main');
+ container.register('router:main', this.Router);
+ }
- function _removeListener(target, method) {
- var actions = actionsFor(obj, eventName),
- actionIndex = indexOf(actions, target, method);
+ return container.lookupFactory('router:main');
+ },
- // action doesn't exist, give up silently
- if (actionIndex === -1) { return; }
+ /**
+ Automatically initialize the application once the DOM has
+ become ready.
- actions.splice(actionIndex, 3);
+ The initialization itself is scheduled on the actions queue
+ which ensures that application loading finishes before
+ booting.
- if ('function' === typeof obj.didRemoveListener) {
- obj.didRemoveListener(eventName, target, method);
+ If you are asynchronously loading code, you should call
+ `deferReadiness()` to defer booting, and then call
+ `advanceReadiness()` once all of your code has finished
+ loading.
+
+ @private
+ @method scheduleInitialize
+ */
+ scheduleInitialize: function() {
+ if (!this.$ || this.$.isReady) {
+ run.schedule('actions', this, '_initialize');
+ } else {
+ this.$().ready(Ember.run.bind(this, '_initialize'));
}
- }
+ },
- if (method) {
- _removeListener(target, method);
- } else {
- var meta = obj[META_KEY],
- actions = meta && meta.listeners && meta.listeners[eventName];
+ /**
+ Use this to defer readiness until some condition is true.
- if (!actions) { return; }
- for (var i = actions.length - 3; i >= 0; i -= 3) {
- _removeListener(actions[i], actions[i+1]);
- }
- }
- }
+ Example:
- /**
- Suspend listener during callback.
+ ```javascript
+ var App = Ember.Application.create();
+
+ App.deferReadiness();
+ // Ember.$ is a reference to the jQuery object/function
+ Ember.$.getJSON('/auth-token', function(token) {
+ App.token = token;
+ App.advanceReadiness();
+ });
+ ```
- This should only be used by the target of the event listener
- when it is taking an action that would cause the event, e.g.
- an object might suspend its property change listener while it is
- setting that property.
+ This allows you to perform asynchronous setup logic and defer
+ booting your application until the setup has finished.
- @method suspendListener
- @for Ember
+ However, if the setup requires a loading UI, it might be better
+ to use the router for this purpose.
- @private
- @param obj
- @param {String} eventName
- @param {Object|Function} targetOrMethod A target object or a function
- @param {Function|String} method A function or the name of a function to be called on `target`
- @param {Function} callback
- */
- function suspendListener(obj, eventName, target, method, callback) {
- if (!method && 'function' === typeof target) {
- method = target;
- target = null;
- }
+ @method deferReadiness
+ */
+ deferReadiness: function() {
+ Ember.assert("You must call deferReadiness on an instance of Ember.Application", this instanceof Application);
+ Ember.assert("You cannot defer readiness since the `ready()` hook has already been called.", this._readinessDeferrals > 0);
+ this._readinessDeferrals++;
+ },
- var actions = actionsFor(obj, eventName),
- actionIndex = indexOf(actions, target, method);
+ /**
+ Call `advanceReadiness` after any asynchronous setup logic has completed.
+ Each call to `deferReadiness` must be matched by a call to `advanceReadiness`
+ or the application will never become ready and routing will not begin.
- if (actionIndex !== -1) {
- actions[actionIndex+2] |= SUSPENDED; // mark the action as suspended
- }
+ @method advanceReadiness
+ @see {Ember.Application#deferReadiness}
+ */
+ advanceReadiness: function() {
+ Ember.assert("You must call advanceReadiness on an instance of Ember.Application", this instanceof Application);
+ this._readinessDeferrals--;
- function tryable() { return callback.call(target); }
- function finalizer() { if (actionIndex !== -1) { actions[actionIndex+2] &= ~SUSPENDED; } }
+ if (this._readinessDeferrals === 0) {
+ run.once(this, this.didBecomeReady);
+ }
+ },
- return tryFinally(tryable, finalizer);
- }
+ /**
+ Registers a factory that can be used for dependency injection (with
+ `App.inject`) or for service lookup. Each factory is registered with
+ a full name including two parts: `type:name`.
- /**
- Suspends multiple listeners during a callback.
+ A simple example:
- @method suspendListeners
- @for Ember
+ ```javascript
+ var App = Ember.Application.create();
+
+ App.Orange = Ember.Object.extend();
+ App.register('fruit:favorite', App.Orange);
+ ```
- @private
- @param obj
- @param {Array} eventName Array of event names
- @param {Object|Function} targetOrMethod A target object or a function
- @param {Function|String} method A function or the name of a function to be called on `target`
- @param {Function} callback
- */
- function suspendListeners(obj, eventNames, target, method, callback) {
- if (!method && 'function' === typeof target) {
- method = target;
- target = null;
- }
+ Ember will resolve factories from the `App` namespace automatically.
+ For example `App.CarsController` will be discovered and returned if
+ an application requests `controller:cars`.
- var suspendedActions = [],
- actionsList = [],
- eventName, actions, i, l;
+ An example of registering a controller with a non-standard name:
- for (i=0, l=eventNames.length; i= 0; i -= 3) { // looping in reverse for once listeners
- var target = actions[i], method = actions[i+1], flags = actions[i+2];
- if (!method) { continue; }
- if (flags & SUSPENDED) { continue; }
- if (flags & ONCE) { removeListener(obj, eventName, target, method); }
- if (!target) { target = obj; }
- if ('string' === typeof method) {
- if (params) {
- applyStr(target, method, params);
- } else {
- target[method]();
- }
- } else {
- if (params) {
- apply(target, method, params);
- } else {
- method.call(target);
- }
- }
- }
- return true;
- }
+ ```javascript
+ var App = Ember.Application.create();
+ var Session = Ember.Object.extend({ isAuthenticated: false });
- /**
- @private
- @method hasListeners
- @for Ember
- @param obj
- @param {String} eventName
- */
- function hasListeners(obj, eventName) {
- var meta = obj[META_KEY],
- actions = meta && meta.listeners && meta.listeners[eventName];
+ // A factory must be registered before it can be injected
+ App.register('session:main', Session);
- return !!(actions && actions.length);
- }
+ // Inject 'session:main' onto all factories of the type 'controller'
+ // with the name 'session'
+ App.inject('controller', 'session', 'session:main');
- /**
- @private
- @method listenersFor
- @for Ember
- @param obj
- @param {String} eventName
- */
- function listenersFor(obj, eventName) {
- var ret = [];
- var meta = obj[META_KEY],
- actions = meta && meta.listeners && meta.listeners[eventName];
+ App.IndexController = Ember.Controller.extend({
+ isLoggedIn: Ember.computed.alias('session.isAuthenticated')
+ });
+ ```
- if (!actions) { return ret; }
+ Injections can also be performed on specific factories.
- for (var i = 0, l = actions.length; i < l; i += 3) {
- var target = actions[i],
- method = actions[i+1];
- ret.push([target, method]);
- }
+ ```javascript
+ App.inject(, , )
+ App.inject('route', 'source', 'source:main')
+ App.inject('route:application', 'email', 'model:email')
+ ```
- return ret;
- }
+ It is important to note that injections can only be performed on
+ classes that are instantiated by Ember itself. Instantiating a class
+ directly (via `create` or `new`) bypasses the dependency injection
+ system.
- /**
- Define a property as a function that should be executed when
- a specified event or events are triggered.
+ **Note:** Ember-Data instantiates its models in a unique manner, and consequently
+ injections onto models (or all models) will not work as expected. Injections
+ on models can be enabled by setting `Ember.MODEL_FACTORY_INJECTIONS`
+ to `true`.
+ @method inject
+ @param factoryNameOrType {String}
+ @param property {String}
+ @param injectionName {String}
+ **/
+ inject: function() {
+ var container = this.__container__;
+ container.injection.apply(container, arguments);
+ },
- ``` javascript
- var Job = Ember.Object.extend({
- logCompleted: Ember.on('completed', function() {
- console.log('Job completed!');
- })
- });
+ /**
+ Calling initialize manually is not supported.
- var job = Job.create();
+ Please see Ember.Application#advanceReadiness and
+ Ember.Application#deferReadiness.
- Ember.sendEvent(job, 'completed'); // Logs 'Job completed!'
- ```
+ @private
+ @deprecated
+ @method initialize
+ **/
+ initialize: function() {
+ Ember.deprecate('Calling initialize manually is not supported. Please see Ember.Application#advanceReadiness and Ember.Application#deferReadiness');
+ },
- @method on
- @for Ember
- @param {String} eventNames*
- @param {Function} func
- @return func
- */
- function on(){
- var func = a_slice.call(arguments, -1)[0],
- events = a_slice.call(arguments, 0, -1);
- func.__ember_listens__ = events;
- return func;
- };
+ /**
+ Initialize the application. This happens automatically.
- __exports__.on = on;
- __exports__.addListener = addListener;
- __exports__.removeListener = removeListener;
- __exports__.suspendListener = suspendListener;
- __exports__.suspendListeners = suspendListeners;
- __exports__.sendEvent = sendEvent;
- __exports__.hasListeners = hasListeners;
- __exports__.watchedEvents = watchedEvents;
- __exports__.listenersFor = listenersFor;
- __exports__.listenersDiff = listenersDiff;
- __exports__.listenersUnion = listenersUnion;
- });
-define("ember-metal/expand_properties",
- ["ember-metal/error","ember-metal/enumerable_utils","exports"],
- function(__dependency1__, __dependency2__, __exports__) {
- "use strict";
- var EmberError = __dependency1__["default"];
- var EnumerableUtils = __dependency2__["default"];
+ Run any initializers and run the application load hook. These hooks may
+ choose to defer readiness. For example, an authentication hook might want
+ to defer readiness until the auth token has been retrieved.
- /**
- @module ember-metal
+ @private
+ @method _initialize
*/
+ _initialize: function() {
+ if (this.isDestroyed) { return; }
- var forEach = EnumerableUtils.forEach,
- BRACE_EXPANSION = /^((?:[^\.]*\.)*)\{(.*)\}$/;
+ // At this point, the App.Router must already be assigned
+ if (this.Router) {
+ var container = this.__container__;
+ container.unregister('router:main');
+ container.register('router:main', this.Router);
+ }
- /**
- Expands `pattern`, invoking `callback` for each expansion.
+ this.runInitializers();
+ runLoadHooks('application', this);
- 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 an example see the last call below.
+ // At this point, any initializers or load hooks that would have wanted
+ // to defer readiness have fired. In general, advancing readiness here
+ // will proceed to didBecomeReady.
+ this.advanceReadiness();
- Example
- ```js
- function echo(arg){ console.log(arg); }
+ return this;
+ },
- 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'
- ```
+ /**
+ Reset the application. This is typically used only in tests. It cleans up
+ the application in the following order:
- @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.
- */
- function expandProperties(pattern, callback) {
- var match, prefix, list;
+ 1. Deactivate existing routes
+ 2. Destroy all objects in the container
+ 3. Create a new application container
+ 4. Re-route to the existing url
- if (pattern.indexOf(' ') > -1) {
- throw new EmberError('Brace expanded properties cannot contain spaces, ' +
- 'e.g. `user.{firstName, lastName}` should be `user.{firstName,lastName}`');
- }
+ Typical Example:
- if (match = BRACE_EXPANSION.exec(pattern)) {
- prefix = match[1];
- list = match[2];
+ ```javascript
+ var App;
- forEach(list.split(','), function (suffix) {
- callback(prefix + suffix);
+ run(function() {
+ App = Ember.Application.create();
});
- } else {
- callback(pattern);
- }
- };
- __exports__["default"] = expandProperties;
- });
-define("ember-metal/get_properties",
- ["ember-metal/property_get","ember-metal/utils","exports"],
- function(__dependency1__, __dependency2__, __exports__) {
- "use strict";
- var get = __dependency1__.get;
- var typeOf = __dependency2__.typeOf;
+ module('acceptance test', {
+ setup: function() {
+ App.reset();
+ }
+ });
- /**
- To get multiple properties at once, call `Ember.getProperties`
- with an object followed by a list of strings or an array:
+ test('first test', function() {
+ // App is freshly reset
+ });
- ```javascript
- Ember.getProperties(record, 'firstName', 'lastName', 'zipCode');
- // { firstName: 'John', lastName: 'Doe', zipCode: '10011' }
- ```
+ test('second test', function() {
+ // App is again freshly reset
+ });
+ ```
- is equivalent to:
+ Advanced Example:
- ```javascript
- Ember.getProperties(record, ['firstName', 'lastName', 'zipCode']);
- // { firstName: 'John', lastName: 'Doe', zipCode: '10011' }
- ```
+ Occasionally you may want to prevent the app from initializing during
+ setup. This could enable extra configuration, or enable asserting prior
+ to the app becoming ready.
- @method getProperties
- @param obj
- @param {String...|Array} list of keys to get
- @return {Hash}
- */
- function getProperties(obj) {
- var ret = {},
- propertyNames = arguments,
- i = 1;
+ ```javascript
+ var App;
- if (arguments.length === 2 && typeOf(arguments[1]) === 'array') {
- i = 0;
- propertyNames = arguments[1];
- }
- for(var len = propertyNames.length; i < len; i++) {
- ret[propertyNames[i]] = get(obj, propertyNames[i]);
- }
- return ret;
- };
-
- __exports__["default"] = getProperties;
- });
-define("ember-metal/instrumentation",
- ["ember-metal/core","ember-metal/utils","exports"],
- function(__dependency1__, __dependency2__, __exports__) {
- "use strict";
- var Ember = __dependency1__["default"];
- var tryCatchFinally = __dependency2__.tryCatchFinally;
+ run(function() {
+ App = Ember.Application.create();
+ });
- /**
- The purpose of the Ember Instrumentation module is
- to provide efficient, general-purpose instrumentation
- for Ember.
+ module('acceptance test', {
+ setup: function() {
+ run(function() {
+ App.reset();
+ App.deferReadiness();
+ });
+ }
+ });
- Subscribe to a listener by using `Ember.subscribe`:
+ test('first test', function() {
+ ok(true, 'something before app is initialized');
- ```javascript
- Ember.subscribe("render", {
- before: function(name, timestamp, payload) {
+ run(function() {
+ App.advanceReadiness();
+ });
+
+ ok(true, 'something after app is initialized');
+ });
+ ```
- },
+ @method reset
+ **/
+ reset: function() {
+ this._readinessDeferrals = 1;
- after: function(name, timestamp, payload) {
+ function handleReset() {
+ var router = this.__container__.lookup('router:main');
+ router.reset();
- }
- });
- ```
+ run(this.__container__, 'destroy');
- If you return a value from the `before` callback, that same
- value will be passed as a fourth parameter to the `after`
- callback.
+ this.buildContainer();
- Instrument a block of code by using `Ember.instrument`:
+ run.schedule('actions', this, '_initialize');
+ }
- ```javascript
- Ember.instrument("render.handlebars", payload, function() {
- // rendering logic
- }, binding);
- ```
+ run.join(this, handleReset);
+ },
- Event names passed to `Ember.instrument` are namespaced
- by periods, from more general to more specific. Subscribers
- can listen for events by whatever level of granularity they
- are interested in.
+ /**
+ @private
+ @method runInitializers
+ */
+ runInitializers: function() {
+ var initializersByName = get(this.constructor, 'initializers');
+ var initializers = props(initializersByName);
+ var container = this.__container__;
+ var graph = new DAG();
+ var namespace = this;
+ var initializer;
- In the above example, the event is `render.handlebars`,
- and the subscriber listened for all events beginning with
- `render`. It would receive callbacks for events named
- `render`, `render.handlebars`, `render.container`, or
- even `render.handlebars.layout`.
+ for (var i = 0; i < initializers.length; i++) {
+ initializer = initializersByName[initializers[i]];
+ graph.addEdges(initializer.name, initializer.initialize, initializer.before, initializer.after);
+ }
- @class Instrumentation
- @namespace Ember
- @static
- */
- var subscribers = [], cache = {};
+ graph.topsort(function (vertex) {
+ var initializer = vertex.value;
+ Ember.assert("No application initializer named '" + vertex.name + "'", !!initializer);
+ initializer(container, namespace);
+ });
+ },
- var populateListeners = function(name) {
- var listeners = [], subscriber;
+ /**
+ @private
+ @method didBecomeReady
+ */
+ didBecomeReady: function() {
+ this.setupEventDispatcher();
+ this.ready(); // user hook
+ this.startRouting();
- for (var i=0, l=subscribers.length; i Ember.TEMPLATES['post']
+ 'template:posts/byline' //=> Ember.TEMPLATES['posts/byline']
+ 'template:posts.byline' //=> Ember.TEMPLATES['posts/byline']
+ 'template:blogPost' //=> Ember.TEMPLATES['blogPost']
+ // OR
+ // Ember.TEMPLATES['blog_post']
+ 'controller:post' //=> App.PostController
+ 'controller:posts.index' //=> App.PostsIndexController
+ 'controller:blog/post' //=> Blog.PostController
+ 'controller:basic' //=> Ember.Controller
+ 'route:post' //=> App.PostRoute
+ 'route:posts.index' //=> App.PostsIndexRoute
+ 'route:blog/post' //=> Blog.PostRoute
+ 'route:basic' //=> Ember.Route
+ 'view:post' //=> App.PostView
+ 'view:posts.index' //=> App.PostsIndexView
+ 'view:blog/post' //=> Blog.PostView
+ 'view:basic' //=> Ember.View
+ 'foo:post' //=> App.PostFoo
+ 'model:post' //=> App.Post
+ ```
- __exports__["default"] = Ember;
- });
-define("ember-metal/map",
- ["ember-metal/property_set","ember-metal/utils","ember-metal/array","ember-metal/platform","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) {
- "use strict";
- /**
- @module ember-metal
+ @class DefaultResolver
+ @namespace Ember
+ @extends Ember.Object
*/
+ var dictionary = __dependency8__["default"];
- /*
- JavaScript (before ES6) does not have a Map implementation. Objects,
- which are often used as dictionaries, may only have Strings as keys.
+ __exports__["default"] = EmberObject.extend({
+ /**
+ This will be set to the Application instance when it is
+ created.
- Because Ember has a way to get a unique identifier for every object
- via `Ember.guidFor`, we can implement a performant Map with arbitrary
- keys. Because it is commonly used in low-level bookkeeping, Map is
- implemented as a pure JavaScript object for performance.
+ @property namespace
+ */
+ namespace: null,
- This implementation follows the current iteration of the ES6 proposal for
- maps (http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets),
- with two exceptions. First, because we need our implementation to be pleasant
- on older browsers, we do not use the `delete` name (using `remove` instead).
- Second, as we do not have the luxury of in-VM iteration, we implement a
- forEach method for iteration.
+ init: function() {
+ this._parseNameCache = dictionary(null);
+ },
+ normalize: function(fullName) {
+ var split = fullName.split(':', 2);
+ var type = split[0];
+ var name = split[1];
- Map is mocked out to look like an Ember object, so you can do
- `Ember.Map.create()` for symmetry with other Ember classes.
- */
+ Ember.assert("Tried to normalize a container name without a colon (:) in it." +
+ " You probably tried to lookup a name that did not contain a type," +
+ " a colon, and a name. A proper lookup name would be `view:post`.", split.length === 2);
- var set = __dependency1__.set;
- var guidFor = __dependency2__.guidFor;
- var indexOf = __dependency3__.indexOf;var create = __dependency4__.create;
+ if (type !== 'template') {
+ var result = name;
- var copy = function(obj) {
- var output = {};
+ if (result.indexOf('.') > -1) {
+ result = result.replace(/\.(.)/g, function(m) {
+ return m.charAt(1).toUpperCase();
+ });
+ }
- for (var prop in obj) {
- if (obj.hasOwnProperty(prop)) { output[prop] = obj[prop]; }
- }
+ if (name.indexOf('_') > -1) {
+ result = result.replace(/_(.)/g, function(m) {
+ return m.charAt(1).toUpperCase();
+ });
+ }
- return output;
- };
+ return type + ':' + result;
+ } else {
+ return fullName;
+ }
+ },
- var copyMap = function(original, newObject) {
- var keys = original.keys.copy(),
- values = copy(original.values);
- newObject.keys = keys;
- newObject.values = values;
- newObject.length = original.length;
+ /**
+ This method is called via the container's resolver method.
+ It parses the provided `fullName` and then looks up and
+ returns the appropriate template or class.
- return newObject;
- };
+ @method resolve
+ @param {String} fullName the lookup string
+ @return {Object} the resolved factory
+ */
+ resolve: function(fullName) {
+ var parsedName = this.parseName(fullName);
+ var resolveMethodName = parsedName.resolveMethodName;
+ var resolved;
- /**
- This class is used internally by Ember and Ember Data.
- Please do not use it at this time. We plan to clean it up
- and add many tests soon.
+ if (!(parsedName.name && parsedName.type)) {
+ throw new TypeError('Invalid fullName: `' + fullName + '`, must be of the form `type:name` ');
+ }
- @class OrderedSet
- @namespace Ember
- @constructor
- @private
- */
- function OrderedSet() {
- this.clear();
- };
+ if (this[resolveMethodName]) {
+ resolved = this[resolveMethodName](parsedName);
+ }
- /**
- @method create
- @static
- @return {Ember.OrderedSet}
- */
- OrderedSet.create = function() {
- return new OrderedSet();
- };
+ if (!resolved) {
+ resolved = this.resolveOther(parsedName);
+ }
+ if (parsedName.root && parsedName.root.LOG_RESOLVER) {
+ this._logLookup(resolved, parsedName);
+ }
- OrderedSet.prototype = {
+ return resolved;
+ },
/**
- @method clear
+ Convert the string name of the form 'type:name' to
+ a Javascript object with the parsed aspects of the name
+ broken out.
+
+ @protected
+ @param {String} fullName the lookup string
+ @method parseName
*/
- clear: function() {
- this.presenceSet = {};
- this.list = [];
+
+ parseName: function(fullName) {
+ return this._parseNameCache[fullName] || (
+ this._parseNameCache[fullName] = this._parseName(fullName)
+ );
+ },
+
+ _parseName: function(fullName) {
+ var nameParts = fullName.split(':');
+ var type = nameParts[0], fullNameWithoutType = nameParts[1];
+ var name = fullNameWithoutType;
+ var namespace = get(this, 'namespace');
+ var root = namespace;
+
+ if (type !== 'template' && name.indexOf('/') !== -1) {
+ var parts = name.split('/');
+ name = parts[parts.length - 1];
+ var namespaceName = capitalize(parts.slice(0, -1).join('.'));
+ root = Namespace.byName(namespaceName);
+
+ Ember.assert('You are looking for a ' + name + ' ' + type +
+ ' in the ' + namespaceName +
+ ' namespace, but the namespace could not be found', root);
+ }
+
+ return {
+ fullName: fullName,
+ type: type,
+ fullNameWithoutType: fullNameWithoutType,
+ name: name,
+ root: root,
+ resolveMethodName: 'resolve' + classify(type)
+ };
},
/**
- @method add
- @param obj
+ Returns a human-readable description for a fullName. Used by the
+ Application namespace in assertions to describe the
+ precise name of the class that Ember is looking for, rather than
+ container keys.
+
+ @protected
+ @param {String} fullName the lookup string
+ @method lookupDescription
*/
- add: function(obj) {
- var guid = guidFor(obj),
- presenceSet = this.presenceSet,
- list = this.list;
+ lookupDescription: function(fullName) {
+ var parsedName = this.parseName(fullName);
+
+ if (parsedName.type === 'template') {
+ return 'template at ' + parsedName.fullNameWithoutType.replace(/\./g, '/');
+ }
- if (guid in presenceSet) { return; }
+ var description = parsedName.root + '.' + classify(parsedName.name);
- presenceSet[guid] = true;
- list.push(obj);
+ if (parsedName.type !== 'model') {
+ description += classify(parsedName.type);
+ }
+
+ return description;
},
+ makeToString: function(factory, fullName) {
+ return factory.toString();
+ },
/**
- @method remove
- @param obj
+ Given a parseName object (output from `parseName`), apply
+ the conventions expected by `Ember.Router`
+
+ @protected
+ @param {Object} parsedName a parseName object with the parsed
+ fullName lookup string
+ @method useRouterNaming
+ */
+ useRouterNaming: function(parsedName) {
+ parsedName.name = parsedName.name.replace(/\./g, '_');
+ if (parsedName.name === 'basic') {
+ parsedName.name = '';
+ }
+ },
+ /**
+ Look up the template in Ember.TEMPLATES
+
+ @protected
+ @param {Object} parsedName a parseName object with the parsed
+ fullName lookup string
+ @method resolveTemplate
*/
- remove: function(obj) {
- var guid = guidFor(obj),
- presenceSet = this.presenceSet,
- list = this.list;
+ resolveTemplate: function(parsedName) {
+ var templateName = parsedName.fullNameWithoutType.replace(/\./g, '/');
- delete presenceSet[guid];
+ if (Ember.TEMPLATES[templateName]) {
+ return Ember.TEMPLATES[templateName];
+ }
- var index = indexOf.call(list, obj);
- if (index > -1) {
- list.splice(index, 1);
+ templateName = decamelize(templateName);
+ if (Ember.TEMPLATES[templateName]) {
+ return Ember.TEMPLATES[templateName];
}
},
/**
- @method isEmpty
- @return {Boolean}
+ Lookup the view using `resolveOther`
+
+ @protected
+ @param {Object} parsedName a parseName object with the parsed
+ fullName lookup string
+ @method resolveView
*/
- isEmpty: function() {
- return this.list.length === 0;
+ resolveView: function(parsedName) {
+ this.useRouterNaming(parsedName);
+ return this.resolveOther(parsedName);
},
/**
- @method has
- @param obj
- @return {Boolean}
+ Lookup the controller using `resolveOther`
+
+ @protected
+ @param {Object} parsedName a parseName object with the parsed
+ fullName lookup string
+ @method resolveController
*/
- has: function(obj) {
- var guid = guidFor(obj),
- presenceSet = this.presenceSet;
+ resolveController: function(parsedName) {
+ this.useRouterNaming(parsedName);
+ return this.resolveOther(parsedName);
+ },
+ /**
+ Lookup the route using `resolveOther`
- return guid in presenceSet;
+ @protected
+ @param {Object} parsedName a parseName object with the parsed
+ fullName lookup string
+ @method resolveRoute
+ */
+ resolveRoute: function(parsedName) {
+ this.useRouterNaming(parsedName);
+ return this.resolveOther(parsedName);
},
/**
- @method forEach
- @param {Function} fn
- @param self
+ Lookup the model on the Application namespace
+
+ @protected
+ @param {Object} parsedName a parseName object with the parsed
+ fullName lookup string
+ @method resolveModel
*/
- forEach: function(fn, self) {
- // allow mutation during iteration
- var list = this.toArray();
+ resolveModel: function(parsedName) {
+ var className = classify(parsedName.name);
+ var factory = get(parsedName.root, className);
- for (var i = 0, j = list.length; i < j; i++) {
- fn.call(self, list[i]);
- }
+ if (factory) { return factory; }
},
+ /**
+ Look up the specified object (from parsedName) on the appropriate
+ namespace (usually on the Application)
+ @protected
+ @param {Object} parsedName a parseName object with the parsed
+ fullName lookup string
+ @method resolveHelper
+ */
+ resolveHelper: function(parsedName) {
+ return this.resolveOther(parsedName) || helpers[parsedName.fullNameWithoutType];
+ },
/**
- @method toArray
- @return {Array}
+ Look up the specified object (from parsedName) on the appropriate
+ namespace (usually on the Application)
+
+ @protected
+ @param {Object} parsedName a parseName object with the parsed
+ fullName lookup string
+ @method resolveOther
*/
- toArray: function() {
- return this.list.slice();
+ resolveOther: function(parsedName) {
+ var className = classify(parsedName.name) + classify(parsedName.type);
+ var factory = get(parsedName.root, className);
+ if (factory) { return factory; }
},
/**
- @method copy
- @return {Ember.OrderedSet}
+ @method _logLookup
+ @param {Boolean} found
+ @param {Object} parsedName
+ @private
*/
- copy: function() {
- var set = new OrderedSet();
+ _logLookup: function(found, parsedName) {
+ var symbol, padding;
- set.presenceSet = copy(this.presenceSet);
- set.list = this.toArray();
+ if (found) { symbol = '[✓]'; }
+ else { symbol = '[ ]'; }
- return set;
- }
- };
-
- /**
- A Map stores values indexed by keys. Unlike JavaScript's
- default Objects, the keys of a Map can be any JavaScript
- object.
+ if (parsedName.fullName.length > 60) {
+ padding = '.';
+ } else {
+ padding = new Array(60 - parsedName.fullName.length).join('.');
+ }
- Internally, a Map has two data structures:
+ Logger.info(symbol, parsedName.fullName, padding, this.lookupDescription(parsedName.fullName));
+ }
+ });
+ });
+enifed("ember-debug",
+ ["ember-metal/core","ember-metal/error","ember-metal/logger","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
+ "use strict";
+ /*global __fail__*/
- 1. `keys`: an OrderedSet of all of the existing keys
- 2. `values`: a JavaScript Object indexed by the `Ember.guidFor(key)`
+ var Ember = __dependency1__["default"];
+ var EmberError = __dependency2__["default"];
+ var Logger = __dependency3__["default"];
- When a key/value pair is added for the first time, we
- add the key to the `keys` OrderedSet, and create or
- replace an entry in `values`. When an entry is deleted,
- we delete its entry in `keys` and `values`.
+ /**
+ Ember Debug
- @class Map
- @namespace Ember
- @private
- @constructor
+ @module ember
+ @submodule ember-debug
*/
- var Map = Ember.Map = function() {
- this.keys = OrderedSet.create();
- this.values = {};
- };
/**
- @method create
- @static
+ @class Ember
*/
- Map.create = function() {
- return new Map();
- };
- Map.prototype = {
- /**
- This property will change as the number of objects in the map changes.
+ /**
+ Define an assertion that will throw an exception if the condition is not
+ met. Ember build tools will remove any calls to `Ember.assert()` when
+ doing a production build. Example:
- @property length
- @type number
- @default 0
- */
- length: 0,
+ ```javascript
+ // Test for truthiness
+ Ember.assert('Must pass a valid object', obj);
+ // Fail unconditionally
+ Ember.assert('This code path should never be run');
+ ```
- /**
- Retrieve the value associated with a given key.
+ @method assert
+ @param {String} desc A description of the assertion. This will become
+ the text of the Error thrown if the assertion fails.
+ @param {Boolean} test Must be truthy for the assertion to pass. If
+ falsy, an exception will be thrown.
+ */
+ Ember.assert = function(desc, test) {
+ var throwAssertion;
- @method get
- @param {*} key
- @return {*} the value associated with the key, or `undefined`
- */
- get: function(key) {
- var values = this.values,
- guid = guidFor(key);
+ if (Ember.typeOf(test) === 'function') {
+ throwAssertion = !test();
+ } else {
+ throwAssertion = !test;
+ }
- return values[guid];
- },
+ if (throwAssertion) {
+ throw new EmberError("Assertion Failed: " + desc);
+ }
+ };
- /**
- Adds a value to the map. If a value for the given key has already been
- provided, the new value will replace the old value.
- @method set
- @param {*} key
- @param {*} value
- */
- set: function(key, value) {
- var keys = this.keys,
- values = this.values,
- guid = guidFor(key);
+ /**
+ Display a warning with the provided message. Ember build tools will
+ remove any calls to `Ember.warn()` when doing a production build.
- keys.add(key);
- values[guid] = value;
- set(this, 'length', keys.list.length);
- },
+ @method warn
+ @param {String} message A warning to display.
+ @param {Boolean} test An optional boolean. If falsy, the warning
+ will be displayed.
+ */
+ Ember.warn = function(message, test) {
+ if (!test) {
+ Logger.warn("WARNING: "+message);
+ if ('trace' in Logger) {
+ Logger.trace();
+ }
+ }
+ };
- /**
- Removes a value from the map for an associated key.
+ /**
+ Display a debug notice. Ember build tools will remove any calls to
+ `Ember.debug()` when doing a production build.
- @method remove
- @param {*} key
- @return {Boolean} true if an item was removed, false otherwise
- */
- remove: function(key) {
- // don't use ES6 "delete" because it will be annoying
- // to use in browsers that are not ES6 friendly;
- var keys = this.keys,
- values = this.values,
- guid = guidFor(key);
+ ```javascript
+ Ember.debug('I\'m a debug notice!');
+ ```
- if (values.hasOwnProperty(guid)) {
- keys.remove(key);
- delete values[guid];
- set(this, 'length', keys.list.length);
- return true;
- } else {
- return false;
- }
- },
+ @method debug
+ @param {String} message A debug message to display.
+ */
+ Ember.debug = function(message) {
+ Logger.debug("DEBUG: "+message);
+ };
- /**
- Check whether a key is present.
+ /**
+ Display a deprecation warning with the provided message and a stack trace
+ (Chrome and Firefox only). Ember build tools will remove any calls to
+ `Ember.deprecate()` when doing a production build.
- @method has
- @param {*} key
- @return {Boolean} true if the item was present, false otherwise
- */
- has: function(key) {
- var values = this.values,
- guid = guidFor(key);
+ @method deprecate
+ @param {String} message A description of the deprecation.
+ @param {Boolean} test An optional boolean. If falsy, the deprecation
+ will be displayed.
+ */
+ Ember.deprecate = function(message, test) {
+ var noDeprecation;
- return values.hasOwnProperty(guid);
- },
+ if (typeof test === 'function') {
+ noDeprecation = test();
+ } else {
+ noDeprecation = test;
+ }
- /**
- Iterate over all the keys and values. Calls the function once
- for each key, passing in the key and value, in that order.
+ if (noDeprecation) { return; }
- The keys are guaranteed to be iterated over in insertion order.
+ if (Ember.ENV.RAISE_ON_DEPRECATION) { throw new EmberError(message); }
- @method forEach
- @param {Function} callback
- @param {*} self if passed, the `this` value inside the
- callback. By default, `this` is the map.
- */
- forEach: function(callback, self) {
- var keys = this.keys,
- values = this.values;
+ var error;
- keys.forEach(function(key) {
- var guid = guidFor(key);
- callback.call(self, key, values[guid]);
- });
- },
+ // When using new Error, we can't do the arguments check for Chrome. Alternatives are welcome
+ try { __fail__.fail(); } catch (e) { error = e; }
- /**
- @method copy
- @return {Ember.Map}
- */
- copy: function() {
- return copyMap(this, new Map());
- }
- };
+ if (Ember.LOG_STACKTRACE_ON_DEPRECATION && error.stack) {
+ var stack;
+ var stackStr = '';
- /**
- @class MapWithDefault
- @namespace Ember
- @extends Ember.Map
- @private
- @constructor
- @param [options]
- @param {*} [options.defaultValue]
- */
- function MapWithDefault(options) {
- Map.call(this);
- this.defaultValue = options.defaultValue;
- };
+ if (error['arguments']) {
+ // Chrome
+ stack = error.stack.replace(/^\s+at\s+/gm, '').
+ replace(/^([^\(]+?)([\n$])/gm, '{anonymous}($1)$2').
+ replace(/^Object.\s*\(([^\)]+)\)/gm, '{anonymous}($1)').split('\n');
+ stack.shift();
+ } else {
+ // Firefox
+ stack = error.stack.replace(/(?:\n@:0)?\s+$/m, '').
+ replace(/^\(/gm, '{anonymous}(').split('\n');
+ }
- /**
- @method create
- @static
- @param [options]
- @param {*} [options.defaultValue]
- @return {Ember.MapWithDefault|Ember.Map} If options are passed, returns
- `Ember.MapWithDefault` otherwise returns `Ember.Map`
- */
- MapWithDefault.create = function(options) {
- if (options) {
- return new MapWithDefault(options);
- } else {
- return new Map();
+ stackStr = "\n " + stack.slice(2).join("\n ");
+ message = message + stackStr;
}
+
+ Logger.warn("DEPRECATION: "+message);
};
- MapWithDefault.prototype = create(Map.prototype);
+
/**
- Retrieve the value associated with a given key.
+ Alias an old, deprecated method with its new counterpart.
- @method get
- @param {*} key
- @return {*} the value associated with the key, or the default value
- */
- MapWithDefault.prototype.get = function(key) {
- var hasValue = this.has(key);
+ Display a deprecation warning with the provided message and a stack trace
+ (Chrome and Firefox only) when the assigned method is called.
- if (hasValue) {
- return Map.prototype.get.call(this, key);
- } else {
- var defaultValue = this.defaultValue(key);
- this.set(key, defaultValue);
- return defaultValue;
- }
- };
+ Ember build tools will not remove calls to `Ember.deprecateFunc()`, though
+ no warnings will be shown in production.
- /**
- @method copy
- @return {Ember.MapWithDefault}
+ ```javascript
+ Ember.oldMethod = Ember.deprecateFunc('Please use the new, updated method', Ember.newMethod);
+ ```
+
+ @method deprecateFunc
+ @param {String} message A description of the deprecation.
+ @param {Function} func The new function called to replace its deprecated counterpart.
+ @return {Function} a new function that wrapped the original function with a deprecation warning
*/
- MapWithDefault.prototype.copy = function() {
- return copyMap(this, new MapWithDefault({
- defaultValue: this.defaultValue
- }));
+ Ember.deprecateFunc = function(message, func) {
+ return function() {
+ Ember.deprecate(message);
+ return func.apply(this, arguments);
+ };
};
- __exports__.OrderedSet = OrderedSet;
- __exports__.Map = Map;
- __exports__.MapWithDefault = MapWithDefault;
- });
-define("ember-metal/merge",
- ["exports"],
- function(__exports__) {
- "use strict";
+
/**
- Merge the contents of two objects together into the first object.
+ Run a function meant for debugging. Ember build tools will remove any calls to
+ `Ember.runInDebug()` when doing a production build.
```javascript
- Ember.merge({first: 'Tom'}, {last: 'Dale'}); // {first: 'Tom', last: 'Dale'}
- var a = {first: 'Yehuda'}, b = {last: 'Katz'};
- Ember.merge(a, b); // a == {first: 'Yehuda', last: 'Katz'}, b == {last: 'Katz'}
+ Ember.runInDebug(function() {
+ Ember.Handlebars.EachView.reopen({
+ didInsertElement: function() {
+ console.log('I\'m happy');
+ }
+ });
+ });
```
- @method merge
- @for Ember
- @param {Object} original The object to merge into
- @param {Object} updates The object to copy properties from
- @return {Object}
+ @method runInDebug
+ @param {Function} func The function to be executed.
+ @since 1.5.0
*/
- function merge(original, updates) {
- for (var prop in updates) {
- if (!updates.hasOwnProperty(prop)) { continue; }
- original[prop] = updates[prop];
- }
- return original;
+ Ember.runInDebug = function(func) {
+ func();
};
- __exports__["default"] = merge;
- });
-define("ember-metal/mixin",
- ["ember-metal/core","ember-metal/merge","ember-metal/array","ember-metal/platform","ember-metal/utils","ember-metal/expand_properties","ember-metal/properties","ember-metal/computed","ember-metal/binding","ember-metal/observer","ember-metal/events","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __exports__) {
- "use strict";
/**
- @module ember
- @submodule ember-metal
- */
-
- var Ember = __dependency1__["default"];
- // warn, assert, wrap, et;
- var merge = __dependency2__["default"];
- var map = __dependency3__.map;
- var indexOf = __dependency3__.indexOf;
- var forEach = __dependency3__.forEach;
- var create = __dependency4__.create;
- var guidFor = __dependency5__.guidFor;
- var meta = __dependency5__.meta;
- var META_KEY = __dependency5__.META_KEY;
- var wrap = __dependency5__.wrap;
- var makeArray = __dependency5__.makeArray;
- var apply = __dependency5__.apply;
- var expandProperties = __dependency6__["default"];
- var Descriptor = __dependency7__.Descriptor;
- var defineProperty = __dependency7__.defineProperty;
- var ComputedProperty = __dependency8__.ComputedProperty;
- var Binding = __dependency9__.Binding;
- var addObserver = __dependency10__.addObserver;
- var removeObserver = __dependency10__.removeObserver;
- var addBeforeObserver = __dependency10__.addBeforeObserver;
- var removeBeforeObserver = __dependency10__.removeBeforeObserver;
- var addListener = __dependency11__.addListener;
- var removeListener = __dependency11__.removeListener;
-
- var REQUIRED, Alias,
- a_map = map,
- a_indexOf = indexOf,
- a_forEach = forEach,
- a_slice = [].slice,
- o_create = create,
- defineProperty = defineProperty,
- metaFor = meta;
+ Will call `Ember.warn()` if ENABLE_ALL_FEATURES, ENABLE_OPTIONAL_FEATURES, or
+ any specific FEATURES flag is truthy.
- function superFunction(){
- var ret, func = this.__nextSuper;
- if (func) {
- this.__nextSuper = null;
- ret = apply(this, func, arguments);
- this.__nextSuper = func;
- }
- return ret;
- }
+ This method is called automatically in debug canary builds.
+
+ @private
+ @method _warnIfUsingStrippedFeatureFlags
+ @return {void}
+ */
+ function _warnIfUsingStrippedFeatureFlags(FEATURES, featuresWereStripped) {
+ if (featuresWereStripped) {
+ Ember.warn('Ember.ENV.ENABLE_ALL_FEATURES is only available in canary builds.', !Ember.ENV.ENABLE_ALL_FEATURES);
+ Ember.warn('Ember.ENV.ENABLE_OPTIONAL_FEATURES is only available in canary builds.', !Ember.ENV.ENABLE_OPTIONAL_FEATURES);
- function mixinsMeta(obj) {
- var m = metaFor(obj, true), ret = m.mixins;
- if (!ret) {
- ret = m.mixins = {};
- } else if (!m.hasOwnProperty('mixins')) {
- ret = m.mixins = o_create(ret);
+ for (var key in FEATURES) {
+ if (FEATURES.hasOwnProperty(key) && key !== 'isEnabled') {
+ Ember.warn('FEATURE["' + key + '"] is set as enabled, but FEATURE flags are only available in canary builds.', !FEATURES[key]);
+ }
+ }
}
- return ret;
}
- function initMixin(mixin, args) {
- if (args && args.length > 0) {
- mixin.mixins = a_map.call(args, function(x) {
- if (x instanceof Mixin) { return x; }
-
- // Note: Manually setup a primitive mixin here. This is the only
- // way to actually get a primitive mixin. This way normal creation
- // of mixins will give you combined mixins...
- var mixin = new Mixin();
- mixin.properties = x;
- return mixin;
- });
- }
- return mixin;
- }
+ __exports__._warnIfUsingStrippedFeatureFlags = _warnIfUsingStrippedFeatureFlags;if (!Ember.testing) {
+ // Complain if they're using FEATURE flags in builds other than canary
+ Ember.FEATURES['features-stripped-test'] = true;
+ var featuresWereStripped = true;
+
+
+ delete Ember.FEATURES['features-stripped-test'];
+ _warnIfUsingStrippedFeatureFlags(Ember.ENV.FEATURES, featuresWereStripped);
- function isMethod(obj) {
- return 'function' === typeof obj &&
- obj.isMethod !== false &&
- obj !== Boolean && obj !== Object && obj !== Number && obj !== Array && obj !== Date && obj !== String;
- }
+ // Inform the developer about the Ember Inspector if not installed.
+ var isFirefox = typeof InstallTrigger !== 'undefined';
+ var isChrome = !!window.chrome && !window.opera;
- var CONTINUE = {};
+ if (typeof window !== 'undefined' && (isFirefox || isChrome) && window.addEventListener) {
+ window.addEventListener("load", function() {
+ if (document.documentElement && document.documentElement.dataset && !document.documentElement.dataset.emberExtension) {
+ var downloadURL;
- function mixinProperties(mixinsMeta, mixin) {
- var guid;
+ if(isChrome) {
+ downloadURL = 'https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi';
+ } else if(isFirefox) {
+ downloadURL = 'https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/';
+ }
- if (mixin instanceof Mixin) {
- guid = guidFor(mixin);
- if (mixinsMeta[guid]) { return CONTINUE; }
- mixinsMeta[guid] = mixin;
- return mixin.properties;
- } else {
- return mixin; // apply anonymous mixin properties
+ Ember.debug('For more advanced debugging, install the Ember Inspector from ' + downloadURL);
+ }
+ }, false);
}
}
- function concatenatedMixinProperties(concatProp, props, values, base) {
- var concats;
-
- // reset before adding each new mixin to pickup concats from previous
- concats = values[concatProp] || base[concatProp];
- if (props[concatProp]) {
- concats = concats ? concats.concat(props[concatProp]) : props[concatProp];
- }
+ /*
+ We are transitioning away from `ember.js` to `ember.debug.js` to make
+ it much clearer that it is only for local development purposes.
- return concats;
+ This flag value is changed by the tooling (by a simple string replacement)
+ so that if `ember.js` (which must be output for backwards compat reasons) is
+ used a nice helpful warning message will be printed out.
+ */
+ var runningNonEmberDebugJS = true;
+ __exports__.runningNonEmberDebugJS = runningNonEmberDebugJS;if (runningNonEmberDebugJS) {
+ Ember.warn('Please use `ember.debug.js` instead of `ember.js` for development and debugging.');
}
+ });
+enifed("ember-extension-support",
+ ["ember-metal/core","ember-extension-support/data_adapter","ember-extension-support/container_debug_adapter"],
+ function(__dependency1__, __dependency2__, __dependency3__) {
+ "use strict";
+ /**
+ Ember Extension Support
- function giveDescriptorSuper(meta, key, property, values, descs) {
- var superProperty;
-
- // Computed properties override methods, and do not call super to them
- if (values[key] === undefined) {
- // Find the original descriptor in a parent mixin
- superProperty = descs[key];
- }
+ @module ember
+ @submodule ember-extension-support
+ @requires ember-application
+ */
- // If we didn't find the original descriptor in a parent mixin, find
- // it on the original object.
- superProperty = superProperty || meta.descs[key];
+ var Ember = __dependency1__["default"];
+ var DataAdapter = __dependency2__["default"];
+ var ContainerDebugAdapter = __dependency3__["default"];
- if (!superProperty || !(superProperty instanceof ComputedProperty)) {
- return property;
- }
+ Ember.DataAdapter = DataAdapter;
+ Ember.ContainerDebugAdapter = ContainerDebugAdapter;
+ });
+enifed("ember-extension-support/container_debug_adapter",
+ ["ember-metal/core","ember-runtime/system/native_array","ember-metal/utils","ember-runtime/system/string","ember-runtime/system/namespace","ember-runtime/system/object","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) {
+ "use strict";
+ var Ember = __dependency1__["default"];
+ var emberA = __dependency2__.A;
+ var typeOf = __dependency3__.typeOf;
+ var dasherize = __dependency4__.dasherize;
+ var classify = __dependency4__.classify;
+ var Namespace = __dependency5__["default"];
+ var EmberObject = __dependency6__["default"];
- // Since multiple mixins may inherit from the same parent, we need
- // to clone the computed property so that other mixins do not receive
- // the wrapped version.
- property = o_create(property);
- property.func = wrap(property.func, superProperty.func);
+ /**
+ @module ember
+ @submodule ember-extension-support
+ */
- return property;
- }
+ /**
+ The `ContainerDebugAdapter` helps the container and resolver interface
+ with tools that debug Ember such as the
+ [Ember Extension](https://github.com/tildeio/ember-extension)
+ for Chrome and Firefox.
- function giveMethodSuper(obj, key, method, values, descs) {
- var superMethod;
+ This class can be extended by a custom resolver implementer
+ to override some of the methods with library-specific code.
- // Methods overwrite computed properties, and do not call super to them.
- if (descs[key] === undefined) {
- // Find the original method in a parent mixin
- superMethod = values[key];
- }
+ The methods likely to be overridden are:
- // If we didn't find the original value in a parent mixin, find it in
- // the original object
- superMethod = superMethod || obj[key];
+ * `canCatalogEntriesByType`
+ * `catalogEntriesByType`
- // Only wrap the new method if the original method was a function
- if ('function' !== typeof superMethod) {
- return method;
- }
+ The adapter will need to be registered
+ in the application's container as `container-debug-adapter:main`
- return wrap(method, superMethod);
- }
+ Example:
- function applyConcatenatedProperties(obj, key, value, values) {
- var baseValue = values[key] || obj[key];
+ ```javascript
+ Application.initializer({
+ name: "containerDebugAdapter",
- if (baseValue) {
- if ('function' === typeof baseValue.concat) {
- return baseValue.concat(value);
- } else {
- return makeArray(baseValue).concat(value);
+ initialize: function(container, application) {
+ application.register('container-debug-adapter:main', require('app/container-debug-adapter'));
}
- } else {
- return makeArray(value);
- }
- }
-
- function applyMergedProperties(obj, key, value, values) {
- var baseValue = values[key] || obj[key];
-
- if (!baseValue) { return value; }
+ });
+ ```
- var newBase = merge({}, baseValue),
- hasFunction = false;
+ @class ContainerDebugAdapter
+ @namespace Ember
+ @extends Ember.Object
+ @since 1.5.0
+ */
+ __exports__["default"] = EmberObject.extend({
+ /**
+ The container of the application being debugged.
+ This property will be injected
+ on creation.
- for (var prop in value) {
- if (!value.hasOwnProperty(prop)) { continue; }
+ @property container
+ @default null
+ */
+ container: null,
- var propValue = value[prop];
- if (isMethod(propValue)) {
- // TODO: support for Computed Properties, etc?
- hasFunction = true;
- newBase[prop] = giveMethodSuper(obj, prop, propValue, baseValue, {});
- } else {
- newBase[prop] = propValue;
- }
- }
+ /**
+ The resolver instance of the application
+ being debugged. This property will be injected
+ on creation.
- if (hasFunction) {
- newBase._super = superFunction;
- }
+ @property resolver
+ @default null
+ */
+ resolver: null,
- return newBase;
- }
+ /**
+ Returns true if it is possible to catalog a list of available
+ classes in the resolver for a given type.
- function addNormalizedProperty(base, key, value, meta, descs, values, concats, mergings) {
- if (value instanceof Descriptor) {
- if (value === REQUIRED && descs[key]) { return CONTINUE; }
+ @method canCatalogEntriesByType
+ @param {String} type The type. e.g. "model", "controller", "route"
+ @return {boolean} whether a list is available for this type.
+ */
+ canCatalogEntriesByType: function(type) {
+ if (type === 'model' || type === 'template') return false;
+ return true;
+ },
- // Wrap descriptor function to implement
- // __nextSuper() if needed
- if (value.func) {
- value = giveDescriptorSuper(meta, key, value, values, descs);
- }
+ /**
+ Returns the available classes a given type.
- descs[key] = value;
- values[key] = undefined;
- } else {
- if ((concats && a_indexOf.call(concats, key) >= 0) ||
- key === 'concatenatedProperties' ||
- key === 'mergedProperties') {
- value = applyConcatenatedProperties(base, key, value, values);
- } else if ((mergings && a_indexOf.call(mergings, key) >= 0)) {
- value = applyMergedProperties(base, key, value, values);
- } else if (isMethod(value)) {
- value = giveMethodSuper(base, key, value, values, descs);
- }
+ @method catalogEntriesByType
+ @param {String} type The type. e.g. "model", "controller", "route"
+ @return {Array} An array of strings.
+ */
+ catalogEntriesByType: function(type) {
+ var namespaces = emberA(Namespace.NAMESPACES), types = emberA();
+ var typeSuffixRegex = new RegExp(classify(type) + "$");
- descs[key] = undefined;
- values[key] = value;
+ namespaces.forEach(function(namespace) {
+ if (namespace !== Ember) {
+ for (var key in namespace) {
+ if (!namespace.hasOwnProperty(key)) { continue; }
+ if (typeSuffixRegex.test(key)) {
+ var klass = namespace[key];
+ if (typeOf(klass) === 'class') {
+ types.push(dasherize(key.replace(typeSuffixRegex, '')));
+ }
+ }
+ }
+ }
+ });
+ return types;
}
- }
+ });
+ });
+enifed("ember-extension-support/data_adapter",
+ ["ember-metal/property_get","ember-metal/run_loop","ember-runtime/system/string","ember-runtime/system/namespace","ember-runtime/system/object","ember-runtime/system/native_array","ember-application/system/application","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __exports__) {
+ "use strict";
+ var get = __dependency1__.get;
+ var run = __dependency2__["default"];
+ var dasherize = __dependency3__.dasherize;
+ var Namespace = __dependency4__["default"];
+ var EmberObject = __dependency5__["default"];
+ var emberA = __dependency6__.A;
+ var Application = __dependency7__["default"];
- function mergeMixins(mixins, m, descs, values, base, keys) {
- var mixin, props, key, concats, mergings, meta;
+ /**
+ @module ember
+ @submodule ember-extension-support
+ */
- function removeKeys(keyName) {
- delete descs[keyName];
- delete values[keyName];
- }
+ /**
+ The `DataAdapter` helps a data persistence library
+ interface with tools that debug Ember such
+ as the [Ember Extension](https://github.com/tildeio/ember-extension)
+ for Chrome and Firefox.
- for(var i=0, l=mixins.length; i= 0) {
- if (_detect(mixins[loc], targetMixin, seen)) { return true; }
- }
- return false;
- }
+ return release;
+ },
- /**
- @method detect
- @param obj
- @return {Boolean}
- */
- MixinPrototype.detect = function(obj) {
- if (!obj) { return false; }
- if (obj instanceof Mixin) { return _detect(obj, this, {}); }
- var m = obj[META_KEY],
- mixins = m && m.mixins;
- if (mixins) {
- return !!mixins[guidFor(this)];
- }
- return false;
- };
- MixinPrototype.without = function() {
- var ret = new Mixin(this);
- ret._without = a_slice.call(arguments);
- return ret;
- };
+ /**
+ Wraps a given model type and observes changes to it.
- function _keys(ret, mixin, seen) {
- if (seen[guidFor(mixin)]) { return; }
- seen[guidFor(mixin)] = true;
+ @private
+ @method wrapModelType
+ @param {Class} type A model class
+ @param {String} Optional name of the class
+ @return {Object} contains the wrapped type and the function to remove observers
+ Format:
+ type: {Object} the wrapped type
+ The wrapped type has the following format:
+ name: {String} name of the type
+ count: {Integer} number of records available
+ columns: {Columns} array of columns to describe the record
+ object: {Class} the actual Model type class
+ release: {Function} The function to remove observers
+ */
+ wrapModelType: function(type, name) {
+ var records = this.getRecords(type);
+ var typeToSend;
- if (mixin.properties) {
- var props = mixin.properties;
- for (var key in props) {
- if (props.hasOwnProperty(key)) { ret[key] = true; }
- }
- } else if (mixin.mixins) {
- a_forEach.call(mixin.mixins, function(x) { _keys(ret, x, seen); });
- }
- }
+ typeToSend = {
+ name: name || type.toString(),
+ count: get(records, 'length'),
+ columns: this.columnsForType(type),
+ object: type
+ };
- MixinPrototype.keys = function() {
- var keys = {}, seen = {}, ret = [];
- _keys(keys, this, seen);
- for(var key in keys) {
- if (keys.hasOwnProperty(key)) { ret.push(key); }
- }
- return ret;
- };
- // returns the mixins currently applied to the specified object
- // TODO: Make Ember.mixin
- Mixin.mixins = function(obj) {
- var m = obj[META_KEY],
- mixins = m && m.mixins, ret = [];
+ return typeToSend;
+ },
- if (!mixins) { return ret; }
- for (var key in mixins) {
- var mixin = mixins[key];
+ /**
+ Fetches all models defined in the application.
- // skip primitive mixins since these are always anonymous
- if (!mixin.properties) { ret.push(mixin); }
- }
+ @private
+ @method getModelTypes
+ @return {Array} Array of model types
+ */
+ getModelTypes: function() {
+ var self = this;
+ var containerDebugAdapter = this.get('containerDebugAdapter');
+ var types;
- return ret;
- };
+ if (containerDebugAdapter.canCatalogEntriesByType('model')) {
+ types = containerDebugAdapter.catalogEntriesByType('model');
+ } else {
+ types = this._getObjectsOnNamespaces();
+ }
- REQUIRED = new Descriptor();
- REQUIRED.toString = function() { return '(Required Property)'; };
+ // New adapters return strings instead of classes
+ types = emberA(types).map(function(name) {
+ return {
+ klass: self._nameToClass(name),
+ name: name
+ };
+ });
+ types = emberA(types).filter(function(type) {
+ return self.detect(type.klass);
+ });
- /**
- Denotes a required property for a mixin
+ return emberA(types);
+ },
- @method required
- @for Ember
- */
- function required() {
- return REQUIRED;
- };
+ /**
+ Loops over all namespaces and all objects
+ attached to them
- Alias = function(methodName) {
- this.methodName = methodName;
- };
- Alias.prototype = new Descriptor();
+ @private
+ @method _getObjectsOnNamespaces
+ @return {Array} Array of model type strings
+ */
+ _getObjectsOnNamespaces: function() {
+ var namespaces = emberA(Namespace.NAMESPACES);
+ var types = emberA();
+ var self = this;
- /**
- Makes a method available via an additional name.
+ namespaces.forEach(function(namespace) {
+ for (var key in namespace) {
+ if (!namespace.hasOwnProperty(key)) { continue; }
+ // Even though we will filter again in `getModelTypes`,
+ // we should not call `lookupContainer` on non-models
+ // (especially when `Ember.MODEL_FACTORY_INJECTIONS` is `true`)
+ if (!self.detect(namespace[key])) { continue; }
+ var name = dasherize(key);
+ if (!(namespace instanceof Application) && namespace.toString()) {
+ name = namespace + '/' + name;
+ }
+ types.push(name);
+ }
+ });
+ return types;
+ },
- ```javascript
- App.Person = Ember.Object.extend({
- name: function() {
- return 'Tomhuda Katzdale';
- },
- moniker: Ember.aliasMethod('name')
- });
+ /**
+ Fetches all loaded records for a given type.
- var goodGuy = App.Person.create();
-
- goodGuy.name(); // 'Tomhuda Katzdale'
- goodGuy.moniker(); // 'Tomhuda Katzdale'
- ```
+ @private
+ @method getRecords
+ @return {Array} An array of records.
+ This array will be observed for changes,
+ so it should update when new records are added/removed.
+ */
+ getRecords: function(type) {
+ return emberA();
+ },
- @method aliasMethod
- @for Ember
- @param {String} methodName name of the method to alias
- @return {Ember.Descriptor}
- */
- function aliasMethod(methodName) {
- return new Alias(methodName);
- };
+ /**
+ Wraps a record and observers changes to it.
- // ..........................................................
- // OBSERVER HELPER
- //
+ @private
+ @method wrapRecord
+ @param {Object} record The record instance.
+ @return {Object} The wrapped record. Format:
+ columnValues: {Array}
+ searchKeywords: {Array}
+ */
+ wrapRecord: function(record) {
+ var recordToSend = { object: record };
- /**
- Specify a method that observes property changes.
+ recordToSend.columnValues = this.getRecordColumnValues(record);
+ recordToSend.searchKeywords = this.getRecordKeywords(record);
+ recordToSend.filterValues = this.getRecordFilterValues(record);
+ recordToSend.color = this.getRecordColor(record);
- ```javascript
- Ember.Object.extend({
- valueObserver: Ember.observer('value', function() {
- // Executes whenever the "value" property changes
- })
- });
- ```
+ return recordToSend;
+ },
- In the future this method may become asynchronous. If you want to ensure
- synchronous behavior, use `immediateObserver`.
+ /**
+ Gets the values for each column.
- Also available as `Function.prototype.observes` if prototype extensions are
- enabled.
+ @private
+ @method getRecordColumnValues
+ @return {Object} Keys should match column names defined
+ by the model type.
+ */
+ getRecordColumnValues: function(record) {
+ return {};
+ },
- @method observer
- @for Ember
- @param {String} propertyNames*
- @param {Function} func
- @return func
- */
- function observer() {
- var func = a_slice.call(arguments, -1)[0];
- var paths;
+ /**
+ Returns keywords to match when searching records.
- var addWatchedProperty = function (path) { paths.push(path); };
- var _paths = a_slice.call(arguments, 0, -1);
+ @private
+ @method getRecordKeywords
+ @return {Array} Relevant keywords for search.
+ */
+ getRecordKeywords: function(record) {
+ return emberA();
+ },
- if (typeof func !== "function") {
- // revert to old, soft-deprecated argument ordering
+ /**
+ Returns the values of filters defined by `getFilters`.
- func = arguments[0];
- _paths = a_slice.call(arguments, 1);
- }
+ @private
+ @method getRecordFilterValues
+ @param {Object} record The record instance
+ @return {Object} The filter values
+ */
+ getRecordFilterValues: function(record) {
+ return {};
+ },
- paths = [];
+ /**
+ Each record can have a color that represents its state.
- for (var i=0; i<_paths.length; ++i) {
- expandProperties(_paths[i], addWatchedProperty);
- }
+ @private
+ @method getRecordColor
+ @param {Object} record The record instance
+ @return {String} The record's color
+ Possible options: black, red, blue, green
+ */
+ getRecordColor: function(record) {
+ return null;
+ },
- if (typeof func !== "function") {
- throw new Ember.Error("Ember.observer called without a function");
- }
+ /**
+ Observes all relevant properties and re-sends the wrapped record
+ when a change occurs.
- func.__ember_observes__ = paths;
- return func;
- };
+ @private
+ @method observerRecord
+ @param {Object} record The record instance
+ @param {Function} recordUpdated The callback to call when a record is updated.
+ @return {Function} The function to call to remove all observers.
+ */
+ observeRecord: function(record, recordUpdated) {
+ return function(){};
+ }
+ });
+ });
+enifed("ember-htmlbars",
+ ["ember-metal/core","ember-template-compiler","ember-htmlbars/hooks/inline","ember-htmlbars/hooks/content","ember-htmlbars/hooks/component","ember-htmlbars/hooks/block","ember-htmlbars/hooks/element","ember-htmlbars/hooks/subexpr","ember-htmlbars/hooks/attribute","ember-htmlbars/hooks/concat","ember-htmlbars/hooks/get","ember-htmlbars/hooks/set","morph","ember-htmlbars/system/make-view-helper","ember-htmlbars/system/make_bound_helper","ember-htmlbars/helpers","ember-htmlbars/helpers/binding","ember-htmlbars/helpers/view","ember-htmlbars/helpers/yield","ember-htmlbars/helpers/with","ember-htmlbars/helpers/log","ember-htmlbars/helpers/debugger","ember-htmlbars/helpers/bind-attr","ember-htmlbars/helpers/if_unless","ember-htmlbars/helpers/loc","ember-htmlbars/helpers/partial","ember-htmlbars/helpers/template","ember-htmlbars/helpers/input","ember-htmlbars/helpers/text_area","ember-htmlbars/helpers/collection","ember-htmlbars/helpers/each","ember-htmlbars/helpers/unbound","ember-htmlbars/system/bootstrap","ember-htmlbars/compat","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __dependency15__, __dependency16__, __dependency17__, __dependency18__, __dependency19__, __dependency20__, __dependency21__, __dependency22__, __dependency23__, __dependency24__, __dependency25__, __dependency26__, __dependency27__, __dependency28__, __dependency29__, __dependency30__, __dependency31__, __dependency32__, __dependency33__, __dependency34__, __exports__) {
+ "use strict";
+ var Ember = __dependency1__["default"];
- /**
- Specify a method that observes property changes.
+ var precompile = __dependency2__.precompile;
+ var compile = __dependency2__.compile;
+ var template = __dependency2__.template;
+ var registerPlugin = __dependency2__.registerPlugin;
+
+ var inline = __dependency3__["default"];
+ var content = __dependency4__["default"];
+ var component = __dependency5__["default"];
+ var block = __dependency6__["default"];
+ var element = __dependency7__["default"];
+ var subexpr = __dependency8__["default"];
+ var attribute = __dependency9__["default"];
+ var concat = __dependency10__["default"];
+ var get = __dependency11__["default"];
+ var set = __dependency12__["default"];
+ var DOMHelper = __dependency13__.DOMHelper;
+ var makeViewHelper = __dependency14__["default"];
+ var makeBoundHelper = __dependency15__["default"];
+
+ var registerHelper = __dependency16__.registerHelper;
+ var helper = __dependency16__.helper;
+ var helpers = __dependency16__["default"];
+ var bindHelper = __dependency17__.bindHelper;
+ var viewHelper = __dependency18__.viewHelper;
+ var yieldHelper = __dependency19__.yieldHelper;
+ var withHelper = __dependency20__.withHelper;
+ var logHelper = __dependency21__.logHelper;
+ var debuggerHelper = __dependency22__.debuggerHelper;
+ var bindAttrHelper = __dependency23__.bindAttrHelper;
+ var bindAttrHelperDeprecated = __dependency23__.bindAttrHelperDeprecated;
+ var ifHelper = __dependency24__.ifHelper;
+ var unlessHelper = __dependency24__.unlessHelper;
+ var unboundIfHelper = __dependency24__.unboundIfHelper;
+ var boundIfHelper = __dependency24__.boundIfHelper;
+ var locHelper = __dependency25__.locHelper;
+ var partialHelper = __dependency26__.partialHelper;
+ var templateHelper = __dependency27__.templateHelper;
+ var inputHelper = __dependency28__.inputHelper;
+ var textareaHelper = __dependency29__.textareaHelper;
+ var collectionHelper = __dependency30__.collectionHelper;
+ var eachHelper = __dependency31__.eachHelper;
+ var unboundHelper = __dependency32__.unboundHelper;
+
+ // importing adds template bootstrapping
+ // initializer to enable embedded templates
+
+ // importing ember-htmlbars/compat updates the
+ // Ember.Handlebars global if htmlbars is enabled
+
+ registerHelper('bindHelper', bindHelper);
+ registerHelper('bind', bindHelper);
+ registerHelper('view', viewHelper);
+ registerHelper('yield', yieldHelper);
+ registerHelper('with', withHelper);
+ registerHelper('if', ifHelper);
+ registerHelper('unless', unlessHelper);
+ registerHelper('unboundIf', unboundIfHelper);
+ registerHelper('boundIf', boundIfHelper);
+ registerHelper('log', logHelper);
+ registerHelper('debugger', debuggerHelper);
+ registerHelper('loc', locHelper);
+ registerHelper('partial', partialHelper);
+ registerHelper('template', templateHelper);
+ registerHelper('bind-attr', bindAttrHelper);
+ registerHelper('bindAttr', bindAttrHelperDeprecated);
+ registerHelper('input', inputHelper);
+ registerHelper('textarea', textareaHelper);
+ registerHelper('collection', collectionHelper);
+ registerHelper('each', eachHelper);
+ registerHelper('unbound', unboundHelper);
+ registerHelper('concat', concat);
- ```javascript
- Ember.Object.extend({
- valueObserver: Ember.immediateObserver('value', function() {
- // Executes whenever the "value" property changes
- })
- });
- ```
+
+ Ember.HTMLBars = {
+ helpers: helpers,
+ helper: helper,
+ _registerHelper: registerHelper,
+ template: template,
+ compile: compile,
+ precompile: precompile,
+ makeViewHelper: makeViewHelper,
+ makeBoundHelper: makeBoundHelper,
+ registerPlugin: registerPlugin
+ };
- In the future, `Ember.observer` may become asynchronous. In this event,
- `Ember.immediateObserver` will maintain the synchronous behavior.
+
- Also available as `Function.prototype.observesImmediately` if prototype extensions are
- enabled.
+ var defaultEnv = {
+ dom: new DOMHelper(),
- @method immediateObserver
- @for Ember
- @param {String} propertyNames*
- @param {Function} func
- @return func
- */
- function immediateObserver() {
- for (var i=0, l=arguments.length; i this.changingFrom ? 'green' : 'red';
- // logic
- }
- }),
+ var merge = __dependency1__["default"];
+ var helpers = __dependency2__["default"];
+ var View = __dependency3__["default"];
+ var Component = __dependency4__["default"];
+ var makeViewHelper = __dependency5__["default"];
+ var makeBoundHelper = __dependency6__["default"];
+ var isStream = __dependency7__.isStream;
- friendsDidChange: Ember.observer('friends.@each.name', function(obj, keyName) {
- // some logic
- // obj.get(keyName) returns friends array
- })
- });
- ```
+ var slice = [].slice;
- Also available as `Function.prototype.observesBefore` if prototype extensions are
- enabled.
+ /**
+ Wraps an Handlebars helper with an HTMLBars helper for backwards compatibility.
- @method beforeObserver
- @for Ember
- @param {String} propertyNames*
- @param {Function} func
- @return func
+ @class HandlebarsCompatibleHelper
+ @constructor
+ @private
*/
- function beforeObserver() {
- var func = a_slice.call(arguments, -1)[0];
- var paths;
+ function HandlebarsCompatibleHelper(fn) {
+ this.helperFunction = function helperFunc(params, hash, options, env) {
+ var param;
+ var handlebarsOptions = {};
+ merge(handlebarsOptions, options);
+ merge(handlebarsOptions, env);
- var addWatchedProperty = function(path) { paths.push(path); };
-
- var _paths = a_slice.call(arguments, 0, -1);
+ handlebarsOptions.hash = {};
+ for (var prop in hash) {
+ param = hash[prop];
- if (typeof func !== "function") {
- // revert to old, soft-deprecated argument ordering
+ if (isStream(param)) {
+ handlebarsOptions.hash[prop] = param._label;
+ } else {
+ handlebarsOptions.hash[prop] = param;
+ }
+ }
- func = arguments[0];
- _paths = a_slice.call(arguments, 1);
- }
+ var args = new Array(params.length);
+ for (var i = 0, l = params.length; i < l; i++) {
+ param = params[i];
- paths = [];
+ if (isStream(param)) {
+ args[i] = param._label;
+ } else {
+ args[i] = param;
+ }
+ }
+ args.push(handlebarsOptions);
- for (var i=0; i<_paths.length; ++i) {
- expandProperties(_paths[i], addWatchedProperty);
- }
+ return fn.apply(this, args);
+ };
- if (typeof func !== "function") {
- throw new Ember.Error("Ember.beforeObserver called without a function");
- }
+ this.isHTMLBars = true;
+ }
- func.__ember_observesBefore__ = paths;
- return func;
+ HandlebarsCompatibleHelper.prototype = {
+ preprocessArguments: function() { }
};
- __exports__.IS_BINDING = IS_BINDING;
- __exports__.mixin = mixin;
- __exports__.Mixin = Mixin;
- __exports__.required = required;
- __exports__.aliasMethod = aliasMethod;
- __exports__.observer = observer;
- __exports__.immediateObserver = immediateObserver;
- __exports__.beforeObserver = beforeObserver;
- });
-define("ember-metal/observer",
- ["ember-metal/watching","ember-metal/array","ember-metal/events","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
- "use strict";
- var watch = __dependency1__.watch;
- var unwatch = __dependency1__.unwatch;
- var map = __dependency2__.map;
- var listenersFor = __dependency3__.listenersFor;
- var addListener = __dependency3__.addListener;
- var removeListener = __dependency3__.removeListener;
- var suspendListeners = __dependency3__.suspendListeners;
- var suspendListener = __dependency3__.suspendListener;
- /**
- @module ember-metal
- */
+ function registerHandlebarsCompatibleHelper(name, value) {
+ helpers[name] = new HandlebarsCompatibleHelper(value);
+ }
- var AFTER_OBSERVERS = ':change',
- BEFORE_OBSERVERS = ':before';
+ __exports__.registerHandlebarsCompatibleHelper = registerHandlebarsCompatibleHelper;function handlebarsHelper(name, value) {
+ Ember.assert("You tried to register a component named '" + name +
+ "', but component names must include a '-'", !Component.detect(value) || name.match(/-/));
- function changeEvent(keyName) {
- return keyName+AFTER_OBSERVERS;
- }
+ if (View.detect(value)) {
+ helpers[name] = makeViewHelper(value);
+ } else {
+ var boundHelperArgs = slice.call(arguments, 1);
+ var boundFn = makeBoundHelper.apply(this, boundHelperArgs);
- function beforeEvent(keyName) {
- return keyName+BEFORE_OBSERVERS;
+ helpers[name] = boundFn;
+ }
}
+ __exports__.handlebarsHelper = handlebarsHelper;__exports__["default"] = HandlebarsCompatibleHelper;
+ });
+enifed("ember-htmlbars/compat/make-bound-helper",
+ ["ember-metal/core","ember-metal/mixin","ember-htmlbars/system/helper","ember-metal/streams/stream","ember-metal/streams/utils","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) {
+ "use strict";
/**
- @method addObserver
- @for Ember
- @param obj
- @param {String} path
- @param {Object|Function} targetOrMethod
- @param {Function|String} [method]
+ @module ember
+ @submodule ember-htmlbars
*/
- function addObserver(obj, _path, target, method) {
- addListener(obj, changeEvent(_path), target, method);
- watch(obj, _path);
- return this;
- };
+ var Ember = __dependency1__["default"];
+ // Ember.FEATURES, Ember.assert, Ember.Handlebars, Ember.lookup
+ var IS_BINDING = __dependency2__.IS_BINDING;
+ var Helper = __dependency3__["default"];
- function observersFor(obj, path) {
- return listenersFor(obj, changeEvent(path));
- };
+ var Stream = __dependency4__["default"];
+ var readArray = __dependency5__.readArray;
+ var scanArray = __dependency5__.scanArray;
+ var scanHash = __dependency5__.scanHash;
+ var readHash = __dependency5__.readHash;
+ var isStream = __dependency5__.isStream;
/**
- @method removeObserver
- @for Ember
- @param obj
- @param {String} path
- @param {Object|Function} targetOrMethod
- @param {Function|String} [method]
- */
- function removeObserver(obj, _path, target, method) {
- unwatch(obj, _path);
- removeListener(obj, changeEvent(_path), target, method);
+ A helper function used by `registerBoundHelper`. Takes the
+ provided Handlebars helper function fn and returns it in wrapped
+ bound helper form.
- return this;
- };
+ The main use case for using this outside of `registerBoundHelper`
+ is for registering helpers on the container:
- /**
- @method addBeforeObserver
- @for Ember
- @param obj
- @param {String} path
- @param {Object|Function} targetOrMethod
- @param {Function|String} [method]
- */
- function addBeforeObserver(obj, _path, target, method) {
- addListener(obj, beforeEvent(_path), target, method);
- watch(obj, _path);
+ ```js
+ var boundHelperFn = Ember.Handlebars.makeBoundHelper(function(word) {
+ return word.toUpperCase();
+ });
- return this;
- };
+ container.register('helper:my-bound-helper', boundHelperFn);
+ ```
- // Suspend observer during callback.
- //
- // This should only be used by the target of the observer
- // while it is setting the observed path.
- function _suspendBeforeObserver(obj, path, target, method, callback) {
- return suspendListener(obj, beforeEvent(path), target, method, callback);
- };
+ In the above example, if the helper function hadn't been wrapped in
+ `makeBoundHelper`, the registered helper would be unbound.
- function _suspendObserver(obj, path, target, method, callback) {
- return suspendListener(obj, changeEvent(path), target, method, callback);
- };
+ @method makeBoundHelper
+ @for Ember.Handlebars
+ @param {Function} function
+ @param {String} dependentKeys*
+ @since 1.2.0
+ @deprecated
+ */
+ __exports__["default"] = function makeBoundHelper(fn, compatMode) {
+ var dependentKeys = [];
+ for (var i = 1; i < arguments.length; i++) {
+ dependentKeys.push(arguments[i]);
+ }
- function _suspendBeforeObservers(obj, paths, target, method, callback) {
- var events = map.call(paths, beforeEvent);
- return suspendListeners(obj, events, target, method, callback);
- };
+ function helperFunc(params, hash, options, env) {
+ var view = this;
+ var numParams = params.length;
+ var param;
- function _suspendObservers(obj, paths, target, method, callback) {
- var events = map.call(paths, changeEvent);
- return suspendListeners(obj, events, target, method, callback);
- };
-
- function beforeObserversFor(obj, path) {
- return listenersFor(obj, beforeEvent(path));
- };
+ Ember.assert("registerBoundHelper-generated helpers do not support use with Handlebars blocks.", !options.template);
- /**
- @method removeBeforeObserver
- @for Ember
- @param obj
- @param {String} path
- @param {Object|Function} targetOrMethod
- @param {Function|String} [method]
- */
- function removeBeforeObserver(obj, _path, target, method) {
- unwatch(obj, _path);
- removeListener(obj, beforeEvent(_path), target, method);
+ for (var prop in hash) {
+ if (IS_BINDING.test(prop)) {
+ hash[prop.slice(0, -7)] = view.getStream(hash[prop]);
+ delete hash[prop];
+ }
+ }
- return this;
- };
+ function valueFn() {
+ var args = readArray(params);
+ var properties = new Array(params.length);
+ for (var i = 0, l = params.length; i < l; i++) {
+ param = params[i];
- __exports__.addObserver = addObserver;
- __exports__.observersFor = observersFor;
- __exports__.removeObserver = removeObserver;
- __exports__.addBeforeObserver = addBeforeObserver;
- __exports__._suspendBeforeObserver = _suspendBeforeObserver;
- __exports__._suspendObserver = _suspendObserver;
- __exports__._suspendBeforeObservers = _suspendBeforeObservers;
- __exports__._suspendObservers = _suspendObservers;
- __exports__.beforeObserversFor = beforeObserversFor;
- __exports__.removeBeforeObserver = removeBeforeObserver;
- });
-define("ember-metal/observer_set",
- ["ember-metal/utils","ember-metal/events","exports"],
- function(__dependency1__, __dependency2__, __exports__) {
- "use strict";
- var guidFor = __dependency1__.guidFor;
- var sendEvent = __dependency2__.sendEvent;
+ if (isStream(param)) {
+ properties[i] = param._label;
+ } else {
+ properties[i] = param;
+ }
+ }
- /*
- this.observerSet = {
- [senderGuid]: { // variable name: `keySet`
- [keyName]: listIndex
+ args.push({
+ hash: readHash(hash),
+ data: { properties: properties }
+ });
+ return fn.apply(view, args);
}
- },
- this.observers = [
- {
- sender: obj,
- keyName: keyName,
- eventName: eventName,
- listeners: [
- [target, method, flags]
- ]
- },
- ...
- ]
- */
- function ObserverSet() {
- this.clear();
- };
- ObserverSet.prototype.add = function(sender, keyName, eventName) {
- var observerSet = this.observerSet,
- observers = this.observers,
- senderGuid = guidFor(sender),
- keySet = observerSet[senderGuid],
- index;
+ // If none of the hash parameters are bound, act as an unbound helper.
+ // This prevents views from being unnecessarily created
+ var hasStream = scanArray(params) || scanHash(hash);
- if (!keySet) {
- observerSet[senderGuid] = keySet = {};
- }
- index = keySet[keyName];
- if (index === undefined) {
- index = observers.push({
- sender: sender,
- keyName: keyName,
- eventName: eventName,
- listeners: []
- }) - 1;
- keySet[keyName] = index;
- }
- return observers[index].listeners;
- };
+ if (env.data.isUnbound || !hasStream){
+ return valueFn();
+ } else {
+ var lazyValue = new Stream(valueFn);
- ObserverSet.prototype.flush = function() {
- var observers = this.observers, i, len, observer, sender;
- this.clear();
- for (i=0, len=observers.length; i < len; ++i) {
- observer = observers[i];
- sender = observer.sender;
- if (sender.isDestroying || sender.isDestroyed) { continue; }
- sendEvent(sender, observer.eventName, [sender, observer.keyName], observer.listeners);
- }
- };
+ for (i = 0; i < numParams; i++) {
+ param = params[i];
+ if (isStream(param)) {
+ param.subscribe(lazyValue.notify, lazyValue);
+ }
+ }
- ObserverSet.prototype.clear = function() {
- this.observerSet = {};
- this.observers = [];
- };
+ for (prop in hash) {
+ param = hash[prop];
+ if (isStream(param)) {
+ param.subscribe(lazyValue.notify, lazyValue);
+ }
+ }
- __exports__["default"] = ObserverSet;
+ if (numParams > 0) {
+ var firstParam = params[0];
+ // Only bother with subscriptions if the first argument
+ // is a stream itself, and not a primitive.
+ if (isStream(firstParam)) {
+ var onDependentKeyNotify = function onDependentKeyNotify(stream) {
+ stream.value();
+ lazyValue.notify();
+ };
+ for (i = 0; i < dependentKeys.length; i++) {
+ var childParam = firstParam.get(dependentKeys[i]);
+ childParam.value();
+ childParam.subscribe(onDependentKeyNotify);
+ }
+ }
+ }
+
+ return lazyValue;
+ }
+ }
+
+ return new Helper(helperFunc);
+ }
});
-define("ember-metal/platform",
- ["ember-metal/core","exports"],
+enifed("ember-htmlbars/compat/precompile",
+ ["htmlbars-compiler/compiler","exports"],
function(__dependency1__, __exports__) {
"use strict";
- /*globals Node */
+ /**
+ @module ember
+ @submodule ember-htmlbars
+ */
- var Ember = __dependency1__["default"];
+ var compile = __dependency1__.compile;
+ var compileSpec = __dependency1__.compileSpec;
+ __exports__["default"] = function(string) {
+ var asObject = arguments[1] === undefined ? true : arguments[1];
+ var compileFunc = asObject ? compile : compileSpec;
+
+ return compileFunc(string);
+ }
+ });
+enifed("ember-htmlbars/compat/register-bound-helper",
+ ["ember-htmlbars/helpers","ember-htmlbars/compat/make-bound-helper","exports"],
+ function(__dependency1__, __dependency2__, __exports__) {
+ "use strict";
/**
- @module ember-metal
+ @module ember
+ @submodule ember-htmlbars
*/
- /**
- Platform specific methods and feature detectors needed by the framework.
+ var helpers = __dependency1__["default"];
+ var makeBoundHelper = __dependency2__["default"];
- @class platform
- @namespace Ember
- @static
- */
- // TODO remove this
- var platform = {};
+ var slice = [].slice;
/**
- Identical to `Object.create()`. Implements if not available natively.
+ Register a bound handlebars helper. Bound helpers behave similarly to regular
+ handlebars helpers, with the added ability to re-render when the underlying data
+ changes.
- @method create
- @for Ember
- */
- var create = Object.create;
+ ## Simple example
- // IE8 has Object.create but it couldn't treat property descriptors.
- if (create) {
- if (create({a: 1}, {a: {value: 2}}).a !== 2) {
- create = null;
- }
- }
+ ```javascript
+ Ember.Handlebars.registerBoundHelper('capitalize', function(value) {
+ return Ember.String.capitalize(value);
+ });
+ ```
- // STUB_OBJECT_CREATE allows us to override other libraries that stub
- // Object.create different than we would prefer
- if (!create || Ember.ENV.STUB_OBJECT_CREATE) {
- var K = function() {};
+ The above bound helper can be used inside of templates as follows:
- create = function(obj, props) {
- K.prototype = obj;
- obj = new K();
- if (props) {
- K.prototype = obj;
- for (var prop in props) {
- K.prototype[prop] = props[prop].value;
- }
- obj = new K();
- }
- K.prototype = null;
+ ```handlebars
+ {{capitalize name}}
+ ```
- return obj;
- };
+ In this case, when the `name` property of the template's context changes,
+ the rendered value of the helper will update to reflect this change.
- create.isSimulated = true;
- }
+ ## Example with options
- var defineProperty = Object.defineProperty;
- var canRedefineProperties, canDefinePropertyOnDOM;
+ Like normal handlebars helpers, bound helpers have access to the options
+ passed into the helper call.
- // Catch IE8 where Object.defineProperty exists but only works on DOM elements
- if (defineProperty) {
- try {
- defineProperty({}, 'a',{get:function() {}});
- } catch (e) {
- defineProperty = null;
- }
- }
+ ```javascript
+ Ember.Handlebars.registerBoundHelper('repeat', function(value, options) {
+ var count = options.hash.count;
+ var a = [];
+ while(a.length < count) {
+ a.push(value);
+ }
+ return a.join('');
+ });
+ ```
- if (defineProperty) {
- // Detects a bug in Android <3.2 where you cannot redefine a property using
- // Object.defineProperty once accessors have already been set.
- canRedefineProperties = (function() {
- var obj = {};
+ This helper could be used in a template as follows:
- defineProperty(obj, 'a', {
- configurable: true,
- enumerable: true,
- get: function() { },
- set: function() { }
- });
+ ```handlebars
+ {{repeat text count=3}}
+ ```
- defineProperty(obj, 'a', {
- configurable: true,
- enumerable: true,
- writable: true,
- value: true
- });
+ ## Example with bound options
- return obj.a === true;
- })();
+ Bound hash options are also supported. Example:
- // This is for Safari 5.0, which supports Object.defineProperty, but not
- // on DOM nodes.
- canDefinePropertyOnDOM = (function() {
- try {
- defineProperty(document.createElement('div'), 'definePropertyOnDOM', {});
- return true;
- } catch(e) { }
+ ```handlebars
+ {{repeat text count=numRepeats}}
+ ```
- return false;
- })();
+ In this example, count will be bound to the value of
+ the `numRepeats` property on the context. If that property
+ changes, the helper will be re-rendered.
- if (!canRedefineProperties) {
- defineProperty = null;
- } else if (!canDefinePropertyOnDOM) {
- defineProperty = function(obj, keyName, desc) {
- var isNode;
+ ## Example with extra dependencies
- if (typeof Node === "object") {
- isNode = obj instanceof Node;
- } else {
- isNode = typeof obj === "object" && typeof obj.nodeType === "number" && typeof obj.nodeName === "string";
- }
+ The `Ember.Handlebars.registerBoundHelper` method takes a variable length
+ third parameter which indicates extra dependencies on the passed in value.
+ This allows the handlebars helper to update when these dependencies change.
- if (isNode) {
- // TODO: Should we have a warning here?
- return (obj[keyName] = desc.value);
- } else {
- return Object.defineProperty(obj, keyName, desc);
- }
- };
- }
- }
+ ```javascript
+ Ember.Handlebars.registerBoundHelper('capitalizeName', function(value) {
+ return value.get('name').toUpperCase();
+ }, 'name');
+ ```
- /**
- @class platform
- @namespace Ember
- */
+ ## Example with multiple bound properties
- /**
- Identical to `Object.defineProperty()`. Implements as much functionality
- as possible if not available natively.
+ `Ember.Handlebars.registerBoundHelper` supports binding to
+ multiple properties, e.g.:
- @method defineProperty
- @param {Object} obj The object to modify
- @param {String} keyName property name to modify
- @param {Object} desc descriptor hash
- @return {void}
- */
- platform.defineProperty = defineProperty;
+ ```javascript
+ Ember.Handlebars.registerBoundHelper('concatenate', function() {
+ var values = Array.prototype.slice.call(arguments, 0, -1);
+ return values.join('||');
+ });
+ ```
- /**
- Set to true if the platform supports native getters and setters.
+ Which allows for template syntax such as `{{concatenate prop1 prop2}}` or
+ `{{concatenate prop1 prop2 prop3}}`. If any of the properties change,
+ the helper will re-render. Note that dependency keys cannot be
+ using in conjunction with multi-property helpers, since it is ambiguous
+ which property the dependent keys would belong to.
- @property hasPropertyAccessors
- @final
- */
- platform.hasPropertyAccessors = true;
+ ## Use with unbound helper
- if (!platform.defineProperty) {
- platform.hasPropertyAccessors = false;
+ The `{{unbound}}` helper can be used with bound helper invocations
+ to render them in their unbound form, e.g.
- platform.defineProperty = function(obj, keyName, desc) {
- if (!desc.get) { obj[keyName] = desc.value; }
- };
+ ```handlebars
+ {{unbound capitalize name}}
+ ```
- platform.defineProperty.isSimulated = true;
- }
+ In this example, if the name property changes, the helper
+ will not re-render.
- if (Ember.ENV.MANDATORY_SETTER && !platform.hasPropertyAccessors) {
- Ember.ENV.MANDATORY_SETTER = false;
- }
+ ## Use with blocks not supported
- __exports__.create = create;
- __exports__.platform = platform;
+ Bound helpers do not support use with Handlebars blocks or
+ the addition of child views of any kind.
+
+ @method registerBoundHelper
+ @for Ember.Handlebars
+ @param {String} name
+ @param {Function} function
+ @param {String} dependentKeys*
+ */
+ __exports__["default"] = function registerBoundHelper(name, fn) {
+ var boundHelperArgs = slice.call(arguments, 1);
+ var boundFn = makeBoundHelper.apply(this, boundHelperArgs);
+
+ helpers[name] = boundFn;
+ }
});
-define("ember-metal/properties",
- ["ember-metal/core","ember-metal/utils","ember-metal/platform","ember-metal/property_events","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) {
+enifed("ember-htmlbars/helpers",
+ ["ember-metal/platform","ember-views/views/view","ember-views/views/component","ember-htmlbars/system/make-view-helper","ember-htmlbars/system/helper","ember-htmlbars/system/make_bound_helper","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) {
"use strict";
/**
- @module ember-metal
+ @module ember
+ @submodule ember-htmlbars
*/
- var Ember = __dependency1__["default"];
- var META_KEY = __dependency2__.META_KEY;
- var meta = __dependency2__.meta;
- var platform = __dependency3__.platform;
- var overrideChains = __dependency4__.overrideChains;
- var metaFor = meta,
- objectDefineProperty = platform.defineProperty;
-
- var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER;
+ var o_create = __dependency1__.create;
- // ..........................................................
- // DESCRIPTOR
- //
+ /**
+ @private
+ @property helpers
+ */
+ var helpers = o_create(null);
/**
- Objects of this type can implement an interface to respond to requests to
- get and set. The default implementation handles simple properties.
+ @module ember
+ @submodule ember-htmlbars
+ */
- You generally won't need to create or subclass this directly.
+ var View = __dependency2__["default"];
+ var Component = __dependency3__["default"];
+ var makeViewHelper = __dependency4__["default"];
+ var Helper = __dependency5__["default"];
+ var makeBoundHelper = __dependency6__["default"];
- @class Descriptor
- @namespace Ember
- @private
- @constructor
- */
- function Descriptor() {};
+ /**
+ Register a bound helper or custom view helper.
- // ..........................................................
- // DEFINING PROPERTIES API
- //
+ ## Simple bound helper example
- var MANDATORY_SETTER_FUNCTION = Ember.MANDATORY_SETTER_FUNCTION = function(value) {
- Ember.assert("You must use Ember.set() to access this property (of " + this + ")", false);
- };
+ ```javascript
+ Ember.HTMLBars.helper('capitalize', function(value) {
+ return value.toUpperCase();
+ });
+ ```
- var DEFAULT_GETTER_FUNCTION = Ember.DEFAULT_GETTER_FUNCTION = function(name) {
- return function() {
- var meta = this[META_KEY];
- return meta && meta.values[name];
- };
- };
+ The above bound helper can be used inside of templates as follows:
- /**
- NOTE: This is a low-level method used by other parts of the API. You almost
- never want to call this method directly. Instead you should use
- `Ember.mixin()` to define new properties.
+ ```handlebars
+ {{capitalize name}}
+ ```
- Defines a property on an object. This method works much like the ES5
- `Object.defineProperty()` method except that it can also accept computed
- properties and other special descriptors.
+ In this case, when the `name` property of the template's context changes,
+ the rendered value of the helper will update to reflect this change.
- Normally this method takes only three parameters. However if you pass an
- instance of `Ember.Descriptor` as the third param then you can pass an
- optional value as the fourth parameter. This is often more efficient than
- creating new descriptor hashes for each property.
+ For more examples of bound helpers, see documentation for
+ `Ember.HTMLBars.registerBoundHelper`.
- ## Examples
+ ## Custom view helper example
+
+ Assuming a view subclass named `App.CalendarView` were defined, a helper
+ for rendering instances of this view could be registered as follows:
```javascript
- // ES5 compatible mode
- Ember.defineProperty(contact, 'firstName', {
- writable: true,
- configurable: false,
- enumerable: true,
- value: 'Charles'
- });
+ Ember.HTMLBars.helper('calendar', App.CalendarView):
+ ```
- // define a simple property
- Ember.defineProperty(contact, 'lastName', undefined, 'Jolley');
+ The above bound helper can be used inside of templates as follows:
- // define a computed property
- Ember.defineProperty(contact, 'fullName', Ember.computed(function() {
- return this.firstName+' '+this.lastName;
- }).property('firstName', 'lastName'));
+ ```handlebars
+ {{calendar}}
```
- @private
- @method defineProperty
- @for Ember
- @param {Object} obj the object to define this property on. This may be a prototype.
- @param {String} keyName the name of the property
- @param {Ember.Descriptor} [desc] an instance of `Ember.Descriptor` (typically a
- computed property) or an ES5 descriptor.
- You must provide this or `data` but not both.
- @param {*} [data] something other than a descriptor, that will
- become the explicit value of this property.
- */
- function defineProperty(obj, keyName, desc, data, meta) {
- var descs, existingDesc, watching, value;
+ Which is functionally equivalent to:
- if (!meta) meta = metaFor(obj);
- descs = meta.descs;
- existingDesc = meta.descs[keyName];
- watching = meta.watching[keyName] > 0;
+ ```handlebars
+ {{view 'calendar'}}
+ ```
- if (existingDesc instanceof Descriptor) {
- existingDesc.teardown(obj, keyName);
- }
+ Options in the helper will be passed to the view in exactly the same
+ manner as with the `view` helper.
- if (desc instanceof Descriptor) {
- value = desc;
+ @private
+ @method helper
+ @for Ember.HTMLBars
+ @param {String} name
+ @param {Function|Ember.View} function or view class constructor
+ */
+ function helper(name, value) {
+ Ember.assert("You tried to register a component named '" + name +
+ "', but component names must include a '-'", !Component.detect(value) || name.match(/-/));
- descs[keyName] = desc;
- if (MANDATORY_SETTER && watching) {
- objectDefineProperty(obj, keyName, {
- configurable: true,
- enumerable: true,
- writable: true,
- value: undefined // make enumerable
- });
- } else {
- obj[keyName] = undefined; // make enumerable
- }
+ if (View.detect(value)) {
+ helpers[name] = makeViewHelper(value);
} else {
- descs[keyName] = undefined; // shadow descriptor in proto
- if (desc == null) {
- value = data;
-
- if (MANDATORY_SETTER && watching) {
- meta.values[keyName] = data;
- objectDefineProperty(obj, keyName, {
- configurable: true,
- enumerable: true,
- set: MANDATORY_SETTER_FUNCTION,
- get: DEFAULT_GETTER_FUNCTION(keyName)
- });
- } else {
- obj[keyName] = data;
- }
- } else {
- value = desc;
-
- // compatibility with ES5
- objectDefineProperty(obj, keyName, desc);
- }
+ registerBoundHelper(name, value);
}
+ }
- // if key is being watched, override chains that
- // were initialized with the prototype
- if (watching) { overrideChains(obj, keyName, meta); }
+ __exports__.helper = helper;/**
+ @private
+ @method registerHelper
+ @for Ember.HTMLBars
+ @param {String} name
+ @param {Function} helperFunc the helper function to add
+ */
+ function registerHelper(name, helperFunc, preprocessFunction) {
+ helpers[name] = new Helper(helperFunc, preprocessFunction);
+ }
- // The `value` passed to the `didDefineProperty` hook is
- // either the descriptor or data, whichever was passed.
- if (obj.didDefineProperty) { obj.didDefineProperty(obj, keyName, value); }
+ __exports__.registerHelper = registerHelper;/**
+ Register a bound helper. Bound helpers behave similarly to regular
+ helpers, with the added ability to re-render when the underlying data
+ changes.
- return this;
- };
+ ## Simple example
- __exports__.Descriptor = Descriptor;
- __exports__.defineProperty = defineProperty;
- });
-define("ember-metal/property_events",
- ["ember-metal/utils","ember-metal/events","ember-metal/observer_set","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
- "use strict";
- var META_KEY = __dependency1__.META_KEY;
- var guidFor = __dependency1__.guidFor;
- var tryFinally = __dependency1__.tryFinally;
- var sendEvent = __dependency2__.sendEvent;
- var listenersUnion = __dependency2__.listenersUnion;
- var listenersDiff = __dependency2__.listenersDiff;
- var ObserverSet = __dependency3__["default"];
+ ```javascript
+ Ember.HTMLBars.registerBoundHelper('capitalize', function(params, hash, options, env) {
+ return Ember.String.capitalize(params[0]);
+ });
+ ```
- var beforeObserverSet = new ObserverSet(),
- observerSet = new ObserverSet(),
- deferred = 0;
+ The above bound helper can be used inside of templates as follows:
- // ..........................................................
- // PROPERTY CHANGES
- //
+ ```handlebars
+ {{capitalize name}}
+ ```
- /**
- This function is called just before an object property is about to change.
- It will notify any before observers and prepare caches among other things.
+ In this case, when the `name` property of the template's context changes,
+ the rendered value of the helper will update to reflect this change.
- Normally you will not need to call this method directly but if for some
- reason you can't directly watch a property you can invoke this method
- manually along with `Ember.propertyDidChange()` which you should call just
- after the property value changes.
+ ## Example with hash parameters
- @method propertyWillChange
- @for Ember
- @param {Object} obj The object with the property that will change
- @param {String} keyName The property key (or path) that will change.
- @return {void}
- */
- function propertyWillChange(obj, keyName) {
- var m = obj[META_KEY],
- watching = (m && m.watching[keyName] > 0) || keyName === 'length',
- proto = m && m.proto,
- desc = m && m.descs[keyName];
-
- if (!watching) { return; }
- if (proto === obj) { return; }
- if (desc && desc.willChange) { desc.willChange(obj, keyName); }
- dependentKeysWillChange(obj, keyName, m);
- chainsWillChange(obj, keyName, m);
- notifyBeforeObservers(obj, keyName);
- }
+ Like normal helpers, bound helpers have access to the hash parameters
+ passed into the helper call.
- /**
- This function is called just after an object property has changed.
- It will notify any observers and clear caches among other things.
+ ```javascript
+ Ember.HTMLBars.registerBoundHelper('repeat', function(params, hash) {
+ var count = hash.count;
+ var value = params[0];
- Normally you will not need to call this method directly but if for some
- reason you can't directly watch a property you can invoke this method
- manually along with `Ember.propertyWillChange()` which you should call just
- before the property value changes.
+ return new Array( count + 1).join( value );
+ });
+ ```
- @method propertyDidChange
- @for Ember
- @param {Object} obj The object with the property that will change
- @param {String} keyName The property key (or path) that will change.
- @return {void}
- */
- function propertyDidChange(obj, keyName) {
- var m = obj[META_KEY],
- watching = (m && m.watching[keyName] > 0) || keyName === 'length',
- proto = m && m.proto,
- desc = m && m.descs[keyName];
+ This helper could be used in a template as follows:
- if (proto === obj) { return; }
+ ```handlebars
+ {{repeat text count=3}}
+ ```
- // shouldn't this mean that we're watching this key?
- if (desc && desc.didChange) { desc.didChange(obj, keyName); }
- if (!watching && keyName !== 'length') { return; }
+ ## Example with bound hash parameters
- dependentKeysDidChange(obj, keyName, m);
- chainsDidChange(obj, keyName, m, false);
- notifyObservers(obj, keyName);
- }
+ Bound hash params are also supported. Example:
- var WILL_SEEN, DID_SEEN;
+ ```handlebars
+ {{repeat text count=numRepeats}}
+ ```
- // called whenever a property is about to change to clear the cache of any dependent keys (and notify those properties of changes, etc...)
- function dependentKeysWillChange(obj, depKey, meta) {
- if (obj.isDestroying) { return; }
+ In this example, count will be bound to the value of
+ the `numRepeats` property on the context. If that property
+ changes, the helper will be re-rendered.
- var seen = WILL_SEEN, top = !seen;
- if (top) { seen = WILL_SEEN = {}; }
- iterDeps(propertyWillChange, obj, depKey, seen, meta);
- if (top) { WILL_SEEN = null; }
- }
+ ## Example with multiple bound properties
- // called whenever a property has just changed to update dependent keys
- function dependentKeysDidChange(obj, depKey, meta) {
- if (obj.isDestroying) { return; }
+ `Ember.HTMLBars.registerBoundHelper` supports binding to
+ multiple properties, e.g.:
- var seen = DID_SEEN, top = !seen;
- if (top) { seen = DID_SEEN = {}; }
- iterDeps(propertyDidChange, obj, depKey, seen, meta);
- if (top) { DID_SEEN = null; }
- }
+ ```javascript
+ Ember.HTMLBars.registerBoundHelper('concatenate', function(params) {
+ return params.join('||');
+ });
+ ```
- function iterDeps(method, obj, depKey, seen, meta) {
- var guid = guidFor(obj);
- if (!seen[guid]) seen[guid] = {};
- if (seen[guid][depKey]) return;
- seen[guid][depKey] = true;
+ Which allows for template syntax such as `{{concatenate prop1 prop2}}` or
+ `{{concatenate prop1 prop2 prop3}}`. If any of the properties change,
+ the helper will re-render.
- var deps = meta.deps;
- deps = deps && deps[depKey];
- if (deps) {
- for(var key in deps) {
- var desc = meta.descs[key];
- if (desc && desc._suspended === obj) continue;
- method(obj, key);
- }
- }
- }
-
- function chainsWillChange(obj, keyName, m) {
- if (!(m.hasOwnProperty('chainWatchers') &&
- m.chainWatchers[keyName])) {
- return;
- }
-
- var nodes = m.chainWatchers[keyName],
- events = [],
- i, l;
-
- for(i = 0, l = nodes.length; i < l; i++) {
- nodes[i].willChange(events);
- }
-
- for (i = 0, l = events.length; i < l; i += 2) {
- propertyWillChange(events[i], events[i+1]);
- }
- }
-
- function chainsDidChange(obj, keyName, m, suppressEvents) {
- if (!(m && m.hasOwnProperty('chainWatchers') &&
- m.chainWatchers[keyName])) {
- return;
- }
+ ## Use with unbound helper
- var nodes = m.chainWatchers[keyName],
- events = suppressEvents ? null : [],
- i, l;
+ The `{{unbound}}` helper can be used with bound helper invocations
+ to render them in their unbound form, e.g.
- for(i = 0, l = nodes.length; i < l; i++) {
- nodes[i].didChange(events);
- }
+ ```handlebars
+ {{unbound capitalize name}}
+ ```
- if (suppressEvents) {
- return;
- }
+ In this example, if the name property changes, the helper
+ will not re-render.
- for (i = 0, l = events.length; i < l; i += 2) {
- propertyDidChange(events[i], events[i+1]);
- }
- }
+ ## Use with blocks not supported
- function overrideChains(obj, keyName, m) {
- chainsDidChange(obj, keyName, m, true);
- };
+ Bound helpers do not support use with blocks or the addition of
+ child views of any kind.
- /**
- @method beginPropertyChanges
- @chainable
@private
+ @method registerBoundHelper
+ @for Ember.HTMLBars
+ @param {String} name
+ @param {Function} function
*/
- function beginPropertyChanges() {
- deferred++;
+ function registerBoundHelper(name, fn) {
+ var boundFn = makeBoundHelper(fn);
+
+ helpers[name] = boundFn;
}
+ __exports__.registerBoundHelper = registerBoundHelper;__exports__["default"] = helpers;
+ });
+enifed("ember-htmlbars/helpers/bind-attr",
+ ["ember-metal/core","ember-runtime/system/string","ember-views/attr_nodes/attr_node","ember-views/attr_nodes/legacy_bind","ember-metal/keys","ember-htmlbars/helpers","ember-metal/enumerable_utils","ember-metal/streams/utils","ember-views/streams/class_name_binding","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __exports__) {
+ "use strict";
/**
- @method endPropertyChanges
- @private
+ @module ember
+ @submodule ember-htmlbars
*/
- function endPropertyChanges() {
- deferred--;
- if (deferred<=0) {
- beforeObserverSet.clear();
- observerSet.flush();
- }
- }
+
+ var Ember = __dependency1__["default"];
+ // Ember.assert
+
+ var fmt = __dependency2__.fmt;
+ var AttrNode = __dependency3__["default"];
+ var LegacyBindAttrNode = __dependency4__["default"];
+ var keys = __dependency5__["default"];
+ var helpers = __dependency6__["default"];
+ var map = __dependency7__.map;
+ var isStream = __dependency8__.isStream;
+ var concat = __dependency8__.concat;
+ var streamifyClassNameBinding = __dependency9__.streamifyClassNameBinding;
/**
- Make a series of property changes together in an
- exception-safe way.
+ `bind-attr` allows you to create a binding between DOM element attributes and
+ Ember objects. For example:
- ```javascript
- Ember.changeProperties(function() {
- obj1.set('foo', mayBlowUpWhenSet);
- obj2.set('bar', baz);
- });
+ ```handlebars
+
```
- @method changeProperties
- @param {Function} callback
- @param [binding]
- */
- function changeProperties(cb, binding) {
- beginPropertyChanges();
- tryFinally(cb, endPropertyChanges, binding);
- };
+ The above handlebars template will fill the ``'s `src` attribute with
+ the value of the property referenced with `imageUrl` and its `alt`
+ attribute with the value of the property referenced with `imageTitle`.
- function notifyBeforeObservers(obj, keyName) {
- if (obj.isDestroying) { return; }
+ If the rendering context of this template is the following object:
- var eventName = keyName + ':before', listeners, diff;
- if (deferred) {
- listeners = beforeObserverSet.add(obj, keyName, eventName);
- diff = listenersDiff(obj, eventName, listeners);
- sendEvent(obj, eventName, [obj, keyName], diff);
- } else {
- sendEvent(obj, eventName, [obj, keyName]);
+ ```javascript
+ {
+ imageUrl: 'http://lolcats.info/haz-a-funny',
+ imageTitle: 'A humorous image of a cat'
}
- }
-
- function notifyObservers(obj, keyName) {
- if (obj.isDestroying) { return; }
+ ```
- var eventName = keyName + ':change', listeners;
- if (deferred) {
- listeners = observerSet.add(obj, keyName, eventName);
- listenersUnion(obj, eventName, listeners);
- } else {
- sendEvent(obj, eventName, [obj, keyName]);
- }
- }
+ The resulting HTML output will be:
- __exports__.propertyWillChange = propertyWillChange;
- __exports__.propertyDidChange = propertyDidChange;
- __exports__.overrideChains = overrideChains;
- __exports__.beginPropertyChanges = beginPropertyChanges;
- __exports__.endPropertyChanges = endPropertyChanges;
- __exports__.changeProperties = changeProperties;
- });
-define("ember-metal/property_get",
- ["ember-metal/core","ember-metal/utils","ember-metal/error","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
- "use strict";
- /**
- @module ember-metal
- */
+ ```html
+
+ ```
- var Ember = __dependency1__["default"];
- var META_KEY = __dependency2__.META_KEY;
- var EmberError = __dependency3__["default"];
+ `bind-attr` cannot redeclare existing DOM element attributes. The use of `src`
+ in the following `bind-attr` example will be ignored and the hard coded value
+ of `src="/failwhale.gif"` will take precedence:
- var get;
+ ```handlebars
+
+ ```
- var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER;
+ ### `bind-attr` and the `class` attribute
- var IS_GLOBAL_PATH = /^([A-Z$]|([0-9][A-Z$])).*[\.]/;
- var HAS_THIS = 'this.';
- var FIRST_KEY = /^([^\.]+)/;
+ `bind-attr` supports a special syntax for handling a number of cases unique
+ to the `class` DOM element attribute. The `class` attribute combines
+ multiple discrete values into a single attribute as a space-delimited
+ list of strings. Each string can be:
- // ..........................................................
- // GET AND SET
- //
- // If we are on a platform that supports accessors we can use those.
- // Otherwise simulate accessors by looking up the property directly on the
- // object.
+ * a string return value of an object's property.
+ * a boolean return value of an object's property
+ * a hard-coded value
- /**
- Gets the value of a property on an object. If the property is computed,
- the function will be invoked. If the property is not defined but the
- object implements the `unknownProperty` method then that will be invoked.
+ A string return value works identically to other uses of `bind-attr`. The
+ return value of the property will become the value of the attribute. For
+ example, the following view and template:
- If you plan to run on IE8 and older browsers then you should use this
- method anytime you want to retrieve a property on an object that you don't
- know for sure is private. (Properties beginning with an underscore '_'
- are considered private.)
+ ```javascript
+ AView = View.extend({
+ someProperty: function() {
+ return "aValue";
+ }.property()
+ })
+ ```
- On all newer browsers, you only need to use this method to retrieve
- properties if the property might not be defined on the object and you want
- to respect the `unknownProperty` handler. Otherwise you can ignore this
- method.
+ ```handlebars
+
+ ```
- Note that if the object itself is `undefined`, this method will throw
- an error.
+ Result in the following rendered output:
- @method get
- @for Ember
- @param {Object} obj The object to retrieve from.
- @param {String} keyName The property key to retrieve
- @return {Object} the property value or `null`.
- */
- get = function get(obj, keyName) {
- // Helpers that operate with 'this' within an #each
- if (keyName === '') {
- return obj;
- }
+ ```html
+
+ ```
- if (!keyName && 'string'===typeof obj) {
- keyName = obj;
- obj = null;
- }
+ A boolean return value will insert a specified class name if the property
+ returns `true` and remove the class name if the property returns `false`.
- Ember.assert("Cannot call get with "+ keyName +" key.", !!keyName);
- Ember.assert("Cannot call get with '"+ keyName +"' on an undefined object.", obj !== undefined);
+ A class name is provided via the syntax
+ `somePropertyName:class-name-if-true`.
- if (obj === null) { return _getPath(obj, keyName); }
+ ```javascript
+ AView = View.extend({
+ someBool: true
+ })
+ ```
- var meta = obj[META_KEY], desc = meta && meta.descs[keyName], ret;
+ ```handlebars
+
+ ```
- if (desc === undefined && keyName.indexOf('.') !== -1) {
- return _getPath(obj, keyName);
- }
+ Result in the following rendered output:
- if (desc) {
- return desc.get(obj, keyName);
- } else {
- if (MANDATORY_SETTER && meta && meta.watching[keyName] > 0) {
- ret = meta.values[keyName];
- } else {
- ret = obj[keyName];
- }
+ ```html
+
+ ```
- if (ret === undefined &&
- 'object' === typeof obj && !(keyName in obj) && 'function' === typeof obj.unknownProperty) {
- return obj.unknownProperty(keyName);
- }
+ An additional section of the binding can be provided if you want to
+ replace the existing class instead of removing it when the boolean
+ value changes:
- return ret;
- }
- };
+ ```handlebars
+
+ ```
- // Currently used only by Ember Data tests
- if (Ember.config.overrideAccessors) {
- Ember.get = get;
- Ember.config.overrideAccessors();
- get = Ember.get;
- }
+ A hard-coded value can be used by prepending `:` to the desired
+ class name: `:class-name-to-always-apply`.
- /**
- Normalizes a target/path pair to reflect that actual target/path that should
- be observed, etc. This takes into account passing in global property
- paths (i.e. a path beginning with a captial letter not defined on the
- target).
+ ```handlebars
+
+ ```
- @private
- @method normalizeTuple
- @for Ember
- @param {Object} target The current target. May be `null`.
- @param {String} path A path on the target or a global property path.
- @return {Array} a temporary array with the normalized target/path pair.
- */
- function normalizeTuple(target, path) {
- var hasThis = path.indexOf(HAS_THIS) === 0,
- isGlobal = !hasThis && IS_GLOBAL_PATH.test(path),
- key;
+ Results in the following rendered output:
- if (!target || isGlobal) target = Ember.lookup;
- if (hasThis) path = path.slice(5);
+ ```html
+
+ ```
- if (target === Ember.lookup) {
- key = path.match(FIRST_KEY)[0];
- target = get(target, key);
- path = path.slice(key.length+1);
- }
+ All three strategies - string return value, boolean return value, and
+ hard-coded value – can be combined in a single declaration:
- // must return some kind of path to be valid else other things will break.
- if (!path || path.length===0) throw new EmberError('Path cannot be empty');
+ ```handlebars
+
+ ```
- return [ target, path ];
- };
+ @method bind-attr
+ @for Ember.Handlebars.helpers
+ @param {Hash} options
+ @return {String} HTML string
+ */
+ function bindAttrHelper(params, hash, options, env) {
+ var element = options.element;
- function _getPath(root, path) {
- var hasThis, parts, tuple, idx, len;
+ Ember.assert("You must specify at least one hash argument to bind-attr", !!keys(hash).length);
- // If there is no root and path is a key name, return that
- // property from the global object.
- // E.g. get('Ember') -> Ember
- if (root === null && path.indexOf('.') === -1) { return get(Ember.lookup, path); }
+ var view = this;
- // detect complicated paths and normalize them
- hasThis = path.indexOf(HAS_THIS) === 0;
+ // Handle classes differently, as we can bind multiple classes
+ var classNameBindings = hash['class'];
+ if (classNameBindings !== null && classNameBindings !== undefined) {
+ if (!isStream(classNameBindings)) {
+ classNameBindings = applyClassNameBindings(classNameBindings, view);
+ }
- if (!root || hasThis) {
- tuple = normalizeTuple(root, path);
- root = tuple[0];
- path = tuple[1];
- tuple.length = 0;
+ var classView = new AttrNode('class', classNameBindings);
+ classView._morph = env.dom.createAttrMorph(element, 'class');
+ view.appendChild(classView);
}
- parts = path.split(".");
- len = parts.length;
- for (idx = 0; root != null && idx < len; idx++) {
- root = get(root, parts[idx], true);
- if (root && root.isDestroyed) { return undefined; }
+ var attrKeys = keys(hash);
+
+ var attr, path, lazyValue, attrView;
+ for (var i=0, l=attrKeys.length;i 0) {
- if (MANDATORY_SETTER) {
- currentValue = meta.values[keyName];
- } else {
- currentValue = obj[keyName];
- }
- // only trigger a change if the value has changed
- if (value !== currentValue) {
- propertyWillChange(obj, keyName);
- if (MANDATORY_SETTER) {
- if ((currentValue === undefined && !(keyName in obj)) || !obj.propertyIsEnumerable(keyName)) {
- defineProperty(obj, keyName, null, value); // setup mandatory setter
- } else {
- meta.values[keyName] = value;
- }
- } else {
- obj[keyName] = value;
- }
- propertyDidChange(obj, keyName);
- }
- } else {
- obj[keyName] = value;
- }
+ if (options.keywords) {
+ viewOptions._keywords = options.keywords;
}
- return value;
- };
- // Currently used only by Ember Data tests
- // ES6TODO: Verify still true
- if (Ember.config.overrideAccessors) {
- Ember.set = set;
- Ember.config.overrideAccessors();
- set = Ember.set;
+ // Create the view that will wrap the output of this template/property
+ // and add it to the nearest view's childViews array.
+ // See the documentation of Ember._BoundView for more.
+ var bindView = this.createChildView(viewClass, viewOptions);
+
+ this.appendChild(bindView);
+
+ lazyValue.subscribe(this._wrapAsScheduled(function() {
+ run.scheduleOnce('render', bindView, 'rerenderIfNeeded');
+ }));
}
- function setPath(root, path, value, tolerant) {
- var keyName;
+ /**
+ `bind` can be used to display a value, then update that value if it
+ changes. For example, if you wanted to print the `title` property of
+ `content`:
- // get the last part of the path
- keyName = path.slice(path.lastIndexOf('.') + 1);
+ ```handlebars
+ {{bind "content.title"}}
+ ```
- // get the first part of the part
- path = (path === keyName) ? keyName : path.slice(0, path.length-(keyName.length+1));
+ This will return the `title` property as a string, then create a new observer
+ at the specified path. If it changes, it will update the value in DOM. Note
+ that if you need to support IE7 and IE8 you must modify the model objects
+ properties using `Ember.get()` and `Ember.set()` for this to work as it
+ relies on Ember's KVO system. For all other browsers this will be handled for
+ you automatically.
- // unless the path is this, look up the first part to
- // get the root
- if (path !== 'this') {
- root = getPath(root, path);
- }
+ @private
+ @method bind
+ @for Ember.Handlebars.helpers
+ @param {String} property Property to bind
+ @param {Function} render Context to provide for rendering
+ @return {String} HTML string
+ */
+ function bindHelper(params, hash, options, env) {
+ Ember.assert("You must pass exactly one argument to the bind helper", params.length === 1);
- if (!keyName || keyName.length === 0) {
- throw new EmberError('Property set failed: You passed an empty path');
- }
+ var property = params[0];
- if (!root) {
- if (tolerant) { return; }
- else { throw new EmberError('Property set failed: object in path "'+path+'" could not be found or was destroyed.'); }
+ if (typeof property === 'string') {
+ property = this.getStream(property);
}
- return set(root, keyName, value);
+ if (options.template) {
+ options.helperName = 'bind';
+ Ember.deprecate("The block form of bind, {{#bind foo}}{{/bind}}, has been deprecated and will be removed.");
+ bind.call(this, property, hash, options, env, false, exists);
+ } else {
+ Ember.deprecate("The `{{bind}}` helper has been deprecated and will be removed.");
+
+ return property;
+ }
}
+ __exports__.bind = bind;
+ __exports__.bindHelper = bindHelper;
+ });
+enifed("ember-htmlbars/helpers/collection",
+ ["ember-metal/core","ember-metal/mixin","ember-runtime/system/string","ember-metal/property_get","ember-htmlbars/helpers/view","ember-views/views/collection_view","ember-views/streams/utils","ember-metal/enumerable_utils","ember-views/streams/class_name_binding","ember-metal/binding","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __exports__) {
+ "use strict";
/**
- Error-tolerant form of `Ember.set`. Will not blow up if any part of the
- chain is `undefined`, `null`, or destroyed.
-
- This is primarily used when syncing bindings, which may try to update after
- an object has been destroyed.
-
- @method trySet
- @for Ember
- @param {Object} obj The object to modify.
- @param {String} path The property path to set
- @param {Object} value The value to set
+ @module ember
+ @submodule ember-htmlbars
*/
- function trySet(root, path, value) {
- return set(root, path, value, true);
- };
- __exports__.set = set;
- __exports__.trySet = trySet;
- });
-define("ember-metal/run_loop",
- ["ember-metal/core","ember-metal/utils","ember-metal/array","ember-metal/property_events","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) {
- "use strict";
var Ember = __dependency1__["default"];
- var apply = __dependency2__.apply;
- var indexOf = __dependency3__.indexOf;
- var beginPropertyChanges = __dependency4__.beginPropertyChanges;
- var endPropertyChanges = __dependency4__.endPropertyChanges;
+ // Ember.assert, Ember.deprecate
+ var IS_BINDING = __dependency2__.IS_BINDING;
+ var fmt = __dependency3__.fmt;
+ var get = __dependency4__.get;
+ var ViewHelper = __dependency5__.ViewHelper;
+ var CollectionView = __dependency6__["default"];
+ var readViewFactory = __dependency7__.readViewFactory;
+ var map = __dependency8__.map;
+ var streamifyClassNameBinding = __dependency9__.streamifyClassNameBinding;
+ var Binding = __dependency10__.Binding;
- var onBegin = function(current) {
- run.currentRunLoop = current;
- };
+ /**
+ `{{collection}}` is a `Ember.Handlebars` helper for adding instances of
+ `Ember.CollectionView` to a template. See [Ember.CollectionView](/api/classes/Ember.CollectionView.html)
+ for additional information on how a `CollectionView` functions.
- var onEnd = function(current, next) {
- run.currentRunLoop = next;
- };
+ `{{collection}}`'s primary use is as a block helper with a `contentBinding`
+ option pointing towards an `Ember.Array`-compatible object. An `Ember.View`
+ instance will be created for each item in its `content` property. Each view
+ will have its own `content` property set to the appropriate item in the
+ collection.
- // ES6TODO: should Backburner become es6?
- var Backburner = requireModule('backburner').Backburner,
- backburner = new Backburner(['sync', 'actions', 'destroy'], {
- sync: {
- before: beginPropertyChanges,
- after: endPropertyChanges
- },
- defaultQueue: 'actions',
- onBegin: onBegin,
- onEnd: onEnd,
- onErrorTarget: Ember,
- onErrorMethod: 'onerror'
- }),
- slice = [].slice,
- concat = [].concat;
+ The provided block will be applied as the template for each item's view.
- // ..........................................................
- // run - this is ideally the only public API the dev sees
- //
+ Given an empty `` the following template:
- /**
- Runs the passed target and method inside of a RunLoop, ensuring any
- deferred actions including bindings and views updates are flushed at the
- end.
+ ```handlebars
+ {{! application.hbs }}
+ {{#collection content=model}}
+ Hi {{view.content.name}}
+ {{/collection}}
+ ```
- Normally you should not need to invoke this method yourself. However if
- you are implementing raw event handlers when interfacing with other
- libraries or plugins, you should probably wrap all of your code inside this
- call.
+ And the following application code
```javascript
- run(function() {
- // code to be execute within a RunLoop
+ App = Ember.Application.create();
+ App.ApplicationRoute = Ember.Route.extend({
+ model: function(){
+ return [{name: 'Yehuda'},{name: 'Tom'},{name: 'Peter'}];
+ }
});
```
- @class run
- @namespace Ember
- @static
- @constructor
- @param {Object} [target] target of method to call
- @param {Function|String} method Method to invoke.
- May be a function or a string. If you pass a string
- then it will be looked up on the passed target.
- @param {Object} [args*] Any additional arguments you wish to pass to the method.
- @return {Object} return value from invoking the passed function.
- */
- var run = function() {
- return apply(backburner, backburner.run, arguments);
- };
+ The following HTML will result:
- /**
- If no run-loop is present, it creates a new one. If a run loop is
- present it will queue itself to run on the existing run-loops action
- queue.
+ ```html
+
+
Hi Yehuda
+
Hi Tom
+
Hi Peter
+
+ ```
- Please note: This is not for normal usage, and should be used sparingly.
+ ### Non-block version of collection
- If invoked when not within a run loop:
+ If you provide an `itemViewClass` option that has its own `template` you may
+ omit the block.
- ```javascript
- run.join(function() {
- // creates a new run-loop
- });
+ The following template:
+
+ ```handlebars
+ {{! application.hbs }}
+ {{collection content=model itemViewClass="an-item"}}
```
- Alternatively, if called within an existing run loop:
+ And application code
```javascript
- run(function() {
- // creates a new run-loop
- run.join(function() {
- // joins with the existing run-loop, and queues for invocation on
- // the existing run-loops action queue.
- });
+ App = Ember.Application.create();
+ App.ApplicationRoute = Ember.Route.extend({
+ model: function(){
+ return [{name: 'Yehuda'},{name: 'Tom'},{name: 'Peter'}];
+ }
+ });
+
+ App.AnItemView = Ember.View.extend({
+ template: Ember.Handlebars.compile("Greetings {{view.content.name}}")
});
```
- @method join
- @namespace Ember
- @param {Object} [target] target of method to call
- @param {Function|String} method Method to invoke.
- May be a function or a string. If you pass a string
- then it will be looked up on the passed target.
- @param {Object} [args*] Any additional arguments you wish to pass to the method.
- @return {Object} Return value from invoking the passed function. Please note,
- when called within an existing loop, no return value is possible.
- */
- run.join = function(target, method /* args */) {
- if (!run.currentRunLoop) {
- return apply(Ember, run, arguments);
- }
+ Will result in the HTML structure below
- var args = slice.call(arguments);
- args.unshift('actions');
- apply(run, run.schedule, args);
- };
+ ```html
+
+
Greetings Yehuda
+
Greetings Tom
+
Greetings Peter
+
+ ```
- /**
- Provides a useful utility for when integrating with non-Ember libraries
- that provide asynchronous callbacks.
+ ### Specifying a CollectionView subclass
- Ember utilizes a run-loop to batch and coalesce changes. This works by
- marking the start and end of Ember-related Javascript execution.
+ By default the `{{collection}}` helper will create an instance of
+ `Ember.CollectionView`. You can supply a `Ember.CollectionView` subclass to
+ the helper by passing it as the first argument:
- 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.
+ ```handlebars
+ {{#collection "my-custom-collection" content=model}}
+ Hi {{view.content.name}}
+ {{/collection}}
+ ```
- For example, the following is rather verbose but is the correct way to combine
- third-party events and Ember code.
+ This example would look for the class `App.MyCustomCollection`.
- ```javascript
- var that = this;
- jQuery(window).on('resize', function(){
- run(function(){
- that.handleResize();
- });
- });
+ ### Forwarded `item.*`-named Options
+
+ As with the `{{view}}`, helper options passed to the `{{collection}}` will be
+ set on the resulting `Ember.CollectionView` as properties. Additionally,
+ options prefixed with `item` will be applied to the views rendered for each
+ item (note the camelcasing):
+
+ ```handlebars
+ {{#collection content=model
+ itemTagName="p"
+ itemClassNames="greeting"}}
+ Howdy {{view.content.name}}
+ {{/collection}}
```
- To reduce the boilerplate, the following can be used to construct a
- run-loop-wrapped callback handler.
+ Will result in the following HTML structure:
- ```javascript
- jQuery(window).on('resize', run.bind(this, this.handleResize));
+ ```html
+
+
Howdy Yehuda
+
Howdy Tom
+
Howdy Peter
+
```
- @method bind
- @namespace 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.
- @since 1.4.0
+ @method collection
+ @for Ember.Handlebars.helpers
+ @param {String} path
+ @param {Hash} options
+ @return {String} HTML string
+ @deprecated Use `{{each}}` helper instead.
*/
- run.bind = function(target, method /* args*/) {
- var args = slice.call(arguments);
- return function() {
- return apply(run, run.join, args.concat(slice.call(arguments)));
- };
- };
+ function collectionHelper(params, hash, options, env) {
+ var path = params[0];
- run.backburner = backburner;
- run.currentRunLoop = null;
- run.queues = backburner.queueNames;
+ Ember.deprecate("Using the {{collection}} helper without specifying a class has been" +
+ " deprecated as the {{each}} helper now supports the same functionality.", path !== 'collection');
- /**
- Begins a new RunLoop. Any deferred actions invoked after the begin will
- be buffered until you invoke a matching call to `run.end()`. This is
- a lower-level way to use a RunLoop instead of using `run()`.
+ Ember.assert("You cannot pass more than one argument to the collection helper", params.length <= 1);
- ```javascript
- run.begin();
- // code to be execute within a RunLoop
- run.end();
- ```
+ var data = env.data;
+ var template = options.template;
+ var inverse = options.inverse;
+ var view = data.view;
- @method begin
- @return {void}
- */
- run.begin = function() {
- backburner.begin();
- };
+ // This should be deterministic, and should probably come from a
+ // parent view and not the controller.
+ var controller = get(view, 'controller');
+ var container = (controller && controller.container ? controller.container : view.container);
- /**
- Ends a RunLoop. This must be called sometime after you call
- `run.begin()` to flush any deferred actions. This is a lower-level way
- to use a RunLoop instead of using `run()`.
+ // If passed a path string, convert that into an object.
+ // Otherwise, just default to the standard class.
+ var collectionClass;
+ if (path) {
+ collectionClass = readViewFactory(path, container);
+ Ember.assert(fmt("%@ #collection: Could not find collection class %@", [data.view, path]), !!collectionClass);
+ }
+ else {
+ collectionClass = CollectionView;
+ }
- ```javascript
- run.begin();
- // code to be execute within a RunLoop
- run.end();
- ```
+ var itemHash = {};
+ var match;
- @method end
- @return {void}
- */
- run.end = function() {
- backburner.end();
- };
+ // Extract item view class if provided else default to the standard class
+ var collectionPrototype = collectionClass.proto();
+ var itemViewClass;
- /**
- Array of named queues. This array determines the order in which queues
- are flushed at the end of the RunLoop. You can define your own queues by
- simply adding the queue name to this array. Normally you should not need
- to inspect or modify this property.
+ if (hash.itemView) {
+ itemViewClass = readViewFactory(hash.itemView, container);
+ } else if (hash.itemViewClass) {
+ itemViewClass = readViewFactory(hash.itemViewClass, container);
+ } else {
+ itemViewClass = collectionPrototype.itemViewClass;
+ }
- @property queues
- @type Array
- @default ['sync', 'actions', 'destroy']
- */
+ if (typeof itemViewClass === 'string') {
+ itemViewClass = container.lookupFactory('view:'+itemViewClass);
+ }
- /**
- Adds the passed target/method and any optional arguments to the named
- queue to be executed at the end of the RunLoop. If you have not already
- started a RunLoop when calling this method one will be started for you
- automatically.
+ Ember.assert(fmt("%@ #collection: Could not find itemViewClass %@", [data.view, itemViewClass]), !!itemViewClass);
- At the end of a RunLoop, any methods scheduled in this way will be invoked.
- Methods will be invoked in an order matching the named queues defined in
- the `run.queues` property.
+ delete hash.itemViewClass;
+ delete hash.itemView;
- ```javascript
- run.schedule('sync', this, function() {
- // this will be executed in the first RunLoop queue, when bindings are synced
- console.log("scheduled on sync queue");
- });
+ // Go through options passed to the {{collection}} helper and extract options
+ // that configure item views instead of the collection itself.
+ for (var prop in hash) {
+ if (prop === 'itemController' || prop === 'itemClassBinding') {
+ continue;
+ }
+ if (hash.hasOwnProperty(prop)) {
+ match = prop.match(/^item(.)(.*)$/);
+ if (match) {
+ var childProp = match[1].toLowerCase() + match[2];
- run.schedule('actions', this, function() {
- // this will be executed in the 'actions' queue, after bindings have synced.
- console.log("scheduled on actions queue");
- });
+ if (IS_BINDING.test(prop)) {
+ itemHash[childProp] = view._getBindingForStream(hash[prop]);
+ } else {
+ itemHash[childProp] = hash[prop];
+ }
+ delete hash[prop];
+ }
+ }
+ }
- // Note the functions will be run in order based on the run queues order.
- // Output would be:
- // scheduled on sync queue
- // scheduled on actions queue
- ```
+ if (template) {
+ itemHash.template = template;
+ delete options.template;
+ }
- @method schedule
- @param {String} queue The name of the queue to schedule against.
- Default queues are 'sync' and 'actions'
- @param {Object} [target] target object to use as the context when invoking a method.
- @param {String|Function} method The method to invoke. If you pass a string it
- will be resolved on the target object at the time the scheduled item is
- invoked allowing you to change the target function.
- @param {Object} [arguments*] Optional arguments to be passed to the queued method.
- @return {void}
- */
- run.schedule = function(queue, target, method) {
- checkAutoRun();
- apply(backburner, backburner.schedule, arguments);
- };
+ var emptyViewClass;
+ if (inverse) {
+ emptyViewClass = get(collectionPrototype, 'emptyViewClass');
+ emptyViewClass = emptyViewClass.extend({
+ template: inverse,
+ tagName: itemHash.tagName
+ });
+ } else if (hash.emptyViewClass) {
+ emptyViewClass = readViewFactory(hash.emptyViewClass, container);
+ }
+ if (emptyViewClass) { hash.emptyView = emptyViewClass; }
- // Used by global test teardown
- run.hasScheduledTimers = function() {
- return backburner.hasTimers();
- };
+ if (hash.keyword) {
+ itemHash._contextBinding = Binding.oneWay('_parentView.context');
+ } else {
+ itemHash._contextBinding = Binding.oneWay('content');
+ }
- // Used by global test teardown
- run.cancelTimers = function () {
- backburner.cancelTimers();
- };
+ var viewOptions = ViewHelper.propertiesFromHTMLOptions(itemHash, {}, { data: data });
- /**
- Immediately flushes any events scheduled in the 'sync' queue. Bindings
- use this queue so this method is a useful way to immediately force all
- bindings in the application to sync.
+ if (hash.itemClassBinding) {
+ var itemClassBindings = hash.itemClassBinding.split(' ');
+ viewOptions.classNameBindings = map(itemClassBindings, function(classBinding){
+ return streamifyClassNameBinding(view, classBinding);
+ });
+ }
- You should call this method anytime you need any changed state to propagate
- throughout the app immediately without repainting the UI (which happens
- in the later 'render' queue added by the `ember-views` package).
+ hash.itemViewClass = itemViewClass;
+ hash._itemViewProps = viewOptions;
- ```javascript
- run.sync();
- ```
+ options.helperName = options.helperName || 'collection';
- @method sync
- @return {void}
+ return env.helpers.view.helperFunction.call(this, [collectionClass], hash, options, env);
+ }
+
+ __exports__.collectionHelper = collectionHelper;
+ });
+enifed("ember-htmlbars/helpers/debugger",
+ ["ember-metal/logger","exports"],
+ function(__dependency1__, __exports__) {
+ "use strict";
+ /*jshint debug:true*/
+
+ /**
+ @module ember
+ @submodule ember-htmlbars
*/
- run.sync = function() {
- if (backburner.currentInstance) {
- backburner.currentInstance.queues.sync.flush();
- }
- };
+ var Logger = __dependency1__["default"];
/**
- Invokes the passed target/method and optional arguments after a specified
- period if time. The last parameter of this method must always be a number
- of milliseconds.
+ Execute the `debugger` statement in the current context.
- You should use this method whenever you need to run some action after a
- period of time instead of using `setTimeout()`. This method will ensure that
- items that expire during the same script execution cycle all execute
- together, which is often more efficient than using a real setTimeout.
+ ```handlebars
+ {{debugger}}
+ ```
- ```javascript
- run.later(myContext, function() {
- // code here will execute within a RunLoop in about 500ms with this == myContext
- }, 500);
+ Before invoking the `debugger` statement, there
+ are a few helpful variables defined in the
+ body of this helper that you can inspect while
+ debugging that describe how and where this
+ helper was invoked:
+
+ - templateContext: this is most likely a controller
+ from which this template looks up / displays properties
+ - typeOfTemplateContext: a string description of
+ what the templateContext is
+
+ For example, if you're wondering why a value `{{foo}}`
+ isn't rendering as expected within a template, you
+ could place a `{{debugger}}` statement, and when
+ the `debugger;` breakpoint is hit, you can inspect
+ `templateContext`, determine if it's the object you
+ expect, and/or evaluate expressions in the console
+ to perform property lookups on the `templateContext`:
+
+ ```
+ > templateContext.get('foo') // -> ""
```
- @method later
- @param {Object} [target] target of method to invoke
- @param {Function|String} method The method to invoke.
- If you pass a string it will be resolved on the
- target at the time the method is invoked.
- @param {Object} [args*] Optional arguments to pass to the timeout.
- @param {Number} wait Number of milliseconds to wait.
- @return {String} a string you can use to cancel the timer in
- `run.cancel` later.
+ @method debugger
+ @for Ember.Handlebars.helpers
+ @param {String} property
*/
- run.later = function(target, method) {
- return apply(backburner, backburner.later, arguments);
- };
+ function debuggerHelper() {
- /**
- Schedule a function to run one time during the current RunLoop. This is equivalent
- to calling `scheduleOnce` with the "actions" queue.
+ // These are helpful values you can inspect while debugging.
+ /* jshint unused: false */
+ var view = this;
+ Logger.info('Use `this` to access the view context.');
- @method once
- @param {Object} [target] The target of the method to invoke.
- @param {Function|String} method The method to invoke.
- If you pass a string it will be resolved on the
- target at the time the method is invoked.
- @param {Object} [args*] Optional arguments to pass to the timeout.
- @return {Object} Timer information for use in cancelling, see `run.cancel`.
+ debugger;
+ }
+
+ __exports__.debuggerHelper = debuggerHelper;
+ });
+enifed("ember-htmlbars/helpers/each",
+ ["ember-metal/core","ember-views/views/each","exports"],
+ function(__dependency1__, __dependency2__, __exports__) {
+ "use strict";
+
+ /**
+ @module ember
+ @submodule ember-htmlbars
*/
- run.once = function(target, method) {
- checkAutoRun();
- var args = slice.call(arguments);
- args.unshift('actions');
- return apply(backburner, backburner.scheduleOnce, args);
- };
+ var Ember = __dependency1__["default"];
+ // Ember.assert;
+ var EachView = __dependency2__["default"];
/**
- Schedules a function to run one time in a given queue of the current RunLoop.
- Calling this method with the same queue/target/method combination will have
- no effect (past the initial call).
+ The `{{#each}}` helper loops over elements in a collection. It is an extension
+ of the base Handlebars `{{#each}}` helper.
- Note that although you can pass optional arguments these will not be
- considered when looking for duplicates. New arguments will replace previous
- calls.
+ The default behavior of `{{#each}}` is to yield its inner block once for every
+ item in an array.
```javascript
- run(function() {
- var sayHi = function() { console.log('hi'); }
- run.scheduleOnce('afterRender', myContext, sayHi);
- run.scheduleOnce('afterRender', myContext, sayHi);
- // sayHi will only be executed once, in the afterRender queue of the RunLoop
- });
+ var developers = [{name: 'Yehuda'},{name: 'Tom'}, {name: 'Paul'}];
```
- Also note that passing an anonymous function to `run.scheduleOnce` will
- not prevent additional calls with an identical anonymous function from
- scheduling the items multiple times, e.g.:
+ ```handlebars
+ {{#each person in developers}}
+ {{person.name}}
+ {{! `this` is whatever it was outside the #each }}
+ {{/each}}
+ ```
+
+ The same rules apply to arrays of primitives, but the items may need to be
+ references with `{{this}}`.
```javascript
- function scheduleIt() {
- run.scheduleOnce('actions', myContext, function() { console.log("Closure"); });
- }
- scheduleIt();
- scheduleIt();
- // "Closure" will print twice, even though we're using `run.scheduleOnce`,
- // because the function we pass to it is anonymous and won't match the
- // previously scheduled operation.
+ var developerNames = ['Yehuda', 'Tom', 'Paul']
```
- Available queues, and their order, can be found at `run.queues`
+ ```handlebars
+ {{#each name in developerNames}}
+ {{name}}
+ {{/each}}
+ ```
- @method scheduleOnce
- @param {String} [queue] The name of the queue to schedule against. Default queues are 'sync' and 'actions'.
- @param {Object} [target] The target of the method to invoke.
- @param {Function|String} method The method to invoke.
- If you pass a string it will be resolved on the
- target at the time the method is invoked.
- @param {Object} [args*] Optional arguments to pass to the timeout.
- @return {Object} Timer information for use in cancelling, see `run.cancel`.
- */
- run.scheduleOnce = function(queue, target, method) {
- checkAutoRun();
- return apply(backburner, backburner.scheduleOnce, arguments);
- };
+ ### {{else}} condition
- /**
- Schedules an item to run from within a separate run loop, after
- control has been returned to the system. This is equivalent to calling
- `run.later` with a wait time of 1ms.
+ `{{#each}}` can have a matching `{{else}}`. The contents of this block will render
+ if the collection is empty.
+
+ ```
+ {{#each person in developers}}
+ {{person.name}}
+ {{else}}
+
Sorry, nobody is available for this task.
+ {{/each}}
+ ```
+
+ ### Specifying an alternative view for each item
+
+ `itemViewClass` can control which view will be used during the render of each
+ item's template.
+
+ The following template:
+
+ ```handlebars
+
+ ```
+
+ Will use the following view for each item
```javascript
- run.next(myContext, function() {
- // code to be executed in the next run loop,
- // which will be scheduled after the current one
+ App.PersonView = Ember.View.extend({
+ tagName: 'li'
});
```
- Multiple operations scheduled with `run.next` will coalesce
- into the same later run loop, along with any other operations
- scheduled by `run.later` that expire right around the same
- time that `run.next` operations will fire.
+ Resulting in HTML output that looks like the following:
- Note that there are often alternatives to using `run.next`.
- For instance, if you'd like to schedule an operation to happen
- after all DOM element operations have completed within the current
- run loop, you can make use of the `afterRender` run loop queue (added
- by the `ember-views` package, along with the preceding `render` queue
- where all the DOM element operations happen). Example:
+ ```html
+
+
Yehuda
+
Tom
+
Paul
+
+ ```
+
+ `itemViewClass` also enables a non-block form of `{{each}}`. The view
+ must {{#crossLink "Ember.View/toc_templates"}}provide its own template{{/crossLink}},
+ and then the block should be dropped. An example that outputs the same HTML
+ as the previous one:
```javascript
- App.MyCollectionView = Ember.CollectionView.extend({
- didInsertElement: function() {
- run.scheduleOnce('afterRender', this, 'processChildElements');
- },
- processChildElements: function() {
- // ... do something with collectionView's child view
- // elements after they've finished rendering, which
- // can't be done within the CollectionView's
- // `didInsertElement` hook because that gets run
- // before the child elements have been added to the DOM.
- }
+ App.PersonView = Ember.View.extend({
+ tagName: 'li',
+ template: '{{developer.name}}'
});
```
- One benefit of the above approach compared to using `run.next` is
- that you will be able to perform DOM/CSS operations before unprocessed
- elements are rendered to the screen, which may prevent flickering or
- other artifacts caused by delaying processing until after rendering.
-
- The other major benefit to the above approach is that `run.next`
- introduces an element of non-determinism, which can make things much
- harder to test, due to its reliance on `setTimeout`; it's much harder
- to guarantee the order of scheduled operations when they are scheduled
- outside of the current run loop, i.e. with `run.next`.
+ ```handlebars
+
+ {{each developer in developers itemViewClass="person"}}
+
+ ```
- @method next
- @param {Object} [target] target of method to invoke
- @param {Function|String} method The method to invoke.
- If you pass a string it will be resolved on the
- target at the time the method is invoked.
- @param {Object} [args*] Optional arguments to pass to the timeout.
- @return {Object} Timer information for use in cancelling, see `run.cancel`.
- */
- run.next = function() {
- var args = slice.call(arguments);
- args.push(1);
- return apply(backburner, backburner.later, args);
- };
+ ### Specifying an alternative view for no items (else)
- /**
- Cancels a scheduled item. Must be a value returned by `run.later()`,
- `run.once()`, `run.next()`, `run.debounce()`, or
- `run.throttle()`.
+ The `emptyViewClass` option provides the same flexibility to the `{{else}}`
+ case of the each helper.
```javascript
- var runNext = run.next(myContext, function() {
- // will not be executed
+ App.NoPeopleView = Ember.View.extend({
+ tagName: 'li',
+ template: 'No person is available, sorry'
});
- run.cancel(runNext);
+ ```
- var runLater = run.later(myContext, function() {
- // will not be executed
- }, 500);
- run.cancel(runLater);
+ ```handlebars
+
+ {{#each developer in developers emptyViewClass="no-people"}}
+
{{developer.name}}
+ {{/each}}
+
+ ```
- var runOnce = run.once(myContext, function() {
- // will not be executed
- });
- run.cancel(runOnce);
+ ### Wrapping each item in a controller
- var throttle = run.throttle(myContext, function() {
- // will not be executed
- }, 1, false);
- run.cancel(throttle);
+ Controllers in Ember manage state and decorate data. In many cases,
+ providing a controller for each item in a list can be useful.
+ Specifically, an {{#crossLink "Ember.ObjectController"}}Ember.ObjectController{{/crossLink}}
+ should probably be used. Item controllers are passed the item they
+ will present as a `model` property, and an object controller will
+ proxy property lookups to `model` for us.
- var debounce = run.debounce(myContext, function() {
- // will not be executed
- }, 1);
- run.cancel(debounce);
+ This allows state and decoration to be added to the controller
+ while any other property lookups are delegated to the model. An example:
- var debounceImmediate = run.debounce(myContext, function() {
- // will be executed since we passed in true (immediate)
- }, 100, true);
- // the 100ms delay until this method can be called again will be cancelled
- run.cancel(debounceImmediate);
+ ```javascript
+ App.RecruitController = Ember.ObjectController.extend({
+ isAvailableForHire: function() {
+ return !this.get('isEmployed') && this.get('isSeekingWork');
+ }.property('isEmployed', 'isSeekingWork')
+ })
```
- @method cancel
- @param {Object} timer Timer object to cancel
- @return {Boolean} true if cancelled or false/undefined if it wasn't found
- */
- run.cancel = function(timer) {
- return backburner.cancel(timer);
- };
-
- /**
- Delay calling the target method until the debounce period has elapsed
- with no additional debounce calls. If `debounce` is called again before
- the specified time has elapsed, the timer is reset and the entire period
- must pass again before the target method is called.
-
- This method should be used when an event may be called multiple times
- but the action should only be called once when the event is done firing.
- A common example is for scroll events where you only want updates to
- happen once scrolling has ceased.
-
- ```javascript
- var myFunc = function() { console.log(this.name + ' ran.'); };
- var myContext = {name: 'debounce'};
+ ```handlebars
+ {{#each person in developers itemController="recruit"}}
+ {{person.name}} {{#if person.isAvailableForHire}}Hire me!{{/if}}
+ {{/each}}
+ ```
- run.debounce(myContext, myFunc, 150);
+ @method each
+ @for Ember.Handlebars.helpers
+ @param [name] {String} name for item (used with `in`)
+ @param [path] {String} path
+ @param [options] {Object} Handlebars key/value pairs of options
+ @param [options.itemViewClass] {String} a path to a view class used for each item
+ @param [options.emptyViewClass] {String} a path to a view class used for each item
+ @param [options.itemController] {String} name of a controller to be created for each item
+ */
+ function eachHelper(params, hash, options, env) {
+ var helperName = 'each';
+ var path = params[0] || this.getStream('');
- // less than 150ms passes
+ Ember.assert(
+ "If you pass more than one argument to the each helper, " +
+ "it must be in the form #each foo in bar",
+ params.length <= 1
+ );
- run.debounce(myContext, myFunc, 150);
+ if (options.template && options.template.blockParams) {
+ hash.keyword = true;
+ }
- // 150ms passes
- // myFunc is invoked with context myContext
- // console logs 'debounce ran.' one time.
- ```
+ Ember.deprecate(
+ "Using the context switching form of {{each}} is deprecated. " +
+ "Please use the keyword form (`{{#each foo in bar}}`) instead. " +
+ "See http://emberjs.com/guides/deprecations/#toc_more-consistent-handlebars-scope " +
+ "for more details.",
+ hash.keyword === true || typeof hash.keyword === 'string'
+ );
- Immediate allows you to run the function immediately, but debounce
- other calls for this function until the wait time has elapsed. If
- `debounce` is called again before the specified time has elapsed,
- the timer is reset and the entire period must pass again before
- the method can be called again.
+ hash.emptyViewClass = Ember._MetamorphView;
+ hash.dataSource = path;
+ options.helperName = options.helperName || helperName;
- ```javascript
- var myFunc = function() { console.log(this.name + ' ran.'); };
- var myContext = {name: 'debounce'};
+ return env.helpers.collection.helperFunction.call(this, [EachView], hash, options, env);
+ }
- run.debounce(myContext, myFunc, 150, true);
+ __exports__.EachView = EachView;
+ __exports__.eachHelper = eachHelper;
+ });
+enifed("ember-htmlbars/helpers/if_unless",
+ ["ember-metal/core","ember-htmlbars/helpers/binding","ember-metal/property_get","ember-metal/utils","ember-views/streams/conditional_stream","ember-metal/streams/utils","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __exports__) {
+ "use strict";
+ /**
+ @module ember
+ @submodule ember-htmlbars
+ */
- // console logs 'debounce ran.' one time immediately.
- // 100ms passes
+ var Ember = __dependency1__["default"];
+ // Ember.assert
+ var bind = __dependency2__.bind;
- run.debounce(myContext, myFunc, 150, true);
+ var get = __dependency3__.get;
+ var isArray = __dependency4__.isArray;
+ var ConditionalStream = __dependency5__["default"];
+ var isStream = __dependency6__.isStream;
- // 150ms passes and nothing else is logged to the console and
- // the debouncee is no longer being watched
+ function shouldDisplayIfHelperContent(result) {
+ var truthy = result && get(result, 'isTruthy');
+ if (typeof truthy === 'boolean') { return truthy; }
- run.debounce(myContext, myFunc, 150, true);
+ if (isArray(result)) {
+ return get(result, 'length') !== 0;
+ } else {
+ return !!result;
+ }
+ }
- // console logs 'debounce ran.' one time immediately.
- // 150ms passes and nothing else is logged to the console and
- // the debouncee is no longer being watched
+ var EMPTY_TEMPLATE = {
+ isHTMLBars: true,
+ render: function() {
+ return '';
+ }
+ };
+ /**
+ Use the `boundIf` helper to create a conditional that re-evaluates
+ whenever the truthiness of the bound value changes.
+ ```handlebars
+ {{#boundIf "content.shouldDisplayTitle"}}
+ {{content.title}}
+ {{/boundIf}}
```
- @method debounce
- @param {Object} [target] target of method to invoke
- @param {Function|String} method The method to invoke.
- May be a function or a string. If you pass a string
- then it will be looked up on the passed target.
- @param {Object} [args*] Optional arguments to pass to the timeout.
- @param {Number} wait Number of milliseconds to wait.
- @param {Boolean} immediate Trigger the function on the leading instead
- of the trailing edge of the wait interval. Defaults to false.
- @return {Array} Timer information for use in cancelling, see `run.cancel`.
+ @private
+ @method boundIf
+ @for Ember.Handlebars.helpers
+ @param {String} property Property to bind
+ @param {Function} fn Context to provide for rendering
+ @return {String} HTML string
*/
- run.debounce = function() {
- return apply(backburner, backburner.debounce, arguments);
- };
+ function boundIfHelper(params, hash, options, env) {
+ options.helperName = options.helperName || 'boundIf';
+ return bind.call(this, params[0], hash, options, env, true, shouldDisplayIfHelperContent, shouldDisplayIfHelperContent, [
+ 'isTruthy',
+ 'length'
+ ]);
+ }
/**
- Ensure that the target method is never called more frequently than
- the specified spacing period. The target method is called immediately.
-
- ```javascript
- var myFunc = function() { console.log(this.name + ' ran.'); };
- var myContext = {name: 'throttle'};
-
- run.throttle(myContext, myFunc, 150);
- // myFunc is invoked with context myContext
- // console logs 'throttle ran.'
-
- // 50ms passes
- run.throttle(myContext, myFunc, 150);
+ @private
- // 50ms passes
- run.throttle(myContext, myFunc, 150);
+ Use the `unboundIf` helper to create a conditional that evaluates once.
- // 150ms passes
- run.throttle(myContext, myFunc, 150);
- // myFunc is invoked with context myContext
- // console logs 'throttle ran.'
+ ```handlebars
+ {{#unboundIf "content.shouldDisplayTitle"}}
+ {{content.title}}
+ {{/unboundIf}}
```
- @method throttle
- @param {Object} [target] target of method to invoke
- @param {Function|String} method The method to invoke.
- May be a function or a string. If you pass a string
- then it will be looked up on the passed target.
- @param {Object} [args*] Optional arguments to pass to the timeout.
- @param {Number} spacing Number of milliseconds to space out requests.
- @param {Boolean} immediate Trigger the function on the leading instead
- of the trailing edge of the wait interval. Defaults to true.
- @return {Array} Timer information for use in cancelling, see `run.cancel`.
+ @method unboundIf
+ @for Ember.Handlebars.helpers
+ @param {String} property Property to bind
+ @param {Function} fn Context to provide for rendering
+ @return {String} HTML string
+ @since 1.4.0
*/
- run.throttle = function() {
- return apply(backburner, backburner.throttle, arguments);
- };
+ function unboundIfHelper(params, hash, options, env) {
+ var template = options.template;
+ var value = params[0];
- // Make sure it's not an autorun during testing
- function checkAutoRun() {
- if (!run.currentRunLoop) {
- Ember.assert("You have turned on testing mode, which disabled the run-loop's autorun. You will need to wrap any code with asynchronous side-effects in an run", !Ember.testing);
+ if (isStream(params[0])) {
+ value = params[0].value();
+ }
+
+ if (!shouldDisplayIfHelperContent(value)) {
+ template = options.inverse;
}
+
+ return template.render(this, env, options.morph.contextualElement);
}
- /**
- Add a new named queue after the specified queue.
+ function _inlineIfAssertion(params) {
+ Ember.assert("If helper in inline form expects between two and three arguments", params.length === 2 || params.length === 3);
+ }
- The queue to add will only be added once.
+ /**
+ See [boundIf](/api/classes/Ember.Handlebars.helpers.html#method_boundIf)
+ and [unboundIf](/api/classes/Ember.Handlebars.helpers.html#method_unboundIf)
- @method _addQueue
- @param {String} name the name of the queue to add.
- @param {String} after the name of the queue to add after.
- @private
+ @method if
+ @for Ember.Handlebars.helpers
+ @param {Function} context
+ @param {Hash} options
+ @return {String} HTML string
*/
- run._addQueue = function(name, after) {
- if (indexOf.call(run.queues, name) === -1) {
- run.queues.splice(indexOf.call(run.queues, after)+1, 0, name);
+ function ifHelper(params, hash, options, env) {
+ Ember.assert("If helper in block form expect exactly one argument", !options.template || params.length === 1);
+
+ options.inverse = options.inverse || EMPTY_TEMPLATE;
+
+ options.helperName = options.helperName || ('if ');
+
+ if (env.data.isUnbound) {
+ env.data.isUnbound = false;
+ return env.helpers.unboundIf.helperFunction.call(this, params, hash, options, env);
+ } else {
+ return env.helpers.boundIf.helperFunction.call(this, params, hash, options, env);
}
}
- __exports__["default"] = run
- });
-define("ember-metal/set_properties",
- ["ember-metal/property_events","ember-metal/property_set","exports"],
- function(__dependency1__, __dependency2__, __exports__) {
- "use strict";
- var changeProperties = __dependency1__.changeProperties;
- var set = __dependency2__.set;
-
/**
- Set a list of properties on an object. These properties are set inside
- a single `beginPropertyChanges` and `endPropertyChanges` batch, so
- observers will be buffered.
+ @method unless
+ @for Ember.Handlebars.helpers
+ @param {Function} context
+ @param {Hash} options
+ @return {String} HTML string
+ */
+ function unlessHelper(params, hash, options, env) {
+ Ember.assert("You must pass exactly one argument to the unless helper", params.length === 1);
+ Ember.assert("You must pass a block to the unless helper", !!options.template);
- ```javascript
- var anObject = Ember.Object.create();
+ var template = options.template;
+ var inverse = options.inverse || EMPTY_TEMPLATE;
+ var helperName = 'unless';
- anObject.setProperties({
- firstName: 'Stanley',
- lastName: 'Stuart',
- age: 21
- });
- ```
+ options.template = inverse;
+ options.inverse = template;
- @method setProperties
- @param self
- @param {Object} hash
- @return self
- */
- function setProperties(self, hash) {
- changeProperties(function() {
- for(var prop in hash) {
- if (hash.hasOwnProperty(prop)) { set(self, prop, hash[prop]); }
- }
- });
- return self;
- };
+ options.helperName = options.helperName || helperName;
+
+ if (env.data.isUnbound) {
+ env.data.isUnbound = false;
+ return env.helpers.unboundIf.helperFunction.call(this, params, hash, options, env);
+ } else {
+ return env.helpers.boundIf.helperFunction.call(this, params, hash, options, env);
+ }
+ }
- __exports__["default"] = setProperties;
+ __exports__.ifHelper = ifHelper;
+ __exports__.boundIfHelper = boundIfHelper;
+ __exports__.unboundIfHelper = unboundIfHelper;
+ __exports__.unlessHelper = unlessHelper;
});
-define("ember-metal/utils",
- ["ember-metal/core","ember-metal/platform","ember-metal/array","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __exports__) {
+enifed("ember-htmlbars/helpers/input",
+ ["ember-views/views/checkbox","ember-views/views/text_field","ember-metal/streams/utils","ember-metal/core","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) {
"use strict";
- var Ember = __dependency1__["default"];
- var platform = __dependency2__.platform;
- var create = __dependency2__.create;
- var forEach = __dependency3__.forEach;
+ var Checkbox = __dependency1__["default"];
+ var TextField = __dependency2__["default"];
+ var read = __dependency3__.read;
+
+ var Ember = __dependency4__["default"];
+ // Ember.assert
/**
- @module ember-metal
+ @module ember
+ @submodule ember-htmlbars
*/
/**
- Prefix used for guids through out Ember.
- @private
- @property GUID_PREFIX
- @for Ember
- @type String
- @final
- */
- var GUID_PREFIX = 'ember';
+ The `{{input}}` helper inserts an HTML `` tag into the template,
+ with a `type` value of either `text` or `checkbox`. If no `type` is provided,
+ `text` will be the default value applied. The attributes of `{{input}}`
+ match those of the native HTML tag as closely as possible for these two types.
- var o_defineProperty = platform.defineProperty,
- o_create = create,
- // Used for guid generation...
- numberCache = [],
- stringCache = {},
- uuid = 0;
+ ## Use as text field
+ An `{{input}}` with no `type` or a `type` of `text` will render an HTML text input.
+ The following HTML attributes can be set via the helper:
- var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER;
+
+
`readonly`
`required`
`autofocus`
+
`value`
`placeholder`
`disabled`
+
`size`
`tabindex`
`maxlength`
+
`name`
`min`
`max`
+
`pattern`
`accept`
`autocomplete`
+
`autosave`
`formaction`
`formenctype`
+
`formmethod`
`formnovalidate`
`formtarget`
+
`height`
`inputmode`
`multiple`
+
`step`
`width`
`form`
+
`selectionDirection`
`spellcheck`
+
- /**
- A unique key used to assign guids and other private metadata to objects.
- If you inspect an object in your browser debugger you will often see these.
- They can be safely ignored.
- On browsers that support it, these properties are added with enumeration
- disabled so they won't show up when you iterate over your properties.
+ When set to a quoted string, these values will be directly applied to the HTML
+ element. When left unquoted, these values will be bound to a property on the
+ template's current rendering context (most typically a controller instance).
- @private
- @property GUID_KEY
- @for Ember
- @type String
- @final
- */
- var GUID_KEY = '__ember' + (+ new Date());
+ ## Unbound:
- var GUID_DESC = {
- writable: false,
- configurable: false,
- enumerable: false,
- value: null
- };
+ ```handlebars
+ {{input value="http://www.facebook.com"}}
+ ```
- /**
- Generates a new guid, optionally saving the guid to the object that you
- pass in. You will rarely need to use this method. Instead you should
- call `Ember.guidFor(obj)`, which return an existing guid if available.
- @private
- @method generateGuid
- @for Ember
- @param {Object} [obj] Object the guid will be used for. If passed in, the guid will
- be saved on the object and reused whenever you pass the same object
- again.
+ ```html
+
+ ```
- If no object is passed, just generate a new guid.
- @param {String} [prefix] Prefix to place in front of the guid. Useful when you want to
- separate the guid into separate namespaces.
- @return {String} the guid
- */
- function generateGuid(obj, prefix) {
- if (!prefix) prefix = GUID_PREFIX;
- var ret = (prefix + (uuid++));
- if (obj) {
- if (obj[GUID_KEY] === null) {
- obj[GUID_KEY] = ret;
- } else {
- GUID_DESC.value = ret;
- o_defineProperty(obj, GUID_KEY, GUID_DESC);
- }
- }
- return ret;
- }
+ ## Bound:
- /**
- Returns a unique id for the object. If the object does not yet have a guid,
- one will be assigned to it. You can call this on any object,
- `Ember.Object`-based or not, but be aware that it will add a `_guid`
- property.
+ ```javascript
+ App.ApplicationController = Ember.Controller.extend({
+ firstName: "Stanley",
+ entryNotAllowed: true
+ });
+ ```
- You can also use this method on DOM Element objects.
- @private
- @method guidFor
- @for Ember
- @param {Object} obj any object, string, number, Element, or primitive
- @return {String} the unique guid for this instance.
- */
- function guidFor(obj) {
+ ```handlebars
+ {{input type="text" value=firstName disabled=entryNotAllowed size="50"}}
+ ```
- // special cases where we don't want to add a key to object
- if (obj === undefined) return "(undefined)";
- if (obj === null) return "(null)";
- var ret;
- var type = typeof obj;
+ ```html
+
+ ```
- // Don't allow prototype changes to String etc. to change the guidFor
- switch(type) {
- case 'number':
- ret = numberCache[obj];
- if (!ret) ret = numberCache[obj] = 'nu'+obj;
- return ret;
+ ## Actions
- case 'string':
- ret = stringCache[obj];
- if (!ret) ret = stringCache[obj] = 'st'+(uuid++);
- return ret;
+ The helper can send multiple actions based on user events.
- case 'boolean':
- return obj ? '(true)' : '(false)';
+ The action property defines the action which is sent when
+ the user presses the return key.
- default:
- if (obj[GUID_KEY]) return obj[GUID_KEY];
- if (obj === Object) return '(Object)';
- if (obj === Array) return '(Array)';
- ret = 'ember' + (uuid++);
+ ```handlebars
+ {{input action="submit"}}
+ ```
- if (obj[GUID_KEY] === null) {
- obj[GUID_KEY] = ret;
- } else {
- GUID_DESC.value = ret;
- o_defineProperty(obj, GUID_KEY, GUID_DESC);
- }
- return ret;
- }
- };
+ The helper allows some user events to send actions.
- // ..........................................................
- // META
- //
+ * `enter`
+ * `insert-newline`
+ * `escape-press`
+ * `focus-in`
+ * `focus-out`
+ * `key-press`
- var META_DESC = {
- writable: true,
- configurable: false,
- enumerable: false,
- value: null
- };
+ For example, if you desire an action to be sent when the input is blurred,
+ you only need to setup the action name to the event name property.
- /**
- The key used to store meta information on object for property observing.
+ ```handlebars
+ {{input focus-in="alertMessage"}}
+ ```
- @property META_KEY
- @for Ember
- @private
- @final
- @type String
- */
- var META_KEY = '__ember_meta__';
+ See more about [Text Support Actions](/api/classes/Ember.TextField.html)
- var isDefinePropertySimulated = platform.defineProperty.isSimulated;
+ ## Extension
- function Meta(obj) {
- this.descs = {};
- this.watching = {};
- this.cache = {};
- this.cacheMeta = {};
- this.source = obj;
- }
+ Internally, `{{input type="text"}}` creates an instance of `Ember.TextField`, passing
+ arguments from the helper to `Ember.TextField`'s `create` method. You can extend the
+ capabilities of text inputs in your applications by reopening this class. For example,
+ if you are building a Bootstrap project where `data-*` attributes are used, you
+ can add one to the `TextField`'s `attributeBindings` property:
- Meta.prototype = {
- descs: null,
- deps: null,
- watching: null,
- listeners: null,
- cache: null,
- cacheMeta: null,
- source: null,
- mixins: null,
- bindings: null,
- chains: null,
- chainWatchers: null,
- values: null,
- proto: null
- };
- if (isDefinePropertySimulated) {
- // on platforms that don't support enumerable false
- // make meta fail jQuery.isPlainObject() to hide from
- // jQuery.extend() by having a property that fails
- // hasOwnProperty check.
- Meta.prototype.__preventPlainObject__ = true;
+ ```javascript
+ Ember.TextField.reopen({
+ attributeBindings: ['data-error']
+ });
+ ```
- // Without non-enumerable properties, meta objects will be output in JSON
- // unless explicitly suppressed
- Meta.prototype.toJSON = function () { };
- }
+ Keep in mind when writing `Ember.TextField` subclasses that `Ember.TextField`
+ itself extends `Ember.Component`, meaning that it does NOT inherit
+ the `controller` of the parent view.
- // Placeholder for non-writable metas.
- var EMPTY_META = new Meta(null);
+ See more about [Ember components](/api/classes/Ember.Component.html)
- if (MANDATORY_SETTER) { EMPTY_META.values = {}; }
- /**
- Retrieves the meta hash for an object. If `writable` is true ensures the
- hash is writable for this object as well.
+ ## Use as checkbox
- The meta object contains information about computed property descriptors as
- well as any watched properties and other information. You generally will
- not access this information directly but instead work with higher level
- methods that manipulate this hash indirectly.
+ An `{{input}}` with a `type` of `checkbox` will render an HTML checkbox input.
+ The following HTML attributes can be set via the helper:
- @method meta
- @for Ember
- @private
+ * `checked`
+ * `disabled`
+ * `tabindex`
+ * `indeterminate`
+ * `name`
+ * `autofocus`
+ * `form`
- @param {Object} obj The object to retrieve meta for
- @param {Boolean} [writable=true] Pass `false` if you do not intend to modify
- the meta hash, allowing the method to avoid making an unnecessary copy.
- @return {Object} the meta hash for an object
- */
- function meta(obj, writable) {
- var ret = obj[META_KEY];
- if (writable===false) return ret || EMPTY_META;
+ When set to a quoted string, these values will be directly applied to the HTML
+ element. When left unquoted, these values will be bound to a property on the
+ template's current rendering context (most typically a controller instance).
- if (!ret) {
- if (!isDefinePropertySimulated) o_defineProperty(obj, META_KEY, META_DESC);
+ ## Unbound:
- ret = new Meta(obj);
+ ```handlebars
+ {{input type="checkbox" name="isAdmin"}}
+ ```
- if (MANDATORY_SETTER) { ret.values = {}; }
+ ```html
+
+ ```
- obj[META_KEY] = ret;
+ ## Bound:
- // make sure we don't accidentally try to create constructor like desc
- ret.descs.constructor = null;
+ ```javascript
+ App.ApplicationController = Ember.Controller.extend({
+ isAdmin: true
+ });
+ ```
- } else if (ret.source !== obj) {
- if (!isDefinePropertySimulated) o_defineProperty(obj, META_KEY, META_DESC);
- ret = o_create(ret);
- ret.descs = o_create(ret.descs);
- ret.watching = o_create(ret.watching);
- ret.cache = {};
- ret.cacheMeta = {};
- ret.source = obj;
+ ```handlebars
+ {{input type="checkbox" checked=isAdmin }}
+ ```
- if (MANDATORY_SETTER) { ret.values = o_create(ret.values); }
- obj[META_KEY] = ret;
- }
- return ret;
- };
+ ```html
+
+ ```
- function getMeta(obj, property) {
- var _meta = meta(obj, false);
- return _meta[property];
- };
+ ## Extension
- function setMeta(obj, property, value) {
- var _meta = meta(obj, true);
- _meta[property] = value;
- return value;
- };
+ Internally, `{{input type="checkbox"}}` creates an instance of `Ember.Checkbox`, passing
+ arguments from the helper to `Ember.Checkbox`'s `create` method. You can extend the
+ capablilties of checkbox inputs in your applications by reopening this class. For example,
+ if you wanted to add a css class to all checkboxes in your application:
- /**
- @deprecated
- @private
- In order to store defaults for a class, a prototype may need to create
- a default meta object, which will be inherited by any objects instantiated
- from the class's constructor.
+ ```javascript
+ Ember.Checkbox.reopen({
+ classNames: ['my-app-checkbox']
+ });
+ ```
- However, the properties of that meta object are only shallow-cloned,
- so if a property is a hash (like the event system's `listeners` hash),
- it will by default be shared across all instances of that class.
- This method allows extensions to deeply clone a series of nested hashes or
- other complex objects. For instance, the event system might pass
- `['listeners', 'foo:change', 'ember157']` to `prepareMetaPath`, which will
- walk down the keys provided.
+ @method input
+ @for Ember.Handlebars.helpers
+ @param {Hash} options
+ */
+ function inputHelper(params, hash, options, env) {
+ Ember.assert('You can only pass attributes to the `input` helper, not arguments', params.length === 0);
- For each key, if the key does not exist, it is created. If it already
- exists and it was inherited from its constructor, the constructor's
- key is cloned.
+ var onEvent = hash.on;
+ var inputType;
- You can also pass false for `writable`, which will simply return
- undefined if `prepareMetaPath` discovers any part of the path that
- shared or undefined.
+ inputType = read(hash.type);
- @method metaPath
- @for Ember
- @param {Object} obj The object whose meta we are examining
- @param {Array} path An array of keys to walk down
- @param {Boolean} writable whether or not to create a new meta
- (or meta property) if one does not already exist or if it's
- shared with its constructor
- */
- function metaPath(obj, path, writable) {
- Ember.deprecate("Ember.metaPath is deprecated and will be removed from future releases.");
- var _meta = meta(obj, writable), keyName, value;
+ if (inputType === 'checkbox') {
+ delete hash.type;
- for (var i=0, l=path.length; i
+ {{loc '_welcome_'}}
+
+ ```
- ```javascript
- Ember.isArray(); // false
- Ember.isArray([]); // true
- Ember.isArray(Ember.ArrayProxy.create({ content: [] })); // true
+ ```html
+
+ Bonjour
+
```
- @method isArray
- @for Ember
- @param {Object} obj The object to test
- @return {Boolean} true if the passed object is an array or Array-like
- */
- // ES6TODO: Move up to runtime? This is only use in ember-metal by concatenatedProperties
- function isArray(obj) {
- var modulePath, type;
+ See [Ember.String.loc](/api/classes/Ember.String.html#method_loc) for how to
+ set up localized string references.
- if (typeof EmberArray === "undefined") {
- modulePath = 'ember-runtime/mixins/array';
- if (requirejs._eak_seen[modulePath]) {
- EmberArray = requireModule(modulePath)['default'];
+ @method loc
+ @for Ember.Handlebars.helpers
+ @param {String} str The string to format
+ @see {Ember.String#loc}
+ */
+ function locHelper(params, hash, options, env) {
+ Ember.assert('You cannot pass bindings to `loc` helper', (function ifParamsContainBindings() {
+ for (var i = 0, l = params.length; i < l; i++) {
+ if (isStream(params[i])) {
+ return false;
+ }
}
- }
-
- if (!obj || obj.setInterval) { return false; }
- if (Array.isArray && Array.isArray(obj)) { return true; }
- if (EmberArray && EmberArray.detect(obj)) { return true; }
+ return true;
+ })());
- type = typeOf(obj);
- if ('array' === type) { return true; }
- if ((obj.length !== undefined) && 'object' === type) { return true; }
- return false;
- };
+ return loc.apply(this, params);
+ }
+ __exports__.locHelper = locHelper;
+ });
+enifed("ember-htmlbars/helpers/log",
+ ["ember-metal/logger","ember-metal/streams/utils","exports"],
+ function(__dependency1__, __dependency2__, __exports__) {
+ "use strict";
/**
- Forces the passed object to be part of an array. If the object is already
- an array or array-like, returns the object. Otherwise adds the object to
- an array. If obj is `null` or `undefined`, returns an empty array.
-
- ```javascript
- Ember.makeArray(); // []
- Ember.makeArray(null); // []
- Ember.makeArray(undefined); // []
- Ember.makeArray('lindsay'); // ['lindsay']
- Ember.makeArray([1, 2, 42]); // [1, 2, 42]
+ @module ember
+ @submodule ember-htmlbars
+ */
+ var Logger = __dependency1__["default"];
+ var read = __dependency2__.read;
- var controller = Ember.ArrayProxy.create({ content: [] });
+ /**
+ `log` allows you to output the value of variables in the current rendering
+ context. `log` also accepts primitive types such as strings or numbers.
- Ember.makeArray(controller) === controller; // true
+ ```handlebars
+ {{log "myVariable:" myVariable }}
```
- @method makeArray
- @for Ember
- @param {Object} obj the object
- @return {Array}
+ @method log
+ @for Ember.Handlebars.helpers
+ @param {String} property
*/
- function makeArray(obj) {
- if (obj === null || obj === undefined) { return []; }
- return isArray(obj) ? obj : [obj];
- };
+ function logHelper(params, hash, options, env) {
+ var logger = Logger.log;
+ var values = [];
- /**
- Checks to see if the `methodName` exists on the `obj`.
+ for (var i = 0; i < params.length; i++) {
+ values.push(read(params[i]));
+ }
- ```javascript
- var foo = { bar: Ember.K, baz: null };
+ logger.apply(logger, values);
+ }
- Ember.canInvoke(foo, 'bar'); // true
- Ember.canInvoke(foo, 'baz'); // false
- Ember.canInvoke(foo, 'bat'); // false
- ```
+ __exports__.logHelper = logHelper;
+ });
+enifed("ember-htmlbars/helpers/partial",
+ ["ember-metal/core","ember-metal/is_none","./binding","ember-metal/streams/utils","exports"],
+ function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) {
+ "use strict";
+ var Ember = __dependency1__["default"];
+ // Ember.assert
- @method canInvoke
- @for Ember
- @param {Object} obj The object to check for the method
- @param {String} methodName The method name to check for
- @return {Boolean}
- */
- function canInvoke(obj, methodName) {
- return !!(obj && typeof obj[methodName] === 'function');
- }
+ var isNone = __dependency2__["default"];
+ var bind = __dependency3__.bind;
+ var isStream = __dependency4__.isStream;
/**
- Checks to see if the `methodName` exists on the `obj`,
- and if it does, invokes it with the arguments passed.
+ @module ember
+ @submodule ember-htmlbars
+ */
- ```javascript
- var d = new Date('03/15/2013');
+ /**
+ The `partial` helper renders another template without
+ changing the template context:
- Ember.tryInvoke(d, 'getTime'); // 1363320000000
- Ember.tryInvoke(d, 'setFullYear', [2014]); // 1394856000000
- Ember.tryInvoke(d, 'noSuchMethod', [2014]); // undefined
+ ```handlebars
+ {{foo}}
+ {{partial "nav"}}
```
- @method tryInvoke
- @for Ember
- @param {Object} obj The object to check for the method
- @param {String} methodName The method name to check for
- @param {Array} [args] The arguments to pass to the method
- @return {*} the return value of the invoked method or undefined if it cannot be invoked
- */
- function tryInvoke(obj, methodName, args) {
- if (canInvoke(obj, methodName)) {
- return args ? applyStr(obj, methodName, args) : applyStr(obj, methodName);
- }
- };
-
- // https://github.com/emberjs/ember.js/pull/1617
- var needsFinallyFix = (function() {
- var count = 0;
- try{
- try { }
- finally {
- count++;
- throw new Error('needsFinallyFixTest');
- }
- } catch (e) {}
-
- return count !== 1;
- })();
+ The above example template will render a template named
+ "_nav", which has the same context as the parent template
+ it's rendered into, so if the "_nav" template also referenced
+ `{{foo}}`, it would print the same thing as the `{{foo}}`
+ in the above example.
- /**
- Provides try/finally functionality, while working
- around Safari's double finally bug.
+ If a "_nav" template isn't found, the `partial` helper will
+ fall back to a template named "nav".
- ```javascript
- var tryable = function() {
- someResource.lock();
- runCallback(); // May throw error.
- };
+ ## Bound template names
- var finalizer = function() {
- someResource.unlock();
- };
+ The parameter supplied to `partial` can also be a path
+ to a property containing a template name, e.g.:
- Ember.tryFinally(tryable, finalizer);
+ ```handlebars
+ {{partial someTemplateName}}
```
- @method tryFinally
- @for Ember
- @param {Function} tryable The function to run the try callback
- @param {Function} finalizer The function to run the finally callback
- @param {Object} [binding] The optional calling object. Defaults to 'this'
- @return {*} The return value is the that of the finalizer,
- unless that value is undefined, in which case it is the return value
- of the tryable
- */
+ The above example will look up the value of `someTemplateName`
+ on the template context (e.g. a controller) and use that
+ value as the name of the template to render. If the resolved
+ value is falsy, nothing will be rendered. If `someTemplateName`
+ changes, the partial will be re-rendered using the new template
+ name.
- var tryFinally;
- if (needsFinallyFix) {
- tryFinally = function(tryable, finalizer, binding) {
- var result, finalResult, finalError;
- binding = binding || this;
+ @method partial
+ @for Ember.Handlebars.helpers
+ @param {String} partialName the name of the template to render minus the leading underscore
+ */
- try {
- result = tryable.call(binding);
- } finally {
- try {
- finalResult = finalizer.call(binding);
- } catch (e) {
- finalError = e;
- }
- }
+ function partialHelper(params, hash, options, env) {
+ options.helperName = options.helperName || 'partial';
- if (finalError) { throw finalError; }
+ var name = params[0];
- return (finalResult === undefined) ? result : finalResult;
- };
- } else {
- tryFinally = function(tryable, finalizer, binding) {
- var result, finalResult;
+ if (isStream(name)) {
+ options.template = createPartialTemplate(name);
+ bind.call(this, name, hash, options, env, true, exists);
+ } else {
+ return renderPartial(name, this, env, options.morph.contextualElement);
+ }
+ }
- binding = binding || this;
+ __exports__.partialHelper = partialHelper;function exists(value) {
+ return !isNone(value);
+ }
- try {
- result = tryable.call(binding);
- } finally {
- finalResult = finalizer.call(binding);
- }
+ function lookupPartial(view, templateName) {
+ var nameParts = templateName.split("/");
+ var lastPart = nameParts[nameParts.length - 1];
- return (finalResult === undefined) ? result : finalResult;
- };
- }
+ nameParts[nameParts.length - 1] = "_" + lastPart;
- /**
- Provides try/catch/finally functionality, while working
- around Safari's double finally bug.
+ var underscoredName = nameParts.join('/');
+ var template = view.templateForName(underscoredName);
+ if (!template) {
+ template = view.templateForName(templateName);
+ }
- ```javascript
- var tryable = function() {
- for (i = 0, l = listeners.length; i < l; i++) {
- listener = listeners[i];
- beforeValues[i] = listener.before(name, time(), payload);
- }
+ Ember.assert('Unable to find partial with name "'+templateName+'"', !!template);
- return callback.call(binding);
- };
+ return template;
+ }
- var catchable = function(e) {
- payload = payload || {};
- payload.exception = e;
- };
+ function renderPartial(name, view, env, contextualElement) {
+ var template = lookupPartial(view, name);
+ return template.render(view, env, contextualElement);
+ }
- var finalizer = function() {
- for (i = 0, l = listeners.length; i < l; i++) {
- listener = listeners[i];
- listener.after(name, time(), payload, beforeValues[i]);
+ function createPartialTemplate(nameStream) {
+ return {
+ isHTMLBars: true,
+ render: function(view, env, contextualElement) {
+ return renderPartial(nameStream.value(), view, env, contextualElement);
}
};
+ }
+ });
+enifed("ember-htmlbars/helpers/template",
+ ["ember-metal/core","exports"],
+ function(__dependency1__, __exports__) {
+ "use strict";
+ var Ember = __dependency1__["default"];
+ // Ember.deprecate;
- Ember.tryCatchFinally(tryable, catchable, finalizer);
- ```
-
- @method tryCatchFinally
- @for Ember
- @param {Function} tryable The function to run the try callback
- @param {Function} catchable The function to run the catchable callback
- @param {Function} finalizer The function to run the finally callback
- @param {Object} [binding] The optional calling object. Defaults to 'this'
- @return {*} The return value is the that of the finalizer,
- unless that value is undefined, in which case it is the return value
- of the tryable.
+ /**
+ @module ember
+ @submodule ember-htmlbars
*/
- var tryCatchFinally;
- if (needsFinallyFix) {
- tryCatchFinally = function(tryable, catchable, finalizer, binding) {
- var result, finalResult, finalError;
- binding = binding || this;
+ /**
+ @deprecated
+ @method template
+ @for Ember.Handlebars.helpers
+ @param {String} templateName the template to render
+ */
+ function templateHelper(params, hash, options, env) {
+ Ember.deprecate("The `template` helper has been deprecated in favor of the `partial` helper." +
+ " Please use `partial` instead, which will work the same way.");
- try {
- result = tryable.call(binding);
- } catch(error) {
- result = catchable.call(binding, error);
- } finally {
- try {
- finalResult = finalizer.call(binding);
- } catch (e) {
- finalError = e;
- }
- }
+ options.helperName = options.helperName || 'template';
- if (finalError) { throw finalError; }
+ return env.helpers.partial.helperFunction.call(this, params, hash, options, env);
+ }
- return (finalResult === undefined) ? result : finalResult;
- };
- } else {
- tryCatchFinally = function(tryable, catchable, finalizer, binding) {
- var result, finalResult;
+ __exports__.templateHelper = templateHelper;
+ });
+enifed("ember-htmlbars/helpers/text_area",
+ ["ember-metal/core","ember-views/views/text_area","exports"],
+ function(__dependency1__, __dependency2__, __exports__) {
+ "use strict";
+ /**
+ @module ember
+ @submodule ember-htmlbars
+ */
- binding = binding || this;
+ var Ember = __dependency1__["default"];
+ // Ember.assert
+ var TextArea = __dependency2__["default"];
- try {
- result = tryable.call(binding);
- } catch(error) {
- result = catchable.call(binding, error);
- } finally {
- finalResult = finalizer.call(binding);
- }
+ /**
+ `{{textarea}}` inserts a new instance of `