| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 /** | |
| 6 * @fileoverview This is a table data model | |
| 7 */ | |
| 8 cr.define('cr.ui.table', function() { | |
| 9 const EventTarget = cr.EventTarget; | |
| 10 const Event = cr.Event; | |
| 11 const ArrayDataModel = cr.ui.ArrayDataModel; | |
| 12 | |
| 13 /** | |
| 14 * A table data model that supports sorting by storing initial indexes of | |
| 15 * elements for each position in sorted array. | |
| 16 * @param {!Array} items The underlying array. | |
| 17 * @constructor | |
| 18 * @extends {ArrayDataModel} | |
| 19 */ | |
| 20 function TableDataModel(items) { | |
| 21 ArrayDataModel.apply(this, arguments); | |
| 22 this.indexes_ = []; | |
| 23 for (var i = 0; i < items.length; i++) { | |
| 24 this.indexes_.push(i); | |
| 25 } | |
| 26 } | |
| 27 | |
| 28 TableDataModel.prototype = { | |
| 29 __proto__: ArrayDataModel.prototype, | |
| 30 | |
| 31 /** | |
| 32 * Returns the item at the given index. | |
| 33 * This implementation returns the item at the given index in the source | |
| 34 * array before sort. | |
| 35 * @param {number} index The index of the element to get. | |
| 36 * @return {*} The element at the given index. | |
| 37 */ | |
| 38 getItemByUnsortedIndex_: function(unsortedIndex) { | |
| 39 return ArrayDataModel.prototype.item.call(this, unsortedIndex); | |
| 40 }, | |
| 41 | |
| 42 /** | |
| 43 * Returns the item at the given index. | |
| 44 * This implementation returns the item at the given index in the sorted | |
| 45 * array. | |
| 46 * @param {number} index The index of the element to get. | |
| 47 * @return {*} The element at the given index. | |
| 48 */ | |
| 49 item: function(index) { | |
| 50 if (index >= 0 && index < this.length) | |
| 51 return this.getItemByUnsortedIndex_(this.indexes_[index]); | |
| 52 return undefined; | |
| 53 }, | |
| 54 | |
| 55 /** | |
| 56 * Returns compare function set for given field. | |
| 57 * @param {string} field The field to get compare function for. | |
| 58 * @return {Function(*, *): number} Compare function set for given field. | |
| 59 */ | |
| 60 compareFunction: function(field) { | |
| 61 return this.compareFunctions_[field]; | |
| 62 }, | |
| 63 | |
| 64 /** | |
| 65 * Sets compare function for given field. | |
| 66 * @param {string} field The field to set compare function. | |
| 67 * @param {Function(*, *): number} Compare function to set for given field. | |
| 68 */ | |
| 69 setCompareFunction: function(field, compareFunction) { | |
| 70 this.compareFunctions_[field] = compareFunction; | |
| 71 }, | |
| 72 | |
| 73 /** | |
| 74 * Returns current sort status. | |
| 75 * @return {!Object} Current sort status. | |
| 76 */ | |
| 77 get sortStatus() { | |
| 78 if (this.sortStatus_) { | |
| 79 return this.createSortStatus( | |
| 80 this.sortStatus_.field, this.sortStatus_.direction); | |
| 81 } else { | |
| 82 return this.createSortStatus(null, null); | |
| 83 } | |
| 84 }, | |
| 85 | |
| 86 /** | |
| 87 * This removes and adds items to the model. | |
| 88 * This dispatches a splice event. | |
| 89 * This implementation runs sort after splice and creates permutation for | |
| 90 * the whole change. | |
| 91 * @param {number} index The index of the item to update. | |
| 92 * @param {number} deleteCount The number of items to remove. | |
| 93 * @param {...*} The items to add. | |
| 94 * @return {!Array} An array with the removed items. | |
| 95 */ | |
| 96 splice: function(index, deleteCount, var_args) { | |
| 97 var addCount = arguments.length - 2; | |
| 98 var newIndexes = []; | |
| 99 var deletePermutation = []; | |
| 100 var deleted = 0; | |
| 101 for (var i = 0; i < this.indexes_.length; i++) { | |
| 102 var oldIndex = this.indexes_[i]; | |
| 103 if (oldIndex < index) { | |
| 104 newIndexes.push(oldIndex); | |
| 105 deletePermutation.push(i - deleted); | |
| 106 } else if (oldIndex >= index + deleteCount) { | |
| 107 newIndexes.push(oldIndex - deleteCount + addCount); | |
| 108 deletePermutation.push(i - deleted); | |
| 109 } else { | |
| 110 deletePermutation.push(-1); | |
| 111 deleted++; | |
| 112 } | |
| 113 } | |
| 114 for (var i = 0; i < addCount; i++) { | |
| 115 newIndexes.push(index + i); | |
| 116 } | |
| 117 this.indexes_ = newIndexes; | |
| 118 | |
| 119 var rv = ArrayDataModel.prototype.splice.apply(this, arguments); | |
| 120 | |
| 121 var splicePermutation; | |
| 122 if (this.sortStatus.field) { | |
| 123 var sortPermutation = this.doSort_(this.sortStatus.field, | |
| 124 this.sortStatus.direction); | |
| 125 splicePermutation = deletePermutation.map(function(element) { | |
| 126 return element != -1 ? sortPermutation[element] : -1; | |
| 127 }); | |
| 128 } else { | |
| 129 splicePermutation = deletePermutation; | |
| 130 } | |
| 131 this.dispatchSortEvent_(splicePermutation); | |
| 132 | |
| 133 return rv; | |
| 134 }, | |
| 135 | |
| 136 /** | |
| 137 * Use this to update a given item in the array. This does not remove and | |
| 138 * reinsert a new item. | |
| 139 * This dispatches a change event. | |
| 140 * This implementation runs sort after updating. | |
| 141 * @param {number} index The index of the item to update. | |
| 142 */ | |
| 143 updateIndex: function(index) { | |
| 144 ArrayDataModel.prototype.updateIndex.apply(this, arguments); | |
| 145 | |
| 146 if (this.sortStatus.field) | |
| 147 this.sort(this.sortStatus.field, this.sortStatus.direction); | |
| 148 }, | |
| 149 | |
| 150 /** | |
| 151 * Creates sort status with given field and direction. | |
| 152 * @param {string} field Sort field. | |
| 153 * @param {string} direction Sort direction. | |
| 154 * @return {!Object} Created sort status. | |
| 155 */ | |
| 156 createSortStatus: function(field, direction) { | |
| 157 return { | |
| 158 field: field, | |
| 159 direction: direction | |
| 160 }; | |
| 161 }, | |
| 162 | |
| 163 /** | |
| 164 * Called before a sort happens so that you may fetch additional data | |
| 165 * required for the sort. | |
| 166 * | |
| 167 * @param {string} field Sort field. | |
| 168 * @param {function()} callback The function to invoke when preparation | |
| 169 * is complete. | |
| 170 */ | |
| 171 prepareSort: function(field, callback) { | |
| 172 callback(); | |
| 173 }, | |
| 174 | |
| 175 /** | |
| 176 * Sorts data model according to given field and direction and dispathes | |
| 177 * sorted event. | |
| 178 * @param {string} field Sort field. | |
| 179 * @param {string} direction Sort direction. | |
| 180 */ | |
| 181 sort: function(field, direction) { | |
| 182 var self = this; | |
| 183 | |
| 184 this.prepareSort(field, function() { | |
| 185 var sortPermutation = self.doSort_(field, direction); | |
| 186 self.dispatchSortEvent_(sortPermutation); | |
| 187 }); | |
| 188 }, | |
| 189 | |
| 190 /** | |
| 191 * Sorts data model according to given field and direction. | |
| 192 * @param {string} field Sort field. | |
| 193 * @param {string} direction Sort direction. | |
| 194 */ | |
| 195 doSort_: function(field, direction) { | |
| 196 var compareFunction = this.sortFunction_(field, direction); | |
| 197 var positions = []; | |
| 198 for (var i = 0; i < this.length; i++) { | |
| 199 positions[this.indexes_[i]] = i; | |
| 200 } | |
| 201 this.indexes_.sort(compareFunction); | |
| 202 this.sortStatus_ = this.createSortStatus(field, direction); | |
| 203 var sortPermutation = []; | |
| 204 for (var i = 0; i < this.length; i++) { | |
| 205 sortPermutation[positions[this.indexes_[i]]] = i; | |
| 206 } | |
| 207 return sortPermutation; | |
| 208 }, | |
| 209 | |
| 210 dispatchSortEvent_: function(sortPermutation) { | |
| 211 var e = new Event('sorted'); | |
| 212 e.sortPermutation = sortPermutation; | |
| 213 this.dispatchEvent(e); | |
| 214 }, | |
| 215 | |
| 216 /** | |
| 217 * Creates compare function for the field. | |
| 218 * Returns the function set as sortFunction for given field | |
| 219 * or default compare function | |
| 220 * @param {string} field Sort field. | |
| 221 * @param {Function(*, *): number} Compare function. | |
| 222 */ | |
| 223 createCompareFunction_: function(field) { | |
| 224 var compareFunction = | |
| 225 this.compareFunctions_ ? this.compareFunctions_[field] : null; | |
| 226 var defaultValuesCompareFunction = this.defaultValuesCompareFunction; | |
| 227 if (compareFunction) { | |
| 228 return compareFunction; | |
| 229 } else { | |
| 230 return function(a, b) { | |
| 231 return defaultValuesCompareFunction.call(null, a[field], b[field]); | |
| 232 } | |
| 233 } | |
| 234 return compareFunction; | |
| 235 }, | |
| 236 | |
| 237 /** | |
| 238 * Creates compare function for given field and direction. | |
| 239 * @param {string} field Sort field. | |
| 240 * @param {string} direction Sort direction. | |
| 241 * @param {Function(*, *): number} Compare function. | |
| 242 */ | |
| 243 sortFunction_: function(field, direction) { | |
| 244 var compareFunction = this.createCompareFunction_(field); | |
| 245 var dirMultiplier = direction == 'desc' ? -1 : 1; | |
| 246 | |
| 247 return function(index1, index2) { | |
| 248 var item1 = this.getItemByUnsortedIndex_(index1); | |
| 249 var item2 = this.getItemByUnsortedIndex_(index2); | |
| 250 | |
| 251 var compareResult = compareFunction.call(null, item1, item2); | |
| 252 if (compareResult != 0) | |
| 253 return dirMultiplier * compareResult; | |
| 254 return dirMultiplier * this.defaultValuesCompareFunction(index1, | |
| 255 index2); | |
| 256 }.bind(this); | |
| 257 }, | |
| 258 | |
| 259 /** | |
| 260 * Default compare function. | |
| 261 */ | |
| 262 defaultValuesCompareFunction: function(a, b) { | |
| 263 // We could insert i18n comparisons here. | |
| 264 if (a < b) | |
| 265 return -1; | |
| 266 if (a > b) | |
| 267 return 1; | |
| 268 return 0; | |
| 269 } | |
| 270 }; | |
| 271 | |
| 272 return { | |
| 273 TableDataModel: TableDataModel | |
| 274 }; | |
| 275 }); | |
| OLD | NEW |