file_browser.js 21 KB

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