newfilemenu.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. /*
  2. * Copyright (c) 2014
  3. *
  4. * This file is licensed under the Affero General Public License version 3
  5. * or later.
  6. *
  7. * See the COPYING-README file.
  8. *
  9. */
  10. /* global Files */
  11. (function() {
  12. var TEMPLATE_MENU =
  13. '<ul>' +
  14. '<li>' +
  15. '<label for="file_upload_start" class="menuitem" data-action="upload" title="{{uploadMaxHumanFilesize}}"><span class="svg icon icon-upload"></span><span class="displayname">{{uploadLabel}}</span></label>' +
  16. '</li>' +
  17. '{{#each items}}' +
  18. '<li>' +
  19. '<a href="#" class="menuitem" data-templatename="{{templateName}}" data-filetype="{{fileType}}" data-action="{{id}}"><span class="icon {{iconClass}} svg"></span><span class="displayname">{{displayName}}</span></a>' +
  20. '</li>' +
  21. '{{/each}}' +
  22. '</ul>';
  23. var TEMPLATE_FILENAME_FORM =
  24. '<form class="filenameform">' +
  25. '<label class="hidden-visually" for="{{cid}}-input-{{fileType}}">{{fileName}}</label>' +
  26. '<input id="{{cid}}-input-{{fileType}}" type="text" value="{{fileName}}">' +
  27. '</form>';
  28. /**
  29. * Construct a new NewFileMenu instance
  30. * @constructs NewFileMenu
  31. *
  32. * @memberof OCA.Files
  33. */
  34. var NewFileMenu = OC.Backbone.View.extend({
  35. tagName: 'div',
  36. className: 'newFileMenu popovermenu bubble hidden open menu',
  37. events: {
  38. 'click .menuitem': '_onClickAction'
  39. },
  40. /**
  41. * @type OCA.Files.FileList
  42. */
  43. fileList: null,
  44. initialize: function(options) {
  45. var self = this;
  46. var $uploadEl = $('#file_upload_start');
  47. if ($uploadEl.length) {
  48. $uploadEl.on('fileuploadstart', function() {
  49. self.trigger('actionPerformed', 'upload');
  50. });
  51. } else {
  52. console.warn('Missing upload element "file_upload_start"');
  53. }
  54. this.fileList = options && options.fileList;
  55. this._menuItems = [{
  56. id: 'folder',
  57. displayName: t('files', 'Folder'),
  58. templateName: t('files', 'New folder'),
  59. iconClass: 'icon-folder',
  60. fileType: 'folder',
  61. actionHandler: function(name) {
  62. self.fileList.createDirectory(name);
  63. }
  64. }];
  65. OC.Plugins.attach('OCA.Files.NewFileMenu', this);
  66. },
  67. template: function(data) {
  68. if (!OCA.Files.NewFileMenu._TEMPLATE) {
  69. OCA.Files.NewFileMenu._TEMPLATE = Handlebars.compile(TEMPLATE_MENU);
  70. }
  71. return OCA.Files.NewFileMenu._TEMPLATE(data);
  72. },
  73. /**
  74. * Event handler whenever an action has been clicked within the menu
  75. *
  76. * @param {Object} event event object
  77. */
  78. _onClickAction: function(event) {
  79. var $target = $(event.target);
  80. if (!$target.hasClass('menuitem')) {
  81. $target = $target.closest('.menuitem');
  82. }
  83. var action = $target.attr('data-action');
  84. // note: clicking the upload label will automatically
  85. // set the focus on the "file_upload_start" hidden field
  86. // which itself triggers the upload dialog.
  87. // Currently the upload logic is still in file-upload.js and filelist.js
  88. if (action === 'upload') {
  89. OC.hideMenus();
  90. } else {
  91. event.preventDefault();
  92. this.$el.find('.menuitem.active').removeClass('active');
  93. $target.addClass('active');
  94. this._promptFileName($target);
  95. }
  96. },
  97. _promptFileName: function($target) {
  98. var self = this;
  99. if (!OCA.Files.NewFileMenu._TEMPLATE_FORM) {
  100. OCA.Files.NewFileMenu._TEMPLATE_FORM = Handlebars.compile(TEMPLATE_FILENAME_FORM);
  101. }
  102. if ($target.find('form').length) {
  103. $target.find('input').focus();
  104. return;
  105. }
  106. // discard other forms
  107. this.$el.find('form').remove();
  108. this.$el.find('.displayname').removeClass('hidden');
  109. $target.find('.displayname').addClass('hidden');
  110. var newName = $target.attr('data-templatename');
  111. var fileType = $target.attr('data-filetype');
  112. var $form = $(OCA.Files.NewFileMenu._TEMPLATE_FORM({
  113. fileName: newName,
  114. cid: this.cid,
  115. fileType: fileType
  116. }));
  117. //this.trigger('actionPerformed', action);
  118. $target.append($form);
  119. // here comes the OLD code
  120. var $input = $form.find('input');
  121. var lastPos;
  122. var checkInput = function () {
  123. var filename = $input.val();
  124. try {
  125. if (!Files.isFileNameValid(filename)) {
  126. // Files.isFileNameValid(filename) throws an exception itself
  127. } else if (self.fileList.inList(filename)) {
  128. throw t('files', '{newname} already exists', {newname: filename});
  129. } else {
  130. return true;
  131. }
  132. } catch (error) {
  133. $input.attr('title', error);
  134. $input.tooltip({placement: 'right', trigger: 'manual'});
  135. $input.tooltip('show');
  136. $input.addClass('error');
  137. }
  138. return false;
  139. };
  140. // verify filename on typing
  141. $input.keyup(function() {
  142. if (checkInput()) {
  143. $input.tooltip('hide');
  144. $input.removeClass('error');
  145. }
  146. });
  147. $input.focus();
  148. // pre select name up to the extension
  149. lastPos = newName.lastIndexOf('.');
  150. if (lastPos === -1) {
  151. lastPos = newName.length;
  152. }
  153. $input.selectRange(0, lastPos);
  154. $form.submit(function(event) {
  155. event.stopPropagation();
  156. event.preventDefault();
  157. if (checkInput()) {
  158. var newname = $input.val();
  159. /* Find the right actionHandler that should be called.
  160. * Actions is retrieved by using `actionSpec.id` */
  161. action = _.filter(self._menuItems, function(item) {
  162. return item.id == $target.attr('data-action');
  163. }).pop();
  164. action.actionHandler(newname);
  165. $form.remove();
  166. $target.find('.displayname').removeClass('hidden');
  167. OC.hideMenus();
  168. }
  169. });
  170. },
  171. /**
  172. * Add a new item menu entry in the “New” file menu (in
  173. * last position). By clicking on the item, the
  174. * `actionHandler` function is called.
  175. *
  176. * @param {Object} actionSpec item’s properties
  177. */
  178. addMenuEntry: function(actionSpec) {
  179. this._menuItems.push({
  180. id: actionSpec.id,
  181. displayName: actionSpec.displayName,
  182. templateName: actionSpec.templateName,
  183. iconClass: actionSpec.iconClass,
  184. fileType: actionSpec.fileType,
  185. actionHandler: actionSpec.actionHandler,
  186. });
  187. },
  188. /**
  189. * Renders the menu with the currently set items
  190. */
  191. render: function() {
  192. this.$el.html(this.template({
  193. uploadMaxHumanFileSize: 'TODO',
  194. uploadLabel: t('files', 'Upload'),
  195. items: this._menuItems
  196. }));
  197. OC.Util.scaleFixForIE8(this.$('.svg'));
  198. },
  199. /**
  200. * Displays the menu under the given element
  201. *
  202. * @param {Object} $target target element
  203. */
  204. showAt: function($target) {
  205. this.render();
  206. var targetOffset = $target.offset();
  207. this.$el.css({
  208. left: targetOffset.left,
  209. top: targetOffset.top + $target.height()
  210. });
  211. this.$el.removeClass('hidden');
  212. OC.showMenu(null, this.$el);
  213. }
  214. });
  215. OCA.Files.NewFileMenu = NewFileMenu;
  216. })();