• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 */
12function NavigationModelItem(label) {
13  this.label_ = label;
14}
15
16NavigationModelItem.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 */
28function NavigationModelShortcutItem(label, entry) {
29  NavigationModelItem.call(this, label);
30  this.entry_ = entry;
31  Object.freeze(this);
32}
33
34NavigationModelShortcutItem.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 */
49function NavigationModelVolumeItem(label, volumeInfo) {
50  NavigationModelItem.call(this, label);
51  this.volumeInfo_ = volumeInfo;
52  // Start resolving 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
59NavigationModelVolumeItem.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 */
73function 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 */
218NavigationListModel.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 */
229NavigationListModel.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 */
241NavigationListModel.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 */
252NavigationListModel.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 of the items is not found on the filesystem.
262 * @param {NavigationModelItem} modelItem The entry which is not found.
263 */
264NavigationListModel.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};
274