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