blob: ae5be6d00c11c08cb522eaf2879dfbe0a66f108d [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @constructor
* @extends cr.EventTarget
* @param {HTMLDivElement} div Div container for breadcrumbs.
*/
function BreadcrumbsController(div) {
this.bc_ = div;
this.hideLast_ = false;
div.addEventListener('click', this.onClick_.bind(this));
}
/**
* Extends cr.EventTarget.
*/
BreadcrumbsController.prototype.__proto__ = cr.EventTarget.prototype;
/**
* Whether to hide the last part of the path.
* @param {boolean} value True if hide.
*/
BreadcrumbsController.prototype.setHideLast = function(value) {
this.hideLast_ = value;
};
/**
* Update the breadcrumb display.
* @param {string} rootPath Path to root.
* @param {string} path Path to directory.
*/
BreadcrumbsController.prototype.update = function(rootPath, path) {
this.bc_.textContent = '';
this.rootPath_ = rootPath;
var relativePath = path.substring(rootPath.length).replace(/\/$/, '');
var pathNames = relativePath.split('/');
if (pathNames[0] == '')
pathNames.splice(0, 1);
// We need a first breadcrumb for root, so placing last name from
// rootPath as first name of relativePath.
var rootPathNames = rootPath.replace(/\/$/, '').split('/');
pathNames.splice(0, 0, rootPathNames[rootPathNames.length - 1]);
rootPathNames.splice(rootPathNames.length - 1, 1);
path = rootPathNames.join('/') + '/';
var doc = this.bc_.ownerDocument;
for (var i = 0;
i < (this.hideLast_ ? pathNames.length - 1 : pathNames.length);
i++) {
if (i > 0) {
var spacer = doc.createElement('div');
spacer.className = 'separator';
this.bc_.appendChild(spacer);
}
var pathName = pathNames[i];
path += pathName;
var div = doc.createElement('div');
div.className = 'breadcrumb-path';
div.textContent = i == 0 ? PathUtil.getRootLabel(path) : pathName;
path = path + '/';
this.bc_.appendChild(div);
if (i == pathNames.length - 1)
div.classList.add('breadcrumb-last');
}
this.truncate();
};
/**
* Updates breadcrumbs widths in order to truncate it properly.
*/
BreadcrumbsController.prototype.truncate = function() {
if (!this.bc_.firstChild)
return;
// Assume style.width == clientWidth (items have no margins or paddings).
for (var item = this.bc_.firstChild; item; item = item.nextSibling) {
item.removeAttribute('style');
item.removeAttribute('collapsed');
}
var containerWidth = this.bc_.clientWidth;
var pathWidth = 0;
var currentWidth = 0;
var lastSeparator;
for (var item = this.bc_.firstChild; item; item = item.nextSibling) {
if (item.className == 'separator') {
pathWidth += currentWidth;
currentWidth = item.clientWidth;
lastSeparator = item;
} else {
currentWidth += item.clientWidth;
}
}
if (pathWidth + currentWidth <= containerWidth)
return;
if (!lastSeparator) {
this.bc_.lastChild.style.width = Math.min(currentWidth, containerWidth) +
'px';
return;
}
var lastCrumbSeparatorWidth = lastSeparator.clientWidth;
// Current directory name may occupy up to 70% of space or even more if the
// path is short.
var maxPathWidth = Math.max(Math.round(containerWidth * 0.3),
containerWidth - currentWidth);
maxPathWidth = Math.min(pathWidth, maxPathWidth);
var parentCrumb = lastSeparator.previousSibling;
var collapsedWidth = 0;
if (parentCrumb && pathWidth - maxPathWidth > parentCrumb.clientWidth) {
// At least one crumb is hidden completely (or almost completely).
// Show sign of hidden crumbs like this:
// root > some di... > ... > current directory.
parentCrumb.setAttribute('collapsed', '');
collapsedWidth = Math.min(maxPathWidth, parentCrumb.clientWidth);
maxPathWidth -= collapsedWidth;
if (parentCrumb.clientWidth != collapsedWidth)
parentCrumb.style.width = collapsedWidth + 'px';
lastSeparator = parentCrumb.previousSibling;
if (!lastSeparator)
return;
collapsedWidth += lastSeparator.clientWidth;
maxPathWidth = Math.max(0, maxPathWidth - lastSeparator.clientWidth);
}
pathWidth = 0;
for (var item = this.bc_.firstChild; item != lastSeparator;
item = item.nextSibling) {
// TODO(serya): Mixing access item.clientWidth and modifying style and
// attributes could cause multiple layout reflows.
if (pathWidth + item.clientWidth <= maxPathWidth) {
pathWidth += item.clientWidth;
} else if (pathWidth == maxPathWidth) {
item.style.width = '0';
} else if (item.classList.contains('separator')) {
// Do not truncate separator. Instead let the last crumb be longer.
item.style.width = '0';
maxPathWidth = pathWidth;
} else {
// Truncate the last visible crumb.
item.style.width = (maxPathWidth - pathWidth) + 'px';
pathWidth = maxPathWidth;
}
}
currentWidth = Math.min(currentWidth,
containerWidth - pathWidth - collapsedWidth);
this.bc_.lastChild.style.width =
(currentWidth - lastCrumbSeparatorWidth) + 'px';
};
/**
* Show breadcrumbs.
* @param {string} rootPath Path to root.
* @param {string} path Path to directory.
*/
BreadcrumbsController.prototype.show = function(rootPath, path) {
console.log('show', rootPath, path);
this.bc_.style.display = '-webkit-box';
this.update(rootPath, path);
};
/**
* Hide breadcrumbs div.
*/
BreadcrumbsController.prototype.hide = function() {
this.bc_.style.display = 'none';
};
/**
* Handle a click event on a breadcrumb element.
* @private
* @param {Event} event The click event.
*/
BreadcrumbsController.prototype.onClick_ = function(event) {
var path = this.getTargetPath(event);
if (!path)
return;
var newEvent = new cr.Event('pathclick');
newEvent.path = path;
this.dispatchEvent(newEvent);
};
/**
* Returns path associated with the event target. Returns empty string for
* inactive elements: separators, empty space and the last chunk.
* @param {Event} event The UI event.
* @return {string} Full path or empty string.
*/
BreadcrumbsController.prototype.getTargetPath = function(event) {
if (!event.target.classList.contains('breadcrumb-path') ||
event.target.classList.contains('breadcrumb-last')) {
return '';
}
var items = this.bc_.querySelectorAll('.breadcrumb-path');
var path = this.rootPath_;
if (event.target != items[0]) {
for (var i = 1; items[i - 1] != event.target; i++) {
path += '/' + items[i].textContent;
}
}
return path;
};
/**
* Returns the breadcrumbs container.
* @return {HTMLElement} Breadcumbs container HTML element.
*/
BreadcrumbsController.prototype.getContainer = function() {
return this.bc_;
};