file_browser.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  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. //bentley: the ModelVerse only examines one folder at a time
  279. let file_list = fnames;
  280. if (!(Array.isArray(fnames))){
  281. file_list = await fnames(folder);
  282. }
  283. let _folder = utils.regexpe(folder);
  284. file_list.forEach(function (fname) {
  285. let matches = fname.match('^' + _folder + '(.+?/)');
  286. if (matches) {
  287. if (!utils.contains(folders, matches[1]))
  288. folders.push(matches[1]);
  289. }
  290. else if ((matches = fname.match('^' + _folder + '(.*)'))) {
  291. if (matches[1].length > 0) {
  292. files.push(matches[1]);
  293. }
  294. }
  295. });
  296. let all_entries = folders.concat(files);
  297. //get the maximum filename length
  298. let maxFnameLength = utils.max(all_entries, function (_) {
  299. return _.length;
  300. });
  301. // var tmpDiv = $("<div>");
  302. all_entries.forEach(function (fname) {
  303. let icon = HttpUtils.getFileIcon(fname);
  304. if (icon) {
  305. icon.css("width", 8 + maxFnameLength + 'ex');
  306. icon.click(function (ev) {
  307. clearSelection();
  308. if (fname.match(/\/$/))
  309. setCurrentFileBrowserFolder(folder + fname, fnames);
  310. else {
  311. input.val(folder + fname);
  312. selection = icon;
  313. selection.attr("class", 'fileb_icon_selected');
  314. }
  315. });
  316. if (draggable) {
  317. //icon.setAttribute('draggable',true);
  318. icon.attr("draggable", true);
  319. icon.get(0).ondragstart =
  320. function (event) {
  321. var uri = HttpUtils.url(folder + fname + '.file', __NO_WID);
  322. event.dataTransfer.effectAllowed = 'copyMove';
  323. event.dataTransfer.setData(
  324. 'DownloadURL',
  325. 'application/zip:' + fname + '.zip:' +
  326. window.location.origin + uri);
  327. event.dataTransfer.setData('uri', uri);
  328. };
  329. }
  330. div.append(icon);
  331. }
  332. });
  333. if (newfile) {
  334. var icon = HttpUtils.getNewFileIcon(function (ev) {
  335. input.val(folder + ev.target.textContent);
  336. });
  337. icon.css("width", 8 + maxFnameLength + 'ex');
  338. div.append(icon);
  339. }
  340. navdiv.empty();
  341. // while (navdiv.children().length > 0) {
  342. // navdiv.find(navdiv.lastChild).remove();
  343. // }
  344. //let tmpDiv = $("<div>");
  345. var subfolders = folder.split('/').filter(function (subf) {
  346. return subf != '';
  347. });
  348. subfolders.unshift('/');
  349. subfolders.forEach(function (subfolder) {
  350. var navbutton = $('<button>');
  351. navbutton.html(subfolder);
  352. navbutton.attr('id', 'navbar_' + subfolder);
  353. navbutton.click(navbuttononclick);
  354. navdiv.append(navbutton);
  355. });
  356. //navdiv.html( tmpDiv.html() );
  357. if (exists) {
  358. // $('#div_fileb-contents').html( div.html() );
  359. // fileb.append(div);
  360. $('#div_fileb-contents').remove();
  361. fileb.append(div);
  362. } else
  363. fileb.append(div);
  364. clearSelection();
  365. currfolder = folder;
  366. };
  367. let file_browser_width = 120;
  368. fileb.css("width", file_browser_width + 'ex')
  369. .css("maxWidth", '100%');
  370. navdiv.css("align", 'left');
  371. input.attr("type", 'hidden');
  372. fileb.append(navdiv);
  373. fileb.append(input);
  374. setCurrentFileBrowserFolder(startfolder ? startfolder : '/', fnames);
  375. return {
  376. 'filebrowser': fileb,
  377. 'filepane': function () {
  378. return $('#div_fileb-contents');
  379. },
  380. 'getcurrfolder': function () {
  381. return currfolder;
  382. },
  383. 'getselection': function () {
  384. return input.val().trim();
  385. },
  386. 'clearselection': function () {
  387. clearSelection();
  388. },
  389. 'refresh': function (fnames, the_folder) {
  390. setCurrentFileBrowserFolder(the_folder || currfolder, fnames);
  391. }
  392. };
  393. }
  394. }