Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(238)

Side by Side Diff: chrome/browser/resources/shared/js/cr/ui/array_data_model.js

Issue 7038024: Move sorting logic from table to list. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fixes Created 9 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 /** 5 /**
6 * @fileoverview This is a data model representin 6 * @fileoverview This is a data model representin
7 */ 7 */
8 8
9 cr.define('cr.ui', function() { 9 cr.define('cr.ui', function() {
10 const EventTarget = cr.EventTarget; 10 const EventTarget = cr.EventTarget;
11 const Event = cr.Event; 11 const Event = cr.Event;
12 12
13 /** 13 /**
14 * A data model that wraps a simple array. 14 * A data model that wraps a simple array and supports sorting by storing
15 * initial indexes of elements for each position in sorted array.
15 * @param {!Array} array The underlying array. 16 * @param {!Array} array The underlying array.
16 * @constructor 17 * @constructor
17 * @extends {EventTarget} 18 * @extends {EventTarget}
18 */ 19 */
19 function ArrayDataModel(array) { 20 function ArrayDataModel(array) {
20 this.array_ = array; 21 this.array_ = array;
22 this.indexes_ = [];
23 for (var i = 0; i < array.length; i++) {
24 this.indexes_.push(i);
25 }
21 } 26 }
22 27
23 ArrayDataModel.prototype = { 28 ArrayDataModel.prototype = {
24 __proto__: EventTarget.prototype, 29 __proto__: EventTarget.prototype,
25 30
26 /** 31 /**
27 * The length of the data model. 32 * The length of the data model.
28 * @type {number} 33 * @type {number}
29 */ 34 */
30 get length() { 35 get length() {
31 return this.array_.length; 36 return this.array_.length;
32 }, 37 },
33 38
34 /** 39 /**
35 * Returns the item at the given index. 40 * Returns the item at the given index.
41 * This implementation returns the item at the given index in the source
42 * array before sort.
43 * @param {number} index The index of the element to get.
44 * @return {*} The element at the given index.
45 */
46 getItemByUnsortedIndex_: function(unsortedIndex) {
arv (Not doing code reviews) 2011/05/19 16:17:56 Why is this needed?
vsevik 2011/05/19 20:07:02 Inlined.
47 return this.array_[unsortedIndex];
48 },
49
50 /**
51 * Returns the item at the given index.
52 * This implementation returns the item at the given index in the sorted
53 * array.
36 * @param {number} index The index of the element to get. 54 * @param {number} index The index of the element to get.
37 * @return {*} The element at the given index. 55 * @return {*} The element at the given index.
38 */ 56 */
39 item: function(index) { 57 item: function(index) {
40 return this.array_[index]; 58 if (index >= 0 && index < this.length)
59 return this.getItemByUnsortedIndex_(this.indexes_[index]);
60 return undefined;
41 }, 61 },
42 62
43 /** 63 /**
64 * Returns compare function set for given field.
65 * @param {string} field The field to get compare function for.
66 * @return {Function(*, *): number} Compare function set for given field.
arv (Not doing code reviews) 2011/05/19 16:17:56 {function(*, *) : number}
vsevik 2011/05/19 20:07:02 Done.
67 */
68 compareFunction: function(field) {
69 return this.compareFunctions_[field];
70 },
71
72 /**
73 * Sets compare function for given field.
74 * @param {string} field The field to set compare function.
75 * @param {Function(*, *): number} Compare function to set for given field.
76 */
77 setCompareFunction: function(field, compareFunction) {
78 this.compareFunctions_[field] = compareFunction;
79 },
80
81 /**
82 * Returns current sort status.
83 * @return {!Object} Current sort status.
84 */
85 get sortStatus() {
86 if (this.sortStatus_) {
87 return this.createSortStatus(
88 this.sortStatus_.field, this.sortStatus_.direction);
89 } else {
90 return this.createSortStatus(null, null);
91 }
92 },
93
94
95
96 /**
44 * Returns the first matching item. 97 * Returns the first matching item.
45 * @param {*} item The item to find. 98 * @param {*} item The item to find.
46 * @param {number=} opt_fromIndex If provided, then the searching start at 99 * @param {number=} opt_fromIndex If provided, then the searching start at
47 * the {@code opt_fromIndex}. 100 * the {@code opt_fromIndex}.
48 * @return {number} The index of the first found element or -1 if not found. 101 * @return {number} The index of the first found element or -1 if not found.
49 */ 102 */
50 indexOf: function(item, opt_fromIndex) { 103 indexOf: function(item, opt_fromIndex) {
51 return this.array_.indexOf(item, opt_fromIndex); 104 return this.array_.indexOf(item, opt_fromIndex);
52 }, 105 },
53 106
54 /** 107 /**
55 * Returns an array of elements in a selected range. 108 * Returns an array of elements in a selected range.
56 * @param {number=} opt_from The starting index of the selected range. 109 * @param {number=} opt_from The starting index of the selected range.
57 * @param {number=} opt_to The ending index of selected range. 110 * @param {number=} opt_to The ending index of selected range.
58 * @return {Array} An array of elements in the selected range. 111 * @return {Array} An array of elements in the selected range.
59 */ 112 */
60 slice: function(opt_from, opt_to) { 113 slice: function(opt_from, opt_to) {
61 return this.array_.slice.apply(this.array_, arguments); 114 return this.array_.slice.apply(this.array_, arguments);
62 }, 115 },
63 116
64 /** 117 /**
65 * This removes and adds items to the model. 118 * This removes and adds items to the model.
66 *
67 * This dispatches a splice event. 119 * This dispatches a splice event.
68 * 120 * This implementation runs sort after splice and creates permutation for
121 * the whole change.
69 * @param {number} index The index of the item to update. 122 * @param {number} index The index of the item to update.
70 * @param {number} deleteCount The number of items to remove. 123 * @param {number} deleteCount The number of items to remove.
71 * @param {...*} The items to add. 124 * @param {...*} The items to add.
72 * @return {!Array} An array with the removed items. 125 * @return {!Array} An array with the removed items.
73 */ 126 */
74 splice: function(index, deleteCount, var_args) { 127 splice: function(index, deleteCount, var_args) {
128 var addCount = arguments.length - 2;
arv (Not doing code reviews) 2011/05/19 16:17:56 Can we skip this work in case there isn't any sort
vsevik 2011/05/19 20:07:02 As discussed, I've done benchmarking that showed r
129 var newIndexes = [];
130 var deletePermutation = [];
131 var deleted = 0;
132 for (var i = 0; i < this.indexes_.length; i++) {
133 var oldIndex = this.indexes_[i];
134 if (oldIndex < index) {
135 newIndexes.push(oldIndex);
136 deletePermutation.push(i - deleted);
137 } else if (oldIndex >= index + deleteCount) {
138 newIndexes.push(oldIndex - deleteCount + addCount);
139 deletePermutation.push(i - deleted);
140 } else {
141 deletePermutation.push(-1);
142 deleted++;
143 }
144 }
145 for (var i = 0; i < addCount; i++) {
146 newIndexes.push(index + i);
147 }
148 this.indexes_ = newIndexes;
149
75 var arr = this.array_; 150 var arr = this.array_;
76 151
77 // TODO(arv): Maybe unify splice and change events? 152 // TODO(arv): Maybe unify splice and change events?
78 var e = new Event('splice'); 153 var spliceEvent = new Event('splice');
79 e.index = index; 154 spliceEvent.index = index;
80 e.removed = arr.slice(index, index + deleteCount); 155 spliceEvent.removed = arr.slice(index, index + deleteCount);
81 e.added = Array.prototype.slice.call(arguments, 2); 156 spliceEvent.added = Array.prototype.slice.call(arguments, 2);
82 157
83 var rv = arr.splice.apply(arr, arguments); 158 var rv = arr.splice.apply(arr, arguments);
84 159
85 this.dispatchEvent(e); 160 this.dispatchPermutedEvent_(deletePermutation);
86 161
162 // if sortStatus.field is null, this restores original order.
163 this.doSort_(this.sortStatus.field, this.sortStatus.direction);
164
165 this.dispatchEvent(spliceEvent);
87 return rv; 166 return rv;
88 }, 167 },
89 168
90 /** 169 /**
91 * Appends items to the end of the model. 170 * Appends items to the end of the model.
92 * 171 *
93 * This dispatches a splice event. 172 * This dispatches a splice event.
94 * 173 *
95 * @param {...*} The items to append. 174 * @param {...*} The items to append.
96 * @return {number} The new length of the model. 175 * @return {number} The new length of the model.
97 */ 176 */
98 push: function(var_args) { 177 push: function(var_args) {
99 var args = Array.prototype.slice.call(arguments); 178 var args = Array.prototype.slice.call(arguments);
100 args.unshift(this.length, 0); 179 args.unshift(this.length, 0);
101 this.splice.apply(this, args); 180 this.splice.apply(this, args);
102 return this.length; 181 return this.length;
103 }, 182 },
104 183
105 /** 184 /**
106 * Use this to update a given item in the array. This does not remove and 185 * Use this to update a given item in the array. This does not remove and
107 * reinsert a new item. 186 * reinsert a new item.
108 *
109 * This dispatches a change event. 187 * This dispatches a change event.
110 * 188 * This runs sort after updating.
111 * @param {number} index The index of the item to update. 189 * @param {number} index The index of the item to update.
112 */ 190 */
113 updateIndex: function(index) { 191 updateIndex: function(index) {
114 if (index < 0 || index >= this.length) 192 if (index < 0 || index >= this.length)
115 throw Error('Invalid index, ' + index); 193 throw Error('Invalid index, ' + index);
116 194
117 // TODO(arv): Maybe unify splice and change events? 195 // TODO(arv): Maybe unify splice and change events?
118 var e = new Event('change'); 196 var e = new Event('change');
119 e.index = index; 197 e.index = index;
120 this.dispatchEvent(e); 198 this.dispatchEvent(e);
199
200 if (this.sortStatus.field)
201 this.doSort_(this.sortStatus.field, this.sortStatus.direction);
202 },
203
204 /**
205 * Creates sort status with given field and direction.
206 * @param {string} field Sort field.
207 * @param {string} direction Sort direction.
208 * @return {!Object} Created sort status.
209 */
210 createSortStatus: function(field, direction) {
211 return {
212 field: field,
213 direction: direction
214 };
215 },
216
217 /**
218 * Called before a sort happens so that you may fetch additional data
219 * required for the sort.
220 *
221 * @param {string} field Sort field.
222 * @param {function()} callback The function to invoke when preparation
223 * is complete.
224 */
225 prepareSort: function(field, callback) {
226 callback();
227 },
228
229 /**
230 * Sorts data model according to given field and direction and dispathes
231 * sorted event.
232 * @param {string} field Sort field.
233 * @param {string} direction Sort direction.
234 */
235 sort: function(field, direction) {
236 var self = this;
237
238 this.prepareSort(field, function() {
239 var sortPermutation = self.doSort_(field, direction);
240 self.dispatchSortEvent_();
241 });
242 },
243
244 /**
245 * Sorts data model according to given field and direction.
246 * @param {string} field Sort field.
247 * @param {string} direction Sort direction.
248 */
249 doSort_: function(field, direction) {
250 var compareFunction = this.sortFunction_(field, direction);
251 var positions = [];
252 for (var i = 0; i < this.length; i++) {
253 positions[this.indexes_[i]] = i;
254 }
255 this.indexes_.sort(compareFunction);
256 this.sortStatus_ = this.createSortStatus(field, direction);
257 var sortPermutation = [];
258 for (var i = 0; i < this.length; i++) {
259 sortPermutation[positions[this.indexes_[i]]] = i;
260 }
261 this.dispatchPermutedEvent_(sortPermutation);
262 },
263
264 dispatchSortEvent_: function() {
265 var e = new Event('sorted');
266 this.dispatchEvent(e);
267 },
268
269 dispatchPermutedEvent_: function(permutation) {
270 var e = new Event('permuted');
271 e.permutation = permutation;
272 this.dispatchEvent(e);
273 },
274
275 /**
276 * Creates compare function for the field.
277 * Returns the function set as sortFunction for given field
278 * or default compare function
279 * @param {string} field Sort field.
280 * @param {Function(*, *): number} Compare function.
281 */
282 createCompareFunction_: function(field) {
283 var compareFunction =
284 this.compareFunctions_ ? this.compareFunctions_[field] : null;
285 var defaultValuesCompareFunction = this.defaultValuesCompareFunction;
286 if (compareFunction) {
287 return compareFunction;
288 } else {
289 return function(a, b) {
290 return defaultValuesCompareFunction.call(null, a[field], b[field]);
291 }
292 }
293 return compareFunction;
294 },
295
296 /**
297 * Creates compare function for given field and direction.
298 * @param {string} field Sort field.
299 * @param {string} direction Sort direction.
300 * @param {Function(*, *): number} Compare function.
301 */
302 sortFunction_: function(field, direction) {
303 var compareFunction = null;
304 if (field !== null)
305 compareFunction = this.createCompareFunction_(field);
306 var dirMultiplier = direction == 'desc' ? -1 : 1;
307
308 return function(index1, index2) {
309 var item1 = this.getItemByUnsortedIndex_(index1);
310 var item2 = this.getItemByUnsortedIndex_(index2);
311
312 var compareResult = 0;
313 if (typeof(compareFunction) === 'function')
314 compareResult = compareFunction.call(null, item1, item2);
315 if (compareResult != 0)
316 return dirMultiplier * compareResult;
317 return dirMultiplier * this.defaultValuesCompareFunction(index1,
318 index2);
319 }.bind(this);
320 },
321
322 /**
323 * Default compare function.
324 */
325 defaultValuesCompareFunction: function(a, b) {
326 // We could insert i18n comparisons here.
327 if (a < b)
328 return -1;
329 if (a > b)
330 return 1;
331 return 0;
121 } 332 }
122 }; 333 };
123 334
124 return { 335 return {
125 ArrayDataModel: ArrayDataModel 336 ArrayDataModel: ArrayDataModel
126 }; 337 };
127 }); 338 });
OLDNEW
« no previous file with comments | « chrome/browser/resources/file_manager/main.html ('k') | chrome/browser/resources/shared/js/cr/ui/list.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698