Index: chrome/browser/resources/shared/js/cr/ui/array_data_model.js |
=================================================================== |
--- chrome/browser/resources/shared/js/cr/ui/array_data_model.js (revision 177292) |
+++ chrome/browser/resources/shared/js/cr/ui/array_data_model.js (working copy) |
@@ -1,413 +0,0 @@ |
-// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-/** |
- * @fileoverview This is a data model representin |
- */ |
- |
-cr.define('cr.ui', function() { |
- /** @const */ var EventTarget = cr.EventTarget; |
- /** @const */ var Event = cr.Event; |
- |
- /** |
- * A data model that wraps a simple array and supports sorting by storing |
- * initial indexes of elements for each position in sorted array. |
- * @param {!Array} array The underlying array. |
- * @constructor |
- * @extends {EventTarget} |
- */ |
- function ArrayDataModel(array) { |
- this.array_ = array; |
- this.indexes_ = []; |
- this.compareFunctions_ = {}; |
- |
- for (var i = 0; i < array.length; i++) { |
- this.indexes_.push(i); |
- } |
- } |
- |
- ArrayDataModel.prototype = { |
- __proto__: EventTarget.prototype, |
- |
- /** |
- * The length of the data model. |
- * @type {number} |
- */ |
- get length() { |
- return this.array_.length; |
- }, |
- |
- /** |
- * Returns the item at the given index. |
- * This implementation returns the item at the given index in the sorted |
- * array. |
- * @param {number} index The index of the element to get. |
- * @return {*} The element at the given index. |
- */ |
- item: function(index) { |
- if (index >= 0 && index < this.length) |
- return this.array_[this.indexes_[index]]; |
- return undefined; |
- }, |
- |
- /** |
- * Returns compare function set for given field. |
- * @param {string} field The field to get compare function for. |
- * @return {function(*, *): number} Compare function set for given field. |
- */ |
- compareFunction: function(field) { |
- return this.compareFunctions_[field]; |
- }, |
- |
- /** |
- * Sets compare function for given field. |
- * @param {string} field The field to set compare function. |
- * @param {function(*, *): number} Compare function to set for given field. |
- */ |
- setCompareFunction: function(field, compareFunction) { |
- if (!this.compareFunctions_) { |
- this.compareFunctions_ = {}; |
- } |
- this.compareFunctions_[field] = compareFunction; |
- }, |
- |
- /** |
- * Returns true if the field has a compare function. |
- * @param {string} field The field to check. |
- * @return {boolean} True if the field is sortable. |
- */ |
- isSortable: function(field) { |
- return this.compareFunctions_ && field in this.compareFunctions_; |
- }, |
- |
- /** |
- * Returns current sort status. |
- * @return {!Object} Current sort status. |
- */ |
- get sortStatus() { |
- if (this.sortStatus_) { |
- return this.createSortStatus( |
- this.sortStatus_.field, this.sortStatus_.direction); |
- } else { |
- return this.createSortStatus(null, null); |
- } |
- }, |
- |
- /** |
- * Returns the first matching item. |
- * @param {*} item The item to find. |
- * @param {number=} opt_fromIndex If provided, then the searching start at |
- * the {@code opt_fromIndex}. |
- * @return {number} The index of the first found element or -1 if not found. |
- */ |
- indexOf: function(item, opt_fromIndex) { |
- for (var i = opt_fromIndex || 0; i < this.indexes_.length; i++) { |
- if (item === this.item(i)) |
- return i; |
- } |
- return -1; |
- }, |
- |
- /** |
- * Returns an array of elements in a selected range. |
- * @param {number=} opt_from The starting index of the selected range. |
- * @param {number=} opt_to The ending index of selected range. |
- * @return {Array} An array of elements in the selected range. |
- */ |
- slice: function(opt_from, opt_to) { |
- var arr = this.array_; |
- return this.indexes_.slice(opt_from, opt_to).map( |
- function(index) { return arr[index] }); |
- }, |
- |
- /** |
- * This removes and adds items to the model. |
- * This dispatches a splice event. |
- * This implementation runs sort after splice and creates permutation for |
- * the whole change. |
- * @param {number} index The index of the item to update. |
- * @param {number} deleteCount The number of items to remove. |
- * @param {...*} The items to add. |
- * @return {!Array} An array with the removed items. |
- */ |
- splice: function(index, deleteCount, var_args) { |
- var addCount = arguments.length - 2; |
- var newIndexes = []; |
- var deletePermutation = []; |
- var deletedItems = []; |
- var newArray = []; |
- index = Math.min(index, this.indexes_.length); |
- deleteCount = Math.min(deleteCount, this.indexes_.length - index); |
- // Copy items before the insertion point. |
- for (var i = 0; i < index; i++) { |
- newIndexes.push(newArray.length); |
- deletePermutation.push(i); |
- newArray.push(this.array_[this.indexes_[i]]); |
- } |
- // Delete items. |
- for (; i < index + deleteCount; i++) { |
- deletePermutation.push(-1); |
- deletedItems.push(this.array_[this.indexes_[i]]); |
- } |
- // Insert new items instead deleted ones. |
- for (var j = 0; j < addCount; j++) { |
- newIndexes.push(newArray.length); |
- newArray.push(arguments[j + 2]); |
- } |
- // Copy items after the insertion point. |
- for (; i < this.indexes_.length; i++) { |
- newIndexes.push(newArray.length); |
- deletePermutation.push(i - deleteCount + addCount); |
- newArray.push(this.array_[this.indexes_[i]]); |
- } |
- |
- this.indexes_ = newIndexes; |
- |
- this.array_ = newArray; |
- |
- // TODO(arv): Maybe unify splice and change events? |
- var spliceEvent = new Event('splice'); |
- spliceEvent.removed = deletedItems; |
- spliceEvent.added = Array.prototype.slice.call(arguments, 2); |
- |
- var status = this.sortStatus; |
- // if sortStatus.field is null, this restores original order. |
- var sortPermutation = this.doSort_(this.sortStatus.field, |
- this.sortStatus.direction); |
- if (sortPermutation) { |
- var splicePermutation = deletePermutation.map(function(element) { |
- return element != -1 ? sortPermutation[element] : -1; |
- }); |
- this.dispatchPermutedEvent_(splicePermutation); |
- spliceEvent.index = sortPermutation[index]; |
- } else { |
- this.dispatchPermutedEvent_(deletePermutation); |
- spliceEvent.index = index; |
- } |
- |
- this.dispatchEvent(spliceEvent); |
- |
- // If real sorting is needed, we should first call prepareSort (data may |
- // change), and then sort again. |
- // Still need to finish the sorting above (including events), so |
- // list will not go to inconsistent state. |
- if (status.field) |
- this.delayedSort_(status.field, status.direction); |
- |
- return deletedItems; |
- }, |
- |
- /** |
- * Appends items to the end of the model. |
- * |
- * This dispatches a splice event. |
- * |
- * @param {...*} The items to append. |
- * @return {number} The new length of the model. |
- */ |
- push: function(var_args) { |
- var args = Array.prototype.slice.call(arguments); |
- args.unshift(this.length, 0); |
- this.splice.apply(this, args); |
- return this.length; |
- }, |
- |
- /** |
- * Use this to update a given item in the array. This does not remove and |
- * reinsert a new item. |
- * This dispatches a change event. |
- * This runs sort after updating. |
- * @param {number} index The index of the item to update. |
- */ |
- updateIndex: function(index) { |
- if (index < 0 || index >= this.length) |
- throw Error('Invalid index, ' + index); |
- |
- // TODO(arv): Maybe unify splice and change events? |
- var e = new Event('change'); |
- e.index = index; |
- this.dispatchEvent(e); |
- |
- if (this.sortStatus.field) { |
- var status = this.sortStatus; |
- var sortPermutation = this.doSort_(this.sortStatus.field, |
- this.sortStatus.direction); |
- if (sortPermutation) |
- this.dispatchPermutedEvent_(sortPermutation); |
- // We should first call prepareSort (data may change), and then sort. |
- // Still need to finish the sorting above (including events), so |
- // list will not go to inconsistent state. |
- this.delayedSort_(status.field, status.direction); |
- } |
- }, |
- |
- /** |
- * Creates sort status with given field and direction. |
- * @param {string} field Sort field. |
- * @param {string} direction Sort direction. |
- * @return {!Object} Created sort status. |
- */ |
- createSortStatus: function(field, direction) { |
- return { |
- field: field, |
- direction: direction |
- }; |
- }, |
- |
- /** |
- * Called before a sort happens so that you may fetch additional data |
- * required for the sort. |
- * |
- * @param {string} field Sort field. |
- * @param {function()} callback The function to invoke when preparation |
- * is complete. |
- */ |
- prepareSort: function(field, callback) { |
- callback(); |
- }, |
- |
- /** |
- * Sorts data model according to given field and direction and dispathes |
- * sorted event with delay. If no need to delay, use sort() instead. |
- * @param {string} field Sort field. |
- * @param {string} direction Sort direction. |
- * @private |
- */ |
- delayedSort_: function(field, direction) { |
- var self = this; |
- setTimeout(function() { |
- // If the sort status has been changed, sorting has already done |
- // on the change event. |
- if (field == self.sortStatus.field && |
- direction == self.sortStatus.direction) { |
- self.sort(field, direction); |
- } |
- }, 0); |
- }, |
- |
- /** |
- * Sorts data model according to given field and direction and dispathes |
- * sorted event. |
- * @param {string} field Sort field. |
- * @param {string} direction Sort direction. |
- */ |
- sort: function(field, direction) { |
- var self = this; |
- |
- this.prepareSort(field, function() { |
- var sortPermutation = self.doSort_(field, direction); |
- if (sortPermutation) |
- self.dispatchPermutedEvent_(sortPermutation); |
- self.dispatchSortEvent_(); |
- }); |
- }, |
- |
- /** |
- * Sorts data model according to given field and direction. |
- * @param {string} field Sort field. |
- * @param {string} direction Sort direction. |
- * @private |
- */ |
- doSort_: function(field, direction) { |
- var compareFunction = this.sortFunction_(field, direction); |
- var positions = []; |
- for (var i = 0; i < this.length; i++) { |
- positions[this.indexes_[i]] = i; |
- } |
- var sorted = this.indexes_.every(function(element, index, array) { |
- return index == 0 || compareFunction(element, array[index - 1]) >= 0; |
- }); |
- if (!sorted) |
- this.indexes_.sort(compareFunction); |
- this.sortStatus_ = this.createSortStatus(field, direction); |
- var sortPermutation = []; |
- var changed = false; |
- for (var i = 0; i < this.length; i++) { |
- if (positions[this.indexes_[i]] != i) |
- changed = true; |
- sortPermutation[positions[this.indexes_[i]]] = i; |
- } |
- if (changed) |
- return sortPermutation; |
- return null; |
- }, |
- |
- dispatchSortEvent_: function() { |
- var e = new Event('sorted'); |
- this.dispatchEvent(e); |
- }, |
- |
- dispatchPermutedEvent_: function(permutation) { |
- var e = new Event('permuted'); |
- e.permutation = permutation; |
- e.newLength = this.length; |
- this.dispatchEvent(e); |
- }, |
- |
- /** |
- * Creates compare function for the field. |
- * Returns the function set as sortFunction for given field |
- * or default compare function |
- * @param {string} field Sort field. |
- * @param {function(*, *): number} Compare function. |
- * @private |
- */ |
- createCompareFunction_: function(field) { |
- var compareFunction = |
- this.compareFunctions_ ? this.compareFunctions_[field] : null; |
- var defaultValuesCompareFunction = this.defaultValuesCompareFunction; |
- if (compareFunction) { |
- return compareFunction; |
- } else { |
- return function(a, b) { |
- return defaultValuesCompareFunction.call(null, a[field], b[field]); |
- } |
- } |
- return compareFunction; |
- }, |
- |
- /** |
- * Creates compare function for given field and direction. |
- * @param {string} field Sort field. |
- * @param {string} direction Sort direction. |
- * @param {function(*, *): number} Compare function. |
- * @private |
- */ |
- sortFunction_: function(field, direction) { |
- var compareFunction = null; |
- if (field !== null) |
- compareFunction = this.createCompareFunction_(field); |
- var dirMultiplier = direction == 'desc' ? -1 : 1; |
- |
- return function(index1, index2) { |
- var item1 = this.array_[index1]; |
- var item2 = this.array_[index2]; |
- |
- var compareResult = 0; |
- if (typeof(compareFunction) === 'function') |
- compareResult = compareFunction.call(null, item1, item2); |
- if (compareResult != 0) |
- return dirMultiplier * compareResult; |
- return dirMultiplier * this.defaultValuesCompareFunction(index1, |
- index2); |
- }.bind(this); |
- }, |
- |
- /** |
- * Default compare function. |
- */ |
- defaultValuesCompareFunction: function(a, b) { |
- // We could insert i18n comparisons here. |
- if (a < b) |
- return -1; |
- if (a > b) |
- return 1; |
- return 0; |
- } |
- }; |
- |
- return { |
- ArrayDataModel: ArrayDataModel |
- }; |
-}); |