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 |