dist/ember.prod.js in ember-source-1.0.0.rc4.1 vs dist/ember.prod.js in ember-source-1.0.0.rc5

- old
+ new

@@ -60,11 +60,11 @@ The core Runtime framework is based on the jQuery API with a number of performance optimizations. @class Ember @static - @version 1.0.0-rc.4 + @version 1.0.0-rc.5 */ if ('undefined' === typeof Ember) { // Create core object. Make it act like an instance of Ember.Namespace so that // objects assigned to it are given a sane string representation. @@ -87,14 +87,14 @@ /** @property VERSION @type String - @default '1.0.0-rc.4' + @default '1.0.0-rc.5' @final */ -Ember.VERSION = '1.0.0-rc.4'; +Ember.VERSION = '1.0.0-rc.5'; /** Standard environmental variables. You can define these in a global `ENV` variable before loading Ember to control various configuration settings. @@ -4468,11 +4468,11 @@ if (typeof method === 'string') { method = target[method]; } - var stack = new Error().stack, + var stack = this.DEBUG ? new Error().stack : undefined, args = arguments.length > 3 ? slice.call(arguments, 3) : undefined; if (!this.currentInstance) { createAutorun(this); } return this.currentInstance.schedule(queueName, target, method, args, false, stack); }, @@ -4484,11 +4484,11 @@ if (typeof method === 'string') { method = target[method]; } - var stack = new Error().stack, + var stack = this.DEBUG ? new Error().stack : undefined, args = arguments.length > 3 ? slice.call(arguments, 3) : undefined; if (!this.currentInstance) { createAutorun(this); } return this.currentInstance.schedule(queueName, target, method, args, true, stack); }, @@ -4533,11 +4533,11 @@ if (laterTimer) { clearTimeout(laterTimer); laterTimer = null; } - laterTimer = setTimeout(function() { + laterTimer = window.setTimeout(function() { executeTimers(self); laterTimer = null; laterTimerExpiresAt = null; }, wait); laterTimerExpiresAt = executeAt; @@ -4554,11 +4554,11 @@ for (var i = 0, l = debouncees.length; i < l; i++) { debouncee = debouncees[i]; if (debouncee[0] === target && debouncee[1] === method) { return; } // do nothing } - var timer = setTimeout(function() { + var timer = window.setTimeout(function() { self.run.apply(self, args); // remove debouncee var index = -1; for (var i = 0, l = debouncees.length; i < l; i++) { @@ -4615,11 +4615,11 @@ Backburner.prototype.scheduleOnce = Backburner.prototype.deferOnce; Backburner.prototype.later = Backburner.prototype.setTimeout; function createAutorun(backburner) { backburner.begin(); - autorun = setTimeout(function() { + autorun = window.setTimeout(function() { backburner.end(); autorun = null; }); } @@ -4640,11 +4640,11 @@ self.schedule(self.options.defaultQueue, null, fns[i]); } }); if (timers.length) { - laterTimer = setTimeout(function() { + laterTimer = window.setTimeout(function() { executeTimers(self); laterTimer = null; laterTimerExpiresAt = null; }, timers[0] - now); laterTimerExpiresAt = timers[0]; @@ -6418,10 +6418,33 @@ return Ember.observer.apply(this, arguments); }; /** + When observers fire, they are called with the arguments `obj`, `keyName` + and `value`. In a typical observer, value is the new, post-change value. + + A `beforeObserver` fires before a property changes. The `value` argument contains + the pre-change value. + + A `beforeObserver` is an alternative form of `.observesBefore()`. + + ```javascript + App.PersonView = Ember.View.extend({ + valueWillChange: function (obj, keyName, value) { + this.changingFrom = value; + }.observesBefore('content.value'), + valueDidChange: function(obj, keyName, value) { + // only run if updating a value already in the DOM + if(this.get('state') === 'inDOM') { + var color = value > this.changingFrom ? 'green' : 'red'; + // logic + } + }.observes('content.value') + }); + ``` + @method beforeObserver @for Ember @param {Function} func @param {String} propertyNames* @return func @@ -13922,49 +13945,10 @@ toDOM: function() { return this.list.join(" "); } }; -var BAD_TAG_NAME_TEST_REGEXP = /[^a-zA-Z\-]/; -var BAD_TAG_NAME_REPLACE_REGEXP = /[^a-zA-Z\-]/g; - -function stripTagName(tagName) { - if (!tagName) { - return tagName; - } - - if (!BAD_TAG_NAME_TEST_REGEXP.test(tagName)) { - return tagName; - } - - return tagName.replace(BAD_TAG_NAME_REPLACE_REGEXP, ''); -} - -var BAD_CHARS_REGEXP = /&(?!\w+;)|[<>"'`]/g; -var POSSIBLE_CHARS_REGEXP = /[&<>"'`]/; - -function escapeAttribute(value) { - // Stolen shamelessly from Handlebars - - var escape = { - "<": "&lt;", - ">": "&gt;", - '"': "&quot;", - "'": "&#x27;", - "`": "&#x60;" - }; - - var escapeChar = function(chr) { - return escape[chr] || "&amp;"; - }; - - var string = value.toString(); - - if(!POSSIBLE_CHARS_REGEXP.test(string)) { return string; } - return string.replace(BAD_CHARS_REGEXP, escapeChar); -} - /** `Ember.RenderBuffer` gathers information regarding the a view and generates the final representation. `Ember.RenderBuffer` will generate HTML which can be pushed to the DOM. @@ -14248,27 +14232,27 @@ attrs = this.elementAttributes, props = this.elementProperties, style = this.elementStyle, attr, prop; - buffer += '<' + stripTagName(tagName); + buffer += '<' + tagName; if (id) { - buffer += ' id="' + escapeAttribute(id) + '"'; + buffer += ' id="' + this._escapeAttribute(id) + '"'; this.elementId = null; } if (classes) { - buffer += ' class="' + escapeAttribute(classes.join(' ')) + '"'; + buffer += ' class="' + this._escapeAttribute(classes.join(' ')) + '"'; this.classes = null; } if (style) { buffer += ' style="'; for (prop in style) { if (style.hasOwnProperty(prop)) { - buffer += prop + ':' + escapeAttribute(style[prop]) + ';'; + buffer += prop + ':' + this._escapeAttribute(style[prop]) + ';'; } } buffer += '"'; @@ -14276,11 +14260,11 @@ } if (attrs) { for (attr in attrs) { if (attrs.hasOwnProperty(attr)) { - buffer += ' ' + attr + '="' + escapeAttribute(attrs[attr]) + '"'; + buffer += ' ' + attr + '="' + this._escapeAttribute(attrs[attr]) + '"'; } } this.elementAttributes = null; } @@ -14291,11 +14275,11 @@ var value = props[prop]; if (value || typeof(value) === 'number') { if (value === true) { buffer += ' ' + prop + '="' + prop + '"'; } else { - buffer += ' ' + prop + '="' + escapeAttribute(props[prop]) + '"'; + buffer += ' ' + prop + '="' + this._escapeAttribute(props[prop]) + '"'; } } } } @@ -14306,11 +14290,11 @@ this.buffer = buffer; }, pushClosingTag: function() { var tagName = this.tagNames.pop(); - if (tagName) { this.buffer += '</' + stripTagName(tagName) + '>'; } + if (tagName) { this.buffer += '</' + tagName + '>'; } }, currentTagName: function() { return this.tagNames[this.tagNames.length-1]; }, @@ -14404,11 +14388,36 @@ } }, innerString: function() { return this.buffer; + }, + + _escapeAttribute: function(value) { + // Stolen shamelessly from Handlebars + + var escape = { + "<": "&lt;", + ">": "&gt;", + '"': "&quot;", + "'": "&#x27;", + "`": "&#x60;" + }; + + var badChars = /&(?!\w+;)|[<>"'`]/g; + var possible = /[&<>"'`]/; + + var escapeChar = function(chr) { + return escape[chr] || "&amp;"; + }; + + var string = value.toString(); + + if(!possible.test(string)) { return string; } + return string.replace(badChars, escapeChar); } + }; })(); @@ -23588,11 +23597,11 @@ Clears the current and target route handlers and triggers exit on each of them starting at the leaf and traversing up through its ancestors. */ reset: function() { - eachHandler(this.currentHandlerInfos, function(handler) { + eachHandler(this.currentHandlerInfos || [], function(handler) { if (handler.exit) { handler.exit(); } }); this.currentHandlerInfos = null; @@ -24729,10 +24738,13 @@ /** Transition into another route while replacing the current URL if possible. Identical to `transitionTo` in all other respects. + Of the bundled location types, only `history` currently supports + this behavior. + @method replaceWith @param {String} name the name of the route @param {...Object} models the */ replaceWith: function() { @@ -25169,10 +25181,12 @@ }); function parentRoute(route) { var handlerInfos = route.router.router.targetHandlerInfos; + if (!handlerInfos) { return; } + var parent, current; for (var i=0, l=handlerInfos.length; i<l; i++) { current = handlerInfos[i].handler; if (current === route) { return parent; } @@ -26079,11 +26093,11 @@ `{{outlet}}` helper, actions will bubble to the current controller, then to the current route, and then up the route hierarchy. Alternatively, a `target` option can be provided to the helper to change which object will receive the method call. This option must be a path - path to an object, accessible in the current context: + to an object, accessible in the current context: ```handlebars <script type="text/x-handlebars" data-template-name='a-template'> <div {{action anActionName target="MyApplication.someObject"}}> click me @@ -27896,11 +27910,11 @@ return resolver.resolve(fullName); }; } function normalize(fullName) { - var split = fullName.split(':'), + var split = fullName.split(':', 2), type = split[0], name = split[1]; if (type !== 'template') { @@ -29304,424 +29318,9 @@ @module ember @submodule ember-states @requires ember-runtime */ - -})(); - -(function() { -var slice = [].slice, - helpers = {}, - originalMethods = {}, - injectHelpersCallbacks = []; - -/** - @class Test - @namespace Ember -*/ -Ember.Test = { - - /** - @public - - `registerHelper` is used to register a - test helper that will be injected when - `App.injectTestHelpers` is called. - - The helper method will always be called - with the current Application as the first - parameter. - - For example: - ```javascript - Ember.Test.registerHelper('boot', function(app)) { - Ember.run(app, app.deferReadiness); - } - ``` - - This helper can later be called without arguments - because it will be called with `app` as the - first parameter. - - ```javascript - App = Ember.Application.create(); - App.injectTestHelpers(); - boot(); - ``` - - Whenever you register a helper that - performs async operations, - make sure you `return wait();` at the - end of the helper. - - If an async helper also needs to return a value, - pass it to the `wait` helper as a first argument: - `return wait(val);` - - @method registerHelper - @param name {String} - @param helperMethod {Function} - */ - registerHelper: function(name, helperMethod) { - helpers[name] = helperMethod; - }, - /** - @public - @method unregisterHelper - @param name {String} - */ - unregisterHelper: function(name) { - delete helpers[name]; - if (originalMethods[name]) { - window[name] = originalMethods[name]; - } - delete originalMethods[name]; - }, - - /** - @public - - Used to register callbacks to be fired - whenever `App.injectTestHelpers` is called - - The callback will receive the current application - as an argument. - - @method unregisterHelper - @param name {String} - */ - onInjectHelpers: function(callback) { - injectHelpersCallbacks.push(callback); - }, - - /** - @public - - This returns a thenable tailored - for testing. It catches failed - `onSuccess` callbacks and invokes - the `Ember.Test.adapter.exception` - callback in the last chained then. - - This method should be returned - by async helpers such as `wait`. - - @method promise - @param resolver {Function} - */ - promise: function(resolver) { - var promise = new Ember.RSVP.Promise(resolver); - var thenable = { - chained: false - }; - thenable.then = function(onSuccess, onFailure) { - var self = this, thenPromise, nextPromise; - thenable.chained = true; - thenPromise = promise.then(onSuccess, onFailure); - // this is to ensure all downstream fulfillment - // handlers are wrapped in the error handling - nextPromise = Ember.Test.promise(function(resolve) { - resolve(thenPromise); - }); - thenPromise.then(null, function(reason) { - // ensure this is the last promise in the chain - // if not, ignore and the exception will propagate - // this prevents the same error from being fired multiple times - if (!nextPromise.chained) { - Ember.Test.adapter.exception(reason); - } - }); - return nextPromise; - }; - return thenable; - }, - - /** - @public - - Used to allow ember-testing - to communicate with a specific - testing framework. - - You can manually set it before calling - `App.setupForTesting()`. - - Example: - 'Ember.Test.adapter = MyCustomAdapter.create()' - - If you do not set it, ember-testing - will default to `Ember.Test.QUnitAdapter`. - */ - adapter: null -}; - -function curry(app, fn) { - return function() { - var args = slice.call(arguments); - args.unshift(app); - return fn.apply(app, args); - }; -} - -Ember.Application.reopen({ - testHelpers: {}, - - setupForTesting: function() { - this.deferReadiness(); - - this.Router.reopen({ - location: 'none' - }); - - // if adapter is not manually set - // default to QUnit - if (!Ember.Test.adapter) { - Ember.Test.adapter = Ember.Test.QUnitAdapter.create(); - } - }, - - injectTestHelpers: function() { - this.testHelpers = {}; - for (var name in helpers) { - originalMethods[name] = window[name]; - this.testHelpers[name] = window[name] = curry(this, helpers[name]); - } - - for(var i = 0, l = injectHelpersCallbacks.length; i < l; i++) { - injectHelpersCallbacks[i](this); - } - }, - - removeTestHelpers: function() { - for (var name in helpers) { - window[name] = originalMethods[name]; - delete this.testHelpers[name]; - delete originalMethods[name]; - } - } -}); - -})(); - - - -(function() { -var Test = Ember.Test; - -/** - @class Adapter - @namespace Ember.Test -*/ -Test.Adapter = Ember.Object.extend({ - /** - @public - - This callback will be called - whenever an async operation - is about to start. - - Override this to call your - framework's methods - that handle async operations - - @method asyncStart - */ - asyncStart: Ember.K, - - /** - @public - - This callback will be called - whenever an async operation - has completed. - - @method asyncEnd - */ - asyncEnd: Ember.K, - - /** - @public - - Override this method with your - testing framework's false assertion - This function is called whenever - an exception occurs causing the testing - promise to fail. - - QUnit example: - - ```javascript - exception: function(error) { - ok(false, error); - } - ``` - - @method exception - @param reason {String} - */ - exception: function(error) { - setTimeout(function() { - throw error; - }); - } -}); - -/** - @class QUnitAdapter - @namespace Ember.Test -*/ -Test.QUnitAdapter = Test.Adapter.extend({ - asyncStart: function() { - stop(); - }, - asyncEnd: function() { - start(); - }, - exception: function(error) { - ok(false, error); - } -}); - -})(); - - - -(function() { -var get = Ember.get, - helper = Ember.Test.registerHelper, - pendingAjaxRequests = 0, - countAsync = 0; - - -Ember.Test.onInjectHelpers(function() { - Ember.$(document).ajaxStart(function() { - pendingAjaxRequests++; - }); - - Ember.$(document).ajaxStop(function() { - pendingAjaxRequests--; - }); -}); - - -function visit(app, url) { - Ember.run(app, app.handleURL, url); - app.__container__.lookup('router:main').location.setURL(url); - return wait(app); -} - -function click(app, selector, context) { - var $el = find(app, selector, context); - Ember.run(function() { - $el.click(); - }); - return wait(app); -} - -function fillIn(app, selector, context, text) { - var $el; - if (typeof text === 'undefined') { - text = context; - context = null; - } - $el = find(app, selector, context); - Ember.run(function() { - $el.val(text).change(); - }); - return wait(app); -} - -function find(app, selector, context) { - var $el; - context = context || get(app, 'rootElement'); - $el = app.$(selector, context); - if ($el.length === 0) { - throw("Element " + selector + " not found."); - } - return $el; -} - -function wait(app, value) { - var promise, obj = {}, helperName; - - promise = Ember.Test.promise(function(resolve) { - if (++countAsync === 1) { - Ember.Test.adapter.asyncStart(); - } - var watcher = setInterval(function() { - var routerIsLoading = app.__container__.lookup('router:main').router.isLoading; - if (routerIsLoading) { return; } - if (pendingAjaxRequests) { return; } - if (Ember.run.hasScheduledTimers() || Ember.run.currentRunLoop) { return; } - clearInterval(watcher); - if (--countAsync === 0) { - Ember.Test.adapter.asyncEnd(); - } - Ember.run(function() { - resolve(value); - }); - }, 10); - }); - - return buildChainObject(app, promise); -} - -/** - Builds an object that contains - all helper methods. This object will be - returned by helpers and then-promises. - - This allows us to chain helpers: - - ```javascript - visit('posts/new') - .click('.add-btn') - .fillIn('.title', 'Post') - .click('.submit') - .then(function() { - equal('.post-title', 'Post'); - }) - .visit('comments') - .then(function() { - equal(find('.comments'),length, 0); - }); - ``` -*/ -function buildChainObject(app, promise) { - var helperName, obj = {}; - for(helperName in app.testHelpers) { - obj[helperName] = chain(app, promise, app.testHelpers[helperName]); - } - obj.then = function(fn) { - var thenPromise = promise.then(fn); - return buildChainObject(app, thenPromise); - }; - return obj; -} - -function chain(app, promise, fn) { - return function() { - var args = arguments, chainedPromise; - chainedPromise = promise.then(function() { - return fn.apply(null, args); - }); - return buildChainObject(app, chainedPromise); - }; -} - -// expose these methods as test helpers -helper('visit', visit); -helper('click', click); -helper('fillIn', fillIn); -helper('find', find); -helper('wait', wait); - -})(); - - - -(function() { })(); })();