Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / ui / file_manager / file_manager / foreground / js / navigation_list_model.js
1 // Copyright (c) 2012 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  * Base item of NavigationListModel. Should not be created directly.
7  * @param {string} label Label.
8  * @constructor
9  */
10 function NavigationModelItem(label) {
11   this.label_ = label;
12 }
13
14 NavigationModelItem.prototype = {
15   get label() { return this.label_; }
16 };
17
18 /**
19  * Check whether given two model items are same.
20  * @param {NavigationModelItem} item1 The first item to be compared.
21  * @param {NavigationModelItem} item2 The second item to be compared.
22  * @return {boolean} True if given two model items are same.
23  */
24 NavigationModelItem.isSame = function(item1, item2) {
25   if (item1.isVolume != item2.isVolume)
26     return false;
27
28   if (item1.isVolume)
29     return item1.volumeInfo === item2.volumeInfo;
30   else
31     return util.isSameEntry(item1.entry, item2.entry);
32 };
33
34 /**
35  * Item of NavigationListModel for shortcuts.
36  *
37  * @param {string} label Label.
38  * @param {!DirectoryEntry} entry Entry. Cannot be null.
39  * @constructor
40  * @extends {NavigationModelItem}
41  */
42 function NavigationModelShortcutItem(label, entry) {
43   NavigationModelItem.call(this, label);
44   this.entry_ = entry;
45   Object.freeze(this);
46 }
47
48 NavigationModelShortcutItem.prototype = {
49   __proto__: NavigationModelItem.prototype,
50   get entry() { return this.entry_; },
51   get isVolume() { return false; },
52   get isShortcut() { return true; }
53 };
54
55 /**
56  * Item of NavigationListModel for volumes.
57  *
58  * @param {string} label Label.
59  * @param {!VolumeInfo} volumeInfo Volume info for the volume. Cannot be null.
60  * @constructor
61  * @extends {NavigationModelItem}
62  */
63 function NavigationModelVolumeItem(label, volumeInfo) {
64   NavigationModelItem.call(this, label);
65   this.volumeInfo_ = volumeInfo;
66   // Start resolving the display root because it is used
67   // for determining executability of commands.
68   this.volumeInfo_.resolveDisplayRoot(
69       function() {}, function() {});
70   Object.freeze(this);
71 }
72
73 NavigationModelVolumeItem.prototype = {
74   __proto__: NavigationModelItem.prototype,
75   get volumeInfo() { return this.volumeInfo_; },
76   get isVolume() { return true; },
77   get isShortcut() { return false; }
78 };
79
80 /**
81  * A navigation list model. This model combines the 2 lists.
82  * @param {VolumeManagerWrapper} volumeManager VolumeManagerWrapper instance.
83  * @param {(cr.ui.ArrayDataModel|FolderShortcutsDataModel)} shortcutListModel
84  *     The list of folder shortcut.
85  * @constructor
86  * @extends {cr.EventTarget}
87  */
88 function NavigationListModel(volumeManager, shortcutListModel) {
89   cr.EventTarget.call(this);
90
91   this.volumeManager_ = volumeManager;
92   this.shortcutListModel_ = shortcutListModel;
93
94   var volumeInfoToModelItem = function(volumeInfo) {
95     return new NavigationModelVolumeItem(
96         volumeInfo.label,
97         volumeInfo);
98   }.bind(this);
99
100   var entryToModelItem = function(entry) {
101     var item = new NavigationModelShortcutItem(
102         entry.name,
103         entry);
104     return item;
105   }.bind(this);
106
107   /**
108    * Type of updated list.
109    * @enum {number}
110    * @const
111    */
112   var ListType = {
113     VOLUME_LIST: 1,
114     SHORTCUT_LIST: 2
115   };
116   Object.freeze(ListType);
117
118   // Generates this.volumeList_ and this.shortcutList_ from the models.
119   this.volumeList_ =
120       this.volumeManager_.volumeInfoList.slice().map(volumeInfoToModelItem);
121
122   this.shortcutList_ = [];
123   for (var i = 0; i < this.shortcutListModel_.length; i++) {
124     var shortcutEntry = /** @type {Entry} */ (this.shortcutListModel_.item(i));
125     var volumeInfo = this.volumeManager_.getVolumeInfo(shortcutEntry);
126     this.shortcutList_.push(entryToModelItem(shortcutEntry));
127   }
128
129   // Generates a combined 'permuted' event from an event of either list.
130   var permutedHandler = function(listType, event) {
131     var permutation;
132
133     // Build the volumeList.
134     if (listType == ListType.VOLUME_LIST) {
135       // The volume is mounted or unmounted.
136       var newList = [];
137
138       // Use the old instances if they just move.
139       for (var i = 0; i < event.permutation.length; i++) {
140         if (event.permutation[i] >= 0)
141           newList[event.permutation[i]] = this.volumeList_[i];
142       }
143
144       // Create missing instances.
145       for (var i = 0; i < event.newLength; i++) {
146         if (!newList[i]) {
147           newList[i] = volumeInfoToModelItem(
148               this.volumeManager_.volumeInfoList.item(i));
149         }
150       }
151       this.volumeList_ = newList;
152
153       permutation = event.permutation.slice();
154
155       // shortcutList part has not been changed, so the permutation should be
156       // just identity mapping with a shift.
157       for (var i = 0; i < this.shortcutList_.length; i++) {
158         permutation.push(i + this.volumeList_.length);
159       }
160     } else {
161       // Build the shortcutList.
162
163       // volumeList part has not been changed, so the permutation should be
164       // identity mapping.
165
166       permutation = [];
167       for (var i = 0; i < this.volumeList_.length; i++) {
168         permutation[i] = i;
169       }
170
171       var modelIndex = 0;
172       var oldListIndex = 0;
173       var newList = [];
174       while (modelIndex < this.shortcutListModel_.length &&
175              oldListIndex < this.shortcutList_.length) {
176         var shortcutEntry = this.shortcutListModel_.item(modelIndex);
177         var cmp = this.shortcutListModel_.compare(
178             shortcutEntry, this.shortcutList_[oldListIndex].entry);
179         if (cmp > 0) {
180           // The shortcut at shortcutList_[oldListIndex] is removed.
181           permutation.push(-1);
182           oldListIndex++;
183           continue;
184         }
185
186         if (cmp === 0) {
187           // Reuse the old instance.
188           permutation.push(newList.length + this.volumeList_.length);
189           newList.push(this.shortcutList_[oldListIndex]);
190           oldListIndex++;
191         } else {
192           // We needs to create a new instance for the shortcut entry.
193           newList.push(entryToModelItem(shortcutEntry));
194         }
195         modelIndex++;
196       }
197
198       // Add remaining (new) shortcuts if necessary.
199       for (; modelIndex < this.shortcutListModel_.length; modelIndex++) {
200         var shortcutEntry = this.shortcutListModel_.item(modelIndex);
201         newList.push(entryToModelItem(shortcutEntry));
202       }
203
204       // Fill remaining permutation if necessary.
205       for (; oldListIndex < this.shortcutList_.length; oldListIndex++)
206         permutation.push(-1);
207
208       this.shortcutList_ = newList;
209     }
210
211     // Dispatch permuted event.
212     var permutedEvent = new Event('permuted');
213     permutedEvent.newLength =
214         this.volumeList_.length + this.shortcutList_.length;
215     permutedEvent.permutation = permutation;
216     this.dispatchEvent(permutedEvent);
217   };
218
219   this.volumeManager_.volumeInfoList.addEventListener(
220       'permuted', permutedHandler.bind(this, ListType.VOLUME_LIST));
221   this.shortcutListModel_.addEventListener(
222       'permuted', permutedHandler.bind(this, ListType.SHORTCUT_LIST));
223
224   // 'change' event is just ignored, because it is not fired neither in
225   // the folder shortcut list nor in the volume info list.
226   // 'splice' and 'sorted' events are not implemented, since they are not used
227   // in list.js.
228 }
229
230 /**
231  * NavigationList inherits cr.EventTarget.
232  */
233 NavigationListModel.prototype = {
234   __proto__: cr.EventTarget.prototype,
235   get length() { return this.length_(); },
236   get folderShortcutList() { return this.shortcutList_; }
237 };
238
239 /**
240  * Returns the item at the given index.
241  * @param {number} index The index of the entry to get.
242  * @return {NavigationModelItem} The item at the given index.
243  */
244 NavigationListModel.prototype.item = function(index) {
245   var offset = this.volumeList_.length;
246   if (index < offset)
247     return this.volumeList_[index];
248   return this.shortcutList_[index - offset];
249 };
250
251 /**
252  * Returns the number of items in the model.
253  * @return {number} The length of the model.
254  * @private
255  */
256 NavigationListModel.prototype.length_ = function() {
257   return this.volumeList_.length + this.shortcutList_.length;
258 };
259
260 /**
261  * Returns the first matching item.
262  * @param {NavigationModelItem} modelItem The entry to find.
263  * @param {number=} opt_fromIndex If provided, then the searching start at
264  *     the {@code opt_fromIndex}.
265  * @return {number} The index of the first found element or -1 if not found.
266  */
267 NavigationListModel.prototype.indexOf = function(modelItem, opt_fromIndex) {
268   for (var i = opt_fromIndex || 0; i < this.length; i++) {
269     if (modelItem === this.item(i))
270       return i;
271   }
272   return -1;
273 };
274
275 /**
276  * Called externally when one of the items is not found on the filesystem.
277  * @param {NavigationModelItem} modelItem The entry which is not found.
278  */
279 NavigationListModel.prototype.onItemNotFoundError = function(modelItem) {
280   if (modelItem.isVolume) {
281     // TODO(mtomasz, yoshiki): Implement when needed.
282     return;
283   }
284   if (modelItem.isShortcut) {
285     // For shortcuts, lets the shortcut model handle this situation.
286     this.shortcutListModel_.onItemNotFoundError(modelItem.entry);
287   }
288 };