Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(439)

Side by Side Diff: chrome/browser/resources/file_manager/foreground/js/ui/breadcrumbs_controller.js

Issue 247123002: Move Files.app files to ui/file_manager (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix the test failure on non-chromeos Created 6 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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.
4
5 'use strict';
6
7 /**
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.
12 * @constructor
13 */
14 function BreadcrumbsController(div, metadataCache, volumeManager) {
15 this.bc_ = div;
16 this.metadataCache_ = metadataCache;
17 this.volumeManager_ = volumeManager;
18 this.entry_ = null;
19
20 /**
21 * Sequence value to skip requests that are out of date.
22 * @type {number}
23 * @private
24 */
25 this.showSequence_ = 0;
26
27 // Register events and seql the object.
28 div.addEventListener('click', this.onClick_.bind(this));
29 }
30
31 /**
32 * Extends cr.EventTarget.
33 */
34 BreadcrumbsController.prototype.__proto__ = cr.EventTarget.prototype;
35
36 /**
37 * Shows breadcrumbs.
38 *
39 * @param {Entry} entry Target entry.
40 */
41 BreadcrumbsController.prototype.show = function(entry) {
42 if (entry === this.entry_)
43 return;
44
45 this.entry_ = entry;
46 this.bc_.hidden = false;
47 this.bc_.textContent = '';
48 this.showSequence_++;
49
50 var queue = new AsyncUtil.Queue();
51 var entries = [];
52 var error = false;
53
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) {
58 error = true;
59 callback();
60 return;
61 }
62
63 if (entryLocationInfo.isRootEntry &&
64 entryLocationInfo.rootType === RootType.DRIVE_OTHER) {
65 this.metadataCache_.getOne(previousEntry, 'drive', function(result) {
66 if (result && result.sharedWithMe) {
67 // Adds the shared-with-me entry instead.
68 var driveVolumeInfo = entryLocationInfo.volumeInfo;
69 var sharedWithMeEntry =
70 driveVolumeInfo.fakeEntries[RootType.DRIVE_SHARED_WITH_ME];
71 if (sharedWithMeEntry)
72 entries.unshift(sharedWithMeEntry);
73 else
74 error = true;
75 } else {
76 entries.unshift(currentEntry);
77 }
78 // Finishes traversal since the current is root.
79 callback();
80 });
81 return;
82 }
83
84 entries.unshift(currentEntry);
85 if (!entryLocationInfo.isRootEntry) {
86 currentEntry.getParent(function(parentEntry) {
87 resolveParent(parentEntry, currentEntry, callback);
88 }.bind(this), function() {
89 error = true;
90 callback();
91 });
92 } else {
93 callback();
94 }
95 }.bind(this);
96
97 queue.run(resolveParent.bind(this, entry, null));
98
99 queue.run(function(callback) {
100 // If an error was occured, just skip.
101 if (error) {
102 callback();
103 return;
104 }
105
106 // If the path is not under the drive other root, it is not needed to
107 // override root type.
108 var locationInfo = this.volumeManager_.getLocationInfo(entry);
109 if (!locationInfo)
110 error = true;
111
112 callback();
113 }.bind(this));
114
115 // Update DOM element.
116 queue.run(function(sequence, callback) {
117 // Check the sequence number to skip requests that are out of date.
118 if (this.showSequence_ === sequence && !error)
119 this.updateInternal_(entries);
120 callback();
121 }.bind(this, this.showSequence_));
122 };
123
124 /**
125 * Updates the breadcrumb display.
126 * @param {Array.<Entry>} entries Entries on the target path.
127 * @private
128 */
129 BreadcrumbsController.prototype.updateInternal_ = function(entries) {
130 // Make elements.
131 var doc = this.bc_.ownerDocument;
132 for (var i = 0; i < entries.length; i++) {
133 // Add a component.
134 var entry = entries[i];
135 var div = doc.createElement('div');
136 div.className = 'breadcrumb-path';
137 div.textContent = util.getEntryLabel(this.volumeManager_, entry);
138 div.entry = entry;
139 this.bc_.appendChild(div);
140
141 // If this is the last component, break here.
142 if (i === entries.length - 1) {
143 div.classList.add('breadcrumb-last');
144 break;
145 }
146
147 // Add a separator.
148 var separator = doc.createElement('div');
149 separator.className = 'separator';
150 this.bc_.appendChild(separator);
151 }
152
153 this.truncate();
154 };
155
156 /**
157 * Updates breadcrumbs widths in order to truncate it properly.
158 */
159 BreadcrumbsController.prototype.truncate = function() {
160 if (!this.bc_.firstChild)
161 return;
162
163 // Assume style.width == clientWidth (items have no margins or paddings).
164
165 for (var item = this.bc_.firstChild; item; item = item.nextSibling) {
166 item.removeAttribute('style');
167 item.removeAttribute('collapsed');
168 }
169
170 var containerWidth = this.bc_.clientWidth;
171
172 var pathWidth = 0;
173 var currentWidth = 0;
174 var lastSeparator;
175 for (var item = this.bc_.firstChild; item; item = item.nextSibling) {
176 if (item.className == 'separator') {
177 pathWidth += currentWidth;
178 currentWidth = item.clientWidth;
179 lastSeparator = item;
180 } else {
181 currentWidth += item.clientWidth;
182 }
183 }
184 if (pathWidth + currentWidth <= containerWidth)
185 return;
186 if (!lastSeparator) {
187 this.bc_.lastChild.style.width = Math.min(currentWidth, containerWidth) +
188 'px';
189 return;
190 }
191 var lastCrumbSeparatorWidth = lastSeparator.clientWidth;
192 // Current directory name may occupy up to 70% of space or even more if the
193 // path is short.
194 var maxPathWidth = Math.max(Math.round(containerWidth * 0.3),
195 containerWidth - currentWidth);
196 maxPathWidth = Math.min(pathWidth, maxPathWidth);
197
198 var parentCrumb = lastSeparator.previousSibling;
199 var collapsedWidth = 0;
200 if (parentCrumb && pathWidth - maxPathWidth > parentCrumb.clientWidth) {
201 // At least one crumb is hidden completely (or almost completely).
202 // Show sign of hidden crumbs like this:
203 // root > some di... > ... > current directory.
204 parentCrumb.setAttribute('collapsed', '');
205 collapsedWidth = Math.min(maxPathWidth, parentCrumb.clientWidth);
206 maxPathWidth -= collapsedWidth;
207 if (parentCrumb.clientWidth != collapsedWidth)
208 parentCrumb.style.width = collapsedWidth + 'px';
209
210 lastSeparator = parentCrumb.previousSibling;
211 if (!lastSeparator)
212 return;
213 collapsedWidth += lastSeparator.clientWidth;
214 maxPathWidth = Math.max(0, maxPathWidth - lastSeparator.clientWidth);
215 }
216
217 pathWidth = 0;
218 for (var item = this.bc_.firstChild; item != lastSeparator;
219 item = item.nextSibling) {
220 // TODO(serya): Mixing access item.clientWidth and modifying style and
221 // attributes could cause multiple layout reflows.
222 if (pathWidth + item.clientWidth <= maxPathWidth) {
223 pathWidth += item.clientWidth;
224 } else if (pathWidth == maxPathWidth) {
225 item.style.width = '0';
226 } else if (item.classList.contains('separator')) {
227 // Do not truncate separator. Instead let the last crumb be longer.
228 item.style.width = '0';
229 maxPathWidth = pathWidth;
230 } else {
231 // Truncate the last visible crumb.
232 item.style.width = (maxPathWidth - pathWidth) + 'px';
233 pathWidth = maxPathWidth;
234 }
235 }
236
237 currentWidth = Math.min(currentWidth,
238 containerWidth - pathWidth - collapsedWidth);
239 this.bc_.lastChild.style.width =
240 (currentWidth - lastCrumbSeparatorWidth) + 'px';
241 };
242
243 /**
244 * Hide breadcrumbs div.
245 */
246 BreadcrumbsController.prototype.hide = function() {
247 this.bc_.hidden = true;
248 };
249
250 /**
251 * Handle a click event on a breadcrumb element.
252 * @param {Event} event The click event.
253 * @private
254 */
255 BreadcrumbsController.prototype.onClick_ = function(event) {
256 if (!event.target.classList.contains('breadcrumb-path') ||
257 event.target.classList.contains('breadcrumb-last'))
258 return;
259
260 var newEvent = new Event('pathclick');
261 newEvent.entry = event.target.entry;
262 this.dispatchEvent(newEvent);
263 };
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698