ec4cf96f0d
Rewrote the breadcrumb calculation to be more readable. Breadcrumb now has a setMaxWidth() method to set the maximum allowed width which is used to fit the breadcrumbs. The breadcrumb width is now based on the container width, passed through setMaxWidth() by the FileList class. Now using fixed widths for the test crumbs to simulate consistent widths across browsers which rendering engines might usually yield different results.
245 lines
6.1 KiB
JavaScript
245 lines
6.1 KiB
JavaScript
/**
|
|
* ownCloud
|
|
*
|
|
* @author Vincent Petry
|
|
* @copyright 2014 Vincent Petry <pvince81@owncloud.com>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
|
|
* License as published by the Free Software Foundation; either
|
|
* version 3 of the License, or any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public
|
|
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
/* global OC */
|
|
(function() {
|
|
/**
|
|
* Creates an breadcrumb element in the given container
|
|
*/
|
|
var BreadCrumb = function(options){
|
|
this.$el = $('<div class="breadcrumb"></div>');
|
|
options = options || {};
|
|
if (options.onClick) {
|
|
this.onClick = options.onClick;
|
|
}
|
|
if (options.onDrop) {
|
|
this.onDrop = options.onDrop;
|
|
}
|
|
if (options.getCrumbUrl) {
|
|
this.getCrumbUrl = options.getCrumbUrl;
|
|
}
|
|
};
|
|
BreadCrumb.prototype = {
|
|
$el: null,
|
|
dir: null,
|
|
|
|
/**
|
|
* Total width of all breadcrumbs
|
|
*/
|
|
totalWidth: 0,
|
|
breadcrumbs: [],
|
|
onClick: null,
|
|
onDrop: null,
|
|
|
|
/**
|
|
* Sets the directory to be displayed as breadcrumb.
|
|
* This will re-render the breadcrumb.
|
|
* @param dir path to be displayed as breadcrumb
|
|
*/
|
|
setDirectory: function(dir) {
|
|
dir = dir || '/';
|
|
if (dir !== this.dir) {
|
|
this.dir = dir;
|
|
this.render();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Returns the full URL to the given directory
|
|
* @param part crumb data as map
|
|
* @param index crumb index
|
|
* @return full URL
|
|
*/
|
|
getCrumbUrl: function(part, index) {
|
|
return '#';
|
|
},
|
|
|
|
/**
|
|
* Renders the breadcrumb elements
|
|
*/
|
|
render: function() {
|
|
var parts = this._makeCrumbs(this.dir || '/');
|
|
var $crumb;
|
|
this.$el.empty();
|
|
this.breadcrumbs = [];
|
|
|
|
for (var i = 0; i < parts.length; i++) {
|
|
var part = parts[i];
|
|
var $image;
|
|
var $link = $('<a></a>').attr('href', this.getCrumbUrl(part, i));
|
|
$link.text(part.name);
|
|
$crumb = $('<div class="crumb svg"></div>');
|
|
$crumb.append($link);
|
|
$crumb.attr('data-dir', part.dir);
|
|
|
|
if (part.img) {
|
|
$image = $('<img class="svg"></img>');
|
|
$image.attr('src', part.img);
|
|
$link.append($image);
|
|
}
|
|
this.breadcrumbs.push($crumb);
|
|
this.$el.append($crumb);
|
|
if (this.onClick) {
|
|
$crumb.on('click', this.onClick);
|
|
}
|
|
}
|
|
$crumb.addClass('last');
|
|
|
|
// in case svg is not supported by the browser we need to execute the fallback mechanism
|
|
if (!OC.Util.hasSVGSupport()) {
|
|
OC.Util.replaceSVG(this.$el);
|
|
}
|
|
|
|
// setup drag and drop
|
|
if (this.onDrop) {
|
|
this.$el.find('.crumb:not(.last)').droppable({
|
|
drop: this.onDrop,
|
|
tolerance: 'pointer'
|
|
});
|
|
}
|
|
|
|
this._updateTotalWidth();
|
|
},
|
|
|
|
/**
|
|
* Makes a breadcrumb structure based on the given path
|
|
* @param dir path to split into a breadcrumb structure
|
|
* @return array of map {dir: path, name: displayName}
|
|
*/
|
|
_makeCrumbs: function(dir) {
|
|
var crumbs = [];
|
|
var pathToHere = '';
|
|
// trim leading and trailing slashes
|
|
dir = dir.replace(/^\/+|\/+$/g, '');
|
|
var parts = dir.split('/');
|
|
if (dir === '') {
|
|
parts = [];
|
|
}
|
|
// root part
|
|
crumbs.push({
|
|
dir: '/',
|
|
name: '',
|
|
img: OC.imagePath('core', 'places/home.svg')
|
|
});
|
|
for (var i = 0; i < parts.length; i++) {
|
|
var part = parts[i];
|
|
pathToHere = pathToHere + '/' + part;
|
|
crumbs.push({
|
|
dir: pathToHere,
|
|
name: part
|
|
});
|
|
}
|
|
return crumbs;
|
|
},
|
|
|
|
/**
|
|
* Calculate the total breadcrumb width when
|
|
* all crumbs are expanded
|
|
*/
|
|
_updateTotalWidth: function () {
|
|
this.totalWidth = 0;
|
|
for (var i = 0; i < this.breadcrumbs.length; i++ ) {
|
|
var $crumb = $(this.breadcrumbs[i]);
|
|
$crumb.data('real-width', $crumb.width());
|
|
this.totalWidth += $crumb.width();
|
|
}
|
|
this._resize();
|
|
},
|
|
|
|
/**
|
|
* Show/hide breadcrumbs to fit the given width
|
|
*/
|
|
setMaxWidth: function (availableWidth) {
|
|
if (this.availableWidth !== availableWidth) {
|
|
this.availableWidth = availableWidth;
|
|
this._resize();
|
|
}
|
|
},
|
|
|
|
_resize: function() {
|
|
var i, $crumb, $ellipsisCrumb;
|
|
|
|
if (!this.availableWidth) {
|
|
this.availableWidth = this.$el.width();
|
|
}
|
|
|
|
if (this.breadcrumbs.length <= 1) {
|
|
return;
|
|
}
|
|
|
|
// reset crumbs
|
|
this.$el.find('.crumb.ellipsized').remove();
|
|
|
|
// unhide all
|
|
this.$el.find('.crumb.hidden').removeClass('hidden');
|
|
|
|
if (this.totalWidth <= this.availableWidth) {
|
|
// no need to compute breadcrumbs, there is enough space
|
|
return;
|
|
}
|
|
|
|
// running width, considering the hidden crumbs
|
|
var currentTotalWidth = $(this.breadcrumbs[0]).data('real-width');
|
|
var firstHidden = true;
|
|
|
|
// insert ellipsis after root part (root part is always visible)
|
|
$ellipsisCrumb = $('<div class="crumb ellipsized svg"><span class="ellipsis">...</span></div>');
|
|
$(this.breadcrumbs[0]).after($ellipsisCrumb);
|
|
currentTotalWidth += $ellipsisCrumb.width();
|
|
|
|
i = this.breadcrumbs.length - 1;
|
|
|
|
// find the first section that would cause the overflow
|
|
// then hide everything in front of that
|
|
//
|
|
// this ensures that the last crumb section stays visible
|
|
// for most of the cases and is always the last one to be
|
|
// hidden when the screen becomes very narrow
|
|
while (i > 0) {
|
|
$crumb = $(this.breadcrumbs[i]);
|
|
// if the current breadcrumb would cause overflow
|
|
if (!firstHidden || currentTotalWidth + $crumb.data('real-width') > this.availableWidth) {
|
|
// hide it
|
|
$crumb.addClass('hidden');
|
|
if (firstHidden) {
|
|
// set the path of this one as title for the ellipsis
|
|
this.$el.find('.crumb.ellipsized')
|
|
.attr('title', $crumb.attr('data-dir'))
|
|
.tipsy();
|
|
}
|
|
// and all the previous ones (going backwards)
|
|
firstHidden = false;
|
|
} else {
|
|
// add to total width
|
|
currentTotalWidth += $crumb.data('real-width');
|
|
}
|
|
i--;
|
|
}
|
|
|
|
if (!OC.Util.hasSVGSupport()) {
|
|
OC.Util.replaceSVG(this.$el);
|
|
}
|
|
}
|
|
};
|
|
|
|
OCA.Files.BreadCrumb = BreadCrumb;
|
|
})();
|
|
|