editor-file.js 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. let vueFile = new Vue({
  2. el: '#modal-editor-file',
  3. data: {
  4. isLoading: false,
  5. isLoadingText: '',
  6. newFolderName: '',
  7. newFolderShow: false,
  8. newFolderError: false,
  9. folders: [],
  10. currentFolder: '',
  11. currentFile: '',
  12. files: [],
  13. uploadSucceeded: false,
  14. postUploadChecks: 0,
  15. renameFileShow: false,
  16. renameFileId: '',
  17. renameFileFilename: '',
  18. deleteFileShow: false,
  19. deleteFileId: '',
  20. deleteFileFilename: ''
  21. },
  22. methods: {
  23. open: () => {
  24. mdeModalOpenState = true;
  25. $('#modal-editor-file').addClass('is-active');
  26. vueFile.refreshFolders();
  27. },
  28. cancel: (ev) => {
  29. mdeModalOpenState = false;
  30. $('#modal-editor-file').removeClass('is-active');
  31. },
  32. // -------------------------------------------
  33. // INSERT LINK TO FILE
  34. // -------------------------------------------
  35. selectFile: (fileId) => {
  36. vueFile.currentFile = fileId;
  37. },
  38. insertFileLink: (ev) => {
  39. if(mde.codemirror.doc.somethingSelected()) {
  40. mde.codemirror.execCommand('singleSelection');
  41. }
  42. let selFile = _.find(vueFile.files, ['_id', vueFile.currentFile]);
  43. selFile.normalizedPath = (selFile.folder === 'f:') ? selFile.filename : selFile.folder.slice(2) + '/' + selFile.filename;
  44. selFile.titleGuess = _.startCase(selFile.basename);
  45. let fileText = '[' + selFile.titleGuess + '](/uploads/' + selFile.normalizedPath + ' "' + selFile.titleGuess + '")';
  46. mde.codemirror.doc.replaceSelection(fileText);
  47. vueFile.cancel();
  48. },
  49. // -------------------------------------------
  50. // NEW FOLDER
  51. // -------------------------------------------
  52. newFolder: (ev) => {
  53. vueFile.newFolderName = '';
  54. vueFile.newFolderError = false;
  55. vueFile.newFolderShow = true;
  56. _.delay(() => { $('#txt-editor-file-newfoldername').focus(); }, 400);
  57. },
  58. newFolderDiscard: (ev) => {
  59. vueFile.newFolderShow = false;
  60. },
  61. newFolderCreate: (ev) => {
  62. let regFolderName = new RegExp("^[a-z0-9][a-z0-9\-]*[a-z0-9]$");
  63. vueFile.newFolderName = _.kebabCase(_.trim(vueFile.newFolderName));
  64. if(_.isEmpty(vueFile.newFolderName) || !regFolderName.test(vueFile.newFolderName)) {
  65. vueFile.newFolderError = true;
  66. return;
  67. }
  68. vueFile.newFolderDiscard();
  69. vueFile.isLoadingText = 'Creating new folder...';
  70. vueFile.isLoading = true;
  71. Vue.nextTick(() => {
  72. socket.emit('uploadsCreateFolder', { foldername: vueFile.newFolderName }, (data) => {
  73. vueFile.folders = data;
  74. vueFile.currentFolder = vueFile.newFolderName;
  75. vueFile.files = [];
  76. vueFile.isLoading = false;
  77. });
  78. });
  79. },
  80. // -------------------------------------------
  81. // RENAME FILE
  82. // -------------------------------------------
  83. renameFile: () => {
  84. let c = _.find(vueFile.files, ['_id', vueFile.renameFileId ]);
  85. vueFile.renameFileFilename = c.basename || '';
  86. vueFile.renameFileShow = true;
  87. _.delay(() => {
  88. $('#txt-editor-renamefile').focus();
  89. _.defer(() => { $('#txt-editor-file-rename').select(); });
  90. }, 400);
  91. },
  92. renameFileDiscard: () => {
  93. vueFile.renameFileShow = false;
  94. },
  95. renameFileGo: () => {
  96. vueFile.renameFileDiscard();
  97. vueFile.isLoadingText = 'Renaming file...';
  98. vueFile.isLoading = true;
  99. Vue.nextTick(() => {
  100. socket.emit('uploadsRenameFile', { uid: vueFile.renameFileId, folder: vueFile.currentFolder, filename: vueFile.renameFileFilename }, (data) => {
  101. if(data.ok) {
  102. vueFile.waitChangeComplete(vueFile.files.length, false);
  103. } else {
  104. vueFile.isLoading = false;
  105. alerts.pushError('Rename error', data.msg);
  106. }
  107. });
  108. });
  109. },
  110. // -------------------------------------------
  111. // MOVE FILE
  112. // -------------------------------------------
  113. moveFile: (uid, fld) => {
  114. vueFile.isLoadingText = 'Moving file...';
  115. vueFile.isLoading = true;
  116. Vue.nextTick(() => {
  117. socket.emit('uploadsMoveFile', { uid, folder: fld }, (data) => {
  118. if(data.ok) {
  119. vueFile.loadFiles();
  120. } else {
  121. vueFile.isLoading = false;
  122. alerts.pushError('Rename error', data.msg);
  123. }
  124. });
  125. });
  126. },
  127. // -------------------------------------------
  128. // DELETE FILE
  129. // -------------------------------------------
  130. deleteFileWarn: (show) => {
  131. if(show) {
  132. let c = _.find(vueFile.files, ['_id', vueFile.deleteFileId ]);
  133. vueFile.deleteFileFilename = c.filename || 'this file';
  134. }
  135. vueFile.deleteFileShow = show;
  136. },
  137. deleteFileGo: () => {
  138. vueFile.deleteFileWarn(false);
  139. vueFile.isLoadingText = 'Deleting file...';
  140. vueFile.isLoading = true;
  141. Vue.nextTick(() => {
  142. socket.emit('uploadsDeleteFile', { uid: vueFile.deleteFileId }, (data) => {
  143. vueFile.loadFiles();
  144. });
  145. });
  146. },
  147. // -------------------------------------------
  148. // LOAD FROM REMOTE
  149. // -------------------------------------------
  150. selectFolder: (fldName) => {
  151. vueFile.currentFolder = fldName;
  152. vueFile.loadFiles();
  153. },
  154. refreshFolders: () => {
  155. vueFile.isLoadingText = 'Fetching folders list...';
  156. vueFile.isLoading = true;
  157. vueFile.currentFolder = '';
  158. vueFile.currentImage = '';
  159. Vue.nextTick(() => {
  160. socket.emit('uploadsGetFolders', { }, (data) => {
  161. vueFile.folders = data;
  162. vueFile.loadFiles();
  163. });
  164. });
  165. },
  166. loadFiles: (silent) => {
  167. if(!silent) {
  168. vueFile.isLoadingText = 'Fetching files...';
  169. vueFile.isLoading = true;
  170. }
  171. return new Promise((resolve, reject) => {
  172. Vue.nextTick(() => {
  173. socket.emit('uploadsGetFiles', { folder: vueFile.currentFolder }, (data) => {
  174. vueFile.files = data;
  175. if(!silent) {
  176. vueFile.isLoading = false;
  177. }
  178. vueFile.attachContextMenus();
  179. resolve(true);
  180. });
  181. });
  182. });
  183. },
  184. waitChangeComplete: (oldAmount, expectChange) => {
  185. expectChange = (_.isBoolean(expectChange)) ? expectChange : true;
  186. vueFile.postUploadChecks++;
  187. vueFile.isLoadingText = 'Processing...';
  188. Vue.nextTick(() => {
  189. vueFile.loadFiles(true).then(() => {
  190. if((vueFile.files.length !== oldAmount) === expectChange) {
  191. vueFile.postUploadChecks = 0;
  192. vueFile.isLoading = false;
  193. } else if(vueFile.postUploadChecks > 5) {
  194. vueFile.postUploadChecks = 0;
  195. vueFile.isLoading = false;
  196. alerts.pushError('Unable to fetch updated listing', 'Try again later');
  197. } else {
  198. _.delay(() => {
  199. vueFile.waitChangeComplete(oldAmount, expectChange);
  200. }, 1500);
  201. }
  202. });
  203. });
  204. },
  205. // -------------------------------------------
  206. // IMAGE CONTEXT MENU
  207. // -------------------------------------------
  208. attachContextMenus: () => {
  209. let moveFolders = _.map(vueFile.folders, (f) => {
  210. return {
  211. name: (f !== '') ? f : '/ (root)',
  212. icon: 'fa-folder',
  213. callback: (key, opt) => {
  214. let moveFileId = _.toString($(opt.$trigger).data('uid'));
  215. let moveFileDestFolder = _.nth(vueFile.folders, key);
  216. vueFile.moveFile(moveFileId, moveFileDestFolder);
  217. }
  218. };
  219. });
  220. $.contextMenu('destroy', '.editor-modal-file-choices > figure');
  221. $.contextMenu({
  222. selector: '.editor-modal-file-choices > figure',
  223. appendTo: '.editor-modal-file-choices',
  224. position: (opt, x, y) => {
  225. $(opt.$trigger).addClass('is-contextopen');
  226. let trigPos = $(opt.$trigger).position();
  227. let trigDim = { w: $(opt.$trigger).width() / 5, h: $(opt.$trigger).height() / 2 };
  228. opt.$menu.css({ top: trigPos.top + trigDim.h, left: trigPos.left + trigDim.w });
  229. },
  230. events: {
  231. hide: (opt) => {
  232. $(opt.$trigger).removeClass('is-contextopen');
  233. }
  234. },
  235. items: {
  236. rename: {
  237. name: "Rename",
  238. icon: "fa-edit",
  239. callback: (key, opt) => {
  240. vueFile.renameFileId = _.toString(opt.$trigger[0].dataset.uid);
  241. vueFile.renameFile();
  242. }
  243. },
  244. move: {
  245. name: "Move to...",
  246. icon: "fa-folder-open-o",
  247. items: moveFolders
  248. },
  249. delete: {
  250. name: "Delete",
  251. icon: "fa-trash",
  252. callback: (key, opt) => {
  253. vueFile.deleteFileId = _.toString(opt.$trigger[0].dataset.uid);
  254. vueFile.deleteFileWarn(true);
  255. }
  256. }
  257. }
  258. });
  259. }
  260. }
  261. });
  262. $('#btn-editor-file-upload input').on('change', (ev) => {
  263. let curFileAmount = vueFile.files.length;
  264. $(ev.currentTarget).simpleUpload("/uploads/file", {
  265. name: 'binfile',
  266. data: {
  267. folder: vueFile.currentFolder
  268. },
  269. limit: 20,
  270. expect: 'json',
  271. maxFileSize: 0,
  272. init: (totalUploads) => {
  273. vueFile.uploadSucceeded = false;
  274. vueFile.isLoadingText = 'Preparing to upload...';
  275. vueFile.isLoading = true;
  276. },
  277. progress: (progress) => {
  278. vueFile.isLoadingText = 'Uploading...' + Math.round(progress) + '%';
  279. },
  280. success: (data) => {
  281. if(data.ok) {
  282. let failedUpls = _.filter(data.results, ['ok', false]);
  283. if(failedUpls.length) {
  284. _.forEach(failedUpls, (u) => {
  285. alerts.pushError('Upload error', u.msg);
  286. });
  287. if(failedUpls.length < data.results.length) {
  288. alerts.push({
  289. title: 'Some uploads succeeded',
  290. message: 'Files that are not mentionned in the errors above were uploaded successfully.'
  291. });
  292. vueFile.uploadSucceeded = true;
  293. }
  294. } else {
  295. vueFile.uploadSucceeded = true;
  296. }
  297. } else {
  298. alerts.pushError('Upload error', data.msg);
  299. }
  300. },
  301. error: (error) => {
  302. alerts.pushError(error.message, this.upload.file.name);
  303. },
  304. finish: () => {
  305. if(vueFile.uploadSucceeded) {
  306. vueFile.waitChangeComplete(curFileAmount, true);
  307. } else {
  308. vueFile.isLoading = false;
  309. }
  310. }
  311. });
  312. });