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

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: Merged with http://codereview.chromium.org/7062001/ to fix this bug. 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 sorted
42 * array.
36 * @param {number} index The index of the element to get. 43 * @param {number} index The index of the element to get.
37 * @return {*} The element at the given index. 44 * @return {*} The element at the given index.
38 */ 45 */
39 item: function(index) { 46 item: function(index) {
40 return this.array_[index]; 47 if (index >= 0 && index < this.length)
48 return this.array_[this.indexes_[index]];
49 return undefined;
41 }, 50 },
42 51
43 /** 52 /**
53 * Returns compare function set for given field.
54 * @param {string} field The field to get compare function for.
55 * @return {function(*, *): number} Compare function set for given field.
56 */
57 compareFunction: function(field) {
58 return this.compareFunctions_[field];
59 },
60
61 /**
62 * Sets compare function for given field.
63 * @param {string} field The field to set compare function.
64 * @param {function(*, *): number} Compare function to set for given field.
65 */
66 setCompareFunction: function(field, compareFunction) {
67 this.compareFunctions_[field] = compareFunction;
68 },
69
70 /**
71 * Returns current sort status.
72 * @return {!Object} Current sort status.
73 */
74 get sortStatus() {
75 if (this.sortStatus_) {
76 return this.createSortStatus(
77 this.sortStatus_.field, this.sortStatus_.direction);
78 } else {
79 return this.createSortStatus(null, null);
80 }
81 },
82
dhollowa 2011/05/24 15:45:49 nit: extra spaces here.
vsevik 2011/05/25 18:12:04 Done.
83
84
85 /**
44 * Returns the first matching item. 86 * Returns the first matching item.
45 * @param {*} item The item to find. 87 * @param {*} item The item to find.
46 * @param {number=} opt_fromIndex If provided, then the searching start at 88 * @param {number=} opt_fromIndex If provided, then the searching start at
47 * the {@code opt_fromIndex}. 89 * the {@code opt_fromIndex}.
48 * @return {number} The index of the first found element or -1 if not found. 90 * @return {number} The index of the first found element or -1 if not found.
49 */ 91 */
50 indexOf: function(item, opt_fromIndex) { 92 indexOf: function(item, opt_fromIndex) {
51 return this.array_.indexOf(item, opt_fromIndex); 93 return this.array_.indexOf(item, opt_fromIndex);
52 }, 94 },
53 95
54 /** 96 /**
55 * Returns an array of elements in a selected range. 97 * Returns an array of elements in a selected range.
56 * @param {number=} opt_from The starting index of the selected range. 98 * @param {number=} opt_from The starting index of the selected range.
57 * @param {number=} opt_to The ending index of selected range. 99 * @param {number=} opt_to The ending index of selected range.
58 * @return {Array} An array of elements in the selected range. 100 * @return {Array} An array of elements in the selected range.
59 */ 101 */
60 slice: function(opt_from, opt_to) { 102 slice: function(opt_from, opt_to) {
61 return this.array_.slice.apply(this.array_, arguments); 103 return this.array_.slice.apply(this.array_, arguments);
62 }, 104 },
63 105
64 /** 106 /**
65 * This removes and adds items to the model. 107 * This removes and adds items to the model.
66 *
67 * This dispatches a splice event. 108 * This dispatches a splice event.
68 * 109 * This implementation runs sort after splice and creates permutation for
110 * the whole change.
69 * @param {number} index The index of the item to update. 111 * @param {number} index The index of the item to update.
70 * @param {number} deleteCount The number of items to remove. 112 * @param {number} deleteCount The number of items to remove.
71 * @param {...*} The items to add. 113 * @param {...*} The items to add.
72 * @return {!Array} An array with the removed items. 114 * @return {!Array} An array with the removed items.
73 */ 115 */
74 splice: function(index, deleteCount, var_args) { 116 splice: function(index, deleteCount, var_args) {
117 var addCount = arguments.length - 2;
118 var newIndexes = [];
119 var deletePermutation = [];
120 var deleted = 0;
121 for (var i = 0; i < this.indexes_.length; i++) {
122 var oldIndex = this.indexes_[i];
123 if (oldIndex < index) {
124 newIndexes.push(oldIndex);
125 deletePermutation.push(i - deleted);
126 } else if (oldIndex >= index + deleteCount) {
127 newIndexes.push(oldIndex - deleteCount + addCount);
128 deletePermutation.push(i - deleted);
129 } else {
130 deletePermutation.push(-1);
131 deleted++;
132 }
133 }
134 for (var i = 0; i < addCount; i++) {
135 newIndexes.push(index + i);
136 }
137 this.indexes_ = newIndexes;
138
75 var arr = this.array_; 139 var arr = this.array_;
76 140
77 // TODO(arv): Maybe unify splice and change events? 141 // TODO(arv): Maybe unify splice and change events?
78 var e = new Event('splice'); 142 var spliceEvent = new Event('splice');
79 e.index = index; 143 spliceEvent.index = index;
80 e.removed = arr.slice(index, index + deleteCount); 144 spliceEvent.removed = arr.slice(index, index + deleteCount);
81 e.added = Array.prototype.slice.call(arguments, 2); 145 spliceEvent.added = Array.prototype.slice.call(arguments, 2);
82 146
83 var rv = arr.splice.apply(arr, arguments); 147 var rv = arr.splice.apply(arr, arguments);
84 148
85 this.dispatchEvent(e); 149 // if sortStatus.field is null, this restores original order.
150 var sortPermutation = this.doSort_(this.sortStatus.field,
151 this.sortStatus.direction);
152 if (sortPermutation) {
153 var splicePermutation = deletePermutation.map(function(element) {
154 return element != -1 ? sortPermutation[element] : -1;
155 });
156 this.dispatchPermutedEvent_(splicePermutation);
157 } else {
158 this.dispatchPermutedEvent_(deletePermutation);
159 }
86 160
161 this.dispatchEvent(spliceEvent);
87 return rv; 162 return rv;
88 }, 163 },
89 164
90 /** 165 /**
91 * Appends items to the end of the model. 166 * Appends items to the end of the model.
92 * 167 *
93 * This dispatches a splice event. 168 * This dispatches a splice event.
94 * 169 *
95 * @param {...*} The items to append. 170 * @param {...*} The items to append.
96 * @return {number} The new length of the model. 171 * @return {number} The new length of the model.
97 */ 172 */
98 push: function(var_args) { 173 push: function(var_args) {
99 var args = Array.prototype.slice.call(arguments); 174 var args = Array.prototype.slice.call(arguments);
100 args.unshift(this.length, 0); 175 args.unshift(this.length, 0);
101 this.splice.apply(this, args); 176 this.splice.apply(this, args);
102 return this.length; 177 return this.length;
103 }, 178 },
104 179
105 /** 180 /**
106 * Use this to update a given item in the array. This does not remove and 181 * Use this to update a given item in the array. This does not remove and
107 * reinsert a new item. 182 * reinsert a new item.
108 *
109 * This dispatches a change event. 183 * This dispatches a change event.
110 * 184 * This runs sort after updating.
111 * @param {number} index The index of the item to update. 185 * @param {number} index The index of the item to update.
112 */ 186 */
113 updateIndex: function(index) { 187 updateIndex: function(index) {
114 if (index < 0 || index >= this.length) 188 if (index < 0 || index >= this.length)
115 throw Error('Invalid index, ' + index); 189 throw Error('Invalid index, ' + index);
116 190
117 // TODO(arv): Maybe unify splice and change events? 191 // TODO(arv): Maybe unify splice and change events?
118 var e = new Event('change'); 192 var e = new Event('change');
119 e.index = index; 193 e.index = index;
120 this.dispatchEvent(e); 194 this.dispatchEvent(e);
195
196 if (this.sortStatus.field) {
197 var sortPermutation = this.doSort_(this.sortStatus.field,
198 this.sortStatus.direction);
199 if (sortPermutation)
200 this.dispatchPermutedEvent_(sortPermutation);
201 }
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 if (sortPermutation)
241 self.dispatchPermutedEvent_(sortPermutation);
242 self.dispatchSortEvent_();
243 });
244 },
245
246 /**
247 * Sorts data model according to given field and direction.
248 * @param {string} field Sort field.
249 * @param {string} direction Sort direction.
250 */
251 doSort_: function(field, direction) {
252 var compareFunction = this.sortFunction_(field, direction);
253 var positions = [];
254 for (var i = 0; i < this.length; i++) {
255 positions[this.indexes_[i]] = i;
256 }
257 this.indexes_.sort(compareFunction);
258 this.sortStatus_ = this.createSortStatus(field, direction);
259 var sortPermutation = [];
260 var changed = false;
261 for (var i = 0; i < this.length; i++) {
262 if (positions[this.indexes_[i]] != i)
263 changed = true;
264 sortPermutation[positions[this.indexes_[i]]] = i;
265 }
266 if (changed)
267 return sortPermutation;
268 return null;
269 },
270
271 dispatchSortEvent_: function() {
272 var e = new Event('sorted');
273 this.dispatchEvent(e);
274 },
275
276 dispatchPermutedEvent_: function(permutation) {
277 var e = new Event('permuted');
278 e.permutation = permutation;
279 e.newLength = this.length;
280 this.dispatchEvent(e);
281 },
282
283 /**
284 * Creates compare function for the field.
285 * Returns the function set as sortFunction for given field
286 * or default compare function
287 * @param {string} field Sort field.
288 * @param {function(*, *): number} Compare function.
289 */
290 createCompareFunction_: function(field) {
291 var compareFunction =
292 this.compareFunctions_ ? this.compareFunctions_[field] : null;
293 var defaultValuesCompareFunction = this.defaultValuesCompareFunction;
294 if (compareFunction) {
295 return compareFunction;
296 } else {
297 return function(a, b) {
298 return defaultValuesCompareFunction.call(null, a[field], b[field]);
299 }
300 }
301 return compareFunction;
302 },
303
304 /**
305 * Creates compare function for given field and direction.
306 * @param {string} field Sort field.
307 * @param {string} direction Sort direction.
308 * @param {function(*, *): number} Compare function.
309 */
310 sortFunction_: function(field, direction) {
311 var compareFunction = null;
312 if (field !== null)
313 compareFunction = this.createCompareFunction_(field);
314 var dirMultiplier = direction == 'desc' ? -1 : 1;
315
316 return function(index1, index2) {
317 var item1 = this.array_[index1];
318 var item2 = this.array_[index2];
319
320 var compareResult = 0;
321 if (typeof(compareFunction) === 'function')
322 compareResult = compareFunction.call(null, item1, item2);
323 if (compareResult != 0)
324 return dirMultiplier * compareResult;
325 return dirMultiplier * this.defaultValuesCompareFunction(index1,
326 index2);
327 }.bind(this);
328 },
329
330 /**
331 * Default compare function.
332 */
333 defaultValuesCompareFunction: function(a, b) {
334 // We could insert i18n comparisons here.
335 if (a < b)
336 return -1;
337 if (a > b)
338 return 1;
339 return 0;
121 } 340 }
122 }; 341 };
123 342
124 return { 343 return {
125 ArrayDataModel: ArrayDataModel 344 ArrayDataModel: ArrayDataModel
126 }; 345 };
127 }); 346 });
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