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.
56 var resolveParent = function(inEntry, callback) {
57 entries.unshift(inEntry);
58 if (!this.volumeManager_.getLocationInfo(inEntry).isRootEntry) {
59 inEntry.getParent(function(parent) {
60 resolveParent(parent, callback);
69 queue.run(resolveParent.bind(null, entry));
71 // Override DRIVE_OTHER root to DRIVE_SHARED_WITH_ME root.
72 queue.run(function(callback) {
73 // If an error was occured, just skip.
79 // If the path is not under the drive other root, it is not needed to
80 // override root type.
81 var locationInfo = this.volumeManager_.getLocationInfo(entry);
87 if (locationInfo.rootType !== RootType.DRIVE_OTHER) {
92 // Otherwise check the metadata of the directory localted at just under
99 this.metadataCache_.getOne(entries[1], 'drive', function(result) {
100 if (result && result.sharedWithMe)
101 entries[0] = RootType.DRIVE_SHARED_WITH_ME;
108 // Update DOM element.
109 queue.run(function(sequence, callback) {
110 // Check the sequence number to skip requests that are out of date.
111 if (this.showSequence_ === sequence && !error)
112 this.updateInternal_(entries);
114 }.bind(this, this.showSequence_));
118 * Updates the breadcrumb display.
119 * TODO(mtomasz): Simplify by passing always Entries (or a fake).
120 * @param {Array.<Entry|RootType>} entries Enries or root types on the target
124 BreadcrumbsController.prototype.updateInternal_ = function(entries) {
126 var doc = this.bc_.ownerDocument;
127 for (var i = 0; i < entries.length; i++) {
129 var entry = entries[i];
130 var div = doc.createElement('div');
131 div.className = 'breadcrumb-path';
132 if (entry === RootType.DRIVE_SHARED_WITH_ME) {
133 div.textContent = PathUtil.getRootTypeLabel(
134 RootType.DRIVE_SHARED_WITH_ME);
136 var locationInfo = this.volumeManager_.getLocationInfo(entry);
137 div.textContent = (locationInfo && locationInfo.isRootEntry) ?
138 PathUtil.getRootTypeLabel(locationInfo.rootType) : entry.name;
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);