file_browser.js 21 KB

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