diff --git a/.gitignore b/.gitignore
index 477ab42..fbe05fc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1 @@
-dist/
-tmp-code.js
+bower_components/
diff --git a/.travis.yml b/.travis.yml
index 25aa509..aaace8b 100755
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,2 +1,7 @@
language: node_js
-script: "phantomjs tests/runner.js tests/index.html"
+
+before_install:
+ - npm install -g bower
+ - bower install
+
+script: "phantomjs tests/runner.js tests/index.html"
\ No newline at end of file
diff --git a/README.md b/README.md
index 19bd1a3..fc971b0 100755
--- a/README.md
+++ b/README.md
@@ -8,7 +8,8 @@ Fully tested.
Usage
-----
-Download the latest distribution and then use it like this:
+Download the latest distribution or use bower to install
+`ember-indexeddb-adapter`.
```js
App.ApplicationSerializer = DS.IndexedDBSerializer.extend();
@@ -161,8 +162,10 @@ send a PR for one of them.
Tests
-----
-Run `rackup` in your terminal (make sure you have Ruby installed). Then visit
-`http://localhost:9292` in your browser.
+First, install depdendencies with bower: `bower install`.
+
+Run `rackup` in your terminal (make sure you have Ruby and the rack gem installed).
+Then visit `http://localhost:9292` in your browser.
Please, disregard Travis CI for now because PhantomJS (1.9.3) doesn't support
IndexedDB. IndexedDBShim.js doesn't work
diff --git a/Rakefile b/Rakefile
index ea66186..7e592e9 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,7 +1,5 @@
desc "Builds the JS files"
task :build do
- version = File.open("VERSION").read.strip
-
files = [
"packages/indexeddb-adapter/lib/indexeddb_migration.js",
"packages/indexeddb-adapter/lib/indexeddb_serializer.js",
@@ -12,7 +10,7 @@ task :build do
code = files.map { |file| File.open(file).read }
FileUtils.mkdir_p("dist")
- file = File.new("dist/ember_indexeddb_adapter_#{version}.js", "w+")
+ file = File.new("dist/ember_indexeddb_adapter.js", "w+")
file.write(code.join("\n"))
file.close
diff --git a/VERSION b/VERSION
deleted file mode 100644
index 8f0916f..0000000
--- a/VERSION
+++ /dev/null
@@ -1 +0,0 @@
-0.5.0
diff --git a/bower.json b/bower.json
new file mode 100644
index 0000000..a26693f
--- /dev/null
+++ b/bower.json
@@ -0,0 +1,16 @@
+{
+ "name": "ember-indexeddb-adapter",
+ "version": "0.5.1",
+ "main": [
+ "dist/ember_indexeddb_adapter.js"
+ ],
+ "dependencies": {
+ "jquery": "1.11.1",
+ "ember": "1.5.1",
+ "ember-data": "1.0.0-beta.7"
+ },
+ "devDependencies": {
+ "qunit": "",
+ "IndexedDBShim": ""
+ }
+}
\ No newline at end of file
diff --git a/dist/ember_indexeddb_adapter.js b/dist/ember_indexeddb_adapter.js
new file mode 100644
index 0000000..07d8166
--- /dev/null
+++ b/dist/ember_indexeddb_adapter.js
@@ -0,0 +1,1183 @@
+/*global Ember*/
+/*global DS*/
+'use strict';
+
+DS.IndexedDBMigration = Ember.Object.extend({
+ /**
+ * This mostly a placeholder, given this value is defined by the adapter.
+ *
+ * @var databaseName
+ */
+ databaseName: "_default",
+
+ /**
+ * Whenever the version is incremented, IndexedDB will run the update code
+ * and update the schema. It's is defined in the adapter and changed by it
+ * here.
+ *
+ * @var databaseName
+ */
+ version: 1,
+
+ /**
+ * This is run the first time the adapter is initialized (when page opens).
+ * It basically checks if the schema needs to be updated, and then runs the
+ * proper migrations.
+ *
+ * @method migrate
+ */
+ migrate: function() {
+ var _this = this;
+
+ return new Ember.RSVP.Promise(function(resolve, reject) {
+ var connection = indexedDB.open(_this.databaseName, _this.version);
+ connection.onsuccess = function() {
+ var result = this.result;
+
+ Em.run(function() {
+ result.close();
+ resolve();
+ });
+ }
+
+ connection.onupgradeneeded = function(event) {
+ Em.run(function() {
+ _this.set('memoizedOpenDatabaseForUpgrade', event.target.result);
+ _this.runMigrations();
+ _this.set('memoizedOpenDatabaseForUpgrade', null);
+ });
+ }
+
+ connection.onerror = function(e) {
+ Em.run(function() {
+ console.log('Failure connecting to IndexedDB database ' + _this.databaseName);
+ reject(e);
+ })
+ }
+ });
+ },
+
+
+ /**
+ * This is a method that can be used inside a migration, such as:
+ *
+ * App.ApplicationAdapter = DS.IndexedDBAdapter.extend({
+ * databaseName: 'some_database_name'
+ * version: 1,
+ * migrations: function() {
+ * this.addModel(App.Person);
+ * this.addModel(App.Phone);
+ * }
+ * });
+ *
+ * In this case, the code will create the schema for App.Person and
+ * App.Phone automatically.
+ *
+ * @method addModel
+ * @param {DS.Model} type
+ */
+ addModel: function(model, opts) {
+ var db = this.memoizedOpenDatabaseForUpgrade,
+ modelName = model.toString(),
+ opts = opts || {},
+ _this = this;
+
+ Em.run(function() {
+ if (!db.objectStoreNames.contains(modelName)) {
+ var keyOpts = { keyPath: "id" },
+ objectStore;
+
+ if (opts.autoIncrement) {
+ keyOpts["autoIncrement"] = opts.autoIncrement;
+ }
+
+ if (opts.keyPath) {
+ keyOpts["keyPath"] = opts.keyPath;
+ }
+ objectStore = db.createObjectStore(modelName, keyOpts);
+ }
+ });
+
+ //objectStore.createIndex("name", "name", { unique: false });
+ //objectStore.createIndex("email", "email", { unique: true });
+ },
+
+ /**
+ * Runs the migrations.
+ *
+ * @method runMigrations
+ * @private
+ */
+ runMigrations: function() {
+ this.migrations.call(this);
+ },
+
+ /**
+ * Deprecated
+ */
+ currentDbVersion: function() {
+ var instance = indexedDB.open(this.databaseName),
+ version;
+
+ return new Ember.RSVP.Promise(function(resolve, reject) {
+ instance.onsuccess = function(event) {
+ var result = this.result;
+ Em.run(function() {
+ result.close();
+ resolve(event.target.result.version);
+ });
+ }
+ instance.onerror = function(event) {
+ var result = this.result;
+ Em.run(function() {
+ result.close();
+ reject(event);
+ });
+ }
+ });
+ },
+
+ memoizedOpenDatabaseForUpgrade: null,
+});
+
+DS.IndexedDBSerializer = DS.JSONSerializer.extend({
+ serializeHasMany: function(record, json, relationship) {
+ var key = relationship.key,
+ relationshipType = DS.RelationshipChange.determineRelationshipType(record.constructor, relationship);
+
+ if (relationshipType === 'manyToNone' ||
+ relationshipType === 'manyToMany' ||
+ relationshipType === 'manyToOne') {
+ json[key] = record.get(key).mapBy('id');
+ // TODO support for polymorphic manyToNone and manyToMany relationships
+ }
+ },
+ /**
+ * Extracts whatever was returned from the adapter.
+ *
+ * If the adapter returns relationships in an embedded way, such as follows:
+ *
+ * ```js
+ * {
+ * "id": 1,
+ * "title": "Rails Rambo",
+ *
+ * "_embedded": {
+ * "comment": [{
+ * "id": 1,
+ * "comment_title": "FIRST"
+ * }, {
+ * "id": 2,
+ * "comment_title": "Rails is unagi"
+ * }]
+ * }
+ * }
+ *
+ * this method will create separated JSON for each resource and then push
+ * them individually to the Store.
+ *
+ * In the end, only the main resource will remain, containing the ids of its
+ * relationships. Given the relations are already in the Store, we will
+ * return a JSON with the main resource alone. The Store will sort out the
+ * associations by itself.
+ *
+ * @method extractSingle
+ * @private
+ * @param {DS.Store} store the returned store
+ * @param {DS.Model} type the type/model
+ * @param {Object} payload returned JSON
+ */
+ extractSingle: function(store, type, payload) {
+ if (payload && payload._embedded) {
+ for (var relation in payload._embedded) {
+ var typeName = Ember.String.singularize(relation),
+ embeddedPayload = payload._embedded[relation];
+
+ if (embeddedPayload) {
+ if (Object.prototype.toString.call(embeddedPayload) === '[object Array]') {
+ store.pushMany(typeName, embeddedPayload);
+ } else {
+ store.push(typeName, embeddedPayload);
+ }
+ }
+ }
+
+ delete payload._embedded;
+ }
+
+ return this.normalize(type, payload);
+ },
+
+ /**
+ * This is exactly the same as extractSingle, but used in an array.
+ *
+ * @method extractSingle
+ * @private
+ * @param {DS.Store} store the returned store
+ * @param {DS.Model} type the type/model
+ * @param {Array} payload returned JSONs
+ */
+ extractArray: function(store, type, payload) {
+ var serializer = this;
+
+ return payload.map(function(record) {
+ var extracted = serializer.extractSingle(store, type, record);
+ return serializer.normalize(type, record);
+ });
+ }
+});
+
+/*global Ember*/
+/*global DS*/
+'use strict';
+
+/**
+ * SmartSearch allows the adapter to make queries that are broader and that
+ * will, in most business cases, yield more relevant results.
+ *
+ * It has a drawback, though: less performant queries. It shouldn't be a problem
+ * for smaller data stores.
+ */
+DS.IndexedDBSmartSearch = Ember.Object.extend({
+
+ field: null,
+ queryString: null,
+ record: null,
+ type: null,
+
+ /**
+ * The entrypoint. It tries to match the current query field against the
+ * record. See below each query explained.
+ *
+ * == Search ==
+ *
+ * store.findQuery('person', {search: "rao"})
+ *
+ * This will will search for any field that has the string rao, such as
+ * "Rambo".
+ *
+ * == Search ==
+ *
+ * store.findQuery('person', {createdAt: "32 days ago"})
+ *
+ * Given `createdAt` field has a transform type `date`, it will returns only
+ * records that match the 32nd day ago.
+ *
+ * If the fields doesn't have the `date` transform, nothing is queried.
+ *
+ * Besides `x days ago`, `today` and `yesterday` are also accepted.
+ */
+ isMatch: function() {
+ var record = this.get('record'),
+ type = this.get('type'),
+ field = this.get('field'),
+ queryString = this.get('queryString'),
+ fieldType = this.fieldType(field),
+ queryType;
+
+ if (fieldType === "search") {
+ queryType = 'searchField';
+ } else if (fieldType === "date") {
+ queryType = 'dateField';
+ } else {
+ queryType = 'regularField';
+ }
+
+ return this[queryType](type, record, field, queryString);
+ },
+
+ /**
+ * Searches for string in any field. Consider the following query:
+ *
+ * store.findQuery('person', {search: "rmbo"})
+ *
+ * This would match a field such as `{name: "Rambo"}`.
+ *
+ * @method searchField
+ */
+ searchField: function(type, record, field, queryString) {
+ var isMatch;
+
+ for (var queriedField in record) {
+ var isSearchField = this.get('fieldSearchCriteria')(queriedField, type),
+ fieldValue = record[queriedField];
+
+ if (!isSearchField)
+ continue;
+
+ if (!queryString || queryString == " ") { return false; }
+
+ if (Object.prototype.toString.call(queryString).match("RegExp")) {
+ isMatch = isMatch || new RegExp(queryString).test(fieldValue);
+ } else {
+ isMatch = isMatch || (fieldValue === queryString);
+
+ var str,
+ strArray = [];
+
+ for (var i = 0, len = queryString.length; i < len; i++) {
+ strArray.push(queryString[i]);
+ }
+
+ str = new RegExp(strArray.join(".*"), "i");
+ isMatch = isMatch || new RegExp(str).test(fieldValue);
+ }
+ }
+
+ return isMatch;
+ },
+
+ dateField: function(type, record, field, queryString) {
+ var rawValue = record[field],
+ date = (new Date(Date.parse(rawValue))),
+ targetDate = new Date(),
+ match;
+
+ var IsMatchToDate = function(targetDate) {
+ var year = targetDate.getFullYear(),
+ month = targetDate.getMonth(),
+ day = targetDate.getDate(),
+ hour = targetDate.getHours(),
+ minute = targetDate.getMinutes();
+
+ if (date.getFullYear() == year &&
+ date.getMonth() == month &&
+ date.getDate() == day) {
+ return true;
+ }
+ }
+
+ if (queryString === "today") {
+ if (IsMatchToDate(targetDate)) {
+ return true;
+ }
+ } else if (queryString === "yesterday") {
+ targetDate.setDate(targetDate.getDate() - 1);
+ if (IsMatchToDate(targetDate)) {
+ return true;
+ }
+ } else if (match = queryString.match(/([0-9]{1,}) days ago/i)) {
+ targetDate.setDate(targetDate.getDate() - match[1]);
+ if (IsMatchToDate(targetDate)) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ regularField: function(type, record, field, queryString) {
+ var queriedField = record[field];
+
+ if (Object.prototype.toString.call(queryString).match("RegExp")) {
+ return new RegExp(queryString).test(queriedField);
+ } else {
+ return (queriedField === queryString);
+ }
+ },
+
+ fieldType: function(fieldName) {
+ if (fieldName === "search") {
+ return "search";
+ } else {
+ var type = this.get('type'),
+ transform;
+
+ type.eachTransformedAttribute(function(name, type) {
+ if (name == fieldName) {
+ transform = type;
+ }
+ });
+
+ return transform;
+ }
+ }
+});
+
+/*global Ember*/
+/*global DS*/
+'use strict';
+
+DS.IndexedDBAdapter = DS.Adapter.extend({
+ databaseName: 'IDBAdapter',
+
+ /**
+ * IndexedDB requires that the database is initialized and have a defined
+ * schema. It's not like localStorage, where you just store things. You have
+ * to define beforehand what Object Stores you want (e.g User, Post etc).
+ *
+ * Whenever we initialize the adapter, we call the DS.IndexedDBMigration
+ * object to do its thing, which is to initialize the database.
+ *
+ * @method init
+ */
+ init: function() {
+ this.set('migration', this.get('migration').create());
+ this.get('migration').set('databaseName', this.databaseName);
+ this.get('migration').set('migrations', this.get('migrations'));
+ this.get('migration').set('version', this.get('version'));
+
+ if (!this.get('smartSearch')) {
+ this.set('smartSearch', false);
+ }
+
+ this.get('migration').migrate();
+ },
+
+ /**
+ * Defines the migration object.
+ *
+ * @method migration
+ * @private
+ */
+ migration: DS.IndexedDBMigration.extend(),
+
+ defaultEmptyReturn: function() {
+ return {id: null};
+ },
+ /**
+ * This methods is used by the store to retrieve one record by ID.
+ *
+ * @method serialize
+ * @param {DS.Store} store
+ * @param {DS.Model} type
+ * @param {Object|String|Integer|null} id
+ * @param {Object|null} opts
+ */
+ find: function (store, type, id, opts) {
+ var adapter = this,
+ allowRecursive = true;
+
+ /**
+ * In the case where there are relationships, this method is called again
+ * for each relation. Given the relations have references to the main
+ * object, we use allowRecursive to avoid going further into infinite
+ * recursiveness.
+ */
+ if (opts && typeof opts.allowRecursive !== 'undefined') {
+ allowRecursive = opts.allowRecursive;
+ }
+
+ return new Ember.RSVP.Promise(function(resolve, reject) {
+ var modelName = type.toString(),
+ connection, transaction, objectStore, findRequest;
+
+ adapter.openDatabase().then(function(db) {
+ transaction = db.transaction(modelName);
+ objectStore = transaction.objectStore(modelName);
+
+ findRequest = objectStore.get(id);
+ findRequest.onsuccess = function(event) {
+ var record = this.result;
+
+ Em.run(function() {
+ if (allowRecursive && record) {
+ adapter.loadRelationships(type, record).then(function(finalRecord) {
+ Em.run(function() {
+ resolve(finalRecord);
+ });
+ });
+ } else {
+ if (!record) {
+ reject();
+ } else {
+ resolve(record);
+ }
+ }
+
+ db.close();
+ });
+ };
+
+ findRequest.onerror = function(event) {
+ Em.run(function() {
+ reject(event.target.result);
+ db.close();
+ });
+ };
+ });
+ });
+ },
+
+ /**
+ * Retrieves many records from the database.
+ *
+ * @method findMany
+ * @private
+ * @param {DS.Store} store
+ * @param {DS.Model} type the model that we're retrieving
+ * @param {Array} ids ids of the records we want to be returned.
+ */
+ findMany: function (store, type, ids) {
+ var adapter = this;
+
+ return new Ember.RSVP.Promise(function(resolve, reject) {
+ var modelName = type.toString(),
+ result = [],
+ connection, transaction, objectStore, findRequest, cursor;
+
+ adapter.openDatabase().then(function(db) {
+ transaction = db.transaction(modelName);
+ objectStore = transaction.objectStore(modelName);
+
+ cursor = objectStore.openCursor();
+ cursor.onsuccess = function(event) {
+ var cursor = event.target.result;
+
+ Em.run(function() {
+ if (cursor) {
+ if (ids.contains(cursor.value.id)) {
+ result.push(cursor.value);
+ }
+ cursor.continue();
+ } else {
+ resolve(result);
+ db.close();
+ }
+ });
+ }
+
+ cursor.onerror = function() {
+ Em.run(function() {
+ reject(event.target.result);
+ db.close();
+ });
+ }
+ });
+ });
+ },
+
+ /**
+ * Retrieves many records from the database according to the query.
+ *
+ * For example, we could do:
+ *
+ * store.findQuery('customer', {name: /rambo|braddock/})
+ *
+ * @method findQuery
+ * @private
+ * @param {DS.Store} store
+ * @param {DS.Model} type the model
+ * @param {Object} query object with fields we want to look for
+ * @param {Array} recordArray
+ */
+ findQuery: function (store, type, query, recordArray) {
+ var adapter = this;
+
+ return new Ember.RSVP.Promise(function(resolve, reject) {
+ var modelName = type.toString(),
+ result = [],
+ connection, transaction, objectStore, findRequest, cursor;
+
+ adapter.openDatabase().then(function(db) {
+ transaction = db.transaction(modelName);
+ objectStore = transaction.objectStore(modelName);
+
+ cursor = objectStore.openCursor();
+ cursor.onsuccess = function(event) {
+ db.close();
+ Em.run(function() {
+ var cursor = event.target.result,
+ isMatch;
+
+ if (cursor) {
+ for (var field in query) {
+ var queryString = query[field];
+
+ /**
+ * If it was already defined that the current record doesn't match
+ * the query, leave the search.
+ */
+ if (typeof isMatch === false) {
+ break;
+ }
+
+ if (isMatch || typeof isMatch == "undefined") {
+ isMatch = adapter.findQueryCriteria(field, queryString, cursor.value, type);
+ }
+ }
+
+ if (isMatch === true) {
+ result.push(cursor.value);
+ }
+
+ cursor.continue();
+ } else {
+ if (!result.length) {
+ reject();
+ } else {
+ resolve(result);
+ }
+ }
+ });
+ }
+
+ cursor.onerror = function(event) {
+ Em.run(function() {
+ reject(event.target.result);
+ db.close();
+ });
+ }
+ });
+ }).then(function(records) {
+ if (records.get('length')) {
+ return adapter.loadRelationshipsForMany(type, records);
+ } else {
+ return records;
+ }
+ });
+ },
+
+ findQueryCriteria: function(field, queryString, record, type) {
+ var queriedField;
+ /**
+ * If smartSearch is enabled, we pass the responsibility on to
+ * `DS.IndexedDBSmartSearch`. This performs tasks like matching dates and
+ * fulltext search.
+ */
+ if (this.get('smartSearch')) {
+ var smartSearch = DS.IndexedDBSmartSearch.createWithMixins({
+ field: field,
+ queryString: queryString,
+ record: record,
+ type: type,
+ fieldSearchCriteria: this.get('findQuerySearchCriteria')
+ });
+
+ return smartSearch.isMatch();
+ } else {
+ queriedField = record[field];
+ if (Object.prototype.toString.call(queryString).match("RegExp")) {
+ return new RegExp(queryString).test(queriedField);
+ } else {
+ return (queriedField === queryString);
+ }
+ }
+ },
+
+ findQuerySearchCriteria: function(fieldName, type) {
+ return true;
+ },
+
+ /**
+ * Returns all records of a given type.
+ *
+ * @method findAll
+ * @private
+ * @param {DS.Store} store
+ * @param {DS.Model} type
+ */
+ findAll: function (store, type) {
+ var _this = this;
+
+ return new Ember.RSVP.Promise(function(resolve, reject) {
+ var modelName = type.toString(),
+ result = [],
+ connection, transaction, objectStore, findRequest, cursor;
+
+ _this.openDatabase().then(function(db) {
+ transaction = db.transaction(modelName);
+ objectStore = transaction.objectStore(modelName);
+
+ cursor = objectStore.openCursor();
+ cursor.onsuccess = function(event) {
+ Em.run(function() {
+ var cursor = event.target.result;
+
+ if (cursor) {
+ result.push(cursor.value);
+
+ cursor.continue();
+ } else {
+ resolve(result);
+ db.close();
+ }
+ });
+ }
+
+ cursor.onerror = function(event) {
+ Em.run(function() {
+ reject(event.target.result);
+ db.close();
+ });
+ }
+ });
+ });
+ },
+
+ /**
+ * Creates a record in the database.
+ *
+ * For example,
+ *
+ * ```js
+ * store.createRecord('user', {name: "Rambo"})
+ * ```
+ *
+ * @method createRecord
+ * @param {DS.Store} store
+ * @param {DS.Model} type
+ * @param {Object} record
+ */
+ createRecord: function (store, type, record) {
+ var _this = this,
+ modelName = type.toString();
+
+ return new Ember.RSVP.Promise(function(resolve, reject) {
+ var connection, transaction, objectStore, saveRequest, serializedRecord;
+
+ _this.openDatabase().then(function(db) {
+ /**
+ * TODO: saving associations should open an appropriate transaction
+ */
+ transaction = db.transaction(modelName, 'readwrite');
+
+ transaction.onerror = function(event) {
+ Em.run(function() {
+ if (Ember.testing) {
+ console.error('transaction error: ' + event);
+ }
+ });
+ }
+
+ transaction.onabort = function(event) {
+ Em.run(function() {
+ if (Ember.testing) {
+ console.error('transaction aborted: ' + event);
+ }
+ });
+ }
+
+ objectStore = transaction.objectStore(modelName);
+
+ serializedRecord = record.serialize({includeId: !objectStore.autoIncrement});
+ saveRequest = objectStore.add(serializedRecord);
+ saveRequest.onsuccess = function(event) {
+ Em.run(function() {
+ db.close();
+ _this.loadRelationships(type, serializedRecord).then(function(finalRecord) {
+ Em.run(function() {
+ resolve(finalRecord);
+ });
+ });
+ });
+ };
+
+ saveRequest.onerror = function(event) {
+ var result = this.result;
+ Em.run(function() {
+ if (Ember.testing) {
+ console.error('Add request error: ' + result);
+ }
+ reject(result);
+ db.close();
+ });
+ };
+ });
+ });
+ },
+
+ /**
+ *
+ * @method updateRecord
+ * @param {DS.Store} store
+ * @param {DS.Model} type
+ * @param {Object} record
+ */
+ updateRecord: function (store, type, record) {
+ var _this = this,
+ serializedRecord = record.serialize({includeId: true});
+
+ return new Ember.RSVP.Promise(function(resolve, reject) {
+ var modelName = type.toString(),
+ id = record.id,
+ connection, transaction, objectStore, putRequest;
+
+ _this.openDatabase().then(function(db) {
+ transaction = db.transaction(modelName, "readwrite");
+
+ transaction.onerror = function(event) {
+ Em.run(function() {
+ if (Ember.testing) {
+ console.error('transaction error: ' + event);
+ }
+ });
+ }
+
+ transaction.onabort = function(event) {
+ Em.run(function() {
+ if (Ember.testing) {
+ console.error('transaction aborted: ' + event);
+ }
+ });
+ }
+
+ objectStore = transaction.objectStore(modelName);
+
+ putRequest = objectStore.put(serializedRecord);
+ putRequest.onsuccess = function(event) {
+ Em.run(function() {
+ resolve(serializedRecord);
+ db.close();
+ });
+ };
+
+ putRequest.onerror = function(event) {
+ Em.run(function() {
+ reject(event.target.result);
+ db.close();
+ });
+ };
+ });
+ });
+ },
+
+ /**
+ *
+ * @method deleteRecord
+ * @param {DS.Store} store
+ * @param {DS.Model} type
+ * @param {Object} record
+ */
+ deleteRecord: function (store, type, record) {
+ var _this = this;
+
+ return new Ember.RSVP.Promise(function(resolve, reject) {
+ var modelName = type.toString(),
+ serializedRecord = record.serialize({includeId: true}),
+ id = serializedRecord.id,
+ connection, transaction, objectStore, operation;
+
+ _this.openDatabase().then(function(db) {
+ transaction = db.transaction(modelName, "readwrite");
+
+ transaction.onerror = function(event) {
+ Em.run(function() {
+ if (Ember.testing) {
+ console.error('transaction error: ' + event);
+ }
+ });
+ }
+
+ transaction.onabort = function(event) {
+ Em.run(function() {
+ if (Ember.testing) {
+ console.error('transaction aborted: ' + event);
+ }
+ });
+ }
+
+ objectStore = transaction.objectStore(modelName);
+
+ transaction.oncomplete = function(t) {
+ Em.run(function() {
+ resolve(serializedRecord);
+ db.close();
+ });
+ }
+
+ if (objectStore.autoIncrement) {
+ id = parseInt(id);
+ }
+ operation = objectStore.delete(id);
+ operation.onsuccess = function(event) {
+ Em.run(function() {
+ db.close();
+ resolve(serializedRecord);
+ });
+ };
+
+ operation.onerror = function(event) {
+ Em.run(function() {
+ db.close();
+ reject(event.target.result);
+ });
+ };
+ });
+ });
+ },
+
+ /**
+ * Generates a random number. You will usually want to implement UUID in your
+ * app and redefine this method.
+ *
+ * @method generateIdForRecord
+ * @private
+ */
+ generateIdForRecord: function() {
+ return Math.random().toString(32).slice(2).substr(0, 5);
+ },
+
+ /**
+ *
+ * @method openDatabase
+ * @private
+ */
+ openDatabase: function() {
+ var _this = this;
+
+ return new Ember.RSVP.Promise(function(resolve, reject) {
+ var request = window.indexedDB.open(_this.databaseName);
+
+ request.onsuccess = function(event) {
+ Em.run(function() {
+ resolve(event.target.result);
+ });
+ }
+
+ request.onerror = function(event) {
+ Em.run(function() {
+ throw('Error opening database ' + _this.databaseName);
+ reject(event);
+ });
+ }
+ });
+ },
+
+ /**
+ *
+ * @method modelRelationships
+ * @private
+ * @param {DS.Model} type the record to serialize
+ */
+ modelRelationships: function(type) {
+ return Ember.get(type, 'relationshipNames');
+ },
+
+ /**
+ * This takes a record, then analyzes the model relationships and replaces
+ * ids with the actual values.
+ *
+ * Consider the following JSON is entered:
+ *
+ * ```js
+ * {
+ * "id": 1,
+ * "title": "Rails Rambo",
+ * "comments": [1, 2]
+ * }
+ *
+ * This will return:
+ *
+ * ```js
+ * {
+ * "id": 1,
+ * "title": "Rails Rambo",
+ * "comments": [1, 2]
+ *
+ * "_embedded": {
+ * "comment": [{
+ * "_id": 1,
+ * "comment_title": "FIRST"
+ * }, {
+ * "_id": 2,
+ * "comment_title": "Rails is unagi"
+ * }]
+ * }
+ * }
+ *
+ * This way, whenever a resource returned, its relationships will be also
+ * returned.
+ *
+ * @method loadRelationships
+ * @private
+ * @param {DS.Model} type
+ * @param {Object} record
+ */
+ loadRelationships: function(type, record) {
+ var adapter = this;
+
+ return new Ember.RSVP.Promise(function(resolve, reject) {
+ var resultJSON = {},
+ typeKey = type.typeKey,
+ relationships,
+ relationshipPromises = [];
+
+ relationships = adapter.modelRelationships(type).belongsTo;
+ relationships.push.apply(relationships, adapter.modelRelationships(type).hasMany);
+
+ relationships.forEach(function(relationName) {
+ var relationModel = type.typeForRelationship(relationName),
+ relationEmbeddedId = record[relationName],
+ relationProp = adapter.relationshipProperties(type, relationName),
+ relationType = relationProp.kind,
+ /**
+ * This is the relationship field.
+ */
+ promise, embedPromise;
+
+ var opts = {allowRecursive: false};
+ /**
+ * embeddedIds are ids of relations that are included in the main
+ * payload, such as:
+ *
+ * {
+ * cart: {
+ * id: "s85fb",
+ * customer: "rld9u"
+ * }
+ * }
+ *
+ * In this case, cart belongsTo customer and its id is present in the
+ * main payload. We find each of these records and add them to _embedded.
+ */
+ if (relationEmbeddedId) {
+ if (relationType == 'belongsTo' || relationType == 'hasOne') {
+ promise = adapter.find(null, relationModel, relationEmbeddedId, opts)
+ } else if (relationType == 'hasMany') {
+ promise = adapter.findMany(null, relationModel, relationEmbeddedId, opts)
+ }
+
+ embedPromise = new Ember.RSVP.Promise(function(resolve, reject) {
+ promise.then(function(relationRecord) {
+ var finalPayload = adapter.addEmbeddedPayload(record, relationName, relationRecord)
+ resolve(finalPayload);
+ });
+ });
+
+ relationshipPromises.push(embedPromise);
+ }
+ });
+
+ Ember.RSVP.all(relationshipPromises).then(function() {
+ resolve(record);
+ });
+ });
+ },
+
+ /**
+ * Given the following payload,
+ *
+ * {
+ * cart: {
+ * id: "1",
+ * customer: "2"
+ * }
+ * }
+ *
+ * With `relationshipName` being `customer` and `relationshipRecord`
+ *
+ * {id: "2", name: "Rambo"}
+ *
+ * This method returns the following payload:
+ *
+ * {
+ * cart: {
+ * id: "1",
+ * customer: "2"
+ * },
+ * _embedded: {
+ * customer: {
+ * id: "2",
+ * name: "Rambo"
+ * }
+ * }
+ * }
+ *
+ * which is then treated by the serializer later.
+ *
+ * @method addEmbeddedPayload
+ * @private
+ * @param {Object} payload
+ * @param {String} relationshipName
+ * @param {Object} relationshipRecord
+ */
+ addEmbeddedPayload: function(payload, relationshipName, relationshipRecord) {
+ var objectHasId = (relationshipRecord && relationshipRecord.id),
+ arrayHasIds = (relationshipRecord.length && relationshipRecord.everyBy("id")),
+ isValidRelationship = (objectHasId || arrayHasIds);
+
+ if (isValidRelationship) {
+ if (!payload['_embedded']) {
+ payload['_embedded'] = {}
+ }
+
+ payload['_embedded'][relationshipName] = relationshipRecord;
+ if (relationshipRecord.length) {
+ payload[relationshipName] = relationshipRecord.mapBy('id');
+ } else {
+ payload[relationshipName] = relationshipRecord.id;
+ }
+ }
+
+ if (this.isArray(payload[relationshipName])) {
+ payload[relationshipName] = payload[relationshipName].filter(function(id) {
+ return id;
+ });
+ }
+
+ return payload;
+ },
+
+ isArray: function(value) {
+ return Object.prototype.toString.call(value) === '[object Array]';
+ },
+
+ /**
+ * Same as `loadRelationships`, but for an array of records.
+ *
+ * @method loadRelationshipsForMany
+ * @private
+ * @param {DS.Model} type
+ * @param {Object} recordsArray
+ */
+ loadRelationshipsForMany: function(type, recordsArray) {
+ var adapter = this;
+
+ return new Ember.RSVP.Promise(function(resolve, reject) {
+ var recordsWithRelationships = [],
+ recordsToBeLoaded = [],
+ promises = [];
+
+ /**
+ * Some times Ember puts some stuff in arrays. We want to clean it so
+ * we know exactly what to iterate over.
+ */
+ for (var i in recordsArray) {
+ if (recordsArray.hasOwnProperty(i)) {
+ recordsToBeLoaded.push(recordsArray[i]);
+ }
+ }
+
+ var loadNextRecord = function(record) {
+ /**
+ * Removes the first item from recordsToBeLoaded
+ */
+ recordsToBeLoaded = recordsToBeLoaded.slice(1);
+
+ var promise = adapter.loadRelationships(type, record);
+
+ promise.then(function(recordWithRelationships) {
+ recordsWithRelationships.push(recordWithRelationships);
+
+ if (recordsToBeLoaded[0]) {
+ loadNextRecord(recordsToBeLoaded[0]);
+ } else {
+ resolve(recordsWithRelationships);
+ }
+ });
+ }
+
+ /**
+ * We start by the first record
+ */
+ loadNextRecord(recordsToBeLoaded[0]);
+ });
+ },
+
+ /**
+ *
+ * @method relationshipProperties
+ * @private
+ * @param {DS.Model} type
+ * @param {String} relationName
+ */
+ relationshipProperties: function(type, relationName) {
+ var relationships = Ember.get(type, 'relationshipsByName');
+ if (relationName) {
+ return relationships.get(relationName);
+ } else {
+ return relationships;
+ }
+ }
+});
diff --git a/tests/index.html b/tests/index.html
index 8028416..f8e49bb 100755
--- a/tests/index.html
+++ b/tests/index.html
@@ -3,22 +3,22 @@
Ember-Data IndexedDB Adapter
-
+
-
+
-
-
-
-
+
+
+
+
-
+
diff --git a/tests/vendor/IndexedDBShim.min.js b/tests/vendor/IndexedDBShim.min.js
deleted file mode 100644
index 371e07a..0000000
--- a/tests/vendor/IndexedDBShim.min.js
+++ /dev/null
@@ -1,2 +0,0 @@
-/*! IndexedDBShim - v0.1.2 - 2013-07-11 */
-"use strict";var idbModules={};(function(e){function t(e,t,n,o){n.target=t,"function"==typeof t[e]&&t[e].apply(t,[n]),"function"==typeof o&&o()}function n(t,n,o){var i=new DOMException.constructor(0,n);throw i.name=t,i.message=n,e.DEBUG&&(console.log(t,n,o,i),console.trace&&console.trace()),i}var o=function(){this.length=0,this._items=[],Object.defineProperty&&Object.defineProperty(this,"_items",{enumerable:!1})};if(o.prototype={contains:function(e){return-1!==this._items.indexOf(e)},item:function(e){return this._items[e]},indexOf:function(e){return this._items.indexOf(e)},push:function(e){this._items.push(e),this.length+=1;for(var t=0;this._items.length>t;t++)this[t]=this._items[t]},splice:function(){this._items.splice.apply(this._items,arguments),this.length=this._items.length;for(var e in this)e===parseInt(e,10)+""&&delete this[e];for(e=0;this._items.length>e;e++)this[e]=this._items[e]}},Object.defineProperty)for(var i in{indexOf:!1,push:!1,splice:!1})Object.defineProperty(o.prototype,i,{enumerable:!1});e.util={throwDOMException:n,callback:t,quote:function(e){return"'"+e+"'"},StringList:o}})(idbModules),function(idbModules){var Sca=function(){return{decycle:function(object,callback){function checkForCompletion(){0===queuedObjects.length&&returnCallback(derezObj)}function readBlobAsDataURL(e,t){var n=new FileReader;n.onloadend=function(e){var n=e.target.result,o="blob";updateEncodedBlob(n,t,o)},n.readAsDataURL(e)}function updateEncodedBlob(dataURL,path,blobtype){var encoded=queuedObjects.indexOf(path);path=path.replace("$","derezObj"),eval(path+'.$enc="'+dataURL+'"'),eval(path+'.$type="'+blobtype+'"'),queuedObjects.splice(encoded,1),checkForCompletion()}function derez(e,t){var n,o,i;if(!("object"!=typeof e||null===e||e instanceof Boolean||e instanceof Date||e instanceof Number||e instanceof RegExp||e instanceof Blob||e instanceof String)){for(n=0;objects.length>n;n+=1)if(objects[n]===e)return{$ref:paths[n]};if(objects.push(e),paths.push(t),"[object Array]"===Object.prototype.toString.apply(e))for(i=[],n=0;e.length>n;n+=1)i[n]=derez(e[n],t+"["+n+"]");else{i={};for(o in e)Object.prototype.hasOwnProperty.call(e,o)&&(i[o]=derez(e[o],t+"["+JSON.stringify(o)+"]"))}return i}return e instanceof Blob?(queuedObjects.push(t),readBlobAsDataURL(e,t)):e instanceof Boolean?e={$type:"bool",$enc:""+e}:e instanceof Date?e={$type:"date",$enc:e.getTime()}:e instanceof Number?e={$type:"num",$enc:""+e}:e instanceof RegExp&&(e={$type:"regex",$enc:""+e}),e}var objects=[],paths=[],queuedObjects=[],returnCallback=callback,derezObj=derez(object,"$");checkForCompletion()},retrocycle:function retrocycle($){function dataURLToBlob(e){var t,n,o,i=";base64,";if(-1===e.indexOf(i))return n=e.split(","),t=n[0].split(":")[1],o=n[1],new Blob([o],{type:t});n=e.split(i),t=n[0].split(":")[1],o=window.atob(n[1]);for(var r=o.length,a=new Uint8Array(r),s=0;r>s;++s)a[s]=o.charCodeAt(s);return new Blob([a.buffer],{type:t})}function rez(value){var i,item,name,path;if(value&&"object"==typeof value)if("[object Array]"===Object.prototype.toString.apply(value))for(i=0;value.length>i;i+=1)item=value[i],item&&"object"==typeof item&&(path=item.$ref,value[i]="string"==typeof path&&px.test(path)?eval(path):rez(item));else if(void 0!==value.$type)switch(value.$type){case"blob":case"file":value=dataURLToBlob(value.$enc);break;case"bool":value=Boolean("true"===value.$enc);break;case"date":value=new Date(value.$enc);break;case"num":value=Number(value.$enc);break;case"regex":value=eval(value.$enc)}else for(name in value)"object"==typeof value[name]&&(item=value[name],item&&(path=item.$ref,value[name]="string"==typeof path&&px.test(path)?eval(path):rez(item)));return value}var px=/^\$(?:\[(?:\d+|\"(?:[^\\\"\u0000-\u001f]|\\([\\\"\/bfnrt]|u[0-9a-zA-Z]{4}))*\")\])*$/;return rez($),$},encode:function(e,t){function n(e){t(JSON.stringify(e))}this.decycle(e,n)},decode:function(e){return this.retrocycle(JSON.parse(e))}}}();idbModules.Sca=Sca}(idbModules),function(e){var t=["","number","string","boolean","object","undefined"],n=function(){return{encode:function(e){return t.indexOf(typeof e)+"-"+JSON.stringify(e)},decode:function(e){return e===void 0?void 0:JSON.parse(e.substring(2))}}},o={number:n("number"),"boolean":n(),object:n(),string:{encode:function(e){return t.indexOf("string")+"-"+e},decode:function(e){return""+e.substring(2)}},undefined:{encode:function(){return t.indexOf("undefined")+"-undefined"},decode:function(){return void 0}}},i=function(){return{encode:function(e){return o[typeof e].encode(e)},decode:function(e){return o[t[e.substring(0,1)]].decode(e)}}}();e.Key=i}(idbModules),function(e){var t=function(e,t){return{type:e,debug:t,bubbles:!1,cancelable:!1,eventPhase:0,timeStamp:new Date}};e.Event=t}(idbModules),function(e){var t=function(){this.onsuccess=this.onerror=this.result=this.error=this.source=this.transaction=null,this.readyState="pending"},n=function(){this.onblocked=this.onupgradeneeded=null};n.prototype=t,e.IDBRequest=t,e.IDBOpenRequest=n}(idbModules),function(e,t){var n=function(e,t,n,o){this.lower=e,this.upper=t,this.lowerOpen=n,this.upperOpen=o};n.only=function(e){return new n(e,e,!0,!0)},n.lowerBound=function(e,o){return new n(e,t,o,t)},n.upperBound=function(e){return new n(t,e,t,open)},n.bound=function(e,t,o,i){return new n(e,t,o,i)},e.IDBKeyRange=n}(idbModules),function(e,t){function n(n,o,i,r,a,s){this.__range=n,this.source=this.__idbObjectStore=i,this.__req=r,this.key=t,this.direction=o,this.__keyColumnName=a,this.__valueColumnName=s,this.source.transaction.__active||e.util.throwDOMException("TransactionInactiveError - The transaction this IDBObjectStore belongs to is not active."),this.__offset=-1,this.__lastKeyContinued=t,this["continue"]()}n.prototype.__find=function(n,o,i,r){var a=this,s=["SELECT * FROM ",e.util.quote(a.__idbObjectStore.name)],u=[];s.push("WHERE ",a.__keyColumnName," NOT NULL"),a.__range&&(a.__range.lower||a.__range.upper)&&(s.push("AND"),a.__range.lower&&(s.push(a.__keyColumnName+(a.__range.lowerOpen?" >":" >= ")+" ?"),u.push(e.Key.encode(a.__range.lower))),a.__range.lower&&a.__range.upper&&s.push("AND"),a.__range.upper&&(s.push(a.__keyColumnName+(a.__range.upperOpen?" < ":" <= ")+" ?"),u.push(e.Key.encode(a.__range.upper)))),n!==t&&(a.__lastKeyContinued=n,a.__offset=0),a.__lastKeyContinued!==t&&(s.push("AND "+a.__keyColumnName+" >= ?"),u.push(e.Key.encode(a.__lastKeyContinued))),s.push("ORDER BY ",a.__keyColumnName),s.push("LIMIT 1 OFFSET "+a.__offset),e.DEBUG&&console.log(s.join(" "),u),o.executeSql(s.join(" "),u,function(n,o){if(1===o.rows.length){var r=e.Key.decode(o.rows.item(0)[a.__keyColumnName]),s="value"===a.__valueColumnName?e.Sca.decode(o.rows.item(0)[a.__valueColumnName]):e.Key.decode(o.rows.item(0)[a.__valueColumnName]);i(r,s)}else e.DEBUG&&console.log("Reached end of cursors"),i(t,t)},function(t,n){e.DEBUG&&console.log("Could not execute Cursor.continue"),r(n)})},n.prototype["continue"]=function(e){var n=this;this.__idbObjectStore.transaction.__addToTransactionQueue(function(o,i,r,a){n.__offset++,n.__find(e,o,function(e,o){n.key=e,n.value=o,r(n.key!==t?n:t,n.__req)},function(e){a(e)})})},n.prototype.advance=function(n){0>=n&&e.util.throwDOMException("Type Error - Count is invalid - 0 or negative",n);var o=this;this.__idbObjectStore.transaction.__addToTransactionQueue(function(e,i,r,a){o.__offset+=n,o.__find(t,e,function(e,n){o.key=e,o.value=n,r(o.key!==t?o:t,o.__req)},function(e){a(e)})})},n.prototype.update=function(n){var o=this,i=this.__idbObjectStore.transaction.__createRequest(function(){});return e.Sca.encode(n,function(n){this.__idbObjectStore.__pushToQueue(i,function(i,r,a,s){o.__find(t,i,function(t){var r="UPDATE "+e.util.quote(o.__idbObjectStore.name)+" SET value = ? WHERE key = ?";e.DEBUG&&console.log(r,n,t),i.executeSql(r,[e.Sca.encode(n),e.Key.encode(t)],function(e,n){1===n.rowsAffected?a(t):s("No rowns with key found"+t)},function(e,t){s(t)})},function(e){s(e)})})}),i},n.prototype["delete"]=function(){var n=this;return this.__idbObjectStore.transaction.__addToTransactionQueue(function(o,i,r,a){n.__find(t,o,function(i){var s="DELETE FROM "+e.util.quote(n.__idbObjectStore.name)+" WHERE key = ?";e.DEBUG&&console.log(s,i),o.executeSql(s,[e.Key.encode(i)],function(e,n){1===n.rowsAffected?r(t):a("No rowns with key found"+i)},function(e,t){a(t)})},function(e){a(e)})})},e.IDBCursor=n}(idbModules),function(idbModules,undefined){function IDBIndex(e,t){this.indexName=this.name=e,this.__idbObjectStore=this.objectStore=this.source=t;var n=t.__storeProps&&t.__storeProps.indexList;n&&(n=JSON.parse(n)),this.keyPath=n&&n[e]&&n[e].keyPath||e,["multiEntry","unique"].forEach(function(t){this[t]=!!(n&&n[e]&&n[e].optionalParams&&n[e].optionalParams[t])},this)}IDBIndex.prototype.__createIndex=function(indexName,keyPath,optionalParameters){var me=this,transaction=me.__idbObjectStore.transaction;transaction.__addToTransactionQueue(function(tx,args,success,failure){me.__idbObjectStore.__getStoreProps(tx,function(){function error(){idbModules.util.throwDOMException(0,"Could not create new index",arguments)}2!==transaction.mode&&idbModules.util.throwDOMException(0,"Invalid State error, not a version transaction",me.transaction);var idxList=JSON.parse(me.__idbObjectStore.__storeProps.indexList);idxList[indexName]!==undefined&&idbModules.util.throwDOMException(0,"Index already exists on store",idxList);var columnName=indexName;idxList[indexName]={columnName:columnName,keyPath:keyPath,optionalParams:optionalParameters},me.__idbObjectStore.__storeProps.indexList=JSON.stringify(idxList);var sql=["ALTER TABLE",idbModules.util.quote(me.__idbObjectStore.name),"ADD",columnName,"BLOB"].join(" ");idbModules.DEBUG&&console.log(sql),tx.executeSql(sql,[],function(tx,data){tx.executeSql("SELECT * FROM "+idbModules.util.quote(me.__idbObjectStore.name),[],function(tx,data){(function initIndexForRow(i){if(data.rows.length>i)try{var value=idbModules.Sca.decode(data.rows.item(i).value),indexKey=eval("value['"+keyPath+"']");tx.executeSql("UPDATE "+idbModules.util.quote(me.__idbObjectStore.name)+" set "+columnName+" = ? where key = ?",[idbModules.Key.encode(indexKey),data.rows.item(i).key],function(){initIndexForRow(i+1)},error)}catch(e){initIndexForRow(i+1)}else idbModules.DEBUG&&console.log("Updating the indexes in table",me.__idbObjectStore.__storeProps),tx.executeSql("UPDATE __sys__ set indexList = ? where name = ?",[me.__idbObjectStore.__storeProps.indexList,me.__idbObjectStore.name],function(){me.__idbObjectStore.__setReadyState("createIndex",!0),success(me)},error)})(0)},error)},error)},"createObjectStore")})},IDBIndex.prototype.openCursor=function(e,t){var n=new idbModules.IDBRequest;return new idbModules.IDBCursor(e,t,this.source,n,this.indexName,"value"),n},IDBIndex.prototype.openKeyCursor=function(e,t){var n=new idbModules.IDBRequest;return new idbModules.IDBCursor(e,t,this.source,n,this.indexName,"key"),n},IDBIndex.prototype.__fetchIndexData=function(e,t){var n=this;return n.__idbObjectStore.transaction.__addToTransactionQueue(function(o,i,r,a){var s=["SELECT * FROM ",idbModules.util.quote(n.__idbObjectStore.name)," WHERE",n.indexName,"NOT NULL"],u=[];e!==undefined&&(s.push("AND",n.indexName," = ?"),u.push(idbModules.Key.encode(e))),idbModules.DEBUG&&console.log("Trying to fetch data for Index",s.join(" "),u),o.executeSql(s.join(" "),u,function(e,n){var o;o="count"==typeof t?n.rows.length:0===n.rows.length?undefined:"key"===t?idbModules.Key.decode(n.rows.item(0).key):idbModules.Sca.decode(n.rows.item(0).value),r(o)},a)})},IDBIndex.prototype.get=function(e){return this.__fetchIndexData(e,"value")},IDBIndex.prototype.getKey=function(e){return this.__fetchIndexData(e,"key")},IDBIndex.prototype.count=function(e){return this.__fetchIndexData(e,"count")},idbModules.IDBIndex=IDBIndex}(idbModules),function(idbModules){var IDBObjectStore=function(e,t,n){this.name=e,this.transaction=t,this.__ready={},this.__setReadyState("createObjectStore",n===void 0?!0:n),this.indexNames=new idbModules.util.StringList};IDBObjectStore.prototype.__setReadyState=function(e,t){this.__ready[e]=t},IDBObjectStore.prototype.__waitForReady=function(e,t){var n=!0;if(t!==void 0)n=this.__ready[t]===void 0?!0:this.__ready[t];else for(var o in this.__ready)this.__ready[o]||(n=!1);if(n)e();else{idbModules.DEBUG&&console.log("Waiting for to be ready",t);var i=this;window.setTimeout(function(){i.__waitForReady(e,t)},100)}},IDBObjectStore.prototype.__getStoreProps=function(e,t,n){var o=this;this.__waitForReady(function(){o.__storeProps?(idbModules.DEBUG&&console.log("Store properties - cached",o.__storeProps),t(o.__storeProps)):e.executeSql("SELECT * FROM __sys__ where name = ?",[o.name],function(e,n){1!==n.rows.length?t():(o.__storeProps={name:n.rows.item(0).name,indexList:n.rows.item(0).indexList,autoInc:n.rows.item(0).autoInc,keyPath:n.rows.item(0).keyPath},idbModules.DEBUG&&console.log("Store properties",o.__storeProps),t(o.__storeProps))},function(){t()})},n)},IDBObjectStore.prototype.__deriveKey=function(tx,value,key,callback){function getNextAutoIncKey(){tx.executeSql("SELECT * FROM sqlite_sequence where name like ?",[me.name],function(e,t){1!==t.rows.length?callback(0):callback(t.rows.item(0).seq)},function(e,t){idbModules.util.throwDOMException(0,"Data Error - Could not get the auto increment value for key",t)})}var me=this;me.__getStoreProps(tx,function(props){if(props||idbModules.util.throwDOMException(0,"Data Error - Could not locate defination for this table",props),props.keyPath)if(key!==void 0&&idbModules.util.throwDOMException(0,"Data Error - The object store uses in-line keys and the key parameter was provided",props),value)try{var primaryKey=eval("value['"+props.keyPath+"']");primaryKey?callback(primaryKey):"true"===props.autoInc?getNextAutoIncKey():idbModules.util.throwDOMException(0,"Data Error - Could not eval key from keyPath")}catch(e){idbModules.util.throwDOMException(0,"Data Error - Could not eval key from keyPath",e)}else idbModules.util.throwDOMException(0,"Data Error - KeyPath was specified, but value was not");else key!==void 0?callback(key):"false"===props.autoInc?idbModules.util.throwDOMException(0,"Data Error - The object store uses out-of-line keys and has no key generator and the key parameter was not provided. ",props):getNextAutoIncKey()})},IDBObjectStore.prototype.__insertData=function(tx,value,primaryKey,success,error){var paramMap={};primaryKey!==void 0&&(paramMap.key=idbModules.Key.encode(primaryKey));var indexes=JSON.parse(this.__storeProps.indexList);for(var key in indexes)try{paramMap[indexes[key].columnName]=idbModules.Key.encode(eval("value['"+indexes[key].keyPath+"']"))}catch(e){error(e)}var sqlStart=["INSERT INTO ",idbModules.util.quote(this.name),"("],sqlEnd=[" VALUES ("],sqlValues=[];for(key in paramMap)sqlStart.push(key+","),sqlEnd.push("?,"),sqlValues.push(paramMap[key]);sqlStart.push("value )"),sqlEnd.push("?)"),sqlValues.push(value);var sql=sqlStart.join(" ")+sqlEnd.join(" ");idbModules.DEBUG&&console.log("SQL for adding",sql,sqlValues),tx.executeSql(sql,sqlValues,function(){success(primaryKey)},function(e,t){error(t)})},IDBObjectStore.prototype.add=function(e,t){var n=this,o=n.transaction.__createRequest(function(){});return idbModules.Sca.encode(e,function(i){n.transaction.__pushToQueue(o,function(o,r,a,s){n.__deriveKey(o,e,t,function(e){n.__insertData(o,i,e,a,s)})})}),o},IDBObjectStore.prototype.put=function(e,t){var n=this,o=n.transaction.__createRequest(function(){});return idbModules.Sca.encode(e,function(i){n.transaction.__pushToQueue(o,function(o,r,a,s){n.__deriveKey(o,e,t,function(e){var t="DELETE FROM "+idbModules.util.quote(n.name)+" where key = ?";o.executeSql(t,[idbModules.Key.encode(e)],function(t,o){idbModules.DEBUG&&console.log("Did the row with the",e,"exist? ",o.rowsAffected),n.__insertData(t,i,e,a,s)},function(e,t){s(t)})})})}),o},IDBObjectStore.prototype.get=function(e){var t=this;return t.transaction.__addToTransactionQueue(function(n,o,i,r){t.__waitForReady(function(){var o=idbModules.Key.encode(e);idbModules.DEBUG&&console.log("Fetching",t.name,o),n.executeSql("SELECT * FROM "+idbModules.util.quote(t.name)+" where key = ?",[o],function(e,t){idbModules.DEBUG&&console.log("Fetched data",t);try{if(0===t.rows.length)return i();i(idbModules.Sca.decode(t.rows.item(0).value))}catch(n){idbModules.DEBUG&&console.log(n),i(void 0)}},function(e,t){r(t)})})})},IDBObjectStore.prototype["delete"]=function(e){var t=this;return t.transaction.__addToTransactionQueue(function(n,o,i,r){t.__waitForReady(function(){var o=idbModules.Key.encode(e);idbModules.DEBUG&&console.log("Fetching",t.name,o),n.executeSql("DELETE FROM "+idbModules.util.quote(t.name)+" where key = ?",[o],function(e,t){idbModules.DEBUG&&console.log("Deleted from database",t.rowsAffected),i()},function(e,t){r(t)})})})},IDBObjectStore.prototype.clear=function(){var e=this;return e.transaction.__addToTransactionQueue(function(t,n,o,i){e.__waitForReady(function(){t.executeSql("DELETE FROM "+idbModules.util.quote(e.name),[],function(e,t){idbModules.DEBUG&&console.log("Cleared all records from database",t.rowsAffected),o()},function(e,t){i(t)})})})},IDBObjectStore.prototype.count=function(e){var t=this;return t.transaction.__addToTransactionQueue(function(n,o,i,r){t.__waitForReady(function(){var o="SELECT * FROM "+idbModules.util.quote(t.name)+(e!==void 0?" WHERE key = ?":""),a=[];e!==void 0&&a.push(idbModules.Key.encode(e)),n.executeSql(o,a,function(e,t){i(t.rows.length)},function(e,t){r(t)})})})},IDBObjectStore.prototype.openCursor=function(e,t){var n=new idbModules.IDBRequest;return new idbModules.IDBCursor(e,t,this,n,"key","value"),n},IDBObjectStore.prototype.index=function(e){var t=new idbModules.IDBIndex(e,this);return t},IDBObjectStore.prototype.createIndex=function(e,t,n){var o=this;n=n||{},o.__setReadyState("createIndex",!1);var i=new idbModules.IDBIndex(e,o);return o.__waitForReady(function(){i.__createIndex(e,t,n)},"createObjectStore"),o.indexNames.push(e),i},IDBObjectStore.prototype.deleteIndex=function(e){var t=new idbModules.IDBIndex(e,this,!1);return t.__deleteIndex(e),t},idbModules.IDBObjectStore=IDBObjectStore}(idbModules),function(e){var t=0,n=1,o=2,i=function(o,i,r){if("number"==typeof i)this.mode=i,2!==i&&e.DEBUG&&console.log("Mode should be a string, but was specified as ",i);else if("string"==typeof i)switch(i){case"readwrite":this.mode=n;break;case"readonly":this.mode=t;break;default:this.mode=t}this.storeNames="string"==typeof o?[o]:o;for(var a=0;this.storeNames.length>a;a++)r.objectStoreNames.contains(this.storeNames[a])||e.util.throwDOMException(0,"The operation failed because the requested database object could not be found. For example, an object store did not exist but was being opened.",this.storeNames[a]);this.__active=!0,this.__running=!1,this.__requests=[],this.__aborted=!1,this.db=r,this.error=null,this.onabort=this.onerror=this.oncomplete=null};i.prototype.__executeRequests=function(){if(this.__running&&this.mode!==o)return e.DEBUG&&console.log("Looks like the request set is already running",this.mode),void 0;this.__running=!0;var t=this;window.setTimeout(function(){2===t.mode||t.__active||e.util.throwDOMException(0,"A request was placed against a transaction which is currently not active, or which is finished",t.__active),t.db.__db.transaction(function(n){function o(t,n){n&&(a.req=n),a.req.readyState="done",a.req.result=t,delete a.req.error;var o=e.Event("success");e.util.callback("onsuccess",a.req,o),s++,r()}function i(){a.req.readyState="done",a.req.error="DOMError";var t=e.Event("error",arguments);e.util.callback("onerror",a.req,t),s++,r()}function r(){return s>=t.__requests.length?(t.__active=!1,t.__requests=[],void 0):(a=t.__requests[s],a.op(n,a.args,o,i),void 0)}t.__tx=n;var a=null,s=0;try{r()}catch(u){e.DEBUG&&console.log("An exception occured in transaction",arguments),"function"==typeof t.onerror&&t.onerror()}},function(){e.DEBUG&&console.log("An error in transaction",arguments),"function"==typeof t.onerror&&t.onerror()},function(){e.DEBUG&&console.log("Transaction completed",arguments),"function"==typeof t.oncomplete&&t.oncomplete()})},1)},i.prototype.__addToTransactionQueue=function(t,n){this.__active||this.mode===o||e.util.throwDOMException(0,"A request was placed against a transaction which is currently not active, or which is finished.",this.__mode);var i=this.__createRequest();return this.__pushToQueue(i,t,n),i},i.prototype.__createRequest=function(){var t=new e.IDBRequest;return t.source=this.db,t},i.prototype.__pushToQueue=function(e,t,n){this.__requests.push({op:t,args:n,req:e}),this.__executeRequests()},i.prototype.objectStore=function(t){return new e.IDBObjectStore(t,this)},i.prototype.abort=function(){!this.__active&&e.util.throwDOMException(0,"A request was placed against a transaction which is currently not active, or which is finished",this.__active)},i.prototype.READ_ONLY=0,i.prototype.READ_WRITE=1,i.prototype.VERSION_CHANGE=2,e.IDBTransaction=i}(idbModules),function(e){var t=function(t,n,o,i){this.__db=t,this.version=o,this.__storeProperties=i,this.objectStoreNames=new e.util.StringList;for(var r=0;i.rows.length>r;r++)this.objectStoreNames.push(i.rows.item(r).name);this.name=n,this.onabort=this.onerror=this.onversionchange=null};t.prototype.createObjectStore=function(t,n){var o=this;n=n||{},n.keyPath=n.keyPath||null;var i=new e.IDBObjectStore(t,o.__versionTransaction,!1),r=o.__versionTransaction;return r.__addToTransactionQueue(function(r,a,s){function u(){e.util.throwDOMException(0,"Could not create new object store",arguments)}o.__versionTransaction||e.util.throwDOMException(0,"Invalid State error",o.transaction);var c=["CREATE TABLE",e.util.quote(t),"(key BLOB",n.autoIncrement?", inc INTEGER PRIMARY KEY AUTOINCREMENT":"PRIMARY KEY",", value BLOB)"].join(" ");e.DEBUG&&console.log(c),r.executeSql(c,[],function(e){e.executeSql("INSERT INTO __sys__ VALUES (?,?,?,?)",[t,n.keyPath,n.autoIncrement?!0:!1,"{}"],function(){i.__setReadyState("createObjectStore",!0),s(i)},u)},u)}),o.objectStoreNames.push(t),i},t.prototype.deleteObjectStore=function(t){var n=function(){e.util.throwDOMException(0,"Could not delete ObjectStore",arguments)},o=this;!o.objectStoreNames.contains(t)&&n("Object Store does not exist"),o.objectStoreNames.splice(o.objectStoreNames.indexOf(t),1);var i=o.__versionTransaction;i.__addToTransactionQueue(function(){o.__versionTransaction||e.util.throwDOMException(0,"Invalid State error",o.transaction),o.__db.transaction(function(o){o.executeSql("SELECT * FROM __sys__ where name = ?",[t],function(o,i){i.rows.length>0&&o.executeSql("DROP TABLE "+e.util.quote(t),[],function(){o.executeSql("DELETE FROM __sys__ WHERE name = ?",[t],function(){},n)},n)})})})},t.prototype.close=function(){},t.prototype.transaction=function(t,n){var o=new e.IDBTransaction(t,n||1,this);return o},e.IDBDatabase=t}(idbModules),function(e){var t=4194304;if(window.openDatabase){var n=window.openDatabase("__sysdb__",1,"System Database",t);n.transaction(function(t){t.executeSql("SELECT * FROM dbVersions",[],function(){},function(){n.transaction(function(t){t.executeSql("CREATE TABLE IF NOT EXISTS dbVersions (name VARCHAR(255), version INT);",[],function(){},function(){e.util.throwDOMException("Could not create table __sysdb__ to save DB versions")})})})},function(){e.DEBUG&&console.log("Error in sysdb transaction - when selecting from dbVersions",arguments)});var o={open:function(o,i){function r(){if(!u){var t=e.Event("error",arguments);s.readyState="done",s.error="DOMError",e.util.callback("onerror",s,t),u=!0}}function a(a){var u=window.openDatabase(o,1,o,t);s.readyState="done",i===void 0&&(i=a||1),(0>=i||a>i)&&e.util.throwDOMException(0,"An attempt was made to open a database using a lower version than the existing version.",i),u.transaction(function(t){t.executeSql("CREATE TABLE IF NOT EXISTS __sys__ (name VARCHAR(255), keyPath VARCHAR(255), autoInc BOOLEAN, indexList BLOB)",[],function(){t.executeSql("SELECT * FROM __sys__",[],function(t,c){var d=e.Event("success");s.source=s.result=new e.IDBDatabase(u,o,i,c),i>a?n.transaction(function(t){t.executeSql("UPDATE dbVersions set version = ? where name = ?",[i,o],function(){var t=e.Event("upgradeneeded");t.oldVersion=a,t.newVersion=i,s.transaction=s.result.__versionTransaction=new e.IDBTransaction([],2,s.source),e.util.callback("onupgradeneeded",s,t,function(){var t=e.Event("success");e.util.callback("onsuccess",s,t)})},r)},r):e.util.callback("onsuccess",s,d)},r)},r)},r)}var s=new e.IDBOpenRequest,u=!1;return n.transaction(function(e){e.executeSql("SELECT * FROM dbVersions where name = ?",[o],function(e,t){0===t.rows.length?e.executeSql("INSERT INTO dbVersions VALUES (?,?)",[o,i||1],function(){a(0)},r):a(t.rows.item(0).version)},r)},r),s},deleteDatabase:function(o){function i(t){if(!s){a.readyState="done",a.error="DOMError";var n=e.Event("error");n.message=t,n.debug=arguments,e.util.callback("onerror",a,n),s=!0}}function r(){n.transaction(function(t){t.executeSql("DELETE FROM dbVersions where name = ? ",[o],function(){a.result=void 0;var t=e.Event("success");t.newVersion=null,t.oldVersion=u,e.util.callback("onsuccess",a,t)},i)},i)}var a=new e.IDBOpenRequest,s=!1,u=null;return n.transaction(function(n){n.executeSql("SELECT * FROM dbVersions where name = ?",[o],function(n,s){if(0===s.rows.length){a.result=void 0;var c=e.Event("success");return c.newVersion=null,c.oldVersion=u,e.util.callback("onsuccess",a,c),void 0}u=s.rows.item(0).version;var d=window.openDatabase(o,1,o,t);d.transaction(function(t){t.executeSql("SELECT * FROM __sys__",[],function(t,n){var o=n.rows;(function a(n){n>=o.length?t.executeSql("DROP TABLE __sys__",[],function(){r()},i):t.executeSql("DROP TABLE "+e.util.quote(o.item(n).name),[],function(){a(n+1)},function(){a(n+1)})})(0)},function(){r()})},i)})},i),a},cmp:function(t,n){return e.Key.encode(t)>e.Key.encode(n)?1:t===n?0:-1}};e.shimIndexedDB=o}}(idbModules),function(e,t){e.openDatabase!==void 0&&(e.shimIndexedDB=t.shimIndexedDB,e.shimIndexedDB&&(e.shimIndexedDB.__useShim=function(){e.indexedDB=t.shimIndexedDB,e.IDBDatabase=t.IDBDatabase,e.IDBTransaction=t.IDBTransaction,e.IDBCursor=t.IDBCursor,e.IDBKeyRange=t.IDBKeyRange},e.shimIndexedDB.__debug=function(e){t.DEBUG=e})),e.indexedDB=e.indexedDB||e.webkitIndexedDB||e.mozIndexedDB||e.oIndexedDB||e.msIndexedDB,e.indexedDB===void 0&&e.openDatabase!==void 0?e.shimIndexedDB.__useShim():(e.IDBDatabase=e.IDBDatabase||e.webkitIDBDatabase,e.IDBTransaction=e.IDBTransaction||e.webkitIDBTransaction,e.IDBCursor=e.IDBCursor||e.webkitIDBCursor,e.IDBKeyRange=e.IDBKeyRange||e.webkitIDBKeyRange,e.IDBTransaction||(e.IDBTransaction={}),e.IDBTransaction.READ_ONLY=e.IDBTransaction.READ_ONLY||"readonly",e.IDBTransaction.READ_WRITE=e.IDBTransaction.READ_WRITE||"readwrite")}(window,idbModules);
diff --git a/tests/vendor/qunit.css b/tests/vendor/qunit.css
deleted file mode 100755
index a05fbf2..0000000
--- a/tests/vendor/qunit.css
+++ /dev/null
@@ -1,235 +0,0 @@
-/**
- * QUnit v1.10.0 - A JavaScript Unit Testing Framework
- *
- * http://qunitjs.com
- *
- * Copyright 2012 jQuery Foundation and other contributors
- * Released under the MIT license.
- * http://jquery.org/license
- */
-
-/** Font Family and Sizes */
-
-#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
- font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
-}
-
-#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
-#qunit-tests { font-size: smaller; }
-
-
-/** Resets */
-
-#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
- margin: 0;
- padding: 0;
-}
-
-
-/** Header */
-
-#qunit-header {
- padding: 0.5em 0 0.5em 1em;
-
- color: #8699a4;
- background-color: #0d3349;
-
- font-size: 1.5em;
- line-height: 1em;
- font-weight: normal;
-
- border-radius: 5px 5px 0 0;
- -moz-border-radius: 5px 5px 0 0;
- -webkit-border-top-right-radius: 5px;
- -webkit-border-top-left-radius: 5px;
-}
-
-#qunit-header a {
- text-decoration: none;
- color: #c2ccd1;
-}
-
-#qunit-header a:hover,
-#qunit-header a:focus {
- color: #fff;
-}
-
-#qunit-testrunner-toolbar label {
- display: inline-block;
- padding: 0 .5em 0 .1em;
-}
-
-#qunit-banner {
- height: 5px;
-}
-
-#qunit-testrunner-toolbar {
- padding: 0.5em 0 0.5em 2em;
- color: #5E740B;
- background-color: #eee;
- overflow: hidden;
-}
-
-#qunit-userAgent {
- padding: 0.5em 0 0.5em 2.5em;
- background-color: #2b81af;
- color: #fff;
- text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
-}
-
-#qunit-modulefilter-container {
- float: right;
-}
-
-/** Tests: Pass/Fail */
-
-#qunit-tests {
- list-style-position: inside;
-}
-
-#qunit-tests li {
- padding: 0.4em 0.5em 0.4em 2.5em;
- border-bottom: 1px solid #fff;
- list-style-position: inside;
-}
-
-#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
- display: none;
-}
-
-#qunit-tests li strong {
- cursor: pointer;
-}
-
-#qunit-tests li a {
- padding: 0.5em;
- color: #c2ccd1;
- text-decoration: none;
-}
-#qunit-tests li a:hover,
-#qunit-tests li a:focus {
- color: #000;
-}
-
-#qunit-tests ol {
- margin-top: 0.5em;
- padding: 0.5em;
-
- background-color: #fff;
-
- border-radius: 5px;
- -moz-border-radius: 5px;
- -webkit-border-radius: 5px;
-}
-
-#qunit-tests table {
- border-collapse: collapse;
- margin-top: .2em;
-}
-
-#qunit-tests th {
- text-align: right;
- vertical-align: top;
- padding: 0 .5em 0 0;
-}
-
-#qunit-tests td {
- vertical-align: top;
-}
-
-#qunit-tests pre {
- margin: 0;
- white-space: pre-wrap;
- word-wrap: break-word;
-}
-
-#qunit-tests del {
- background-color: #e0f2be;
- color: #374e0c;
- text-decoration: none;
-}
-
-#qunit-tests ins {
- background-color: #ffcaca;
- color: #500;
- text-decoration: none;
-}
-
-/*** Test Counts */
-
-#qunit-tests b.counts { color: black; }
-#qunit-tests b.passed { color: #5E740B; }
-#qunit-tests b.failed { color: #710909; }
-
-#qunit-tests li li {
- padding: 5px;
- background-color: #fff;
- border-bottom: none;
- list-style-position: inside;
-}
-
-/*** Passing Styles */
-
-#qunit-tests li li.pass {
- color: #3c510c;
- background-color: #fff;
- border-left: 10px solid #C6E746;
-}
-
-#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
-#qunit-tests .pass .test-name { color: #366097; }
-
-#qunit-tests .pass .test-actual,
-#qunit-tests .pass .test-expected { color: #999999; }
-
-#qunit-banner.qunit-pass { background-color: #C6E746; }
-
-/*** Failing Styles */
-
-#qunit-tests li li.fail {
- color: #710909;
- background-color: #fff;
- border-left: 10px solid #EE5757;
- white-space: pre;
-}
-
-#qunit-tests > li:last-child {
- border-radius: 0 0 5px 5px;
- -moz-border-radius: 0 0 5px 5px;
- -webkit-border-bottom-right-radius: 5px;
- -webkit-border-bottom-left-radius: 5px;
-}
-
-#qunit-tests .fail { color: #000000; background-color: #EE5757; }
-#qunit-tests .fail .test-name,
-#qunit-tests .fail .module-name { color: #000000; }
-
-#qunit-tests .fail .test-actual { color: #EE5757; }
-#qunit-tests .fail .test-expected { color: green; }
-
-#qunit-banner.qunit-fail { background-color: #EE5757; }
-
-
-/** Result */
-
-#qunit-testresult {
- padding: 0.5em 0.5em 0.5em 2.5em;
-
- color: #2b81af;
- background-color: #D2E0E6;
-
- border-bottom: 1px solid white;
-}
-#qunit-testresult .module-name {
- font-weight: bold;
-}
-
-/** Fixture */
-
-#qunit-fixture {
- position: absolute;
- top: -10000px;
- left: -10000px;
- width: 1000px;
- height: 1000px;
-}
\ No newline at end of file
diff --git a/tests/vendor/qunit.js b/tests/vendor/qunit.js
deleted file mode 100755
index 84c7390..0000000
--- a/tests/vendor/qunit.js
+++ /dev/null
@@ -1,2212 +0,0 @@
-/**
- * QUnit v1.12.0 - A JavaScript Unit Testing Framework
- *
- * http://qunitjs.com
- *
- * Copyright 2013 jQuery Foundation and other contributors
- * Released under the MIT license.
- * https://jquery.org/license/
- */
-
-(function( window ) {
-
-var QUnit,
- assert,
- config,
- onErrorFnPrev,
- testId = 0,
- fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""),
- toString = Object.prototype.toString,
- hasOwn = Object.prototype.hasOwnProperty,
- // Keep a local reference to Date (GH-283)
- Date = window.Date,
- setTimeout = window.setTimeout,
- defined = {
- setTimeout: typeof window.setTimeout !== "undefined",
- sessionStorage: (function() {
- var x = "qunit-test-string";
- try {
- sessionStorage.setItem( x, x );
- sessionStorage.removeItem( x );
- return true;
- } catch( e ) {
- return false;
- }
- }())
- },
- /**
- * Provides a normalized error string, correcting an issue
- * with IE 7 (and prior) where Error.prototype.toString is
- * not properly implemented
- *
- * Based on http://es5.github.com/#x15.11.4.4
- *
- * @param {String|Error} error
- * @return {String} error message
- */
- errorString = function( error ) {
- var name, message,
- errorString = error.toString();
- if ( errorString.substring( 0, 7 ) === "[object" ) {
- name = error.name ? error.name.toString() : "Error";
- message = error.message ? error.message.toString() : "";
- if ( name && message ) {
- return name + ": " + message;
- } else if ( name ) {
- return name;
- } else if ( message ) {
- return message;
- } else {
- return "Error";
- }
- } else {
- return errorString;
- }
- },
- /**
- * Makes a clone of an object using only Array or Object as base,
- * and copies over the own enumerable properties.
- *
- * @param {Object} obj
- * @return {Object} New object with only the own properties (recursively).
- */
- objectValues = function( obj ) {
- // Grunt 0.3.x uses an older version of jshint that still has jshint/jshint#392.
- /*jshint newcap: false */
- var key, val,
- vals = QUnit.is( "array", obj ) ? [] : {};
- for ( key in obj ) {
- if ( hasOwn.call( obj, key ) ) {
- val = obj[key];
- vals[key] = val === Object(val) ? objectValues(val) : val;
- }
- }
- return vals;
- };
-
-function Test( settings ) {
- extend( this, settings );
- this.assertions = [];
- this.testNumber = ++Test.count;
-}
-
-Test.count = 0;
-
-Test.prototype = {
- init: function() {
- var a, b, li,
- tests = id( "qunit-tests" );
-
- if ( tests ) {
- b = document.createElement( "strong" );
- b.innerHTML = this.nameHtml;
-
- // `a` initialized at top of scope
- a = document.createElement( "a" );
- a.innerHTML = "Rerun";
- a.href = QUnit.url({ testNumber: this.testNumber });
-
- li = document.createElement( "li" );
- li.appendChild( b );
- li.appendChild( a );
- li.className = "running";
- li.id = this.id = "qunit-test-output" + testId++;
-
- tests.appendChild( li );
- }
- },
- setup: function() {
- if (
- // Emit moduleStart when we're switching from one module to another
- this.module !== config.previousModule ||
- // They could be equal (both undefined) but if the previousModule property doesn't
- // yet exist it means this is the first test in a suite that isn't wrapped in a
- // module, in which case we'll just emit a moduleStart event for 'undefined'.
- // Without this, reporters can get testStart before moduleStart which is a problem.
- !hasOwn.call( config, "previousModule" )
- ) {
- if ( hasOwn.call( config, "previousModule" ) ) {
- runLoggingCallbacks( "moduleDone", QUnit, {
- name: config.previousModule,
- failed: config.moduleStats.bad,
- passed: config.moduleStats.all - config.moduleStats.bad,
- total: config.moduleStats.all
- });
- }
- config.previousModule = this.module;
- config.moduleStats = { all: 0, bad: 0 };
- runLoggingCallbacks( "moduleStart", QUnit, {
- name: this.module
- });
- }
-
- config.current = this;
-
- this.testEnvironment = extend({
- setup: function() {},
- teardown: function() {}
- }, this.moduleTestEnvironment );
-
- this.started = +new Date();
- runLoggingCallbacks( "testStart", QUnit, {
- name: this.testName,
- module: this.module
- });
-
- /*jshint camelcase:false */
-
-
- /**
- * Expose the current test environment.
- *
- * @deprecated since 1.12.0: Use QUnit.config.current.testEnvironment instead.
- */
- QUnit.current_testEnvironment = this.testEnvironment;
-
- /*jshint camelcase:true */
-
- if ( !config.pollution ) {
- saveGlobal();
- }
- if ( config.notrycatch ) {
- this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert );
- return;
- }
- try {
- this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert );
- } catch( e ) {
- QUnit.pushFailure( "Setup failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );
- }
- },
- run: function() {
- config.current = this;
-
- var running = id( "qunit-testresult" );
-
- if ( running ) {
- running.innerHTML = "Running: " + this.nameHtml;
- }
-
- if ( this.async ) {
- QUnit.stop();
- }
-
- this.callbackStarted = +new Date();
-
- if ( config.notrycatch ) {
- this.callback.call( this.testEnvironment, QUnit.assert );
- this.callbackRuntime = +new Date() - this.callbackStarted;
- return;
- }
-
- try {
- this.callback.call( this.testEnvironment, QUnit.assert );
- this.callbackRuntime = +new Date() - this.callbackStarted;
- } catch( e ) {
- this.callbackRuntime = +new Date() - this.callbackStarted;
-
- QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
- // else next test will carry the responsibility
- saveGlobal();
-
- // Restart the tests if they're blocking
- if ( config.blocking ) {
- QUnit.start();
- }
- }
- },
- teardown: function() {
- config.current = this;
- if ( config.notrycatch ) {
- if ( typeof this.callbackRuntime === "undefined" ) {
- this.callbackRuntime = +new Date() - this.callbackStarted;
- }
- this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert );
- return;
- } else {
- try {
- this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert );
- } catch( e ) {
- QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );
- }
- }
- checkPollution();
- },
- finish: function() {
- config.current = this;
- if ( config.requireExpects && this.expected === null ) {
- QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack );
- } else if ( this.expected !== null && this.expected !== this.assertions.length ) {
- QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack );
- } else if ( this.expected === null && !this.assertions.length ) {
- QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack );
- }
-
- var i, assertion, a, b, time, li, ol,
- test = this,
- good = 0,
- bad = 0,
- tests = id( "qunit-tests" );
-
- this.runtime = +new Date() - this.started;
- config.stats.all += this.assertions.length;
- config.moduleStats.all += this.assertions.length;
-
- if ( tests ) {
- ol = document.createElement( "ol" );
- ol.className = "qunit-assert-list";
-
- for ( i = 0; i < this.assertions.length; i++ ) {
- assertion = this.assertions[i];
-
- li = document.createElement( "li" );
- li.className = assertion.result ? "pass" : "fail";
- li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" );
- ol.appendChild( li );
-
- if ( assertion.result ) {
- good++;
- } else {
- bad++;
- config.stats.bad++;
- config.moduleStats.bad++;
- }
- }
-
- // store result when possible
- if ( QUnit.config.reorder && defined.sessionStorage ) {
- if ( bad ) {
- sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad );
- } else {
- sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName );
- }
- }
-
- if ( bad === 0 ) {
- addClass( ol, "qunit-collapsed" );
- }
-
- // `b` initialized at top of scope
- b = document.createElement( "strong" );
- b.innerHTML = this.nameHtml + " (" + bad + " , " + good + " , " + this.assertions.length + ") ";
-
- addEvent(b, "click", function() {
- var next = b.parentNode.lastChild,
- collapsed = hasClass( next, "qunit-collapsed" );
- ( collapsed ? removeClass : addClass )( next, "qunit-collapsed" );
- });
-
- addEvent(b, "dblclick", function( e ) {
- var target = e && e.target ? e.target : window.event.srcElement;
- if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) {
- target = target.parentNode;
- }
- if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
- window.location = QUnit.url({ testNumber: test.testNumber });
- }
- });
-
- // `time` initialized at top of scope
- time = document.createElement( "span" );
- time.className = "runtime";
- time.innerHTML = this.runtime + " ms";
-
- // `li` initialized at top of scope
- li = id( this.id );
- li.className = bad ? "fail" : "pass";
- li.removeChild( li.firstChild );
- a = li.firstChild;
- li.appendChild( b );
- li.appendChild( a );
- li.appendChild( time );
- li.appendChild( ol );
-
- } else {
- for ( i = 0; i < this.assertions.length; i++ ) {
- if ( !this.assertions[i].result ) {
- bad++;
- config.stats.bad++;
- config.moduleStats.bad++;
- }
- }
- }
-
- runLoggingCallbacks( "testDone", QUnit, {
- name: this.testName,
- module: this.module,
- failed: bad,
- passed: this.assertions.length - bad,
- total: this.assertions.length,
- duration: this.runtime
- });
-
- QUnit.reset();
-
- config.current = undefined;
- },
-
- queue: function() {
- var bad,
- test = this;
-
- synchronize(function() {
- test.init();
- });
- function run() {
- // each of these can by async
- synchronize(function() {
- test.setup();
- });
- synchronize(function() {
- test.run();
- });
- synchronize(function() {
- test.teardown();
- });
- synchronize(function() {
- test.finish();
- });
- }
-
- // `bad` initialized at top of scope
- // defer when previous test run passed, if storage is available
- bad = QUnit.config.reorder && defined.sessionStorage &&
- +sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName );
-
- if ( bad ) {
- run();
- } else {
- synchronize( run, true );
- }
- }
-};
-
-// Root QUnit object.
-// `QUnit` initialized at top of scope
-QUnit = {
-
- // call on start of module test to prepend name to all tests
- module: function( name, testEnvironment ) {
- config.currentModule = name;
- config.currentModuleTestEnvironment = testEnvironment;
- config.modules[name] = true;
- },
-
- asyncTest: function( testName, expected, callback ) {
- if ( arguments.length === 2 ) {
- callback = expected;
- expected = null;
- }
-
- QUnit.test( testName, expected, callback, true );
- },
-
- test: function( testName, expected, callback, async ) {
- var test,
- nameHtml = "" + escapeText( testName ) + " ";
-
- if ( arguments.length === 2 ) {
- callback = expected;
- expected = null;
- }
-
- if ( config.currentModule ) {
- nameHtml = "" + escapeText( config.currentModule ) + " : " + nameHtml;
- }
-
- test = new Test({
- nameHtml: nameHtml,
- testName: testName,
- expected: expected,
- async: async,
- callback: callback,
- module: config.currentModule,
- moduleTestEnvironment: config.currentModuleTestEnvironment,
- stack: sourceFromStacktrace( 2 )
- });
-
- if ( !validTest( test ) ) {
- return;
- }
-
- test.queue();
- },
-
- // Specify the number of expected assertions to guarantee that failed test (no assertions are run at all) don't slip through.
- expect: function( asserts ) {
- if (arguments.length === 1) {
- config.current.expected = asserts;
- } else {
- return config.current.expected;
- }
- },
-
- start: function( count ) {
- // QUnit hasn't been initialized yet.
- // Note: RequireJS (et al) may delay onLoad
- if ( config.semaphore === undefined ) {
- QUnit.begin(function() {
- // This is triggered at the top of QUnit.load, push start() to the event loop, to allow QUnit.load to finish first
- setTimeout(function() {
- QUnit.start( count );
- });
- });
- return;
- }
-
- config.semaphore -= count || 1;
- // don't start until equal number of stop-calls
- if ( config.semaphore > 0 ) {
- return;
- }
- // ignore if start is called more often then stop
- if ( config.semaphore < 0 ) {
- config.semaphore = 0;
- QUnit.pushFailure( "Called start() while already started (QUnit.config.semaphore was 0 already)", null, sourceFromStacktrace(2) );
- return;
- }
- // A slight delay, to avoid any current callbacks
- if ( defined.setTimeout ) {
- setTimeout(function() {
- if ( config.semaphore > 0 ) {
- return;
- }
- if ( config.timeout ) {
- clearTimeout( config.timeout );
- }
-
- config.blocking = false;
- process( true );
- }, 13);
- } else {
- config.blocking = false;
- process( true );
- }
- },
-
- stop: function( count ) {
- config.semaphore += count || 1;
- config.blocking = true;
-
- if ( config.testTimeout && defined.setTimeout ) {
- clearTimeout( config.timeout );
- config.timeout = setTimeout(function() {
- QUnit.ok( false, "Test timed out" );
- config.semaphore = 1;
- QUnit.start();
- }, config.testTimeout );
- }
- }
-};
-
-// `assert` initialized at top of scope
-// Assert helpers
-// All of these must either call QUnit.push() or manually do:
-// - runLoggingCallbacks( "log", .. );
-// - config.current.assertions.push({ .. });
-// We attach it to the QUnit object *after* we expose the public API,
-// otherwise `assert` will become a global variable in browsers (#341).
-assert = {
- /**
- * Asserts rough true-ish result.
- * @name ok
- * @function
- * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
- */
- ok: function( result, msg ) {
- if ( !config.current ) {
- throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) );
- }
- result = !!result;
- msg = msg || (result ? "okay" : "failed" );
-
- var source,
- details = {
- module: config.current.module,
- name: config.current.testName,
- result: result,
- message: msg
- };
-
- msg = "" + escapeText( msg ) + " ";
-
- if ( !result ) {
- source = sourceFromStacktrace( 2 );
- if ( source ) {
- details.source = source;
- msg += "Source: " + escapeText( source ) + "
";
- }
- }
- runLoggingCallbacks( "log", QUnit, details );
- config.current.assertions.push({
- result: result,
- message: msg
- });
- },
-
- /**
- * Assert that the first two arguments are equal, with an optional message.
- * Prints out both actual and expected values.
- * @name equal
- * @function
- * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
- */
- equal: function( actual, expected, message ) {
- /*jshint eqeqeq:false */
- QUnit.push( expected == actual, actual, expected, message );
- },
-
- /**
- * @name notEqual
- * @function
- */
- notEqual: function( actual, expected, message ) {
- /*jshint eqeqeq:false */
- QUnit.push( expected != actual, actual, expected, message );
- },
-
- /**
- * @name propEqual
- * @function
- */
- propEqual: function( actual, expected, message ) {
- actual = objectValues(actual);
- expected = objectValues(expected);
- QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
- },
-
- /**
- * @name notPropEqual
- * @function
- */
- notPropEqual: function( actual, expected, message ) {
- actual = objectValues(actual);
- expected = objectValues(expected);
- QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
- },
-
- /**
- * @name deepEqual
- * @function
- */
- deepEqual: function( actual, expected, message ) {
- QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
- },
-
- /**
- * @name notDeepEqual
- * @function
- */
- notDeepEqual: function( actual, expected, message ) {
- QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
- },
-
- /**
- * @name strictEqual
- * @function
- */
- strictEqual: function( actual, expected, message ) {
- QUnit.push( expected === actual, actual, expected, message );
- },
-
- /**
- * @name notStrictEqual
- * @function
- */
- notStrictEqual: function( actual, expected, message ) {
- QUnit.push( expected !== actual, actual, expected, message );
- },
-
- "throws": function( block, expected, message ) {
- var actual,
- expectedOutput = expected,
- ok = false;
-
- // 'expected' is optional
- if ( typeof expected === "string" ) {
- message = expected;
- expected = null;
- }
-
- config.current.ignoreGlobalErrors = true;
- try {
- block.call( config.current.testEnvironment );
- } catch (e) {
- actual = e;
- }
- config.current.ignoreGlobalErrors = false;
-
- if ( actual ) {
- // we don't want to validate thrown error
- if ( !expected ) {
- ok = true;
- expectedOutput = null;
- // expected is a regexp
- } else if ( QUnit.objectType( expected ) === "regexp" ) {
- ok = expected.test( errorString( actual ) );
- // expected is a constructor
- } else if ( actual instanceof expected ) {
- ok = true;
- // expected is a validation function which returns true is validation passed
- } else if ( expected.call( {}, actual ) === true ) {
- expectedOutput = null;
- ok = true;
- }
-
- QUnit.push( ok, actual, expectedOutput, message );
- } else {
- QUnit.pushFailure( message, null, "No exception was thrown." );
- }
- }
-};
-
-/**
- * @deprecated since 1.8.0
- * Kept assertion helpers in root for backwards compatibility.
- */
-extend( QUnit, assert );
-
-/**
- * @deprecated since 1.9.0
- * Kept root "raises()" for backwards compatibility.
- * (Note that we don't introduce assert.raises).
- */
-QUnit.raises = assert[ "throws" ];
-
-/**
- * @deprecated since 1.0.0, replaced with error pushes since 1.3.0
- * Kept to avoid TypeErrors for undefined methods.
- */
-QUnit.equals = function() {
- QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );
-};
-QUnit.same = function() {
- QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" );
-};
-
-// We want access to the constructor's prototype
-(function() {
- function F() {}
- F.prototype = QUnit;
- QUnit = new F();
- // Make F QUnit's constructor so that we can add to the prototype later
- QUnit.constructor = F;
-}());
-
-/**
- * Config object: Maintain internal state
- * Later exposed as QUnit.config
- * `config` initialized at top of scope
- */
-config = {
- // The queue of tests to run
- queue: [],
-
- // block until document ready
- blocking: true,
-
- // when enabled, show only failing tests
- // gets persisted through sessionStorage and can be changed in UI via checkbox
- hidepassed: false,
-
- // by default, run previously failed tests first
- // very useful in combination with "Hide passed tests" checked
- reorder: true,
-
- // by default, modify document.title when suite is done
- altertitle: true,
-
- // when enabled, all tests must call expect()
- requireExpects: false,
-
- // add checkboxes that are persisted in the query-string
- // when enabled, the id is set to `true` as a `QUnit.config` property
- urlConfig: [
- {
- id: "noglobals",
- label: "Check for Globals",
- tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings."
- },
- {
- id: "notrycatch",
- label: "No try-catch",
- tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings."
- }
- ],
-
- // Set of all modules.
- modules: {},
-
- // logging callback queues
- begin: [],
- done: [],
- log: [],
- testStart: [],
- testDone: [],
- moduleStart: [],
- moduleDone: []
-};
-
-// Export global variables, unless an 'exports' object exists,
-// in that case we assume we're in CommonJS (dealt with on the bottom of the script)
-if ( typeof exports === "undefined" ) {
- extend( window, QUnit.constructor.prototype );
-
- // Expose QUnit object
- window.QUnit = QUnit;
-}
-
-// Initialize more QUnit.config and QUnit.urlParams
-(function() {
- var i,
- location = window.location || { search: "", protocol: "file:" },
- params = location.search.slice( 1 ).split( "&" ),
- length = params.length,
- urlParams = {},
- current;
-
- if ( params[ 0 ] ) {
- for ( i = 0; i < length; i++ ) {
- current = params[ i ].split( "=" );
- current[ 0 ] = decodeURIComponent( current[ 0 ] );
- // allow just a key to turn on a flag, e.g., test.html?noglobals
- current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
- urlParams[ current[ 0 ] ] = current[ 1 ];
- }
- }
-
- QUnit.urlParams = urlParams;
-
- // String search anywhere in moduleName+testName
- config.filter = urlParams.filter;
-
- // Exact match of the module name
- config.module = urlParams.module;
-
- config.testNumber = parseInt( urlParams.testNumber, 10 ) || null;
-
- // Figure out if we're running the tests from a server or not
- QUnit.isLocal = location.protocol === "file:";
-}());
-
-// Extend QUnit object,
-// these after set here because they should not be exposed as global functions
-extend( QUnit, {
- assert: assert,
-
- config: config,
-
- // Initialize the configuration options
- init: function() {
- extend( config, {
- stats: { all: 0, bad: 0 },
- moduleStats: { all: 0, bad: 0 },
- started: +new Date(),
- updateRate: 1000,
- blocking: false,
- autostart: true,
- autorun: false,
- filter: "",
- queue: [],
- semaphore: 1
- });
-
- var tests, banner, result,
- qunit = id( "qunit" );
-
- if ( qunit ) {
- qunit.innerHTML =
- "" +
- " " +
- "
" +
- " " +
- " ";
- }
-
- tests = id( "qunit-tests" );
- banner = id( "qunit-banner" );
- result = id( "qunit-testresult" );
-
- if ( tests ) {
- tests.innerHTML = "";
- }
-
- if ( banner ) {
- banner.className = "";
- }
-
- if ( result ) {
- result.parentNode.removeChild( result );
- }
-
- if ( tests ) {
- result = document.createElement( "p" );
- result.id = "qunit-testresult";
- result.className = "result";
- tests.parentNode.insertBefore( result, tests );
- result.innerHTML = "Running... ";
- }
- },
-
- // Resets the test setup. Useful for tests that modify the DOM.
- /*
- DEPRECATED: Use multiple tests instead of resetting inside a test.
- Use testStart or testDone for custom cleanup.
- This method will throw an error in 2.0, and will be removed in 2.1
- */
- reset: function() {
- var fixture = id( "qunit-fixture" );
- if ( fixture ) {
- fixture.innerHTML = config.fixture;
- }
- },
-
- // Trigger an event on an element.
- // @example triggerEvent( document.body, "click" );
- triggerEvent: function( elem, type, event ) {
- if ( document.createEvent ) {
- event = document.createEvent( "MouseEvents" );
- event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
- 0, 0, 0, 0, 0, false, false, false, false, 0, null);
-
- elem.dispatchEvent( event );
- } else if ( elem.fireEvent ) {
- elem.fireEvent( "on" + type );
- }
- },
-
- // Safe object type checking
- is: function( type, obj ) {
- return QUnit.objectType( obj ) === type;
- },
-
- objectType: function( obj ) {
- if ( typeof obj === "undefined" ) {
- return "undefined";
- // consider: typeof null === object
- }
- if ( obj === null ) {
- return "null";
- }
-
- var match = toString.call( obj ).match(/^\[object\s(.*)\]$/),
- type = match && match[1] || "";
-
- switch ( type ) {
- case "Number":
- if ( isNaN(obj) ) {
- return "nan";
- }
- return "number";
- case "String":
- case "Boolean":
- case "Array":
- case "Date":
- case "RegExp":
- case "Function":
- return type.toLowerCase();
- }
- if ( typeof obj === "object" ) {
- return "object";
- }
- return undefined;
- },
-
- push: function( result, actual, expected, message ) {
- if ( !config.current ) {
- throw new Error( "assertion outside test context, was " + sourceFromStacktrace() );
- }
-
- var output, source,
- details = {
- module: config.current.module,
- name: config.current.testName,
- result: result,
- message: message,
- actual: actual,
- expected: expected
- };
-
- message = escapeText( message ) || ( result ? "okay" : "failed" );
- message = "" + message + " ";
- output = message;
-
- if ( !result ) {
- expected = escapeText( QUnit.jsDump.parse(expected) );
- actual = escapeText( QUnit.jsDump.parse(actual) );
- output += "Expected: " + expected + " ";
-
- if ( actual !== expected ) {
- output += "Result: " + actual + " ";
- output += "Diff: " + QUnit.diff( expected, actual ) + " ";
- }
-
- source = sourceFromStacktrace();
-
- if ( source ) {
- details.source = source;
- output += "Source: " + escapeText( source ) + " ";
- }
-
- output += "
";
- }
-
- runLoggingCallbacks( "log", QUnit, details );
-
- config.current.assertions.push({
- result: !!result,
- message: output
- });
- },
-
- pushFailure: function( message, source, actual ) {
- if ( !config.current ) {
- throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) );
- }
-
- var output,
- details = {
- module: config.current.module,
- name: config.current.testName,
- result: false,
- message: message
- };
-
- message = escapeText( message ) || "error";
- message = "" + message + " ";
- output = message;
-
- output += "";
-
- if ( actual ) {
- output += "Result: " + escapeText( actual ) + " ";
- }
-
- if ( source ) {
- details.source = source;
- output += "Source: " + escapeText( source ) + " ";
- }
-
- output += "
";
-
- runLoggingCallbacks( "log", QUnit, details );
-
- config.current.assertions.push({
- result: false,
- message: output
- });
- },
-
- url: function( params ) {
- params = extend( extend( {}, QUnit.urlParams ), params );
- var key,
- querystring = "?";
-
- for ( key in params ) {
- if ( hasOwn.call( params, key ) ) {
- querystring += encodeURIComponent( key ) + "=" +
- encodeURIComponent( params[ key ] ) + "&";
- }
- }
- return window.location.protocol + "//" + window.location.host +
- window.location.pathname + querystring.slice( 0, -1 );
- },
-
- extend: extend,
- id: id,
- addEvent: addEvent,
- addClass: addClass,
- hasClass: hasClass,
- removeClass: removeClass
- // load, equiv, jsDump, diff: Attached later
-});
-
-/**
- * @deprecated: Created for backwards compatibility with test runner that set the hook function
- * into QUnit.{hook}, instead of invoking it and passing the hook function.
- * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here.
- * Doing this allows us to tell if the following methods have been overwritten on the actual
- * QUnit object.
- */
-extend( QUnit.constructor.prototype, {
-
- // Logging callbacks; all receive a single argument with the listed properties
- // run test/logs.html for any related changes
- begin: registerLoggingCallback( "begin" ),
-
- // done: { failed, passed, total, runtime }
- done: registerLoggingCallback( "done" ),
-
- // log: { result, actual, expected, message }
- log: registerLoggingCallback( "log" ),
-
- // testStart: { name }
- testStart: registerLoggingCallback( "testStart" ),
-
- // testDone: { name, failed, passed, total, duration }
- testDone: registerLoggingCallback( "testDone" ),
-
- // moduleStart: { name }
- moduleStart: registerLoggingCallback( "moduleStart" ),
-
- // moduleDone: { name, failed, passed, total }
- moduleDone: registerLoggingCallback( "moduleDone" )
-});
-
-if ( typeof document === "undefined" || document.readyState === "complete" ) {
- config.autorun = true;
-}
-
-QUnit.load = function() {
- runLoggingCallbacks( "begin", QUnit, {} );
-
- // Initialize the config, saving the execution queue
- var banner, filter, i, label, len, main, ol, toolbar, userAgent, val,
- urlConfigCheckboxesContainer, urlConfigCheckboxes, moduleFilter,
- numModules = 0,
- moduleNames = [],
- moduleFilterHtml = "",
- urlConfigHtml = "",
- oldconfig = extend( {}, config );
-
- QUnit.init();
- extend(config, oldconfig);
-
- config.blocking = false;
-
- len = config.urlConfig.length;
-
- for ( i = 0; i < len; i++ ) {
- val = config.urlConfig[i];
- if ( typeof val === "string" ) {
- val = {
- id: val,
- label: val,
- tooltip: "[no tooltip available]"
- };
- }
- config[ val.id ] = QUnit.urlParams[ val.id ];
- urlConfigHtml += "" + val.label + " ";
- }
- for ( i in config.modules ) {
- if ( config.modules.hasOwnProperty( i ) ) {
- moduleNames.push(i);
- }
- }
- numModules = moduleNames.length;
- moduleNames.sort( function( a, b ) {
- return a.localeCompare( b );
- });
- moduleFilterHtml += "Module: < All Modules > ";
-
-
- for ( i = 0; i < numModules; i++) {
- moduleFilterHtml += "" + escapeText(moduleNames[i]) + " ";
- }
- moduleFilterHtml += " ";
-
- // `userAgent` initialized at top of scope
- userAgent = id( "qunit-userAgent" );
- if ( userAgent ) {
- userAgent.innerHTML = navigator.userAgent;
- }
-
- // `banner` initialized at top of scope
- banner = id( "qunit-header" );
- if ( banner ) {
- banner.innerHTML = "" + banner.innerHTML + " ";
- }
-
- // `toolbar` initialized at top of scope
- toolbar = id( "qunit-testrunner-toolbar" );
- if ( toolbar ) {
- // `filter` initialized at top of scope
- filter = document.createElement( "input" );
- filter.type = "checkbox";
- filter.id = "qunit-filter-pass";
-
- addEvent( filter, "click", function() {
- var tmp,
- ol = document.getElementById( "qunit-tests" );
-
- if ( filter.checked ) {
- ol.className = ol.className + " hidepass";
- } else {
- tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
- ol.className = tmp.replace( / hidepass /, " " );
- }
- if ( defined.sessionStorage ) {
- if (filter.checked) {
- sessionStorage.setItem( "qunit-filter-passed-tests", "true" );
- } else {
- sessionStorage.removeItem( "qunit-filter-passed-tests" );
- }
- }
- });
-
- if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) {
- filter.checked = true;
- // `ol` initialized at top of scope
- ol = document.getElementById( "qunit-tests" );
- ol.className = ol.className + " hidepass";
- }
- toolbar.appendChild( filter );
-
- // `label` initialized at top of scope
- label = document.createElement( "label" );
- label.setAttribute( "for", "qunit-filter-pass" );
- label.setAttribute( "title", "Only show tests and assertions that fail. Stored in sessionStorage." );
- label.innerHTML = "Hide passed tests";
- toolbar.appendChild( label );
-
- urlConfigCheckboxesContainer = document.createElement("span");
- urlConfigCheckboxesContainer.innerHTML = urlConfigHtml;
- urlConfigCheckboxes = urlConfigCheckboxesContainer.getElementsByTagName("input");
- // For oldIE support:
- // * Add handlers to the individual elements instead of the container
- // * Use "click" instead of "change"
- // * Fallback from event.target to event.srcElement
- addEvents( urlConfigCheckboxes, "click", function( event ) {
- var params = {},
- target = event.target || event.srcElement;
- params[ target.name ] = target.checked ? true : undefined;
- window.location = QUnit.url( params );
- });
- toolbar.appendChild( urlConfigCheckboxesContainer );
-
- if (numModules > 1) {
- moduleFilter = document.createElement( "span" );
- moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );
- moduleFilter.innerHTML = moduleFilterHtml;
- addEvent( moduleFilter.lastChild, "change", function() {
- var selectBox = moduleFilter.getElementsByTagName("select")[0],
- selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value);
-
- window.location = QUnit.url({
- module: ( selectedModule === "" ) ? undefined : selectedModule,
- // Remove any existing filters
- filter: undefined,
- testNumber: undefined
- });
- });
- toolbar.appendChild(moduleFilter);
- }
- }
-
- // `main` initialized at top of scope
- main = id( "qunit-fixture" );
- if ( main ) {
- config.fixture = main.innerHTML;
- }
-
- if ( config.autostart ) {
- QUnit.start();
- }
-};
-
-addEvent( window, "load", QUnit.load );
-
-// `onErrorFnPrev` initialized at top of scope
-// Preserve other handlers
-onErrorFnPrev = window.onerror;
-
-// Cover uncaught exceptions
-// Returning true will suppress the default browser handler,
-// returning false will let it run.
-window.onerror = function ( error, filePath, linerNr ) {
- var ret = false;
- if ( onErrorFnPrev ) {
- ret = onErrorFnPrev( error, filePath, linerNr );
- }
-
- // Treat return value as window.onerror itself does,
- // Only do our handling if not suppressed.
- if ( ret !== true ) {
- if ( QUnit.config.current ) {
- if ( QUnit.config.current.ignoreGlobalErrors ) {
- return true;
- }
- QUnit.pushFailure( error, filePath + ":" + linerNr );
- } else {
- QUnit.test( "global failure", extend( function() {
- QUnit.pushFailure( error, filePath + ":" + linerNr );
- }, { validTest: validTest } ) );
- }
- return false;
- }
-
- return ret;
-};
-
-function done() {
- config.autorun = true;
-
- // Log the last module results
- if ( config.currentModule ) {
- runLoggingCallbacks( "moduleDone", QUnit, {
- name: config.currentModule,
- failed: config.moduleStats.bad,
- passed: config.moduleStats.all - config.moduleStats.bad,
- total: config.moduleStats.all
- });
- }
- delete config.previousModule;
-
- var i, key,
- banner = id( "qunit-banner" ),
- tests = id( "qunit-tests" ),
- runtime = +new Date() - config.started,
- passed = config.stats.all - config.stats.bad,
- html = [
- "Tests completed in ",
- runtime,
- " milliseconds. ",
- "",
- passed,
- " assertions of ",
- config.stats.all,
- " passed, ",
- config.stats.bad,
- " failed."
- ].join( "" );
-
- if ( banner ) {
- banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" );
- }
-
- if ( tests ) {
- id( "qunit-testresult" ).innerHTML = html;
- }
-
- if ( config.altertitle && typeof document !== "undefined" && document.title ) {
- // show ✖ for good, ✔ for bad suite result in title
- // use escape sequences in case file gets loaded with non-utf-8-charset
- document.title = [
- ( config.stats.bad ? "\u2716" : "\u2714" ),
- document.title.replace( /^[\u2714\u2716] /i, "" )
- ].join( " " );
- }
-
- // clear own sessionStorage items if all tests passed
- if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) {
- // `key` & `i` initialized at top of scope
- for ( i = 0; i < sessionStorage.length; i++ ) {
- key = sessionStorage.key( i++ );
- if ( key.indexOf( "qunit-test-" ) === 0 ) {
- sessionStorage.removeItem( key );
- }
- }
- }
-
- // scroll back to top to show results
- if ( window.scrollTo ) {
- window.scrollTo(0, 0);
- }
-
- runLoggingCallbacks( "done", QUnit, {
- failed: config.stats.bad,
- passed: passed,
- total: config.stats.all,
- runtime: runtime
- });
-}
-
-/** @return Boolean: true if this test should be ran */
-function validTest( test ) {
- var include,
- filter = config.filter && config.filter.toLowerCase(),
- module = config.module && config.module.toLowerCase(),
- fullName = (test.module + ": " + test.testName).toLowerCase();
-
- // Internally-generated tests are always valid
- if ( test.callback && test.callback.validTest === validTest ) {
- delete test.callback.validTest;
- return true;
- }
-
- if ( config.testNumber ) {
- return test.testNumber === config.testNumber;
- }
-
- if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) {
- return false;
- }
-
- if ( !filter ) {
- return true;
- }
-
- include = filter.charAt( 0 ) !== "!";
- if ( !include ) {
- filter = filter.slice( 1 );
- }
-
- // If the filter matches, we need to honour include
- if ( fullName.indexOf( filter ) !== -1 ) {
- return include;
- }
-
- // Otherwise, do the opposite
- return !include;
-}
-
-// so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions)
-// Later Safari and IE10 are supposed to support error.stack as well
-// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
-function extractStacktrace( e, offset ) {
- offset = offset === undefined ? 3 : offset;
-
- var stack, include, i;
-
- if ( e.stacktrace ) {
- // Opera
- return e.stacktrace.split( "\n" )[ offset + 3 ];
- } else if ( e.stack ) {
- // Firefox, Chrome
- stack = e.stack.split( "\n" );
- if (/^error$/i.test( stack[0] ) ) {
- stack.shift();
- }
- if ( fileName ) {
- include = [];
- for ( i = offset; i < stack.length; i++ ) {
- if ( stack[ i ].indexOf( fileName ) !== -1 ) {
- break;
- }
- include.push( stack[ i ] );
- }
- if ( include.length ) {
- return include.join( "\n" );
- }
- }
- return stack[ offset ];
- } else if ( e.sourceURL ) {
- // Safari, PhantomJS
- // hopefully one day Safari provides actual stacktraces
- // exclude useless self-reference for generated Error objects
- if ( /qunit.js$/.test( e.sourceURL ) ) {
- return;
- }
- // for actual exceptions, this is useful
- return e.sourceURL + ":" + e.line;
- }
-}
-function sourceFromStacktrace( offset ) {
- try {
- throw new Error();
- } catch ( e ) {
- return extractStacktrace( e, offset );
- }
-}
-
-/**
- * Escape text for attribute or text content.
- */
-function escapeText( s ) {
- if ( !s ) {
- return "";
- }
- s = s + "";
- // Both single quotes and double quotes (for attributes)
- return s.replace( /['"<>&]/g, function( s ) {
- switch( s ) {
- case "'":
- return "'";
- case "\"":
- return """;
- case "<":
- return "<";
- case ">":
- return ">";
- case "&":
- return "&";
- }
- });
-}
-
-function synchronize( callback, last ) {
- config.queue.push( callback );
-
- if ( config.autorun && !config.blocking ) {
- process( last );
- }
-}
-
-function process( last ) {
- function next() {
- process( last );
- }
- var start = new Date().getTime();
- config.depth = config.depth ? config.depth + 1 : 1;
-
- while ( config.queue.length && !config.blocking ) {
- if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {
- config.queue.shift()();
- } else {
- setTimeout( next, 13 );
- break;
- }
- }
- config.depth--;
- if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
- done();
- }
-}
-
-function saveGlobal() {
- config.pollution = [];
-
- if ( config.noglobals ) {
- for ( var key in window ) {
- if ( hasOwn.call( window, key ) ) {
- // in Opera sometimes DOM element ids show up here, ignore them
- if ( /^qunit-test-output/.test( key ) ) {
- continue;
- }
- config.pollution.push( key );
- }
- }
- }
-}
-
-function checkPollution() {
- var newGlobals,
- deletedGlobals,
- old = config.pollution;
-
- saveGlobal();
-
- newGlobals = diff( config.pollution, old );
- if ( newGlobals.length > 0 ) {
- QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") );
- }
-
- deletedGlobals = diff( old, config.pollution );
- if ( deletedGlobals.length > 0 ) {
- QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") );
- }
-}
-
-// returns a new Array with the elements that are in a but not in b
-function diff( a, b ) {
- var i, j,
- result = a.slice();
-
- for ( i = 0; i < result.length; i++ ) {
- for ( j = 0; j < b.length; j++ ) {
- if ( result[i] === b[j] ) {
- result.splice( i, 1 );
- i--;
- break;
- }
- }
- }
- return result;
-}
-
-function extend( a, b ) {
- for ( var prop in b ) {
- if ( hasOwn.call( b, prop ) ) {
- // Avoid "Member not found" error in IE8 caused by messing with window.constructor
- if ( !( prop === "constructor" && a === window ) ) {
- if ( b[ prop ] === undefined ) {
- delete a[ prop ];
- } else {
- a[ prop ] = b[ prop ];
- }
- }
- }
- }
-
- return a;
-}
-
-/**
- * @param {HTMLElement} elem
- * @param {string} type
- * @param {Function} fn
- */
-function addEvent( elem, type, fn ) {
- // Standards-based browsers
- if ( elem.addEventListener ) {
- elem.addEventListener( type, fn, false );
- // IE
- } else {
- elem.attachEvent( "on" + type, fn );
- }
-}
-
-/**
- * @param {Array|NodeList} elems
- * @param {string} type
- * @param {Function} fn
- */
-function addEvents( elems, type, fn ) {
- var i = elems.length;
- while ( i-- ) {
- addEvent( elems[i], type, fn );
- }
-}
-
-function hasClass( elem, name ) {
- return (" " + elem.className + " ").indexOf(" " + name + " ") > -1;
-}
-
-function addClass( elem, name ) {
- if ( !hasClass( elem, name ) ) {
- elem.className += (elem.className ? " " : "") + name;
- }
-}
-
-function removeClass( elem, name ) {
- var set = " " + elem.className + " ";
- // Class name may appear multiple times
- while ( set.indexOf(" " + name + " ") > -1 ) {
- set = set.replace(" " + name + " " , " ");
- }
- // If possible, trim it for prettiness, but not necessarily
- elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, "");
-}
-
-function id( name ) {
- return !!( typeof document !== "undefined" && document && document.getElementById ) &&
- document.getElementById( name );
-}
-
-function registerLoggingCallback( key ) {
- return function( callback ) {
- config[key].push( callback );
- };
-}
-
-// Supports deprecated method of completely overwriting logging callbacks
-function runLoggingCallbacks( key, scope, args ) {
- var i, callbacks;
- if ( QUnit.hasOwnProperty( key ) ) {
- QUnit[ key ].call(scope, args );
- } else {
- callbacks = config[ key ];
- for ( i = 0; i < callbacks.length; i++ ) {
- callbacks[ i ].call( scope, args );
- }
- }
-}
-
-// Test for equality any JavaScript type.
-// Author: Philippe Rathé
-QUnit.equiv = (function() {
-
- // Call the o related callback with the given arguments.
- function bindCallbacks( o, callbacks, args ) {
- var prop = QUnit.objectType( o );
- if ( prop ) {
- if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {
- return callbacks[ prop ].apply( callbacks, args );
- } else {
- return callbacks[ prop ]; // or undefined
- }
- }
- }
-
- // the real equiv function
- var innerEquiv,
- // stack to decide between skip/abort functions
- callers = [],
- // stack to avoiding loops from circular referencing
- parents = [],
- parentsB = [],
-
- getProto = Object.getPrototypeOf || function ( obj ) {
- /*jshint camelcase:false */
- return obj.__proto__;
- },
- callbacks = (function () {
-
- // for string, boolean, number and null
- function useStrictEquality( b, a ) {
- /*jshint eqeqeq:false */
- if ( b instanceof a.constructor || a instanceof b.constructor ) {
- // to catch short annotation VS 'new' annotation of a
- // declaration
- // e.g. var i = 1;
- // var j = new Number(1);
- return a == b;
- } else {
- return a === b;
- }
- }
-
- return {
- "string": useStrictEquality,
- "boolean": useStrictEquality,
- "number": useStrictEquality,
- "null": useStrictEquality,
- "undefined": useStrictEquality,
-
- "nan": function( b ) {
- return isNaN( b );
- },
-
- "date": function( b, a ) {
- return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
- },
-
- "regexp": function( b, a ) {
- return QUnit.objectType( b ) === "regexp" &&
- // the regex itself
- a.source === b.source &&
- // and its modifiers
- a.global === b.global &&
- // (gmi) ...
- a.ignoreCase === b.ignoreCase &&
- a.multiline === b.multiline &&
- a.sticky === b.sticky;
- },
-
- // - skip when the property is a method of an instance (OOP)
- // - abort otherwise,
- // initial === would have catch identical references anyway
- "function": function() {
- var caller = callers[callers.length - 1];
- return caller !== Object && typeof caller !== "undefined";
- },
-
- "array": function( b, a ) {
- var i, j, len, loop, aCircular, bCircular;
-
- // b could be an object literal here
- if ( QUnit.objectType( b ) !== "array" ) {
- return false;
- }
-
- len = a.length;
- if ( len !== b.length ) {
- // safe and faster
- return false;
- }
-
- // track reference to avoid circular references
- parents.push( a );
- parentsB.push( b );
- for ( i = 0; i < len; i++ ) {
- loop = false;
- for ( j = 0; j < parents.length; j++ ) {
- aCircular = parents[j] === a[i];
- bCircular = parentsB[j] === b[i];
- if ( aCircular || bCircular ) {
- if ( a[i] === b[i] || aCircular && bCircular ) {
- loop = true;
- } else {
- parents.pop();
- parentsB.pop();
- return false;
- }
- }
- }
- if ( !loop && !innerEquiv(a[i], b[i]) ) {
- parents.pop();
- parentsB.pop();
- return false;
- }
- }
- parents.pop();
- parentsB.pop();
- return true;
- },
-
- "object": function( b, a ) {
- /*jshint forin:false */
- var i, j, loop, aCircular, bCircular,
- // Default to true
- eq = true,
- aProperties = [],
- bProperties = [];
-
- // comparing constructors is more strict than using
- // instanceof
- if ( a.constructor !== b.constructor ) {
- // Allow objects with no prototype to be equivalent to
- // objects with Object as their constructor.
- if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) ||
- ( getProto(b) === null && getProto(a) === Object.prototype ) ) ) {
- return false;
- }
- }
-
- // stack constructor before traversing properties
- callers.push( a.constructor );
-
- // track reference to avoid circular references
- parents.push( a );
- parentsB.push( b );
-
- // be strict: don't ensure hasOwnProperty and go deep
- for ( i in a ) {
- loop = false;
- for ( j = 0; j < parents.length; j++ ) {
- aCircular = parents[j] === a[i];
- bCircular = parentsB[j] === b[i];
- if ( aCircular || bCircular ) {
- if ( a[i] === b[i] || aCircular && bCircular ) {
- loop = true;
- } else {
- eq = false;
- break;
- }
- }
- }
- aProperties.push(i);
- if ( !loop && !innerEquiv(a[i], b[i]) ) {
- eq = false;
- break;
- }
- }
-
- parents.pop();
- parentsB.pop();
- callers.pop(); // unstack, we are done
-
- for ( i in b ) {
- bProperties.push( i ); // collect b's properties
- }
-
- // Ensures identical properties name
- return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
- }
- };
- }());
-
- innerEquiv = function() { // can take multiple arguments
- var args = [].slice.apply( arguments );
- if ( args.length < 2 ) {
- return true; // end transition
- }
-
- return (function( a, b ) {
- if ( a === b ) {
- return true; // catch the most you can
- } else if ( a === null || b === null || typeof a === "undefined" ||
- typeof b === "undefined" ||
- QUnit.objectType(a) !== QUnit.objectType(b) ) {
- return false; // don't lose time with error prone cases
- } else {
- return bindCallbacks(a, callbacks, [ b, a ]);
- }
-
- // apply transition with (1..n) arguments
- }( args[0], args[1] ) && innerEquiv.apply( this, args.splice(1, args.length - 1 )) );
- };
-
- return innerEquiv;
-}());
-
-/**
- * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |
- * http://flesler.blogspot.com Licensed under BSD
- * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008
- *
- * @projectDescription Advanced and extensible data dumping for Javascript.
- * @version 1.0.0
- * @author Ariel Flesler
- * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
- */
-QUnit.jsDump = (function() {
- function quote( str ) {
- return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\"";
- }
- function literal( o ) {
- return o + "";
- }
- function join( pre, arr, post ) {
- var s = jsDump.separator(),
- base = jsDump.indent(),
- inner = jsDump.indent(1);
- if ( arr.join ) {
- arr = arr.join( "," + s + inner );
- }
- if ( !arr ) {
- return pre + post;
- }
- return [ pre, inner + arr, base + post ].join(s);
- }
- function array( arr, stack ) {
- var i = arr.length, ret = new Array(i);
- this.up();
- while ( i-- ) {
- ret[i] = this.parse( arr[i] , undefined , stack);
- }
- this.down();
- return join( "[", ret, "]" );
- }
-
- var reName = /^function (\w+)/,
- jsDump = {
- // type is used mostly internally, you can fix a (custom)type in advance
- parse: function( obj, type, stack ) {
- stack = stack || [ ];
- var inStack, res,
- parser = this.parsers[ type || this.typeOf(obj) ];
-
- type = typeof parser;
- inStack = inArray( obj, stack );
-
- if ( inStack !== -1 ) {
- return "recursion(" + (inStack - stack.length) + ")";
- }
- if ( type === "function" ) {
- stack.push( obj );
- res = parser.call( this, obj, stack );
- stack.pop();
- return res;
- }
- return ( type === "string" ) ? parser : this.parsers.error;
- },
- typeOf: function( obj ) {
- var type;
- if ( obj === null ) {
- type = "null";
- } else if ( typeof obj === "undefined" ) {
- type = "undefined";
- } else if ( QUnit.is( "regexp", obj) ) {
- type = "regexp";
- } else if ( QUnit.is( "date", obj) ) {
- type = "date";
- } else if ( QUnit.is( "function", obj) ) {
- type = "function";
- } else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) {
- type = "window";
- } else if ( obj.nodeType === 9 ) {
- type = "document";
- } else if ( obj.nodeType ) {
- type = "node";
- } else if (
- // native arrays
- toString.call( obj ) === "[object Array]" ||
- // NodeList objects
- ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) )
- ) {
- type = "array";
- } else if ( obj.constructor === Error.prototype.constructor ) {
- type = "error";
- } else {
- type = typeof obj;
- }
- return type;
- },
- separator: function() {
- return this.multiline ? this.HTML ? " " : "\n" : this.HTML ? " " : " ";
- },
- // extra can be a number, shortcut for increasing-calling-decreasing
- indent: function( extra ) {
- if ( !this.multiline ) {
- return "";
- }
- var chr = this.indentChar;
- if ( this.HTML ) {
- chr = chr.replace( /\t/g, " " ).replace( / /g, " " );
- }
- return new Array( this.depth + ( extra || 0 ) ).join(chr);
- },
- up: function( a ) {
- this.depth += a || 1;
- },
- down: function( a ) {
- this.depth -= a || 1;
- },
- setParser: function( name, parser ) {
- this.parsers[name] = parser;
- },
- // The next 3 are exposed so you can use them
- quote: quote,
- literal: literal,
- join: join,
- //
- depth: 1,
- // This is the list of parsers, to modify them, use jsDump.setParser
- parsers: {
- window: "[Window]",
- document: "[Document]",
- error: function(error) {
- return "Error(\"" + error.message + "\")";
- },
- unknown: "[Unknown]",
- "null": "null",
- "undefined": "undefined",
- "function": function( fn ) {
- var ret = "function",
- // functions never have name in IE
- name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];
-
- if ( name ) {
- ret += " " + name;
- }
- ret += "( ";
-
- ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" );
- return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" );
- },
- array: array,
- nodelist: array,
- "arguments": array,
- object: function( map, stack ) {
- /*jshint forin:false */
- var ret = [ ], keys, key, val, i;
- QUnit.jsDump.up();
- keys = [];
- for ( key in map ) {
- keys.push( key );
- }
- keys.sort();
- for ( i = 0; i < keys.length; i++ ) {
- key = keys[ i ];
- val = map[ key ];
- ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) );
- }
- QUnit.jsDump.down();
- return join( "{", ret, "}" );
- },
- node: function( node ) {
- var len, i, val,
- open = QUnit.jsDump.HTML ? "<" : "<",
- close = QUnit.jsDump.HTML ? ">" : ">",
- tag = node.nodeName.toLowerCase(),
- ret = open + tag,
- attrs = node.attributes;
-
- if ( attrs ) {
- for ( i = 0, len = attrs.length; i < len; i++ ) {
- val = attrs[i].nodeValue;
- // IE6 includes all attributes in .attributes, even ones not explicitly set.
- // Those have values like undefined, null, 0, false, "" or "inherit".
- if ( val && val !== "inherit" ) {
- ret += " " + attrs[i].nodeName + "=" + QUnit.jsDump.parse( val, "attribute" );
- }
- }
- }
- ret += close;
-
- // Show content of TextNode or CDATASection
- if ( node.nodeType === 3 || node.nodeType === 4 ) {
- ret += node.nodeValue;
- }
-
- return ret + open + "/" + tag + close;
- },
- // function calls it internally, it's the arguments part of the function
- functionArgs: function( fn ) {
- var args,
- l = fn.length;
-
- if ( !l ) {
- return "";
- }
-
- args = new Array(l);
- while ( l-- ) {
- // 97 is 'a'
- args[l] = String.fromCharCode(97+l);
- }
- return " " + args.join( ", " ) + " ";
- },
- // object calls it internally, the key part of an item in a map
- key: quote,
- // function calls it internally, it's the content of the function
- functionCode: "[code]",
- // node calls it internally, it's an html attribute value
- attribute: quote,
- string: quote,
- date: quote,
- regexp: literal,
- number: literal,
- "boolean": literal
- },
- // if true, entities are escaped ( <, >, \t, space and \n )
- HTML: false,
- // indentation unit
- indentChar: " ",
- // if true, items in a collection, are separated by a \n, else just a space.
- multiline: true
- };
-
- return jsDump;
-}());
-
-// from jquery.js
-function inArray( elem, array ) {
- if ( array.indexOf ) {
- return array.indexOf( elem );
- }
-
- for ( var i = 0, length = array.length; i < length; i++ ) {
- if ( array[ i ] === elem ) {
- return i;
- }
- }
-
- return -1;
-}
-
-/*
- * Javascript Diff Algorithm
- * By John Resig (http://ejohn.org/)
- * Modified by Chu Alan "sprite"
- *
- * Released under the MIT license.
- *
- * More Info:
- * http://ejohn.org/projects/javascript-diff-algorithm/
- *
- * Usage: QUnit.diff(expected, actual)
- *
- * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick brown fox jumped jumps over"
- */
-QUnit.diff = (function() {
- /*jshint eqeqeq:false, eqnull:true */
- function diff( o, n ) {
- var i,
- ns = {},
- os = {};
-
- for ( i = 0; i < n.length; i++ ) {
- if ( !hasOwn.call( ns, n[i] ) ) {
- ns[ n[i] ] = {
- rows: [],
- o: null
- };
- }
- ns[ n[i] ].rows.push( i );
- }
-
- for ( i = 0; i < o.length; i++ ) {
- if ( !hasOwn.call( os, o[i] ) ) {
- os[ o[i] ] = {
- rows: [],
- n: null
- };
- }
- os[ o[i] ].rows.push( i );
- }
-
- for ( i in ns ) {
- if ( hasOwn.call( ns, i ) ) {
- if ( ns[i].rows.length === 1 && hasOwn.call( os, i ) && os[i].rows.length === 1 ) {
- n[ ns[i].rows[0] ] = {
- text: n[ ns[i].rows[0] ],
- row: os[i].rows[0]
- };
- o[ os[i].rows[0] ] = {
- text: o[ os[i].rows[0] ],
- row: ns[i].rows[0]
- };
- }
- }
- }
-
- for ( i = 0; i < n.length - 1; i++ ) {
- if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null &&
- n[ i + 1 ] == o[ n[i].row + 1 ] ) {
-
- n[ i + 1 ] = {
- text: n[ i + 1 ],
- row: n[i].row + 1
- };
- o[ n[i].row + 1 ] = {
- text: o[ n[i].row + 1 ],
- row: i + 1
- };
- }
- }
-
- for ( i = n.length - 1; i > 0; i-- ) {
- if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null &&
- n[ i - 1 ] == o[ n[i].row - 1 ]) {
-
- n[ i - 1 ] = {
- text: n[ i - 1 ],
- row: n[i].row - 1
- };
- o[ n[i].row - 1 ] = {
- text: o[ n[i].row - 1 ],
- row: i - 1
- };
- }
- }
-
- return {
- o: o,
- n: n
- };
- }
-
- return function( o, n ) {
- o = o.replace( /\s+$/, "" );
- n = n.replace( /\s+$/, "" );
-
- var i, pre,
- str = "",
- out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ),
- oSpace = o.match(/\s+/g),
- nSpace = n.match(/\s+/g);
-
- if ( oSpace == null ) {
- oSpace = [ " " ];
- }
- else {
- oSpace.push( " " );
- }
-
- if ( nSpace == null ) {
- nSpace = [ " " ];
- }
- else {
- nSpace.push( " " );
- }
-
- if ( out.n.length === 0 ) {
- for ( i = 0; i < out.o.length; i++ ) {
- str += "" + out.o[i] + oSpace[i] + "";
- }
- }
- else {
- if ( out.n[0].text == null ) {
- for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) {
- str += "" + out.o[n] + oSpace[n] + "";
- }
- }
-
- for ( i = 0; i < out.n.length; i++ ) {
- if (out.n[i].text == null) {
- str += "" + out.n[i] + nSpace[i] + " ";
- }
- else {
- // `pre` initialized at top of scope
- pre = "";
-
- for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) {
- pre += "" + out.o[n] + oSpace[n] + "";
- }
- str += " " + out.n[i].text + nSpace[i] + pre;
- }
- }
- }
-
- return str;
- };
-}());
-
-// for CommonJS environments, export everything
-if ( typeof exports !== "undefined" ) {
- extend( exports, QUnit.constructor.prototype );
-}
-
-// get at whatever the global object is, like window in browsers
-}( (function() {return this;}.call()) ));
diff --git a/vendor/ember-data.js b/vendor/ember-data.js
deleted file mode 100755
index 80ce4b8..0000000
--- a/vendor/ember-data.js
+++ /dev/null
@@ -1,10554 +0,0 @@
-/*!
- * @overview Ember Data
- * @copyright Copyright 2011-2014 Tilde Inc. and contributors.
- * Portions Copyright 2011 LivingSocial Inc.
- * @license Licensed under MIT license (see license.js)
- * @version 1.0.0-beta.6+canary.456107ba
- */
-
-
-(function() {
-var define, requireModule;
-
-(function() {
- var registry = {}, seen = {};
-
- define = function(name, deps, callback) {
- registry[name] = { deps: deps, callback: callback };
- };
-
- requireModule = function(name) {
- if (seen[name]) { return seen[name]; }
- seen[name] = {};
-
- var mod, deps, callback, reified , exports;
-
- mod = registry[name];
-
- if (!mod) {
- throw new Error("Module '" + name + "' not found.");
- }
-
- deps = mod.deps;
- callback = mod.callback;
- reified = [];
- exports;
-
- for (var i=0, l=deps.length; i self.attributeLimit) { return false; }
- var desc = capitalize(underscore(name).replace('_', ' '));
- columns.push({ name: name, desc: desc });
- });
- return columns;
- },
-
- getRecords: function(type) {
- return this.get('store').all(type);
- },
-
- getRecordColumnValues: function(record) {
- var self = this, count = 0,
- columnValues = { id: get(record, 'id') };
-
- record.eachAttribute(function(key) {
- if (count++ > self.attributeLimit) {
- return false;
- }
- var value = get(record, key);
- columnValues[key] = value;
- });
- return columnValues;
- },
-
- getRecordKeywords: function(record) {
- var keywords = [], keys = Ember.A(['id']);
- record.eachAttribute(function(key) {
- keys.push(key);
- });
- keys.forEach(function(key) {
- keywords.push(get(record, key));
- });
- return keywords;
- },
-
- getRecordFilterValues: function(record) {
- return {
- isNew: record.get('isNew'),
- isModified: record.get('isDirty') && !record.get('isNew'),
- isClean: !record.get('isDirty')
- };
- },
-
- getRecordColor: function(record) {
- var color = 'black';
- if (record.get('isNew')) {
- color = 'green';
- } else if (record.get('isDirty')) {
- color = 'blue';
- }
- return color;
- },
-
- observeRecord: function(record, recordUpdated) {
- var releaseMethods = Ember.A(), self = this,
- keysToObserve = Ember.A(['id', 'isNew', 'isDirty']);
-
- record.eachAttribute(function(key) {
- keysToObserve.push(key);
- });
-
- keysToObserve.forEach(function(key) {
- var handler = function() {
- recordUpdated(self.wrapRecord(record));
- };
- Ember.addObserver(record, key, handler);
- releaseMethods.push(function() {
- Ember.removeObserver(record, key, handler);
- });
- });
-
- var release = function() {
- releaseMethods.forEach(function(fn) { fn(); } );
- };
-
- return release;
- }
-
-});
-
-})();
-
-
-
-(function() {
-/**
- The `DS.Transform` class is used to serialize and deserialize model
- attributes when they are saved or loaded from an
- adapter. Subclassing `DS.Transform` is useful for creating custom
- attributes. All subclasses of `DS.Transform` must implement a
- `serialize` and a `deserialize` method.
-
- Example
-
- ```javascript
- App.RawTransform = DS.Transform.extend({
- deserialize: function(serialized) {
- return serialized;
- },
- serialize: function(deserialized) {
- return deserialized;
- }
- });
- ```
-
- Usage
-
- ```javascript
- var attr = DS.attr;
- App.Requirement = DS.Model.extend({
- name: attr('string'),
- optionsArray: attr('raw')
- });
- ```
-
- @class Transform
- @namespace DS
- */
-DS.Transform = Ember.Object.extend({
- /**
- When given a deserialized value from a record attribute this
- method must return the serialized value.
-
- Example
-
- ```javascript
- serialize: function(deserialized) {
- return Ember.isEmpty(deserialized) ? null : Number(deserialized);
- }
- ```
-
- @method serialize
- @param deserialized The deserialized value
- @return The serialized value
- */
- serialize: Ember.required(),
-
- /**
- When given a serialize value from a JSON object this method must
- return the deserialized value for the record attribute.
-
- Example
-
- ```javascript
- deserialize: function(serialized) {
- return empty(serialized) ? null : Number(serialized);
- }
- ```
-
- @method deserialize
- @param serialized The serialized value
- @return The deserialized value
- */
- deserialize: Ember.required()
-
-});
-
-})();
-
-
-
-(function() {
-
-/**
- The `DS.BooleanTransform` class is used to serialize and deserialize
- boolean attributes on Ember Data record objects. This transform is
- used when `boolean` is passed as the type parameter to the
- [DS.attr](../../data#method_attr) function.
-
- Usage
-
- ```javascript
- var attr = DS.attr;
- App.User = DS.Model.extend({
- isAdmin: attr('boolean'),
- name: attr('string'),
- email: attr('string')
- });
- ```
-
- @class BooleanTransform
- @extends DS.Transform
- @namespace DS
- */
-DS.BooleanTransform = DS.Transform.extend({
- deserialize: function(serialized) {
- var type = typeof serialized;
-
- if (type === "boolean") {
- return serialized;
- } else if (type === "string") {
- return serialized.match(/^true$|^t$|^1$/i) !== null;
- } else if (type === "number") {
- return serialized === 1;
- } else {
- return false;
- }
- },
-
- serialize: function(deserialized) {
- return Boolean(deserialized);
- }
-});
-
-})();
-
-
-
-(function() {
-/**
- The `DS.DateTransform` class is used to serialize and deserialize
- date attributes on Ember Data record objects. This transform is used
- when `date` is passed as the type parameter to the
- [DS.attr](../../data#method_attr) function.
-
- ```javascript
- var attr = DS.attr;
- App.Score = DS.Model.extend({
- value: attr('number'),
- player: DS.belongsTo('player'),
- date: attr('date')
- });
- ```
-
- @class DateTransform
- @extends DS.Transform
- @namespace DS
- */
-DS.DateTransform = DS.Transform.extend({
-
- deserialize: function(serialized) {
- var type = typeof serialized;
-
- if (type === "string") {
- return new Date(Ember.Date.parse(serialized));
- } else if (type === "number") {
- return new Date(serialized);
- } else if (serialized === null || serialized === undefined) {
- // if the value is not present in the data,
- // return undefined, not null.
- return serialized;
- } else {
- return null;
- }
- },
-
- serialize: function(date) {
- if (date instanceof Date) {
- var days = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
- var months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
-
- var pad = function(num) {
- return num < 10 ? "0"+num : ""+num;
- };
-
- var utcYear = date.getUTCFullYear(),
- utcMonth = date.getUTCMonth(),
- utcDayOfMonth = date.getUTCDate(),
- utcDay = date.getUTCDay(),
- utcHours = date.getUTCHours(),
- utcMinutes = date.getUTCMinutes(),
- utcSeconds = date.getUTCSeconds();
-
-
- var dayOfWeek = days[utcDay];
- var dayOfMonth = pad(utcDayOfMonth);
- var month = months[utcMonth];
-
- return dayOfWeek + ", " + dayOfMonth + " " + month + " " + utcYear + " " +
- pad(utcHours) + ":" + pad(utcMinutes) + ":" + pad(utcSeconds) + " GMT";
- } else {
- return null;
- }
- }
-
-});
-
-})();
-
-
-
-(function() {
-var empty = Ember.isEmpty;
-/**
- The `DS.NumberTransform` class is used to serialize and deserialize
- numeric attributes on Ember Data record objects. This transform is
- used when `number` is passed as the type parameter to the
- [DS.attr](../../data#method_attr) function.
-
- Usage
-
- ```javascript
- var attr = DS.attr;
- App.Score = DS.Model.extend({
- value: attr('number'),
- player: DS.belongsTo('player'),
- date: attr('date')
- });
- ```
-
- @class NumberTransform
- @extends DS.Transform
- @namespace DS
- */
-DS.NumberTransform = DS.Transform.extend({
-
- deserialize: function(serialized) {
- return empty(serialized) ? null : Number(serialized);
- },
-
- serialize: function(deserialized) {
- return empty(deserialized) ? null : Number(deserialized);
- }
-});
-
-})();
-
-
-
-(function() {
-var none = Ember.isNone;
-
-/**
- The `DS.StringTransform` class is used to serialize and deserialize
- string attributes on Ember Data record objects. This transform is
- used when `string` is passed as the type parameter to the
- [DS.attr](../../data#method_attr) function.
-
- Usage
-
- ```javascript
- var attr = DS.attr;
- App.User = DS.Model.extend({
- isAdmin: attr('boolean'),
- name: attr('string'),
- email: attr('string')
- });
- ```
-
- @class StringTransform
- @extends DS.Transform
- @namespace DS
- */
-DS.StringTransform = DS.Transform.extend({
-
- deserialize: function(serialized) {
- return none(serialized) ? null : String(serialized);
- },
-
- serialize: function(deserialized) {
- return none(deserialized) ? null : String(deserialized);
- }
-
-});
-
-})();
-
-
-
-(function() {
-
-})();
-
-
-
-(function() {
-/**
- @module ember-data
-*/
-
-var set = Ember.set;
-
-/*
- This code registers an injection for Ember.Application.
-
- If an Ember.js developer defines a subclass of DS.Store on their application,
- this code will automatically instantiate it and make it available on the
- router.
-
- Additionally, after an application's controllers have been injected, they will
- each have the store made available to them.
-
- For example, imagine an Ember.js application with the following classes:
-
- App.Store = DS.Store.extend({
- adapter: 'custom'
- });
-
- App.PostsController = Ember.ArrayController.extend({
- // ...
- });
-
- When the application is initialized, `App.Store` will automatically be
- instantiated, and the instance of `App.PostsController` will have its `store`
- property set to that instance.
-
- Note that this code will only be run if the `ember-application` package is
- loaded. If Ember Data is being used in an environment other than a
- typical application (e.g., node.js where only `ember-runtime` is available),
- this code will be ignored.
-*/
-
-Ember.onLoad('Ember.Application', function(Application) {
- Application.initializer({
- name: "store",
-
- initialize: function(container, application) {
- application.register('store:main', application.Store || DS.Store);
- application.register('serializer:_default', DS.JSONSerializer);
- application.register('serializer:_rest', DS.RESTSerializer);
- application.register('adapter:_rest', DS.RESTAdapter);
-
- // Eagerly generate the store so defaultStore is populated.
- // TODO: Do this in a finisher hook
- container.lookup('store:main');
- }
- });
-
- Application.initializer({
- name: "transforms",
- before: "store",
-
- initialize: function(container, application) {
- application.register('transform:boolean', DS.BooleanTransform);
- application.register('transform:date', DS.DateTransform);
- application.register('transform:number', DS.NumberTransform);
- application.register('transform:string', DS.StringTransform);
- }
- });
-
- Application.initializer({
- name: "dataAdapter",
- before: "store",
-
- initialize: function(container, application) {
- application.register('dataAdapter:main', DS.DebugAdapter);
- }
- });
-
- Application.initializer({
- name: "injectStore",
- before: "store",
-
- initialize: function(container, application) {
- application.inject('controller', 'store', 'store:main');
- application.inject('route', 'store', 'store:main');
- application.inject('serializer', 'store', 'store:main');
- application.inject('dataAdapter', 'store', 'store:main');
- }
- });
-
-});
-
-})();
-
-
-
-(function() {
-/**
- @module ember-data
-*/
-
-/**
- Date.parse with progressive enhancement for ISO 8601
-
- © 2011 Colin Snover
-
- Released under MIT license.
-
- @class Date
- @namespace Ember
- @static
-*/
-Ember.Date = Ember.Date || {};
-
-var origParse = Date.parse, numericKeys = [ 1, 4, 5, 6, 7, 10, 11 ];
-
-/**
- @method parse
- @param date
-*/
-Ember.Date.parse = function (date) {
- var timestamp, struct, minutesOffset = 0;
-
- // ES5 §15.9.4.2 states that the string should attempt to be parsed as a Date Time String Format string
- // before falling back to any implementation-specific date parsing, so that’s what we do, even if native
- // implementations could be faster
- // 1 YYYY 2 MM 3 DD 4 HH 5 mm 6 ss 7 msec 8 Z 9 ± 10 tzHH 11 tzmm
- if ((struct = /^(\d{4}|[+\-]\d{6})(?:-(\d{2})(?:-(\d{2}))?)?(?:T(\d{2}):(\d{2})(?::(\d{2})(?:\.(\d{3}))?)?(?:(Z)|([+\-])(\d{2})(?::(\d{2}))?)?)?$/.exec(date))) {
- // avoid NaN timestamps caused by “undefined” values being passed to Date.UTC
- for (var i = 0, k; (k = numericKeys[i]); ++i) {
- struct[k] = +struct[k] || 0;
- }
-
- // allow undefined days and months
- struct[2] = (+struct[2] || 1) - 1;
- struct[3] = +struct[3] || 1;
-
- if (struct[8] !== 'Z' && struct[9] !== undefined) {
- minutesOffset = struct[10] * 60 + struct[11];
-
- if (struct[9] === '+') {
- minutesOffset = 0 - minutesOffset;
- }
- }
-
- timestamp = Date.UTC(struct[1], struct[2], struct[3], struct[4], struct[5] + minutesOffset, struct[6], struct[7]);
- }
- else {
- timestamp = origParse ? origParse(date) : NaN;
- }
-
- return timestamp;
-};
-
-if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Date) {
- Date.parse = Ember.Date.parse;
-}
-
-})();
-
-
-
-(function() {
-
-})();
-
-
-
-(function() {
-/**
- @module ember-data
-*/
-
-var get = Ember.get, set = Ember.set;
-
-/**
- A record array is an array that contains records of a certain type. The record
- array materializes records as needed when they are retrieved for the first
- time. You should not create record arrays yourself. Instead, an instance of
- `DS.RecordArray` or its subclasses will be returned by your application's store
- in response to queries.
-
- @class RecordArray
- @namespace DS
- @extends Ember.ArrayProxy
- @uses Ember.Evented
-*/
-
-DS.RecordArray = Ember.ArrayProxy.extend(Ember.Evented, {
- /**
- The model type contained by this record array.
-
- @property type
- @type DS.Model
- */
- type: null,
-
- /**
- The array of client ids backing the record array. When a
- record is requested from the record array, the record
- for the client id at the same index is materialized, if
- necessary, by the store.
-
- @property content
- @private
- @type Ember.Array
- */
- content: null,
-
- /**
- The flag to signal a `RecordArray` is currently loading data.
-
- Example
-
- ```javascript
- var people = store.all(App.Person);
- people.get('isLoaded'); // true
- ```
-
- @property isLoaded
- @type Boolean
- */
- isLoaded: false,
- /**
- The flag to signal a `RecordArray` is currently loading data.
-
- Example
-
- ```javascript
- var people = store.all(App.Person);
- people.get('isUpdating'); // false
- people.update();
- people.get('isUpdating'); // true
- ```
-
- @property isUpdating
- @type Boolean
- */
- isUpdating: false,
-
- /**
- The store that created this record array.
-
- @property store
- @private
- @type DS.Store
- */
- store: null,
-
- /**
- Retrieves an object from the content by index.
-
- @method objectAtContent
- @private
- @param {Number} index
- @return {DS.Model} record
- */
- objectAtContent: function(index) {
- var content = get(this, 'content');
-
- return content.objectAt(index);
- },
-
- /**
- Used to get the latest version of all of the records in this array
- from the adapter.
-
- Example
-
- ```javascript
- var people = store.all(App.Person);
- people.get('isUpdating'); // false
- people.update();
- people.get('isUpdating'); // true
- ```
-
- @method update
- */
- update: function() {
- if (get(this, 'isUpdating')) { return; }
-
- var store = get(this, 'store'),
- type = get(this, 'type');
-
- store.fetchAll(type, this);
- },
-
- /**
- Adds a record to the `RecordArray`.
-
- @method addRecord
- @private
- @param {DS.Model} record
- */
- addRecord: function(record) {
- get(this, 'content').addObject(record);
- },
-
- /**
- Removes a record to the `RecordArray`.
-
- @method removeRecord
- @private
- @param {DS.Model} record
- */
- removeRecord: function(record) {
- get(this, 'content').removeObject(record);
- },
-
- /**
- Saves all of the records in the `RecordArray`.
-
- Example
-
- ```javascript
- var messages = store.all(App.Message);
- messages.forEach(function(message) {
- message.set('hasBeenSeen', true);
- });
- messages.save();
- ```
-
- @method save
- @return {DS.PromiseArray} promise
- */
- save: function() {
- var promiseLabel = "DS: RecordArray#save " + get(this, 'type');
- var promise = Ember.RSVP.all(this.invoke("save"), promiseLabel).then(function(array) {
- return Ember.A(array);
- }, null, "DS: RecordArray#save apply Ember.NativeArray");
-
- return DS.PromiseArray.create({ promise: promise });
- }
-});
-
-})();
-
-
-
-(function() {
-/**
- @module ember-data
-*/
-
-var get = Ember.get;
-
-/**
- Represents a list of records whose membership is determined by the
- store. As records are created, loaded, or modified, the store
- evaluates them to determine if they should be part of the record
- array.
-
- @class FilteredRecordArray
- @namespace DS
- @extends DS.RecordArray
-*/
-DS.FilteredRecordArray = DS.RecordArray.extend({
- /**
- The filterFunction is a function used to test records from the store to
- determine if they should be part of the record array.
-
- Example
-
- ```javascript
- var allPeople = store.all('person');
- allPeople.mapBy('name'); // ["Tom Dale", "Yehuda Katz", "Trek Glowacki"]
-
- var people = store.filter('person', function(person) {
- if (person.get('name').match(/Katz$/)) { return true; }
- });
- people.mapBy('name'); // ["Yehuda Katz"]
-
- var notKatzFilter = function(person) {
- return !person.get('name').match(/Katz$/);
- };
- people.set('filterFunction', notKatzFilter);
- people.mapBy('name'); // ["Tom Dale", "Trek Glowacki"]
- ```
-
- @method filterFunction
- @param {DS.Model} record
- @return {Boolean} `true` if the record should be in the array
- */
- filterFunction: null,
- isLoaded: true,
-
- replace: function() {
- var type = get(this, 'type').toString();
- throw new Error("The result of a client-side filter (on " + type + ") is immutable.");
- },
-
- /**
- @method updateFilter
- @private
- */
- updateFilter: Ember.observer(function() {
- var manager = get(this, 'manager');
- manager.updateFilter(this, get(this, 'type'), get(this, 'filterFunction'));
- }, 'filterFunction')
-});
-
-})();
-
-
-
-(function() {
-/**
- @module ember-data
-*/
-
-var get = Ember.get, set = Ember.set;
-
-/**
- Represents an ordered list of records whose order and membership is
- determined by the adapter. For example, a query sent to the adapter
- may trigger a search on the server, whose results would be loaded
- into an instance of the `AdapterPopulatedRecordArray`.
-
- @class AdapterPopulatedRecordArray
- @namespace DS
- @extends DS.RecordArray
-*/
-DS.AdapterPopulatedRecordArray = DS.RecordArray.extend({
- query: null,
-
- replace: function() {
- var type = get(this, 'type').toString();
- throw new Error("The result of a server query (on " + type + ") is immutable.");
- },
-
- /**
- @method load
- @private
- @param {Array} data
- */
- load: function(data) {
- var store = get(this, 'store'),
- type = get(this, 'type'),
- records = store.pushMany(type, data),
- meta = store.metadataFor(type);
-
- this.setProperties({
- content: Ember.A(records),
- isLoaded: true,
- meta: meta
- });
-
- // TODO: does triggering didLoad event should be the last action of the runLoop?
- Ember.run.once(this, 'trigger', 'didLoad');
- }
-});
-
-})();
-
-
-
-(function() {
-/**
- @module ember-data
-*/
-
-var get = Ember.get, set = Ember.set;
-var map = Ember.EnumerableUtils.map;
-
-/**
- A `ManyArray` is a `RecordArray` that represents the contents of a has-many
- relationship.
-
- The `ManyArray` is instantiated lazily the first time the relationship is
- requested.
-
- ### Inverses
-
- Often, the relationships in Ember Data applications will have
- an inverse. For example, imagine the following models are
- defined:
-
- ```javascript
- App.Post = DS.Model.extend({
- comments: DS.hasMany('comment')
- });
-
- App.Comment = DS.Model.extend({
- post: DS.belongsTo('post')
- });
- ```
-
- If you created a new instance of `App.Post` and added
- a `App.Comment` record to its `comments` has-many
- relationship, you would expect the comment's `post`
- property to be set to the post that contained
- the has-many.
-
- We call the record to which a relationship belongs the
- relationship's _owner_.
-
- @class ManyArray
- @namespace DS
- @extends DS.RecordArray
-*/
-DS.ManyArray = DS.RecordArray.extend({
- init: function() {
- this._super.apply(this, arguments);
- this._changesToSync = Ember.OrderedSet.create();
- },
-
- /**
- The property name of the relationship
-
- @property {String} name
- @private
- */
- name: null,
-
- /**
- The record to which this relationship belongs.
-
- @property {DS.Model} owner
- @private
- */
- owner: null,
-
- /**
- `true` if the relationship is polymorphic, `false` otherwise.
-
- @property {Boolean} isPolymorphic
- @private
- */
- isPolymorphic: false,
-
- // LOADING STATE
-
- isLoaded: false,
-
- /**
- Used for async `hasMany` arrays
- to keep track of when they will resolve.
-
- @property {Ember.RSVP.Promise} promise
- @private
- */
- promise: null,
-
- /**
- @method loadingRecordsCount
- @param {Number} count
- @private
- */
- loadingRecordsCount: function(count) {
- this.loadingRecordsCount = count;
- },
-
- /**
- @method loadedRecord
- @private
- */
- loadedRecord: function() {
- this.loadingRecordsCount--;
- if (this.loadingRecordsCount === 0) {
- set(this, 'isLoaded', true);
- this.trigger('didLoad');
- }
- },
-
- /**
- @method fetch
- @private
- */
- fetch: function() {
- var records = get(this, 'content'),
- store = get(this, 'store'),
- owner = get(this, 'owner'),
- resolver = Ember.RSVP.defer("DS: ManyArray#fetch " + get(this, 'type'));
-
- var unloadedRecords = records.filterProperty('isEmpty', true);
- store.fetchMany(unloadedRecords, owner, resolver);
- },
-
- // Overrides Ember.Array's replace method to implement
- replaceContent: function(index, removed, added) {
- // Map the array of record objects into an array of client ids.
- added = map(added, function(record) {
- Ember.assert("You cannot add '" + record.constructor.typeKey + "' records to this relationship (only '" + this.type.typeKey + "' allowed)", !this.type || record instanceof this.type);
- return record;
- }, this);
-
- this._super(index, removed, added);
- },
-
- arrangedContentDidChange: function() {
- Ember.run.once(this, 'fetch');
- },
-
- arrayContentWillChange: function(index, removed, added) {
- var owner = get(this, 'owner'),
- name = get(this, 'name');
-
- if (!owner._suspendedRelationships) {
- // This code is the first half of code that continues inside
- // of arrayContentDidChange. It gets or creates a change from
- // the child object, adds the current owner as the old
- // parent if this is the first time the object was removed
- // from a ManyArray, and sets `newParent` to null.
- //
- // Later, if the object is added to another ManyArray,
- // the `arrayContentDidChange` will set `newParent` on
- // the change.
- for (var i=index; i "root.created.uncommitted"
- ```
-
- The hierarchy of valid states that ship with ember data looks like
- this:
-
- ```text
- * root
- * deleted
- * saved
- * uncommitted
- * inFlight
- * empty
- * loaded
- * created
- * uncommitted
- * inFlight
- * saved
- * updated
- * uncommitted
- * inFlight
- * loading
- ```
-
- The `DS.Model` states are themselves stateless. What we mean is
- that, the hierarchical states that each of *those* points to is a
- shared data structure. For performance reasons, instead of each
- record getting its own copy of the hierarchy of states, each record
- points to this global, immutable shared instance. How does a state
- know which record it should be acting on? We pass the record
- instance into the state's event handlers as the first argument.
-
- The record passed as the first parameter is where you should stash
- state about the record if needed; you should never store data on the state
- object itself.
-
- ### Events and Flags
-
- A state may implement zero or more events and flags.
-
- #### Events
-
- Events are named functions that are invoked when sent to a record. The
- record will first look for a method with the given name on the
- current state. If no method is found, it will search the current
- state's parent, and then its grandparent, and so on until reaching
- the top of the hierarchy. If the root is reached without an event
- handler being found, an exception will be raised. This can be very
- helpful when debugging new features.
-
- Here's an example implementation of a state with a `myEvent` event handler:
-
- ```javascript
- aState: DS.State.create({
- myEvent: function(manager, param) {
- console.log("Received myEvent with", param);
- }
- })
- ```
-
- To trigger this event:
-
- ```javascript
- record.send('myEvent', 'foo');
- //=> "Received myEvent with foo"
- ```
-
- Note that an optional parameter can be sent to a record's `send()` method,
- which will be passed as the second parameter to the event handler.
-
- Events should transition to a different state if appropriate. This can be
- done by calling the record's `transitionTo()` method with a path to the
- desired state. The state manager will attempt to resolve the state path
- relative to the current state. If no state is found at that path, it will
- attempt to resolve it relative to the current state's parent, and then its
- parent, and so on until the root is reached. For example, imagine a hierarchy
- like this:
-
- * created
- * uncommitted <-- currentState
- * inFlight
- * updated
- * inFlight
-
- If we are currently in the `uncommitted` state, calling
- `transitionTo('inFlight')` would transition to the `created.inFlight` state,
- while calling `transitionTo('updated.inFlight')` would transition to
- the `updated.inFlight` state.
-
- Remember that *only events* should ever cause a state transition. You should
- never call `transitionTo()` from outside a state's event handler. If you are
- tempted to do so, create a new event and send that to the state manager.
-
- #### Flags
-
- Flags are Boolean values that can be used to introspect a record's current
- state in a more user-friendly way than examining its state path. For example,
- instead of doing this:
-
- ```javascript
- var statePath = record.get('stateManager.currentPath');
- if (statePath === 'created.inFlight') {
- doSomething();
- }
- ```
-
- You can say:
-
- ```javascript
- if (record.get('isNew') && record.get('isSaving')) {
- doSomething();
- }
- ```
-
- If your state does not set a value for a given flag, the value will
- be inherited from its parent (or the first place in the state hierarchy
- where it is defined).
-
- The current set of flags are defined below. If you want to add a new flag,
- in addition to the area below, you will also need to declare it in the
- `DS.Model` class.
-
-
- * [isEmpty](DS.Model.html#property_isEmpty)
- * [isLoading](DS.Model.html#property_isLoading)
- * [isLoaded](DS.Model.html#property_isLoaded)
- * [isDirty](DS.Model.html#property_isDirty)
- * [isSaving](DS.Model.html#property_isSaving)
- * [isDeleted](DS.Model.html#property_isDeleted)
- * [isNew](DS.Model.html#property_isNew)
- * [isValid](DS.Model.html#property_isValid)
-
- @namespace DS
- @class RootState
-*/
-
-var hasDefinedProperties = function(object) {
- // Ignore internal property defined by simulated `Ember.create`.
- var names = Ember.keys(object);
- var i, l, name;
- for (i = 0, l = names.length; i < l; i++ ) {
- name = names[i];
- if (object.hasOwnProperty(name) && object[name]) { return true; }
- }
-
- return false;
-};
-
-var didSetProperty = function(record, context) {
- if (context.value === context.originalValue) {
- delete record._attributes[context.name];
- record.send('propertyWasReset', context.name);
- } else if (context.value !== context.oldValue) {
- record.send('becomeDirty');
- }
-
- record.updateRecordArraysLater();
-};
-
-// Implementation notes:
-//
-// Each state has a boolean value for all of the following flags:
-//
-// * isLoaded: The record has a populated `data` property. When a
-// record is loaded via `store.find`, `isLoaded` is false
-// until the adapter sets it. When a record is created locally,
-// its `isLoaded` property is always true.
-// * isDirty: The record has local changes that have not yet been
-// saved by the adapter. This includes records that have been
-// created (but not yet saved) or deleted.
-// * isSaving: The record has been committed, but
-// the adapter has not yet acknowledged that the changes have
-// been persisted to the backend.
-// * isDeleted: The record was marked for deletion. When `isDeleted`
-// is true and `isDirty` is true, the record is deleted locally
-// but the deletion was not yet persisted. When `isSaving` is
-// true, the change is in-flight. When both `isDirty` and
-// `isSaving` are false, the change has persisted.
-// * isError: The adapter reported that it was unable to save
-// local changes to the backend. This may also result in the
-// record having its `isValid` property become false if the
-// adapter reported that server-side validations failed.
-// * isNew: The record was created on the client and the adapter
-// did not yet report that it was successfully saved.
-// * isValid: No client-side validations have failed and the
-// adapter did not report any server-side validation failures.
-
-// The dirty state is a abstract state whose functionality is
-// shared between the `created` and `updated` states.
-//
-// The deleted state shares the `isDirty` flag with the
-// subclasses of `DirtyState`, but with a very different
-// implementation.
-//
-// Dirty states have three child states:
-//
-// `uncommitted`: the store has not yet handed off the record
-// to be saved.
-// `inFlight`: the store has handed off the record to be saved,
-// but the adapter has not yet acknowledged success.
-// `invalid`: the record has invalid information and cannot be
-// send to the adapter yet.
-var DirtyState = {
- initialState: 'uncommitted',
-
- // FLAGS
- isDirty: true,
-
- // SUBSTATES
-
- // When a record first becomes dirty, it is `uncommitted`.
- // This means that there are local pending changes, but they
- // have not yet begun to be saved, and are not invalid.
- uncommitted: {
- // EVENTS
- didSetProperty: didSetProperty,
-
- propertyWasReset: function(record, name) {
- var stillDirty = false;
-
- for (var prop in record._attributes) {
- stillDirty = true;
- break;
- }
-
- if (!stillDirty) { record.send('rolledBack'); }
- },
-
- pushedData: Ember.K,
-
- becomeDirty: Ember.K,
-
- willCommit: function(record) {
- record.transitionTo('inFlight');
- },
-
- reloadRecord: function(record, resolve) {
- resolve(get(record, 'store').reloadRecord(record));
- },
-
- rolledBack: function(record) {
- record.transitionTo('loaded.saved');
- },
-
- becameInvalid: function(record) {
- record.transitionTo('invalid');
- },
-
- rollback: function(record) {
- record.rollback();
- }
- },
-
- // Once a record has been handed off to the adapter to be
- // saved, it is in the 'in flight' state. Changes to the
- // record cannot be made during this window.
- inFlight: {
- // FLAGS
- isSaving: true,
-
- // EVENTS
- didSetProperty: didSetProperty,
- becomeDirty: Ember.K,
- pushedData: Ember.K,
-
- // TODO: More robust semantics around save-while-in-flight
- willCommit: Ember.K,
-
- didCommit: function(record) {
- var dirtyType = get(this, 'dirtyType');
-
- record.transitionTo('saved');
- record.send('invokeLifecycleCallbacks', dirtyType);
- },
-
- becameInvalid: function(record) {
- record.transitionTo('invalid');
- record.send('invokeLifecycleCallbacks');
- },
-
- becameError: function(record) {
- record.transitionTo('uncommitted');
- record.triggerLater('becameError', record);
- }
- },
-
- // A record is in the `invalid` state when its client-side
- // invalidations have failed, or if the adapter has indicated
- // the the record failed server-side invalidations.
- invalid: {
- // FLAGS
- isValid: false,
-
- // EVENTS
- deleteRecord: function(record) {
- record.transitionTo('deleted.uncommitted');
- record.clearRelationships();
- },
-
- didSetProperty: function(record, context) {
- get(record, 'errors').remove(context.name);
-
- didSetProperty(record, context);
- },
-
- becomeDirty: Ember.K,
-
- rolledBack: function(record) {
- get(record, 'errors').clear();
- },
-
- becameValid: function(record) {
- record.transitionTo('uncommitted');
- },
-
- invokeLifecycleCallbacks: function(record) {
- record.triggerLater('becameInvalid', record);
- }
- }
-};
-
-// The created and updated states are created outside the state
-// chart so we can reopen their substates and add mixins as
-// necessary.
-
-function deepClone(object) {
- var clone = {}, value;
-
- for (var prop in object) {
- value = object[prop];
- if (value && typeof value === 'object') {
- clone[prop] = deepClone(value);
- } else {
- clone[prop] = value;
- }
- }
-
- return clone;
-}
-
-function mixin(original, hash) {
- for (var prop in hash) {
- original[prop] = hash[prop];
- }
-
- return original;
-}
-
-function dirtyState(options) {
- var newState = deepClone(DirtyState);
- return mixin(newState, options);
-}
-
-var createdState = dirtyState({
- dirtyType: 'created',
-
- // FLAGS
- isNew: true
-});
-
-createdState.uncommitted.rolledBack = function(record) {
- record.transitionTo('deleted.saved');
-};
-
-var updatedState = dirtyState({
- dirtyType: 'updated'
-});
-
-createdState.uncommitted.deleteRecord = function(record) {
- record.clearRelationships();
- record.transitionTo('deleted.saved');
-};
-
-createdState.uncommitted.rollback = function(record) {
- DirtyState.uncommitted.rollback.apply(this, arguments);
- record.transitionTo('deleted.saved');
-};
-
-updatedState.uncommitted.deleteRecord = function(record) {
- record.transitionTo('deleted.uncommitted');
- record.clearRelationships();
-};
-
-var RootState = {
- // FLAGS
- isEmpty: false,
- isLoading: false,
- isLoaded: false,
- isDirty: false,
- isSaving: false,
- isDeleted: false,
- isNew: false,
- isValid: true,
-
- // DEFAULT EVENTS
-
- // Trying to roll back if you're not in the dirty state
- // doesn't change your state. For example, if you're in the
- // in-flight state, rolling back the record doesn't move
- // you out of the in-flight state.
- rolledBack: Ember.K,
-
- propertyWasReset: Ember.K,
-
- // SUBSTATES
-
- // A record begins its lifecycle in the `empty` state.
- // If its data will come from the adapter, it will
- // transition into the `loading` state. Otherwise, if
- // the record is being created on the client, it will
- // transition into the `created` state.
- empty: {
- isEmpty: true,
-
- // EVENTS
- loadingData: function(record, promise) {
- record._loadingPromise = promise;
- record.transitionTo('loading');
- },
-
- loadedData: function(record) {
- record.transitionTo('loaded.created.uncommitted');
-
- record.suspendRelationshipObservers(function() {
- record.notifyPropertyChange('data');
- });
- },
-
- pushedData: function(record) {
- record.transitionTo('loaded.saved');
- record.triggerLater('didLoad');
- }
- },
-
- // A record enters this state when the store askes
- // the adapter for its data. It remains in this state
- // until the adapter provides the requested data.
- //
- // Usually, this process is asynchronous, using an
- // XHR to retrieve the data.
- loading: {
- // FLAGS
- isLoading: true,
-
- exit: function(record) {
- record._loadingPromise = null;
- },
-
- // EVENTS
- pushedData: function(record) {
- record.transitionTo('loaded.saved');
- record.triggerLater('didLoad');
- set(record, 'isError', false);
- },
-
- becameError: function(record) {
- record.triggerLater('becameError', record);
- },
-
- notFound: function(record) {
- record.transitionTo('empty');
- }
- },
-
- // A record enters this state when its data is populated.
- // Most of a record's lifecycle is spent inside substates
- // of the `loaded` state.
- loaded: {
- initialState: 'saved',
-
- // FLAGS
- isLoaded: true,
-
- // SUBSTATES
-
- // If there are no local changes to a record, it remains
- // in the `saved` state.
- saved: {
- setup: function(record) {
- var attrs = record._attributes,
- isDirty = false;
-
- for (var prop in attrs) {
- if (attrs.hasOwnProperty(prop)) {
- isDirty = true;
- break;
- }
- }
-
- if (isDirty) {
- record.adapterDidDirty();
- }
- },
-
- // EVENTS
- didSetProperty: didSetProperty,
-
- pushedData: Ember.K,
-
- becomeDirty: function(record) {
- record.transitionTo('updated.uncommitted');
- },
-
- willCommit: function(record) {
- record.transitionTo('updated.inFlight');
- },
-
- reloadRecord: function(record, resolve) {
- resolve(get(record, 'store').reloadRecord(record));
- },
-
- deleteRecord: function(record) {
- record.transitionTo('deleted.uncommitted');
- record.clearRelationships();
- },
-
- unloadRecord: function(record) {
- // clear relationships before moving to deleted state
- // otherwise it fails
- record.clearRelationships();
- record.transitionTo('deleted.saved');
- },
-
- didCommit: function(record) {
- record.send('invokeLifecycleCallbacks', get(record, 'lastDirtyType'));
- },
-
- // loaded.saved.notFound would be triggered by a failed
- // `reload()` on an unchanged record
- notFound: Ember.K
-
- },
-
- // A record is in this state after it has been locally
- // created but before the adapter has indicated that
- // it has been saved.
- created: createdState,
-
- // A record is in this state if it has already been
- // saved to the server, but there are new local changes
- // that have not yet been saved.
- updated: updatedState
- },
-
- // A record is in this state if it was deleted from the store.
- deleted: {
- initialState: 'uncommitted',
- dirtyType: 'deleted',
-
- // FLAGS
- isDeleted: true,
- isLoaded: true,
- isDirty: true,
-
- // TRANSITIONS
- setup: function(record) {
- record.updateRecordArrays();
- },
-
- // SUBSTATES
-
- // When a record is deleted, it enters the `start`
- // state. It will exit this state when the record
- // starts to commit.
- uncommitted: {
-
- // EVENTS
-
- willCommit: function(record) {
- record.transitionTo('inFlight');
- },
-
- rollback: function(record) {
- record.rollback();
- },
-
- becomeDirty: Ember.K,
- deleteRecord: Ember.K,
-
- rolledBack: function(record) {
- record.transitionTo('loaded.saved');
- }
- },
-
- // After a record starts committing, but
- // before the adapter indicates that the deletion
- // has saved to the server, a record is in the
- // `inFlight` substate of `deleted`.
- inFlight: {
- // FLAGS
- isSaving: true,
-
- // EVENTS
-
- // TODO: More robust semantics around save-while-in-flight
- willCommit: Ember.K,
- didCommit: function(record) {
- record.transitionTo('saved');
-
- record.send('invokeLifecycleCallbacks');
- },
-
- becameError: function(record) {
- record.transitionTo('uncommitted');
- record.triggerLater('becameError', record);
- }
- },
-
- // Once the adapter indicates that the deletion has
- // been saved, the record enters the `saved` substate
- // of `deleted`.
- saved: {
- // FLAGS
- isDirty: false,
-
- setup: function(record) {
- var store = get(record, 'store');
- store.dematerializeRecord(record);
- },
-
- invokeLifecycleCallbacks: function(record) {
- record.triggerLater('didDelete', record);
- record.triggerLater('didCommit', record);
- }
- }
- },
-
- invokeLifecycleCallbacks: function(record, dirtyType) {
- if (dirtyType === 'created') {
- record.triggerLater('didCreate', record);
- } else {
- record.triggerLater('didUpdate', record);
- }
-
- record.triggerLater('didCommit', record);
- }
-};
-
-function wireState(object, parent, name) {
- /*jshint proto:true*/
- // TODO: Use Object.create and copy instead
- object = mixin(parent ? Ember.create(parent) : {}, object);
- object.parentState = parent;
- object.stateName = name;
-
- for (var prop in object) {
- if (!object.hasOwnProperty(prop) || prop === 'parentState' || prop === 'stateName') { continue; }
- if (typeof object[prop] === 'object') {
- object[prop] = wireState(object[prop], object, name + "." + prop);
- }
- }
-
- return object;
-}
-
-RootState = wireState(RootState, null, "root");
-
-DS.RootState = RootState;
-
-})();
-
-
-
-(function() {
-var get = Ember.get, isEmpty = Ember.isEmpty;
-
-/**
-@module ember-data
-*/
-
-/**
- Holds validation errors for a given record organized by attribute names.
-
- @class Errors
- @namespace DS
- @extends Ember.Object
- @uses Ember.Enumerable
- @uses Ember.Evented
- */
-DS.Errors = Ember.Object.extend(Ember.Enumerable, Ember.Evented, {
- /**
- Register with target handler
-
- @method registerHandlers
- @param {Object} target
- @param {Function} becameInvalid
- @param {Function} becameValid
- */
- registerHandlers: function(target, becameInvalid, becameValid) {
- this.on('becameInvalid', target, becameInvalid);
- this.on('becameValid', target, becameValid);
- },
-
- /**
- @property errorsByAttributeName
- @type {Ember.MapWithDefault}
- @private
- */
- errorsByAttributeName: Ember.reduceComputed("content", {
- initialValue: function() {
- return Ember.MapWithDefault.create({
- defaultValue: function() {
- return Ember.A();
- }
- });
- },
-
- addedItem: function(errors, error) {
- errors.get(error.attribute).pushObject(error);
-
- return errors;
- },
-
- removedItem: function(errors, error) {
- errors.get(error.attribute).removeObject(error);
-
- return errors;
- }
- }),
-
- /**
- Returns errors for a given attribute
-
- @method errorsFor
- @param {String} attribute
- @returns {Array}
- */
- errorsFor: function(attribute) {
- return get(this, 'errorsByAttributeName').get(attribute);
- },
-
- /**
- */
- messages: Ember.computed.mapBy('content', 'message'),
-
- /**
- @property content
- @type {Array}
- @private
- */
- content: Ember.computed(function() {
- return Ember.A();
- }),
-
- /**
- @method unknownProperty
- @private
- */
- unknownProperty: function(attribute) {
- var errors = this.errorsFor(attribute);
- if (isEmpty(errors)) { return null; }
- return errors;
- },
-
- /**
- @method nextObject
- @private
- */
- nextObject: function(index, previousObject, context) {
- return get(this, 'content').objectAt(index);
- },
-
- /**
- Total number of errors.
-
- @property length
- @type {Number}
- @readOnly
- */
- length: Ember.computed.oneWay('content.length').readOnly(),
-
- /**
- @property isEmpty
- @type {Boolean}
- @readOnly
- */
- isEmpty: Ember.computed.not('length').readOnly(),
-
- /**
- Adds error messages to a given attribute and sends
- `becameInvalid` event to the record.
-
- @method add
- @param {String} attribute
- @param {Array|String} messages
- */
- add: function(attribute, messages) {
- var wasEmpty = get(this, 'isEmpty');
-
- messages = this._findOrCreateMessages(attribute, messages);
- get(this, 'content').addObjects(messages);
-
- this.notifyPropertyChange(attribute);
- this.enumerableContentDidChange();
-
- if (wasEmpty && !get(this, 'isEmpty')) {
- this.trigger('becameInvalid');
- }
- },
-
- /**
- @method _findOrCreateMessages
- @private
- */
- _findOrCreateMessages: function(attribute, messages) {
- var errors = this.errorsFor(attribute);
-
- return Ember.makeArray(messages).map(function(message) {
- return errors.findBy('message', message) || {
- attribute: attribute,
- message: message
- };
- });
- },
-
- /**
- Removes all error messages from the given attribute and sends
- `becameValid` event to the record if there no more errors left.
-
- @method remove
- @param {String} attribute
- */
- remove: function(attribute) {
- if (get(this, 'isEmpty')) { return; }
-
- var content = get(this, 'content').rejectBy('attribute', attribute);
- get(this, 'content').setObjects(content);
-
- this.notifyPropertyChange(attribute);
- this.enumerableContentDidChange();
-
- if (get(this, 'isEmpty')) {
- this.trigger('becameValid');
- }
- },
-
- /**
- Removes all error messages and sends `becameValid` event
- to the record.
-
- @method clear
- */
- clear: function() {
- if (get(this, 'isEmpty')) { return; }
-
- get(this, 'content').clear();
- this.enumerableContentDidChange();
-
- this.trigger('becameValid');
- },
-
- /**
- Checks if there is error messages for the given attribute.
-
- @method has
- @param {String} attribute
- @returns {Boolean} true if there some errors on given attribute
- */
- has: function(attribute) {
- return !isEmpty(this.errorsFor(attribute));
- }
-});
-
-})();
-
-
-
-(function() {
-/**
- @module ember-data
-*/
-
-var get = Ember.get, set = Ember.set,
- merge = Ember.merge, once = Ember.run.once;
-
-var retrieveFromCurrentState = Ember.computed('currentState', function(key, value) {
- return get(get(this, 'currentState'), key);
-}).readOnly();
-
-/**
-
- The model class that all Ember Data records descend from.
-
- @class Model
- @namespace DS
- @extends Ember.Object
- @uses Ember.Evented
-*/
-DS.Model = Ember.Object.extend(Ember.Evented, {
- /**
- If this property is `true` the record is in the `empty`
- state. Empty is the first state all records enter after they have
- been created. Most records created by the store will quickly
- transition to the `loading` state if data needs to be fetched from
- the server or the `created` state if the record is created on the
- client. A record can also enter the empty state if the adapter is
- unable to locate the record.
-
- @property isEmpty
- @type {Boolean}
- @readOnly
- */
- isEmpty: retrieveFromCurrentState,
- /**
- If this property is `true` the record is in the `loading` state. A
- record enters this state when the store askes the adapter for its
- data. It remains in this state until the adapter provides the
- requested data.
-
- @property isLoading
- @type {Boolean}
- @readOnly
- */
- isLoading: retrieveFromCurrentState,
- /**
- If this property is `true` the record is in the `loaded` state. A
- record enters this state when its data is populated. Most of a
- record's lifecycle is spent inside substates of the `loaded`
- state.
-
- Example
-
- ```javascript
- var record = store.createRecord(App.Model);
- record.get('isLoaded'); // true
-
- store.find('model', 1).then(function(model) {
- model.get('isLoaded'); // true
- });
- ```
-
- @property isLoaded
- @type {Boolean}
- @readOnly
- */
- isLoaded: retrieveFromCurrentState,
- /**
- If this property is `true` the record is in the `dirty` state. The
- record has local changes that have not yet been saved by the
- adapter. This includes records that have been created (but not yet
- saved) or deleted.
-
- Example
-
- ```javascript
- var record = store.createRecord(App.Model);
- record.get('isDirty'); // true
-
- store.find('model', 1).then(function(model) {
- model.get('isDirty'); // false
- model.set('foo', 'some value');
- model.set('isDirty'); // true
- });
- ```
-
- @property isDirty
- @type {Boolean}
- @readOnly
- */
- isDirty: retrieveFromCurrentState,
- /**
- If this property is `true` the record is in the `saving` state. A
- record enters the saving state when `save` is called, but the
- adapter has not yet acknowledged that the changes have been
- persisted to the backend.
-
- Example
-
- ```javascript
- var record = store.createRecord(App.Model);
- record.get('isSaving'); // false
- var promise = record.save();
- record.get('isSaving'); // true
- promise.then(function() {
- record.get('isSaving'); // false
- });
- ```
-
- @property isSaving
- @type {Boolean}
- @readOnly
- */
- isSaving: retrieveFromCurrentState,
- /**
- If this property is `true` the record is in the `deleted` state
- and has been marked for deletion. When `isDeleted` is true and
- `isDirty` is true, the record is deleted locally but the deletion
- was not yet persisted. When `isSaving` is true, the change is
- in-flight. When both `isDirty` and `isSaving` are false, the
- change has persisted.
-
- Example
-
- ```javascript
- var record = store.createRecord(App.Model);
- record.get('isDeleted'); // false
- record.deleteRecord();
- record.get('isDeleted'); // true
- ```
-
- @property isDeleted
- @type {Boolean}
- @readOnly
- */
- isDeleted: retrieveFromCurrentState,
- /**
- If this property is `true` the record is in the `new` state. A
- record will be in the `new` state when it has been created on the
- client and the adapter has not yet report that it was successfully
- saved.
-
- Example
-
- ```javascript
- var record = store.createRecord(App.Model);
- record.get('isNew'); // true
-
- store.find('model', 1).then(function(model) {
- model.get('isNew'); // false
- });
- ```
-
- @property isNew
- @type {Boolean}
- @readOnly
- */
- isNew: retrieveFromCurrentState,
- /**
- If this property is `true` the record is in the `valid` state. A
- record will be in the `valid` state when no client-side
- validations have failed and the adapter did not report any
- server-side validation failures.
-
- @property isValid
- @type {Boolean}
- @readOnly
- */
- isValid: retrieveFromCurrentState,
- /**
- If the record is in the dirty state this property will report what
- kind of change has caused it to move into the dirty
- state. Possible values are:
-
- - `created` The record has been created by the client and not yet saved to the adapter.
- - `updated` The record has been updated by the client and not yet saved to the adapter.
- - `deleted` The record has been deleted by the client and not yet saved to the adapter.
-
- Example
-
- ```javascript
- var record = store.createRecord(App.Model);
- record.get('dirtyType'); // 'created'
- ```
-
- @property dirtyType
- @type {String}
- @readOnly
- */
- dirtyType: retrieveFromCurrentState,
-
- /**
- If `true` the adapter reported that it was unable to save local
- changes to the backend. This may also result in the record having
- its `isValid` property become false if the adapter reported that
- server-side validations failed.
-
- Example
-
- ```javascript
- record.get('isError'); // false
- record.set('foo', 'invalid value');
- record.save().then(null, function() {
- record.get('isError'); // true
- });
- ```
-
- @property isError
- @type {Boolean}
- @readOnly
- */
- isError: false,
- /**
- If `true` the store is attempting to reload the record form the adapter.
-
- Example
-
- ```javascript
- record.get('isReloading'); // false
- record.reload();
- record.get('isReloading'); // true
- ```
-
- @property isReloading
- @type {Boolean}
- @readOnly
- */
- isReloading: false,
-
- /**
- The `clientId` property is a transient numerical identifier
- generated at runtime by the data store. It is important
- primarily because newly created objects may not yet have an
- externally generated id.
-
- @property clientId
- @private
- @type {Number|String}
- */
- clientId: null,
- /**
- All ember models have an id property. This is an identifier
- managed by an external source. These are always coerced to be
- strings before being used internally. Note when declaring the
- attributes for a model it is an error to declare an id
- attribute.
-
- ```javascript
- var record = store.createRecord(App.Model);
- record.get('id'); // null
-
- store.find('model', 1).then(function(model) {
- model.get('id'); // '1'
- });
- ```
-
- @property id
- @type {String}
- */
- id: null,
- transaction: null,
- /**
- @property currentState
- @private
- @type {Object}
- */
- currentState: null,
- /**
- When the record is in the `invalid` state this object will contain
- any errors returned by the adapter. When present the errors hash
- typically contains keys coresponding to the invalid property names
- and values which are an array of error messages.
-
- ```javascript
- record.get('errors.length'); // 0
- record.set('foo', 'invalid value');
- record.save().then(null, function() {
- record.get('errors').get('foo'); // ['foo should be a number.']
- });
- ```
-
- @property errors
- @type {Object}
- */
- errors: null,
-
- /**
- Create a JSON representation of the record, using the serialization
- strategy of the store's adapter.
-
- `serialize` takes an optional hash as a parameter, currently
- supported options are:
-
- - `includeId`: `true` if the record's ID should be included in the
- JSON representation.
-
- @method serialize
- @param {Object} options
- @returns {Object} an object whose values are primitive JSON values only
- */
- serialize: function(options) {
- var store = get(this, 'store');
- return store.serialize(this, options);
- },
-
- /**
- Use [DS.JSONSerializer](DS.JSONSerializer.html) to
- get the JSON representation of a record.
-
- `toJSON` takes an optional hash as a parameter, currently
- supported options are:
-
- - `includeId`: `true` if the record's ID should be included in the
- JSON representation.
-
- @method toJSON
- @param {Object} options
- @returns {Object} A JSON representation of the object.
- */
- toJSON: function(options) {
- // container is for lazy transform lookups
- var serializer = DS.JSONSerializer.create({ container: this.container });
- return serializer.serialize(this, options);
- },
-
- /**
- Fired when the record is loaded from the server.
-
- @event didLoad
- */
- didLoad: Ember.K,
-
- /**
- Fired when the record is updated.
-
- @event didUpdate
- */
- didUpdate: Ember.K,
-
- /**
- Fired when the record is created.
-
- @event didCreate
- */
- didCreate: Ember.K,
-
- /**
- Fired when the record is deleted.
-
- @event didDelete
- */
- didDelete: Ember.K,
-
- /**
- Fired when the record becomes invalid.
-
- @event becameInvalid
- */
- becameInvalid: Ember.K,
-
- /**
- Fired when the record enters the error state.
-
- @event becameError
- */
- becameError: Ember.K,
-
- /**
- @property data
- @private
- @type {Object}
- */
- data: Ember.computed(function() {
- this._data = this._data || {};
- return this._data;
- }).property(),
-
- _data: null,
-
- init: function() {
- set(this, 'currentState', DS.RootState.empty);
- var errors = DS.Errors.create();
- errors.registerHandlers(this, function() {
- this.send('becameInvalid');
- }, function() {
- this.send('becameValid');
- });
- set(this, 'errors', errors);
- this._super();
- this._setup();
- },
-
- _setup: function() {
- this._changesToSync = {};
- this._deferredTriggers = [];
- this._data = {};
- this._attributes = {};
- this._inFlightAttributes = {};
- this._relationships = {};
- },
-
- /**
- @method send
- @private
- @param {String} name
- @param {Object} context
- */
- send: function(name, context) {
- var currentState = get(this, 'currentState');
-
- if (!currentState[name]) {
- this._unhandledEvent(currentState, name, context);
- }
-
- return currentState[name](this, context);
- },
-
- /**
- @method transitionTo
- @private
- @param {String} name
- */
- transitionTo: function(name) {
- // POSSIBLE TODO: Remove this code and replace with
- // always having direct references to state objects
-
- var pivotName = name.split(".", 1),
- currentState = get(this, 'currentState'),
- state = currentState;
-
- do {
- if (state.exit) { state.exit(this); }
- state = state.parentState;
- } while (!state.hasOwnProperty(pivotName));
-
- var path = name.split(".");
-
- var setups = [], enters = [], i, l;
-
- for (i=0, l=path.length; i')` from " + this.toString(), name !== 'id');
-
- meta.name = name;
- map.set(name, meta);
- }
- });
-
- return map;
- }),
-
- /**
- A map whose keys are the attributes of the model (properties
- described by DS.attr) and whose values are type of transformation
- applied to each attribute. This map does not include any
- attributes that do not have an transformation type.
-
- Example
-
- ```javascript
- App.Person = DS.Model.extend({
- firstName: attr(),
- lastName: attr('string'),
- birthday: attr('date')
- });
-
- var transformedAttributes = Ember.get(App.Person, 'transformedAttributes')
-
- transformedAttributes.forEach(function(field, type) {
- console.log(field, type);
- });
-
- // prints:
- // lastName string
- // birthday date
- ```
-
- @property transformedAttributes
- @static
- @type {Ember.Map}
- @readOnly
- */
- transformedAttributes: Ember.computed(function() {
- var map = Ember.Map.create();
-
- this.eachAttribute(function(key, meta) {
- if (meta.type) {
- map.set(key, meta.type);
- }
- });
-
- return map;
- }),
-
- /**
- Iterates through the attributes of the model, calling the passed function on each
- attribute.
-
- The callback method you provide should have the following signature (all
- parameters are optional):
-
- ```javascript
- function(name, meta);
- ```
-
- - `name` the name of the current property in the iteration
- - `meta` the meta object for the attribute property in the iteration
-
- Note that in addition to a callback, you can also pass an optional target
- object that will be set as `this` on the context.
-
- Example
-
- ```javascript
- App.Person = DS.Model.extend({
- firstName: attr('string'),
- lastName: attr('string'),
- birthday: attr('date')
- });
-
- App.Person.eachAttribute(function(name, meta) {
- console.log(name, meta);
- });
-
- // prints:
- // firstName {type: "string", isAttribute: true, options: Object, parentType: function, name: "firstName"}
- // lastName {type: "string", isAttribute: true, options: Object, parentType: function, name: "lastName"}
- // birthday {type: "date", isAttribute: true, options: Object, parentType: function, name: "birthday"}
- ```
-
- @method eachAttribute
- @param {Function} callback The callback to execute
- @param {Object} [target] The target object to use
- @static
- */
- eachAttribute: function(callback, binding) {
- get(this, 'attributes').forEach(function(name, meta) {
- callback.call(binding, name, meta);
- }, binding);
- },
-
- /**
- Iterates through the transformedAttributes of the model, calling
- the passed function on each attribute. Note the callback will not be
- called for any attributes that do not have an transformation type.
-
- The callback method you provide should have the following signature (all
- parameters are optional):
-
- ```javascript
- function(name, type);
- ```
-
- - `name` the name of the current property in the iteration
- - `type` a tring contrining the name of the type of transformed
- applied to the attribute
-
- Note that in addition to a callback, you can also pass an optional target
- object that will be set as `this` on the context.
-
- Example
-
- ```javascript
- App.Person = DS.Model.extend({
- firstName: attr(),
- lastName: attr('string'),
- birthday: attr('date')
- });
-
- App.Person.eachTransformedAttribute(function(name, type) {
- console.log(name, type);
- });
-
- // prints:
- // lastName string
- // birthday date
- ```
-
- @method eachTransformedAttribute
- @param {Function} callback The callback to execute
- @param {Object} [target] The target object to use
- @static
- */
- eachTransformedAttribute: function(callback, binding) {
- get(this, 'transformedAttributes').forEach(function(name, type) {
- callback.call(binding, name, type);
- });
- }
-});
-
-
-DS.Model.reopen({
- eachAttribute: function(callback, binding) {
- this.constructor.eachAttribute(callback, binding);
- }
-});
-
-function getDefaultValue(record, options, key) {
- if (typeof options.defaultValue === "function") {
- return options.defaultValue();
- } else {
- return options.defaultValue;
- }
-}
-
-function hasValue(record, key) {
- return record._attributes.hasOwnProperty(key) ||
- record._inFlightAttributes.hasOwnProperty(key) ||
- record._data.hasOwnProperty(key);
-}
-
-function getValue(record, key) {
- if (record._attributes.hasOwnProperty(key)) {
- return record._attributes[key];
- } else if (record._inFlightAttributes.hasOwnProperty(key)) {
- return record._inFlightAttributes[key];
- } else {
- return record._data[key];
- }
-}
-
-/**
- `DS.attr` defines an attribute on a [DS.Model](DS.Model.html).
- By default, attributes are passed through as-is, however you can specify an
- optional type to have the value automatically transformed.
- Ember Data ships with four basic transform types: `string`, `number`,
- `boolean` and `date`. You can define your own transforms by subclassing
- [DS.Transform](DS.Transform.html).
-
- `DS.attr` takes an optional hash as a second parameter, currently
- supported options are:
-
- - `defaultValue`: Pass a string or a function to be called to set the attribute
- to a default value if none is supplied.
-
- Example
-
- ```javascript
- var attr = DS.attr;
-
- App.User = DS.Model.extend({
- username: attr('string'),
- email: attr('string'),
- verified: attr('boolean', {defaultValue: false})
- });
- ```
-
- @namespace
- @method attr
- @for DS
- @param {String} type the attribute type
- @param {Object} options a hash of options
- @return {Attribute}
-*/
-
-DS.attr = function(type, options) {
- options = options || {};
-
- var meta = {
- type: type,
- isAttribute: true,
- options: options
- };
-
- return Ember.computed(function(key, value) {
- if (arguments.length > 1) {
- Ember.assert("You may not set `id` as an attribute on your model. Please remove any lines that look like: `id: DS.attr('')` from " + this.constructor.toString(), key !== 'id');
- var oldValue = this._attributes[key] || this._inFlightAttributes[key] || this._data[key];
-
- this.send('didSetProperty', {
- name: key,
- oldValue: oldValue,
- originalValue: this._data[key],
- value: value
- });
-
- this._attributes[key] = value;
- return value;
- } else if (hasValue(this, key)) {
- return getValue(this, key);
- } else {
- return getDefaultValue(this, options, key);
- }
-
- // `data` is never set directly. However, it may be
- // invalidated from the state manager's setData
- // event.
- }).property('data').meta(meta);
-};
-
-
-})();
-
-
-
-(function() {
-/**
- @module ember-data
-*/
-
-})();
-
-
-
-(function() {
-/**
- @module ember-data
-*/
-
-/**
- An AttributeChange object is created whenever a record's
- attribute changes value. It is used to track changes to a
- record between transaction commits.
-
- @class AttributeChange
- @namespace DS
- @private
- @constructor
-*/
-var AttributeChange = DS.AttributeChange = function(options) {
- this.record = options.record;
- this.store = options.store;
- this.name = options.name;
- this.value = options.value;
- this.oldValue = options.oldValue;
-};
-
-AttributeChange.createChange = function(options) {
- return new AttributeChange(options);
-};
-
-AttributeChange.prototype = {
- sync: function() {
- if (this.value !== this.oldValue) {
- this.record.send('becomeDirty');
- this.record.updateRecordArraysLater();
- }
-
- // TODO: Use this object in the commit process
- this.destroy();
- },
-
- /**
- If the AttributeChange is destroyed (either by being rolled back
- or being committed), remove it from the list of pending changes
- on the record.
-
- @method destroy
- */
- destroy: function() {
- delete this.record._changesToSync[this.name];
- }
-};
-
-})();
-
-
-
-(function() {
-/**
- @module ember-data
-*/
-
-var get = Ember.get, set = Ember.set;
-var forEach = Ember.EnumerableUtils.forEach;
-
-/**
- @class RelationshipChange
- @namespace DS
- @private
- @construtor
-*/
-DS.RelationshipChange = function(options) {
- this.parentRecord = options.parentRecord;
- this.childRecord = options.childRecord;
- this.firstRecord = options.firstRecord;
- this.firstRecordKind = options.firstRecordKind;
- this.firstRecordName = options.firstRecordName;
- this.secondRecord = options.secondRecord;
- this.secondRecordKind = options.secondRecordKind;
- this.secondRecordName = options.secondRecordName;
- this.changeType = options.changeType;
- this.store = options.store;
-
- this.committed = {};
-};
-
-/**
- @class RelationshipChangeAdd
- @namespace DS
- @private
- @construtor
-*/
-DS.RelationshipChangeAdd = function(options){
- DS.RelationshipChange.call(this, options);
-};
-
-/**
- @class RelationshipChangeRemove
- @namespace DS
- @private
- @construtor
-*/
-DS.RelationshipChangeRemove = function(options){
- DS.RelationshipChange.call(this, options);
-};
-
-DS.RelationshipChange.create = function(options) {
- return new DS.RelationshipChange(options);
-};
-
-DS.RelationshipChangeAdd.create = function(options) {
- return new DS.RelationshipChangeAdd(options);
-};
-
-DS.RelationshipChangeRemove.create = function(options) {
- return new DS.RelationshipChangeRemove(options);
-};
-
-DS.OneToManyChange = {};
-DS.OneToNoneChange = {};
-DS.ManyToNoneChange = {};
-DS.OneToOneChange = {};
-DS.ManyToManyChange = {};
-
-DS.RelationshipChange._createChange = function(options){
- if(options.changeType === "add"){
- return DS.RelationshipChangeAdd.create(options);
- }
- if(options.changeType === "remove"){
- return DS.RelationshipChangeRemove.create(options);
- }
-};
-
-
-DS.RelationshipChange.determineRelationshipType = function(recordType, knownSide){
- var knownKey = knownSide.key, key, otherKind;
- var knownKind = knownSide.kind;
-
- var inverse = recordType.inverseFor(knownKey);
-
- if (inverse){
- key = inverse.name;
- otherKind = inverse.kind;
- }
-
- if (!inverse){
- return knownKind === "belongsTo" ? "oneToNone" : "manyToNone";
- }
- else{
- if(otherKind === "belongsTo"){
- return knownKind === "belongsTo" ? "oneToOne" : "manyToOne";
- }
- else{
- return knownKind === "belongsTo" ? "oneToMany" : "manyToMany";
- }
- }
-
-};
-
-DS.RelationshipChange.createChange = function(firstRecord, secondRecord, store, options){
- // Get the type of the child based on the child's client ID
- var firstRecordType = firstRecord.constructor, changeType;
- changeType = DS.RelationshipChange.determineRelationshipType(firstRecordType, options);
- if (changeType === "oneToMany"){
- return DS.OneToManyChange.createChange(firstRecord, secondRecord, store, options);
- }
- else if (changeType === "manyToOne"){
- return DS.OneToManyChange.createChange(secondRecord, firstRecord, store, options);
- }
- else if (changeType === "oneToNone"){
- return DS.OneToNoneChange.createChange(firstRecord, secondRecord, store, options);
- }
- else if (changeType === "manyToNone"){
- return DS.ManyToNoneChange.createChange(firstRecord, secondRecord, store, options);
- }
- else if (changeType === "oneToOne"){
- return DS.OneToOneChange.createChange(firstRecord, secondRecord, store, options);
- }
- else if (changeType === "manyToMany"){
- return DS.ManyToManyChange.createChange(firstRecord, secondRecord, store, options);
- }
-};
-
-DS.OneToNoneChange.createChange = function(childRecord, parentRecord, store, options) {
- var key = options.key;
- var change = DS.RelationshipChange._createChange({
- parentRecord: parentRecord,
- childRecord: childRecord,
- firstRecord: childRecord,
- store: store,
- changeType: options.changeType,
- firstRecordName: key,
- firstRecordKind: "belongsTo"
- });
-
- store.addRelationshipChangeFor(childRecord, key, parentRecord, null, change);
-
- return change;
-};
-
-DS.ManyToNoneChange.createChange = function(childRecord, parentRecord, store, options) {
- var key = options.key;
- var change = DS.RelationshipChange._createChange({
- parentRecord: childRecord,
- childRecord: parentRecord,
- secondRecord: childRecord,
- store: store,
- changeType: options.changeType,
- secondRecordName: options.key,
- secondRecordKind: "hasMany"
- });
-
- store.addRelationshipChangeFor(childRecord, key, parentRecord, null, change);
- return change;
-};
-
-
-DS.ManyToManyChange.createChange = function(childRecord, parentRecord, store, options) {
- // If the name of the belongsTo side of the relationship is specified,
- // use that
- // If the type of the parent is specified, look it up on the child's type
- // definition.
- var key = options.key;
-
- var change = DS.RelationshipChange._createChange({
- parentRecord: parentRecord,
- childRecord: childRecord,
- firstRecord: childRecord,
- secondRecord: parentRecord,
- firstRecordKind: "hasMany",
- secondRecordKind: "hasMany",
- store: store,
- changeType: options.changeType,
- firstRecordName: key
- });
-
- store.addRelationshipChangeFor(childRecord, key, parentRecord, null, change);
-
-
- return change;
-};
-
-DS.OneToOneChange.createChange = function(childRecord, parentRecord, store, options) {
- var key;
-
- // If the name of the belongsTo side of the relationship is specified,
- // use that
- // If the type of the parent is specified, look it up on the child's type
- // definition.
- if (options.parentType) {
- key = options.parentType.inverseFor(options.key).name;
- } else if (options.key) {
- key = options.key;
- } else {
- Ember.assert("You must pass either a parentType or belongsToName option to OneToManyChange.forChildAndParent", false);
- }
-
- var change = DS.RelationshipChange._createChange({
- parentRecord: parentRecord,
- childRecord: childRecord,
- firstRecord: childRecord,
- secondRecord: parentRecord,
- firstRecordKind: "belongsTo",
- secondRecordKind: "belongsTo",
- store: store,
- changeType: options.changeType,
- firstRecordName: key
- });
-
- store.addRelationshipChangeFor(childRecord, key, parentRecord, null, change);
-
-
- return change;
-};
-
-DS.OneToOneChange.maintainInvariant = function(options, store, childRecord, key){
- if (options.changeType === "add" && store.recordIsMaterialized(childRecord)) {
- var oldParent = get(childRecord, key);
- if (oldParent){
- var correspondingChange = DS.OneToOneChange.createChange(childRecord, oldParent, store, {
- parentType: options.parentType,
- hasManyName: options.hasManyName,
- changeType: "remove",
- key: options.key
- });
- store.addRelationshipChangeFor(childRecord, key, options.parentRecord , null, correspondingChange);
- correspondingChange.sync();
- }
- }
-};
-
-DS.OneToManyChange.createChange = function(childRecord, parentRecord, store, options) {
- var key;
-
- // If the name of the belongsTo side of the relationship is specified,
- // use that
- // If the type of the parent is specified, look it up on the child's type
- // definition.
- if (options.parentType) {
- key = options.parentType.inverseFor(options.key).name;
- DS.OneToManyChange.maintainInvariant( options, store, childRecord, key );
- } else if (options.key) {
- key = options.key;
- } else {
- Ember.assert("You must pass either a parentType or belongsToName option to OneToManyChange.forChildAndParent", false);
- }
-
- var change = DS.RelationshipChange._createChange({
- parentRecord: parentRecord,
- childRecord: childRecord,
- firstRecord: childRecord,
- secondRecord: parentRecord,
- firstRecordKind: "belongsTo",
- secondRecordKind: "hasMany",
- store: store,
- changeType: options.changeType,
- firstRecordName: key
- });
-
- store.addRelationshipChangeFor(childRecord, key, parentRecord, change.getSecondRecordName(), change);
-
-
- return change;
-};
-
-
-DS.OneToManyChange.maintainInvariant = function(options, store, childRecord, key){
- if (options.changeType === "add" && childRecord) {
- var oldParent = get(childRecord, key);
- if (oldParent){
- var correspondingChange = DS.OneToManyChange.createChange(childRecord, oldParent, store, {
- parentType: options.parentType,
- hasManyName: options.hasManyName,
- changeType: "remove",
- key: options.key
- });
- store.addRelationshipChangeFor(childRecord, key, options.parentRecord, correspondingChange.getSecondRecordName(), correspondingChange);
- correspondingChange.sync();
- }
- }
-};
-
-/**
- @class RelationshipChange
- @namespace DS
-*/
-DS.RelationshipChange.prototype = {
-
- getSecondRecordName: function() {
- var name = this.secondRecordName, parent;
-
- if (!name) {
- parent = this.secondRecord;
- if (!parent) { return; }
-
- var childType = this.firstRecord.constructor;
- var inverse = childType.inverseFor(this.firstRecordName);
- this.secondRecordName = inverse.name;
- }
-
- return this.secondRecordName;
- },
-
- /**
- Get the name of the relationship on the belongsTo side.
-
- @method getFirstRecordName
- @return {String}
- */
- getFirstRecordName: function() {
- var name = this.firstRecordName;
- return name;
- },
-
- /**
- @method destroy
- @private
- */
- destroy: function() {
- var childRecord = this.childRecord,
- belongsToName = this.getFirstRecordName(),
- hasManyName = this.getSecondRecordName(),
- store = this.store;
-
- store.removeRelationshipChangeFor(childRecord, belongsToName, this.parentRecord, hasManyName, this.changeType);
- },
-
- getSecondRecord: function(){
- return this.secondRecord;
- },
-
- /**
- @method getFirstRecord
- @private
- */
- getFirstRecord: function() {
- return this.firstRecord;
- },
-
- coalesce: function(){
- var relationshipPairs = this.store.relationshipChangePairsFor(this.firstRecord);
- forEach(relationshipPairs, function(pair){
- var addedChange = pair["add"];
- var removedChange = pair["remove"];
- if(addedChange && removedChange) {
- addedChange.destroy();
- removedChange.destroy();
- }
- });
- }
-};
-
-DS.RelationshipChangeAdd.prototype = Ember.create(DS.RelationshipChange.create({}));
-DS.RelationshipChangeRemove.prototype = Ember.create(DS.RelationshipChange.create({}));
-
-// the object is a value, and not a promise
-function isValue(object) {
- return typeof object === 'object' && (!object.then || typeof object.then !== 'function');
-}
-
-DS.RelationshipChangeAdd.prototype.changeType = "add";
-DS.RelationshipChangeAdd.prototype.sync = function() {
- var secondRecordName = this.getSecondRecordName(),
- firstRecordName = this.getFirstRecordName(),
- firstRecord = this.getFirstRecord(),
- secondRecord = this.getSecondRecord();
-
- //Ember.assert("You specified a hasMany (" + hasManyName + ") on " + (!belongsToName && (newParent || oldParent || this.lastParent).constructor) + " but did not specify an inverse belongsTo on " + child.constructor, belongsToName);
- //Ember.assert("You specified a belongsTo (" + belongsToName + ") on " + child.constructor + " but did not specify an inverse hasMany on " + (!hasManyName && (newParent || oldParent || this.lastParentRecord).constructor), hasManyName);
-
- if (secondRecord instanceof DS.Model && firstRecord instanceof DS.Model) {
- if(this.secondRecordKind === "belongsTo"){
- secondRecord.suspendRelationshipObservers(function(){
- set(secondRecord, secondRecordName, firstRecord);
- });
-
- }
- else if(this.secondRecordKind === "hasMany"){
- secondRecord.suspendRelationshipObservers(function(){
- var relationship = get(secondRecord, secondRecordName);
- if (isValue(relationship)) { relationship.addObject(firstRecord); }
- });
- }
- }
-
- if (firstRecord instanceof DS.Model && secondRecord instanceof DS.Model && get(firstRecord, firstRecordName) !== secondRecord) {
- if(this.firstRecordKind === "belongsTo"){
- firstRecord.suspendRelationshipObservers(function(){
- set(firstRecord, firstRecordName, secondRecord);
- });
- }
- else if(this.firstRecordKind === "hasMany"){
- firstRecord.suspendRelationshipObservers(function(){
- var relationship = get(firstRecord, firstRecordName);
- if (isValue(relationship)) { relationship.addObject(secondRecord); }
- });
- }
- }
-
- this.coalesce();
-};
-
-DS.RelationshipChangeRemove.prototype.changeType = "remove";
-DS.RelationshipChangeRemove.prototype.sync = function() {
- var secondRecordName = this.getSecondRecordName(),
- firstRecordName = this.getFirstRecordName(),
- firstRecord = this.getFirstRecord(),
- secondRecord = this.getSecondRecord();
-
- //Ember.assert("You specified a hasMany (" + hasManyName + ") on " + (!belongsToName && (newParent || oldParent || this.lastParent).constructor) + " but did not specify an inverse belongsTo on " + child.constructor, belongsToName);
- //Ember.assert("You specified a belongsTo (" + belongsToName + ") on " + child.constructor + " but did not specify an inverse hasMany on " + (!hasManyName && (newParent || oldParent || this.lastParentRecord).constructor), hasManyName);
-
- if (secondRecord instanceof DS.Model && firstRecord instanceof DS.Model) {
- if(this.secondRecordKind === "belongsTo"){
- secondRecord.suspendRelationshipObservers(function(){
- set(secondRecord, secondRecordName, null);
- });
- }
- else if(this.secondRecordKind === "hasMany"){
- secondRecord.suspendRelationshipObservers(function(){
- var relationship = get(secondRecord, secondRecordName);
- if (isValue(relationship)) { relationship.removeObject(firstRecord); }
- });
- }
- }
-
- if (firstRecord instanceof DS.Model && get(firstRecord, firstRecordName)) {
- if(this.firstRecordKind === "belongsTo"){
- firstRecord.suspendRelationshipObservers(function(){
- set(firstRecord, firstRecordName, null);
- });
- }
- else if(this.firstRecordKind === "hasMany"){
- firstRecord.suspendRelationshipObservers(function(){
- var relationship = get(firstRecord, firstRecordName);
- if (isValue(relationship)) { relationship.removeObject(secondRecord); }
- });
- }
- }
-
- this.coalesce();
-};
-
-})();
-
-
-
-(function() {
-/**
- @module ember-data
-*/
-
-})();
-
-
-
-(function() {
-var get = Ember.get, set = Ember.set,
- isNone = Ember.isNone;
-
-/**
- @module ember-data
-*/
-
-function asyncBelongsTo(type, options, meta) {
- return Ember.computed(function(key, value) {
- var data = get(this, 'data'),
- store = get(this, 'store'),
- promiseLabel = "DS: Async belongsTo " + this + " : " + key;
-
- if (arguments.length === 2) {
- Ember.assert("You can only add a '" + type + "' record to this relationship", !value || value instanceof store.modelFor(type));
- return value === undefined ? null : DS.PromiseObject.create({ promise: Ember.RSVP.resolve(value, promiseLabel) });
- }
-
- var link = data.links && data.links[key],
- belongsTo = data[key];
-
- if(!isNone(belongsTo)) {
- var promise = store.fetchRecord(belongsTo) || Ember.RSVP.resolve(belongsTo, promiseLabel);
- return DS.PromiseObject.create({ promise: promise});
- } else if (link) {
- var resolver = Ember.RSVP.defer("DS: Async belongsTo (link) " + this + " : " + key);
- store.findBelongsTo(this, link, meta, resolver);
- return DS.PromiseObject.create({ promise: resolver.promise });
- } else {
- return null;
- }
- }).property('data').meta(meta);
-}
-
-/**
- `DS.belongsTo` is used to define One-To-One and One-To-Many
- relationships on a [DS.Model](DS.Model.html).
-
-
- `DS.belongsTo` takes an optional hash as a second parameter, currently
- supported options are:
-
- - `async`: A boolean value used to explicitly declare this to be an async relationship.
- - `inverse`: A string used to identify the inverse property on a
- related model in a One-To-Many relationship. See [Explicit Inverses](#toc_explicit-inverses)
-
- #### One-To-One
- To declare a one-to-one relationship between two models, use
- `DS.belongsTo`:
-
- ```javascript
- App.User = DS.Model.extend({
- profile: DS.belongsTo('profile')
- });
-
- App.Profile = DS.Model.extend({
- user: DS.belongsTo('user')
- });
- ```
-
- #### One-To-Many
- To declare a one-to-many relationship between two models, use
- `DS.belongsTo` in combination with `DS.hasMany`, like this:
-
- ```javascript
- App.Post = DS.Model.extend({
- comments: DS.hasMany('comment')
- });
-
- App.Comment = DS.Model.extend({
- post: DS.belongsTo('post')
- });
- ```
-
- @namespace
- @method belongsTo
- @for DS
- @param {String or DS.Model} type the model type of the relationship
- @param {Object} options a hash of options
- @return {Ember.computed} relationship
-*/
-DS.belongsTo = function(type, options) {
- if (typeof type === 'object') {
- options = type;
- type = undefined;
- } else {
- Ember.assert("The first argument DS.belongsTo must be a model type or string, like DS.belongsTo(App.Person)", !!type && (typeof type === 'string' || DS.Model.detect(type)));
- }
-
- options = options || {};
-
- var meta = { type: type, isRelationship: true, options: options, kind: 'belongsTo' };
-
- if (options.async) {
- return asyncBelongsTo(type, options, meta);
- }
-
- return Ember.computed(function(key, value) {
- var data = get(this, 'data'),
- store = get(this, 'store'), belongsTo, typeClass;
-
- if (typeof type === 'string') {
- typeClass = store.modelFor(type);
- } else {
- typeClass = type;
- }
-
- if (arguments.length === 2) {
- Ember.assert("You can only add a '" + type + "' record to this relationship", !value || value instanceof typeClass);
- return value === undefined ? null : value;
- }
-
- belongsTo = data[key];
-
- if (isNone(belongsTo)) { return null; }
-
- store.fetchRecord(belongsTo);
-
- return belongsTo;
- }).property('data').meta(meta);
-};
-
-/**
- These observers observe all `belongsTo` relationships on the record. See
- `relationships/ext` to see how these observers get their dependencies.
-
- @class Model
- @namespace DS
-*/
-DS.Model.reopen({
-
- /**
- @method belongsToWillChange
- @private
- @static
- @param record
- @param key
- */
- belongsToWillChange: Ember.beforeObserver(function(record, key) {
- if (get(record, 'isLoaded')) {
- var oldParent = get(record, key);
-
- if (oldParent) {
- var store = get(record, 'store'),
- change = DS.RelationshipChange.createChange(record, oldParent, store, { key: key, kind: "belongsTo", changeType: "remove" });
-
- change.sync();
- this._changesToSync[key] = change;
- }
- }
- }),
-
- /**
- @method belongsToDidChange
- @private
- @static
- @param record
- @param key
- */
- belongsToDidChange: Ember.immediateObserver(function(record, key) {
- if (get(record, 'isLoaded')) {
- var newParent = get(record, key);
-
- if (newParent) {
- var store = get(record, 'store'),
- change = DS.RelationshipChange.createChange(record, newParent, store, { key: key, kind: "belongsTo", changeType: "add" });
-
- change.sync();
- }
- }
-
- delete this._changesToSync[key];
- })
-});
-
-})();
-
-
-
-(function() {
-/**
- @module ember-data
-*/
-
-var get = Ember.get, set = Ember.set, setProperties = Ember.setProperties;
-
-function asyncHasMany(type, options, meta) {
- return Ember.computed(function(key, value) {
- var relationship = this._relationships[key],
- promiseLabel = "DS: Async hasMany " + this + " : " + key;
-
- if (!relationship) {
- var resolver = Ember.RSVP.defer(promiseLabel);
- relationship = buildRelationship(this, key, options, function(store, data) {
- var link = data.links && data.links[key];
- var rel;
- if (link) {
- rel = store.findHasMany(this, link, meta, resolver);
- } else {
- rel = store.findMany(this, data[key], meta.type, resolver);
- }
- // cache the promise so we can use it
- // when we come back and don't need to rebuild
- // the relationship.
- set(rel, 'promise', resolver.promise);
- return rel;
- });
- }
-
- var promise = relationship.get('promise').then(function() {
- return relationship;
- }, null, "DS: Async hasMany records received");
-
- return DS.PromiseArray.create({ promise: promise });
- }).property('data').meta(meta);
-}
-
-function buildRelationship(record, key, options, callback) {
- var rels = record._relationships;
-
- if (rels[key]) { return rels[key]; }
-
- var data = get(record, 'data'),
- store = get(record, 'store');
-
- var relationship = rels[key] = callback.call(record, store, data);
-
- return setProperties(relationship, {
- owner: record, name: key, isPolymorphic: options.polymorphic
- });
-}
-
-function hasRelationship(type, options) {
- options = options || {};
-
- var meta = { type: type, isRelationship: true, options: options, kind: 'hasMany' };
-
- if (options.async) {
- return asyncHasMany(type, options, meta);
- }
-
- return Ember.computed(function(key, value) {
- return buildRelationship(this, key, options, function(store, data) {
- var records = data[key];
- Ember.assert("You looked up the '" + key + "' relationship on '" + this + "' but some of the associated records were not loaded. Either make sure they are all loaded together with the parent record, or specify that the relationship is async (`DS.hasMany({ async: true })`)", Ember.A(records).everyProperty('isEmpty', false));
- return store.findMany(this, data[key], meta.type);
- });
- }).property('data').meta(meta);
-}
-
-/**
- `DS.hasMany` is used to define One-To-Many and Many-To-Many
- relationships on a [DS.Model](DS.Model.html).
-
- `DS.hasMany` takes an optional hash as a second parameter, currently
- supported options are:
-
- - `async`: A boolean value used to explicitly declare this to be an async relationship.
- - `inverse`: A string used to identify the inverse property on a related model.
-
- #### One-To-Many
- To declare a one-to-many relationship between two models, use
- `DS.belongsTo` in combination with `DS.hasMany`, like this:
-
- ```javascript
- App.Post = DS.Model.extend({
- comments: DS.hasMany('comment')
- });
-
- App.Comment = DS.Model.extend({
- post: DS.belongsTo('post')
- });
- ```
-
- #### Many-To-Many
- To declare a many-to-many relationship between two models, use
- `DS.hasMany`:
-
- ```javascript
- App.Post = DS.Model.extend({
- tags: DS.hasMany('tag')
- });
-
- App.Tag = DS.Model.extend({
- posts: DS.hasMany('post')
- });
- ```
-
- #### Explicit Inverses
-
- Ember Data will do its best to discover which relationships map to
- one another. In the one-to-many code above, for example, Ember Data
- can figure out that changing the `comments` relationship should update
- the `post` relationship on the inverse because post is the only
- relationship to that model.
-
- However, sometimes you may have multiple `belongsTo`/`hasManys` for the
- same type. You can specify which property on the related model is
- the inverse using `DS.hasMany`'s `inverse` option:
-
- ```javascript
- var belongsTo = DS.belongsTo,
- hasMany = DS.hasMany;
-
- App.Comment = DS.Model.extend({
- onePost: belongsTo('post'),
- twoPost: belongsTo('post'),
- redPost: belongsTo('post'),
- bluePost: belongsTo('post')
- });
-
- App.Post = DS.Model.extend({
- comments: hasMany('comment', {
- inverse: 'redPost'
- })
- });
- ```
-
- You can also specify an inverse on a `belongsTo`, which works how
- you'd expect.
-
- @namespace
- @method hasMany
- @for DS
- @param {String or DS.Model} type the model type of the relationship
- @param {Object} options a hash of options
- @return {Ember.computed} relationship
-*/
-DS.hasMany = function(type, options) {
- if (typeof type === 'object') {
- options = type;
- type = undefined;
- }
- return hasRelationship(type, options);
-};
-
-})();
-
-
-
-(function() {
-var get = Ember.get, set = Ember.set;
-
-/**
- @module ember-data
-*/
-
-/*
- This file defines several extensions to the base `DS.Model` class that
- add support for one-to-many relationships.
-*/
-
-/**
- @class Model
- @namespace DS
-*/
-DS.Model.reopen({
-
- /**
- This Ember.js hook allows an object to be notified when a property
- is defined.
-
- In this case, we use it to be notified when an Ember Data user defines a
- belongs-to relationship. In that case, we need to set up observers for
- each one, allowing us to track relationship changes and automatically
- reflect changes in the inverse has-many array.
-
- This hook passes the class being set up, as well as the key and value
- being defined. So, for example, when the user does this:
-
- ```javascript
- DS.Model.extend({
- parent: DS.belongsTo('user')
- });
- ```
-
- This hook would be called with "parent" as the key and the computed
- property returned by `DS.belongsTo` as the value.
-
- @method didDefineProperty
- @param proto
- @param key
- @param value
- */
- didDefineProperty: function(proto, key, value) {
- // Check if the value being set is a computed property.
- if (value instanceof Ember.Descriptor) {
-
- // If it is, get the metadata for the relationship. This is
- // populated by the `DS.belongsTo` helper when it is creating
- // the computed property.
- var meta = value.meta();
-
- if (meta.isRelationship && meta.kind === 'belongsTo') {
- Ember.addObserver(proto, key, null, 'belongsToDidChange');
- Ember.addBeforeObserver(proto, key, null, 'belongsToWillChange');
- }
-
- meta.parentType = proto.constructor;
- }
- }
-});
-
-/*
- These DS.Model extensions add class methods that provide relationship
- introspection abilities about relationships.
-
- A note about the computed properties contained here:
-
- **These properties are effectively sealed once called for the first time.**
- To avoid repeatedly doing expensive iteration over a model's fields, these
- values are computed once and then cached for the remainder of the runtime of
- your application.
-
- If your application needs to modify a class after its initial definition
- (for example, using `reopen()` to add additional attributes), make sure you
- do it before using your model with the store, which uses these properties
- extensively.
-*/
-
-DS.Model.reopenClass({
- /**
- For a given relationship name, returns the model type of the relationship.
-
- For example, if you define a model like this:
-
- ```javascript
- App.Post = DS.Model.extend({
- comments: DS.hasMany('comment')
- });
- ```
-
- Calling `App.Post.typeForRelationship('comments')` will return `App.Comment`.
-
- @method typeForRelationship
- @static
- @param {String} name the name of the relationship
- @return {subclass of DS.Model} the type of the relationship, or undefined
- */
- typeForRelationship: function(name) {
- var relationship = get(this, 'relationshipsByName').get(name);
- return relationship && relationship.type;
- },
-
- inverseFor: function(name) {
- var inverseType = this.typeForRelationship(name);
-
- if (!inverseType) { return null; }
-
- var options = this.metaForProperty(name).options;
-
- if (options.inverse === null) { return null; }
-
- var inverseName, inverseKind;
-
- if (options.inverse) {
- inverseName = options.inverse;
- inverseKind = Ember.get(inverseType, 'relationshipsByName').get(inverseName).kind;
- } else {
- var possibleRelationships = findPossibleInverses(this, inverseType);
-
- if (possibleRelationships.length === 0) { return null; }
-
- Ember.assert("You defined the '" + name + "' relationship on " + this + ", but multiple possible inverse relationships of type " + this + " were found on " + inverseType + ". Look at http://emberjs.com/guides/models/defining-models/#toc_explicit-inverses for how to explicitly specify inverses", possibleRelationships.length === 1);
-
- inverseName = possibleRelationships[0].name;
- inverseKind = possibleRelationships[0].kind;
- }
-
- function findPossibleInverses(type, inverseType, possibleRelationships) {
- possibleRelationships = possibleRelationships || [];
-
- var relationshipMap = get(inverseType, 'relationships');
- if (!relationshipMap) { return; }
-
- var relationships = relationshipMap.get(type);
- if (relationships) {
- possibleRelationships.push.apply(possibleRelationships, relationshipMap.get(type));
- }
-
- if (type.superclass) {
- findPossibleInverses(type.superclass, inverseType, possibleRelationships);
- }
-
- return possibleRelationships;
- }
-
- return {
- type: inverseType,
- name: inverseName,
- kind: inverseKind
- };
- },
-
- /**
- The model's relationships as a map, keyed on the type of the
- relationship. The value of each entry is an array containing a descriptor
- for each relationship with that type, describing the name of the relationship
- as well as the type.
-
- For example, given the following model definition:
-
- ```javascript
- App.Blog = DS.Model.extend({
- users: DS.hasMany('user'),
- owner: DS.belongsTo('user'),
- posts: DS.hasMany('post')
- });
- ```
-
- This computed property would return a map describing these
- relationships, like this:
-
- ```javascript
- var relationships = Ember.get(App.Blog, 'relationships');
- relationships.get(App.User);
- //=> [ { name: 'users', kind: 'hasMany' },
- // { name: 'owner', kind: 'belongsTo' } ]
- relationships.get(App.Post);
- //=> [ { name: 'posts', kind: 'hasMany' } ]
- ```
-
- @property relationships
- @static
- @type Ember.Map
- @readOnly
- */
- relationships: Ember.computed(function() {
- var map = new Ember.MapWithDefault({
- defaultValue: function() { return []; }
- });
-
- // Loop through each computed property on the class
- this.eachComputedProperty(function(name, meta) {
-
- // If the computed property is a relationship, add
- // it to the map.
- if (meta.isRelationship) {
- if (typeof meta.type === 'string') {
- meta.type = this.store.modelFor(meta.type);
- }
-
- var relationshipsForType = map.get(meta.type);
-
- relationshipsForType.push({ name: name, kind: meta.kind });
- }
- });
-
- return map;
- }),
-
- /**
- A hash containing lists of the model's relationships, grouped
- by the relationship kind. For example, given a model with this
- definition:
-
- ```javascript
- App.Blog = DS.Model.extend({
- users: DS.hasMany('user'),
- owner: DS.belongsTo('user'),
-
- posts: DS.hasMany('post')
- });
- ```
-
- This property would contain the following:
-
- ```javascript
- var relationshipNames = Ember.get(App.Blog, 'relationshipNames');
- relationshipNames.hasMany;
- //=> ['users', 'posts']
- relationshipNames.belongsTo;
- //=> ['owner']
- ```
-
- @property relationshipNames
- @static
- @type Object
- @readOnly
- */
- relationshipNames: Ember.computed(function() {
- var names = { hasMany: [], belongsTo: [] };
-
- this.eachComputedProperty(function(name, meta) {
- if (meta.isRelationship) {
- names[meta.kind].push(name);
- }
- });
-
- return names;
- }),
-
- /**
- An array of types directly related to a model. Each type will be
- included once, regardless of the number of relationships it has with
- the model.
-
- For example, given a model with this definition:
-
- ```javascript
- App.Blog = DS.Model.extend({
- users: DS.hasMany('user'),
- owner: DS.belongsTo('user'),
-
- posts: DS.hasMany('post')
- });
- ```
-
- This property would contain the following:
-
- ```javascript
- var relatedTypes = Ember.get(App.Blog, 'relatedTypes');
- //=> [ App.User, App.Post ]
- ```
-
- @property relatedTypes
- @static
- @type Ember.Array
- @readOnly
- */
- relatedTypes: Ember.computed(function() {
- var type,
- types = Ember.A();
-
- // Loop through each computed property on the class,
- // and create an array of the unique types involved
- // in relationships
- this.eachComputedProperty(function(name, meta) {
- if (meta.isRelationship) {
- type = meta.type;
-
- if (typeof type === 'string') {
- type = get(this, type, false) || this.store.modelFor(type);
- }
-
- Ember.assert("You specified a hasMany (" + meta.type + ") on " + meta.parentType + " but " + meta.type + " was not found.", type);
-
- if (!types.contains(type)) {
- Ember.assert("Trying to sideload " + name + " on " + this.toString() + " but the type doesn't exist.", !!type);
- types.push(type);
- }
- }
- });
-
- return types;
- }),
-
- /**
- A map whose keys are the relationships of a model and whose values are
- relationship descriptors.
-
- For example, given a model with this
- definition:
-
- ```javascript
- App.Blog = DS.Model.extend({
- users: DS.hasMany('user'),
- owner: DS.belongsTo('user'),
-
- posts: DS.hasMany('post')
- });
- ```
-
- This property would contain the following:
-
- ```javascript
- var relationshipsByName = Ember.get(App.Blog, 'relationshipsByName');
- relationshipsByName.get('users');
- //=> { key: 'users', kind: 'hasMany', type: App.User }
- relationshipsByName.get('owner');
- //=> { key: 'owner', kind: 'belongsTo', type: App.User }
- ```
-
- @property relationshipsByName
- @static
- @type Ember.Map
- @readOnly
- */
- relationshipsByName: Ember.computed(function() {
- var map = Ember.Map.create(), type;
-
- this.eachComputedProperty(function(name, meta) {
- if (meta.isRelationship) {
- meta.key = name;
- type = meta.type;
-
- if (!type && meta.kind === 'hasMany') {
- type = Ember.String.singularize(name);
- } else if (!type) {
- type = name;
- }
-
- if (typeof type === 'string') {
- meta.type = this.store.modelFor(type);
- }
-
- map.set(name, meta);
- }
- });
-
- return map;
- }),
-
- /**
- A map whose keys are the fields of the model and whose values are strings
- describing the kind of the field. A model's fields are the union of all of its
- attributes and relationships.
-
- For example:
-
- ```javascript
-
- App.Blog = DS.Model.extend({
- users: DS.hasMany('user'),
- owner: DS.belongsTo('user'),
-
- posts: DS.hasMany('post'),
-
- title: DS.attr('string')
- });
-
- var fields = Ember.get(App.Blog, 'fields');
- fields.forEach(function(field, kind) {
- console.log(field, kind);
- });
-
- // prints:
- // users, hasMany
- // owner, belongsTo
- // posts, hasMany
- // title, attribute
- ```
-
- @property fields
- @static
- @type Ember.Map
- @readOnly
- */
- fields: Ember.computed(function() {
- var map = Ember.Map.create();
-
- this.eachComputedProperty(function(name, meta) {
- if (meta.isRelationship) {
- map.set(name, meta.kind);
- } else if (meta.isAttribute) {
- map.set(name, 'attribute');
- }
- });
-
- return map;
- }),
-
- /**
- Given a callback, iterates over each of the relationships in the model,
- invoking the callback with the name of each relationship and its relationship
- descriptor.
-
- @method eachRelationship
- @static
- @param {Function} callback the callback to invoke
- @param {any} binding the value to which the callback's `this` should be bound
- */
- eachRelationship: function(callback, binding) {
- get(this, 'relationshipsByName').forEach(function(name, relationship) {
- callback.call(binding, name, relationship);
- });
- },
-
- /**
- Given a callback, iterates over each of the types related to a model,
- invoking the callback with the related type's class. Each type will be
- returned just once, regardless of how many different relationships it has
- with a model.
-
- @method eachRelatedType
- @static
- @param {Function} callback the callback to invoke
- @param {any} binding the value to which the callback's `this` should be bound
- */
- eachRelatedType: function(callback, binding) {
- get(this, 'relatedTypes').forEach(function(type) {
- callback.call(binding, type);
- });
- }
-});
-
-DS.Model.reopen({
- /**
- Given a callback, iterates over each of the relationships in the model,
- invoking the callback with the name of each relationship and its relationship
- descriptor.
-
- @method eachRelationship
- @param {Function} callback the callback to invoke
- @param {any} binding the value to which the callback's `this` should be bound
- */
- eachRelationship: function(callback, binding) {
- this.constructor.eachRelationship(callback, binding);
- }
-});
-
-})();
-
-
-
-(function() {
-/**
- @module ember-data
-*/
-
-})();
-
-
-
-(function() {
-/**
- @module ember-data
-*/
-
-var get = Ember.get, set = Ember.set;
-var once = Ember.run.once;
-var forEach = Ember.EnumerableUtils.forEach;
-
-/**
- @class RecordArrayManager
- @namespace DS
- @private
- @extends Ember.Object
-*/
-DS.RecordArrayManager = Ember.Object.extend({
- init: function() {
- this.filteredRecordArrays = Ember.MapWithDefault.create({
- defaultValue: function() { return []; }
- });
-
- this.changedRecords = [];
- },
-
- recordDidChange: function(record) {
- this.changedRecords.push(record);
- once(this, this.updateRecordArrays);
- },
-
- recordArraysForRecord: function(record) {
- record._recordArrays = record._recordArrays || Ember.OrderedSet.create();
- return record._recordArrays;
- },
-
- /**
- This method is invoked whenever data is loaded into the store by the
- adapter or updated by the adapter, or when a record has changed.
-
- It updates all record arrays that a record belongs to.
-
- To avoid thrashing, it only runs at most once per run loop.
-
- @method updateRecordArrays
- @param {Class} type
- @param {Number|String} clientId
- */
- updateRecordArrays: function() {
- forEach(this.changedRecords, function(record) {
- if (get(record, 'isDeleted')) {
- this._recordWasDeleted(record);
- } else {
- this._recordWasChanged(record);
- }
- }, this);
-
- this.changedRecords = [];
- },
-
- _recordWasDeleted: function (record) {
- var recordArrays = record._recordArrays;
-
- if (!recordArrays) { return; }
-
- forEach(recordArrays, function(array) {
- array.removeRecord(record);
- });
- },
-
- _recordWasChanged: function (record) {
- var type = record.constructor,
- recordArrays = this.filteredRecordArrays.get(type),
- filter;
-
- forEach(recordArrays, function(array) {
- filter = get(array, 'filterFunction');
- this.updateRecordArray(array, filter, type, record);
- }, this);
-
- // loop through all manyArrays containing an unloaded copy of this
- // clientId and notify them that the record was loaded.
- var manyArrays = record._loadingRecordArrays;
-
- if (manyArrays) {
- for (var i=0, l=manyArrays.length; i 'kine'
- inflector.singularize('kine') //=> 'cow'
- ```
-
- Creating an inflector and adding rules later.
-
- ```javascript
- var inflector = Ember.Inflector.inflector;
-
- inflector.pluralize('advice') // => 'advices'
- inflector.uncountable('advice');
- inflector.pluralize('advice') // => 'advice'
-
- inflector.pluralize('formula') // => 'formulas'
- inflector.irregular('formula', 'formulae');
- inflector.pluralize('formula') // => 'formulae'
-
- // you would not need to add these as they are the default rules
- inflector.plural(/$/, 's');
- inflector.singular(/s$/i, '');
- ```
-
- Creating an inflector with a nondefault ruleset.
-
- ```javascript
- var rules = {
- plurals: [ /$/, 's' ],
- singular: [ /\s$/, '' ],
- irregularPairs: [
- [ 'cow', 'kine' ]
- ],
- uncountable: [ 'fish' ]
- };
-
- var inflector = new Ember.Inflector(rules);
- ```
-
- @class Inflector
- @namespace Ember
-*/
-function Inflector(ruleSet) {
- ruleSet = ruleSet || {};
- ruleSet.uncountable = ruleSet.uncountable || {};
- ruleSet.irregularPairs = ruleSet.irregularPairs || {};
-
- var rules = this.rules = {
- plurals: ruleSet.plurals || [],
- singular: ruleSet.singular || [],
- irregular: {},
- irregularInverse: {},
- uncountable: {}
- };
-
- loadUncountable(rules, ruleSet.uncountable);
- loadIrregular(rules, ruleSet.irregularPairs);
-}
-
-Inflector.prototype = {
- /**
- @method plural
- @param {RegExp} regex
- @param {String} string
- */
- plural: function(regex, string) {
- this.rules.plurals.push([regex, string.toLowerCase()]);
- },
-
- /**
- @method singular
- @param {RegExp} regex
- @param {String} string
- */
- singular: function(regex, string) {
- this.rules.singular.push([regex, string.toLowerCase()]);
- },
-
- /**
- @method uncountable
- @param {String} regex
- */
- uncountable: function(string) {
- loadUncountable(this.rules, [string.toLowerCase()]);
- },
-
- /**
- @method irregular
- @param {String} singular
- @param {String} plural
- */
- irregular: function (singular, plural) {
- loadIrregular(this.rules, [[singular, plural]]);
- },
-
- /**
- @method pluralize
- @param {String} word
- */
- pluralize: function(word) {
- return this.inflect(word, this.rules.plurals, this.rules.irregular);
- },
-
- /**
- @method singularize
- @param {String} word
- */
- singularize: function(word) {
- return this.inflect(word, this.rules.singular, this.rules.irregularInverse);
- },
-
- /**
- @protected
-
- @method inflect
- @param {String} word
- @param {Object} typeRules
- @param {Object} irregular
- */
- inflect: function(word, typeRules, irregular) {
- var inflection, substitution, result, lowercase, isBlank,
- isUncountable, isIrregular, isIrregularInverse, rule;
-
- isBlank = BLANK_REGEX.test(word);
-
- if (isBlank) {
- return word;
- }
-
- lowercase = word.toLowerCase();
-
- isUncountable = this.rules.uncountable[lowercase];
-
- if (isUncountable) {
- return word;
- }
-
- isIrregular = irregular && irregular[lowercase];
-
- if (isIrregular) {
- return isIrregular;
- }
-
- for (var i = typeRules.length, min = 0; i > min; i--) {
- inflection = typeRules[i-1];
- rule = inflection[0];
-
- if (rule.test(word)) {
- break;
- }
- }
-
- inflection = inflection || [];
-
- rule = inflection[0];
- substitution = inflection[1];
-
- result = word.replace(rule, substitution);
-
- return result;
- }
-};
-
-Ember.Inflector = Inflector;
-
-})();
-
-
-
-(function() {
-Ember.Inflector.defaultRules = {
- plurals: [
- [/$/, 's'],
- [/s$/i, 's'],
- [/^(ax|test)is$/i, '$1es'],
- [/(octop|vir)us$/i, '$1i'],
- [/(octop|vir)i$/i, '$1i'],
- [/(alias|status)$/i, '$1es'],
- [/(bu)s$/i, '$1ses'],
- [/(buffal|tomat)o$/i, '$1oes'],
- [/([ti])um$/i, '$1a'],
- [/([ti])a$/i, '$1a'],
- [/sis$/i, 'ses'],
- [/(?:([^f])fe|([lr])f)$/i, '$1$2ves'],
- [/(hive)$/i, '$1s'],
- [/([^aeiouy]|qu)y$/i, '$1ies'],
- [/(x|ch|ss|sh)$/i, '$1es'],
- [/(matr|vert|ind)(?:ix|ex)$/i, '$1ices'],
- [/^(m|l)ouse$/i, '$1ice'],
- [/^(m|l)ice$/i, '$1ice'],
- [/^(ox)$/i, '$1en'],
- [/^(oxen)$/i, '$1'],
- [/(quiz)$/i, '$1zes']
- ],
-
- singular: [
- [/s$/i, ''],
- [/(ss)$/i, '$1'],
- [/(n)ews$/i, '$1ews'],
- [/([ti])a$/i, '$1um'],
- [/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '$1sis'],
- [/(^analy)(sis|ses)$/i, '$1sis'],
- [/([^f])ves$/i, '$1fe'],
- [/(hive)s$/i, '$1'],
- [/(tive)s$/i, '$1'],
- [/([lr])ves$/i, '$1f'],
- [/([^aeiouy]|qu)ies$/i, '$1y'],
- [/(s)eries$/i, '$1eries'],
- [/(m)ovies$/i, '$1ovie'],
- [/(x|ch|ss|sh)es$/i, '$1'],
- [/^(m|l)ice$/i, '$1ouse'],
- [/(bus)(es)?$/i, '$1'],
- [/(o)es$/i, '$1'],
- [/(shoe)s$/i, '$1'],
- [/(cris|test)(is|es)$/i, '$1is'],
- [/^(a)x[ie]s$/i, '$1xis'],
- [/(octop|vir)(us|i)$/i, '$1us'],
- [/(alias|status)(es)?$/i, '$1'],
- [/^(ox)en/i, '$1'],
- [/(vert|ind)ices$/i, '$1ex'],
- [/(matr)ices$/i, '$1ix'],
- [/(quiz)zes$/i, '$1'],
- [/(database)s$/i, '$1']
- ],
-
- irregularPairs: [
- ['person', 'people'],
- ['man', 'men'],
- ['child', 'children'],
- ['sex', 'sexes'],
- ['move', 'moves'],
- ['cow', 'kine'],
- ['zombie', 'zombies']
- ],
-
- uncountable: [
- 'equipment',
- 'information',
- 'rice',
- 'money',
- 'species',
- 'series',
- 'fish',
- 'sheep',
- 'jeans',
- 'police'
- ]
-};
-
-})();
-
-
-
-(function() {
-if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) {
- /**
- See {{#crossLink "Ember.String/pluralize"}}{{/crossLink}}
-
- @method pluralize
- @for String
- */
- String.prototype.pluralize = function() {
- return Ember.String.pluralize(this);
- };
-
- /**
- See {{#crossLink "Ember.String/singularize"}}{{/crossLink}}
-
- @method singularize
- @for String
- */
- String.prototype.singularize = function() {
- return Ember.String.singularize(this);
- };
-}
-
-})();
-
-
-
-(function() {
-Ember.Inflector.inflector = new Ember.Inflector(Ember.Inflector.defaultRules);
-
-})();
-
-
-
-(function() {
-
-})();
-
-(function() {
-/**
- @module ember-data
-*/
-
-var get = Ember.get;
-var forEach = Ember.EnumerableUtils.forEach;
-
-DS.ActiveModelSerializer = DS.RESTSerializer.extend({
- // SERIALIZE
-
- /**
- Converts camelcased attributes to underscored when serializing.
-
- @method keyForAttribute
- @param {String} attribute
- @returns String
- */
- keyForAttribute: function(attr) {
- return Ember.String.decamelize(attr);
- },
-
- /**
- Underscores relationship names and appends "_id" or "_ids" when serializing
- relationship keys.
-
- @method keyForRelationship
- @param {String} key
- @param {String} kind
- @returns String
- */
- keyForRelationship: function(key, kind) {
- key = Ember.String.decamelize(key);
- if (kind === "belongsTo") {
- return key + "_id";
- } else if (kind === "hasMany") {
- return Ember.String.singularize(key) + "_ids";
- } else {
- return key;
- }
- },
-
- /**
- Does not serialize hasMany relationships by default.
- */
- serializeHasMany: Ember.K,
-
- /**
- Underscores the JSON root keys when serializing.
-
- @method serializeIntoHash
- @param {Object} hash
- @param {subclass of DS.Model} type
- @param {DS.Model} record
- @param {Object} options
- */
- serializeIntoHash: function(data, type, record, options) {
- var root = Ember.String.decamelize(type.typeKey);
- data[root] = this.serialize(record, options);
- },
-
- /**
- Serializes a polymorphic type as a fully capitalized model name.
-
- @method serializePolymorphicType
- @param {DS.Model} record
- @param {Object} json
- @param relationship
- */
- serializePolymorphicType: function(record, json, relationship) {
- var key = relationship.key,
- belongsTo = get(record, key);
- key = this.keyForAttribute(key);
- json[key + "_type"] = Ember.String.capitalize(belongsTo.constructor.typeKey);
- },
-
- // EXTRACT
-
- /**
- Extracts the model typeKey from underscored root objects.
-
- @method typeForRoot
- @param {String} root
- @returns String the model's typeKey
- */
- typeForRoot: function(root) {
- var camelized = Ember.String.camelize(root);
- return Ember.String.singularize(camelized);
- },
-
- /**
- Add extra step to `DS.RESTSerializer.normalize` so links are
- normalized.
-
- If your payload looks like this
-
- ```js
- {
- "post": {
- "id": 1,
- "title": "Rails is omakase",
- "links": { "flagged_comments": "api/comments/flagged" }
- }
- }
- ```
- The normalized version would look like this
-
- ```js
- {
- "post": {
- "id": 1,
- "title": "Rails is omakase",
- "links": { "flaggedComments": "api/comments/flagged" }
- }
- }
- ```
-
- @method normalize
- @param {subclass of DS.Model} type
- @param {Object} hash
- @param {String} prop
- @returns Object
- */
-
- normalize: function(type, hash, prop) {
- this.normalizeLinks(hash);
-
- return this._super(type, hash, prop);
- },
-
- /**
- Convert `snake_cased` links to `camelCase`
-
- @method normalizeLinks
- @param {Object} hash
- */
-
- normalizeLinks: function(data){
- if (data.links) {
- var links = data.links;
-
- for (var link in links) {
- var camelizedLink = Ember.String.camelize(link);
-
- if (camelizedLink !== link) {
- links[camelizedLink] = links[link];
- delete links[link];
- }
- }
- }
- },
-
- /**
- Normalize the polymorphic type from the JSON.
-
- Normalize:
- ```js
- {
- id: "1"
- minion: { type: "evil_minion", id: "12"}
- }
- ```
-
- To:
- ```js
- {
- id: "1"
- minion: { type: "evilMinion", id: "12"}
- }
- ```
-
- @method normalizeRelationships
- @private
- */
- normalizeRelationships: function(type, hash) {
- var payloadKey, payload;
-
- if (this.keyForRelationship) {
- type.eachRelationship(function(key, relationship) {
- if (relationship.options.polymorphic) {
- payloadKey = this.keyForAttribute(key);
- payload = hash[payloadKey];
- if (payload && payload.type) {
- payload.type = this.typeForRoot(payload.type);
- } else if (payload && relationship.kind === "hasMany") {
- var self = this;
- forEach(payload, function(single) {
- single.type = self.typeForRoot(single.type);
- });
- }
- } else {
- payloadKey = this.keyForRelationship(key, relationship.kind);
- payload = hash[payloadKey];
- }
-
- hash[key] = payload;
-
- if (key !== payloadKey) {
- delete hash[payloadKey];
- }
- }, this);
- }
- }
-});
-
-})();
-
-
-
-(function() {
-var get = Ember.get;
-var forEach = Ember.EnumerableUtils.forEach;
-
-/**
- The EmbeddedRecordsMixin allows you to add embedded record support to your
- serializers.
- To set up embedded records, you include the mixin into the serializer and then
- define your embedded relations.
-
- ```js
- App.PostSerializer = DS.ActiveModelSerializer.extend(DS.EmbeddedRecordsMixin, {
- attrs: {
- comments: {embedded: 'always'}
- }
- })
- ```
-
- Currently only `{embedded: 'always'}` records are supported.
-
- @class EmbeddedRecordsMixin
- @namespace DS
-*/
-DS.EmbeddedRecordsMixin = Ember.Mixin.create({
-
- /**
- Serialize has-may relationship when it is configured as embedded objects.
-
- @method serializeHasMany
- */
- serializeHasMany: function(record, json, relationship) {
- var key = relationship.key,
- attrs = get(this, 'attrs'),
- embed = attrs && attrs[key] && attrs[key].embedded === 'always';
-
- if (embed) {
- json[this.keyForAttribute(key)] = get(record, key).map(function(relation) {
- var data = relation.serialize(),
- primaryKey = get(this, 'primaryKey');
-
- data[primaryKey] = get(relation, primaryKey);
-
- return data;
- }, this);
- }
- },
-
- /**
- Extract embedded objects out of the payload for a single object
- and add them as sideloaded objects instead.
-
- @method extractSingle
- */
- extractSingle: function(store, primaryType, payload, recordId, requestType) {
- var root = this.keyForAttribute(primaryType.typeKey),
- partial = payload[root];
-
- updatePayloadWithEmbedded(store, this, primaryType, partial, payload);
-
- return this._super(store, primaryType, payload, recordId, requestType);
- },
-
- /**
- Extract embedded objects out of a standard payload
- and add them as sideloaded objects instead.
-
- @method extractArray
- */
- extractArray: function(store, type, payload) {
- var root = this.keyForAttribute(type.typeKey),
- partials = payload[Ember.String.pluralize(root)];
-
- forEach(partials, function(partial) {
- updatePayloadWithEmbedded(store, this, type, partial, payload);
- }, this);
-
- return this._super(store, type, payload);
- }
-});
-
-function updatePayloadWithEmbedded(store, serializer, type, partial, payload) {
- var attrs = get(serializer, 'attrs');
-
- if (!attrs) {
- return;
- }
-
- type.eachRelationship(function(key, relationship) {
- var expandedKey, embeddedTypeKey, attribute, ids,
- config = attrs[key],
- serializer = store.serializerFor(relationship.type.typeKey),
- primaryKey = get(serializer, "primaryKey");
-
- if (relationship.kind !== "hasMany") {
- return;
- }
-
- if (config && (config.embedded === 'always' || config.embedded === 'load')) {
- // underscore forces the embedded records to be side loaded.
- // it is needed when main type === relationship.type
- embeddedTypeKey = '_' + Ember.String.pluralize(relationship.type.typeKey);
- expandedKey = this.keyForRelationship(key, relationship.kind);
- attribute = this.keyForAttribute(key);
- ids = [];
-
- if (!partial[attribute]) {
- return;
- }
-
- payload[embeddedTypeKey] = payload[embeddedTypeKey] || [];
-
- forEach(partial[attribute], function(data) {
- var embeddedType = store.modelFor(relationship.type.typeKey);
- updatePayloadWithEmbedded(store, serializer, embeddedType, data, payload);
- ids.push(data[primaryKey]);
- payload[embeddedTypeKey].push(data);
- });
-
- partial[expandedKey] = ids;
- delete partial[attribute];
- }
- }, serializer);
-}
-})();
-
-
-
-(function() {
-/**
- @module ember-data
-*/
-
-var forEach = Ember.EnumerableUtils.forEach;
-
-/**
- The ActiveModelAdapter is a subclass of the RESTAdapter designed to integrate
- with a JSON API that uses an underscored naming convention instead of camelcasing.
- It has been designed to work out of the box with the
- [active_model_serializers](http://github.com/rails-api/active_model_serializers)
- Ruby gem.
-
- This adapter extends the DS.RESTAdapter by making consistent use of the camelization,
- decamelization and pluralization methods to normalize the serialized JSON into a
- format that is compatible with a conventional Rails backend and Ember Data.
-
- ## JSON Structure
-
- The ActiveModelAdapter expects the JSON returned from your server to follow
- the REST adapter conventions substituting underscored keys for camelcased ones.
-
- ### Conventional Names
-
- Attribute names in your JSON payload should be the underscored versions of
- the attributes in your Ember.js models.
-
- For example, if you have a `Person` model:
-
- ```js
- App.FamousPerson = DS.Model.extend({
- firstName: DS.attr('string'),
- lastName: DS.attr('string'),
- occupation: DS.attr('string')
- });
- ```
-
- The JSON returned should look like this:
-
- ```js
- {
- "famous_person": {
- "first_name": "Barack",
- "last_name": "Obama",
- "occupation": "President"
- }
- }
- ```
-
- @class ActiveModelAdapter
- @constructor
- @namespace DS
- @extends DS.Adapter
-**/
-
-DS.ActiveModelAdapter = DS.RESTAdapter.extend({
- defaultSerializer: '_ams',
- /**
- The ActiveModelAdapter overrides the `pathForType` method to build
- underscored URLs by decamelizing and pluralizing the object type name.
-
- ```js
- this.pathForType("famousPerson");
- //=> "famous_people"
- ```
-
- @method pathForType
- @param {String} type
- @returns String
- */
- pathForType: function(type) {
- var decamelized = Ember.String.decamelize(type);
- return Ember.String.pluralize(decamelized);
- },
-
- /**
- The ActiveModelAdapter overrides the `ajaxError` method
- to return a DS.InvalidError for all 422 Unprocessable Entity
- responses.
-
- A 422 HTTP response from the server generally implies that the request
- was well formed but the API was unable to process it because the
- content was not semantically correct or meaningful per the API.
-
- For more information on 422 HTTP Error code see 11.2 WebDAV RFC 4918
- https://tools.ietf.org/html/rfc4918#section-11.2
-
- @method ajaxError
- @param jqXHR
- @returns error
- */
- ajaxError: function(jqXHR) {
- var error = this._super(jqXHR);
-
- if (jqXHR && jqXHR.status === 422) {
- var jsonErrors = Ember.$.parseJSON(jqXHR.responseText)["errors"],
- errors = {};
-
- forEach(Ember.keys(jsonErrors), function(key) {
- errors[Ember.String.camelize(key)] = jsonErrors[key];
- });
-
- return new DS.InvalidError(errors);
- } else {
- return error;
- }
- }
-});
-
-})();
-
-
-
-(function() {
-
-})();
-
-
-
-(function() {
-Ember.onLoad('Ember.Application', function(Application) {
- Application.initializer({
- name: "activeModelAdapter",
-
- initialize: function(container, application) {
- application.register('serializer:_ams', DS.ActiveModelSerializer);
- application.register('adapter:_ams', DS.ActiveModelAdapter);
- }
- });
-});
-
-})();
-
-
-
-(function() {
-
-})();
-
-
-})();
diff --git a/vendor/ember.js b/vendor/ember.js
deleted file mode 100755
index 5814ebf..0000000
--- a/vendor/ember.js
+++ /dev/null
@@ -1,38294 +0,0 @@
-// ==========================================================================
-// Project: Ember - JavaScript Application Framework
-// Copyright: Copyright 2011-2013 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.2.1+pre.ce3a6b7c
-
-(function() {
-/*global __fail__*/
-
-/**
-Ember Debug
-
-@module ember
-@submodule ember-debug
-*/
-
-/**
-@class Ember
-*/
-
-if ('undefined' === typeof Ember) {
- Ember = {};
-
- if ('undefined' !== typeof window) {
- window.Em = window.Ember = Em = Ember;
- }
-}
-
-Ember.ENV = 'undefined' === typeof ENV ? {} : ENV;
-
-if (!('MANDATORY_SETTER' in Ember.ENV)) {
- Ember.ENV.MANDATORY_SETTER = true; // default to true for debug dist
-}
-
-/**
- Define an assertion that will throw an exception if the condition is not
- met. Ember build tools will remove any calls to `Ember.assert()` when
- doing a production build. Example:
-
- ```javascript
- // Test for truthiness
- Ember.assert('Must pass a valid object', obj);
- // Fail unconditionally
- Ember.assert('This code path should never be run')
- ```
-
- @method assert
- @param {String} desc A description of the assertion. This will become
- the text of the Error thrown if the assertion fails.
- @param {Boolean} test Must be truthy for the assertion to pass. If
- falsy, an exception will be thrown.
-*/
-Ember.assert = function(desc, test) {
- if (!test) {
- Ember.Logger.assert(test, desc);
- }
-
- if (Ember.testing && !test) {
- // when testing, ensure test failures when assertions fail
- throw new Ember.Error("Assertion Failed: " + desc);
- }
-};
-
-
-/**
- Display a warning with the provided message. Ember build tools will
- remove any calls to `Ember.warn()` when doing a production build.
-
- @method warn
- @param {String} message A warning to display.
- @param {Boolean} test An optional boolean. If falsy, the warning
- will be displayed.
-*/
-Ember.warn = function(message, test) {
- if (!test) {
- Ember.Logger.warn("WARNING: "+message);
- if ('trace' in Ember.Logger) Ember.Logger.trace();
- }
-};
-
-/**
- Display a debug notice. Ember build tools will remove any calls to
- `Ember.debug()` when doing a production build.
-
- ```javascript
- Ember.debug("I'm a debug notice!");
- ```
-
- @method debug
- @param {String} message A debug message to display.
-*/
-Ember.debug = function(message) {
- Ember.Logger.debug("DEBUG: "+message);
-};
-
-/**
- Display a deprecation warning with the provided message and a stack trace
- (Chrome and Firefox only). Ember build tools will remove any calls to
- `Ember.deprecate()` when doing a production build.
-
- @method deprecate
- @param {String} message A description of the deprecation.
- @param {Boolean} test An optional boolean. If falsy, the deprecation
- will be displayed.
-*/
-Ember.deprecate = function(message, test) {
- if (Ember.TESTING_DEPRECATION) { return; }
-
- if (arguments.length === 1) { test = false; }
- if (test) { return; }
-
- if (Ember.ENV.RAISE_ON_DEPRECATION) { throw new Ember.Error(message); }
-
- var error;
-
- // When using new Error, we can't do the arguments check for Chrome. Alternatives are welcome
- try { __fail__.fail(); } catch (e) { error = e; }
-
- if (Ember.LOG_STACKTRACE_ON_DEPRECATION && error.stack) {
- var stack, stackStr = '';
- if (error['arguments']) {
- // Chrome
- stack = error.stack.replace(/^\s+at\s+/gm, '').
- replace(/^([^\(]+?)([\n$])/gm, '{anonymous}($1)$2').
- replace(/^Object.\s*\(([^\)]+)\)/gm, '{anonymous}($1)').split('\n');
- stack.shift();
- } else {
- // Firefox
- stack = error.stack.replace(/(?:\n@:0)?\s+$/m, '').
- replace(/^\(/gm, '{anonymous}(').split('\n');
- }
-
- stackStr = "\n " + stack.slice(2).join("\n ");
- message = message + stackStr;
- }
-
- Ember.Logger.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.deprecateFunc = function(message, func) {
- return function() {
- Ember.deprecate(message);
- return func.apply(this, arguments);
- };
-};
-
-
-// Inform the developer about the Ember Inspector if not installed.
-if (!Ember.testing) {
- if (typeof window !== 'undefined' && window.chrome && window.addEventListener) {
- window.addEventListener("load", function() {
- if (document.body && document.body.dataset && !document.body.dataset.emberExtension) {
- Ember.debug('For more advanced debugging, install the Ember Inspector from https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi');
- }
- }, false);
- }
-}
-
-})();
-
-// ==========================================================================
-// Project: Ember - JavaScript Application Framework
-// Copyright: Copyright 2011-2013 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.2.1+pre.ce3a6b7c
-
-(function() {
-var define, requireModule;
-
-(function() {
- var registry = {}, seen = {};
-
- define = function(name, deps, callback) {
- registry[name] = { deps: deps, callback: callback };
- };
-
- requireModule = function(name) {
- if (seen[name]) { return seen[name]; }
- seen[name] = {};
-
- var mod, deps, callback, reified, exports;
-
- mod = registry[name];
-
- if (!mod) {
- throw new Error("Module '" + name + "' not found.");
- }
-
- deps = mod.deps;
- callback = mod.callback;
- reified = [];
-
- for (var i=0, l=deps.length; i -1;
-};
-
-// From: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/map
-var arrayMap = isNativeFunc(Array.prototype.map) ? Array.prototype.map : function(fun /*, thisp */) {
- //"use strict";
-
- if (this === void 0 || this === null) {
- throw new TypeError();
- }
-
- var t = Object(this);
- var len = t.length >>> 0;
- if (typeof fun !== "function") {
- throw new TypeError();
- }
-
- var res = new Array(len);
- var thisp = arguments[1];
- for (var i = 0; i < len; i++) {
- if (i in t) {
- res[i] = fun.call(thisp, t[i], i, t);
- }
- }
-
- return res;
-};
-
-// From: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/foreach
-var arrayForEach = isNativeFunc(Array.prototype.forEach) ? Array.prototype.forEach : function(fun /*, thisp */) {
- //"use strict";
-
- if (this === void 0 || this === null) {
- throw new TypeError();
- }
-
- var t = Object(this);
- var len = t.length >>> 0;
- if (typeof fun !== "function") {
- throw new TypeError();
- }
-
- var thisp = arguments[1];
- for (var i = 0; i < len; i++) {
- if (i in t) {
- fun.call(thisp, t[i], i, t);
- }
- }
-};
-
-var arrayIndexOf = isNativeFunc(Array.prototype.indexOf) ? Array.prototype.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;
-};
-
-/**
- Array polyfills to support ES5 features in older browsers.
-
- @namespace Ember
- @property ArrayPolyfills
-*/
-Ember.ArrayPolyfills = {
- map: arrayMap,
- forEach: arrayForEach,
- indexOf: arrayIndexOf
-};
-
-if (Ember.SHIM_ES5) {
- if (!Array.prototype.map) {
- Array.prototype.map = arrayMap;
- }
-
- if (!Array.prototype.forEach) {
- Array.prototype.forEach = arrayForEach;
- }
-
- if (!Array.prototype.indexOf) {
- Array.prototype.indexOf = arrayIndexOf;
- }
-}
-
-})();
-
-
-
-(function() {
-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
-*/
-Ember.Error = function() {
- var tmp = Error.apply(this, arguments);
-
- // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
- for (var idx = 0; idx < errorProps.length; idx++) {
- this[errorProps[idx]] = tmp[errorProps[idx]];
- }
-};
-
-Ember.Error.prototype = Ember.create(Error.prototype);
-
-// ..........................................................
-// ERROR HANDLING
-//
-
-/**
- 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'
- });
- };
- ```
-
- @event onerror
- @for Ember
- @param {Exception} error the error object
-*/
-Ember.onerror = null;
-
-/**
- @private
-
- Wrap code block in a try/catch if `Ember.onerror` is set.
-
- @method handleErrors
- @for Ember
- @param {Function} func
- @param [context]
-*/
-Ember.handleErrors = function(func, context) {
- // Unfortunately in some browsers we lose the backtrace if we rethrow the existing error,
- // so in the event that we don't have an `onerror` handler we don't wrap in a try/catch
- if ('function' === typeof Ember.onerror) {
- try {
- return func.call(context || this);
- } catch (error) {
- Ember.onerror(error);
- }
- } else {
- return func.call(context || this);
- }
-};
-
-})();
-
-
-
-(function() {
-/**
-@module ember-metal
-*/
-
-/**
- @private
-
- Prefix used for guids through out Ember.
-
-*/
-Ember.GUID_PREFIX = 'ember';
-
-
-var o_defineProperty = Ember.platform.defineProperty,
- o_create = Ember.create,
- // Used for guid generation...
- GUID_KEY = '__ember'+ (+ new Date()),
- uuid = 0,
- numberCache = [],
- stringCache = {};
-
-var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER;
-
-/**
- @private
-
- 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.
-
- @property GUID_KEY
- @for Ember
- @type String
- @final
-*/
-Ember.GUID_KEY = GUID_KEY;
-
-var GUID_DESC = {
- writable: false,
- configurable: false,
- enumerable: false,
- value: null
-};
-
-/**
- @private
-
- Generates a new guid, optionally saving the guid to the object that you
- pass in. You will rarely need to use this method. Instead you should
- call `Ember.guidFor(obj)`, which return an existing guid if available.
-
- @method generateGuid
- @for Ember
- @param {Object} [obj] Object the guid will be used for. If passed in, the guid will
- be saved on the object and reused whenever you pass the same object
- again.
-
- If no object is passed, just generate a new guid.
- @param {String} [prefix] Prefix to place in front of the guid. Useful when you want to
- separate the guid into separate namespaces.
- @return {String} the guid
-*/
-Ember.generateGuid = function generateGuid(obj, prefix) {
- if (!prefix) prefix = Ember.GUID_PREFIX;
- var ret = (prefix + (uuid++));
- if (obj) {
- GUID_DESC.value = ret;
- o_defineProperty(obj, GUID_KEY, GUID_DESC);
- }
- return ret;
-};
-
-/**
- @private
-
- 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.
-
- @method guidFor
- @for Ember
- @param {Object} obj any object, string, number, Element, or primitive
- @return {String} the unique guid for this instance.
-*/
-Ember.guidFor = 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 = 'ember'+(uuid++);
- GUID_DESC.value = ret;
- o_defineProperty(obj, GUID_KEY, GUID_DESC);
- return ret;
- }
-};
-
-// ..........................................................
-// META
-//
-
-var META_DESC = {
- writable: true,
- configurable: false,
- enumerable: false,
- value: null
-};
-
-var META_KEY = Ember.GUID_KEY+'_meta';
-
-/**
- The key used to store meta information on object for property observing.
-
- @property META_KEY
- @for Ember
- @private
- @final
- @type String
-*/
-Ember.META_KEY = META_KEY;
-
-// Placeholder for non-writable metas.
-var EMPTY_META = {
- descs: {},
- watching: {}
-};
-
-if (MANDATORY_SETTER) { EMPTY_META.values = {}; }
-
-Ember.EMPTY_META = EMPTY_META;
-
-if (Object.freeze) Object.freeze(EMPTY_META);
-
-var isDefinePropertySimulated = Ember.platform.defineProperty.isSimulated;
-
-function Meta(obj) {
- this.descs = {};
- this.watching = {};
- this.cache = {};
- this.source = obj;
-}
-
-if (isDefinePropertySimulated) {
- // on platforms that don't support enumerable false
- // make meta fail jQuery.isPlainObject() to hide from
- // jQuery.extend() by having a property that fails
- // hasOwnProperty check.
- Meta.prototype.__preventPlainObject__ = true;
-
- // Without non-enumerable properties, meta objects will be output in JSON
- // unless explicitly suppressed
- Meta.prototype.toJSON = function () { };
-}
-
-/**
- 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
-*/
-Ember.meta = function meta(obj, writable) {
-
- var ret = obj[META_KEY];
- if (writable===false) return ret || EMPTY_META;
-
- if (!ret) {
- if (!isDefinePropertySimulated) o_defineProperty(obj, META_KEY, META_DESC);
-
- ret = new Meta(obj);
-
- if (MANDATORY_SETTER) { ret.values = {}; }
-
- obj[META_KEY] = ret;
-
- // make sure we don't accidentally try to create constructor like desc
- ret.descs.constructor = null;
-
- } else if (ret.source !== obj) {
- if (!isDefinePropertySimulated) o_defineProperty(obj, META_KEY, META_DESC);
-
- ret = o_create(ret);
- ret.descs = o_create(ret.descs);
- ret.watching = o_create(ret.watching);
- ret.cache = {};
- ret.source = obj;
-
- if (MANDATORY_SETTER) { ret.values = o_create(ret.values); }
-
- obj[META_KEY] = ret;
- }
- return ret;
-};
-
-Ember.getMeta = function getMeta(obj, property) {
- var meta = Ember.meta(obj, false);
- return meta[property];
-};
-
-Ember.setMeta = function setMeta(obj, property, value) {
- var meta = Ember.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
-*/
-Ember.metaPath = function metaPath(obj, path, writable) {
- Ember.deprecate("Ember.metaPath is deprecated and will be removed from future releases.");
- var meta = Ember.meta(obj, writable), keyName, value;
-
- for (var i=0, l=path.length; i 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;
- },
-
- replace: function(array, idx, amt, objects) {
- if (array.replace) {
- return array.replace(idx, amt, objects);
- } else {
- return utils._replace(array, idx, amt, objects);
- }
- },
-
- intersection: function(array1, array2) {
- var intersection = [];
-
- utils.forEach(array1, function(element) {
- if (utils.indexOf(array2, element) >= 0) {
- intersection.push(element);
- }
- });
-
- return intersection;
- }
-};
-
-})();
-
-
-
-(function() {
-/**
-@module ember-metal
-*/
-
-var META_KEY = Ember.META_KEY, get;
-
-var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER;
-
-var IS_GLOBAL_PATH = /^([A-Z$]|([0-9][A-Z$])).*[\.\*]/;
-var HAS_THIS = /^this[\.\*]/;
-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`.
-*/
-get = function get(obj, keyName) {
- // Helpers that operate with 'this' within an #each
- if (keyName === '') {
- return obj;
- }
-
- if (!keyName && 'string'===typeof obj) {
- keyName = obj;
- obj = null;
- }
-
- Ember.assert("Cannot call get with "+ keyName +" key.", !!keyName);
- Ember.assert("Cannot call get with '"+ keyName +"' on an undefined object.", obj !== undefined);
-
- if (obj === null || keyName.indexOf('.') !== -1) {
- return getPath(obj, keyName);
- }
-
- var meta = obj[META_KEY], desc = meta && meta.descs[keyName], ret;
- if (desc) {
- return desc.get(obj, keyName);
- } else {
- if (MANDATORY_SETTER && 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;
- }
-};
-
-// Currently used only by Ember Data tests
-if (Ember.config.overrideAccessors) {
- Ember.get = get;
- Ember.config.overrideAccessors();
- get = Ember.get;
-}
-
-/**
- @private
-
- Normalizes a target/path pair to reflect that actual target/path that should
- be observed, etc. This takes into account passing in global property
- paths (i.e. a path beginning with a captial letter not defined on the
- target) and * separators.
-
- @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.
-*/
-var normalizeTuple = Ember.normalizeTuple = function(target, path) {
- var hasThis = HAS_THIS.test(path),
- isGlobal = !hasThis && IS_GLOBAL_PATH.test(path),
- key;
-
- if (!target || isGlobal) target = Ember.lookup;
- if (hasThis) path = path.slice(5);
-
- if (target === Ember.lookup) {
- 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.
- if (!path || path.length===0) throw new Ember.Error('Invalid Path');
-
- return [ target, path ];
-};
-
-var getPath = Ember._getPath = function(root, path) {
- var hasThis, parts, tuple, idx, len;
-
- // If there is no root and path is a key name, return that
- // property from the global object.
- // E.g. get('Ember') -> Ember
- if (root === null && path.indexOf('.') === -1) { return get(Ember.lookup, path); }
-
- // detect complicated paths and normalize them
- hasThis = HAS_THIS.test(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;
-};
-
-Ember.getWithDefault = function(root, key, defaultValue) {
- var value = get(root, key);
-
- if (value === undefined) { return defaultValue; }
- return value;
-};
-
-
-Ember.get = get;
-
-})();
-
-
-
-(function() {
-/**
-@module ember-metal
-*/
-
-var o_create = Ember.create,
- metaFor = Ember.meta,
- META_KEY = Ember.META_KEY,
- a_slice = [].slice,
- /* listener flags */
- ONCE = 1, 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;
- for (var i = 0, l = array.length; i < l; i += 3) {
- if (target === array[i] && method === array[i+1]) { index = i; break; }
- }
- return index;
-}
-
-function actionsFor(obj, eventName) {
- var meta = metaFor(obj, true),
- actions;
-
- if (!meta.listeners) { meta.listeners = {}; }
-
- if (!meta.hasOwnProperty('listeners')) {
- // setup inherited copy of the listeners object
- meta.listeners = o_create(meta.listeners);
- }
-
- actions = meta.listeners[eventName];
-
- // if there are actions, but the eventName doesn't exist in our listeners, then copy them from the prototype
- if (actions && !meta.listeners.hasOwnProperty(eventName)) {
- actions = meta.listeners[eventName] = meta.listeners[eventName].slice();
- } else if (!actions) {
- actions = meta.listeners[eventName] = [];
- }
-
- return actions;
-}
-
-function actionsUnion(obj, eventName, otherActions) {
- var meta = obj[META_KEY],
- actions = meta && meta.listeners && meta.listeners[eventName];
-
- if (!actions) { return; }
- for (var i = actions.length - 3; i >= 0; i -= 3) {
- var target = actions[i],
- method = actions[i+1],
- flags = actions[i+2],
- actionIndex = indexOf(otherActions, target, method);
-
- if (actionIndex === -1) {
- otherActions.push(target, method, flags);
- }
- }
-}
-
-function actionsDiff(obj, eventName, otherActions) {
- var meta = obj[META_KEY],
- actions = meta && meta.listeners && meta.listeners[eventName],
- diffActions = [];
-
- if (!actions) { return; }
- for (var i = actions.length - 3; i >= 0; i -= 3) {
- var target = actions[i],
- method = actions[i+1],
- flags = actions[i+2],
- actionIndex = indexOf(otherActions, target, method);
-
- if (actionIndex !== -1) { continue; }
-
- otherActions.push(target, method, flags);
- diffActions.push(target, method, flags);
- }
-
- return diffActions;
-}
-
-/**
- Add an event listener
-
- @method addListener
- @for Ember
- @param obj
- @param {String} eventName
- @param {Object|Function} targetOrMethod A target object or a function
- @param {Function|String} method A function or the name of a function to be called on `target`
- @param {Boolean} once A flag whether a function should only be called once
-*/
-function addListener(obj, eventName, target, method, once) {
- Ember.assert("You must pass at least an object and event name to Ember.addListener", !!obj && !!eventName);
-
- if (!method && 'function' === typeof target) {
- method = target;
- target = null;
- }
-
- var actions = actionsFor(obj, eventName),
- actionIndex = indexOf(actions, target, method),
- 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} targetOrMethod A target object or a function
- @param {Function|String} method A function or the name of a function to be called on `target`
-*/
-function removeListener(obj, eventName, target, method) {
- Ember.assert("You must pass at least an object and event name to Ember.removeListener", !!obj && !!eventName);
-
- if (!method && 'function' === typeof target) {
- method = target;
- target = null;
- }
-
- function _removeListener(target, method) {
- var actions = actionsFor(obj, eventName),
- 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[META_KEY],
- 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]);
- }
- }
-}
-
-/**
- @private
-
- 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
- @param obj
- @param {String} eventName
- @param {Object|Function} targetOrMethod A target object or a function
- @param {Function|String} method A function or the name of a function to be called on `target`
- @param {Function} callback
-*/
-function suspendListener(obj, eventName, target, method, callback) {
- if (!method && 'function' === typeof target) {
- method = target;
- target = null;
- }
-
- var actions = actionsFor(obj, eventName),
- 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 Ember.tryFinally(tryable, finalizer);
-}
-
-/**
- @private
-
- Suspends multiple listeners during a callback.
-
-
- @method suspendListeners
- @for Ember
- @param obj
- @param {Array} eventName Array of event names
- @param {Object|Function} targetOrMethod A target object or a function
- @param {Function|String} method A function or the name of a function to be called on `target`
- @param {Function} callback
-*/
-function suspendListeners(obj, eventNames, target, method, callback) {
- if (!method && 'function' === typeof target) {
- method = target;
- target = null;
- }
-
- var suspendedActions = [],
- actionsList = [],
- eventName, actions, i, l;
-
- for (i=0, l=eventNames.length; i= 0; i -= 3) { // looping in reverse for once listeners
- var target = actions[i], method = actions[i+1], flags = actions[i+2];
- if (!method) { continue; }
- if (flags & SUSPENDED) { continue; }
- if (flags & ONCE) { removeListener(obj, eventName, target, method); }
- if (!target) { target = obj; }
- if ('string' === typeof method) { method = target[method]; }
- if (params) {
- method.apply(target, params);
- } else {
- method.call(target);
- }
- }
- return true;
-}
-
-/**
- @private
- @method hasListeners
- @for Ember
- @param obj
- @param {String} eventName
-*/
-function hasListeners(obj, eventName) {
- var meta = obj[META_KEY],
- actions = meta && meta.listeners && meta.listeners[eventName];
-
- return !!(actions && actions.length);
-}
-
-/**
- @private
- @method listenersFor
- @for Ember
- @param obj
- @param {String} eventName
-*/
-function listenersFor(obj, eventName) {
- var ret = [];
- var meta = obj[META_KEY],
- actions = meta && meta.listeners && meta.listeners[eventName];
-
- if (!actions) { return ret; }
-
- for (var i = 0, l = actions.length; i < l; i += 3) {
- var target = actions[i],
- 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.
-
- 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
-*/
-Ember.on = function(){
- var func = a_slice.call(arguments, -1)[0],
- events = a_slice.call(arguments, 0, -1);
- func.__ember_listens__ = events;
- return func;
-};
-
-Ember.addListener = addListener;
-Ember.removeListener = removeListener;
-Ember._suspendListener = suspendListener;
-Ember._suspendListeners = suspendListeners;
-Ember.sendEvent = sendEvent;
-Ember.hasListeners = hasListeners;
-Ember.watchedEvents = watchedEvents;
-Ember.listenersFor = listenersFor;
-Ember.listenersDiff = actionsDiff;
-Ember.listenersUnion = actionsUnion;
-
-})();
-
-
-
-(function() {
-var guidFor = Ember.guidFor,
- sendEvent = Ember.sendEvent;
-
-/*
- this.observerSet = {
- [senderGuid]: { // variable name: `keySet`
- [keyName]: listIndex
- }
- },
- this.observers = [
- {
- sender: obj,
- keyName: keyName,
- eventName: eventName,
- listeners: [
- [target, method, flags]
- ]
- },
- ...
- ]
-*/
-var ObserverSet = Ember._ObserverSet = function() {
- this.clear();
-};
-
-ObserverSet.prototype.add = function(sender, keyName, eventName) {
- var observerSet = this.observerSet,
- observers = this.observers,
- senderGuid = guidFor(sender),
- keySet = observerSet[senderGuid],
- 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, i, len, observer, sender;
- this.clear();
- for (i=0, len=observers.length; i < len; ++i) {
- observer = observers[i];
- sender = observer.sender;
- if (sender.isDestroying || sender.isDestroyed) { continue; }
- sendEvent(sender, observer.eventName, [sender, observer.keyName], observer.listeners);
- }
-};
-
-ObserverSet.prototype.clear = function() {
- this.observerSet = {};
- this.observers = [];
-};
-})();
-
-
-
-(function() {
-var metaFor = Ember.meta,
- guidFor = Ember.guidFor,
- tryFinally = Ember.tryFinally,
- sendEvent = Ember.sendEvent,
- listenersUnion = Ember.listenersUnion,
- listenersDiff = Ember.listenersDiff,
- ObserverSet = Ember._ObserverSet,
- beforeObserverSet = new ObserverSet(),
- observerSet = new ObserverSet(),
- 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 = metaFor(obj, false),
- watching = m.watching[keyName] > 0 || keyName === 'length',
- proto = m.proto,
- desc = m.descs[keyName];
-
- if (!watching) { return; }
- if (proto === obj) { return; }
- if (desc && desc.willChange) { desc.willChange(obj, keyName); }
- dependentKeysWillChange(obj, keyName, m);
- chainsWillChange(obj, keyName, m);
- notifyBeforeObservers(obj, keyName);
-}
-Ember.propertyWillChange = propertyWillChange;
-
-/**
- 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 = metaFor(obj, false),
- watching = m.watching[keyName] > 0 || keyName === 'length',
- proto = m.proto,
- desc = m.descs[keyName];
-
- 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; }
-
- dependentKeysDidChange(obj, keyName, m);
- chainsDidChange(obj, keyName, m, false);
- notifyObservers(obj, keyName);
-}
-Ember.propertyDidChange = propertyDidChange;
-
-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 seen = WILL_SEEN, top = !seen;
- if (top) { seen = WILL_SEEN = {}; }
- iterDeps(propertyWillChange, obj, 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 seen = DID_SEEN, top = !seen;
- if (top) { seen = DID_SEEN = {}; }
- iterDeps(propertyDidChange, obj, depKey, seen, meta);
- if (top) { DID_SEEN = null; }
-}
-
-function iterDeps(method, obj, depKey, seen, meta) {
- var guid = guidFor(obj);
- if (!seen[guid]) seen[guid] = {};
- if (seen[guid][depKey]) return;
- seen[guid][depKey] = true;
-
- var deps = meta.deps;
- deps = deps && deps[depKey];
- if (deps) {
- for(var key in deps) {
- var desc = meta.descs[key];
- if (desc && desc._suspended === obj) continue;
- method(obj, key);
- }
- }
-}
-
-function chainsWillChange(obj, keyName, m) {
- if (!(m.hasOwnProperty('chainWatchers') &&
- m.chainWatchers[keyName])) {
- return;
- }
-
- var nodes = m.chainWatchers[keyName],
- events = [],
- i, l;
-
- for(i = 0, l = nodes.length; i < l; i++) {
- nodes[i].willChange(events);
- }
-
- for (i = 0, l = events.length; i < l; i += 2) {
- propertyWillChange(events[i], events[i+1]);
- }
-}
-
-function chainsDidChange(obj, keyName, m, suppressEvents) {
- if (!(m.hasOwnProperty('chainWatchers') &&
- m.chainWatchers[keyName])) {
- return;
- }
-
- var nodes = m.chainWatchers[keyName],
- events = suppressEvents ? null : [],
- 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]);
- }
-}
-
-Ember.overrideChains = function(obj, keyName, m) {
- chainsDidChange(obj, keyName, m, true);
-};
-
-/**
- @method beginPropertyChanges
- @chainable
- @private
-*/
-function beginPropertyChanges() {
- deferred++;
-}
-
-Ember.beginPropertyChanges = beginPropertyChanges;
-
-/**
- @method endPropertyChanges
- @private
-*/
-function endPropertyChanges() {
- deferred--;
- if (deferred<=0) {
- beforeObserverSet.clear();
- observerSet.flush();
- }
-}
-
-Ember.endPropertyChanges = endPropertyChanges;
-
-/**
- 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]
-*/
-Ember.changeProperties = function(cb, binding) {
- beginPropertyChanges();
- tryFinally(cb, endPropertyChanges, binding);
-};
-
-function notifyBeforeObservers(obj, keyName) {
- if (obj.isDestroying) { return; }
-
- var eventName = keyName + ':before', listeners, diff;
- if (deferred) {
- listeners = beforeObserverSet.add(obj, keyName, eventName);
- diff = listenersDiff(obj, eventName, listeners);
- sendEvent(obj, eventName, [obj, keyName], diff);
- } else {
- sendEvent(obj, eventName, [obj, keyName]);
- }
-}
-
-function notifyObservers(obj, keyName) {
- if (obj.isDestroying) { return; }
-
- var eventName = keyName + ':change', listeners;
- if (deferred) {
- listeners = observerSet.add(obj, keyName, eventName);
- listenersUnion(obj, eventName, listeners);
- } else {
- sendEvent(obj, eventName, [obj, keyName]);
- }
-}
-
-})();
-
-
-
-(function() {
-// META_KEY
-// _getPath
-// propertyWillChange, propertyDidChange
-
-var META_KEY = Ember.META_KEY,
- MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER,
- IS_GLOBAL = /^([A-Z$]|([0-9][A-Z$]))/,
- getPath = Ember._getPath;
-
-/**
- Sets the value of a property on an object, respecting computed properties
- and notifying observers and other listeners of the change. If the
- property is not defined but the object implements the `setUnknownProperty`
- method then that will be invoked as well.
-
- If you plan to run on IE8 and older browsers then you should use this
- method anytime you want to set a property on an object that you don't
- know for sure is private. (Properties beginning with an underscore '_'
- are considered private.)
-
- On all newer browsers, you only need to use this method to set
- properties if the property might not be defined on the object and you want
- to respect the `setUnknownProperty` handler. Otherwise you can ignore this
- method.
-
- @method set
- @for Ember
- @param {Object} obj The object to modify.
- @param {String} keyName The property key to set
- @param {Object} value The value to set
- @return {Object} the passed value.
-*/
-var set = function set(obj, keyName, value, tolerant) {
- if (typeof obj === 'string') {
- Ember.assert("Path '" + obj + "' must be global if no obj is given.", IS_GLOBAL.test(obj));
- value = keyName;
- keyName = obj;
- obj = null;
- }
-
- Ember.assert("Cannot call set with "+ keyName +" key.", !!keyName);
-
- if (!obj || keyName.indexOf('.') !== -1) {
- return setPath(obj, keyName, value, tolerant);
- }
-
- Ember.assert("You need to provide an object and key to `set`.", !!obj && keyName !== undefined);
- Ember.assert('calling set on destroyed object', !obj.isDestroyed);
-
- var meta = obj[META_KEY], desc = meta && meta.descs[keyName],
- isUnknown, currentValue;
- if (desc) {
- desc.set(obj, keyName, value);
- } else {
- 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 (MANDATORY_SETTER) {
- currentValue = meta.values[keyName];
- } else {
- currentValue = obj[keyName];
- }
- // only trigger a change if the value has changed
- if (value !== currentValue) {
- Ember.propertyWillChange(obj, keyName);
- if (MANDATORY_SETTER) {
- if (currentValue === undefined && !(keyName in obj)) {
- Ember.defineProperty(obj, keyName, null, value); // setup mandatory setter
- } else {
- meta.values[keyName] = value;
- }
- } else {
- obj[keyName] = value;
- }
- Ember.propertyDidChange(obj, keyName);
- }
- } else {
- obj[keyName] = value;
- }
- }
- return value;
-};
-
-// Currently used only by Ember Data tests
-if (Ember.config.overrideAccessors) {
- Ember.set = set;
- Ember.config.overrideAccessors();
- set = Ember.set;
-}
-
-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.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 = getPath(root, path);
- }
-
- if (!keyName || keyName.length === 0) {
- throw new Ember.Error('You passed an empty path');
- }
-
- if (!root) {
- if (tolerant) { return; }
- else { throw new Ember.Error('Object in path '+path+' could not be found or was destroyed.'); }
- }
-
- return set(root, keyName, value);
-}
-
-Ember.set = set;
-
-/**
- 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
-*/
-Ember.trySet = function(root, path, value) {
- return set(root, path, value, true);
-};
-
-})();
-
-
-
-(function() {
-/**
-@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 two exceptions. First, because we need our implementation to be pleasant
- on older browsers, we do not use the `delete` name (using `remove` instead).
- Second, as we do not have the luxury of in-VM iteration, we implement a
- forEach method for iteration.
-
- Map is mocked out to look like an Ember object, so you can do
- `Ember.Map.create()` for symmetry with other Ember classes.
-*/
-var set = Ember.set,
- guidFor = Ember.guidFor,
- indexOf = Ember.ArrayPolyfills.indexOf;
-
-var copy = function(obj) {
- var output = {};
-
- for (var prop in obj) {
- if (obj.hasOwnProperty(prop)) { output[prop] = obj[prop]; }
- }
-
- return output;
-};
-
-var copyMap = function(original, newObject) {
- var keys = original.keys.copy(),
- values = copy(original.values);
-
- newObject.keys = keys;
- newObject.values = values;
- newObject.length = original.length;
-
- 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
-*/
-var OrderedSet = Ember.OrderedSet = function() {
- this.clear();
-};
-
-/**
- @method create
- @static
- @return {Ember.OrderedSet}
-*/
-OrderedSet.create = function() {
- return new OrderedSet();
-};
-
-
-OrderedSet.prototype = {
- /**
- @method clear
- */
- clear: function() {
- this.presenceSet = {};
- this.list = [];
- },
-
- /**
- @method add
- @param obj
- */
- add: function(obj) {
- var guid = guidFor(obj),
- presenceSet = this.presenceSet,
- list = this.list;
-
- if (guid in presenceSet) { return; }
-
- presenceSet[guid] = true;
- list.push(obj);
- },
-
- /**
- @method remove
- @param obj
- */
- remove: function(obj) {
- var guid = guidFor(obj),
- presenceSet = this.presenceSet,
- list = this.list;
-
- delete presenceSet[guid];
-
- var index = indexOf.call(list, obj);
- if (index > -1) {
- list.splice(index, 1);
- }
- },
-
- /**
- @method isEmpty
- @return {Boolean}
- */
- isEmpty: function() {
- return this.list.length === 0;
- },
-
- /**
- @method has
- @param obj
- @return {Boolean}
- */
- has: function(obj) {
- var guid = guidFor(obj),
- presenceSet = this.presenceSet;
-
- return guid in presenceSet;
- },
-
- /**
- @method forEach
- @param {Function} fn
- @param self
- */
- forEach: function(fn, self) {
- // allow mutation during iteration
- var list = this.toArray();
-
- for (var i = 0, j = list.length; i < j; i++) {
- fn.call(self, list[i]);
- }
- },
-
- /**
- @method toArray
- @return {Array}
- */
- toArray: function() {
- return this.list.slice();
- },
-
- /**
- @method copy
- @return {Ember.OrderedSet}
- */
- copy: function() {
- var set = new OrderedSet();
-
- set.presenceSet = copy(this.presenceSet);
- set.list = this.toArray();
-
- return set;
- }
-};
-
-/**
- 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
-*/
-var Map = Ember.Map = function() {
- this.keys = Ember.OrderedSet.create();
- this.values = {};
-};
-
-/**
- @method create
- @static
-*/
-Map.create = function() {
- return new Map();
-};
-
-Map.prototype = {
- /**
- This property will change as the number of objects in the map changes.
-
- @property length
- @type number
- @default 0
- */
- length: 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) {
- var values = this.values,
- guid = 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
- */
- set: function(key, value) {
- var keys = this.keys,
- values = this.values,
- guid = guidFor(key);
-
- keys.add(key);
- values[guid] = value;
- set(this, 'length', keys.list.length);
- },
-
- /**
- 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) {
- // don't use ES6 "delete" because it will be annoying
- // to use in browsers that are not ES6 friendly;
- var keys = this.keys,
- values = this.values,
- guid = guidFor(key);
-
- if (values.hasOwnProperty(guid)) {
- keys.remove(key);
- delete values[guid];
- set(this, 'length', keys.list.length);
- 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) {
- var values = this.values,
- guid = guidFor(key);
-
- return values.hasOwnProperty(guid);
- },
-
- /**
- Iterate over all the keys and values. Calls the function once
- for each key, passing in the key and value, 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, self) {
- var keys = this.keys,
- values = this.values;
-
- keys.forEach(function(key) {
- var guid = guidFor(key);
- callback.call(self, key, values[guid]);
- });
- },
-
- /**
- @method copy
- @return {Ember.Map}
- */
- copy: function() {
- return copyMap(this, new Map());
- }
-};
-
-/**
- @class MapWithDefault
- @namespace Ember
- @extends Ember.Map
- @private
- @constructor
- @param [options]
- @param {*} [options.defaultValue]
-*/
-var MapWithDefault = Ember.MapWithDefault = function(options) {
- Map.call(this);
- 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 = Ember.create(Map.prototype);
-
-/**
- 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 Map.prototype.get.call(this, key);
- } else {
- var defaultValue = this.defaultValue(key);
- this.set(key, defaultValue);
- return defaultValue;
- }
-};
-
-/**
- @method copy
- @return {Ember.MapWithDefault}
-*/
-MapWithDefault.prototype.copy = function() {
- return copyMap(this, new MapWithDefault({
- defaultValue: this.defaultValue
- }));
-};
-
-})();
-
-
-
-(function() {
-function consoleMethod(name) {
- var consoleObj;
- if (Ember.imports.console) {
- consoleObj = Ember.imports.console;
- } else if (typeof console !== 'undefined') {
- consoleObj = console;
- }
-
- var method = typeof consoleObj === 'object' ? consoleObj[name] : null;
-
- if (method) {
- // Older IE doesn't support apply, but Chrome needs it
- if (method.apply) {
- return function() {
- method.apply(consoleObj, arguments);
- };
- } 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 Ember.Error("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
-*/
-Ember.Logger = {
- /**
- 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') || Ember.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') || Ember.K,
- /**
- Prints the arguments to the console with an error icon, red text and a stack race.
- 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') || Ember.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') || Ember.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') || Ember.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
-};
-
-
-})();
-
-
-
-(function() {
-/**
-@module ember-metal
-*/
-
-var META_KEY = Ember.META_KEY,
- metaFor = Ember.meta,
- objectDefineProperty = Ember.platform.defineProperty;
-
-var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER;
-
-// ..........................................................
-// DESCRIPTOR
-//
-
-/**
- Objects of this type can implement an interface to respond to requests to
- get and set. The default implementation handles simple properties.
-
- You generally won't need to create or subclass this directly.
-
- @class Descriptor
- @namespace Ember
- @private
- @constructor
-*/
-Ember.Descriptor = function() {};
-
-// ..........................................................
-// DEFINING PROPERTIES API
-//
-
-var MANDATORY_SETTER_FUNCTION = Ember.MANDATORY_SETTER_FUNCTION = function(value) {
- Ember.assert("You must use Ember.set() to access this property (of " + this + ")", false);
-};
-
-var DEFAULT_GETTER_FUNCTION = Ember.DEFAULT_GETTER_FUNCTION = function(name) {
- return function() {
- var meta = this[META_KEY];
- return meta && meta.values[name];
- };
-};
-
-/**
- @private
-
- 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 `Ember.Descriptor` as the third param then you can pass an
- optional value as the fourth parameter. This is often more efficient than
- creating new descriptor hashes for each property.
-
- ## 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'));
- ```
-
- @method defineProperty
- @for Ember
- @param {Object} obj the object to define this property on. This may be a prototype.
- @param {String} keyName the name of the property
- @param {Ember.Descriptor} [desc] an instance of `Ember.Descriptor` (typically a
- computed property) or an ES5 descriptor.
- You must provide this or `data` but not both.
- @param {*} [data] something other than a descriptor, that will
- become the explicit value of this property.
-*/
-Ember.defineProperty = function(obj, keyName, desc, data, meta) {
- var descs, existingDesc, watching, value;
-
- if (!meta) meta = metaFor(obj);
- descs = meta.descs;
- existingDesc = meta.descs[keyName];
- watching = meta.watching[keyName] > 0;
-
- if (existingDesc instanceof Ember.Descriptor) {
- existingDesc.teardown(obj, keyName);
- }
-
- if (desc instanceof Ember.Descriptor) {
- value = desc;
-
- descs[keyName] = desc;
- if (MANDATORY_SETTER && watching) {
- objectDefineProperty(obj, keyName, {
- configurable: true,
- enumerable: true,
- writable: true,
- value: undefined // make enumerable
- });
- } else {
- obj[keyName] = undefined; // make enumerable
- }
- } else {
- descs[keyName] = undefined; // shadow descriptor in proto
- if (desc == null) {
- value = data;
-
- if (MANDATORY_SETTER && watching) {
- meta.values[keyName] = data;
- objectDefineProperty(obj, keyName, {
- configurable: true,
- enumerable: true,
- set: MANDATORY_SETTER_FUNCTION,
- get: DEFAULT_GETTER_FUNCTION(keyName)
- });
- } else {
- obj[keyName] = data;
- }
- } else {
- value = desc;
-
- // compatibility with ES5
- objectDefineProperty(obj, keyName, desc);
- }
- }
-
- // if key is being watched, override chains that
- // were initialized with the prototype
- if (watching) { Ember.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;
-};
-
-
-})();
-
-
-
-(function() {
-var get = Ember.get;
-
-/**
- To get multiple properties at once, call `Ember.getProperties`
- with an object followed by a list of strings or an array:
-
- ```javascript
- Ember.getProperties(record, 'firstName', 'lastName', 'zipCode'); // { firstName: 'John', lastName: 'Doe', zipCode: '10011' }
- ```
-
- is equivalent to:
-
- ```javascript
- Ember.getProperties(record, ['firstName', 'lastName', 'zipCode']); // { firstName: 'John', lastName: 'Doe', zipCode: '10011' }
- ```
-
- @method getProperties
- @param obj
- @param {String...|Array} list of keys to get
- @return {Hash}
-*/
-Ember.getProperties = function(obj) {
- var ret = {},
- propertyNames = arguments,
- i = 1;
-
- if (arguments.length === 2 && Ember.typeOf(arguments[1]) === 'array') {
- i = 0;
- propertyNames = arguments[1];
- }
- for(var len = propertyNames.length; i < len; i++) {
- ret[propertyNames[i]] = get(obj, propertyNames[i]);
- }
- return ret;
-};
-
-})();
-
-
-
-(function() {
-var changeProperties = Ember.changeProperties,
- set = Ember.set;
-
-/**
- Set a list of properties on an object. These properties are set inside
- a single `beginPropertyChanges` and `endPropertyChanges` batch, so
- observers will be buffered.
-
- ```javascript
- anObject.setProperties({
- firstName: "Stanley",
- lastName: "Stuart",
- age: "21"
- })
- ```
-
- @method setProperties
- @param self
- @param {Object} hash
- @return self
-*/
-Ember.setProperties = function(self, hash) {
- changeProperties(function() {
- for(var prop in hash) {
- if (hash.hasOwnProperty(prop)) { set(self, prop, hash[prop]); }
- }
- });
- return self;
-};
-
-})();
-
-
-
-(function() {
-var metaFor = Ember.meta, // utils.js
- typeOf = Ember.typeOf, // utils.js
- MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER,
- o_defineProperty = Ember.platform.defineProperty;
-
-Ember.watchKey = function(obj, keyName) {
- // can't watch length on Array - it is special...
- if (keyName === 'length' && typeOf(obj) === 'array') { return; }
-
- var m = metaFor(obj), watching = m.watching;
-
- // activate watching first time
- if (!watching[keyName]) {
- watching[keyName] = 1;
-
- if ('function' === typeof obj.willWatchProperty) {
- obj.willWatchProperty(keyName);
- }
-
- if (MANDATORY_SETTER && keyName in obj) {
- m.values[keyName] = obj[keyName];
- o_defineProperty(obj, keyName, {
- configurable: true,
- enumerable: true,
- set: Ember.MANDATORY_SETTER_FUNCTION,
- get: Ember.DEFAULT_GETTER_FUNCTION(keyName)
- });
- }
- } else {
- watching[keyName] = (watching[keyName] || 0) + 1;
- }
-};
-
-
-Ember.unwatchKey = function(obj, keyName) {
- var m = metaFor(obj), watching = m.watching;
-
- if (watching[keyName] === 1) {
- watching[keyName] = 0;
-
- if ('function' === typeof obj.didUnwatchProperty) {
- obj.didUnwatchProperty(keyName);
- }
-
- if (MANDATORY_SETTER && keyName in obj) {
- o_defineProperty(obj, keyName, {
- configurable: true,
- enumerable: true,
- writable: true,
- value: m.values[keyName]
- });
- delete m.values[keyName];
- }
- } else if (watching[keyName] > 1) {
- watching[keyName]--;
- }
-};
-
-})();
-
-
-
-(function() {
-var metaFor = Ember.meta, // utils.js
- get = Ember.get, // property_get.js
- normalizeTuple = Ember.normalizeTuple, // property_get.js
- forEach = Ember.ArrayPolyfills.forEach, // array.js
- warn = Ember.warn,
- watchKey = Ember.watchKey,
- unwatchKey = Ember.unwatchKey,
- FIRST_KEY = /^([^\.\*]+)/;
-
-function firstKey(path) {
- return path.match(FIRST_KEY)[0];
-}
-
-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.
-Ember.flushPendingChains = function() {
- if (pendingQueue.length === 0) { return; } // nothing to do
-
- var queue = pendingQueue;
- pendingQueue = [];
-
- 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 (!obj || ('object' !== typeof obj)) { return; } // nothing to do
-
- var m = metaFor(obj), nodes = m.chainWatchers;
-
- if (!m.hasOwnProperty('chainWatchers')) {
- nodes = m.chainWatchers = {};
- }
-
- if (!nodes[keyName]) { nodes[keyName] = []; }
- nodes[keyName].push(node);
- watchKey(obj, keyName);
-}
-
-var removeChainWatcher = Ember.removeChainWatcher = function(obj, keyName, node) {
- if (!obj || 'object' !== typeof obj) { return; } // nothing to do
-
- var m = metaFor(obj, false);
- if (!m.hasOwnProperty('chainWatchers')) { return; } // nothing to do
-
- var nodes = m.chainWatchers;
-
- if (nodes[keyName]) {
- nodes = nodes[keyName];
- for (var i = 0, l = nodes.length; i < l; i++) {
- if (nodes[i] === node) { nodes.splice(i, 1); }
- }
- }
- unwatchKey(obj, keyName);
-};
-
-// 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.
-var ChainNode = Ember._ChainNode = function(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();
- }
-};
-
-var ChainNodePrototype = ChainNode.prototype;
-
-function lazyGet(obj, key) {
- if (!obj) return undefined;
-
- var meta = metaFor(obj, false);
- // check if object meant only to be a prototype
- if (meta.proto === obj) return undefined;
-
- if (key === "@each") return get(obj, key);
-
- // if a CP only return cached value
- var desc = meta.descs[key];
- if (desc && desc._cacheable) {
- if (key in meta.cache) {
- return meta.cache[key];
- } else {
- return undefined;
- }
- }
-
- return get(obj, key);
-}
-
-ChainNodePrototype.value = function() {
- if (this._value === undefined && this._watching) {
- var obj = this._parent.value();
- this._value = lazyGet(obj, this._key);
- }
- return this._value;
-};
-
-ChainNodePrototype.destroy = function() {
- if (this._watching) {
- var obj = this._object;
- if (obj) { removeChainWatcher(obj, this._key, this); }
- this._watching = false; // so future calls do nothing
- }
-};
-
-// copies a top level object only
-ChainNodePrototype.copy = function(obj) {
- var ret = new ChainNode(null, null, obj),
- paths = this._paths, path;
- for (path in paths) {
- if (paths[path] <= 0) { continue; } // this check will also catch non-number vals.
- ret.add(path);
- }
- return ret;
-};
-
-// called on the root node of a chain to setup watchers on the specified
-// path.
-ChainNodePrototype.add = function(path) {
- var obj, tuple, key, src, paths;
-
- paths = this._paths;
- paths[path] = (paths[path] || 0) + 1;
-
- obj = this.value();
- tuple = 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
-ChainNodePrototype.remove = function(path) {
- var obj, tuple, key, src, paths;
-
- paths = this._paths;
- if (paths[path] > 0) { paths[path]--; }
-
- obj = this.value();
- tuple = normalizeTuple(obj, path);
- if (tuple[0] === obj) {
- path = tuple[1];
- key = firstKey(path);
- path = path.slice(key.length+1);
- } else {
- src = tuple[0];
- key = path.slice(0, 0-(tuple[1].length+1));
- path = tuple[1];
- }
-
- tuple.length = 0;
- this.unchain(key, path);
-};
-
-ChainNodePrototype.count = 0;
-
-ChainNodePrototype.chain = function(key, path, src) {
- var chains = this._chains, 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 && path.length>0) {
- key = firstKey(path);
- path = path.slice(key.length+1);
- node.chain(key, path); // NOTE: no src means it will observe changes...
- }
-};
-
-ChainNodePrototype.unchain = function(key, path) {
- var chains = this._chains, node = chains[key];
-
- // unchain rest of path first...
- if (path && path.length>1) {
- key = firstKey(path);
- path = path.slice(key.length+1);
- node.unchain(key, path);
- }
-
- // delete node if needed.
- node.count--;
- if (node.count<=0) {
- delete chains[node._key];
- node.destroy();
- }
-
-};
-
-ChainNodePrototype.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); }
-};
-
-ChainNodePrototype.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);
- }
- }
-};
-
-ChainNodePrototype.chainDidChange = function(chain, path, depth, events) {
- if (this._key) { path = this._key + '.' + path; }
- if (this._parent) {
- this._parent.chainDidChange(this, path, depth+1, events);
- } else {
- if (depth > 1) {
- events.push(this.value(), path);
- }
- path = 'this.' + path;
- if (this._paths[path] > 0) {
- events.push(this.value(), path);
- }
- }
-};
-
-ChainNodePrototype.didChange = function(events) {
- // invalidate my own value first.
- if (this._watching) {
- var obj = this._parent.value();
- if (obj !== this._object) {
- removeChainWatcher(this._object, this._key, this);
- this._object = obj;
- addChainWatcher(obj, this._key, this);
- }
- this._value = undefined;
-
- // 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); }
-};
-
-Ember.finishChains = function(obj) {
- var m = metaFor(obj, false), chains = m.chains;
- if (chains) {
- if (chains.value() !== obj) {
- m.chains = chains = chains.copy(obj);
- }
- chains.didChange(null);
- }
-};
-
-})();
-
-
-
-(function() {
-var metaFor = Ember.meta, // utils.js
- typeOf = Ember.typeOf, // utils.js
- ChainNode = Ember._ChainNode; // chains.js
-
-// get the chains for the current object. If the current object has
-// chains inherited from the proto they will be cloned and reconfigured for
-// the current object.
-function chainsFor(obj) {
- var m = metaFor(obj), ret = m.chains;
- if (!ret) {
- ret = m.chains = new ChainNode(null, null, obj);
- } else if (ret.value() !== obj) {
- ret = m.chains = ret.copy(obj);
- }
- return ret;
-}
-
-Ember.watchPath = function(obj, keyPath) {
- // can't watch length on Array - it is special...
- if (keyPath === 'length' && typeOf(obj) === 'array') { return; }
-
- var m = metaFor(obj), watching = m.watching;
-
- if (!watching[keyPath]) { // activate watching first time
- watching[keyPath] = 1;
- chainsFor(obj).add(keyPath);
- } else {
- watching[keyPath] = (watching[keyPath] || 0) + 1;
- }
-};
-
-Ember.unwatchPath = function(obj, keyPath) {
- var m = metaFor(obj), watching = m.watching;
-
- if (watching[keyPath] === 1) {
- watching[keyPath] = 0;
- chainsFor(obj).remove(keyPath);
- } else if (watching[keyPath] > 1) {
- watching[keyPath]--;
- }
-};
-})();
-
-
-
-(function() {
-/**
-@module ember-metal
-*/
-
-var metaFor = Ember.meta, // utils.js
- GUID_KEY = Ember.GUID_KEY, // utils.js
- META_KEY = Ember.META_KEY, // utils.js
- removeChainWatcher = Ember.removeChainWatcher,
- watchKey = Ember.watchKey, // watch_key.js
- unwatchKey = Ember.unwatchKey,
- watchPath = Ember.watchPath, // watch_path.js
- unwatchPath = Ember.unwatchPath,
- typeOf = Ember.typeOf, // utils.js
- generateGuid = Ember.generateGuid,
- IS_PATH = /[\.\*]/;
-
-// returns true if the passed path is just a keyName
-function isKeyName(path) {
- return path==='*' || !IS_PATH.test(path);
-}
-
-/**
- @private
-
- Starts watching a property on an object. Whenever the property changes,
- invokes `Ember.propertyWillChange` and `Ember.propertyDidChange`. This is the
- primitive used by observers and dependent keys; usually you will never call
- this method directly but instead use higher level methods like
- `Ember.addObserver()`
-
- @method watch
- @for Ember
- @param obj
- @param {String} keyName
-*/
-Ember.watch = function(obj, _keyPath) {
- // can't watch length on Array - it is special...
- if (_keyPath === 'length' && typeOf(obj) === 'array') { return; }
-
- if (isKeyName(_keyPath)) {
- watchKey(obj, _keyPath);
- } else {
- watchPath(obj, _keyPath);
- }
-};
-
-Ember.isWatching = function isWatching(obj, key) {
- var meta = obj[META_KEY];
- return (meta && meta.watching[key]) > 0;
-};
-
-Ember.watch.flushPending = Ember.flushPendingChains;
-
-Ember.unwatch = function(obj, _keyPath) {
- // can't watch length on Array - it is special...
- if (_keyPath === 'length' && typeOf(obj) === 'array') { return; }
-
- if (isKeyName(_keyPath)) {
- unwatchKey(obj, _keyPath);
- } else {
- unwatchPath(obj, _keyPath);
- }
-};
-
-/**
- @private
-
- Call on an object when you first beget it from another object. This will
- setup any chained watchers on the object instance as needed. This method is
- safe to call multiple times.
-
- @method rewatch
- @for Ember
- @param obj
-*/
-Ember.rewatch = function(obj) {
- var m = metaFor(obj, false), chains = m.chains;
-
- // make sure the object has its own guid.
- if (GUID_KEY in obj && !obj.hasOwnProperty(GUID_KEY)) {
- generateGuid(obj);
- }
-
- // make sure any chained watchers update.
- if (chains && chains.value() !== obj) {
- m.chains = chains.copy(obj);
- }
-};
-
-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}
-*/
-Ember.destroy = function (obj) {
- var meta = obj[META_KEY], node, nodes, key, nodeObject;
- if (meta) {
- obj[META_KEY] = 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) {
- removeChainWatcher(nodeObject, node._key, node);
- }
- }
- }
- }
- }
-};
-
-})();
-
-
-
-(function() {
-/**
-@module ember-metal
-*/
-
-Ember.warn("The CP_DEFAULT_CACHEABLE flag has been removed and computed properties are always cached by default. Use `volatile` if you don't want caching.", Ember.ENV.CP_DEFAULT_CACHEABLE !== false);
-
-
-var get = Ember.get,
- set = Ember.set,
- metaFor = Ember.meta,
- a_slice = [].slice,
- o_create = Ember.create,
- META_KEY = Ember.META_KEY,
- watch = Ember.watch,
- unwatch = Ember.unwatch;
-
-// ..........................................................
-// 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(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 depKeys = desc._dependentKeys, 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);
- // Increment the number of times depKey depends on keyName.
- keys[keyName] = (keys[keyName] || 0) + 1;
- // Watch the depKey
- watch(obj, depKey);
- }
-}
-
-function removeDependentKeys(desc, obj, keyName, meta) {
- // the descriptor has a list of dependent keys, so
- // add all of its dependent keys.
- var depKeys = desc._dependentKeys, depsMeta, idx, len, depKey, keys;
- if (!depKeys) return;
-
- 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
- unwatch(obj, depKey);
- }
-}
-
-// ..........................................................
-// COMPUTED PROPERTY
-//
-
-/**
- A computed property transforms an objects 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 is dependent 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 properties
- dependencies (depending on firstName and lastName). The fullName function
- will be called once (regardless of how many times it is accessed) as long
- as it's dependencies have not been changed. Once firstName or lastName are updated
- any future calls (or anything bound) to fullName will incorporate the new
- values.
-
- ```javascript
- 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
-
- 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
- @extends Ember.Descriptor
- @constructor
-*/
-function ComputedProperty(func, opts) {
- this.func = func;
-
- this._cacheable = (opts && opts.cacheable !== undefined) ? opts.cacheable : true;
- this._dependentKeys = opts && opts.dependentKeys;
- this._readOnly = opts && (opts.readOnly !== undefined || !!opts.readOnly);
-}
-
-Ember.ComputedProperty = ComputedProperty;
-ComputedProperty.prototype = new Ember.Descriptor();
-
-var ComputedPropertyPrototype = ComputedProperty.prototype;
-
-/**
- Properties are cacheable by default. Computed property will automatically
- cache the return value of your function until one of the dependent keys changes.
-
- 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
-*/
-ComputedPropertyPrototype.cacheable = function(aFlag) {
- 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
- MyApp.outsideService = Ember.Object.create({
- value: function() {
- return OutsideService.getValue();
- }.property().volatile()
- });
- ```
-
- @method volatile
- @return {Ember.ComputedProperty} this
- @chainable
-*/
-ComputedPropertyPrototype.volatile = function() {
- return this.cacheable(false);
-};
-
-/**
- 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
- MyApp.person = Ember.Object.create({
- guid: function() {
- return 'guid-guid-guid';
- }.property().readOnly()
- });
-
- MyApp.person.set('guid', 'new-guid'); // will throw an exception
- ```
-
- @method readOnly
- @return {Ember.ComputedProperty} this
- @chainable
-*/
-ComputedPropertyPrototype.readOnly = function(readOnly) {
- this._readOnly = readOnly === undefined || !!readOnly;
- 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
- MyApp.president = Ember.Object.create({
- fullName: Ember.computed(function() {
- return this.get('firstName') + ' ' + this.get('lastName');
-
- // Tell Ember that this computed property depends on firstName
- // and lastName
- }).property('firstName', 'lastName')
- });
- ```
-
- @method property
- @param {String} path* zero or more property paths
- @return {Ember.ComputedProperty} this
- @chainable
-*/
-ComputedPropertyPrototype.property = function() {
- var addArg;
-
- var args = [];
- for (var i = 0, l = arguments.length; i < l; i++) {
- args.push(arguments[i]);
- }
- 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 (keyName in meta.cache) {
- delete meta.cache[keyName];
- removeDependentKeys(this, obj, keyName, meta);
- }
- }
-};
-
-function finishChains(chainNodes)
-{
- for (var i=0, l=chainNodes.length; i 1) {
- args = a_slice.call(arguments, 0, -1);
- func = a_slice.call(arguments, -1)[0];
- }
-
- if (typeof func !== "function") {
- throw new Ember.Error("Computed Property declared without a property function");
- }
-
- var cp = new ComputedProperty(func);
-
- 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
-*/
-Ember.cacheFor = function cacheFor(obj, key) {
- var cache = metaFor(obj, false).cache;
-
- if (cache && key in cache) {
- return cache[key];
- }
-};
-
-function getProperties(self, propertyNames) {
- var ret = {};
- for(var i = 0; i < propertyNames.length; i++) {
- ret[propertyNames[i]] = get(self, propertyNames[i]);
- }
- return ret;
-}
-
-function registerComputed(name, macro) {
- Ember.computed[name] = function(dependentKey) {
- var args = a_slice.call(arguments);
- return Ember.computed(dependentKey, function() {
- return macro.apply(this, args);
- });
- };
-}
-
-function registerComputedWithProperties(name, macro) {
- Ember.computed[name] = function() {
- var properties = a_slice.call(arguments);
-
- var computed = Ember.computed(function() {
- return macro.apply(this, [getProperties(this, properties)]);
- });
-
- return computed.property.apply(computed, properties);
- };
-}
-
-/**
- A computed property that returns true if the value of the dependent
- property is null, an empty string, empty array, or empty function.
-
- Note: When using `Ember.computed.empty` to watch an array make sure to
- use the `array.[]` syntax so the computed can subscribe to transitions
- from empty to non-empty states.
-
- Example
-
- ```javascript
- var ToDoList = Ember.Object.extend({
- done: Ember.computed.empty('todos.[]') // detect array changes
- });
- var todoList = ToDoList.create({todos: ['Unit Test', 'Documentation', 'Release']});
- todoList.get('done'); // false
- todoList.get('todos').clear(); // []
- todoList.get('done'); // true
- ```
-
- @method computed.empty
- @for Ember
- @param {String} dependentKey
- @return {Ember.ComputedProperty} computed property which negate
- the original value for property
-*/
-registerComputed('empty', function(dependentKey) {
- return Ember.isEmpty(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 computed.notEmpty
- @for Ember
- @param {String} dependentKey
- @return {Ember.ComputedProperty} computed property which returns true if
- original value for property is not empty.
-*/
-registerComputed('notEmpty', function(dependentKey) {
- return !Ember.isEmpty(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 computed.none
- @for Ember
- @param {String} dependentKey
- @return {Ember.ComputedProperty} computed property which
- returns true if original value for property is null or undefined.
-*/
-registerComputed('none', function(dependentKey) {
- return Ember.isNone(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 computed.not
- @for Ember
- @param {String} dependentKey
- @return {Ember.ComputedProperty} computed property which returns
- inverse of the original value for property
-*/
-registerComputed('not', function(dependentKey) {
- return !get(this, dependentKey);
-});
-
-/**
- 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 computed.bool
- @for Ember
- @param {String} dependentKey
- @return {Ember.ComputedProperty} computed property which converts
- to boolean the original value for property
-*/
-registerComputed('bool', function(dependentKey) {
- return !!get(this, dependentKey);
-});
-
-/**
- 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 computed.match
- @for Ember
- @param {String} dependentKey
- @param {RegExp} regexp
- @return {Ember.ComputedProperty} computed property which match
- the original value for property against a given RegExp
-*/
-registerComputed('match', function(dependentKey, regexp) {
- var value = get(this, dependentKey);
- return typeof value === 'string' ? regexp.test(value) : false;
-});
-
-/**
- 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 computed.equal
- @for Ember
- @param {String} dependentKey
- @param {String|Number|Object} value
- @return {Ember.ComputedProperty} computed property which returns true if
- the original value for property is equal to the given value.
-*/
-registerComputed('equal', function(dependentKey, value) {
- return get(this, dependentKey) === value;
-});
-
-/**
- A computed property that returns true if the provied 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 computed.gt
- @for Ember
- @param {String} dependentKey
- @param {Number} value
- @return {Ember.ComputedProperty} computed property which returns true if
- the original value for property is greater then given value.
-*/
-registerComputed('gt', function(dependentKey, value) {
- return get(this, dependentKey) > value;
-});
-
-/**
- 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 computed.gte
- @for Ember
- @param {String} dependentKey
- @param {Number} value
- @return {Ember.ComputedProperty} computed property which returns true if
- the original value for property is greater or equal then given value.
-*/
-registerComputed('gte', function(dependentKey, value) {
- return get(this, dependentKey) >= value;
-});
-
-/**
- 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 computed.lt
- @for Ember
- @param {String} dependentKey
- @param {Number} value
- @return {Ember.ComputedProperty} computed property which returns true if
- the original value for property is less then given value.
-*/
-registerComputed('lt', function(dependentKey, value) {
- return get(this, dependentKey) < value;
-});
-
-/**
- A computed property that returns true if the provided dependent property
- is less than or equal to the provided value.
-
- Example
-
- ```javascript
- var Hamster = Ember.Object.extend({
- needsMoreBananas: Ember.computed.lte('numBananas', 3)
- });
- 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 computed.lte
- @for Ember
- @param {String} dependentKey
- @param {Number} value
- @return {Ember.ComputedProperty} computed property which returns true if
- the original value for property is less or equal then given value.
-*/
-registerComputed('lte', function(dependentKey, value) {
- return get(this, dependentKey) <= value;
-});
-
-/**
- A computed property that performs a logical `and` on the
- original values for the provided dependent properties.
-
- Example
-
- ```javascript
- var Hamster = Ember.Object.extend({
- readyForCamp: Ember.computed.and('hasTent', 'hasBackpack')
- });
- var hamster = Hamster.create();
- hamster.get('readyForCamp'); // false
- hamster.set('hasTent', true);
- hamster.get('readyForCamp'); // false
- hamster.set('hasBackpack', true);
- hamster.get('readyForCamp'); // true
- ```
-
- @method computed.and
- @for Ember
- @param {String} dependentKey*
- @return {Ember.ComputedProperty} computed property which performs
- a logical `and` on the values of all the original values for properties.
-*/
-registerComputedWithProperties('and', function(properties) {
- for (var key in properties) {
- if (properties.hasOwnProperty(key) && !properties[key]) {
- return false;
- }
- }
- return true;
-});
-
-/**
- A computed property which performs a logical `or` on the
- original values for the provided dependent properties.
-
- Example
-
- ```javascript
- var Hamster = Ember.Object.extend({
- readyForRain: Ember.computed.or('hasJacket', 'hasUmbrella')
- });
- var hamster = Hamster.create();
- hamster.get('readyForRain'); // false
- hamster.set('hasJacket', true);
- hamster.get('readyForRain'); // true
- ```
-
- @method computed.or
- @for Ember
- @param {String} dependentKey*
- @return {Ember.ComputedProperty} computed property which performs
- a logical `or` on the values of all the original values for properties.
-*/
-registerComputedWithProperties('or', function(properties) {
- for (var key in properties) {
- if (properties.hasOwnProperty(key) && properties[key]) {
- return true;
- }
- }
- return false;
-});
-
-/**
- A computed property that returns the first truthy value
- from a list of dependent properties.
-
- Example
-
- ```javascript
- var Hamster = Ember.Object.extend({
- hasClothes: Ember.computed.any('hat', 'shirt')
- });
- var hamster = Hamster.create();
- hamster.get('hasClothes'); // null
- hamster.set('shirt', 'Hawaiian Shirt');
- hamster.get('hasClothes'); // 'Hawaiian Shirt'
- ```
-
- @method computed.any
- @for Ember
- @param {String} dependentKey*
- @return {Ember.ComputedProperty} computed property which returns
- the first truthy value of given list of properties.
-*/
-registerComputedWithProperties('any', function(properties) {
- for (var key in properties) {
- if (properties.hasOwnProperty(key) && properties[key]) {
- return properties[key];
- }
- }
- return null;
-});
-
-/**
- A computed property that returns the array of values
- for the provided dependent properties.
-
- Example
-
- ```javascript
- var Hamster = Ember.Object.extend({
- clothes: Ember.computed.map('hat', 'shirt')
- });
- var hamster = Hamster.create();
- hamster.get('clothes'); // [null, null]
- hamster.set('hat', 'Camp Hat');
- hamster.set('shirt', 'Camp Shirt');
- hamster.get('clothes'); // ['Camp Hat', 'Camp Shirt']
- ```
-
- @method computed.map
- @for Ember
- @param {String} dependentKey*
- @return {Ember.ComputedProperty} computed property which maps
- values of all passed properties in to an array.
-*/
-registerComputedWithProperties('collect', function(properties) {
- var res = [];
- for (var key in properties) {
- if (properties.hasOwnProperty(key)) {
- if (Ember.isNone(properties[key])) {
- res.push(null);
- } else {
- res.push(properties[key]);
- }
- }
- }
- return res;
-});
-
-/**
- Creates a new property that is an alias for another property
- on an object. Calls to `get` or `set` this property behave as
- though they were called on the original property.
-
- ```javascript
- Person = Ember.Object.extend({
- name: 'Alex Matchneer',
- nomen: Ember.computed.alias('name')
- });
-
- alex = Person.create();
- alex.get('nomen'); // 'Alex Matchneer'
- alex.get('name'); // 'Alex Matchneer'
-
- alex.set('nomen', '@machty');
- alex.get('name'); // '@machty'
- ```
- @method computed.alias
- @for Ember
- @param {String} dependentKey
- @return {Ember.ComputedProperty} computed property which creates an
- alias to the original value for property.
-*/
-Ember.computed.alias = function(dependentKey) {
- return Ember.computed(dependentKey, function(key, value) {
- if (arguments.length > 1) {
- set(this, dependentKey, value);
- return value;
- } else {
- return get(this, dependentKey);
- }
- });
-};
-
-/**
- Where `computed.alias` aliases `get` and `set`, and allows for bidirectional
- data flow, `computed.oneWay` only provides an aliased `get`. The `set` will
- not mutate the upstream property, rather causes the current property to
- become the value set. This causes the downstream property to permentantly
- diverge from the upstream property.
-
- Example
-
- ```javascript
- User = Ember.Object.extend({
- firstName: null,
- lastName: null,
- nickName: Ember.computed.oneWay('firstName')
- });
-
- user = User.create({
- firstName: 'Teddy',
- lastName: 'Zeenny'
- });
-
- user.get('nickName');
- # 'Teddy'
-
- user.set('nickName', 'TeddyBear');
- # 'TeddyBear'
-
- user.get('firstName');
- # 'Teddy'
- ```
-
- @method computed.oneWay
- @for Ember
- @param {String} dependentKey
- @return {Ember.ComputedProperty} computed property which creates a
- one way computed property to the original value for property.
-*/
-Ember.computed.oneWay = function(dependentKey) {
- return Ember.computed(dependentKey, function() {
- return get(this, dependentKey);
- });
-};
-
-
-/**
- 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 computed.defaultTo
- @for Ember
- @param {String} defaultPath
- @return {Ember.ComputedProperty} computed property which acts like
- a standard getter and setter, but defaults to the value from `defaultPath`.
-*/
-Ember.computed.defaultTo = function(defaultPath) {
- return Ember.computed(function(key, newValue, cachedValue) {
- if (arguments.length === 1) {
- return cachedValue != null ? cachedValue : get(this, defaultPath);
- }
- return newValue != null ? newValue : get(this, defaultPath);
- });
-};
-
-
-})();
-
-
-
-(function() {
-// Ember.tryFinally
-/**
-@module ember-metal
-*/
-
-var AFTER_OBSERVERS = ':change',
- BEFORE_OBSERVERS = ':before';
-
-function changeEvent(keyName) {
- return keyName+AFTER_OBSERVERS;
-}
-
-function beforeEvent(keyName) {
- return keyName+BEFORE_OBSERVERS;
-}
-
-/**
- @method addObserver
- @param obj
- @param {String} path
- @param {Object|Function} targetOrMethod
- @param {Function|String} [method]
-*/
-Ember.addObserver = function(obj, _path, target, method) {
- Ember.addListener(obj, changeEvent(_path), target, method);
- Ember.watch(obj, _path);
-
- return this;
-};
-
-Ember.observersFor = function(obj, path) {
- return Ember.listenersFor(obj, changeEvent(path));
-};
-
-/**
- @method removeObserver
- @param obj
- @param {String} path
- @param {Object|Function} targetOrMethod
- @param {Function|String} [method]
-*/
-Ember.removeObserver = function(obj, _path, target, method) {
- Ember.unwatch(obj, _path);
- Ember.removeListener(obj, changeEvent(_path), target, method);
- return this;
-};
-
-/**
- @method addBeforeObserver
- @param obj
- @param {String} path
- @param {Object|Function} targetOrMethod
- @param {Function|String} [method]
-*/
-Ember.addBeforeObserver = function(obj, _path, target, method) {
- Ember.addListener(obj, beforeEvent(_path), target, method);
- Ember.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.
-Ember._suspendBeforeObserver = function(obj, path, target, method, callback) {
- return Ember._suspendListener(obj, beforeEvent(path), target, method, callback);
-};
-
-Ember._suspendObserver = function(obj, path, target, method, callback) {
- return Ember._suspendListener(obj, changeEvent(path), target, method, callback);
-};
-
-var map = Ember.ArrayPolyfills.map;
-
-Ember._suspendBeforeObservers = function(obj, paths, target, method, callback) {
- var events = map.call(paths, beforeEvent);
- return Ember._suspendListeners(obj, events, target, method, callback);
-};
-
-Ember._suspendObservers = function(obj, paths, target, method, callback) {
- var events = map.call(paths, changeEvent);
- return Ember._suspendListeners(obj, events, target, method, callback);
-};
-
-Ember.beforeObserversFor = function(obj, path) {
- return Ember.listenersFor(obj, beforeEvent(path));
-};
-
-/**
- @method removeBeforeObserver
- @param obj
- @param {String} path
- @param {Object|Function} targetOrMethod
- @param {Function|String} [method]
-*/
-Ember.removeBeforeObserver = function(obj, _path, target, method) {
- Ember.unwatch(obj, _path);
- Ember.removeListener(obj, beforeEvent(_path), target, method);
- return this;
-};
-
-})();
-
-
-
-(function() {
-define("backburner/queue",
- ["exports"],
- function(__exports__) {
- "use strict";
- function Queue(daq, name, options) {
- this.daq = daq;
- this.name = name;
- this.options = options;
- this._queue = [];
- }
-
- Queue.prototype = {
- daq: null,
- name: null,
- options: null,
- _queue: null,
-
- push: function(target, method, args, stack) {
- var queue = this._queue;
- queue.push(target, method, args, stack);
- return {queue: this, target: target, method: method};
- },
-
- pushUnique: function(target, method, args, stack) {
- var queue = this._queue, currentTarget, currentMethod, i, l;
-
- for (i = 0, l = queue.length; i < l; i += 4) {
- currentTarget = queue[i];
- currentMethod = queue[i+1];
-
- if (currentTarget === target && currentMethod === method) {
- queue[i+2] = args; // replace args
- queue[i+3] = stack; // replace stack
- return {queue: this, target: target, method: method}; // TODO: test this code path
- }
- }
-
- this._queue.push(target, method, args, stack);
- return {queue: this, target: target, method: method};
- },
-
- // TODO: remove me, only being used for Ember.run.sync
- flush: function() {
- var queue = this._queue,
- options = this.options,
- before = options && options.before,
- after = options && options.after,
- target, method, args, stack, i, l = queue.length;
-
- if (l && before) { before(); }
- for (i = 0; i < l; i += 4) {
- target = queue[i];
- method = queue[i+1];
- args = queue[i+2];
- stack = queue[i+3]; // Debugging assistance
-
- // TODO: error handling
- if (args && args.length > 0) {
- method.apply(target, args);
- } else {
- method.call(target);
- }
- }
- if (l && after) { after(); }
-
- // check if new items have been added
- if (queue.length > l) {
- this._queue = queue.slice(l);
- this.flush();
- } else {
- this._queue.length = 0;
- }
- },
-
- cancel: function(actionToCancel) {
- var queue = this._queue, currentTarget, currentMethod, i, l;
-
- for (i = 0, l = queue.length; i < l; i += 4) {
- currentTarget = queue[i];
- currentMethod = queue[i+1];
-
- if (currentTarget === actionToCancel.target && currentMethod === actionToCancel.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 === actionToCancel.target && currentMethod === actionToCancel.method) {
- // don't mess with array during flush
- // just nullify the method
- queue[i+1] = null;
- return true;
- }
- }
- }
- };
-
-
- __exports__.Queue = Queue;
- });
-
-define("backburner/deferred_action_queues",
- ["backburner/queue","exports"],
- function(__dependency1__, __exports__) {
- "use strict";
- var Queue = __dependency1__.Queue;
-
- function DeferredActionQueues(queueNames, options) {
- var queues = this.queues = {};
- this.queueNames = queueNames = queueNames || [];
-
- var queueName;
- for (var i = 0, l = queueNames.length; i < l; i++) {
- queueName = queueNames[i];
- queues[queueName] = new Queue(this, queueName, options[queueName]);
- }
- }
-
- DeferredActionQueues.prototype = {
- queueNames: null,
- queues: null,
-
- schedule: function(queueName, target, method, args, onceFlag, stack) {
- var queues = this.queues,
- queue = queues[queueName];
-
- if (!queue) { throw new Error("You attempted to schedule an action in a queue (" + queueName + ") that doesn't exist"); }
-
- if (onceFlag) {
- return queue.pushUnique(target, method, args, stack);
- } else {
- return queue.push(target, method, args, stack);
- }
- },
-
- flush: function() {
- var queues = this.queues,
- queueNames = this.queueNames,
- queueName, queue, queueItems, priorQueueNameIndex,
- queueNameIndex = 0, numberOfQueues = queueNames.length;
-
- outerloop:
- while (queueNameIndex < numberOfQueues) {
- queueName = queueNames[queueNameIndex];
- queue = queues[queueName];
- queueItems = queue._queueBeingFlushed = queue._queue.slice();
- queue._queue = [];
-
- var options = queue.options,
- before = options && options.before,
- after = options && options.after,
- target, method, args, stack,
- queueIndex = 0, numberOfQueueItems = queueItems.length;
-
- if (numberOfQueueItems && before) { before(); }
- while (queueIndex < numberOfQueueItems) {
- target = queueItems[queueIndex];
- method = queueItems[queueIndex+1];
- args = queueItems[queueIndex+2];
- stack = queueItems[queueIndex+3]; // Debugging assistance
-
- if (typeof method === 'string') { method = target[method]; }
-
- // method could have been nullified / canceled during flush
- if (method) {
- // TODO: error handling
- if (args && args.length > 0) {
- method.apply(target, args);
- } else {
- method.call(target);
- }
- }
-
- queueIndex += 4;
- }
- queue._queueBeingFlushed = null;
- if (numberOfQueueItems && after) { after(); }
-
- if ((priorQueueNameIndex = indexOfPriorQueueWithActions(this, queueNameIndex)) !== -1) {
- queueNameIndex = priorQueueNameIndex;
- continue outerloop;
- }
-
- queueNameIndex++;
- }
- }
- };
-
- function indexOfPriorQueueWithActions(daq, currentQueueIndex) {
- var queueName, queue;
-
- for (var i = 0, l = currentQueueIndex; i <= l; i++) {
- queueName = daq.queueNames[i];
- queue = daq.queues[queueName];
- if (queue._queue.length) { return i; }
- }
-
- return -1;
- }
-
-
- __exports__.DeferredActionQueues = DeferredActionQueues;
- });
-
-define("backburner",
- ["backburner/deferred_action_queues","exports"],
- function(__dependency1__, __exports__) {
- "use strict";
- var DeferredActionQueues = __dependency1__.DeferredActionQueues;
-
- var slice = [].slice,
- pop = [].pop,
- throttlers = [],
- debouncees = [],
- timers = [],
- autorun, laterTimer, laterTimerExpiresAt,
- global = this,
- NUMBER = /\d+/;
-
- function isCoercableNumber(number) {
- return typeof number === 'number' || NUMBER.test(number);
- }
-
- function Backburner(queueNames, options) {
- this.queueNames = queueNames;
- this.options = options || {};
- if (!this.options.defaultQueue) {
- this.options.defaultQueue = queueNames[0];
- }
- this.instanceStack = [];
- }
-
- Backburner.prototype = {
- queueNames: null,
- options: null,
- currentInstance: null,
- instanceStack: null,
-
- begin: function() {
- var onBegin = this.options && this.options.onBegin,
- previousInstance = this.currentInstance;
-
- if (previousInstance) {
- this.instanceStack.push(previousInstance);
- }
-
- this.currentInstance = new DeferredActionQueues(this.queueNames, this.options);
- if (onBegin) {
- onBegin(this.currentInstance, previousInstance);
- }
- },
-
- end: function() {
- var onEnd = this.options && this.options.onEnd,
- currentInstance = this.currentInstance,
- nextInstance = null;
-
- try {
- currentInstance.flush();
- } finally {
- this.currentInstance = null;
-
- if (this.instanceStack.length) {
- nextInstance = this.instanceStack.pop();
- this.currentInstance = nextInstance;
- }
-
- if (onEnd) {
- onEnd(currentInstance, nextInstance);
- }
- }
- },
-
- run: function(target, method /*, args */) {
- var ret;
- this.begin();
-
- if (!method) {
- method = target;
- target = null;
- }
-
- if (typeof method === 'string') {
- method = target[method];
- }
-
- // Prevent Safari double-finally.
- var finallyAlreadyCalled = false;
- try {
- if (arguments.length > 2) {
- ret = method.apply(target, slice.call(arguments, 2));
- } else {
- ret = method.call(target);
- }
- } finally {
- if (!finallyAlreadyCalled) {
- finallyAlreadyCalled = true;
- this.end();
- }
- }
- return ret;
- },
-
- defer: function(queueName, target, method /* , args */) {
- if (!method) {
- method = target;
- target = null;
- }
-
- if (typeof method === 'string') {
- method = target[method];
- }
-
- var stack = this.DEBUG ? new Error().stack : undefined,
- args = arguments.length > 3 ? slice.call(arguments, 3) : undefined;
- if (!this.currentInstance) { createAutorun(this); }
- return this.currentInstance.schedule(queueName, target, method, args, false, stack);
- },
-
- deferOnce: function(queueName, target, method /* , args */) {
- if (!method) {
- method = target;
- target = null;
- }
-
- if (typeof method === 'string') {
- method = target[method];
- }
-
- var stack = this.DEBUG ? new Error().stack : undefined,
- args = arguments.length > 3 ? slice.call(arguments, 3) : undefined;
- if (!this.currentInstance) { createAutorun(this); }
- return this.currentInstance.schedule(queueName, target, method, args, true, stack);
- },
-
- setTimeout: function() {
- var args = slice.call(arguments);
- var length = args.length;
- var method, wait, target;
- var self = this;
- var 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 (typeof methodOrWait === 'function' || typeof methodOrTarget[methodOrWait] === 'function') {
- 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();
- }
-
- methodOrTarget = args[0];
- methodOrArgs = args[1];
-
- if (typeof methodOrArgs === 'function' || (typeof methodOrArgs === 'string' &&
- methodOrTarget !== null &&
- methodOrArgs in methodOrTarget)) {
- target = args.shift();
- method = args.shift();
- } else {
- method = args.shift();
- }
- }
-
- var executeAt = (+new Date()) + parseInt(wait, 10);
-
- if (typeof method === 'string') {
- method = target[method];
- }
-
- function fn() {
- method.apply(target, args);
- }
-
- // find position to insert - TODO: binary search
- var i, l;
- for (i = 0, l = timers.length; i < l; i += 2) {
- if (executeAt < timers[i]) { break; }
- }
-
- timers.splice(i, 0, executeAt, fn);
-
- if (laterTimer && laterTimerExpiresAt < executeAt) { return fn; }
-
- if (laterTimer) {
- clearTimeout(laterTimer);
- laterTimer = null;
- }
- laterTimer = global.setTimeout(function() {
- executeTimers(self);
- laterTimer = null;
- laterTimerExpiresAt = null;
- }, wait);
- laterTimerExpiresAt = executeAt;
-
- return fn;
- },
-
- throttle: function(target, method /* , args, wait */) {
- var self = this,
- args = arguments,
- wait = parseInt(pop.call(args), 10),
- throttler;
-
- for (var i = 0, l = throttlers.length; i < l; i++) {
- throttler = throttlers[i];
- if (throttler[0] === target && throttler[1] === method) { return; } // do nothing
- }
-
- var timer = global.setTimeout(function() {
- self.run.apply(self, args);
-
- // remove throttler
- var index = -1;
- for (var i = 0, l = throttlers.length; i < l; i++) {
- throttler = throttlers[i];
- if (throttler[0] === target && throttler[1] === method) {
- index = i;
- break;
- }
- }
-
- if (index > -1) { throttlers.splice(index, 1); }
- }, wait);
-
- throttlers.push([target, method, timer]);
- },
-
- debounce: function(target, method /* , args, wait, [immediate] */) {
- var self = this,
- args = arguments,
- immediate = pop.call(args),
- wait,
- index,
- debouncee;
-
- if (typeof immediate === "number" || typeof immediate === "string") {
- wait = immediate;
- immediate = false;
- } else {
- wait = pop.call(args);
- }
-
- wait = parseInt(wait, 10);
- // Remove debouncee
- index = findDebouncee(target, method);
-
- if (index !== -1) {
- debouncee = debouncees[index];
- debouncees.splice(index, 1);
- clearTimeout(debouncee[2]);
- }
-
- var timer = global.setTimeout(function() {
- if (!immediate) {
- self.run.apply(self, args);
- }
- index = findDebouncee(target, method);
- if (index) {
- debouncees.splice(index, 1);
- }
- }, wait);
-
- if (immediate && index === -1) {
- self.run.apply(self, args);
- }
-
- debouncees.push([target, method, timer]);
- },
-
- cancelTimers: function() {
- var i, len;
-
- for (i = 0, len = throttlers.length; i < len; i++) {
- clearTimeout(throttlers[i][2]);
- }
- throttlers = [];
-
- for (i = 0, len = debouncees.length; i < len; i++) {
- clearTimeout(debouncees[i][2]);
- }
- debouncees = [];
-
- if (laterTimer) {
- clearTimeout(laterTimer);
- laterTimer = null;
- }
- timers = [];
-
- if (autorun) {
- clearTimeout(autorun);
- autorun = null;
- }
- },
-
- hasTimers: function() {
- return !!timers.length || autorun;
- },
-
- cancel: function(timer) {
- if (timer && typeof timer === 'object' && timer.queue && timer.method) { // we're cancelling a deferOnce
- return timer.queue.cancel(timer);
- } else if (typeof timer === 'function') { // we're cancelling a setTimeout
- for (var i = 0, l = timers.length; i < l; i += 2) {
- if (timers[i + 1] === timer) {
- timers.splice(i, 2); // remove the two elements
- return true;
- }
- }
- } else {
- return; // timer was null or not a timer
- }
- }
- };
-
- Backburner.prototype.schedule = Backburner.prototype.defer;
- Backburner.prototype.scheduleOnce = Backburner.prototype.deferOnce;
- Backburner.prototype.later = Backburner.prototype.setTimeout;
-
- function createAutorun(backburner) {
- backburner.begin();
- autorun = global.setTimeout(function() {
- autorun = null;
- backburner.end();
- });
- }
-
- function executeTimers(self) {
- var now = +new Date(),
- time, fns, i, l;
-
- self.run(function() {
- // TODO: binary search
- for (i = 0, l = timers.length; i < l; i += 2) {
- time = timers[i];
- if (time > now) { break; }
- }
-
- fns = timers.splice(0, i);
-
- for (i = 1, l = fns.length; i < l; i += 2) {
- self.schedule(self.options.defaultQueue, null, fns[i]);
- }
- });
-
- if (timers.length) {
- laterTimer = global.setTimeout(function() {
- executeTimers(self);
- laterTimer = null;
- laterTimerExpiresAt = null;
- }, timers[0] - now);
- laterTimerExpiresAt = timers[0];
- }
- }
-
- function findDebouncee(target, method) {
- var debouncee,
- index = -1;
-
- for (var i = 0, l = debouncees.length; i < l; i++) {
- debouncee = debouncees[i];
- if (debouncee[0] === target && debouncee[1] === method) {
- index = i;
- break;
- }
- }
-
- return index;
- }
-
-
- __exports__.Backburner = Backburner;
- });
-
-})();
-
-
-
-(function() {
-var onBegin = function(current) {
- Ember.run.currentRunLoop = current;
-};
-
-var onEnd = function(current, next) {
- Ember.run.currentRunLoop = next;
-};
-
-var Backburner = requireModule('backburner').Backburner,
- backburner = new Backburner(['sync', 'actions', 'destroy'], {
- sync: {
- before: Ember.beginPropertyChanges,
- after: Ember.endPropertyChanges
- },
- defaultQueue: 'actions',
- onBegin: onBegin,
- onEnd: onEnd
- }),
- slice = [].slice;
-
-// ..........................................................
-// Ember.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
- Ember.run(function() {
- // code to be execute 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.
-*/
-Ember.run = function(target, method) {
- var ret;
-
- if (Ember.onerror) {
- try {
- ret = backburner.run.apply(backburner, arguments);
- } catch (e) {
- Ember.onerror(e);
- }
- } else {
- ret = backburner.run.apply(backburner, arguments);
- }
-
- return ret;
-};
-
-/**
-
- 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
- Ember.run.join(function() {
- // creates a new run-loop
- });
- ```
-
- Alternatively, if called within an existing run loop:
-
- ```javascript
- Ember.run(function() {
- // creates a new run-loop
- Ember.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.
-*/
-Ember.run.join = function(target, method) {
- if (!Ember.run.currentRunLoop) {
- return Ember.run.apply(Ember.run, arguments);
- }
-
- var args = slice.call(arguments);
- args.unshift('actions');
- Ember.run.schedule.apply(Ember.run, args);
-};
-
-Ember.run.backburner = backburner;
-
-var run = Ember.run;
-
-Ember.run.currentRunLoop = null;
-
-Ember.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 `Ember.run.end()`. This is
- a lower-level way to use a RunLoop instead of using `Ember.run()`.
-
- ```javascript
- Ember.run.begin();
- // code to be execute within a RunLoop
- Ember.run.end();
- ```
-
- @method begin
- @return {void}
-*/
-Ember.run.begin = function() {
- backburner.begin();
-};
-
-/**
- Ends a RunLoop. This must be called sometime after you call
- `Ember.run.begin()` to flush any deferred actions. This is a lower-level way
- to use a RunLoop instead of using `Ember.run()`.
-
- ```javascript
- Ember.run.begin();
- // code to be execute within a RunLoop
- Ember.run.end();
- ```
-
- @method end
- @return {void}
-*/
-Ember.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 `Ember.run.queues` property.
-
- ```javascript
- Ember.run.schedule('sync', this, function() {
- // this will be executed in the first RunLoop queue, when bindings are synced
- console.log("scheduled on sync queue");
- });
-
- Ember.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}
-*/
-Ember.run.schedule = function(queue, target, method) {
- checkAutoRun();
- backburner.schedule.apply(backburner, arguments);
-};
-
-// Used by global test teardown
-Ember.run.hasScheduledTimers = function() {
- return backburner.hasTimers();
-};
-
-// Used by global test teardown
-Ember.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
- Ember.run.sync();
- ```
-
- @method sync
- @return {void}
-*/
-Ember.run.sync = function() {
- if (backburner.currentInstance) {
- backburner.currentInstance.queues.sync.flush();
- }
-};
-
-/**
- Invokes the passed target/method and optional arguments after a specified
- period if time. The last parameter of this method must always be a number
- of milliseconds.
-
- 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
- Ember.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 {String} a string you can use to cancel the timer in
- `Ember.run.cancel` later.
-*/
-Ember.run.later = function(target, method) {
- 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
-*/
-Ember.run.once = function(target, method) {
- checkAutoRun();
- var args = slice.call(arguments);
- 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
- Ember.run(function() {
- var sayHi = function() { console.log('hi'); }
- Ember.run.scheduleOnce('afterRender', myContext, sayHi);
- Ember.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 `Ember.run.scheduleOnce` will
- not prevent additional calls with an identical anonymous function from
- scheduling the items multiple times, e.g.:
-
- ```javascript
- function scheduleIt() {
- Ember.run.scheduleOnce('actions', myContext, function() { console.log("Closure"); });
- }
- scheduleIt();
- scheduleIt();
- // "Closure" will print twice, even though we're using `Ember.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 `Ember.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
-*/
-Ember.run.scheduleOnce = function(queue, target, method) {
- 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
- `Ember.run.later` with a wait time of 1ms.
-
- ```javascript
- Ember.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 `Ember.run.next` will coalesce
- into the same later run loop, along with any other operations
- scheduled by `Ember.run.later` that expire right around the same
- time that `Ember.run.next` operations will fire.
-
- Note that there are often alternatives to using `Ember.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() {
- Ember.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 `Ember.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 `Ember.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 `Ember.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
-*/
-Ember.run.next = function() {
- var args = slice.call(arguments);
- args.push(1);
- return backburner.later.apply(backburner, args);
-};
-
-/**
- Cancels a scheduled item. Must be a value returned by `Ember.run.later()`,
- `Ember.run.once()`, or `Ember.run.next()`.
-
- ```javascript
- var runNext = Ember.run.next(myContext, function() {
- // will not be executed
- });
- Ember.run.cancel(runNext);
-
- var runLater = Ember.run.later(myContext, function() {
- // will not be executed
- }, 500);
- Ember.run.cancel(runLater);
-
- var runOnce = Ember.run.once(myContext, function() {
- // will not be executed
- });
- Ember.run.cancel(runOnce);
- ```
-
- @method cancel
- @param {Object} timer Timer object to cancel
- @return {void}
-*/
-Ember.run.cancel = function(timer) {
- return backburner.cancel(timer);
-};
-
-/**
- Delay calling the target method until the debounce period has elapsed
- with no additional debounce calls. If `debounce` is called again before
- the specified time has elapsed, the timer is reset and the entire period
- must pass again before the target method is called.
-
- This method should be used when an event may be called multiple times
- but the action should only be called once when the event is done firing.
- A common example is for scroll events where you only want updates to
- happen once scrolling has ceased.
-
- ```javascript
- var myFunc = function() { console.log(this.name + ' ran.'); };
- var myContext = {name: 'debounce'};
-
- Ember.run.debounce(myContext, myFunc, 150);
-
- // less than 150ms passes
-
- Ember.run.debounce(myContext, myFunc, 150);
-
- // 150ms passes
- // myFunc is invoked with context myContext
- // console logs 'debounce ran.' one time.
- ```
-
- @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.
- @return {void}
-*/
-Ember.run.debounce = function() {
- return backburner.debounce.apply(backburner, arguments);
-};
-
-/**
- Ensure that the target method is never called more frequently than
- the specified spacing period.
-
- ```javascript
- var myFunc = function() { console.log(this.name + ' ran.'); };
- var myContext = {name: 'throttle'};
-
- Ember.run.throttle(myContext, myFunc, 150);
-
- // 50ms passes
- Ember.run.throttle(myContext, myFunc, 150);
-
- // 50ms passes
- Ember.run.throttle(myContext, myFunc, 150);
-
- // 50ms passes
- Ember.run.throttle(myContext, myFunc, 150);
-
- // 150ms passes
- // myFunc is invoked with context myContext
- // console logs 'throttle ran.' twice, 150ms apart.
- ```
-
- @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.
- @return {void}
-*/
-Ember.run.throttle = function() {
- return backburner.throttle.apply(backburner, arguments);
-};
-
-// Make sure it's not an autorun during testing
-function checkAutoRun() {
- if (!Ember.run.currentRunLoop) {
- Ember.assert("You have turned on testing mode, which disabled the run-loop's autorun. You will need to wrap any code with asynchronous side-effects in an Ember.run", !Ember.testing);
- }
-}
-
-})();
-
-
-
-(function() {
-// Ember.Logger
-// get
-// set
-// guidFor, meta
-// addObserver, removeObserver
-// Ember.run.schedule
-/**
-@module ember-metal
-*/
-
-// ..........................................................
-// CONSTANTS
-//
-
-/**
- Debug parameter you can turn on. This will log all bindings that fire to
- the console. This should be disabled in production code. Note that you
- can also enable this from the console or temporarily.
-
- @property LOG_BINDINGS
- @for Ember
- @type Boolean
- @default false
-*/
-Ember.LOG_BINDINGS = false || !!Ember.ENV.LOG_BINDINGS;
-
-var get = Ember.get,
- set = Ember.set,
- guidFor = Ember.guidFor,
- IS_GLOBAL = /^([A-Z$]|([0-9][A-Z$]))/;
-
-/**
- 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
-*/
-var isGlobalPath = Ember.isGlobalPath = function(path) {
- return IS_GLOBAL.test(path);
-};
-
-function getWithGlobals(obj, path) {
- return get(isGlobalPath(path) ? Ember.lookup : obj, path);
-}
-
-// ..........................................................
-// BINDING
-//
-
-var Binding = function(toPath, fromPath) {
- this._direction = 'fwd';
- this._from = fromPath;
- this._to = toPath;
- this._directionMap = Ember.Map.create();
-};
-
-/**
-@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<" + 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.assert('Must pass a valid object to Ember.Binding.connect()', !!obj);
-
- var fromPath = this._from, toPath = this._to;
- Ember.trySet(obj, toPath, getWithGlobals(obj, fromPath));
-
- // add an observer on the object to be notified when the binding should be updated
- Ember.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) { Ember.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.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.
- Ember.removeObserver(obj, this._from, this, this.fromDidChange);
-
- // if the binding is two-way, remove the observer from the target as well
- if (twoWay) { Ember.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 directionMap = this._directionMap;
- var existingDir = directionMap.get(obj);
-
- // if we haven't scheduled the binding yet, schedule it
- if (!existingDir) {
- Ember.run.schedule('sync', this, this._sync, obj);
- directionMap.set(obj, 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') {
- directionMap.set(obj, 'fwd');
- }
- },
-
- _sync: function(obj) {
- var log = Ember.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 directionMap = this._directionMap;
- var direction = directionMap.get(obj);
-
- var fromPath = this._from, toPath = this._to;
-
- directionMap.remove(obj);
-
- // if we're synchronizing from the remote object...
- if (direction === 'fwd') {
- var fromValue = getWithGlobals(obj, this._from);
- if (log) {
- Ember.Logger.log(' ', this.toString(), '->', fromValue, obj);
- }
- if (this._oneWay) {
- Ember.trySet(obj, toPath, fromValue);
- } else {
- Ember._suspendObserver(obj, toPath, this, this.toDidChange, function () {
- Ember.trySet(obj, toPath, fromValue);
- });
- }
- // if we're synchronizing *to* the remote object
- } else if (direction === 'back') {
- var toValue = get(obj, this._to);
- if (log) {
- Ember.Logger.log(' ', this.toString(), '<-', toValue, obj);
- }
- Ember._suspendObserver(obj, fromPath, this, this.fromDidChange, function () {
- Ember.trySet(Ember.isGlobalPath(fromPath) ? Ember.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() {
- var C = this, binding = new C();
- return binding.from.apply(binding, arguments);
- },
-
- /*
- See `Ember.Binding.to`.
-
- @method to
- @static
- */
- to: function() {
- var C = this, binding = new C();
- return binding.to.apply(binding, arguments);
- },
-
- /**
- 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, binding = new C(null, from);
- return binding.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 instanced 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(this.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.
-
- 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;
-
-
-/**
- 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
-*/
-Ember.bind = function(obj, to, from) {
- return new Ember.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
-*/
-Ember.oneWay = function(obj, to, from) {
- return new Ember.Binding(to, from).oneWay().connect(obj);
-};
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-metal
-*/
-
-var Mixin, REQUIRED, Alias,
- a_map = Ember.ArrayPolyfills.map,
- a_indexOf = Ember.ArrayPolyfills.indexOf,
- a_forEach = Ember.ArrayPolyfills.forEach,
- a_slice = [].slice,
- o_create = Ember.create,
- defineProperty = Ember.defineProperty,
- guidFor = Ember.guidFor;
-
-function mixinsMeta(obj) {
- var m = Ember.meta(obj, true), ret = m.mixins;
- if (!ret) {
- ret = m.mixins = {};
- } else if (!m.hasOwnProperty('mixins')) {
- ret = m.mixins = o_create(ret);
- }
- return ret;
-}
-
-function initMixin(mixin, args) {
- if (args && args.length > 0) {
- mixin.mixins = a_map.call(args, function(x) {
- if (x instanceof Mixin) { return x; }
-
- // Note: Manually setup a primitive mixin here. This is the only
- // way to actually get a primitive mixin. This way normal creation
- // of mixins will give you combined mixins...
- var mixin = new Mixin();
- mixin.properties = x;
- return mixin;
- });
- }
- return mixin;
-}
-
-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 = 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) {
- 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.
- superProperty = superProperty || meta.descs[key];
-
- if (!superProperty || !(superProperty instanceof Ember.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(property);
- property.func = Ember.wrap(property.func, superProperty.func);
-
- return property;
-}
-
-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 ('function' !== typeof superMethod) {
- return method;
- }
-
- return Ember.wrap(method, superMethod);
-}
-
-function applyConcatenatedProperties(obj, key, value, values) {
- var baseValue = values[key] || obj[key];
-
- if (baseValue) {
- if ('function' === typeof baseValue.concat) {
- return baseValue.concat(value);
- } else {
- return Ember.makeArray(baseValue).concat(value);
- }
- } else {
- return Ember.makeArray(value);
- }
-}
-
-function applyMergedProperties(obj, key, value, values) {
- var baseValue = values[key] || obj[key];
-
- if (!baseValue) { return value; }
-
- var newBase = Ember.merge({}, baseValue);
- for (var prop in value) {
- if (!value.hasOwnProperty(prop)) { continue; }
-
- var propValue = value[prop];
- if (isMethod(propValue)) {
- // TODO: support for Computed Properties, etc?
- newBase[prop] = giveMethodSuper(obj, prop, propValue, baseValue, {});
- } else {
- newBase[prop] = propValue;
- }
- }
-
- return newBase;
-}
-
-function addNormalizedProperty(base, key, value, meta, descs, values, concats, mergings) {
- if (value instanceof Ember.Descriptor) {
- if (value === REQUIRED && descs[key]) { return CONTINUE; }
-
- // Wrap descriptor function to implement
- // _super() if needed
- if (value.func) {
- value = giveDescriptorSuper(meta, key, value, values, descs);
- }
-
- descs[key] = value;
- values[key] = undefined;
- } else {
- if ((concats && a_indexOf.call(concats, key) >= 0) ||
- key === 'concatenatedProperties' ||
- key === 'mergedProperties') {
- value = applyConcatenatedProperties(base, key, value, values);
- } else if ((mergings && a_indexOf.call(mergings, key) >= 0)) {
- value = applyMergedProperties(base, key, value, values);
- } else if (isMethod(value)) {
- value = giveMethodSuper(base, key, value, values, descs);
- }
-
- descs[key] = undefined;
- values[key] = value;
- }
-}
-
-function mergeMixins(mixins, m, descs, values, base, keys) {
- var mixin, props, key, concats, mergings, meta;
-
- function removeKeys(keyName) {
- delete descs[keyName];
- delete values[keyName];
- }
-
- for(var i=0, l=mixins.length; i= 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 mixins = Ember.meta(obj, false).mixins;
- if (mixins) {
- return !!mixins[guidFor(this)];
- }
- return false;
-};
-
-MixinPrototype.without = function() {
- var ret = new Mixin(this);
- ret._without = a_slice.call(arguments);
- return ret;
-};
-
-function _keys(ret, mixin, seen) {
- if (seen[guidFor(mixin)]) { return; }
- seen[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) {
- a_forEach.call(mixin.mixins, function(x) { _keys(ret, x, seen); });
- }
-}
-
-MixinPrototype.keys = function() {
- var keys = {}, seen = {}, ret = [];
- _keys(keys, this, seen);
- for(var key in keys) {
- if (keys.hasOwnProperty(key)) { ret.push(key); }
- }
- return ret;
-};
-
-// returns the mixins currently applied to the specified object
-// TODO: Make Ember.mixin
-Mixin.mixins = function(obj) {
- var mixins = Ember.meta(obj, false).mixins, ret = [];
-
- if (!mixins) { return ret; }
-
- for (var key in mixins) {
- var mixin = mixins[key];
-
- // skip primitive mixins since these are always anonymous
- if (!mixin.properties) { ret.push(mixin); }
- }
-
- return ret;
-};
-
-REQUIRED = new Ember.Descriptor();
-REQUIRED.toString = function() { return '(Required Property)'; };
-
-/**
- Denotes a required property for a mixin
-
- @method required
- @for Ember
-*/
-Ember.required = function() {
- return REQUIRED;
-};
-
-Alias = function(methodName) {
- this.methodName = methodName;
-};
-Alias.prototype = new Ember.Descriptor();
-
-/**
- Makes a property or method available via an additional name.
-
- ```javascript
- App.PaintSample = Ember.Object.extend({
- color: 'red',
- colour: Ember.alias('color'),
- name: function() {
- return "Zed";
- },
- moniker: Ember.alias("name")
- });
-
- var paintSample = App.PaintSample.create()
- paintSample.get('colour'); // 'red'
- paintSample.moniker(); // 'Zed'
- ```
-
- @method alias
- @for Ember
- @param {String} methodName name of the method or property to alias
- @return {Ember.Descriptor}
- @deprecated Use `Ember.aliasMethod` or `Ember.computed.alias` instead
-*/
-Ember.alias = function(methodName) {
- Ember.deprecate("Ember.alias is deprecated. Please use Ember.aliasMethod or Ember.computed.alias instead.");
- return new Alias(methodName);
-};
-
-/**
- Makes a method available via an additional name.
-
- ```javascript
- App.Person = Ember.Object.extend({
- name: function() {
- return 'Tomhuda Katzdale';
- },
- moniker: Ember.aliasMethod('name')
- });
-
- var goodGuy = App.Person.create()
- ```
-
- @method aliasMethod
- @for Ember
- @param {String} methodName name of the method to alias
- @return {Ember.Descriptor}
-*/
-Ember.aliasMethod = function(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
-*/
-Ember.observer = function() {
- var func = a_slice.call(arguments, -1)[0];
- var paths = a_slice.call(arguments, 0, -1);
-
- if (typeof func !== "function") {
- // revert to old, soft-deprecated argument ordering
-
- func = arguments[0];
- paths = a_slice.call(arguments, 1);
- }
-
- if (typeof func !== "function") {
- throw new Ember.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
-*/
-Ember.immediateObserver = function() {
- for (var i=0, l=arguments.length; i 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
-*/
-Ember.beforeObserver = function() {
- var func = a_slice.call(arguments, -1)[0];
- var paths = a_slice.call(arguments, 0, -1);
-
- if (typeof func !== "function") {
- // revert to old, soft-deprecated argument ordering
-
- func = arguments[0];
- paths = a_slice.call(arguments, 1);
- }
-
- if (typeof func !== "function") {
- throw new Ember.Error("Ember.beforeObserver called without a function");
- }
-
- func.__ember_observesBefore__ = paths;
- return func;
-};
-
-})();
-
-
-
-(function() {
-// Provides a way to register library versions with ember.
-var forEach = Ember.EnumerableUtils.forEach,
- indexOf = Ember.EnumerableUtils.indexOf;
-
-Ember.libraries = function() {
- var libraries = [];
- var coreLibIndex = 0;
-
- var getLibrary = function(name) {
- for (var i = 0; i < libraries.length; i++) {
- if (libraries[i].name === name) {
- return libraries[i];
- }
- }
- };
-
- libraries.register = function(name, version) {
- if (!getLibrary(name)) {
- libraries.push({name: name, version: version});
- }
- };
-
- libraries.registerCoreLibrary = function(name, version) {
- if (!getLibrary(name)) {
- libraries.splice(coreLibIndex++, 0, {name: name, version: version});
- }
- };
-
- libraries.deRegister = function(name) {
- var lib = getLibrary(name);
- if (lib) libraries.splice(indexOf(libraries, lib), 1);
- };
-
- libraries.each = function (callback) {
- forEach(libraries, function(lib) {
- callback(lib.name, lib.version);
- });
- };
-
- return libraries;
-}();
-
-Ember.libraries.registerCoreLibrary('Ember', Ember.VERSION);
-
-})();
-
-
-
-(function() {
-/**
-Ember Metal
-
-@module ember
-@submodule ember-metal
-*/
-
-})();
-
-(function() {
-define("rsvp/all",
- ["rsvp/promise","exports"],
- function(__dependency1__, __exports__) {
- "use strict";
- var Promise = __dependency1__.Promise;
- /* global toString */
-
-
- function all(promises) {
- if (Object.prototype.toString.call(promises) !== "[object Array]") {
- throw new TypeError('You must pass an array to all.');
- }
-
- return new Promise(function(resolve, reject) {
- var results = [], remaining = promises.length,
- promise;
-
- if (remaining === 0) {
- resolve([]);
- }
-
- function resolver(index) {
- return function(value) {
- resolveAll(index, value);
- };
- }
-
- function resolveAll(index, value) {
- results[index] = value;
- if (--remaining === 0) {
- resolve(results);
- }
- }
-
- for (var i = 0; i < promises.length; i++) {
- promise = promises[i];
-
- if (promise && typeof promise.then === 'function') {
- promise.then(resolver(i), reject);
- } else {
- resolveAll(i, promise);
- }
- }
- });
- }
-
-
- __exports__.all = all;
- });
-define("rsvp/async",
- ["exports"],
- function(__exports__) {
- "use strict";
- var browserGlobal = (typeof window !== 'undefined') ? window : {};
- var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
- var async;
- var local = (typeof global !== 'undefined') ? global : this;
-
- // old node
- function useNextTick() {
- return function(callback, arg) {
- process.nextTick(function() {
- callback(arg);
- });
- };
- }
-
- // node >= 0.10.x
- function useSetImmediate() {
- return function(callback, arg) {
- /* global setImmediate */
- setImmediate(function(){
- callback(arg);
- });
- };
- }
-
- function useMutationObserver() {
- var queue = [];
-
- var observer = new BrowserMutationObserver(function() {
- var toProcess = queue.slice();
- queue = [];
-
- toProcess.forEach(function(tuple) {
- var callback = tuple[0], arg= tuple[1];
- callback(arg);
- });
- });
-
- var element = document.createElement('div');
- observer.observe(element, { attributes: true });
-
- // Chrome Memory Leak: https://bugs.webkit.org/show_bug.cgi?id=93661
- window.addEventListener('unload', function(){
- observer.disconnect();
- observer = null;
- }, false);
-
- return function(callback, arg) {
- queue.push([callback, arg]);
- element.setAttribute('drainQueue', 'drainQueue');
- };
- }
-
- function useSetTimeout() {
- return function(callback, arg) {
- local.setTimeout(function() {
- callback(arg);
- }, 1);
- };
- }
-
- if (typeof setImmediate === 'function') {
- async = useSetImmediate();
- } else if (typeof process !== 'undefined' && {}.toString.call(process) === '[object process]') {
- async = useNextTick();
- } else if (BrowserMutationObserver) {
- async = useMutationObserver();
- } else {
- async = useSetTimeout();
- }
-
-
- __exports__.async = async;
- });
-define("rsvp/config",
- ["rsvp/async","exports"],
- function(__dependency1__, __exports__) {
- "use strict";
- var async = __dependency1__.async;
-
- var config = {};
- config.async = async;
-
-
- __exports__.config = config;
- });
-define("rsvp/defer",
- ["rsvp/promise","exports"],
- function(__dependency1__, __exports__) {
- "use strict";
- var Promise = __dependency1__.Promise;
-
- function defer() {
- var deferred = {
- // pre-allocate shape
- resolve: undefined,
- reject: undefined,
- promise: undefined
- };
-
- deferred.promise = new Promise(function(resolve, reject) {
- deferred.resolve = resolve;
- deferred.reject = reject;
- });
-
- return deferred;
- }
-
-
- __exports__.defer = defer;
- });
-define("rsvp/events",
- ["exports"],
- function(__exports__) {
- "use strict";
- var Event = function(type, options) {
- this.type = type;
-
- for (var option in options) {
- if (!options.hasOwnProperty(option)) { continue; }
-
- this[option] = options[option];
- }
- };
-
- var indexOf = function(callbacks, callback) {
- for (var i=0, l=callbacks.length; i 2) {
- resolve(Array.prototype.slice.call(arguments, 1));
- } else {
- resolve(value);
- }
- };
- }
-
- function denodeify(nodeFunc) {
- return function() {
- var nodeArgs = Array.prototype.slice.call(arguments), resolve, reject;
- var thisArg = this;
-
- var promise = new Promise(function(nodeResolve, nodeReject) {
- resolve = nodeResolve;
- reject = nodeReject;
- });
-
- all(nodeArgs).then(function(nodeArgs) {
- nodeArgs.push(makeNodeCallbackFor(resolve, reject));
-
- try {
- nodeFunc.apply(thisArg, nodeArgs);
- } catch(e) {
- reject(e);
- }
- });
-
- return promise;
- };
- }
-
-
- __exports__.denodeify = denodeify;
- });
-define("rsvp/promise",
- ["rsvp/config","rsvp/events","exports"],
- function(__dependency1__, __dependency2__, __exports__) {
- "use strict";
- var config = __dependency1__.config;
- var EventTarget = __dependency2__.EventTarget;
-
- function objectOrFunction(x) {
- return isFunction(x) || (typeof x === "object" && x !== null);
- }
-
- function isFunction(x){
- return typeof x === "function";
- }
-
- var Promise = function(resolver) {
- var promise = this,
- resolved = false;
-
- if (typeof resolver !== 'function') {
- throw new TypeError('You must pass a resolver function as the sole argument to the promise constructor');
- }
-
- if (!(promise instanceof Promise)) {
- return new Promise(resolver);
- }
-
- var resolvePromise = function(value) {
- if (resolved) { return; }
- resolved = true;
- resolve(promise, value);
- };
-
- var rejectPromise = function(value) {
- if (resolved) { return; }
- resolved = true;
- reject(promise, value);
- };
-
- this.on('promise:resolved', function(event) {
- this.trigger('success', { detail: event.detail });
- }, this);
-
- this.on('promise:failed', function(event) {
- this.trigger('error', { detail: event.detail });
- }, this);
-
- this.on('error', onerror);
-
- try {
- resolver(resolvePromise, rejectPromise);
- } catch(e) {
- rejectPromise(e);
- }
- };
-
- function onerror(event) {
- if (config.onerror) {
- config.onerror(event.detail);
- }
- }
-
- var invokeCallback = function(type, promise, callback, event) {
- var hasCallback = isFunction(callback),
- value, error, succeeded, failed;
-
- if (hasCallback) {
- try {
- value = callback(event.detail);
- succeeded = true;
- } catch(e) {
- failed = true;
- error = e;
- }
- } else {
- value = event.detail;
- succeeded = true;
- }
-
- if (handleThenable(promise, value)) {
- return;
- } else if (hasCallback && succeeded) {
- resolve(promise, value);
- } else if (failed) {
- reject(promise, error);
- } else if (type === 'resolve') {
- resolve(promise, value);
- } else if (type === 'reject') {
- reject(promise, value);
- }
- };
-
- Promise.prototype = {
- constructor: Promise,
-
- isRejected: undefined,
- isFulfilled: undefined,
- rejectedReason: undefined,
- fulfillmentValue: undefined,
-
- then: function(done, fail) {
- this.off('error', onerror);
-
- var thenPromise = new this.constructor(function() {});
-
- if (this.isFulfilled) {
- config.async(function(promise) {
- invokeCallback('resolve', thenPromise, done, { detail: promise.fulfillmentValue });
- }, this);
- }
-
- if (this.isRejected) {
- config.async(function(promise) {
- invokeCallback('reject', thenPromise, fail, { detail: promise.rejectedReason });
- }, this);
- }
-
- this.on('promise:resolved', function(event) {
- invokeCallback('resolve', thenPromise, done, event);
- });
-
- this.on('promise:failed', function(event) {
- invokeCallback('reject', thenPromise, fail, event);
- });
-
- return thenPromise;
- },
-
- fail: function(fail) {
- return this.then(null, fail);
- }
- };
-
- EventTarget.mixin(Promise.prototype);
-
- function resolve(promise, value) {
- if (promise === value) {
- fulfill(promise, value);
- } else if (!handleThenable(promise, value)) {
- fulfill(promise, value);
- }
- }
-
- function handleThenable(promise, value) {
- var then = null,
- resolved;
-
- try {
- if (promise === value) {
- throw new TypeError("A promises callback cannot return that same promise.");
- }
-
- if (objectOrFunction(value)) {
- then = value.then;
-
- if (isFunction(then)) {
- then.call(value, function(val) {
- if (resolved) { return true; }
- resolved = true;
-
- if (value !== val) {
- resolve(promise, val);
- } else {
- fulfill(promise, val);
- }
- }, function(val) {
- if (resolved) { return true; }
- resolved = true;
-
- reject(promise, val);
- });
-
- return true;
- }
- }
- } catch (error) {
- reject(promise, error);
- return true;
- }
-
- return false;
- }
-
- function fulfill(promise, value) {
- config.async(function() {
- promise.trigger('promise:resolved', { detail: value });
- promise.isFulfilled = true;
- promise.fulfillmentValue = value;
- });
- }
-
- function reject(promise, value) {
- config.async(function() {
- promise.trigger('promise:failed', { detail: value });
- promise.isRejected = true;
- promise.rejectedReason = value;
- });
- }
-
-
- __exports__.Promise = Promise;
- });
-define("rsvp/reject",
- ["rsvp/promise","exports"],
- function(__dependency1__, __exports__) {
- "use strict";
- var Promise = __dependency1__.Promise;
-
- function reject(reason) {
- return new Promise(function (resolve, reject) {
- reject(reason);
- });
- }
-
-
- __exports__.reject = reject;
- });
-define("rsvp/resolve",
- ["rsvp/promise","exports"],
- function(__dependency1__, __exports__) {
- "use strict";
- var Promise = __dependency1__.Promise;
-
- function resolve(thenable) {
- return new Promise(function(resolve, reject) {
- resolve(thenable);
- });
- }
-
-
- __exports__.resolve = resolve;
- });
-define("rsvp/rethrow",
- ["exports"],
- function(__exports__) {
- "use strict";
- var local = (typeof global === "undefined") ? this : global;
-
- function rethrow(reason) {
- local.setTimeout(function() {
- throw reason;
- });
- throw reason;
- }
-
-
- __exports__.rethrow = rethrow;
- });
-define("rsvp",
- ["rsvp/events","rsvp/promise","rsvp/node","rsvp/all","rsvp/hash","rsvp/rethrow","rsvp/defer","rsvp/config","rsvp/resolve","rsvp/reject","exports"],
- function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __exports__) {
- "use strict";
- var EventTarget = __dependency1__.EventTarget;
- var Promise = __dependency2__.Promise;
- var denodeify = __dependency3__.denodeify;
- var all = __dependency4__.all;
- var hash = __dependency5__.hash;
- var rethrow = __dependency6__.rethrow;
- var defer = __dependency7__.defer;
- var config = __dependency8__.config;
- var resolve = __dependency9__.resolve;
- var reject = __dependency10__.reject;
-
- function configure(name, value) {
- config[name] = value;
- }
-
-
- __exports__.Promise = Promise;
- __exports__.EventTarget = EventTarget;
- __exports__.all = all;
- __exports__.hash = hash;
- __exports__.rethrow = rethrow;
- __exports__.defer = defer;
- __exports__.denodeify = denodeify;
- __exports__.configure = configure;
- __exports__.resolve = resolve;
- __exports__.reject = reject;
- });
-})();
-
-(function() {
-/**
-@private
-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
-*/
-
-/*
- 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 || !!Ember.ENV.MODEL_FACTORY_INJECTIONS;
-
-define("container",
- [],
- function() {
-
- // A safe and simple inheriting object.
- function InheritingDict(parent) {
- this.parent = parent;
- this.dict = {};
- }
-
- InheritingDict.prototype = {
-
- /**
- @property parent
- @type InheritingDict
- @default null
- */
-
- parent: null,
-
- /**
- Object used to store the current nodes data.
-
- @property dict
- @type Object
- @default Object
- */
- dict: null,
-
- /**
- Retrieve the value given a key, if the value is present at the current
- level use it, otherwise walk up the parent hierarchy and try again. If
- no matching key is found, return undefined.
-
- @method get
- @return {any}
- */
- get: function(key) {
- var dict = this.dict;
-
- if (dict.hasOwnProperty(key)) {
- return dict[key];
- }
-
- if (this.parent) {
- return this.parent.get(key);
- }
- },
-
- /**
- Set the given value for the given key, at the current level.
-
- @method set
- @param {String} key
- @param {Any} value
- */
- set: function(key, value) {
- this.dict[key] = value;
- },
-
- /**
- Delete the given key
-
- @method remove
- @param {String} key
- */
- remove: function(key) {
- delete this.dict[key];
- },
-
- /**
- Check for the existence of given a key, if the key is present at the current
- level return true, otherwise walk up the parent hierarchy and try again. If
- no matching key is found, return false.
-
- @method has
- @param {String} key
- @return {Boolean}
- */
- has: function(key) {
- var dict = this.dict;
-
- if (dict.hasOwnProperty(key)) {
- return true;
- }
-
- if (this.parent) {
- return this.parent.has(key);
- }
-
- return false;
- },
-
- /**
- Iterate and invoke a callback for each local key-value pair.
-
- @method eachLocal
- @param {Function} callback
- @param {Object} binding
- */
- eachLocal: function(callback, binding) {
- var dict = this.dict;
-
- for (var prop in dict) {
- if (dict.hasOwnProperty(prop)) {
- callback.call(binding, prop, dict[prop]);
- }
- }
- }
- };
-
-
- // A lightweight container that helps to assemble and decouple components.
- // Public api for the container is still in flux.
- // The public api, specified on the application namespace should be considered the stable api.
- function Container(parent) {
- this.parent = parent;
- this.children = [];
-
- this.resolver = parent && parent.resolver || function() {};
-
- this.registry = new InheritingDict(parent && parent.registry);
- this.cache = new InheritingDict(parent && parent.cache);
- this.factoryCache = new InheritingDict(parent && parent.cache);
- this.typeInjections = new InheritingDict(parent && parent.typeInjections);
- this.injections = {};
-
- this.factoryTypeInjections = new InheritingDict(parent && parent.factoryTypeInjections);
- this.factoryInjections = {};
-
- this._options = new InheritingDict(parent && parent._options);
- this._typeOptions = new InheritingDict(parent && parent._typeOptions);
- }
-
- Container.prototype = {
-
- /**
- @property parent
- @type Container
- @default null
- */
- parent: null,
-
- /**
- @property children
- @type Array
- @default []
- */
- children: null,
-
- /**
- @property resolver
- @type function
- */
- resolver: null,
-
- /**
- @property registry
- @type InheritingDict
- */
- registry: null,
-
- /**
- @property cache
- @type InheritingDict
- */
- cache: null,
-
- /**
- @property typeInjections
- @type InheritingDict
- */
- typeInjections: null,
-
- /**
- @property injections
- @type Object
- @default {}
- */
- injections: null,
-
- /**
- @private
-
- @property _options
- @type InheritingDict
- @default null
- */
- _options: null,
-
- /**
- @private
-
- @property _typeOptions
- @type InheritingDict
- */
- _typeOptions: null,
-
- /**
- Returns a new child of the current container. These children are configured
- to correctly inherit from the current container.
-
- @method child
- @return {Container}
- */
- child: function() {
- var container = new Container(this);
- this.children.push(container);
- return container;
- },
-
- /**
- Sets a key-value pair on the current container. If a parent container,
- has the same key, once set on a child, the parent and child will diverge
- as expected.
-
- @method set
- @param {Object} object
- @param {String} key
- @param {any} value
- */
- set: function(object, key, value) {
- object[key] = value;
- },
-
- /**
- Registers a factory for later injection.
-
- Example:
-
- ```javascript
- var container = new Container();
-
- container.register('model:user', Person, {singleton: false });
- container.register('fruit:favorite', Orange);
- container.register('communication:main', Email, {singleton: false});
- ```
-
- @method register
- @param {String} fullName
- @param {Function} factory
- @param {Object} options
- */
- register: function(fullName, factory, options) {
- if (fullName.indexOf(':') === -1) {
- throw new TypeError("malformed fullName, expected: `type:name` got: " + fullName + "");
- }
-
- if (factory === undefined) {
- throw new TypeError('Attempting to register an unknown factory: `' + fullName + '`');
- }
-
- var normalizedName = this.normalize(fullName);
-
- if (this.cache.has(normalizedName)) {
- throw new Error('Cannot re-register: `' + fullName +'`, as it has already been looked up.');
- }
-
- this.registry.set(normalizedName, factory);
- this._options.set(normalizedName, options || {});
- },
-
- /**
- Unregister a fullName
-
- ```javascript
- var container = new Container();
- container.register('model:user', User);
-
- container.lookup('model:user') instanceof User //=> true
-
- container.unregister('model:user')
- container.lookup('model:user') === undefined //=> true
- ```
-
- @method unregister
- @param {String} fullName
- */
- unregister: function(fullName) {
- var normalizedName = this.normalize(fullName);
-
- this.registry.remove(normalizedName);
- this.cache.remove(normalizedName);
- this.factoryCache.remove(normalizedName);
- this._options.remove(normalizedName);
- },
-
- /**
- Given a fullName return the corresponding factory.
-
- By default `resolve` will retrieve the factory from
- its container's registry.
-
- ```javascript
- var container = new Container();
- container.register('api:twitter', Twitter);
-
- container.resolve('api:twitter') // => Twitter
- ```
-
- Optionally the container can be provided with a custom resolver.
- If provided, `resolve` will first provide the custom resolver
- the oppertunity to resolve the fullName, otherwise it will fallback
- to the registry.
-
- ```javascript
- var container = new Container();
- container.resolver = function(fullName) {
- // lookup via the module system of choice
- };
-
- // the twitter factory is added to the module system
- container.resolve('api:twitter') // => Twitter
- ```
-
- @method resolve
- @param {String} fullName
- @return {Function} fullName's factory
- */
- resolve: function(fullName) {
- return this.resolver(fullName) || this.registry.get(fullName);
- },
-
- /**
- 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
- */
- describe: function(fullName) {
- return fullName;
- },
-
- /**
- A hook to enable custom fullName normalization behaviour
-
- @method normalize
- @param {String} fullName
- @return {string} normalized fullName
- */
- normalize: function(fullName) {
- return fullName;
- },
-
- /**
- @method makeToString
-
- @param {any} factory
- @param {string} fullName
- @return {function} toString function
- */
- makeToString: function(factory, fullName) {
- return factory.toString();
- },
-
- /**
- 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 container = new Container();
- container.register('api:twitter', Twitter);
-
- var twitter = container.lookup('api:twitter');
-
- twitter instanceof Twitter; // => true
-
- // by default the container will return singletons
- twitter2 = container.lookup('api:twitter');
- twitter instanceof Twitter; // => true
-
- twitter === twitter2; //=> true
- ```
-
- If singletons are not wanted an optional flag can be provided at lookup.
-
- ```javascript
- var container = new Container();
- container.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) {
- fullName = this.normalize(fullName);
-
- options = options || {};
-
- if (this.cache.has(fullName) && options.singleton !== false) {
- return this.cache.get(fullName);
- }
-
- var value = instantiate(this, fullName);
-
- if (value === undefined) { return; }
-
- if (isSingleton(this, fullName) && options.singleton !== false) {
- this.cache.set(fullName, value);
- }
-
- return value;
- },
-
- /**
- Given a fullName return the corresponding factory.
-
- @method lookupFactory
- @param {String} fullName
- @return {any}
- */
- lookupFactory: function(fullName) {
- return factoryFor(this, fullName);
- },
-
- /**
- 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) {
- if (this.cache.has(fullName)) {
- return true;
- }
-
- return !!factoryFor(this, fullName);
- },
-
- /**
- Allow registering options for all factories of a type.
-
- ```javascript
- var container = new Container();
-
- // if all of type `connection` must not be singletons
- container.optionsForType('connection', { singleton: false });
-
- container.register('connection:twitter', TwitterConnection);
- container.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) {
- if (this.parent) { illegalChildOperation('optionsForType'); }
-
- this._typeOptions.set(type, options);
- },
-
- /**
- @method options
- @param {String} type
- @param {Object} options
- */
- options: function(type, options) {
- this.optionsForType(type, options);
- },
-
- /**
- @private
-
- 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 container = new Container();
-
- container.register('router:main', Router);
- container.register('controller:user', UserController);
- container.register('controller:post', PostController);
-
- container.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
- ```
-
- @method typeInjection
- @param {String} type
- @param {String} property
- @param {String} fullName
- */
- typeInjection: function(type, property, fullName) {
- if (this.parent) { illegalChildOperation('typeInjection'); }
-
- addTypeInjection(this.typeInjections, type, property, 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 container = new Container();
-
- container.register('source:main', Source);
- container.register('model:user', User);
- container.register('model:post', Post);
-
- // injecting one fullName on another fullName
- // eg. each user model gets a post model
- container.injection('model:user', 'post', 'model:post');
-
- // injecting one fullName on another type
- container.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(factoryName, property, injectionName) {
- if (this.parent) { illegalChildOperation('injection'); }
-
- if (factoryName.indexOf(':') === -1) {
- return this.typeInjection(factoryName, property, injectionName);
- }
-
- addInjection(this.injections, factoryName, property, injectionName);
- },
-
-
- /**
- @private
-
- 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 container = new Container();
-
- container.registerFactory('model:user', User);
- container.register('store:main', SomeStore);
-
- container.factoryTypeInjection('model', 'store', 'store:main');
-
- var store = container.lookup('store:main');
- var UserFactory = container.lookupFactory('model:user');
-
- UserFactory.store instanceof SomeStore; //=> true
- ```
-
- @method factoryTypeInjection
- @param {String} type
- @param {String} property
- @param {String} fullName
- */
- factoryTypeInjection: function(type, property, fullName) {
- if (this.parent) { illegalChildOperation('factoryTypeInjection'); }
-
- addTypeInjection(this.factoryTypeInjections, type, property, fullName);
- },
-
- /**
- Defines factory injection rules.
-
- Similar to regular injection rules, but are run against factories, via
- `Container#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 container = new Container();
-
- container.register('store:main', Store);
- container.register('store:secondary', OtherStore);
- container.register('model:user', User);
- container.register('model:post', Post);
-
- // injecting one fullName on another type
- container.factoryInjection('model', 'store', 'store:main');
-
- // injecting one fullName on another fullName
- container.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(factoryName, property, injectionName) {
- if (this.parent) { illegalChildOperation('injection'); }
-
- if (factoryName.indexOf(':') === -1) {
- return this.factoryTypeInjection(factoryName, property, injectionName);
- }
-
- addInjection(this.factoryInjections, factoryName, property, injectionName);
- },
-
- /**
- A depth first traversal, destroying the container, its descendant containers and all
- their managed objects.
-
- @method destroy
- */
- destroy: function() {
- this.isDestroyed = true;
-
- for (var i=0, l=this.children.length; i w.
-*/
-Ember.compare = function compare(v, w) {
- if (v === w) { return 0; }
-
- var type1 = Ember.typeOf(v);
- var type2 = Ember.typeOf(w);
-
- var Comparable = Ember.Comparable;
- if (Comparable) {
- if (type1==='instance' && Comparable.detect(v.constructor)) {
- return v.constructor.compare(v, w);
- }
-
- if (type2 === 'instance' && Comparable.detect(w.constructor)) {
- return 1-w.constructor.compare(w, v);
- }
- }
-
- // If we haven't yet generated a reverse-mapping of Ember.ORDER_DEFINITION,
- // do so now.
- var mapping = Ember.ORDER_DEFINITION_MAPPING;
- if (!mapping) {
- var order = Ember.ORDER_DEFINITION;
- mapping = Ember.ORDER_DEFINITION_MAPPING = {};
- var idx, len;
- for (idx = 0, len = order.length; idx < len; ++idx) {
- mapping[order[idx]] = idx;
- }
-
- // We no longer need Ember.ORDER_DEFINITION.
- delete Ember.ORDER_DEFINITION;
- }
-
- var type1Index = mapping[type1];
- var type2Index = mapping[type2];
-
- if (type1Index < type2Index) { return -1; }
- if (type1Index > type2Index) { return 1; }
-
- // types are equal - so we have to check values now
- switch (type1) {
- case 'boolean':
- case 'number':
- if (v < w) { return -1; }
- if (v > w) { return 1; }
- return 0;
-
- case 'string':
- var comp = v.localeCompare(w);
- if (comp < 0) { return -1; }
- if (comp > 0) { return 1; }
- return 0;
-
- case 'array':
- var vLen = v.length;
- var wLen = w.length;
- var l = Math.min(vLen, wLen);
- var r = 0;
- var i = 0;
- while (r === 0 && i < l) {
- r = compare(v[i],w[i]);
- i++;
- }
- if (r !== 0) { return r; }
-
- // all elements are equal now
- // shorter array should be ordered first
- if (vLen < wLen) { return -1; }
- if (vLen > wLen) { return 1; }
- // arrays are equal now
- return 0;
-
- case 'instance':
- if (Ember.Comparable && Ember.Comparable.detect(v)) {
- return v.compare(v, w);
- }
- return 0;
-
- case 'date':
- var vNum = v.getTime();
- var wNum = w.getTime();
- if (vNum < wNum) { return -1; }
- if (vNum > wNum) { return 1; }
- return 0;
-
- default:
- return 0;
- }
-};
-
-function _copy(obj, deep, seen, copies) {
- var ret, loc, key;
-
- // primitive data types are immutable, just return them.
- if ('object' !== typeof obj || obj===null) return obj;
-
- // avoid cyclical loops
- if (deep && (loc=indexOf(seen, obj))>=0) return copies[loc];
-
- Ember.assert('Cannot clone an Ember.Object that does not implement Ember.Copyable', !(obj instanceof Ember.Object) || (Ember.Copyable && Ember.Copyable.detect(obj)));
-
- // IMPORTANT: this specific test will detect a native array only. Any other
- // object will need to implement Copyable.
- if (Ember.typeOf(obj) === 'array') {
- ret = obj.slice();
- if (deep) {
- loc = ret.length;
- while(--loc>=0) ret[loc] = _copy(ret[loc], deep, seen, copies);
- }
- } else if (Ember.Copyable && Ember.Copyable.detect(obj)) {
- ret = obj.copy(deep, seen, copies);
- } else {
- ret = {};
- for(key in obj) {
- if (!obj.hasOwnProperty(key)) continue;
-
- // Prevents browsers that don't respect non-enumerability from
- // copying internal Ember properties
- if (key.substring(0,2) === '__') continue;
-
- ret[key] = deep ? _copy(obj[key], deep, seen, copies) : obj[key];
- }
- }
-
- if (deep) {
- seen.push(obj);
- copies.push(ret);
- }
-
- return ret;
-}
-
-/**
- Creates a clone of the passed object. This function can take just about
- any type of object and create a clone of it, including primitive values
- (which are not actually cloned because they are immutable).
-
- If the passed object implements the `clone()` method, then this function
- will simply call that method and return the result.
-
- @method copy
- @for Ember
- @param {Object} obj The object to clone
- @param {Boolean} deep If true, a deep copy of the object is made
- @return {Object} The cloned object
-*/
-Ember.copy = function(obj, deep) {
- // fast paths
- if ('object' !== typeof obj || obj===null) return obj; // can't copy primitives
- if (Ember.Copyable && Ember.Copyable.detect(obj)) return obj.copy(deep);
- return _copy(obj, deep, deep ? [] : null, deep ? [] : null);
-};
-
-/**
- Convenience method to inspect an object. This method will attempt to
- convert the object into a useful string description.
-
- It is a pretty simple implementation. If you want something more robust,
- use something like JSDump: https://github.com/NV/jsDump
-
- @method inspect
- @for Ember
- @param {Object} obj The object you want to inspect.
- @return {String} A description of the object
-*/
-Ember.inspect = function(obj) {
- var type = Ember.typeOf(obj);
- if (type === 'array') {
- return '[' + obj + ']';
- }
- if (type !== 'object') {
- return obj + '';
- }
-
- var v, ret = [];
- for(var key in obj) {
- if (obj.hasOwnProperty(key)) {
- v = obj[key];
- if (v === 'toString') { continue; } // ignore useless items
- if (Ember.typeOf(v) === 'function') { v = "function() { ... }"; }
- ret.push(key + ": " + v);
- }
- }
- return "{" + ret.join(", ") + "}";
-};
-
-/**
- Compares two objects, returning true if they are logically equal. This is
- a deeper comparison than a simple triple equal. For sets it will compare the
- internal objects. For any other object that implements `isEqual()` it will
- respect that method.
-
- ```javascript
- Ember.isEqual('hello', 'hello'); // true
- Ember.isEqual(1, 2); // false
- Ember.isEqual([4,2], [4,2]); // false
- ```
-
- @method isEqual
- @for Ember
- @param {Object} a first object to compare
- @param {Object} b second object to compare
- @return {Boolean}
-*/
-Ember.isEqual = function(a, b) {
- if (a && 'function'===typeof a.isEqual) return a.isEqual(b);
- return a === b;
-};
-
-// Used by Ember.compare
-Ember.ORDER_DEFINITION = Ember.ENV.ORDER_DEFINITION || [
- 'undefined',
- 'null',
- 'boolean',
- 'number',
- 'string',
- 'array',
- 'object',
- 'instance',
- 'function',
- 'class',
- 'date'
-];
-
-/**
- Returns all of the keys defined on an object or hash. This is useful
- when inspecting objects for debugging. On browsers that support it, this
- uses the native `Object.keys` implementation.
-
- @method keys
- @for Ember
- @param {Object} obj
- @return {Array} Array containing keys of obj
-*/
-Ember.keys = Object.keys;
-
-if (!Ember.keys || Ember.create.isSimulated) {
- var prototypeProperties = [
- 'constructor',
- 'hasOwnProperty',
- 'isPrototypeOf',
- 'propertyIsEnumerable',
- 'valueOf',
- 'toLocaleString',
- 'toString'
- ],
- pushPropertyName = function(obj, array, key) {
- // Prevents browsers that don't respect non-enumerability from
- // copying internal Ember properties
- if (key.substring(0,2) === '__') return;
- if (key === '_super') return;
- if (indexOf(array, key) >= 0) return;
- if (!obj.hasOwnProperty(key)) return;
-
- array.push(key);
- };
-
- Ember.keys = function(obj) {
- var ret = [], key;
- for (key in obj) {
- pushPropertyName(obj, ret, key);
- }
-
- // IE8 doesn't enumerate property that named the same as prototype properties.
- for (var i = 0, l = prototypeProperties.length; i < l; i++) {
- key = prototypeProperties[i];
-
- pushPropertyName(obj, ret, key);
- }
-
- return ret;
- };
-}
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-runtime
-*/
-
-var STRING_DASHERIZE_REGEXP = (/[ _]/g);
-var STRING_DASHERIZE_CACHE = {};
-var STRING_DECAMELIZE_REGEXP = (/([a-z\d])([A-Z])/g);
-var STRING_CAMELIZE_REGEXP = (/(\-|_|\.|\s)+(.)?/g);
-var STRING_UNDERSCORE_REGEXP_1 = (/([a-z\d])([A-Z]+)/g);
-var STRING_UNDERSCORE_REGEXP_2 = (/\-|\s+/g);
-
-/**
- Defines the hash of localized strings for the current language. Used by
- the `Ember.String.loc()` helper. To localize, add string values to this
- hash.
-
- @property STRINGS
- @for Ember
- @type Hash
-*/
-Ember.STRINGS = {};
-
-/**
- Defines string helper methods including string formatting and localization.
- Unless `Ember.EXTEND_PROTOTYPES.String` is `false` these methods will also be
- added to the `String.prototype` as well.
-
- @class String
- @namespace Ember
- @static
-*/
-Ember.String = {
-
- /**
- Apply formatting options to the string. This will look for occurrences
- of "%@" in your string and substitute them with the arguments you pass into
- this method. If you want to control the specific order of replacement,
- you can add a number after the key as well to indicate which argument
- you want to insert.
-
- Ordered insertions are most useful when building loc strings where values
- you need to insert may appear in different orders.
-
- ```javascript
- "Hello %@ %@".fmt('John', 'Doe'); // "Hello John Doe"
- "Hello %@2, %@1".fmt('John', 'Doe'); // "Hello Doe, John"
- ```
-
- @method fmt
- @param {String} str The string to format
- @param {Array} formats An array of parameters to interpolate into string.
- @return {String} formatted string
- */
- fmt: function(str, formats) {
- // first, replace any ORDERED replacements.
- var idx = 0; // the current index for non-numerical replacements
- return str.replace(/%@([0-9]+)?/g, function(s, argIndex) {
- argIndex = (argIndex) ? parseInt(argIndex, 10) - 1 : idx++;
- s = formats[argIndex];
- return (s === null) ? '(null)' : (s === undefined) ? '' : Ember.inspect(s);
- }) ;
- },
-
- /**
- Formats the passed string, but first looks up the string in the localized
- strings hash. This is a convenient way to localize text. See
- `Ember.String.fmt()` for more information on formatting.
-
- Note that it is traditional but not required to prefix localized string
- keys with an underscore or other character so you can easily identify
- localized strings.
-
- ```javascript
- Ember.STRINGS = {
- '_Hello World': 'Bonjour le monde',
- '_Hello %@ %@': 'Bonjour %@ %@'
- };
-
- Ember.String.loc("_Hello World"); // 'Bonjour le monde';
- Ember.String.loc("_Hello %@ %@", ["John", "Smith"]); // "Bonjour John Smith";
- ```
-
- @method loc
- @param {String} str The string to format
- @param {Array} formats Optional array of parameters to interpolate into string.
- @return {String} formatted string
- */
- loc: function(str, formats) {
- str = Ember.STRINGS[str] || str;
- return Ember.String.fmt(str, formats) ;
- },
-
- /**
- Splits a string into separate units separated by spaces, eliminating any
- empty strings in the process. This is a convenience method for split that
- is mostly useful when applied to the `String.prototype`.
-
- ```javascript
- Ember.String.w("alpha beta gamma").forEach(function(key) {
- console.log(key);
- });
-
- // > alpha
- // > beta
- // > gamma
- ```
-
- @method w
- @param {String} str The string to split
- @return {String} split string
- */
- w: function(str) { return str.split(/\s+/); },
-
- /**
- Converts a camelized string into all lower case separated by underscores.
-
- ```javascript
- 'innerHTML'.decamelize(); // 'inner_html'
- 'action_name'.decamelize(); // 'action_name'
- 'css-class-name'.decamelize(); // 'css-class-name'
- 'my favorite items'.decamelize(); // 'my favorite items'
- ```
-
- @method decamelize
- @param {String} str The string to decamelize.
- @return {String} the decamelized string.
- */
- decamelize: function(str) {
- return str.replace(STRING_DECAMELIZE_REGEXP, '$1_$2').toLowerCase();
- },
-
- /**
- Replaces underscores, spaces, or camelCase with dashes.
-
- ```javascript
- 'innerHTML'.dasherize(); // 'inner-html'
- 'action_name'.dasherize(); // 'action-name'
- 'css-class-name'.dasherize(); // 'css-class-name'
- 'my favorite items'.dasherize(); // 'my-favorite-items'
- ```
-
- @method dasherize
- @param {String} str The string to dasherize.
- @return {String} the dasherized string.
- */
- dasherize: function(str) {
- var cache = STRING_DASHERIZE_CACHE,
- hit = cache.hasOwnProperty(str),
- ret;
-
- if (hit) {
- return cache[str];
- } else {
- ret = Ember.String.decamelize(str).replace(STRING_DASHERIZE_REGEXP,'-');
- cache[str] = ret;
- }
-
- return ret;
- },
-
- /**
- Returns the lowerCamelCase form of a string.
-
- ```javascript
- 'innerHTML'.camelize(); // 'innerHTML'
- 'action_name'.camelize(); // 'actionName'
- 'css-class-name'.camelize(); // 'cssClassName'
- 'my favorite items'.camelize(); // 'myFavoriteItems'
- 'My Favorite Items'.camelize(); // 'myFavoriteItems'
- ```
-
- @method camelize
- @param {String} str The string to camelize.
- @return {String} the camelized string.
- */
- camelize: function(str) {
- return str.replace(STRING_CAMELIZE_REGEXP, function(match, separator, chr) {
- return chr ? chr.toUpperCase() : '';
- }).replace(/^([A-Z])/, function(match, separator, chr) {
- return match.toLowerCase();
- });
- },
-
- /**
- Returns the UpperCamelCase form of a string.
-
- ```javascript
- 'innerHTML'.classify(); // 'InnerHTML'
- 'action_name'.classify(); // 'ActionName'
- 'css-class-name'.classify(); // 'CssClassName'
- 'my favorite items'.classify(); // 'MyFavoriteItems'
- ```
-
- @method classify
- @param {String} str the string to classify
- @return {String} the classified string
- */
- classify: function(str) {
- var parts = str.split("."),
- out = [];
-
- for (var i=0, l=parts.length; i= 0) {
- var baseValue = this[keyName];
-
- if (baseValue) {
- if ('function' === typeof baseValue.concat) {
- value = baseValue.concat(value);
- } else {
- value = Ember.makeArray(baseValue).concat(value);
- }
- } else {
- value = Ember.makeArray(value);
- }
- }
-
- if (desc) {
- desc.set(this, keyName, value);
- } else {
- if (typeof this.setUnknownProperty === 'function' && !(keyName in this)) {
- this.setUnknownProperty(keyName, value);
- } else if (MANDATORY_SETTER) {
- Ember.defineProperty(this, keyName, null, value); // setup mandatory setter
- } else {
- this[keyName] = value;
- }
- }
- }
- }
- }
- finishPartial(this, m);
- this.init.apply(this, arguments);
- m.proto = proto;
- finishChains(this);
- sendEvent(this, "init");
- };
-
- Class.toString = Mixin.prototype.toString;
- Class.willReopen = function() {
- if (wasApplied) {
- Class.PrototypeMixin = Mixin.create(Class.PrototypeMixin);
- }
-
- wasApplied = false;
- };
- Class._initMixins = function(args) { initMixins = args; };
- Class._initProperties = function(args) { initProperties = args; };
-
- Class.proto = function() {
- var superclass = Class.superclass;
- if (superclass) { superclass.proto(); }
-
- if (!wasApplied) {
- wasApplied = true;
- Class.PrototypeMixin.applyPartial(Class.prototype);
- rewatch(Class.prototype);
- }
-
- return this.prototype;
- };
-
- return Class;
-
-}
-
-/**
- @class CoreObject
- @namespace Ember
-*/
-var CoreObject = makeCtor();
-CoreObject.toString = function() { return "Ember.CoreObject"; };
-
-CoreObject.PrototypeMixin = Mixin.create({
- reopen: function() {
- applyMixin(this, arguments, true);
- return this;
- },
-
- /**
- An overridable method called when objects are instantiated. By default,
- does nothing unless it is overridden during class definition.
-
- Example:
-
- ```javascript
- App.Person = Ember.Object.extend({
- init: function() {
- alert('Name is ' + this.get('name'));
- }
- });
-
- var steve = App.Person.create({
- name: "Steve"
- });
-
- // alerts 'Name is Steve'.
- ```
-
- NOTE: If you do override `init` for a framework class like `Ember.View` or
- `Ember.ArrayController`, be sure to call `this._super()` in your
- `init` declaration! If you don't, Ember may not have an opportunity to
- do important setup work, and you'll see strange behavior in your
- application.
-
- @method init
- */
- init: function() {},
-
- /**
- Defines the properties that will be concatenated from the superclass
- (instead of overridden).
-
- By default, when you extend an Ember class a property defined in
- the subclass overrides a property with the same name that is defined
- in the superclass. However, there are some cases where it is preferable
- to build up a property's value by combining the superclass' property
- value with the subclass' value. An example of this in use within Ember
- is the `classNames` property of `Ember.View`.
-
- Here is some sample code showing the difference between a concatenated
- property and a normal one:
-
- ```javascript
- App.BarView = Ember.View.extend({
- someNonConcatenatedProperty: ['bar'],
- classNames: ['bar']
- });
-
- App.FooBarView = App.BarView.extend({
- someNonConcatenatedProperty: ['foo'],
- classNames: ['foo'],
- });
-
- var fooBarView = App.FooBarView.create();
- fooBarView.get('someNonConcatenatedProperty'); // ['foo']
- fooBarView.get('classNames'); // ['ember-view', 'bar', 'foo']
- ```
-
- This behavior extends to object creation as well. Continuing the
- above example:
-
- ```javascript
- var view = App.FooBarView.create({
- someNonConcatenatedProperty: ['baz'],
- classNames: ['baz']
- })
- view.get('someNonConcatenatedProperty'); // ['baz']
- view.get('classNames'); // ['ember-view', 'bar', 'foo', 'baz']
- ```
- Adding a single property that is not an array will just add it in the array:
-
- ```javascript
- var view = App.FooBarView.create({
- classNames: 'baz'
- })
- view.get('classNames'); // ['ember-view', 'bar', 'foo', 'baz']
- ```
-
- Using the `concatenatedProperties` property, we can tell to Ember that mix
- the content of the properties.
-
- In `Ember.View` the `classNameBindings` and `attributeBindings` properties
- are also concatenated, in addition to `classNames`.
-
- This feature is available for you to use throughout the Ember object model,
- although typical app developers are likely to use it infrequently. Since
- it changes expectations about behavior of properties, you should properly
- document its usage in each individual concatenated property (to not
- mislead your users to think they can override the property in a subclass).
-
- @property concatenatedProperties
- @type Array
- @default null
- */
- concatenatedProperties: null,
-
- /**
- Destroyed object property flag.
-
- if this property is `true` the observers and bindings were already
- removed by the effect of calling the `destroy()` method.
-
- @property isDestroyed
- @default false
- */
- isDestroyed: false,
-
- /**
- Destruction scheduled flag. The `destroy()` method has been called.
-
- The object stays intact until the end of the run loop at which point
- the `isDestroyed` flag is set.
-
- @property isDestroying
- @default false
- */
- isDestroying: false,
-
- /**
- Destroys an object by setting the `isDestroyed` flag and removing its
- metadata, which effectively destroys observers and bindings.
-
- If you try to set a property on a destroyed object, an exception will be
- raised.
-
- Note that destruction is scheduled for the end of the run loop and does not
- happen immediately. It will set an isDestroying flag immediately.
-
- @method destroy
- @return {Ember.Object} receiver
- */
- destroy: function() {
- if (this.isDestroying) { return; }
- this.isDestroying = true;
-
- schedule('actions', this, this.willDestroy);
- schedule('destroy', this, this._scheduledDestroy);
- return this;
- },
-
- /**
- Override to implement teardown.
-
- @method willDestroy
- */
- willDestroy: Ember.K,
-
- /**
- @private
-
- Invoked by the run loop to actually destroy the object. This is
- scheduled for execution by the `destroy` method.
-
- @method _scheduledDestroy
- */
- _scheduledDestroy: function() {
- if (this.isDestroyed) { return; }
- destroy(this);
- this.isDestroyed = true;
- },
-
- bind: function(to, from) {
- if (!(from instanceof Ember.Binding)) { from = Ember.Binding.from(from); }
- from.to(to).connect(this);
- return from;
- },
-
- /**
- Returns a string representation which attempts to provide more information
- than Javascript's `toString` typically does, in a generic way for all Ember
- objects.
-
- App.Person = Em.Object.extend()
- person = App.Person.create()
- person.toString() //=> ""
-
- If the object's class is not defined on an Ember namespace, it will
- indicate it is a subclass of the registered superclass:
-
- Student = App.Person.extend()
- student = Student.create()
- student.toString() //=> "<(subclass of App.Person):ember1025>"
-
- If the method `toStringExtension` is defined, its return value will be
- included in the output.
-
- App.Teacher = App.Person.extend({
- toStringExtension: function() {
- return this.get('fullName');
- }
- });
- teacher = App.Teacher.create()
- teacher.toString(); //=> ""
-
- @method toString
- @return {String} string representation
- */
- toString: function toString() {
- var hasToStringExtension = typeof this.toStringExtension === 'function',
- extension = hasToStringExtension ? ":" + this.toStringExtension() : '';
- var ret = '<'+this.constructor.toString()+':'+guidFor(this)+extension+'>';
- this.toString = makeToString(ret);
- return ret;
- }
-});
-
-CoreObject.PrototypeMixin.ownerConstructor = CoreObject;
-
-function makeToString(ret) {
- return function() { return ret; };
-}
-
-if (Ember.config.overridePrototypeMixin) {
- Ember.config.overridePrototypeMixin(CoreObject.PrototypeMixin);
-}
-
-CoreObject.__super__ = null;
-
-var ClassMixin = Mixin.create({
-
- ClassMixin: Ember.required(),
-
- PrototypeMixin: Ember.required(),
-
- isClass: true,
-
- isMethod: false,
-
- /**
- Creates a new subclass.
-
- ```javascript
- App.Person = Ember.Object.extend({
- say: function(thing) {
- alert(thing);
- }
- });
- ```
-
- This defines a new subclass of Ember.Object: `App.Person`. It contains one method: `say()`.
-
- You can also create a subclass from any existing class by calling its `extend()` method. For example, you might want to create a subclass of Ember's built-in `Ember.View` class:
-
- ```javascript
- App.PersonView = Ember.View.extend({
- tagName: 'li',
- classNameBindings: ['isAdministrator']
- });
- ```
-
- When defining a subclass, you can override methods but still access the implementation of your parent class by calling the special `_super()` method:
-
- ```javascript
- App.Person = Ember.Object.extend({
- say: function(thing) {
- var name = this.get('name');
- alert(name + ' says: ' + thing);
- }
- });
-
- App.Soldier = App.Person.extend({
- say: function(thing) {
- this._super(thing + ", sir!");
- },
- march: function(numberOfHours) {
- alert(this.get('name') + ' marches for ' + numberOfHours + ' hours.')
- }
- });
-
- var yehuda = App.Soldier.create({
- name: "Yehuda Katz"
- });
-
- yehuda.say("Yes"); // alerts "Yehuda Katz says: Yes, sir!"
- ```
-
- The `create()` on line #17 creates an *instance* of the `App.Soldier` class. The `extend()` on line #8 creates a *subclass* of `App.Person`. Any instance of the `App.Person` class will *not* have the `march()` method.
-
- You can also pass `Ember.Mixin` classes to add additional properties to the subclass.
-
- ```javascript
- App.Person = Ember.Object.extend({
- say: function(thing) {
- alert(this.get('name') + ' says: ' + thing);
- }
- });
-
- App.SingingMixin = Ember.Mixin.create({
- sing: function(thing){
- alert(this.get('name') + ' sings: la la la ' + thing);
- }
- });
-
- App.BroadwayStar = App.Person.extend(App.SingingMixin, {
- dance: function() {
- alert(this.get('name') + ' dances: tap tap tap tap ');
- }
- });
- ```
-
- The `App.BroadwayStar` class contains three methods: `say()`, `sing()`, and `dance()`.
-
- @method extend
- @static
-
- @param {Ember.Mixin} [mixins]* One or more Ember.Mixin classes
- @param {Object} [arguments]* Object containing values to use within the new class
- */
- extend: function() {
- var Class = makeCtor(), proto;
- Class.ClassMixin = Mixin.create(this.ClassMixin);
- Class.PrototypeMixin = Mixin.create(this.PrototypeMixin);
-
- Class.ClassMixin.ownerConstructor = Class;
- Class.PrototypeMixin.ownerConstructor = Class;
-
- reopen.apply(Class.PrototypeMixin, arguments);
-
- Class.superclass = this;
- Class.__super__ = this.prototype;
-
- proto = Class.prototype = o_create(this.prototype);
- proto.constructor = Class;
- generateGuid(proto);
- meta(proto).proto = proto; // this will disable observers on prototype
-
- Class.ClassMixin.apply(Class);
- return Class;
- },
-
- /**
- Equivalent to doing `extend(arguments).create()`.
- If possible use the normal `create` method instead.
-
- @method createWithMixins
- @static
- @param [arguments]*
- */
- createWithMixins: function() {
- var C = this;
- if (arguments.length>0) { this._initMixins(arguments); }
- return new C();
- },
-
- /**
- Creates an instance of a class. Accepts either no arguments, or an object
- containing values to initialize the newly instantiated object with.
-
- ```javascript
- App.Person = Ember.Object.extend({
- helloWorld: function() {
- alert("Hi, my name is " + this.get('name'));
- }
- });
-
- var tom = App.Person.create({
- name: 'Tom Dale'
- });
-
- tom.helloWorld(); // alerts "Hi, my name is Tom Dale".
- ```
-
- `create` will call the `init` function if defined during
- `Ember.AnyObject.extend`
-
- If no arguments are passed to `create`, it will not set values to the new
- instance during initialization:
-
- ```javascript
- var noName = App.Person.create();
- noName.helloWorld(); // alerts undefined
- ```
-
- NOTE: For performance reasons, you cannot declare methods or computed
- properties during `create`. You should instead declare methods and computed
- properties when using `extend` or use the `createWithMixins` shorthand.
-
- @method create
- @static
- @param [arguments]*
- */
- create: function() {
- var C = this;
- if (arguments.length>0) { this._initProperties(arguments); }
- return new C();
- },
-
- /**
-
- Augments a constructor's prototype with additional
- properties and functions:
-
- ```javascript
- MyObject = Ember.Object.extend({
- name: 'an object'
- });
-
- o = MyObject.create();
- o.get('name'); // 'an object'
-
- MyObject.reopen({
- say: function(msg){
- console.log(msg);
- }
- })
-
- o2 = MyObject.create();
- o2.say("hello"); // logs "hello"
-
- o.say("goodbye"); // logs "goodbye"
- ```
-
- To add functions and properties to the constructor itself,
- see `reopenClass`
-
- @method reopen
- */
- reopen: function() {
- this.willReopen();
- reopen.apply(this.PrototypeMixin, arguments);
- return this;
- },
-
- /**
- Augments a constructor's own properties and functions:
-
- ```javascript
- MyObject = Ember.Object.extend({
- name: 'an object'
- });
-
-
- MyObject.reopenClass({
- canBuild: false
- });
-
- MyObject.canBuild; // false
- o = MyObject.create();
- ```
-
- In other words, this creates static properties and functions for the class. These are only available on the class
- and not on any instance of that class.
-
- ```javascript
- App.Person = Ember.Object.extend({
- name : "",
- sayHello : function(){
- alert("Hello. My name is " + this.get('name'));
- }
- });
-
- App.Person.reopenClass({
- species : "Homo sapiens",
- createPerson: function(newPersonsName){
- return App.Person.create({
- name:newPersonsName
- });
- }
- });
-
- var tom = App.Person.create({
- name : "Tom Dale"
- });
- var yehuda = App.Person.createPerson("Yehuda Katz");
-
- tom.sayHello(); // "Hello. My name is Tom Dale"
- yehuda.sayHello(); // "Hello. My name is Yehuda Katz"
- alert(App.Person.species); // "Homo sapiens"
- ```
-
- Note that `species` and `createPerson` are *not* valid on the `tom` and `yehuda`
- variables. They are only valid on `App.Person`.
-
- To add functions and properties to instances of
- a constructor by extending the constructor's prototype
- see `reopen`
-
- @method reopenClass
- */
- reopenClass: function() {
- reopen.apply(this.ClassMixin, arguments);
- applyMixin(this, arguments, false);
- return this;
- },
-
- detect: function(obj) {
- if ('function' !== typeof obj) { return false; }
- while(obj) {
- if (obj===this) { return true; }
- obj = obj.superclass;
- }
- return false;
- },
-
- detectInstance: function(obj) {
- return obj instanceof 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:
-
- ```javascript
- person: function() {
- var personId = this.get('personId');
- return App.Person.create({ id: personId });
- }.property().meta({ type: App.Person })
- ```
-
- Once you've done this, you can retrieve the values saved to the computed
- property from your class like this:
-
- ```javascript
- MyClass.metaForProperty('person');
- ```
-
- This will return the original hash that was passed to `meta()`.
-
- @method metaForProperty
- @param key {String} property name
- */
- metaForProperty: function(key) {
- var desc = meta(this.proto(), false).descs[key];
-
- Ember.assert("metaForProperty() could not find a computed property with key '"+key+"'.", !!desc && desc instanceof Ember.ComputedProperty);
- return desc._meta || {};
- },
-
- /**
- Iterate over each computed property for the class, passing its name
- and any associated metadata (see `metaForProperty`) to the callback.
-
- @method eachComputedProperty
- @param {Function} callback
- @param {Object} binding
- */
- eachComputedProperty: function(callback, binding) {
- var proto = this.proto(),
- descs = meta(proto).descs,
- empty = {},
- property;
-
- for (var name in descs) {
- property = descs[name];
-
- if (property instanceof Ember.ComputedProperty) {
- callback.call(binding || this, name, property._meta || empty);
- }
- }
- }
-
-});
-
-ClassMixin.ownerConstructor = CoreObject;
-
-if (Ember.config.overrideClassMixin) {
- Ember.config.overrideClassMixin(ClassMixin);
-}
-
-CoreObject.ClassMixin = ClassMixin;
-ClassMixin.apply(CoreObject);
-
-Ember.CoreObject = CoreObject;
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-runtime
-*/
-
-/**
- `Ember.Object` is the main base class for all Ember objects. It is a subclass
- of `Ember.CoreObject` with the `Ember.Observable` mixin applied. For details,
- see the documentation for each of these.
-
- @class Object
- @namespace Ember
- @extends Ember.CoreObject
- @uses Ember.Observable
-*/
-Ember.Object = Ember.CoreObject.extend(Ember.Observable);
-Ember.Object.toString = function() { return "Ember.Object"; };
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-runtime
-*/
-
-var get = Ember.get, indexOf = Ember.ArrayPolyfills.indexOf;
-
-/**
- A Namespace is an object usually used to contain other objects or methods
- such as an application or framework. Create a namespace anytime you want
- to define one of these new containers.
-
- # Example Usage
-
- ```javascript
- MyFramework = Ember.Namespace.create({
- VERSION: '1.0.0'
- });
- ```
-
- @class Namespace
- @namespace Ember
- @extends Ember.Object
-*/
-var Namespace = Ember.Namespace = Ember.Object.extend({
- isNamespace: true,
-
- init: function() {
- Ember.Namespace.NAMESPACES.push(this);
- Ember.Namespace.PROCESSED = false;
- },
-
- toString: function() {
- var name = get(this, 'name');
- if (name) { return name; }
-
- findNamespaces();
- return this[Ember.GUID_KEY+'_name'];
- },
-
- nameClasses: function() {
- processNamespace([this.toString()], this, {});
- },
-
- destroy: function() {
- var namespaces = Ember.Namespace.NAMESPACES;
- Ember.lookup[this.toString()] = undefined;
- namespaces.splice(indexOf.call(namespaces, this), 1);
- this._super();
- }
-});
-
-Namespace.reopenClass({
- NAMESPACES: [Ember],
- NAMESPACES_BY_ID: {},
- PROCESSED: false,
- processAll: processAllNamespaces,
- byName: function(name) {
- if (!Ember.BOOTED) {
- processAllNamespaces();
- }
-
- return NAMESPACES_BY_ID[name];
- }
-});
-
-var NAMESPACES_BY_ID = Namespace.NAMESPACES_BY_ID;
-
-var hasOwnProp = ({}).hasOwnProperty,
- guidFor = Ember.guidFor;
-
-function processNamespace(paths, root, seen) {
- var idx = paths.length;
-
- NAMESPACES_BY_ID[paths.join('.')] = root;
-
- // Loop over all of the keys in the namespace, looking for classes
- for(var key in root) {
- if (!hasOwnProp.call(root, key)) { continue; }
- var obj = root[key];
-
- // If we are processing the `Ember` namespace, for example, the
- // `paths` will start with `["Ember"]`. Every iteration through
- // the loop will update the **second** element of this list with
- // the key, so processing `Ember.View` will make the Array
- // `['Ember', 'View']`.
- paths[idx] = key;
-
- // If we have found an unprocessed class
- if (obj && obj.toString === classToString) {
- // Replace the class' `toString` with the dot-separated path
- // and set its `NAME_KEY`
- obj.toString = makeToString(paths.join('.'));
- obj[NAME_KEY] = paths.join('.');
-
- // Support nested namespaces
- } else if (obj && obj.isNamespace) {
- // Skip aliased namespaces
- if (seen[guidFor(obj)]) { continue; }
- seen[guidFor(obj)] = true;
-
- // Process the child namespace
- processNamespace(paths, obj, seen);
- }
- }
-
- paths.length = idx; // cut out last item
-}
-
-function findNamespaces() {
- var Namespace = Ember.Namespace, lookup = Ember.lookup, obj, isNamespace;
-
- if (Namespace.PROCESSED) { return; }
-
- for (var prop in lookup) {
- // These don't raise exceptions but can cause warnings
- if (prop === "parent" || prop === "top" || prop === "frameElement" || prop === "webkitStorageInfo") { continue; }
-
- // get(window.globalStorage, 'isNamespace') would try to read the storage for domain isNamespace and cause exception in Firefox.
- // globalStorage is a storage obsoleted by the WhatWG storage specification. See https://developer.mozilla.org/en/DOM/Storage#globalStorage
- if (prop === "globalStorage" && lookup.StorageList && lookup.globalStorage instanceof lookup.StorageList) { continue; }
- // Unfortunately, some versions of IE don't support window.hasOwnProperty
- if (lookup.hasOwnProperty && !lookup.hasOwnProperty(prop)) { continue; }
-
- // At times we are not allowed to access certain properties for security reasons.
- // There are also times where even if we can access them, we are not allowed to access their properties.
- try {
- obj = Ember.lookup[prop];
- isNamespace = obj && obj.isNamespace;
- } catch (e) {
- continue;
- }
-
- if (isNamespace) {
- Ember.deprecate("Namespaces should not begin with lowercase.", /^[A-Z]/.test(prop));
- obj[NAME_KEY] = prop;
- }
- }
-}
-
-var NAME_KEY = Ember.NAME_KEY = Ember.GUID_KEY + '_name';
-
-function superClassString(mixin) {
- var superclass = mixin.superclass;
- if (superclass) {
- if (superclass[NAME_KEY]) { return superclass[NAME_KEY]; }
- else { return superClassString(superclass); }
- } else {
- return;
- }
-}
-
-function classToString() {
- if (!Ember.BOOTED && !this[NAME_KEY]) {
- processAllNamespaces();
- }
-
- var ret;
-
- if (this[NAME_KEY]) {
- ret = this[NAME_KEY];
- } else if (this._toString) {
- ret = this._toString;
- } else {
- var str = superClassString(this);
- if (str) {
- ret = "(subclass of " + str + ")";
- } else {
- ret = "(unknown mixin)";
- }
- this.toString = makeToString(ret);
- }
-
- return ret;
-}
-
-function processAllNamespaces() {
- var unprocessedNamespaces = !Namespace.PROCESSED,
- unprocessedMixins = Ember.anyUnprocessedMixins;
-
- if (unprocessedNamespaces) {
- findNamespaces();
- Namespace.PROCESSED = true;
- }
-
- if (unprocessedNamespaces || unprocessedMixins) {
- var namespaces = Namespace.NAMESPACES, namespace;
- for (var i=0, l=namespaces.length; i1) args = a_slice.call(arguments, 1);
-
- this.forEach(function(x, idx) {
- var method = x && x[methodName];
- if ('function' === typeof method) {
- ret[idx] = args ? method.apply(x, args) : method.call(x);
- }
- }, this);
-
- return ret;
- },
-
- /**
- Simply converts the enumerable into a genuine array. The order is not
- guaranteed. Corresponds to the method implemented by Prototype.
-
- @method toArray
- @return {Array} the enumerable as an array.
- */
- toArray: function() {
- var ret = Ember.A();
- this.forEach(function(o, idx) { ret[idx] = o; });
- return ret ;
- },
-
- /**
- Returns a copy of the array with all null and undefined elements removed.
-
- ```javascript
- var arr = ["a", null, "c", undefined];
- arr.compact(); // ["a", "c"]
- ```
-
- @method compact
- @return {Array} the array without null and undefined elements.
- */
- compact: function() {
- return this.filter(function(value) { return value != null; });
- },
-
- /**
- Returns a new enumerable that excludes the passed value. The default
- implementation returns an array regardless of the receiver type unless
- the receiver does not contain the value.
-
- ```javascript
- var arr = ["a", "b", "a", "c"];
- arr.without("a"); // ["b", "c"]
- ```
-
- @method without
- @param {Object} value
- @return {Ember.Enumerable}
- */
- without: function(value) {
- if (!this.contains(value)) return this; // nothing to do
- var ret = Ember.A();
- this.forEach(function(k) {
- if (k !== value) ret[ret.length] = k;
- }) ;
- return ret ;
- },
-
- /**
- Returns a new enumerable that contains only unique values. The default
- implementation returns an array regardless of the receiver type.
-
- ```javascript
- var arr = ["a", "a", "b", "b"];
- arr.uniq(); // ["a", "b"]
- ```
-
- @method uniq
- @return {Ember.Enumerable}
- */
- uniq: function() {
- var ret = Ember.A();
- this.forEach(function(k) {
- if (a_indexOf(ret, k)<0) ret.push(k);
- });
- return ret;
- },
-
- /**
- This property will trigger anytime the enumerable's content changes.
- You can observe this property to be notified of changes to the enumerables
- content.
-
- For plain enumerables, this property is read only. `Ember.Array` overrides
- this method.
-
- @property []
- @type Ember.Array
- @return this
- */
- '[]': Ember.computed(function(key, value) {
- return this;
- }),
-
- // ..........................................................
- // ENUMERABLE OBSERVERS
- //
-
- /**
- Registers an enumerable observer. Must implement `Ember.EnumerableObserver`
- mixin.
-
- @method addEnumerableObserver
- @param {Object} target
- @param {Hash} [opts]
- @return this
- */
- addEnumerableObserver: function(target, opts) {
- var willChange = (opts && opts.willChange) || 'enumerableWillChange',
- didChange = (opts && opts.didChange) || 'enumerableDidChange';
-
- var hasObservers = get(this, 'hasEnumerableObservers');
- if (!hasObservers) Ember.propertyWillChange(this, 'hasEnumerableObservers');
- Ember.addListener(this, '@enumerable:before', target, willChange);
- Ember.addListener(this, '@enumerable:change', target, didChange);
- if (!hasObservers) Ember.propertyDidChange(this, 'hasEnumerableObservers');
- return this;
- },
-
- /**
- Removes a registered enumerable observer.
-
- @method removeEnumerableObserver
- @param {Object} target
- @param {Hash} [opts]
- @return this
- */
- removeEnumerableObserver: function(target, opts) {
- var willChange = (opts && opts.willChange) || 'enumerableWillChange',
- didChange = (opts && opts.didChange) || 'enumerableDidChange';
-
- var hasObservers = get(this, 'hasEnumerableObservers');
- if (hasObservers) Ember.propertyWillChange(this, 'hasEnumerableObservers');
- Ember.removeListener(this, '@enumerable:before', target, willChange);
- Ember.removeListener(this, '@enumerable:change', target, didChange);
- if (hasObservers) Ember.propertyDidChange(this, 'hasEnumerableObservers');
- return this;
- },
-
- /**
- Becomes true whenever the array currently has observers watching changes
- on the array.
-
- @property hasEnumerableObservers
- @type Boolean
- */
- hasEnumerableObservers: Ember.computed(function() {
- return Ember.hasListeners(this, '@enumerable:change') || Ember.hasListeners(this, '@enumerable:before');
- }),
-
-
- /**
- Invoke this method just before the contents of your enumerable will
- change. You can either omit the parameters completely or pass the objects
- to be removed or added if available or just a count.
-
- @method enumerableContentWillChange
- @param {Ember.Enumerable|Number} removing An enumerable of the objects to
- be removed or the number of items to be removed.
- @param {Ember.Enumerable|Number} adding An enumerable of the objects to be
- added or the number of items to be added.
- @chainable
- */
- enumerableContentWillChange: function(removing, adding) {
-
- var removeCnt, addCnt, hasDelta;
-
- if ('number' === typeof removing) removeCnt = removing;
- else if (removing) removeCnt = get(removing, 'length');
- else removeCnt = removing = -1;
-
- if ('number' === typeof adding) addCnt = adding;
- else if (adding) addCnt = get(adding,'length');
- else addCnt = adding = -1;
-
- hasDelta = addCnt<0 || removeCnt<0 || addCnt-removeCnt!==0;
-
- if (removing === -1) removing = null;
- if (adding === -1) adding = null;
-
- Ember.propertyWillChange(this, '[]');
- if (hasDelta) Ember.propertyWillChange(this, 'length');
- Ember.sendEvent(this, '@enumerable:before', [this, removing, adding]);
-
- return this;
- },
-
- /**
- Invoke this method when the contents of your enumerable has changed.
- This will notify any observers watching for content changes. If your are
- implementing an ordered enumerable (such as an array), also pass the
- start and end values where the content changed so that it can be used to
- notify range observers.
-
- @method enumerableContentDidChange
- @param {Number} [start] optional start offset for the content change.
- For unordered enumerables, you should always pass -1.
- @param {Ember.Enumerable|Number} removing An enumerable of the objects to
- be removed or the number of items to be removed.
- @param {Ember.Enumerable|Number} adding An enumerable of the objects to
- be added or the number of items to be added.
- @chainable
- */
- enumerableContentDidChange: function(removing, adding) {
- var removeCnt, addCnt, hasDelta;
-
- if ('number' === typeof removing) removeCnt = removing;
- else if (removing) removeCnt = get(removing, 'length');
- else removeCnt = removing = -1;
-
- if ('number' === typeof adding) addCnt = adding;
- else if (adding) addCnt = get(adding, 'length');
- else addCnt = adding = -1;
-
- hasDelta = addCnt<0 || removeCnt<0 || addCnt-removeCnt!==0;
-
- if (removing === -1) removing = null;
- if (adding === -1) adding = null;
-
- Ember.sendEvent(this, '@enumerable:change', [this, removing, adding]);
- if (hasDelta) Ember.propertyDidChange(this, 'length');
- Ember.propertyDidChange(this, '[]');
-
- return this ;
- }
-
-});
-
-Ember.Enumerable.reopen({
- /**
- Converts the enumerable into an array and sorts by the keys
- specified in the argument.
-
- You may provide multiple arguments to sort by multiple properties.
-
- @method sortBy
- @param {String} property name(s) to sort on
- @return {Array} The sorted array.
- */
- sortBy: function() {
- var sortKeys = arguments;
- return this.toArray().sort(function(a, b){
- for(var i = 0; i < sortKeys.length; i++) {
- var key = sortKeys[i],
- propA = get(a, key),
- propB = get(b, key);
- // return 1 or -1 else continue to the next sortKey
- var compareValue = Ember.compare(propA, propB);
- if (compareValue) { return compareValue; }
- }
- return 0;
- });
- }
-});
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-runtime
-*/
-
-// ..........................................................
-// HELPERS
-//
-
-var get = Ember.get, set = Ember.set, isNone = Ember.isNone, map = Ember.EnumerableUtils.map, cacheFor = Ember.cacheFor;
-
-// ..........................................................
-// ARRAY
-//
-/**
- This module implements Observer-friendly Array-like behavior. This mixin is
- picked up by the Array class as well as other controllers, etc. that want to
- appear to be arrays.
-
- Unlike `Ember.Enumerable,` this mixin defines methods specifically for
- collections that provide index-ordered access to their contents. When you
- are designing code that needs to accept any kind of Array-like object, you
- should use these methods instead of Array primitives because these will
- properly notify observers of changes to the array.
-
- Although these methods are efficient, they do add a layer of indirection to
- your application so it is a good idea to use them only when you need the
- flexibility of using both true JavaScript arrays and "virtual" arrays such
- as controllers and collections.
-
- You can use the methods defined in this module to access and modify array
- contents in a KVO-friendly way. You can also be notified whenever the
- membership of an array changes by changing the syntax of the property to
- `.observes('*myProperty.[]')`.
-
- To support `Ember.Array` in your own class, you must override two
- primitives to use it: `replace()` and `objectAt()`.
-
- Note that the Ember.Array mixin also incorporates the `Ember.Enumerable`
- mixin. All `Ember.Array`-like objects are also enumerable.
-
- @class Array
- @namespace Ember
- @uses Ember.Enumerable
- @since Ember 0.9.0
-*/
-Ember.Array = Ember.Mixin.create(Ember.Enumerable, /** @scope Ember.Array.prototype */ {
-
- /**
- Your array must support the `length` property. Your replace methods should
- set this property whenever it changes.
-
- @property {Number} length
- */
- length: Ember.required(),
-
- /**
- Returns the object at the given `index`. If the given `index` is negative
- or is greater or equal than the array length, returns `undefined`.
-
- This is one of the primitives you must implement to support `Ember.Array`.
- If your object supports retrieving the value of an array item using `get()`
- (i.e. `myArray.get(0)`), then you do not need to implement this method
- yourself.
-
- ```javascript
- var arr = ['a', 'b', 'c', 'd'];
- arr.objectAt(0); // "a"
- arr.objectAt(3); // "d"
- arr.objectAt(-1); // undefined
- arr.objectAt(4); // undefined
- arr.objectAt(5); // undefined
- ```
-
- @method objectAt
- @param {Number} idx The index of the item to return.
- @return {*} item at index or undefined
- */
- objectAt: function(idx) {
- if ((idx < 0) || (idx>=get(this, 'length'))) return undefined ;
- return get(this, idx);
- },
-
- /**
- This returns the objects at the specified indexes, using `objectAt`.
-
- ```javascript
- var arr = ['a', 'b', 'c', 'd'];
- arr.objectsAt([0, 1, 2]); // ["a", "b", "c"]
- arr.objectsAt([2, 3, 4]); // ["c", "d", undefined]
- ```
-
- @method objectsAt
- @param {Array} indexes An array of indexes of items to return.
- @return {Array}
- */
- objectsAt: function(indexes) {
- var self = this;
- return map(indexes, function(idx) { return self.objectAt(idx); });
- },
-
- // overrides Ember.Enumerable version
- nextObject: function(idx) {
- return this.objectAt(idx);
- },
-
- /**
- This is the handler for the special array content property. If you get
- this property, it will return this. If you set this property it a new
- array, it will replace the current content.
-
- This property overrides the default property defined in `Ember.Enumerable`.
-
- @property []
- @return this
- */
- '[]': Ember.computed(function(key, value) {
- if (value !== undefined) this.replace(0, get(this, 'length'), value) ;
- return this ;
- }),
-
- firstObject: Ember.computed(function() {
- return this.objectAt(0);
- }),
-
- lastObject: Ember.computed(function() {
- return this.objectAt(get(this, 'length')-1);
- }),
-
- // optimized version from Enumerable
- contains: function(obj) {
- return this.indexOf(obj) >= 0;
- },
-
- // Add any extra methods to Ember.Array that are native to the built-in Array.
- /**
- Returns a new array that is a slice of the receiver. This implementation
- uses the observable array methods to retrieve the objects for the new
- slice.
-
- ```javascript
- var arr = ['red', 'green', 'blue'];
- arr.slice(0); // ['red', 'green', 'blue']
- arr.slice(0, 2); // ['red', 'green']
- arr.slice(1, 100); // ['green', 'blue']
- ```
-
- @method slice
- @param {Integer} beginIndex (Optional) index to begin slicing from.
- @param {Integer} endIndex (Optional) index to end the slice at.
- @return {Array} New array with specified slice
- */
- slice: function(beginIndex, endIndex) {
- var ret = Ember.A();
- var length = get(this, 'length') ;
- if (isNone(beginIndex)) beginIndex = 0 ;
- if (isNone(endIndex) || (endIndex > length)) endIndex = length ;
-
- if (beginIndex < 0) beginIndex = length + beginIndex;
- if (endIndex < 0) endIndex = length + endIndex;
-
- while(beginIndex < endIndex) {
- ret[ret.length] = this.objectAt(beginIndex++) ;
- }
- return ret ;
- },
-
- /**
- Returns the index of the given object's first occurrence.
- If no `startAt` argument is given, the starting location to
- search is 0. If it's negative, will count backward from
- the end of the array. Returns -1 if no match is found.
-
- ```javascript
- var arr = ["a", "b", "c", "d", "a"];
- arr.indexOf("a"); // 0
- arr.indexOf("z"); // -1
- arr.indexOf("a", 2); // 4
- arr.indexOf("a", -1); // 4
- arr.indexOf("b", 3); // -1
- arr.indexOf("a", 100); // -1
- ```
-
- @method indexOf
- @param {Object} object the item to search for
- @param {Number} startAt optional starting location to search, default 0
- @return {Number} index or -1 if not found
- */
- indexOf: function(object, startAt) {
- var idx, len = get(this, 'length');
-
- if (startAt === undefined) startAt = 0;
- if (startAt < 0) startAt += len;
-
- for(idx=startAt;idx= len) startAt = len-1;
- if (startAt < 0) startAt += len;
-
- for(idx=startAt;idx>=0;idx--) {
- if (this.objectAt(idx) === object) return idx ;
- }
- return -1;
- },
-
- // ..........................................................
- // ARRAY OBSERVERS
- //
-
- /**
- Adds an array observer to the receiving array. The array observer object
- normally must implement two methods:
-
- * `arrayWillChange(observedObj, start, removeCount, addCount)` - This method will be
- called just before the array is modified.
- * `arrayDidChange(observedObj, start, removeCount, addCount)` - This method will be
- called just after the array is modified.
-
- Both callbacks will be passed the observed object, starting index of the
- change as well a a count of the items to be removed and added. You can use
- these callbacks to optionally inspect the array during the change, clear
- caches, or do any other bookkeeping necessary.
-
- In addition to passing a target, you can also include an options hash
- which you can use to override the method names that will be invoked on the
- target.
-
- @method addArrayObserver
- @param {Object} target The observer object.
- @param {Hash} opts Optional hash of configuration options including
- `willChange` and `didChange` option.
- @return {Ember.Array} receiver
- */
- addArrayObserver: function(target, opts) {
- var willChange = (opts && opts.willChange) || 'arrayWillChange',
- didChange = (opts && opts.didChange) || 'arrayDidChange';
-
- var hasObservers = get(this, 'hasArrayObservers');
- if (!hasObservers) Ember.propertyWillChange(this, 'hasArrayObservers');
- Ember.addListener(this, '@array:before', target, willChange);
- Ember.addListener(this, '@array:change', target, didChange);
- if (!hasObservers) Ember.propertyDidChange(this, 'hasArrayObservers');
- return this;
- },
-
- /**
- Removes an array observer from the object if the observer is current
- registered. Calling this method multiple times with the same object will
- have no effect.
-
- @method removeArrayObserver
- @param {Object} target The object observing the array.
- @param {Hash} opts Optional hash of configuration options including
- `willChange` and `didChange` option.
- @return {Ember.Array} receiver
- */
- removeArrayObserver: function(target, opts) {
- var willChange = (opts && opts.willChange) || 'arrayWillChange',
- didChange = (opts && opts.didChange) || 'arrayDidChange';
-
- var hasObservers = get(this, 'hasArrayObservers');
- if (hasObservers) Ember.propertyWillChange(this, 'hasArrayObservers');
- Ember.removeListener(this, '@array:before', target, willChange);
- Ember.removeListener(this, '@array:change', target, didChange);
- if (hasObservers) Ember.propertyDidChange(this, 'hasArrayObservers');
- return this;
- },
-
- /**
- Becomes true whenever the array currently has observers watching changes
- on the array.
-
- @property Boolean
- */
- hasArrayObservers: Ember.computed(function() {
- return Ember.hasListeners(this, '@array:change') || Ember.hasListeners(this, '@array:before');
- }),
-
- /**
- If you are implementing an object that supports `Ember.Array`, call this
- method just before the array content changes to notify any observers and
- invalidate any related properties. Pass the starting index of the change
- as well as a delta of the amounts to change.
-
- @method arrayContentWillChange
- @param {Number} startIdx The starting index in the array that will change.
- @param {Number} removeAmt The number of items that will be removed. If you
- pass `null` assumes 0
- @param {Number} addAmt The number of items that will be added. If you
- pass `null` assumes 0.
- @return {Ember.Array} receiver
- */
- arrayContentWillChange: function(startIdx, removeAmt, addAmt) {
-
- // if no args are passed assume everything changes
- if (startIdx===undefined) {
- startIdx = 0;
- removeAmt = addAmt = -1;
- } else {
- if (removeAmt === undefined) removeAmt=-1;
- if (addAmt === undefined) addAmt=-1;
- }
-
- // Make sure the @each proxy is set up if anyone is observing @each
- if (Ember.isWatching(this, '@each')) { get(this, '@each'); }
-
- Ember.sendEvent(this, '@array:before', [this, startIdx, removeAmt, addAmt]);
-
- var removing, lim;
- if (startIdx>=0 && removeAmt>=0 && get(this, 'hasEnumerableObservers')) {
- removing = [];
- lim = startIdx+removeAmt;
- for(var idx=startIdx;idx=0 && addAmt>=0 && get(this, 'hasEnumerableObservers')) {
- adding = [];
- lim = startIdx+addAmt;
- for(var idx=startIdx;idx Ember.TrackedArray instances. We use
- // this to lazily recompute indexes for item property observers.
- this.trackedArraysByGuid = {};
-
- // We suspend observers to ignore replacements from `reset` when totally
- // recomputing. Unfortunately we cannot properly suspend the observers
- // because we only have the key; instead we make the observers no-ops
- this.suspended = false;
-
- // This is used to coalesce item changes from property observers.
- this.changedItems = {};
-}
-
-function ItemPropertyObserverContext (dependentArray, index, trackedArray) {
- Ember.assert("Internal error: trackedArray is null or undefined", trackedArray);
-
- this.dependentArray = dependentArray;
- this.index = index;
- this.item = dependentArray.objectAt(index);
- this.trackedArray = trackedArray;
- this.beforeObserver = null;
- this.observer = null;
-
- this.destroyed = false;
-}
-
-DependentArraysObserver.prototype = {
- setValue: function (newValue) {
- this.instanceMeta.setValue(newValue, true);
- },
- getValue: function () {
- return this.instanceMeta.getValue();
- },
-
- setupObservers: function (dependentArray, dependentKey) {
- Ember.assert("dependent array must be an `Ember.Array`", Ember.Array.detect(dependentArray));
-
- this.dependentKeysByGuid[guidFor(dependentArray)] = dependentKey;
-
- dependentArray.addArrayObserver(this, {
- willChange: 'dependentArrayWillChange',
- didChange: 'dependentArrayDidChange'
- });
-
- if (this.cp._itemPropertyKeys[dependentKey]) {
- this.setupPropertyObservers(dependentKey, this.cp._itemPropertyKeys[dependentKey]);
- }
- },
-
- teardownObservers: function (dependentArray, dependentKey) {
- var itemPropertyKeys = this.cp._itemPropertyKeys[dependentKey] || [];
-
- delete this.dependentKeysByGuid[guidFor(dependentArray)];
-
- this.teardownPropertyObservers(dependentKey, itemPropertyKeys);
-
- dependentArray.removeArrayObserver(this, {
- willChange: 'dependentArrayWillChange',
- didChange: 'dependentArrayDidChange'
- });
- },
-
- suspendArrayObservers: function (callback, binding) {
- var oldSuspended = this.suspended;
- this.suspended = true;
- callback.call(binding);
- this.suspended = oldSuspended;
- },
-
- setupPropertyObservers: function (dependentKey, itemPropertyKeys) {
- var dependentArray = get(this.instanceMeta.context, dependentKey),
- length = get(dependentArray, 'length'),
- observerContexts = new Array(length);
-
- this.resetTransformations(dependentKey, observerContexts);
-
- forEach(dependentArray, function (item, index) {
- var observerContext = this.createPropertyObserverContext(dependentArray, index, this.trackedArraysByGuid[dependentKey]);
- observerContexts[index] = observerContext;
-
- forEach(itemPropertyKeys, function (propertyKey) {
- addBeforeObserver(item, propertyKey, this, observerContext.beforeObserver);
- addObserver(item, propertyKey, this, observerContext.observer);
- }, this);
- }, this);
- },
-
- teardownPropertyObservers: function (dependentKey, itemPropertyKeys) {
- var dependentArrayObserver = this,
- trackedArray = this.trackedArraysByGuid[dependentKey],
- beforeObserver,
- observer,
- item;
-
- if (!trackedArray) { return; }
-
- trackedArray.apply(function (observerContexts, offset, operation) {
- if (operation === Ember.TrackedArray.DELETE) { return; }
-
- forEach(observerContexts, function (observerContext) {
- observerContext.destroyed = true;
- beforeObserver = observerContext.beforeObserver;
- observer = observerContext.observer;
- item = observerContext.item;
-
- forEach(itemPropertyKeys, function (propertyKey) {
- removeBeforeObserver(item, propertyKey, dependentArrayObserver, beforeObserver);
- removeObserver(item, propertyKey, dependentArrayObserver, observer);
- });
- });
- });
- },
-
- createPropertyObserverContext: function (dependentArray, index, trackedArray) {
- var observerContext = new ItemPropertyObserverContext(dependentArray, index, trackedArray);
-
- this.createPropertyObserver(observerContext);
-
- return observerContext;
- },
-
- createPropertyObserver: function (observerContext) {
- var dependentArrayObserver = this;
-
- observerContext.beforeObserver = function (obj, keyName) {
- return dependentArrayObserver.itemPropertyWillChange(obj, keyName, observerContext.dependentArray, observerContext);
- };
- observerContext.observer = function (obj, keyName) {
- return dependentArrayObserver.itemPropertyDidChange(obj, keyName, observerContext.dependentArray, observerContext);
- };
- },
-
- resetTransformations: function (dependentKey, observerContexts) {
- this.trackedArraysByGuid[dependentKey] = new Ember.TrackedArray(observerContexts);
- },
-
- trackAdd: function (dependentKey, index, newItems) {
- var trackedArray = this.trackedArraysByGuid[dependentKey];
- if (trackedArray) {
- trackedArray.addItems(index, newItems);
- }
- },
-
- trackRemove: function (dependentKey, index, removedCount) {
- var trackedArray = this.trackedArraysByGuid[dependentKey];
-
- if (trackedArray) {
- return trackedArray.removeItems(index, removedCount);
- }
-
- return [];
- },
-
- updateIndexes: function (trackedArray, array) {
- var length = get(array, 'length');
- // OPTIMIZE: we could stop updating once we hit the object whose observer
- // fired; ie partially apply the transformations
- trackedArray.apply(function (observerContexts, offset, operation) {
- // we don't even have observer contexts for removed items, even if we did,
- // they no longer have any index in the array
- if (operation === Ember.TrackedArray.DELETE) { return; }
- if (operation === Ember.TrackedArray.RETAIN && observerContexts.length === length && offset === 0) {
- // If we update many items we don't want to walk the array each time: we
- // only need to update the indexes at most once per run loop.
- return;
- }
-
- forEach(observerContexts, function (context, index) {
- context.index = index + offset;
- });
- });
- },
-
- dependentArrayWillChange: function (dependentArray, index, removedCount, addedCount) {
- if (this.suspended) { return; }
-
- var removedItem = this.callbacks.removedItem,
- changeMeta,
- guid = guidFor(dependentArray),
- dependentKey = this.dependentKeysByGuid[guid],
- itemPropertyKeys = this.cp._itemPropertyKeys[dependentKey] || [],
- length = get(dependentArray, 'length'),
- normalizedIndex = normalizeIndex(index, length, 0),
- normalizedRemoveCount = normalizeRemoveCount(normalizedIndex, length, removedCount),
- item,
- itemIndex,
- sliceIndex,
- observerContexts;
-
- observerContexts = this.trackRemove(dependentKey, normalizedIndex, normalizedRemoveCount);
-
- function removeObservers(propertyKey) {
- observerContexts[sliceIndex].destroyed = true;
- removeBeforeObserver(item, propertyKey, this, observerContexts[sliceIndex].beforeObserver);
- removeObserver(item, propertyKey, this, observerContexts[sliceIndex].observer);
- }
-
- for (sliceIndex = normalizedRemoveCount - 1; sliceIndex >= 0; --sliceIndex) {
- itemIndex = normalizedIndex + sliceIndex;
- if (itemIndex >= length) { break; }
-
- item = dependentArray.objectAt(itemIndex);
-
- forEach(itemPropertyKeys, removeObservers, this);
-
- changeMeta = createChangeMeta(dependentArray, item, itemIndex, this.instanceMeta.propertyName, this.cp);
- this.setValue( removedItem.call(
- this.instanceMeta.context, this.getValue(), item, changeMeta, this.instanceMeta.sugarMeta));
- }
- },
-
- dependentArrayDidChange: function (dependentArray, index, removedCount, addedCount) {
- if (this.suspended) { return; }
-
- var addedItem = this.callbacks.addedItem,
- guid = guidFor(dependentArray),
- dependentKey = this.dependentKeysByGuid[guid],
- observerContexts = new Array(addedCount),
- itemPropertyKeys = this.cp._itemPropertyKeys[dependentKey],
- length = get(dependentArray, 'length'),
- normalizedIndex = normalizeIndex(index, length, addedCount),
- changeMeta,
- observerContext;
-
- forEach(dependentArray.slice(normalizedIndex, normalizedIndex + addedCount), function (item, sliceIndex) {
- if (itemPropertyKeys) {
- observerContext =
- observerContexts[sliceIndex] =
- this.createPropertyObserverContext(dependentArray, normalizedIndex + sliceIndex, this.trackedArraysByGuid[dependentKey]);
- forEach(itemPropertyKeys, function (propertyKey) {
- addBeforeObserver(item, propertyKey, this, observerContext.beforeObserver);
- addObserver(item, propertyKey, this, observerContext.observer);
- }, this);
- }
-
- changeMeta = createChangeMeta(dependentArray, item, normalizedIndex + sliceIndex, this.instanceMeta.propertyName, this.cp);
- this.setValue( addedItem.call(
- this.instanceMeta.context, this.getValue(), item, changeMeta, this.instanceMeta.sugarMeta));
- }, this);
-
- this.trackAdd(dependentKey, normalizedIndex, observerContexts);
- },
-
- itemPropertyWillChange: function (obj, keyName, array, observerContext) {
- var guid = guidFor(obj);
-
- if (!this.changedItems[guid]) {
- this.changedItems[guid] = {
- array: array,
- observerContext: observerContext,
- obj: obj,
- previousValues: {}
- };
- }
-
- this.changedItems[guid].previousValues[keyName] = get(obj, keyName);
- },
-
- itemPropertyDidChange: function(obj, keyName, array, observerContext) {
- this.flushChanges();
- },
-
- flushChanges: function() {
- var changedItems = this.changedItems, key, c, changeMeta;
-
- for (key in changedItems) {
- c = changedItems[key];
- if (c.observerContext.destroyed) { continue; }
-
- this.updateIndexes(c.observerContext.trackedArray, c.observerContext.dependentArray);
-
- changeMeta = createChangeMeta(c.array, c.obj, c.observerContext.index, this.instanceMeta.propertyName, this.cp, c.previousValues);
- this.setValue(
- this.callbacks.removedItem.call(this.instanceMeta.context, this.getValue(), c.obj, changeMeta, this.instanceMeta.sugarMeta));
- this.setValue(
- this.callbacks.addedItem.call(this.instanceMeta.context, this.getValue(), c.obj, changeMeta, this.instanceMeta.sugarMeta));
- }
- this.changedItems = {};
- }
-};
-
-function normalizeIndex(index, length, newItemsOffset) {
- if (index < 0) {
- return Math.max(0, length + index);
- } else if (index < length) {
- return index;
- } else /* index > length */ {
- return Math.min(length - newItemsOffset, index);
- }
-}
-
-function normalizeRemoveCount(index, length, removedCount) {
- return Math.min(removedCount, length - index);
-}
-
-function createChangeMeta(dependentArray, item, index, propertyName, property, previousValues) {
- var meta = {
- arrayChanged: dependentArray,
- index: index,
- item: item,
- propertyName: propertyName,
- property: property
- };
-
- if (previousValues) {
- // previous values only available for item property changes
- meta.previousValues = previousValues;
- }
-
- return meta;
-}
-
-function addItems (dependentArray, callbacks, cp, propertyName, meta) {
- forEach(dependentArray, function (item, index) {
- meta.setValue( callbacks.addedItem.call(
- this, meta.getValue(), item, createChangeMeta(dependentArray, item, index, propertyName, cp), meta.sugarMeta));
- }, this);
-}
-
-function reset(cp, propertyName) {
- var callbacks = cp._callbacks(),
- meta;
-
- if (cp._hasInstanceMeta(this, propertyName)) {
- meta = cp._instanceMeta(this, propertyName);
- meta.setValue(cp.resetValue(meta.getValue()));
- } else {
- meta = cp._instanceMeta(this, propertyName);
- }
-
- if (cp.options.initialize) {
- cp.options.initialize.call(this, meta.getValue(), { property: cp, propertyName: propertyName }, meta.sugarMeta);
- }
-}
-
-function ReduceComputedPropertyInstanceMeta(context, propertyName, initialValue) {
- this.context = context;
- this.propertyName = propertyName;
- this.cache = metaFor(context).cache;
-
- this.dependentArrays = {};
- this.sugarMeta = {};
-
- this.initialValue = initialValue;
-}
-
-ReduceComputedPropertyInstanceMeta.prototype = {
- getValue: function () {
- if (this.propertyName in this.cache) {
- return this.cache[this.propertyName];
- } else {
- return this.initialValue;
- }
- },
-
- setValue: function(newValue, triggerObservers) {
- // This lets sugars force a recomputation, handy for very simple
- // implementations of eg max.
- if (newValue !== undefined) {
- var fireObservers = triggerObservers && (newValue !== this.cache[this.propertyName]);
-
- if (fireObservers) {
- propertyWillChange(this.context, this.propertyName);
- }
-
- this.cache[this.propertyName] = newValue;
-
- if (fireObservers) {
- propertyDidChange(this.context, this.propertyName);
- }
- } else {
- delete this.cache[this.propertyName];
- }
- }
-};
-
-/**
- A computed property whose dependent keys are arrays and which is updated with
- "one at a time" semantics.
-
- @class ReduceComputedProperty
- @namespace Ember
- @extends Ember.ComputedProperty
- @constructor
-*/
-function ReduceComputedProperty(options) {
- var cp = this;
-
- this.options = options;
- this._instanceMetas = {};
-
- this._dependentKeys = null;
- // A map of dependentKey -> [itemProperty, ...] that tracks what properties of
- // items in the array we must track to update this property.
- this._itemPropertyKeys = {};
- this._previousItemPropertyKeys = {};
-
- this.readOnly();
- this.cacheable();
-
- this.recomputeOnce = function(propertyName) {
- // What we really want to do is coalesce by .
- // We need a form of `scheduleOnce` that accepts an arbitrary token to
- // coalesce by, in addition to the target and method.
- Ember.run.once(this, recompute, propertyName);
- };
- var recompute = function(propertyName) {
- var dependentKeys = cp._dependentKeys,
- meta = cp._instanceMeta(this, propertyName),
- callbacks = cp._callbacks();
-
- reset.call(this, cp, propertyName);
-
- meta.dependentArraysObserver.suspendArrayObservers(function () {
- forEach(cp._dependentKeys, function (dependentKey) {
- var dependentArray = get(this, dependentKey),
- previousDependentArray = meta.dependentArrays[dependentKey];
-
- if (dependentArray === previousDependentArray) {
- // The array may be the same, but our item property keys may have
- // changed, so we set them up again. We can't easily tell if they've
- // changed: the array may be the same object, but with different
- // contents.
- if (cp._previousItemPropertyKeys[dependentKey]) {
- delete cp._previousItemPropertyKeys[dependentKey];
- meta.dependentArraysObserver.setupPropertyObservers(dependentKey, cp._itemPropertyKeys[dependentKey]);
- }
- } else {
- meta.dependentArrays[dependentKey] = dependentArray;
-
- if (previousDependentArray) {
- meta.dependentArraysObserver.teardownObservers(previousDependentArray, dependentKey);
- }
-
- if (dependentArray) {
- meta.dependentArraysObserver.setupObservers(dependentArray, dependentKey);
- }
- }
- }, this);
- }, this);
-
- forEach(cp._dependentKeys, function(dependentKey) {
- var dependentArray = get(this, dependentKey);
- if (dependentArray) {
- addItems.call(this, dependentArray, callbacks, cp, propertyName, meta);
- }
- }, this);
- };
-
- this.func = function (propertyName) {
- Ember.assert("Computed reduce values require at least one dependent key", cp._dependentKeys);
-
- recompute.call(this, propertyName);
-
- return cp._instanceMeta(this, propertyName).getValue();
- };
-}
-
-Ember.ReduceComputedProperty = ReduceComputedProperty;
-ReduceComputedProperty.prototype = o_create(ComputedProperty.prototype);
-
-function defaultCallback(computedValue) {
- return computedValue;
-}
-
-ReduceComputedProperty.prototype._callbacks = function () {
- if (!this.callbacks) {
- var options = this.options;
- this.callbacks = {
- removedItem: options.removedItem || defaultCallback,
- addedItem: options.addedItem || defaultCallback
- };
- }
- return this.callbacks;
-};
-
-ReduceComputedProperty.prototype._hasInstanceMeta = function (context, propertyName) {
- var guid = guidFor(context),
- key = guid + ':' + propertyName;
-
- return !!this._instanceMetas[key];
-};
-
-ReduceComputedProperty.prototype._instanceMeta = function (context, propertyName) {
- var guid = guidFor(context),
- key = guid + ':' + propertyName,
- meta = this._instanceMetas[key];
-
- if (!meta) {
- meta = this._instanceMetas[key] = new ReduceComputedPropertyInstanceMeta(context, propertyName, this.initialValue());
- meta.dependentArraysObserver = new DependentArraysObserver(this._callbacks(), this, meta, context, propertyName, meta.sugarMeta);
- }
-
- return meta;
-};
-
-ReduceComputedProperty.prototype.initialValue = function () {
- if (typeof this.options.initialValue === 'function') {
- return this.options.initialValue();
- }
- else {
- return this.options.initialValue;
- }
-};
-
-ReduceComputedProperty.prototype.resetValue = function (value) {
- return this.initialValue();
-};
-
-ReduceComputedProperty.prototype.itemPropertyKey = function (dependentArrayKey, itemPropertyKey) {
- this._itemPropertyKeys[dependentArrayKey] = this._itemPropertyKeys[dependentArrayKey] || [];
- this._itemPropertyKeys[dependentArrayKey].push(itemPropertyKey);
-};
-
-ReduceComputedProperty.prototype.clearItemPropertyKeys = function (dependentArrayKey) {
- if (this._itemPropertyKeys[dependentArrayKey]) {
- this._previousItemPropertyKeys[dependentArrayKey] = this._itemPropertyKeys[dependentArrayKey];
- this._itemPropertyKeys[dependentArrayKey] = [];
- }
-};
-
-ReduceComputedProperty.prototype.property = function () {
- var cp = this,
- args = a_slice.call(arguments),
- propertyArgs = new Ember.Set(),
- match,
- dependentArrayKey,
- itemPropertyKey;
-
- forEach(a_slice.call(arguments), function (dependentKey) {
- if (doubleEachPropertyPattern.test(dependentKey)) {
- throw new Ember.Error("Nested @each properties not supported: " + dependentKey);
- } else if (match = eachPropertyPattern.exec(dependentKey)) {
- dependentArrayKey = match[1];
- itemPropertyKey = match[2];
- cp.itemPropertyKey(dependentArrayKey, itemPropertyKey);
- propertyArgs.add(dependentArrayKey);
- } else {
- propertyArgs.add(dependentKey);
- }
- });
-
- return ComputedProperty.prototype.property.apply(this, propertyArgs.toArray());
-};
-
-/**
- Creates a computed property which operates on dependent arrays and
- is updated with "one at a time" semantics. When items are added or
- removed from the dependent array(s) a reduce computed only operates
- on the change instead of re-evaluating the entire array.
-
- If there are more than one arguments the first arguments are
- considered to be dependent property keys. The last argument is
- required to be an options object. The options object can have the
- following four properties:
-
- `initialValue` - A value or function that will be used as the initial
- value for the computed. If this property is a function the result of calling
- the function will be used as the initial value. This property is required.
-
- `initialize` - An optional initialize function. Typically this will be used
- to set up state on the instanceMeta object.
-
- `removedItem` - A function that is called each time an element is removed
- from the array.
-
- `addedItem` - A function that is called each time an element is added to
- the array.
-
-
- The `initialize` function has the following signature:
-
- ```javascript
- function (initialValue, changeMeta, instanceMeta)
- ```
-
- `initialValue` - The value of the `initialValue` property from the
- options object.
-
- `changeMeta` - An object which contains meta information about the
- computed. It contains the following properties:
-
- - `property` the computed property
- - `propertyName` the name of the property on the object
-
- `instanceMeta` - An object that can be used to store meta
- information needed for calculating your computed. For example a
- unique computed might use this to store the number of times a given
- element is found in the dependent array.
-
-
- The `removedItem` and `addedItem` functions both have the following signature:
-
- ```javascript
- function (accumulatedValue, item, changeMeta, instanceMeta)
- ```
-
- `accumulatedValue` - The value returned from the last time
- `removedItem` or `addedItem` was called or `initialValue`.
-
- `item` - the element added or removed from the array
-
- `changeMeta` - An object which contains meta information about the
- change. It contains the following properties:
-
- - `property` the computed property
- - `propertyName` the name of the property on the object
- - `index` the index of the added or removed item
- - `item` the added or removed item: this is exactly the same as
- the second arg
- - `arrayChanged` the array that triggered the change. Can be
- useful when depending on multiple arrays.
-
- For property changes triggered on an item property change (when
- depKey is something like `someArray.@each.someProperty`),
- `changeMeta` will also contain the following property:
-
- - `previousValues` an object whose keys are the properties that changed on
- the item, and whose values are the item's previous values.
-
- `previousValues` is important Ember coalesces item property changes via
- Ember.run.once. This means that by the time removedItem gets called, item has
- the new values, but you may need the previous value (eg for sorting &
- filtering).
-
- `instanceMeta` - An object that can be used to store meta
- information needed for calculating your computed. For example a
- unique computed might use this to store the number of times a given
- element is found in the dependent array.
-
- The `removedItem` and `addedItem` functions should return the accumulated
- value. It is acceptable to not return anything (ie return undefined)
- to invalidate the computation. This is generally not a good idea for
- arrayComputed but it's used in eg max and min.
-
- Note that observers will be fired if either of these functions return a value
- that differs from the accumulated value. When returning an object that
- mutates in response to array changes, for example an array that maps
- everything from some other array (see `Ember.computed.map`), it is usually
- important that the *same* array be returned to avoid accidentally triggering observers.
-
- Example
-
- ```javascript
- Ember.computed.max = function (dependentKey) {
- return Ember.reduceComputed.call(null, dependentKey, {
- initialValue: -Infinity,
-
- addedItem: function (accumulatedValue, item, changeMeta, instanceMeta) {
- return Math.max(accumulatedValue, item);
- },
-
- removedItem: function (accumulatedValue, item, changeMeta, instanceMeta) {
- if (item < accumulatedValue) {
- return accumulatedValue;
- }
- }
- });
- };
- ```
-
- Dependent keys may refer to `@this` to observe changes to the object itself,
- which must be array-like, rather than a property of the object. This is
- mostly useful for array proxies, to ensure objects are retrieved via
- `objectAtContent`. This is how you could sort items by properties defined on an item controller.
-
- Example
-
- ```javascript
- App.PeopleController = Ember.ArrayController.extend({
- itemController: 'person',
-
- sortedPeople: Ember.computed.sort('@this.@each.reversedName', function(personA, personB) {
- // `reversedName` isn't defined on Person, but we have access to it via
- // the item controller App.PersonController. If we'd used
- // `content.@each.reversedName` above, we would be getting the objects
- // directly and not have access to `reversedName`.
- //
- var reversedNameA = get(personA, 'reversedName'),
- reversedNameB = get(personB, 'reversedName');
-
- return Ember.compare(reversedNameA, reversedNameB);
- })
- });
-
- App.PersonController = Ember.ObjectController.extend({
- reversedName: function () {
- return reverse(get(this, 'name'));
- }.property('name')
- })
- ```
-
- @method reduceComputed
- @for Ember
- @param {String} [dependentKeys*]
- @param {Object} options
- @return {Ember.ComputedProperty}
-*/
-Ember.reduceComputed = function (options) {
- var args;
-
- if (arguments.length > 1) {
- args = a_slice.call(arguments, 0, -1);
- options = a_slice.call(arguments, -1)[0];
- }
-
- if (typeof options !== "object") {
- throw new Ember.Error("Reduce Computed Property declared without an options hash");
- }
-
- if (!('initialValue' in options)) {
- throw new Ember.Error("Reduce Computed Property declared without an initial value");
- }
-
- var cp = new ReduceComputedProperty(options);
-
- if (args) {
- cp.property.apply(cp, args);
- }
-
- return cp;
-};
-
-})();
-
-
-
-(function() {
-var ReduceComputedProperty = Ember.ReduceComputedProperty,
- a_slice = [].slice,
- o_create = Ember.create,
- forEach = Ember.EnumerableUtils.forEach;
-
-function ArrayComputedProperty() {
- var cp = this;
-
- ReduceComputedProperty.apply(this, arguments);
-
- this.func = (function(reduceFunc) {
- return function (propertyName) {
- if (!cp._hasInstanceMeta(this, propertyName)) {
- // When we recompute an array computed property, we need already
- // retrieved arrays to be updated; we can't simply empty the cache and
- // hope the array is re-retrieved.
- forEach(cp._dependentKeys, function(dependentKey) {
- Ember.addObserver(this, dependentKey, function() {
- cp.recomputeOnce.call(this, propertyName);
- });
- }, this);
- }
-
- return reduceFunc.apply(this, arguments);
- };
- })(this.func);
-
- return this;
-}
-Ember.ArrayComputedProperty = ArrayComputedProperty;
-ArrayComputedProperty.prototype = o_create(ReduceComputedProperty.prototype);
-ArrayComputedProperty.prototype.initialValue = function () {
- return Ember.A();
-};
-ArrayComputedProperty.prototype.resetValue = function (array) {
- array.clear();
- return array;
-};
-
-/**
- Creates a computed property which operates on dependent arrays and
- is updated with "one at a time" semantics. When items are added or
- removed from the dependent array(s) an array computed only operates
- on the change instead of re-evaluating the entire array. This should
- return an array, if you'd like to use "one at a time" semantics and
- compute some value other then an array look at
- `Ember.reduceComputed`.
-
- If there are more than one arguments the first arguments are
- considered to be dependent property keys. The last argument is
- required to be an options object. The options object can have the
- following three properties.
-
- `initialize` - An optional initialize function. Typically this will be used
- to set up state on the instanceMeta object.
-
- `removedItem` - A function that is called each time an element is
- removed from the array.
-
- `addedItem` - A function that is called each time an element is
- added to the array.
-
-
- The `initialize` function has the following signature:
-
- ```javascript
- function (array, changeMeta, instanceMeta)
- ```
-
- `array` - The initial value of the arrayComputed, an empty array.
-
- `changeMeta` - An object which contains meta information about the
- computed. It contains the following properties:
-
- - `property` the computed property
- - `propertyName` the name of the property on the object
-
- `instanceMeta` - An object that can be used to store meta
- information needed for calculating your computed. For example a
- unique computed might use this to store the number of times a given
- element is found in the dependent array.
-
-
- The `removedItem` and `addedItem` functions both have the following signature:
-
- ```javascript
- function (accumulatedValue, item, changeMeta, instanceMeta)
- ```
-
- `accumulatedValue` - The value returned from the last time
- `removedItem` or `addedItem` was called or an empty array.
-
- `item` - the element added or removed from the array
-
- `changeMeta` - An object which contains meta information about the
- change. It contains the following properties:
-
- - `property` the computed property
- - `propertyName` the name of the property on the object
- - `index` the index of the added or removed item
- - `item` the added or removed item: this is exactly the same as
- the second arg
- - `arrayChanged` the array that triggered the change. Can be
- useful when depending on multiple arrays.
-
- For property changes triggered on an item property change (when
- depKey is something like `someArray.@each.someProperty`),
- `changeMeta` will also contain the following property:
-
- - `previousValues` an object whose keys are the properties that changed on
- the item, and whose values are the item's previous values.
-
- `previousValues` is important Ember coalesces item property changes via
- Ember.run.once. This means that by the time removedItem gets called, item has
- the new values, but you may need the previous value (eg for sorting &
- filtering).
-
- `instanceMeta` - An object that can be used to store meta
- information needed for calculating your computed. For example a
- unique computed might use this to store the number of times a given
- element is found in the dependent array.
-
- The `removedItem` and `addedItem` functions should return the accumulated
- value. It is acceptable to not return anything (ie return undefined)
- to invalidate the computation. This is generally not a good idea for
- arrayComputed but it's used in eg max and min.
-
- Example
-
- ```javascript
- Ember.computed.map = function(dependentKey, callback) {
- var options = {
- addedItem: function(array, item, changeMeta, instanceMeta) {
- var mapped = callback(item);
- array.insertAt(changeMeta.index, mapped);
- return array;
- },
- removedItem: function(array, item, changeMeta, instanceMeta) {
- array.removeAt(changeMeta.index, 1);
- return array;
- }
- };
-
- return Ember.arrayComputed(dependentKey, options);
- };
- ```
-
- @method arrayComputed
- @for Ember
- @param {String} [dependentKeys*]
- @param {Object} options
- @return {Ember.ComputedProperty}
-*/
-Ember.arrayComputed = function (options) {
- var args;
-
- if (arguments.length > 1) {
- args = a_slice.call(arguments, 0, -1);
- options = a_slice.call(arguments, -1)[0];
- }
-
- if (typeof options !== "object") {
- throw new Ember.Error("Array Computed Property declared without an options hash");
- }
-
- var cp = new ArrayComputedProperty(options);
-
- if (args) {
- cp.property.apply(cp, args);
- }
-
- return cp;
-};
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-runtime
-*/
-
-var get = Ember.get,
- set = Ember.set,
- guidFor = Ember.guidFor,
- merge = Ember.merge,
- a_slice = [].slice,
- forEach = Ember.EnumerableUtils.forEach,
- map = Ember.EnumerableUtils.map,
- SearchProxy;
-
-/**
- A computed property that calculates the maximum value in the
- dependent array. This will return `-Infinity` when the dependent
- array is empty.
-
- Example
-
- ```javascript
- App.Person = Ember.Object.extend({
- childAges: Ember.computed.mapBy('children', 'age'),
- maxChildAge: Ember.computed.max('childAges')
- });
-
- var lordByron = App.Person.create({children: []});
- lordByron.get('maxChildAge'); // -Infinity
- lordByron.get('children').pushObject({name: 'Augusta Ada Byron', age: 7});
- lordByron.get('maxChildAge'); // 7
- lordByron.get('children').pushObjects([{name: 'Allegra Byron', age: 5}, {name: 'Elizabeth Medora Leigh', age: 8}]);
- lordByron.get('maxChildAge'); // 8
- ```
-
- @method computed.max
- @for Ember
- @param {String} dependentKey
- @return {Ember.ComputedProperty} computes the largest value in the dependentKey's array
-*/
-Ember.computed.max = function (dependentKey) {
- return Ember.reduceComputed.call(null, dependentKey, {
- initialValue: -Infinity,
-
- addedItem: function (accumulatedValue, item, changeMeta, instanceMeta) {
- return Math.max(accumulatedValue, item);
- },
-
- removedItem: function (accumulatedValue, item, changeMeta, instanceMeta) {
- if (item < accumulatedValue) {
- return accumulatedValue;
- }
- }
- });
-};
-
-/**
- A computed property that calculates the minimum value in the
- dependent array. This will return `Infinity` when the dependent
- array is empty.
-
- Example
-
- ```javascript
- App.Person = Ember.Object.extend({
- childAges: Ember.computed.mapBy('children', 'age'),
- minChildAge: Ember.computed.min('childAges')
- });
-
- var lordByron = App.Person.create({children: []});
- lordByron.get('minChildAge'); // Infinity
- lordByron.get('children').pushObject({name: 'Augusta Ada Byron', age: 7});
- lordByron.get('minChildAge'); // 7
- lordByron.get('children').pushObjects([{name: 'Allegra Byron', age: 5}, {name: 'Elizabeth Medora Leigh', age: 8}]);
- lordByron.get('minChildAge'); // 5
- ```
-
- @method computed.min
- @for Ember
- @param {String} dependentKey
- @return {Ember.ComputedProperty} computes the smallest value in the dependentKey's array
-*/
-Ember.computed.min = function (dependentKey) {
- return Ember.reduceComputed.call(null, dependentKey, {
- initialValue: Infinity,
-
- addedItem: function (accumulatedValue, item, changeMeta, instanceMeta) {
- return Math.min(accumulatedValue, item);
- },
-
- removedItem: function (accumulatedValue, item, changeMeta, instanceMeta) {
- if (item > accumulatedValue) {
- return accumulatedValue;
- }
- }
- });
-};
-
-/**
- Returns an array mapped via the callback
-
- The callback method you provide should have the following signature:
-
- ```javascript
- function(item);
- ```
-
- - `item` is the current item in the iteration.
-
- Example
-
- ```javascript
- App.Hamster = Ember.Object.extend({
- excitingChores: Ember.computed.map('chores', function(chore) {
- return chore.toUpperCase() + '!';
- })
- });
-
- var hamster = App.Hamster.create({chores: ['cook', 'clean', 'write more unit tests']});
- hamster.get('excitingChores'); // ['COOK!', 'CLEAN!', 'WRITE MORE UNIT TESTS!']
- ```
-
- @method computed.map
- @for Ember
- @param {String} dependentKey
- @param {Function} callback
- @return {Ember.ComputedProperty} an array mapped via the callback
-*/
-Ember.computed.map = function(dependentKey, callback) {
- var options = {
- addedItem: function(array, item, changeMeta, instanceMeta) {
- var mapped = callback.call(this, item);
- array.insertAt(changeMeta.index, mapped);
- return array;
- },
- removedItem: function(array, item, changeMeta, instanceMeta) {
- array.removeAt(changeMeta.index, 1);
- return array;
- }
- };
-
- return Ember.arrayComputed(dependentKey, options);
-};
-
-/**
- Returns an array mapped to the specified key.
-
- Example
-
- ```javascript
- App.Person = Ember.Object.extend({
- childAges: Ember.computed.mapBy('children', 'age')
- });
-
- var lordByron = App.Person.create({children: []});
- lordByron.get('childAges'); // []
- lordByron.get('children').pushObject({name: 'Augusta Ada Byron', age: 7});
- lordByron.get('childAges'); // [7]
- lordByron.get('children').pushObjects([{name: 'Allegra Byron', age: 5}, {name: 'Elizabeth Medora Leigh', age: 8}]);
- lordByron.get('childAges'); // [7, 5, 8]
- ```
-
- @method computed.mapBy
- @for Ember
- @param {String} dependentKey
- @param {String} propertyKey
- @return {Ember.ComputedProperty} an array mapped to the specified key
-*/
-Ember.computed.mapBy = function(dependentKey, propertyKey) {
- var callback = function(item) { return get(item, propertyKey); };
- return Ember.computed.map(dependentKey + '.@each.' + propertyKey, callback);
-};
-
-/**
- @method computed.mapProperty
- @for Ember
- @deprecated Use `Ember.computed.mapBy` instead
- @param dependentKey
- @param propertyKey
-*/
-Ember.computed.mapProperty = Ember.computed.mapBy;
-
-/**
- Filters the array by the callback.
-
- The callback method you provide should have the following signature:
-
- ```javascript
- function(item);
- ```
-
- - `item` is the current item in the iteration.
-
- Example
-
- ```javascript
- App.Hamster = Ember.Object.extend({
- remainingChores: Ember.computed.filter('chores', function(chore) {
- return !chore.done;
- })
- });
-
- var hamster = App.Hamster.create({chores: [
- {name: 'cook', done: true},
- {name: 'clean', done: true},
- {name: 'write more unit tests', done: false}
- ]});
- hamster.get('remainingChores'); // [{name: 'write more unit tests', done: false}]
- ```
-
- @method computed.filter
- @for Ember
- @param {String} dependentKey
- @param {Function} callback
- @return {Ember.ComputedProperty} the filtered array
-*/
-Ember.computed.filter = function(dependentKey, callback) {
- var options = {
- initialize: function (array, changeMeta, instanceMeta) {
- instanceMeta.filteredArrayIndexes = new Ember.SubArray();
- },
-
- addedItem: function(array, item, changeMeta, instanceMeta) {
- var match = !!callback.call(this, item),
- filterIndex = instanceMeta.filteredArrayIndexes.addItem(changeMeta.index, match);
-
- if (match) {
- array.insertAt(filterIndex, item);
- }
-
- return array;
- },
-
- removedItem: function(array, item, changeMeta, instanceMeta) {
- var filterIndex = instanceMeta.filteredArrayIndexes.removeItem(changeMeta.index);
-
- if (filterIndex > -1) {
- array.removeAt(filterIndex);
- }
-
- return array;
- }
- };
-
- return Ember.arrayComputed(dependentKey, options);
-};
-
-/**
- Filters the array by the property and value
-
- Example
-
- ```javascript
- App.Hamster = Ember.Object.extend({
- remainingChores: Ember.computed.filterBy('chores', 'done', false)
- });
-
- var hamster = App.Hamster.create({chores: [
- {name: 'cook', done: true},
- {name: 'clean', done: true},
- {name: 'write more unit tests', done: false}
- ]});
- hamster.get('remainingChores'); // [{name: 'write more unit tests', done: false}]
- ```
-
- @method computed.filterBy
- @for Ember
- @param {String} dependentKey
- @param {String} propertyKey
- @param {String} value
- @return {Ember.ComputedProperty} the filtered array
-*/
-Ember.computed.filterBy = function(dependentKey, propertyKey, value) {
- var callback;
-
- if (arguments.length === 2) {
- callback = function(item) {
- return get(item, propertyKey);
- };
- } else {
- callback = function(item) {
- return get(item, propertyKey) === value;
- };
- }
-
- return Ember.computed.filter(dependentKey + '.@each.' + propertyKey, callback);
-};
-
-/**
- @method computed.filterProperty
- @for Ember
- @param dependentKey
- @param propertyKey
- @param value
- @deprecated Use `Ember.computed.filterBy` instead
-*/
-Ember.computed.filterProperty = Ember.computed.filterBy;
-
-/**
- A computed property which returns a new array with all the unique
- elements from one or more dependent arrays.
-
- Example
-
- ```javascript
- App.Hamster = Ember.Object.extend({
- uniqueFruits: Ember.computed.uniq('fruits')
- });
-
- var hamster = App.Hamster.create({fruits: [
- 'banana',
- 'grape',
- 'kale',
- 'banana'
- ]});
- hamster.get('uniqueFruits'); // ['banana', 'grape', 'kale']
- ```
-
- @method computed.uniq
- @for Ember
- @param {String} propertyKey*
- @return {Ember.ComputedProperty} computes a new array with all the
- unique elements from the dependent array
-*/
-Ember.computed.uniq = function() {
- var args = a_slice.call(arguments);
- args.push({
- initialize: function(array, changeMeta, instanceMeta) {
- instanceMeta.itemCounts = {};
- },
-
- addedItem: function(array, item, changeMeta, instanceMeta) {
- var guid = guidFor(item);
-
- if (!instanceMeta.itemCounts[guid]) {
- instanceMeta.itemCounts[guid] = 1;
- } else {
- ++instanceMeta.itemCounts[guid];
- }
- array.addObject(item);
- return array;
- },
- removedItem: function(array, item, _, instanceMeta) {
- var guid = guidFor(item),
- itemCounts = instanceMeta.itemCounts;
-
- if (--itemCounts[guid] === 0) {
- array.removeObject(item);
- }
- return array;
- }
- });
- return Ember.arrayComputed.apply(null, args);
-};
-
-/**
- Alias for [Ember.computed.uniq](/api/#method_computed_uniq).
-
- @method computed.union
- @for Ember
- @param {String} propertyKey*
- @return {Ember.ComputedProperty} computes a new array with all the
- unique elements from the dependent array
-*/
-Ember.computed.union = Ember.computed.uniq;
-
-/**
- A computed property which returns a new array with all the duplicated
- elements from two or more dependeny arrays.
-
- Example
-
- ```javascript
- var obj = Ember.Object.createWithMixins({
- adaFriends: ['Charles Babbage', 'John Hobhouse', 'William King', 'Mary Somerville'],
- charlesFriends: ['William King', 'Mary Somerville', 'Ada Lovelace', 'George Peacock'],
- friendsInCommon: Ember.computed.intersect('adaFriends', 'charlesFriends')
- });
-
- obj.get('friendsInCommon'); // ['William King', 'Mary Somerville']
- ```
-
- @method computed.intersect
- @for Ember
- @param {String} propertyKey*
- @return {Ember.ComputedProperty} computes a new array with all the
- duplicated elements from the dependent arrays
-*/
-Ember.computed.intersect = function () {
- var getDependentKeyGuids = function (changeMeta) {
- return map(changeMeta.property._dependentKeys, function (dependentKey) {
- return guidFor(dependentKey);
- });
- };
-
- var args = a_slice.call(arguments);
- args.push({
- initialize: function (array, changeMeta, instanceMeta) {
- instanceMeta.itemCounts = {};
- },
-
- addedItem: function(array, item, changeMeta, instanceMeta) {
- var itemGuid = guidFor(item),
- dependentGuids = getDependentKeyGuids(changeMeta),
- dependentGuid = guidFor(changeMeta.arrayChanged),
- numberOfDependentArrays = changeMeta.property._dependentKeys.length,
- itemCounts = instanceMeta.itemCounts;
-
- if (!itemCounts[itemGuid]) { itemCounts[itemGuid] = {}; }
- if (itemCounts[itemGuid][dependentGuid] === undefined) { itemCounts[itemGuid][dependentGuid] = 0; }
-
- if (++itemCounts[itemGuid][dependentGuid] === 1 &&
- numberOfDependentArrays === Ember.keys(itemCounts[itemGuid]).length) {
-
- array.addObject(item);
- }
- return array;
- },
- removedItem: function(array, item, changeMeta, instanceMeta) {
- var itemGuid = guidFor(item),
- dependentGuids = getDependentKeyGuids(changeMeta),
- dependentGuid = guidFor(changeMeta.arrayChanged),
- numberOfDependentArrays = changeMeta.property._dependentKeys.length,
- numberOfArraysItemAppearsIn,
- itemCounts = instanceMeta.itemCounts;
-
- if (itemCounts[itemGuid][dependentGuid] === undefined) { itemCounts[itemGuid][dependentGuid] = 0; }
- if (--itemCounts[itemGuid][dependentGuid] === 0) {
- delete itemCounts[itemGuid][dependentGuid];
- numberOfArraysItemAppearsIn = Ember.keys(itemCounts[itemGuid]).length;
-
- if (numberOfArraysItemAppearsIn === 0) {
- delete itemCounts[itemGuid];
- }
- array.removeObject(item);
- }
- return array;
- }
- });
- return Ember.arrayComputed.apply(null, args);
-};
-
-/**
- A computed property which returns a new array with all the
- properties from the first dependent array that are not in the second
- dependent array.
-
- Example
-
- ```javascript
- App.Hamster = Ember.Object.extend({
- likes: ['banana', 'grape', 'kale'],
- wants: Ember.computed.setDiff('likes', 'fruits')
- });
-
- var hamster = App.Hamster.create({fruits: [
- 'grape',
- 'kale',
- ]});
- hamster.get('wants'); // ['banana']
- ```
-
- @method computed.setDiff
- @for Ember
- @param {String} setAProperty
- @param {String} setBProperty
- @return {Ember.ComputedProperty} computes a new array with all the
- items from the first dependent array that are not in the second
- dependent array
-*/
-Ember.computed.setDiff = function (setAProperty, setBProperty) {
- if (arguments.length !== 2) {
- throw new Ember.Error("setDiff requires exactly two dependent arrays.");
- }
- return Ember.arrayComputed.call(null, setAProperty, setBProperty, {
- addedItem: function (array, item, changeMeta, instanceMeta) {
- var setA = get(this, setAProperty),
- setB = get(this, setBProperty);
-
- if (changeMeta.arrayChanged === setA) {
- if (!setB.contains(item)) {
- array.addObject(item);
- }
- } else {
- array.removeObject(item);
- }
- return array;
- },
-
- removedItem: function (array, item, changeMeta, instanceMeta) {
- var setA = get(this, setAProperty),
- setB = get(this, setBProperty);
-
- if (changeMeta.arrayChanged === setB) {
- if (setA.contains(item)) {
- array.addObject(item);
- }
- } else {
- array.removeObject(item);
- }
- return array;
- }
- });
-};
-
-function binarySearch(array, item, low, high) {
- var mid, midItem, res, guidMid, guidItem;
-
- if (arguments.length < 4) { high = get(array, 'length'); }
- if (arguments.length < 3) { low = 0; }
-
- if (low === high) {
- return low;
- }
-
- mid = low + Math.floor((high - low) / 2);
- midItem = array.objectAt(mid);
-
- guidMid = _guidFor(midItem);
- guidItem = _guidFor(item);
-
- if (guidMid === guidItem) {
- return mid;
- }
-
- res = this.order(midItem, item);
- if (res === 0) {
- res = guidMid < guidItem ? -1 : 1;
- }
-
-
- if (res < 0) {
- return this.binarySearch(array, item, mid+1, high);
- } else if (res > 0) {
- return this.binarySearch(array, item, low, mid);
- }
-
- return mid;
-
- function _guidFor(item) {
- if (SearchProxy.detectInstance(item)) {
- return guidFor(get(item, 'content'));
- }
- return guidFor(item);
- }
-}
-
-
-SearchProxy = Ember.ObjectProxy.extend();
-
-/**
- A computed property which returns a new array with all the
- properties from the first dependent array sorted based on a property
- or sort function.
-
- The callback method you provide should have the following signature:
-
- ```javascript
- function(itemA, itemB);
- ```
-
- - `itemA` the first item to compare.
- - `itemB` the second item to compare.
-
- This function should return `-1` when `itemA` should come before
- `itemB`. It should return `1` when `itemA` should come after
- `itemB`. If the `itemA` and `itemB` are equal this function should return `0`.
-
- Example
-
- ```javascript
- var ToDoList = Ember.Object.extend({
- todosSorting: ['name'],
- sortedTodos: Ember.computed.sort('todos', 'todosSorting'),
- priorityTodos: Ember.computed.sort('todos', function(a, b){
- if (a.priority > b.priority) {
- return 1;
- } else if (a.priority < b.priority) {
- return -1;
- }
- return 0;
- }),
- });
- var todoList = ToDoList.create({todos: [
- {name: 'Unit Test', priority: 2},
- {name: 'Documentation', priority: 3},
- {name: 'Release', priority: 1}
- ]});
-
- todoList.get('sortedTodos'); // [{name:'Documentation', priority:3}, {name:'Release', priority:1}, {name:'Unit Test', priority:2}]
- todoList.get('priorityTodos'); // [{name:'Release', priority:1}, {name:'Unit Test', priority:2}, {name:'Documentation', priority:3}]
- ```
-
- @method computed.sort
- @for Ember
- @param {String} dependentKey
- @param {String or Function} sortDefinition a dependent key to an
- array of sort properties or a function to use when sorting
- @return {Ember.ComputedProperty} computes a new sorted array based
- on the sort property array or callback function
-*/
-Ember.computed.sort = function (itemsKey, sortDefinition) {
- Ember.assert("Ember.computed.sort requires two arguments: an array key to sort and either a sort properties key or sort function", arguments.length === 2);
-
- var initFn, sortPropertiesKey;
-
- if (typeof sortDefinition === 'function') {
- initFn = function (array, changeMeta, instanceMeta) {
- instanceMeta.order = sortDefinition;
- instanceMeta.binarySearch = binarySearch;
- };
- } else {
- sortPropertiesKey = sortDefinition;
- initFn = function (array, changeMeta, instanceMeta) {
- function setupSortProperties() {
- var sortPropertyDefinitions = get(this, sortPropertiesKey),
- sortProperty,
- sortProperties = instanceMeta.sortProperties = [],
- sortPropertyAscending = instanceMeta.sortPropertyAscending = {},
- idx,
- asc;
-
- Ember.assert("Cannot sort: '" + sortPropertiesKey + "' is not an array.", Ember.isArray(sortPropertyDefinitions));
-
- changeMeta.property.clearItemPropertyKeys(itemsKey);
-
- forEach(sortPropertyDefinitions, function (sortPropertyDefinition) {
- if ((idx = sortPropertyDefinition.indexOf(':')) !== -1) {
- sortProperty = sortPropertyDefinition.substring(0, idx);
- asc = sortPropertyDefinition.substring(idx+1).toLowerCase() !== 'desc';
- } else {
- sortProperty = sortPropertyDefinition;
- asc = true;
- }
-
- sortProperties.push(sortProperty);
- sortPropertyAscending[sortProperty] = asc;
- changeMeta.property.itemPropertyKey(itemsKey, sortProperty);
- });
-
- sortPropertyDefinitions.addObserver('@each', this, updateSortPropertiesOnce);
- }
-
- function updateSortPropertiesOnce() {
- Ember.run.once(this, updateSortProperties, changeMeta.propertyName);
- }
-
- function updateSortProperties(propertyName) {
- setupSortProperties.call(this);
- changeMeta.property.recomputeOnce.call(this, propertyName);
- }
-
- Ember.addObserver(this, sortPropertiesKey, updateSortPropertiesOnce);
-
- setupSortProperties.call(this);
-
-
- instanceMeta.order = function (itemA, itemB) {
- var sortProperty, result, asc;
- for (var i = 0; i < this.sortProperties.length; ++i) {
- sortProperty = this.sortProperties[i];
- result = Ember.compare(get(itemA, sortProperty), get(itemB, sortProperty));
-
- if (result !== 0) {
- asc = this.sortPropertyAscending[sortProperty];
- return asc ? result : (-1 * result);
- }
- }
-
- return 0;
- };
-
- instanceMeta.binarySearch = binarySearch;
- };
- }
-
- return Ember.arrayComputed.call(null, itemsKey, {
- initialize: initFn,
-
- addedItem: function (array, item, changeMeta, instanceMeta) {
- var index = instanceMeta.binarySearch(array, item);
- array.insertAt(index, item);
- return array;
- },
-
- removedItem: function (array, item, changeMeta, instanceMeta) {
- var proxyProperties, index, searchItem;
-
- if (changeMeta.previousValues) {
- proxyProperties = merge({ content: item }, changeMeta.previousValues);
-
- searchItem = SearchProxy.create(proxyProperties);
- } else {
- searchItem = item;
- }
-
- index = instanceMeta.binarySearch(array, searchItem);
- array.removeAt(index);
- return array;
- }
- });
-};
-
-})();
-
-
-
-(function() {
-/**
- Expose RSVP implementation
-
- Documentation can be found here: https://github.com/tildeio/rsvp.js/blob/master/README.md
-
- @class RSVP
- @namespace Ember
- @constructor
-*/
-Ember.RSVP = requireModule('rsvp');
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-runtime
-*/
-
-var a_slice = Array.prototype.slice;
-
-if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Function) {
-
- /**
- The `property` extension of Javascript's Function prototype is available
- when `Ember.EXTEND_PROTOTYPES` or `Ember.EXTEND_PROTOTYPES.Function` is
- `true`, which is the default.
-
- Computed properties allow you to treat a function like a property:
-
- ```javascript
- MyApp.President = Ember.Object.extend({
- firstName: '',
- lastName: '',
-
- fullName: function() {
- return this.get('firstName') + ' ' + this.get('lastName');
-
- // Call this flag to mark the function as a property
- }.property()
- });
-
- var president = MyApp.President.create({
- firstName: "Barack",
- lastName: "Obama"
- });
-
- president.get('fullName'); // "Barack Obama"
- ```
-
- Treating a function like a property is useful because they can work with
- bindings, just like any other property.
-
- Many computed properties have dependencies on other properties. For
- example, in the above example, the `fullName` property depends on
- `firstName` and `lastName` to determine its value. You can tell Ember
- about these dependencies like this:
-
- ```javascript
- MyApp.President = Ember.Object.extend({
- firstName: '',
- lastName: '',
-
- fullName: function() {
- return this.get('firstName') + ' ' + this.get('lastName');
-
- // Tell Ember.js that this computed property depends on firstName
- // and lastName
- }.property('firstName', 'lastName')
- });
- ```
-
- Make sure you list these dependencies so Ember knows when to update
- bindings that connect to a computed property. Changing a dependency
- will not immediately trigger an update of the computed property, but
- will instead clear the cache so that it is updated when the next `get`
- is called on the property.
-
- See [Ember.ComputedProperty](/api/classes/Ember.ComputedProperty.html), [Ember.computed](/api/#method_computed).
-
- @method property
- @for Function
- */
- Function.prototype.property = function() {
- var ret = Ember.computed(this);
- return ret.property.apply(ret, arguments);
- };
-
- /**
- The `observes` extension of Javascript's Function prototype is available
- when `Ember.EXTEND_PROTOTYPES` or `Ember.EXTEND_PROTOTYPES.Function` is
- true, which is the default.
-
- You can observe property changes simply by adding the `observes`
- call to the end of your method declarations in classes that you write.
- For example:
-
- ```javascript
- Ember.Object.extend({
- valueObserver: function() {
- // Executes whenever the "value" property changes
- }.observes('value')
- });
- ```
-
- In the future this method may become asynchronous. If you want to ensure
- synchronous behavior, use `observesImmediately`.
-
- See `Ember.observer`.
-
- @method observes
- @for Function
- */
- Function.prototype.observes = function() {
- this.__ember_observes__ = a_slice.call(arguments);
- return this;
- };
-
- /**
- The `observesImmediately` extension of Javascript's Function prototype is
- available when `Ember.EXTEND_PROTOTYPES` or
- `Ember.EXTEND_PROTOTYPES.Function` is true, which is the default.
-
- You can observe property changes simply by adding the `observesImmediately`
- call to the end of your method declarations in classes that you write.
- For example:
-
- ```javascript
- Ember.Object.extend({
- valueObserver: function() {
- // Executes immediately after the "value" property changes
- }.observesImmediately('value')
- });
- ```
-
- In the future, `observes` may become asynchronous. In this event,
- `observesImmediately` will maintain the synchronous behavior.
-
- See `Ember.immediateObserver`.
-
- @method observesImmediately
- @for Function
- */
- Function.prototype.observesImmediately = function() {
- for (var i=0, l=arguments.length; i b`
-
- Default implementation raises an exception.
-
- @method compare
- @param a {Object} the first object to compare
- @param b {Object} the second object to compare
- @return {Integer} the result of the comparison
- */
- compare: Ember.required(Function)
-
-});
-
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-runtime
-*/
-
-
-
-var get = Ember.get, set = Ember.set;
-
-/**
- Implements some standard methods for copying an object. Add this mixin to
- any object you create that can create a copy of itself. This mixin is
- added automatically to the built-in array.
-
- You should generally implement the `copy()` method to return a copy of the
- receiver.
-
- Note that `frozenCopy()` will only work if you also implement
- `Ember.Freezable`.
-
- @class Copyable
- @namespace Ember
- @since Ember 0.9
-*/
-Ember.Copyable = Ember.Mixin.create(/** @scope Ember.Copyable.prototype */ {
-
- /**
- Override to return a copy of the receiver. Default implementation raises
- an exception.
-
- @method copy
- @param {Boolean} deep if `true`, a deep copy of the object should be made
- @return {Object} copy of receiver
- */
- copy: Ember.required(Function),
-
- /**
- If the object implements `Ember.Freezable`, then this will return a new
- copy if the object is not frozen and the receiver if the object is frozen.
-
- Raises an exception if you try to call this method on a object that does
- not support freezing.
-
- You should use this method whenever you want a copy of a freezable object
- since a freezable object can simply return itself without actually
- consuming more memory.
-
- @method frozenCopy
- @return {Object} copy of receiver or receiver
- */
- frozenCopy: function() {
- if (Ember.Freezable && Ember.Freezable.detect(this)) {
- return get(this, 'isFrozen') ? this : this.copy().freeze();
- } else {
- throw new Ember.Error(Ember.String.fmt("%@ does not support freezing", [this]));
- }
- }
-});
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-runtime
-*/
-
-
-var get = Ember.get, set = Ember.set;
-
-/**
- The `Ember.Freezable` mixin implements some basic methods for marking an
- object as frozen. Once an object is frozen it should be read only. No changes
- may be made the internal state of the object.
-
- ## Enforcement
-
- To fully support freezing in your subclass, you must include this mixin and
- override any method that might alter any property on the object to instead
- raise an exception. You can check the state of an object by checking the
- `isFrozen` property.
-
- Although future versions of JavaScript may support language-level freezing
- object objects, that is not the case today. Even if an object is freezable,
- it is still technically possible to modify the object, even though it could
- break other parts of your application that do not expect a frozen object to
- change. It is, therefore, very important that you always respect the
- `isFrozen` property on all freezable objects.
-
- ## Example Usage
-
- The example below shows a simple object that implement the `Ember.Freezable`
- protocol.
-
- ```javascript
- Contact = Ember.Object.extend(Ember.Freezable, {
- firstName: null,
- lastName: null,
-
- // swaps the names
- swapNames: function() {
- if (this.get('isFrozen')) throw Ember.FROZEN_ERROR;
- var tmp = this.get('firstName');
- this.set('firstName', this.get('lastName'));
- this.set('lastName', tmp);
- return this;
- }
-
- });
-
- c = Contact.create({ firstName: "John", lastName: "Doe" });
- c.swapNames(); // returns c
- c.freeze();
- c.swapNames(); // EXCEPTION
- ```
-
- ## Copying
-
- Usually the `Ember.Freezable` protocol is implemented in cooperation with the
- `Ember.Copyable` protocol, which defines a `frozenCopy()` method that will
- return a frozen object, if the object implements this method as well.
-
- @class Freezable
- @namespace Ember
- @since Ember 0.9
-*/
-Ember.Freezable = Ember.Mixin.create(/** @scope Ember.Freezable.prototype */ {
-
- /**
- Set to `true` when the object is frozen. Use this property to detect
- whether your object is frozen or not.
-
- @property isFrozen
- @type Boolean
- */
- isFrozen: false,
-
- /**
- Freezes the object. Once this method has been called the object should
- no longer allow any properties to be edited.
-
- @method freeze
- @return {Object} receiver
- */
- freeze: function() {
- if (get(this, 'isFrozen')) return this;
- set(this, 'isFrozen', true);
- return this;
- }
-
-});
-
-Ember.FROZEN_ERROR = "Frozen object cannot be modified.";
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-runtime
-*/
-
-var forEach = Ember.EnumerableUtils.forEach;
-
-/**
- This mixin defines the API for modifying generic enumerables. These methods
- can be applied to an object regardless of whether it is ordered or
- unordered.
-
- Note that an Enumerable can change even if it does not implement this mixin.
- For example, a MappedEnumerable cannot be directly modified but if its
- underlying enumerable changes, it will change also.
-
- ## Adding Objects
-
- To add an object to an enumerable, use the `addObject()` method. This
- method will only add the object to the enumerable if the object is not
- already present and is of a type supported by the enumerable.
-
- ```javascript
- set.addObject(contact);
- ```
-
- ## Removing Objects
-
- To remove an object from an enumerable, use the `removeObject()` method. This
- will only remove the object if it is present in the enumerable, otherwise
- this method has no effect.
-
- ```javascript
- set.removeObject(contact);
- ```
-
- ## Implementing In Your Own Code
-
- If you are implementing an object and want to support this API, just include
- this mixin in your class and implement the required methods. In your unit
- tests, be sure to apply the Ember.MutableEnumerableTests to your object.
-
- @class MutableEnumerable
- @namespace Ember
- @uses Ember.Enumerable
-*/
-Ember.MutableEnumerable = Ember.Mixin.create(Ember.Enumerable, {
-
- /**
- __Required.__ You must implement this method to apply this mixin.
-
- Attempts to add the passed object to the receiver if the object is not
- already present in the collection. If the object is present, this method
- has no effect.
-
- If the passed object is of a type not supported by the receiver,
- then this method should raise an exception.
-
- @method addObject
- @param {Object} object The object to add to the enumerable.
- @return {Object} the passed object
- */
- addObject: Ember.required(Function),
-
- /**
- Adds each object in the passed enumerable to the receiver.
-
- @method addObjects
- @param {Ember.Enumerable} objects the objects to add.
- @return {Object} receiver
- */
- addObjects: function(objects) {
- Ember.beginPropertyChanges(this);
- forEach(objects, function(obj) { this.addObject(obj); }, this);
- Ember.endPropertyChanges(this);
- return this;
- },
-
- /**
- __Required.__ You must implement this method to apply this mixin.
-
- Attempts to remove the passed object from the receiver collection if the
- object is present in the collection. If the object is not present,
- this method has no effect.
-
- If the passed object is of a type not supported by the receiver,
- then this method should raise an exception.
-
- @method removeObject
- @param {Object} object The object to remove from the enumerable.
- @return {Object} the passed object
- */
- removeObject: Ember.required(Function),
-
-
- /**
- Removes each object in the passed enumerable from the receiver.
-
- @method removeObjects
- @param {Ember.Enumerable} objects the objects to remove
- @return {Object} receiver
- */
- removeObjects: function(objects) {
- Ember.beginPropertyChanges(this);
- forEach(objects, function(obj) { this.removeObject(obj); }, this);
- Ember.endPropertyChanges(this);
- return this;
- }
-
-});
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-runtime
-*/
-// ..........................................................
-// CONSTANTS
-//
-
-var OUT_OF_RANGE_EXCEPTION = "Index out of range" ;
-var EMPTY = [];
-
-// ..........................................................
-// HELPERS
-//
-
-var get = Ember.get, set = Ember.set;
-
-/**
- This mixin defines the API for modifying array-like objects. These methods
- can be applied only to a collection that keeps its items in an ordered set.
-
- Note that an Array can change even if it does not implement this mixin.
- For example, one might implement a SparseArray that cannot be directly
- modified, but if its underlying enumerable changes, it will change also.
-
- @class MutableArray
- @namespace Ember
- @uses Ember.Array
- @uses Ember.MutableEnumerable
-*/
-Ember.MutableArray = Ember.Mixin.create(Ember.Array, Ember.MutableEnumerable,/** @scope Ember.MutableArray.prototype */ {
-
- /**
- __Required.__ You must implement this method to apply this mixin.
-
- This is one of the primitives you must implement to support `Ember.Array`.
- You should replace amt objects started at idx with the objects in the
- passed array. You should also call `this.enumerableContentDidChange()`
-
- @method replace
- @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*
- */
- replace: Ember.required(),
-
- /**
- Remove all elements from self. This is useful if you
- want to reuse an existing array without having to recreate it.
-
- ```javascript
- var colors = ["red", "green", "blue"];
- color.length(); // 3
- colors.clear(); // []
- colors.length(); // 0
- ```
-
- @method clear
- @return {Ember.Array} An empty Array.
- */
- clear: function () {
- var len = get(this, 'length');
- if (len === 0) return this;
- this.replace(0, len, EMPTY);
- return this;
- },
-
- /**
- This will use the primitive `replace()` method to insert an object at the
- specified index.
-
- ```javascript
- var colors = ["red", "green", "blue"];
- colors.insertAt(2, "yellow"); // ["red", "green", "yellow", "blue"]
- colors.insertAt(5, "orange"); // Error: Index out of range
- ```
-
- @method insertAt
- @param {Number} idx index of insert the object at.
- @param {Object} object object to insert
- @return this
- */
- insertAt: function(idx, object) {
- if (idx > get(this, 'length')) throw new Ember.Error(OUT_OF_RANGE_EXCEPTION) ;
- this.replace(idx, 0, [object]) ;
- return this ;
- },
-
- /**
- Remove an object at the specified index using the `replace()` primitive
- method. You can pass either a single index, or a start and a length.
-
- If you pass a start and length that is beyond the
- length this method will throw an `OUT_OF_RANGE_EXCEPTION`
-
- ```javascript
- var colors = ["red", "green", "blue", "yellow", "orange"];
- colors.removeAt(0); // ["green", "blue", "yellow", "orange"]
- colors.removeAt(2, 2); // ["green", "blue"]
- colors.removeAt(4, 2); // Error: Index out of range
- ```
-
- @method removeAt
- @param {Number} start index, start of range
- @param {Number} len length of passing range
- @return {Object} receiver
- */
- removeAt: function(start, len) {
- if ('number' === typeof start) {
-
- if ((start < 0) || (start >= get(this, 'length'))) {
- throw new Ember.Error(OUT_OF_RANGE_EXCEPTION);
- }
-
- // fast case
- if (len === undefined) len = 1;
- this.replace(start, len, EMPTY);
- }
-
- return this ;
- },
-
- /**
- Push the object onto the end of the array. Works just like `push()` but it
- is KVO-compliant.
-
- ```javascript
- var colors = ["red", "green", "blue"];
- colors.pushObject("black"); // ["red", "green", "blue", "black"]
- colors.pushObject(["yellow", "orange"]); // ["red", "green", "blue", "black", ["yellow", "orange"]]
- ```
-
- @method pushObject
- @param {*} obj object to push
- @return {*} the same obj passed as param
- */
- pushObject: function(obj) {
- this.insertAt(get(this, 'length'), obj) ;
- return obj ;
- },
-
- /**
- Add the objects in the passed numerable to the end of the array. Defers
- notifying observers of the change until all objects are added.
-
- ```javascript
- var colors = ["red", "green", "blue"];
- colors.pushObjects(["black"]); // ["red", "green", "blue", "black"]
- colors.pushObjects(["yellow", "orange"]); // ["red", "green", "blue", "black", "yellow", "orange"]
- ```
-
- @method pushObjects
- @param {Ember.Enumerable} objects the objects to add
- @return {Ember.Array} receiver
- */
- pushObjects: function(objects) {
- if (!(Ember.Enumerable.detect(objects) || Ember.isArray(objects))) {
- throw new TypeError("Must pass Ember.Enumerable to Ember.MutableArray#pushObjects");
- }
- this.replace(get(this, 'length'), 0, objects);
- return this;
- },
-
- /**
- Pop object from array or nil if none are left. Works just like `pop()` but
- it is KVO-compliant.
-
- ```javascript
- var colors = ["red", "green", "blue"];
- colors.popObject(); // "blue"
- console.log(colors); // ["red", "green"]
- ```
-
- @method popObject
- @return object
- */
- popObject: function() {
- var len = get(this, 'length') ;
- if (len === 0) return null ;
-
- var ret = this.objectAt(len-1) ;
- this.removeAt(len-1, 1) ;
- return ret ;
- },
-
- /**
- Shift an object from start of array or nil if none are left. Works just
- like `shift()` but it is KVO-compliant.
-
- ```javascript
- var colors = ["red", "green", "blue"];
- colors.shiftObject(); // "red"
- console.log(colors); // ["green", "blue"]
- ```
-
- @method shiftObject
- @return object
- */
- shiftObject: function() {
- if (get(this, 'length') === 0) return null ;
- var ret = this.objectAt(0) ;
- this.removeAt(0) ;
- return ret ;
- },
-
- /**
- Unshift an object to start of array. Works just like `unshift()` but it is
- KVO-compliant.
-
- ```javascript
- var colors = ["red", "green", "blue"];
- colors.unshiftObject("yellow"); // ["yellow", "red", "green", "blue"]
- colors.unshiftObject(["black", "white"]); // [["black", "white"], "yellow", "red", "green", "blue"]
- ```
-
- @method unshiftObject
- @param {*} obj object to unshift
- @return {*} the same obj passed as param
- */
- unshiftObject: function(obj) {
- this.insertAt(0, obj) ;
- return obj ;
- },
-
- /**
- Adds the named objects to the beginning of the array. Defers notifying
- observers until all objects have been added.
-
- ```javascript
- var colors = ["red", "green", "blue"];
- colors.unshiftObjects(["black", "white"]); // ["black", "white", "red", "green", "blue"]
- colors.unshiftObjects("yellow"); // Type Error: 'undefined' is not a function
- ```
-
- @method unshiftObjects
- @param {Ember.Enumerable} objects the objects to add
- @return {Ember.Array} receiver
- */
- unshiftObjects: function(objects) {
- this.replace(0, 0, objects);
- return this;
- },
-
- /**
- Reverse objects in the array. Works just like `reverse()` but it is
- KVO-compliant.
-
- @method reverseObjects
- @return {Ember.Array} receiver
- */
- reverseObjects: function() {
- var len = get(this, 'length');
- if (len === 0) return this;
- var objects = this.toArray().reverse();
- this.replace(0, len, objects);
- return this;
- },
-
- /**
- Replace all the the receiver's content with content of the argument.
- If argument is an empty array receiver will be cleared.
-
- ```javascript
- var colors = ["red", "green", "blue"];
- colors.setObjects(["black", "white"]); // ["black", "white"]
- colors.setObjects([]); // []
- ```
-
- @method setObjects
- @param {Ember.Array} objects array whose content will be used for replacing
- the content of the receiver
- @return {Ember.Array} receiver with the new content
- */
- setObjects: function(objects) {
- if (objects.length === 0) return this.clear();
-
- var len = get(this, 'length');
- this.replace(0, len, objects);
- return this;
- },
-
- // ..........................................................
- // IMPLEMENT Ember.MutableEnumerable
- //
-
- removeObject: function(obj) {
- var loc = get(this, 'length') || 0;
- while(--loc >= 0) {
- var curObject = this.objectAt(loc) ;
- if (curObject === obj) this.removeAt(loc) ;
- }
- return this ;
- },
-
- addObject: function(obj) {
- if (!this.contains(obj)) this.pushObject(obj);
- return this ;
- }
-
-});
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-runtime
-*/
-
-var get = Ember.get, set = Ember.set;
-
-/**
-`Ember.TargetActionSupport` is a mixin that can be included in a class
-to add a `triggerAction` method with semantics similar to the Handlebars
-`{{action}}` helper. In normal Ember usage, the `{{action}}` helper is
-usually the best choice. This mixin is most often useful when you are
-doing more complex event handling in View objects.
-
-See also `Ember.ViewTargetActionSupport`, which has
-view-aware defaults for target and actionContext.
-
-@class TargetActionSupport
-@namespace Ember
-@extends Ember.Mixin
-*/
-Ember.TargetActionSupport = Ember.Mixin.create({
- target: null,
- action: null,
- actionContext: null,
-
- targetObject: Ember.computed(function() {
- var target = get(this, 'target');
-
- if (Ember.typeOf(target) === "string") {
- var value = get(this, target);
- if (value === undefined) { value = get(Ember.lookup, target); }
- return value;
- } else {
- return target;
- }
- }).property('target'),
-
- actionContextObject: Ember.computed(function() {
- var actionContext = get(this, 'actionContext');
-
- if (Ember.typeOf(actionContext) === "string") {
- var value = get(this, actionContext);
- if (value === undefined) { value = get(Ember.lookup, actionContext); }
- return value;
- } else {
- return actionContext;
- }
- }).property('actionContext'),
-
- /**
- Send an "action" with an "actionContext" to a "target". The action, actionContext
- and target will be retrieved from properties of the object. For example:
-
- ```javascript
- App.SaveButtonView = Ember.View.extend(Ember.TargetActionSupport, {
- target: Ember.computed.alias('controller'),
- action: 'save',
- actionContext: Ember.computed.alias('context'),
- click: function() {
- this.triggerAction(); // Sends the `save` action, along with the current context
- // to the current controller
- }
- });
- ```
-
- The `target`, `action`, and `actionContext` can be provided as properties of
- an optional object argument to `triggerAction` as well.
-
- ```javascript
- App.SaveButtonView = Ember.View.extend(Ember.TargetActionSupport, {
- click: function() {
- this.triggerAction({
- action: 'save',
- target: this.get('controller'),
- actionContext: this.get('context'),
- }); // Sends the `save` action, along with the current context
- // to the current controller
- }
- });
- ```
-
- The `actionContext` defaults to the object you mixing `TargetActionSupport` into.
- But `target` and `action` must be specified either as properties or with the argument
- to `triggerAction`, or a combination:
-
- ```javascript
- App.SaveButtonView = Ember.View.extend(Ember.TargetActionSupport, {
- target: Ember.computed.alias('controller'),
- click: function() {
- this.triggerAction({
- action: 'save'
- }); // Sends the `save` action, along with a reference to `this`,
- // to the current controller
- }
- });
- ```
-
- @method triggerAction
- @param opts {Hash} (optional, with the optional keys action, target and/or actionContext)
- @return {Boolean} true if the action was sent successfully and did not return false
- */
- triggerAction: function(opts) {
- opts = opts || {};
- var action = opts.action || get(this, 'action'),
- target = opts.target || get(this, 'targetObject'),
- actionContext = opts.actionContext;
-
- function args(options, actionName) {
- var ret = [];
- if (actionName) { ret.push(actionName); }
-
- return ret.concat(options);
- }
-
- if (typeof actionContext === 'undefined') {
- actionContext = get(this, 'actionContextObject') || this;
- }
-
- if (target && action) {
- var ret;
-
- if (target.send) {
- ret = target.send.apply(target, args(actionContext, action));
- } else {
- Ember.assert("The action '" + action + "' did not exist on " + target, typeof target[action] === 'function');
- ret = target[action].apply(target, args(actionContext));
- }
-
- if (ret !== false) ret = true;
-
- return ret;
- } else {
- return false;
- }
- }
-});
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-runtime
-*/
-
-/**
- This mixin allows for Ember objects to subscribe to and emit events.
-
- ```javascript
- App.Person = Ember.Object.extend(Ember.Evented, {
- greet: function() {
- // ...
- this.trigger('greet');
- }
- });
-
- var person = App.Person.create();
-
- person.on('greet', function() {
- console.log('Our person has greeted');
- });
-
- person.greet();
-
- // outputs: 'Our person has greeted'
- ```
-
- You can also chain multiple event subscriptions:
-
- ```javascript
- person.on('greet', function() {
- console.log('Our person has greeted');
- }).one('greet', function() {
- console.log('Offer one-time special');
- }).off('event', this, forgetThis);
- ```
-
- @class Evented
- @namespace Ember
- */
-Ember.Evented = Ember.Mixin.create({
-
- /**
- Subscribes to a named event with given function.
-
- ```javascript
- person.on('didLoad', function() {
- // fired once the person has loaded
- });
- ```
-
- An optional target can be passed in as the 2nd argument that will
- be set as the "this" for the callback. This is a good way to give your
- function access to the object triggering the event. When the target
- parameter is used the callback becomes the third argument.
-
- @method on
- @param {String} name The name of the event
- @param {Object} [target] The "this" binding for the callback
- @param {Function} method The callback to execute
- @return this
- */
- on: function(name, target, method) {
- Ember.addListener(this, name, target, method);
- return this;
- },
-
- /**
- Subscribes a function to a named event and then cancels the subscription
- after the first time the event is triggered. It is good to use ``one`` when
- you only care about the first time an event has taken place.
-
- This function takes an optional 2nd argument that will become the "this"
- value for the callback. If this argument is passed then the 3rd argument
- becomes the function.
-
- @method one
- @param {String} name The name of the event
- @param {Object} [target] The "this" binding for the callback
- @param {Function} method The callback to execute
- @return this
- */
- one: function(name, target, method) {
- if (!method) {
- method = target;
- target = null;
- }
-
- Ember.addListener(this, name, target, method, true);
- return this;
- },
-
- /**
- Triggers a named event for the object. Any additional arguments
- will be passed as parameters to the functions that are subscribed to the
- event.
-
- ```javascript
- person.on('didEat', function(food) {
- console.log('person ate some ' + food);
- });
-
- person.trigger('didEat', 'broccoli');
-
- // outputs: person ate some broccoli
- ```
- @method trigger
- @param {String} name The name of the event
- @param {Object...} args Optional arguments to pass on
- */
- trigger: function(name) {
- var args = [], i, l;
- for (i = 1, l = arguments.length; i < l; i++) {
- args.push(arguments[i]);
- }
- Ember.sendEvent(this, name, args);
- },
-
- /**
- Cancels subscription for given name, target, and method.
-
- @method off
- @param {String} name The name of the event
- @param {Object} target The target of the subscription
- @param {Function} method The function of the subscription
- @return this
- */
- off: function(name, target, method) {
- Ember.removeListener(this, name, target, method);
- return this;
- },
-
- /**
- Checks to see if object has any subscriptions for named event.
-
- @method has
- @param {String} name The name of the event
- @return {Boolean} does the object have a subscription for event
- */
- has: function(name) {
- return Ember.hasListeners(this, name);
- }
-});
-
-})();
-
-
-
-(function() {
-var RSVP = requireModule("rsvp");
-
-RSVP.configure('async', function(callback, promise) {
- Ember.run.schedule('actions', promise, callback, promise);
-});
-
-/**
-@module ember
-@submodule ember-runtime
-*/
-
-var get = Ember.get;
-
-/**
- @class Deferred
- @namespace Ember
- */
-Ember.DeferredMixin = Ember.Mixin.create({
- /**
- Add handlers to be called when the Deferred object is resolved or rejected.
-
- @method then
- @param {Function} resolve a callback function to be called when done
- @param {Function} reject a callback function to be called when failed
- */
- then: function(resolve, reject) {
- var deferred, promise, entity;
-
- entity = this;
- deferred = get(this, '_deferred');
- promise = deferred.promise;
-
- function fulfillmentHandler(fulfillment) {
- if (fulfillment === promise) {
- return resolve(entity);
- } else {
- return resolve(fulfillment);
- }
- }
-
- return promise.then(resolve && fulfillmentHandler, reject);
- },
-
- /**
- Resolve a Deferred object and call any `doneCallbacks` with the given args.
-
- @method resolve
- */
- resolve: function(value) {
- var deferred, promise;
-
- deferred = get(this, '_deferred');
- promise = deferred.promise;
-
- if (value === this) {
- deferred.resolve(promise);
- } else {
- deferred.resolve(value);
- }
- },
-
- /**
- Reject a Deferred object and call any `failCallbacks` with the given args.
-
- @method reject
- */
- reject: function(value) {
- get(this, '_deferred').reject(value);
- },
-
- _deferred: Ember.computed(function() {
- return RSVP.defer();
- })
-});
-
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-runtime
-*/
-
-var get = Ember.get, typeOf = Ember.typeOf;
-
-/**
- The `Ember.ActionHandler` mixin implements support for moving an `actions`
- property to an `_actions` property at extend time, and adding `_actions`
- to the object's mergedProperties list.
-
- `Ember.ActionHandler` is used internally by Ember in `Ember.View`,
- `Ember.Controller`, and `Ember.Route`.
-
- @class ActionHandler
- @namespace Ember
-*/
-Ember.ActionHandler = Ember.Mixin.create({
- mergedProperties: ['_actions'],
-
- /**
- @private
-
- Moves `actions` to `_actions` at extend time. Note that this currently
- modifies the mixin themselves, which is technically dubious but
- is practically of little consequence. This may change in the future.
-
- @method willMergeMixin
- */
- willMergeMixin: function(props) {
- var hashName;
-
- if (!props._actions) {
- if (typeOf(props.actions) === 'object') {
- hashName = 'actions';
- } else if (typeOf(props.events) === 'object') {
- Ember.deprecate('Action handlers contained in an `events` object are deprecated in favor of putting them in an `actions` object', false);
- hashName = 'events';
- }
-
- if (hashName) {
- props._actions = Ember.merge(props._actions || {}, props[hashName]);
- }
-
- delete props[hashName];
- }
- },
-
- send: function(actionName) {
- var args = [].slice.call(arguments, 1), target;
-
- if (this._actions && this._actions[actionName]) {
- if (this._actions[actionName].apply(this, args) === true) {
- // handler returned true, so this action will bubble
- } else {
- return;
- }
- } else if (this.deprecatedSend && this.deprecatedSendHandles && this.deprecatedSendHandles(actionName)) {
- if (this.deprecatedSend.apply(this, [].slice.call(arguments)) === true) {
- // handler return true, so this action will bubble
- } else {
- return;
- }
- }
-
- if (target = get(this, 'target')) {
- Ember.assert("The `target` for " + this + " (" + target + ") does not have a `send` method", typeof target.send === 'function');
- target.send.apply(target, arguments);
- }
- }
-
-});
-
-})();
-
-
-
-(function() {
-var set = Ember.set, get = Ember.get,
- resolve = Ember.RSVP.resolve,
- rethrow = Ember.RSVP.rethrow,
- not = Ember.computed.not,
- or = Ember.computed.or;
-
-/**
- @module ember
- @submodule ember-runtime
- */
-
-function observePromise(proxy, promise) {
- promise.then(function(value) {
- set(proxy, 'isFulfilled', true);
- set(proxy, 'content', value);
- }, function(reason) {
- set(proxy, 'isRejected', true);
- set(proxy, 'reason', reason);
- // don't re-throw, as we are merely observing
- });
-}
-
-/**
- A low level mixin making ObjectProxy, ObjectController or ArrayController's promise aware.
-
- ```javascript
- var ObjectPromiseController = Ember.ObjectController.extend(Ember.PromiseProxyMixin);
-
- var controller = ObjectPromiseController.create({
- promise: $.getJSON('/some/remote/data.json')
- });
-
- controller.then(function(json){
- // the json
- }, function(reason) {
- // the reason why you have no json
- });
- ```
-
- the controller has bindable attributes which
- track the promises life cycle
-
- ```javascript
- controller.get('isPending') //=> true
- controller.get('isSettled') //=> false
- controller.get('isRejected') //=> false
- controller.get('isFulfilled') //=> false
- ```
-
- When the the $.getJSON completes, and the promise is fulfilled
- with json, the life cycle attributes will update accordingly.
-
- ```javascript
- controller.get('isPending') //=> false
- controller.get('isSettled') //=> true
- controller.get('isRejected') //=> false
- controller.get('isFulfilled') //=> true
- ```
-
- As the controller is an ObjectController, and the json now its content,
- all the json properties will be available directly from the controller.
-
- ```javascript
- // Assuming the following json:
- {
- firstName: 'Stefan',
- lastName: 'Penner'
- }
-
- // both properties will accessible on the controller
- controller.get('firstName') //=> 'Stefan'
- controller.get('lastName') //=> 'Penner'
- ```
-
- If the controller is backing a template, the attributes are
- bindable from within that template
-
- ```handlebars
- {{#if isPending}}
- loading...
- {{else}}
- firstName: {{firstName}}
- lastName: {{lastName}}
- {{/if}}
- ```
- @class Ember.PromiseProxyMixin
-*/
-Ember.PromiseProxyMixin = Ember.Mixin.create({
- reason: null,
- isPending: not('isSettled').readOnly(),
- isSettled: or('isRejected', 'isFulfilled').readOnly(),
- isRejected: false,
- isFulfilled: false,
-
- promise: Ember.computed(function(key, promise) {
- if (arguments.length === 2) {
- promise = resolve(promise);
- observePromise(this, promise);
- return promise.then(); // fork the promise.
- } else {
- throw new Ember.Error("PromiseProxy's promise must be set");
- }
- }),
-
- then: function(fulfill, reject) {
- return get(this, 'promise').then(fulfill, reject);
- }
-});
-
-
-
-})();
-
-
-
-(function() {
-
-})();
-
-
-
-(function() {
-var get = Ember.get,
- forEach = Ember.EnumerableUtils.forEach,
- RETAIN = 'r',
- INSERT = 'i',
- DELETE = 'd';
-
-/**
- An `Ember.TrackedArray` tracks array operations. It's useful when you want to
- lazily compute the indexes of items in an array after they've been shifted by
- subsequent operations.
-
- @class TrackedArray
- @namespace Ember
- @param {array} [items=[]] The array to be tracked. This is used just to get
- the initial items for the starting state of retain:n.
-*/
-Ember.TrackedArray = function (items) {
- if (arguments.length < 1) { items = []; }
-
- var length = get(items, 'length');
-
- if (length) {
- this._operations = [new ArrayOperation(RETAIN, length, items)];
- } else {
- this._operations = [];
- }
-};
-
-Ember.TrackedArray.RETAIN = RETAIN;
-Ember.TrackedArray.INSERT = INSERT;
-Ember.TrackedArray.DELETE = DELETE;
-
-Ember.TrackedArray.prototype = {
-
- /**
- Track that `newItems` were added to the tracked array at `index`.
-
- @method addItems
- @param index
- @param newItems
- */
- addItems: function (index, newItems) {
- var count = get(newItems, 'length');
- if (count < 1) { return; }
-
- var match = this._findArrayOperation(index),
- arrayOperation = match.operation,
- arrayOperationIndex = match.index,
- arrayOperationRangeStart = match.rangeStart,
- composeIndex,
- splitIndex,
- splitItems,
- splitArrayOperation,
- newArrayOperation;
-
- newArrayOperation = new ArrayOperation(INSERT, count, newItems);
-
- if (arrayOperation) {
- if (!match.split) {
- // insert left of arrayOperation
- this._operations.splice(arrayOperationIndex, 0, newArrayOperation);
- composeIndex = arrayOperationIndex;
- } else {
- this._split(arrayOperationIndex, index - arrayOperationRangeStart, newArrayOperation);
- composeIndex = arrayOperationIndex + 1;
- }
- } else {
- // insert at end
- this._operations.push(newArrayOperation);
- composeIndex = arrayOperationIndex;
- }
-
- this._composeInsert(composeIndex);
- },
-
- /**
- Track that `count` items were removed at `index`.
-
- @method removeItems
- @param index
- @param count
- */
- removeItems: function (index, count) {
- if (count < 1) { return; }
-
- var match = this._findArrayOperation(index),
- arrayOperation = match.operation,
- arrayOperationIndex = match.index,
- arrayOperationRangeStart = match.rangeStart,
- newArrayOperation,
- composeIndex;
-
- newArrayOperation = new ArrayOperation(DELETE, count);
- if (!match.split) {
- // insert left of arrayOperation
- this._operations.splice(arrayOperationIndex, 0, newArrayOperation);
- composeIndex = arrayOperationIndex;
- } else {
- this._split(arrayOperationIndex, index - arrayOperationRangeStart, newArrayOperation);
- composeIndex = arrayOperationIndex + 1;
- }
-
- return this._composeDelete(composeIndex);
- },
-
- /**
- Apply all operations, reducing them to retain:n, for `n`, the number of
- items in the array.
-
- `callback` will be called for each operation and will be passed the following arguments:
- * {array} items The items for the given operation
- * {number} offset The computed offset of the items, ie the index in the
- array of the first item for this operation.
- * {string} operation The type of the operation. One of
- `Ember.TrackedArray.{RETAIN, DELETE, INSERT}`
-
- @method apply
- @param {function} callback
- */
- apply: function (callback) {
- var items = [],
- offset = 0;
-
- forEach(this._operations, function (arrayOperation) {
- callback(arrayOperation.items, offset, arrayOperation.type);
-
- if (arrayOperation.type !== DELETE) {
- offset += arrayOperation.count;
- items = items.concat(arrayOperation.items);
- }
- });
-
- this._operations = [new ArrayOperation(RETAIN, items.length, items)];
- },
-
- /**
- Return an ArrayOperationMatch for the operation that contains the item at `index`.
-
- @method _findArrayOperation
-
- @param {number} index the index of the item whose operation information
- should be returned.
- @private
- */
- _findArrayOperation: function (index) {
- var arrayOperationIndex,
- len,
- split = false,
- arrayOperation,
- arrayOperationRangeStart,
- arrayOperationRangeEnd;
-
- // OPTIMIZE: we could search these faster if we kept a balanced tree.
- // find leftmost arrayOperation to the right of `index`
- for (arrayOperationIndex = arrayOperationRangeStart = 0, len = this._operations.length; arrayOperationIndex < len; ++arrayOperationIndex) {
- arrayOperation = this._operations[arrayOperationIndex];
-
- if (arrayOperation.type === DELETE) { continue; }
-
- arrayOperationRangeEnd = arrayOperationRangeStart + arrayOperation.count - 1;
-
- if (index === arrayOperationRangeStart) {
- break;
- } else if (index > arrayOperationRangeStart && index <= arrayOperationRangeEnd) {
- split = true;
- break;
- } else {
- arrayOperationRangeStart = arrayOperationRangeEnd + 1;
- }
- }
-
- return new ArrayOperationMatch(arrayOperation, arrayOperationIndex, split, arrayOperationRangeStart);
- },
-
- _split: function (arrayOperationIndex, splitIndex, newArrayOperation) {
- var arrayOperation = this._operations[arrayOperationIndex],
- splitItems = arrayOperation.items.slice(splitIndex),
- splitArrayOperation = new ArrayOperation(arrayOperation.type, splitItems.length, splitItems);
-
- // truncate LHS
- arrayOperation.count = splitIndex;
- arrayOperation.items = arrayOperation.items.slice(0, splitIndex);
-
- this._operations.splice(arrayOperationIndex + 1, 0, newArrayOperation, splitArrayOperation);
- },
-
- // see SubArray for a better implementation.
- _composeInsert: function (index) {
- var newArrayOperation = this._operations[index],
- leftArrayOperation = this._operations[index-1], // may be undefined
- rightArrayOperation = this._operations[index+1], // may be undefined
- leftOp = leftArrayOperation && leftArrayOperation.type,
- rightOp = rightArrayOperation && rightArrayOperation.type;
-
- if (leftOp === INSERT) {
- // merge left
- leftArrayOperation.count += newArrayOperation.count;
- leftArrayOperation.items = leftArrayOperation.items.concat(newArrayOperation.items);
-
- if (rightOp === INSERT) {
- // also merge right (we have split an insert with an insert)
- leftArrayOperation.count += rightArrayOperation.count;
- leftArrayOperation.items = leftArrayOperation.items.concat(rightArrayOperation.items);
- this._operations.splice(index, 2);
- } else {
- // only merge left
- this._operations.splice(index, 1);
- }
- } else if (rightOp === INSERT) {
- // merge right
- newArrayOperation.count += rightArrayOperation.count;
- newArrayOperation.items = newArrayOperation.items.concat(rightArrayOperation.items);
- this._operations.splice(index + 1, 1);
- }
- },
-
- _composeDelete: function (index) {
- var arrayOperation = this._operations[index],
- deletesToGo = arrayOperation.count,
- leftArrayOperation = this._operations[index-1], // may be undefined
- leftOp = leftArrayOperation && leftArrayOperation.type,
- nextArrayOperation,
- nextOp,
- nextCount,
- removeNewAndNextOp = false,
- removedItems = [];
-
- if (leftOp === DELETE) {
- arrayOperation = leftArrayOperation;
- index -= 1;
- }
-
- for (var i = index + 1; deletesToGo > 0; ++i) {
- nextArrayOperation = this._operations[i];
- nextOp = nextArrayOperation.type;
- nextCount = nextArrayOperation.count;
-
- if (nextOp === DELETE) {
- arrayOperation.count += nextCount;
- continue;
- }
-
- if (nextCount > deletesToGo) {
- // d:2 {r,i}:5 we reduce the retain or insert, but it stays
- removedItems = removedItems.concat(nextArrayOperation.items.splice(0, deletesToGo));
- nextArrayOperation.count -= deletesToGo;
-
- // In the case where we truncate the last arrayOperation, we don't need to
- // remove it; also the deletesToGo reduction is not the entirety of
- // nextCount
- i -= 1;
- nextCount = deletesToGo;
-
- deletesToGo = 0;
- } else {
- if (nextCount === deletesToGo) {
- // Handle edge case of d:2 i:2 in which case both operations go away
- // during composition.
- removeNewAndNextOp = true;
- }
- removedItems = removedItems.concat(nextArrayOperation.items);
- deletesToGo -= nextCount;
- }
-
- if (nextOp === INSERT) {
- // d:2 i:3 will result in delete going away
- arrayOperation.count -= nextCount;
- }
- }
-
- if (arrayOperation.count > 0) {
- // compose our new delete with possibly several operations to the right of
- // disparate types
- this._operations.splice(index+1, i-1-index);
- } else {
- // The delete operation can go away; it has merely reduced some other
- // operation, as in d:3 i:4; it may also have eliminated that operation,
- // as in d:3 i:3.
- this._operations.splice(index, removeNewAndNextOp ? 2 : 1);
- }
-
- return removedItems;
- },
-
- toString: function () {
- var str = "";
- forEach(this._operations, function (operation) {
- str += " " + operation.type + ":" + operation.count;
- });
- return str.substring(1);
- }
-};
-
-/**
- Internal data structure to represent an array operation.
-
- @method ArrayOperation
- @private
- @property {string} type The type of the operation. One of
- `Ember.TrackedArray.{RETAIN, INSERT, DELETE}`
- @property {number} count The number of items in this operation.
- @property {array} items The items of the operation, if included. RETAIN and
- INSERT include their items, DELETE does not.
-*/
-function ArrayOperation (operation, count, items) {
- this.type = operation; // RETAIN | INSERT | DELETE
- this.count = count;
- this.items = items;
-}
-
-/**
- Internal data structure used to include information when looking up operations
- by item index.
-
- @method ArrayOperationMatch
- @private
- @property {ArrayOperation} operation
- @property {number} index The index of `operation` in the array of operations.
- @property {boolean} split Whether or not the item index searched for would
- require a split for a new operation type.
- @property {number} rangeStart The index of the first item in the operation,
- with respect to the tracked array. The index of the last item can be computed
- from `rangeStart` and `operation.count`.
-*/
-function ArrayOperationMatch(operation, index, split, rangeStart) {
- this.operation = operation;
- this.index = index;
- this.split = split;
- this.rangeStart = rangeStart;
-}
-
-})();
-
-
-
-(function() {
-var get = Ember.get,
- forEach = Ember.EnumerableUtils.forEach,
- RETAIN = 'r',
- FILTER = 'f';
-
-function Operation (type, count) {
- this.type = type;
- this.count = count;
-}
-
-/**
- An `Ember.SubArray` tracks an array in a way similar to, but more specialized
- than, `Ember.TrackedArray`. It is useful for keeping track of the indexes of
- items within a filtered array.
-
- @class SubArray
- @namespace Ember
-*/
-Ember.SubArray = function (length) {
- if (arguments.length < 1) { length = 0; }
-
- if (length > 0) {
- this._operations = [new Operation(RETAIN, length)];
- } else {
- this._operations = [];
- }
-};
-
-Ember.SubArray.prototype = {
- /**
- Track that an item was added to the tracked array.
-
- @method addItem
-
- @param {number} index The index of the item in the tracked array.
- @param {boolean} match `true` iff the item is included in the subarray.
-
- @return {number} The index of the item in the subarray.
- */
- addItem: function(index, match) {
- var returnValue = -1,
- itemType = match ? RETAIN : FILTER,
- self = this;
-
- this._findOperation(index, function(operation, operationIndex, rangeStart, rangeEnd, seenInSubArray) {
- var newOperation, splitOperation;
-
- if (itemType === operation.type) {
- ++operation.count;
- } else if (index === rangeStart) {
- // insert to the left of `operation`
- self._operations.splice(operationIndex, 0, new Operation(itemType, 1));
- } else {
- newOperation = new Operation(itemType, 1);
- splitOperation = new Operation(operation.type, rangeEnd - index + 1);
- operation.count = index - rangeStart;
-
- self._operations.splice(operationIndex + 1, 0, newOperation, splitOperation);
- }
-
- if (match) {
- if (operation.type === RETAIN) {
- returnValue = seenInSubArray + (index - rangeStart);
- } else {
- returnValue = seenInSubArray;
- }
- }
-
- self._composeAt(operationIndex);
- }, function(seenInSubArray) {
- self._operations.push(new Operation(itemType, 1));
-
- if (match) {
- returnValue = seenInSubArray;
- }
-
- self._composeAt(self._operations.length-1);
- });
-
- return returnValue;
- },
-
- /**
- Track that an item was removed from the tracked array.
-
- @method removeItem
-
- @param {number} index The index of the item in the tracked array.
-
- @return {number} The index of the item in the subarray, or `-1` if the item
- was not in the subarray.
- */
- removeItem: function(index) {
- var returnValue = -1,
- self = this;
-
- this._findOperation(index, function (operation, operationIndex, rangeStart, rangeEnd, seenInSubArray) {
- if (operation.type === RETAIN) {
- returnValue = seenInSubArray + (index - rangeStart);
- }
-
- if (operation.count > 1) {
- --operation.count;
- } else {
- self._operations.splice(operationIndex, 1);
- self._composeAt(operationIndex);
- }
- }, function() {
- throw new Ember.Error("Can't remove an item that has never been added.");
- });
-
- return returnValue;
- },
-
-
- _findOperation: function (index, foundCallback, notFoundCallback) {
- var operationIndex,
- len,
- operation,
- rangeStart,
- rangeEnd,
- seenInSubArray = 0;
-
- // OPTIMIZE: change to balanced tree
- // find leftmost operation to the right of `index`
- for (operationIndex = rangeStart = 0, len = this._operations.length; operationIndex < len; rangeStart = rangeEnd + 1, ++operationIndex) {
- operation = this._operations[operationIndex];
- rangeEnd = rangeStart + operation.count - 1;
-
- if (index >= rangeStart && index <= rangeEnd) {
- foundCallback(operation, operationIndex, rangeStart, rangeEnd, seenInSubArray);
- return;
- } else if (operation.type === RETAIN) {
- seenInSubArray += operation.count;
- }
- }
-
- notFoundCallback(seenInSubArray);
- },
-
- _composeAt: function(index) {
- var op = this._operations[index],
- otherOp;
-
- if (!op) {
- // Composing out of bounds is a no-op, as when removing the last operation
- // in the list.
- return;
- }
-
- if (index > 0) {
- otherOp = this._operations[index-1];
- if (otherOp.type === op.type) {
- op.count += otherOp.count;
- this._operations.splice(index-1, 1);
- --index;
- }
- }
-
- if (index < this._operations.length-1) {
- otherOp = this._operations[index+1];
- if (otherOp.type === op.type) {
- op.count += otherOp.count;
- this._operations.splice(index+1, 1);
- }
- }
- },
-
- toString: function () {
- var str = "";
- forEach(this._operations, function (operation) {
- str += " " + operation.type + ":" + operation.count;
- });
- return str.substring(1);
- }
-};
-
-})();
-
-
-
-(function() {
-Ember.Container = requireModule('container');
-Ember.Container.set = Ember.set;
-
-})();
-
-
-
-(function() {
-Ember.Application = Ember.Namespace.extend();
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-runtime
-*/
-
-var OUT_OF_RANGE_EXCEPTION = "Index out of range";
-var EMPTY = [];
-
-var get = Ember.get, set = Ember.set;
-
-/**
- An ArrayProxy wraps any other object that implements `Ember.Array` and/or
- `Ember.MutableArray,` forwarding all requests. This makes it very useful for
- a number of binding use cases or other cases where being able to swap
- out the underlying array is useful.
-
- A simple example of usage:
-
- ```javascript
- var pets = ['dog', 'cat', 'fish'];
- var ap = Ember.ArrayProxy.create({ content: Ember.A(pets) });
-
- ap.get('firstObject'); // 'dog'
- ap.set('content', ['amoeba', 'paramecium']);
- ap.get('firstObject'); // 'amoeba'
- ```
-
- This class can also be useful as a layer to transform the contents of
- an array, as they are accessed. This can be done by overriding
- `objectAtContent`:
-
- ```javascript
- var pets = ['dog', 'cat', 'fish'];
- var ap = Ember.ArrayProxy.create({
- content: Ember.A(pets),
- objectAtContent: function(idx) {
- return this.get('content').objectAt(idx).toUpperCase();
- }
- });
-
- ap.get('firstObject'); // . 'DOG'
- ```
-
- @class ArrayProxy
- @namespace Ember
- @extends Ember.Object
- @uses Ember.MutableArray
-*/
-Ember.ArrayProxy = Ember.Object.extend(Ember.MutableArray,/** @scope Ember.ArrayProxy.prototype */ {
-
- /**
- The content array. Must be an object that implements `Ember.Array` and/or
- `Ember.MutableArray.`
-
- @property content
- @type Ember.Array
- */
- content: null,
-
- /**
- The array that the proxy pretends to be. In the default `ArrayProxy`
- implementation, this and `content` are the same. Subclasses of `ArrayProxy`
- can override this property to provide things like sorting and filtering.
-
- @property arrangedContent
- */
- arrangedContent: Ember.computed.alias('content'),
-
- /**
- Should actually retrieve the object at the specified index from the
- content. You can override this method in subclasses to transform the
- content item to something new.
-
- This method will only be called if content is non-`null`.
-
- @method objectAtContent
- @param {Number} idx The index to retrieve.
- @return {Object} the value or undefined if none found
- */
- objectAtContent: function(idx) {
- return get(this, 'arrangedContent').objectAt(idx);
- },
-
- /**
- Should actually replace the specified objects on the content array.
- You can override this method in subclasses to transform the content item
- into something new.
-
- This method will only be called if content is non-`null`.
-
- @method replaceContent
- @param {Number} idx The starting index
- @param {Number} amt The number of items to remove from the content.
- @param {Array} objects Optional array of objects to insert or null if no
- objects.
- @return {void}
- */
- replaceContent: function(idx, amt, objects) {
- get(this, 'content').replace(idx, amt, objects);
- },
-
- /**
- @private
-
- Invoked when the content property is about to change. Notifies observers that the
- entire array content will change.
-
- @method _contentWillChange
- */
- _contentWillChange: Ember.beforeObserver('content', function() {
- this._teardownContent();
- }),
-
- _teardownContent: function() {
- var content = get(this, 'content');
-
- if (content) {
- content.removeArrayObserver(this, {
- willChange: 'contentArrayWillChange',
- didChange: 'contentArrayDidChange'
- });
- }
- },
-
- contentArrayWillChange: Ember.K,
- contentArrayDidChange: Ember.K,
-
- /**
- @private
-
- Invoked when the content property changes. Notifies observers that the
- entire array content has changed.
-
- @method _contentDidChange
- */
- _contentDidChange: Ember.observer('content', function() {
- var content = get(this, 'content');
-
- Ember.assert("Can't set ArrayProxy's content to itself", content !== this);
-
- this._setupContent();
- }),
-
- _setupContent: function() {
- var content = get(this, 'content');
-
- if (content) {
- content.addArrayObserver(this, {
- willChange: 'contentArrayWillChange',
- didChange: 'contentArrayDidChange'
- });
- }
- },
-
- _arrangedContentWillChange: Ember.beforeObserver('arrangedContent', function() {
- var arrangedContent = get(this, 'arrangedContent'),
- len = arrangedContent ? get(arrangedContent, 'length') : 0;
-
- this.arrangedContentArrayWillChange(this, 0, len, undefined);
- this.arrangedContentWillChange(this);
-
- this._teardownArrangedContent(arrangedContent);
- }),
-
- _arrangedContentDidChange: Ember.observer('arrangedContent', function() {
- var arrangedContent = get(this, 'arrangedContent'),
- len = arrangedContent ? get(arrangedContent, 'length') : 0;
-
- Ember.assert("Can't set ArrayProxy's content to itself", arrangedContent !== this);
-
- this._setupArrangedContent();
-
- this.arrangedContentDidChange(this);
- this.arrangedContentArrayDidChange(this, 0, undefined, len);
- }),
-
- _setupArrangedContent: function() {
- var arrangedContent = get(this, 'arrangedContent');
-
- if (arrangedContent) {
- arrangedContent.addArrayObserver(this, {
- willChange: 'arrangedContentArrayWillChange',
- didChange: 'arrangedContentArrayDidChange'
- });
- }
- },
-
- _teardownArrangedContent: function() {
- var arrangedContent = get(this, 'arrangedContent');
-
- if (arrangedContent) {
- arrangedContent.removeArrayObserver(this, {
- willChange: 'arrangedContentArrayWillChange',
- didChange: 'arrangedContentArrayDidChange'
- });
- }
- },
-
- arrangedContentWillChange: Ember.K,
- arrangedContentDidChange: Ember.K,
-
- objectAt: function(idx) {
- return get(this, 'content') && this.objectAtContent(idx);
- },
-
- length: Ember.computed(function() {
- var arrangedContent = get(this, 'arrangedContent');
- return arrangedContent ? get(arrangedContent, 'length') : 0;
- // No dependencies since Enumerable notifies length of change
- }),
-
- _replace: function(idx, amt, objects) {
- var content = get(this, 'content');
- Ember.assert('The content property of '+ this.constructor + ' should be set before modifying it', content);
- if (content) this.replaceContent(idx, amt, objects);
- return this;
- },
-
- replace: function() {
- if (get(this, 'arrangedContent') === get(this, 'content')) {
- this._replace.apply(this, arguments);
- } else {
- throw new Ember.Error("Using replace on an arranged ArrayProxy is not allowed.");
- }
- },
-
- _insertAt: function(idx, object) {
- if (idx > get(this, 'content.length')) throw new Ember.Error(OUT_OF_RANGE_EXCEPTION);
- this._replace(idx, 0, [object]);
- return this;
- },
-
- insertAt: function(idx, object) {
- if (get(this, 'arrangedContent') === get(this, 'content')) {
- return this._insertAt(idx, object);
- } else {
- throw new Ember.Error("Using insertAt on an arranged ArrayProxy is not allowed.");
- }
- },
-
- removeAt: function(start, len) {
- if ('number' === typeof start) {
- var content = get(this, 'content'),
- arrangedContent = get(this, 'arrangedContent'),
- indices = [], i;
-
- if ((start < 0) || (start >= get(this, 'length'))) {
- throw new Ember.Error(OUT_OF_RANGE_EXCEPTION);
- }
-
- if (len === undefined) len = 1;
-
- // Get a list of indices in original content to remove
- for (i=start; i=idx) {
- var item = content.objectAt(loc);
- if (item) {
- Ember.assert('When using @each to observe the array ' + content + ', the array must return an object', Ember.typeOf(item) === 'instance' || Ember.typeOf(item) === 'object');
- Ember.addBeforeObserver(item, keyName, proxy, 'contentKeyWillChange');
- Ember.addObserver(item, keyName, proxy, 'contentKeyDidChange');
-
- // keep track of the index each item was found at so we can map
- // it back when the obj changes.
- guid = guidFor(item);
- if (!objects[guid]) objects[guid] = [];
- objects[guid].push(loc);
- }
- }
-}
-
-function removeObserverForContentKey(content, keyName, proxy, idx, loc) {
- var objects = proxy._objects;
- if (!objects) objects = proxy._objects = {};
- var indicies, guid;
-
- while(--loc>=idx) {
- var item = content.objectAt(loc);
- if (item) {
- Ember.removeBeforeObserver(item, keyName, proxy, 'contentKeyWillChange');
- Ember.removeObserver(item, keyName, proxy, 'contentKeyDidChange');
-
- guid = guidFor(item);
- indicies = objects[guid];
- indicies[indexOf.call(indicies, loc)] = null;
- }
- }
-}
-
-/**
- This is the object instance returned when you get the `@each` property on an
- array. It uses the unknownProperty handler to automatically create
- EachArray instances for property names.
-
- @private
- @class EachProxy
- @namespace Ember
- @extends Ember.Object
-*/
-Ember.EachProxy = Ember.Object.extend({
-
- init: function(content) {
- this._super();
- this._content = content;
- content.addArrayObserver(this);
-
- // in case someone is already observing some keys make sure they are
- // added
- forEach(Ember.watchedEvents(this), function(eventName) {
- this.didAddListener(eventName);
- }, this);
- },
-
- /**
- You can directly access mapped properties by simply requesting them.
- The `unknownProperty` handler will generate an EachArray of each item.
-
- @method unknownProperty
- @param keyName {String}
- @param value {*}
- */
- unknownProperty: function(keyName, value) {
- var ret;
- ret = new EachArray(this._content, keyName, this);
- Ember.defineProperty(this, keyName, null, ret);
- this.beginObservingContentKey(keyName);
- return ret;
- },
-
- // ..........................................................
- // ARRAY CHANGES
- // Invokes whenever the content array itself changes.
-
- arrayWillChange: function(content, idx, removedCnt, addedCnt) {
- var keys = this._keys, key, lim;
-
- lim = removedCnt>0 ? idx+removedCnt : -1;
- Ember.beginPropertyChanges(this);
-
- for(key in keys) {
- if (!keys.hasOwnProperty(key)) { continue; }
-
- if (lim>0) { removeObserverForContentKey(content, key, this, idx, lim); }
-
- Ember.propertyWillChange(this, key);
- }
-
- Ember.propertyWillChange(this._content, '@each');
- Ember.endPropertyChanges(this);
- },
-
- arrayDidChange: function(content, idx, removedCnt, addedCnt) {
- var keys = this._keys, lim;
-
- lim = addedCnt>0 ? idx+addedCnt : -1;
- Ember.changeProperties(function() {
- for(var key in keys) {
- if (!keys.hasOwnProperty(key)) { continue; }
-
- if (lim>0) { addObserverForContentKey(content, key, this, idx, lim); }
-
- Ember.propertyDidChange(this, key);
- }
-
- Ember.propertyDidChange(this._content, '@each');
- }, this);
- },
-
- // ..........................................................
- // LISTEN FOR NEW OBSERVERS AND OTHER EVENT LISTENERS
- // Start monitoring keys based on who is listening...
-
- didAddListener: function(eventName) {
- if (IS_OBSERVER.test(eventName)) {
- this.beginObservingContentKey(eventName.slice(0, -7));
- }
- },
-
- didRemoveListener: function(eventName) {
- if (IS_OBSERVER.test(eventName)) {
- this.stopObservingContentKey(eventName.slice(0, -7));
- }
- },
-
- // ..........................................................
- // CONTENT KEY OBSERVING
- // Actual watch keys on the source content.
-
- beginObservingContentKey: function(keyName) {
- var keys = this._keys;
- if (!keys) keys = this._keys = {};
- if (!keys[keyName]) {
- keys[keyName] = 1;
- var content = this._content,
- len = get(content, 'length');
- addObserverForContentKey(content, keyName, this, 0, len);
- } else {
- keys[keyName]++;
- }
- },
-
- stopObservingContentKey: function(keyName) {
- var keys = this._keys;
- if (keys && (keys[keyName]>0) && (--keys[keyName]<=0)) {
- var content = this._content,
- len = get(content, 'length');
- removeObserverForContentKey(content, keyName, this, 0, len);
- }
- },
-
- contentKeyWillChange: function(obj, keyName) {
- Ember.propertyWillChange(this, keyName);
- },
-
- contentKeyDidChange: function(obj, keyName) {
- Ember.propertyDidChange(this, keyName);
- }
-
-});
-
-
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-runtime
-*/
-
-
-var get = Ember.get, set = Ember.set, replace = Ember.EnumerableUtils._replace;
-
-// Add Ember.Array to Array.prototype. Remove methods with native
-// implementations and supply some more optimized versions of generic methods
-// because they are so common.
-var NativeArray = Ember.Mixin.create(Ember.MutableArray, Ember.Observable, Ember.Copyable, {
-
- // because length is a built-in property we need to know to just get the
- // original property.
- get: function(key) {
- if (key==='length') return this.length;
- else if ('number' === typeof key) return this[key];
- else return this._super(key);
- },
-
- objectAt: function(idx) {
- return this[idx];
- },
-
- // primitive for array support.
- replace: function(idx, amt, objects) {
-
- if (this.isFrozen) throw Ember.FROZEN_ERROR;
-
- // if we replaced exactly the same number of items, then pass only the
- // replaced range. Otherwise, pass the full remaining array length
- // since everything has shifted
- var len = objects ? get(objects, 'length') : 0;
- this.arrayContentWillChange(idx, amt, len);
-
- if (len === 0) {
- this.splice(idx, amt);
- } else {
- replace(this, idx, amt, objects);
- }
-
- this.arrayContentDidChange(idx, amt, len);
- return this;
- },
-
- // If you ask for an unknown property, then try to collect the value
- // from member items.
- unknownProperty: function(key, value) {
- var ret;// = this.reducedProperty(key, value) ;
- if ((value !== undefined) && ret === undefined) {
- ret = this[key] = value;
- }
- return ret ;
- },
-
- // If browser did not implement indexOf natively, then override with
- // specialized version
- indexOf: function(object, startAt) {
- var idx, len = this.length;
-
- if (startAt === undefined) startAt = 0;
- else startAt = (startAt < 0) ? Math.ceil(startAt) : Math.floor(startAt);
- if (startAt < 0) startAt += len;
-
- for(idx=startAt;idx=0;idx--) {
- if (this[idx] === object) return idx ;
- }
- return -1;
- },
-
- copy: function(deep) {
- if (deep) {
- return this.map(function(item) { return Ember.copy(item, true); });
- }
-
- return this.slice();
- }
-});
-
-// Remove any methods implemented natively so we don't override them
-var ignore = ['length'];
-Ember.EnumerableUtils.forEach(NativeArray.keys(), function(methodName) {
- if (Array.prototype[methodName]) ignore.push(methodName);
-});
-
-if (ignore.length>0) {
- NativeArray = NativeArray.without.apply(NativeArray, ignore);
-}
-
-/**
- The NativeArray mixin contains the properties needed to to make the native
- Array support Ember.MutableArray and all of its dependent APIs. Unless you
- have `Ember.EXTEND_PROTOTYPES` or `Ember.EXTEND_PROTOTYPES.Array` set to
- false, this will be applied automatically. Otherwise you can apply the mixin
- at anytime by calling `Ember.NativeArray.activate`.
-
- @class NativeArray
- @namespace Ember
- @uses Ember.MutableArray
- @uses Ember.Observable
- @uses Ember.Copyable
-*/
-Ember.NativeArray = NativeArray;
-
-/**
- Creates an `Ember.NativeArray` from an Array like object.
- Does not modify the original object. Ember.A is not needed if
- `Ember.EXTEND_PROTOTYPES` is `true` (the default value). However,
- it is recommended that you use Ember.A when creating addons for
- ember or when you can not guarantee that `Ember.EXTEND_PROTOTYPES`
- will be `true`.
-
- Example
-
- ```js
- var Pagination = Ember.CollectionView.extend({
- tagName: 'ul',
- classNames: ['pagination'],
- init: function() {
- this._super();
- if (!this.get('content')) {
- this.set('content', Ember.A([]));
- }
- }
- });
- ```
-
- @method A
- @for Ember
- @return {Ember.NativeArray}
-*/
-Ember.A = function(arr) {
- if (arr === undefined) { arr = []; }
- return Ember.Array.detect(arr) ? arr : Ember.NativeArray.apply(arr);
-};
-
-/**
- Activates the mixin on the Array.prototype if not already applied. Calling
- this method more than once is safe. This will be called when ember is loaded
- unless you have `Ember.EXTEND_PROTOTYPES` or `Ember.EXTEND_PROTOTYPES.Array`
- set to `false`.
-
- Example
-
- ```js
- if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Array) {
- Ember.NativeArray.activate();
- }
- ```
-
- @method activate
- @for Ember.NativeArray
- @static
- @return {void}
-*/
-Ember.NativeArray.activate = function() {
- NativeArray.apply(Array.prototype);
-
- Ember.A = function(arr) { return arr || []; };
-};
-
-if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Array) {
- Ember.NativeArray.activate();
-}
-
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-runtime
-*/
-
-var get = Ember.get, set = Ember.set, guidFor = Ember.guidFor, isNone = Ember.isNone, fmt = Ember.String.fmt;
-
-/**
- An unordered collection of objects.
-
- A Set works a bit like an array except that its items are not ordered. You
- can create a set to efficiently test for membership for an object. You can
- also iterate through a set just like an array, even accessing objects by
- index, however there is no guarantee as to their order.
-
- All Sets are observable via the Enumerable Observer API - which works
- on any enumerable object including both Sets and Arrays.
-
- ## Creating a Set
-
- You can create a set like you would most objects using
- `new Ember.Set()`. Most new sets you create will be empty, but you can
- also initialize the set with some content by passing an array or other
- enumerable of objects to the constructor.
-
- Finally, you can pass in an existing set and the set will be copied. You
- can also create a copy of a set by calling `Ember.Set#copy()`.
-
- ```javascript
- // creates a new empty set
- var foundNames = new Ember.Set();
-
- // creates a set with four names in it.
- var names = new Ember.Set(["Charles", "Tom", "Juan", "Alex"]); // :P
-
- // creates a copy of the names set.
- var namesCopy = new Ember.Set(names);
-
- // same as above.
- var anotherNamesCopy = names.copy();
- ```
-
- ## Adding/Removing Objects
-
- You generally add or remove objects from a set using `add()` or
- `remove()`. You can add any type of object including primitives such as
- numbers, strings, and booleans.
-
- Unlike arrays, objects can only exist one time in a set. If you call `add()`
- on a set with the same object multiple times, the object will only be added
- once. Likewise, calling `remove()` with the same object multiple times will
- remove the object the first time and have no effect on future calls until
- you add the object to the set again.
-
- NOTE: You cannot add/remove `null` or `undefined` to a set. Any attempt to do
- so will be ignored.
-
- In addition to add/remove you can also call `push()`/`pop()`. Push behaves
- just like `add()` but `pop()`, unlike `remove()` will pick an arbitrary
- object, remove it and return it. This is a good way to use a set as a job
- queue when you don't care which order the jobs are executed in.
-
- ## Testing for an Object
-
- To test for an object's presence in a set you simply call
- `Ember.Set#contains()`.
-
- ## Observing changes
-
- When using `Ember.Set`, you can observe the `"[]"` property to be
- alerted whenever the content changes. You can also add an enumerable
- observer to the set to be notified of specific objects that are added and
- removed from the set. See [Ember.Enumerable](/api/classes/Ember.Enumerable.html)
- for more information on enumerables.
-
- This is often unhelpful. If you are filtering sets of objects, for instance,
- it is very inefficient to re-filter all of the items each time the set
- changes. It would be better if you could just adjust the filtered set based
- on what was changed on the original set. The same issue applies to merging
- sets, as well.
-
- ## Other Methods
-
- `Ember.Set` primary implements other mixin APIs. For a complete reference
- on the methods you will use with `Ember.Set`, please consult these mixins.
- The most useful ones will be `Ember.Enumerable` and
- `Ember.MutableEnumerable` which implement most of the common iterator
- methods you are used to on Array.
-
- Note that you can also use the `Ember.Copyable` and `Ember.Freezable`
- APIs on `Ember.Set` as well. Once a set is frozen it can no longer be
- modified. The benefit of this is that when you call `frozenCopy()` on it,
- Ember will avoid making copies of the set. This allows you to write
- code that can know with certainty when the underlying set data will or
- will not be modified.
-
- @class Set
- @namespace Ember
- @extends Ember.CoreObject
- @uses Ember.MutableEnumerable
- @uses Ember.Copyable
- @uses Ember.Freezable
- @since Ember 0.9
-*/
-Ember.Set = Ember.CoreObject.extend(Ember.MutableEnumerable, Ember.Copyable, Ember.Freezable,
- /** @scope Ember.Set.prototype */ {
-
- // ..........................................................
- // IMPLEMENT ENUMERABLE APIS
- //
-
- /**
- This property will change as the number of objects in the set changes.
-
- @property length
- @type number
- @default 0
- */
- length: 0,
-
- /**
- Clears the set. This is useful if you want to reuse an existing set
- without having to recreate it.
-
- ```javascript
- var colors = new Ember.Set(["red", "green", "blue"]);
- colors.length; // 3
- colors.clear();
- colors.length; // 0
- ```
-
- @method clear
- @return {Ember.Set} An empty Set
- */
- clear: function() {
- if (this.isFrozen) { throw new Ember.Error(Ember.FROZEN_ERROR); }
-
- var len = get(this, 'length');
- if (len === 0) { return this; }
-
- var guid;
-
- this.enumerableContentWillChange(len, 0);
- Ember.propertyWillChange(this, 'firstObject');
- Ember.propertyWillChange(this, 'lastObject');
-
- for (var i=0; i < len; i++) {
- guid = guidFor(this[i]);
- delete this[guid];
- delete this[i];
- }
-
- set(this, 'length', 0);
-
- Ember.propertyDidChange(this, 'firstObject');
- Ember.propertyDidChange(this, 'lastObject');
- this.enumerableContentDidChange(len, 0);
-
- return this;
- },
-
- /**
- Returns true if the passed object is also an enumerable that contains the
- same objects as the receiver.
-
- ```javascript
- var colors = ["red", "green", "blue"],
- same_colors = new Ember.Set(colors);
-
- same_colors.isEqual(colors); // true
- same_colors.isEqual(["purple", "brown"]); // false
- ```
-
- @method isEqual
- @param {Ember.Set} obj the other object.
- @return {Boolean}
- */
- isEqual: function(obj) {
- // fail fast
- if (!Ember.Enumerable.detect(obj)) return false;
-
- var loc = get(this, 'length');
- if (get(obj, 'length') !== loc) return false;
-
- while(--loc >= 0) {
- if (!obj.contains(this[loc])) return false;
- }
-
- return true;
- },
-
- /**
- Adds an object to the set. Only non-`null` objects can be added to a set
- and those can only be added once. If the object is already in the set or
- the passed value is null this method will have no effect.
-
- This is an alias for `Ember.MutableEnumerable.addObject()`.
-
- ```javascript
- var colors = new Ember.Set();
- colors.add("blue"); // ["blue"]
- colors.add("blue"); // ["blue"]
- colors.add("red"); // ["blue", "red"]
- colors.add(null); // ["blue", "red"]
- colors.add(undefined); // ["blue", "red"]
- ```
-
- @method add
- @param {Object} obj The object to add.
- @return {Ember.Set} The set itself.
- */
- add: Ember.aliasMethod('addObject'),
-
- /**
- Removes the object from the set if it is found. If you pass a `null` value
- or an object that is already not in the set, this method will have no
- effect. This is an alias for `Ember.MutableEnumerable.removeObject()`.
-
- ```javascript
- var colors = new Ember.Set(["red", "green", "blue"]);
- colors.remove("red"); // ["blue", "green"]
- colors.remove("purple"); // ["blue", "green"]
- colors.remove(null); // ["blue", "green"]
- ```
-
- @method remove
- @param {Object} obj The object to remove
- @return {Ember.Set} The set itself.
- */
- remove: Ember.aliasMethod('removeObject'),
-
- /**
- Removes the last element from the set and returns it, or `null` if it's empty.
-
- ```javascript
- var colors = new Ember.Set(["green", "blue"]);
- colors.pop(); // "blue"
- colors.pop(); // "green"
- colors.pop(); // null
- ```
-
- @method pop
- @return {Object} The removed object from the set or null.
- */
- pop: function() {
- if (get(this, 'isFrozen')) throw new Ember.Error(Ember.FROZEN_ERROR);
- var obj = this.length > 0 ? this[this.length-1] : null;
- this.remove(obj);
- return obj;
- },
-
- /**
- Inserts the given object on to the end of the set. It returns
- the set itself.
-
- This is an alias for `Ember.MutableEnumerable.addObject()`.
-
- ```javascript
- var colors = new Ember.Set();
- colors.push("red"); // ["red"]
- colors.push("green"); // ["red", "green"]
- colors.push("blue"); // ["red", "green", "blue"]
- ```
-
- @method push
- @return {Ember.Set} The set itself.
- */
- push: Ember.aliasMethod('addObject'),
-
- /**
- Removes the last element from the set and returns it, or `null` if it's empty.
-
- This is an alias for `Ember.Set.pop()`.
-
- ```javascript
- var colors = new Ember.Set(["green", "blue"]);
- colors.shift(); // "blue"
- colors.shift(); // "green"
- colors.shift(); // null
- ```
-
- @method shift
- @return {Object} The removed object from the set or null.
- */
- shift: Ember.aliasMethod('pop'),
-
- /**
- Inserts the given object on to the end of the set. It returns
- the set itself.
-
- This is an alias of `Ember.Set.push()`
-
- ```javascript
- var colors = new Ember.Set();
- colors.unshift("red"); // ["red"]
- colors.unshift("green"); // ["red", "green"]
- colors.unshift("blue"); // ["red", "green", "blue"]
- ```
-
- @method unshift
- @return {Ember.Set} The set itself.
- */
- unshift: Ember.aliasMethod('push'),
-
- /**
- Adds each object in the passed enumerable to the set.
-
- This is an alias of `Ember.MutableEnumerable.addObjects()`
-
- ```javascript
- var colors = new Ember.Set();
- colors.addEach(["red", "green", "blue"]); // ["red", "green", "blue"]
- ```
-
- @method addEach
- @param {Ember.Enumerable} objects the objects to add.
- @return {Ember.Set} The set itself.
- */
- addEach: Ember.aliasMethod('addObjects'),
-
- /**
- Removes each object in the passed enumerable to the set.
-
- This is an alias of `Ember.MutableEnumerable.removeObjects()`
-
- ```javascript
- var colors = new Ember.Set(["red", "green", "blue"]);
- colors.removeEach(["red", "blue"]); // ["green"]
- ```
-
- @method removeEach
- @param {Ember.Enumerable} objects the objects to remove.
- @return {Ember.Set} The set itself.
- */
- removeEach: Ember.aliasMethod('removeObjects'),
-
- // ..........................................................
- // PRIVATE ENUMERABLE SUPPORT
- //
-
- init: function(items) {
- this._super();
- if (items) this.addObjects(items);
- },
-
- // implement Ember.Enumerable
- nextObject: function(idx) {
- return this[idx];
- },
-
- // more optimized version
- firstObject: Ember.computed(function() {
- return this.length > 0 ? this[0] : undefined;
- }),
-
- // more optimized version
- lastObject: Ember.computed(function() {
- return this.length > 0 ? this[this.length-1] : undefined;
- }),
-
- // implements Ember.MutableEnumerable
- addObject: function(obj) {
- if (get(this, 'isFrozen')) throw new Ember.Error(Ember.FROZEN_ERROR);
- if (isNone(obj)) return this; // nothing to do
-
- var guid = guidFor(obj),
- idx = this[guid],
- len = get(this, 'length'),
- added ;
-
- if (idx>=0 && idx=0 && idx=0;
- },
-
- copy: function() {
- var C = this.constructor, ret = new C(), loc = get(this, 'length');
- set(ret, 'length', loc);
- while(--loc>=0) {
- ret[loc] = this[loc];
- ret[guidFor(this[loc])] = loc;
- }
- return ret;
- },
-
- toString: function() {
- var len = this.length, idx, array = [];
- for(idx = 0; idx < len; idx++) {
- array[idx] = this[idx];
- }
- return fmt("Ember.Set<%@>", [array.join(',')]);
- }
-
-});
-
-})();
-
-
-
-(function() {
-var DeferredMixin = Ember.DeferredMixin, // mixins/deferred
- get = Ember.get;
-
-var Deferred = Ember.Object.extend(DeferredMixin);
-
-Deferred.reopenClass({
- promise: function(callback, binding) {
- var deferred = Deferred.create();
- callback.call(binding, deferred);
- return deferred;
- }
-});
-
-Ember.Deferred = Deferred;
-
-})();
-
-
-
-(function() {
-var forEach = Ember.ArrayPolyfills.forEach;
-
-/**
-@module ember
-@submodule ember-runtime
-*/
-
-var loadHooks = Ember.ENV.EMBER_LOAD_HOOKS || {};
-var loaded = {};
-
-/**
-
-Detects when a specific package of Ember (e.g. 'Ember.Handlebars')
-has fully loaded and is available for extension.
-
-The provided `callback` will be called with the `name` passed
-resolved from a string into the object:
-
-```javascript
-Ember.onLoad('Ember.Handlebars' function(hbars){
- hbars.registerHelper(...);
-});
-```
-
-
-@method onLoad
-@for Ember
-@param name {String} name of hook
-@param callback {Function} callback to be called
-*/
-Ember.onLoad = function(name, callback) {
- var object;
-
- loadHooks[name] = loadHooks[name] || Ember.A();
- loadHooks[name].pushObject(callback);
-
- if (object = loaded[name]) {
- callback(object);
- }
-};
-
-/**
-
-Called when an Ember.js package (e.g Ember.Handlebars) has finished
-loading. Triggers any callbacks registered for this event.
-
-@method runLoadHooks
-@for Ember
-@param name {String} name of hook
-@param object {Object} object to pass to callbacks
-*/
-Ember.runLoadHooks = function(name, object) {
- loaded[name] = object;
-
- if (loadHooks[name]) {
- forEach.call(loadHooks[name], function(callback) {
- callback(object);
- });
- }
-};
-
-})();
-
-
-
-(function() {
-
-})();
-
-
-
-(function() {
-var get = Ember.get;
-
-/**
-@module ember
-@submodule ember-runtime
-*/
-
-/**
- `Ember.ControllerMixin` provides a standard interface for all classes that
- compose Ember's controller layer: `Ember.Controller`,
- `Ember.ArrayController`, and `Ember.ObjectController`.
-
- @class ControllerMixin
- @namespace Ember
-*/
-Ember.ControllerMixin = Ember.Mixin.create(Ember.ActionHandler, {
- /* ducktype as a controller */
- isController: true,
-
- /**
- The object to which actions from the view should be sent.
-
- For example, when a Handlebars template uses the `{{action}}` helper,
- it will attempt to send the action to the view's controller's `target`.
-
- By default, a controller's `target` is set to the router after it is
- instantiated by `Ember.Application#initialize`.
-
- @property target
- @default null
- */
- target: null,
-
- container: null,
-
- parentController: null,
-
- store: null,
-
- model: Ember.computed.alias('content'),
-
- deprecatedSendHandles: function(actionName) {
- return !!this[actionName];
- },
-
- deprecatedSend: function(actionName) {
- var args = [].slice.call(arguments, 1);
- Ember.assert('' + this + " has the action " + actionName + " but it is not a function", typeof this[actionName] === 'function');
- Ember.deprecate('Action handlers implemented directly on controllers are deprecated in favor of action handlers on an `actions` object (' + actionName + ' on ' + this + ')', false);
- this[actionName].apply(this, args);
- return;
- }
-});
-
-/**
- @class Controller
- @namespace Ember
- @extends Ember.Object
- @uses Ember.ControllerMixin
-*/
-Ember.Controller = Ember.Object.extend(Ember.ControllerMixin);
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-runtime
-*/
-
-var get = Ember.get, set = Ember.set, forEach = Ember.EnumerableUtils.forEach;
-
-/**
- `Ember.SortableMixin` provides a standard interface for array proxies
- to specify a sort order and maintain this sorting when objects are added,
- removed, or updated without changing the implicit order of their underlying
- content array:
-
- ```javascript
- songs = [
- {trackNumber: 4, title: 'Ob-La-Di, Ob-La-Da'},
- {trackNumber: 2, title: 'Back in the U.S.S.R.'},
- {trackNumber: 3, title: 'Glass Onion'},
- ];
-
- songsController = Ember.ArrayController.create({
- content: songs,
- sortProperties: ['trackNumber'],
- sortAscending: true
- });
-
- songsController.get('firstObject'); // {trackNumber: 2, title: 'Back in the U.S.S.R.'}
-
- songsController.addObject({trackNumber: 1, title: 'Dear Prudence'});
- songsController.get('firstObject'); // {trackNumber: 1, title: 'Dear Prudence'}
- ```
-
- If you add or remove the properties to sort by or change the sort direction the content
- sort order will be automatically updated.
-
- ```javascript
- songsController.set('sortProperties', ['title']);
- songsController.get('firstObject'); // {trackNumber: 2, title: 'Back in the U.S.S.R.'}
-
- songsController.toggleProperty('sortAscending');
- songsController.get('firstObject'); // {trackNumber: 4, title: 'Ob-La-Di, Ob-La-Da'}
- ```
-
- SortableMixin works by sorting the arrangedContent array, which is the array that
- arrayProxy displays. Due to the fact that the underlying 'content' array is not changed, that
- array will not display the sorted list:
-
- ```javascript
- songsController.get('content').get('firstObject'); // Returns the unsorted original content
- songsController.get('firstObject'); // Returns the sorted content.
- ```
-
- Although the sorted content can also be accessed through the arrangedContent property,
- it is preferable to use the proxied class and not the arrangedContent array directly.
-
- @class SortableMixin
- @namespace Ember
- @uses Ember.MutableEnumerable
-*/
-Ember.SortableMixin = Ember.Mixin.create(Ember.MutableEnumerable, {
-
- /**
- Specifies which properties dictate the arrangedContent's sort order.
-
- When specifying multiple properties the sorting will use properties
- from the `sortProperties` array prioritized from first to last.
-
- @property {Array} sortProperties
- */
- sortProperties: null,
-
- /**
- Specifies the arrangedContent's sort direction
-
- @property {Boolean} sortAscending
- */
- sortAscending: true,
-
- /**
- The function used to compare two values. You can override this if you
- want to do custom comparisons. Functions must be of the type expected by
- Array#sort, i.e.
- return 0 if the two parameters are equal,
- return a negative value if the first parameter is smaller than the second or
- return a positive value otherwise:
-
- ```javascript
- function(x,y) { // These are assumed to be integers
- if (x === y)
- return 0;
- return x < y ? -1 : 1;
- }
- ```
-
- @property sortFunction
- @type {Function}
- @default Ember.compare
- */
- sortFunction: Ember.compare,
-
- orderBy: function(item1, item2) {
- var result = 0,
- sortProperties = get(this, 'sortProperties'),
- sortAscending = get(this, 'sortAscending'),
- sortFunction = get(this, 'sortFunction');
-
- Ember.assert("you need to define `sortProperties`", !!sortProperties);
-
- forEach(sortProperties, function(propertyName) {
- if (result === 0) {
- result = sortFunction(get(item1, propertyName), get(item2, propertyName));
- if ((result !== 0) && !sortAscending) {
- result = (-1) * result;
- }
- }
- });
-
- return result;
- },
-
- destroy: function() {
- var content = get(this, 'content'),
- sortProperties = get(this, 'sortProperties');
-
- if (content && sortProperties) {
- forEach(content, function(item) {
- forEach(sortProperties, function(sortProperty) {
- Ember.removeObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange');
- }, this);
- }, this);
- }
-
- return this._super();
- },
-
- isSorted: Ember.computed.bool('sortProperties'),
-
- /**
- Overrides the default arrangedContent from arrayProxy in order to sort by sortFunction.
- Also sets up observers for each sortProperty on each item in the content Array.
-
- @property arrangedContent
- */
-
- arrangedContent: Ember.computed('content', 'sortProperties.@each', function(key, value) {
- var content = get(this, 'content'),
- isSorted = get(this, 'isSorted'),
- sortProperties = get(this, 'sortProperties'),
- self = this;
-
- if (content && isSorted) {
- content = content.slice();
- content.sort(function(item1, item2) {
- return self.orderBy(item1, item2);
- });
- forEach(content, function(item) {
- forEach(sortProperties, function(sortProperty) {
- Ember.addObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange');
- }, this);
- }, this);
- return Ember.A(content);
- }
-
- return content;
- }),
-
- _contentWillChange: Ember.beforeObserver('content', function() {
- var content = get(this, 'content'),
- sortProperties = get(this, 'sortProperties');
-
- if (content && sortProperties) {
- forEach(content, function(item) {
- forEach(sortProperties, function(sortProperty) {
- Ember.removeObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange');
- }, this);
- }, this);
- }
-
- this._super();
- }),
-
- sortAscendingWillChange: Ember.beforeObserver('sortAscending', function() {
- this._lastSortAscending = get(this, 'sortAscending');
- }),
-
- sortAscendingDidChange: Ember.observer('sortAscending', function() {
- if (get(this, 'sortAscending') !== this._lastSortAscending) {
- var arrangedContent = get(this, 'arrangedContent');
- arrangedContent.reverseObjects();
- }
- }),
-
- contentArrayWillChange: function(array, idx, removedCount, addedCount) {
- var isSorted = get(this, 'isSorted');
-
- if (isSorted) {
- var arrangedContent = get(this, 'arrangedContent');
- var removedObjects = array.slice(idx, idx+removedCount);
- var sortProperties = get(this, 'sortProperties');
-
- forEach(removedObjects, function(item) {
- arrangedContent.removeObject(item);
-
- forEach(sortProperties, function(sortProperty) {
- Ember.removeObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange');
- }, this);
- }, this);
- }
-
- return this._super(array, idx, removedCount, addedCount);
- },
-
- contentArrayDidChange: function(array, idx, removedCount, addedCount) {
- var isSorted = get(this, 'isSorted'),
- sortProperties = get(this, 'sortProperties');
-
- if (isSorted) {
- var addedObjects = array.slice(idx, idx+addedCount);
-
- forEach(addedObjects, function(item) {
- this.insertItemSorted(item);
-
- forEach(sortProperties, function(sortProperty) {
- Ember.addObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange');
- }, this);
- }, this);
- }
-
- return this._super(array, idx, removedCount, addedCount);
- },
-
- insertItemSorted: function(item) {
- var arrangedContent = get(this, 'arrangedContent');
- var length = get(arrangedContent, 'length');
-
- var idx = this._binarySearch(item, 0, length);
- arrangedContent.insertAt(idx, item);
- },
-
- contentItemSortPropertyDidChange: function(item) {
- var arrangedContent = get(this, 'arrangedContent'),
- oldIndex = arrangedContent.indexOf(item),
- leftItem = arrangedContent.objectAt(oldIndex - 1),
- rightItem = arrangedContent.objectAt(oldIndex + 1),
- leftResult = leftItem && this.orderBy(item, leftItem),
- rightResult = rightItem && this.orderBy(item, rightItem);
-
- if (leftResult < 0 || rightResult > 0) {
- arrangedContent.removeObject(item);
- this.insertItemSorted(item);
- }
- },
-
- _binarySearch: function(item, low, high) {
- var mid, midItem, res, arrangedContent;
-
- if (low === high) {
- return low;
- }
-
- arrangedContent = get(this, 'arrangedContent');
-
- mid = low + Math.floor((high - low) / 2);
- midItem = arrangedContent.objectAt(mid);
-
- res = this.orderBy(midItem, item);
-
- if (res < 0) {
- return this._binarySearch(item, mid+1, high);
- } else if (res > 0) {
- return this._binarySearch(item, low, mid);
- }
-
- return mid;
- }
-});
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-runtime
-*/
-
-var get = Ember.get, set = Ember.set, forEach = Ember.EnumerableUtils.forEach,
- replace = Ember.EnumerableUtils.replace;
-
-/**
- `Ember.ArrayController` provides a way for you to publish a collection of
- objects so that you can easily bind to the collection from a Handlebars
- `#each` helper, an `Ember.CollectionView`, or other controllers.
-
- The advantage of using an `ArrayController` is that you only have to set up
- your view bindings once; to change what's displayed, simply swap out the
- `content` property on the controller.
-
- For example, imagine you wanted to display a list of items fetched via an XHR
- request. Create an `Ember.ArrayController` and set its `content` property:
-
- ```javascript
- MyApp.listController = Ember.ArrayController.create();
-
- $.get('people.json', function(data) {
- MyApp.listController.set('content', data);
- });
- ```
-
- Then, create a view that binds to your new controller:
-
- ```handlebars
- {{#each MyApp.listController}}
- {{firstName}} {{lastName}}
- {{/each}}
- ```
-
- Although you are binding to the controller, the behavior of this controller
- is to pass through any methods or properties to the underlying array. This
- capability comes from `Ember.ArrayProxy`, which this class inherits from.
-
- Sometimes you want to display computed properties within the body of an
- `#each` helper that depend on the underlying items in `content`, but are not
- present on those items. To do this, set `itemController` to the name of a
- controller (probably an `ObjectController`) that will wrap each individual item.
-
- For example:
-
- ```handlebars
- {{#each post in controller}}
- {{title}} ({{titleLength}} characters)
- {{/each}}
- ```
-
- ```javascript
- App.PostsController = Ember.ArrayController.extend({
- itemController: 'post'
- });
-
- App.PostController = Ember.ObjectController.extend({
- // the `title` property will be proxied to the underlying post.
-
- titleLength: function() {
- return this.get('title').length;
- }.property('title')
- });
- ```
-
- In some cases it is helpful to return a different `itemController` depending
- on the particular item. Subclasses can do this by overriding
- `lookupItemController`.
-
- For example:
-
- ```javascript
- App.MyArrayController = Ember.ArrayController.extend({
- lookupItemController: function( object ) {
- if (object.get('isSpecial')) {
- return "special"; // use App.SpecialController
- } else {
- return "regular"; // use App.RegularController
- }
- }
- });
- ```
-
- The itemController instances will have a `parentController` property set to
- either the the `parentController` property of the `ArrayController`
- or to the `ArrayController` instance itself.
-
- @class ArrayController
- @namespace Ember
- @extends Ember.ArrayProxy
- @uses Ember.SortableMixin
- @uses Ember.ControllerMixin
-*/
-
-Ember.ArrayController = Ember.ArrayProxy.extend(Ember.ControllerMixin,
- Ember.SortableMixin, {
-
- /**
- The controller used to wrap items, if any.
-
- @property itemController
- @type String
- @default null
- */
- itemController: null,
-
- /**
- Return the name of the controller to wrap items, or `null` if items should
- be returned directly. The default implementation simply returns the
- `itemController` property, but subclasses can override this method to return
- different controllers for different objects.
-
- For example:
-
- ```javascript
- App.MyArrayController = Ember.ArrayController.extend({
- lookupItemController: function( object ) {
- if (object.get('isSpecial')) {
- return "special"; // use App.SpecialController
- } else {
- return "regular"; // use App.RegularController
- }
- }
- });
- ```
-
- @method lookupItemController
- @param {Object} object
- @return {String}
- */
- lookupItemController: function(object) {
- return get(this, 'itemController');
- },
-
- objectAtContent: function(idx) {
- var length = get(this, 'length'),
- arrangedContent = get(this,'arrangedContent'),
- object = arrangedContent && arrangedContent.objectAt(idx);
-
- if (idx >= 0 && idx < length) {
- var controllerClass = this.lookupItemController(object);
- if (controllerClass) {
- return this.controllerAt(idx, object, controllerClass);
- }
- }
-
- // When `controllerClass` is falsy, we have not opted in to using item
- // controllers, so return the object directly.
-
- // When the index is out of range, we want to return the "out of range"
- // value, whatever that might be. Rather than make assumptions
- // (e.g. guessing `null` or `undefined`) we defer this to `arrangedContent`.
- return object;
- },
-
- arrangedContentDidChange: function() {
- this._super();
- this._resetSubControllers();
- },
-
- arrayContentDidChange: function(idx, removedCnt, addedCnt) {
- var subControllers = get(this, '_subControllers'),
- subControllersToRemove = subControllers.slice(idx, idx+removedCnt);
-
- forEach(subControllersToRemove, function(subController) {
- if (subController) { subController.destroy(); }
- });
-
- replace(subControllers, idx, removedCnt, new Array(addedCnt));
-
- // The shadow array of subcontrollers must be updated before we trigger
- // observers, otherwise observers will get the wrong subcontainer when
- // calling `objectAt`
- this._super(idx, removedCnt, addedCnt);
- },
-
- init: function() {
- this._super();
-
- this.set('_subControllers', Ember.A());
- },
-
- content: Ember.computed(function () {
- return Ember.A();
- }),
-
- controllerAt: function(idx, object, controllerClass) {
- var container = get(this, 'container'),
- subControllers = get(this, '_subControllers'),
- subController = subControllers[idx],
- factory, fullName;
-
- if (subController) { return subController; }
-
- fullName = "controller:" + controllerClass;
-
- if (!container.has(fullName)) {
- throw new Ember.Error('Could not resolve itemController: "' + controllerClass + '"');
- }
-
- subController = container.lookupFactory(fullName).create({
- target: this,
- parentController: get(this, 'parentController') || this,
- content: object
- });
-
- subControllers[idx] = subController;
-
- return subController;
- },
-
- _subControllers: null,
-
- _resetSubControllers: function() {
- var subControllers = get(this, '_subControllers');
- if (subControllers) {
- forEach(subControllers, function(subController) {
- if (subController) { subController.destroy(); }
- });
- }
-
- this.set('_subControllers', Ember.A());
- }
-});
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-runtime
-*/
-
-/**
- `Ember.ObjectController` is part of Ember's Controller layer. It is intended
- to wrap a single object, proxying unhandled attempts to `get` and `set` to the underlying
- content object, and to forward unhandled action attempts to its `target`.
-
- `Ember.ObjectController` derives this functionality from its superclass
- `Ember.ObjectProxy` and the `Ember.ControllerMixin` mixin.
-
- @class ObjectController
- @namespace Ember
- @extends Ember.ObjectProxy
- @uses Ember.ControllerMixin
-**/
-Ember.ObjectController = Ember.ObjectProxy.extend(Ember.ControllerMixin);
-
-})();
-
-
-
-(function() {
-
-})();
-
-
-
-(function() {
-/**
-Ember Runtime
-
-@module ember
-@submodule ember-runtime
-@requires ember-metal
-*/
-
-})();
-
-(function() {
-/**
-@module ember
-@submodule ember-views
-*/
-
-var jQuery = this.jQuery || (Ember.imports && Ember.imports.jQuery);
-if (!jQuery && typeof require === 'function') {
- jQuery = require('jquery');
-}
-
-Ember.assert("Ember Views require jQuery 1.7, 1.8, 1.9, 1.10, or 2.0", jQuery && (jQuery().jquery.match(/^((1\.(7|8|9|10))|2.0)(\.\d+)?(pre|rc\d?)?/) || Ember.ENV.FORCE_JQUERY));
-
-/**
- Alias for jQuery
-
- @method $
- @for Ember
-*/
-Ember.$ = jQuery;
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-views
-*/
-if (Ember.$) {
- // http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#dndevents
- var dragEvents = Ember.String.w('dragstart drag dragenter dragleave dragover drop dragend');
-
- // Copies the `dataTransfer` property from a browser event object onto the
- // jQuery event object for the specified events
- Ember.EnumerableUtils.forEach(dragEvents, function(eventName) {
- Ember.$.event.fixHooks[eventName] = { props: ['dataTransfer'] };
- });
-}
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-views
-*/
-
-/* BEGIN METAMORPH HELPERS */
-
-// Internet Explorer prior to 9 does not allow setting innerHTML if the first element
-// is a "zero-scope" element. This problem can be worked around by making
-// the first node an invisible text node. We, like Modernizr, use
-
-var needsShy = this.document && (function() {
- var testEl = document.createElement('div');
- testEl.innerHTML = "
";
- testEl.firstChild.innerHTML = "";
- return testEl.firstChild.innerHTML === '';
-})();
-
-// IE 8 (and likely earlier) likes to move whitespace preceeding
-// a script tag to appear after it. This means that we can
-// accidentally remove whitespace when updating a morph.
-var movesWhitespace = this.document && (function() {
- var testEl = document.createElement('div');
- testEl.innerHTML = "Test: Value";
- return testEl.childNodes[0].nodeValue === 'Test:' &&
- testEl.childNodes[2].nodeValue === ' Value';
-})();
-
-// Use this to find children by ID instead of using jQuery
-var findChildById = function(element, id) {
- if (element.getAttribute('id') === id) { return element; }
-
- var len = element.childNodes.length, idx, node, found;
- for (idx=0; idx 0) {
- var len = matches.length, idx;
- for (idx=0; idxTest');
- canSet = el.options.length === 1;
- }
-
- innerHTMLTags[tagName] = canSet;
-
- return canSet;
-};
-
-var setInnerHTML = function(element, html) {
- var tagName = element.tagName;
-
- if (canSetInnerHTML(tagName)) {
- setInnerHTMLWithoutFix(element, html);
- } else {
- // Firefox versions < 11 do not have support for element.outerHTML.
- var outerHTML = element.outerHTML || new XMLSerializer().serializeToString(element);
- Ember.assert("Can't set innerHTML on "+element.tagName+" in this browser", outerHTML);
-
- var startTag = outerHTML.match(new RegExp("<"+tagName+"([^>]*)>", 'i'))[0],
- endTag = ''+tagName+'>';
-
- var wrapper = document.createElement('div');
- setInnerHTMLWithoutFix(wrapper, startTag + html + endTag);
- element = wrapper.firstChild;
- while (element.tagName !== tagName) {
- element = element.nextSibling;
- }
- }
-
- return element;
-};
-
-function isSimpleClick(event) {
- var modifier = event.shiftKey || event.metaKey || event.altKey || event.ctrlKey,
- secondaryClick = event.which > 1; // IE9 may return undefined
-
- return !modifier && !secondaryClick;
-}
-
-Ember.ViewUtils = {
- setInnerHTML: setInnerHTML,
- isSimpleClick: isSimpleClick
-};
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-views
-*/
-
-var get = Ember.get, set = Ember.set;
-
-var ClassSet = function() {
- this.seen = {};
- this.list = [];
-};
-
-ClassSet.prototype = {
- add: function(string) {
- if (string in this.seen) { return; }
- this.seen[string] = true;
-
- this.list.push(string);
- },
-
- toDOM: function() {
- return this.list.join(" ");
- }
-};
-
-var BAD_TAG_NAME_TEST_REGEXP = /[^a-zA-Z0-9\-]/;
-var BAD_TAG_NAME_REPLACE_REGEXP = /[^a-zA-Z0-9\-]/g;
-
-function stripTagName(tagName) {
- if (!tagName) {
- return tagName;
- }
-
- if (!BAD_TAG_NAME_TEST_REGEXP.test(tagName)) {
- return tagName;
- }
-
- return tagName.replace(BAD_TAG_NAME_REPLACE_REGEXP, '');
-}
-
-var BAD_CHARS_REGEXP = /&(?!\w+;)|[<>"'`]/g;
-var POSSIBLE_CHARS_REGEXP = /[&<>"'`]/;
-
-function escapeAttribute(value) {
- // Stolen shamelessly from Handlebars
-
- var escape = {
- "<": "<",
- ">": ">",
- '"': """,
- "'": "'",
- "`": "`"
- };
-
- var escapeChar = function(chr) {
- return escape[chr] || "&";
- };
-
- var string = value.toString();
-
- if(!POSSIBLE_CHARS_REGEXP.test(string)) { return string; }
- return string.replace(BAD_CHARS_REGEXP, escapeChar);
-}
-
-// IE 6/7 have bugs arond setting names on inputs during creation.
-// From http://msdn.microsoft.com/en-us/library/ie/ms536389(v=vs.85).aspx:
-// "To include the NAME attribute at run time on objects created with the createElement method, use the eTag."
-var canSetNameOnInputs = (function() {
- var div = document.createElement('div'),
- el = document.createElement('input');
-
- el.setAttribute('name', 'foo');
- div.appendChild(el);
-
- return !!div.innerHTML.match('foo');
-})();
-
-/**
- `Ember.RenderBuffer` gathers information regarding the a view and generates the
- final representation. `Ember.RenderBuffer` will generate HTML which can be pushed
- to the DOM.
-
- ```javascript
- var buffer = Ember.RenderBuffer('div');
- ```
-
- @class RenderBuffer
- @namespace Ember
- @constructor
- @param {String} tagName tag name (such as 'div' or 'p') used for the buffer
-*/
-Ember.RenderBuffer = function(tagName) {
- return new Ember._RenderBuffer(tagName);
-};
-
-Ember._RenderBuffer = function(tagName) {
- this.tagNames = [tagName || null];
- this.buffer = "";
-};
-
-Ember._RenderBuffer.prototype =
-/** @scope Ember.RenderBuffer.prototype */ {
-
- // The root view's element
- _element: null,
-
- _hasElement: true,
-
- /**
- @private
-
- An internal set used to de-dupe class names when `addClass()` is
- used. After each call to `addClass()`, the `classes` property
- will be updated.
-
- @property elementClasses
- @type Array
- @default []
- */
- elementClasses: null,
-
- /**
- Array of class names which will be applied in the class attribute.
-
- You can use `setClasses()` to set this property directly. If you
- use `addClass()`, it will be maintained for you.
-
- @property classes
- @type Array
- @default []
- */
- classes: null,
-
- /**
- The id in of the element, to be applied in the id attribute.
-
- You should not set this property yourself, rather, you should use
- the `id()` method of `Ember.RenderBuffer`.
-
- @property elementId
- @type String
- @default null
- */
- elementId: null,
-
- /**
- A hash keyed on the name of the attribute and whose value will be
- applied to that attribute. For example, if you wanted to apply a
- `data-view="Foo.bar"` property to an element, you would set the
- elementAttributes hash to `{'data-view':'Foo.bar'}`.
-
- You should not maintain this hash yourself, rather, you should use
- the `attr()` method of `Ember.RenderBuffer`.
-
- @property elementAttributes
- @type Hash
- @default {}
- */
- elementAttributes: null,
-
- /**
- A hash keyed on the name of the properties and whose value will be
- applied to that property. For example, if you wanted to apply a
- `checked=true` property to an element, you would set the
- elementProperties hash to `{'checked':true}`.
-
- You should not maintain this hash yourself, rather, you should use
- the `prop()` method of `Ember.RenderBuffer`.
-
- @property elementProperties
- @type Hash
- @default {}
- */
- elementProperties: null,
-
- /**
- The tagname of the element an instance of `Ember.RenderBuffer` represents.
-
- Usually, this gets set as the first parameter to `Ember.RenderBuffer`. For
- example, if you wanted to create a `p` tag, then you would call
-
- ```javascript
- Ember.RenderBuffer('p')
- ```
-
- @property elementTag
- @type String
- @default null
- */
- elementTag: null,
-
- /**
- A hash keyed on the name of the style attribute and whose value will
- be applied to that attribute. For example, if you wanted to apply a
- `background-color:black;` style to an element, you would set the
- elementStyle hash to `{'background-color':'black'}`.
-
- You should not maintain this hash yourself, rather, you should use
- the `style()` method of `Ember.RenderBuffer`.
-
- @property elementStyle
- @type Hash
- @default {}
- */
- elementStyle: null,
-
- /**
- Nested `RenderBuffers` will set this to their parent `RenderBuffer`
- instance.
-
- @property parentBuffer
- @type Ember._RenderBuffer
- */
- parentBuffer: null,
-
- /**
- Adds a string of HTML to the `RenderBuffer`.
-
- @method push
- @param {String} string HTML to push into the buffer
- @chainable
- */
- push: function(string) {
- this.buffer += string;
- return this;
- },
-
- /**
- Adds a class to the buffer, which will be rendered to the class attribute.
-
- @method addClass
- @param {String} className Class name to add to the buffer
- @chainable
- */
- addClass: function(className) {
- // lazily create elementClasses
- this.elementClasses = (this.elementClasses || new ClassSet());
- this.elementClasses.add(className);
- this.classes = this.elementClasses.list;
-
- return this;
- },
-
- setClasses: function(classNames) {
- this.classes = classNames;
- },
-
- /**
- Sets the elementID to be used for the element.
-
- @method id
- @param {String} id
- @chainable
- */
- id: function(id) {
- this.elementId = id;
- return this;
- },
-
- // duck type attribute functionality like jQuery so a render buffer
- // can be used like a jQuery object in attribute binding scenarios.
-
- /**
- Adds an attribute which will be rendered to the element.
-
- @method attr
- @param {String} name The name of the attribute
- @param {String} value The value to add to the attribute
- @chainable
- @return {Ember.RenderBuffer|String} this or the current attribute value
- */
- attr: function(name, value) {
- var attributes = this.elementAttributes = (this.elementAttributes || {});
-
- if (arguments.length === 1) {
- return attributes[name];
- } else {
- attributes[name] = value;
- }
-
- return this;
- },
-
- /**
- Remove an attribute from the list of attributes to render.
-
- @method removeAttr
- @param {String} name The name of the attribute
- @chainable
- */
- removeAttr: function(name) {
- var attributes = this.elementAttributes;
- if (attributes) { delete attributes[name]; }
-
- return this;
- },
-
- /**
- Adds an property which will be rendered to the element.
-
- @method prop
- @param {String} name The name of the property
- @param {String} value The value to add to the property
- @chainable
- @return {Ember.RenderBuffer|String} this or the current property value
- */
- prop: function(name, value) {
- var properties = this.elementProperties = (this.elementProperties || {});
-
- if (arguments.length === 1) {
- return properties[name];
- } else {
- properties[name] = value;
- }
-
- return this;
- },
-
- /**
- Remove an property from the list of properties to render.
-
- @method removeProp
- @param {String} name The name of the property
- @chainable
- */
- removeProp: function(name) {
- var properties = this.elementProperties;
- if (properties) { delete properties[name]; }
-
- return this;
- },
-
- /**
- Adds a style to the style attribute which will be rendered to the element.
-
- @method style
- @param {String} name Name of the style
- @param {String} value
- @chainable
- */
- style: function(name, value) {
- this.elementStyle = (this.elementStyle || {});
-
- this.elementStyle[name] = value;
- return this;
- },
-
- begin: function(tagName) {
- this.tagNames.push(tagName || null);
- return this;
- },
-
- pushOpeningTag: function() {
- var tagName = this.currentTagName();
- if (!tagName) { return; }
-
- if (this._hasElement && !this._element && this.buffer.length === 0) {
- this._element = this.generateElement();
- return;
- }
-
- var buffer = this.buffer,
- id = this.elementId,
- classes = this.classes,
- attrs = this.elementAttributes,
- props = this.elementProperties,
- style = this.elementStyle,
- attr, prop;
-
- buffer += '<' + stripTagName(tagName);
-
- if (id) {
- buffer += ' id="' + escapeAttribute(id) + '"';
- this.elementId = null;
- }
- if (classes) {
- buffer += ' class="' + escapeAttribute(classes.join(' ')) + '"';
- this.classes = null;
- }
-
- if (style) {
- buffer += ' style="';
-
- for (prop in style) {
- if (style.hasOwnProperty(prop)) {
- buffer += prop + ':' + escapeAttribute(style[prop]) + ';';
- }
- }
-
- buffer += '"';
-
- this.elementStyle = null;
- }
-
- if (attrs) {
- for (attr in attrs) {
- if (attrs.hasOwnProperty(attr)) {
- buffer += ' ' + attr + '="' + escapeAttribute(attrs[attr]) + '"';
- }
- }
-
- this.elementAttributes = null;
- }
-
- if (props) {
- for (prop in props) {
- if (props.hasOwnProperty(prop)) {
- var value = props[prop];
- if (value || typeof(value) === 'number') {
- if (value === true) {
- buffer += ' ' + prop + '="' + prop + '"';
- } else {
- buffer += ' ' + prop + '="' + escapeAttribute(props[prop]) + '"';
- }
- }
- }
- }
-
- this.elementProperties = null;
- }
-
- buffer += '>';
- this.buffer = buffer;
- },
-
- pushClosingTag: function() {
- var tagName = this.tagNames.pop();
- if (tagName) { this.buffer += '' + stripTagName(tagName) + '>'; }
- },
-
- currentTagName: function() {
- return this.tagNames[this.tagNames.length-1];
- },
-
- generateElement: function() {
- var tagName = this.tagNames.pop(), // pop since we don't need to close
- id = this.elementId,
- classes = this.classes,
- attrs = this.elementAttributes,
- props = this.elementProperties,
- style = this.elementStyle,
- styleBuffer = '', attr, prop, tagString;
-
- if (attrs && attrs.name && !canSetNameOnInputs) {
- // IE allows passing a tag to createElement. See note on `canSetNameOnInputs` above as well.
- tagString = '<'+stripTagName(tagName)+' name="'+escapeAttribute(attrs.name)+'">';
- } else {
- tagString = tagName;
- }
-
- var element = document.createElement(tagString),
- $element = Ember.$(element);
-
- if (id) {
- $element.attr('id', id);
- this.elementId = null;
- }
- if (classes) {
- $element.attr('class', classes.join(' '));
- this.classes = null;
- }
-
- if (style) {
- for (prop in style) {
- if (style.hasOwnProperty(prop)) {
- styleBuffer += (prop + ':' + style[prop] + ';');
- }
- }
-
- $element.attr('style', styleBuffer);
-
- this.elementStyle = null;
- }
-
- if (attrs) {
- for (attr in attrs) {
- if (attrs.hasOwnProperty(attr)) {
- $element.attr(attr, attrs[attr]);
- }
- }
-
- this.elementAttributes = null;
- }
-
- if (props) {
- for (prop in props) {
- if (props.hasOwnProperty(prop)) {
- $element.prop(prop, props[prop]);
- }
- }
-
- this.elementProperties = null;
- }
-
- return element;
- },
-
- /**
- @method element
- @return {DOMElement} The element corresponding to the generated HTML
- of this buffer
- */
- element: function() {
- var html = this.innerString();
-
- if (html) {
- this._element = Ember.ViewUtils.setInnerHTML(this._element, html);
- }
-
- return this._element;
- },
-
- /**
- Generates the HTML content for this buffer.
-
- @method string
- @return {String} The generated HTML
- */
- string: function() {
- if (this._hasElement && this._element) {
- // Firefox versions < 11 do not have support for element.outerHTML.
- var thisElement = this.element(), outerHTML = thisElement.outerHTML;
- if (typeof outerHTML === 'undefined') {
- return Ember.$('
').append(thisElement).html();
- }
- return outerHTML;
- } else {
- return this.innerString();
- }
- },
-
- innerString: function() {
- return this.buffer;
- }
-};
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-views
-*/
-
-var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt;
-
-/**
- `Ember.EventDispatcher` handles delegating browser events to their
- corresponding `Ember.Views.` For example, when you click on a view,
- `Ember.EventDispatcher` ensures that that view's `mouseDown` method gets
- called.
-
- @class EventDispatcher
- @namespace Ember
- @private
- @extends Ember.Object
-*/
-Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.prototype */{
-
- /**
- The set of events names (and associated handler function names) to be setup
- and dispatched by the `EventDispatcher`. Custom events can added to this list at setup
- time, generally via the `Ember.Application.customEvents` hash. Only override this
- default set to prevent the EventDispatcher from listening on some events all together.
-
- This set will be modified by `setup` to also include any events added at that time.
-
- @property events
- @type Object
- */
- events: {
- touchstart : 'touchStart',
- touchmove : 'touchMove',
- touchend : 'touchEnd',
- touchcancel : 'touchCancel',
- keydown : 'keyDown',
- keyup : 'keyUp',
- keypress : 'keyPress',
- mousedown : 'mouseDown',
- mouseup : 'mouseUp',
- contextmenu : 'contextMenu',
- click : 'click',
- dblclick : 'doubleClick',
- mousemove : 'mouseMove',
- focusin : 'focusIn',
- focusout : 'focusOut',
- mouseenter : 'mouseEnter',
- mouseleave : 'mouseLeave',
- submit : 'submit',
- input : 'input',
- change : 'change',
- dragstart : 'dragStart',
- drag : 'drag',
- dragenter : 'dragEnter',
- dragleave : 'dragLeave',
- dragover : 'dragOver',
- drop : 'drop',
- dragend : 'dragEnd'
- },
-
- /**
- @private
-
- The root DOM element to which event listeners should be attached. Event
- listeners will be attached to the document unless this is overridden.
-
- Can be specified as a DOMElement or a selector string.
-
- The default body is a string since this may be evaluated before document.body
- exists in the DOM.
-
- @property rootElement
- @type DOMElement
- @default 'body'
- */
- rootElement: 'body',
-
- /**
- @private
-
- Sets up event listeners for standard browser events.
-
- This will be called after the browser sends a `DOMContentReady` event. By
- default, it will set up all of the listeners on the document body. If you
- would like to register the listeners on a different element, set the event
- dispatcher's `root` property.
-
- @method setup
- @param addedEvents {Hash}
- */
- setup: function(addedEvents, rootElement) {
- var event, events = get(this, 'events');
-
- Ember.$.extend(events, addedEvents || {});
-
-
- if (!Ember.isNone(rootElement)) {
- set(this, 'rootElement', rootElement);
- }
-
- rootElement = Ember.$(get(this, 'rootElement'));
-
- Ember.assert(fmt('You cannot use the same root element (%@) multiple times in an Ember.Application', [rootElement.selector || rootElement[0].tagName]), !rootElement.is('.ember-application'));
- Ember.assert('You cannot make a new Ember.Application using a root element that is a descendent of an existing Ember.Application', !rootElement.closest('.ember-application').length);
- Ember.assert('You cannot make a new Ember.Application using a root element that is an ancestor of an existing Ember.Application', !rootElement.find('.ember-application').length);
-
- rootElement.addClass('ember-application');
-
- Ember.assert('Unable to add "ember-application" class to rootElement. Make sure you set rootElement to the body or an element in the body.', rootElement.is('.ember-application'));
-
- for (event in events) {
- if (events.hasOwnProperty(event)) {
- this.setupHandler(rootElement, event, events[event]);
- }
- }
- },
-
- /**
- @private
-
- Registers an event listener on the document. If the given event is
- triggered, the provided event handler will be triggered on the target view.
-
- If the target view does not implement the event handler, or if the handler
- returns `false`, the parent view will be called. The event will continue to
- bubble to each successive parent view until it reaches the top.
-
- For example, to have the `mouseDown` method called on the target view when
- a `mousedown` event is received from the browser, do the following:
-
- ```javascript
- setupHandler('mousedown', 'mouseDown');
- ```
-
- @method setupHandler
- @param {Element} rootElement
- @param {String} event the browser-originated event to listen to
- @param {String} eventName the name of the method to call on the view
- */
- setupHandler: function(rootElement, event, eventName) {
- var self = this;
-
- rootElement.on(event + '.ember', '.ember-view', function(evt, triggeringManager) {
- return Ember.handleErrors(function() {
- var view = Ember.View.views[this.id],
- result = true, manager = null;
-
- manager = self._findNearestEventManager(view,eventName);
-
- if (manager && manager !== triggeringManager) {
- result = self._dispatchEvent(manager, evt, eventName, view);
- } else if (view) {
- result = self._bubbleEvent(view,evt,eventName);
- } else {
- evt.stopPropagation();
- }
-
- return result;
- }, this);
- });
-
- rootElement.on(event + '.ember', '[data-ember-action]', function(evt) {
- return Ember.handleErrors(function() {
- var actionId = Ember.$(evt.currentTarget).attr('data-ember-action'),
- action = Ember.Handlebars.ActionHelper.registeredActions[actionId];
-
- // We have to check for action here since in some cases, jQuery will trigger
- // an event on `removeChild` (i.e. focusout) after we've already torn down the
- // action handlers for the view.
- if (action && action.eventName === eventName) {
- return action.handler(evt);
- }
- }, this);
- });
- },
-
- _findNearestEventManager: function(view, eventName) {
- var manager = null;
-
- while (view) {
- manager = get(view, 'eventManager');
- if (manager && manager[eventName]) { break; }
-
- view = get(view, 'parentView');
- }
-
- return manager;
- },
-
- _dispatchEvent: function(object, evt, eventName, view) {
- var result = true;
-
- var handler = object[eventName];
- if (Ember.typeOf(handler) === 'function') {
- result = Ember.run(function() {
- return handler.call(object, evt, view);
- });
- // Do not preventDefault in eventManagers.
- evt.stopPropagation();
- }
- else {
- result = this._bubbleEvent(view, evt, eventName);
- }
-
- return result;
- },
-
- _bubbleEvent: function(view, evt, eventName) {
- return Ember.run(function() {
- return view.handleEvent(eventName, evt);
- });
- },
-
- destroy: function() {
- var rootElement = get(this, 'rootElement');
- Ember.$(rootElement).off('.ember', '**').removeClass('ember-application');
- return this._super();
- }
-});
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-views
-*/
-
-// Add a new named queue for rendering views that happens
-// after bindings have synced, and a queue for scheduling actions
-// that that should occur after view rendering.
-var queues = Ember.run.queues,
- indexOf = Ember.ArrayPolyfills.indexOf;
-queues.splice(indexOf.call(queues, 'actions')+1, 0, 'render', 'afterRender');
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-views
-*/
-
-var get = Ember.get, set = Ember.set;
-
-// Original class declaration and documentation in runtime/lib/controllers/controller.js
-// NOTE: It may be possible with YUIDoc to combine docs in two locations
-
-/**
-Additional methods for the ControllerMixin
-
-@class ControllerMixin
-@namespace Ember
-*/
-Ember.ControllerMixin.reopen({
- target: null,
- namespace: null,
- view: null,
- container: null,
- _childContainers: null,
-
- init: function() {
- this._super();
- set(this, '_childContainers', {});
- },
-
- _modelDidChange: Ember.observer('model', function() {
- var containers = get(this, '_childContainers');
-
- for (var prop in containers) {
- if (!containers.hasOwnProperty(prop)) { continue; }
- containers[prop].destroy();
- }
-
- set(this, '_childContainers', {});
- })
-});
-
-})();
-
-
-
-(function() {
-
-})();
-
-
-
-(function() {
-var states = {};
-
-/**
-@module ember
-@submodule ember-views
-*/
-
-var get = Ember.get, set = Ember.set;
-var guidFor = Ember.guidFor;
-var a_forEach = Ember.EnumerableUtils.forEach;
-var a_addObject = Ember.EnumerableUtils.addObject;
-var meta = Ember.meta;
-
-var childViewsProperty = Ember.computed(function() {
- var childViews = this._childViews, ret = Ember.A(), view = this;
-
- a_forEach(childViews, function(view) {
- var currentChildViews;
- if (view.isVirtual) {
- if (currentChildViews = get(view, 'childViews')) {
- ret.pushObjects(currentChildViews);
- }
- } else {
- ret.push(view);
- }
- });
-
- ret.replace = function (idx, removedCount, addedViews) {
- if (view instanceof Ember.ContainerView) {
- Ember.deprecate("Manipulating an Ember.ContainerView through its childViews property is deprecated. Please use the ContainerView instance itself as an Ember.MutableArray.");
- return view.replace(idx, removedCount, addedViews);
- }
- throw new Ember.Error("childViews is immutable");
- };
-
- return ret;
-});
-
-Ember.warn("The VIEW_PRESERVES_CONTEXT flag has been removed and the functionality can no longer be disabled.", Ember.ENV.VIEW_PRESERVES_CONTEXT !== false);
-
-/**
- Global hash of shared templates. This will automatically be populated
- by the build tools so that you can store your Handlebars templates in
- separate files that get loaded into JavaScript at buildtime.
-
- @property TEMPLATES
- @for Ember
- @type Hash
-*/
-Ember.TEMPLATES = {};
-
-/**
- `Ember.CoreView` is an abstract class that exists to give view-like behavior
- to both Ember's main view class `Ember.View` and other classes like
- `Ember._SimpleMetamorphView` that don't need the fully functionaltiy of
- `Ember.View`.
-
- Unless you have specific needs for `CoreView`, you will use `Ember.View`
- in your applications.
-
- @class CoreView
- @namespace Ember
- @extends Ember.Object
- @uses Ember.Evented
-*/
-
-Ember.CoreView = Ember.Object.extend(Ember.Evented, Ember.ActionHandler, {
- isView: true,
-
- states: states,
-
- init: function() {
- this._super();
- this.transitionTo('preRender');
- },
-
- /**
- If the view is currently inserted into the DOM of a parent view, this
- property will point to the parent of the view.
-
- @property parentView
- @type Ember.View
- @default null
- */
- parentView: Ember.computed(function() {
- var parent = this._parentView;
-
- if (parent && parent.isVirtual) {
- return get(parent, 'parentView');
- } else {
- return parent;
- }
- }).property('_parentView'),
-
- state: null,
-
- _parentView: null,
-
- // return the current view, not including virtual views
- concreteView: Ember.computed(function() {
- if (!this.isVirtual) { return this; }
- else { return get(this, 'parentView'); }
- }).property('parentView'),
-
- instrumentName: 'core_view',
-
- instrumentDetails: function(hash) {
- hash.object = this.toString();
- },
-
- /**
- @private
-
- Invoked by the view system when this view needs to produce an HTML
- representation. This method will create a new render buffer, if needed,
- then apply any default attributes, such as class names and visibility.
- Finally, the `render()` method is invoked, which is responsible for
- doing the bulk of the rendering.
-
- You should not need to override this method; instead, implement the
- `template` property, or if you need more control, override the `render`
- method.
-
- @method renderToBuffer
- @param {Ember.RenderBuffer} buffer the render buffer. If no buffer is
- passed, a default buffer, using the current view's `tagName`, will
- be used.
- */
- renderToBuffer: function(parentBuffer, bufferOperation) {
- var name = 'render.' + this.instrumentName,
- details = {};
-
- this.instrumentDetails(details);
-
- return Ember.instrument(name, details, function() {
- return this._renderToBuffer(parentBuffer, bufferOperation);
- }, this);
- },
-
- _renderToBuffer: function(parentBuffer, bufferOperation) {
- // If this is the top-most view, start a new buffer. Otherwise,
- // create a new buffer relative to the original using the
- // provided buffer operation (for example, `insertAfter` will
- // insert a new buffer after the "parent buffer").
- var tagName = this.tagName;
-
- if (tagName === null || tagName === undefined) {
- tagName = 'div';
- }
-
- var buffer = this.buffer = parentBuffer && parentBuffer.begin(tagName) || Ember.RenderBuffer(tagName);
- this.transitionTo('inBuffer', false);
-
- this.beforeRender(buffer);
- this.render(buffer);
- this.afterRender(buffer);
-
- return buffer;
- },
-
- /**
- @private
-
- Override the default event firing from `Ember.Evented` to
- also call methods with the given name.
-
- @method trigger
- @param name {String}
- */
- trigger: function(name) {
- this._super.apply(this, arguments);
- var method = this[name];
- if (method) {
- var args = [], i, l;
- for (i = 1, l = arguments.length; i < l; i++) {
- args.push(arguments[i]);
- }
- return method.apply(this, args);
- }
- },
-
- deprecatedSendHandles: function(actionName) {
- return !!this[actionName];
- },
-
- deprecatedSend: function(actionName) {
- var args = [].slice.call(arguments, 1);
- Ember.assert('' + this + " has the action " + actionName + " but it is not a function", typeof this[actionName] === 'function');
- Ember.deprecate('Action handlers implemented directly on views are deprecated in favor of action handlers on an `actions` object (' + actionName + ' on ' + this + ')', false);
- this[actionName].apply(this, args);
- return;
- },
-
- has: function(name) {
- return Ember.typeOf(this[name]) === 'function' || this._super(name);
- },
-
- destroy: function() {
- var parent = this._parentView;
-
- if (!this._super()) { return; }
-
- // destroy the element -- this will avoid each child view destroying
- // the element over and over again...
- if (!this.removedFromDOM) { this.destroyElement(); }
-
- // remove from parent if found. Don't call removeFromParent,
- // as removeFromParent will try to remove the element from
- // the DOM again.
- if (parent) { parent.removeChild(this); }
-
- this.transitionTo('destroying', false);
-
- return this;
- },
-
- clearRenderedChildren: Ember.K,
- triggerRecursively: Ember.K,
- invokeRecursively: Ember.K,
- transitionTo: Ember.K,
- destroyElement: Ember.K
-});
-
-var ViewCollection = Ember._ViewCollection = function(initialViews) {
- var views = this.views = initialViews || [];
- this.length = views.length;
-};
-
-ViewCollection.prototype = {
- length: 0,
-
- trigger: function(eventName) {
- var views = this.views, view;
- for (var i = 0, l = views.length; i < l; i++) {
- view = views[i];
- if (view.trigger) { view.trigger(eventName); }
- }
- },
-
- triggerRecursively: function(eventName) {
- var views = this.views;
- for (var i = 0, l = views.length; i < l; i++) {
- views[i].triggerRecursively(eventName);
- }
- },
-
- invokeRecursively: function(fn) {
- var views = this.views, view;
-
- for (var i = 0, l = views.length; i < l; i++) {
- view = views[i];
- fn(view);
- }
- },
-
- transitionTo: function(state, children) {
- var views = this.views;
- for (var i = 0, l = views.length; i < l; i++) {
- views[i].transitionTo(state, children);
- }
- },
-
- push: function() {
- this.length += arguments.length;
- var views = this.views;
- return views.push.apply(views, arguments);
- },
-
- objectAt: function(idx) {
- return this.views[idx];
- },
-
- forEach: function(callback) {
- var views = this.views;
- return a_forEach(views, callback);
- },
-
- clear: function() {
- this.length = 0;
- this.views.length = 0;
- }
-};
-
-var EMPTY_ARRAY = [];
-
-/**
- `Ember.View` is the class in Ember responsible for encapsulating templates of
- HTML content, combining templates with data to render as sections of a page's
- DOM, and registering and responding to user-initiated events.
-
- ## HTML Tag
-
- The default HTML tag name used for a view's DOM representation is `div`. This
- can be customized by setting the `tagName` property. The following view
- class:
-
- ```javascript
- ParagraphView = Ember.View.extend({
- tagName: 'em'
- });
- ```
-
- Would result in instances with the following HTML:
-
- ```html
-
- ```
-
- ## HTML `class` Attribute
-
- The HTML `class` attribute of a view's tag can be set by providing a
- `classNames` property that is set to an array of strings:
-
- ```javascript
- MyView = Ember.View.extend({
- classNames: ['my-class', 'my-other-class']
- });
- ```
-
- Will result in view instances with an HTML representation of:
-
- ```html
-
- ```
-
- `class` attribute values can also be set by providing a `classNameBindings`
- property set to an array of properties names for the view. The return value
- of these properties will be added as part of the value for the view's `class`
- attribute. These properties can be computed properties:
-
- ```javascript
- MyView = Ember.View.extend({
- classNameBindings: ['propertyA', 'propertyB'],
- propertyA: 'from-a',
- propertyB: function() {
- if (someLogic) { return 'from-b'; }
- }.property()
- });
- ```
-
- Will result in view instances with an HTML representation of:
-
- ```html
-
- ```
-
- If the value of a class name binding returns a boolean the property name
- itself will be used as the class name if the property is true. The class name
- will not be added if the value is `false` or `undefined`.
-
- ```javascript
- MyView = Ember.View.extend({
- classNameBindings: ['hovered'],
- hovered: true
- });
- ```
-
- Will result in view instances with an HTML representation of:
-
- ```html
-
- ```
-
- When using boolean class name bindings you can supply a string value other
- than the property name for use as the `class` HTML attribute by appending the
- preferred value after a ":" character when defining the binding:
-
- ```javascript
- MyView = Ember.View.extend({
- classNameBindings: ['awesome:so-very-cool'],
- awesome: true
- });
- ```
-
- Will result in view instances with an HTML representation of:
-
- ```html
-
- ```
-
- Boolean value class name bindings whose property names are in a
- camelCase-style format will be converted to a dasherized format:
-
- ```javascript
- MyView = Ember.View.extend({
- classNameBindings: ['isUrgent'],
- isUrgent: true
- });
- ```
-
- Will result in view instances with an HTML representation of:
-
- ```html
-
- ```
-
- Class name bindings can also refer to object values that are found by
- traversing a path relative to the view itself:
-
- ```javascript
- MyView = Ember.View.extend({
- classNameBindings: ['messages.empty']
- messages: Ember.Object.create({
- empty: true
- })
- });
- ```
-
- Will result in view instances with an HTML representation of:
-
- ```html
-
- ```
-
- If you want to add a class name for a property which evaluates to true and
- and a different class name if it evaluates to false, you can pass a binding
- like this:
-
- ```javascript
- // Applies 'enabled' class when isEnabled is true and 'disabled' when isEnabled is false
- Ember.View.extend({
- classNameBindings: ['isEnabled:enabled:disabled']
- isEnabled: true
- });
- ```
-
- Will result in view instances with an HTML representation of:
-
- ```html
-
- ```
-
- When isEnabled is `false`, the resulting HTML reprensentation looks like
- this:
-
- ```html
-
- ```
-
- This syntax offers the convenience to add a class if a property is `false`:
-
- ```javascript
- // Applies no class when isEnabled is true and class 'disabled' when isEnabled is false
- Ember.View.extend({
- classNameBindings: ['isEnabled::disabled']
- isEnabled: true
- });
- ```
-
- Will result in view instances with an HTML representation of:
-
- ```html
-
- ```
-
- When the `isEnabled` property on the view is set to `false`, it will result
- in view instances with an HTML representation of:
-
- ```html
-
- ```
-
- Updates to the the value of a class name binding will result in automatic
- update of the HTML `class` attribute in the view's rendered HTML
- representation. If the value becomes `false` or `undefined` the class name
- will be removed.
-
- Both `classNames` and `classNameBindings` are concatenated properties. See
- [Ember.Object](/api/classes/Ember.Object.html) documentation for more
- information about concatenated properties.
-
- ## HTML Attributes
-
- The HTML attribute section of a view's tag can be set by providing an
- `attributeBindings` property set to an array of property names on the view.
- The return value of these properties will be used as the value of the view's
- HTML associated attribute:
-
- ```javascript
- AnchorView = Ember.View.extend({
- tagName: 'a',
- attributeBindings: ['href'],
- href: 'http://google.com'
- });
- ```
-
- Will result in view instances with an HTML representation of:
-
- ```html
-
- ```
-
- If the return value of an `attributeBindings` monitored property is a boolean
- the property will follow HTML's pattern of repeating the attribute's name as
- its value:
-
- ```javascript
- MyTextInput = Ember.View.extend({
- tagName: 'input',
- attributeBindings: ['disabled'],
- disabled: true
- });
- ```
-
- Will result in view instances with an HTML representation of:
-
- ```html
-
- ```
-
- `attributeBindings` can refer to computed properties:
-
- ```javascript
- MyTextInput = Ember.View.extend({
- tagName: 'input',
- attributeBindings: ['disabled'],
- disabled: function() {
- if (someLogic) {
- return true;
- } else {
- return false;
- }
- }.property()
- });
- ```
-
- Updates to the the property of an attribute binding will result in automatic
- update of the HTML attribute in the view's rendered HTML representation.
-
- `attributeBindings` is a concatenated property. See [Ember.Object](/api/classes/Ember.Object.html)
- documentation for more information about concatenated properties.
-
- ## Templates
-
- The HTML contents of a view's rendered representation are determined by its
- template. Templates can be any function that accepts an optional context
- parameter and returns a string of HTML that will be inserted within the
- view's tag. Most typically in Ember this function will be a compiled
- `Ember.Handlebars` template.
-
- ```javascript
- AView = Ember.View.extend({
- template: Ember.Handlebars.compile('I am the template')
- });
- ```
-
- Will result in view instances with an HTML representation of:
-
- ```html
- I am the template
- ```
-
- Within an Ember application is more common to define a Handlebars templates as
- part of a page:
-
- ```html
-
- ```
-
- And associate it by name using a view's `templateName` property:
-
- ```javascript
- AView = Ember.View.extend({
- templateName: 'some-template'
- });
- ```
-
- Using a value for `templateName` that does not have a Handlebars template
- with a matching `data-template-name` attribute will throw an error.
-
- For views classes that may have a template later defined (e.g. as the block
- portion of a `{{view}}` Handlebars helper call in another template or in
- a subclass), you can provide a `defaultTemplate` property set to compiled
- template function. If a template is not later provided for the view instance
- the `defaultTemplate` value will be used:
-
- ```javascript
- AView = Ember.View.extend({
- defaultTemplate: Ember.Handlebars.compile('I was the default'),
- template: null,
- templateName: null
- });
- ```
-
- Will result in instances with an HTML representation of:
-
- ```html
- I was the default
- ```
-
- If a `template` or `templateName` is provided it will take precedence over
- `defaultTemplate`:
-
- ```javascript
- AView = Ember.View.extend({
- defaultTemplate: Ember.Handlebars.compile('I was the default')
- });
-
- aView = AView.create({
- template: Ember.Handlebars.compile('I was the template, not default')
- });
- ```
-
- Will result in the following HTML representation when rendered:
-
- ```html
- I was the template, not default
- ```
-
- ## View Context
-
- The default context of the compiled template is the view's controller:
-
- ```javascript
- AView = Ember.View.extend({
- template: Ember.Handlebars.compile('Hello {{excitedGreeting}}')
- });
-
- aController = Ember.Object.create({
- firstName: 'Barry',
- excitedGreeting: function() {
- return this.get("content.firstName") + "!!!"
- }.property()
- });
-
- aView = AView.create({
- controller: aController,
- });
- ```
-
- Will result in an HTML representation of:
-
- ```html
- Hello Barry!!!
- ```
-
- A context can also be explicitly supplied through the view's `context`
- property. If the view has neither `context` nor `controller` properties, the
- `parentView`'s context will be used.
-
- ## Layouts
-
- Views can have a secondary template that wraps their main template. Like
- primary templates, layouts can be any function that accepts an optional
- context parameter and returns a string of HTML that will be inserted inside
- view's tag. Views whose HTML element is self closing (e.g. ` `)
- cannot have a layout and this property will be ignored.
-
- Most typically in Ember a layout will be a compiled `Ember.Handlebars`
- template.
-
- A view's layout can be set directly with the `layout` property or reference
- an existing Handlebars template by name with the `layoutName` property.
-
- A template used as a layout must contain a single use of the Handlebars
- `{{yield}}` helper. The HTML contents of a view's rendered `template` will be
- inserted at this location:
-
- ```javascript
- AViewWithLayout = Ember.View.extend({
- layout: Ember.Handlebars.compile("{{yield}}
")
- template: Ember.Handlebars.compile("I got wrapped"),
- });
- ```
-
- Will result in view instances with an HTML representation of:
-
- ```html
-
- ```
-
- See [Ember.Handlebars.helpers.yield](/api/classes/Ember.Handlebars.helpers.html#method_yield)
- for more information.
-
- ## Responding to Browser Events
-
- Views can respond to user-initiated events in one of three ways: method
- implementation, through an event manager, and through `{{action}}` helper use
- in their template or layout.
-
- ### Method Implementation
-
- Views can respond to user-initiated events by implementing a method that
- matches the event name. A `jQuery.Event` object will be passed as the
- argument to this method.
-
- ```javascript
- AView = Ember.View.extend({
- click: function(event) {
- // will be called when when an instance's
- // rendered element is clicked
- }
- });
- ```
-
- ### Event Managers
-
- Views can define an object as their `eventManager` property. This object can
- then implement methods that match the desired event names. Matching events
- that occur on the view's rendered HTML or the rendered HTML of any of its DOM
- descendants will trigger this method. A `jQuery.Event` object will be passed
- as the first argument to the method and an `Ember.View` object as the
- second. The `Ember.View` will be the view whose rendered HTML was interacted
- with. This may be the view with the `eventManager` property or one of its
- descendent views.
-
- ```javascript
- AView = Ember.View.extend({
- eventManager: Ember.Object.create({
- doubleClick: function(event, view) {
- // will be called when when an instance's
- // rendered element or any rendering
- // of this views's descendent
- // elements is clicked
- }
- })
- });
- ```
-
- An event defined for an event manager takes precedence over events of the
- same name handled through methods on the view.
-
- ```javascript
- AView = Ember.View.extend({
- mouseEnter: function(event) {
- // will never trigger.
- },
- eventManager: Ember.Object.create({
- mouseEnter: function(event, view) {
- // takes precedence over AView#mouseEnter
- }
- })
- });
- ```
-
- Similarly a view's event manager will take precedence for events of any views
- rendered as a descendent. A method name that matches an event name will not
- be called if the view instance was rendered inside the HTML representation of
- a view that has an `eventManager` property defined that handles events of the
- name. Events not handled by the event manager will still trigger method calls
- on the descendent.
-
- ```javascript
- OuterView = Ember.View.extend({
- template: Ember.Handlebars.compile("outer {{#view InnerView}}inner{{/view}} outer"),
- eventManager: Ember.Object.create({
- mouseEnter: function(event, view) {
- // view might be instance of either
- // OuterView or InnerView depending on
- // where on the page the user interaction occured
- }
- })
- });
-
- InnerView = Ember.View.extend({
- click: function(event) {
- // will be called if rendered inside
- // an OuterView because OuterView's
- // eventManager doesn't handle click events
- },
- mouseEnter: function(event) {
- // will never be called if rendered inside
- // an OuterView.
- }
- });
- ```
-
- ### Handlebars `{{action}}` Helper
-
- See [Handlebars.helpers.action](/api/classes/Ember.Handlebars.helpers.html#method_action).
-
- ### Event Names
-
- All of the event handling approaches described above respond to the same set
- of events. The names of the built-in events are listed below. (The hash of
- built-in events exists in `Ember.EventDispatcher`.) Additional, custom events
- can be registered by using `Ember.Application.customEvents`.
-
- Touch events:
-
- * `touchStart`
- * `touchMove`
- * `touchEnd`
- * `touchCancel`
-
- Keyboard events
-
- * `keyDown`
- * `keyUp`
- * `keyPress`
-
- Mouse events
-
- * `mouseDown`
- * `mouseUp`
- * `contextMenu`
- * `click`
- * `doubleClick`
- * `mouseMove`
- * `focusIn`
- * `focusOut`
- * `mouseEnter`
- * `mouseLeave`
-
- Form events:
-
- * `submit`
- * `change`
- * `focusIn`
- * `focusOut`
- * `input`
-
- HTML5 drag and drop events:
-
- * `dragStart`
- * `drag`
- * `dragEnter`
- * `dragLeave`
- * `drop`
- * `dragEnd`
-
- ## Handlebars `{{view}}` Helper
-
- Other `Ember.View` instances can be included as part of a view's template by
- using the `{{view}}` Handlebars helper. See [Ember.Handlebars.helpers.view](/api/classes/Ember.Handlebars.helpers.html#method_view)
- for additional information.
-
- @class View
- @namespace Ember
- @extends Ember.CoreView
-*/
-Ember.View = Ember.CoreView.extend(
-/** @scope Ember.View.prototype */ {
-
- concatenatedProperties: ['classNames', 'classNameBindings', 'attributeBindings'],
-
- /**
- @property isView
- @type Boolean
- @default true
- @final
- */
- isView: true,
-
- // ..........................................................
- // TEMPLATE SUPPORT
- //
-
- /**
- The name of the template to lookup if no template is provided.
-
- By default `Ember.View` will lookup a template with this name in
- `Ember.TEMPLATES` (a shared global object).
-
- @property templateName
- @type String
- @default null
- */
- templateName: null,
-
- /**
- The name of the layout to lookup if no layout is provided.
-
- By default `Ember.View` will lookup a template with this name in
- `Ember.TEMPLATES` (a shared global object).
-
- @property layoutName
- @type String
- @default null
- */
- layoutName: null,
-
- /**
- The template used to render the view. This should be a function that
- accepts an optional context parameter and returns a string of HTML that
- will be inserted into the DOM relative to its parent view.
-
- In general, you should set the `templateName` property instead of setting
- the template yourself.
-
- @property template
- @type Function
- */
- template: Ember.computed(function(key, value) {
- if (value !== undefined) { return value; }
-
- var templateName = get(this, 'templateName'),
- template = this.templateForName(templateName, 'template');
-
- Ember.assert("You specified the templateName " + templateName + " for " + this + ", but it did not exist.", !templateName || template);
-
- return template || get(this, 'defaultTemplate');
- }).property('templateName'),
-
- /**
- The controller managing this view. If this property is set, it will be
- made available for use by the template.
-
- @property controller
- @type Object
- */
- controller: Ember.computed(function(key) {
- var parentView = get(this, '_parentView');
- return parentView ? get(parentView, 'controller') : null;
- }).property('_parentView'),
-
- /**
- A view may contain a layout. A layout is a regular template but
- supersedes the `template` property during rendering. It is the
- responsibility of the layout template to retrieve the `template`
- property from the view (or alternatively, call `Handlebars.helpers.yield`,
- `{{yield}}`) to render it in the correct location.
-
- This is useful for a view that has a shared wrapper, but which delegates
- the rendering of the contents of the wrapper to the `template` property
- on a subclass.
-
- @property layout
- @type Function
- */
- layout: Ember.computed(function(key) {
- var layoutName = get(this, 'layoutName'),
- layout = this.templateForName(layoutName, 'layout');
-
- Ember.assert("You specified the layoutName " + layoutName + " for " + this + ", but it did not exist.", !layoutName || layout);
-
- return layout || get(this, 'defaultLayout');
- }).property('layoutName'),
-
- _yield: function(context, options) {
- var template = get(this, 'template');
- if (template) { template(context, options); }
- },
-
- templateForName: function(name, type) {
- if (!name) { return; }
- Ember.assert("templateNames are not allowed to contain periods: "+name, name.indexOf('.') === -1);
-
- // the defaultContainer is deprecated
- var container = this.container || (Ember.Container && Ember.Container.defaultContainer);
- return container && container.lookup('template:' + name);
- },
-
- /**
- The object from which templates should access properties.
-
- This object will be passed to the template function each time the render
- method is called, but it is up to the individual function to decide what
- to do with it.
-
- By default, this will be the view's controller.
-
- @property context
- @type Object
- */
- context: Ember.computed(function(key, value) {
- if (arguments.length === 2) {
- set(this, '_context', value);
- return value;
- } else {
- return get(this, '_context');
- }
- }).volatile(),
-
- /**
- @private
-
- Private copy of the view's template context. This can be set directly
- by Handlebars without triggering the observer that causes the view
- to be re-rendered.
-
- The context of a view is looked up as follows:
-
- 1. Supplied context (usually by Handlebars)
- 2. Specified controller
- 3. `parentView`'s context (for a child of a ContainerView)
-
- The code in Handlebars that overrides the `_context` property first
- checks to see whether the view has a specified controller. This is
- something of a hack and should be revisited.
-
- @property _context
- */
- _context: Ember.computed(function(key) {
- var parentView, controller;
-
- if (controller = get(this, 'controller')) {
- return controller;
- }
-
- parentView = this._parentView;
- if (parentView) {
- return get(parentView, '_context');
- }
-
- return null;
- }),
-
- /**
- @private
-
- If a value that affects template rendering changes, the view should be
- re-rendered to reflect the new value.
-
- @method _contextDidChange
- */
- _contextDidChange: Ember.observer('context', function() {
- this.rerender();
- }),
-
- /**
- If `false`, the view will appear hidden in DOM.
-
- @property isVisible
- @type Boolean
- @default null
- */
- isVisible: true,
-
- /**
- @private
-
- Array of child views. You should never edit this array directly.
- Instead, use `appendChild` and `removeFromParent`.
-
- @property childViews
- @type Array
- @default []
- */
- childViews: childViewsProperty,
-
- _childViews: EMPTY_ARRAY,
-
- // When it's a virtual view, we need to notify the parent that their
- // childViews will change.
- _childViewsWillChange: Ember.beforeObserver('childViews', function() {
- if (this.isVirtual) {
- var parentView = get(this, 'parentView');
- if (parentView) { Ember.propertyWillChange(parentView, 'childViews'); }
- }
- }),
-
- // When it's a virtual view, we need to notify the parent that their
- // childViews did change.
- _childViewsDidChange: Ember.observer('childViews', function() {
- if (this.isVirtual) {
- var parentView = get(this, 'parentView');
- if (parentView) { Ember.propertyDidChange(parentView, 'childViews'); }
- }
- }),
-
- /**
- Return the nearest ancestor that is an instance of the provided
- class.
-
- @property nearestInstanceOf
- @param {Class} klass Subclass of Ember.View (or Ember.View itself)
- @return Ember.View
- @deprecated
- */
- nearestInstanceOf: function(klass) {
- Ember.deprecate("nearestInstanceOf is deprecated and will be removed from future releases. Use nearestOfType.");
- var view = get(this, 'parentView');
-
- while (view) {
- if (view instanceof klass) { return view; }
- view = get(view, 'parentView');
- }
- },
-
- /**
- Return the nearest ancestor that is an instance of the provided
- class or mixin.
-
- @property nearestOfType
- @param {Class,Mixin} klass Subclass of Ember.View (or Ember.View itself),
- or an instance of Ember.Mixin.
- @return Ember.View
- */
- nearestOfType: function(klass) {
- var view = get(this, 'parentView'),
- isOfType = klass instanceof Ember.Mixin ?
- function(view) { return klass.detect(view); } :
- function(view) { return klass.detect(view.constructor); };
-
- while (view) {
- if (isOfType(view)) { return view; }
- view = get(view, 'parentView');
- }
- },
-
- /**
- Return the nearest ancestor that has a given property.
-
- @property nearestWithProperty
- @param {String} property A property name
- @return Ember.View
- */
- nearestWithProperty: function(property) {
- var view = get(this, 'parentView');
-
- while (view) {
- if (property in view) { return view; }
- view = get(view, 'parentView');
- }
- },
-
- /**
- Return the nearest ancestor whose parent is an instance of
- `klass`.
-
- @property nearestChildOf
- @param {Class} klass Subclass of Ember.View (or Ember.View itself)
- @return Ember.View
- */
- nearestChildOf: function(klass) {
- var view = get(this, 'parentView');
-
- while (view) {
- if (get(view, 'parentView') instanceof klass) { return view; }
- view = get(view, 'parentView');
- }
- },
-
- /**
- @private
-
- When the parent view changes, recursively invalidate `controller`
-
- @method _parentViewDidChange
- */
- _parentViewDidChange: Ember.observer('_parentView', function() {
- if (this.isDestroying) { return; }
-
- this.trigger('parentViewDidChange');
-
- if (get(this, 'parentView.controller') && !get(this, 'controller')) {
- this.notifyPropertyChange('controller');
- }
- }),
-
- _controllerDidChange: Ember.observer('controller', function() {
- if (this.isDestroying) { return; }
-
- this.rerender();
-
- this.forEachChildView(function(view) {
- view.propertyDidChange('controller');
- });
- }),
-
- cloneKeywords: function() {
- var templateData = get(this, 'templateData');
-
- var keywords = templateData ? Ember.copy(templateData.keywords) : {};
- set(keywords, 'view', get(this, 'concreteView'));
- set(keywords, '_view', this);
- set(keywords, 'controller', get(this, 'controller'));
-
- return keywords;
- },
-
- /**
- Called on your view when it should push strings of HTML into a
- `Ember.RenderBuffer`. Most users will want to override the `template`
- or `templateName` properties instead of this method.
-
- By default, `Ember.View` will look for a function in the `template`
- property and invoke it with the value of `context`. The value of
- `context` will be the view's controller unless you override it.
-
- @method render
- @param {Ember.RenderBuffer} buffer The render buffer
- */
- render: function(buffer) {
- // If this view has a layout, it is the responsibility of the
- // the layout to render the view's template. Otherwise, render the template
- // directly.
- var template = get(this, 'layout') || get(this, 'template');
-
- if (template) {
- var context = get(this, 'context');
- var keywords = this.cloneKeywords();
- var output;
-
- var data = {
- view: this,
- buffer: buffer,
- isRenderData: true,
- keywords: keywords,
- insideGroup: get(this, 'templateData.insideGroup')
- };
-
- // Invoke the template with the provided template context, which
- // is the view's controller by default. A hash of data is also passed that provides
- // the template with access to the view and render buffer.
-
- Ember.assert('template must be a function. Did you mean to call Ember.Handlebars.compile("...") or specify templateName instead?', typeof template === 'function');
- // The template should write directly to the render buffer instead
- // of returning a string.
- output = template(context, { data: data });
-
- // If the template returned a string instead of writing to the buffer,
- // push the string onto the buffer.
- if (output !== undefined) { buffer.push(output); }
- }
- },
-
- /**
- Renders the view again. This will work regardless of whether the
- view is already in the DOM or not. If the view is in the DOM, the
- rendering process will be deferred to give bindings a chance
- to synchronize.
-
- If children were added during the rendering process using `appendChild`,
- `rerender` will remove them, because they will be added again
- if needed by the next `render`.
-
- In general, if the display of your view changes, you should modify
- the DOM element directly instead of manually calling `rerender`, which can
- be slow.
-
- @method rerender
- */
- rerender: function() {
- return this.currentState.rerender(this);
- },
-
- clearRenderedChildren: function() {
- var lengthBefore = this.lengthBeforeRender,
- lengthAfter = this.lengthAfterRender;
-
- // If there were child views created during the last call to render(),
- // remove them under the assumption that they will be re-created when
- // we re-render.
-
- // VIEW-TODO: Unit test this path.
- var childViews = this._childViews;
- for (var i=lengthAfter-1; i>=lengthBefore; i--) {
- if (childViews[i]) { childViews[i].destroy(); }
- }
- },
-
- /**
- @private
-
- Iterates over the view's `classNameBindings` array, inserts the value
- of the specified property into the `classNames` array, then creates an
- observer to update the view's element if the bound property ever changes
- in the future.
-
- @method _applyClassNameBindings
- */
- _applyClassNameBindings: function(classBindings) {
- var classNames = this.classNames,
- elem, newClass, dasherizedClass;
-
- // Loop through all of the configured bindings. These will be either
- // property names ('isUrgent') or property paths relative to the view
- // ('content.isUrgent')
- a_forEach(classBindings, function(binding) {
-
- // Variable in which the old class value is saved. The observer function
- // closes over this variable, so it knows which string to remove when
- // the property changes.
- var oldClass;
- // Extract just the property name from bindings like 'foo:bar'
- var parsedPath = Ember.View._parsePropertyPath(binding);
-
- // Set up an observer on the context. If the property changes, toggle the
- // class name.
- var observer = function() {
- // Get the current value of the property
- newClass = this._classStringForProperty(binding);
- elem = this.$();
-
- // If we had previously added a class to the element, remove it.
- if (oldClass) {
- elem.removeClass(oldClass);
- // Also remove from classNames so that if the view gets rerendered,
- // the class doesn't get added back to the DOM.
- classNames.removeObject(oldClass);
- }
-
- // If necessary, add a new class. Make sure we keep track of it so
- // it can be removed in the future.
- if (newClass) {
- elem.addClass(newClass);
- oldClass = newClass;
- } else {
- oldClass = null;
- }
- };
-
- // Get the class name for the property at its current value
- dasherizedClass = this._classStringForProperty(binding);
-
- if (dasherizedClass) {
- // Ensure that it gets into the classNames array
- // so it is displayed when we render.
- a_addObject(classNames, dasherizedClass);
-
- // Save a reference to the class name so we can remove it
- // if the observer fires. Remember that this variable has
- // been closed over by the observer.
- oldClass = dasherizedClass;
- }
-
- this.registerObserver(this, parsedPath.path, observer);
- // Remove className so when the view is rerendered,
- // the className is added based on binding reevaluation
- this.one('willClearRender', function() {
- if (oldClass) {
- classNames.removeObject(oldClass);
- oldClass = null;
- }
- });
-
- }, this);
- },
-
- /**
- @private
-
- Iterates through the view's attribute bindings, sets up observers for each,
- then applies the current value of the attributes to the passed render buffer.
-
- @method _applyAttributeBindings
- @param {Ember.RenderBuffer} buffer
- */
- _applyAttributeBindings: function(buffer, attributeBindings) {
- var attributeValue, elem;
-
- a_forEach(attributeBindings, function(binding) {
- var split = binding.split(':'),
- property = split[0],
- attributeName = split[1] || property;
-
- // Create an observer to add/remove/change the attribute if the
- // JavaScript property changes.
- var observer = function() {
- elem = this.$();
-
- attributeValue = get(this, property);
-
- Ember.View.applyAttributeBindings(elem, attributeName, attributeValue);
- };
-
- this.registerObserver(this, property, observer);
-
- // Determine the current value and add it to the render buffer
- // if necessary.
- attributeValue = get(this, property);
- Ember.View.applyAttributeBindings(buffer, attributeName, attributeValue);
- }, this);
- },
-
- /**
- @private
-
- Given a property name, returns a dasherized version of that
- property name if the property evaluates to a non-falsy value.
-
- For example, if the view has property `isUrgent` that evaluates to true,
- passing `isUrgent` to this method will return `"is-urgent"`.
-
- @method _classStringForProperty
- @param property
- */
- _classStringForProperty: function(property) {
- var parsedPath = Ember.View._parsePropertyPath(property);
- var path = parsedPath.path;
-
- var val = get(this, path);
- if (val === undefined && Ember.isGlobalPath(path)) {
- val = get(Ember.lookup, path);
- }
-
- return Ember.View._classStringForValue(path, val, parsedPath.className, parsedPath.falsyClassName);
- },
-
- // ..........................................................
- // ELEMENT SUPPORT
- //
-
- /**
- Returns the current DOM element for the view.
-
- @property element
- @type DOMElement
- */
- element: Ember.computed(function(key, value) {
- if (value !== undefined) {
- return this.currentState.setElement(this, value);
- } else {
- return this.currentState.getElement(this);
- }
- }).property('_parentView'),
-
- /**
- Returns a jQuery object for this view's element. If you pass in a selector
- string, this method will return a jQuery object, using the current element
- as its buffer.
-
- For example, calling `view.$('li')` will return a jQuery object containing
- all of the `li` elements inside the DOM element of this view.
-
- @method $
- @param {String} [selector] a jQuery-compatible selector string
- @return {jQuery} the jQuery object for the DOM node
- */
- $: function(sel) {
- return this.currentState.$(this, sel);
- },
-
- mutateChildViews: function(callback) {
- var childViews = this._childViews,
- idx = childViews.length,
- view;
-
- while(--idx >= 0) {
- view = childViews[idx];
- callback(this, view, idx);
- }
-
- return this;
- },
-
- forEachChildView: function(callback) {
- var childViews = this._childViews;
-
- if (!childViews) { return this; }
-
- var len = childViews.length,
- view, idx;
-
- for (idx = 0; idx < len; idx++) {
- view = childViews[idx];
- callback(view);
- }
-
- return this;
- },
-
- /**
- Appends the view's element to the specified parent element.
-
- If the view does not have an HTML representation yet, `createElement()`
- will be called automatically.
-
- Note that this method just schedules the view to be appended; the DOM
- element will not be appended to the given element until all bindings have
- finished synchronizing.
-
- This is not typically a function that you will need to call directly when
- building your application. You might consider using `Ember.ContainerView`
- instead. If you do need to use `appendTo`, be sure that the target element
- you are providing is associated with an `Ember.Application` and does not
- have an ancestor element that is associated with an Ember view.
-
- @method appendTo
- @param {String|DOMElement|jQuery} A selector, element, HTML string, or jQuery object
- @return {Ember.View} receiver
- */
- appendTo: function(target) {
- // Schedule the DOM element to be created and appended to the given
- // element after bindings have synchronized.
- this._insertElementLater(function() {
- Ember.assert("You tried to append to (" + target + ") but that isn't in the DOM", Ember.$(target).length > 0);
- Ember.assert("You cannot append to an existing Ember.View. Consider using Ember.ContainerView instead.", !Ember.$(target).is('.ember-view') && !Ember.$(target).parents().is('.ember-view'));
- this.$().appendTo(target);
- });
-
- return this;
- },
-
- /**
- Replaces the content of the specified parent element with this view's
- element. If the view does not have an HTML representation yet,
- `createElement()` will be called automatically.
-
- Note that this method just schedules the view to be appended; the DOM
- element will not be appended to the given element until all bindings have
- finished synchronizing
-
- @method replaceIn
- @param {String|DOMElement|jQuery} A selector, element, HTML string, or jQuery object
- @return {Ember.View} received
- */
- replaceIn: function(target) {
- Ember.assert("You tried to replace in (" + target + ") but that isn't in the DOM", Ember.$(target).length > 0);
- Ember.assert("You cannot replace an existing Ember.View. Consider using Ember.ContainerView instead.", !Ember.$(target).is('.ember-view') && !Ember.$(target).parents().is('.ember-view'));
-
- this._insertElementLater(function() {
- Ember.$(target).empty();
- this.$().appendTo(target);
- });
-
- return this;
- },
-
- /**
- @private
-
- Schedules a DOM operation to occur during the next render phase. This
- ensures that all bindings have finished synchronizing before the view is
- rendered.
-
- To use, pass a function that performs a DOM operation.
-
- Before your function is called, this view and all child views will receive
- the `willInsertElement` event. After your function is invoked, this view
- and all of its child views will receive the `didInsertElement` event.
-
- ```javascript
- view._insertElementLater(function() {
- this.createElement();
- this.$().appendTo('body');
- });
- ```
-
- @method _insertElementLater
- @param {Function} fn the function that inserts the element into the DOM
- */
- _insertElementLater: function(fn) {
- this._scheduledInsert = Ember.run.scheduleOnce('render', this, '_insertElement', fn);
- },
-
- _insertElement: function (fn) {
- this._scheduledInsert = null;
- this.currentState.insertElement(this, fn);
- },
-
- /**
- Appends the view's element to the document body. If the view does
- not have an HTML representation yet, `createElement()` will be called
- automatically.
-
- If your application uses the `rootElement` property, you must append
- the view within that element. Rendering views outside of the `rootElement`
- is not supported.
-
- Note that this method just schedules the view to be appended; the DOM
- element will not be appended to the document body until all bindings have
- finished synchronizing.
-
- @method append
- @return {Ember.View} receiver
- */
- append: function() {
- return this.appendTo(document.body);
- },
-
- /**
- Removes the view's element from the element to which it is attached.
-
- @method remove
- @return {Ember.View} receiver
- */
- remove: function() {
- // What we should really do here is wait until the end of the run loop
- // to determine if the element has been re-appended to a different
- // element.
- // In the interim, we will just re-render if that happens. It is more
- // important than elements get garbage collected.
- if (!this.removedFromDOM) { this.destroyElement(); }
- this.invokeRecursively(function(view) {
- if (view.clearRenderedChildren) { view.clearRenderedChildren(); }
- });
- },
-
- elementId: null,
-
- /**
- Attempts to discover the element in the parent element. The default
- implementation looks for an element with an ID of `elementId` (or the
- view's guid if `elementId` is null). You can override this method to
- provide your own form of lookup. For example, if you want to discover your
- element using a CSS class name instead of an ID.
-
- @method findElementInParentElement
- @param {DOMElement} parentElement The parent's DOM element
- @return {DOMElement} The discovered element
- */
- findElementInParentElement: function(parentElem) {
- var id = "#" + this.elementId;
- return Ember.$(id)[0] || Ember.$(id, parentElem)[0];
- },
-
- /**
- Creates a DOM representation of the view and all of its
- child views by recursively calling the `render()` method.
-
- After the element has been created, `didInsertElement` will
- be called on this view and all of its child views.
-
- @method createElement
- @return {Ember.View} receiver
- */
- createElement: function() {
- if (get(this, 'element')) { return this; }
-
- var buffer = this.renderToBuffer();
- set(this, 'element', buffer.element());
-
- return this;
- },
-
- /**
- Called when a view is going to insert an element into the DOM.
-
- @event willInsertElement
- */
- willInsertElement: Ember.K,
-
- /**
- Called when the element of the view has been inserted into the DOM
- or after the view was re-rendered. Override this function to do any
- set up that requires an element in the document body.
-
- @event didInsertElement
- */
- didInsertElement: Ember.K,
-
- /**
- Called when the view is about to rerender, but before anything has
- been torn down. This is a good opportunity to tear down any manual
- observers you have installed based on the DOM state
-
- @event willClearRender
- */
- willClearRender: Ember.K,
-
- /**
- @private
-
- Run this callback on the current view (unless includeSelf is false) and recursively on child views.
-
- @method invokeRecursively
- @param fn {Function}
- @param includeSelf (optional, default true)
- */
- invokeRecursively: function(fn, includeSelf) {
- var childViews = (includeSelf === false) ? this._childViews : [this];
- var currentViews, view, currentChildViews;
-
- while (childViews.length) {
- currentViews = childViews.slice();
- childViews = [];
-
- for (var i=0, l=currentViews.length; i` tag for views.
-
- @property tagName
- @type String
- @default null
- */
-
- // We leave this null by default so we can tell the difference between
- // the default case and a user-specified tag.
- tagName: null,
-
- /**
- The WAI-ARIA role of the control represented by this view. For example, a
- button may have a role of type 'button', or a pane may have a role of
- type 'alertdialog'. This property is used by assistive software to help
- visually challenged users navigate rich web applications.
-
- The full list of valid WAI-ARIA roles is available at:
- [http://www.w3.org/TR/wai-aria/roles#roles_categorization](http://www.w3.org/TR/wai-aria/roles#roles_categorization)
-
- @property ariaRole
- @type String
- @default null
- */
- ariaRole: null,
-
- /**
- Standard CSS class names to apply to the view's outer element. This
- property automatically inherits any class names defined by the view's
- superclasses as well.
-
- @property classNames
- @type Array
- @default ['ember-view']
- */
- classNames: ['ember-view'],
-
- /**
- A list of properties of the view to apply as class names. If the property
- is a string value, the value of that string will be applied as a class
- name.
-
- ```javascript
- // Applies the 'high' class to the view element
- Ember.View.extend({
- classNameBindings: ['priority']
- priority: 'high'
- });
- ```
-
- If the value of the property is a Boolean, the name of that property is
- added as a dasherized class name.
-
- ```javascript
- // Applies the 'is-urgent' class to the view element
- Ember.View.extend({
- classNameBindings: ['isUrgent']
- isUrgent: true
- });
- ```
-
- If you would prefer to use a custom value instead of the dasherized
- property name, you can pass a binding like this:
-
- ```javascript
- // Applies the 'urgent' class to the view element
- Ember.View.extend({
- classNameBindings: ['isUrgent:urgent']
- isUrgent: true
- });
- ```
-
- This list of properties is inherited from the view's superclasses as well.
-
- @property classNameBindings
- @type Array
- @default []
- */
- classNameBindings: EMPTY_ARRAY,
-
- /**
- A list of properties of the view to apply as attributes. If the property is
- a string value, the value of that string will be applied as the attribute.
-
- ```javascript
- // Applies the type attribute to the element
- // with the value "button", like
- Ember.View.extend({
- attributeBindings: ['type'],
- type: 'button'
- });
- ```
-
- If the value of the property is a Boolean, the name of that property is
- added as an attribute.
-
- ```javascript
- // Renders something like
- Ember.View.extend({
- attributeBindings: ['enabled'],
- enabled: true
- });
- ```
-
- @property attributeBindings
- */
- attributeBindings: EMPTY_ARRAY,
-
- // .......................................................
- // CORE DISPLAY METHODS
- //
-
- /**
- @private
-
- Setup a view, but do not finish waking it up.
- - configure `childViews`
- - register the view with the global views hash, which is used for event
- dispatch
-
- @method init
- */
- init: function() {
- this.elementId = this.elementId || guidFor(this);
-
- this._super();
-
- // setup child views. be sure to clone the child views array first
- this._childViews = this._childViews.slice();
-
- Ember.assert("Only arrays are allowed for 'classNameBindings'", Ember.typeOf(this.classNameBindings) === 'array');
- this.classNameBindings = Ember.A(this.classNameBindings.slice());
-
- Ember.assert("Only arrays are allowed for 'classNames'", Ember.typeOf(this.classNames) === 'array');
- this.classNames = Ember.A(this.classNames.slice());
- },
-
- appendChild: function(view, options) {
- return this.currentState.appendChild(this, view, options);
- },
-
- /**
- Removes the child view from the parent view.
-
- @method removeChild
- @param {Ember.View} view
- @return {Ember.View} receiver
- */
- removeChild: function(view) {
- // If we're destroying, the entire subtree will be
- // freed, and the DOM will be handled separately,
- // so no need to mess with childViews.
- if (this.isDestroying) { return; }
-
- // update parent node
- set(view, '_parentView', null);
-
- // remove view from childViews array.
- var childViews = this._childViews;
-
- Ember.EnumerableUtils.removeObject(childViews, view);
-
- this.propertyDidChange('childViews'); // HUH?! what happened to will change?
-
- return this;
- },
-
- /**
- Removes all children from the `parentView`.
-
- @method removeAllChildren
- @return {Ember.View} receiver
- */
- removeAllChildren: function() {
- return this.mutateChildViews(function(parentView, view) {
- parentView.removeChild(view);
- });
- },
-
- destroyAllChildren: function() {
- return this.mutateChildViews(function(parentView, view) {
- view.destroy();
- });
- },
-
- /**
- Removes the view from its `parentView`, if one is found. Otherwise
- does nothing.
-
- @method removeFromParent
- @return {Ember.View} receiver
- */
- removeFromParent: function() {
- var parent = this._parentView;
-
- // Remove DOM element from parent
- this.remove();
-
- if (parent) { parent.removeChild(this); }
- return this;
- },
-
- /**
- You must call `destroy` on a view to destroy the view (and all of its
- child views). This will remove the view from any parent node, then make
- sure that the DOM element managed by the view can be released by the
- memory manager.
-
- @method destroy
- */
- destroy: function() {
- var childViews = this._childViews,
- // get parentView before calling super because it'll be destroyed
- nonVirtualParentView = get(this, 'parentView'),
- viewName = this.viewName,
- childLen, i;
-
- if (!this._super()) { return; }
-
- childLen = childViews.length;
- for (i=childLen-1; i>=0; i--) {
- childViews[i].removedFromDOM = true;
- }
-
- // remove from non-virtual parent view if viewName was specified
- if (viewName && nonVirtualParentView) {
- nonVirtualParentView.set(viewName, null);
- }
-
- childLen = childViews.length;
- for (i=childLen-1; i>=0; i--) {
- childViews[i].destroy();
- }
-
- return this;
- },
-
- /**
- Instantiates a view to be added to the childViews array during view
- initialization. You generally will not call this method directly unless
- you are overriding `createChildViews()`. Note that this method will
- automatically configure the correct settings on the new view instance to
- act as a child of the parent.
-
- @method createChildView
- @param {Class|String} viewClass
- @param {Hash} [attrs] Attributes to add
- @return {Ember.View} new instance
- */
- createChildView: function(view, attrs) {
- if (!view) {
- throw new TypeError("createChildViews first argument must exist");
- }
-
- if (view.isView && view._parentView === this && view.container === this.container) {
- return view;
- }
-
- attrs = attrs || {};
- attrs._parentView = this;
-
- if (Ember.CoreView.detect(view)) {
- attrs.templateData = attrs.templateData || get(this, 'templateData');
-
- attrs.container = this.container;
- view = view.create(attrs);
-
- // don't set the property on a virtual view, as they are invisible to
- // consumers of the view API
- if (view.viewName) {
- set(get(this, 'concreteView'), view.viewName, view);
- }
- } else if ('string' === typeof view) {
- var fullName = 'view:' + view;
- var View = this.container.lookupFactory(fullName);
-
- Ember.assert("Could not find view: '" + fullName + "'", !!View);
-
- attrs.templateData = get(this, 'templateData');
- view = View.create(attrs);
- } else {
- Ember.assert('You must pass instance or subclass of View', view.isView);
- attrs.container = this.container;
-
- if (!get(view, 'templateData')) {
- attrs.templateData = get(this, 'templateData');
- }
-
- Ember.setProperties(view, attrs);
-
- }
-
- return view;
- },
-
- becameVisible: Ember.K,
- becameHidden: Ember.K,
-
- /**
- @private
-
- When the view's `isVisible` property changes, toggle the visibility
- element of the actual DOM element.
-
- @method _isVisibleDidChange
- */
- _isVisibleDidChange: Ember.observer('isVisible', function() {
- var $el = this.$();
- if (!$el) { return; }
-
- var isVisible = get(this, 'isVisible');
-
- $el.toggle(isVisible);
-
- if (this._isAncestorHidden()) { return; }
-
- if (isVisible) {
- this._notifyBecameVisible();
- } else {
- this._notifyBecameHidden();
- }
- }),
-
- _notifyBecameVisible: function() {
- this.trigger('becameVisible');
-
- this.forEachChildView(function(view) {
- var isVisible = get(view, 'isVisible');
-
- if (isVisible || isVisible === null) {
- view._notifyBecameVisible();
- }
- });
- },
-
- _notifyBecameHidden: function() {
- this.trigger('becameHidden');
- this.forEachChildView(function(view) {
- var isVisible = get(view, 'isVisible');
-
- if (isVisible || isVisible === null) {
- view._notifyBecameHidden();
- }
- });
- },
-
- _isAncestorHidden: function() {
- var parent = get(this, 'parentView');
-
- while (parent) {
- if (get(parent, 'isVisible') === false) { return true; }
-
- parent = get(parent, 'parentView');
- }
-
- return false;
- },
-
- clearBuffer: function() {
- this.invokeRecursively(function(view) {
- view.buffer = null;
- });
- },
-
- transitionTo: function(state, children) {
- var priorState = this.currentState,
- currentState = this.currentState = this.states[state];
- this.state = state;
-
- if (priorState && priorState.exit) { priorState.exit(this); }
- if (currentState.enter) { currentState.enter(this); }
- if (state === 'inDOM') { delete Ember.meta(this).cache.element; }
-
- if (children !== false) {
- this.forEachChildView(function(view) {
- view.transitionTo(state);
- });
- }
- },
-
- // .......................................................
- // EVENT HANDLING
- //
-
- /**
- @private
-
- Handle events from `Ember.EventDispatcher`
-
- @method handleEvent
- @param eventName {String}
- @param evt {Event}
- */
- handleEvent: function(eventName, evt) {
- return this.currentState.handleEvent(this, eventName, evt);
- },
-
- registerObserver: function(root, path, target, observer) {
- if (!observer && 'function' === typeof target) {
- observer = target;
- target = null;
- }
-
- if (!root || typeof root !== 'object') {
- return;
- }
-
- var view = this,
- stateCheckedObserver = function() {
- view.currentState.invokeObserver(this, observer);
- },
- scheduledObserver = function() {
- Ember.run.scheduleOnce('render', this, stateCheckedObserver);
- };
-
- Ember.addObserver(root, path, target, scheduledObserver);
-
- this.one('willClearRender', function() {
- Ember.removeObserver(root, path, target, scheduledObserver);
- });
- }
-
-});
-
-/*
- Describe how the specified actions should behave in the various
- states that a view can exist in. Possible states:
-
- * preRender: when a view is first instantiated, and after its
- element was destroyed, it is in the preRender state
- * inBuffer: once a view has been rendered, but before it has
- been inserted into the DOM, it is in the inBuffer state
- * inDOM: once a view has been inserted into the DOM it is in
- the inDOM state. A view spends the vast majority of its
- existence in this state.
- * destroyed: once a view has been destroyed (using the destroy
- method), it is in this state. No further actions can be invoked
- on a destroyed view.
-*/
-
- // in the destroyed state, everything is illegal
-
- // before rendering has begun, all legal manipulations are noops.
-
- // inside the buffer, legal manipulations are done on the buffer
-
- // once the view has been inserted into the DOM, legal manipulations
- // are done on the DOM element.
-
-function notifyMutationListeners() {
- Ember.run.once(Ember.View, 'notifyMutationListeners');
-}
-
-var DOMManager = {
- prepend: function(view, html) {
- view.$().prepend(html);
- notifyMutationListeners();
- },
-
- after: function(view, html) {
- view.$().after(html);
- notifyMutationListeners();
- },
-
- html: function(view, html) {
- view.$().html(html);
- notifyMutationListeners();
- },
-
- replace: function(view) {
- var element = get(view, 'element');
-
- set(view, 'element', null);
-
- view._insertElementLater(function() {
- Ember.$(element).replaceWith(get(view, 'element'));
- notifyMutationListeners();
- });
- },
-
- remove: function(view) {
- view.$().remove();
- notifyMutationListeners();
- },
-
- empty: function(view) {
- view.$().empty();
- notifyMutationListeners();
- }
-};
-
-Ember.View.reopen({
- domManager: DOMManager
-});
-
-Ember.View.reopenClass({
-
- /**
- @private
-
- Parse a path and return an object which holds the parsed properties.
-
- For example a path like "content.isEnabled:enabled:disabled" will return the
- following object:
-
- ```javascript
- {
- path: "content.isEnabled",
- className: "enabled",
- falsyClassName: "disabled",
- classNames: ":enabled:disabled"
- }
- ```
-
- @method _parsePropertyPath
- @static
- */
- _parsePropertyPath: function(path) {
- var split = path.split(':'),
- propertyPath = split[0],
- classNames = "",
- className,
- falsyClassName;
-
- // check if the property is defined as prop:class or prop:trueClass:falseClass
- if (split.length > 1) {
- className = split[1];
- if (split.length === 3) { falsyClassName = split[2]; }
-
- classNames = ':' + className;
- if (falsyClassName) { classNames += ":" + falsyClassName; }
- }
-
- return {
- path: propertyPath,
- classNames: classNames,
- className: (className === '') ? undefined : className,
- falsyClassName: falsyClassName
- };
- },
-
- /**
- @private
-
- Get the class name for a given value, based on the path, optional
- `className` and optional `falsyClassName`.
-
- - if a `className` or `falsyClassName` has been specified:
- - if the value is truthy and `className` has been specified,
- `className` is returned
- - if the value is falsy and `falsyClassName` has been specified,
- `falsyClassName` is returned
- - otherwise `null` is returned
- - if the value is `true`, the dasherized last part of the supplied path
- is returned
- - if the value is not `false`, `undefined` or `null`, the `value`
- is returned
- - if none of the above rules apply, `null` is returned
-
- @method _classStringForValue
- @param path
- @param val
- @param className
- @param falsyClassName
- @static
- */
- _classStringForValue: function(path, val, className, falsyClassName) {
- // When using the colon syntax, evaluate the truthiness or falsiness
- // of the value to determine which className to return
- if (className || falsyClassName) {
- if (className && !!val) {
- return className;
-
- } else if (falsyClassName && !val) {
- return falsyClassName;
-
- } else {
- return null;
- }
-
- // If value is a Boolean and true, return the dasherized property
- // name.
- } else if (val === true) {
- // Normalize property path to be suitable for use
- // as a class name. For exaple, content.foo.barBaz
- // becomes bar-baz.
- var parts = path.split('.');
- return Ember.String.dasherize(parts[parts.length-1]);
-
- // If the value is not false, undefined, or null, return the current
- // value of the property.
- } else if (val !== false && val != null) {
- return val;
-
- // Nothing to display. Return null so that the old class is removed
- // but no new class is added.
- } else {
- return null;
- }
- }
-});
-
-var mutation = Ember.Object.extend(Ember.Evented).create();
-
-Ember.View.addMutationListener = function(callback) {
- mutation.on('change', callback);
-};
-
-Ember.View.removeMutationListener = function(callback) {
- mutation.off('change', callback);
-};
-
-Ember.View.notifyMutationListeners = function() {
- mutation.trigger('change');
-};
-
-/**
- Global views hash
-
- @property views
- @static
- @type Hash
-*/
-Ember.View.views = {};
-
-// If someone overrides the child views computed property when
-// defining their class, we want to be able to process the user's
-// supplied childViews and then restore the original computed property
-// at view initialization time. This happens in Ember.ContainerView's init
-// method.
-Ember.View.childViewsProperty = childViewsProperty;
-
-Ember.View.applyAttributeBindings = function(elem, name, value) {
- var type = Ember.typeOf(value);
-
- // if this changes, also change the logic in ember-handlebars/lib/helpers/binding.js
- if (name !== 'value' && (type === 'string' || (type === 'number' && !isNaN(value)))) {
- if (value !== elem.attr(name)) {
- elem.attr(name, value);
- }
- } else if (name === 'value' || type === 'boolean') {
- // We can't set properties to undefined or null
- if (Ember.isNone(value)) { value = ''; }
-
- if (value !== elem.prop(name)) {
- // value and booleans should always be properties
- elem.prop(name, value);
- }
- } else if (!value) {
- elem.removeAttr(name);
- }
-};
-
-Ember.View.states = states;
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-views
-*/
-
-var get = Ember.get, set = Ember.set;
-
-Ember.View.states._default = {
- // appendChild is only legal while rendering the buffer.
- appendChild: function() {
- throw "You can't use appendChild outside of the rendering process";
- },
-
- $: function() {
- return undefined;
- },
-
- getElement: function() {
- return null;
- },
-
- // Handle events from `Ember.EventDispatcher`
- handleEvent: function() {
- return true; // continue event propagation
- },
-
- destroyElement: function(view) {
- set(view, 'element', null);
- if (view._scheduledInsert) {
- Ember.run.cancel(view._scheduledInsert);
- view._scheduledInsert = null;
- }
- return view;
- },
-
- renderToBufferIfNeeded: function () {
- return false;
- },
-
- rerender: Ember.K,
- invokeObserver: Ember.K
-};
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-views
-*/
-
-var preRender = Ember.View.states.preRender = Ember.create(Ember.View.states._default);
-
-Ember.merge(preRender, {
- // a view leaves the preRender state once its element has been
- // created (createElement).
- insertElement: function(view, fn) {
- view.createElement();
- var viewCollection = view.viewHierarchyCollection();
-
- viewCollection.trigger('willInsertElement');
-
- fn.call(view);
-
- // We transition to `inDOM` if the element exists in the DOM
- var element = view.get('element');
- while (element = element.parentNode) {
- if (element === document) {
- viewCollection.transitionTo('inDOM', false);
- viewCollection.trigger('didInsertElement');
- }
- }
-
- },
-
- renderToBufferIfNeeded: function(view, buffer) {
- view.renderToBuffer(buffer);
- return true;
- },
-
- empty: Ember.K,
-
- setElement: function(view, value) {
- if (value !== null) {
- view.transitionTo('hasElement');
- }
- return value;
- }
-});
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-views
-*/
-
-var get = Ember.get, set = Ember.set;
-
-var inBuffer = Ember.View.states.inBuffer = Ember.create(Ember.View.states._default);
-
-Ember.merge(inBuffer, {
- $: function(view, sel) {
- // if we don't have an element yet, someone calling this.$() is
- // trying to update an element that isn't in the DOM. Instead,
- // rerender the view to allow the render method to reflect the
- // changes.
- view.rerender();
- return Ember.$();
- },
-
- // when a view is rendered in a buffer, rerendering it simply
- // replaces the existing buffer with a new one
- rerender: function(view) {
- throw new Ember.Error("Something you did caused a view to re-render after it rendered but before it was inserted into the DOM.");
- },
-
- // when a view is rendered in a buffer, appending a child
- // view will render that view and append the resulting
- // buffer into its buffer.
- appendChild: function(view, childView, options) {
- var buffer = view.buffer, _childViews = view._childViews;
-
- childView = view.createChildView(childView, options);
- if (!_childViews.length) { _childViews = view._childViews = _childViews.slice(); }
- _childViews.push(childView);
-
- childView.renderToBuffer(buffer);
-
- view.propertyDidChange('childViews');
-
- return childView;
- },
-
- // when a view is rendered in a buffer, destroying the
- // element will simply destroy the buffer and put the
- // state back into the preRender state.
- destroyElement: function(view) {
- view.clearBuffer();
- var viewCollection = view._notifyWillDestroyElement();
- viewCollection.transitionTo('preRender', false);
-
- return view;
- },
-
- empty: function() {
- Ember.assert("Emptying a view in the inBuffer state is not allowed and should not happen under normal circumstances. Most likely there is a bug in your application. This may be due to excessive property change notifications.");
- },
-
- renderToBufferIfNeeded: function (view, buffer) {
- return false;
- },
-
- // It should be impossible for a rendered view to be scheduled for
- // insertion.
- insertElement: function() {
- throw "You can't insert an element that has already been rendered";
- },
-
- setElement: function(view, value) {
- if (value === null) {
- view.transitionTo('preRender');
- } else {
- view.clearBuffer();
- view.transitionTo('hasElement');
- }
-
- return value;
- },
-
- invokeObserver: function(target, observer) {
- observer.call(target);
- }
-});
-
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-views
-*/
-
-var get = Ember.get, set = Ember.set;
-
-var hasElement = Ember.View.states.hasElement = Ember.create(Ember.View.states._default);
-
-Ember.merge(hasElement, {
- $: function(view, sel) {
- var elem = get(view, 'element');
- return sel ? Ember.$(sel, elem) : Ember.$(elem);
- },
-
- getElement: function(view) {
- var parent = get(view, 'parentView');
- if (parent) { parent = get(parent, 'element'); }
- if (parent) { return view.findElementInParentElement(parent); }
- return Ember.$("#" + get(view, 'elementId'))[0];
- },
-
- setElement: function(view, value) {
- if (value === null) {
- view.transitionTo('preRender');
- } else {
- throw "You cannot set an element to a non-null value when the element is already in the DOM.";
- }
-
- return value;
- },
-
- // once the view has been inserted into the DOM, rerendering is
- // deferred to allow bindings to synchronize.
- rerender: function(view) {
- view.triggerRecursively('willClearRender');
-
- view.clearRenderedChildren();
-
- view.domManager.replace(view);
- return view;
- },
-
- // once the view is already in the DOM, destroying it removes it
- // from the DOM, nukes its element, and puts it back into the
- // preRender state if inDOM.
-
- destroyElement: function(view) {
- view._notifyWillDestroyElement();
- view.domManager.remove(view);
- set(view, 'element', null);
- if (view._scheduledInsert) {
- Ember.run.cancel(view._scheduledInsert);
- view._scheduledInsert = null;
- }
- return view;
- },
-
- empty: function(view) {
- var _childViews = view._childViews, len, idx;
- if (_childViews) {
- len = _childViews.length;
- for (idx = 0; idx < len; idx++) {
- _childViews[idx]._notifyWillDestroyElement();
- }
- }
- view.domManager.empty(view);
- },
-
- // Handle events from `Ember.EventDispatcher`
- handleEvent: function(view, eventName, evt) {
- if (view.has(eventName)) {
- // Handler should be able to re-dispatch events, so we don't
- // preventDefault or stopPropagation.
- return view.trigger(eventName, evt);
- } else {
- return true; // continue event propagation
- }
- },
-
- invokeObserver: function(target, observer) {
- observer.call(target);
- }
-});
-
-var inDOM = Ember.View.states.inDOM = Ember.create(hasElement);
-
-Ember.merge(inDOM, {
- enter: function(view) {
- // Register the view for event handling. This hash is used by
- // Ember.EventDispatcher to dispatch incoming events.
- if (!view.isVirtual) {
- Ember.assert("Attempted to register a view with an id already in use: "+view.elementId, !Ember.View.views[view.elementId]);
- Ember.View.views[view.elementId] = view;
- }
-
- view.addBeforeObserver('elementId', function() {
- throw new Ember.Error("Changing a view's elementId after creation is not allowed");
- });
- },
-
- exit: function(view) {
- if (!this.isVirtual) delete Ember.View.views[view.elementId];
- },
-
- insertElement: function(view, fn) {
- throw "You can't insert an element into the DOM that has already been inserted";
- }
-});
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-views
-*/
-
-var destroyingError = "You can't call %@ on a view being destroyed", fmt = Ember.String.fmt;
-
-var destroying = Ember.View.states.destroying = Ember.create(Ember.View.states._default);
-
-Ember.merge(destroying, {
- appendChild: function() {
- throw fmt(destroyingError, ['appendChild']);
- },
- rerender: function() {
- throw fmt(destroyingError, ['rerender']);
- },
- destroyElement: function() {
- throw fmt(destroyingError, ['destroyElement']);
- },
- empty: function() {
- throw fmt(destroyingError, ['empty']);
- },
-
- setElement: function() {
- throw fmt(destroyingError, ["set('element', ...)"]);
- },
-
- renderToBufferIfNeeded: function() {
- return false;
- },
-
- // Since element insertion is scheduled, don't do anything if
- // the view has been destroyed between scheduling and execution
- insertElement: Ember.K
-});
-
-
-})();
-
-
-
-(function() {
-Ember.View.cloneStates = function(from) {
- var into = {};
-
- into._default = {};
- into.preRender = Ember.create(into._default);
- into.destroying = Ember.create(into._default);
- into.inBuffer = Ember.create(into._default);
- into.hasElement = Ember.create(into._default);
- into.inDOM = Ember.create(into.hasElement);
-
- for (var stateName in from) {
- if (!from.hasOwnProperty(stateName)) { continue; }
- Ember.merge(into[stateName], from[stateName]);
- }
-
- return into;
-};
-
-})();
-
-
-
-(function() {
-var states = Ember.View.cloneStates(Ember.View.states);
-
-/**
-@module ember
-@submodule ember-views
-*/
-
-var get = Ember.get, set = Ember.set;
-var forEach = Ember.EnumerableUtils.forEach;
-var ViewCollection = Ember._ViewCollection;
-
-/**
- A `ContainerView` is an `Ember.View` subclass that implements `Ember.MutableArray`
- allowing programatic management of its child views.
-
- ## Setting Initial Child Views
-
- The initial array of child views can be set in one of two ways. You can
- provide a `childViews` property at creation time that contains instance of
- `Ember.View`:
-
- ```javascript
- aContainer = Ember.ContainerView.create({
- childViews: [Ember.View.create(), Ember.View.create()]
- });
- ```
-
- You can also provide a list of property names whose values are instances of
- `Ember.View`:
-
- ```javascript
- aContainer = Ember.ContainerView.create({
- childViews: ['aView', 'bView', 'cView'],
- aView: Ember.View.create(),
- bView: Ember.View.create(),
- cView: Ember.View.create()
- });
- ```
-
- The two strategies can be combined:
-
- ```javascript
- aContainer = Ember.ContainerView.create({
- childViews: ['aView', Ember.View.create()],
- aView: Ember.View.create()
- });
- ```
-
- Each child view's rendering will be inserted into the container's rendered
- HTML in the same order as its position in the `childViews` property.
-
- ## Adding and Removing Child Views
-
- The container view implements `Ember.MutableArray` allowing programatic management of its child views.
-
- To remove a view, pass that view into a `removeObject` call on the container view.
-
- Given an empty `` the following code
-
- ```javascript
- aContainer = Ember.ContainerView.create({
- classNames: ['the-container'],
- childViews: ['aView', 'bView'],
- aView: Ember.View.create({
- template: Ember.Handlebars.compile("A")
- }),
- bView: Ember.View.create({
- template: Ember.Handlebars.compile("B")
- })
- });
-
- aContainer.appendTo('body');
- ```
-
- Results in the HTML
-
- ```html
-
- ```
-
- Removing a view
-
- ```javascript
- aContainer.toArray(); // [aContainer.aView, aContainer.bView]
- aContainer.removeObject(aContainer.get('bView'));
- aContainer.toArray(); // [aContainer.aView]
- ```
-
- Will result in the following HTML
-
- ```html
-
- ```
-
- Similarly, adding a child view is accomplished by adding `Ember.View` instances to the
- container view.
-
- Given an empty `` the following code
-
- ```javascript
- aContainer = Ember.ContainerView.create({
- classNames: ['the-container'],
- childViews: ['aView', 'bView'],
- aView: Ember.View.create({
- template: Ember.Handlebars.compile("A")
- }),
- bView: Ember.View.create({
- template: Ember.Handlebars.compile("B")
- })
- });
-
- aContainer.appendTo('body');
- ```
-
- Results in the HTML
-
- ```html
-
- ```
-
- Adding a view
-
- ```javascript
- AnotherViewClass = Ember.View.extend({
- template: Ember.Handlebars.compile("Another view")
- });
-
- aContainer.toArray(); // [aContainer.aView, aContainer.bView]
- aContainer.pushObject(AnotherViewClass.create());
- aContainer.toArray(); // [aContainer.aView, aContainer.bView,
]
- ```
-
- Will result in the following HTML
-
- ```html
-
- ```
-
- ## Templates and Layout
-
- A `template`, `templateName`, `defaultTemplate`, `layout`, `layoutName` or
- `defaultLayout` property on a container view will not result in the template
- or layout being rendered. The HTML contents of a `Ember.ContainerView`'s DOM
- representation will only be the rendered HTML of its child views.
-
- @class ContainerView
- @namespace Ember
- @extends Ember.View
-*/
-Ember.ContainerView = Ember.View.extend(Ember.MutableArray, {
- states: states,
-
- init: function() {
- this._super();
-
- var childViews = get(this, 'childViews');
-
- // redefine view's childViews property that was obliterated
- Ember.defineProperty(this, 'childViews', Ember.View.childViewsProperty);
-
- var _childViews = this._childViews;
-
- forEach(childViews, function(viewName, idx) {
- var view;
-
- if ('string' === typeof viewName) {
- view = get(this, viewName);
- view = this.createChildView(view);
- set(this, viewName, view);
- } else {
- view = this.createChildView(viewName);
- }
-
- _childViews[idx] = view;
- }, this);
-
- var currentView = get(this, 'currentView');
- if (currentView) {
- if (!_childViews.length) { _childViews = this._childViews = this._childViews.slice(); }
- _childViews.push(this.createChildView(currentView));
- }
- },
-
- replace: function(idx, removedCount, addedViews) {
- var addedCount = addedViews ? get(addedViews, 'length') : 0;
- var self = this;
- Ember.assert("You can't add a child to a container that is already a child of another view", Ember.A(addedViews).every(function(item) { return !get(item, '_parentView') || get(item, '_parentView') === self; }));
-
- this.arrayContentWillChange(idx, removedCount, addedCount);
- this.childViewsWillChange(this._childViews, idx, removedCount);
-
- if (addedCount === 0) {
- this._childViews.splice(idx, removedCount) ;
- } else {
- var args = [idx, removedCount].concat(addedViews);
- if (addedViews.length && !this._childViews.length) { this._childViews = this._childViews.slice(); }
- this._childViews.splice.apply(this._childViews, args);
- }
-
- this.arrayContentDidChange(idx, removedCount, addedCount);
- this.childViewsDidChange(this._childViews, idx, removedCount, addedCount);
-
- return this;
- },
-
- objectAt: function(idx) {
- return this._childViews[idx];
- },
-
- length: Ember.computed(function () {
- return this._childViews.length;
- }).volatile(),
-
- /**
- @private
-
- Instructs each child view to render to the passed render buffer.
-
- @method render
- @param {Ember.RenderBuffer} buffer the buffer to render to
- */
- render: function(buffer) {
- this.forEachChildView(function(view) {
- view.renderToBuffer(buffer);
- });
- },
-
- instrumentName: 'container',
-
- /**
- @private
-
- When a child view is removed, destroy its element so that
- it is removed from the DOM.
-
- The array observer that triggers this action is set up in the
- `renderToBuffer` method.
-
- @method childViewsWillChange
- @param {Ember.Array} views the child views array before mutation
- @param {Number} start the start position of the mutation
- @param {Number} removed the number of child views removed
- **/
- childViewsWillChange: function(views, start, removed) {
- this.propertyWillChange('childViews');
-
- if (removed > 0) {
- var changedViews = views.slice(start, start+removed);
- // transition to preRender before clearing parentView
- this.currentState.childViewsWillChange(this, views, start, removed);
- this.initializeViews(changedViews, null, null);
- }
- },
-
- removeChild: function(child) {
- this.removeObject(child);
- return this;
- },
-
- /**
- @private
-
- When a child view is added, make sure the DOM gets updated appropriately.
-
- If the view has already rendered an element, we tell the child view to
- create an element and insert it into the DOM. If the enclosing container
- view has already written to a buffer, but not yet converted that buffer
- into an element, we insert the string representation of the child into the
- appropriate place in the buffer.
-
- @method childViewsDidChange
- @param {Ember.Array} views the array of child views afte the mutation has occurred
- @param {Number} start the start position of the mutation
- @param {Number} removed the number of child views removed
- @param {Number} the number of child views added
- */
- childViewsDidChange: function(views, start, removed, added) {
- if (added > 0) {
- var changedViews = views.slice(start, start+added);
- this.initializeViews(changedViews, this, get(this, 'templateData'));
- this.currentState.childViewsDidChange(this, views, start, added);
- }
- this.propertyDidChange('childViews');
- },
-
- initializeViews: function(views, parentView, templateData) {
- forEach(views, function(view) {
- set(view, '_parentView', parentView);
-
- if (!view.container && parentView) {
- set(view, 'container', parentView.container);
- }
-
- if (!get(view, 'templateData')) {
- set(view, 'templateData', templateData);
- }
- });
- },
-
- currentView: null,
-
- _currentViewWillChange: Ember.beforeObserver('currentView', function() {
- var currentView = get(this, 'currentView');
- if (currentView) {
- currentView.destroy();
- }
- }),
-
- _currentViewDidChange: Ember.observer('currentView', function() {
- var currentView = get(this, 'currentView');
- if (currentView) {
- Ember.assert("You tried to set a current view that already has a parent. Make sure you don't have multiple outlets in the same view.", !get(currentView, '_parentView'));
- this.pushObject(currentView);
- }
- }),
-
- _ensureChildrenAreInDOM: function () {
- this.currentState.ensureChildrenAreInDOM(this);
- }
-});
-
-Ember.merge(states._default, {
- childViewsWillChange: Ember.K,
- childViewsDidChange: Ember.K,
- ensureChildrenAreInDOM: Ember.K
-});
-
-Ember.merge(states.inBuffer, {
- childViewsDidChange: function(parentView, views, start, added) {
- throw new Ember.Error('You cannot modify child views while in the inBuffer state');
- }
-});
-
-Ember.merge(states.hasElement, {
- childViewsWillChange: function(view, views, start, removed) {
- for (var i=start; i` and the following code:
-
- ```javascript
- someItemsView = Ember.CollectionView.create({
- classNames: ['a-collection'],
- content: ['A','B','C'],
- itemViewClass: Ember.View.extend({
- template: Ember.Handlebars.compile("the letter: {{view.content}}")
- })
- });
-
- someItemsView.appendTo('body');
- ```
-
- Will result in the following HTML structure
-
- ```html
-
-
the letter: A
-
the letter: B
-
the letter: C
-
- ```
-
- ## Automatic matching of parent/child tagNames
-
- Setting the `tagName` property of a `CollectionView` to any of
- "ul", "ol", "table", "thead", "tbody", "tfoot", "tr", or "select" will result
- in the item views receiving an appropriately matched `tagName` property.
-
- Given an empty `` and the following code:
-
- ```javascript
- anUndorderedListView = Ember.CollectionView.create({
- tagName: 'ul',
- content: ['A','B','C'],
- itemViewClass: Ember.View.extend({
- template: Ember.Handlebars.compile("the letter: {{view.content}}")
- })
- });
-
- anUndorderedListView.appendTo('body');
- ```
-
- Will result in the following HTML structure
-
- ```html
-
- the letter: A
- the letter: B
- the letter: C
-
- ```
-
- Additional `tagName` pairs can be provided by adding to
- `Ember.CollectionView.CONTAINER_MAP `
-
- ```javascript
- Ember.CollectionView.CONTAINER_MAP['article'] = 'section'
- ```
-
- ## Programatic creation of child views
-
- For cases where additional customization beyond the use of a single
- `itemViewClass` or `tagName` matching is required CollectionView's
- `createChildView` method can be overidden:
-
- ```javascript
- CustomCollectionView = Ember.CollectionView.extend({
- createChildView: function(viewClass, attrs) {
- if (attrs.content.kind == 'album') {
- viewClass = App.AlbumView;
- } else {
- viewClass = App.SongView;
- }
- return this._super(viewClass, attrs);
- }
- });
- ```
-
- ## Empty View
-
- You can provide an `Ember.View` subclass to the `Ember.CollectionView`
- instance as its `emptyView` property. If the `content` property of a
- `CollectionView` is set to `null` or an empty array, an instance of this view
- will be the `CollectionView`s only child.
-
- ```javascript
- aListWithNothing = Ember.CollectionView.create({
- classNames: ['nothing']
- content: null,
- emptyView: Ember.View.extend({
- template: Ember.Handlebars.compile("The collection is empty")
- })
- });
-
- aListWithNothing.appendTo('body');
- ```
-
- Will result in the following HTML structure
-
- ```html
-
-
- The collection is empty
-
-
- ```
-
- ## Adding and Removing items
-
- The `childViews` property of a `CollectionView` should not be directly
- manipulated. Instead, add, remove, replace items from its `content` property.
- This will trigger appropriate changes to its rendered HTML.
-
-
- @class CollectionView
- @namespace Ember
- @extends Ember.ContainerView
- @since Ember 0.9
-*/
-Ember.CollectionView = Ember.ContainerView.extend(/** @scope Ember.CollectionView.prototype */ {
-
- /**
- A list of items to be displayed by the `Ember.CollectionView`.
-
- @property content
- @type Ember.Array
- @default null
- */
- content: null,
-
- /**
- @private
-
- This provides metadata about what kind of empty view class this
- collection would like if it is being instantiated from another
- system (like Handlebars)
-
- @property emptyViewClass
- */
- emptyViewClass: Ember.View,
-
- /**
- An optional view to display if content is set to an empty array.
-
- @property emptyView
- @type Ember.View
- @default null
- */
- emptyView: null,
-
- /**
- @property itemViewClass
- @type Ember.View
- @default Ember.View
- */
- itemViewClass: Ember.View,
-
- /**
- Setup a CollectionView
-
- @method init
- */
- init: function() {
- var ret = this._super();
- this._contentDidChange();
- return ret;
- },
-
- /**
- @private
-
- Invoked when the content property is about to change. Notifies observers that the
- entire array content will change.
-
- @method _contentWillChange
- */
- _contentWillChange: Ember.beforeObserver('content', function() {
- var content = this.get('content');
-
- if (content) { content.removeArrayObserver(this); }
- var len = content ? get(content, 'length') : 0;
- this.arrayWillChange(content, 0, len);
- }),
-
- /**
- @private
-
- Check to make sure that the content has changed, and if so,
- update the children directly. This is always scheduled
- asynchronously, to allow the element to be created before
- bindings have synchronized and vice versa.
-
- @method _contentDidChange
- */
- _contentDidChange: Ember.observer('content', function() {
- var content = get(this, 'content');
-
- if (content) {
- this._assertArrayLike(content);
- content.addArrayObserver(this);
- }
-
- var len = content ? get(content, 'length') : 0;
- this.arrayDidChange(content, 0, null, len);
- }),
-
- /**
- @private
-
- Ensure that the content implements Ember.Array
-
- @method _assertArrayLike
- */
- _assertArrayLike: function(content) {
- Ember.assert(fmt("an Ember.CollectionView's content must implement Ember.Array. You passed %@", [content]), Ember.Array.detect(content));
- },
-
- /**
- Removes the content and content observers.
-
- @method destroy
- */
- destroy: function() {
- if (!this._super()) { return; }
-
- var content = get(this, 'content');
- if (content) { content.removeArrayObserver(this); }
-
- if (this._createdEmptyView) {
- this._createdEmptyView.destroy();
- }
-
- return this;
- },
-
- /**
- Called when a mutation to the underlying content array will occur.
-
- This method will remove any views that are no longer in the underlying
- content array.
-
- Invokes whenever the content array itself will change.
-
- @method arrayWillChange
- @param {Array} content the managed collection of objects
- @param {Number} start the index at which the changes will occurr
- @param {Number} removed number of object to be removed from content
- */
- arrayWillChange: function(content, start, removedCount) {
- // If the contents were empty before and this template collection has an
- // empty view remove it now.
- var emptyView = get(this, 'emptyView');
- if (emptyView && emptyView instanceof Ember.View) {
- emptyView.removeFromParent();
- }
-
- // Loop through child views that correspond with the removed items.
- // Note that we loop from the end of the array to the beginning because
- // we are mutating it as we go.
- var childViews = this._childViews, childView, idx, len;
-
- len = this._childViews.length;
-
- var removingAll = removedCount === len;
-
- if (removingAll) {
- this.currentState.empty(this);
- this.invokeRecursively(function(view) {
- view.removedFromDOM = true;
- }, false);
- }
-
- for (idx = start + removedCount - 1; idx >= start; idx--) {
- childView = childViews[idx];
- childView.destroy();
- }
- },
-
- /**
- Called when a mutation to the underlying content array occurs.
-
- This method will replay that mutation against the views that compose the
- `Ember.CollectionView`, ensuring that the view reflects the model.
-
- This array observer is added in `contentDidChange`.
-
- @method arrayDidChange
- @param {Array} content the managed collection of objects
- @param {Number} start the index at which the changes occurred
- @param {Number} removed number of object removed from content
- @param {Number} added number of object added to content
- */
- arrayDidChange: function(content, start, removed, added) {
- var addedViews = [], view, item, idx, len, itemViewClass,
- emptyView;
-
- len = content ? get(content, 'length') : 0;
-
- if (len) {
- itemViewClass = get(this, 'itemViewClass');
-
- if ('string' === typeof itemViewClass) {
- itemViewClass = get(itemViewClass) || itemViewClass;
- }
-
- Ember.assert(fmt("itemViewClass must be a subclass of Ember.View, not %@", [itemViewClass]), 'string' === typeof itemViewClass || Ember.View.detect(itemViewClass));
-
- for (idx = start; idx < start+added; idx++) {
- item = content.objectAt(idx);
-
- view = this.createChildView(itemViewClass, {
- content: item,
- contentIndex: idx
- });
-
- addedViews.push(view);
- }
- } else {
- emptyView = get(this, 'emptyView');
-
- if (!emptyView) { return; }
-
- if ('string' === typeof emptyView) {
- emptyView = get(emptyView) || emptyView;
- }
-
- emptyView = this.createChildView(emptyView);
- addedViews.push(emptyView);
- set(this, 'emptyView', emptyView);
-
- if (Ember.CoreView.detect(emptyView)) {
- this._createdEmptyView = emptyView;
- }
- }
-
- this.replace(start, 0, addedViews);
- },
-
- /**
- Instantiates a view to be added to the childViews array during view
- initialization. You generally will not call this method directly unless
- you are overriding `createChildViews()`. Note that this method will
- automatically configure the correct settings on the new view instance to
- act as a child of the parent.
-
- The tag name for the view will be set to the tagName of the viewClass
- passed in.
-
- @method createChildView
- @param {Class} viewClass
- @param {Hash} [attrs] Attributes to add
- @return {Ember.View} new instance
- */
- createChildView: function(view, attrs) {
- view = this._super(view, attrs);
-
- var itemTagName = get(view, 'tagName');
-
- if (itemTagName === null || itemTagName === undefined) {
- itemTagName = Ember.CollectionView.CONTAINER_MAP[get(this, 'tagName')];
- set(view, 'tagName', itemTagName);
- }
-
- return view;
- }
-});
-
-/**
- A map of parent tags to their default child tags. You can add
- additional parent tags if you want collection views that use
- a particular parent tag to default to a child tag.
-
- @property CONTAINER_MAP
- @type Hash
- @static
- @final
-*/
-Ember.CollectionView.CONTAINER_MAP = {
- ul: 'li',
- ol: 'li',
- table: 'tr',
- thead: 'tr',
- tbody: 'tr',
- tfoot: 'tr',
- tr: 'td',
- select: 'option'
-};
-
-})();
-
-
-
-(function() {
-var get = Ember.get, set = Ember.set, isNone = Ember.isNone,
- a_slice = Array.prototype.slice;
-
-
-/**
-@module ember
-@submodule ember-views
-*/
-
-/**
- An `Ember.Component` is a view that is completely
- isolated. Property access in its templates go
- to the view object and actions are targeted at
- the view object. There is no access to the
- surrounding context or outer controller; all
- contextual information is passed in.
-
- The easiest way to create an `Ember.Component` is via
- a template. If you name a template
- `components/my-foo`, you will be able to use
- `{{my-foo}}` in other templates, which will make
- an instance of the isolated component.
-
- ```html
- {{app-profile person=currentUser}}
- ```
-
- ```html
-
- {{person.title}}
-
- {{person.signature}}
- ```
-
- You can also use `yield` inside a template to
- include the **contents** of the custom tag:
-
- ```html
- {{#app-profile person=currentUser}}
- Admin mode
- {{/app-profile}}
- ```
-
- ```html
-
- {{person.title}}
- {{yield}}
- ```
-
- If you want to customize the component, in order to
- handle events or actions, you implement a subclass
- of `Ember.Component` named after the name of the
- component. Note that `Component` needs to be appended to the name of
- your subclass like `AppProfileComponent`.
-
- For example, you could implement the action
- `hello` for the `app-profile` component:
-
- ```javascript
- App.AppProfileComponent = Ember.Component.extend({
- actions: {
- hello: function(name) {
- console.log("Hello", name);
- }
- }
- });
- ```
-
- And then use it in the component's template:
-
- ```html
-
-
- {{person.title}}
- {{yield}}
-
-
- Say Hello to {{person.name}}
-
- ```
-
- Components must have a `-` in their name to avoid
- conflicts with built-in controls that wrap HTML
- elements. This is consistent with the same
- requirement in web components.
-
- @class Component
- @namespace Ember
- @extends Ember.View
-*/
-Ember.Component = Ember.View.extend(Ember.TargetActionSupport, {
- init: function() {
- this._super();
- set(this, 'context', this);
- set(this, 'controller', this);
- },
-
- // during render, isolate keywords
- cloneKeywords: function() {
- return {
- view: this,
- controller: this
- };
- },
-
- _yield: function(context, options) {
- var view = options.data.view,
- parentView = this._parentView,
- template = get(this, 'template');
-
- if (template) {
- Ember.assert("A Component must have a parent view in order to yield.", parentView);
-
- view.appendChild(Ember.View, {
- isVirtual: true,
- tagName: '',
- _contextView: parentView,
- template: template,
- context: get(parentView, 'context'),
- controller: get(parentView, 'controller'),
- templateData: { keywords: parentView.cloneKeywords() }
- });
- }
- },
-
- /**
- If the component is currently inserted into the DOM of a parent view, this
- property will point to the controller of the parent view.
-
- @property targetObject
- @type Ember.Controller
- @default null
- */
- targetObject: Ember.computed(function(key) {
- var parentView = get(this, '_parentView');
- return parentView ? get(parentView, 'controller') : null;
- }).property('_parentView'),
-
- /**
- Triggers a named action on the controller context where the component is used if
- this controller has registered for notifications of the action.
-
- For example a component for playing or pausing music may translate click events
- into action notifications of "play" or "stop" depending on some internal state
- of the component:
-
-
- ```javascript
- App.PlayButtonComponent = Ember.Component.extend({
- click: function(){
- if (this.get('isPlaying')) {
- this.triggerAction('play');
- } else {
- this.triggerAction('stop');
- }
- }
- });
- ```
-
- When used inside a template these component actions are configured to
- trigger actions in the outer application context:
-
- ```handlebars
- {{! application.hbs }}
- {{play-button play="musicStarted" stop="musicStopped"}}
- ```
-
- When the component receives a browser `click` event it translate this
- interaction into application-specific semantics ("play" or "stop") and
- triggers the specified action name on the controller for the template
- where the component is used:
-
-
- ```javascript
- App.ApplicationController = Ember.Controller.extend({
- actions: {
- musicStarted: function(){
- // called when the play button is clicked
- // and the music started playing
- },
- musicStopped: function(){
- // called when the play button is clicked
- // and the music stopped playing
- }
- }
- });
- ```
-
- If no action name is passed to `sendAction` a default name of "action"
- is assumed.
-
- ```javascript
- App.NextButtonComponent = Ember.Component.extend({
- click: function(){
- this.sendAction();
- }
- });
- ```
-
- ```handlebars
- {{! application.hbs }}
- {{next-button action="playNextSongInAlbum"}}
- ```
-
- ```javascript
- App.ApplicationController = Ember.Controller.extend({
- actions: {
- playNextSongInAlbum: function(){
- ...
- }
- }
- });
- ```
-
- @method sendAction
- @param [action] {String} the action to trigger
- @param [context] {*} a context to send with the action
- */
- sendAction: function(action) {
- var actionName,
- contexts = a_slice.call(arguments, 1);
-
- // Send the default action
- if (action === undefined) {
- actionName = get(this, 'action');
- Ember.assert("The default action was triggered on the component " + this.toString() + ", but the action name (" + actionName + ") was not a string.", isNone(actionName) || typeof actionName === 'string');
- } else {
- actionName = get(this, action);
- Ember.assert("The " + action + " action was triggered on the component " + this.toString() + ", but the action name (" + actionName + ") was not a string.", isNone(actionName) || typeof actionName === 'string');
- }
-
- // If no action name for that action could be found, just abort.
- if (actionName === undefined) { return; }
-
- this.triggerAction({
- action: actionName,
- actionContext: contexts
- });
- }
-});
-
-})();
-
-
-
-(function() {
-
-})();
-
-
-
-(function() {
-/**
-`Ember.ViewTargetActionSupport` is a mixin that can be included in a
-view class to add a `triggerAction` method with semantics similar to
-the Handlebars `{{action}}` helper. It provides intelligent defaults
-for the action's target: the view's controller; and the context that is
-sent with the action: the view's context.
-
-Note: In normal Ember usage, the `{{action}}` helper is usually the best
-choice. This mixin is most often useful when you are doing more complex
-event handling in custom View subclasses.
-
-For example:
-
-```javascript
-App.SaveButtonView = Ember.View.extend(Ember.ViewTargetActionSupport, {
- action: 'save',
- click: function() {
- this.triggerAction(); // Sends the `save` action, along with the current context
- // to the current controller
- }
-});
-```
-
-The `action` can be provided as properties of an optional object argument
-to `triggerAction` as well.
-
-```javascript
-App.SaveButtonView = Ember.View.extend(Ember.ViewTargetActionSupport, {
- click: function() {
- this.triggerAction({
- action: 'save'
- }); // Sends the `save` action, along with the current context
- // to the current controller
- }
-});
-```
-
-@class ViewTargetActionSupport
-@namespace Ember
-@extends Ember.TargetActionSupport
-*/
-Ember.ViewTargetActionSupport = Ember.Mixin.create(Ember.TargetActionSupport, {
- /**
- @property target
- */
- target: Ember.computed.alias('controller'),
- /**
- @property actionContext
- */
- actionContext: Ember.computed.alias('context')
-});
-
-})();
-
-
-
-(function() {
-
-})();
-
-
-
-(function() {
-/**
-Ember Views
-
-@module ember
-@submodule ember-views
-@requires ember-runtime
-@main ember-views
-*/
-
-})();
-
-(function() {
-define("metamorph",
- [],
- function() {
- "use strict";
- // ==========================================================================
- // Project: metamorph
- // Copyright: ©2011 My Company Inc. All rights reserved.
- // ==========================================================================
-
- var K = function() {},
- guid = 0,
- document = this.document,
- disableRange = ('undefined' === typeof ENV ? {} : ENV).DISABLE_RANGE_API,
-
- // Feature-detect the W3C range API, the extended check is for IE9 which only partially supports ranges
- supportsRange = (!disableRange) && document && ('createRange' in document) && (typeof Range !== 'undefined') && Range.prototype.createContextualFragment,
-
- // Internet Explorer prior to 9 does not allow setting innerHTML if the first element
- // is a "zero-scope" element. This problem can be worked around by making
- // the first node an invisible text node. We, like Modernizr, use
- needsShy = document && (function() {
- var testEl = document.createElement('div');
- testEl.innerHTML = "
";
- testEl.firstChild.innerHTML = "";
- return testEl.firstChild.innerHTML === '';
- })(),
-
-
- // IE 8 (and likely earlier) likes to move whitespace preceeding
- // a script tag to appear after it. This means that we can
- // accidentally remove whitespace when updating a morph.
- movesWhitespace = document && (function() {
- var testEl = document.createElement('div');
- testEl.innerHTML = "Test: Value";
- return testEl.childNodes[0].nodeValue === 'Test:' &&
- testEl.childNodes[2].nodeValue === ' Value';
- })();
-
- // Constructor that supports either Metamorph('foo') or new
- // Metamorph('foo');
- //
- // Takes a string of HTML as the argument.
-
- var Metamorph = function(html) {
- var self;
-
- if (this instanceof Metamorph) {
- self = this;
- } else {
- self = new K();
- }
-
- self.innerHTML = html;
- var myGuid = 'metamorph-'+(guid++);
- self.start = myGuid + '-start';
- self.end = myGuid + '-end';
-
- return self;
- };
-
- K.prototype = Metamorph.prototype;
-
- var rangeFor, htmlFunc, removeFunc, outerHTMLFunc, appendToFunc, afterFunc, prependFunc, startTagFunc, endTagFunc;
-
- outerHTMLFunc = function() {
- return this.startTag() + this.innerHTML + this.endTag();
- };
-
- startTagFunc = function() {
- /*
- * We replace chevron by its hex code in order to prevent escaping problems.
- * Check this thread for more explaination:
- * http://stackoverflow.com/questions/8231048/why-use-x3c-instead-of-when-generating-html-from-javascript
- */
- return "hi ";
- * div.firstChild.firstChild.tagName //=> ""
- *
- * If our script markers are inside such a node, we need to find that
- * node and use *it* as the marker.
- */
- var realNode = function(start) {
- while (start.parentNode.tagName === "") {
- start = start.parentNode;
- }
-
- return start;
- };
-
- /*
- * When automatically adding a tbody, Internet Explorer inserts the
- * tbody immediately before the first . Other browsers create it
- * before the first node, no matter what.
- *
- * This means the the following code:
- *
- * div = document.createElement("div");
- * div.innerHTML = "
- *
- * Generates the following DOM in IE:
- *
- * + div
- * + table
- * - script id='first'
- * + tbody
- * + tr
- * + td
- * - "hi"
- * - script id='last'
- *
- * Which means that the two script tags, even though they were
- * inserted at the same point in the hierarchy in the original
- * HTML, now have different parents.
- *
- * This code reparents the first script tag by making it the tbody's
- * first child.
- *
- */
- var fixParentage = function(start, end) {
- if (start.parentNode !== end.parentNode) {
- end.parentNode.insertBefore(start, end.parentNode.firstChild);
- }
- };
-
- htmlFunc = function(html, outerToo) {
- // get the real starting node. see realNode for details.
- var start = realNode(document.getElementById(this.start));
- var end = document.getElementById(this.end);
- var parentNode = end.parentNode;
- var node, nextSibling, last;
-
- // make sure that the start and end nodes share the same
- // parent. If not, fix it.
- fixParentage(start, end);
-
- // remove all of the nodes after the starting placeholder and
- // before the ending placeholder.
- node = start.nextSibling;
- while (node) {
- nextSibling = node.nextSibling;
- last = node === end;
-
- // if this is the last node, and we want to remove it as well,
- // set the `end` node to the next sibling. This is because
- // for the rest of the function, we insert the new nodes
- // before the end (note that insertBefore(node, null) is
- // the same as appendChild(node)).
- //
- // if we do not want to remove it, just break.
- if (last) {
- if (outerToo) { end = node.nextSibling; } else { break; }
- }
-
- node.parentNode.removeChild(node);
-
- // if this is the last node and we didn't break before
- // (because we wanted to remove the outer nodes), break
- // now.
- if (last) { break; }
-
- node = nextSibling;
- }
-
- // get the first node for the HTML string, even in cases like
- // tables and lists where a simple innerHTML on a div would
- // swallow some of the content.
- node = firstNodeFor(start.parentNode, html);
-
- // copy the nodes for the HTML between the starting and ending
- // placeholder.
- while (node) {
- nextSibling = node.nextSibling;
- parentNode.insertBefore(node, end);
- node = nextSibling;
- }
- };
-
- // remove the nodes in the DOM representing this metamorph.
- //
- // this includes the starting and ending placeholders.
- removeFunc = function() {
- var start = realNode(document.getElementById(this.start));
- var end = document.getElementById(this.end);
-
- this.html('');
- start.parentNode.removeChild(start);
- end.parentNode.removeChild(end);
- };
-
- appendToFunc = function(parentNode) {
- var node = firstNodeFor(parentNode, this.outerHTML());
- var nextSibling;
-
- while (node) {
- nextSibling = node.nextSibling;
- parentNode.appendChild(node);
- node = nextSibling;
- }
- };
-
- afterFunc = function(html) {
- // get the real starting node. see realNode for details.
- var end = document.getElementById(this.end);
- var insertBefore = end.nextSibling;
- var parentNode = end.parentNode;
- var nextSibling;
- var node;
-
- // get the first node for the HTML string, even in cases like
- // tables and lists where a simple innerHTML on a div would
- // swallow some of the content.
- node = firstNodeFor(parentNode, html);
-
- // copy the nodes for the HTML between the starting and ending
- // placeholder.
- while (node) {
- nextSibling = node.nextSibling;
- parentNode.insertBefore(node, insertBefore);
- node = nextSibling;
- }
- };
-
- prependFunc = function(html) {
- var start = document.getElementById(this.start);
- var parentNode = start.parentNode;
- var nextSibling;
- var node;
-
- node = firstNodeFor(parentNode, html);
- var insertBefore = start.nextSibling;
-
- while (node) {
- nextSibling = node.nextSibling;
- parentNode.insertBefore(node, insertBefore);
- node = nextSibling;
- }
- };
- }
-
- Metamorph.prototype.html = function(html) {
- this.checkRemoved();
- if (html === undefined) { return this.innerHTML; }
-
- htmlFunc.call(this, html);
-
- this.innerHTML = html;
- };
-
- Metamorph.prototype.replaceWith = function(html) {
- this.checkRemoved();
- htmlFunc.call(this, html, true);
- };
-
- Metamorph.prototype.remove = removeFunc;
- Metamorph.prototype.outerHTML = outerHTMLFunc;
- Metamorph.prototype.appendTo = appendToFunc;
- Metamorph.prototype.after = afterFunc;
- Metamorph.prototype.prepend = prependFunc;
- Metamorph.prototype.startTag = startTagFunc;
- Metamorph.prototype.endTag = endTagFunc;
-
- Metamorph.prototype.isRemoved = function() {
- var before = document.getElementById(this.start);
- var after = document.getElementById(this.end);
-
- return !before || !after;
- };
-
- Metamorph.prototype.checkRemoved = function() {
- if (this.isRemoved()) {
- throw new Error("Cannot perform operations on a Metamorph that is not in the DOM.");
- }
- };
-
- return Metamorph;
- });
-
-})();
-
-(function() {
-/**
-@module ember
-@submodule ember-handlebars-compiler
-*/
-
-// Eliminate dependency on any Ember to simplify precompilation workflow
-var objectCreate = Object.create || function(parent) {
- function F() {}
- F.prototype = parent;
- return new F();
-};
-
-var Handlebars = this.Handlebars || (Ember.imports && Ember.imports.Handlebars);
-if (!Handlebars && typeof require === 'function') {
- Handlebars = require('handlebars');
-}
-
-Ember.assert("Ember Handlebars requires Handlebars version 1.0 or 1.1. Include a SCRIPT tag in the HTML HEAD linking to the Handlebars file before you link to Ember.", Handlebars);
-Ember.assert("Ember Handlebars requires Handlebars version 1.0 or 1.1, COMPILER_REVISION expected: 4, got: " + Handlebars.COMPILER_REVISION + " - Please note: Builds of master may have other COMPILER_REVISION values.", Handlebars.COMPILER_REVISION === 4);
-
-/**
- Prepares the Handlebars templating library for use inside Ember's view
- system.
-
- The `Ember.Handlebars` object is the standard Handlebars library, extended to
- use Ember's `get()` method instead of direct property access, which allows
- computed properties to be used inside templates.
-
- To create an `Ember.Handlebars` template, call `Ember.Handlebars.compile()`.
- This will return a function that can be used by `Ember.View` for rendering.
-
- @class Handlebars
- @namespace Ember
-*/
-Ember.Handlebars = objectCreate(Handlebars);
-
-/**
- Register a bound helper or custom view helper.
-
- ## Simple bound helper example
-
- ```javascript
- Ember.Handlebars.helper('capitalize', function(value) {
- return value.toUpperCase();
- });
- ```
-
- The above bound helper can be used inside of templates as follows:
-
- ```handlebars
- {{capitalize name}}
- ```
-
- In this case, when the `name` property of the template's context changes,
- the rendered value of the helper will update to reflect this change.
-
- For more examples of bound helpers, see documentation for
- `Ember.Handlebars.registerBoundHelper`.
-
- ## Custom view helper example
-
- Assuming a view subclass named `App.CalendarView` were defined, a helper
- for rendering instances of this view could be registered as follows:
-
- ```javascript
- Ember.Handlebars.helper('calendar', App.CalendarView):
- ```
-
- The above bound helper can be used inside of templates as follows:
-
- ```handlebars
- {{calendar}}
- ```
-
- Which is functionally equivalent to:
-
- ```handlebars
- {{view App.CalendarView}}
- ```
-
- Options in the helper will be passed to the view in exactly the same
- manner as with the `view` helper.
-
- @method helper
- @for Ember.Handlebars
- @param {String} name
- @param {Function|Ember.View} function or view class constructor
- @param {String} dependentKeys*
-*/
-Ember.Handlebars.helper = function(name, value) {
- Ember.assert("You tried to register a component named '" + name + "', but component names must include a '-'", !Ember.Component.detect(value) || name.match(/-/));
-
- if (Ember.View.detect(value)) {
- Ember.Handlebars.registerHelper(name, Ember.Handlebars.makeViewHelper(value));
- } else {
- Ember.Handlebars.registerBoundHelper.apply(null, arguments);
- }
-};
-
-/**
- @private
-
- Returns a helper function that renders the provided ViewClass.
-
- Used internally by Ember.Handlebars.helper and other methods
- involving helper/component registration.
-
- @method helper
- @for Ember.Handlebars
- @param {Function} ViewClass view class constructor
-*/
-Ember.Handlebars.makeViewHelper = function(ViewClass) {
- return function(options) {
- Ember.assert("You can only pass attributes (such as name=value) not bare values to a helper for a View", arguments.length < 2);
- return Ember.Handlebars.helpers.view.call(this, ViewClass, options);
- };
-};
-
-/**
-@class helpers
-@namespace Ember.Handlebars
-*/
-Ember.Handlebars.helpers = objectCreate(Handlebars.helpers);
-
-/**
- Override the the opcode compiler and JavaScript compiler for Handlebars.
-
- @class Compiler
- @namespace Ember.Handlebars
- @private
- @constructor
-*/
-Ember.Handlebars.Compiler = function() {};
-
-// Handlebars.Compiler doesn't exist in runtime-only
-if (Handlebars.Compiler) {
- Ember.Handlebars.Compiler.prototype = objectCreate(Handlebars.Compiler.prototype);
-}
-
-Ember.Handlebars.Compiler.prototype.compiler = Ember.Handlebars.Compiler;
-
-/**
- @class JavaScriptCompiler
- @namespace Ember.Handlebars
- @private
- @constructor
-*/
-Ember.Handlebars.JavaScriptCompiler = function() {};
-
-// Handlebars.JavaScriptCompiler doesn't exist in runtime-only
-if (Handlebars.JavaScriptCompiler) {
- Ember.Handlebars.JavaScriptCompiler.prototype = objectCreate(Handlebars.JavaScriptCompiler.prototype);
- Ember.Handlebars.JavaScriptCompiler.prototype.compiler = Ember.Handlebars.JavaScriptCompiler;
-}
-
-
-Ember.Handlebars.JavaScriptCompiler.prototype.namespace = "Ember.Handlebars";
-
-Ember.Handlebars.JavaScriptCompiler.prototype.initializeBuffer = function() {
- return "''";
-};
-
-/**
- @private
-
- Override the default buffer for Ember Handlebars. By default, Handlebars
- creates an empty String at the beginning of each invocation and appends to
- it. Ember's Handlebars overrides this to append to a single shared buffer.
-
- @method appendToBuffer
- @param string {String}
-*/
-Ember.Handlebars.JavaScriptCompiler.prototype.appendToBuffer = function(string) {
- return "data.buffer.push("+string+");";
-};
-
-// Hacks ahead:
-// Handlebars presently has a bug where the `blockHelperMissing` hook
-// doesn't get passed the name of the missing helper name, but rather
-// gets passed the value of that missing helper evaluated on the current
-// context, which is most likely `undefined` and totally useless.
-//
-// So we alter the compiled template function to pass the name of the helper
-// instead, as expected.
-//
-// This can go away once the following is closed:
-// https://github.com/wycats/handlebars.js/issues/617
-
-var DOT_LOOKUP_REGEX = /helpers\.(.*?)\)/,
- BRACKET_STRING_LOOKUP_REGEX = /helpers\['(.*?)'/,
- INVOCATION_SPLITTING_REGEX = /(.*blockHelperMissing\.call\(.*)(stack[0-9]+)(,.*)/;
-
-Ember.Handlebars.JavaScriptCompiler.stringifyLastBlockHelperMissingInvocation = function(source) {
- var helperInvocation = source[source.length - 1],
- helperName = (DOT_LOOKUP_REGEX.exec(helperInvocation) || BRACKET_STRING_LOOKUP_REGEX.exec(helperInvocation))[1],
- matches = INVOCATION_SPLITTING_REGEX.exec(helperInvocation);
-
- source[source.length - 1] = matches[1] + "'" + helperName + "'" + matches[3];
-}
-var stringifyBlockHelperMissing = Ember.Handlebars.JavaScriptCompiler.stringifyLastBlockHelperMissingInvocation;
-
-var originalBlockValue = Ember.Handlebars.JavaScriptCompiler.prototype.blockValue;
-Ember.Handlebars.JavaScriptCompiler.prototype.blockValue = function() {
- originalBlockValue.apply(this, arguments);
- stringifyBlockHelperMissing(this.source);
-};
-
-var originalAmbiguousBlockValue = Ember.Handlebars.JavaScriptCompiler.prototype.ambiguousBlockValue;
-Ember.Handlebars.JavaScriptCompiler.prototype.ambiguousBlockValue = function() {
- originalAmbiguousBlockValue.apply(this, arguments);
- stringifyBlockHelperMissing(this.source);
-};
-
-var prefix = "ember" + (+new Date()), incr = 1;
-
-/**
- @private
-
- Rewrite simple mustaches from `{{foo}}` to `{{bind "foo"}}`. This means that
- all simple mustaches in Ember's Handlebars will also set up an observer to
- keep the DOM up to date when the underlying property changes.
-
- @method mustache
- @for Ember.Handlebars.Compiler
- @param mustache
-*/
-Ember.Handlebars.Compiler.prototype.mustache = function(mustache) {
- if (mustache.isHelper && mustache.id.string === 'control') {
- mustache.hash = mustache.hash || new Handlebars.AST.HashNode([]);
- mustache.hash.pairs.push(["controlID", new Handlebars.AST.StringNode(prefix + incr++)]);
- } else if (mustache.params.length || mustache.hash) {
- // no changes required
- } else {
- var id = new Handlebars.AST.IdNode([{ part: '_triageMustache' }]);
-
- // Update the mustache node to include a hash value indicating whether the original node
- // was escaped. This will allow us to properly escape values when the underlying value
- // changes and we need to re-render the value.
- if (!mustache.escaped) {
- mustache.hash = mustache.hash || new Handlebars.AST.HashNode([]);
- mustache.hash.pairs.push(["unescaped", new Handlebars.AST.StringNode("true")]);
- }
- mustache = new Handlebars.AST.MustacheNode([id].concat([mustache.id]), mustache.hash, !mustache.escaped);
- }
-
- return Handlebars.Compiler.prototype.mustache.call(this, mustache);
-};
-
-/**
- Used for precompilation of Ember Handlebars templates. This will not be used
- during normal app execution.
-
- @method precompile
- @for Ember.Handlebars
- @static
- @param {String} string The template to precompile
-*/
-Ember.Handlebars.precompile = function(string) {
- var ast = Handlebars.parse(string);
-
- var options = {
- knownHelpers: {
- action: true,
- unbound: true,
- bindAttr: true,
- template: true,
- view: true,
- _triageMustache: true
- },
- data: true,
- stringParams: true
- };
-
- var environment = new Ember.Handlebars.Compiler().compile(ast, options);
- return new Ember.Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
-};
-
-// We don't support this for Handlebars runtime-only
-if (Handlebars.compile) {
- /**
- The entry point for Ember Handlebars. This replaces the default
- `Handlebars.compile` and turns on template-local data and String
- parameters.
-
- @method compile
- @for Ember.Handlebars
- @static
- @param {String} string The template to compile
- @return {Function}
- */
- Ember.Handlebars.compile = function(string) {
- var ast = Handlebars.parse(string);
- var options = { data: true, stringParams: true };
- var environment = new Ember.Handlebars.Compiler().compile(ast, options);
- var templateSpec = new Ember.Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
-
- var template = Ember.Handlebars.template(templateSpec);
- template.isMethod = false; //Make sure we don't wrap templates with ._super
-
- return template;
- };
-}
-
-
-})();
-
-(function() {
-var slice = Array.prototype.slice,
- originalTemplate = Ember.Handlebars.template;
-
-/**
- @private
-
- If a path starts with a reserved keyword, returns the root
- that should be used.
-
- @method normalizePath
- @for Ember
- @param root {Object}
- @param path {String}
- @param data {Hash}
-*/
-var normalizePath = Ember.Handlebars.normalizePath = function(root, path, data) {
- var keywords = (data && data.keywords) || {},
- keyword, isKeyword;
-
- // Get the first segment of the path. For example, if the
- // path is "foo.bar.baz", returns "foo".
- keyword = path.split('.', 1)[0];
-
- // Test to see if the first path is a keyword that has been
- // passed along in the view's data hash. If so, we will treat
- // that object as the new root.
- if (keywords.hasOwnProperty(keyword)) {
- // Look up the value in the template's data hash.
- root = keywords[keyword];
- isKeyword = true;
-
- // Handle cases where the entire path is the reserved
- // word. In that case, return the object itself.
- if (path === keyword) {
- path = '';
- } else {
- // Strip the keyword from the path and look up
- // the remainder from the newly found root.
- path = path.substr(keyword.length+1);
- }
- }
-
- return { root: root, path: path, isKeyword: isKeyword };
-};
-
-
-/**
- Lookup both on root and on window. If the path starts with
- a keyword, the corresponding object will be looked up in the
- template's data hash and used to resolve the path.
-
- @method get
- @for Ember.Handlebars
- @param {Object} root The object to look up the property on
- @param {String} path The path to be lookedup
- @param {Object} options The template's option hash
-*/
-var handlebarsGet = Ember.Handlebars.get = function(root, path, options) {
- var data = options && options.data,
- normalizedPath = normalizePath(root, path, data),
- value;
-
- // In cases where the path begins with a keyword, change the
- // root to the value represented by that keyword, and ensure
- // the path is relative to it.
- root = normalizedPath.root;
- path = normalizedPath.path;
-
- value = Ember.get(root, path);
-
- // If the path starts with a capital letter, look it up on Ember.lookup,
- // which defaults to the `window` object in browsers.
- if (value === undefined && root !== Ember.lookup && Ember.isGlobalPath(path)) {
- value = Ember.get(Ember.lookup, path);
- }
- return value;
-};
-
-Ember.Handlebars.resolveParams = function(context, params, options) {
- var resolvedParams = [], types = options.types, param, type;
-
- for (var i=0, l=params.length; isomeString')
- ```
-
- @method htmlSafe
- @for Ember.String
- @static
- @return {Handlebars.SafeString} a string that will not be html escaped by Handlebars
-*/
-Ember.String.htmlSafe = function(str) {
- return new Handlebars.SafeString(str);
-};
-
-var htmlSafe = Ember.String.htmlSafe;
-
-if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) {
-
- /**
- Mark a string as being safe for unescaped output with Handlebars.
-
- ```javascript
- 'someString
'.htmlSafe()
- ```
-
- See [Ember.String.htmlSafe](/api/classes/Ember.String.html#method_htmlSafe).
-
- @method htmlSafe
- @for String
- @return {Handlebars.SafeString} a string that will not be html escaped by Handlebars
- */
- String.prototype.htmlSafe = function() {
- return htmlSafe(this);
- };
-}
-
-})();
-
-
-
-(function() {
-Ember.Handlebars.resolvePaths = function(options) {
- var ret = [],
- contexts = options.contexts,
- roots = options.roots,
- data = options.data;
-
- for (var i=0, l=contexts.length; i
- ```
-
- The above handlebars template will fill the ` `'s `src` attribute will
- the value of the property referenced with `"imageUrl"` and its `alt`
- attribute with the value of the property referenced with `"imageTitle"`.
-
- If the rendering context of this template is the following object:
-
- ```javascript
- {
- imageUrl: 'http://lolcats.info/haz-a-funny',
- imageTitle: 'A humorous image of a cat'
- }
- ```
-
- The resulting HTML output will be:
-
- ```html
-
- ```
-
- `bind-attr` cannot redeclare existing DOM element attributes. The use of `src`
- in the following `bind-attr` example will be ignored and the hard coded value
- of `src="/failwhale.gif"` will take precedence:
-
- ```handlebars
-
- ```
-
- ### `bind-attr` and the `class` attribute
-
- `bind-attr` supports a special syntax for handling a number of cases unique
- to the `class` DOM element attribute. The `class` attribute combines
- multiple discrete values into a single attribute as a space-delimited
- list of strings. Each string can be:
-
- * a string return value of an object's property.
- * a boolean return value of an object's property
- * a hard-coded value
-
- A string return value works identically to other uses of `bind-attr`. The
- return value of the property will become the value of the attribute. For
- example, the following view and template:
-
- ```javascript
- AView = Ember.View.extend({
- someProperty: function() {
- return "aValue";
- }.property()
- })
- ```
-
- ```handlebars
-
- ```
-
- A boolean return value will insert a specified class name if the property
- returns `true` and remove the class name if the property returns `false`.
-
- A class name is provided via the syntax
- `somePropertyName:class-name-if-true`.
-
- ```javascript
- AView = Ember.View.extend({
- someBool: true
- })
- ```
-
- ```handlebars
-
- ```
-
- Result in the following rendered output:
-
- ```html
-
- ```
-
- An additional section of the binding can be provided if you want to
- replace the existing class instead of removing it when the boolean
- value changes:
-
- ```handlebars
-
- ```
-
- A hard-coded value can be used by prepending `:` to the desired
- class name: `:class-name-to-always-apply`.
-
- ```handlebars
-
- ```
-
- Results in the following rendered output:
-
- ```html
-
- ```
-
- All three strategies - string return value, boolean return value, and
- hard-coded value – can be combined in a single declaration:
-
- ```handlebars
-
- ```
-
- @method bind-attr
- @for Ember.Handlebars.helpers
- @param {Hash} options
- @return {String} HTML string
-*/
-EmberHandlebars.registerHelper('bind-attr', function(options) {
-
- var attrs = options.hash;
-
- Ember.assert("You must specify at least one hash argument to bind-attr", !!Ember.keys(attrs).length);
-
- var view = options.data.view;
- var ret = [];
- var ctx = this;
-
- // Generate a unique id for this element. This will be added as a
- // data attribute to the element so it can be looked up when
- // the bound property changes.
- var dataId = ++Ember.uuid;
-
- // Handle classes differently, as we can bind multiple classes
- var classBindings = attrs['class'];
- if (classBindings != null) {
- var classResults = EmberHandlebars.bindClasses(this, classBindings, view, dataId, options);
-
- ret.push('class="' + Handlebars.Utils.escapeExpression(classResults.join(' ')) + '"');
- delete attrs['class'];
- }
-
- var attrKeys = Ember.keys(attrs);
-
- // For each attribute passed, create an observer and emit the
- // current value of the property as an attribute.
- forEach.call(attrKeys, function(attr) {
- var path = attrs[attr],
- normalized;
-
- Ember.assert(fmt("You must provide an expression as the value of bound attribute. You specified: %@=%@", [attr, path]), typeof path === 'string');
-
- normalized = normalizePath(ctx, path, options.data);
-
- var value = (path === 'this') ? normalized.root : handlebarsGet(ctx, path, options),
- type = Ember.typeOf(value);
-
- Ember.assert(fmt("Attributes must be numbers, strings or booleans, not %@", [value]), value === null || value === undefined || type === 'number' || type === 'string' || type === 'boolean');
-
- var observer, invoker;
-
- observer = function observer() {
- var result = handlebarsGet(ctx, path, options);
-
- Ember.assert(fmt("Attributes must be numbers, strings or booleans, not %@", [result]), result === null || result === undefined || typeof result === 'number' || typeof result === 'string' || typeof result === 'boolean');
-
- var elem = view.$("[data-bindattr-" + dataId + "='" + dataId + "']");
-
- // If we aren't able to find the element, it means the element
- // to which we were bound has been removed from the view.
- // In that case, we can assume the template has been re-rendered
- // and we need to clean up the observer.
- if (!elem || elem.length === 0) {
- Ember.removeObserver(normalized.root, normalized.path, invoker);
- return;
- }
-
- Ember.View.applyAttributeBindings(elem, attr, result);
- };
-
- // Add an observer to the view for when the property changes.
- // When the observer fires, find the element using the
- // unique data id and update the attribute to the new value.
- // Note: don't add observer when path is 'this' or path
- // is whole keyword e.g. {{#each x in list}} ... {{bind-attr attr="x"}}
- if (path !== 'this' && !(normalized.isKeyword && normalized.path === '' )) {
- view.registerObserver(normalized.root, normalized.path, observer);
- }
-
- // if this changes, also change the logic in ember-views/lib/views/view.js
- if ((type === 'string' || (type === 'number' && !isNaN(value)))) {
- ret.push(attr + '="' + Handlebars.Utils.escapeExpression(value) + '"');
- } else if (value && type === 'boolean') {
- // The developer controls the attr name, so it should always be safe
- ret.push(attr + '="' + attr + '"');
- }
- }, this);
-
- // Add the unique identifier
- // NOTE: We use all lower-case since Firefox has problems with mixed case in SVG
- ret.push('data-bindattr-' + dataId + '="' + dataId + '"');
- return new EmberHandlebars.SafeString(ret.join(' '));
-});
-
-/**
- See `bind-attr`
-
- @method bindAttr
- @for Ember.Handlebars.helpers
- @deprecated
- @param {Function} context
- @param {Hash} options
- @return {String} HTML string
-*/
-EmberHandlebars.registerHelper('bindAttr', EmberHandlebars.helpers['bind-attr']);
-
-/**
- @private
-
- Helper that, given a space-separated string of property paths and a context,
- returns an array of class names. Calling this method also has the side
- effect of setting up observers at those property paths, such that if they
- change, the correct class name will be reapplied to the DOM element.
-
- For example, if you pass the string "fooBar", it will first look up the
- "fooBar" value of the context. If that value is true, it will add the
- "foo-bar" class to the current element (i.e., the dasherized form of
- "fooBar"). If the value is a string, it will add that string as the class.
- Otherwise, it will not add any new class name.
-
- @method bindClasses
- @for Ember.Handlebars
- @param {Ember.Object} context The context from which to lookup properties
- @param {String} classBindings A string, space-separated, of class bindings
- to use
- @param {Ember.View} view The view in which observers should look for the
- element to update
- @param {Srting} bindAttrId Optional bindAttr id used to lookup elements
- @return {Array} An array of class names to add
-*/
-EmberHandlebars.bindClasses = function(context, classBindings, view, bindAttrId, options) {
- var ret = [], newClass, value, elem;
-
- // Helper method to retrieve the property from the context and
- // determine which class string to return, based on whether it is
- // a Boolean or not.
- var classStringForPath = function(root, parsedPath, options) {
- var val,
- path = parsedPath.path;
-
- if (path === 'this') {
- val = root;
- } else if (path === '') {
- val = true;
- } else {
- val = handlebarsGet(root, path, options);
- }
-
- return Ember.View._classStringForValue(path, val, parsedPath.className, parsedPath.falsyClassName);
- };
-
- // For each property passed, loop through and setup
- // an observer.
- forEach.call(classBindings.split(' '), function(binding) {
-
- // Variable in which the old class value is saved. The observer function
- // closes over this variable, so it knows which string to remove when
- // the property changes.
- var oldClass;
-
- var observer, invoker;
-
- var parsedPath = Ember.View._parsePropertyPath(binding),
- path = parsedPath.path,
- pathRoot = context,
- normalized;
-
- if (path !== '' && path !== 'this') {
- normalized = normalizePath(context, path, options.data);
-
- pathRoot = normalized.root;
- path = normalized.path;
- }
-
- // Set up an observer on the context. If the property changes, toggle the
- // class name.
- observer = function() {
- // Get the current value of the property
- newClass = classStringForPath(context, parsedPath, options);
- elem = bindAttrId ? view.$("[data-bindattr-" + bindAttrId + "='" + bindAttrId + "']") : view.$();
-
- // If we can't find the element anymore, a parent template has been
- // re-rendered and we've been nuked. Remove the observer.
- if (!elem || elem.length === 0) {
- Ember.removeObserver(pathRoot, path, invoker);
- } else {
- // If we had previously added a class to the element, remove it.
- if (oldClass) {
- elem.removeClass(oldClass);
- }
-
- // If necessary, add a new class. Make sure we keep track of it so
- // it can be removed in the future.
- if (newClass) {
- elem.addClass(newClass);
- oldClass = newClass;
- } else {
- oldClass = null;
- }
- }
- };
-
- if (path !== '' && path !== 'this') {
- view.registerObserver(pathRoot, path, observer);
- }
-
- // We've already setup the observer; now we just need to figure out the
- // correct behavior right now on the first pass through.
- value = classStringForPath(context, parsedPath, options);
-
- if (value) {
- ret.push(value);
-
- // Make sure we save the current value so that it can be removed if the
- // observer fires.
- oldClass = value;
- }
- });
-
- return ret;
-};
-
-
-})();
-
-
-
-(function() {
-/*globals Handlebars */
-
-// TODO: Don't require the entire module
-/**
-@module ember
-@submodule ember-handlebars
-*/
-
-var get = Ember.get, set = Ember.set;
-var EmberHandlebars = Ember.Handlebars;
-var LOWERCASE_A_Z = /^[a-z]/;
-var VIEW_PREFIX = /^view\./;
-
-function makeBindings(thisContext, options) {
- var hash = options.hash,
- hashType = options.hashTypes;
-
- for (var prop in hash) {
- if (hashType[prop] === 'ID') {
-
- var value = hash[prop];
-
- if (Ember.IS_BINDING.test(prop)) {
- Ember.warn("You're attempting to render a view by passing " + prop + "=" + value + " to a view helper, but this syntax is ambiguous. You should either surround " + value + " in quotes or remove `Binding` from " + prop + ".");
- } else {
- hash[prop + 'Binding'] = value;
- hashType[prop + 'Binding'] = 'STRING';
- delete hash[prop];
- delete hashType[prop];
- }
- }
- }
-
- if (hash.hasOwnProperty('idBinding')) {
- // id can't be bound, so just perform one-time lookup.
- hash.id = EmberHandlebars.get(thisContext, hash.idBinding, options);
- hashType.id = 'STRING';
- delete hash.idBinding;
- delete hashType.idBinding;
- }
-}
-
-EmberHandlebars.ViewHelper = Ember.Object.create({
-
- propertiesFromHTMLOptions: function(options) {
- var hash = options.hash, data = options.data;
- var extensions = {},
- classes = hash['class'],
- dup = false;
-
- if (hash.id) {
- extensions.elementId = hash.id;
- dup = true;
- }
-
- if (hash.tag) {
- extensions.tagName = hash.tag;
- dup = true;
- }
-
- if (classes) {
- classes = classes.split(' ');
- extensions.classNames = classes;
- dup = true;
- }
-
- if (hash.classBinding) {
- extensions.classNameBindings = hash.classBinding.split(' ');
- dup = true;
- }
-
- if (hash.classNameBindings) {
- if (extensions.classNameBindings === undefined) extensions.classNameBindings = [];
- extensions.classNameBindings = extensions.classNameBindings.concat(hash.classNameBindings.split(' '));
- dup = true;
- }
-
- if (hash.attributeBindings) {
- Ember.assert("Setting 'attributeBindings' via Handlebars is not allowed. Please subclass Ember.View and set it there instead.");
- extensions.attributeBindings = null;
- dup = true;
- }
-
- if (dup) {
- hash = Ember.$.extend({}, hash);
- delete hash.id;
- delete hash.tag;
- delete hash['class'];
- delete hash.classBinding;
- }
-
- // Set the proper context for all bindings passed to the helper. This applies to regular attribute bindings
- // as well as class name bindings. If the bindings are local, make them relative to the current context
- // instead of the view.
- var path;
-
- // Evaluate the context of regular attribute bindings:
- for (var prop in hash) {
- if (!hash.hasOwnProperty(prop)) { continue; }
-
- // Test if the property ends in "Binding"
- if (Ember.IS_BINDING.test(prop) && typeof hash[prop] === 'string') {
- path = this.contextualizeBindingPath(hash[prop], data);
- if (path) { hash[prop] = path; }
- }
- }
-
- // Evaluate the context of class name bindings:
- if (extensions.classNameBindings) {
- for (var b in extensions.classNameBindings) {
- var full = extensions.classNameBindings[b];
- if (typeof full === 'string') {
- // Contextualize the path of classNameBinding so this:
- //
- // classNameBinding="isGreen:green"
- //
- // is converted to this:
- //
- // classNameBinding="_parentView.context.isGreen:green"
- var parsedPath = Ember.View._parsePropertyPath(full);
- path = this.contextualizeBindingPath(parsedPath.path, data);
- if (path) { extensions.classNameBindings[b] = path + parsedPath.classNames; }
- }
- }
- }
-
- return Ember.$.extend(hash, extensions);
- },
-
- // Transform bindings from the current context to a context that can be evaluated within the view.
- // Returns null if the path shouldn't be changed.
- //
- // TODO: consider the addition of a prefix that would allow this method to return `path`.
- contextualizeBindingPath: function(path, data) {
- var normalized = Ember.Handlebars.normalizePath(null, path, data);
- if (normalized.isKeyword) {
- return 'templateData.keywords.' + path;
- } else if (Ember.isGlobalPath(path)) {
- return null;
- } else if (path === 'this') {
- return '_parentView.context';
- } else {
- return '_parentView.context.' + path;
- }
- },
-
- helper: function(thisContext, path, options) {
- var data = options.data,
- fn = options.fn,
- newView;
-
- makeBindings(thisContext, options);
-
- if ('string' === typeof path) {
-
- // TODO: this is a lame conditional, this should likely change
- // but something along these lines will likely need to be added
- // as deprecation warnings
- //
- if (options.types[0] === 'STRING' && LOWERCASE_A_Z.test(path) && !VIEW_PREFIX.test(path)) {
- Ember.assert("View requires a container", !!data.view.container);
- newView = data.view.container.lookupFactory('view:' + path);
- } else {
- newView = EmberHandlebars.get(thisContext, path, options);
- }
-
- Ember.assert("Unable to find view at path '" + path + "'", !!newView);
- } else {
- newView = path;
- }
-
- Ember.assert(Ember.String.fmt('You must pass a view to the #view helper, not %@ (%@)', [path, newView]), Ember.View.detect(newView) || Ember.View.detectInstance(newView));
-
- var viewOptions = this.propertiesFromHTMLOptions(options, thisContext);
- var currentView = data.view;
- viewOptions.templateData = data;
- var newViewProto = newView.proto ? newView.proto() : newView;
-
- if (fn) {
- Ember.assert("You cannot provide a template block if you also specified a templateName", !get(viewOptions, 'templateName') && !get(newViewProto, 'templateName'));
- viewOptions.template = fn;
- }
-
- // We only want to override the `_context` computed property if there is
- // no specified controller. See View#_context for more information.
- if (!newViewProto.controller && !newViewProto.controllerBinding && !viewOptions.controller && !viewOptions.controllerBinding) {
- viewOptions._context = thisContext;
- }
-
- currentView.appendChild(newView, viewOptions);
- }
-});
-
-/**
- `{{view}}` inserts a new instance of `Ember.View` into a template passing its
- options to the `Ember.View`'s `create` method and using the supplied block as
- the view's own template.
-
- An empty `` and the following template:
-
- ```handlebars
- A span:
- {{#view tagName="span"}}
- hello.
- {{/view}}
- ```
-
- Will result in HTML structure:
-
- ```html
-
-
-
-
- A span:
-
- Hello.
-
-
-
- ```
-
- ### `parentView` setting
-
- The `parentView` property of the new `Ember.View` instance created through
- `{{view}}` will be set to the `Ember.View` instance of the template where
- `{{view}}` was called.
-
- ```javascript
- aView = Ember.View.create({
- template: Ember.Handlebars.compile("{{#view}} my parent: {{parentView.elementId}} {{/view}}")
- });
-
- aView.appendTo('body');
- ```
-
- Will result in HTML structure:
-
- ```html
-
-
- my parent: ember1
-
-
- ```
-
- ### Setting CSS id and class attributes
-
- The HTML `id` attribute can be set on the `{{view}}`'s resulting element with
- the `id` option. This option will _not_ be passed to `Ember.View.create`.
-
- ```handlebars
- {{#view tagName="span" id="a-custom-id"}}
- hello.
- {{/view}}
- ```
-
- Results in the following HTML structure:
-
- ```html
-
-
- hello.
-
-
- ```
-
- The HTML `class` attribute can be set on the `{{view}}`'s resulting element
- with the `class` or `classNameBindings` options. The `class` option will
- directly set the CSS `class` attribute and will not be passed to
- `Ember.View.create`. `classNameBindings` will be passed to `create` and use
- `Ember.View`'s class name binding functionality:
-
- ```handlebars
- {{#view tagName="span" class="a-custom-class"}}
- hello.
- {{/view}}
- ```
-
- Results in the following HTML structure:
-
- ```html
-
-
- hello.
-
-
- ```
-
- ### Supplying a different view class
-
- `{{view}}` can take an optional first argument before its supplied options to
- specify a path to a custom view class.
-
- ```handlebars
- {{#view "MyApp.CustomView"}}
- hello.
- {{/view}}
- ```
-
- The first argument can also be a relative path accessible from the current
- context.
-
- ```javascript
- MyApp = Ember.Application.create({});
- MyApp.OuterView = Ember.View.extend({
- innerViewClass: Ember.View.extend({
- classNames: ['a-custom-view-class-as-property']
- }),
- template: Ember.Handlebars.compile('{{#view "view.innerViewClass"}} hi {{/view}}')
- });
-
- MyApp.OuterView.create().appendTo('body');
- ```
-
- Will result in the following HTML:
-
- ```html
-
- ```
-
- ### Blockless use
-
- If you supply a custom `Ember.View` subclass that specifies its own template
- or provide a `templateName` option to `{{view}}` it can be used without
- supplying a block. Attempts to use both a `templateName` option and supply a
- block will throw an error.
-
- ```handlebars
- {{view "MyApp.ViewWithATemplateDefined"}}
- ```
-
- ### `viewName` property
-
- You can supply a `viewName` option to `{{view}}`. The `Ember.View` instance
- will be referenced as a property of its parent view by this name.
-
- ```javascript
- aView = Ember.View.create({
- template: Ember.Handlebars.compile('{{#view viewName="aChildByName"}} hi {{/view}}')
- });
-
- aView.appendTo('body');
- aView.get('aChildByName') // the instance of Ember.View created by {{view}} helper
- ```
-
- @method view
- @for Ember.Handlebars.helpers
- @param {String} path
- @param {Hash} options
- @return {String} HTML string
-*/
-EmberHandlebars.registerHelper('view', function(path, options) {
- Ember.assert("The view helper only takes a single argument", arguments.length <= 2);
-
- // If no path is provided, treat path param as options.
- if (path && path.data && path.data.isRenderData) {
- options = path;
- path = "Ember.View";
- }
-
- return EmberHandlebars.ViewHelper.helper(this, path, options);
-});
-
-
-})();
-
-
-
-(function() {
-/*globals Handlebars */
-
-// TODO: Don't require all of this module
-/**
-@module ember
-@submodule ember-handlebars
-*/
-
-var get = Ember.get, handlebarsGet = Ember.Handlebars.get, fmt = Ember.String.fmt;
-
-/**
- `{{collection}}` is a `Ember.Handlebars` helper for adding instances of
- `Ember.CollectionView` to a template. See [Ember.CollectionView](/api/classes/Ember.CollectionView.html)
- for additional information on how a `CollectionView` functions.
-
- `{{collection}}`'s primary use is as a block helper with a `contentBinding`
- option pointing towards an `Ember.Array`-compatible object. An `Ember.View`
- instance will be created for each item in its `content` property. Each view
- will have its own `content` property set to the appropriate item in the
- collection.
-
- The provided block will be applied as the template for each item's view.
-
- Given an empty `` the following template:
-
- ```handlebars
- {{#collection contentBinding="App.items"}}
- Hi {{view.content.name}}
- {{/collection}}
- ```
-
- And the following application code
-
- ```javascript
- App = Ember.Application.create()
- App.items = [
- Ember.Object.create({name: 'Dave'}),
- Ember.Object.create({name: 'Mary'}),
- Ember.Object.create({name: 'Sara'})
- ]
- ```
-
- Will result in the HTML structure below
-
- ```html
-
-
Hi Dave
-
Hi Mary
-
Hi Sara
-
- ```
-
- ### Blockless Use
-
- If you provide an `itemViewClass` option that has its own `template` you can
- omit the block.
-
- The following template:
-
- ```handlebars
- {{collection contentBinding="App.items" itemViewClass="App.AnItemView"}}
- ```
-
- And application code
-
- ```javascript
- App = Ember.Application.create();
- App.items = [
- Ember.Object.create({name: 'Dave'}),
- Ember.Object.create({name: 'Mary'}),
- Ember.Object.create({name: 'Sara'})
- ];
-
- App.AnItemView = Ember.View.extend({
- template: Ember.Handlebars.compile("Greetings {{view.content.name}}")
- });
- ```
-
- Will result in the HTML structure below
-
- ```html
-
-
Greetings Dave
-
Greetings Mary
-
Greetings Sara
-
- ```
-
- ### Specifying a CollectionView subclass
-
- By default the `{{collection}}` helper will create an instance of
- `Ember.CollectionView`. You can supply a `Ember.CollectionView` subclass to
- the helper by passing it as the first argument:
-
- ```handlebars
- {{#collection App.MyCustomCollectionClass contentBinding="App.items"}}
- Hi {{view.content.name}}
- {{/collection}}
- ```
-
- ### Forwarded `item.*`-named Options
-
- As with the `{{view}}`, helper options passed to the `{{collection}}` will be
- set on the resulting `Ember.CollectionView` as properties. Additionally,
- options prefixed with `item` will be applied to the views rendered for each
- item (note the camelcasing):
-
- ```handlebars
- {{#collection contentBinding="App.items"
- itemTagName="p"
- itemClassNames="greeting"}}
- Howdy {{view.content.name}}
- {{/collection}}
- ```
-
- Will result in the following HTML structure:
-
- ```html
-
-
Howdy Dave
-
Howdy Mary
-
Howdy Sara
-
- ```
-
- @method collection
- @for Ember.Handlebars.helpers
- @param {String} path
- @param {Hash} options
- @return {String} HTML string
- @deprecated Use `{{each}}` helper instead.
-*/
-Ember.Handlebars.registerHelper('collection', function(path, options) {
- Ember.deprecate("Using the {{collection}} helper without specifying a class has been deprecated as the {{each}} helper now supports the same functionality.", path !== 'collection');
-
- // If no path is provided, treat path param as options.
- if (path && path.data && path.data.isRenderData) {
- options = path;
- path = undefined;
- Ember.assert("You cannot pass more than one argument to the collection helper", arguments.length === 1);
- } else {
- Ember.assert("You cannot pass more than one argument to the collection helper", arguments.length === 2);
- }
-
- var fn = options.fn;
- var data = options.data;
- var inverse = options.inverse;
- var view = options.data.view;
-
- // If passed a path string, convert that into an object.
- // Otherwise, just default to the standard class.
- var collectionClass;
- collectionClass = path ? handlebarsGet(this, path, options) : Ember.CollectionView;
- Ember.assert(fmt("%@ #collection: Could not find collection class %@", [data.view, path]), !!collectionClass);
-
- var hash = options.hash, itemHash = {}, match;
-
- // Extract item view class if provided else default to the standard class
- var collectionPrototype = collectionClass.proto(),
- itemViewClass;
-
- if (hash.itemView) {
- var controller = data.keywords.controller;
- Ember.assert('You specified an itemView, but the current context has no container to look the itemView up in. This probably means that you created a view manually, instead of through the container. Instead, use container.lookup("view:viewName"), which will properly instantiate your view.', controller && controller.container);
- var container = controller.container;
- itemViewClass = container.resolve('view:' + hash.itemView);
- Ember.assert('You specified the itemView ' + hash.itemView + ", but it was not found at " + container.describe("view:" + hash.itemView) + " (and it was not registered in the container)", !!itemViewClass);
- } else if (hash.itemViewClass) {
- itemViewClass = handlebarsGet(collectionPrototype, hash.itemViewClass, options);
- } else {
- itemViewClass = collectionPrototype.itemViewClass;
- }
-
- Ember.assert(fmt("%@ #collection: Could not find itemViewClass %@", [data.view, itemViewClass]), !!itemViewClass);
-
- delete hash.itemViewClass;
- delete hash.itemView;
-
- // Go through options passed to the {{collection}} helper and extract options
- // that configure item views instead of the collection itself.
- for (var prop in hash) {
- if (hash.hasOwnProperty(prop)) {
- match = prop.match(/^item(.)(.*)$/);
-
- if (match && prop !== 'itemController') {
- // Convert itemShouldFoo -> shouldFoo
- itemHash[match[1].toLowerCase() + match[2]] = hash[prop];
- // Delete from hash as this will end up getting passed to the
- // {{view}} helper method.
- delete hash[prop];
- }
- }
- }
-
- if (fn) {
- itemHash.template = fn;
- delete options.fn;
- }
-
- var emptyViewClass;
- if (inverse && inverse !== Handlebars.VM.noop) {
- emptyViewClass = get(collectionPrototype, 'emptyViewClass');
- emptyViewClass = emptyViewClass.extend({
- template: inverse,
- tagName: itemHash.tagName
- });
- } else if (hash.emptyViewClass) {
- emptyViewClass = handlebarsGet(this, hash.emptyViewClass, options);
- }
- if (emptyViewClass) { hash.emptyView = emptyViewClass; }
-
- if (!hash.keyword) {
- itemHash._context = Ember.computed.alias('content');
- }
-
- var viewOptions = Ember.Handlebars.ViewHelper.propertiesFromHTMLOptions({ data: data, hash: itemHash }, this);
- hash.itemViewClass = itemViewClass.extend(viewOptions);
-
- return Ember.Handlebars.helpers.view.call(this, collectionClass, options);
-});
-
-
-})();
-
-
-
-(function() {
-/*globals Handlebars */
-/**
-@module ember
-@submodule ember-handlebars
-*/
-
-var handlebarsGet = Ember.Handlebars.get;
-
-/**
- `unbound` allows you to output a property without binding. *Important:* The
- output will not be updated if the property changes. Use with caution.
-
- ```handlebars
- {{unbound somePropertyThatDoesntChange}}
- ```
-
- `unbound` can also be used in conjunction with a bound helper to
- render it in its unbound form:
-
- ```handlebars
- {{unbound helperName somePropertyThatDoesntChange}}
- ```
-
- @method unbound
- @for Ember.Handlebars.helpers
- @param {String} property
- @return {String} HTML string
-*/
-Ember.Handlebars.registerHelper('unbound', function(property, fn) {
- var options = arguments[arguments.length - 1], helper, context, out;
-
- if (arguments.length > 2) {
- // Unbound helper call.
- options.data.isUnbound = true;
- helper = Ember.Handlebars.helpers[arguments[0]] || Ember.Handlebars.helperMissing;
- out = helper.apply(this, Array.prototype.slice.call(arguments, 1));
- delete options.data.isUnbound;
- return out;
- }
-
- context = (fn.contexts && fn.contexts.length) ? fn.contexts[0] : this;
- return handlebarsGet(context, property, fn);
-});
-
-})();
-
-
-
-(function() {
-/*jshint debug:true*/
-/**
-@module ember
-@submodule ember-handlebars
-*/
-
-var handlebarsGet = Ember.Handlebars.get, normalizePath = Ember.Handlebars.normalizePath;
-
-/**
- `log` allows you to output the value of a variable in the current rendering
- context.
-
- ```handlebars
- {{log myVariable}}
- ```
-
- @method log
- @for Ember.Handlebars.helpers
- @param {String} property
-*/
-Ember.Handlebars.registerHelper('log', function(property, options) {
- var context = (options.contexts && options.contexts.length) ? options.contexts[0] : this,
- normalized = normalizePath(context, property, options.data),
- pathRoot = normalized.root,
- path = normalized.path,
- value = (path === 'this') ? pathRoot : handlebarsGet(pathRoot, path, options);
- Ember.Logger.log(value);
-});
-
-/**
- Execute the `debugger` statement in the current context.
-
- ```handlebars
- {{debugger}}
- ```
-
- @method debugger
- @for Ember.Handlebars.helpers
- @param {String} property
-*/
-Ember.Handlebars.registerHelper('debugger', function(options) {
- debugger;
-});
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-handlebars
-*/
-
-var get = Ember.get, set = Ember.set;
-
-Ember.Handlebars.EachView = Ember.CollectionView.extend(Ember._Metamorph, {
- init: function() {
- var itemController = get(this, 'itemController');
- var binding;
-
- if (itemController) {
- var controller = get(this, 'controller.container').lookupFactory('controller:array').create({
- parentController: get(this, 'controller'),
- itemController: itemController,
- target: get(this, 'controller'),
- _eachView: this
- });
-
- this.disableContentObservers(function() {
- set(this, 'content', controller);
- binding = new Ember.Binding('content', '_eachView.dataSource').oneWay();
- binding.connect(controller);
- });
-
- set(this, '_arrayController', controller);
- } else {
- this.disableContentObservers(function() {
- binding = new Ember.Binding('content', 'dataSource').oneWay();
- binding.connect(this);
- });
- }
-
- return this._super();
- },
-
- _assertArrayLike: function(content) {
- Ember.assert("The value that #each loops over must be an Array. You passed " + content.constructor + ", but it should have been an ArrayController", !Ember.ControllerMixin.detect(content) || (content && content.isGenerated) || content instanceof Ember.ArrayController);
- Ember.assert("The value that #each loops over must be an Array. You passed " + ((Ember.ControllerMixin.detect(content) && content.get('model') !== undefined) ? ("" + content.get('model') + " (wrapped in " + content + ")") : ("" + content)), Ember.Array.detect(content));
- },
-
- disableContentObservers: function(callback) {
- Ember.removeBeforeObserver(this, 'content', null, '_contentWillChange');
- Ember.removeObserver(this, 'content', null, '_contentDidChange');
-
- callback.call(this);
-
- Ember.addBeforeObserver(this, 'content', null, '_contentWillChange');
- Ember.addObserver(this, 'content', null, '_contentDidChange');
- },
-
- itemViewClass: Ember._MetamorphView,
- emptyViewClass: Ember._MetamorphView,
-
- createChildView: function(view, attrs) {
- view = this._super(view, attrs);
-
- // At the moment, if a container view subclass wants
- // to insert keywords, it is responsible for cloning
- // the keywords hash. This will be fixed momentarily.
- var keyword = get(this, 'keyword');
- var content = get(view, 'content');
-
- if (keyword) {
- var data = get(view, 'templateData');
-
- data = Ember.copy(data);
- data.keywords = view.cloneKeywords();
- set(view, 'templateData', data);
-
- // In this case, we do not bind, because the `content` of
- // a #each item cannot change.
- data.keywords[keyword] = content;
- }
-
- // If {{#each}} is looping over an array of controllers,
- // point each child view at their respective controller.
- if (content && get(content, 'isController')) {
- set(view, 'controller', content);
- }
-
- return view;
- },
-
- destroy: function() {
- if (!this._super()) { return; }
-
- var arrayController = get(this, '_arrayController');
-
- if (arrayController) {
- arrayController.destroy();
- }
-
- return this;
- }
-});
-
-var GroupedEach = Ember.Handlebars.GroupedEach = function(context, path, options) {
- var self = this,
- normalized = Ember.Handlebars.normalizePath(context, path, options.data);
-
- this.context = context;
- this.path = path;
- this.options = options;
- this.template = options.fn;
- this.containingView = options.data.view;
- this.normalizedRoot = normalized.root;
- this.normalizedPath = normalized.path;
- this.content = this.lookupContent();
-
- this.addContentObservers();
- this.addArrayObservers();
-
- this.containingView.on('willClearRender', function() {
- self.destroy();
- });
-};
-
-GroupedEach.prototype = {
- contentWillChange: function() {
- this.removeArrayObservers();
- },
-
- contentDidChange: function() {
- this.content = this.lookupContent();
- this.addArrayObservers();
- this.rerenderContainingView();
- },
-
- contentArrayWillChange: Ember.K,
-
- contentArrayDidChange: function() {
- this.rerenderContainingView();
- },
-
- lookupContent: function() {
- return Ember.Handlebars.get(this.normalizedRoot, this.normalizedPath, this.options);
- },
-
- addArrayObservers: function() {
- if (!this.content) { return; }
-
- this.content.addArrayObserver(this, {
- willChange: 'contentArrayWillChange',
- didChange: 'contentArrayDidChange'
- });
- },
-
- removeArrayObservers: function() {
- if (!this.content) { return; }
-
- this.content.removeArrayObserver(this, {
- willChange: 'contentArrayWillChange',
- didChange: 'contentArrayDidChange'
- });
- },
-
- addContentObservers: function() {
- Ember.addBeforeObserver(this.normalizedRoot, this.normalizedPath, this, this.contentWillChange);
- Ember.addObserver(this.normalizedRoot, this.normalizedPath, this, this.contentDidChange);
- },
-
- removeContentObservers: function() {
- Ember.removeBeforeObserver(this.normalizedRoot, this.normalizedPath, this.contentWillChange);
- Ember.removeObserver(this.normalizedRoot, this.normalizedPath, this.contentDidChange);
- },
-
- render: function() {
- if (!this.content) { return; }
-
- var content = this.content,
- contentLength = get(content, 'length'),
- data = this.options.data,
- template = this.template;
-
- data.insideEach = true;
- for (var i = 0; i < contentLength; i++) {
- template(content.objectAt(i), { data: data });
- }
- },
-
- rerenderContainingView: function() {
- var self = this;
- Ember.run.scheduleOnce('render', this, function() {
- // It's possible it's been destroyed after we enqueued a re-render call.
- if (!self.destroyed) {
- self.containingView.rerender();
- }
- });
- },
-
- destroy: function() {
- this.removeContentObservers();
- if (this.content) {
- this.removeArrayObservers();
- }
- this.destroyed = true;
- }
-};
-
-/**
- The `{{#each}}` helper loops over elements in a collection, rendering its
- block once for each item. It is an extension of the base Handlebars `{{#each}}`
- helper:
-
- ```javascript
- Developers = [{name: 'Yehuda'},{name: 'Tom'}, {name: 'Paul'}];
- ```
-
- ```handlebars
- {{#each Developers}}
- {{name}}
- {{/each}}
- ```
-
- `{{each}}` supports an alternative syntax with element naming:
-
- ```handlebars
- {{#each person in Developers}}
- {{person.name}}
- {{/each}}
- ```
-
- When looping over objects that do not have properties, `{{this}}` can be used
- to render the object:
-
- ```javascript
- DeveloperNames = ['Yehuda', 'Tom', 'Paul']
- ```
-
- ```handlebars
- {{#each DeveloperNames}}
- {{this}}
- {{/each}}
- ```
- ### {{else}} condition
- `{{#each}}` can have a matching `{{else}}`. The contents of this block will render
- if the collection is empty.
-
- ```
- {{#each person in Developers}}
- {{person.name}}
- {{else}}
- Sorry, nobody is available for this task.
- {{/each}}
- ```
- ### Specifying a View class for items
- If you provide an `itemViewClass` option that references a view class
- with its own `template` you can omit the block.
-
- The following template:
-
- ```handlebars
- {{#view App.MyView }}
- {{each view.items itemViewClass="App.AnItemView"}}
- {{/view}}
- ```
-
- And application code
-
- ```javascript
- App = Ember.Application.create({
- MyView: Ember.View.extend({
- items: [
- Ember.Object.create({name: 'Dave'}),
- Ember.Object.create({name: 'Mary'}),
- Ember.Object.create({name: 'Sara'})
- ]
- })
- });
-
- App.AnItemView = Ember.View.extend({
- template: Ember.Handlebars.compile("Greetings {{name}}")
- });
- ```
-
- Will result in the HTML structure below
-
- ```html
-
-
Greetings Dave
-
Greetings Mary
-
Greetings Sara
-
- ```
-
- If an `itemViewClass` is defined on the helper, and therefore the helper is not
- being used as a block, an `emptyViewClass` can also be provided optionally.
- The `emptyViewClass` will match the behavior of the `{{else}}` condition
- described above. That is, the `emptyViewClass` will render if the collection
- is empty.
-
- ### Representing each item with a Controller.
- By default the controller lookup within an `{{#each}}` block will be
- the controller of the template where the `{{#each}}` was used. If each
- item needs to be presented by a custom controller you can provide a
- `itemController` option which references a controller by lookup name.
- Each item in the loop will be wrapped in an instance of this controller
- and the item itself will be set to the `content` property of that controller.
-
- This is useful in cases where properties of model objects need transformation
- or synthesis for display:
-
- ```javascript
- App.DeveloperController = Ember.ObjectController.extend({
- isAvailableForHire: function() {
- return !this.get('content.isEmployed') && this.get('content.isSeekingWork');
- }.property('isEmployed', 'isSeekingWork')
- })
- ```
-
- ```handlebars
- {{#each person in developers itemController="developer"}}
- {{person.name}} {{#if person.isAvailableForHire}}Hire me!{{/if}}
- {{/each}}
- ```
-
- Each itemController will receive a reference to the current controller as
- a `parentController` property.
-
- ### (Experimental) Grouped Each
-
- When used in conjunction with the experimental [group helper](https://github.com/emberjs/group-helper),
- you can inform Handlebars to re-render an entire group of items instead of
- re-rendering them one at a time (in the event that they are changed en masse
- or an item is added/removed).
-
- ```handlebars
- {{#group}}
- {{#each people}}
- {{firstName}} {{lastName}}
- {{/each}}
- {{/group}}
- ```
-
- This can be faster than the normal way that Handlebars re-renders items
- in some cases.
-
- If for some reason you have a group with more than one `#each`, you can make
- one of the collections be updated in normal (non-grouped) fashion by setting
- the option `groupedRows=true` (counter-intuitive, I know).
-
- For example,
-
- ```handlebars
- {{dealershipName}}
-
- {{#group}}
- {{#each dealers}}
- {{firstName}} {{lastName}}
- {{/each}}
-
- {{#each car in cars groupedRows=true}}
- {{car.make}} {{car.model}} {{car.color}}
- {{/each}}
- {{/group}}
- ```
- Any change to `dealershipName` or the `dealers` collection will cause the
- entire group to be re-rendered. However, changes to the `cars` collection
- will be re-rendered individually (as normal).
-
- Note that `group` behavior is also disabled by specifying an `itemViewClass`.
-
- @method each
- @for Ember.Handlebars.helpers
- @param [name] {String} name for item (used with `in`)
- @param [path] {String} path
- @param [options] {Object} Handlebars key/value pairs of options
- @param [options.itemViewClass] {String} a path to a view class used for each item
- @param [options.itemController] {String} name of a controller to be created for each item
- @param [options.groupedRows] {boolean} enable normal item-by-item rendering when inside a `#group` helper
-*/
-Ember.Handlebars.registerHelper('each', function(path, options) {
- if (arguments.length === 4) {
- Ember.assert("If you pass more than one argument to the each helper, it must be in the form #each foo in bar", arguments[1] === "in");
-
- var keywordName = arguments[0];
-
- options = arguments[3];
- path = arguments[2];
- if (path === '') { path = "this"; }
-
- options.hash.keyword = keywordName;
- }
-
- if (arguments.length === 1) {
- options = path;
- path = 'this';
- }
-
- options.hash.dataSourceBinding = path;
- // Set up emptyView as a metamorph with no tag
- //options.hash.emptyViewClass = Ember._MetamorphView;
-
- if (options.data.insideGroup && !options.hash.groupedRows && !options.hash.itemViewClass) {
- new Ember.Handlebars.GroupedEach(this, path, options).render();
- } else {
- return Ember.Handlebars.helpers.collection.call(this, 'Ember.Handlebars.EachView', options);
- }
-});
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-handlebars
-*/
-
-/**
- `template` allows you to render a template from inside another template.
- This allows you to re-use the same template in multiple places. For example:
-
- ```html
-
- ```
-
- ```html
-
- ```
-
- ```handlebars
- {{#if isUser}}
- {{template "user_info"}}
- {{else}}
- {{template "unlogged_user_info"}}
- {{/if}}
- ```
-
- This helper looks for templates in the global `Ember.TEMPLATES` hash. If you
- add `
- ```
-
- Take note that `"welcome"` is a string and not an object
- reference.
-
- @method loc
- @for Ember.Handlebars.helpers
- @param {String} str The string to format
-*/
-
-Ember.Handlebars.registerHelper('loc', function(str) {
- return Ember.String.loc(str);
-});
-
-})();
-
-
-
-(function() {
-
-})();
-
-
-
-(function() {
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-handlebars
-*/
-
-var set = Ember.set, get = Ember.get;
-
-/**
- The internal class used to create text inputs when the `{{input}}`
- helper is used with `type` of `checkbox`.
-
- See Handlebars.helpers.input for usage details.
-
- ## Direct manipulation of `checked`
-
- The `checked` attribute of an `Ember.Checkbox` object should always be set
- through the Ember object or by interacting with its rendered element
- representation via the mouse, keyboard, or touch. Updating the value of the
- checkbox via jQuery will result in the checked value of the object and its
- element losing synchronization.
-
- ## Layout and LayoutName properties
-
- Because HTML `input` elements are self closing `layout` and `layoutName`
- properties will not be applied. See [Ember.View](/api/classes/Ember.View.html)'s
- layout section for more information.
-
- @class Checkbox
- @namespace Ember
- @extends Ember.View
-*/
-Ember.Checkbox = Ember.View.extend({
- classNames: ['ember-checkbox'],
-
- tagName: 'input',
-
- attributeBindings: ['type', 'checked', 'indeterminate', 'disabled', 'tabindex', 'name'],
-
- type: "checkbox",
- checked: false,
- disabled: false,
- indeterminate: false,
-
- init: function() {
- this._super();
- this.on("change", this, this._updateElementValue);
- },
-
- didInsertElement: function() {
- this._super();
- this.get('element').indeterminate = !!this.get('indeterminate');
- },
-
- _updateElementValue: function() {
- set(this, 'checked', this.$().prop('checked'));
- }
-});
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-handlebars
-*/
-
-var get = Ember.get, set = Ember.set;
-
-/**
- Shared mixin used by `Ember.TextField` and `Ember.TextArea`.
-
- @class TextSupport
- @namespace Ember
- @private
-*/
-Ember.TextSupport = Ember.Mixin.create({
- value: "",
-
- attributeBindings: ['placeholder', 'disabled', 'maxlength', 'tabindex', 'readonly'],
- placeholder: null,
- disabled: false,
- maxlength: null,
-
- init: function() {
- this._super();
- this.on("focusOut", this, this._elementValueDidChange);
- this.on("change", this, this._elementValueDidChange);
- this.on("paste", this, this._elementValueDidChange);
- this.on("cut", this, this._elementValueDidChange);
- this.on("input", this, this._elementValueDidChange);
- this.on("keyUp", this, this.interpretKeyEvents);
- },
-
- /**
- The action to be sent when the user presses the return key.
-
- This is similar to the `{{action}}` helper, but is fired when
- the user presses the return key when editing a text field, and sends
- the value of the field as the context.
-
- @property action
- @type String
- @default null
- */
- action: null,
-
- /**
- The event that should send the action.
-
- Options are:
-
- * `enter`: the user pressed enter
- * `keyPress`: the user pressed a key
-
- @property onEvent
- @type String
- @default enter
- */
- onEvent: 'enter',
-
- /**
- Whether they `keyUp` event that triggers an `action` to be sent continues
- propagating to other views.
-
- By default, when the user presses the return key on their keyboard and
- the text field has an `action` set, the action will be sent to the view's
- controller and the key event will stop propagating.
-
- If you would like parent views to receive the `keyUp` event even after an
- action has been dispatched, set `bubbles` to true.
-
- @property bubbles
- @type Boolean
- @default false
- */
- bubbles: false,
-
- interpretKeyEvents: function(event) {
- var map = Ember.TextSupport.KEY_EVENTS;
- var method = map[event.keyCode];
-
- this._elementValueDidChange();
- if (method) { return this[method](event); }
- },
-
- _elementValueDidChange: function() {
- set(this, 'value', this.$().val());
- },
-
- /**
- The action to be sent when the user inserts a new line.
-
- Called by the `Ember.TextSupport` mixin on keyUp if keycode matches 13.
- Uses sendAction to send the `enter` action to the controller.
-
- @method insertNewline
- @param {Event} event
- */
- insertNewline: function(event) {
- sendAction('enter', this, event);
- sendAction('insert-newline', this, event);
- },
-
- /**
- Called when the user hits escape.
-
- Called by the `Ember.TextSupport` mixin on keyUp if keycode matches 27.
- Uses sendAction to send the `escape-press` action to the controller.
-
- @method cancel
- @param {Event} event
- */
- cancel: function(event) {
- sendAction('escape-press', this, event);
- },
-
- /**
- Called when the text area is focused.
-
- @method focusIn
- @param {Event} event
- */
- focusIn: function(event) {
- sendAction('focus-in', this, event);
- },
-
- /**
- Called when the text area is blurred.
-
- @method focusOut
- @param {Event} event
- */
- focusOut: function(event) {
- sendAction('focus-out', this, event);
- },
-
- /**
- The action to be sent when the user presses a key. Enabled by setting
- the `onEvent` property to `keyPress`.
-
- Uses sendAction to send the `keyPress` action to the controller.
-
- @method keyPress
- @param {Event} event
- */
- keyPress: function(event) {
- sendAction('key-press', this, event);
- }
-
-});
-
-Ember.TextSupport.KEY_EVENTS = {
- 13: 'insertNewline',
- 27: 'cancel'
-};
-
-// In principle, this shouldn't be necessary, but the legacy
-// sectionAction semantics for TextField are different from
-// the component semantics so this method normalizes them.
-function sendAction(eventName, view, event) {
- var action = get(view, eventName),
- on = get(view, 'onEvent'),
- value = get(view, 'value');
-
- // back-compat support for keyPress as an event name even though
- // it's also a method name that consumes the event (and therefore
- // incompatible with sendAction semantics).
- if (on === eventName || (on === 'keyPress' && eventName === 'key-press')) {
- view.sendAction('action', value);
- }
-
- view.sendAction(eventName, value);
-
- if (action || on === eventName) {
- if(!get(view, 'bubbles')) {
- event.stopPropagation();
- }
- }
-}
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-handlebars
-*/
-
-var get = Ember.get, set = Ember.set;
-
-/**
-
- The internal class used to create text inputs when the `{{input}}`
- helper is used with `type` of `text`.
-
- See [handlebars.helpers.input](Ember.Handlebars.helpers.html#method_input) for usage details.
-
- ## Layout and LayoutName properties
-
- Because HTML `input` elements are self closing `layout` and `layoutName`
- properties will not be applied. See [Ember.View](/api/classes/Ember.View.html)'s
- layout section for more information.
-
- @class TextField
- @namespace Ember
- @extends Ember.Component
- @uses Ember.TextSupport
-*/
-Ember.TextField = Ember.Component.extend(Ember.TextSupport,
- /** @scope Ember.TextField.prototype */ {
-
- classNames: ['ember-text-field'],
- tagName: "input",
- attributeBindings: ['type', 'value', 'size', 'pattern', 'name'],
-
- /**
- The `value` attribute of the input element. As the user inputs text, this
- property is updated live.
-
- @property value
- @type String
- @default ""
- */
- value: "",
-
- /**
- The `type` attribute of the input element.
-
- @property type
- @type String
- @default "text"
- */
- type: "text",
-
- /**
- The `size` of the text field in characters.
-
- @property size
- @type String
- @default null
- */
- size: null,
-
- /**
- The `pattern` the pattern attribute of input element.
-
- @property pattern
- @type String
- @default null
- */
- pattern: null
-});
-
-})();
-
-
-
-(function() {
-/*
-@module ember
-@submodule ember-handlebars
-*/
-
-var get = Ember.get, set = Ember.set;
-
-/*
- @class Button
- @namespace Ember
- @extends Ember.View
- @uses Ember.TargetActionSupport
- @deprecated
-*/
-Ember.Button = Ember.View.extend(Ember.TargetActionSupport, {
- classNames: ['ember-button'],
- classNameBindings: ['isActive'],
-
- tagName: 'button',
-
- propagateEvents: false,
-
- attributeBindings: ['type', 'disabled', 'href', 'tabindex'],
-
- /*
- @private
-
- Overrides `TargetActionSupport`'s `targetObject` computed
- property to use Handlebars-specific path resolution.
-
- @property targetObject
- */
- targetObject: Ember.computed(function() {
- var target = get(this, 'target'),
- root = get(this, 'context'),
- data = get(this, 'templateData');
-
- if (typeof target !== 'string') { return target; }
-
- return Ember.Handlebars.get(root, target, { data: data });
- }).property('target'),
-
- // Defaults to 'button' if tagName is 'input' or 'button'
- type: Ember.computed(function(key) {
- var tagName = this.tagName;
- if (tagName === 'input' || tagName === 'button') { return 'button'; }
- }),
-
- disabled: false,
-
- // Allow 'a' tags to act like buttons
- href: Ember.computed(function() {
- return this.tagName === 'a' ? '#' : null;
- }),
-
- mouseDown: function() {
- if (!get(this, 'disabled')) {
- set(this, 'isActive', true);
- this._mouseDown = true;
- this._mouseEntered = true;
- }
- return get(this, 'propagateEvents');
- },
-
- mouseLeave: function() {
- if (this._mouseDown) {
- set(this, 'isActive', false);
- this._mouseEntered = false;
- }
- },
-
- mouseEnter: function() {
- if (this._mouseDown) {
- set(this, 'isActive', true);
- this._mouseEntered = true;
- }
- },
-
- mouseUp: function(event) {
- if (get(this, 'isActive')) {
- // Actually invoke the button's target and action.
- // This method comes from the Ember.TargetActionSupport mixin.
- this.triggerAction();
- set(this, 'isActive', false);
- }
-
- this._mouseDown = false;
- this._mouseEntered = false;
- return get(this, 'propagateEvents');
- },
-
- keyDown: function(event) {
- // Handle space or enter
- if (event.keyCode === 13 || event.keyCode === 32) {
- this.mouseDown();
- }
- },
-
- keyUp: function(event) {
- // Handle space or enter
- if (event.keyCode === 13 || event.keyCode === 32) {
- this.mouseUp();
- }
- },
-
- // TODO: Handle proper touch behavior. Including should make inactive when
- // finger moves more than 20x outside of the edge of the button (vs mouse
- // which goes inactive as soon as mouse goes out of edges.)
-
- touchStart: function(touch) {
- return this.mouseDown(touch);
- },
-
- touchEnd: function(touch) {
- return this.mouseUp(touch);
- },
-
- init: function() {
- Ember.deprecate("Ember.Button is deprecated and will be removed from future releases. Consider using the `{{action}}` helper.");
- this._super();
- }
-});
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-handlebars
-*/
-
-var get = Ember.get, set = Ember.set;
-
-/**
- The internal class used to create textarea element when the `{{textarea}}`
- helper is used.
-
- See [handlebars.helpers.textarea](/api/classes/Ember.Handlebars.helpers.html#method_textarea) for usage details.
-
- ## Layout and LayoutName properties
-
- Because HTML `textarea` elements do not contain inner HTML the `layout` and
- `layoutName` properties will not be applied. See [Ember.View](/api/classes/Ember.View.html)'s
- layout section for more information.
-
- @class TextArea
- @namespace Ember
- @extends Ember.Component
- @uses Ember.TextSupport
-*/
-Ember.TextArea = Ember.Component.extend(Ember.TextSupport, {
- classNames: ['ember-text-area'],
-
- tagName: "textarea",
- attributeBindings: ['rows', 'cols', 'name'],
- rows: null,
- cols: null,
-
- _updateElementValue: Ember.observer('value', function() {
- // We do this check so cursor position doesn't get affected in IE
- var value = get(this, 'value'),
- $el = this.$();
- if ($el && value !== $el.val()) {
- $el.val(value);
- }
- }),
-
- init: function() {
- this._super();
- this.on("didInsertElement", this, this._updateElementValue);
- }
-
-});
-
-})();
-
-
-
-(function() {
-/*jshint eqeqeq:false */
-
-/**
-@module ember
-@submodule ember-handlebars
-*/
-
-var set = Ember.set,
- get = Ember.get,
- indexOf = Ember.EnumerableUtils.indexOf,
- indexesOf = Ember.EnumerableUtils.indexesOf,
- forEach = Ember.EnumerableUtils.forEach,
- replace = Ember.EnumerableUtils.replace,
- isArray = Ember.isArray,
- precompileTemplate = Ember.Handlebars.compile;
-
-Ember.SelectOption = Ember.View.extend({
- tagName: 'option',
- attributeBindings: ['value', 'selected'],
-
- defaultTemplate: function(context, options) {
- options = { data: options.data, hash: {} };
- Ember.Handlebars.helpers.bind.call(context, "view.label", options);
- },
-
- init: function() {
- this.labelPathDidChange();
- this.valuePathDidChange();
-
- this._super();
- },
-
- selected: Ember.computed(function() {
- var content = get(this, 'content'),
- selection = get(this, 'parentView.selection');
- if (get(this, 'parentView.multiple')) {
- return selection && indexOf(selection, content.valueOf()) > -1;
- } else {
- // Primitives get passed through bindings as objects... since
- // `new Number(4) !== 4`, we use `==` below
- return content == selection;
- }
- }).property('content', 'parentView.selection'),
-
- labelPathDidChange: Ember.observer('parentView.optionLabelPath', function() {
- var labelPath = get(this, 'parentView.optionLabelPath');
-
- if (!labelPath) { return; }
-
- Ember.defineProperty(this, 'label', Ember.computed(function() {
- return get(this, labelPath);
- }).property(labelPath));
- }),
-
- valuePathDidChange: Ember.observer('parentView.optionValuePath', function() {
- var valuePath = get(this, 'parentView.optionValuePath');
-
- if (!valuePath) { return; }
-
- Ember.defineProperty(this, 'value', Ember.computed(function() {
- return get(this, valuePath);
- }).property(valuePath));
- })
-});
-
-Ember.SelectOptgroup = Ember.CollectionView.extend({
- tagName: 'optgroup',
- attributeBindings: ['label'],
-
- selectionBinding: 'parentView.selection',
- multipleBinding: 'parentView.multiple',
- optionLabelPathBinding: 'parentView.optionLabelPath',
- optionValuePathBinding: 'parentView.optionValuePath',
-
- itemViewClassBinding: 'parentView.optionView'
-});
-
-/**
- The `Ember.Select` view class renders a
- [select](https://developer.mozilla.org/en/HTML/Element/select) HTML element,
- allowing the user to choose from a list of options.
-
- The text and `value` property of each `` element within the
- `` element are populated from the objects in the `Element.Select`'s
- `content` property. The underlying data object of the selected `` is
- stored in the `Element.Select`'s `value` property.
-
- ## The Content Property (array of strings)
-
- The simplest version of an `Ember.Select` takes an array of strings as its
- `content` property. The string will be used as both the `value` property and
- the inner text of each ` ` element inside the rendered ``.
-
- Example:
-
- ```javascript
- App.ApplicationController = Ember.Controller.extend({
- names: ["Yehuda", "Tom"]
- });
- ```
-
- ```handlebars
- {{view Ember.Select content=names}}
- ```
-
- Would result in the following HTML:
-
- ```html
-
- Yehuda
- Tom
-
- ```
-
- You can control which `` is selected through the `Ember.Select`'s
- `value` property:
-
- ```javascript
- App.ApplicationController = Ember.Controller.extend({
- selectedName: 'Tom',
- names: ["Yehuda", "Tom"]
- });
- ```
-
- ```handlebars
- {{view Ember.Select
- content=names
- value=selectedName
- }}
- ```
-
- Would result in the following HTML with the ` ` for 'Tom' selected:
-
- ```html
-
- Yehuda
- Tom
-
- ```
-
- A user interacting with the rendered `` to choose "Yehuda" would
- update the value of `selectedName` to "Yehuda".
-
- ## The Content Property (array of Objects)
-
- An `Ember.Select` can also take an array of JavaScript or Ember objects as
- its `content` property.
-
- When using objects you need to tell the `Ember.Select` which property should
- be accessed on each object to supply the `value` attribute of the ``
- and which property should be used to supply the element text.
-
- The `optionValuePath` option is used to specify the path on each object to
- the desired property for the `value` attribute. The `optionLabelPath`
- specifies the path on each object to the desired property for the
- element's text. Both paths must reference each object itself as `content`:
-
- ```javascript
- App.ApplicationController = Ember.Controller.extend({
- programmers: [
- {firstName: "Yehuda", id: 1},
- {firstName: "Tom", id: 2}
- ]
- });
- ```
-
- ```handlebars
- {{view Ember.Select
- content=programmers
- optionValuePath="content.id"
- optionLabelPath="content.firstName"}}
- ```
-
- Would result in the following HTML:
-
- ```html
-
- Yehuda
- Tom
-
- ```
-
- The `value` attribute of the selected ` ` within an `Ember.Select`
- can be bound to a property on another object:
-
- ```javascript
- App.ApplicationController = Ember.Controller.extend({
- programmers: [
- {firstName: "Yehuda", id: 1},
- {firstName: "Tom", id: 2}
- ],
- currentProgrammer: {
- id: 2
- }
- });
- ```
-
- ```handlebars
- {{view Ember.Select
- content=programmers
- optionValuePath="content.id"
- optionLabelPath="content.firstName"
- value=currentProgrammer.id}}
- ```
-
- Would result in the following HTML with a selected option:
-
- ```html
-
- Yehuda
- Tom
-
- ```
-
- Interacting with the rendered element by selecting the first option
- ('Yehuda') will update the `id` of `currentProgrammer`
- to match the `value` property of the newly selected ` `.
-
- Alternatively, you can control selection through the underlying objects
- used to render each object by binding the `selection` option. When the selected
- ` ` is changed, the property path provided to `selection`
- will be updated to match the content object of the rendered ` `
- element:
-
- ```javascript
- App.ApplicationController = Ember.Controller.extend({
- selectedPerson: null,
- programmers: [
- {firstName: "Yehuda", id: 1},
- {firstName: "Tom", id: 2}
- ]
- });
- ```
-
- ```handlebars
- {{view Ember.Select
- content=programmers
- optionValuePath="content.id"
- optionLabelPath="content.firstName"
- selection=selectedPerson}}
- ```
-
- Would result in the following HTML with a selected option:
-
- ```html
-
- Yehuda
- Tom
-
- ```
-
- Interacting with the rendered element by selecting the first option
- ('Yehuda') will update the `selectedPerson` to match the object of
- the newly selected ` `. In this case it is the first object
- in the `programmers`
-
- ## Supplying a Prompt
-
- A `null` value for the `Ember.Select`'s `value` or `selection` property
- results in there being no ` ` with a `selected` attribute:
-
- ```javascript
- App.ApplicationController = Ember.Controller.extend({
- selectedProgrammer: null,
- programmers: [
- "Yehuda",
- "Tom"
- ]
- });
- ```
-
- ``` handlebars
- {{view Ember.Select
- content=programmers
- value=selectedProgrammer
- }}
- ```
-
- Would result in the following HTML:
-
- ```html
-
- Yehuda
- Tom
-
- ```
-
- Although `selectedProgrammer` is `null` and no ` `
- has a `selected` attribute the rendered HTML will display the
- first item as though it were selected. You can supply a string
- value for the `Ember.Select` to display when there is no selection
- with the `prompt` option:
-
- ```javascript
- App.ApplicationController = Ember.Controller.extend({
- selectedProgrammer: null,
- programmers: [
- "Yehuda",
- "Tom"
- ]
- });
- ```
-
- ```handlebars
- {{view Ember.Select
- content=programmers
- value=selectedProgrammer
- prompt="Please select a name"
- }}
- ```
-
- Would result in the following HTML:
-
- ```html
-
- Please select a name
- Yehuda
- Tom
-
- ```
-
- @class Select
- @namespace Ember
- @extends Ember.View
-*/
-Ember.Select = Ember.View.extend(
- /** @scope Ember.Select.prototype */ {
-
- tagName: 'select',
- classNames: ['ember-select'],
- defaultTemplate: Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) {
-this.compilerInfo = [4,'>= 1.0.0'];
-helpers = this.merge(helpers, Ember.Handlebars.helpers); data = data || {};
- var buffer = '', stack1, hashTypes, hashContexts, escapeExpression=this.escapeExpression, self=this;
-
-function program1(depth0,data) {
-
- var buffer = '', hashTypes, hashContexts;
- data.buffer.push(" ");
- hashTypes = {};
- hashContexts = {};
- data.buffer.push(escapeExpression(helpers._triageMustache.call(depth0, "view.prompt", {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
- data.buffer.push(" ");
- return buffer;
- }
-
-function program3(depth0,data) {
-
- var stack1, hashTypes, hashContexts;
- hashTypes = {};
- hashContexts = {};
- stack1 = helpers.each.call(depth0, "view.groupedContent", {hash:{},inverse:self.noop,fn:self.program(4, program4, data),contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data});
- if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
- else { data.buffer.push(''); }
- }
-function program4(depth0,data) {
-
- var hashContexts, hashTypes;
- hashContexts = {'content': depth0,'label': depth0};
- hashTypes = {'content': "ID",'label': "ID"};
- data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.groupView", {hash:{
- 'content': ("content"),
- 'label': ("label")
- },contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
- }
-
-function program6(depth0,data) {
-
- var stack1, hashTypes, hashContexts;
- hashTypes = {};
- hashContexts = {};
- stack1 = helpers.each.call(depth0, "view.content", {hash:{},inverse:self.noop,fn:self.program(7, program7, data),contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data});
- if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
- else { data.buffer.push(''); }
- }
-function program7(depth0,data) {
-
- var hashContexts, hashTypes;
- hashContexts = {'content': depth0};
- hashTypes = {'content': "ID"};
- data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.optionView", {hash:{
- 'content': ("")
- },contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data})));
- }
-
- hashTypes = {};
- hashContexts = {};
- stack1 = helpers['if'].call(depth0, "view.prompt", {hash:{},inverse:self.noop,fn:self.program(1, program1, data),contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data});
- if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
- hashTypes = {};
- hashContexts = {};
- stack1 = helpers['if'].call(depth0, "view.optionGroupPath", {hash:{},inverse:self.program(6, program6, data),fn:self.program(3, program3, data),contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data});
- if(stack1 || stack1 === 0) { data.buffer.push(stack1); }
- return buffer;
-
-}),
- attributeBindings: ['multiple', 'disabled', 'tabindex', 'name'],
-
- /**
- The `multiple` attribute of the select element. Indicates whether multiple
- options can be selected.
-
- @property multiple
- @type Boolean
- @default false
- */
- multiple: false,
-
- /**
- The `disabled` attribute of the select element. Indicates whether
- the element is disabled from interactions.
-
- @property disabled
- @type Boolean
- @default false
- */
- disabled: false,
-
- /**
- The list of options.
-
- If `optionLabelPath` and `optionValuePath` are not overridden, this should
- be a list of strings, which will serve simultaneously as labels and values.
-
- Otherwise, this should be a list of objects. For instance:
-
- ```javascript
- Ember.Select.create({
- content: Ember.A([
- { id: 1, firstName: 'Yehuda' },
- { id: 2, firstName: 'Tom' }
- ]),
- optionLabelPath: 'content.firstName',
- optionValuePath: 'content.id'
- });
- ```
-
- @property content
- @type Array
- @default null
- */
- content: null,
-
- /**
- When `multiple` is `false`, the element of `content` that is currently
- selected, if any.
-
- When `multiple` is `true`, an array of such elements.
-
- @property selection
- @type Object or Array
- @default null
- */
- selection: null,
-
- /**
- In single selection mode (when `multiple` is `false`), value can be used to
- get the current selection's value or set the selection by it's value.
-
- It is not currently supported in multiple selection mode.
-
- @property value
- @type String
- @default null
- */
- value: Ember.computed(function(key, value) {
- if (arguments.length === 2) { return value; }
- var valuePath = get(this, 'optionValuePath').replace(/^content\.?/, '');
- return valuePath ? get(this, 'selection.' + valuePath) : get(this, 'selection');
- }).property('selection'),
-
- /**
- If given, a top-most dummy option will be rendered to serve as a user
- prompt.
-
- @property prompt
- @type String
- @default null
- */
- prompt: null,
-
- /**
- The path of the option labels. See [content](/api/classes/Ember.Select.html#property_content).
-
- @property optionLabelPath
- @type String
- @default 'content'
- */
- optionLabelPath: 'content',
-
- /**
- The path of the option values. See [content](/api/classes/Ember.Select.html#property_content).
-
- @property optionValuePath
- @type String
- @default 'content'
- */
- optionValuePath: 'content',
-
- /**
- The path of the option group.
- When this property is used, `content` should be sorted by `optionGroupPath`.
-
- @property optionGroupPath
- @type String
- @default null
- */
- optionGroupPath: null,
-
- /**
- The view class for optgroup.
-
- @property groupView
- @type Ember.View
- @default Ember.SelectOptgroup
- */
- groupView: Ember.SelectOptgroup,
-
- groupedContent: Ember.computed(function() {
- var groupPath = get(this, 'optionGroupPath');
- var groupedContent = Ember.A();
- var content = get(this, 'content') || [];
-
- forEach(content, function(item) {
- var label = get(item, groupPath);
-
- if (get(groupedContent, 'lastObject.label') !== label) {
- groupedContent.pushObject({
- label: label,
- content: Ember.A()
- });
- }
-
- get(groupedContent, 'lastObject.content').push(item);
- });
-
- return groupedContent;
- }).property('optionGroupPath', 'content.@each'),
-
- /**
- The view class for option.
-
- @property optionView
- @type Ember.View
- @default Ember.SelectOption
- */
- optionView: Ember.SelectOption,
-
- _change: function() {
- if (get(this, 'multiple')) {
- this._changeMultiple();
- } else {
- this._changeSingle();
- }
- },
-
- selectionDidChange: Ember.observer('selection.@each', function() {
- var selection = get(this, 'selection');
- if (get(this, 'multiple')) {
- if (!isArray(selection)) {
- set(this, 'selection', Ember.A([selection]));
- return;
- }
- this._selectionDidChangeMultiple();
- } else {
- this._selectionDidChangeSingle();
- }
- }),
-
- valueDidChange: Ember.observer('value', function() {
- var content = get(this, 'content'),
- value = get(this, 'value'),
- valuePath = get(this, 'optionValuePath').replace(/^content\.?/, ''),
- selectedValue = (valuePath ? get(this, 'selection.' + valuePath) : get(this, 'selection')),
- selection;
-
- if (value !== selectedValue) {
- selection = content ? content.find(function(obj) {
- return value === (valuePath ? get(obj, valuePath) : obj);
- }) : null;
-
- this.set('selection', selection);
- }
- }),
-
-
- _triggerChange: function() {
- var selection = get(this, 'selection');
- var value = get(this, 'value');
-
- if (!Ember.isNone(selection)) { this.selectionDidChange(); }
- if (!Ember.isNone(value)) { this.valueDidChange(); }
-
- this._change();
- },
-
- _changeSingle: function() {
- var selectedIndex = this.$()[0].selectedIndex,
- content = get(this, 'content'),
- prompt = get(this, 'prompt');
-
- if (!content || !get(content, 'length')) { return; }
- if (prompt && selectedIndex === 0) { set(this, 'selection', null); return; }
-
- if (prompt) { selectedIndex -= 1; }
- set(this, 'selection', content.objectAt(selectedIndex));
- },
-
-
- _changeMultiple: function() {
- var options = this.$('option:selected'),
- prompt = get(this, 'prompt'),
- offset = prompt ? 1 : 0,
- content = get(this, 'content'),
- selection = get(this, 'selection');
-
- if (!content) { return; }
- if (options) {
- var selectedIndexes = options.map(function() {
- return this.index - offset;
- }).toArray();
- var newSelection = content.objectsAt(selectedIndexes);
-
- if (isArray(selection)) {
- replace(selection, 0, get(selection, 'length'), newSelection);
- } else {
- set(this, 'selection', newSelection);
- }
- }
- },
-
- _selectionDidChangeSingle: function() {
- var el = this.get('element');
- if (!el) { return; }
-
- var content = get(this, 'content'),
- selection = get(this, 'selection'),
- selectionIndex = content ? indexOf(content, selection) : -1,
- prompt = get(this, 'prompt');
-
- if (prompt) { selectionIndex += 1; }
- if (el) { el.selectedIndex = selectionIndex; }
- },
-
- _selectionDidChangeMultiple: function() {
- var content = get(this, 'content'),
- selection = get(this, 'selection'),
- selectedIndexes = content ? indexesOf(content, selection) : [-1],
- prompt = get(this, 'prompt'),
- offset = prompt ? 1 : 0,
- options = this.$('option'),
- adjusted;
-
- if (options) {
- options.each(function() {
- adjusted = this.index > -1 ? this.index - offset : -1;
- this.selected = indexOf(selectedIndexes, adjusted) > -1;
- });
- }
- },
-
- init: function() {
- this._super();
- this.on("didInsertElement", this, this._triggerChange);
- this.on("change", this, this._change);
- }
-});
-
-})();
-
-
-
-(function() {
-/**
-@module ember
-@submodule ember-handlebars-compiler
-*/
-
-/**
-
- The `{{input}}` helper inserts an HTML ` ` tag into the template,
- with a `type` value of either `text` or `checkbox`. If no `type` is provided,
- `text` will be the default value applied. The attributes of `{{input}}`
- match those of the native HTML tag as closely as possible for these two types.
-
- ## Use as text field
- An `{{input}}` with no `type` or a `type` of `text` will render an HTML text input.
- The following HTML attributes can be set via the helper:
-
-* `value`
-* `size`
-* `name`
-* `pattern`
-* `placeholder`
-* `disabled`
-* `maxlength`
-* `tabindex`
-
-
- When set to a quoted string, these values will be directly applied to the HTML
- element. When left unquoted, these values will be bound to a property on the
- template's current rendering context (most typically a controller instance).
-
- ## Unbound:
-
- ```handlebars
- {{input value="http://www.facebook.com"}}
- ```
-
-
- ```html
-
- ```
-
- ## Bound:
-
- ```javascript
- App.ApplicationController = Ember.Controller.extend({
- firstName: "Stanley",
- entryNotAllowed: true
- });
- ```
-
-
- ```handlebars
- {{input type="text" value=firstName disabled=entryNotAllowed size="50"}}
- ```
-
-
- ```html
-
- ```
-
- ## Extension
-
- Internally, `{{input type="text"}}` creates an instance of `Ember.TextField`, passing
- arguments from the helper to `Ember.TextField`'s `create` method. You can extend the
- capablilties of text inputs in your applications by reopening this class. For example,
- if you are deploying to browsers where the `required` attribute is used, you
- can add this to the `TextField`'s `attributeBindings` property:
-
- ```javascript
- Ember.TextField.reopen({
- attributeBindings: ['required']
- });
- ```
-
- Keep in mind when writing `Ember.TextField` subclasses that `Ember.TextField`
- itself extends `Ember.Component`, meaning that it does NOT inherit
- the `controller` of the parent view.
-
- See more about [Ember components](api/classes/Ember.Component.html)
-
-
- ## Use as checkbox
-
- An `{{input}}` with a `type` of `checkbox` will render an HTML checkbox input.
- The following HTML attributes can be set via the helper:
-
-* `checked`
-* `disabled`
-* `tabindex`
-* `indeterminate`
-* `name`
-
-
- When set to a quoted string, these values will be directly applied to the HTML
- element. When left unquoted, these values will be bound to a property on the
- template's current rendering context (most typically a controller instance).
-
- ## Unbound:
-
- ```handlebars
- {{input type="checkbox" name="isAdmin"}}
- ```
-
- ```html
-
- ```
-
- ## Bound:
-
- ```javascript
- App.ApplicationController = Ember.Controller.extend({
- isAdmin: true
- });
- ```
-
-
- ```handlebars
- {{input type="checkbox" checked=isAdmin }}
- ```
-
-
- ```html
-
- ```
-
- ## Extension
-
- Internally, `{{input type="checkbox"}}` creates an instance of `Ember.Checkbox`, passing
- arguments from the helper to `Ember.Checkbox`'s `create` method. You can extend the
- capablilties of checkbox inputs in your applications by reopening this class. For example,
- if you wanted to add a css class to all checkboxes in your application:
-
- ```javascript
- Ember.Checkbox.reopen({
- classNames: ['my-app-checkbox']
- });
- ```
-
-
- @method input
- @for Ember.Handlebars.helpers
- @param {Hash} options
-*/
-Ember.Handlebars.registerHelper('input', function(options) {
- Ember.assert('You can only pass attributes to the `input` helper, not arguments', arguments.length < 2);
-
- var hash = options.hash,
- types = options.hashTypes,
- inputType = hash.type,
- onEvent = hash.on;
-
- delete hash.type;
- delete hash.on;
-
- if (inputType === 'checkbox') {
- return Ember.Handlebars.helpers.view.call(this, Ember.Checkbox, options);
- } else {
- if (inputType) { hash.type = inputType; }
- hash.onEvent = onEvent || 'enter';
- return Ember.Handlebars.helpers.view.call(this, Ember.TextField, options);
- }
-});
-
-/**
- `{{textarea}}` inserts a new instance of `