You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

navigation.js 8.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. /*
  2. * @Copyright 2014 Vincent Petry <pvince81@owncloud.com>
  3. *
  4. * @author Vincent Petry
  5. * @author Felix Nüsse <felix.nuesse@t-online.de>
  6. *
  7. *
  8. * This file is licensed under the Affero General Public License version 3
  9. * or later.
  10. *
  11. * See the COPYING-README file.
  12. *
  13. */
  14. (function () {
  15. /**
  16. * @class OCA.Files.Navigation
  17. * @classdesc Navigation control for the files app sidebar.
  18. *
  19. * @param $el element containing the navigation
  20. */
  21. var Navigation = function ($el) {
  22. this.initialize($el);
  23. };
  24. /**
  25. * @memberof OCA.Files
  26. */
  27. Navigation.prototype = {
  28. /**
  29. * Currently selected item in the list
  30. */
  31. _activeItem: null,
  32. /**
  33. * Currently selected container
  34. */
  35. $currentContent: null,
  36. /**
  37. * Key for the quick-acces-list
  38. */
  39. $quickAccessListKey: 'sublist-favorites',
  40. /**
  41. * Initializes the navigation from the given container
  42. *
  43. * @private
  44. * @param $el element containing the navigation
  45. */
  46. initialize: function ($el) {
  47. this.$el = $el;
  48. this._activeItem = null;
  49. this.$currentContent = null;
  50. this._setupEvents();
  51. this.setInitialQuickaccessSettings();
  52. },
  53. /**
  54. * Setup UI events
  55. */
  56. _setupEvents: function () {
  57. this.$el.on('click', 'li a', _.bind(this._onClickItem, this));
  58. this.$el.on('click', 'li button', _.bind(this._onClickMenuButton, this));
  59. var trashBinElement = $('.nav-trashbin');
  60. trashBinElement.droppable({
  61. over: function (event, ui) {
  62. trashBinElement.addClass('dropzone-background');
  63. },
  64. out: function (event, ui) {
  65. trashBinElement.removeClass('dropzone-background');
  66. },
  67. activate: function (event, ui) {
  68. var element = trashBinElement.find('a').first();
  69. element.addClass('nav-icon-trashbin-starred').removeClass('nav-icon-trashbin');
  70. },
  71. deactivate: function (event, ui) {
  72. var element = trashBinElement.find('a').first();
  73. element.addClass('nav-icon-trashbin').removeClass('nav-icon-trashbin-starred');
  74. },
  75. drop: function (event, ui) {
  76. trashBinElement.removeClass('dropzone-background');
  77. var $selectedFiles = $(ui.draggable);
  78. // FIXME: when there are a lot of selected files the helper
  79. // contains only a subset of them; the list of selected
  80. // files should be gotten from the file list instead to
  81. // ensure that all of them are removed.
  82. var item = ui.helper.find('tr');
  83. for (var i = 0; i < item.length; i++) {
  84. $selectedFiles.trigger('droppedOnTrash', item[i].getAttribute('data-file'), item[i].getAttribute('data-dir'));
  85. }
  86. }
  87. });
  88. },
  89. /**
  90. * Returns the container of the currently active app.
  91. *
  92. * @return app container
  93. */
  94. getActiveContainer: function () {
  95. return this.$currentContent;
  96. },
  97. /**
  98. * Returns the currently active item
  99. *
  100. * @return item ID
  101. */
  102. getActiveItem: function () {
  103. return this._activeItem;
  104. },
  105. /**
  106. * Switch the currently selected item, mark it as selected and
  107. * make the content container visible, if any.
  108. *
  109. * @param string itemId id of the navigation item to select
  110. * @param array options "silent" to not trigger event
  111. */
  112. setActiveItem: function (itemId, options) {
  113. var currentItem = this.$el.find('li[data-id="' + itemId + '"]');
  114. var itemDir = currentItem.data('dir');
  115. var itemView = currentItem.data('view');
  116. var oldItemId = this._activeItem;
  117. if (itemId === this._activeItem) {
  118. if (!options || !options.silent) {
  119. this.$el.trigger(
  120. new $.Event('itemChanged', {
  121. itemId: itemId,
  122. previousItemId: oldItemId,
  123. dir: itemDir,
  124. view: itemView
  125. })
  126. );
  127. }
  128. return;
  129. }
  130. this.$el.find('li a').removeClass('active');
  131. if (this.$currentContent) {
  132. this.$currentContent.addClass('hidden');
  133. this.$currentContent.trigger(jQuery.Event('hide'));
  134. }
  135. this._activeItem = itemId;
  136. currentItem.children('a').addClass('active');
  137. this.$currentContent = $('#app-content-' + (typeof itemView === 'string' && itemView !== '' ? itemView : itemId));
  138. this.$currentContent.removeClass('hidden');
  139. if (!options || !options.silent) {
  140. this.$currentContent.trigger(jQuery.Event('show', {
  141. itemId: itemId,
  142. previousItemId: oldItemId,
  143. dir: itemDir,
  144. view: itemView
  145. }));
  146. this.$el.trigger(
  147. new $.Event('itemChanged', {
  148. itemId: itemId,
  149. previousItemId: oldItemId,
  150. dir: itemDir,
  151. view: itemView
  152. })
  153. );
  154. }
  155. },
  156. /**
  157. * Returns whether a given item exists
  158. */
  159. itemExists: function (itemId) {
  160. return this.$el.find('li[data-id="' + itemId + '"]').length;
  161. },
  162. /**
  163. * Event handler for when clicking on an item.
  164. */
  165. _onClickItem: function (ev) {
  166. var $target = $(ev.target);
  167. var itemId = $target.closest('li').attr('data-id');
  168. if (!_.isUndefined(itemId)) {
  169. this.setActiveItem(itemId);
  170. }
  171. ev.preventDefault();
  172. },
  173. /**
  174. * Event handler for clicking a button
  175. */
  176. _onClickMenuButton: function (ev) {
  177. var $target = $(ev.target);
  178. var $menu = $target.parent('li');
  179. var itemId = $target.closest('button').attr('id');
  180. var collapsibleToggles = [];
  181. var dotmenuToggles = [];
  182. if ($menu.hasClass('collapsible') && $menu.data('expandedstate')) {
  183. $menu.toggleClass('open');
  184. var show = $menu.hasClass('open') ? 1 : 0;
  185. var key = $menu.data('expandedstate');
  186. $.post(OC.generateUrl("/apps/files/api/v1/toggleShowFolder/" + key), {show: show});
  187. }
  188. dotmenuToggles.forEach(function foundToggle (item) {
  189. if (item[0] === ("#" + itemId)) {
  190. document.getElementById(item[1]).classList.toggle('open');
  191. }
  192. });
  193. ev.preventDefault();
  194. },
  195. /**
  196. * Sort initially as setup of sidebar for QuickAccess
  197. */
  198. setInitialQuickaccessSettings: function () {
  199. var quickAccessKey = this.$quickAccessListKey;
  200. var quickAccessMenu = document.getElementById(quickAccessKey);
  201. if (quickAccessMenu) {
  202. var list = quickAccessMenu.getElementsByTagName('li');
  203. this.QuickSort(list, 0, list.length - 1);
  204. }
  205. var favoritesListElement = $(quickAccessMenu).parent();
  206. favoritesListElement.droppable({
  207. over: function (event, ui) {
  208. favoritesListElement.addClass('dropzone-background');
  209. },
  210. out: function (event, ui) {
  211. favoritesListElement.removeClass('dropzone-background');
  212. },
  213. activate: function (event, ui) {
  214. var element = favoritesListElement.find('a').first();
  215. element.addClass('nav-icon-favorites-starred').removeClass('nav-icon-favorites');
  216. },
  217. deactivate: function (event, ui) {
  218. var element = favoritesListElement.find('a').first();
  219. element.addClass('nav-icon-favorites').removeClass('nav-icon-favorites-starred');
  220. },
  221. drop: function (event, ui) {
  222. favoritesListElement.removeClass('dropzone-background');
  223. var $selectedFiles = $(ui.draggable);
  224. if (ui.helper.find('tr').size() === 1) {
  225. var $tr = $selectedFiles.closest('tr');
  226. if ($tr.attr("data-favorite")) {
  227. return;
  228. }
  229. $selectedFiles.trigger('droppedOnFavorites', $tr.attr('data-file'));
  230. } else {
  231. // FIXME: besides the issue described for dropping on
  232. // the trash bin, for favoriting it is not possible to
  233. // use the data from the helper; due to some bugs the
  234. // tags are not always added to the selected files, and
  235. // thus that data can not be accessed through the helper
  236. // to prevent triggering the favorite action on an
  237. // already favorited file (which would remove it from
  238. // favorites).
  239. OC.Notification.showTemporary(t('files', 'You can only favorite a single file or folder at a time'));
  240. }
  241. }
  242. });
  243. },
  244. /**
  245. * Sorting-Algorithm for QuickAccess
  246. */
  247. QuickSort: function (list, start, end) {
  248. var lastMatch;
  249. if (list.length > 1) {
  250. lastMatch = this.quicksort_helper(list, start, end);
  251. if (start < lastMatch - 1) {
  252. this.QuickSort(list, start, lastMatch - 1);
  253. }
  254. if (lastMatch < end) {
  255. this.QuickSort(list, lastMatch, end);
  256. }
  257. }
  258. },
  259. /**
  260. * Sorting-Algorithm-Helper for QuickAccess
  261. */
  262. quicksort_helper: function (list, start, end) {
  263. var pivot = Math.floor((end + start) / 2);
  264. var pivotElement = this.getCompareValue(list, pivot);
  265. var i = start;
  266. var j = end;
  267. while (i <= j) {
  268. while (this.getCompareValue(list, i) < pivotElement) {
  269. i++;
  270. }
  271. while (this.getCompareValue(list, j) > pivotElement) {
  272. j--;
  273. }
  274. if (i <= j) {
  275. this.swap(list, i, j);
  276. i++;
  277. j--;
  278. }
  279. }
  280. return i;
  281. },
  282. /**
  283. * Sorting-Algorithm-Helper for QuickAccess
  284. * This method allows easy access to the element which is sorted by.
  285. */
  286. getCompareValue: function (nodes, int, strategy) {
  287. return nodes[int].getElementsByTagName('a')[0].innerHTML.toLowerCase();
  288. },
  289. /**
  290. * Sorting-Algorithm-Helper for QuickAccess
  291. * This method allows easy swapping of elements.
  292. */
  293. swap: function (list, j, i) {
  294. var before = function(node, insertNode) {
  295. node.parentNode.insertBefore(insertNode, node);
  296. }
  297. before(list[i], list[j]);
  298. before(list[j], list[i]);
  299. }
  300. };
  301. OCA.Files.Navigation = Navigation;
  302. })();