From 22844b9e4663a1dd06040cd5913f02f571f9672f Mon Sep 17 00:00:00 2001 From: Robin Ward Date: Tue, 11 Aug 2015 17:34:02 -0400 Subject: [PATCH] Ember 1.12 support --- .jshintignore => .eslintignore | 2 +- .eslintrc | 105 + .jshintrc | 83 - Gemfile.lock | 8 +- .../admin/components/site-setting.js.es6 | 24 +- app/assets/javascripts/discourse.js | 51 +- .../desktop-notification-config.js.es6 | 32 +- .../discourse/controllers/quote-button.js.es6 | 2 +- .../discourse/controllers/search.js.es6 | 15 +- .../discourse/controllers/topic.js.es6 | 65 +- .../apply-flagged-properties.js.es6 | 1 - .../discourse/initializers/csrf-token.js.es6 | 1 - .../initializers/enable-emoji.js.es6 | 1 - .../initializers/inject-objects.js.es6 | 48 +- .../initializers/page-tracking.js.es6 | 1 - .../register-discourse-location.js.es6 | 8 +- .../dynamic-route-builders.js.es6 | 3 +- .../inject-discourse-objects.js.es6 | 53 + .../map-routes.js.es6 | 2 +- .../register-dom-templates.js.es6 | 0 .../sniff-capabilities.js.es6 | 0 .../discourse/views/topic-unsubscribe.js.es6 | 2 +- .../ember-addons/decorator-alias.js.es6 | 22 + .../ember-computed-decorators.js.es6 | 63 + .../ember-addons/macro-alias.js.es6 | 24 + .../ember-addons/utils/extract-value.js.es6 | 4 + .../utils/handle-descriptor.js.es6 | 67 + .../ember-addons/utils/is-descriptor.js.es6 | 7 + app/assets/javascripts/ember_include.js.erb | 2 +- app/assets/javascripts/main_include.js | 5 + .../common/_discourse_javascript.html.erb | 4 +- lib/discourse_iife.rb | 2 +- .../tilt/es6_module_transpiler_template.rb | 6 +- test/javascripts/jshint-test.js.es6.erb | 107 - test/javascripts/lib/click-track-test.js.es6 | 2 +- test/javascripts/test_helper.js | 4 +- .../javascripts/ember-template-compiler.js | 9924 +++- .../assets/javascripts/ember.custom.debug.js | 49400 ---------------- vendor/assets/javascripts/jshint.js | 12047 ---- 39 files changed, 10322 insertions(+), 61875 deletions(-) rename .jshintignore => .eslintignore (96%) create mode 100644 .eslintrc delete mode 100644 .jshintrc rename app/assets/javascripts/discourse/{initializers => pre-initializers}/dynamic-route-builders.js.es6 (95%) create mode 100644 app/assets/javascripts/discourse/pre-initializers/inject-discourse-objects.js.es6 rename app/assets/javascripts/discourse/{initializers => pre-initializers}/map-routes.js.es6 (93%) rename app/assets/javascripts/discourse/{initializers => pre-initializers}/register-dom-templates.js.es6 (100%) rename app/assets/javascripts/discourse/{initializers => pre-initializers}/sniff-capabilities.js.es6 (100%) create mode 100644 app/assets/javascripts/ember-addons/decorator-alias.js.es6 create mode 100644 app/assets/javascripts/ember-addons/ember-computed-decorators.js.es6 create mode 100644 app/assets/javascripts/ember-addons/macro-alias.js.es6 create mode 100644 app/assets/javascripts/ember-addons/utils/extract-value.js.es6 create mode 100644 app/assets/javascripts/ember-addons/utils/handle-descriptor.js.es6 create mode 100644 app/assets/javascripts/ember-addons/utils/is-descriptor.js.es6 delete mode 100644 test/javascripts/jshint-test.js.es6.erb delete mode 100644 vendor/assets/javascripts/ember.custom.debug.js delete mode 100644 vendor/assets/javascripts/jshint.js diff --git a/.jshintignore b/.eslintignore similarity index 96% rename from .jshintignore rename to .eslintignore index ac11baa97e8..1ecda00738c 100644 --- a/.jshintignore +++ b/.eslintignore @@ -20,5 +20,5 @@ vendor/ test/javascripts/helpers/ test/javascripts/test_helper.js test/javascripts/test_helper.js +test/javascripts/fixtures app/assets/javascripts/ember-addons/ - diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 00000000000..149d8424889 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,105 @@ +{ + "env": { + "jasmine": true, + "node": true, + "mocha": true, + "browser": true, + "builtin": true + }, + ecmaVersion: 7, + "globals": + {"Ember":true, + "jQuery":true, + "$":true, + "RSVP":true, + "Discourse":true, + "Em":true, + "PreloadStore":true, + "Handlebars":true, + "I18n":true, + "bootbox":true, + "module":true, + "moduleFor":true, + "moduleForComponent":true, + "Pretender":true, + "sandbox":true, + "controllerFor":true, + "test":true, + "ok":true, + "not":true, + "expect":true, + "equal":true, + "visit":true, + "andThen":true, + "click":true, + "currentPath":true, + "currentRouteName":true, + "currentURL":true, + "fillIn":true, + "keyEvent":true, + "triggerEvent":true, + "count":true, + "exists":true, + "visible":true, + "invisible":true, + "asyncRender":true, + "selectDropdown":true, + "asyncTestDiscourse":true, + "fixture":true, + "find":true, + "sinon":true, + "moment":true, + "start":true, + "_":true, + "alert":true, + "containsInstance":true, + "deepEqual":true, + "notEqual":true, + "define":true, + "require":true, + "requirejs":true, + "hasModule":true, + "Blob":true, + "File":true}, + "rules": { + "block-scoped-var": 2, + "dot-notation": 0, + "eqeqeq": [ + 2, + "allow-null" + ], + "guard-for-in": 2, + "no-bitwise": 2, + "no-caller": 2, + "no-cond-assign": 0, + "no-debugger": 2, + "no-empty": 0, + "no-eval": 2, + "no-extend-native": 2, + "no-extra-parens": 0, + "no-irregular-whitespace": 2, + "no-iterator": 2, + "no-loop-func": 2, + "no-multi-str": 2, + "no-new": 2, + "no-plusplus": 0, + "no-proto": 2, + "no-script-url": 2, + "no-sequences": 2, + "no-shadow": 2, + "no-undef": 2, + "no-unused-vars": 2, + "no-with": 2, + "semi": [ + 0, + "never" + ], + "strict": 0, + "valid-typeof": 2, + "wrap-iife": [ + 2, + "inside" + ] + }, + "parser": "babel-eslint" +} diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index cbe6bffe52a..00000000000 --- a/.jshintrc +++ /dev/null @@ -1,83 +0,0 @@ -{ - "predef":["Ember", - "jQuery", - "$", - "RSVP", - "Discourse", - "Em", - "PreloadStore", - "Handlebars", - "I18n", - "bootbox", - "module", - "moduleFor", - "moduleForComponent", - "Pretender", - "sandbox", - "controllerFor", - "test", - "ok", - "not", - "expect", - "equal", - "visit", - "andThen", - "click", - "currentPath", - "currentRouteName", - "currentURL", - "fillIn", - "keyEvent", - "triggerEvent", - "count", - "exists", - "visible", - "invisible", - "asyncRender", - "selectDropdown", - "asyncTestDiscourse", - "fixture", - "find", - "sinon", - "moment", - "start", - "_", - "alert", - "containsInstance", - "deepEqual", - "notEqual", - "define", - "require", - "requirejs", - "hasModule", - "Blob", - "File"], - "node" : false, - "browser" : true, - "boss" : true, - "curly": false, - "debug": false, - "devel": false, - "eqeqeq": true, - "evil": true, - "forin": false, - "immed": false, - "laxbreak": false, - "newcap": true, - "noarg": true, - "noempty": false, - "nonew": false, - "nomen": false, - "onevar": false, - "plusplus": false, - "regexp": false, - "undef": true, - "unused": true, - "sub": true, - "strict": false, - "white": false, - "eqnull": true, - "quotmark": false, - "lastsemic": true, - "esnext": true -} diff --git a/Gemfile.lock b/Gemfile.lock index d10b146ad8b..c369c55fe83 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -46,9 +46,9 @@ GEM multi_json (~> 1.0) aws-sdk-resources (2.0.45) aws-sdk-core (= 2.0.45) - babel-source (4.6.6) - babel-transpiler (0.6.0) - babel-source (>= 4.0, < 5) + babel-source (5.8.19) + babel-transpiler (0.7.0) + babel-source (>= 4.0, < 6) execjs (~> 2.0) barber (0.9.0) ember-source (>= 1.0, < 2) @@ -86,7 +86,7 @@ GEM ember-source (>= 1.1.0) jquery-rails (>= 1.0.17) railties (>= 3.1) - ember-source (1.11.3.1) + ember-source (1.12.1) erubis (2.7.0) eventmachine (1.0.7) excon (0.45.3) diff --git a/app/assets/javascripts/admin/components/site-setting.js.es6 b/app/assets/javascripts/admin/components/site-setting.js.es6 index 10925e8a2e3..15058e87160 100644 --- a/app/assets/javascripts/admin/components/site-setting.js.es6 +++ b/app/assets/javascripts/admin/components/site-setting.js.es6 @@ -2,6 +2,7 @@ import BufferedContent from 'discourse/mixins/buffered-content'; import ScrollTop from 'discourse/mixins/scroll-top'; import SiteSetting from 'admin/models/site-setting'; import { propertyNotEqual } from 'discourse/lib/computed'; +import computed from 'ember-addons/ember-computed-decorators'; const CustomTypes = ['bool', 'enum', 'list', 'url_list', 'host_list']; @@ -20,19 +21,22 @@ export default Ember.Component.extend(BufferedContent, ScrollTop, { } }.property('buffered.value'), - typeClass: function() { + @computed('partialType') + typeClass() { return this.get('partialType').replace("_", "-"); - }.property('partialType'), + }, - enabled: function(key, value) { - if (arguments.length > 1) { + @computed('buffered.value') + enabled: { + get() { + const bufferedValue = this.get('buffered.value'); + if (Ember.isEmpty(bufferedValue)) { return false; } + return bufferedValue === 'true'; + }, + set(key, value) { this.set('buffered.value', value ? 'true' : 'false'); } - - const bufferedValue = this.get('buffered.value'); - if (Ember.isEmpty(bufferedValue)) { return false; } - return bufferedValue === 'true'; - }.property('buffered.value'), + }, settingName: function() { return this.get('setting.setting').replace(/\_/g, ' '); @@ -40,7 +44,7 @@ export default Ember.Component.extend(BufferedContent, ScrollTop, { partialType: function() { let type = this.get('setting.type'); - return (CustomTypes.indexOf(type) !== -1) ? type : 'string'; + return CustomTypes.indexOf(type) !== -1 ? type : 'string'; }.property('setting.type'), partialName: function() { diff --git a/app/assets/javascripts/discourse.js b/app/assets/javascripts/discourse.js index 3d548b5fa16..7a26b76e7e2 100644 --- a/app/assets/javascripts/discourse.js +++ b/app/assets/javascripts/discourse.js @@ -3,7 +3,7 @@ var DiscourseResolver = require('discourse/ember/resolver').default; // Allow us to import Ember define('ember', ['exports'], function(__exports__) { - __exports__["default"] = Ember; + __exports__.default = Ember; }); window.Discourse = Ember.Application.createWithMixins(Discourse.Ajax, { @@ -16,7 +16,7 @@ window.Discourse = Ember.Application.createWithMixins(Discourse.Ajax, { // if it's a non relative URL, return it. if (!/^\/[^\/]/.test(url)) return url; - var u = (Discourse.BaseUri === undefined ? "/" : Discourse.BaseUri); + var u = Discourse.BaseUri === undefined ? "/" : Discourse.BaseUri; if (u[u.length-1] === '/') u = u.substring(0, u.length-1); if (url.indexOf(u) !== -1) return url; @@ -66,7 +66,7 @@ window.Discourse = Ember.Application.createWithMixins(Discourse.Ajax, { // The classes of buttons to show on a post postButtons: function() { return Discourse.SiteSettings.post_menu.split("|").map(function(i) { - return (i.replace(/\+/, '').capitalize()); + return i.replace(/\+/, '').capitalize(); }); }.property(), @@ -109,12 +109,26 @@ window.Discourse = Ember.Application.createWithMixins(Discourse.Ajax, { $('noscript').remove(); - // Load any ES6 initializers + Ember.keys(requirejs._eak_seen).forEach(function(key) { + if (/\/pre\-initializers\//.test(key)) { + var module = require(key, null, null, true); + if (!module) { throw new Error(key + ' must export an initializer.'); } + Discourse.initializer(module.default); + } + }); + Ember.keys(requirejs._eak_seen).forEach(function(key) { if (/\/initializers\//.test(key)) { var module = require(key, null, null, true); if (!module) { throw new Error(key + ' must export an initializer.'); } - Discourse.initializer(module.default); + + var init = module.default; + var oldInitialize = init.initialize; + init.initialize = function(app) { + oldInitialize.call(this, app.container, app); + }; + + Discourse.instanceInitializer(init); } }); @@ -125,17 +139,22 @@ window.Discourse = Ember.Application.createWithMixins(Discourse.Ajax, { return desired && Discourse.get("currentAssetVersion") !== desired; }.property("currentAssetVersion", "desiredAssetVersion"), - assetVersion: function(prop, val) { - if(val) { - if(this.get("currentAssetVersion")){ - this.set("desiredAssetVersion", val); - } else { - this.set("currentAssetVersion", val); - } - } - return this.get("currentAssetVersion"); - }.property() + assetVersion: Ember.computed({ + get: function() { + return this.get("currentAssetVersion"); + }, + set: function(key, val) { + if(val) { + if (this.get("currentAssetVersion")) { + this.set("desiredAssetVersion", val); + } else { + this.set("currentAssetVersion", val); + } + } + return this.get("currentAssetVersion"); + } + }) }); // TODO: Remove this, it is in for backwards compatibiltiy with plugins @@ -159,5 +178,3 @@ proxyDep('URL', function() { return require('discourse/lib/url').default }); proxyDep('Quote', function() { return require('discourse/lib/quote').default }); proxyDep('debounce', function() { return require('discourse/lib/debounce').default }); proxyDep('View', function() { return Ember.View }, "Use `Ember.View` instead"); -proxyDep('Controller', function() { return Ember.Controller }, "Use `Ember.Controller` instead"); -proxyDep('ObjectController', function() { return Ember.ObjectController }, "Use `Ember.Controller` instead"); diff --git a/app/assets/javascripts/discourse/components/desktop-notification-config.js.es6 b/app/assets/javascripts/discourse/components/desktop-notification-config.js.es6 index 5a362280c38..1d5f4bd77bd 100644 --- a/app/assets/javascripts/discourse/components/desktop-notification-config.js.es6 +++ b/app/assets/javascripts/discourse/components/desktop-notification-config.js.es6 @@ -1,23 +1,31 @@ +import computed from 'ember-addons/ember-computed-decorators'; + export default Ember.Component.extend({ classNames: ['controls'], - notificationsPermission: function() { + @computed + notificationsPermission() { if (this.get('isNotSupported')) return ''; - return Notification.permission; - }.property(), + }, - notificationsDisabled: function(_, value) { - if (arguments.length > 1) { - localStorage.setItem('notifications-disabled', value); + @computed + notificationsDisabled: { + set(key, value) { + if (arguments.length > 1) { + localStorage.setItem('notifications-disabled', value); + } + return localStorage.getItem('notifications-disabled'); + }, + get() { + return localStorage.getItem('notifications-disabled'); } - return localStorage.getItem('notifications-disabled'); - }.property(), + }, - - isNotSupported: function() { - return !window['Notification']; - }.property(), + @computed + isNotSupported() { + return typeof window.Notification === "undefined"; + }, isDefaultPermission: function() { if (this.get('isNotSupported')) return false; diff --git a/app/assets/javascripts/discourse/controllers/quote-button.js.es6 b/app/assets/javascripts/discourse/controllers/quote-button.js.es6 index cfdd30b5205..372728decb6 100644 --- a/app/assets/javascripts/discourse/controllers/quote-button.js.es6 +++ b/app/assets/javascripts/discourse/controllers/quote-button.js.es6 @@ -50,7 +50,7 @@ export default Ember.Controller.extend({ // create a marker element const markerElement = document.createElement("span"); // containing a single invisible character - markerElement.appendChild(document.createTextNode("\u{feff}")); + markerElement.appendChild(document.createTextNode("\ufeff")); // collapse the range at the beginning/end of the selection range.collapse(!Discourse.Mobile.isMobileDevice); diff --git a/app/assets/javascripts/discourse/controllers/search.js.es6 b/app/assets/javascripts/discourse/controllers/search.js.es6 index eff99ec98e1..1d9cb22ad27 100644 --- a/app/assets/javascripts/discourse/controllers/search.js.es6 +++ b/app/assets/javascripts/discourse/controllers/search.js.es6 @@ -1,20 +1,27 @@ import searchForTerm from 'discourse/lib/search-for-term'; import DiscourseURL from 'discourse/lib/url'; +import computed from 'ember-addons/ember-computed-decorators'; let _dontSearch = false; export default Em.Controller.extend({ typeFilter: null, - contextType: function(key, value){ - if(arguments.length > 1) { + @computed('searchContext') + contextType: { + get(searchContext) { + if (searchContext) { + return Ember.get(searchContext, 'type'); + } + }, + set(key, value) { // a bit hacky, consider cleaning this up, need to work through all observers though const context = $.extend({}, this.get('searchContext')); context.type = value; this.set('searchContext', context); + return this.get('searchContext.type'); } - return this.get('searchContext.type'); - }.property('searchContext'), + }, contextChanged: function(){ if (this.get('searchContextEnabled')) { diff --git a/app/assets/javascripts/discourse/controllers/topic.js.es6 b/app/assets/javascripts/discourse/controllers/topic.js.es6 index 736ad9415bb..c112f3458c5 100644 --- a/app/assets/javascripts/discourse/controllers/topic.js.es6 +++ b/app/assets/javascripts/discourse/controllers/topic.js.es6 @@ -4,6 +4,7 @@ import { spinnerHTML } from 'discourse/helpers/loading-spinner'; import Topic from 'discourse/models/topic'; import Quote from 'discourse/lib/quote'; import { setting } from 'discourse/lib/computed'; +import computed from 'ember-addons/ember-computed-decorators'; export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, { multiSelect: false, @@ -65,35 +66,53 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, { }.observes('model.postStream', 'model.postStream.loadedAllPosts'), - show_deleted: function(key, value) { - const postStream = this.get('model.postStream'); - if (!postStream) { return; } + @computed('model.postStream.summary') + show_deleted: { + set(key, value) { + const postStream = this.get('model.postStream'); + if (!postStream) { return; } - if (arguments.length > 1) { - postStream.set('show_deleted', value); + if (arguments.length > 1) { + postStream.set('show_deleted', value); + } + return postStream.get('show_deleted') ? true : undefined; + }, + get() { + return this.get('postStream.show_deleted') ? true : undefined; } - return postStream.get('show_deleted') ? true : undefined; - }.property('model.postStream.summary'), + }, - filter: function(key, value) { - const postStream = this.get('model.postStream'); - if (!postStream) { return; } + @computed('model.postStream.summary') + filter: { + set(key, value) { + const postStream = this.get('model.postStream'); + if (!postStream) { return; } - if (arguments.length > 1) { - postStream.set('summary', value === "summary"); + if (arguments.length > 1) { + postStream.set('summary', value === "summary"); + } + return postStream.get('summary') ? "summary" : undefined; + }, + get() { + return this.get('postStream.summary') ? "summary" : undefined; } - return postStream.get('summary') ? "summary" : undefined; - }.property('model.postStream.summary'), + }, - username_filters: function(key, value) { - const postStream = this.get('model.postStream'); - if (!postStream) { return; } + @computed('model.postStream.streamFilters.username_filters') + username_filters: { + set(key, value) { + const postStream = this.get('model.postStream'); + if (!postStream) { return; } - if (arguments.length > 1) { - postStream.set('streamFilters.username_filters', value); + if (arguments.length > 1) { + postStream.set('streamFilters.username_filters', value); + } + return postStream.get('streamFilters.username_filters'); + }, + get() { + return this.get('postStream.streamFilters.username_filters'); } - return postStream.get('streamFilters.username_filters'); - }.property('model.postStream.streamFilters.username_filters'), + }, _clearSelected: function() { this.set('selectedPosts', []); @@ -489,13 +508,13 @@ export default Ember.Controller.extend(SelectedPostsCount, BufferedContent, { canMergeTopic: function() { if (!this.get('model.details.can_move_posts')) return false; - return (this.get('selectedPostsCount') > 0); + return this.get('selectedPostsCount') > 0; }.property('selectedPostsCount'), canSplitTopic: function() { if (!this.get('model.details.can_move_posts')) return false; if (this.get('allPostsSelected')) return false; - return (this.get('selectedPostsCount') > 0); + return this.get('selectedPostsCount') > 0; }.property('selectedPostsCount'), canChangeOwner: function() { diff --git a/app/assets/javascripts/discourse/initializers/apply-flagged-properties.js.es6 b/app/assets/javascripts/discourse/initializers/apply-flagged-properties.js.es6 index f446f60c28b..54085954518 100644 --- a/app/assets/javascripts/discourse/initializers/apply-flagged-properties.js.es6 +++ b/app/assets/javascripts/discourse/initializers/apply-flagged-properties.js.es6 @@ -2,6 +2,5 @@ import { applyFlaggedProperties } from 'discourse/controllers/header'; export default { name: 'apply-flagged-properties', - after: 'register-discourse-location', initialize: applyFlaggedProperties }; diff --git a/app/assets/javascripts/discourse/initializers/csrf-token.js.es6 b/app/assets/javascripts/discourse/initializers/csrf-token.js.es6 index f83ea3caa85..90be518ddd0 100644 --- a/app/assets/javascripts/discourse/initializers/csrf-token.js.es6 +++ b/app/assets/javascripts/discourse/initializers/csrf-token.js.es6 @@ -1,7 +1,6 @@ // Append our CSRF token to AJAX requests when necessary. export default { name: "csrf-token", - after: 'inject-objects', initialize: function(container) { var session = container.lookup('session:main'); diff --git a/app/assets/javascripts/discourse/initializers/enable-emoji.js.es6 b/app/assets/javascripts/discourse/initializers/enable-emoji.js.es6 index bda7e1b8eba..b7b269f78f0 100644 --- a/app/assets/javascripts/discourse/initializers/enable-emoji.js.es6 +++ b/app/assets/javascripts/discourse/initializers/enable-emoji.js.es6 @@ -2,7 +2,6 @@ import { showSelector } from "discourse/lib/emoji/emoji-toolbar"; export default { name: 'enable-emoji', - after: 'inject-objects', initialize(container) { const siteSettings = container.lookup('site-settings:main'); diff --git a/app/assets/javascripts/discourse/initializers/inject-objects.js.es6 b/app/assets/javascripts/discourse/initializers/inject-objects.js.es6 index 197eb06d6bb..f15f57fa8a0 100644 --- a/app/assets/javascripts/discourse/initializers/inject-objects.js.es6 +++ b/app/assets/javascripts/discourse/initializers/inject-objects.js.es6 @@ -1,50 +1,6 @@ -import Session from 'discourse/models/session'; -import AppEvents from 'discourse/lib/app-events'; -import Store from 'discourse/models/store'; -import DiscourseURL from 'discourse/lib/url'; - -function inject() { - const app = arguments[0], - name = arguments[1], - singletonName = Ember.String.underscore(name).replace(/_/, '-') + ':main'; - - Array.prototype.slice.call(arguments, 2).forEach(function(dest) { - app.inject(dest, name, singletonName); - }); -} - -function injectAll(app, name) { - inject(app, name, 'controller', 'component', 'route', 'view', 'model'); -} +// backwards compatibility for plugins that depend on this initializer export default { name: "inject-objects", - initialize(container, app) { - const appEvents = AppEvents.create(); - app.register('app-events:main', appEvents, { instantiate: false }); - injectAll(app, 'appEvents'); - DiscourseURL.appEvents = appEvents; - - app.register('store:main', Store); - inject(app, 'store', 'route', 'controller'); - - // Inject Discourse.Site to avoid using Discourse.Site.current() - const site = Discourse.Site.current(); - app.register('site:main', site, { instantiate: false }); - injectAll(app, 'site'); - - // Inject Discourse.SiteSettings to avoid using Discourse.SiteSettings globals - app.register('site-settings:main', Discourse.SiteSettings, { instantiate: false }); - injectAll(app, 'siteSettings'); - - // Inject Session for transient data - app.register('session:main', Session.current(), { instantiate: false }); - injectAll(app, 'session'); - - app.register('current-user:main', Discourse.User.current(), { instantiate: false }); - inject(app, 'currentUser', 'component', 'route', 'controller'); - - app.register('message-bus:main', window.MessageBus, { instantiate: false }); - inject(app, 'messageBus', 'route', 'controller', 'view', 'component'); - } + initialize: Ember.K }; diff --git a/app/assets/javascripts/discourse/initializers/page-tracking.js.es6 b/app/assets/javascripts/discourse/initializers/page-tracking.js.es6 index 791e1983e6e..c3a5dd46bd9 100644 --- a/app/assets/javascripts/discourse/initializers/page-tracking.js.es6 +++ b/app/assets/javascripts/discourse/initializers/page-tracking.js.es6 @@ -3,7 +3,6 @@ import PageTracker from 'discourse/lib/page-tracker'; export default { name: "page-tracking", - after: 'register-discourse-location', initialize(container) { diff --git a/app/assets/javascripts/discourse/initializers/register-discourse-location.js.es6 b/app/assets/javascripts/discourse/initializers/register-discourse-location.js.es6 index 2e173697554..1f073e95cfb 100644 --- a/app/assets/javascripts/discourse/initializers/register-discourse-location.js.es6 +++ b/app/assets/javascripts/discourse/initializers/register-discourse-location.js.es6 @@ -1,10 +1,6 @@ -import DiscourseLocation from 'discourse/lib/discourse-location'; +// backwards compatibility for plugins that depend on this initializer export default { name: "register-discourse-location", - after: 'inject-objects', - - initialize: function(container, application) { - application.register('location:discourse-location', DiscourseLocation); - } + initialize: Ember.K }; diff --git a/app/assets/javascripts/discourse/initializers/dynamic-route-builders.js.es6 b/app/assets/javascripts/discourse/pre-initializers/dynamic-route-builders.js.es6 similarity index 95% rename from app/assets/javascripts/discourse/initializers/dynamic-route-builders.js.es6 rename to app/assets/javascripts/discourse/pre-initializers/dynamic-route-builders.js.es6 index 64d7f8e0355..a01c73def8a 100644 --- a/app/assets/javascripts/discourse/initializers/dynamic-route-builders.js.es6 +++ b/app/assets/javascripts/discourse/pre-initializers/dynamic-route-builders.js.es6 @@ -4,14 +4,13 @@ import DiscoverySortableController from 'discourse/controllers/discovery-sortabl export default { name: 'dynamic-route-builders', - after: 'register-discourse-location', initialize(container, app) { app.DiscoveryCategoryRoute = buildCategoryRoute('latest'); app.DiscoveryParentCategoryRoute = buildCategoryRoute('latest'); app.DiscoveryCategoryNoneRoute = buildCategoryRoute('latest', {no_subcategories: true}); - var site = container.lookup('site:main'); + const site = Discourse.Site.current(); site.get('filters').forEach(function(filter) { app["Discovery" + filter.capitalize() + "Controller"] = DiscoverySortableController.extend(); app["Discovery" + filter.capitalize() + "Route"] = buildTopicRoute(filter); diff --git a/app/assets/javascripts/discourse/pre-initializers/inject-discourse-objects.js.es6 b/app/assets/javascripts/discourse/pre-initializers/inject-discourse-objects.js.es6 new file mode 100644 index 00000000000..960277e02a5 --- /dev/null +++ b/app/assets/javascripts/discourse/pre-initializers/inject-discourse-objects.js.es6 @@ -0,0 +1,53 @@ +import Session from 'discourse/models/session'; +import AppEvents from 'discourse/lib/app-events'; +import Store from 'discourse/models/store'; +import DiscourseURL from 'discourse/lib/url'; +import DiscourseLocation from 'discourse/lib/discourse-location'; + +function inject() { + const app = arguments[0], + name = arguments[1], + singletonName = Ember.String.underscore(name).replace(/_/, '-') + ':main'; + + Array.prototype.slice.call(arguments, 2).forEach(function(dest) { + app.inject(dest, name, singletonName); + }); +} + +function injectAll(app, name) { + inject(app, name, 'controller', 'component', 'route', 'view', 'model'); +} + +export default { + name: "inject-discourse-objects", + initialize(container, app) { + const appEvents = AppEvents.create(); + app.register('app-events:main', appEvents, { instantiate: false }); + injectAll(app, 'appEvents'); + DiscourseURL.appEvents = appEvents; + + app.register('store:main', Store); + inject(app, 'store', 'route', 'controller'); + + // Inject Discourse.Site to avoid using Discourse.Site.current() + const site = Discourse.Site.current(); + app.register('site:main', site, { instantiate: false }); + injectAll(app, 'site'); + + // Inject Discourse.SiteSettings to avoid using Discourse.SiteSettings globals + app.register('site-settings:main', Discourse.SiteSettings, { instantiate: false }); + injectAll(app, 'siteSettings'); + + // Inject Session for transient data + app.register('session:main', Session.current(), { instantiate: false }); + injectAll(app, 'session'); + + app.register('current-user:main', Discourse.User.current(), { instantiate: false }); + inject(app, 'currentUser', 'component', 'route', 'controller'); + + app.register('message-bus:main', window.MessageBus, { instantiate: false }); + inject(app, 'messageBus', 'route', 'controller', 'view', 'component'); + + app.register('location:discourse-location', DiscourseLocation); + } +}; diff --git a/app/assets/javascripts/discourse/initializers/map-routes.js.es6 b/app/assets/javascripts/discourse/pre-initializers/map-routes.js.es6 similarity index 93% rename from app/assets/javascripts/discourse/initializers/map-routes.js.es6 rename to app/assets/javascripts/discourse/pre-initializers/map-routes.js.es6 index e83537569eb..e3492014424 100644 --- a/app/assets/javascripts/discourse/initializers/map-routes.js.es6 +++ b/app/assets/javascripts/discourse/pre-initializers/map-routes.js.es6 @@ -2,7 +2,7 @@ import { mapRoutes } from 'discourse/router'; export default { name: "map-routes", - after: 'inject-objects', + after: 'inject-discourse-objects', initialize(container, app) { app.register('router:main', mapRoutes()); diff --git a/app/assets/javascripts/discourse/initializers/register-dom-templates.js.es6 b/app/assets/javascripts/discourse/pre-initializers/register-dom-templates.js.es6 similarity index 100% rename from app/assets/javascripts/discourse/initializers/register-dom-templates.js.es6 rename to app/assets/javascripts/discourse/pre-initializers/register-dom-templates.js.es6 diff --git a/app/assets/javascripts/discourse/initializers/sniff-capabilities.js.es6 b/app/assets/javascripts/discourse/pre-initializers/sniff-capabilities.js.es6 similarity index 100% rename from app/assets/javascripts/discourse/initializers/sniff-capabilities.js.es6 rename to app/assets/javascripts/discourse/pre-initializers/sniff-capabilities.js.es6 diff --git a/app/assets/javascripts/discourse/views/topic-unsubscribe.js.es6 b/app/assets/javascripts/discourse/views/topic-unsubscribe.js.es6 index a8469728173..46ac72e0f06 100644 --- a/app/assets/javascripts/discourse/views/topic-unsubscribe.js.es6 +++ b/app/assets/javascripts/discourse/views/topic-unsubscribe.js.es6 @@ -1,3 +1,3 @@ -export default Discourse.View.extend({ +export default Ember.View.extend({ classNames: ["topic-unsubscribe"] }); diff --git a/app/assets/javascripts/ember-addons/decorator-alias.js.es6 b/app/assets/javascripts/ember-addons/decorator-alias.js.es6 new file mode 100644 index 00000000000..615b6cc1e6b --- /dev/null +++ b/app/assets/javascripts/ember-addons/decorator-alias.js.es6 @@ -0,0 +1,22 @@ +import extractValue from './utils/extract-value'; + +export default function decoratorAlias(fn, errorMessage) { + return function(...params) { + // determine if user called as @computed('blah', 'blah') or @computed + if (params.length === 0) { + throw new Error(errorMessage); + } else { + return function(target, key, desc) { + return { + enumerable: desc.enumerable, + configurable: desc.configurable, + writable: desc.writable, + initializer: function() { + var value = extractValue(desc); + return fn.apply(null, params.concat(value)); + } + }; + }; + } + }; +} diff --git a/app/assets/javascripts/ember-addons/ember-computed-decorators.js.es6 b/app/assets/javascripts/ember-addons/ember-computed-decorators.js.es6 new file mode 100644 index 00000000000..e8d3b7ba88f --- /dev/null +++ b/app/assets/javascripts/ember-addons/ember-computed-decorators.js.es6 @@ -0,0 +1,63 @@ +import handleDescriptor from './utils/handle-descriptor'; +import isDescriptor from './utils/is-descriptor'; +import extractValue from './utils/extract-value'; + +export default function computedDecorator(...params) { + // determine if user called as @computed('blah', 'blah') or @computed + if (isDescriptor(params[params.length - 1])) { + return handleDescriptor(...arguments); + } else { + return function(/* target, key, desc */) { + return handleDescriptor(...arguments, params); + }; + } +} + +export function readOnly(target, name, desc) { + return { + writable: false, + enumerable: desc.enumerable, + configurable: desc.configurable, + initializer: function() { + var value = extractValue(desc); + return value.readOnly(); + } + }; +} + +import decoratorAlias from './decorator-alias'; + +export var on = decoratorAlias(Ember.on, 'Can not `on` without event names'); +export var observes = decoratorAlias(Ember.observer, 'Can not `observe` without property names'); + +import macroAlias from './macro-alias'; + +export var alias = macroAlias(Ember.computed.alias); +export var and = macroAlias(Ember.computed.and); +export var bool = macroAlias(Ember.computed.bool); +export var collect = macroAlias(Ember.computed.collect); +export var empty = macroAlias(Ember.computed.empty); +export var equal = macroAlias(Ember.computed.equal); +export var filter = macroAlias(Ember.computed.filter); +export var filterBy = macroAlias(Ember.computed.filterBy); +export var gt = macroAlias(Ember.computed.gt); +export var gte = macroAlias(Ember.computed.gte); +export var lt = macroAlias(Ember.computed.lt); +export var lte = macroAlias(Ember.computed.lte); +export var map = macroAlias(Ember.computed.map); +export var mapBy = macroAlias(Ember.computed.mapBy); +export var match = macroAlias(Ember.computed.match); +export var max = macroAlias(Ember.computed.max); +export var min = macroAlias(Ember.computed.min); +export var none = macroAlias(Ember.computed.none); +export var not = macroAlias(Ember.computed.not); +export var notEmpty = macroAlias(Ember.computed.notEmpty); +export var oneWay = macroAlias(Ember.computed.oneWay); +export var or = macroAlias(Ember.computed.or); +export var readOnly = macroAlias(Ember.computed.readOnly); +export var reads = macroAlias(Ember.computed.reads); +export var setDiff = macroAlias(Ember.computed.setDiff); +export var sort = macroAlias(Ember.computed.sort); +export var sum = macroAlias(Ember.computed.sum); +export var union = macroAlias(Ember.computed.union); +export var uniq = macroAlias(Ember.computed.uniq); diff --git a/app/assets/javascripts/ember-addons/macro-alias.js.es6 b/app/assets/javascripts/ember-addons/macro-alias.js.es6 new file mode 100644 index 00000000000..640e703ca03 --- /dev/null +++ b/app/assets/javascripts/ember-addons/macro-alias.js.es6 @@ -0,0 +1,24 @@ +import isDescriptor from './utils/is-descriptor'; + +function handleDescriptor(target, property, desc, fn, params = []) { + return { + enumerable: desc.enumerable, + configurable: desc.configurable, + writable: desc.writable, + initializer: function() { + return fn(...params); + } + }; +} + +export default function macroAlias(fn) { + return function(...params) { + if (isDescriptor(params[params.length - 1])) { + return handleDescriptor(...params, fn); + } else { + return function(target, property, desc) { + return handleDescriptor(target, property, desc, fn, params); + }; + } + }; +} diff --git a/app/assets/javascripts/ember-addons/utils/extract-value.js.es6 b/app/assets/javascripts/ember-addons/utils/extract-value.js.es6 new file mode 100644 index 00000000000..da83b54b37c --- /dev/null +++ b/app/assets/javascripts/ember-addons/utils/extract-value.js.es6 @@ -0,0 +1,4 @@ +export default function extractValue(desc) { + return desc.value || + (typeof desc.initializer === 'function' && desc.initializer()); +} diff --git a/app/assets/javascripts/ember-addons/utils/handle-descriptor.js.es6 b/app/assets/javascripts/ember-addons/utils/handle-descriptor.js.es6 new file mode 100644 index 00000000000..fee32cac710 --- /dev/null +++ b/app/assets/javascripts/ember-addons/utils/handle-descriptor.js.es6 @@ -0,0 +1,67 @@ +import Ember from 'ember'; +import extractValue from './extract-value'; + +const { computed, get } = Ember; + +export default function handleDescriptor(target, key, desc, params = []) { + return { + enumerable: desc.enumerable, + configurable: desc.configurable, + writeable: desc.writeable, + initializer: function() { + let computedDescriptor; + + if (desc.writable) { + var val = extractValue(desc); + if (typeof val === 'object') { + let value = { }; + if (val.get) { value.get = callUserSuppliedGet(params, val.get); } + if (val.set) { value.set = callUserSuppliedSet(params, val.set); } + computedDescriptor = value; + } else { + computedDescriptor = callUserSuppliedGet(params, val); + } + } else { + throw new Error('ember-computed-decorators does not support using getters and setters'); + } + + return computed.apply(null, params.concat(computedDescriptor)); + } + }; +} + +function niceAttr(attr) { + const parts = attr.split('.'); + let i; + + for (i = 0; i < parts.length; i++) { + if (parts[i] === '@each' || + parts[i] === '[]' || + parts[i].indexOf('{') !== -1) { + break; + } + } + + return parts.slice(0, i).join('.'); +} + +function callUserSuppliedGet(params, func) { + params = params.map(niceAttr); + return function() { + let paramValues = params.map(p => get(this, p)); + + return func.apply(this, paramValues); + }; +} + + +function callUserSuppliedSet(params, func) { + params = params.map(niceAttr); + return function(key, value) { + let paramValues = params.map(p => get(this, p)); + paramValues.unshift(value); + + return func.apply(this, paramValues); + }; +} + diff --git a/app/assets/javascripts/ember-addons/utils/is-descriptor.js.es6 b/app/assets/javascripts/ember-addons/utils/is-descriptor.js.es6 new file mode 100644 index 00000000000..f956952ab23 --- /dev/null +++ b/app/assets/javascripts/ember-addons/utils/is-descriptor.js.es6 @@ -0,0 +1,7 @@ +export default function isDescriptor(item) { + return item && + typeof item === 'object' && + 'writable' in item && + 'enumerable' in item && + 'configurable' in item; +} diff --git a/app/assets/javascripts/ember_include.js.erb b/app/assets/javascripts/ember_include.js.erb index a5842eb6a76..72857343a42 100644 --- a/app/assets/javascripts/ember_include.js.erb +++ b/app/assets/javascripts/ember_include.js.erb @@ -1,7 +1,7 @@ <% if Rails.env.development? || Rails.env.test? require_asset ("ember-template-compiler.js") - require_asset ("ember.custom.debug.js") + require_asset ("ember.debug.js") else require_asset ("ember-template-compiler.js") require_asset ("ember.prod.js") diff --git a/app/assets/javascripts/main_include.js b/app/assets/javascripts/main_include.js index 403271d5fe6..0c02cfe9232 100644 --- a/app/assets/javascripts/main_include.js +++ b/app/assets/javascripts/main_include.js @@ -5,6 +5,10 @@ //= require ./pagedown_custom.js // Stuff we need to load first +//= require_tree ./ember-addons/utils +//= require ./ember-addons/decorator-alias +//= require ./ember-addons/macro-alias +//= require ./ember-addons/ember-computed-decorators //= require ./discourse/lib/load-script //= require ./discourse/lib/notification-levels //= require ./discourse/lib/app-events @@ -94,4 +98,5 @@ //= require_tree ./discourse/helpers //= require_tree ./discourse/templates //= require_tree ./discourse/routes +//= require_tree ./discourse/pre-initializers //= require_tree ./discourse/initializers diff --git a/app/views/common/_discourse_javascript.html.erb b/app/views/common/_discourse_javascript.html.erb index df1977566ef..0f67c8f1c4f 100644 --- a/app/views/common/_discourse_javascript.html.erb +++ b/app/views/common/_discourse_javascript.html.erb @@ -26,8 +26,8 @@ }); <% if Rails.env.development? || Rails.env.test? %> - Ember.ENV.RAISE_ON_DEPRECATION = true - Ember.LOG_STACKTRACE_ON_DEPRECATION = true + //Ember.ENV.RAISE_ON_DEPRECATION = true + //Ember.LOG_STACKTRACE_ON_DEPRECATION = true <% end %> diff --git a/lib/discourse_iife.rb b/lib/discourse_iife.rb index fa80aa43fdf..5c25fc63200 100644 --- a/lib/discourse_iife.rb +++ b/lib/discourse_iife.rb @@ -31,7 +31,7 @@ class DiscourseIIFE < Sprockets::Processor req_path = path.sub(Rails.root.to_s, '') .sub("/app/assets/javascripts", "") .sub("/test/javascripts", "") - res << "\nwindow.__jshintSrc = window.__jshintSrc || {}; window.__jshintSrc['/assets#{req_path}'] = #{data.to_json};\n" + res << "\nwindow.__eslintSrc = window.__eslintSrc || {}; window.__eslintSrc['/assets#{req_path}'] = #{data.to_json};\n" end res diff --git a/lib/es6_module_transpiler/tilt/es6_module_transpiler_template.rb b/lib/es6_module_transpiler/tilt/es6_module_transpiler_template.rb index 3c91c2623ff..2a320ebf59e 100644 --- a/lib/es6_module_transpiler/tilt/es6_module_transpiler_template.rb +++ b/lib/es6_module_transpiler/tilt/es6_module_transpiler_template.rb @@ -83,8 +83,6 @@ module Tilt @output = klass.v8.eval(generate_source(scope)) end - source = @output.dup - # For backwards compatibility with plugins, for now export the Global format too. # We should eventually have an upgrade system for plugins to use ES6 or some other # resolve based API. @@ -129,7 +127,7 @@ module Tilt end req_path = "/assets/#{scope.logical_path}.#{extension}" - @output << "\nwindow.__jshintSrc = window.__jshintSrc || {}; window.__jshintSrc['#{req_path}'] = #{data.to_json};\n" + @output << "\nwindow.__eslintSrc = window.__eslintSrc || {}; window.__eslintSrc['#{req_path}'] = #{data.to_json};\n" end @output @@ -139,7 +137,7 @@ module Tilt def generate_source(scope) js_source = ::JSON.generate(data, quirks_mode: true) - js_source = "babel.transform(#{js_source}, {ast: false, whitelist: ['es6.constants', 'es6.properties.shorthand', 'es6.arrowFunctions', 'es6.blockScoping', 'es6.destructuring', 'es6.templateLiterals', 'es6.regex.unicode']})['code']" + js_source = "babel.transform(#{js_source}, {ast: false, whitelist: ['es6.constants', 'es6.properties.shorthand', 'es6.arrowFunctions', 'es6.blockScoping', 'es6.destructuring', 'es6.spread', 'es6.parameters', 'es6.templateLiterals', 'es6.regex.unicode', 'es7.decorators']})['code']" "new module.exports.Compiler(#{js_source}, '#{module_name(scope.root_path, scope.logical_path)}', #{compiler_options}).#{compiler_method}()" end diff --git a/test/javascripts/jshint-test.js.es6.erb b/test/javascripts/jshint-test.js.es6.erb deleted file mode 100644 index 6c5884aeaa7..00000000000 --- a/test/javascripts/jshint-test.js.es6.erb +++ /dev/null @@ -1,107 +0,0 @@ -module("JSHint"); - -<%= "const JSHINT_OPTS = #{File.read(File.join(Rails.root, '.jshintrc'))};" %> - -var qHint = function(name, sourceFile) { - return asyncTestDiscourse(name, function() { - if (typeof window.__jshintSrc !== "undefined") { - var src = window.__jshintSrc[sourceFile]; - if (src) { - start(); - qHint.validateFile(src, JSHINT_OPTS); - return; - } - } - }); -}; - -qHint.validateFile = function (source, options, globals) { - var i, len, err; - - source = source.replace(/^[^]*\/\/ IIFE Wrapped Content Begins:\n\n/m, ""); - source = source.replace(/\n\n\/\/ IIFE Wrapped Content Ends[^]*$/m, ""); - - if (JSHINT(source, options, globals)) { - ok(true); - return; - } - - for (i = 0, len = JSHINT.errors.length; i < len; i++) { - err = JSHINT.errors[i]; - if (!err) { - continue; - } - - ok(false, err.reason + - " on line " + err.line + - ", character " + err.character); - } -}; - -var XMLHttpFactories = [ - function () { return new XMLHttpRequest(); }, - function () { return new ActiveXObject("Msxml2.XMLHTTP"); }, - function () { return new ActiveXObject("Msxml3.XMLHTTP"); }, - function () { return new ActiveXObject("Microsoft.XMLHTTP"); } -]; - -function createXMLHTTPObject() { - for (var i = 0; i < XMLHttpFactories.length; i++) { - try { - return XMLHttpFactories[i](); - } catch (e) {} - } - return false; -} - -// modified version of XHR script by PPK -// http://www.quirksmode.org/js/xmlhttp.html -// attached to qHint to allow substitution / mocking -qHint.sendRequest = function (url, callback) { - var req = createXMLHTTPObject(); - if (!req) { - return; - } - - var method = "GET"; - req.open(method,url + "?" + (new Date().getTime()),true); - req.onreadystatechange = function () { - if (req.readyState != 4) { - return; - } - - callback(req); - }; - - if (req.readyState == 4) { - return; - } - req.send(); -}; - -<% - TO_IGNORE = File.read("#{Rails.root}/.jshintignore").split("\n") - - def jshint(dir, remove) - result = "" - - Dir.glob(dir).each do |f| - filename = f.sub("#{Rails.root}/#{remove}", "") - - ok = true - TO_IGNORE.each do |ig| - ok = false unless (filename.index(ig.sub(remove, '')).nil?) - end - - depend_on filename - result << "qHint('#{filename}', '/assets/#{filename}', JSHINT_OPTS);\n" if ok - - end - result - end -%> - -<%= jshint("#{Rails.root}/test/**/*.js", "test/javascripts/") %> -<%= jshint("#{Rails.root}/app/assets/javascripts/**/*.js", "app/assets/javascripts/") %> -<%= jshint("#{Rails.root}/app/assets/javascripts/**/*.es6", "app/assets/javascripts/") %> - diff --git a/test/javascripts/lib/click-track-test.js.es6 b/test/javascripts/lib/click-track-test.js.es6 index b01d8b42394..3b228d59aec 100644 --- a/test/javascripts/lib/click-track-test.js.es6 +++ b/test/javascripts/lib/click-track-test.js.es6 @@ -75,7 +75,7 @@ test("removes the href and put it as a data attribute", function() { ok(DiscourseURL.redirectTo.calledOnce); }); -asyncTest("restores the href after a while", function() { +asyncTestDiscourse("restores the href after a while", function() { expect(1); track(generateClickEventOn('a')); diff --git a/test/javascripts/test_helper.js b/test/javascripts/test_helper.js index 746badbd25b..533f2286d8c 100644 --- a/test/javascripts/test_helper.js +++ b/test/javascripts/test_helper.js @@ -1,4 +1,3 @@ -/*jshint maxlen:250 */ /*global document, sinon, QUnit, Logster */ //= require env @@ -12,7 +11,7 @@ //= require jquery.debug //= require jquery.ui.widget //= require handlebars -//= require ember.custom.debug +//= require ember.debug //= require message-bus //= require ember-qunit //= require fake_xml_http_request @@ -36,7 +35,6 @@ //= require sinon-1.7.1 //= require sinon-qunit-1.0.0 -//= require jshint //= require helpers/qunit-helpers //= require helpers/assertions diff --git a/vendor/assets/javascripts/ember-template-compiler.js b/vendor/assets/javascripts/ember-template-compiler.js index 13bb96ffcd8..2e49d243540 100644 --- a/vendor/assets/javascripts/ember-template-compiler.js +++ b/vendor/assets/javascripts/ember-template-compiler.js @@ -5,7 +5,7 @@ * Portions Copyright 2008-2011 Apple Inc. All rights reserved. * @license Licensed under MIT license * See https://raw.github.com/emberjs/ember.js/master/LICENSE - * @version 1.11.3 + * @version 1.12.1 */ (function() { @@ -16,7 +16,6 @@ var mainContext = this; Ember = this.Ember = this.Ember || {}; if (typeof Ember === 'undefined') { Ember = {}; }; - function UNDEFINED() { } if (typeof Ember.__loader === 'undefined') { var registry = {}; @@ -37,35 +36,43 @@ var mainContext = this; }; requirejs = eriuqer = requireModule = function(name) { - var s = seen[name]; + return internalRequire(name, null); + } - if (s !== undefined) { return seen[name]; } - if (s === UNDEFINED) { return undefined; } + function internalRequire(name, referrerName) { + var exports = seen[name]; - seen[name] = {}; + if (exports !== undefined) { + return exports; + } + + exports = seen[name] = {}; if (!registry[name]) { - throw new Error('Could not find module ' + name); + if (referrerName) { + throw new Error('Could not find module ' + name + ' required by: ' + referrerName); + } else { + throw new Error('Could not find module ' + name); + } } var mod = registry[name]; var deps = mod.deps; var callback = mod.callback; var reified = []; - var exports; var length = deps.length; for (var i=0; i\s*\(([^\)]+)\)/gm, "{anonymous}($1)").split("\n"); + stack.shift(); + } else { + // Firefox + stack = error.stack.replace(/(?:\n@:0)?\s+$/m, "").replace(/^\(/gm, "{anonymous}(").split("\n"); + } + + stackStr = "\n " + stack.slice(2).join("\n "); + message = message + stackStr; + } + + Logger['default'].warn("DEPRECATION: " + message); + }; + + /** + Alias an old, deprecated method with its new counterpart. + + Display a deprecation warning with the provided message and a stack trace + (Chrome and Firefox only) when the assigned method is called. + + Ember build tools will not remove calls to `Ember.deprecateFunc()`, though + no warnings will be shown in production. + + ```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 + */ + Ember['default'].deprecateFunc = function (message, func) { + return function () { + Ember['default'].deprecate(message); + return func.apply(this, arguments); + }; + }; + + /** + Run a function meant for debugging. Ember build tools will remove any calls to + `Ember.runInDebug()` when doing a production build. + + ```javascript + Ember.runInDebug(function() { + Ember.Handlebars.EachView.reopen({ + didInsertElement: function() { + console.log('I\'m happy'); + } + }); + }); + ``` + + @method runInDebug + @param {Function} func The function to be executed. + @since 1.5.0 + */ + Ember['default'].runInDebug = function (func) { + func(); + }; + + /** + Will call `Ember.warn()` if ENABLE_ALL_FEATURES, ENABLE_OPTIONAL_FEATURES, or + any specific FEATURES flag is truthy. + + This method is called automatically in debug canary builds. + + @private + @method _warnIfUsingStrippedFeatureFlags + @return {void} + */ + function _warnIfUsingStrippedFeatureFlags(FEATURES, featuresWereStripped) { + if (featuresWereStripped) { + Ember['default'].warn("Ember.ENV.ENABLE_ALL_FEATURES is only available in canary builds.", !Ember['default'].ENV.ENABLE_ALL_FEATURES); + Ember['default'].warn("Ember.ENV.ENABLE_OPTIONAL_FEATURES is only available in canary builds.", !Ember['default'].ENV.ENABLE_OPTIONAL_FEATURES); + + for (var key in FEATURES) { + if (FEATURES.hasOwnProperty(key) && key !== "isEnabled") { + Ember['default'].warn("FEATURE[\"" + key + "\"] is set as enabled, but FEATURE flags are only available in canary builds.", !FEATURES[key]); + } + } + } + } + + if (!Ember['default'].testing) { + // Complain if they're using FEATURE flags in builds other than canary + Ember['default'].FEATURES["features-stripped-test"] = true; + var featuresWereStripped = true; + + + delete Ember['default'].FEATURES["features-stripped-test"]; + _warnIfUsingStrippedFeatureFlags(Ember['default'].ENV.FEATURES, featuresWereStripped); + + // Inform the developer about the Ember Inspector if not installed. + var isFirefox = typeof InstallTrigger !== "undefined"; + var isChrome = environment['default'].isChrome; + + if (typeof window !== "undefined" && (isFirefox || isChrome) && window.addEventListener) { + window.addEventListener("load", function () { + if (document.documentElement && document.documentElement.dataset && !document.documentElement.dataset.emberExtension) { + var downloadURL; + + if (isChrome) { + downloadURL = "https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi"; + } else if (isFirefox) { + downloadURL = "https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/"; + } + + Ember['default'].debug("For more advanced debugging, install the Ember Inspector from " + downloadURL); + } + }, false); + } + } + + /* + We are transitioning away from `ember.js` to `ember.debug.js` to make + it much clearer that it is only for local development purposes. + + 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 = false; + if (runningNonEmberDebugJS) { + Ember['default'].warn("Please use `ember.debug.js` instead of `ember.js` for development and debugging."); + } + + exports.runningNonEmberDebugJS = runningNonEmberDebugJS; + +}); +enifed('ember-metal', ['exports', 'ember-metal/core', 'ember-metal/merge', 'ember-metal/instrumentation', 'ember-metal/utils', 'ember-metal/error', 'ember-metal/enumerable_utils', 'ember-metal/cache', 'ember-metal/platform/define_property', 'ember-metal/platform/create', 'ember-metal/array', 'ember-metal/logger', 'ember-metal/property_get', 'ember-metal/events', 'ember-metal/observer_set', 'ember-metal/property_events', 'ember-metal/properties', 'ember-metal/property_set', 'ember-metal/map', 'ember-metal/get_properties', 'ember-metal/set_properties', 'ember-metal/watch_key', 'ember-metal/chains', 'ember-metal/watch_path', 'ember-metal/watching', 'ember-metal/expand_properties', 'ember-metal/computed', 'ember-metal/alias', 'ember-metal/computed_macros', 'ember-metal/observer', 'ember-metal/mixin', 'ember-metal/binding', 'ember-metal/run_loop', 'ember-metal/libraries', 'ember-metal/is_none', 'ember-metal/is_empty', 'ember-metal/is_blank', 'ember-metal/is_present', 'ember-metal/keys', 'backburner', 'ember-metal/streams/utils', 'ember-metal/streams/stream'], function (exports, Ember, merge, instrumentation, utils, EmberError, EnumerableUtils, Cache, define_property, create, array, Logger, property_get, events, ObserverSet, property_events, properties, property_set, map, getProperties, setProperties, watch_key, chains, watch_path, watching, expandProperties, computed, alias, computed_macros, observer, mixin, binding, run, Libraries, isNone, isEmpty, isBlank, isPresent, keys, Backburner, streams__utils, Stream) { + + 'use strict'; + + /** + Ember Metal + + @module ember + @submodule ember-metal + */ + + // BEGIN IMPORTS + computed.computed.empty = computed_macros.empty; + computed.computed.notEmpty = computed_macros.notEmpty; + computed.computed.none = computed_macros.none; + computed.computed.not = computed_macros.not; + computed.computed.bool = computed_macros.bool; + computed.computed.match = computed_macros.match; + computed.computed.equal = computed_macros.equal; + computed.computed.gt = computed_macros.gt; + computed.computed.gte = computed_macros.gte; + computed.computed.lt = computed_macros.lt; + computed.computed.lte = computed_macros.lte; + computed.computed.alias = alias['default']; + computed.computed.oneWay = computed_macros.oneWay; + computed.computed.reads = computed_macros.oneWay; + computed.computed.readOnly = computed_macros.readOnly; + computed.computed.defaultTo = computed_macros.defaultTo; + computed.computed.deprecatingAlias = computed_macros.deprecatingAlias; + computed.computed.and = computed_macros.and; + computed.computed.or = computed_macros.or; + computed.computed.any = computed_macros.any; + computed.computed.collect = computed_macros.collect; // END IMPORTS + + // BEGIN EXPORTS + var EmberInstrumentation = Ember['default'].Instrumentation = {}; + EmberInstrumentation.instrument = instrumentation.instrument; + EmberInstrumentation.subscribe = instrumentation.subscribe; + EmberInstrumentation.unsubscribe = instrumentation.unsubscribe; + EmberInstrumentation.reset = instrumentation.reset; + + Ember['default'].instrument = instrumentation.instrument; + Ember['default'].subscribe = instrumentation.subscribe; + + Ember['default']._Cache = Cache['default']; + + Ember['default'].generateGuid = utils.generateGuid; + Ember['default'].GUID_KEY = utils.GUID_KEY; + Ember['default'].create = create['default']; + Ember['default'].keys = keys['default']; + Ember['default'].platform = { + defineProperty: properties.defineProperty, + hasPropertyAccessors: define_property.hasPropertyAccessors + }; + + var EmberArrayPolyfills = Ember['default'].ArrayPolyfills = {}; + + EmberArrayPolyfills.map = array.map; + EmberArrayPolyfills.forEach = array.forEach; + EmberArrayPolyfills.filter = array.filter; + EmberArrayPolyfills.indexOf = array.indexOf; + + Ember['default'].Error = EmberError['default']; + Ember['default'].guidFor = utils.guidFor; + Ember['default'].META_DESC = utils.META_DESC; + Ember['default'].EMPTY_META = utils.EMPTY_META; + Ember['default'].meta = utils.meta; + Ember['default'].getMeta = utils.getMeta; + Ember['default'].setMeta = utils.setMeta; + Ember['default'].metaPath = utils.metaPath; + Ember['default'].inspect = utils.inspect; + Ember['default'].typeOf = utils.typeOf; + Ember['default'].tryCatchFinally = utils.deprecatedTryCatchFinally; + Ember['default'].isArray = utils.isArray; + Ember['default'].makeArray = utils.makeArray; + Ember['default'].canInvoke = utils.canInvoke; + Ember['default'].tryInvoke = utils.tryInvoke; + Ember['default'].tryFinally = utils.deprecatedTryFinally; + Ember['default'].wrap = utils.wrap; + Ember['default'].apply = utils.apply; + Ember['default'].applyStr = utils.applyStr; + Ember['default'].uuid = utils.uuid; + + Ember['default'].Logger = Logger['default']; + + Ember['default'].get = property_get.get; + Ember['default'].getWithDefault = property_get.getWithDefault; + Ember['default'].normalizeTuple = property_get.normalizeTuple; + Ember['default']._getPath = property_get._getPath; + + Ember['default'].EnumerableUtils = EnumerableUtils['default']; + + Ember['default'].on = events.on; + Ember['default'].addListener = events.addListener; + Ember['default'].removeListener = events.removeListener; + Ember['default']._suspendListener = events.suspendListener; + Ember['default']._suspendListeners = events.suspendListeners; + Ember['default'].sendEvent = events.sendEvent; + Ember['default'].hasListeners = events.hasListeners; + Ember['default'].watchedEvents = events.watchedEvents; + Ember['default'].listenersFor = events.listenersFor; + Ember['default'].accumulateListeners = events.accumulateListeners; + + Ember['default']._ObserverSet = ObserverSet['default']; + + Ember['default'].propertyWillChange = property_events.propertyWillChange; + Ember['default'].propertyDidChange = property_events.propertyDidChange; + Ember['default'].overrideChains = property_events.overrideChains; + Ember['default'].beginPropertyChanges = property_events.beginPropertyChanges; + Ember['default'].endPropertyChanges = property_events.endPropertyChanges; + Ember['default'].changeProperties = property_events.changeProperties; + + Ember['default'].defineProperty = properties.defineProperty; + + Ember['default'].set = property_set.set; + Ember['default'].trySet = property_set.trySet; + + Ember['default'].OrderedSet = map.OrderedSet; + Ember['default'].Map = map.Map; + Ember['default'].MapWithDefault = map.MapWithDefault; + + Ember['default'].getProperties = getProperties['default']; + Ember['default'].setProperties = setProperties['default']; + + Ember['default'].watchKey = watch_key.watchKey; + Ember['default'].unwatchKey = watch_key.unwatchKey; + + Ember['default'].flushPendingChains = chains.flushPendingChains; + Ember['default'].removeChainWatcher = chains.removeChainWatcher; + Ember['default']._ChainNode = chains.ChainNode; + Ember['default'].finishChains = chains.finishChains; + + Ember['default'].watchPath = watch_path.watchPath; + Ember['default'].unwatchPath = watch_path.unwatchPath; + + Ember['default'].watch = watching.watch; + Ember['default'].isWatching = watching.isWatching; + Ember['default'].unwatch = watching.unwatch; + Ember['default'].rewatch = watching.rewatch; + Ember['default'].destroy = watching.destroy; + + Ember['default'].expandProperties = expandProperties['default']; + + Ember['default'].ComputedProperty = computed.ComputedProperty; + Ember['default'].computed = computed.computed; + Ember['default'].cacheFor = computed.cacheFor; + + Ember['default'].addObserver = observer.addObserver; + Ember['default'].observersFor = observer.observersFor; + Ember['default'].removeObserver = observer.removeObserver; + Ember['default'].addBeforeObserver = observer.addBeforeObserver; + Ember['default']._suspendBeforeObserver = observer._suspendBeforeObserver; + Ember['default']._suspendBeforeObservers = observer._suspendBeforeObservers; + Ember['default']._suspendObserver = observer._suspendObserver; + Ember['default']._suspendObservers = observer._suspendObservers; + Ember['default'].beforeObserversFor = observer.beforeObserversFor; + Ember['default'].removeBeforeObserver = observer.removeBeforeObserver; + + Ember['default'].IS_BINDING = mixin.IS_BINDING; + Ember['default'].required = mixin.required; + Ember['default'].aliasMethod = mixin.aliasMethod; + Ember['default'].observer = mixin.observer; + Ember['default'].immediateObserver = mixin.immediateObserver; + Ember['default'].beforeObserver = mixin.beforeObserver; + Ember['default'].mixin = mixin.mixin; + Ember['default'].Mixin = mixin.Mixin; + + Ember['default'].oneWay = binding.oneWay; + Ember['default'].bind = binding.bind; + Ember['default'].Binding = binding.Binding; + Ember['default'].isGlobalPath = binding.isGlobalPath; + + Ember['default'].run = run['default']; + + /** + * @class Backburner + * @for Ember + * @private + */ + Ember['default'].Backburner = Backburner['default']; + + Ember['default'].libraries = new Libraries['default'](); + Ember['default'].libraries.registerCoreLibrary("Ember", Ember['default'].VERSION); + + Ember['default'].isNone = isNone['default']; + Ember['default'].isEmpty = isEmpty['default']; + Ember['default'].isBlank = isBlank['default']; + Ember['default'].isPresent = isPresent['default']; + + Ember['default'].merge = merge['default']; + + + /** + A function may be assigned to `Ember.onerror` to be called when Ember + internals encounter an error. This is useful for specialized error handling + and reporting code. + + ```javascript + Ember.onerror = function(error) { + Em.$.ajax('/report-error', 'POST', { + stack: error.stack, + otherInformation: 'whatever app state you want to provide' + }); + }; + ``` + + Internally, `Ember.onerror` is used as Backburner's error handler. + + @event onerror + @for Ember + @param {Exception} error the error object + */ + Ember['default'].onerror = null; + // END EXPORTS + + // do this for side-effects of updating Ember.assert, warn, etc when + // ember-debug is present + if (Ember['default'].__loader.registry["ember-debug"]) { + requireModule("ember-debug"); + } + + exports['default'] = Ember['default']; + +}); +enifed('ember-metal/alias', ['exports', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-metal/core', 'ember-metal/error', 'ember-metal/properties', 'ember-metal/computed', 'ember-metal/platform/create', 'ember-metal/utils', 'ember-metal/dependent_keys'], function (exports, property_get, property_set, Ember, EmberError, properties, computed, create, utils, dependent_keys) { + + 'use strict'; + + exports.AliasedProperty = AliasedProperty; + + exports['default'] = alias; + + function alias(altKey) { + return new AliasedProperty(altKey); + } + + function AliasedProperty(altKey) { + this.isDescriptor = true; + this.altKey = altKey; + this._dependentKeys = [altKey]; + } + + AliasedProperty.prototype = create['default'](properties.Descriptor.prototype); + + AliasedProperty.prototype.get = function AliasedProperty_get(obj, keyName) { + return property_get.get(obj, this.altKey); + }; + + AliasedProperty.prototype.set = function AliasedProperty_set(obj, keyName, value) { + return property_set.set(obj, this.altKey, value); + }; + + AliasedProperty.prototype.willWatch = function (obj, keyName) { + dependent_keys.addDependentKeys(this, obj, keyName, utils.meta(obj)); + }; + + AliasedProperty.prototype.didUnwatch = function (obj, keyName) { + dependent_keys.removeDependentKeys(this, obj, keyName, utils.meta(obj)); + }; + + AliasedProperty.prototype.setup = function (obj, keyName) { + Ember['default'].assert("Setting alias '" + keyName + "' on self", this.altKey !== keyName); + var m = utils.meta(obj); + if (m.watching[keyName]) { + dependent_keys.addDependentKeys(this, obj, keyName, m); + } + }; + + AliasedProperty.prototype.teardown = function (obj, keyName) { + var m = utils.meta(obj); + if (m.watching[keyName]) { + dependent_keys.removeDependentKeys(this, obj, keyName, m); + } + }; + + AliasedProperty.prototype.readOnly = function () { + this.set = AliasedProperty_readOnlySet; + return this; + }; + + function AliasedProperty_readOnlySet(obj, keyName, value) { + throw new EmberError['default']("Cannot set read-only property '" + keyName + "' on object: " + utils.inspect(obj)); + } + + AliasedProperty.prototype.oneWay = function () { + this.set = AliasedProperty_oneWaySet; + return this; + }; + + function AliasedProperty_oneWaySet(obj, keyName, value) { + properties.defineProperty(obj, keyName, null); + return property_set.set(obj, keyName, value); + } + + // Backwards compatibility with Ember Data + AliasedProperty.prototype._meta = undefined; + AliasedProperty.prototype.meta = computed.ComputedProperty.prototype.meta; + +}); +enifed('ember-metal/array', ['exports'], function (exports) { + + 'use strict'; + + /** + @module ember-metal + */ + + var ArrayPrototype = Array.prototype; + + // 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; + }; + + var defineNativeShim = function (nativeFunc, shim) { + if (isNativeFunc(nativeFunc)) { + return nativeFunc; + } + return shim; + }; + + // From: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/map + var map = defineNativeShim(ArrayPrototype.map, function (fun) { + //"use strict"; + + if (this === void 0 || this === null || typeof fun !== "function") { + throw new TypeError(); + } + + var t = Object(this); + var len = t.length >>> 0; + var res = new Array(len); + + for (var i = 0; i < len; i++) { + if (i in t) { + res[i] = fun.call(arguments[1], t[i], i, t); + } + } + + return res; + }); + + // From: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/foreach + var forEach = defineNativeShim(ArrayPrototype.forEach, function (fun) { + //"use strict"; + + if (this === void 0 || this === null || typeof fun !== "function") { + throw new TypeError(); + } + + var t = Object(this); + var len = t.length >>> 0; + + for (var i = 0; i < len; i++) { + if (i in t) { + fun.call(arguments[1], t[i], i, t); + } + } + }); + + var indexOf = defineNativeShim(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; + }); + + var lastIndexOf = defineNativeShim(ArrayPrototype.lastIndexOf, function (obj, fromIndex) { + var len = this.length; + var idx; + + if (fromIndex === undefined) { + fromIndex = len - 1; + } else { + fromIndex = fromIndex < 0 ? Math.ceil(fromIndex) : Math.floor(fromIndex); + } + + if (fromIndex < 0) { + fromIndex += len; + } + + for (idx = fromIndex; idx >= 0; idx--) { + if (this[idx] === obj) { + return idx; + } + } + return -1; + }); + + var filter = defineNativeShim(ArrayPrototype.filter, function (fn, context) { + var i, value; + var result = []; + var length = this.length; + + for (i = 0; i < length; i++) { + if (this.hasOwnProperty(i)) { + value = this[i]; + if (fn.call(context, value, i, this)) { + result.push(value); + } + } + } + return result; + }); + + if (Ember.SHIM_ES5) { + ArrayPrototype.map = ArrayPrototype.map || map; + ArrayPrototype.forEach = ArrayPrototype.forEach || forEach; + ArrayPrototype.filter = ArrayPrototype.filter || filter; + ArrayPrototype.indexOf = ArrayPrototype.indexOf || indexOf; + ArrayPrototype.lastIndexOf = ArrayPrototype.lastIndexOf || lastIndexOf; + } + + /** + Array polyfills to support ES5 features in older browsers. + + @namespace Ember + @property ArrayPolyfills + */ + + exports.map = map; + exports.forEach = forEach; + exports.filter = filter; + exports.indexOf = indexOf; + exports.lastIndexOf = lastIndexOf; + +}); +enifed('ember-metal/binding', ['exports', 'ember-metal/core', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-metal/utils', 'ember-metal/observer', 'ember-metal/run_loop', 'ember-metal/path_cache'], function (exports, Ember, property_get, property_set, utils, observer, run, path_cache) { + + 'use strict'; + + exports.bind = bind; + exports.oneWay = oneWay; + exports.Binding = Binding; + + Ember['default'].LOG_BINDINGS = false || !!Ember['default'].ENV.LOG_BINDINGS; + + /** + Returns true if the provided path is global (e.g., `MyApp.fooController.bar`) + instead of local (`foo.bar.baz`). + + @method isGlobalPath + @for Ember + @private + @param {String} path + @return Boolean + */ + + function getWithGlobals(obj, path) { + return property_get.get(path_cache.isGlobal(path) ? Ember['default'].lookup : obj, path); + } + + // .......................................................... + // BINDING + // + + function Binding(toPath, fromPath) { + this._direction = undefined; + this._from = fromPath; + this._to = toPath; + this._readyToSync = undefined; + this._oneWay = undefined; + } + + /** + @class Binding + @namespace Ember + */ + + Binding.prototype = { + /** + This copies the Binding so it can be connected to another object. + @method copy + @return {Ember.Binding} `this` + */ + copy: function () { + var copy = new Binding(this._to, this._from); + if (this._oneWay) { + copy._oneWay = true; + } + return copy; + }, + + // .......................................................... + // CONFIG + // + + /** + 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. + 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. + @method from + @param {String} path the property path to connect to + @return {Ember.Binding} `this` + */ + from: function (path) { + this._from = path; + return this; + }, + + /** + 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. + 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. + @method to + @param {String|Tuple} path A property path or tuple + @return {Ember.Binding} `this` + */ + to: function (path) { + this._to = path; + return this; + }, + + /** + 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. + @method oneWay + @return {Ember.Binding} `this` + */ + oneWay: function () { + this._oneWay = true; + return this; + }, + + /** + @method toString + @return {String} string representation of binding + */ + toString: function () { + var oneWay = this._oneWay ? "[oneWay]" : ""; + return "Ember.Binding<" + utils.guidFor(this) + ">(" + this._from + " -> " + this._to + ")" + oneWay; + }, + + // .......................................................... + // CONNECT AND SYNC + // + + /** + 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. + @method connect + @param {Object} obj The root object for this binding. + @return {Ember.Binding} `this` + */ + connect: function (obj) { + Ember['default'].assert("Must pass a valid object to Ember.Binding.connect()", !!obj); + + var fromPath = this._from; + var toPath = this._to; + property_set.trySet(obj, toPath, getWithGlobals(obj, fromPath)); + + // add an observer on the object to be notified when the binding should be updated + observer.addObserver(obj, fromPath, this, this.fromDidChange); + + // if the binding is a two-way binding, also set up an observer on the target + if (!this._oneWay) { + observer.addObserver(obj, toPath, this, this.toDidChange); + } + + this._readyToSync = true; + + return this; + }, + + /** + Disconnects the binding instance. Changes will no longer be relayed. You + will not usually need to call this method. + @method disconnect + @param {Object} obj The root object you passed when connecting the binding. + @return {Ember.Binding} `this` + */ + disconnect: function (obj) { + Ember['default'].assert("Must pass a valid object to Ember.Binding.disconnect()", !!obj); + + var twoWay = !this._oneWay; + + // remove an observer on the object so we're no longer notified of + // changes that should update bindings. + observer.removeObserver(obj, this._from, this, this.fromDidChange); + + // if the binding is two-way, remove the observer from the target as well + if (twoWay) { + observer.removeObserver(obj, this._to, this, this.toDidChange); + } + + this._readyToSync = false; // disable scheduled syncs... + return this; + }, + + // .......................................................... + // PRIVATE + // + + /* called when the from side changes */ + fromDidChange: function (target) { + this._scheduleSync(target, "fwd"); + }, + + /* called when the to side changes */ + toDidChange: function (target) { + this._scheduleSync(target, "back"); + }, + + _scheduleSync: function (obj, dir) { + var existingDir = this._direction; + + // if we haven't scheduled the binding yet, schedule it + if (existingDir === undefined) { + run['default'].schedule("sync", this, this._sync, obj); + this._direction = dir; + } + + // 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") { + this._direction = "fwd"; + } + }, + + _sync: function (obj) { + var log = Ember['default'].LOG_BINDINGS; + + // don't synchronize destroyed objects or disconnected bindings + if (obj.isDestroyed || !this._readyToSync) { + return; + } + + // get the direction of the binding for the object we are + // synchronizing from + var direction = this._direction; + + var fromPath = this._from; + var toPath = this._to; + + this._direction = undefined; + + // if we're synchronizing from the remote object... + if (direction === "fwd") { + var fromValue = getWithGlobals(obj, this._from); + if (log) { + Ember['default'].Logger.log(" ", this.toString(), "->", fromValue, obj); + } + if (this._oneWay) { + property_set.trySet(obj, toPath, fromValue); + } else { + observer._suspendObserver(obj, toPath, this, this.toDidChange, function () { + property_set.trySet(obj, toPath, fromValue); + }); + } + // if we're synchronizing *to* the remote object + } else if (direction === "back") { + var toValue = property_get.get(obj, this._to); + if (log) { + Ember['default'].Logger.log(" ", this.toString(), "<-", toValue, obj); + } + observer._suspendObserver(obj, fromPath, this, this.fromDidChange, function () { + property_set.trySet(path_cache.isGlobal(fromPath) ? Ember['default'].lookup : obj, fromPath, toValue); + }); + } + } + + }; + + function mixinProperties(to, from) { + for (var key in from) { + if (from.hasOwnProperty(key)) { + to[key] = from[key]; + } + } + } + + mixinProperties(Binding, { + + /* + See `Ember.Binding.from`. + @method from + @static + */ + from: function (from) { + var C = this; + return new C(undefined, from); + }, + + /* + See `Ember.Binding.to`. + @method to + @static + */ + to: function (to) { + var C = this; + return new C(to, undefined); + }, + + /** + 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. + See `Binding.oneWay`. + @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; + return new C(undefined, from).oneWay(flag); + } + + }); + /** + 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. + + ## Automatic Creation of Bindings with `/^*Binding/`-named Properties + + You do not usually create Binding objects directly but instead describe + bindings in your class or object definition using automatic binding + detection. + + 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 instance created using Binding helpers + (see "One Way Bindings"): + + ``` + valueBinding: "MyApp.someController.title" + ``` + + 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. + + ## One Way Bindings + + 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: + + ``` + bigTitlesBinding: Ember.Binding.oneWay("MyApp.preferencesController.bigTitles") + ``` + + 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`. + + 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. + + 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). + + ## Adding Bindings Manually + + 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. + + 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. + + 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: + + ```javascript + binding = Ember.Binding.from("valueBinding").to("value"); + ``` + + 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: + + ```javascript + binding.connect(this); + ``` + + 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. + + This also allows you to bind between objects using the paths you declare in + `from` and `to`: + + ```javascript + // Example 1 + binding = Ember.Binding.from("App.someObject.value").to("value"); + binding.connect(this); + + // Example 2 + binding = Ember.Binding.from("parentView.value").to("App.someObject.value"); + binding.connect(this); + ``` + + Now that the binding is connected, it will observe both the from and to side + and relay changes. + + 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): + + ```javascript + Ember.bind(MyApp.anotherObject, "value", "MyApp.someController.value"); + ``` + + Both of these code fragments have the same effect as doing the most friendly + form of binding creation like so: + + ```javascript + MyApp.anotherObject = Ember.Object.create({ + valueBinding: "MyApp.someController.value", + + // OTHER CODE FOR THIS OBJECT... + }); + ``` + + 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. + + @class Binding + @namespace Ember + @since Ember 0.9 + */ + // Ember.Binding = Binding; ES6TODO: where to put this? + + /** + 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. + + @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); + } + + exports.isGlobalPath = path_cache.isGlobal; + +}); +enifed('ember-metal/cache', ['exports', 'ember-metal/dictionary'], function (exports, dictionary) { + + 'use strict'; + + exports['default'] = Cache; + + function Cache(limit, func) { + this.store = dictionary['default'](null); + this.size = 0; + this.misses = 0; + this.hits = 0; + this.limit = limit; + this.func = func; + } + + var UNDEFINED = function () {}; + + Cache.prototype = { + set: function (key, value) { + if (this.limit > this.size) { + this.size++; + if (value === undefined) { + this.store[key] = UNDEFINED; + } else { + this.store[key] = value; + } + } + + return value; + }, + + get: function (key) { + var value = this.store[key]; + + if (value === undefined) { + this.misses++; + value = this.set(key, this.func(key)); + } else if (value === UNDEFINED) { + this.hits++; + value = undefined; + } else { + this.hits++; + // nothing to translate + } + + return value; + }, + + purge: function () { + this.store = dictionary['default'](null); + this.size = 0; + this.hits = 0; + this.misses = 0; + } + }; + +}); +enifed('ember-metal/chains', ['exports', 'ember-metal/core', 'ember-metal/property_get', 'ember-metal/utils', 'ember-metal/array', 'ember-metal/watch_key'], function (exports, Ember, property_get, utils, array, watch_key) { + + 'use strict'; + + exports.flushPendingChains = flushPendingChains; + exports.finishChains = finishChains; + exports.removeChainWatcher = removeChainWatcher; + exports.ChainNode = ChainNode; + + var warn = Ember['default'].warn; + var FIRST_KEY = /^([^\.]+)/; + + function firstKey(path) { + return path.match(FIRST_KEY)[0]; + } + + function isObject(obj) { + return obj && typeof obj === "object"; + } + + var pendingQueue = []; + + // 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; + } + + var queue = pendingQueue; + pendingQueue = []; + + array.forEach.call(queue, function (q) { + q[0].add(q[1]); + }); + + 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); + } + + function addChainWatcher(obj, keyName, node) { + if (!isObject(obj)) { + return; + } + + var m = utils.meta(obj); + var nodes = m.chainWatchers; + + if (!m.hasOwnProperty("chainWatchers")) { + // FIXME?! + nodes = m.chainWatchers = {}; + } + + if (!nodes[keyName]) { + nodes[keyName] = []; + } + nodes[keyName].push(node); + watch_key.watchKey(obj, keyName, m); + } + + function removeChainWatcher(obj, keyName, node) { + if (!isObject(obj)) { + return; + } + + var m = obj["__ember_meta__"]; + if (m && !m.hasOwnProperty("chainWatchers")) { + return; + } + + var nodes = m && m.chainWatchers; + + 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); + break; + } + } + } + watch_key.unwatchKey(obj, keyName, m); + } + + // A ChainNode watches a single key on an object. If you provide a starting + // value for the key then the node won't actually watch it. For a root node + // pass null for parent and key and object for value. + function ChainNode(parent, key, value) { + this._parent = parent; + this._key = key; + + // _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._value = value; + this._paths = {}; + if (this._watching) { + this._object = parent.value(); + if (this._object) { + addChainWatcher(this._object, this._key, this); + } + } + + // 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(); + } + } + + function lazyGet(obj, key) { + if (!obj) { + return; + } + + var meta = obj["__ember_meta__"]; + // check if object meant only to be a prototype + if (meta && meta.proto === obj) { + return; + } + + if (key === "@each") { + return property_get.get(obj, key); + } + + // if a CP only return cached value + var possibleDesc = obj[key]; + var desc = possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor ? possibleDesc : undefined; + if (desc && desc._cacheable) { + if (meta.cache && key in meta.cache) { + return meta.cache[key]; + } else { + return; + } + } + + return property_get.get(obj, key); + } + + ChainNode.prototype = { + value: function () { + if (this._value === undefined && this._watching) { + var obj = this._parent.value(); + this._value = lazyGet(obj, this._key); + } + return this._value; + }, + + destroy: function () { + if (this._watching) { + var obj = this._object; + if (obj) { + removeChainWatcher(obj, this._key, this); + } + this._watching = false; // so future calls do nothing + } + }, + + // copies a top level object only + copy: function (obj) { + var ret = new ChainNode(null, null, obj); + var paths = this._paths; + var path; + + for (path in paths) { + // this check will also catch non-number vals. + if (paths[path] <= 0) { + continue; + } + ret.add(path); + } + return ret; + }, + + // called on the root node of a chain to setup watchers on the specified + // path. + add: function (path) { + var obj, tuple, key, src, paths; + + paths = this._paths; + paths[path] = (paths[path] || 0) + 1; + + obj = this.value(); + tuple = property_get.normalizeTuple(obj, path); + + // the path was a local path + if (tuple[0] && tuple[0] === obj) { + path = tuple[1]; + key = firstKey(path); + path = path.slice(key.length + 1); + + // 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; + + // global path, and object already exists + } else { + src = tuple[0]; + key = path.slice(0, 0 - (tuple[1].length + 1)); + path = tuple[1]; + } + + tuple.length = 0; + this.chain(key, path, src); + }, + + // called on the root node of a chain to teardown watcher on the specified + // path + remove: function (path) { + var obj, tuple, key, src, paths; + + paths = this._paths; + if (paths[path] > 0) { + paths[path]--; + } + + obj = this.value(); + tuple = property_get.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]; + } + + tuple.length = 0; + this.unchain(key, path); + }, + + count: 0, + + chain: function (key, path, src) { + var chains = this._chains; + var node; + if (!chains) { + chains = this._chains = {}; + } + + node = chains[key]; + if (!node) { + node = chains[key] = new ChainNode(this, key, src); + } + node.count++; // count chains... + + // chain rest of path if there is one + if (path) { + key = firstKey(path); + path = path.slice(key.length + 1); + node.chain(key, path); // NOTE: no src means it will observe changes... + } + }, + + unchain: function (key, path) { + var chains = this._chains; + var node = chains[key]; + + // unchain rest of path first... + if (path && path.length > 1) { + var nextKey = firstKey(path); + var nextPath = path.slice(nextKey.length + 1); + node.unchain(nextKey, nextPath); + } + + // delete node if needed. + node.count--; + if (node.count <= 0) { + delete chains[node._key]; + node.destroy(); + } + }, + + willChange: function (events) { + var chains = this._chains; + if (chains) { + for (var key in chains) { + if (!chains.hasOwnProperty(key)) { + continue; + } + chains[key].willChange(events); + } + } + + if (this._parent) { + this._parent.chainWillChange(this, this._key, 1, events); + } + }, + + chainWillChange: function (chain, path, depth, events) { + if (this._key) { + path = this._key + "." + path; + } + + 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); + } + } + }, + + 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); + } + } + }, + + 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; + + // Special-case: the EachProxy relies on immediate evaluation to + // establish its observers. + if (this._parent && this._parent._key === "@each") { + this.value(); + } + } + + // then notify chains... + var chains = this._chains; + if (chains) { + for (var key in chains) { + if (!chains.hasOwnProperty(key)) { + continue; + } + chains[key].didChange(events); + } + } + + // if no events are passed in then we only care about the above wiring update + if (events === null) { + return; + } + + // and finally tell parent about my path changing... + if (this._parent) { + this._parent.chainDidChange(this, this._key, 1, events); + } + } + }; + function finishChains(obj) { + // We only create meta if we really have to + var m = obj["__ember_meta__"]; + var chains, chainWatchers, chainNodes; + + if (m) { + // finish any current chains node watchers that reference obj + chainWatchers = m.chainWatchers; + if (chainWatchers) { + for (var key in chainWatchers) { + if (!chainWatchers.hasOwnProperty(key)) { + continue; + } + + chainNodes = chainWatchers[key]; + if (chainNodes) { + for (var i = 0, l = chainNodes.length; i < l; i++) { + chainNodes[i].didChange(null); + } + } + } + } + // copy chains from prototype + chains = m.chains; + if (chains && chains.value() !== obj) { + utils.meta(obj).chains = chains = chains.copy(obj); + } + } + } + +}); +enifed('ember-metal/computed', ['exports', 'ember-metal/property_set', 'ember-metal/utils', 'ember-metal/expand_properties', 'ember-metal/error', 'ember-metal/properties', 'ember-metal/property_events', 'ember-metal/dependent_keys'], function (exports, property_set, utils, expandProperties, EmberError, properties, property_events, dependent_keys) { + + 'use strict'; + + exports.ComputedProperty = ComputedProperty; + exports.computed = computed; + exports.cacheFor = cacheFor; + + var metaFor = utils.meta; + + function UNDEFINED() {} + + // .......................................................... + // COMPUTED PROPERTY + // + + /** + A computed property transforms an object's function into a property. + + 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 depends on. This will force the cached + result to be recomputed if the dependencies are modified. + + In the following example we declare a computed property (by calling + `.property()` on the fullName function) and setup the property + dependencies (depending on firstName and lastName). The fullName function + will be called once (regardless of how many times it is accessed) as long + as its dependencies have not changed. Once firstName or lastName are updated + any future calls (or anything bound) to fullName will incorporate the new + values. + + ```javascript + var Person = Ember.Object.extend({ + // these will be supplied by `create` + firstName: null, + lastName: null, + + fullName: function() { + var firstName = this.get('firstName'); + var lastName = this.get('lastName'); + + return firstName + ' ' + lastName; + }.property('firstName', 'lastName') + }); + + var tom = Person.create({ + firstName: 'Tom', + lastName: 'Dale' + }); + + tom.get('fullName') // 'Tom Dale' + ``` + + 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 Person = Ember.Object.extend({ + // these will be supplied by `create` + firstName: null, + lastName: null, + + fullName: function(key, value, oldValue) { + // getter + if (arguments.length === 1) { + var firstName = this.get('firstName'); + var lastName = this.get('lastName'); + + return firstName + ' ' + lastName; + + // setter + } else { + var name = value.split(' '); + + this.set('firstName', name[0]); + this.set('lastName', name[1]); + + return value; + } + }.property('firstName', 'lastName') + }); + + var person = Person.create(); + + person.set('fullName', 'Peter Wagenet'); + person.get('firstName'); // 'Peter' + person.get('lastName'); // 'Wagenet' + ``` + + @class ComputedProperty + @namespace Ember + @constructor + */ + function ComputedProperty(config, opts) { + this.isDescriptor = true; + + if (typeof config === "function") { + config.__ember_arity = config.length; + this._getter = config; + if (config.__ember_arity > 1) { + Ember.deprecate("Using the same function as getter and setter is deprecated.", false, { + url: "http://emberjs.com/deprecations/v1.x/#toc_computed-properties-with-a-shared-getter-and-setter" + }); + this._setter = config; + } + } else { + this._getter = config.get; + this._setter = config.set; + if (this._setter && this._setter.__ember_arity === undefined) { + this._setter.__ember_arity = this._setter.length; + } + } + + this._dependentKeys = undefined; + this._suspended = undefined; + this._meta = undefined; + + Ember.deprecate("Passing opts.cacheable to the CP constructor is deprecated. Invoke `volatile()` on the CP instead.", !opts || !opts.hasOwnProperty("cacheable")); + this._cacheable = opts && opts.cacheable !== undefined ? opts.cacheable : true; // TODO: Set always to `true` once this deprecation is gone. + this._dependentKeys = opts && opts.dependentKeys; + Ember.deprecate("Passing opts.readOnly to the CP constructor is deprecated. All CPs are writable by default. You can invoke `readOnly()` on the CP to change this.", !opts || !opts.hasOwnProperty("readOnly")); + this._readOnly = opts && (opts.readOnly !== undefined || !!opts.readOnly) || false; // TODO: Set always to `false` once this deprecation is gone. + } + + ComputedProperty.prototype = new properties.Descriptor(); + + var ComputedPropertyPrototype = ComputedProperty.prototype; + + /** + Properties are cacheable by default. Computed property will automatically + cache the return value of your function until one of the dependent keys changes. + + Call `volatile()` to set it into non-cached mode. When in this mode + the computed property will not automatically cache the return value. + + However, if a property is properly observable, there is no reason to disable + caching. + + @method cacheable + @param {Boolean} aFlag optional set to `false` to disable caching + @return {Ember.ComputedProperty} this + @chainable + @deprecated All computed properties are cacheble by default. Use `volatile()` instead to opt-out to caching. + */ + ComputedPropertyPrototype.cacheable = function (aFlag) { + Ember.deprecate("ComputedProperty.cacheable() is deprecated. All computed properties are cacheable by default."); + this._cacheable = aFlag !== false; + return this; + }; + + /** + 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. + + ```javascript + var outsideService = Ember.Object.extend({ + value: function() { + return OutsideService.getValue(); + }.property().volatile() + }).create(); + ``` + + @method volatile + @return {Ember.ComputedProperty} this + @chainable + */ + ComputedPropertyPrototype["volatile"] = function () { + this._cacheable = false; + return this; + }; + + /** + 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. + + ```javascript + var Person = Ember.Object.extend({ + guid: function() { + return 'guid-guid-guid'; + }.property().readOnly() + }); + + var person = Person.create(); + + person.set('guid', 'new-guid'); // will throw an exception + ``` + + @method readOnly + @return {Ember.ComputedProperty} this + @chainable + */ + ComputedPropertyPrototype.readOnly = function (readOnly) { + Ember.deprecate("Passing arguments to ComputedProperty.readOnly() is deprecated.", arguments.length === 0); + this._readOnly = readOnly === undefined || !!readOnly; // Force to true once this deprecation is gone + Ember.assert("Computed properties that define a setter using the new syntax cannot be read-only", !(this._readOnly && this._setter && this._setter !== this._getter)); + + return this; + }; + + /** + Sets the dependent keys on this computed property. Pass any number of + arguments containing key paths that this computed property depends on. + + ```javascript + var President = Ember.Object.extend({ + fullName: computed(function() { + return this.get('firstName') + ' ' + this.get('lastName'); + + // Tell Ember that this computed property depends on firstName + // and lastName + }).property('firstName', 'lastName') + }); + + var president = President.create({ + firstName: 'Barack', + lastName: 'Obama' + }); + + president.get('fullName'); // 'Barack Obama' + ``` + + @method property + @param {String} path* zero or more property paths + @return {Ember.ComputedProperty} this + @chainable + */ + ComputedPropertyPrototype.property = function () { + var args; + + var addArg = function (property) { + args.push(property); + }; + + args = []; + for (var i = 0, l = arguments.length; i < l; i++) { + expandProperties['default'](arguments[i], addArg); + } + + this._dependentKeys = args; + return this; + }; + + /** + 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. + + You can pass a hash of these values to a computed property like this: + + ``` + person: function() { + var personId = this.get('personId'); + return App.Person.create({ id: personId }); + }.property().meta({ type: App.Person }) + ``` + + 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. + + @method meta + @param {Hash} meta + @chainable + */ + + ComputedPropertyPrototype.meta = function (meta) { + if (arguments.length === 0) { + return this._meta || {}; + } else { + this._meta = meta; + return this; + } + }; + + /* 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 && meta.cache[keyName] !== undefined) { + meta.cache[keyName] = undefined; + dependent_keys.removeDependentKeys(this, obj, keyName, meta); + } + } + }; + + function finishChains(chainNodes) { + for (var i = 0, l = chainNodes.length; i < l; i++) { + chainNodes[i].didChange(null); + } + } + + /** + Access the value of the function backing the computed property. + If this property has already been cached, return the cached result. + Otherwise, call the function passing the property name as an argument. + + ```javascript + var Person = Ember.Object.extend({ + fullName: function(keyName) { + // the keyName parameter is 'fullName' in this case. + return this.get('firstName') + ' ' + this.get('lastName'); + }.property('firstName', 'lastName') + }); + + + var tom = Person.create({ + firstName: 'Tom', + lastName: 'Dale' + }); + + tom.get('fullName') // 'Tom Dale' + ``` + + @method get + @param {String} keyName The key being accessed. + @return {Object} The return value of the function backing the CP. + */ + ComputedPropertyPrototype.get = function (obj, keyName) { + var ret, cache, meta, chainNodes; + if (this._cacheable) { + meta = metaFor(obj); + cache = meta.cache; + + var result = cache && cache[keyName]; + + if (result === UNDEFINED) { + return undefined; + } else if (result !== undefined) { + return result; + } + + ret = this._getter.call(obj, keyName); + cache = meta.cache; + if (!cache) { + cache = meta.cache = {}; + } + if (ret === undefined) { + cache[keyName] = UNDEFINED; + } else { + cache[keyName] = ret; + } + + chainNodes = meta.chainWatchers && meta.chainWatchers[keyName]; + if (chainNodes) { + finishChains(chainNodes); + } + dependent_keys.addDependentKeys(this, obj, keyName, meta); + } else { + ret = this._getter.call(obj, keyName); + } + return ret; + }; + + /** + 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. + + Generally speaking if you intend for your computed property to be set + your backing function should accept either two or three arguments. + + ```javascript + var Person = Ember.Object.extend({ + // these will be supplied by `create` + firstName: null, + lastName: null, + + fullName: function(key, value, oldValue) { + // getter + if (arguments.length === 1) { + var firstName = this.get('firstName'); + var lastName = this.get('lastName'); + + return firstName + ' ' + lastName; + + // setter + } else { + var name = value.split(' '); + + this.set('firstName', name[0]); + this.set('lastName', name[1]); + + return value; + } + }.property('firstName', 'lastName') + }); + + var person = Person.create(); + + person.set('fullName', 'Peter Wagenet'); + person.get('firstName'); // 'Peter' + person.get('lastName'); // 'Wagenet' + ``` + + @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 computedPropertySetWithSuspend(obj, keyName, value) { + var oldSuspended = this._suspended; + + this._suspended = obj; + + try { + this._set(obj, keyName, value); + } finally { + this._suspended = oldSuspended; + } + }; + + ComputedPropertyPrototype._set = function computedPropertySet(obj, keyName, value) { + var cacheable = this._cacheable; + var setter = this._setter; + var meta = metaFor(obj, cacheable); + var cache = meta.cache; + var hadCachedValue = false; + + var cachedValue, ret; + + if (this._readOnly) { + throw new EmberError['default']("Cannot set read-only property \"" + keyName + "\" on object: " + utils.inspect(obj)); + } + + if (cacheable && cache && cache[keyName] !== undefined) { + if (cache[keyName] !== UNDEFINED) { + cachedValue = cache[keyName]; + } + + hadCachedValue = true; + } + + if (!setter) { + properties.defineProperty(obj, keyName, null, cachedValue); + property_set.set(obj, keyName, value); + return; + } else if (setter.__ember_arity === 2) { + // Is there any way of deprecate this in a sensitive way? + // Maybe now that getters and setters are the prefered options we can.... + ret = setter.call(obj, keyName, value); + } else { + ret = setter.call(obj, keyName, value, cachedValue); + } + + if (hadCachedValue && cachedValue === ret) { + return; + } + + var watched = meta.watching[keyName]; + if (watched) { + property_events.propertyWillChange(obj, keyName); + } + + if (hadCachedValue) { + cache[keyName] = undefined; + } + + if (cacheable) { + if (!hadCachedValue) { + dependent_keys.addDependentKeys(this, obj, keyName, meta); + } + if (!cache) { + cache = meta.cache = {}; + } + if (ret === undefined) { + cache[keyName] = UNDEFINED; + } else { + cache[keyName] = ret; + } + } + + if (watched) { + property_events.propertyDidChange(obj, keyName); + } + + return ret; + }; + + /* called before property is overridden */ + ComputedPropertyPrototype.teardown = function (obj, keyName) { + var meta = metaFor(obj); + + if (meta.cache) { + if (keyName in meta.cache) { + dependent_keys.removeDependentKeys(this, obj, keyName, meta); + } + + if (this._cacheable) { + delete meta.cache[keyName]; + } + } + + return null; // no value to restore + }; + + /** + 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()`. + + 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. + + A computed property defined in this way might look like this: + + ```js + var Person = Ember.Object.extend({ + firstName: 'Betty', + lastName: 'Jones', + + fullName: Ember.computed('firstName', 'lastName', function(key, value) { + return this.get('firstName') + ' ' + this.get('lastName'); + }) + }); + + var client = Person.create(); + + client.get('fullName'); // 'Betty Jones' + + client.set('lastName', 'Fuller'); + client.get('fullName'); // 'Betty Fuller' + ``` + + _Note: This is the preferred way to define computed properties when writing third-party + libraries that depend on or use Ember, since there is no guarantee that the user + will have prototype extensions enabled._ + + You might use this method if you disabled + [Prototype Extensions](http://emberjs.com/guides/configuring-ember/disabling-prototype-extensions/). + The alternative syntax might look like this + (if prototype extensions are enabled, which is the default behavior): + + ```js + fullName: function () { + return this.get('firstName') + ' ' + this.get('lastName'); + }.property('firstName', 'lastName') + ``` + + @class computed + @namespace Ember + @constructor + @static + @param {String} [dependentKeys*] Optional dependent keys that trigger this computed property. + @param {Function} func The computed property function. + @return {Ember.ComputedProperty} property descriptor instance + */ + function computed(func) { + var args; + + if (arguments.length > 1) { + args = [].slice.call(arguments); + func = args.pop(); + } + + var cp = new ComputedProperty(func); + // jscs:disable + + if (args) { + cp.property.apply(cp, args); + } + + return cp; + } + + /** + 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. + + @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["__ember_meta__"]; + var cache = meta && meta.cache; + var ret = cache && cache[key]; + + if (ret === UNDEFINED) { + return undefined; + } + return ret; + } + + cacheFor.set = function (cache, key, value) { + if (value === undefined) { + cache[key] = UNDEFINED; + } else { + cache[key] = value; + } + }; + + cacheFor.get = function (cache, key) { + var ret = cache[key]; + if (ret === UNDEFINED) { + return undefined; + } + return ret; + }; + + cacheFor.remove = function (cache, key) { + cache[key] = undefined; + }; + +}); +enifed('ember-metal/computed_macros', ['exports', 'ember-metal/core', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-metal/computed', 'ember-metal/is_empty', 'ember-metal/is_none', 'ember-metal/alias'], function (exports, Ember, property_get, property_set, computed, isEmpty, isNone, alias) { + + 'use strict'; + + exports.empty = empty; + exports.notEmpty = notEmpty; + exports.none = none; + exports.not = not; + exports.bool = bool; + exports.match = match; + exports.equal = equal; + exports.gt = gt; + exports.gte = gte; + exports.lt = lt; + exports.lte = lte; + exports.oneWay = oneWay; + exports.readOnly = readOnly; + exports.defaultTo = defaultTo; + exports.deprecatingAlias = deprecatingAlias; + + function getProperties(self, propertyNames) { + var ret = {}; + for (var i = 0; i < propertyNames.length; i++) { + ret[propertyNames[i]] = property_get.get(self, propertyNames[i]); + } + return ret; + } + + function generateComputedWithProperties(macro) { + return function () { + for (var _len = arguments.length, properties = Array(_len), _key = 0; _key < _len; _key++) { + properties[_key] = arguments[_key]; + } + + var computedFunc = computed.computed(function () { + return macro.apply(this, [getProperties(this, properties)]); + }); + + return computedFunc.property.apply(computedFunc, properties); + }; + } + + /** + A computed property that returns true if the value of the dependent + property is null, an empty string, empty array, or empty function. + + Example + + ```javascript + var ToDoList = Ember.Object.extend({ + isDone: Ember.computed.empty('todos') + }); + + var todoList = ToDoList.create({ + todos: ['Unit Test', 'Documentation', 'Release'] + }); + + todoList.get('isDone'); // false + todoList.get('todos').clear(); + todoList.get('isDone'); // true + ``` + + @since 1.6.0 + @method empty + @for Ember.computed + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which negate + the original value for property + */ + function empty(dependentKey) { + return computed.computed(dependentKey + ".length", function () { + return isEmpty['default'](property_get.get(this, dependentKey)); + }); + } + + /** + A computed property that returns true if the value of the dependent + property is NOT null, an empty string, empty array, or empty function. + + Example + + ```javascript + var Hamster = Ember.Object.extend({ + hasStuff: Ember.computed.notEmpty('backpack') + }); + + var hamster = Hamster.create({ backpack: ['Food', 'Sleeping Bag', 'Tent'] }); + + hamster.get('hasStuff'); // true + hamster.get('backpack').clear(); // [] + hamster.get('hasStuff'); // false + ``` + + @method notEmpty + @for Ember.computed + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which returns true if + original value for property is not empty. + */ + function notEmpty(dependentKey) { + return computed.computed(dependentKey + ".length", function () { + return !isEmpty['default'](property_get.get(this, dependentKey)); + }); + } + + /** + 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. + + Example + + ```javascript + var Hamster = Ember.Object.extend({ + isHungry: Ember.computed.none('food') + }); + + var hamster = Hamster.create(); + + hamster.get('isHungry'); // true + hamster.set('food', 'Banana'); + hamster.get('isHungry'); // false + hamster.set('food', null); + hamster.get('isHungry'); // true + ``` + + @method none + @for Ember.computed + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which + returns true if original value for property is null or undefined. + */ + function none(dependentKey) { + return computed.computed(dependentKey, function () { + return isNone['default'](property_get.get(this, dependentKey)); + }); + } + + /** + A computed property that returns the inverse boolean value + of the original value for the dependent property. + + Example + + ```javascript + var User = Ember.Object.extend({ + isAnonymous: Ember.computed.not('loggedIn') + }); + + var user = User.create({loggedIn: false}); + + user.get('isAnonymous'); // true + user.set('loggedIn', true); + user.get('isAnonymous'); // false + ``` + + @method not + @for Ember.computed + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which returns + inverse of the original value for property + */ + function not(dependentKey) { + return computed.computed(dependentKey, function () { + return !property_get.get(this, dependentKey); + }); + } + + /** + A computed property that converts the provided dependent property + into a boolean value. + + ```javascript + var Hamster = Ember.Object.extend({ + hasBananas: Ember.computed.bool('numBananas') + }); + + var hamster = Hamster.create(); + + 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 + ``` + + @method bool + @for Ember.computed + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which converts + to boolean the original value for property + */ + function bool(dependentKey) { + return computed.computed(dependentKey, function () { + return !!property_get.get(this, dependentKey); + }); + } + + /** + 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. + + Example + + ```javascript + var User = Ember.Object.extend({ + hasValidEmail: Ember.computed.match('email', /^.+@.+\..+$/) + }); + + var user = User.create({loggedIn: false}); + + user.get('hasValidEmail'); // false + user.set('email', ''); + user.get('hasValidEmail'); // false + user.set('email', 'ember_hamster@example.com'); + user.get('hasValidEmail'); // true + ``` + + @method match + @for Ember.computed + @param {String} dependentKey + @param {RegExp} regexp + @return {Ember.ComputedProperty} computed property which match + the original value for property against a given RegExp + */ + function match(dependentKey, regexp) { + return computed.computed(dependentKey, function () { + var value = property_get.get(this, dependentKey); + + return typeof value === "string" ? regexp.test(value) : false; + }); + } + + /** + A computed property that returns true if the provided dependent property + is equal to the given value. + + Example + + ```javascript + var Hamster = Ember.Object.extend({ + napTime: Ember.computed.equal('state', 'sleepy') + }); + + var hamster = Hamster.create(); + + hamster.get('napTime'); // false + hamster.set('state', 'sleepy'); + hamster.get('napTime'); // true + hamster.set('state', 'hungry'); + hamster.get('napTime'); // false + ``` + + @method equal + @for Ember.computed + @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. + */ + function equal(dependentKey, value) { + return computed.computed(dependentKey, function () { + return property_get.get(this, dependentKey) === value; + }); + } + + /** + A computed property that returns true if the provided dependent property + is greater than the provided value. + + Example + + ```javascript + var Hamster = Ember.Object.extend({ + hasTooManyBananas: Ember.computed.gt('numBananas', 10) + }); + + var hamster = Hamster.create(); + + hamster.get('hasTooManyBananas'); // false + hamster.set('numBananas', 3); + hamster.get('hasTooManyBananas'); // false + hamster.set('numBananas', 11); + hamster.get('hasTooManyBananas'); // true + ``` + + @method gt + @for Ember.computed + @param {String} dependentKey + @param {Number} value + @return {Ember.ComputedProperty} computed property which returns true if + the original value for property is greater than given value. + */ + function gt(dependentKey, value) { + return computed.computed(dependentKey, function () { + return property_get.get(this, dependentKey) > value; + }); + } + + /** + A computed property that returns true if the provided dependent property + is greater than or equal to the provided value. + + Example + + ```javascript + var Hamster = Ember.Object.extend({ + hasTooManyBananas: Ember.computed.gte('numBananas', 10) + }); + + var hamster = Hamster.create(); + + hamster.get('hasTooManyBananas'); // false + hamster.set('numBananas', 3); + hamster.get('hasTooManyBananas'); // false + hamster.set('numBananas', 10); + hamster.get('hasTooManyBananas'); // true + ``` + + @method gte + @for Ember.computed + @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. + */ + function gte(dependentKey, value) { + return computed.computed(dependentKey, function () { + return property_get.get(this, dependentKey) >= value; + }); + } + + /** + A computed property that returns true if the provided dependent property + is less than the provided value. + + Example + + ```javascript + var Hamster = Ember.Object.extend({ + needsMoreBananas: Ember.computed.lt('numBananas', 3) + }); + + var hamster = Hamster.create(); + + hamster.get('needsMoreBananas'); // true + hamster.set('numBananas', 3); + hamster.get('needsMoreBananas'); // false + hamster.set('numBananas', 2); + hamster.get('needsMoreBananas'); // true + ``` + + @method lt + @for Ember.computed + @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. + */ + function lt(dependentKey, value) { + return computed.computed(dependentKey, function () { + return property_get.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) + }); + + var hamster = Hamster.create(); + + hamster.get('needsMoreBananas'); // true + hamster.set('numBananas', 5); + hamster.get('needsMoreBananas'); // false + hamster.set('numBananas', 3); + hamster.get('needsMoreBananas'); // true + ``` + + @method lte + @for Ember.computed + @param {String} dependentKey + @param {Number} value + @return {Ember.ComputedProperty} computed property which returns true if + the original value for property is less or equal than given value. + */ + function lte(dependentKey, value) { + return computed.computed(dependentKey, function () { + return property_get.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') + }); + + var hamster = Hamster.create(); + + hamster.get('readyForCamp'); // false + hamster.set('hasTent', true); + hamster.get('readyForCamp'); // false + hamster.set('hasBackpack', true); + hamster.get('readyForCamp'); // true + hamster.set('hasBackpack', 'Yes'); + hamster.get('readyForCamp'); // 'Yes' + ``` + + @method and + @for Ember.computed + @param {String} dependentKey* + @return {Ember.ComputedProperty} computed property which performs + a logical `and` on the values of all the original values for properties. + */ + var and = generateComputedWithProperties(function (properties) { + var value; + for (var key in properties) { + value = properties[key]; + if (properties.hasOwnProperty(key) && !value) { + return false; + } + } + return value; + }); + + var or = generateComputedWithProperties(function (properties) { + for (var key in properties) { + if (properties.hasOwnProperty(key) && properties[key]) { + return properties[key]; + } + } + return false; + }); + + var any = generateComputedWithProperties(function (properties) { + for (var key in properties) { + if (properties.hasOwnProperty(key) && properties[key]) { + return properties[key]; + } + } + return null; + }); + + var collect = generateComputedWithProperties(function (properties) { + var res = Ember['default'].A(); + for (var key in properties) { + if (properties.hasOwnProperty(key)) { + if (isNone['default'](properties[key])) { + res.push(null); + } else { + res.push(properties[key]); + } + } + } + return res; + }); + + function oneWay(dependentKey) { + return alias['default'](dependentKey).oneWay(); + } + + /** + This is a more semantically meaningful alias of `computed.oneWay`, + whose name is somewhat ambiguous as to which direction the data flows. + + @method reads + @for Ember.computed + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which creates a + one way computed property to the original value for property. + */ + + /** + 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 propagate back up, as they will replace the value. + + This prevents the reverse flow, and also throws an exception when it occurs. + + Example + + ```javascript + var User = Ember.Object.extend({ + firstName: null, + lastName: null, + nickName: Ember.computed.readOnly('firstName') + }); + + var teddy = User.create({ + firstName: 'Teddy', + lastName: 'Zeenny' + }); + + teddy.get('nickName'); // 'Teddy' + teddy.set('nickName', 'TeddyBear'); // throws Exception + // throw new Ember.Error('Cannot Set: nickName on: ' );` + teddy.get('firstName'); // 'Teddy' + ``` + + @method readOnly + @for Ember.computed + @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 + */ + function readOnly(dependentKey) { + return alias['default'](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 + + Example + + ```javascript + var Hamster = Ember.Object.extend({ + wishList: Ember.computed.defaultTo('favoriteFood') + }); + + var hamster = Hamster.create({ favoriteFood: 'Banana' }); + + hamster.get('wishList'); // 'Banana' + hamster.set('wishList', 'More Unit Tests'); + hamster.get('wishList'); // 'More Unit Tests' + hamster.get('favoriteFood'); // 'Banana' + ``` + + @method defaultTo + @for Ember.computed + @param {String} defaultPath + @return {Ember.ComputedProperty} computed property which acts like + a standard getter and setter, but defaults to the value from `defaultPath`. + @deprecated Use `Ember.computed.oneWay` or custom CP with default instead. + */ + function defaultTo(defaultPath) { + return computed.computed({ + get: function (key) { + Ember['default'].deprecate("Usage of Ember.computed.defaultTo is deprecated, use `Ember.computed.oneWay` instead."); + return property_get.get(this, defaultPath); + }, + + set: function (key, newValue, cachedValue) { + Ember['default'].deprecate("Usage of Ember.computed.defaultTo is deprecated, use `Ember.computed.oneWay` instead."); + return newValue != null ? newValue : property_get.get(this, defaultPath); + } + }); + } + + /** + 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, but also + print a deprecation warning. + + @method deprecatingAlias + @for Ember.computed + @param {String} dependentKey + @return {Ember.ComputedProperty} computed property which creates an + alias with a deprecation to the original value for property. + @since 1.7.0 + */ + function deprecatingAlias(dependentKey) { + return computed.computed(dependentKey, { + get: function (key) { + Ember['default'].deprecate("Usage of `" + key + "` is deprecated, use `" + dependentKey + "` instead."); + return property_get.get(this, dependentKey); + }, + set: function (key, value) { + Ember['default'].deprecate("Usage of `" + key + "` is deprecated, use `" + dependentKey + "` instead."); + property_set.set(this, dependentKey, value); + return value; + } + }); + } + + exports.and = and; + exports.or = or; + exports.any = any; + exports.collect = collect; + +}); enifed('ember-metal/core', ['exports'], function (exports) { 'use strict'; @@ -133,7 +2957,7 @@ enifed('ember-metal/core', ['exports'], function (exports) { @class Ember @static - @version 1.11.3 + @version 1.12.1 */ if ('undefined' === typeof Ember) { @@ -145,8 +2969,8 @@ enifed('ember-metal/core', ['exports'], function (exports) { // Default imports, exports and lookup to the global object; var global = mainContext || {}; // jshint ignore:line Ember.imports = Ember.imports || global; - Ember.lookup = Ember.lookup || global; - var emExports = Ember.exports = Ember.exports || global; + Ember.lookup = Ember.lookup || global; + var emExports = Ember.exports = Ember.exports || global; // aliases needed to keep minifiers from removing the global context emExports.Em = emExports.Ember = Ember; @@ -155,16 +2979,17 @@ enifed('ember-metal/core', ['exports'], function (exports) { Ember.isNamespace = true; - Ember.toString = function() { return "Ember"; }; - + Ember.toString = function () { + return 'Ember'; + }; /** @property VERSION @type String - @default '1.11.3' + @default '1.12.1' @static */ - Ember.VERSION = '1.11.3'; + Ember.VERSION = '1.12.1'; /** Standard environmental variables. You can define these in a global `EmberENV` @@ -205,11 +3030,14 @@ enifed('ember-metal/core', ['exports'], function (exports) { @static @since 1.1.0 */ + Ember.FEATURES = { 'features-stripped-test': false, 'ember-routing-named-substates': true, 'mandatory-setter': true, 'ember-htmlbars-component-generation': false, 'ember-htmlbars-component-helper': true, 'ember-htmlbars-inline-if-helper': true, 'ember-htmlbars-attribute-syntax': true, 'ember-routing-transitioning-classes': true, 'new-computed-syntax': true, 'ember-testing-checkbox-helpers': false, 'ember-metal-stream': false, 'ember-application-instance-initializers': true, 'ember-application-initializer-context': true, 'ember-router-willtransition': true, 'ember-application-visit': false, 'ember-views-component-block-info': false, 'ember-routing-core-outlet': false, 'ember-libraries-isregistered': false }; //jshint ignore:line - Ember.FEATURES = Ember.ENV.FEATURES; - - if (!Ember.FEATURES) { - Ember.FEATURES = {"features-stripped-test":false,"ember-routing-named-substates":true,"mandatory-setter":true,"ember-htmlbars-component-generation":false,"ember-htmlbars-component-helper":true,"ember-htmlbars-inline-if-helper":true,"ember-htmlbars-attribute-syntax":true,"ember-routing-transitioning-classes":true,"new-computed-syntax":false,"ember-testing-checkbox-helpers":false,"ember-metal-stream":false,"ember-htmlbars-each-with-index":true,"ember-application-instance-initializers":false,"ember-application-initializer-context":false,"ember-router-willtransition":true,"ember-application-visit":false}; //jshint ignore:line + if (Ember.ENV.FEATURES) { + for (var feature in Ember.ENV.FEATURES) { + if (Ember.ENV.FEATURES.hasOwnProperty(feature)) { + Ember.FEATURES[feature] = Ember.ENV.FEATURES[feature]; + } + } } /** @@ -229,7 +3057,7 @@ enifed('ember-metal/core', ['exports'], function (exports) { @since 1.1.0 */ - Ember.FEATURES.isEnabled = function(feature) { + Ember.FEATURES.isEnabled = function (feature) { var featureValue = Ember.FEATURES[feature]; if (Ember.ENV.ENABLE_ALL_FEATURES) { @@ -275,7 +3103,7 @@ enifed('ember-metal/core', ['exports'], function (exports) { @type Boolean @default true */ - Ember.LOG_STACKTRACE_ON_DEPRECATION = (Ember.ENV.LOG_STACKTRACE_ON_DEPRECATION !== false); + Ember.LOG_STACKTRACE_ON_DEPRECATION = Ember.ENV.LOG_STACKTRACE_ON_DEPRECATION !== false; /** Determines whether Ember should add ECMAScript 5 Array shims to older browsers. @@ -284,7 +3112,7 @@ enifed('ember-metal/core', ['exports'], function (exports) { @type Boolean @default Ember.EXTEND_PROTOTYPES */ - Ember.SHIM_ES5 = (Ember.ENV.SHIM_ES5 === false) ? false : Ember.EXTEND_PROTOTYPES; + Ember.SHIM_ES5 = Ember.ENV.SHIM_ES5 === false ? false : Ember.EXTEND_PROTOTYPES; /** Determines whether Ember logs info about version of used libraries @@ -293,7 +3121,7 @@ enifed('ember-metal/core', ['exports'], function (exports) { @type Boolean @default true */ - Ember.LOG_VERSION = (Ember.ENV.LOG_VERSION === false) ? false : true; + Ember.LOG_VERSION = Ember.ENV.LOG_VERSION === false ? false : true; /** Empty function. Useful for some operations. Always returns `this`. @@ -302,36 +3130,6948 @@ enifed('ember-metal/core', ['exports'], function (exports) { @private @return {Object} */ - function K() { return this; } + function K() { + return this; + } Ember.K = K; //TODO: ES6 GLOBAL TODO // Stub out the methods defined by the ember-debug package in case it's not loaded - if ('undefined' === typeof Ember.assert) { Ember.assert = K; } - if ('undefined' === typeof Ember.warn) { Ember.warn = K; } - if ('undefined' === typeof Ember.debug) { Ember.debug = K; } - if ('undefined' === typeof Ember.runInDebug) { Ember.runInDebug = K; } - if ('undefined' === typeof Ember.deprecate) { Ember.deprecate = K; } + if ('undefined' === typeof Ember.assert) { + Ember.assert = K; + } + if ('undefined' === typeof Ember.warn) { + Ember.warn = K; + } + if ('undefined' === typeof Ember.debug) { + Ember.debug = K; + } + if ('undefined' === typeof Ember.runInDebug) { + Ember.runInDebug = K; + } + if ('undefined' === typeof Ember.deprecate) { + Ember.deprecate = K; + } if ('undefined' === typeof Ember.deprecateFunc) { - Ember.deprecateFunc = function(_, func) { return func; }; + Ember.deprecateFunc = function (_, func) { + return func; + }; } exports['default'] = Ember; }); -enifed('ember-template-compiler', ['exports', 'ember-metal/core', 'ember-template-compiler/system/precompile', 'ember-template-compiler/system/compile', 'ember-template-compiler/system/template', 'ember-template-compiler/plugins', 'ember-template-compiler/plugins/transform-each-in-to-hash', 'ember-template-compiler/plugins/transform-with-as-to-hash', 'ember-template-compiler/compat'], function (exports, _Ember, precompile, compile, template, plugins, TransformEachInToHash, TransformWithAsToHash) { +enifed('ember-metal/dependent_keys', ['exports', 'ember-metal/platform/create', 'ember-metal/watching'], function (exports, o_create, watching) { + + + exports.addDependentKeys = addDependentKeys; + exports.removeDependentKeys = removeDependentKeys; + + "REMOVE_USE_STRICT: true"; /** + @module ember-metal + */ + + // .......................................................... + // DEPENDENT KEYS + // + + // data structure: + // meta.deps = { + // 'depKey': { + // 'keyName': count, + // } + // } + + /* + 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['default'](keys); + } + return keys; + } + + function metaForDeps(meta) { + return keysForDep(meta, "deps"); + } + function addDependentKeys(desc, obj, keyName, meta) { + // the descriptor has a list of dependent keys, so + // add all of its dependent keys. + var depsMeta, idx, len, depKey, keys; + var depKeys = desc._dependentKeys; + if (!depKeys) { + return; + } + + depsMeta = metaForDeps(meta); + + 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 + watching.watch(obj, depKey, meta); + } + } + + function removeDependentKeys(desc, obj, keyName, meta) { + // the descriptor has a list of dependent keys, so + // remove all of its dependent keys. + var depKeys = desc._dependentKeys; + var depsMeta, idx, len, depKey, keys; + if (!depKeys) { + return; + } + + depsMeta = metaForDeps(meta); + + for (idx = 0, len = depKeys.length; idx < len; idx++) { + depKey = depKeys[idx]; + // Lookup keys meta for depKey + keys = keysForDep(depsMeta, depKey); + // Decrement the number of times depKey depends on keyName. + keys[keyName] = (keys[keyName] || 0) - 1; + // Unwatch the depKey + watching.unwatch(obj, depKey, meta); + } + } + +}); +enifed('ember-metal/deprecate_property', ['exports', 'ember-metal/core', 'ember-metal/platform/define_property', 'ember-metal/properties', 'ember-metal/property_get', 'ember-metal/property_set'], function (exports, Ember, define_property, properties, property_get, property_set) { 'use strict'; - plugins.registerPlugin('ast', TransformWithAsToHash['default']); - plugins.registerPlugin('ast', TransformEachInToHash['default']); + exports.deprecateProperty = deprecateProperty; - exports._Ember = _Ember['default']; - exports.precompile = precompile['default']; - exports.compile = compile['default']; - exports.template = template['default']; - exports.registerPlugin = plugins.registerPlugin; + function deprecateProperty(object, deprecatedKey, newKey) { + function deprecate() { + Ember['default'].deprecate("Usage of `" + deprecatedKey + "` is deprecated, use `" + newKey + "` instead."); + } + + if (define_property.hasPropertyAccessors) { + properties.defineProperty(object, deprecatedKey, { + configurable: true, + enumerable: false, + set: function (value) { + deprecate(); + property_set.set(this, newKey, value); + }, + get: function () { + deprecate(); + return property_get.get(this, newKey); + } + }); + } + } + +}); +enifed('ember-metal/dictionary', ['exports', 'ember-metal/platform/create'], function (exports, create) { + + 'use strict'; + + + exports['default'] = makeDictionary; + function makeDictionary(parent) { + var dict = create['default'](parent); + dict['_dict'] = null; + delete dict['_dict']; + return dict; + } + +}); +enifed('ember-metal/enumerable_utils', ['exports', 'ember-metal/array'], function (exports, ember_metal__array) { + + 'use strict'; + + exports.map = map; + exports.forEach = forEach; + exports.filter = filter; + exports.indexOf = indexOf; + exports.indexesOf = indexesOf; + exports.addObject = addObject; + exports.removeObject = removeObject; + exports._replace = _replace; + exports.replace = replace; + exports.intersection = intersection; + + var splice = Array.prototype.splice; + + /** + * Defines some convenience methods for working with Enumerables. + * `Ember.EnumerableUtils` uses `Ember.ArrayPolyfills` when necessary. + * + * @class EnumerableUtils + * @namespace Ember + * @static + * */ + + /** + * 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. + */ + function map(obj, callback, thisArg) { + return obj.map ? obj.map(callback, thisArg) : ember_metal__array.map.call(obj, callback, thisArg); + } + + /** + * Calls the forEach function on the passed object with a specified callback. This + * uses `Ember.ArrayPolyfill`'s-forEach method when necessary. + * + * @method forEach + * @param {Object} obj The object to call forEach on + * @param {Function} callback The callback to execute + * @param {Object} thisArg Value to use as this when executing *callback* + * + */ + function forEach(obj, callback, thisArg) { + return obj.forEach ? obj.forEach(callback, thisArg) : ember_metal__array.forEach.call(obj, callback, thisArg); + } + + /** + * Calls the filter function on the passed object with a specified callback. This + * uses `Ember.ArrayPolyfill`'s-filter method when necessary. + * + * @method filter + * @param {Object} obj The object to call filter on + * @param {Function} callback The callback to execute + * @param {Object} thisArg Value to use as this when executing *callback* + * + * @return {Array} An array containing the filtered values + * @since 1.4.0 + */ + function filter(obj, callback, thisArg) { + return obj.filter ? obj.filter(callback, thisArg) : ember_metal__array.filter.call(obj, callback, thisArg); + } + + /** + * Calls the indexOf function on the passed object with a specified callback. This + * uses `Ember.ArrayPolyfill`'s-indexOf method when necessary. + * + * @method indexOf + * @param {Object} obj The object to call indexOn on + * @param {Function} callback The callback to execute + * @param {Object} index The index to start searching from + * + */ + function indexOf(obj, element, index) { + return obj.indexOf ? obj.indexOf(element, index) : ember_metal__array.indexOf.call(obj, element, index); + } + + /** + * Returns an array of indexes of the first occurrences of the passed elements + * on the passed object. + * + * ```javascript + * var array = [1, 2, 3, 4, 5]; + * Ember.EnumerableUtils.indexesOf(array, [2, 5]); // [1, 4] + * + * var fubar = "Fubarr"; + * Ember.EnumerableUtils.indexesOf(fubar, ['b', 'r']); // [2, 4] + * ``` + * + * @method indexesOf + * @param {Object} obj The object to check for element indexes + * @param {Array} elements The elements to search for on *obj* + * + * @return {Array} An array of indexes. + * + */ + function indexesOf(obj, elements) { + return elements === undefined ? [] : map(elements, function (item) { + return indexOf(obj, item); + }); + } + + /** + * Adds an object to an array. If the array already includes the object this + * method has no effect. + * + * @method addObject + * @param {Array} array The array the passed item should be added to + * @param {Object} item The item to add to the passed array + * + * @return 'undefined' + */ + function addObject(array, item) { + var index = indexOf(array, item); + if (index === -1) { + array.push(item); + } + } + + /** + * Removes an object from an array. If the array does not contain the passed + * object this method has no effect. + * + * @method removeObject + * @param {Array} array The array to remove the item from. + * @param {Object} item The item to remove from the passed array. + * + * @return 'undefined' + */ + function removeObject(array, item) { + var index = indexOf(array, item); + if (index !== -1) { + array.splice(index, 1); + } + } + + function _replace(array, idx, amt, objects) { + var args = [].concat(objects); + var ret = []; + // https://code.google.com/p/chromium/issues/detail?id=56588 + var size = 60000; + var start = idx; + var ends = amt; + var count, chunk; + + while (args.length) { + count = ends > size ? size : ends; + if (count <= 0) { + count = 0; + } + + chunk = args.splice(0, size); + chunk = [start, count].concat(chunk); + + start += size; + ends -= count; + + ret = ret.concat(splice.apply(array, chunk)); + } + return ret; + } + + /** + * Replaces objects in an array with the passed objects. + * + * ```javascript + * var array = [1,2,3]; + * Ember.EnumerableUtils.replace(array, 1, 2, [4, 5]); // [1, 4, 5] + * + * var array = [1,2,3]; + * Ember.EnumerableUtils.replace(array, 1, 1, [4, 5]); // [1, 4, 5, 3] + * + * var array = [1,2,3]; + * Ember.EnumerableUtils.replace(array, 10, 1, [4, 5]); // [1, 2, 3, 4, 5] + * ``` + * + * @method replace + * @param {Array} array The array the objects should be inserted into. + * @param {Number} idx Starting index in the array to replace. If *idx* >= + * length, then append to the end of the array. + * @param {Number} amt Number of elements that should be 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. + */ + function replace(array, idx, amt, objects) { + if (array.replace) { + return array.replace(idx, amt, objects); + } else { + return _replace(array, idx, amt, objects); + } + } + + /** + * Calculates the intersection of two arrays. This method returns a new array + * filled with the records that the two passed arrays share with each other. + * If there is no intersection, an empty array will be returned. + * + * ```javascript + * var array1 = [1, 2, 3, 4, 5]; + * var array2 = [1, 3, 5, 6, 7]; + * + * Ember.EnumerableUtils.intersection(array1, array2); // [1, 3, 5] + * + * var array1 = [1, 2, 3]; + * var array2 = [4, 5, 6]; + * + * Ember.EnumerableUtils.intersection(array1, array2); // [] + * ``` + * + * @method intersection + * @param {Array} array1 The first array + * @param {Array} array2 The second array + * + * @return {Array} The intersection of the two passed arrays. + */ + function intersection(array1, array2) { + var result = []; + forEach(array1, function (element) { + if (indexOf(array2, element) >= 0) { + result.push(element); + } + }); + + return result; + } + + // TODO: this only exists to maintain the existing api, as we move forward it + // should only be part of the "global build" via some shim + exports['default'] = { + _replace: _replace, + addObject: addObject, + filter: filter, + forEach: forEach, + indexOf: indexOf, + indexesOf: indexesOf, + intersection: intersection, + map: map, + removeObject: removeObject, + replace: replace + }; + +}); +enifed('ember-metal/environment', ['exports', 'ember-metal/core'], function (exports, Ember) { + + 'use strict'; + + var environment; + + // This code attempts to automatically detect an environment with DOM + // by searching for window and document.createElement. An environment + // with DOM may disable the DOM functionality of Ember explicitly by + // defining a `disableBrowserEnvironment` ENV. + var hasDOM = typeof window !== 'undefined' && typeof document !== 'undefined' && typeof document.createElement !== 'undefined' && !Ember['default'].ENV.disableBrowserEnvironment; + + if (hasDOM) { + environment = { + hasDOM: true, + isChrome: !!window.chrome && !window.opera, + location: window.location, + history: window.history, + userAgent: window.navigator.userAgent, + global: window + }; + } else { + environment = { + hasDOM: false, + isChrome: false, + location: null, + history: null, + userAgent: 'Lynx (textmode)', + global: null + }; + } + + exports['default'] = environment; + +}); +enifed('ember-metal/error', ['exports', 'ember-metal/platform/create'], function (exports, create) { + + 'use strict'; + + var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; + + /** + A subclass of the JavaScript Error object for use in Ember. + + @class Error + @namespace Ember + @extends Error + @constructor + */ + function EmberError() { + 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]]; + } + } + + EmberError.prototype = create['default'](Error.prototype); + + exports['default'] = EmberError; + +}); +enifed('ember-metal/events', ['exports', 'ember-metal/core', 'ember-metal/utils', 'ember-metal/platform/create'], function (exports, Ember, utils, create) { + + + exports.accumulateListeners = accumulateListeners; + exports.addListener = addListener; + exports.suspendListener = suspendListener; + exports.suspendListeners = suspendListeners; + exports.watchedEvents = watchedEvents; + exports.sendEvent = sendEvent; + exports.hasListeners = hasListeners; + exports.listenersFor = listenersFor; + exports.on = on; + exports.removeListener = removeListener; + + "REMOVE_USE_STRICT: true"; /* listener flags */ + var ONCE = 1; + var SUSPENDED = 2; + + /* + 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 hashes are stored in the object's meta hash, and look like this: + + // Object's meta hash + { + listeners: { // variable name: `listenerSet` + "foo:changed": [ // variable name: `actions` + target, method, flags + ] + } + } + + */ + + 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; + } + + function actionsFor(obj, eventName) { + var meta = utils.meta(obj, true); + var actions; + var listeners = meta.listeners; + + if (!listeners) { + listeners = meta.listeners = create['default'](null); + listeners.__source__ = obj; + } else if (listeners.__source__ !== obj) { + // setup inherited copy of the listeners object + listeners = meta.listeners = create['default'](listeners); + listeners.__source__ = obj; + } + + actions = listeners[eventName]; + + // if there are actions, but the eventName doesn't exist in our listeners, then copy them from the prototype + if (actions && actions.__source__ !== obj) { + actions = listeners[eventName] = listeners[eventName].slice(); + actions.__source__ = obj; + } else if (!actions) { + actions = listeners[eventName] = []; + actions.__source__ = obj; + } + + return actions; + } + function accumulateListeners(obj, eventName, otherActions) { + var meta = obj["__ember_meta__"]; + var actions = meta && meta.listeners && meta.listeners[eventName]; + + if (!actions) { + return; + } + + var newActions = []; + + for (var i = actions.length - 3; i >= 0; i -= 3) { + var target = actions[i]; + var method = actions[i + 1]; + var flags = actions[i + 2]; + var actionIndex = indexOf(otherActions, target, method); + + if (actionIndex === -1) { + otherActions.push(target, method, flags); + newActions.push(target, method, flags); + } + } + + return newActions; + } + + /** + Add an event listener + + @method addListener + @for Ember + @param obj + @param {String} eventName + @param {Object|Function} target 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['default'].assert("You must pass at least an object and event name to Ember.addListener", !!obj && !!eventName); + + if (!method && "function" === typeof target) { + method = target; + target = null; + } + + var actions = actionsFor(obj, eventName); + var actionIndex = indexOf(actions, target, method); + var flags = 0; + + if (once) { + flags |= ONCE; + } + + if (actionIndex !== -1) { + return; + } + + actions.push(target, method, flags); + + if ("function" === typeof obj.didAddListener) { + obj.didAddListener(eventName, target, method); + } + } + + /** + Remove an event listener + + Arguments should match those passed to `Ember.addListener`. + + @method removeListener + @for Ember + @param obj + @param {String} eventName + @param {Object|Function} target 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['default'].assert("You must pass at least an object and event name to Ember.removeListener", !!obj && !!eventName); + + if (!method && "function" === typeof target) { + method = target; + target = null; + } + + function _removeListener(target, method) { + var actions = actionsFor(obj, eventName); + var actionIndex = indexOf(actions, target, method); + + // action doesn't exist, give up silently + if (actionIndex === -1) { + return; + } + + actions.splice(actionIndex, 3); + + if ("function" === typeof obj.didRemoveListener) { + obj.didRemoveListener(eventName, target, method); + } + } + + if (method) { + _removeListener(target, method); + } else { + var meta = obj["__ember_meta__"]; + var actions = meta && meta.listeners && meta.listeners[eventName]; + + if (!actions) { + return; + } + for (var i = actions.length - 3; i >= 0; i -= 3) { + _removeListener(actions[i], actions[i + 1]); + } + } + } + + /** + Suspend listener during callback. + + 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. + + @method suspendListener + @for Ember + + @private + @param obj + @param {String} eventName + @param {Object|Function} target 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; + } + + var actions = actionsFor(obj, eventName); + var actionIndex = indexOf(actions, target, method); + + if (actionIndex !== -1) { + actions[actionIndex + 2] |= SUSPENDED; // mark the action as suspended + } + + function tryable() { + return callback.call(target); + } + function finalizer() { + if (actionIndex !== -1) { + actions[actionIndex + 2] &= ~SUSPENDED; + } + } + + return utils.tryFinally(tryable, finalizer); + } + + /** + Suspends multiple listeners during a callback. + + @method suspendListeners + @for Ember + + @private + @param obj + @param {Array} eventNames Array of event names + @param {Object|Function} target 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; + } + + var suspendedActions = []; + var actionsList = []; + var eventName, actions, i, l; + + for (i = 0, l = eventNames.length; i < l; i++) { + eventName = eventNames[i]; + actions = actionsFor(obj, eventName); + var actionIndex = indexOf(actions, target, method); + + if (actionIndex !== -1) { + actions[actionIndex + 2] |= SUSPENDED; + suspendedActions.push(actionIndex); + actionsList.push(actions); + } + } + + function tryable() { + return callback.call(target); + } + + function finalizer() { + for (var i = 0, l = suspendedActions.length; i < l; i++) { + var actionIndex = suspendedActions[i]; + actionsList[i][actionIndex + 2] &= ~SUSPENDED; + } + } + + return utils.tryFinally(tryable, finalizer); + } + + /** + Return a list of currently watched events + + @private + @method watchedEvents + @for Ember + @param obj + */ + function watchedEvents(obj) { + var listeners = obj["__ember_meta__"].listeners; + var ret = []; + + if (listeners) { + for (var eventName in listeners) { + if (eventName !== "__source__" && listeners[eventName]) { + ret.push(eventName); + } + } + } + return ret; + } + + /** + Send an event. The execution of suspended listeners + is skipped, and once listeners are removed. A listener without + a target is executed on the passed object. If an array of actions + is not passed, the actions stored on the passed object are invoked. + + @method sendEvent + @for Ember + @param obj + @param {String} eventName + @param {Array} params Optional parameters for each listener. + @param {Array} actions Optional array of actions (listeners). + @return true + */ + function sendEvent(obj, eventName, params, actions) { + // first give object a chance to handle it + if (obj !== Ember['default'] && "function" === typeof obj.sendEvent) { + obj.sendEvent(eventName, params); + } + + if (!actions) { + var meta = obj["__ember_meta__"]; + actions = meta && meta.listeners && meta.listeners[eventName]; + } + + if (!actions) { + return; + } + + for (var i = actions.length - 3; i >= 0; i -= 3) { + // looping in reverse for once listeners + var target = actions[i]; + var method = actions[i + 1]; + var 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) { + utils.applyStr(target, method, params); + } else { + target[method](); + } + } else { + if (params) { + utils.apply(target, method, params); + } else { + method.call(target); + } + } + } + return true; + } + + /** + @private + @method hasListeners + @for Ember + @param obj + @param {String} eventName + */ + function hasListeners(obj, eventName) { + var meta = obj["__ember_meta__"]; + var actions = meta && meta.listeners && meta.listeners[eventName]; + + return !!(actions && actions.length); + } + + /** + @private + @method listenersFor + @for Ember + @param obj + @param {String} eventName + */ + function listenersFor(obj, eventName) { + var ret = []; + var meta = obj["__ember_meta__"]; + var actions = meta && meta.listeners && meta.listeners[eventName]; + + if (!actions) { + return ret; + } + + for (var i = 0, l = actions.length; i < l; i += 3) { + var target = actions[i]; + var method = actions[i + 1]; + ret.push([target, method]); + } + + return ret; + } + + /** + Define a property as a function that should be executed when + a specified event or events are triggered. + + + ``` javascript + var Job = Ember.Object.extend({ + logCompleted: Ember.on('completed', function() { + console.log('Job completed!'); + }) + }); + + var job = Job.create(); + + Ember.sendEvent(job, 'completed'); // Logs 'Job completed!' + ``` + + @method on + @for Ember + @param {String} eventNames* + @param {Function} func + @return func + */ + function on() { + for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + var func = args.pop(); + var events = args; + func.__ember_listens__ = events; + return func; + } + +}); +enifed('ember-metal/expand_properties', ['exports', 'ember-metal/error', 'ember-metal/enumerable_utils', 'ember-metal/utils'], function (exports, EmberError, enumerable_utils, utils) { + + 'use strict'; + + + exports['default'] = expandProperties; + + var SPLIT_REGEX = /\{|\}/; + + /** + Expands `pattern`, invoking `callback` for each expansion. + + The only pattern supported is brace-expansion, anything else will be passed + once to `callback` directly. + + Example + + ```js + function echo(arg){ console.log(arg); } + + Ember.expandProperties('foo.bar', echo); //=> 'foo.bar' + Ember.expandProperties('{foo,bar}', echo); //=> 'foo', 'bar' + Ember.expandProperties('foo.{bar,baz}', echo); //=> 'foo.bar', 'foo.baz' + Ember.expandProperties('{foo,bar}.baz', echo); //=> 'foo.baz', 'bar.baz' + Ember.expandProperties('foo.{bar,baz}.@each', echo) //=> 'foo.bar.@each', 'foo.baz.@each' + Ember.expandProperties('{foo,bar}.{spam,eggs}', echo) //=> 'foo.spam', 'foo.eggs', 'bar.spam', 'bar.eggs' + Ember.expandProperties('{foo}.bar.{baz}') //=> 'foo.bar.baz' + ``` + + @method + @private + @param {String} pattern The property pattern to expand. + @param {Function} callback The callback to invoke. It is invoked once per + expansion, and is passed the expansion. + */ + function expandProperties(pattern, callback) { + if (pattern.indexOf(' ') > -1) { + throw new EmberError['default']('Brace expanded properties cannot contain spaces, e.g. \'user.{firstName, lastName}\' should be \'user.{firstName,lastName}\''); + } + + if ('string' === utils.typeOf(pattern)) { + var parts = pattern.split(SPLIT_REGEX); + var properties = [parts]; + + enumerable_utils.forEach(parts, function (part, index) { + if (part.indexOf(',') >= 0) { + properties = duplicateAndReplace(properties, part.split(','), index); + } + }); + + enumerable_utils.forEach(properties, function (property) { + callback(property.join('')); + }); + } else { + callback(pattern); + } + } + + function duplicateAndReplace(properties, currentParts, index) { + var all = []; + + enumerable_utils.forEach(properties, function (property) { + enumerable_utils.forEach(currentParts, function (part) { + var current = property.slice(0); + current[index] = part; + all.push(current); + }); + }); + + return all; + } + +}); +enifed('ember-metal/get_properties', ['exports', 'ember-metal/property_get', 'ember-metal/utils'], function (exports, property_get, utils) { + + 'use strict'; + + + exports['default'] = getProperties; + function getProperties(obj) { + var ret = {}; + var propertyNames = arguments; + var i = 1; + + if (arguments.length === 2 && utils.typeOf(arguments[1]) === "array") { + i = 0; + propertyNames = arguments[1]; + } + for (var len = propertyNames.length; i < len; i++) { + ret[propertyNames[i]] = property_get.get(obj, propertyNames[i]); + } + return ret; + } + +}); +enifed('ember-metal/injected_property', ['exports', 'ember-metal/core', 'ember-metal/computed', 'ember-metal/alias', 'ember-metal/properties', 'ember-metal/platform/create'], function (exports, Ember, computed, alias, properties, create) { + + 'use strict'; + + function InjectedProperty(type, name) { + this.type = type; + this.name = name; + + this._super$Constructor(injectedPropertyGet); + AliasedPropertyPrototype.oneWay.call(this); + } + + function injectedPropertyGet(keyName) { + var possibleDesc = this[keyName]; + var desc = possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor ? possibleDesc : undefined; + + Ember['default'].assert("Attempting to lookup an injected property on an object without a container, ensure that the object was instantiated via a container.", this.container); + + return this.container.lookup(desc.type + ":" + (desc.name || keyName)); + } + + InjectedProperty.prototype = create['default'](properties.Descriptor.prototype); + + var InjectedPropertyPrototype = InjectedProperty.prototype; + var ComputedPropertyPrototype = computed.ComputedProperty.prototype; + var AliasedPropertyPrototype = alias.AliasedProperty.prototype; + + InjectedPropertyPrototype._super$Constructor = computed.ComputedProperty; + + InjectedPropertyPrototype.get = ComputedPropertyPrototype.get; + InjectedPropertyPrototype.readOnly = ComputedPropertyPrototype.readOnly; + + InjectedPropertyPrototype.teardown = ComputedPropertyPrototype.teardown; + + exports['default'] = InjectedProperty; + +}); +enifed('ember-metal/instrumentation', ['exports', 'ember-metal/core', 'ember-metal/utils'], function (exports, Ember, utils) { + + 'use strict'; + + exports.instrument = instrument; + exports._instrumentStart = _instrumentStart; + exports.subscribe = subscribe; + exports.unsubscribe = unsubscribe; + exports.reset = reset; + + var subscribers = []; + var cache = {}; + + var populateListeners = function (name) { + var listeners = []; + var subscriber; + + for (var i = 0, l = subscribers.length; i < l; i++) { + subscriber = subscribers[i]; + if (subscriber.regex.test(name)) { + listeners.push(subscriber.object); + } + } + + cache[name] = listeners; + return listeners; + }; + + var time = (function () { + var perf = "undefined" !== typeof window ? window.performance || {} : {}; + var fn = perf.now || perf.mozNow || perf.webkitNow || perf.msNow || perf.oNow; + // fn.bind will be available in all the browsers that support the advanced window.performance... ;-) + return fn ? fn.bind(perf) : function () { + return +new Date(); + }; + })(); + + /** + Notifies event's subscribers, calls `before` and `after` hooks. + + @method instrument + @namespace Ember.Instrumentation + + @param {String} [name] Namespaced event name. + @param {Object} payload + @param {Function} callback Function that you're instrumenting. + @param {Object} binding Context that instrument function is called with. + */ + function instrument(name, _payload, callback, binding) { + if (arguments.length <= 3 && typeof _payload === "function") { + binding = callback; + callback = _payload; + _payload = undefined; + } + if (subscribers.length === 0) { + return callback.call(binding); + } + var payload = _payload || {}; + var finalizer = _instrumentStart(name, function () { + return payload; + }); + if (finalizer) { + var tryable = function _instrumenTryable() { + return callback.call(binding); + }; + var catchable = function _instrumentCatchable(e) { + payload.exception = e; + }; + return utils.tryCatchFinally(tryable, catchable, finalizer); + } else { + return callback.call(binding); + } + } + + // private for now + + function _instrumentStart(name, _payload) { + var listeners = cache[name]; + + if (!listeners) { + listeners = populateListeners(name); + } + + if (listeners.length === 0) { + return; + } + + var payload = _payload(); + + var STRUCTURED_PROFILE = Ember['default'].STRUCTURED_PROFILE; + var timeName; + if (STRUCTURED_PROFILE) { + timeName = name + ": " + payload.object; + console.time(timeName); + } + + var l = listeners.length; + var beforeValues = new Array(l); + var i, listener; + var timestamp = time(); + for (i = 0; i < l; i++) { + listener = listeners[i]; + beforeValues[i] = listener.before(name, timestamp, payload); + } + + return function _instrumentEnd() { + var i, l, listener; + var timestamp = time(); + for (i = 0, l = listeners.length; i < l; i++) { + listener = listeners[i]; + listener.after(name, timestamp, payload, beforeValues[i]); + } + + if (STRUCTURED_PROFILE) { + console.timeEnd(timeName); + } + }; + } + + /** + Subscribes to a particular event or instrumented block of code. + + @method subscribe + @namespace Ember.Instrumentation + + @param {String} [pattern] Namespaced event name. + @param {Object} [object] Before and After hooks. + + @return {Subscriber} + */ + function subscribe(pattern, object) { + var paths = pattern.split("."); + var path; + var regex = []; + + for (var i = 0, l = paths.length; i < l; i++) { + path = paths[i]; + if (path === "*") { + regex.push("[^\\.]*"); + } else { + regex.push(path); + } + } + + regex = regex.join("\\."); + regex = regex + "(\\..*)?"; + + var subscriber = { + pattern: pattern, + regex: new RegExp("^" + regex + "$"), + object: object + }; + + subscribers.push(subscriber); + cache = {}; + + return subscriber; + } + + /** + Unsubscribes from a particular event or instrumented block of code. + + @method unsubscribe + @namespace Ember.Instrumentation + + @param {Object} [subscriber] + */ + function unsubscribe(subscriber) { + var index; + + for (var i = 0, l = subscribers.length; i < l; i++) { + if (subscribers[i] === subscriber) { + index = i; + } + } + + subscribers.splice(index, 1); + cache = {}; + } + + /** + Resets `Ember.Instrumentation` by flushing list of subscribers. + + @method reset + @namespace Ember.Instrumentation + */ + function reset() { + subscribers.length = 0; + cache = {}; + } + + exports.subscribers = subscribers; + +}); +enifed('ember-metal/is_blank', ['exports', 'ember-metal/is_empty'], function (exports, isEmpty) { + + 'use strict'; + + + exports['default'] = isBlank; + function isBlank(obj) { + return isEmpty['default'](obj) || typeof obj === 'string' && obj.match(/\S/) === null; + } + +}); +enifed('ember-metal/is_empty', ['exports', 'ember-metal/property_get', 'ember-metal/is_none'], function (exports, property_get, isNone) { + + 'use strict'; + + function isEmpty(obj) { + var none = isNone['default'](obj); + if (none) { + return none; + } + + if (typeof obj.size === 'number') { + return !obj.size; + } + + var objectType = typeof obj; + + if (objectType === 'object') { + var size = property_get.get(obj, 'size'); + if (typeof size === 'number') { + return !size; + } + } + + if (typeof obj.length === 'number' && objectType !== 'function') { + return !obj.length; + } + + if (objectType === 'object') { + var length = property_get.get(obj, 'length'); + if (typeof length === 'number') { + return !length; + } + } + + return false; + } + + exports['default'] = isEmpty; + +}); +enifed('ember-metal/is_none', ['exports'], function (exports) { + + 'use strict'; + + /** + Returns true if the passed value is null or undefined. This avoids errors + from JSLint complaining about use of ==, which can be technically + confusing. + + ```javascript + Ember.isNone(); // true + Ember.isNone(null); // true + Ember.isNone(undefined); // true + Ember.isNone(''); // false + Ember.isNone([]); // false + Ember.isNone(function() {}); // false + ``` + + @method isNone + @for Ember + @param {Object} obj Value to test + @return {Boolean} + */ + function isNone(obj) { + return obj === null || obj === undefined; + } + + exports['default'] = isNone; + +}); +enifed('ember-metal/is_present', ['exports', 'ember-metal/is_blank'], function (exports, isBlank) { + + 'use strict'; + + + exports['default'] = isPresent; + function isPresent(obj) { + return !isBlank['default'](obj); + } + +}); +enifed('ember-metal/keys', ['exports', 'ember-metal/platform/define_property'], function (exports, define_property) { + + 'use strict'; + + var keys = Object.keys; + + if (!keys || !define_property.canDefineNonEnumerableProperties) { + // modified from + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys + keys = (function () { + var hasOwnProperty = Object.prototype.hasOwnProperty; + var hasDontEnumBug = !({ toString: null }).propertyIsEnumerable('toString'); + var dontEnums = ['toString', 'toLocaleString', 'valueOf', 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor']; + var dontEnumsLength = dontEnums.length; + + return function keys(obj) { + if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) { + throw new TypeError('Object.keys called on non-object'); + } + + var result = []; + var prop, i; + + for (prop in obj) { + if (prop !== '_super' && prop.lastIndexOf('__', 0) !== 0 && hasOwnProperty.call(obj, prop)) { + result.push(prop); + } + } + + if (hasDontEnumBug) { + for (i = 0; i < dontEnumsLength; i++) { + if (hasOwnProperty.call(obj, dontEnums[i])) { + result.push(dontEnums[i]); + } + } + } + return result; + }; + })(); + } + + exports['default'] = keys; + +}); +enifed('ember-metal/libraries', ['exports', 'ember-metal/core', 'ember-metal/enumerable_utils'], function (exports, Ember, enumerable_utils) { + + 'use strict'; + + function Libraries() { + this._registry = []; + this._coreLibIndex = 0; + } + + Libraries.prototype = { + constructor: Libraries, + + _getLibraryByName: function (name) { + var libs = this._registry; + var count = libs.length; + + for (var i = 0; i < count; i++) { + if (libs[i].name === name) { + return libs[i]; + } + } + }, + + register: function (name, version, isCoreLibrary) { + var index = this._registry.length; + + if (!this._getLibraryByName(name)) { + if (isCoreLibrary) { + index = this._coreLibIndex++; + } + this._registry.splice(index, 0, { name: name, version: version }); + } else { + Ember['default'].warn("Library \"" + name + "\" is already registered with Ember."); + } + }, + + registerCoreLibrary: function (name, version) { + this.register(name, version, true); + }, + + deRegister: function (name) { + var lib = this._getLibraryByName(name); + var index; + + if (lib) { + index = enumerable_utils.indexOf(this._registry, lib); + this._registry.splice(index, 1); + } + }, + + each: function (callback) { + Ember['default'].deprecate("Using Ember.libraries.each() is deprecated. Access to a list of registered libraries is currently a private API. If you are not knowingly accessing this method, your out-of-date Ember Inspector may be doing so."); + enumerable_utils.forEach(this._registry, function (lib) { + callback(lib.name, lib.version); + }); + } + }; + + + exports['default'] = Libraries; + +}); +enifed('ember-metal/logger', ['exports', 'ember-metal/core', 'ember-metal/error'], function (exports, Ember, EmberError) { + + 'use strict'; + + function K() { + return this; + } + + function consoleMethod(name) { + var consoleObj, logToConsole; + if (Ember['default'].imports.console) { + consoleObj = Ember['default'].imports.console; + } else if (typeof console !== "undefined") { + consoleObj = console; + } + + var method = typeof consoleObj === "object" ? consoleObj[name] : null; + + if (method) { + // Older IE doesn't support bind, but Chrome needs it + if (typeof method.bind === "function") { + logToConsole = method.bind(consoleObj); + logToConsole.displayName = "console." + name; + return logToConsole; + } else if (typeof method.apply === "function") { + logToConsole = function () { + method.apply(consoleObj, arguments); + }; + logToConsole.displayName = "console." + name; + return logToConsole; + } else { + return function () { + var message = Array.prototype.join.call(arguments, ", "); + method(message); + }; + } + } + } + + function assertPolyfill(test, message) { + if (!test) { + try { + // attempt to preserve the stack + throw new EmberError['default']("assertion failed: " + message); + } catch (error) { + setTimeout(function () { + throw error; + }, 0); + } + } + } + + /** + Inside Ember-Metal, simply uses the methods from `imports.console`. + Override this to provide more robust logging functionality. + + @class Logger + @namespace Ember + */ + exports['default'] = { + /** + Logs the arguments to the console. + You can pass as many arguments as you want and they will be joined together with a space. + ```javascript + var foo = 1; + Ember.Logger.log('log value of foo:', foo); + // "log value of foo: 1" will be printed to the console + ``` + @method log + @for Ember.Logger + @param {*} arguments + */ + log: consoleMethod("log") || K, + + /** + Prints the arguments to the console with a warning icon. + You can pass as many arguments as you want and they will be joined together with a space. + ```javascript + Ember.Logger.warn('Something happened!'); + // "Something happened!" will be printed to the console with a warning icon. + ``` + @method warn + @for Ember.Logger + @param {*} arguments + */ + warn: consoleMethod("warn") || K, + + /** + Prints the arguments to the console with an error icon, red text and a stack trace. + You can pass as many arguments as you want and they will be joined together with a space. + ```javascript + Ember.Logger.error('Danger! Danger!'); + // "Danger! Danger!" will be printed to the console in red text. + ``` + @method error + @for Ember.Logger + @param {*} arguments + */ + error: consoleMethod("error") || K, + + /** + Logs the arguments to the console. + You can pass as many arguments as you want and they will be joined together with a space. + ```javascript + var foo = 1; + Ember.Logger.info('log value of foo:', foo); + // "log value of foo: 1" will be printed to the console + ``` + @method info + @for Ember.Logger + @param {*} arguments + */ + info: consoleMethod("info") || K, + + /** + Logs the arguments to the console in blue text. + You can pass as many arguments as you want and they will be joined together with a space. + ```javascript + var foo = 1; + Ember.Logger.debug('log value of foo:', foo); + // "log value of foo: 1" will be printed to the console + ``` + @method debug + @for Ember.Logger + @param {*} arguments + */ + debug: consoleMethod("debug") || consoleMethod("info") || K, + + /** + If the value passed into `Ember.Logger.assert` is not truthy it will throw an error with a stack trace. + ```javascript + Ember.Logger.assert(true); // undefined + Ember.Logger.assert(true === false); // Throws an Assertion failed error. + ``` + @method assert + @for Ember.Logger + @param {Boolean} bool Value to test + */ + assert: consoleMethod("assert") || assertPolyfill + }; + +}); +enifed('ember-metal/map', ['exports', 'ember-metal/utils', 'ember-metal/array', 'ember-metal/platform/create', 'ember-metal/deprecate_property'], function (exports, utils, array, create, deprecate_property) { + + 'use strict'; + + exports.OrderedSet = OrderedSet; + exports.Map = Map; + exports.MapWithDefault = MapWithDefault; + + /** + @module ember-metal + */ + + /* + JavaScript (before ES6) does not have a Map implementation. Objects, + which are often used as dictionaries, may only have Strings as keys. + + 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. + + 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 one exception: as we do not have the luxury of in-VM iteration, we implement a + forEach method for iteration. + + Map is mocked out to look like an Ember object, so you can do + `Ember.Map.create()` for symmetry with other Ember classes. + */ + + function missingFunction(fn) { + throw new TypeError("" + Object.prototype.toString.call(fn) + " is not a function"); + } + + function missingNew(name) { + throw new TypeError("Constructor " + name + " requires 'new'"); + } + + function copyNull(obj) { + var output = create['default'](null); + + for (var prop in obj) { + // hasOwnPropery is not needed because obj is Object.create(null); + output[prop] = obj[prop]; + } + + return output; + } + + function copyMap(original, newObject) { + var keys = original._keys.copy(); + var values = copyNull(original._values); + + newObject._keys = keys; + newObject._values = values; + newObject.size = original.size; + + return newObject; + } + + /** + 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. + + @class OrderedSet + @namespace Ember + @constructor + @private + */ + function OrderedSet() { + + if (this instanceof OrderedSet) { + this.clear(); + this._silenceRemoveDeprecation = false; + } else { + missingNew("OrderedSet"); + } + } + + /** + @method create + @static + @return {Ember.OrderedSet} + */ + OrderedSet.create = function () { + var Constructor = this; + + return new Constructor(); + }; + + OrderedSet.prototype = { + constructor: OrderedSet, + /** + @method clear + */ + clear: function () { + this.presenceSet = create['default'](null); + this.list = []; + this.size = 0; + }, + + /** + @method add + @param obj + @param guid (optional, and for internal use) + @return {Ember.OrderedSet} + */ + add: function (obj, _guid) { + var guid = _guid || utils.guidFor(obj); + var presenceSet = this.presenceSet; + var list = this.list; + + if (presenceSet[guid] !== true) { + presenceSet[guid] = true; + this.size = list.push(obj); + } + + return this; + }, + + /** + @deprecated + @method remove + @param obj + @param _guid (optional and for internal use only) + @return {Boolean} + */ + remove: function (obj, _guid) { + Ember.deprecate("Calling `OrderedSet.prototype.remove` has been deprecated, please use `OrderedSet.prototype.delete` instead.", this._silenceRemoveDeprecation); + + return this["delete"](obj, _guid); + }, + + /** + @since 1.8.0 + @method delete + @param obj + @param _guid (optional and for internal use only) + @return {Boolean} + */ + "delete": function (obj, _guid) { + var guid = _guid || utils.guidFor(obj); + var presenceSet = this.presenceSet; + var list = this.list; + + if (presenceSet[guid] === true) { + delete presenceSet[guid]; + var index = array.indexOf.call(list, obj); + if (index > -1) { + list.splice(index, 1); + } + this.size = list.length; + return true; + } else { + return false; + } + }, + + /** + @method isEmpty + @return {Boolean} + */ + isEmpty: function () { + return this.size === 0; + }, + + /** + @method has + @param obj + @return {Boolean} + */ + has: function (obj) { + if (this.size === 0) { + return false; + } + + var guid = utils.guidFor(obj); + var presenceSet = this.presenceSet; + + return presenceSet[guid] === true; + }, + + /** + @method forEach + @param {Function} fn + @param self + */ + forEach: function (fn /*, ...thisArg*/) { + if (typeof fn !== "function") { + missingFunction(fn); + } + + if (this.size === 0) { + return; + } + + var list = this.list; + var length = arguments.length; + var i; + + if (length === 2) { + for (i = 0; i < list.length; i++) { + fn.call(arguments[1], list[i]); + } + } else { + for (i = 0; i < list.length; i++) { + fn(list[i]); + } + } + }, + + /** + @method toArray + @return {Array} + */ + toArray: function () { + return this.list.slice(); + }, + + /** + @method copy + @return {Ember.OrderedSet} + */ + copy: function () { + var Constructor = this.constructor; + var set = new Constructor(); + + set._silenceRemoveDeprecation = this._silenceRemoveDeprecation; + set.presenceSet = copyNull(this.presenceSet); + set.list = this.toArray(); + set.size = this.size; + + return set; + } + }; + + deprecate_property.deprecateProperty(OrderedSet.prototype, "length", "size"); + + /** + A Map stores values indexed by keys. Unlike JavaScript's + default Objects, the keys of a Map can be any JavaScript + object. + + Internally, a Map has two data structures: + + 1. `keys`: an OrderedSet of all of the existing keys + 2. `values`: a JavaScript Object indexed by the `Ember.guidFor(key)` + + 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`. + + @class Map + @namespace Ember + @private + @constructor + */ + function Map() { + if (this instanceof this.constructor) { + this._keys = OrderedSet.create(); + this._keys._silenceRemoveDeprecation = true; + this._values = create['default'](null); + this.size = 0; + } else { + missingNew("OrderedSet"); + } + } + + Ember.Map = Map; + + /** + @method create + @static + */ + Map.create = function () { + var Constructor = this; + return new Constructor(); + }; + + Map.prototype = { + constructor: Map, + + /** + This property will change as the number of objects in the map changes. + @since 1.8.0 + @property size + @type number + @default 0 + */ + size: 0, + + /** + Retrieve the value associated with a given key. + @method get + @param {*} key + @return {*} the value associated with the key, or `undefined` + */ + get: function (key) { + if (this.size === 0) { + return; + } + + var values = this._values; + var guid = utils.guidFor(key); + + return values[guid]; + }, + + /** + 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 + @return {Ember.Map} + */ + set: function (key, value) { + var keys = this._keys; + var values = this._values; + var guid = utils.guidFor(key); + + // ensure we don't store -0 + var k = key === -0 ? 0 : key; + + keys.add(k, guid); + + values[guid] = value; + + this.size = keys.size; + + return this; + }, + + /** + @deprecated see delete + Removes a value from the map for an associated key. + @method remove + @param {*} key + @return {Boolean} true if an item was removed, false otherwise + */ + remove: function (key) { + Ember.deprecate("Calling `Map.prototype.remove` has been deprecated, please use `Map.prototype.delete` instead."); + + return this["delete"](key); + }, + + /** + Removes a value from the map for an associated key. + @since 1.8.0 + @method delete + @param {*} key + @return {Boolean} true if an item was removed, false otherwise + */ + "delete": function (key) { + if (this.size === 0) { + return false; + } + // don't use ES6 "delete" because it will be annoying + // to use in browsers that are not ES6 friendly; + var keys = this._keys; + var values = this._values; + var guid = utils.guidFor(key); + + if (keys["delete"](key, guid)) { + delete values[guid]; + this.size = keys.size; + return true; + } else { + return false; + } + }, + + /** + Check whether a key is present. + @method has + @param {*} key + @return {Boolean} true if the item was present, false otherwise + */ + has: function (key) { + return this._keys.has(key); + }, + + /** + Iterate over all the keys and values. Calls the function once + for each key, passing in value, key, and the map being iterated over, + in that order. + The keys are guaranteed to be iterated over in insertion order. + @method forEach + @param {Function} callback + @param {*} self if passed, the `this` value inside the + callback. By default, `this` is the map. + */ + forEach: function (callback /*, ...thisArg*/) { + if (typeof callback !== "function") { + missingFunction(callback); + } + + if (this.size === 0) { + return; + } + + var length = arguments.length; + var map = this; + var cb, thisArg; + + if (length === 2) { + thisArg = arguments[1]; + cb = function (key) { + callback.call(thisArg, map.get(key), key, map); + }; + } else { + cb = function (key) { + callback(map.get(key), key, map); + }; + } + + this._keys.forEach(cb); + }, + + /** + @method clear + */ + clear: function () { + this._keys.clear(); + this._values = create['default'](null); + this.size = 0; + }, + + /** + @method copy + @return {Ember.Map} + */ + copy: function () { + return copyMap(this, new Map()); + } + }; + + deprecate_property.deprecateProperty(Map.prototype, "length", "size"); + + /** + @class MapWithDefault + @namespace Ember + @extends Ember.Map + @private + @constructor + @param [options] + @param {*} [options.defaultValue] + */ + function MapWithDefault(options) { + this._super$constructor(); + this.defaultValue = options.defaultValue; + } + + /** + @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(); + } + }; + + MapWithDefault.prototype = create['default'](Map.prototype); + MapWithDefault.prototype.constructor = MapWithDefault; + MapWithDefault.prototype._super$constructor = Map; + MapWithDefault.prototype._super$get = Map.prototype.get; + + /** + Retrieve the value associated with a given key. + + @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); + + if (hasValue) { + return this._super$get(key); + } else { + var defaultValue = this.defaultValue(key); + this.set(key, defaultValue); + return defaultValue; + } + }; + + /** + @method copy + @return {Ember.MapWithDefault} + */ + MapWithDefault.prototype.copy = function () { + var Constructor = this.constructor; + return copyMap(this, new Constructor({ + defaultValue: this.defaultValue + })); + }; + + exports['default'] = Map; + +}); +enifed('ember-metal/merge', ['exports', 'ember-metal/keys'], function (exports, keys) { + + 'use strict'; + + + exports['default'] = merge; + function merge(original, updates) { + if (!updates || typeof updates !== 'object') { + return original; + } + + var props = keys['default'](updates); + var prop; + var length = props.length; + + for (var i = 0; i < length; i++) { + prop = props[i]; + original[prop] = updates[prop]; + } + + return original; + } + +}); +enifed('ember-metal/mixin', ['exports', 'ember-metal/core', 'ember-metal/merge', 'ember-metal/array', 'ember-metal/platform/create', 'ember-metal/property_get', 'ember-metal/property_set', 'ember-metal/utils', 'ember-metal/expand_properties', 'ember-metal/properties', 'ember-metal/computed', 'ember-metal/binding', 'ember-metal/observer', 'ember-metal/events', 'ember-metal/streams/utils'], function (exports, Ember, merge, array, o_create, property_get, property_set, utils, expandProperties, ember_metal__properties, computed, ember_metal__binding, ember_metal__observer, events, streams__utils) { + + + exports.mixin = mixin; + exports.required = required; + exports.aliasMethod = aliasMethod; + exports.observer = observer; + exports.immediateObserver = immediateObserver; + exports.beforeObserver = beforeObserver; + exports.Mixin = Mixin; + + "REMOVE_USE_STRICT: true";var REQUIRED; + var a_slice = [].slice; + + function superFunction() { + var func = this.__nextSuper; + var ret; + + if (func) { + var length = arguments.length; + this.__nextSuper = null; + if (length === 0) { + ret = func.call(this); + } else if (length === 1) { + ret = func.call(this, arguments[0]); + } else if (length === 2) { + ret = func.call(this, arguments[0], arguments[1]); + } else { + ret = func.apply(this, arguments); + } + this.__nextSuper = func; + return ret; + } + } + + // ensure we prime superFunction to mitigate + // v8 bug potentially incorrectly deopts this function: https://code.google.com/p/v8/issues/detail?id=3709 + var primer = { + __nextSuper: function (a, b, c, d) {} + }; + + superFunction.call(primer); + superFunction.call(primer, 1); + superFunction.call(primer, 1, 2); + superFunction.call(primer, 1, 2, 3); + + function mixinsMeta(obj) { + var m = utils.meta(obj, true); + var ret = m.mixins; + if (!ret) { + ret = m.mixins = {}; + } else if (!m.hasOwnProperty("mixins")) { + ret = m.mixins = o_create['default'](ret); + } + return ret; + } + + function isMethod(obj) { + return "function" === typeof obj && obj.isMethod !== false && obj !== Boolean && obj !== Object && obj !== Number && obj !== Array && obj !== Date && obj !== String; + } + + var CONTINUE = {}; + + function mixinProperties(mixinsMeta, mixin) { + var guid; + + if (mixin instanceof Mixin) { + guid = utils.guidFor(mixin); + if (mixinsMeta[guid]) { + return CONTINUE; + } + mixinsMeta[guid] = mixin; + return mixin.properties; + } else { + return mixin; // apply anonymous mixin properties + } + } + + 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]; + } + + return concats; + } + + function giveDescriptorSuper(meta, key, property, values, descs, base) { + 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]; + } + + // If we didn't find the original descriptor in a parent mixin, find + // it on the original object. + if (!superProperty) { + var possibleDesc = base[key]; + var superDesc = possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor ? possibleDesc : undefined; + + superProperty = superDesc; + } + + if (superProperty === undefined || !(superProperty instanceof computed.ComputedProperty)) { + return property; + } + + // 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['default'](property); + property._getter = utils.wrap(property._getter, superProperty._getter); + if (superProperty._setter) { + if (property._setter) { + property._setter = utils.wrap(property._setter, superProperty._setter); + } else { + property._setter = superProperty._setter; + } + } + + return property; + } + + var sourceAvailable = (function () { + return this; + }).toString().indexOf("return this;") > -1; + + function giveMethodSuper(obj, key, method, values, descs) { + var superMethod; + + // 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]; + } + + // If we didn't find the original value in a parent mixin, find it in + // the original object + superMethod = superMethod || obj[key]; + + // Only wrap the new method if the original method was a function + if (superMethod === undefined || "function" !== typeof superMethod) { + return method; + } + + var hasSuper; + if (sourceAvailable) { + hasSuper = method.__hasSuper; + + if (hasSuper === undefined) { + hasSuper = method.toString().indexOf("_super") > -1; + method.__hasSuper = hasSuper; + } + } + + if (sourceAvailable === false || hasSuper) { + return utils.wrap(method, superMethod); + } else { + return method; + } + } + + function applyConcatenatedProperties(obj, key, value, values) { + var baseValue = values[key] || obj[key]; + + if (baseValue) { + if ("function" === typeof baseValue.concat) { + if (value === null || value === undefined) { + return baseValue; + } else { + return baseValue.concat(value); + } + } else { + return utils.makeArray(baseValue).concat(value); + } + } else { + return utils.makeArray(value); + } + } + + function applyMergedProperties(obj, key, value, values) { + var baseValue = values[key] || obj[key]; + + Ember['default'].assert("You passed in `" + JSON.stringify(value) + "` as the value for `" + key + "` but `" + key + "` cannot be an Array", !utils.isArray(value)); + + if (!baseValue) { + return value; + } + + var newBase = merge['default']({}, baseValue); + var hasFunction = false; + + for (var prop in value) { + if (!value.hasOwnProperty(prop)) { + continue; + } + + 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; + } + } + + if (hasFunction) { + newBase._super = superFunction; + } + + return newBase; + } + + function addNormalizedProperty(base, key, value, meta, descs, values, concats, mergings) { + if (value instanceof ember_metal__properties.Descriptor) { + if (value === REQUIRED && descs[key]) { + return CONTINUE; + } + + // Wrap descriptor function to implement + // __nextSuper() if needed + if (value._getter) { + value = giveDescriptorSuper(meta, key, value, values, descs, base); + } + + descs[key] = value; + values[key] = undefined; + } else { + if (concats && array.indexOf.call(concats, key) >= 0 || key === "concatenatedProperties" || key === "mergedProperties") { + value = applyConcatenatedProperties(base, key, value, values); + } else if (mergings && array.indexOf.call(mergings, key) >= 0) { + value = applyMergedProperties(base, key, value, values); + } else if (isMethod(value)) { + value = giveMethodSuper(base, key, value, values, descs); + } + + descs[key] = undefined; + values[key] = value; + } + } + + function mergeMixins(mixins, m, descs, values, base, keys) { + var currentMixin, props, key, concats, mergings, meta; + + function removeKeys(keyName) { + delete descs[keyName]; + delete values[keyName]; + } + + for (var i = 0, l = mixins.length; i < l; i++) { + currentMixin = mixins[i]; + Ember['default'].assert("Expected hash or Mixin instance, got " + Object.prototype.toString.call(currentMixin), typeof currentMixin === "object" && currentMixin !== null && Object.prototype.toString.call(currentMixin) !== "[object Array]"); + + props = mixinProperties(m, currentMixin); + if (props === CONTINUE) { + continue; + } + + if (props) { + meta = utils.meta(base); + if (base.willMergeMixin) { + base.willMergeMixin(props); + } + concats = concatenatedMixinProperties("concatenatedProperties", props, values, base); + mergings = concatenatedMixinProperties("mergedProperties", props, values, base); + + for (key in props) { + if (!props.hasOwnProperty(key)) { + continue; + } + keys.push(key); + addNormalizedProperty(base, key, props[key], meta, descs, values, concats, mergings); + } + + // manually copy toString() because some JS engines do not enumerate it + if (props.hasOwnProperty("toString")) { + base.toString = props.toString; + } + } else if (currentMixin.mixins) { + mergeMixins(currentMixin.mixins, m, descs, values, base, keys); + if (currentMixin._without) { + array.forEach.call(currentMixin._without, removeKeys); + } + } + } + } + + var IS_BINDING = /^.+Binding$/; + + function detectBinding(obj, key, value, m) { + if (IS_BINDING.test(key)) { + var bindings = m.bindings; + if (!bindings) { + bindings = m.bindings = {}; + } else if (!m.hasOwnProperty("bindings")) { + bindings = m.bindings = o_create['default'](m.bindings); + } + bindings[key] = value; + } + } + + function connectStreamBinding(obj, key, stream) { + var onNotify = function (stream) { + ember_metal__observer._suspendObserver(obj, key, null, didChange, function () { + property_set.trySet(obj, key, stream.value()); + }); + }; + + var didChange = function () { + stream.setValue(property_get.get(obj, key), onNotify); + }; + + // Initialize value + property_set.set(obj, key, stream.value()); + + ember_metal__observer.addObserver(obj, key, null, didChange); + + stream.subscribe(onNotify); + + if (obj._streamBindingSubscriptions === undefined) { + obj._streamBindingSubscriptions = o_create['default'](null); + } + + obj._streamBindingSubscriptions[key] = onNotify; + } + + function connectBindings(obj, m) { + // TODO Mixin.apply(instance) should disconnect binding if exists + var bindings = m.bindings; + var key, binding, to; + if (bindings) { + for (key in bindings) { + binding = bindings[key]; + if (binding) { + to = key.slice(0, -7); // strip Binding off end + if (streams__utils.isStream(binding)) { + connectStreamBinding(obj, to, binding); + continue; + } else if (binding instanceof ember_metal__binding.Binding) { + binding = binding.copy(); // copy prototypes' instance + binding.to(to); + } else { + // binding is string path + binding = new ember_metal__binding.Binding(to, binding); + } + binding.connect(obj); + obj[key] = binding; + } + } + // mark as applied + m.bindings = {}; + } + } + + function finishPartial(obj, m) { + connectBindings(obj, m || utils.meta(obj)); + return obj; + } + + function followAlias(obj, desc, m, descs, values) { + var altKey = desc.methodName; + var value; + var possibleDesc; + if (descs[altKey] || values[altKey]) { + value = values[altKey]; + desc = descs[altKey]; + } else if ((possibleDesc = obj[altKey]) && possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor) { + desc = possibleDesc; + value = undefined; + } else { + desc = undefined; + value = obj[altKey]; + } + + return { desc: desc, value: value }; + } + + function updateObserversAndListeners(obj, key, observerOrListener, pathsKey, updateMethod) { + var paths = observerOrListener[pathsKey]; + + if (paths) { + for (var i = 0, l = paths.length; i < l; i++) { + updateMethod(obj, paths[i], null, key); + } + } + } + + function replaceObserversAndListeners(obj, key, observerOrListener) { + var prev = obj[key]; + + if ("function" === typeof prev) { + updateObserversAndListeners(obj, key, prev, "__ember_observesBefore__", ember_metal__observer.removeBeforeObserver); + updateObserversAndListeners(obj, key, prev, "__ember_observes__", ember_metal__observer.removeObserver); + updateObserversAndListeners(obj, key, prev, "__ember_listens__", events.removeListener); + } + + if ("function" === typeof observerOrListener) { + updateObserversAndListeners(obj, key, observerOrListener, "__ember_observesBefore__", ember_metal__observer.addBeforeObserver); + updateObserversAndListeners(obj, key, observerOrListener, "__ember_observes__", ember_metal__observer.addObserver); + updateObserversAndListeners(obj, key, observerOrListener, "__ember_listens__", events.addListener); + } + } + + function applyMixin(obj, mixins, partial) { + var descs = {}; + var values = {}; + var m = utils.meta(obj); + var keys = []; + var key, value, desc; + + obj._super = superFunction; + + // Go through all mixins and hashes passed in, and: + // + // * Handle concatenated properties + // * Handle merged properties + // * Set up _super wrapping if necessary + // * Set up computed property descriptors + // * Copying `toString` in broken browsers + mergeMixins(mixins, mixinsMeta(obj), descs, values, obj, keys); + + for (var i = 0, l = keys.length; i < l; i++) { + key = keys[i]; + if (key === "constructor" || !values.hasOwnProperty(key)) { + continue; + } + + desc = descs[key]; + value = values[key]; + + if (desc === REQUIRED) { + continue; + } + + while (desc && desc instanceof Alias) { + var followed = followAlias(obj, desc, m, descs, values); + desc = followed.desc; + value = followed.value; + } + + if (desc === undefined && value === undefined) { + continue; + } + + replaceObserversAndListeners(obj, key, value); + detectBinding(obj, key, value, m); + ember_metal__properties.defineProperty(obj, key, desc, value, m); + } + + if (!partial) { + // don't apply to prototype + finishPartial(obj, m); + } + + return obj; + } + + /** + @method mixin + @for Ember + @param obj + @param mixins* + @return obj + */ + function mixin(obj) { + for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + applyMixin(obj, args, false); + return obj; + } + + /** + The `Ember.Mixin` class allows you to create mixins, whose properties can be + added to other classes. For instance, + + ```javascript + App.Editable = Ember.Mixin.create({ + edit: function() { + console.log('starting to edit'); + this.set('isEditing', true); + }, + isEditing: false + }); + + // Mix mixins into classes by passing them as the first arguments to + // .extend. + App.CommentView = Ember.View.extend(App.Editable, { + template: Ember.Handlebars.compile('{{#if view.isEditing}}...{{else}}...{{/if}}') + }); + + commentView = App.CommentView.create(); + commentView.edit(); // outputs 'starting to edit' + ``` + + Note that Mixins are created with `Ember.Mixin.create`, not + `Ember.Mixin.extend`. + + Note that mixins extend a constructor's prototype so arrays and object literals + defined as properties will be shared amongst objects that implement the mixin. + If you want to define a property in a mixin that is not shared, you can define + it either as a computed property or have it be created on initialization of the object. + + ```javascript + //filters array will be shared amongst any object implementing mixin + App.Filterable = Ember.Mixin.create({ + filters: Ember.A() + }); + + //filters will be a separate array for every object implementing the mixin + App.Filterable = Ember.Mixin.create({ + filters: Ember.computed(function() {return Ember.A();}) + }); + + //filters will be created as a separate array during the object's initialization + App.Filterable = Ember.Mixin.create({ + init: function() { + this._super.apply(this, arguments); + this.set("filters", Ember.A()); + } + }); + ``` + + @class Mixin + @namespace Ember + */ + exports['default'] = Mixin; + function Mixin(args, properties) { + this.properties = properties; + + var length = args && args.length; + + if (length > 0) { + var m = new Array(length); + + for (var i = 0; i < length; i++) { + var x = args[i]; + if (x instanceof Mixin) { + m[i] = x; + } else { + m[i] = new Mixin(undefined, x); + } + } + + this.mixins = m; + } else { + this.mixins = undefined; + } + this.ownerConstructor = undefined; + } + + Mixin._apply = applyMixin; + + Mixin.applyPartial = function (obj) { + var args = a_slice.call(arguments, 1); + return applyMixin(obj, args, true); + }; + + Mixin.finishPartial = finishPartial; + + // ES6TODO: this relies on a global state? + Ember['default'].anyUnprocessedMixins = false; + + /** + @method create + @static + @param arguments* + */ + Mixin.create = function () { + for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } + + // ES6TODO: this relies on a global state? + Ember['default'].anyUnprocessedMixins = true; + var M = this; + return new M(args, undefined); + }; + + var MixinPrototype = Mixin.prototype; + + /** + @method reopen + @param arguments* + */ + MixinPrototype.reopen = function () { + var currentMixin; + + if (this.properties) { + currentMixin = new Mixin(undefined, this.properties); + this.properties = undefined; + this.mixins = [currentMixin]; + } else if (!this.mixins) { + this.mixins = []; + } + + var len = arguments.length; + var mixins = this.mixins; + var idx; + + for (idx = 0; idx < len; idx++) { + currentMixin = arguments[idx]; + Ember['default'].assert("Expected hash or Mixin instance, got " + Object.prototype.toString.call(currentMixin), typeof currentMixin === "object" && currentMixin !== null && Object.prototype.toString.call(currentMixin) !== "[object Array]"); + + if (currentMixin instanceof Mixin) { + mixins.push(currentMixin); + } else { + mixins.push(new Mixin(undefined, currentMixin)); + } + } + + return this; + }; + + /** + @method apply + @param obj + @return applied object + */ + MixinPrototype.apply = function (obj) { + return applyMixin(obj, [this], false); + }; + + MixinPrototype.applyPartial = function (obj) { + return applyMixin(obj, [this], true); + }; + + function _detect(curMixin, targetMixin, seen) { + var guid = utils.guidFor(curMixin); + + if (seen[guid]) { + return false; + } + seen[guid] = true; + + if (curMixin === targetMixin) { + return true; + } + var mixins = curMixin.mixins; + var loc = mixins ? mixins.length : 0; + while (--loc >= 0) { + if (_detect(mixins[loc], targetMixin, seen)) { + return true; + } + } + return false; + } + + /** + @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["__ember_meta__"]; + var mixins = m && m.mixins; + if (mixins) { + return !!mixins[utils.guidFor(this)]; + } + return false; + }; + + MixinPrototype.without = function () { + for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { + args[_key3] = arguments[_key3]; + } + + var ret = new Mixin([this]); + ret._without = args; + return ret; + }; + + function _keys(ret, mixin, seen) { + if (seen[utils.guidFor(mixin)]) { + return; + } + seen[utils.guidFor(mixin)] = true; + + if (mixin.properties) { + var props = mixin.properties; + for (var key in props) { + if (props.hasOwnProperty(key)) { + ret[key] = true; + } + } + } else if (mixin.mixins) { + array.forEach.call(mixin.mixins, function (x) { + _keys(ret, x, seen); + }); + } + } + + MixinPrototype.keys = function () { + var keys = {}; + var seen = {}; + var 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["__ember_meta__"]; + var mixins = m && m.mixins; + var ret = []; + + if (!mixins) { + return ret; + } + + for (var key in mixins) { + var currentMixin = mixins[key]; + + // skip primitive mixins since these are always anonymous + if (!currentMixin.properties) { + ret.push(currentMixin); + } + } + + return ret; + }; + + REQUIRED = new ember_metal__properties.Descriptor(); + REQUIRED.toString = function () { + return "(Required Property)"; + }; + + /** + Denotes a required property for a mixin + + @method required + @for Ember + */ + function required() { + Ember['default'].deprecate("Ember.required is deprecated as its behavior is inconsistent and unreliable.", false); + return REQUIRED; + } + + function Alias(methodName) { + this.isDescriptor = true; + this.methodName = methodName; + } + + Alias.prototype = new ember_metal__properties.Descriptor(); + + /** + Makes a method available via an additional name. + + ```javascript + App.Person = Ember.Object.extend({ + name: function() { + return 'Tomhuda Katzdale'; + }, + moniker: Ember.aliasMethod('name') + }); + + var goodGuy = App.Person.create(); + + goodGuy.name(); // 'Tomhuda Katzdale' + goodGuy.moniker(); // 'Tomhuda Katzdale' + ``` + + @method aliasMethod + @for Ember + @param {String} methodName name of the method to alias + */ + function aliasMethod(methodName) { + return new Alias(methodName); + } + + // .......................................................... + // OBSERVER HELPER + // + + /** + Specify a method that observes property changes. + + ```javascript + Ember.Object.extend({ + valueObserver: Ember.observer('value', function() { + // Executes whenever the "value" property changes + }) + }); + ``` + + In the future this method may become asynchronous. If you want to ensure + synchronous behavior, use `immediateObserver`. + + Also available as `Function.prototype.observes` if prototype extensions are + enabled. + + @method observer + @for Ember + @param {String} propertyNames* + @param {Function} func + @return func + */ + function observer() { + for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { + args[_key4] = arguments[_key4]; + } + + var func = args.slice(-1)[0]; + var paths; + + var addWatchedProperty = function (path) { + paths.push(path); + }; + var _paths = args.slice(0, -1); + + if (typeof func !== "function") { + // revert to old, soft-deprecated argument ordering + + func = args[0]; + _paths = args.slice(1); + } + + paths = []; + + for (var i = 0; i < _paths.length; ++i) { + expandProperties['default'](_paths[i], addWatchedProperty); + } + + if (typeof func !== "function") { + throw new Ember['default'].Error("Ember.observer called without a function"); + } + + func.__ember_observes__ = paths; + return func; + } + + /** + Specify a method that observes property changes. + + ```javascript + Ember.Object.extend({ + valueObserver: Ember.immediateObserver('value', function() { + // Executes whenever the "value" property changes + }) + }); + ``` + + 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. + + @method immediateObserver + @for Ember + @param {String} propertyNames* + @param {Function} func + @return func + */ + function immediateObserver() { + for (var i = 0, l = arguments.length; i < l; i++) { + var arg = arguments[i]; + Ember['default'].assert("Immediate observers must observe internal properties only, not properties on other objects.", typeof arg !== "string" || arg.indexOf(".") === -1); + } + + return observer.apply(this, arguments); + } + + /** + When observers fire, they are called with the arguments `obj`, `keyName`. + + Note, `@each.property` observer is called per each add or replace of an element + and it's not called with a specific enumeration item. + + A `beforeObserver` fires before a property changes. + + A `beforeObserver` is an alternative form of `.observesBefore()`. + + ```javascript + App.PersonView = Ember.View.extend({ + friends: [{ name: 'Tom' }, { name: 'Stefan' }, { name: 'Kris' }], + + valueWillChange: Ember.beforeObserver('content.value', function(obj, keyName) { + this.changingFrom = obj.get(keyName); + }), + + valueDidChange: Ember.observer('content.value', function(obj, keyName) { + // only run if updating a value already in the DOM + if (this.get('state') === 'inDOM') { + var color = obj.get(keyName) > this.changingFrom ? 'green' : 'red'; + // logic + } + }), + + friendsDidChange: Ember.observer('friends.@each.name', function(obj, keyName) { + // some logic + // obj.get(keyName) returns friends array + }) + }); + ``` + + Also available as `Function.prototype.observesBefore` if prototype extensions are + enabled. + + @method beforeObserver + @for Ember + @param {String} propertyNames* + @param {Function} func + @return func + */ + function beforeObserver() { + for (var _len5 = arguments.length, args = Array(_len5), _key5 = 0; _key5 < _len5; _key5++) { + args[_key5] = arguments[_key5]; + } + + var func = args.slice(-1)[0]; + var paths; + + var addWatchedProperty = function (path) { + paths.push(path); + }; + + var _paths = args.slice(0, -1); + + if (typeof func !== "function") { + // revert to old, soft-deprecated argument ordering + + func = args[0]; + _paths = args.slice(1); + } + + paths = []; + + for (var i = 0; i < _paths.length; ++i) { + expandProperties['default'](_paths[i], addWatchedProperty); + } + + if (typeof func !== "function") { + throw new Ember['default'].Error("Ember.beforeObserver called without a function"); + } + + func.__ember_observesBefore__ = paths; + return func; + } + + exports.IS_BINDING = IS_BINDING; + exports.REQUIRED = REQUIRED; + +}); +enifed('ember-metal/observer', ['exports', 'ember-metal/watching', 'ember-metal/array', 'ember-metal/events'], function (exports, watching, array, ember_metal__events) { + + 'use strict'; + + 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; + + var AFTER_OBSERVERS = ":change"; + var BEFORE_OBSERVERS = ":before"; + + function changeEvent(keyName) { + return keyName + AFTER_OBSERVERS; + } + + function beforeEvent(keyName) { + return keyName + BEFORE_OBSERVERS; + } + + /** + @method addObserver + @for Ember + @param obj + @param {String} path + @param {Object|Function} targetOrMethod + @param {Function|String} [method] + */ + function addObserver(obj, _path, target, method) { + ember_metal__events.addListener(obj, changeEvent(_path), target, method); + watching.watch(obj, _path); + + return this; + } + + function observersFor(obj, path) { + return ember_metal__events.listenersFor(obj, changeEvent(path)); + } + + /** + @method removeObserver + @for Ember + @param obj + @param {String} path + @param {Object|Function} target + @param {Function|String} [method] + */ + function removeObserver(obj, path, target, method) { + watching.unwatch(obj, path); + ember_metal__events.removeListener(obj, changeEvent(path), target, method); + + return this; + } + + /** + @method addBeforeObserver + @for Ember + @param obj + @param {String} path + @param {Object|Function} target + @param {Function|String} [method] + */ + function addBeforeObserver(obj, path, target, method) { + ember_metal__events.addListener(obj, beforeEvent(path), target, method); + watching.watch(obj, path); + + return this; + } + + // 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 ember_metal__events.suspendListener(obj, beforeEvent(path), target, method, callback); + } + + function _suspendObserver(obj, path, target, method, callback) { + return ember_metal__events.suspendListener(obj, changeEvent(path), target, method, callback); + } + + function _suspendBeforeObservers(obj, paths, target, method, callback) { + var events = array.map.call(paths, beforeEvent); + return ember_metal__events.suspendListeners(obj, events, target, method, callback); + } + + function _suspendObservers(obj, paths, target, method, callback) { + var events = array.map.call(paths, changeEvent); + return ember_metal__events.suspendListeners(obj, events, target, method, callback); + } + + function beforeObserversFor(obj, path) { + return ember_metal__events.listenersFor(obj, beforeEvent(path)); + } + + /** + @method removeBeforeObserver + @for Ember + @param obj + @param {String} path + @param {Object|Function} target + @param {Function|String} [method] + */ + function removeBeforeObserver(obj, path, target, method) { + watching.unwatch(obj, path); + ember_metal__events.removeListener(obj, beforeEvent(path), target, method); + + return this; + } + +}); +enifed('ember-metal/observer_set', ['exports', 'ember-metal/utils', 'ember-metal/events'], function (exports, utils, events) { + + 'use strict'; + + exports['default'] = ObserverSet; + function ObserverSet() { + this.clear(); + } + + ObserverSet.prototype.add = function (sender, keyName, eventName) { + var observerSet = this.observerSet; + var observers = this.observers; + var senderGuid = utils.guidFor(sender); + var keySet = observerSet[senderGuid]; + var index; + + 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; + }; + + ObserverSet.prototype.flush = function () { + var observers = this.observers; + var 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; + } + events.sendEvent(sender, observer.eventName, [sender, observer.keyName], observer.listeners); + } + }; + + ObserverSet.prototype.clear = function () { + this.observerSet = {}; + this.observers = []; + }; + +}); +enifed('ember-metal/path_cache', ['exports', 'ember-metal/cache'], function (exports, Cache) { + + 'use strict'; + + exports.isGlobal = isGlobal; + exports.isGlobalPath = isGlobalPath; + exports.hasThis = hasThis; + exports.isPath = isPath; + exports.getFirstKey = getFirstKey; + exports.getTailPath = getTailPath; + + var IS_GLOBAL = /^[A-Z$]/; + var IS_GLOBAL_PATH = /^[A-Z$].*[\.]/; + var HAS_THIS = 'this.'; + + var isGlobalCache = new Cache['default'](1000, function (key) { + return IS_GLOBAL.test(key); + }); + + var isGlobalPathCache = new Cache['default'](1000, function (key) { + return IS_GLOBAL_PATH.test(key); + }); + + var hasThisCache = new Cache['default'](1000, function (key) { + return key.lastIndexOf(HAS_THIS, 0) === 0; + }); + + var firstDotIndexCache = new Cache['default'](1000, function (key) { + return key.indexOf('.'); + }); + + var firstKeyCache = new Cache['default'](1000, function (path) { + var index = firstDotIndexCache.get(path); + if (index === -1) { + return path; + } else { + return path.slice(0, index); + } + }); + + var tailPathCache = new Cache['default'](1000, function (path) { + var index = firstDotIndexCache.get(path); + if (index !== -1) { + return path.slice(index + 1); + } + }); + + var caches = { + isGlobalCache: isGlobalCache, + isGlobalPathCache: isGlobalPathCache, + hasThisCache: hasThisCache, + firstDotIndexCache: firstDotIndexCache, + firstKeyCache: firstKeyCache, + tailPathCache: tailPathCache + };function isGlobal(path) { + return isGlobalCache.get(path); + } + + function isGlobalPath(path) { + return isGlobalPathCache.get(path); + } + + function hasThis(path) { + return hasThisCache.get(path); + } + + function isPath(path) { + return firstDotIndexCache.get(path) !== -1; + } + + function getFirstKey(path) { + return firstKeyCache.get(path); + } + + function getTailPath(path) { + return tailPathCache.get(path); + } + + exports.caches = caches; + +}); +enifed('ember-metal/platform/create', ['exports', 'ember-metal/platform/define_properties'], function (exports, defineProperties) { + + + + + 'REMOVE_USE_STRICT: true'; /** + @class platform + @namespace Ember + @static + */ + + /** + Identical to `Object.create()`. Implements if not available natively. + + @since 1.8.0 + @method create + @for Ember + */ + var create; + // ES5 15.2.3.5 + // http://es5.github.com/#x15.2.3.5 + if (!(Object.create && !Object.create(null).hasOwnProperty)) { + /* jshint scripturl:true, proto:true */ + // Contributed by Brandon Benvie, October, 2012 + var createEmpty; + var supportsProto = !({ '__proto__': null } instanceof Object); + // the following produces false positives + // in Opera Mini => not a reliable check + // Object.prototype.__proto__ === null + if (supportsProto || typeof document === 'undefined') { + createEmpty = function () { + return { '__proto__': null }; + }; + } else { + // In old IE __proto__ can't be used to manually set `null`, nor does + // any other method exist to make an object that inherits from nothing, + // aside from Object.prototype itself. Instead, create a new global + // object and *steal* its Object.prototype and strip it bare. This is + // used as the prototype to create nullary objects. + createEmpty = function () { + var iframe = document.createElement('iframe'); + var parent = document.body || document.documentElement; + iframe.style.display = 'none'; + parent.appendChild(iframe); + iframe.src = 'javascript:'; + var empty = iframe.contentWindow.Object.prototype; + parent.removeChild(iframe); + iframe = null; + delete empty.constructor; + delete empty.hasOwnProperty; + delete empty.propertyIsEnumerable; + delete empty.isPrototypeOf; + delete empty.toLocaleString; + delete empty.toString; + delete empty.valueOf; + + function Empty() {} + Empty.prototype = empty; + // short-circuit future calls + createEmpty = function () { + return new Empty(); + }; + return new Empty(); + }; + } + + create = Object.create = function create(prototype, properties) { + + var object; + function Type() {} // An empty constructor. + + if (prototype === null) { + object = createEmpty(); + } else { + if (typeof prototype !== 'object' && typeof prototype !== 'function') { + // In the native implementation `parent` can be `null` + // OR *any* `instanceof Object` (Object|Function|Array|RegExp|etc) + // Use `typeof` tho, b/c in old IE, DOM elements are not `instanceof Object` + // like they are in modern browsers. Using `Object.create` on DOM elements + // is...err...probably inappropriate, but the native version allows for it. + throw new TypeError('Object prototype may only be an Object or null'); // same msg as Chrome + } + + Type.prototype = prototype; + + object = new Type(); + } + + if (properties !== undefined) { + defineProperties['default'](object, properties); + } + + return object; + }; + } else { + create = Object.create; + } + + exports['default'] = create; + +}); +enifed('ember-metal/platform/define_properties', ['exports', 'ember-metal/platform/define_property'], function (exports, define_property) { + + 'use strict'; + + var defineProperties = Object.defineProperties; + + // ES5 15.2.3.7 + // http://es5.github.com/#x15.2.3.7 + if (!defineProperties) { + defineProperties = function defineProperties(object, properties) { + for (var property in properties) { + if (properties.hasOwnProperty(property) && property !== "__proto__") { + define_property.defineProperty(object, property, properties[property]); + } + } + return object; + }; + + Object.defineProperties = defineProperties; + } + + exports['default'] = defineProperties; + +}); +enifed('ember-metal/platform/define_property', ['exports'], function (exports) { + + 'use strict'; + + /*globals Node */ + + /** + @class platform + @namespace Ember + @static + */ + + /** + Set to true if the platform supports native getters and setters. + + @property hasPropertyAccessors + @final + */ + + /** + Identical to `Object.defineProperty()`. Implements as much functionality + as possible if not available natively. + + @method defineProperty + @param {Object} obj The object to modify + @param {String} keyName property name to modify + @param {Object} desc descriptor hash + @return {void} + */ + var defineProperty = (function checkCompliance(defineProperty) { + if (!defineProperty) { + return; + } + + try { + var a = 5; + var obj = {}; + defineProperty(obj, 'a', { + configurable: true, + enumerable: true, + get: function () { + return a; + }, + set: function (v) { + a = v; + } + }); + if (obj.a !== 5) { + return; + } + + obj.a = 10; + if (a !== 10) { + return; + } + + // check non-enumerability + defineProperty(obj, 'a', { + configurable: true, + enumerable: false, + writable: true, + value: true + }); + for (var key in obj) { + if (key === 'a') { + return; + } + } + + // Detects a bug in Android <3.2 where you cannot redefine a property using + // Object.defineProperty once accessors have already been set. + if (obj.a !== true) { + return; + } + + // Detects a bug in Android <3 where redefining a property without a value changes the value + // Object.defineProperty once accessors have already been set. + defineProperty(obj, 'a', { + enumerable: false + }); + if (obj.a !== true) { + return; + } + + // defineProperty is compliant + return defineProperty; + } catch (e) { + // IE8 defines Object.defineProperty but calling it on an Object throws + return; + } + })(Object.defineProperty); + + var hasES5CompliantDefineProperty = !!defineProperty; + + if (hasES5CompliantDefineProperty && typeof document !== 'undefined') { + // This is for Safari 5.0, which supports Object.defineProperty, but not + // on DOM nodes. + var canDefinePropertyOnDOM = (function () { + try { + defineProperty(document.createElement('div'), 'definePropertyOnDOM', {}); + return true; + } catch (e) {} + + return false; + })(); + + if (!canDefinePropertyOnDOM) { + defineProperty = function (obj, keyName, desc) { + var isNode; + + if (typeof Node === 'object') { + isNode = obj instanceof Node; + } else { + isNode = typeof obj === 'object' && typeof obj.nodeType === 'number' && typeof obj.nodeName === 'string'; + } + + if (isNode) { + // TODO: Should we have a warning here? + return obj[keyName] = desc.value; + } else { + return Object.defineProperty(obj, keyName, desc); + } + }; + } + } + + if (!hasES5CompliantDefineProperty) { + defineProperty = function definePropertyPolyfill(obj, keyName, desc) { + if (!desc.get) { + obj[keyName] = desc.value; + } + }; + } + + var hasPropertyAccessors = hasES5CompliantDefineProperty; + var canDefineNonEnumerableProperties = hasES5CompliantDefineProperty; + + exports.hasES5CompliantDefineProperty = hasES5CompliantDefineProperty; + exports.defineProperty = defineProperty; + exports.hasPropertyAccessors = hasPropertyAccessors; + exports.canDefineNonEnumerableProperties = canDefineNonEnumerableProperties; + +}); +enifed('ember-metal/properties', ['exports', 'ember-metal/core', 'ember-metal/utils', 'ember-metal/platform/define_property', 'ember-metal/property_events'], function (exports, Ember, utils, define_property, property_events) { + + 'use strict'; + + exports.Descriptor = Descriptor; + exports.MANDATORY_SETTER_FUNCTION = MANDATORY_SETTER_FUNCTION; + exports.DEFAULT_GETTER_FUNCTION = DEFAULT_GETTER_FUNCTION; + exports.defineProperty = defineProperty; + + function Descriptor() { + this.isDescriptor = true; + } + + // .......................................................... + // DEFINING PROPERTIES API + // + + function MANDATORY_SETTER_FUNCTION(name) { + return function SETTER_FUNCTION(value) { + Ember['default'].assert("You must use Ember.set() to set the `" + name + "` property (of " + this + ") to `" + value + "`.", false); + }; + } + + function DEFAULT_GETTER_FUNCTION(name) { + return function GETTER_FUNCTION() { + var meta = this["__ember_meta__"]; + return meta && meta.values[name]; + }; + } + + /** + 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. + + 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. + + Normally this method takes only three parameters. However if you pass an + instance of `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. + + ## Examples + + ```javascript + // ES5 compatible mode + Ember.defineProperty(contact, 'firstName', { + writable: true, + configurable: false, + enumerable: true, + value: 'Charles' + }); + + // define a simple property + Ember.defineProperty(contact, 'lastName', undefined, 'Jolley'); + + // define a computed property + Ember.defineProperty(contact, 'fullName', Ember.computed(function() { + return this.firstName+' '+this.lastName; + }).property('firstName', 'lastName')); + ``` + + @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 {Descriptor} [desc] an instance of `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 possibleDesc, existingDesc, watching, value; + + if (!meta) { + meta = utils.meta(obj); + } + var watchEntry = meta.watching[keyName]; + possibleDesc = obj[keyName]; + existingDesc = possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor ? possibleDesc : undefined; + + watching = watchEntry !== undefined && watchEntry > 0; + + if (existingDesc) { + existingDesc.teardown(obj, keyName); + } + + if (desc instanceof Descriptor) { + value = desc; + + + if (watching && define_property.hasPropertyAccessors) { + define_property.defineProperty(obj, keyName, { + configurable: true, + enumerable: true, + writable: true, + value: value + }); + } else { + obj[keyName] = value; + } + if (desc.setup) { + desc.setup(obj, keyName); + } + } else { + if (desc == null) { + value = data; + + + if (watching && define_property.hasPropertyAccessors) { + meta.values[keyName] = data; + define_property.defineProperty(obj, keyName, { + configurable: true, + enumerable: true, + set: MANDATORY_SETTER_FUNCTION(keyName), + get: DEFAULT_GETTER_FUNCTION(keyName) + }); + } else { + obj[keyName] = data; + } + } else { + value = desc; + + // compatibility with ES5 + define_property.defineProperty(obj, keyName, desc); + } + } + + // if key is being watched, override chains that + // were initialized with the prototype + if (watching) { + property_events.overrideChains(obj, keyName, meta); + } + + // The `value` passed to the `didDefineProperty` hook is + // either the descriptor or data, whichever was passed. + if (obj.didDefineProperty) { + obj.didDefineProperty(obj, keyName, value); + } + + return this; + } + +}); +enifed('ember-metal/property_events', ['exports', 'ember-metal/utils', 'ember-metal/events', 'ember-metal/observer_set'], function (exports, utils, ember_metal__events, ObserverSet) { + + 'use strict'; + + exports.propertyWillChange = propertyWillChange; + exports.propertyDidChange = propertyDidChange; + exports.overrideChains = overrideChains; + exports.beginPropertyChanges = beginPropertyChanges; + exports.endPropertyChanges = endPropertyChanges; + exports.changeProperties = changeProperties; + + var beforeObserverSet = new ObserverSet['default'](); + var observerSet = new ObserverSet['default'](); + var deferred = 0; + + // .......................................................... + // PROPERTY CHANGES + // + + /** + 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. + + 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. + + @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["__ember_meta__"]; + var watching = m && m.watching[keyName] > 0 || keyName === "length"; + var proto = m && m.proto; + var possibleDesc = obj[keyName]; + var desc = possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor ? possibleDesc : undefined; + + 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); + } + + /** + This function is called just after an object property has changed. + It will notify any observers and clear caches among other things. + + 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. + + @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["__ember_meta__"]; + var watching = m && m.watching[keyName] > 0 || keyName === "length"; + var proto = m && m.proto; + var possibleDesc = obj[keyName]; + var desc = possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor ? possibleDesc : undefined; + + if (proto === obj) { + return; + } + + // shouldn't this mean that we're watching this key? + if (desc && desc.didChange) { + desc.didChange(obj, keyName); + } + + if (!watching && keyName !== "length") { + return; + } + + if (m && m.deps && m.deps[keyName]) { + dependentKeysDidChange(obj, keyName, m); + } + + chainsDidChange(obj, keyName, m, false); + notifyObservers(obj, keyName); + } + + var WILL_SEEN, DID_SEEN; + // 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; + } + + var deps; + if (meta && meta.deps && (deps = meta.deps[depKey])) { + var seen = WILL_SEEN; + var top = !seen; + + if (top) { + seen = WILL_SEEN = {}; + } + + iterDeps(propertyWillChange, obj, deps, depKey, seen, meta); + + if (top) { + WILL_SEEN = null; + } + } + } + + // called whenever a property has just changed to update dependent keys + function dependentKeysDidChange(obj, depKey, meta) { + if (obj.isDestroying) { + return; + } + + var deps; + if (meta && meta.deps && (deps = meta.deps[depKey])) { + var seen = DID_SEEN; + var top = !seen; + + if (top) { + seen = DID_SEEN = {}; + } + + iterDeps(propertyDidChange, obj, deps, depKey, seen, meta); + + if (top) { + DID_SEEN = null; + } + } + } + + function keysOf(obj) { + var keys = []; + + for (var key in obj) { + keys.push(key); + } + + return keys; + } + + function iterDeps(method, obj, deps, depKey, seen, meta) { + var keys, key, i, possibleDesc, desc; + var guid = utils.guidFor(obj); + var current = seen[guid]; + + if (!current) { + current = seen[guid] = {}; + } + + if (current[depKey]) { + return; + } + + current[depKey] = true; + + if (deps) { + keys = keysOf(deps); + for (i = 0; i < keys.length; i++) { + key = keys[i]; + possibleDesc = obj[key]; + desc = possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor ? possibleDesc : undefined; + + 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]; + var events = []; + var 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; + } + + var nodes = m.chainWatchers[keyName]; + var events = suppressEvents ? null : []; + var i, l; + + for (i = 0, l = nodes.length; i < l; i++) { + nodes[i].didChange(events); + } + + if (suppressEvents) { + return; + } + + for (i = 0, l = events.length; i < l; i += 2) { + propertyDidChange(events[i], events[i + 1]); + } + } + + function overrideChains(obj, keyName, m) { + chainsDidChange(obj, keyName, m, true); + } + + /** + @method beginPropertyChanges + @chainable + @private + */ + function beginPropertyChanges() { + deferred++; + } + + /** + @method endPropertyChanges + @private + */ + function endPropertyChanges() { + deferred--; + if (deferred <= 0) { + beforeObserverSet.clear(); + observerSet.flush(); + } + } + + /** + Make a series of property changes together in an + exception-safe way. + + ```javascript + Ember.changeProperties(function() { + obj1.set('foo', mayBlowUpWhenSet); + obj2.set('bar', baz); + }); + ``` + + @method changeProperties + @param {Function} callback + @param [binding] + */ + function changeProperties(callback, binding) { + beginPropertyChanges(); + utils.tryFinally(callback, endPropertyChanges, binding); + } + + function notifyBeforeObservers(obj, keyName) { + if (obj.isDestroying) { + return; + } + + var eventName = keyName + ":before"; + var listeners, added; + if (deferred) { + listeners = beforeObserverSet.add(obj, keyName, eventName); + added = ember_metal__events.accumulateListeners(obj, eventName, listeners); + ember_metal__events.sendEvent(obj, eventName, [obj, keyName], added); + } else { + ember_metal__events.sendEvent(obj, eventName, [obj, keyName]); + } + } + + function notifyObservers(obj, keyName) { + if (obj.isDestroying) { + return; + } + + var eventName = keyName + ":change"; + var listeners; + if (deferred) { + listeners = observerSet.add(obj, keyName, eventName); + ember_metal__events.accumulateListeners(obj, eventName, listeners); + } else { + ember_metal__events.sendEvent(obj, eventName, [obj, keyName]); + } + } + +}); +enifed('ember-metal/property_get', ['exports', 'ember-metal/core', 'ember-metal/error', 'ember-metal/path_cache', 'ember-metal/platform/define_property', 'ember-metal/is_none'], function (exports, Ember, EmberError, path_cache, define_property, isNone) { + + 'use strict'; + + exports.get = get; + exports.normalizeTuple = normalizeTuple; + exports._getPath = _getPath; + exports.getWithDefault = getWithDefault; + + var FIRST_KEY = /^([^\.]+)/; + + // .......................................................... + // 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. + + /** + 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. + + 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.) + + 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. + + Note that if the object itself is `undefined`, this method will throw + an error. + + @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`. + */ + function get(obj, keyName) { + // Helpers that operate with 'this' within an #each + if (keyName === "") { + return obj; + } + + if (!keyName && "string" === typeof obj) { + keyName = obj; + obj = Ember['default'].lookup; + } + + Ember['default'].assert("Cannot call get with " + keyName + " key.", !!keyName); + Ember['default'].assert("Cannot call get with '" + keyName + "' on an undefined object.", obj !== undefined); + + if (isNone['default'](obj)) { + return _getPath(obj, keyName); + } + + var meta = obj["__ember_meta__"]; + var possibleDesc = obj[keyName]; + var desc = possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor ? possibleDesc : undefined; + var ret; + + if (desc === undefined && path_cache.isPath(keyName)) { + return _getPath(obj, keyName); + } + + if (desc) { + return desc.get(obj, keyName); + } else { + + if (define_property.hasPropertyAccessors && meta && meta.watching[keyName] > 0) { + ret = meta.values[keyName]; + } else { + ret = obj[keyName]; + } + + if (ret === undefined && "object" === typeof obj && !(keyName in obj) && "function" === typeof obj.unknownProperty) { + return obj.unknownProperty(keyName); + } + + return ret; + } + } + + /** + 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 capital letter not defined on the + target). + + @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_cache.hasThis(path); + var isGlobal = !hasThis && path_cache.isGlobal(path); + var key; + + if (!target && !isGlobal) { + return [undefined, ""]; + } + + if (hasThis) { + path = path.slice(5); + } + + if (!target || isGlobal) { + target = Ember['default'].lookup; + } + + if (isGlobal && path_cache.isPath(path)) { + key = path.match(FIRST_KEY)[0]; + target = get(target, key); + path = path.slice(key.length + 1); + } + + // must return some kind of path to be valid else other things will break. + validateIsPath(path); + + return [target, path]; + } + + function validateIsPath(path) { + if (!path || path.length === 0) { + throw new EmberError['default']("Object in path " + path + " could not be found or was destroyed."); + } + } + function _getPath(root, path) { + var hasThis, parts, tuple, idx, len; + + // detect complicated paths and normalize them + hasThis = path_cache.hasThis(path); + + if (!root || hasThis) { + tuple = normalizeTuple(root, path); + root = tuple[0]; + path = tuple[1]; + tuple.length = 0; + } + + 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; + } + } + return root; + } + + function getWithDefault(root, key, defaultValue) { + var value = get(root, key); + + if (value === undefined) { + return defaultValue; + } + return value; + } + + exports['default'] = get; + +}); +enifed('ember-metal/property_set', ['exports', 'ember-metal/core', 'ember-metal/property_get', 'ember-metal/property_events', 'ember-metal/properties', 'ember-metal/error', 'ember-metal/path_cache', 'ember-metal/platform/define_property'], function (exports, Ember, property_get, property_events, properties, EmberError, path_cache, define_property) { + + 'use strict'; + + exports.set = set; + exports.trySet = trySet; + + function set(obj, keyName, value, tolerant) { + if (typeof obj === "string") { + Ember['default'].assert("Path '" + obj + "' must be global if no obj is given.", path_cache.isGlobalPath(obj)); + value = keyName; + keyName = obj; + obj = Ember['default'].lookup; + } + + Ember['default'].assert("Cannot call set with '" + keyName + "' key.", !!keyName); + + if (obj === Ember['default'].lookup) { + return setPath(obj, keyName, value, tolerant); + } + + var meta, possibleDesc, desc; + if (obj) { + meta = obj["__ember_meta__"]; + possibleDesc = obj[keyName]; + desc = possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor ? possibleDesc : undefined; + } + + var isUnknown, currentValue; + if ((!obj || desc === undefined) && path_cache.isPath(keyName)) { + return setPath(obj, keyName, value, tolerant); + } + + Ember['default'].assert("You need to provide an object and key to `set`.", !!obj && keyName !== undefined); + Ember['default'].assert("calling set on destroyed object", !obj.isDestroyed); + + if (desc) { + desc.set(obj, keyName, value); + } else { + + if (obj !== null && value !== undefined && typeof obj === "object" && obj[keyName] === value) { + return value; + } + + isUnknown = "object" === typeof obj && !(keyName in obj); + + // setUnknownProperty is called if `obj` is an object, + // the property does not already exist, and the + // `setUnknownProperty` method exists on the object + if (isUnknown && "function" === typeof obj.setUnknownProperty) { + obj.setUnknownProperty(keyName, value); + } else if (meta && meta.watching[keyName] > 0) { + if (meta.proto !== obj) { + + if (define_property.hasPropertyAccessors) { + currentValue = meta.values[keyName]; + } else { + currentValue = obj[keyName]; + } + } + // only trigger a change if the value has changed + if (value !== currentValue) { + property_events.propertyWillChange(obj, keyName); + + if (define_property.hasPropertyAccessors) { + if (currentValue === undefined && !(keyName in obj) || !Object.prototype.propertyIsEnumerable.call(obj, keyName)) { + properties.defineProperty(obj, keyName, null, value); // setup mandatory setter + } else { + meta.values[keyName] = value; + } + } else { + obj[keyName] = value; + } + property_events.propertyDidChange(obj, keyName); + } + } else { + obj[keyName] = value; + } + } + return value; + } + + function setPath(root, path, value, tolerant) { + var keyName; + + // get the last part of the path + keyName = path.slice(path.lastIndexOf(".") + 1); + + // get the first part of the part + path = path === keyName ? keyName : path.slice(0, path.length - (keyName.length + 1)); + + // unless the path is this, look up the first part to + // get the root + if (path !== "this") { + root = property_get._getPath(root, path); + } + + if (!keyName || keyName.length === 0) { + throw new EmberError['default']("Property set failed: You passed an empty path"); + } + + if (!root) { + if (tolerant) { + return; + } else { + throw new EmberError['default']("Property set failed: object in path \"" + path + "\" could not be found or was destroyed."); + } + } + + return set(root, keyName, value); + } + + /** + 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 + */ + function trySet(root, path, value) { + return set(root, path, value, true); + } + +}); +enifed('ember-metal/run_loop', ['exports', 'ember-metal/core', 'ember-metal/utils', 'ember-metal/array', 'ember-metal/property_events', 'backburner'], function (exports, Ember, utils, array, property_events, Backburner) { + + 'use strict'; + + function onBegin(current) { + run.currentRunLoop = current; + } + + function onEnd(current, next) { + run.currentRunLoop = next; + } + + // ES6TODO: should Backburner become es6? + var backburner = new Backburner['default'](['sync', 'actions', 'destroy'], { + GUID_KEY: utils.GUID_KEY, + sync: { + before: property_events.beginPropertyChanges, + after: property_events.endPropertyChanges + }, + defaultQueue: 'actions', + onBegin: onBegin, + onEnd: onEnd, + onErrorTarget: Ember['default'], + onErrorMethod: 'onerror' + }); + + // .......................................................... + // run - this is ideally the only public API the dev sees + // + + /** + Runs the passed target and method inside of a RunLoop, ensuring any + deferred actions including bindings and views updates are flushed at the + end. + + 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. + + ```javascript + run(function() { + // code to be executed within a RunLoop + }); + ``` + + @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. + */ + exports['default'] = run; + function run() { + return backburner.run.apply(backburner, arguments); + } + + /** + 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. + + Please note: This is not for normal usage, and should be used sparingly. + + If invoked when not within a run loop: + + ```javascript + run.join(function() { + // creates a new run-loop + }); + ``` + + Alternatively, if called within an existing run loop: + + ```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. + }); + }); + ``` + + @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 () { + return backburner.join.apply(backburner, arguments); + }; + + /** + Allows you to specify which context to call the specified function in while + adding the execution of that function to the Ember run loop. This ability + makes this method a great way to asynchronously integrate third-party libraries + into your Ember application. + + `run.bind` takes two main arguments, the desired context and the function to + invoke in that context. Any additional arguments will be supplied as arguments + to the function that is passed in. + + Let's use the creation of a TinyMCE component as an example. Currently, + TinyMCE provides a setup configuration option we can use to do some processing + after the TinyMCE instance is initialized but before it is actually rendered. + We can use that setup option to do some additional setup for our component. + The component itself could look something like the following: + + ```javascript + App.RichTextEditorComponent = Ember.Component.extend({ + initializeTinyMCE: Ember.on('didInsertElement', function() { + tinymce.init({ + selector: '#' + this.$().prop('id'), + setup: Ember.run.bind(this, this.setupEditor) + }); + }), + + setupEditor: function(editor) { + this.set('editor', editor); + + editor.on('change', function() { + console.log('content changed!'); + }); + } + }); + ``` + + In this example, we use Ember.run.bind to bind the setupEditor method to the + context of the App.RichTextEditorComponent and to have the invocation of that + method be safely handled and executed by the Ember run loop. + + @method bind + @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 {Function} returns a new function that will always have a particular context + @since 1.4.0 + */ + run.bind = function () { + for (var _len = arguments.length, curried = Array(_len), _key = 0; _key < _len; _key++) { + curried[_key] = arguments[_key]; + } + + return function () { + for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + args[_key2] = arguments[_key2]; + } + + return run.join.apply(run, curried.concat(args)); + }; + }; + + run.backburner = backburner; + run.currentRunLoop = null; + run.queues = backburner.queueNames; + + /** + 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()`. + + ```javascript + run.begin(); + // code to be executed within a RunLoop + run.end(); + ``` + + @method begin + @return {void} + */ + run.begin = function () { + backburner.begin(); + }; + + /** + 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()`. + + ```javascript + run.begin(); + // code to be executed within a RunLoop + run.end(); + ``` + + @method end + @return {void} + */ + run.end = function () { + backburner.end(); + }; + + /** + 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. + + @property queues + @type Array + @default ['sync', 'actions', 'destroy'] + */ + + /** + 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. + + 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. + + ```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'); + }); + + run.schedule('actions', this, function() { + // this will be executed in the 'actions' queue, after bindings have synced. + console.log('scheduled on actions queue'); + }); + + // 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 + ``` + + @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 () { + checkAutoRun(); + backburner.schedule.apply(backburner, arguments); + }; + + // Used by global test teardown + run.hasScheduledTimers = function () { + return backburner.hasTimers(); + }; + + // Used by global test teardown + run.cancelTimers = function () { + backburner.cancelTimers(); + }; + + /** + 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. + + 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). + + ```javascript + run.sync(); + ``` + + @method sync + @return {void} + */ + run.sync = function () { + if (backburner.currentInstance) { + backburner.currentInstance.queues.sync.flush(); + } + }; + + /** + Invokes the passed target/method and optional arguments after a specified + period of time. The last parameter of this method must always be a number + of milliseconds. + + 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. + + ```javascript + run.later(myContext, function() { + // code here will execute within a RunLoop in about 500ms with this == myContext + }, 500); + ``` + + @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 {*} Timer information for use in cancelling, see `run.cancel`. + */ + run.later = function () { + return backburner.later.apply(backburner, arguments); + }; + + /** + Schedule a function to run one time during the current RunLoop. This is equivalent + to calling `scheduleOnce` with the "actions" queue. + + @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`. + */ + run.once = function () { + for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { + args[_key3] = arguments[_key3]; + } + + checkAutoRun(); + args.unshift('actions'); + return backburner.scheduleOnce.apply(backburner, args); + }; + + /** + 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). + + Note that although you can pass optional arguments these will not be + considered when looking for duplicates. New arguments will replace previous + calls. + + ```javascript + function sayHi() { + console.log('hi'); + } + + run(function() { + run.scheduleOnce('afterRender', myContext, sayHi); + run.scheduleOnce('afterRender', myContext, sayHi); + // sayHi will only be executed once, in the afterRender queue of the RunLoop + }); + ``` + + 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.: + + ```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. + ``` + + Available queues, and their order, can be found at `run.queues` + + @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 () { + checkAutoRun(); + return backburner.scheduleOnce.apply(backburner, arguments); + }; + + /** + 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. + + ```javascript + run.next(myContext, function() { + // code to be executed in the next run loop, + // which will be scheduled after the current one + }); + ``` + + 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. + + 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: + + ```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. + } + }); + ``` + + 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`. + + @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 () { + for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { + args[_key4] = arguments[_key4]; + } + + args.push(1); + return backburner.later.apply(backburner, args); + }; + + /** + Cancels a scheduled item. Must be a value returned by `run.later()`, + `run.once()`, `run.next()`, `run.debounce()`, or + `run.throttle()`. + + ```javascript + var runNext = run.next(myContext, function() { + // will not be executed + }); + + run.cancel(runNext); + + var runLater = run.later(myContext, function() { + // will not be executed + }, 500); + + run.cancel(runLater); + + var runOnce = run.once(myContext, function() { + // will not be executed + }); + + run.cancel(runOnce); + + var throttle = run.throttle(myContext, function() { + // will not be executed + }, 1, false); + + run.cancel(throttle); + + var debounce = run.debounce(myContext, function() { + // will not be executed + }, 1); + + run.cancel(debounce); + + 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); + ``` + + @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 + function whoRan() { + console.log(this.name + ' ran.'); + } + + var myContext = { name: 'debounce' }; + + run.debounce(myContext, whoRan, 150); + + // less than 150ms passes + run.debounce(myContext, whoRan, 150); + + // 150ms passes + // whoRan is invoked with context myContext + // console logs 'debounce ran.' one time. + ``` + + Immediate allows you to run the function immediately, but debounce + other calls for this function until the wait time has elapsed. If + `debounce` is called again before the specified time has elapsed, + the timer is reset and the entire period must pass again before + the method can be called again. + + ```javascript + function whoRan() { + console.log(this.name + ' ran.'); + } + + var myContext = { name: 'debounce' }; + + run.debounce(myContext, whoRan, 150, true); + + // console logs 'debounce ran.' one time immediately. + // 100ms passes + run.debounce(myContext, whoRan, 150, true); + + // 150ms passes and nothing else is logged to the console and + // the debouncee is no longer being watched + run.debounce(myContext, whoRan, 150, true); + + // 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 + + ``` + + @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`. + */ + run.debounce = function () { + return backburner.debounce.apply(backburner, arguments); + }; + + /** + Ensure that the target method is never called more frequently than + the specified spacing period. The target method is called immediately. + + ```javascript + function whoRan() { + console.log(this.name + ' ran.'); + } + + var myContext = { name: 'throttle' }; + + run.throttle(myContext, whoRan, 150); + // whoRan is invoked with context myContext + // console logs 'throttle ran.' + + // 50ms passes + run.throttle(myContext, whoRan, 150); + + // 50ms passes + run.throttle(myContext, whoRan, 150); + + // 150ms passes + run.throttle(myContext, whoRan, 150); + // whoRan is invoked with context myContext + // console logs 'throttle ran.' + ``` + + @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`. + */ + run.throttle = function () { + return backburner.throttle.apply(backburner, arguments); + }; + + // Make sure it's not an autorun during testing + function checkAutoRun() { + if (!run.currentRunLoop) { + Ember['default'].assert('You have turned on testing mode, which disabled the run-loop\'s autorun.\n You will need to wrap any code with asynchronous side-effects in a run', !Ember['default'].testing); + } + } + + /** + Add a new named queue after the specified queue. + + The queue to add will only be added once. + + @method _addQueue + @param {String} name the name of the queue to add. + @param {String} after the name of the queue to add after. + @private + */ + run._addQueue = function (name, after) { + if (array.indexOf.call(run.queues, name) === -1) { + run.queues.splice(array.indexOf.call(run.queues, after) + 1, 0, name); + } + }; + /* queue, target, method */ /*target, method*/ /*queue, target, method*/ + +}); +enifed('ember-metal/set_properties', ['exports', 'ember-metal/property_events', 'ember-metal/property_set', 'ember-metal/keys'], function (exports, property_events, property_set, keys) { + + 'use strict'; + + + exports['default'] = setProperties; + function setProperties(obj, properties) { + if (!properties || typeof properties !== "object") { + return obj; + } + property_events.changeProperties(function () { + var props = keys['default'](properties); + var propertyName; + + for (var i = 0, l = props.length; i < l; i++) { + propertyName = props[i]; + + property_set.set(obj, propertyName, properties[propertyName]); + } + }); + return obj; + } + +}); +enifed('ember-metal/streams/conditional', ['exports', 'ember-metal/streams/stream', 'ember-metal/streams/utils', 'ember-metal/platform/create'], function (exports, Stream, utils, create) { + + 'use strict'; + + + + exports['default'] = conditional; + + function conditional(test, consequent, alternate) { + if (utils.isStream(test)) { + return new ConditionalStream(test, consequent, alternate); + } else { + if (test) { + return consequent; + } else { + return alternate; + } + } + } + + function ConditionalStream(test, consequent, alternate) { + this.init(); + + this.oldTestResult = undefined; + this.test = test; + this.consequent = consequent; + this.alternate = alternate; + } + + ConditionalStream.prototype = create['default'](Stream['default'].prototype); + + ConditionalStream.prototype.valueFn = function () { + var oldTestResult = this.oldTestResult; + var newTestResult = !!utils.read(this.test); + + if (newTestResult !== oldTestResult) { + switch (oldTestResult) { + case true: + utils.unsubscribe(this.consequent, this.notify, this);break; + case false: + utils.unsubscribe(this.alternate, this.notify, this);break; + case undefined: + utils.subscribe(this.test, this.notify, this); + } + + switch (newTestResult) { + case true: + utils.subscribe(this.consequent, this.notify, this);break; + case false: + utils.subscribe(this.alternate, this.notify, this); + } + + this.oldTestResult = newTestResult; + } + + return newTestResult ? utils.read(this.consequent) : utils.read(this.alternate); + }; + +}); +enifed('ember-metal/streams/simple', ['exports', 'ember-metal/merge', 'ember-metal/streams/stream', 'ember-metal/platform/create', 'ember-metal/streams/utils'], function (exports, merge, Stream, create, utils) { + + 'use strict'; + + function SimpleStream(source) { + this.init(); + this.source = source; + + if (utils.isStream(source)) { + source.subscribe(this._didChange, this); + } + } + + SimpleStream.prototype = create['default'](Stream['default'].prototype); + + merge['default'](SimpleStream.prototype, { + valueFn: function () { + return utils.read(this.source); + }, + + setValue: function (value) { + var source = this.source; + + if (utils.isStream(source)) { + source.setValue(value); + } + }, + + setSource: function (nextSource) { + var prevSource = this.source; + if (nextSource !== prevSource) { + if (utils.isStream(prevSource)) { + prevSource.unsubscribe(this._didChange, this); + } + + if (utils.isStream(nextSource)) { + nextSource.subscribe(this._didChange, this); + } + + this.source = nextSource; + this.notify(); + } + }, + + _didChange: function () { + this.notify(); + }, + + _super$destroy: Stream['default'].prototype.destroy, + + destroy: function () { + if (this._super$destroy()) { + if (utils.isStream(this.source)) { + this.source.unsubscribe(this._didChange, this); + } + this.source = undefined; + return true; + } + } + }); + + exports['default'] = SimpleStream; + +}); +enifed('ember-metal/streams/stream', ['exports', 'ember-metal/platform/create', 'ember-metal/path_cache'], function (exports, create, path_cache) { + + 'use strict'; + + function Subscriber(callback, context) { + this.next = null; + this.prev = null; + this.callback = callback; + this.context = context; + } + + Subscriber.prototype.removeFrom = function (stream) { + var next = this.next; + var prev = this.prev; + + if (prev) { + prev.next = next; + } else { + stream.subscriberHead = next; + } + + if (next) { + next.prev = prev; + } else { + stream.subscriberTail = prev; + } + }; + + /* + @public + @class Stream + @namespace Ember.stream + @constructor + */ + function Stream(fn) { + this.init(); + this.valueFn = fn; + } + + Stream.prototype = { + isStream: true, + + init: function () { + this.state = "dirty"; + this.cache = undefined; + this.subscriberHead = null; + this.subscriberTail = null; + this.children = undefined; + this._label = undefined; + }, + + get: function (path) { + var firstKey = path_cache.getFirstKey(path); + var tailPath = path_cache.getTailPath(path); + + if (this.children === undefined) { + this.children = create['default'](null); + } + + var keyStream = this.children[firstKey]; + + if (keyStream === undefined) { + keyStream = this._makeChildStream(firstKey, path); + this.children[firstKey] = keyStream; + } + + if (tailPath === undefined) { + return keyStream; + } else { + return keyStream.get(tailPath); + } + }, + + value: function () { + if (this.state === "clean") { + return this.cache; + } else if (this.state === "dirty") { + this.state = "clean"; + return this.cache = this.valueFn(); + } + // TODO: Ensure value is never called on a destroyed stream + // so that we can uncomment this assertion. + // + // Ember.assert("Stream error: value was called in an invalid state: " + this.state); + }, + + valueFn: function () { + throw new Error("Stream error: valueFn not implemented"); + }, + + setValue: function () { + throw new Error("Stream error: setValue not implemented"); + }, + + notify: function () { + this.notifyExcept(); + }, + + notifyExcept: function (callbackToSkip, contextToSkip) { + if (this.state === "clean") { + this.state = "dirty"; + this._notifySubscribers(callbackToSkip, contextToSkip); + } + }, + + subscribe: function (callback, context) { + var subscriber = new Subscriber(callback, context, this); + if (this.subscriberHead === null) { + this.subscriberHead = this.subscriberTail = subscriber; + } else { + var tail = this.subscriberTail; + tail.next = subscriber; + subscriber.prev = tail; + this.subscriberTail = subscriber; + } + + var stream = this; + return function () { + subscriber.removeFrom(stream); + }; + }, + + unsubscribe: function (callback, context) { + var subscriber = this.subscriberHead; + + while (subscriber) { + var next = subscriber.next; + if (subscriber.callback === callback && subscriber.context === context) { + subscriber.removeFrom(this); + } + subscriber = next; + } + }, + + _notifySubscribers: function (callbackToSkip, contextToSkip) { + var subscriber = this.subscriberHead; + + while (subscriber) { + var next = subscriber.next; + + var callback = subscriber.callback; + var context = subscriber.context; + + subscriber = next; + + if (callback === callbackToSkip && context === contextToSkip) { + continue; + } + + if (context === undefined) { + callback(this); + } else { + callback.call(context, this); + } + } + }, + + destroy: function () { + if (this.state !== "destroyed") { + this.state = "destroyed"; + + var children = this.children; + for (var key in children) { + children[key].destroy(); + } + + this.subscriberHead = this.subscriberTail = null; + + return true; + } + }, + + isGlobal: function () { + var stream = this; + while (stream !== undefined) { + if (stream._isRoot) { + return stream._isGlobal; + } + stream = stream.source; + } + } + }; + + exports['default'] = Stream; + +}); +enifed('ember-metal/streams/stream_binding', ['exports', 'ember-metal/platform/create', 'ember-metal/merge', 'ember-metal/run_loop', 'ember-metal/streams/stream'], function (exports, create, merge, run, Stream) { + + 'use strict'; + + function StreamBinding(stream) { + Ember.assert("StreamBinding error: tried to bind to object that is not a stream", stream && stream.isStream); + + this.init(); + this.stream = stream; + this.senderCallback = undefined; + this.senderContext = undefined; + this.senderValue = undefined; + + stream.subscribe(this._onNotify, this); + } + + StreamBinding.prototype = create['default'](Stream['default'].prototype); + + merge['default'](StreamBinding.prototype, { + valueFn: function () { + return this.stream.value(); + }, + + _onNotify: function () { + this._scheduleSync(undefined, undefined, this); + }, + + setValue: function (value, callback, context) { + this._scheduleSync(value, callback, context); + }, + + _scheduleSync: function (value, callback, context) { + if (this.senderCallback === undefined && this.senderContext === undefined) { + this.senderCallback = callback; + this.senderContext = context; + this.senderValue = value; + run['default'].schedule("sync", this, this._sync); + } else if (this.senderContext !== this) { + this.senderCallback = callback; + this.senderContext = context; + this.senderValue = value; + } + }, + + _sync: function () { + if (this.state === "destroyed") { + return; + } + + if (this.senderContext !== this) { + this.stream.setValue(this.senderValue); + } + + var senderCallback = this.senderCallback; + var senderContext = this.senderContext; + this.senderCallback = undefined; + this.senderContext = undefined; + this.senderValue = undefined; + + // Force StreamBindings to always notify + this.state = "clean"; + + this.notifyExcept(senderCallback, senderContext); + }, + + _super$destroy: Stream['default'].prototype.destroy, + + destroy: function () { + if (this._super$destroy()) { + this.stream.unsubscribe(this._onNotify, this); + return true; + } + } + }); + + exports['default'] = StreamBinding; + +}); +enifed('ember-metal/streams/utils', ['exports', './stream'], function (exports, Stream) { + + 'use strict'; + + exports.isStream = isStream; + exports.subscribe = subscribe; + exports.unsubscribe = unsubscribe; + exports.read = read; + exports.readArray = readArray; + exports.readHash = readHash; + exports.scanArray = scanArray; + exports.scanHash = scanHash; + exports.concat = concat; + exports.chain = chain; + + function isStream(object) { + return object && object.isStream; + } + + /* + A method of subscribing to a stream which is safe for use with a non-stream + object. If a non-stream object is passed, the function does nothing. + + @public + @for Ember.stream + @function subscribe + @param {Object|Stream} object object or stream to potentially subscribe to + @param {Function} callback function to run when stream value changes + @param {Object} [context] the callback will be executed with this context if it + is provided + */ + function subscribe(object, callback, context) { + if (object && object.isStream) { + object.subscribe(callback, context); + } + } + + /* + A method of unsubscribing from a stream which is safe for use with a non-stream + object. If a non-stream object is passed, the function does nothing. + + @public + @for Ember.stream + @function unsubscribe + @param {Object|Stream} object object or stream to potentially unsubscribe from + @param {Function} callback function originally passed to `subscribe()` + @param {Object} [context] object originally passed to `subscribe()` + */ + function unsubscribe(object, callback, context) { + if (object && object.isStream) { + object.unsubscribe(callback, context); + } + } + + /* + Retrieve the value of a stream, or in the case a non-stream object is passed, + return the object itself. + + @public + @for Ember.stream + @function read + @param {Object|Stream} object object to return the value of + @return the stream's current value, or the non-stream object itself + */ + function read(object) { + if (object && object.isStream) { + return object.value(); + } else { + return object; + } + } + + /* + Map an array, replacing any streams with their values. + + @public + @for Ember.stream + @function readArray + @param {Array} array The array to read values from + @return {Array} a new array of the same length with the values of non-stream + objects mapped from their original positions untouched, and + the values of stream objects retaining their original position + and replaced with the stream's current value. + */ + function readArray(array) { + var length = array.length; + var ret = new Array(length); + for (var i = 0; i < length; i++) { + ret[i] = read(array[i]); + } + return ret; + } + + /* + Map a hash, replacing any stream property values with the current value of that + stream. + + @public + @for Ember.stream + @function readHash + @param {Object} object The hash to read keys and values from + @return {Object} a new object with the same keys as the passed object. The + property values in the new object are the original values in + the case of non-stream objects, and the streams' current + values in the case of stream objects. + */ + function readHash(object) { + var ret = {}; + for (var key in object) { + ret[key] = read(object[key]); + } + return ret; + } + + /* + Check whether an array contains any stream values + + @public + @for Ember.stream + @function scanArray + @param {Array} array array given to a handlebars helper + @return {Boolean} `true` if the array contains a stream/bound value, `false` + otherwise + */ + function scanArray(array) { + var length = array.length; + var containsStream = false; + + for (var i = 0; i < length; i++) { + if (isStream(array[i])) { + containsStream = true; + break; + } + } + + return containsStream; + } + + /* + Check whether a hash has any stream property values + + @public + @for Ember.stream + @function scanHash + @param {Object} hash "hash" argument given to a handlebars helper + @return {Boolean} `true` if the object contains a stream/bound value, `false` + otherwise + */ + function scanHash(hash) { + var containsStream = false; + + for (var prop in hash) { + if (isStream(hash[prop])) { + containsStream = true; + break; + } + } + + return containsStream; + } + + /* + Join an array, with any streams replaced by their current values + + @public + @for Ember.stream + @function concat + @param {Array} array An array containing zero or more stream objects and + zero or more non-stream objects + @param {String} separator string to be used to join array elements + @return {String} String with array elements concatenated and joined by the + provided separator, and any stream array members having been + replaced by the current value of the stream + */ + function concat(array, separator) { + // TODO: Create subclass ConcatStream < Stream. Defer + // subscribing to streams until the value() is called. + var hasStream = scanArray(array); + if (hasStream) { + var i, l; + var stream = new Stream['default'](function () { + return readArray(array).join(separator); + }); + + for (i = 0, l = array.length; i < l; i++) { + subscribe(array[i], stream.notify, stream); + } + + return stream; + } else { + return array.join(separator); + } + } + + /* + Generate a new stream by providing a source stream and a function that can + be used to transform the stream's value. In the case of a non-stream object, + returns the result of the function. + + The value to transform would typically be available to the function you pass + to `chain()` via scope. For example: + + ```javascript + var source = ...; // stream returning a number + // or a numeric (non-stream) object + var result = chain(source, function() { + var currentValue = read(source); + return currentValue + 1; + }); + ``` + + In the example, result is a stream if source is a stream, or a number of + source was numeric. + + @public + @for Ember.stream + @function chain + @param {Object|Stream} value A stream or non-stream object + @param {Function} fn function to be run when the stream value changes, or to + be run once in the case of a non-stream object + @return {Object|Stream} In the case of a stream `value` parameter, a new + stream that will be updated with the return value of + the provided function `fn`. In the case of a + non-stream object, the return value of the provided + function `fn`. + */ + function chain(value, fn) { + if (isStream(value)) { + var stream = new Stream['default'](fn); + subscribe(value, stream.notify, stream); + return stream; + } else { + return fn(); + } + } + +}); +enifed('ember-metal/utils', ['exports', 'ember-metal/core', 'ember-metal/platform/create', 'ember-metal/platform/define_property', 'ember-metal/array'], function (exports, Ember, o_create, define_property, array) { + + + exports.uuid = uuid; + exports.generateGuid = generateGuid; + exports.guidFor = guidFor; + exports.getMeta = getMeta; + exports.setMeta = setMeta; + exports.metaPath = metaPath; + exports.wrap = wrap; + exports.makeArray = makeArray; + exports.tryInvoke = tryInvoke; + exports.inspect = inspect; + exports.apply = apply; + exports.applyStr = applyStr; + exports.meta = meta; + exports.typeOf = typeOf; + exports.isArray = isArray; + exports.canInvoke = canInvoke; + + "REMOVE_USE_STRICT: true"; /** + @module ember-metal + */ + + /** + Previously we used `Ember.$.uuid`, however `$.uuid` has been removed from + jQuery master. We'll just bootstrap our own uuid now. + + @private + @return {Number} the uuid + */ + var _uuid = 0; + + /** + Generates a universally unique identifier. This method + is used internally by Ember for assisting with + the generation of GUID's and other unique identifiers + such as `bind-attr` data attributes. + + @public + @return {Number} [description] + */ + function uuid() { + return ++_uuid; + } + + /** + Prefix used for guids through out Ember. + @private + @property GUID_PREFIX + @for Ember + @type String + @final + */ + var GUID_PREFIX = "ember"; + + // Used for guid generation... + var numberCache = []; + var stringCache = {}; + + /** + Strongly hint runtimes to intern the provided string. + + When do I need to use this function? + + For the most part, never. Pre-mature optimization is bad, and often the + runtime does exactly what you need it to, and more often the trade-off isn't + worth it. + + Why? + + Runtimes store strings in at least 2 different representations: + Ropes and Symbols (interned strings). The Rope provides a memory efficient + data-structure for strings created from concatenation or some other string + manipulation like splitting. + + Unfortunately checking equality of different ropes can be quite costly as + runtimes must resort to clever string comparison algorithms. These + algorithms typically cost in proportion to the length of the string. + Luckily, this is where the Symbols (interned strings) shine. As Symbols are + unique by their string content, equality checks can be done by pointer + comparison. + + How do I know if my string is a rope or symbol? + + Typically (warning general sweeping statement, but truthy in runtimes at + present) static strings created as part of the JS source are interned. + Strings often used for comparisons can be interned at runtime if some + criteria are met. One of these criteria can be the size of the entire rope. + For example, in chrome 38 a rope longer then 12 characters will not + intern, nor will segments of that rope. + + Some numbers: http://jsperf.com/eval-vs-keys/8 + + Known Trick™ + + @private + @return {String} interned version of the provided string + */ + function intern(str) { + var obj = {}; + obj[str] = 1; + for (var key in obj) { + if (key === str) { + return key; + } + } + return str; + } + + /** + 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. + + @private + @property GUID_KEY + @for Ember + @type String + @final + */ + var GUID_KEY = intern("__ember" + +new Date()); + + var GUID_DESC = { + writable: true, + configurable: true, + enumerable: false, + value: null + }; + + var undefinedDescriptor = { + configurable: true, + writable: true, + enumerable: false, + value: undefined + }; + + var nullDescriptor = { + configurable: true, + writable: true, + enumerable: false, + value: null + }; + + var META_DESC = { + writable: true, + configurable: true, + enumerable: false, + value: null + }; + + var EMBER_META_PROPERTY = { + name: "__ember_meta__", + descriptor: META_DESC + }; + + var GUID_KEY_PROPERTY = { + name: GUID_KEY, + descriptor: nullDescriptor + }; + + var NEXT_SUPER_PROPERTY = { + name: "__nextSuper", + descriptor: undefinedDescriptor + }; + + 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; + if (obj.__defineNonEnumerable) { + obj.__defineNonEnumerable(GUID_KEY_PROPERTY); + } else { + define_property.defineProperty(obj, GUID_KEY, GUID_DESC); + } + } + } + return ret; + } + + /** + 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. + + 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) { + + // 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; + + // 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; + + case "string": + ret = stringCache[obj]; + + if (!ret) { + ret = stringCache[obj] = "st" + uuid(); + } + + return ret; + + case "boolean": + return obj ? "(true)" : "(false)"; + + default: + if (obj[GUID_KEY]) { + return obj[GUID_KEY]; + } + + if (obj === Object) { + return "(Object)"; + } + + if (obj === Array) { + return "(Array)"; + } + + ret = GUID_PREFIX + uuid(); + + if (obj[GUID_KEY] === null) { + obj[GUID_KEY] = ret; + } else { + GUID_DESC.value = ret; + + if (obj.__defineNonEnumerable) { + obj.__defineNonEnumerable(GUID_KEY_PROPERTY); + } else { + define_property.defineProperty(obj, GUID_KEY, GUID_DESC); + } + } + return ret; + } + } + + // .......................................................... + // META + // + function Meta(obj) { + this.watching = {}; + this.cache = undefined; + this.cacheMeta = undefined; + this.source = obj; + this.deps = undefined; + this.listeners = undefined; + this.mixins = undefined; + this.bindings = undefined; + this.chains = undefined; + this.values = undefined; + this.proto = undefined; + } + + Meta.prototype = { + chainWatchers: null // FIXME + }; + + if (!define_property.canDefineNonEnumerableProperties) { + // 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; + + // Without non-enumerable properties, meta objects will be output in JSON + // unless explicitly suppressed + Meta.prototype.toJSON = function () {}; + } + + // Placeholder for non-writable metas. + var EMPTY_META = new Meta(null); + + + if (define_property.hasPropertyAccessors) { + EMPTY_META.values = {}; + } + + + /** + Retrieves the meta hash for an object. If `writable` is true ensures the + hash is writable for this object as well. + + 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. + + @method meta + @for Ember + @private + + @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.__ember_meta__; + if (writable === false) { + return ret || EMPTY_META; + } + + if (!ret) { + if (define_property.canDefineNonEnumerableProperties) { + if (obj.__defineNonEnumerable) { + obj.__defineNonEnumerable(EMBER_META_PROPERTY); + } else { + define_property.defineProperty(obj, "__ember_meta__", META_DESC); + } + } + + ret = new Meta(obj); + + + if (define_property.hasPropertyAccessors) { + ret.values = {}; + } + + + obj.__ember_meta__ = ret; + } else if (ret.source !== obj) { + if (obj.__defineNonEnumerable) { + obj.__defineNonEnumerable(EMBER_META_PROPERTY); + } else { + define_property.defineProperty(obj, "__ember_meta__", META_DESC); + } + + ret = o_create['default'](ret); + ret.watching = o_create['default'](ret.watching); + ret.cache = undefined; + ret.cacheMeta = undefined; + ret.source = obj; + + + if (define_property.hasPropertyAccessors) { + ret.values = o_create['default'](ret.values); + } + + + obj["__ember_meta__"] = ret; + } + return ret; + } + function getMeta(obj, property) { + var _meta = meta(obj, false); + return _meta[property]; + } + + function setMeta(obj, property, value) { + var _meta = meta(obj, true); + _meta[property] = value; + return value; + } + + /** + @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. + + 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. + + 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. + + You can also pass false for `writable`, which will simply return + undefined if `prepareMetaPath` discovers any part of the path that + shared or undefined. + + @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['default'].deprecate("Ember.metaPath is deprecated and will be removed from future releases."); + var _meta = meta(obj, writable); + var keyName, value; + + for (var i = 0, l = path.length; i < l; i++) { + keyName = path[i]; + value = _meta[keyName]; + + if (!value) { + if (!writable) { + return undefined; + } + value = _meta[keyName] = { __ember_source__: obj }; + } else if (value.__ember_source__ !== obj) { + if (!writable) { + return undefined; + } + value = _meta[keyName] = o_create['default'](value); + value.__ember_source__ = obj; + } + + _meta = value; + } + + return value; + } + + /** + Wraps the passed function so that `this._super` will point to the superFunc + when the function is invoked. This is the primitive we use to implement + calls to super. + + @private + @method wrap + @for Ember + @param {Function} func The function to call + @param {Function} superFunc The super function. + @return {Function} wrapped function. + */ + function wrap(func, superFunc) { + function superWrapper() { + var ret; + var sup = this && this.__nextSuper; + var length = arguments.length; + + if (this) { + this.__nextSuper = superFunc; + } + + if (length === 0) { + ret = func.call(this); + } else if (length === 1) { + ret = func.call(this, arguments[0]); + } else if (length === 2) { + ret = func.call(this, arguments[0], arguments[1]); + } else { + var args = new Array(length); + for (var i = 0; i < length; i++) { + args[i] = arguments[i]; + } + ret = apply(this, func, args); + } + + if (this) { + this.__nextSuper = sup; + } + + return ret; + } + + superWrapper.wrappedFunction = func; + superWrapper.__ember_observes__ = func.__ember_observes__; + superWrapper.__ember_observesBefore__ = func.__ember_observesBefore__; + superWrapper.__ember_listens__ = func.__ember_listens__; + + return superWrapper; + } + + var EmberArray; + + /** + Returns true if the passed object is an array or Array-like. + + Ember Array Protocol: + + - the object has an objectAt property + - the object is a native Array + - the object is an Object, and has a length property + + Unlike `Ember.typeOf` this method returns true even if the passed object is + not formally array but appears to be array-like (i.e. implements `Ember.Array`) + + ```javascript + Ember.isArray(); // false + Ember.isArray([]); // true + Ember.isArray(Ember.ArrayProxy.create({ content: [] })); // true + ``` + + @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; + + if (typeof EmberArray === "undefined") { + modulePath = "ember-runtime/mixins/array"; + if (Ember['default'].__loader.registry[modulePath]) { + EmberArray = Ember['default'].__loader.require(modulePath)["default"]; + } + } + + if (!obj || obj.setInterval) { + return false; + } + if (Array.isArray && Array.isArray(obj)) { + return true; + } + if (EmberArray && EmberArray.detect(obj)) { + return true; + } + + type = typeOf(obj); + if ("array" === type) { + return true; + } + if (obj.length !== undefined && "object" === type) { + return true; + } + return false; + } + + /** + Forces the passed object to be part of an array. If the object is already + an array or array-like, it will return the object. Otherwise, it will add the object to + an array. If obj is `null` or `undefined`, it will return an empty array. + + ```javascript + Ember.makeArray(); // [] + Ember.makeArray(null); // [] + Ember.makeArray(undefined); // [] + Ember.makeArray('lindsay'); // ['lindsay'] + Ember.makeArray([1, 2, 42]); // [1, 2, 42] + + var controller = Ember.ArrayProxy.create({ content: [] }); + + Ember.makeArray(controller) === controller; // true + ``` + + @method makeArray + @for Ember + @param {Object} obj the object + @return {Array} + */ + function makeArray(obj) { + if (obj === null || obj === undefined) { + return []; + } + return isArray(obj) ? obj : [obj]; + } + + /** + Checks to see if the `methodName` exists on the `obj`. + + ```javascript + var foo = { bar: function() { return 'bar'; }, baz: null }; + + Ember.canInvoke(foo, 'bar'); // true + Ember.canInvoke(foo, 'baz'); // false + Ember.canInvoke(foo, 'bat'); // false + ``` + + @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"); + } + + /** + Checks to see if the `methodName` exists on the `obj`, + and if it does, invokes it with the arguments passed. + + ```javascript + var d = new Date('03/15/2013'); + + Ember.tryInvoke(d, 'getTime'); // 1363320000000 + Ember.tryInvoke(d, 'setFullYear', [2014]); // 1394856000000 + Ember.tryInvoke(d, 'noSuchMethod', [2014]); // undefined + ``` + + @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 { + // jscs:disable + try {} finally { + count++; + throw new Error("needsFinallyFixTest"); + } + // jscs:enable + } catch (e) {} + + return count !== 1; + })(); + + /** + Provides try/finally functionality, while working + around Safari's double finally bug. + + ```javascript + var tryable = function() { + someResource.lock(); + runCallback(); // May throw error. + }; + + var finalizer = function() { + someResource.unlock(); + }; + + Ember.tryFinally(tryable, finalizer); + ``` + + @method tryFinally + @deprecated Use JavaScript's native try/finally + @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 + */ + + var tryFinally; + if (needsFinallyFix) { + tryFinally = function (tryable, finalizer, binding) { + var result, finalResult, finalError; + + binding = binding || this; + + try { + result = tryable.call(binding); + } finally { + try { + finalResult = finalizer.call(binding); + } catch (e) { + finalError = e; + } + } + + if (finalError) { + throw finalError; + } + + return finalResult === undefined ? result : finalResult; + }; + } else { + tryFinally = function (tryable, finalizer, binding) { + var result, finalResult; + + binding = binding || this; + + try { + result = tryable.call(binding); + } finally { + finalResult = finalizer.call(binding); + } + + return finalResult === undefined ? result : finalResult; + }; + } + + var deprecatedTryFinally = function () { + Ember['default'].deprecate("tryFinally is deprecated. Please use JavaScript's native try/finally.", false); + return tryFinally.apply(this, arguments); + }; + + /** + Provides try/catch/finally functionality, while working + around Safari's double finally bug. + + ```javascript + var tryable = function() { + for (i = 0, l = listeners.length; i < l; i++) { + listener = listeners[i]; + beforeValues[i] = listener.before(name, time(), payload); + } + + return callback.call(binding); + }; + + var catchable = function(e) { + payload = payload || {}; + payload.exception = e; + }; + + var finalizer = function() { + for (i = 0, l = listeners.length; i < l; i++) { + listener = listeners[i]; + listener.after(name, time(), payload, beforeValues[i]); + } + }; + + Ember.tryCatchFinally(tryable, catchable, finalizer); + ``` + + @method tryCatchFinally + @deprecated Use JavaScript's native try/catch/finally instead + @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. + */ + var tryCatchFinally; + if (needsFinallyFix) { + tryCatchFinally = function (tryable, catchable, finalizer, binding) { + var result, finalResult, finalError; + + binding = binding || this; + + try { + result = tryable.call(binding); + } catch (error) { + result = catchable.call(binding, error); + } finally { + try { + finalResult = finalizer.call(binding); + } catch (e) { + finalError = e; + } + } + + if (finalError) { + throw finalError; + } + + return finalResult === undefined ? result : finalResult; + }; + } else { + tryCatchFinally = function (tryable, catchable, finalizer, binding) { + var result, finalResult; + + binding = binding || this; + + try { + result = tryable.call(binding); + } catch (error) { + result = catchable.call(binding, error); + } finally { + finalResult = finalizer.call(binding); + } + + return finalResult === undefined ? result : finalResult; + }; + } + + var deprecatedTryCatchFinally = function () { + Ember['default'].deprecate("tryCatchFinally is deprecated. Please use JavaScript's native try/catch/finally.", false); + return tryCatchFinally.apply(this, arguments); + }; + + // ........................................ + // TYPING & ARRAY MESSAGING + // + + var TYPE_MAP = {}; + var t = "Boolean Number String Function Array Date RegExp Object".split(" "); + array.forEach.call(t, function (name) { + TYPE_MAP["[object " + name + "]"] = name.toLowerCase(); + }); + + var toString = Object.prototype.toString; + + var EmberObject; + + /** + Returns a consistent type for the passed item. + + Use this instead of the built-in `typeof` to get the type of an item. + It will return the same result across all browsers and includes a bit + more detail. Here is what will be returned: + + | Return Value | Meaning | + |---------------|------------------------------------------------------| + | 'string' | String primitive or String object. | + | 'number' | Number primitive or Number object. | + | 'boolean' | Boolean primitive or Boolean object. | + | 'null' | Null value | + | 'undefined' | Undefined value | + | 'function' | A function | + | 'array' | An instance of Array | + | 'regexp' | An instance of RegExp | + | 'date' | An instance of Date | + | 'class' | An Ember class (created using Ember.Object.extend()) | + | 'instance' | An Ember object instance | + | 'error' | An instance of the Error object | + | 'object' | A JavaScript object not inheriting from Ember.Object | + + Examples: + + ```javascript + Ember.typeOf(); // 'undefined' + Ember.typeOf(null); // 'null' + Ember.typeOf(undefined); // 'undefined' + Ember.typeOf('michael'); // 'string' + Ember.typeOf(new String('michael')); // 'string' + Ember.typeOf(101); // 'number' + Ember.typeOf(new Number(101)); // 'number' + Ember.typeOf(true); // 'boolean' + Ember.typeOf(new Boolean(true)); // 'boolean' + Ember.typeOf(Ember.makeArray); // 'function' + Ember.typeOf([1, 2, 90]); // 'array' + Ember.typeOf(/abc/); // 'regexp' + Ember.typeOf(new Date()); // 'date' + Ember.typeOf(Ember.Object.extend()); // 'class' + Ember.typeOf(Ember.Object.create()); // 'instance' + Ember.typeOf(new Error('teamocil')); // 'error' + + // 'normal' JavaScript object + Ember.typeOf({ a: 'b' }); // 'object' + ``` + + @method typeOf + @for Ember + @param {Object} item the item to check + @return {String} the type + */ + function typeOf(item) { + var ret, modulePath; + + // ES6TODO: Depends on Ember.Object which is defined in runtime. + if (typeof EmberObject === "undefined") { + modulePath = "ember-runtime/system/object"; + if (Ember['default'].__loader.registry[modulePath]) { + EmberObject = Ember['default'].__loader.require(modulePath)["default"]; + } + } + + ret = item === null || item === undefined ? String(item) : TYPE_MAP[toString.call(item)] || "object"; + + if (ret === "function") { + if (EmberObject && EmberObject.detect(item)) { + ret = "class"; + } + } else if (ret === "object") { + if (item instanceof Error) { + ret = "error"; + } else if (EmberObject && item instanceof EmberObject) { + ret = "instance"; + } else if (item instanceof Date) { + ret = "date"; + } + } + + return ret; + } + + /** + Convenience method to inspect an object. This method will attempt to + convert the object into a useful string description. + + It is a pretty simple implementation. If you want something more robust, + use something like JSDump: https://github.com/NV/jsDump + + @method inspect + @for Ember + @param {Object} obj The object you want to inspect. + @return {String} A description of the object + @since 1.4.0 + */ + function inspect(obj) { + var type = typeOf(obj); + if (type === "array") { + return "[" + obj + "]"; + } + if (type !== "object") { + return obj + ""; + } + + var v; + var ret = []; + for (var key in obj) { + if (obj.hasOwnProperty(key)) { + v = obj[key]; + if (v === "toString") { + continue; + } // ignore useless items + if (typeOf(v) === "function") { + v = "function() { ... }"; + } + + if (v && typeof v.toString !== "function") { + ret.push(key + ": " + toString.call(v)); + } else { + ret.push(key + ": " + v); + } + } + } + return "{" + ret.join(", ") + "}"; + } + + // The following functions are intentionally minified to keep the functions + // below Chrome's function body size inlining limit of 600 chars. + /** + @param {Object} target + @param {Function} method + @param {Array} args + */ + function apply(t, m, a) { + var l = a && a.length; + if (!a || !l) { + return m.call(t); + } + switch (l) { + case 1: + return m.call(t, a[0]); + case 2: + return m.call(t, a[0], a[1]); + case 3: + return m.call(t, a[0], a[1], a[2]); + case 4: + return m.call(t, a[0], a[1], a[2], a[3]); + case 5: + return m.call(t, a[0], a[1], a[2], a[3], a[4]); + default: + return m.apply(t, a); + } + } + + /** + @param {Object} target + @param {String} method + @param {Array} args + */ + function applyStr(t, m, a) { + var l = a && a.length; + if (!a || !l) { + return t[m](); + } + switch (l) { + case 1: + return t[m](a[0]); + case 2: + return t[m](a[0], a[1]); + case 3: + return t[m](a[0], a[1], a[2]); + case 4: + return t[m](a[0], a[1], a[2], a[3]); + case 5: + return t[m](a[0], a[1], a[2], a[3], a[4]); + default: + return t[m].apply(t, a); + } + } + + exports.GUID_DESC = GUID_DESC; + exports.EMBER_META_PROPERTY = EMBER_META_PROPERTY; + exports.GUID_KEY_PROPERTY = GUID_KEY_PROPERTY; + exports.NEXT_SUPER_PROPERTY = NEXT_SUPER_PROPERTY; + exports.GUID_KEY = GUID_KEY; + exports.META_DESC = META_DESC; + exports.EMPTY_META = EMPTY_META; + exports.tryCatchFinally = tryCatchFinally; + exports.deprecatedTryCatchFinally = deprecatedTryCatchFinally; + exports.tryFinally = tryFinally; + exports.deprecatedTryFinally = deprecatedTryFinally; + +}); +enifed('ember-metal/watch_key', ['exports', 'ember-metal/core', 'ember-metal/utils', 'ember-metal/platform/define_property', 'ember-metal/properties'], function (exports, Ember, utils, define_property, properties) { + + 'use strict'; + + exports.watchKey = watchKey; + exports.unwatchKey = unwatchKey; + + function watchKey(obj, keyName, meta) { + // can't watch length on Array - it is special... + if (keyName === "length" && utils.typeOf(obj) === "array") { + return; + } + + var m = meta || utils.meta(obj); + var watching = m.watching; + + // activate watching first time + if (!watching[keyName]) { + watching[keyName] = 1; + + var possibleDesc = obj[keyName]; + var desc = possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor ? possibleDesc : undefined; + if (desc && desc.willWatch) { + desc.willWatch(obj, keyName); + } + + if ("function" === typeof obj.willWatchProperty) { + obj.willWatchProperty(keyName); + } + + + if (define_property.hasPropertyAccessors) { + handleMandatorySetter(m, obj, keyName); + } + + } else { + watching[keyName] = (watching[keyName] || 0) + 1; + } + } + + + var handleMandatorySetter = function handleMandatorySetter(m, obj, keyName) { + var descriptor = Object.getOwnPropertyDescriptor && Object.getOwnPropertyDescriptor(obj, keyName); + var configurable = descriptor ? descriptor.configurable : true; + var isWritable = descriptor ? descriptor.writable : true; + var hasValue = descriptor ? "value" in descriptor : true; + var possibleDesc = descriptor && descriptor.value; + var isDescriptor = possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor; + + if (isDescriptor) { + return; + } + + // this x in Y deopts, so keeping it in this function is better; + if (configurable && isWritable && hasValue && keyName in obj) { + m.values[keyName] = obj[keyName]; + define_property.defineProperty(obj, keyName, { + configurable: true, + enumerable: Object.prototype.propertyIsEnumerable.call(obj, keyName), + set: properties.MANDATORY_SETTER_FUNCTION(keyName), + get: properties.DEFAULT_GETTER_FUNCTION(keyName) + }); + } + }; + + + // This is super annoying, but required until + // https://github.com/babel/babel/issues/906 is resolved + ; // jshint ignore:line + + function unwatchKey(obj, keyName, meta) { + var m = meta || utils.meta(obj); + var watching = m.watching; + + if (watching[keyName] === 1) { + watching[keyName] = 0; + + var possibleDesc = obj[keyName]; + var desc = possibleDesc !== null && typeof possibleDesc === "object" && possibleDesc.isDescriptor ? possibleDesc : undefined; + if (desc && desc.didUnwatch) { + desc.didUnwatch(obj, keyName); + } + + if ("function" === typeof obj.didUnwatchProperty) { + obj.didUnwatchProperty(keyName); + } + + + if (!desc && define_property.hasPropertyAccessors && keyName in obj) { + define_property.defineProperty(obj, keyName, { + configurable: true, + enumerable: Object.prototype.propertyIsEnumerable.call(obj, keyName), + set: function (val) { + // redefine to set as enumerable + define_property.defineProperty(obj, keyName, { + configurable: true, + writable: true, + enumerable: true, + value: val + }); + delete m.values[keyName]; + }, + get: properties.DEFAULT_GETTER_FUNCTION(keyName) + }); + } + + } else if (watching[keyName] > 1) { + watching[keyName]--; + } + } + +}); +enifed('ember-metal/watch_path', ['exports', 'ember-metal/utils', 'ember-metal/chains'], function (exports, utils, chains) { + + 'use strict'; + + exports.watchPath = watchPath; + exports.unwatchPath = unwatchPath; + + function chainsFor(obj, meta) { + var m = meta || utils.meta(obj); + var ret = m.chains; + if (!ret) { + ret = m.chains = new chains.ChainNode(null, null, obj); + } else if (ret.value() !== obj) { + ret = m.chains = ret.copy(obj); + } + return ret; + } + function watchPath(obj, keyPath, meta) { + // can't watch length on Array - it is special... + if (keyPath === "length" && utils.typeOf(obj) === "array") { + return; + } + + var m = meta || utils.meta(obj); + var watching = m.watching; + + if (!watching[keyPath]) { + // activate watching first time + watching[keyPath] = 1; + chainsFor(obj, m).add(keyPath); + } else { + watching[keyPath] = (watching[keyPath] || 0) + 1; + } + } + + function unwatchPath(obj, keyPath, meta) { + var m = meta || utils.meta(obj); + var watching = m.watching; + + if (watching[keyPath] === 1) { + watching[keyPath] = 0; + chainsFor(obj, m).remove(keyPath); + } else if (watching[keyPath] > 1) { + watching[keyPath]--; + } + } + +}); +enifed('ember-metal/watching', ['exports', 'ember-metal/utils', 'ember-metal/chains', 'ember-metal/watch_key', 'ember-metal/watch_path', 'ember-metal/path_cache'], function (exports, utils, chains, watch_key, watch_path, path_cache) { + + 'use strict'; + + exports.isWatching = isWatching; + exports.unwatch = unwatch; + exports.destroy = destroy; + exports.watch = watch; + + function watch(obj, _keyPath, m) { + // can't watch length on Array - it is special... + if (_keyPath === "length" && utils.typeOf(obj) === "array") { + return; + } + + if (!path_cache.isPath(_keyPath)) { + watch_key.watchKey(obj, _keyPath, m); + } else { + watch_path.watchPath(obj, _keyPath, m); + } + } + + function isWatching(obj, key) { + var meta = obj["__ember_meta__"]; + return (meta && meta.watching[key]) > 0; + } + + watch.flushPending = chains.flushPendingChains; + function unwatch(obj, _keyPath, m) { + // can't watch length on Array - it is special... + if (_keyPath === "length" && utils.typeOf(obj) === "array") { + return; + } + + if (!path_cache.isPath(_keyPath)) { + watch_key.unwatchKey(obj, _keyPath, m); + } else { + watch_path.unwatchPath(obj, _keyPath, m); + } + } + + var NODE_STACK = []; + + /** + Tears down the meta on an object so that it can be garbage collected. + Multiple calls will have no effect. + + @method destroy + @for Ember + @param {Object} obj the object to destroy + @return {void} + */ + function destroy(obj) { + var meta = obj["__ember_meta__"]; + var node, nodes, key, nodeObject; + + if (meta) { + obj["__ember_meta__"] = null; + // remove chainWatchers to remove circular references that would prevent GC + node = meta.chains; + if (node) { + NODE_STACK.push(node); + // process tree + while (NODE_STACK.length > 0) { + node = NODE_STACK.pop(); + // push children + nodes = node._chains; + if (nodes) { + for (key in nodes) { + if (nodes.hasOwnProperty(key)) { + NODE_STACK.push(nodes[key]); + } + } + } + // remove chainWatcher in node object + if (node._watching) { + nodeObject = node._object; + if (nodeObject) { + chains.removeChainWatcher(nodeObject, node._key, node); + } + } + } + } + } + } + +}); +enifed('ember-template-compiler', ['exports', 'ember-metal/core', 'ember-template-compiler/system/precompile', 'ember-template-compiler/system/compile', 'ember-template-compiler/system/template', 'ember-template-compiler/plugins', 'ember-template-compiler/plugins/transform-each-in-to-hash', 'ember-template-compiler/plugins/transform-with-as-to-hash', 'ember-template-compiler/compat'], function (exports, _Ember, precompile, compile, template, plugins, TransformEachInToHash, TransformWithAsToHash) { + + 'use strict'; + + plugins.registerPlugin("ast", TransformWithAsToHash['default']); + plugins.registerPlugin("ast", TransformEachInToHash['default']); + + exports._Ember = _Ember['default']; + exports.precompile = precompile['default']; + exports.compile = compile['default']; + exports.template = template['default']; + exports.registerPlugin = plugins.registerPlugin; }); enifed('ember-template-compiler/compat', ['ember-metal/core', 'ember-template-compiler/compat/precompile', 'ember-template-compiler/system/compile', 'ember-template-compiler/system/template'], function (Ember, precompile, compile, template) { @@ -355,7 +10095,7 @@ enifed('ember-template-compiler/compat/precompile', ['exports', 'ember-template- */ var compile, compileSpec; - exports['default'] = function(string) { + exports['default'] = function (string) { if ((!compile || !compileSpec) && Ember.__loader.registry['htmlbars-compiler/compiler']) { var Compiler = requireModule('htmlbars-compiler/compiler'); @@ -380,15 +10120,6 @@ enifed('ember-template-compiler/plugins', ['exports'], function (exports) { exports.registerPlugin = registerPlugin; - /** - @module ember - @submodule ember-template-compiler - */ - - /** - @private - @property helpers - */ var plugins = { ast: [] }; @@ -419,7 +10150,6 @@ enifed('ember-template-compiler/plugins/transform-each-in-to-hash', ['exports'], @submodule ember-htmlbars */ - /** An HTMLBars AST transformation that replaces all instances of @@ -438,9 +10168,10 @@ enifed('ember-template-compiler/plugins/transform-each-in-to-hash', ['exports'], @class TransformEachInToHash @private */ - function TransformEachInToHash() { + function TransformEachInToHash(options) { // set later within HTMLBars to the syntax package this.syntax = null; + this.options = options || {}; } /** @@ -453,7 +10184,7 @@ enifed('ember-template-compiler/plugins/transform-each-in-to-hash', ['exports'], var walker = new pluginContext.syntax.Walker(); var b = pluginContext.syntax.builders; - walker.visit(ast, function(node) { + walker.visit(ast, function (node) { if (pluginContext.validate(node)) { if (node.program && node.program.blockParams.length) { @@ -468,10 +10199,7 @@ enifed('ember-template-compiler/plugins/transform-each-in-to-hash', ['exports'], node.sexpr.hash = b.hash(); } - node.sexpr.hash.pairs.push(b.pair( - 'keyword', - b.string(keyword) - )); + node.sexpr.hash.pairs.push(b.pair('keyword', b.string(keyword))); } }); @@ -479,11 +10207,7 @@ enifed('ember-template-compiler/plugins/transform-each-in-to-hash', ['exports'], }; TransformEachInToHash.prototype.validate = function TransformEachInToHash_validate(node) { - return (node.type === 'BlockStatement' || node.type === 'MustacheStatement') && - node.sexpr.path.original === 'each' && - node.sexpr.params.length === 3 && - node.sexpr.params[1].type === 'PathExpression' && - node.sexpr.params[1].original === 'in'; + return (node.type === 'BlockStatement' || node.type === 'MustacheStatement') && node.sexpr.path.original === 'each' && node.sexpr.params.length === 3 && node.sexpr.params[1].type === 'PathExpression' && node.sexpr.params[1].original === 'in'; }; exports['default'] = TransformEachInToHash; @@ -516,9 +10240,10 @@ enifed('ember-template-compiler/plugins/transform-with-as-to-hash', ['exports'], @private @class TransformWithAsToHash */ - function TransformWithAsToHash() { + function TransformWithAsToHash(options) { // set later within HTMLBars to the syntax package this.syntax = null; + this.options = options; } /** @@ -529,14 +10254,17 @@ enifed('ember-template-compiler/plugins/transform-with-as-to-hash', ['exports'], TransformWithAsToHash.prototype.transform = function TransformWithAsToHash_transform(ast) { var pluginContext = this; var walker = new pluginContext.syntax.Walker(); + var moduleName = this.options.moduleName; - walker.visit(ast, function(node) { + walker.visit(ast, function (node) { if (pluginContext.validate(node)) { if (node.program && node.program.blockParams.length) { - throw new Error('You cannot use keyword (`{{with foo as bar}}`) and block params (`{{with foo as |bar|}}`) at the same time.'); + throw new Error("You cannot use keyword (`{{with foo as bar}}`) and block params (`{{with foo as |bar|}}`) at the same time."); } + Ember.deprecate("Using {{with}} without block syntax is deprecated. " + "Please use standard block form (`{{#with foo as |bar|}}`) " + (moduleName ? " in `" + moduleName + "` " : "") + "instead.", false, { url: "http://emberjs.com/deprecations/v1.x/#toc_code-as-code-sytnax-for-code-with-code" }); + var removedParams = node.sexpr.params.splice(1, 2); var keyword = removedParams[1].original; node.program.blockParams = [keyword]; @@ -547,11 +10275,7 @@ enifed('ember-template-compiler/plugins/transform-with-as-to-hash', ['exports'], }; TransformWithAsToHash.prototype.validate = function TransformWithAsToHash_validate(node) { - return node.type === 'BlockStatement' && - node.sexpr.path.original === 'with' && - node.sexpr.params.length === 3 && - node.sexpr.params[1].type === 'PathExpression' && - node.sexpr.params[1].original === 'as'; + return node.type === "BlockStatement" && node.sexpr.path.original === "with" && node.sexpr.params.length === 3 && node.sexpr.params[1].type === "PathExpression" && node.sexpr.params[1].original === "as"; }; exports['default'] = TransformWithAsToHash; @@ -561,22 +10285,27 @@ enifed('ember-template-compiler/system/compile', ['exports', 'ember-template-com 'use strict'; - /** - @module ember - @submodule ember-template-compiler - */ - var compile; - exports['default'] = function(templateString) { - if (!compile && Ember.__loader.registry['htmlbars-compiler/compiler']) { - compile = requireModule('htmlbars-compiler/compiler').compile; + var compile; /** + Uses HTMLBars `compile` function to process a string into a compiled template. + + This is not present in production builds. + + @private + @method compile + @param {String} templateString This is the string to be compiled by HTMLBars. + @param {Object} options This is an options hash to augment the compiler options. + */ + exports['default'] = function (templateString, options) { + if (!compile && Ember.__loader.registry["htmlbars-compiler/compiler"]) { + compile = requireModule("htmlbars-compiler/compiler").compile; } if (!compile) { - throw new Error('Cannot call `compile` without the template compiler loaded. Please load `ember-template-compiler.js` prior to calling `compile`.'); + throw new Error("Cannot call `compile` without the template compiler loaded. Please load `ember-template-compiler.js` prior to calling `compile`."); } - var templateSpec = compile(templateString, compileOptions['default']()); + var templateSpec = compile(templateString, compileOptions['default'](options)); return template['default'](templateSpec); } @@ -591,16 +10320,22 @@ enifed('ember-template-compiler/system/compile_options', ['exports', 'ember-meta @submodule ember-template-compiler */ - exports['default'] = function() { + exports['default'] = function (_options) { var disableComponentGeneration = true; - return { - revision: 'Ember@1.11.3', + var options = _options || {}; + // When calling `Ember.Handlebars.compile()` a second argument of `true` + // had a special meaning (long since lost), this just gaurds against + // `options` being true, and causing an error during compilation. + if (options === true) { + options = {}; + } - disableComponentGeneration: disableComponentGeneration, + options.revision = "Ember@1.12.1"; + options.disableComponentGeneration = disableComponentGeneration; + options.plugins = plugins['default']; - plugins: plugins['default'] - }; + return options; } }); @@ -625,7 +10360,7 @@ enifed('ember-template-compiler/system/precompile', ['exports', 'ember-template- @method precompile @param {String} templateString This is the string to be compiled by HTMLBars. */ - exports['default'] = function(templateString) { + exports['default'] = function (templateString, options) { if (!compileSpec && Ember.__loader.registry['htmlbars-compiler/compiler']) { compileSpec = requireModule('htmlbars-compiler/compiler').compileSpec; } @@ -634,7 +10369,7 @@ enifed('ember-template-compiler/system/precompile', ['exports', 'ember-template- throw new Error('Cannot call `compileSpec` without the template compiler loaded. Please load `ember-template-compiler.js` prior to calling `compileSpec`.'); } - return compileSpec(templateString, compileOptions['default']()); + return compileSpec(templateString, compileOptions['default'](options)); } }); @@ -656,7 +10391,7 @@ enifed('ember-template-compiler/system/template', ['exports'], function (exports @param {Function} templateSpec This is the compiled HTMLBars template spec. */ - exports['default'] = function(templateSpec) { + exports['default'] = function (templateSpec) { templateSpec.isTop = true; templateSpec.isMethod = false; @@ -1555,7 +11290,7 @@ enifed("htmlbars-compiler/template-compiler", function TemplateCompiler(options) { this.options = options || {}; - this.revision = this.options.revision || "HTMLBars@v0.11.2"; + this.revision = this.options.revision || "HTMLBars@v0.11.3"; this.fragmentOpcodeCompiler = new FragmentOpcodeCompiler(); this.fragmentCompiler = new FragmentJavaScriptCompiler(); this.hydrationOpcodeCompiler = new HydrationOpcodeCompiler(); @@ -3748,7 +13483,7 @@ enifed("htmlbars-syntax/parser", if (options && options.plugins && options.plugins.ast) { for (var i = 0, l = options.plugins.ast.length; i < l; i++) { - var plugin = new options.plugins.ast[i](); + var plugin = new options.plugins.ast[i](options); plugin.syntax = syntax; @@ -7347,10 +17082,11 @@ enifed("simple-html-tokenizer/utils", __exports__.preprocessInput = preprocessInput; }); +requireModule("ember-debug"); requireModule("ember-template-compiler"); })(); ; if (typeof exports === "object") { module.exports = Ember.__loader.require("ember-template-compiler"); - } \ No newline at end of file + }//# sourceMappingURL=ember-template-compiler.map \ No newline at end of file diff --git a/vendor/assets/javascripts/ember.custom.debug.js b/vendor/assets/javascripts/ember.custom.debug.js deleted file mode 100644 index 7cb4c446a42..00000000000 --- a/vendor/assets/javascripts/ember.custom.debug.js +++ /dev/null @@ -1,49400 +0,0 @@ -/*! - * @overview Ember - JavaScript Application Framework - * @copyright Copyright 2011-2015 Tilde Inc. and contributors - * Portions Copyright 2006-2011 Strobe Inc. - * Portions Copyright 2008-2011 Apple Inc. All rights reserved. - * @license Licensed under MIT license - * See https://raw.github.com/emberjs/ember.js/master/LICENSE - * @version 1.11.3 - */ - -(function() { -var enifed, requireModule, eriuqer, requirejs, Ember; -var mainContext = this; - -(function() { - - Ember = this.Ember = this.Ember || {}; - if (typeof Ember === 'undefined') { Ember = {}; }; - function UNDEFINED() { } - - if (typeof Ember.__loader === 'undefined') { - var registry = {}; - var seen = {}; - - enifed = function(name, deps, callback) { - var value = { }; - - if (!callback) { - value.deps = []; - value.callback = deps; - } else { - value.deps = deps; - value.callback = callback; - } - - registry[name] = value; - }; - - 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); - } - - var mod = registry[name]; - var deps = mod.deps; - var callback = mod.callback; - var reified = []; - var exports; - var length = deps.length; - - for (var i=0; i 3) { - args = new Array(length - 3); - for (var i = 3; i < length; i++) { - args[i-3] = arguments[i]; - } - } else { - args = undefined; - } - - if (!this.currentInstance) { createAutorun(this); } - return this.currentInstance.schedule(queueName, target, method, args, false, stack); - }, - - deferOnce: function(queueName, target, method /* , args */) { - if (!method) { - method = target; - target = null; - } - - if (isString(method)) { - method = target[method]; - } - - var stack = this.DEBUG ? new Error() : undefined; - var length = arguments.length; - var args; - - if (length > 3) { - args = new Array(length - 3); - for (var i = 3; i < length; i++) { - args[i-3] = arguments[i]; - } - } else { - args = undefined; - } - - if (!this.currentInstance) { - createAutorun(this); - } - return this.currentInstance.schedule(queueName, target, method, args, true, stack); - }, - - setTimeout: function() { - var l = arguments.length; - var args = new Array(l); - - for (var x = 0; x < l; x++) { - args[x] = arguments[x]; - } - - var length = args.length, - method, wait, target, - methodOrTarget, methodOrWait, methodOrArgs; - - if (length === 0) { - return; - } else if (length === 1) { - method = args.shift(); - wait = 0; - } else if (length === 2) { - methodOrTarget = args[0]; - methodOrWait = args[1]; - - 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]; - - if (isCoercableNumber(last)) { - wait = args.pop(); - } else { - wait = 0; - } - - methodOrTarget = args[0]; - methodOrArgs = args[1]; - - if (isFunction(methodOrArgs) || (isString(methodOrArgs) && - methodOrTarget !== null && - methodOrArgs in methodOrTarget)) { - target = args.shift(); - method = args.shift(); - } else { - method = args.shift(); - } - } - - var executeAt = now() + parseInt(wait, 10); - - if (isString(method)) { - method = target[method]; - } - - var onError = getOnError(this.options); - - function fn() { - if (onError) { - try { - method.apply(target, args); - } catch (e) { - onError(e); - } - } else { - method.apply(target, args); - } - } - - // find position to insert - var i = searchTimer(executeAt, this._timers); - - this._timers.splice(i, 0, executeAt, fn); - - updateLaterTimer(this, executeAt, wait); - - return fn; - }, - - 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); - } - - wait = parseInt(wait, 10); - - index = findThrottler(target, method, this._throttlers); - if (index > -1) { return this._throttlers[index]; } // throttled - - 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); - } - - throttler = [target, method, timer]; - - this._throttlers.push(throttler); - - return throttler; - }, - - debounce: function(target, method /* , args, wait, [immediate] */) { - var backburner = this; - var args = arguments; - var immediate = pop.call(args); - var wait, index, debouncee, timer; - - if (isNumber(immediate) || isString(immediate)) { - wait = immediate; - immediate = false; - } else { - wait = pop.call(args); - } - - wait = parseInt(wait, 10); - // Remove debouncee - index = findDebouncee(target, method, this._debouncees); - - if (index > -1) { - debouncee = this._debouncees[index]; - this._debouncees.splice(index, 1); - clearTimeout(debouncee[2]); - } - - 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); - - if (immediate && index === -1) { - backburner.run.apply(backburner, args); - } - - debouncee = [ - target, - method, - timer - ]; - - backburner._debouncees.push(debouncee); - - return debouncee; - }, - - cancelTimers: function() { - var clearItems = function(item) { - clearTimeout(item[2]); - }; - - each(this._throttlers, clearItems); - this._throttlers = []; - - each(this._debouncees, clearItems); - this._debouncees = []; - - if (this._laterTimer) { - clearTimeout(this._laterTimer); - this._laterTimer = null; - } - this._timers = []; - - if (this._autorun) { - clearTimeout(this._autorun); - this._autorun = null; - } - }, - - hasTimers: function() { - return !!this._timers.length || !!this._debouncees.length || !!this._throttlers.length || this._autorun; - }, - - cancel: function(timer) { - var timerType = typeof timer; - - 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 - } - }, - - _cancelItem: function(findMethod, array, timer){ - var item, index; - - if (timer.length < 3) { return false; } - - index = findMethod(timer[0], timer[1], array); - - if (index > -1) { - - item = array[index]; - - if (item[2] === timer[2]) { - array.splice(index, 1); - clearTimeout(timer[2]); - return true; - } - } - - return false; - } - }; - - Backburner.prototype.schedule = Backburner.prototype.defer; - Backburner.prototype.scheduleOnce = Backburner.prototype.deferOnce; - Backburner.prototype.later = Backburner.prototype.setTimeout; - - if (needsIETryCatchFix) { - var originalRun = Backburner.prototype.run; - Backburner.prototype.run = wrapInTryCatch(originalRun); - - var originalEnd = Backburner.prototype.end; - Backburner.prototype.end = wrapInTryCatch(originalEnd); - } - - function getOnError(options) { - return options.onError || (options.onErrorTarget && options.onErrorTarget[options.onErrorMethod]); - } - - function createAutorun(backburner) { - backburner.begin(); - backburner._autorun = global.setTimeout(function() { - backburner._autorun = null; - backburner.end(); - }); - } - - function updateLaterTimer(backburner, executeAt, wait) { - var n = now(); - if (!backburner._laterTimer || executeAt < backburner._laterTimerExpiresAt || backburner._laterTimerExpiresAt < n) { - - if (backburner._laterTimer) { - // Clear when: - // - Already expired - // - New timer is earlier - clearTimeout(backburner._laterTimer); - - if (backburner._laterTimerExpiresAt < n) { // If timer was never triggered - // Calculate the left-over wait-time - wait = Math.max(0, executeAt - n); - } - } - - backburner._laterTimer = global.setTimeout(function() { - backburner._laterTimer = null; - backburner._laterTimerExpiresAt = null; - executeTimers(backburner); - }, wait); - - backburner._laterTimerExpiresAt = n + wait; - } - } - - function executeTimers(backburner) { - var n = now(); - var fns, i, l; - - backburner.run(function() { - i = searchTimer(n, backburner._timers); - - fns = backburner._timers.splice(0, i); - - for (i = 1, l = fns.length; i < l; i += 2) { - backburner.schedule(backburner.options.defaultQueue, null, fns[i]); - } - }); - - if (backburner._timers.length) { - updateLaterTimer(backburner, backburner._timers[0], backburner._timers[0] - n); - } - } - - function findDebouncee(target, method, debouncees) { - return findItem(target, method, debouncees); - } - - function findThrottler(target, method, throttlers) { - return findItem(target, method, throttlers); - } - - function findItem(target, method, collection) { - var item; - var index = -1; - - for (var i = 0, l = collection.length; i < l; i++) { - item = collection[i]; - if (item[0] === target && item[1] === method) { - index = i; - break; - } - } - - return index; - } - - __exports__["default"] = Backburner; - }); -enifed("backburner.umd", - ["./backburner"], - function(__dependency1__) { - "use strict"; - var Backburner = __dependency1__["default"]; - - /* 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; - - 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; - } - } - - 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"]; - - function DeferredActionQueues(queueNames, options) { - var queues = this.queues = Object.create(null); - this.queueNames = queueNames = queueNames || []; - - this.options = options; - - each(queueNames, function(queueName) { - queues[queueName] = new Queue(queueName, options[queueName], options); - }); - } - - function noSuchQueue(name) { - throw new Error("You attempted to schedule an action in a queue (" + name + ") that doesn't exist"); - } - - DeferredActionQueues.prototype = { - schedule: function(name, target, method, args, onceFlag, stack) { - var queues = this.queues; - var queue = queues[name]; - - 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 { - queue.flush(false /* async */); - queueNameIndex = 0; - } - } - } - }; - - __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; - - function Queue(name, options, globalOptions) { - this.name = name; - this.globalOptions = globalOptions || {}; - this.options = options; - this._queue = []; - this.targetQueues = Object.create(null); - this._queueBeingFlushed = undefined; - } - - Queue.prototype = { - push: function(target, method, args, stack) { - var queue = this._queue; - queue.push(target, method, args, stack); - - return { - queue: this, - target: target, - method: method - }; - }, - - pushUniqueWithoutGuid: function(target, method, args, stack) { - var queue = this._queue; - - for (var i = 0, l = queue.length; i < l; i += 4) { - var currentTarget = queue[i]; - var currentMethod = queue[i+1]; - - if (currentTarget === target && currentMethod === method) { - queue[i+2] = args; // replace args - queue[i+3] = stack; // replace stack - return; - } - } - - queue.push(target, method, args, stack); - }, - - 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]; - - if (currentMethod === method) { - queue[currentIndex + 2] = args; // replace args - queue[currentIndex + 3] = stack; // replace stack - return; - } - } - - targetQueue.push( - method, - queue.push(target, method, args, stack) - 4 - ); - }, - - pushUniqueWithGuid: function(guid, target, method, args, stack) { - var hasLocalQueue = this.targetQueues[guid]; - - if (hasLocalQueue) { - this.targetQueue(hasLocalQueue, target, method, args, stack); - } else { - this.targetQueues[guid] = [ - method, - this._queue.push(target, method, args, stack) - 4 - ]; - } - - return { - queue: this, - target: target, - method: method - }; - }, - - pushUnique: function(target, method, args, stack) { - var queue = this._queue, currentTarget, currentMethod, i, l; - var KEY = this.globalOptions.GUID_KEY; - - if (target && KEY) { - var guid = target[KEY]; - if (guid) { - return this.pushUniqueWithGuid(guid, target, method, args, stack); - } - } - - this.pushUniqueWithoutGuid(target, method, args, stack); - - return { - queue: this, - target: target, - method: method - }; - }, - - invoke: function(target, method, args, _, _errorRecordedForStack) { - if (args && args.length > 0) { - method.apply(target, args); - } else { - method.call(target); - } - }, - - 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); - } - }, - - flush: function(sync) { - var queue = this._queue; - var length = queue.length; - - if (length === 0) { - return; - } - - 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; - - this.targetQueues = Object.create(null); - var queueItems = this._queueBeingFlushed = this._queue.slice(); - this._queue = []; - - if (before) { - before(); - } - - 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 - - if (isString(method)) { - method = target[method]; - } - - // 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); - } - } - - if (after) { - after(); - } - - this._queueBeingFlushed = undefined; - - if (sync !== false && - this._queue.length > 0) { - // check if new items have been added - this.flush(true); - } - }, - - 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; - - if (GUID_KEY && this.targetQueues && target) { - var targetQueue = this.targetQueues[target[GUID_KEY]]; - - if (targetQueue) { - for (i = 0, l = targetQueue.length; i < l; i++) { - if (targetQueue[i] === method) { - targetQueue.splice(i, 1); - } - } - } - } - - for (i = 0, l = queue.length; i < l; i += 4) { - currentTarget = queue[i]; - currentMethod = queue[i+1]; - - if (currentTarget === target && - currentMethod === method) { - queue.splice(i, 4); - return true; - } - } - - // if not found in current queue - // could be in the queue that is being flushed - queue = this._queueBeingFlushed; - - if (!queue) { - return; - } - - for (i = 0, l = queue.length; i < l; i += 4) { - currentTarget = queue[i]; - currentMethod = queue[i+1]; - - if (currentTarget === target && - currentMethod === method) { - // don't mess with array during flush - // just nullify the method - queue[i+1] = null; - return true; - } - } - } - }; - - __exports__["default"] = Queue; - }); -enifed("backburner/utils", - ["exports"], - function(__exports__) { - "use strict"; - var NUMBER = /\d+/; - - function each(collection, callback) { - for (var i = 0; i < collection.length; i++) { - callback(collection[i]); - } - } - - __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'; - } - - __exports__.isString = isString;function isFunction(suspect) { - return typeof suspect === 'function'; - } - - __exports__.isFunction = isFunction;function isNumber(suspect) { - return typeof suspect === 'number'; - } - - __exports__.isNumber = isNumber;function isCoercableNumber(number) { - return isNumber(number) || NUMBER.test(number); - } - - __exports__.isCoercableNumber = isCoercableNumber;function wrapInTryCatch(func) { - return function () { - try { - return func.apply(this, arguments); - } catch (e) { - throw e; - } - }; - } - - __exports__.wrapInTryCatch = wrapInTryCatch; - }); -enifed("calculateVersion", - [], - function() { - "use strict"; - 'use strict'; - - var fs = eriuqer('fs'); - var path = eriuqer('path'); - - module.exports = function () { - var packageVersion = eriuqer('../package.json').version; - var output = [packageVersion]; - var gitPath = path.join(__dirname,'..','.git'); - var headFilePath = path.join(gitPath, 'HEAD'); - - 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; - } - - output.push(branchSHA.slice(0,10)); - } - } catch (err) { - console.error(err.stack); - } - return output.join('.'); - } else { - return packageVersion; - } - }; - }); -enifed('container', ['exports', 'container/registry', 'container/container'], function (exports, Registry, Container) { - - '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 - */ - - /* - 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; - - if (Ember.ENV && typeof Ember.ENV.MODEL_FACTORY_INJECTIONS !== 'undefined') { - Ember.MODEL_FACTORY_INJECTIONS = !!Ember.ENV.MODEL_FACTORY_INJECTIONS; - } - - exports.Registry = Registry['default']; - exports.Container = Container['default']; - -}); -enifed('container/container', ['exports', 'ember-metal/core', 'ember-metal/keys', 'ember-metal/dictionary'], function (exports, Ember, emberKeys, dictionary) { - - 'use strict'; - - var Registry; - - /** - A lightweight container used to instantiate and cache objects. - - Every `Container` must be associated with a `Registry`, which is referenced - to determine the factory and options that should be used to instantiate - objects. - - The public API for `Container` is still in flux and should not be considered - stable. - - @private - @class Container - */ - function Container(registry, options) { - this._registry = registry || (function() { - Ember['default'].deprecate( - "A container should only be created for an already instantiated " + - "registry. For backward compatibility, an isolated registry will " + - "be instantiated just for this container." - ); - - // TODO - See note above about transpiler import workaround. - if (!Registry) { Registry = requireModule('container/registry')['default']; } - - return new Registry(); - }()); - - this.cache = dictionary['default'](options && options.cache ? options.cache : null); - this.factoryCache = dictionary['default'](options && options.factoryCache ? options.factoryCache : null); - this.validationCache = dictionary['default'](options && options.validationCache ? options.validationCache : null); - } - - Container.prototype = { - /** - @private - - @property _registry - @type Registry - @since 1.11.0 - */ - _registry: null, - - /** - @property cache - @type InheritingDict - */ - cache: null, - - /** - @property factoryCache - @type InheritingDict - */ - factoryCache: null, - - /** - @property validationCache - @type InheritingDict - */ - validationCache: null, - - /** - Given a fullName return a corresponding instance. - - 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. - - ```javascript - var registry = new Registry(); - var container = registry.container(); - - registry.register('api:twitter', Twitter); - - var twitter = container.lookup('api:twitter'); - - twitter instanceof Twitter; // => true - - // by default the container will return singletons - var twitter2 = container.lookup('api:twitter'); - twitter2 instanceof Twitter; // => true - - twitter === twitter2; //=> true - ``` - - If singletons are not wanted an optional flag can be provided at lookup. - - ```javascript - var registry = new Registry(); - var container = registry.container(); - - registry.register('api:twitter', Twitter); - - var twitter = container.lookup('api:twitter', { singleton: false }); - var twitter2 = container.lookup('api:twitter', { singleton: false }); - - twitter === twitter2; //=> false - ``` - - @method lookup - @param {String} fullName - @param {Object} options - @return {any} - */ - lookup: function(fullName, options) { - Ember['default'].assert('fullName must be a proper full name', this._registry.validateFullName(fullName)); - return lookup(this, this._registry.normalize(fullName), options); - }, - - /** - Given a fullName return the corresponding factory. - - @method lookupFactory - @param {String} fullName - @return {any} - */ - lookupFactory: function(fullName) { - Ember['default'].assert('fullName must be a proper full name', this._registry.validateFullName(fullName)); - return factoryFor(this, this._registry.normalize(fullName)); - }, - - /** - A depth first traversal, destroying the container, its descendant containers and all - their managed objects. - - @method destroy - */ - destroy: function() { - eachDestroyable(this, function(item) { - if (item.destroy) { - item.destroy(); - } - }); - - this.isDestroyed = true; - }, - - /** - Clear either the entire cache or just the cache for a particular key. - - @method reset - @param {String} fullName optional key to reset; if missing, resets everything - */ - reset: function(fullName) { - if (arguments.length > 0) { - resetMember(this, this._registry.normalize(fullName)); - } else { - resetCache(this); - } - } - }; - - (function exposeRegistryMethods() { - var methods = [ - 'register', - 'unregister', - 'resolve', - 'normalize', - 'typeInjection', - 'injection', - 'factoryInjection', - 'factoryTypeInjection', - 'has', - 'options', - 'optionsForType' - ]; - - function exposeRegistryMethod(method) { - Container.prototype[method] = function() { - Ember['default'].deprecate(method + ' should be called on the registry instead of the container'); - return this._registry[method].apply(this._registry, arguments); - }; - } - - for (var i = 0, l = methods.length; i < l; i++) { - exposeRegistryMethod(methods[i]); - } - })(); - - function lookup(container, fullName, options) { - options = options || {}; - - if (container.cache[fullName] && options.singleton !== false) { - return container.cache[fullName]; - } - - var value = instantiate(container, fullName); - - if (value === undefined) { return; } - - if (container._registry.getOption(fullName, 'singleton') !== false && options.singleton !== false) { - container.cache[fullName] = value; - } - - return value; - } - - function buildInjections(container) { - var hash = {}; - - if (arguments.length > 1) { - var injectionArgs = Array.prototype.slice.call(arguments, 1); - var injections = []; - var injection; - - for (var i = 0, l = injectionArgs.length; i < l; i++) { - if (injectionArgs[i]) { - injections = injections.concat(injectionArgs[i]); - } - } - - container._registry.validateInjections(injections); - - for (i = 0, l = injections.length; i < l; i++) { - injection = injections[i]; - hash[injection.property] = lookup(container, injection.fullName); - } - } - - return hash; - } - - function factoryFor(container, fullName) { - var cache = container.factoryCache; - if (cache[fullName]) { - return cache[fullName]; - } - var registry = container._registry; - var factory = registry.resolve(fullName); - if (factory === undefined) { return; } - - var type = fullName.split(':')[0]; - if (!factory || typeof factory.extend !== 'function' || (!Ember['default'].MODEL_FACTORY_INJECTIONS && type === 'model')) { - if (factory && typeof factory._onLookup === 'function') { - factory._onLookup(fullName); - } - - // 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); - - factoryInjections._toString = registry.makeToString(factory, fullName); - - var injectedFactory = factory.extend(injections); - injectedFactory.reopenClass(factoryInjections); - - if (factory && typeof factory._onLookup === 'function') { - factory._onLookup(fullName); - } - - cache[fullName] = injectedFactory; - - return injectedFactory; - } - } - - function injectionsFor(container, fullName) { - var registry = container._registry; - var splitName = fullName.split(':'); - var type = splitName[0]; - - var injections = buildInjections(container, - registry.getTypeInjections(type), - registry.getInjections(fullName)); - injections._debugContainerKey = fullName; - injections.container = container; - - return injections; - } - - function factoryInjectionsFor(container, fullName) { - var registry = container._registry; - var splitName = fullName.split(':'); - var type = splitName[0]; - - var factoryInjections = buildInjections(container, - registry.getFactoryTypeInjections(type), - registry.getFactoryInjections(fullName)); - factoryInjections._debugContainerKey = fullName; - - return factoryInjections; - } - - function instantiate(container, fullName) { - var factory = factoryFor(container, fullName); - var lazyInjections, validationCache; - - if (container._registry.getOption(fullName, 'instantiate') === false) { - return factory; - } - - 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.'); - } - - validationCache = container.validationCache; - - // Ensure that all lazy injections are valid at instantiation time - if (!validationCache[fullName] && typeof factory._lazyInjections === 'function') { - lazyInjections = factory._lazyInjections(); - lazyInjections = container._registry.normalizeInjectionsHash(lazyInjections); - - container._registry.validateInjections(lazyInjections); - } - - validationCache[fullName] = true; - - 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)); - } - } - } - - function eachDestroyable(container, callback) { - var cache = container.cache; - var keys = emberKeys['default'](cache); - var key, value; - - for (var i = 0, l = keys.length; i < l; i++) { - key = keys[i]; - value = cache[key]; - - if (container._registry.getOption(key, 'instantiate') !== false) { - callback(value); - } - } - } - - function resetCache(container) { - eachDestroyable(container, function(value) { - if (value.destroy) { - value.destroy(); - } - }); - - container.cache.dict = dictionary['default'](null); - } - - function resetMember(container, fullName) { - var member = container.cache[fullName]; - - delete container.factoryCache[fullName]; - - if (member) { - delete container.cache[fullName]; - - if (member.destroy) { - member.destroy(); - } - } - } - - exports['default'] = Container; - -}); -enifed('container/registry', ['exports', 'ember-metal/core', 'ember-metal/dictionary', './container'], function (exports, Ember, dictionary, Container) { - - 'use strict'; - - var VALID_FULL_NAME_REGEXP = /^[^:]+.+:[^:]+$/; - - var instanceInitializersFeatureEnabled; - - /** - A lightweight registry used to store factory and option information keyed - by type. - - A `Registry` stores the factory and option information needed by a - `Container` to instantiate and cache objects. - - The public API for `Registry` is still in flux and should not be considered - stable. - - @private - @class Registry - @since 1.11.0 - */ - function Registry(options) { - this.fallback = options && options.fallback ? options.fallback : null; - - this.resolver = options && options.resolver ? options.resolver : function() {}; - - this.registrations = dictionary['default'](options && options.registrations ? options.registrations : null); - - this._typeInjections = dictionary['default'](null); - this._injections = dictionary['default'](null); - this._factoryTypeInjections = dictionary['default'](null); - this._factoryInjections = dictionary['default'](null); - - this._normalizeCache = dictionary['default'](null); - this._resolveCache = dictionary['default'](null); - - this._options = dictionary['default'](null); - this._typeOptions = dictionary['default'](null); - } - - Registry.prototype = { - /** - A backup registry for resolving registrations when no matches can be found. - - @property fallback - @type Registry - */ - fallback: null, - - /** - @property resolver - @type function - */ - resolver: null, - - /** - @property registrations - @type InheritingDict - */ - registrations: null, - - /** - @private - - @property _typeInjections - @type InheritingDict - */ - _typeInjections: null, - - /** - @private - - @property _injections - @type InheritingDict - */ - _injections: null, - - /** - @private - - @property _factoryTypeInjections - @type InheritingDict - */ - _factoryTypeInjections: null, - - /** - @private - - @property _factoryInjections - @type InheritingDict - */ - _factoryInjections: null, - - /** - @private - - @property _normalizeCache - @type InheritingDict - */ - _normalizeCache: null, - - /** - @private - - @property _resolveCache - @type InheritingDict - */ - _resolveCache: null, - - /** - @private - - @property _options - @type InheritingDict - */ - _options: null, - - /** - @private - - @property _typeOptions - @type InheritingDict - */ - _typeOptions: null, - - /** - The first container created for this registry. - - This allows deprecated access to `lookup` and `lookupFactory` to avoid - breaking compatibility for Ember 1.x initializers. - - @private - @property _defaultContainer - @type Container - */ - _defaultContainer: null, - - /** - Creates a container based on this registry. - - @method container - @param {Object} options - @return {Container} created container - */ - container: function(options) { - var container = new Container['default'](this, options); - - // 2.0TODO - remove `registerContainer` - this.registerContainer(container); - - return container; - }, - - /** - Register the first container created for a registery to allow deprecated - access to its `lookup` and `lookupFactory` methods to avoid breaking - compatibility for Ember 1.x initializers. - - 2.0TODO: Remove this method. The bookkeeping is only needed to support - deprecated behavior. - - @param {Container} newly created container - */ - registerContainer: function(container) { - if (!this._defaultContainer) { - this._defaultContainer = container; - } - if (this.fallback) { - this.fallback.registerContainer(container); - } - }, - - lookup: function(fullName, options) { - Ember['default'].assert('Create a container on the registry (with `registry.container()`) before calling `lookup`.', this._defaultContainer); - - if (instanceInitializersFeatureEnabled) { - Ember['default'].deprecate( - '`lookup` was called on a Registry. The `initializer` API no longer receives a container, and you should use an `instanceInitializer` to look up objects from the container.', - false, - { url: "http://emberjs.com/guides/deprecations#toc_access-to-instances-in-initializers" } - ); - } - - return this._defaultContainer.lookup(fullName, options); - }, - - lookupFactory: function(fullName) { - Ember['default'].assert('Create a container on the registry (with `registry.container()`) before calling `lookupFactory`.', this._defaultContainer); - - if (instanceInitializersFeatureEnabled) { - Ember['default'].deprecate( - '`lookupFactory` was called on a Registry. The `initializer` API no longer receives a container, and you should use an `instanceInitializer` to look up objects from the container.', - false, - { url: "http://emberjs.com/guides/deprecations#toc_access-to-instances-in-initializers" } - ); - } - - return this._defaultContainer.lookupFactory(fullName); - }, - - /** - Registers a factory for later injection. - - Example: - - ```javascript - var registry = new Registry(); - - registry.register('model:user', Person, {singleton: false }); - registry.register('fruit:favorite', Orange); - registry.register('communication:main', Email, {singleton: false}); - ``` - - @method register - @param {String} fullName - @param {Function} factory - @param {Object} options - */ - register: function(fullName, factory, options) { - Ember['default'].assert('fullName must be a proper full name', this.validateFullName(fullName)); - - if (factory === undefined) { - throw new TypeError('Attempting to register an unknown factory: `' + fullName + '`'); - } - - var normalizedName = this.normalize(fullName); - - if (this._resolveCache[normalizedName]) { - throw new Error('Cannot re-register: `' + fullName +'`, as it has already been resolved.'); - } - - this.registrations[normalizedName] = factory; - this._options[normalizedName] = (options || {}); - }, - - /** - Unregister a fullName - - ```javascript - var registry = new Registry(); - registry.register('model:user', User); - - registry.resolve('model:user').create() instanceof User //=> true - - registry.unregister('model:user') - registry.resolve('model:user') === undefined //=> true - ``` - - @method unregister - @param {String} fullName - */ - unregister: function(fullName) { - Ember['default'].assert('fullName must be a proper full name', this.validateFullName(fullName)); - - var normalizedName = this.normalize(fullName); - - delete this.registrations[normalizedName]; - delete this._resolveCache[normalizedName]; - delete this._options[normalizedName]; - }, - - /** - Given a fullName return the corresponding factory. - - By default `resolve` will retrieve the factory from - the registry. - - ```javascript - var registry = new Registry(); - registry.register('api:twitter', Twitter); - - registry.resolve('api:twitter') // => Twitter - ``` - - Optionally the registry 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. - - ```javascript - var registry = new Registry(); - registry.resolver = function(fullName) { - // lookup via the module system of choice - }; - - // the twitter factory is added to the module system - registry.resolve('api:twitter') // => Twitter - ``` - - @method resolve - @param {String} fullName - @return {Function} fullName's factory - */ - resolve: function(fullName) { - Ember['default'].assert('fullName must be a proper full name', this.validateFullName(fullName)); - var factory = resolve(this, this.normalize(fullName)); - if (factory === undefined && this.fallback) { - factory = this.fallback.resolve(fullName); - } - return factory; - }, - - /** - A hook that can be used to describe how the resolver will - attempt to find the factory. - - For example, the default Ember `.describe` returns the full - class name (including namespace) where Ember's resolver expects - to find the `fullName`. - - @method describe - @param {String} fullName - @return {string} described fullName - */ - describe: function(fullName) { - return fullName; - }, - - /** - A hook to enable custom fullName normalization behaviour - - @method normalizeFullName - @param {String} fullName - @return {string} normalized fullName - */ - normalizeFullName: function(fullName) { - return fullName; - }, - - /** - normalize a fullName based on the applications conventions - - @method normalize - @param {String} fullName - @return {string} normalized fullName - */ - normalize: function(fullName) { - return this._normalizeCache[fullName] || ( - this._normalizeCache[fullName] = this.normalizeFullName(fullName) - ); - }, - - /** - @method makeToString - - @param {any} factory - @param {string} fullName - @return {function} toString function - */ - makeToString: function(factory, fullName) { - return factory.toString(); - }, - - /** - Given a fullName check if the container is aware of its factory - or singleton instance. - - @method has - @param {String} fullName - @return {Boolean} - */ - has: function(fullName) { - Ember['default'].assert('fullName must be a proper full name', this.validateFullName(fullName)); - return has(this, this.normalize(fullName)); - }, - - /** - Allow registering options for all factories of a type. - - ```javascript - var registry = new Registry(); - var container = registry.container(); - - // if all of type `connection` must not be singletons - registry.optionsForType('connection', { singleton: false }); - - registry.register('connection:twitter', TwitterConnection); - registry.register('connection:facebook', FacebookConnection); - - var twitter = container.lookup('connection:twitter'); - var twitter2 = container.lookup('connection:twitter'); - - twitter === twitter2; // => false - - var facebook = container.lookup('connection:facebook'); - var facebook2 = container.lookup('connection:facebook'); - - facebook === facebook2; // => false - ``` - - @method optionsForType - @param {String} type - @param {Object} options - */ - optionsForType: function(type, options) { - this._typeOptions[type] = options; - }, - - getOptionsForType: function(type) { - var optionsForType = this._typeOptions[type]; - if (optionsForType === undefined && this.fallback) { - optionsForType = this.fallback.getOptionsForType(type); - } - return optionsForType; - }, - - /** - @method options - @param {String} fullName - @param {Object} options - */ - options: function(fullName, options) { - options = options || {}; - var normalizedName = this.normalize(fullName); - this._options[normalizedName] = options; - }, - - getOptions: function(fullName) { - var normalizedName = this.normalize(fullName); - var options = this._options[normalizedName]; - if (options === undefined && this.fallback) { - options = this.fallback.getOptions(fullName); - } - return options; - }, - - getOption: function(fullName, optionName) { - var options = this._options[fullName]; - - if (options && options[optionName] !== undefined) { - return options[optionName]; - } - - var type = fullName.split(':')[0]; - options = this._typeOptions[type]; - - if (options && options[optionName] !== undefined) { - return options[optionName]; - - } else if (this.fallback) { - return this.fallback.getOption(fullName, optionName); - } - }, - - option: function(fullName, optionName) { - Ember['default'].deprecate('`Registry.option()` has been deprecated. Call `Registry.getOption()` instead.'); - return this.getOption(fullName, optionName); - }, - - /** - Used only via `injection`. - - Provides a specialized form of injection, specifically enabling - all objects of one type to be injected with a reference to another - object. - - For example, provided each object of type `controller` needed a `router`. - one would do the following: - - ```javascript - var registry = new Registry(); - var container = registry.container(); - - registry.register('router:main', Router); - registry.register('controller:user', UserController); - registry.register('controller:post', PostController); - - registry.typeInjection('controller', 'router', 'router:main'); - - var user = container.lookup('controller:user'); - var post = container.lookup('controller:post'); - - user.router instanceof Router; //=> true - post.router instanceof Router; //=> true - - // both controllers share the same router - user.router === post.router; //=> true - ``` - - @private - @method typeInjection - @param {String} type - @param {String} property - @param {String} fullName - */ - typeInjection: function(type, property, fullName) { - Ember['default'].assert('fullName must be a proper full name', this.validateFullName(fullName)); - - var fullNameType = fullName.split(':')[0]; - if (fullNameType === type) { - throw new Error('Cannot inject a `' + fullName + - '` on other ' + type + '(s).'); - } - - var injections = this._typeInjections[type] || - (this._typeInjections[type] = []); - - injections.push({ - property: property, - fullName: fullName - }); - }, - - /** - Defines injection rules. - - These rules are used to inject dependencies onto objects when they - are instantiated. - - Two forms of injections are possible: - - * Injecting one fullName on another fullName - * Injecting one fullName on a type - - Example: - - ```javascript - var registry = new Registry(); - var container = registry.container(); - - registry.register('source:main', Source); - registry.register('model:user', User); - registry.register('model:post', Post); - - // injecting one fullName on another fullName - // eg. each user model gets a post model - registry.injection('model:user', 'post', 'model:post'); - - // injecting one fullName on another type - registry.injection('model', 'source', 'source:main'); - - var user = container.lookup('model:user'); - var post = container.lookup('model:post'); - - user.source instanceof Source; //=> true - post.source instanceof Source; //=> true - - user.post instanceof Post; //=> true - - // and both models share the same source - user.source === post.source; //=> true - ``` - - @method injection - @param {String} factoryName - @param {String} property - @param {String} injectionName - */ - injection: function(fullName, property, injectionName) { - this.validateFullName(injectionName); - var normalizedInjectionName = this.normalize(injectionName); - - if (fullName.indexOf(':') === -1) { - return this.typeInjection(fullName, property, normalizedInjectionName); - } - - Ember['default'].assert('fullName must be a proper full name', this.validateFullName(fullName)); - var normalizedName = this.normalize(fullName); - - var injections = this._injections[normalizedName] || - (this._injections[normalizedName] = []); - - injections.push({ - property: property, - fullName: normalizedInjectionName - }); - }, - - - /** - Used only via `factoryInjection`. - - Provides a specialized form of injection, specifically enabling - all factory of one type to be injected with a reference to another - object. - - For example, provided each factory of type `model` needed a `store`. - one would do the following: - - ```javascript - var registry = new Registry(); - - registry.register('store:main', SomeStore); - - registry.factoryTypeInjection('model', 'store', 'store:main'); - - var store = registry.lookup('store:main'); - var UserFactory = registry.lookupFactory('model:user'); - - UserFactory.store instanceof SomeStore; //=> true - ``` - - @private - @method factoryTypeInjection - @param {String} type - @param {String} property - @param {String} fullName - */ - factoryTypeInjection: function(type, property, fullName) { - var injections = this._factoryTypeInjections[type] || - (this._factoryTypeInjections[type] = []); - - injections.push({ - property: property, - fullName: this.normalize(fullName) - }); - }, - - /** - Defines factory injection rules. - - Similar to regular injection rules, but are run against factories, via - `Registry#lookupFactory`. - - These rules are used to inject objects onto factories when they - are looked up. - - Two forms of injections are possible: - - * Injecting one fullName on another fullName - * Injecting one fullName on a type - - Example: - - ```javascript - var registry = new Registry(); - var container = registry.container(); - - registry.register('store:main', Store); - registry.register('store:secondary', OtherStore); - registry.register('model:user', User); - registry.register('model:post', Post); - - // injecting one fullName on another type - registry.factoryInjection('model', 'store', 'store:main'); - - // injecting one fullName on another fullName - registry.factoryInjection('model:post', 'secondaryStore', 'store:secondary'); - - var UserFactory = container.lookupFactory('model:user'); - var PostFactory = container.lookupFactory('model:post'); - var store = container.lookup('store:main'); - - UserFactory.store instanceof Store; //=> true - UserFactory.secondaryStore instanceof OtherStore; //=> false - - PostFactory.store instanceof Store; //=> true - PostFactory.secondaryStore instanceof OtherStore; //=> true - - // and both models share the same source instance - UserFactory.store === PostFactory.store; //=> true - ``` - - @method factoryInjection - @param {String} factoryName - @param {String} property - @param {String} injectionName - */ - factoryInjection: function(fullName, property, injectionName) { - var normalizedName = this.normalize(fullName); - var normalizedInjectionName = this.normalize(injectionName); - - this.validateFullName(injectionName); - - if (fullName.indexOf(':') === -1) { - return this.factoryTypeInjection(normalizedName, property, normalizedInjectionName); - } - - var injections = this._factoryInjections[normalizedName] || (this._factoryInjections[normalizedName] = []); - - injections.push({ - property: property, - fullName: normalizedInjectionName - }); - }, - - validateFullName: function(fullName) { - if (!VALID_FULL_NAME_REGEXP.test(fullName)) { - throw new TypeError('Invalid Fullname, expected: `type:name` got: ' + fullName); - } - return true; - }, - - validateInjections: function(injections) { - if (!injections) { return; } - - var fullName; - - for (var i = 0, length = injections.length; i < length; i++) { - fullName = injections[i].fullName; - - if (!this.has(fullName)) { - throw new Error('Attempting to inject an unknown injection: `' + fullName + '`'); - } - } - }, - - normalizeInjectionsHash: function(hash) { - var injections = []; - - for (var key in hash) { - if (hash.hasOwnProperty(key)) { - Ember['default'].assert("Expected a proper full name, given '" + hash[key] + "'", this.validateFullName(hash[key])); - - injections.push({ - property: key, - fullName: hash[key] - }); - } - } - - return injections; - }, - - getInjections: function(fullName) { - var injections = this._injections[fullName] || []; - if (this.fallback) { - injections = injections.concat(this.fallback.getInjections(fullName)); - } - return injections; - }, - - getTypeInjections: function(type) { - var injections = this._typeInjections[type] || []; - if (this.fallback) { - injections = injections.concat(this.fallback.getTypeInjections(type)); - } - return injections; - }, - - getFactoryInjections: function(fullName) { - var injections = this._factoryInjections[fullName] || []; - if (this.fallback) { - injections = injections.concat(this.fallback.getFactoryInjections(fullName)); - } - return injections; - }, - - getFactoryTypeInjections: function(type) { - var injections = this._factoryTypeInjections[type] || []; - if (this.fallback) { - injections = injections.concat(this.fallback.getFactoryTypeInjections(type)); - } - return injections; - } - }; - - function resolve(registry, normalizedName) { - var cached = registry._resolveCache[normalizedName]; - if (cached) { return cached; } - - var resolved = registry.resolver(normalizedName) || registry.registrations[normalizedName]; - registry._resolveCache[normalizedName] = resolved; - - return resolved; - } - - function has(registry, fullName) { - return registry.resolve(fullName) !== undefined; - } - - exports['default'] = Registry; - -}); -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; - - 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(); - } - - - /** - * 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); - } - - /** - * DAG Vertex - * - * @class Vertex - * @constructor - */ - - function Vertex(name) { - this.name = name; - this.incoming = {}; - this.incomingNames = []; - this.hasOutgoing = false; - this.value = null; - } - - /** - * 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"); - } - if (this.vertices[name] !== undefined) { - return this.vertices[name]; - } - var vertex = new Vertex(name); - this.vertices[name] = vertex; - this.names.push(name); - return vertex; - }; - - /** - * 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; - }; - - /** - * 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(" <- ")); - } - } - visit(from, checkCycle); - from.hasOutgoing = true; - to.incoming[fromName] = from; - to.incomingNames.push(fromName); - }; - - /** - * 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; - - for (i = 0; i < len; i++) { - vertex = vertices[names[i]]; - if (!vertex.hasOutgoing) { - visit(vertex, fn, visited); - } - } - }; - - /** - * 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]); - } - } - } - if (after) { - if (typeof after === 'string') { - this.addEdge(after, name); - } else { - for (i = 0; i < after.length; i++) { - this.addEdge(after[i], name); - } - } - } - }; - - __exports__["default"] = DAG; - }); -enifed("dag-map.umd", - ["./dag-map"], - function(__dependency1__) { - "use strict"; - var DAG = __dependency1__["default"]; - - /* 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("dom-helper", - ["./morph-range","./morph-attr","./dom-helper/build-html-dom","./dom-helper/classes","./dom-helper/prop","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { - "use strict"; - var Morph = __dependency1__["default"]; - var AttrMorph = __dependency2__["default"]; - var buildHTMLDOM = __dependency3__.buildHTMLDOM; - var svgNamespace = __dependency3__.svgNamespace; - var svgHTMLIntegrationPoints = __dependency3__.svgHTMLIntegrationPoints; - var addClasses = __dependency4__.addClasses; - var removeClasses = __dependency4__.removeClasses; - var normalizeProperty = __dependency5__.normalizeProperty; - var isAttrRemovalValue = __dependency5__.isAttrRemovalValue; - - var doc = typeof document === 'undefined' ? false : document; - - var deletesBlankTextNodes = doc && (function(document){ - var element = document.createElement('div'); - element.appendChild( document.createTextNode('') ); - var clonedElement = element.cloneNode(true); - return clonedElement.childNodes.length === 0; - })(doc); - - var ignoresCheckedAttribute = doc && (function(document){ - var element = document.createElement('input'); - element.setAttribute('checked', 'checked'); - var clonedElement = element.cloneNode(false); - return !clonedElement.checked; - })(doc); - - var canRemoveSvgViewBoxAttribute = doc && (doc.createElementNS ? (function(document){ - var element = document.createElementNS(svgNamespace, 'svg'); - element.setAttribute('viewBox', '0 0 100 100'); - element.removeAttribute('viewBox'); - return !element.getAttribute('viewBox'); - })(doc) : true); - - var canClone = doc && (function(document){ - var element = document.createElement('div'); - element.appendChild( document.createTextNode(' ')); - element.appendChild( document.createTextNode(' ')); - var clonedElement = element.cloneNode(true); - return clonedElement.childNodes[0].nodeValue === ' '; - })(doc); - - // This is not the namespace of the element, but of - // the elements inside that elements. - function interiorNamespace(element){ - if ( - element && - element.namespaceURI === svgNamespace && - !svgHTMLIntegrationPoints[element.tagName] - ) { - return svgNamespace; - } else { - return null; - } - } - - // The HTML spec allows for "omitted start tags". These tags are optional - // when their intended child is the first thing in the parent tag. For - // example, this is a tbody start tag: - // - // - // - // - // - // The tbody may be omitted, and the browser will accept and render: - // - //
- // - // - // However, the omitted start tag will still be added to the DOM. Here - // we test the string and context to see if the browser is about to - // perform this cleanup. - // - // http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#optional-tags - // describes which tags are omittable. The spec for tbody and colgroup - // explains this behavior: - // - // http://www.whatwg.org/specs/web-apps/current-work/multipage/tables.html#the-tbody-element - // http://www.whatwg.org/specs/web-apps/current-work/multipage/tables.html#the-colgroup-element - // - - var omittedStartTagChildTest = /<([\w:]+)/; - function detectOmittedStartTag(string, contextualElement){ - // Omitted start tags are only inside table tags. - if (contextualElement.tagName === 'TABLE') { - var omittedStartTagChildMatch = omittedStartTagChildTest.exec(string); - if (omittedStartTagChildMatch) { - var omittedStartTagChild = omittedStartTagChildMatch[1]; - // It is already asserted that the contextual element is a table - // and not the proper start tag. Just see if a tag was omitted. - return omittedStartTagChild === 'tr' || - omittedStartTagChild === 'col'; - } - } - } - - function buildSVGDOM(html, dom){ - var div = dom.document.createElement('div'); - div.innerHTML = ''+html+''; - return div.firstChild.childNodes; - } - - /* - * A class wrapping DOM functions to address environment compatibility, - * namespaces, contextual elements for morph un-escaped content - * insertion. - * - * When entering a template, a DOMHelper should be passed: - * - * template(context, { hooks: hooks, dom: new DOMHelper() }); - * - * TODO: support foreignObject as a passed contextual element. It has - * a namespace (svg) that does not match its internal namespace - * (xhtml). - * - * @class DOMHelper - * @constructor - * @param {HTMLDocument} _document The document DOM methods are proxied to - */ - function DOMHelper(_document){ - this.document = _document || document; - if (!this.document) { - throw new Error("A document object must be passed to the DOMHelper, or available on the global scope"); - } - this.canClone = canClone; - this.namespace = null; - } - - var prototype = DOMHelper.prototype; - prototype.constructor = DOMHelper; - - prototype.getElementById = function(id, rootNode) { - rootNode = rootNode || this.document; - return rootNode.getElementById(id); - }; - - prototype.insertBefore = function(element, childElement, referenceChild) { - return element.insertBefore(childElement, referenceChild); - }; - - prototype.appendChild = function(element, childElement) { - return element.appendChild(childElement); - }; - - prototype.childAt = function(element, indices) { - var child = element; - - for (var i = 0; i < indices.length; i++) { - child = child.childNodes.item(indices[i]); - } - - return child; - }; - - // Note to a Fellow Implementor: - // Ahh, accessing a child node at an index. Seems like it should be so simple, - // doesn't it? Unfortunately, this particular method has caused us a surprising - // amount of pain. As you'll note below, this method has been modified to walk - // the linked list of child nodes rather than access the child by index - // directly, even though there are two (2) APIs in the DOM that do this for us. - // If you're thinking to yourself, "What an oversight! What an opportunity to - // optimize this code!" then to you I say: stop! For I have a tale to tell. - // - // First, this code must be compatible with simple-dom for rendering on the - // server where there is no real DOM. Previously, we accessed a child node - // directly via `element.childNodes[index]`. While we *could* in theory do a - // full-fidelity simulation of a live `childNodes` array, this is slow, - // complicated and error-prone. - // - // "No problem," we thought, "we'll just use the similar - // `childNodes.item(index)` API." Then, we could just implement our own `item` - // method in simple-dom and walk the child node linked list there, allowing - // us to retain the performance advantages of the (surely optimized) `item()` - // API in the browser. - // - // Unfortunately, an enterprising soul named Samy Alzahrani discovered that in - // IE8, accessing an item out-of-bounds via `item()` causes an exception where - // other browsers return null. This necessitated a... check of - // `childNodes.length`, bringing us back around to having to support a - // full-fidelity `childNodes` array! - // - // Worst of all, Kris Selden investigated how browsers are actualy implemented - // and discovered that they're all linked lists under the hood anyway. Accessing - // `childNodes` requires them to allocate a new live collection backed by that - // linked list, which is itself a rather expensive operation. Our assumed - // optimization had backfired! That is the danger of magical thinking about - // the performance of native implementations. - // - // And this, my friends, is why the following implementation just walks the - // linked list, as surprised as that may make you. Please ensure you understand - // the above before changing this and submitting a PR. - // - // Tom Dale, January 18th, 2015, Portland OR - prototype.childAtIndex = function(element, index) { - var node = element.firstChild; - - for (var idx = 0; node && idx < index; idx++) { - node = node.nextSibling; - } - - return node; - }; - - prototype.appendText = function(element, text) { - return element.appendChild(this.document.createTextNode(text)); - }; - - prototype.setAttribute = function(element, name, value) { - element.setAttribute(name, String(value)); - }; - - prototype.setAttributeNS = function(element, namespace, name, value) { - element.setAttributeNS(namespace, name, String(value)); - }; - - if (canRemoveSvgViewBoxAttribute){ - prototype.removeAttribute = function(element, name) { - element.removeAttribute(name); - }; - } else { - prototype.removeAttribute = function(element, name) { - if (element.tagName === 'svg' && name === 'viewBox') { - element.setAttribute(name, null); - } else { - element.removeAttribute(name); - } - }; - } - - prototype.setPropertyStrict = function(element, name, value) { - element[name] = value; - }; - - prototype.setProperty = function(element, name, value, namespace) { - var lowercaseName = name.toLowerCase(); - if (element.namespaceURI === svgNamespace || lowercaseName === 'style') { - if (isAttrRemovalValue(value)) { - element.removeAttribute(name); - } else { - if (namespace) { - element.setAttributeNS(namespace, name, value); - } else { - element.setAttribute(name, value); - } - } - } else { - var normalized = normalizeProperty(element, name); - if (normalized) { - element[normalized] = value; - } else { - if (isAttrRemovalValue(value)) { - element.removeAttribute(name); - } else { - if (namespace && element.setAttributeNS) { - element.setAttributeNS(namespace, name, value); - } else { - element.setAttribute(name, value); - } - } - } - } - }; - - if (doc && doc.createElementNS) { - // Only opt into namespace detection if a contextualElement - // is passed. - prototype.createElement = function(tagName, contextualElement) { - var namespace = this.namespace; - if (contextualElement) { - if (tagName === 'svg') { - namespace = svgNamespace; - } else { - namespace = interiorNamespace(contextualElement); - } - } - if (namespace) { - return this.document.createElementNS(namespace, tagName); - } else { - return this.document.createElement(tagName); - } - }; - prototype.setAttributeNS = function(element, namespace, name, value) { - element.setAttributeNS(namespace, name, String(value)); - }; - } else { - prototype.createElement = function(tagName) { - return this.document.createElement(tagName); - }; - prototype.setAttributeNS = function(element, namespace, name, value) { - element.setAttribute(name, String(value)); - }; - } - - prototype.addClasses = addClasses; - prototype.removeClasses = removeClasses; - - prototype.setNamespace = function(ns) { - this.namespace = ns; - }; - - prototype.detectNamespace = function(element) { - this.namespace = interiorNamespace(element); - }; - - prototype.createDocumentFragment = function(){ - return this.document.createDocumentFragment(); - }; - - prototype.createTextNode = function(text){ - return this.document.createTextNode(text); - }; - - prototype.createComment = function(text){ - return this.document.createComment(text); - }; - - prototype.repairClonedNode = function(element, blankChildTextNodes, isChecked){ - if (deletesBlankTextNodes && blankChildTextNodes.length > 0) { - for (var i=0, len=blankChildTextNodes.length;i 0) { - var currentNode = childNodes[0]; - - // We prepend an