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

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: Fixed autofill phones. 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
83 /**
44 * Returns the first matching item. 84 * Returns the first matching item.
45 * @param {*} item The item to find. 85 * @param {*} item The item to find.
46 * @param {number=} opt_fromIndex If provided, then the searching start at 86 * @param {number=} opt_fromIndex If provided, then the searching start at
47 * the {@code opt_fromIndex}. 87 * the {@code opt_fromIndex}.
48 * @return {number} The index of the first found element or -1 if not found. 88 * @return {number} The index of the first found element or -1 if not found.
49 */ 89 */
50 indexOf: function(item, opt_fromIndex) { 90 indexOf: function(item, opt_fromIndex) {
51 return this.array_.indexOf(item, opt_fromIndex); 91 return this.array_.indexOf(item, opt_fromIndex);
52 }, 92 },
53 93
54 /** 94 /**
55 * Returns an array of elements in a selected range. 95 * Returns an array of elements in a selected range.
56 * @param {number=} opt_from The starting index of the selected range. 96 * @param {number=} opt_from The starting index of the selected range.
57 * @param {number=} opt_to The ending index of selected range. 97 * @param {number=} opt_to The ending index of selected range.
58 * @return {Array} An array of elements in the selected range. 98 * @return {Array} An array of elements in the selected range.
59 */ 99 */
60 slice: function(opt_from, opt_to) { 100 slice: function(opt_from, opt_to) {
61 return this.array_.slice.apply(this.array_, arguments); 101 return this.array_.slice.apply(this.array_, arguments);
62 }, 102 },
63 103
64 /** 104 /**
65 * This removes and adds items to the model. 105 * This removes and adds items to the model.
66 *
67 * This dispatches a splice event. 106 * This dispatches a splice event.
68 * 107 * This implementation runs sort after splice and creates permutation for
108 * the whole change.
69 * @param {number} index The index of the item to update. 109 * @param {number} index The index of the item to update.
70 * @param {number} deleteCount The number of items to remove. 110 * @param {number} deleteCount The number of items to remove.
71 * @param {...*} The items to add. 111 * @param {...*} The items to add.
72 * @return {!Array} An array with the removed items. 112 * @return {!Array} An array with the removed items.
73 */ 113 */
74 splice: function(index, deleteCount, var_args) { 114 splice: function(index, deleteCount, var_args) {
115 var addCount = arguments.length - 2;
116 var newIndexes = [];
117 var deletePermutation = [];
118 var deleted = 0;
119 for (var i = 0; i < this.indexes_.length; i++) {
120 var oldIndex = this.indexes_[i];
121 if (oldIndex < index) {
122 newIndexes.push(oldIndex);
123 deletePermutation.push(i - deleted);
124 } else if (oldIndex >= index + deleteCount) {
125 newIndexes.push(oldIndex - deleteCount + addCount);
126 deletePermutation.push(i - deleted);
127 } else {
128 deletePermutation.push(-1);
129 deleted++;
130 }
131 }
132 for (var i = 0; i < addCount; i++) {
133 newIndexes.push(index + i);
134 }
135 this.indexes_ = newIndexes;
136
75 var arr = this.array_; 137 var arr = this.array_;
76 138
77 // TODO(arv): Maybe unify splice and change events? 139 // TODO(arv): Maybe unify splice and change events?
78 var e = new Event('splice'); 140 var spliceEvent = new Event('splice');
79 e.index = index; 141 spliceEvent.index = index;
80 e.removed = arr.slice(index, index + deleteCount); 142 spliceEvent.removed = arr.slice(index, index + deleteCount);
81 e.added = Array.prototype.slice.call(arguments, 2); 143 spliceEvent.added = Array.prototype.slice.call(arguments, 2);
82 144
83 var rv = arr.splice.apply(arr, arguments); 145 var rv = arr.splice.apply(arr, arguments);
84 146
85 this.dispatchEvent(e); 147 // if sortStatus.field is null, this restores original order.
148 var sortPermutation = this.doSort_(this.sortStatus.field,
149 this.sortStatus.direction);
150 if (sortPermutation) {
151 var splicePermutation = deletePermutation.map(function(element) {
152 return element != -1 ? sortPermutation[element] : -1;
153 });
154 this.dispatchPermutedEvent_(splicePermutation);
155 } else {
156 this.dispatchPermutedEvent_(deletePermutation);
157 }
86 158
159 this.dispatchEvent(spliceEvent);
87 return rv; 160 return rv;
88 }, 161 },
89 162
90 /** 163 /**
91 * Appends items to the end of the model. 164 * Appends items to the end of the model.
92 * 165 *
93 * This dispatches a splice event. 166 * This dispatches a splice event.
94 * 167 *
95 * @param {...*} The items to append. 168 * @param {...*} The items to append.
96 * @return {number} The new length of the model. 169 * @return {number} The new length of the model.
97 */ 170 */
98 push: function(var_args) { 171 push: function(var_args) {
99 var args = Array.prototype.slice.call(arguments); 172 var args = Array.prototype.slice.call(arguments);
100 args.unshift(this.length, 0); 173 args.unshift(this.length, 0);
101 this.splice.apply(this, args); 174 this.splice.apply(this, args);
102 return this.length; 175 return this.length;
103 }, 176 },
104 177
105 /** 178 /**
106 * Use this to update a given item in the array. This does not remove and 179 * Use this to update a given item in the array. This does not remove and
107 * reinsert a new item. 180 * reinsert a new item.
108 *
109 * This dispatches a change event. 181 * This dispatches a change event.
110 * 182 * This runs sort after updating.
111 * @param {number} index The index of the item to update. 183 * @param {number} index The index of the item to update.
112 */ 184 */
113 updateIndex: function(index) { 185 updateIndex: function(index) {
114 if (index < 0 || index >= this.length) 186 if (index < 0 || index >= this.length)
115 throw Error('Invalid index, ' + index); 187 throw Error('Invalid index, ' + index);
116 188
117 // TODO(arv): Maybe unify splice and change events? 189 // TODO(arv): Maybe unify splice and change events?
118 var e = new Event('change'); 190 var e = new Event('change');
119 e.index = index; 191 e.index = index;
120 this.dispatchEvent(e); 192 this.dispatchEvent(e);
193
194 if (this.sortStatus.field) {
195 var sortPermutation = this.doSort_(this.sortStatus.field,
196 this.sortStatus.direction);
197 if (sortPermutation)
198 this.dispatchPermutedEvent_(sortPermutation);
199 }
200 },
201
202 /**
203 * Creates sort status with given field and direction.
204 * @param {string} field Sort field.
205 * @param {string} direction Sort direction.
206 * @return {!Object} Created sort status.
207 */
208 createSortStatus: function(field, direction) {
209 return {
210 field: field,
211 direction: direction
212 };
213 },
214
215 /**
216 * Called before a sort happens so that you may fetch additional data
217 * required for the sort.
218 *
219 * @param {string} field Sort field.
220 * @param {function()} callback The function to invoke when preparation
221 * is complete.
222 */
223 prepareSort: function(field, callback) {
224 callback();
225 },
226
227 /**
228 * Sorts data model according to given field and direction and dispathes
229 * sorted event.
230 * @param {string} field Sort field.
231 * @param {string} direction Sort direction.
232 */
233 sort: function(field, direction) {
234 var self = this;
235
236 this.prepareSort(field, function() {
237 var sortPermutation = self.doSort_(field, direction);
238 if (sortPermutation)
239 self.dispatchPermutedEvent_(sortPermutation);
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 var changed = false;
259 for (var i = 0; i < this.length; i++) {
260 if (positions[this.indexes_[i]] != i)
261 changed = true;
262 sortPermutation[positions[this.indexes_[i]]] = i;
263 }
264 if (changed)
265 return sortPermutation;
266 return null;
267 },
268
269 dispatchSortEvent_: function() {
270 var e = new Event('sorted');
271 this.dispatchEvent(e);
272 },
273
274 dispatchPermutedEvent_: function(permutation) {
275 var e = new Event('permuted');
276 e.permutation = permutation;
277 e.newLength = this.length;
278 this.dispatchEvent(e);
279 },
280
281 /**
282 * Creates compare function for the field.
283 * Returns the function set as sortFunction for given field
284 * or default compare function
285 * @param {string} field Sort field.
286 * @param {function(*, *): number} Compare function.
287 */
288 createCompareFunction_: function(field) {
289 var compareFunction =
290 this.compareFunctions_ ? this.compareFunctions_[field] : null;
291 var defaultValuesCompareFunction = this.defaultValuesCompareFunction;
292 if (compareFunction) {
293 return compareFunction;
294 } else {
295 return function(a, b) {
296 return defaultValuesCompareFunction.call(null, a[field], b[field]);
297 }
298 }
299 return compareFunction;
300 },
301
302 /**
303 * Creates compare function for given field and direction.
304 * @param {string} field Sort field.
305 * @param {string} direction Sort direction.
306 * @param {function(*, *): number} Compare function.
307 */
308 sortFunction_: function(field, direction) {
309 var compareFunction = null;
310 if (field !== null)
311 compareFunction = this.createCompareFunction_(field);
312 var dirMultiplier = direction == 'desc' ? -1 : 1;
313
314 return function(index1, index2) {
315 var item1 = this.array_[index1];
316 var item2 = this.array_[index2];
317
318 var compareResult = 0;
319 if (typeof(compareFunction) === 'function')
320 compareResult = compareFunction.call(null, item1, item2);
321 if (compareResult != 0)
322 return dirMultiplier * compareResult;
323 return dirMultiplier * this.defaultValuesCompareFunction(index1,
324 index2);
325 }.bind(this);
326 },
327
328 /**
329 * Default compare function.
330 */
331 defaultValuesCompareFunction: function(a, b) {
332 // We could insert i18n comparisons here.
333 if (a < b)
334 return -1;
335 if (a > b)
336 return 1;
337 return 0;
121 } 338 }
122 }; 339 };
123 340
124 return { 341 return {
125 ArrayDataModel: ArrayDataModel 342 ArrayDataModel: ArrayDataModel
126 }; 343 };
127 }); 344 });
OLDNEW
« no previous file with comments | « chrome/browser/resources/options/autofill_options_list.js ('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