file_browser.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. class FileBrowser{
  2. static buildFileBrowser(extensions, manualInput, title, startDir, callback) {
  3. HttpUtils.httpReq(
  4. 'GET',
  5. HttpUtils.url('/filelist', __NO_WID),
  6. undefined,
  7. function (statusCode, resp) {
  8. extensions.push('/');
  9. var fnames = __localizeFilenames(
  10. __filterFilenamesByExtension(
  11. resp.split('\n'),
  12. extensions || ['.*'])
  13. ).sort(),
  14. folder_buttons = $('<div>'),
  15. new_folder_b = $('<button>'),
  16. rename_folder_b = $('<button>'),
  17. delete_folder_b = $('<button>'),
  18. move_folder_b = $('<button>'),
  19. file_buttons = $('<div>'),
  20. rename_file_b = $('<button>'),
  21. delete_file_b = $('<button>'),
  22. move_file_b = $('<button>'),
  23. feedbackarea = $('<div>'),
  24. feedback = GUIUtils.getTextSpan('', "feedback"),
  25. fileb =
  26. FileBrowser.getFileBrowser(fnames, false, manualInput, __getRecentDir(startDir));
  27. new_folder_b.html('new folder')
  28. .click(function (ev) {
  29. var folder_name = prompt("please fill in a name for the folder");
  30. if (folder_name != null) {
  31. folder_name = folder_name.replace(/^\s+|\s+$/g, ''); // trim
  32. if (!folder_name.match(/^[a-zA-Z0-9_\s]+$/i)) {
  33. feedback.html("invalid folder name: " + folder_name);
  34. } else {
  35. console.log("/" + window.localStorage.getItem('user') + fileb['getcurrfolder']() + folder_name + '.folder');
  36. DataUtils.createFolder("/" + window.localStorage.getItem('user') + fileb['getcurrfolder']() + folder_name + '.folder', function (statusCode, resp) {
  37. if (!utils.isHttpSuccessCode(statusCode)) {
  38. feedback.html(resp)
  39. } else {
  40. feedback.html('created ' + folder_name);
  41. fnames.push(fileb['getcurrfolder']() + folder_name + "/")
  42. fileb['refresh'](fnames);
  43. }
  44. });
  45. }
  46. }
  47. });
  48. folder_buttons.append(new_folder_b);
  49. rename_folder_b.html('rename folder')
  50. .click(function (ev) {
  51. var value = fileb['getcurrfolder']();
  52. var folder_name = prompt("please fill in a new name for folder " + value);
  53. if (folder_name != null) {
  54. folder_name = folder_name.replace(/^\s+|\s+$/g, ''); // trim
  55. if (!folder_name.match(/^[a-zA-Z0-9_\s]+$/i)) {
  56. feedback.html("invalid folder name: " + folder_name);
  57. } else {
  58. DataUtils.renameInCloud("/" + window.localStorage.getItem('user') + value.slice(0, -1) + ".folder", folder_name, function (statusCode, resp) {
  59. if (!utils.isHttpSuccessCode(statusCode)) {
  60. feedback.html(resp);
  61. } else {
  62. var matches = value.match(/^\/(.*\/)?(.*)\/$/),
  63. newvalue = "/" + (matches[1] || "") + folder_name + "/";
  64. for (var idx in fnames) {
  65. fnames[idx] = fnames[idx].replace(new RegExp("^(" + value + ")(.*)"), newvalue + "$2");
  66. }
  67. fileb['refresh'](fnames, newvalue);
  68. fileb['clearselection']();
  69. feedback.html('renamed ' + value + ' to ' + newvalue);
  70. }
  71. });
  72. }
  73. }
  74. });
  75. folder_buttons.append(rename_folder_b);
  76. delete_folder_b.html('delete folder')
  77. .click(function (ev) {
  78. var value = fileb['getcurrfolder']();
  79. if (confirm("are you sure you want to delete " + value + "?")) {
  80. DataUtils.deleteFromCloud("/" + window.localStorage.getItem('user') + value.slice(0, -1) + ".folder", function (statusCode, resp) {
  81. if (!utils.isHttpSuccessCode(statusCode)) {
  82. feedback.html(resp);
  83. } else {
  84. var matches = value.match(/^\/(.*\/)?(.*)\/$/),
  85. newvalue = "/_Trash_" + value;
  86. for (var idx in fnames) {
  87. fnames[idx] = fnames[idx].replace(new RegExp("^(" + value + ")(.*)"), newvalue + "$2");
  88. }
  89. fileb['refresh'](fnames);
  90. fileb['clearselection']();
  91. feedback.html('deleted ' + value);
  92. }
  93. });
  94. }
  95. });
  96. folder_buttons.append(delete_folder_b);
  97. move_folder_b.html('move folder')
  98. .click(function (ev) {
  99. var value = fileb['getcurrfolder']();
  100. var folder_loc = prompt("please fill in a new parent folder for folder " + value);
  101. if (folder_loc != null) {
  102. folder_loc = folder_loc.replace(/^\s+|\s+$/g, ''); // trim
  103. if (!folder_loc.match(/^\/([a-zA-Z0-9_\s]+\/)*$/i)) {
  104. feedback.html("invalid parent location: " + folder_loc);
  105. } else {
  106. DataUtils.moveInCloud("/" + window.localStorage.getItem('user') + value.slice(0, -1) + ".folder", folder_loc, function (statusCode, resp) {
  107. if (!utils.isHttpSuccessCode(statusCode)) {
  108. feedback.html(resp);
  109. } else {
  110. var matches = value.match(/^\/(.*\/)?(.*)\/$/),
  111. newvalue = folder_loc + matches[2] + "/";
  112. for (var idx in fnames) {
  113. fnames[idx] = fnames[idx].replace(new RegExp("^(" + value + ")(.*)"), newvalue + "$2");
  114. }
  115. fileb['refresh'](fnames, newvalue);
  116. fileb['clearselection']();
  117. feedback.html('moved ' + value + ' to ' + folder_loc);
  118. }
  119. });
  120. }
  121. }
  122. });
  123. folder_buttons.append(move_folder_b);
  124. rename_file_b.html('rename file')
  125. .click(function (ev) {
  126. var value = fileb['getselection']();
  127. var file_name = prompt("please fill in a new name for file " + value);
  128. if (file_name != null) {
  129. file_name = file_name.replace(/^\s+|\s+$/g, ''); // trim
  130. if (!file_name.match(/^[a-zA-Z0-9_\s\.]+$/i)) {
  131. feedback.html("invalid file name: " + file_name);
  132. } else {
  133. DataUtils.renameInCloud("/" + window.localStorage.getItem('user') + value + ".file", file_name, function (statusCode, resp) {
  134. if (!utils.isHttpSuccessCode(statusCode)) {
  135. feedback.html(resp);
  136. } else {
  137. var matches = value.match(/^\/(.*\/)?(.*)$/),
  138. newvalue = "/" + (matches[1] || "") + file_name;
  139. var idx = fnames.indexOf(value);
  140. if (idx >= 0) {
  141. fnames[idx] = newvalue;
  142. }
  143. fileb['refresh'](fnames);
  144. fileb['clearselection']();
  145. feedback.html('renamed ' + value + ' to ' + newvalue);
  146. }
  147. });
  148. }
  149. }
  150. });
  151. file_buttons.append(rename_file_b);
  152. delete_file_b.html('delete file')
  153. .click(function (ev) {
  154. var value = fileb['getselection']();
  155. if (confirm("are you sure you want to delete " + value + "?")) {
  156. DataUtils.deleteFromCloud("/" + window.localStorage.getItem('user') + value + ".file", function (statusCode, resp) {
  157. if (!utils.isHttpSuccessCode(statusCode)) {
  158. feedback.html(resp);
  159. } else {
  160. feedback.html('deleted ' + value);
  161. var idx = fnames.indexOf(value);
  162. if (idx >= 0) {
  163. fnames.splice(idx, 1);
  164. }
  165. fileb['refresh'](fnames);
  166. fileb['clearselection']();
  167. }
  168. });
  169. }
  170. });
  171. file_buttons.append(delete_file_b);
  172. move_file_b.html('move file')
  173. .click(function (ev) {
  174. var value = fileb['getselection']();
  175. var folder_loc = prompt("please fill in a new parent folder for file " + value);
  176. if (folder_loc != null) {
  177. folder_loc = folder_loc.replace(/^\s+|\s+$/g, ''); // trim
  178. if (!folder_loc.match(/^\/([a-zA-Z0-9_\s]+\/)*$/i)) {
  179. feedback.html("invalid parent location: " + folder_loc);
  180. } else {
  181. DataUtils.moveInCloud("/" + window.localStorage.getItem('user') + value + ".file", folder_loc, function (statusCode, resp) {
  182. if (!utils.isHttpSuccessCode(statusCode)) {
  183. feedback.html(resp);
  184. } else {
  185. var matches = value.match(/^\/(.*\/)?(.*)$/),
  186. newvalue = folder_loc + matches[2];
  187. feedback.html('moved ' + value + ' to ' + folder_loc);
  188. var idx = fnames.indexOf(value);
  189. if (idx >= 0) {
  190. fnames[idx] = newvalue;
  191. }
  192. fileb['refresh'](fnames);
  193. fileb['clearselection']();
  194. }
  195. });
  196. }
  197. }
  198. });
  199. file_buttons.append(move_file_b);
  200. GUIUtils.setupAndShowDialog(
  201. [fileb['filebrowser'], folder_buttons, file_buttons, feedback],
  202. function () {
  203. var value = [fileb['getselection']()];
  204. if (value.length > 0 && value[0] != "" && startDir) {
  205. __setRecentDir(startDir, value[0].substring(0, value[0].lastIndexOf('/') + 1));
  206. }
  207. return value;
  208. },
  209. __TWO_BUTTONS,
  210. title,
  211. callback);
  212. });
  213. };
  214. /**
  215. * Returns a <div> with an interactive file browser within it
  216. *
  217. * @param fnames - a complete list of all files in the directory tree
  218. * structure
  219. * @param draggable - when true, files and folders can be meaningfully
  220. * dragged
  221. * @param newfile when true, an editable new file icon is present
  222. * @param startfolder if set, starts navigation at the specified folder
  223. */
  224. static getFileBrowser(fnames, draggable, newfile, startfolder) {
  225. var maxFnameLength = utils.max(fnames, function (_) {
  226. return _.length;
  227. }),
  228. fileb = $("<div>"),
  229. navdiv = $("<div>"),
  230. input = $("<input>"),
  231. selection = undefined,
  232. currfolder = '/',
  233. clearSelection =
  234. function () {
  235. if (selection) {
  236. selection.attr("class", 'fileb_icon');
  237. selection = undefined;
  238. input.val('');
  239. }
  240. },
  241. navbuttononclick =
  242. /* 1 construct the full path associated to the clicked button
  243. 2 remove 'deeper' buttons
  244. 3 chdir to selected folder */
  245. function (ev) {
  246. var path = '';
  247. for (var i = 0; i < navdiv.children("button").length; i++) {
  248. path += $(navdiv.children()[i]).html() + '/';
  249. if ($(navdiv.children()[i]).html() == $(ev.target).html())
  250. break;
  251. }
  252. setCurrentFileBrowserFolder(path.substring(1), fnames);
  253. },
  254. setCurrentFileBrowserFolder =
  255. /* 1 determine files and folders from the given folder
  256. 2 produce icons for them and add them to to-be content div
  257. 3 create navigation toolbar for complete directory hierarchy
  258. 4 replace previous content div, if any, with new one
  259. 5 clear past selection, if any, and remember current folder */
  260. function (folder, fnames) {
  261. var div = $('#div_fileb-contents'),
  262. folders = [],
  263. files = [],
  264. maxFnameLength = 0,
  265. exists = false;
  266. // If it already exists, remove everything!
  267. if (div.length > 0) {
  268. $('#div_fileb-contents').remove();
  269. // exists = true;
  270. }
  271. div = $("<div>");
  272. div.attr("class", 'fileb_pane')
  273. .attr("id", 'div_fileb-contents');
  274. fnames.forEach(function (fname) {
  275. let _folder = utils.regexpe(folder);
  276. let matches = fname.match('^' + _folder + '(.+?/)');
  277. if (matches) {
  278. if (!utils.contains(folders, matches[1]))
  279. folders.push(matches[1]);
  280. else
  281. return;
  282. }
  283. else if ((matches = fname.match('^' + _folder + '(.*)'))) {
  284. if (matches[1].length > 0) {
  285. files.push(matches[1]);
  286. } else {
  287. return;
  288. }
  289. }
  290. else
  291. return;
  292. maxFnameLength =
  293. Math.max(maxFnameLength, matches[1].length);
  294. });
  295. // var tmpDiv = $("<div>");
  296. folders.concat(files).forEach(function (fname) {
  297. let icon = HttpUtils.getFileIcon(fname);
  298. if (icon) {
  299. icon.css("width", 8 + maxFnameLength + 'ex');
  300. icon.click(function (ev) {
  301. clearSelection();
  302. if (fname.match(/\/$/))
  303. setCurrentFileBrowserFolder(folder + fname, fnames);
  304. else {
  305. input.val(folder + fname);
  306. selection = icon;
  307. selection.attr("class", 'fileb_icon_selected');
  308. }
  309. });
  310. if (draggable) {
  311. //icon.setAttribute('draggable',true);
  312. icon.attr("draggable", true);
  313. icon.get(0).ondragstart =
  314. function (event) {
  315. var uri = HttpUtils.url(folder + fname + '.file', __NO_WID);
  316. event.dataTransfer.effectAllowed = 'copyMove';
  317. event.dataTransfer.setData(
  318. 'DownloadURL',
  319. 'application/zip:' + fname + '.zip:' +
  320. window.location.origin + uri);
  321. event.dataTransfer.setData('uri', uri);
  322. };
  323. }
  324. div.append(icon);
  325. }
  326. });
  327. if (newfile) {
  328. var icon = HttpUtils.getNewFileIcon(function (ev) {
  329. input.val(folder + ev.target.textContent);
  330. });
  331. icon.css("width", 8 + maxFnameLength + 'ex');
  332. div.append(icon);
  333. }
  334. navdiv.empty();
  335. // while (navdiv.children().length > 0) {
  336. // navdiv.find(navdiv.lastChild).remove();
  337. // }
  338. //let tmpDiv = $("<div>");
  339. var subfolders = folder.split('/').filter(function (subf) {
  340. return subf != '';
  341. });
  342. subfolders.unshift('/');
  343. subfolders.forEach(function (subfolder) {
  344. var navbutton = $('<button>');
  345. navbutton.html(subfolder);
  346. navbutton.click(navbuttononclick);
  347. navdiv.append(navbutton);
  348. });
  349. //navdiv.html( tmpDiv.html() );
  350. if (exists) {
  351. // $('#div_fileb-contents').html( div.html() );
  352. // fileb.append(div);
  353. $('#div_fileb-contents').remove();
  354. fileb.append(div);
  355. } else
  356. fileb.append(div);
  357. clearSelection();
  358. currfolder = folder;
  359. };
  360. fileb.css("width", maxFnameLength + 'ex')
  361. .css("maxWidth", '100%');
  362. navdiv.css("align", 'left');
  363. input.attr("type", 'hidden');
  364. fileb.append(navdiv);
  365. fileb.append(input);
  366. setCurrentFileBrowserFolder(startfolder ? startfolder : '/', fnames);
  367. return {
  368. 'filebrowser': fileb,
  369. 'filepane': function () {
  370. return $('#div_fileb-contents');
  371. },
  372. 'getcurrfolder': function () {
  373. return currfolder;
  374. },
  375. 'getselection': function () {
  376. return input.val();
  377. },
  378. 'clearselection': function () {
  379. clearSelection()
  380. },
  381. 'refresh': function (fnames, the_folder) {
  382. setCurrentFileBrowserFolder(the_folder || currfolder, fnames);
  383. }
  384. };
  385. };
  386. }