1 // Copyright 2013 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.
8 * @extends cr.EventTarget
9 * @param {HTMLDivElement} div Div container for breadcrumbs.
10 * @param {MetadataCache} metadataCache To retrieve metadata.
11 * @param {VolumeManagerWrapper} volumeManager Volume manager.
14 function BreadcrumbsController(div, metadataCache, volumeManager) {
16 this.metadataCache_ = metadataCache;
17 this.volumeManager_ = volumeManager;
21 * Sequence value to skip requests that are out of date.
25 this.showSequence_ = 0;
27 // Register events and seql the object.
28 div.addEventListener('click', this.onClick_.bind(this));
32 * Extends cr.EventTarget.
34 BreadcrumbsController.prototype.__proto__ = cr.EventTarget.prototype;
39 * @param {Entry} entry Target entry.
41 BreadcrumbsController.prototype.show = function(entry) {
42 if (entry === this.entry_)
46 this.bc_.hidden = false;
47 this.bc_.textContent = '';
50 var queue = new AsyncUtil.Queue();
54 // Obtain entries from the target entry to the root.
55 var resolveParent = function(currentEntry, previousEntry, callback) {
56 var entryLocationInfo = this.volumeManager_.getLocationInfo(currentEntry);
57 if (!entryLocationInfo) {
63 if (entryLocationInfo.isRootEntry &&
64 entryLocationInfo.rootType ===
65 VolumeManagerCommon.RootType.DRIVE_OTHER) {
66 this.metadataCache_.getOne(previousEntry, 'drive', function(result) {
67 if (result && result.sharedWithMe) {
68 // Adds the shared-with-me entry instead.
69 var driveVolumeInfo = entryLocationInfo.volumeInfo;
70 var sharedWithMeEntry =
71 driveVolumeInfo.fakeEntries[
72 VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME];
73 if (sharedWithMeEntry)
74 entries.unshift(sharedWithMeEntry);
78 entries.unshift(currentEntry);
80 // Finishes traversal since the current is root.
86 entries.unshift(currentEntry);
87 if (!entryLocationInfo.isRootEntry) {
88 currentEntry.getParent(function(parentEntry) {
89 resolveParent(parentEntry, currentEntry, callback);
90 }.bind(this), function() {
99 queue.run(resolveParent.bind(this, entry, null));
101 queue.run(function(callback) {
102 // If an error was occured, just skip.
108 // If the path is not under the drive other root, it is not needed to
109 // override root type.
110 var locationInfo = this.volumeManager_.getLocationInfo(entry);
117 // Update DOM element.
118 queue.run(function(sequence, callback) {
119 // Check the sequence number to skip requests that are out of date.
120 if (this.showSequence_ === sequence && !error)
121 this.updateInternal_(entries);
123 }.bind(this, this.showSequence_));
127 * Updates the breadcrumb display.
128 * @param {Array.<Entry>} entries Entries on the target path.
131 BreadcrumbsController.prototype.updateInternal_ = function(entries) {
133 var doc = this.bc_.ownerDocument;
134 for (var i = 0; i < entries.length; i++) {
136 var entry = entries[i];
137 var div = doc.createElement('div');
138 div.className = 'breadcrumb-path';
139 div.textContent = util.getEntryLabel(this.volumeManager_, entry);
141 this.bc_.appendChild(div);
143 // If this is the last component, break here.
144 if (i === entries.length - 1) {
145 div.classList.add('breadcrumb-last');
150 var separator = doc.createElement('div');
151 separator.className = 'separator';
152 this.bc_.appendChild(separator);
159 * Updates breadcrumbs widths in order to truncate it properly.
161 BreadcrumbsController.prototype.truncate = function() {
162 if (!this.bc_.firstChild)
165 // Assume style.width == clientWidth (items have no margins or paddings).
167 for (var item = this.bc_.firstChild; item; item = item.nextSibling) {
168 item.removeAttribute('style');
169 item.removeAttribute('collapsed');
172 var containerWidth = this.bc_.clientWidth;
175 var currentWidth = 0;
177 for (var item = this.bc_.firstChild; item; item = item.nextSibling) {
178 if (item.className == 'separator') {
179 pathWidth += currentWidth;
180 currentWidth = item.clientWidth;
181 lastSeparator = item;
183 currentWidth += item.clientWidth;
186 if (pathWidth + currentWidth <= containerWidth)
188 if (!lastSeparator) {
189 this.bc_.lastChild.style.width = Math.min(currentWidth, containerWidth) +
193 var lastCrumbSeparatorWidth = lastSeparator.clientWidth;
194 // Current directory name may occupy up to 70% of space or even more if the
196 var maxPathWidth = Math.max(Math.round(containerWidth * 0.3),
197 containerWidth - currentWidth);
198 maxPathWidth = Math.min(pathWidth, maxPathWidth);
200 var parentCrumb = lastSeparator.previousSibling;
201 var collapsedWidth = 0;
202 if (parentCrumb && pathWidth - maxPathWidth > parentCrumb.clientWidth) {
203 // At least one crumb is hidden completely (or almost completely).
204 // Show sign of hidden crumbs like this:
205 // root > some di... > ... > current directory.
206 parentCrumb.setAttribute('collapsed', '');
207 collapsedWidth = Math.min(maxPathWidth, parentCrumb.clientWidth);
208 maxPathWidth -= collapsedWidth;
209 if (parentCrumb.clientWidth != collapsedWidth)
210 parentCrumb.style.width = collapsedWidth + 'px';
212 lastSeparator = parentCrumb.previousSibling;
215 collapsedWidth += lastSeparator.clientWidth;
216 maxPathWidth = Math.max(0, maxPathWidth - lastSeparator.clientWidth);
220 for (var item = this.bc_.firstChild; item != lastSeparator;
221 item = item.nextSibling) {
222 // TODO(serya): Mixing access item.clientWidth and modifying style and
223 // attributes could cause multiple layout reflows.
224 if (pathWidth + item.clientWidth <= maxPathWidth) {
225 pathWidth += item.clientWidth;
226 } else if (pathWidth == maxPathWidth) {
227 item.style.width = '0';
228 } else if (item.classList.contains('separator')) {
229 // Do not truncate separator. Instead let the last crumb be longer.
230 item.style.width = '0';
231 maxPathWidth = pathWidth;
233 // Truncate the last visible crumb.
234 item.style.width = (maxPathWidth - pathWidth) + 'px';
235 pathWidth = maxPathWidth;
239 currentWidth = Math.min(currentWidth,
240 containerWidth - pathWidth - collapsedWidth);
241 this.bc_.lastChild.style.width =
242 (currentWidth - lastCrumbSeparatorWidth) + 'px';
246 * Hide breadcrumbs div.
248 BreadcrumbsController.prototype.hide = function() {
249 this.bc_.hidden = true;
253 * Handle a click event on a breadcrumb element.
254 * @param {Event} event The click event.
257 BreadcrumbsController.prototype.onClick_ = function(event) {
258 if (!event.target.classList.contains('breadcrumb-path') ||
259 event.target.classList.contains('breadcrumb-last'))
262 var newEvent = new Event('pathclick');
263 newEvent.entry = event.target.entry;
264 this.dispatchEvent(newEvent);