breadcrumb.js 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /**
  2. * ownCloud
  3. *
  4. * @author Vincent Petry
  5. * @copyright 2014 Vincent Petry <pvince81@owncloud.com>
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
  9. * License as published by the Free Software Foundation; either
  10. * version 3 of the License, or any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public
  18. * License along with this library. If not, see <http://www.gnu.org/licenses/>.
  19. *
  20. */
  21. (function() {
  22. /**
  23. * @class BreadCrumb
  24. * @memberof OCA.Files
  25. * @classdesc Breadcrumbs that represent the current path.
  26. *
  27. * @param {Object} [options] options
  28. * @param {Function} [options.onClick] click event handler
  29. * @param {Function} [options.onDrop] drop event handler
  30. * @param {Function} [options.getCrumbUrl] callback that returns
  31. * the URL of a given breadcrumb
  32. */
  33. var BreadCrumb = function(options){
  34. this.$el = $('<div class="breadcrumb"></div>');
  35. options = options || {};
  36. if (options.onClick) {
  37. this.onClick = options.onClick;
  38. }
  39. if (options.onDrop) {
  40. this.onDrop = options.onDrop;
  41. }
  42. if (options.getCrumbUrl) {
  43. this.getCrumbUrl = options.getCrumbUrl;
  44. }
  45. };
  46. /**
  47. * @memberof OCA.Files
  48. */
  49. BreadCrumb.prototype = {
  50. $el: null,
  51. dir: null,
  52. /**
  53. * Total width of all breadcrumbs
  54. * @type int
  55. * @private
  56. */
  57. totalWidth: 0,
  58. breadcrumbs: [],
  59. onClick: null,
  60. onDrop: null,
  61. /**
  62. * Sets the directory to be displayed as breadcrumb.
  63. * This will re-render the breadcrumb.
  64. * @param dir path to be displayed as breadcrumb
  65. */
  66. setDirectory: function(dir) {
  67. dir = dir.replace(/\\/g, '/');
  68. dir = dir || '/';
  69. if (dir !== this.dir) {
  70. this.dir = dir;
  71. this.render();
  72. }
  73. },
  74. /**
  75. * Returns the full URL to the given directory
  76. *
  77. * @param {Object.<String, String>} part crumb data as map
  78. * @param {int} index crumb index
  79. * @return full URL
  80. */
  81. getCrumbUrl: function(part, index) {
  82. return '#';
  83. },
  84. /**
  85. * Renders the breadcrumb elements
  86. */
  87. render: function() {
  88. var parts = this._makeCrumbs(this.dir || '/');
  89. var $crumb;
  90. this.$el.empty();
  91. this.breadcrumbs = [];
  92. for (var i = 0; i < parts.length; i++) {
  93. var part = parts[i];
  94. var $image;
  95. var $link = $('<a></a>').attr('href', this.getCrumbUrl(part, i));
  96. $link.text(part.name);
  97. $crumb = $('<div class="crumb svg"></div>');
  98. $crumb.append($link);
  99. $crumb.attr('data-dir', part.dir);
  100. if (part.img) {
  101. $image = $('<img class="svg"></img>');
  102. $image.attr('src', part.img);
  103. $image.attr('alt', part.alt);
  104. $link.append($image);
  105. }
  106. this.breadcrumbs.push($crumb);
  107. this.$el.append($crumb);
  108. if (this.onClick) {
  109. $crumb.on('click', this.onClick);
  110. }
  111. }
  112. $crumb.addClass('last');
  113. // in case svg is not supported by the browser we need to execute the fallback mechanism
  114. if (!OC.Util.hasSVGSupport()) {
  115. OC.Util.replaceSVG(this.$el);
  116. }
  117. // setup drag and drop
  118. if (this.onDrop) {
  119. this.$el.find('.crumb:not(.last)').droppable({
  120. drop: this.onDrop,
  121. tolerance: 'pointer'
  122. });
  123. }
  124. this._updateTotalWidth();
  125. },
  126. /**
  127. * Makes a breadcrumb structure based on the given path
  128. *
  129. * @param {String} dir path to split into a breadcrumb structure
  130. * @return {Object.<String, String>} map of {dir: path, name: displayName}
  131. */
  132. _makeCrumbs: function(dir) {
  133. var crumbs = [];
  134. var pathToHere = '';
  135. // trim leading and trailing slashes
  136. dir = dir.replace(/^\/+|\/+$/g, '');
  137. var parts = dir.split('/');
  138. if (dir === '') {
  139. parts = [];
  140. }
  141. // root part
  142. crumbs.push({
  143. dir: '/',
  144. name: '',
  145. alt: t('files', 'Home'),
  146. img: OC.imagePath('core', 'places/home.svg')
  147. });
  148. for (var i = 0; i < parts.length; i++) {
  149. var part = parts[i];
  150. pathToHere = pathToHere + '/' + part;
  151. crumbs.push({
  152. dir: pathToHere,
  153. name: part
  154. });
  155. }
  156. return crumbs;
  157. },
  158. /**
  159. * Calculate the total breadcrumb width when
  160. * all crumbs are expanded
  161. */
  162. _updateTotalWidth: function () {
  163. this.totalWidth = 0;
  164. for (var i = 0; i < this.breadcrumbs.length; i++ ) {
  165. var $crumb = $(this.breadcrumbs[i]);
  166. $crumb.data('real-width', $crumb.width());
  167. this.totalWidth += $crumb.width();
  168. }
  169. this._resize();
  170. },
  171. /**
  172. * Show/hide breadcrumbs to fit the given width
  173. *
  174. * @param {int} availableWidth available width
  175. */
  176. setMaxWidth: function (availableWidth) {
  177. if (this.availableWidth !== availableWidth) {
  178. this.availableWidth = availableWidth;
  179. this._resize();
  180. }
  181. },
  182. _resize: function() {
  183. var i, $crumb, $ellipsisCrumb;
  184. if (!this.availableWidth) {
  185. this.availableWidth = this.$el.width();
  186. }
  187. if (this.breadcrumbs.length <= 1) {
  188. return;
  189. }
  190. // reset crumbs
  191. this.$el.find('.crumb.ellipsized').remove();
  192. // unhide all
  193. this.$el.find('.crumb.hidden').removeClass('hidden');
  194. if (this.totalWidth <= this.availableWidth) {
  195. // no need to compute breadcrumbs, there is enough space
  196. return;
  197. }
  198. // running width, considering the hidden crumbs
  199. var currentTotalWidth = $(this.breadcrumbs[0]).data('real-width');
  200. var firstHidden = true;
  201. // insert ellipsis after root part (root part is always visible)
  202. $ellipsisCrumb = $('<div class="crumb ellipsized svg"><span class="ellipsis">...</span></div>');
  203. $(this.breadcrumbs[0]).after($ellipsisCrumb);
  204. currentTotalWidth += $ellipsisCrumb.width();
  205. i = this.breadcrumbs.length - 1;
  206. // find the first section that would cause the overflow
  207. // then hide everything in front of that
  208. //
  209. // this ensures that the last crumb section stays visible
  210. // for most of the cases and is always the last one to be
  211. // hidden when the screen becomes very narrow
  212. while (i > 0) {
  213. $crumb = $(this.breadcrumbs[i]);
  214. // if the current breadcrumb would cause overflow
  215. if (!firstHidden || currentTotalWidth + $crumb.data('real-width') > this.availableWidth) {
  216. // hide it
  217. $crumb.addClass('hidden');
  218. if (firstHidden) {
  219. // set the path of this one as title for the ellipsis
  220. this.$el.find('.crumb.ellipsized')
  221. .attr('title', $crumb.attr('data-dir'))
  222. .tipsy();
  223. this.$el.find('.ellipsis')
  224. .wrap('<a class="ellipsislink" href="' + encodeURI(OC.generateUrl('apps/files/?dir=' + $crumb.attr('data-dir'))) + '"></a>');
  225. }
  226. // and all the previous ones (going backwards)
  227. firstHidden = false;
  228. } else {
  229. // add to total width
  230. currentTotalWidth += $crumb.data('real-width');
  231. }
  232. i--;
  233. }
  234. if (!OC.Util.hasSVGSupport()) {
  235. OC.Util.replaceSVG(this.$el);
  236. }
  237. }
  238. };
  239. OCA.Files.BreadCrumb = BreadCrumb;
  240. })();