Supercharged Local Directory File Browser

Makes file:/// directory ("Index of...") pages actually useful. Adds navigation links, file preview pane, user-defined shortcuts, filtering, keyboard navigation, more.

Tính đến 23-03-2018. Xem phiên bản mới nhất.

  1. // ==UserScript==
  2. // @name Supercharged Local Directory File Browser
  3. // @version 1.4
  4. // @description Makes file:/// directory ("Index of...") pages actually useful. Adds navigation links, file preview pane, user-defined shortcuts, filtering, keyboard navigation, more.
  5. // @author Gaspar Schott
  6. // @license GPL-3.0
  7. // @match file:///*
  8. // @require http://code.jquery.com/jquery-latest.min.js
  9.  
  10. // This script was developed in Vivaldi, running on Mac OS High Sierra. It has been tested in Chromium, Opera Next, Iridium, and in general it should work in all Chrome-based browsers.
  11. // It does not work in Safari because Safari does not allow local directories to be browsed.
  12. // In theory, it should work in Firefox, but because Firefox uses a different DOM for building the Index pages, the script would have to be rewritten.
  13.  
  14. // NOTE: By default, Greasemonkey and Tampermonkey will not run scripts on file:/// urls, so for this script to work, you will have to enable it first.
  15. // For Greasemonkey, open about:config and change greasemonkey.fileIsGreaseable to true.
  16. // For Tampermonkey, go to Chrome extension page, and tick the 'Allow access to file URLs' checkbox at the Tampermonkey extension section.
  17.  
  18. // CHANGELOG:
  19.  
  20. // v. 1.4
  21. // Added: Initial support for Firefox. Tested in Firefox 59, Waterfox 56.
  22. // Changed: Use SVG for menu icons
  23. // Changed: Code cleanup, reorganization
  24.  
  25. // v. 1.3
  26. // Fixed: Keyboard navigation of ignored or invisible items fails if "Hide Invisibles" is toggled off after loading a directory.
  27. // Added: Also hide ignored files if "Hide Invisibles" is checked.
  28. // Added: Content reload button.
  29. // Changed: Reorganized settings to reduce confusion about how ignored files are treated.
  30.  
  31. // v. 1.2
  32. // Click to show menus instead of hover.
  33. // Added Cmd/Crl+Shift+O keybinding to open selected item in new window
  34. // Arrow navigation bugfixes
  35.  
  36. // TODO:
  37. // Resize elements on window resize (e.g. menus)
  38. // Add dark mode: better to use UserStyle instead?
  39. // Fix Scroll into view -- dir_table doesn't scroll
  40. // Support for Apache index pages (uses ul and li instead of tables)
  41. // Support for Firefox index pages (uses embedded tables)
  42. // if invisible item is selected when "hide invisibles" is unchecked, select nearest previous visible item when hide invisible is checked agagin?
  43.  
  44. // @namespace https://greatest.deepsurf.us/users/16170
  45. // ==/UserScript==
  46.  
  47. (function() {
  48. 'use strict';
  49. var $ = jQuery;
  50.  
  51. function platformIsMac() {
  52. return navigator.platform.indexOf('Mac') > -1;
  53. }
  54. function platformIsWin() {
  55. return navigator.platform.indexOf('Win') > -1;
  56. }
  57. // Don't run script in iframes or files (only directories)
  58. // if ( window.top != window.self ) {
  59. if ( window.frameElement !== null ) {
  60. return;
  61. } else if ( window.location.pathname.slice(-1) != '/') {
  62. return;
  63. }
  64.  
  65. // ***** USER SETTINGS ***** //
  66.  
  67. var $settings = {
  68.  
  69. user_name: // Your computer user name
  70. '',
  71. // Shortcuts: add directories and files here. (You can use your browser's bookmarks, of course.)
  72. root_shortcuts: // Root directories: add or remove as you please (but at leave empty brackets). These defaults are applicable to Mac OS.
  73. ['Applications','Library','Users','Volumes'],
  74. // ['C:/Users','C:/Program Files','C:/Windows'],
  75. user_shortcuts: // User directories; you must enter your user_name above.
  76. ['Documents','Photos'],
  77. file_shortcuts: // Add specific file paths, e.g.: 'Users/MyUserName/Documents/MyDocument.html'
  78. // These files will be selected (loaded) automatically when their containing directory is loaded
  79. // Limitations: only works for one file per directory; if more than one file per directory is listed, the last item will be selected.
  80. ['path/to/file.ext'],
  81. ignore_files: // If true, ignored files (see below) will be greyed-out (default) in the file list and will not be loaded in the content pane when selected;
  82. // If false, they will be treated as normal files, so if they are selected, the browser will attempt to download any file types it can't handle (which makes keyboard navigation inconvenient).
  83. true,
  84. hide_ignored_files: // If true, ignored files will be hidden in the file list;
  85. // if false, they will appear greyed-out (default).
  86. false,
  87. ignore_file_types: // ignore files with these extensions:
  88. ['exe','.doc','.docx','ppt','pptx','xls','xlsx','odt','odp','msi','dll','rtf','indd','idml','.pages','.tif','tiff','.eps','.psd','.ai','.zip','pkg','.swf','.pls','.ics','.ds_store','alias','.dmg','.gz','.qxp','icon.jpg','thumbs.db'], // lowercase
  89. hide_invisibles: // Mac OS only: Files beginning with a "." will be ignored.
  90. true,
  91. apps_as_dirs: // Mac OS only: if true, treat apps as directories; allows app contents to be browsed. This is the default behavior for Chrome.
  92. // If false, treat apps as ignored files.
  93. true,
  94. dark_mode: // To be implemented
  95. false
  96. };
  97.  
  98. // ***** END USER SETTINGS ***** //
  99.  
  100. // ***** BUILD MENUS ***** //
  101.  
  102. // PATHS
  103. var $location = window.location.pathname;
  104. var $current_dir_path = $location.replace(/%20/g,' ').replace(/\//g,'/<wbr>').replace(/_/g,'_<wbr>').replace(/—/g,'—<wbr>').replace(/\\/g,'/');
  105. var $current_dir_name = $location.replace(/%20/g,' ').slice(0,-1);
  106. $current_dir_name = $current_dir_name.slice($current_dir_name.lastIndexOf('/') + 1);
  107. var $location_arr = $location.split('/');
  108. var $parent_dir_link = $location_arr.slice(0,-2).join('/') + '/';
  109.  
  110. // Parents Link Menu Items
  111. var $parent_links_arr = function() {
  112. var $paths_arr = [];
  113. for ( var i = 1; i < $location_arr.length - 1; i++ ) {
  114. $paths_arr[0] = ''; // root
  115. $paths_arr[i] = $paths_arr[i - 1] + $location_arr[i] + '/';
  116. }
  117. return $paths_arr;
  118. };
  119.  
  120. // function to build menu list items
  121. function menu_items(x,y,i) {
  122. var $menu_item = '<li><a href="file:///' + x[i] + '" style="margin:0;padding:4px 6px;display:block;text-indent:0;text-decoration:none;color:#333;white-space:normal;">' + y[i] + '</a></li>';
  123. return $menu_item;
  124. }
  125.  
  126. // Parents Directory Menu Items
  127. var $parents_dir_menu_arr = function() {
  128. var $parents_dir_menu_items = [];
  129. for ( var i = 1; i < $parent_links_arr().length; i++ ) {
  130. $parents_dir_menu_items[0] = menu_items('/','/',0); // root
  131. $parents_dir_menu_items[i] = menu_items($parent_links_arr(),$parent_links_arr(),i);
  132. }
  133. $parents_dir_menu_items.pop(); // remove current directory
  134. $parents_dir_menu_items = $parents_dir_menu_items.reverse().join('').replace(/%20/g,' ');
  135. return $parents_dir_menu_items;
  136. };
  137.  
  138. // Root Shortcuts Menu Items
  139. $settings.root_shortcuts = $settings.root_shortcuts.map(i => i + '/'); // add '/'
  140.  
  141. var $root_shortcuts_menu_arr = function() {
  142. if ( $settings.root_shortcuts.length ) {
  143. var $root_shortcut_items = [];
  144. for ( var i = 0; i < $settings.root_shortcuts.length; i++ ) {
  145. $root_shortcut_items[i] = menu_items($settings.root_shortcuts,$settings.root_shortcuts,i);
  146. }
  147. $root_shortcut_items = $root_shortcut_items.join('');
  148. return $root_shortcut_items;
  149. }
  150. };
  151.  
  152. // User Shortcuts Menu Items
  153. var $user_shortcuts_display_name = $settings.user_shortcuts.map(i => $settings.user_name + '/' + i + '/' ); // build display names
  154. $settings.user_shortcuts = $settings.user_shortcuts.map(i => 'users/' + $settings.user_name + '/' + i + '/'); // build link fragments
  155.  
  156. var $user_shortcuts_menu_arr = function() {
  157. if ( $settings.user_name && $settings.user_shortcuts.length ) {
  158. var $user_shortcut_items = [];
  159. for ( var i = 0; i < $settings.user_shortcuts.length; i++ ) {
  160. $user_shortcut_items[i] = menu_items($settings.user_shortcuts,$user_shortcuts_display_name,i);
  161. }
  162. $user_shortcut_items = $user_shortcut_items.join('');
  163. return $user_shortcut_items;
  164. }
  165. };
  166.  
  167. // File Shortcuts Menu Items
  168. var $file_shortcuts_display_name = $settings.file_shortcuts.map(i => i.split('/').pop()); // get file names from paths
  169.  
  170. var $file_shortcuts_menu_arr = function() {
  171. if ( $settings.file_shortcuts.length ) {
  172. var $file_shortcut_items = [];
  173. for ( var i = 0; i < $settings.file_shortcuts.length; i++ ) {
  174. $file_shortcut_items[i] = menu_items($settings.file_shortcuts,$file_shortcuts_display_name,i);
  175. }
  176. $file_shortcut_items = $file_shortcut_items.join('').replace(/\/<\/a>/g,'<\/a>').replace(/<a /g,'<a class="file_shortcut" ').replace(/%20/g,' ');
  177. return $file_shortcut_items;
  178. }
  179. };
  180.  
  181. // ***** END BUILD MENUS ***** //
  182.  
  183.  
  184. // ***** BUILD UI ELEMENTS ***** //
  185.  
  186. // ***** SIDEBAR ELEMENTS ***** //
  187.  
  188. // 1. Parent Directory Menu
  189. var $up_arrow = 'url("data:image/svg+xml;utf8,<svg version=\'1.1\' id=\'Layer_1\' xmlns=\'http://www.w3.org/2000/svg\' xmlns:xlink=\'http://www.w3.org/1999/xlink\' x=\'0px\' y=\'0px\' width=\'12.728px\' height=\'7.779px\' viewBox=\'0 0 12.728 7.779\' enable-background=\'new 0 0 12.728 7.779\' xml:space=\'preserve\'><path fill=\'%23444444\' d=\'M6.364,2.828l4.95,4.949l1.414-1.414L6.364,0l0,0L0,6.363l1.413,1.416L6.364,2.828\'/></svg>")';
  190. var $parent_dir_menu = $(
  191. '<nav id="parent_dir_menu" style="margin:0;padding:0;display:table;width:100%;">' +
  192. '<a href="" style="width:100%;padding:0;display:table-cell;text-align:center;vertical-align:middle;text-decoration:none;opacity:0.7"></a>' +
  193. '</nav>'
  194. );
  195. $parent_dir_menu.find('a').attr('href',$parent_dir_link).css({'background':$up_arrow + 'center no-repeat'});
  196.  
  197. // 2. Current Directory Name and Parents Directory Menu
  198. var $parents_dir_menu = $(
  199. '<nav id="parents_dir_menu" style="margin:0;padding:0;text-align:center;">' +
  200. '<div style="padding:4px 6px;display:inline-block;text-align:center;vertical-align:middle;overflow:hidden;cursor:pointer;hyphens:none;white-space:normal;"></div>' +
  201. '<ul class="menu" style="display:none;margin:0;padding:0;position:absolute;top:;right:0;left:0;z-index:100;text-indent:0;text-align:left;background:lightgray;border-top:solid 1px gray;border-bottom:solid 1px gray;list-style-type:none;box-shadow: 0px 2px 3px -2px #888;"></ul>' +
  202. '</nav>'
  203. );
  204. $parents_dir_menu.find('div').append( $current_dir_path );
  205. $parents_dir_menu.find('ul').append( $parents_dir_menu_arr() );
  206.  
  207. // 3. Shortcuts Menu
  208. var $divider = $(
  209. '<li><hr style="margin:0;border-bottom:0;"></li>'
  210. );
  211. var $menu_icon = 'url("data:image/svg+xml;utf8,<svg version=\'1.1\' id=\'Layer_1\' xmlns=\'http://www.w3.org/2000/svg\' xmlns:xlink=\'http://www.w3.org/1999/xlink\' x=\'0px\' y=\'0px\' width=\'13px\' height=\'10px\' viewBox=\'0 0 13 10\' enable-background=\'new 0 0 13 10\' xml:space=\'preserve\'><rect fill=\'%23444444\' width=\'13\' height=\'2\'/><rect y=\'4\' fill=\'%23444444\' width=\'13\' height=\'2\'/><rect y=\'8\' fill=\'%23444444\' width=\'13\' height=\'2\'/></svg>")';
  212. var $shortcuts_menu = $(
  213. '<nav id="shortcuts_menu" style="margin:0;padding:0;">' +
  214. '<div style="width:6em;display:table-cell;text-align:center;vertical-align:middle;font-size:18px;cursor:pointer;opacity:0.7"></div>' +
  215. '<ul class="menu" style="display:none;margin:0;padding:0;position:absolute;right:0;left:0;z-index:100;text-indent:0;text-align:left;background:lightgray;border-top:solid 1px gray;border-bottom:solid 1px gray;list-style-type:none;box-shadow: 0px 2px 3px -2px #888;"></ul>' +
  216. '</nav>'
  217. );
  218. $shortcuts_menu.find('div').css({'background':$menu_icon + 'center no-repeat'});
  219. $shortcuts_menu.find('ul').append( $root_shortcuts_menu_arr(), $divider, $user_shortcuts_menu_arr(), $divider.clone(), $file_shortcuts_menu_arr(), $divider.clone() );
  220.  
  221. // 4. Details Button
  222. var $details_btn = $(
  223. '<button id="details_btn" style="margin:1em 0.5em 0.5em;clear:both" tabindex="-1">' +
  224. '<span id="show">Show details</span><span id="hide" style="display:none">' +
  225. 'Hide details' +
  226. '</span>' +
  227. '</button>'
  228. );
  229.  
  230. // 5. Invisibles Checkbox
  231. var $inv_checkbox = $(
  232. '<label id="inv_checkbox" for="inv_checkbox">' +
  233. '<input type="checkbox" name="inv_checkbox" tabindex="-1" />' +
  234. 'Hide Invisibles' +
  235. '</label>'
  236. );
  237.  
  238. // 6. Image Grid Button
  239. var $grid_icon = 'url("data:image/svg+xml;utf8,<svg version=\'1.1\' id=\'Layer_1\' xmlns=\'http://www.w3.org/2000/svg\' xmlns:xlink=\'http://www.w3.org/1999/xlink\' x=\'0px\' y=\'0px\' width=\'16px\' height=\'16px\' viewBox=\'0 0 16 16\' enable-background=\'new 0 0 16 16\' xml:space=\'preserve\'><g><path fill=\'%23666666\' d=\'M5,2v3H2V2H5 M7,0H0v7h7V0L7,0z\'/></g><g><path fill=\'%23666666\' d=\'M14,2v3h-3V2H14 M16,0H9v7h7V0L16,0z\'/></g><g><path fill=\'%23666666\' d=\'M5,11v3H2v-3H5 M7,9H0v7h7V9L7,9z\'/></g><g><path fill=\'%23666666\' d=\'M14,11v3h-3v-3H14 M16,9H9v7h7V9L16,9z\'/></g></svg>")';
  240. var $content_grid_btn = $(
  241. '<div id="grid_btn" style="margin:0 0 -0.5em 1em;width:17px;height:17px;display:none;position:absolute;top:1em;right:0.5em;line-height:0;cursor:pointer;outline:0;opacity:0.7;" tabindex="-1" title="Show Image Grid">' +
  242. '</div>'
  243. );
  244. $content_grid_btn.css({'background':$grid_icon + 'no-repeat center 100%'});
  245.  
  246. // ASSEMBLE SIDEBAR HEADER
  247. var $sidebar_header = $(
  248. '<table id="sidebar_header" style="width:100%;position:relative;border:0;user-select:none;border-collapse:collapse;">' +
  249. '<thead>' +
  250. '<tr style="border-bottom:solid 1px grey;background-color:#BBB;">' +
  251. '<th colspan="3" style="padding:4px;font-weight:normal;font-size:0.875em;letter-spacing:0.5em;cursor:default;">' +
  252. 'INDEX OF' +
  253. '</th>' +
  254. '</tr>' +
  255. '</thead>' +
  256. '<tbody>' +
  257. '<tr style="border-bottom:solid 1px grey;background-color:#BBB;">' +
  258. '<td style="width:24px;max-width:24px;min-width:24px;padding:0;float:left;"></td>' +
  259. '<td style="width:100%;padding:0;border-left:solid 1px grey;border-right:solid 1px grey;"></td>' +
  260. '<td style="width:24px;max-width:24px;min-width:24px;padding:0;float:right;"></td>' +
  261. '</tr>' +
  262. '<tr>' +
  263. '<td colspan="3" style="position:relative;"></td>' +
  264. '</tr>' +
  265. '</tbody>' +
  266. '</table>'
  267. );
  268. $sidebar_header.find('tbody tr:nth-child(1)').find('td:nth-child(1)').append( $parent_dir_menu );
  269. $sidebar_header.find('tbody tr:nth-child(1)').find('td:nth-child(2)').append( $parents_dir_menu );
  270. $sidebar_header.find('tbody tr:nth-child(1)').find('td:nth-child(3)').append( $shortcuts_menu );
  271. $sidebar_header.find('tbody tr:last-child td').append( $details_btn, $inv_checkbox, $content_grid_btn );
  272. if ( platformIsWin() ) { $inv_checkbox.hide(); }
  273.  
  274. // 7. Sidebar
  275. var $sidebar = $(
  276. '<div id="sidebar" style="background-color:lightgray;height:'+ window.innerHeight +'px;min-height:100%;overflow-wrap:break-word;box-sizing:border-box;border-right:solid 1px gray;font-size:0.875em;color:#333;"></div>'
  277. );
  278. $sidebar.append($sidebar_header);
  279.  
  280. // 8. Resize Handle
  281. var $handle = $(
  282. '<div id="handle" style="width:8px;position:absolute;top:0;right:-4px;bottom:0;z-index:1000;cursor:col-resize"></div>'
  283. );
  284.  
  285. // Assemble Sidebar Elements
  286. var $sidebar_wrapper = $(
  287. '<td id="sidebar_wrapper" class="focused" style="width:25%;min-width:220px;will-change:width;padding:0;position:relative;border:0;background:lightgray"></td>'
  288. );
  289. $sidebar_wrapper.append( $sidebar, $handle );
  290.  
  291. // ***** END SIDEBAR ELEMENTS ***** //
  292.  
  293. // ***** BUILD CONTENT PANE ELEMENTS ***** //
  294.  
  295. // 1. Reload Button Element
  296. var $content_reload_btn = $(
  297. '<td style="width:6em;padding:4px 6px 3px;vertical-align:middle;float:left;">' +
  298. '<button id="reload_btn" style="hyphens:none;" tabindex="-1">Reload</button>' +
  299. '</td>'
  300. );
  301.  
  302. // 2. Title Element
  303. var $content_title = $(
  304. '<td id="content_title" style="padding:4px 1em 3px;vertical-align:middle;word-break:break-word;text-transform:uppercase;"></td>'
  305. );
  306.  
  307. // 3. Close Button Element
  308. var $content_close_btn = $(
  309. '<td style="width:6em;padding:4px 6px 3px;vertical-align:middle;float:right;">' +
  310. '<button id="close_btn" tabindex="-1">Close</button>' +
  311. '</td>'
  312. );
  313.  
  314. // 4. Content Header Element
  315. var $content_header = $(
  316. '<header id="content_header" style="width:100%;display:none;position:absolute;top:0;right:0;left:0;z-index:200;background:lightgray;border-bottom:solid 1px #AAA;font-size:0.875em;color:#333;text-align:center;">' +
  317. '<table style="width:100%;padding:7px 12px 5px;border-collapse:collapse;"><tbody><tr></tr></tbody></table>' +
  318. '</header>'
  319. );
  320. $content_header.find('tr').append($content_reload_btn, $content_title, $content_close_btn);
  321.  
  322. // 5. Content Mask Element
  323. var $content_mask = $(
  324. '<div id="content_mask" style="position:absolute;top:0;right:0;bottom:0;left:0;display:none;z-index:2000;"></div>'
  325. );
  326. $content_mask.css({'height':window.innerHeight + 'px'});
  327.  
  328. // 6. Image Grid Element
  329. var $content_grid = $(
  330. '<div id="content_grid" style="width:auto;padding:0;display:none;position:absolute;right:0;bottom:0;left:0;overflow:auto;background:#333;grid-gap:0;grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));grid-template-rows: repeat(auto, 150px);"></div>'
  331. );
  332.  
  333. // 7. Image Element
  334. var $image = $(
  335. '<img class="default" style="width:auto;height:auto;max-width:100%;max-height:calc(100% - 3px);cursor:zoom-in;-webkit-user-select:none;position:relative;top:50%;transform:translateY(-50%);" />'
  336. );
  337. var $content_image = $(
  338. '<div id="content_image" style="padding:2em;position:absolute;top:0;right:0;bottom:0;left:0;display:none;background:#333;overflow:auto;text-align:center;"></div>'
  339. );
  340. $content_image.append($image);
  341.  
  342. // 8. Pdf (embed) Element
  343. var $content_embed = $(
  344. '<embed id="content_embed" style="width:100%;height:100%;position:absolute;0;right:0;bottom:0;left:0;display:none;" name="plugin" id="plugin" type="application/pdf" tabindex="0"></embed>'
  345. );
  346.  
  347. // 9. Iframe Element
  348. var $content_iframe = $(
  349. '<iframe id="content_iframe" style="width:100%;height:100%;padding:0;position:absolute;top:0;right:0;bottom:0;left:0;display:none;border:0;" sandbox="allow-scripts allow-same-origin allow-modals" tabindex="0"></iframe>'
  350. );
  351.  
  352. // 10. Object Element
  353. // var $content_object = $(
  354. // '<object id="content_object" style="width:100%;height:100%;padding:0;position:absolute;top:0;right:0;bottom:0;left:0;display:none;border:0;" tabindex="0"></object>'
  355. // );
  356.  
  357. // 9. Next/Prev Elements
  358. var $prev_btn = $('<div id="prev_btn" style="padding:0 1em;display:none;position:absolute;top:0;bottom:0;left:0;z-index:100;opacity:0.6;"></div>');
  359. var $next_btn = $('<div id="next_btn" style="padding:0 1em;display:none;position:absolute;top:0;bottom:0;right:0;z-index:100;opacity:0.6;"></div>');
  360. var $svg_arrow = 'url("data:image/svg+xml;utf8,<svg version=\'1.1\' id=\'Layer_1\' xmlns=\'http://www.w3.org/2000/svg\' x=\'0px\' y=\'0px\' width=\'11px\' height=\'16px\' viewBox=\'234.5 248 11 16\' enable-background=\'new 234.5 248 11 16\' xml:space=\'preserve\'><path d=\'M245.5,261l-3,3l-8-8l8-8l3,3l-5,5L245.5,261z\'/></svg>")';
  361. $prev_btn.css({'filter':'invert(50%)','background':$svg_arrow + ' no-repeat center'});
  362. $next_btn.css({'filter':'invert(50%)','background':$svg_arrow + ' no-repeat center','transform':'rotate(180deg)'});
  363.  
  364. // 10. Content container
  365. var $content_container = $('<section id="content_container" style="width:100%;height:100%;top:0;overflow:visible;"></section>');
  366. $content_container.append( $content_header, $content_mask, $content_grid, $content_image, $content_embed, $content_iframe );
  367.  
  368. // Assemble Content Pane Elements
  369. var $content_pane = $('<td id="content_pane" class="unfocused" style="width:75%;will-change:width;padding:0;border:0;background:white;position:relative;"></td>');
  370.  
  371. $content_pane.append( $content_container, $prev_btn, $next_btn );
  372.  
  373. // Set content height
  374. function setContentHeight() {
  375. var $content_headerHeight = $content_header.outerHeight();
  376. $content_image.css({'top':$content_headerHeight });
  377. $content_grid.add($content_embed).add($content_iframe).css({'height':window.innerHeight - $content_headerHeight,'top':$content_headerHeight });
  378. }
  379. setContentHeight();
  380.  
  381. // resize head and content header
  382. $('window').on('resize', function() {
  383. headerHeight();
  384. setContentHeight();
  385. });
  386.  
  387. // ***** END BUILD CONTENT PANE ELEMENTS ***** //
  388.  
  389.  
  390. // ASSEMBLE MAIN CONTENT = SIDEBAR + CONTENT PANE
  391. var $main_content = $('<table id="main_content" style="width:100%;height:100%;border:0;border-collapse:collapse;"><tbody><tr></tr></tbody></table>');
  392. $main_content.find('tr').append( $sidebar_wrapper, $content_pane );
  393.  
  394. // END BUILD UI ELEMENTS
  395.  
  396.  
  397. // DEFAULT HTML, BODY STYLES
  398.  
  399. // Conditional Styles:
  400. var $userAgent = navigator.userAgent;
  401.  
  402. var $gecko_styles = '<style type="text/css">button { padding:0; } thead {font-size:100%;} #dir_table .dir::before {position:absolute;} </style>';
  403. if( $userAgent.indexOf('Firefox') > -1 ){
  404. document.querySelector('head').innerHTML += $gecko_styles;
  405. }
  406. if( $userAgent.indexOf('Chrome') > -1 ){
  407. }
  408.  
  409. var $body = $('body');
  410. var $dir_table = $body.find('> table');
  411.  
  412. $body.find('> h1:contains("Index of"),> #parentDirLinkBox,> #UI_goUp').remove();
  413. $body.attr('lang','en').parents('html').addBack().css({'margin':'0','padding':'0','max-width':'100%','height':'100%','font-family':'lucidagrande,"fira sans",helvetica,sans-serif','font-size':'13px','hyphens':'auto','overflow':'hidden','border-radius':0});
  414. $body.prepend($main_content);
  415.  
  416. // ***** SIDEBAR ***** //
  417.  
  418. // ***** SIDEBAR HEADER ***** //
  419.  
  420. // Sidebar Header: Set menu container heights
  421. function headerHeight() {
  422. $sidebar_header.find('nav').find('*').addBack().css({'height': 'auto' });
  423. $shortcuts_menu.find('div').add($parent_dir_menu).find('a').addBack().css({'height': ($shortcuts_menu.closest('tr').height() ) +'px' });
  424. }
  425. headerHeight();
  426.  
  427. // Sidebar Header: Show Menu function
  428. function showMenu(x) {
  429. if ( $(x).parents('td').siblings('td').find('.clicked').length ) {
  430. hideMenu();
  431. }
  432. $(x).toggleClass('clicked').find('.menu').toggle();
  433. }
  434. // Sidebar Header: Show Menu on click
  435. $parents_dir_menu.add($shortcuts_menu).on('click',function(e) {
  436. e.stopPropagation();
  437. showMenu(this);
  438. });
  439.  
  440. // Sidebar Header: Hide Menu function
  441. function hideMenu() {
  442. $('.clicked').removeClass('clicked').find('.menu').hide();
  443. }
  444. // Sidebar Header: Hide Menu on click
  445. $(document).on('click',function() {
  446. hideMenu();
  447. });
  448.  
  449. // Sidebar Header: Menu Icons Hover
  450. $('#grid_btn,#shortcuts_menu div,#parent_dir_menu').hover(function() {
  451. $(this).css({'opacity':1});
  452. }, function() {
  453. $(this).css({'opacity':0.7});
  454. });
  455.  
  456. // Sidebar Header: Menu item Hover
  457. $parents_dir_menu.add($shortcuts_menu).find('li').hover(function() {
  458. $(this).css({'background-color':'#BBB'});
  459. }, function() {
  460. $(this).css({'background-color':'lightgrey'});
  461. });
  462.  
  463. // Sidebar Header: Hide invisibles checkbox user setting
  464. if ( $settings.hide_invisibles === true ) {
  465. $inv_checkbox.find('input').prop('checked',true);
  466. }
  467. // Sidebar Header: Toggle Invisibles checkbox
  468. $inv_checkbox.on('click', function(){
  469. if ( $(this).find('input').is(':checked') ) {
  470. $(this).find('input').prop('checked',false);
  471. } else {
  472. $(this).find('input').prop('checked',true);
  473. }
  474. $('.invisible').add('.ignore').toggle().filter('.selected').removeClass('selected');
  475. });
  476.  
  477. // ***** DIR_TABLE SETUP ***** //
  478.  
  479. $dir_table.detach().attr('id','dir_table');
  480.  
  481. // DIR_TABLE: Variables
  482. var $dir_table_head = $dir_table.find('> thead');
  483. var $dir_table_head_cell = $dir_table_head.find('th');
  484. var $dir_table_head_name = $dir_table_head_cell.filter(':first-of-type');
  485. var $dir_table_head_details = $dir_table_head_name.nextAll();
  486. var $dir_table_body = $dir_table.find('> tbody');
  487. var $dir_table_row = $dir_table_body.find('> tr');
  488. var $dir_table_cell = $dir_table_row.find('td');
  489. var $dir_table_item_name = $dir_table_cell.filter(':nth-of-type(1)');
  490. var $dir_table_details = $dir_table_item_name.nextAll();
  491. var $dir_table_link = $dir_table_item_name.find('a');
  492.  
  493. // Dir_table: Styles
  494. $dir_table.css({'width':'100%','min-width':'100px','height':'calc(100% - ' + $sidebar_header.height() + 'px)','border':'0','position':'relative','overflow':'hidden','table-layout':'fixed','border-collapse':'collapse'});
  495. $dir_table_head.css({'text-align':'left','position':'absolute'});
  496. $dir_table_head_name.css({'padding-left':'2em'});
  497. $dir_table_head_cell.css({'padding':'0 24px 4px;'});
  498. $dir_table_body.css({'width':'100%','position':'absolute','top':'18px','right':'0','bottom':'0','left':'0','overflow-y':'auto','outline':'0'});//.attr('tabindex','0');
  499. $dir_table_row.css({'display':'block','margin-inline-start':'0','clear':'both'});
  500. $dir_table_cell.css({'padding':'0px'});
  501. $dir_table_link.css({'margin':'0px','display':'block','background-size':'auto 13px','-webkit-padding-start':'2em','padding':'4px 6px 4px 24px','color':'#333','text-decoration':'none','overflow':'hidden','background-position':'6px 4px','white-space':'normal'});
  502. $dir_table_item_name.css({'display':'block','clear':'right'});
  503. $dir_table_details.add($dir_table_head_details).css({'font-size':'0.875em','text-align':'left','vertical-align':'top'}).hide();
  504. $dir_table_details.not('th').css({'padding':'0 24px 4px','float':'left'});
  505.  
  506. // Sidebar Header: Show details button click function
  507. $details_btn.on('click',function() {
  508. $dir_table_details.add($dir_table_head_details).toggle();
  509. $dir_table_body.css({'top':$dir_table_head.height() + 1 + 'px'});
  510. $(this).find('span').toggle();
  511. });
  512.  
  513. // Dir_table: Row hover effects
  514. $dir_table_row.hover(function() {
  515. $(this).not('.selected').css({'background-color':'#BBB'});
  516. // Highlight corresponding grid item
  517. if ( $content_grid.hasClass('visible') ) {
  518. var $this_href = $(this).find('a').attr('href');
  519. $content_grid.find('a[href="' + $this_href + '"]').parent('div').css({'background':'#555'}).siblings('div:not(".selected")').css({'background':'transparent'});
  520. }
  521. }, function() {
  522. $(this).not('.selected').css({'background-color':'transparent'});
  523. if ( $content_grid.is(':visible') ) {
  524. $content_grid.find('div:not(".selected")').css({'background':'transparent'});
  525. }
  526. });
  527.  
  528. // Dir_table: create link arrays
  529. var $dir_table_dir_link_arr = [];
  530. var $dir_table_file_link_arr = [];
  531. var $dir_table_file_ext_arr = [];
  532. $dir_table_row.not('.ignore,.invisible').find('a').each(function() {
  533. var $this_link = $(this).attr('href').toLowerCase();
  534. if ( $this_link.endsWith('/') ) {
  535. $dir_table_dir_link_arr.push($this_link);
  536. return $dir_table_dir_link_arr;
  537. } else {
  538. var $this_link_ext = $this_link.slice($this_link.lastIndexOf('.'));
  539. $dir_table_file_link_arr.push($this_link);
  540. if ( $dir_table_file_ext_arr.indexOf($this_link_ext) < 0 ) {
  541. $dir_table_file_ext_arr.push($this_link_ext);
  542. }
  543. return $dir_table_file_link_arr, $dir_table_file_ext_arr;
  544. }
  545. });
  546. // Dir_table: array of all dir_table links
  547. var $dir_table_link_arr = [];
  548. $dir_table_link_arr = $dir_table_dir_link_arr.concat($dir_table_file_link_arr);
  549.  
  550. // Dir_table: array of image types
  551. var $image_ext_arr = ['.jpg','.jpeg','.png','apng','.gif','.bmp','webp'];
  552.  
  553. // Dir_table: Classify items
  554. $dir_table_row.each(function() {
  555.  
  556. var $this_href = $(this).find('a').attr('href').toString().toLowerCase();
  557.  
  558. // Directories or files
  559. if ( $this_href.endsWith('/') ) {
  560. $(this).addClass('dir');
  561. } else {
  562. $(this).addClass('file');
  563. }
  564. // pdf
  565. if ( $this_href.endsWith('.pdf') ) {
  566. $(this).addClass('pdf');
  567. } else
  568. // images
  569. if ( $.inArray( $this_href.slice($this_href.lastIndexOf('.') ), $image_ext_arr ) != -1 ) {
  570. $(this).addClass('img');
  571. }
  572.  
  573. // invisibles
  574. if ( $this_href.slice($this_href.lastIndexOf('/') + 1 ) === '.' || $this_href.startsWith('.') || $(this).find('a').text().startsWith('.') ) {
  575. $(this).addClass('invisible');
  576. if ( $settings.hide_invisibles === true ) {
  577. $(this).hide();
  578. }
  579. }
  580.  
  581. // ignored
  582. if ( $settings.ignore_files === true ) {
  583. for ( var i = 0; i < $settings.ignore_file_types.length; i++ ) {
  584. if ( $this_href.endsWith( $settings.ignore_file_types[i] ) ) {
  585. $(this).closest('#dir_table > tbody > tr').addClass('ignore').find('a').css({'color':'#888'});
  586. if ( $settings.hide_ignored_files === true || $settings.hide_invisibles == true ) {
  587. $(this).hide();
  588. }
  589. }
  590. }
  591. }
  592.  
  593. // directories as Files and hide ignored files
  594. if ( $settings.apps_as_dirs === false ) {
  595. if ( $this_href.endsWith('.app/') ) {
  596. $(this).addClass('ignore app').find('a').css({'color':'#888'});
  597. var $app_name = $(this).find('a').text().slice(0,-1);
  598. $(this).find('a').text($app_name);
  599. }
  600. }
  601. }); // end classify dir_table items
  602.  
  603. $dir_table.appendTo('#sidebar');
  604.  
  605. // ***** End dir_table setup ***** //
  606.  
  607. // ***************************** //
  608.  
  609. // ***** SHOW/HIDE CONTENT ***** //
  610.  
  611. function setContentTitle(el) {
  612. if ( el == $content_grid ) {
  613. $content_title.empty().prepend('Images from: /' + $current_dir_name);
  614. } else {
  615. $content_title.empty().prepend( $('.selected').find('a').text() );
  616. }
  617. if ( $('.selected').hasClass('ignore') ) {
  618. $content_title.append(' (Ignored content)' );
  619. }
  620. }
  621.  
  622. function getDimensions(link, callback) {
  623. var img = new Image();
  624. img.src = link;
  625. img.onload = function() { callback( this.width, this.height ); };
  626. }
  627.  
  628. function showThis(el,link) {
  629. if ( el == $content_image ) {
  630. el.find('img').attr('src',link);
  631. getDimensions( link, function( width, height ) {
  632. $content_title.append(' <span style="text-transform:lowercase;">(' + width + 'px &times; ' + height + 'px</span>)' );
  633. });
  634. } else if ( el == $content_embed ) {
  635. el.attr('type','application/pdf').attr('src',link + '?#zoom=100&scrollbar=1&toolbar=1&navpanes=1');
  636. } else {
  637. el.attr('src',link);
  638. }
  639. unhideThis(el);
  640. }
  641.  
  642. function showContentHeader() {
  643. $content_header.css({'display':'table'});
  644. }
  645.  
  646. function showPrevNextBtns() {
  647. if ( $dir_table.find('.img').length > 1 ) {
  648. $prev_btn.add($next_btn).css({'display':'block'});
  649. }
  650. }
  651.  
  652. function hidePrevNextBtns() {
  653. $prev_btn.add($next_btn).css({'display':'none'});
  654. }
  655.  
  656. function hideThis(el) {
  657. el.removeClass('visible').addClass('hidden').css({'z-index':'-1'}).hide();
  658. }
  659.  
  660. function unhideThis(el) {
  661. el.removeClass('hidden').addClass('visible').css({'z-index':'auto'});
  662. if ( el == $content_grid ) {
  663. el.css({'display':'grid'});
  664. showPrevNextBtns();
  665. } else if ( el == $content_image) {
  666. el.show();
  667. showPrevNextBtns();
  668. } else {
  669. el.show();
  670. }
  671. setContentTitle(el);
  672. showContentHeader();
  673. }
  674.  
  675. function closeThis(el) {
  676. if ( el == $content_grid ) {
  677. $content_grid.hide().empty();
  678. } else if ( el == $content_image ) {
  679. $content_image.hide().find('img').removeAttr('src');
  680. } else {
  681. el.removeClass('visible').hide().removeAttr('src');
  682. hidePrevNextBtns();
  683. $content_header.hide();
  684. }
  685. }
  686.  
  687. function emptyContentPane() {
  688. closeThis($content_image);
  689. closeThis($content_embed);
  690. closeThis($content_iframe);
  691. }
  692.  
  693. $content_close_btn.on('click',function() {
  694.  
  695. var $visible = $content_container.find('.visible');
  696. var $hidden = $content_container.find('.hidden');
  697. hideMenu();
  698.  
  699. if ( $visible == $content_grid && ( $hidden == $content_iframe || $hidden == $content_embed ) ) {
  700. var $this_content_src = $hidden.attr('src');//.replace(/%20/g,' ');
  701. $dir_table.find('a[href*="' + $this_content_src + '"]').each(function() {
  702. $(this).click();
  703. });
  704. } else if ( $visible == $content_image && $hidden == $content_grid ) {
  705. $content_grid.find('a[href="' + $dir_table.find('.selected').find('a').attr('href') + '"]').parent('div').addClass('selected').css({'background':'#666'}).siblings('div').removeClass('selected').css({'background':'transparent'});
  706. }
  707. closeThis( $visible );
  708. if ( $hidden.length ) {
  709. unhideThis($hidden);
  710. }
  711. scrollSidebar();
  712.  
  713. });
  714.  
  715. // ***** MAIN CLICK FUNCTION FOR SHOWING CONTENT ***** //
  716.  
  717. $dir_table_row.on('click','a',function(e) {
  718.  
  719. var $this_row = $(this).closest('#dir_table > tbody > tr');
  720. var $this_link = 'file://' + $(this).attr('href');
  721. $this_link = $this_link.slice($this_link.lastIndexOf('/') + 1 );
  722.  
  723. if ( $sidebar_wrapper.hasClass('unfocused') ) {
  724. sidebarFocus();
  725. }
  726. hideMenu();
  727.  
  728. // if app, ignore or treat as directory
  729. if ( $this_row.hasClass('app') && $settings.apps_as_dirs === false ) {
  730. e.preventDefault();
  731. } else if ( $this_row.hasClass('dir') ) {
  732. return;
  733. } else {
  734. e.preventDefault();
  735.  
  736. selectItem($this_row);
  737.  
  738. if ( $content_grid.hasClass('visible') ) {
  739. hideThis($content_grid);
  740. }
  741. if ( $this_row.hasClass('ignore') ) {
  742. emptyContentPane();
  743. showContentHeader();
  744. } else if ( $this_row.hasClass('img') ) {
  745. closeThis($content_embed);
  746. closeThis($content_iframe);
  747. showThis($content_image,$this_link);
  748. showPrevNextBtns();
  749. } else if ( $this_row.hasClass('pdf') ) {
  750. emptyContentPane();
  751. showThis($content_embed,$this_link);
  752. } else { // iframe
  753. emptyContentPane();
  754. showThis($content_iframe,$this_link);
  755. }
  756. }
  757. setContentTitle();
  758. setContentHeight();
  759. });
  760.  
  761. // File shortcuts: load directory then auto-select file
  762. $('.file_shortcut').on('click',function(e) {
  763. e.preventDefault();
  764. var $this_link = $(this).attr('href');
  765. var $this_dir = $this_link.slice(0,$this_link.lastIndexOf('/') );
  766. window.location = $this_dir;
  767. });
  768.  
  769. $('#reload_btn').on('click',function() {
  770. $('.selected').find('a').click();
  771. });
  772.  
  773. // END MAIN CLICK FUNCTIONS
  774.  
  775. // Auto-select file from file shortcut list;
  776. // Limitations: only loads last file from list found in directory, doesn't know anything about which actual file shortcut was selected
  777. function autoSelectFile() {
  778. if ( $settings.file_shortcuts.length ) {
  779. for ( var i = 0; i < $settings.file_shortcuts.length; i++ ) {
  780. if ( $.inArray($settings.file_shortcuts[i], $dir_table_link_arr ) ) {
  781. $dir_table.find( 'a[href*="/' + $settings.file_shortcuts[i] + '"]').click();
  782. }
  783. }
  784. }
  785. }
  786. autoSelectFile();
  787.  
  788. function selectItem(el) {
  789. $(el).addClass('selected').css({'background-color':'lightsteelblue'}).find('a').css({'font-weight':'bold','color':'#333'});
  790. $(el).siblings().removeClass('selected').css({'background-color':'transparent'}).find('a').css({'font-weight':'normal'});
  791. $(el).siblings('.ignore').find('a').css({'color':'#888'});
  792. // select grid item
  793. var $selected_link = $(el).find('a').attr('href');
  794. $content_grid.find('a[href="' + $selected_link + '"]').parent('div').addClass('selected').css({'background':'#666'}).siblings().removeClass('selected').css({'background':'transparent'});
  795. scrollSidebar();
  796. scrollGrid();
  797. }
  798.  
  799. function selectBlur(el) { $(el).css({'background-color':'#BBB'}).find('a').css({'font-weight':'normal','color':'#444'}); }
  800.  
  801. function sidebarFocus() {
  802. $sidebar_wrapper.removeClass('unfocused').addClass('focused');
  803. $content_pane.removeClass('focused').addClass('unfocused');
  804. selectItem('.selected');
  805. }
  806.  
  807. function contentFocus() {
  808. $sidebar_wrapper.removeClass('focused').addClass('unfocused');
  809. $content_pane.removeClass('unfocused').addClass('focused');
  810. if ( $content_image.find('img').attr('src') !== undefined ) {
  811. $content_image.focus();
  812. }
  813. selectBlur('.selected');
  814. }
  815.  
  816. // Scroll Behavior
  817. var $block = '';
  818. if( $userAgent.indexOf('Firefox') > -1 ){
  819. $block = 'start';
  820. } else {
  821. $block = 'nearest';
  822. }
  823.  
  824. function scrollGrid() {
  825. if ( $content_grid.hasClass('visible') && $content_grid.find('.selected').length ) {
  826. $content_grid.find('.selected')[0].scrollIntoView({ behavior:'smooth',block:$block,inline:'nearest' });
  827. }
  828. }
  829. function scrollSidebar() {
  830. if ( $sidebar.find('.selected').length ) {
  831. $sidebar.find('.selected')[0].scrollIntoView({ behavior:'smooth',block:$block,inline:'nearest' });
  832. }
  833. }
  834.  
  835. // Zoom Images
  836. $content_image.find('img').on('click',function() {
  837. if ( $(this).hasClass('default') ) {
  838. $(this).removeClass('default').css({'max-width':'','max-height':'','cursor':'zoom-out','top':'0','transform':'translateY(0%)'});
  839. } else {
  840. $(this).addClass('default').css({'max-width':'100%','max-height':'calc(100% - 3px)','cursor':'zoom-in','top':'50%','transform':'translateY(-50%)'});
  841. }
  842. });
  843.  
  844. // Focus content pane on click
  845. // $content_image.add($content_iframe).add($content_embed).on('click', contentFocus() );
  846.  
  847. // ***** KEYBOARD EVENTS ***** //
  848.  
  849. $body.on('keydown',$dir_table,function(e) {
  850.  
  851. var $selected = $dir_table.find('.selected');
  852. var $selected_href = $selected.find('a').attr('href');
  853. var $first_item = $dir_table_row.filter(':visible').first();
  854. var $last_item = $dir_table_row.filter(':visible').last();
  855. var $prev_item = $selected.prevAll('tr:visible').first();
  856. var $next_item = $selected.nextAll('tr:visible').first();
  857. var $prev_image = $selected.prevAll('tr.img:visible').first();
  858. var $next_image = $selected.nextAll('tr.img:visible').first();
  859.  
  860. switch ( e.key ) {
  861.  
  862. case 'ArrowUp':
  863.  
  864. // Go to parent folder
  865. if ( (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) ) {
  866. window.location = $parent_dir_link;
  867. break;
  868. }
  869.  
  870. // Default action when content pane is focused
  871. if ( $sidebar_wrapper.hasClass('unfocused') ) {
  872. return;
  873. }
  874.  
  875. if ( $first_item.hasClass('selected') ) {
  876. break;
  877. } else if ( $selected.length < 1 && $last_item.hasClass('dir') ) {
  878. emptyContentPane();
  879. selectItem($last_item);
  880. } else if ( $prev_item.length && $prev_item.hasClass('dir') ) {
  881. emptyContentPane();
  882. selectItem($prev_item);
  883. } else if ( $selected.length < 1 ) {
  884. $last_item.find('a').click();
  885. } else {
  886. e.preventDefault();
  887. $prev_item.find('a').click();
  888. }
  889. break;
  890.  
  891. case 'ArrowDown':
  892.  
  893. if ( (e.ctrl || e.metaKey) && $selected.hasClass('app') && $settings.apps_as_dirs === false ) {
  894. return;
  895. } else if ( $sidebar_wrapper.hasClass('unfocused') ) {
  896. return;
  897. } else if ( (e.ctrl || e.metaKey) && $selected.hasClass('dir') ) {
  898. window.location = $selected_href;
  899. break;
  900. }
  901.  
  902. if ( $last_item.hasClass('selected') ) {
  903. e.preventDefault();
  904. } else if ( $selected.length < 1 && $first_item.hasClass('dir') ) {
  905. selectItem($first_item);
  906. } else if ( $selected.length < 1 ) {
  907. $first_item.find('a').click();
  908. } else if ( $next_item.length > 0 && $next_item.hasClass('dir') ) {
  909. e.preventDefault();
  910. emptyContentPane();
  911. selectItem($next_item);
  912. } else {
  913. e.preventDefault();
  914. $next_item.find('a').click();
  915. }
  916. break;
  917.  
  918. case 'ArrowLeft':
  919.  
  920. if ( (e.ctrl || e.metaKey) || ( e.ctrl && e.metaKey ) ) {
  921. return;
  922. } else if ( $sidebar_wrapper.hasClass('unfocused') ) {
  923. return;
  924. }
  925. // Navigate Grid or Images
  926. if ( $content_grid.hasClass('visible') ) {
  927. if ( $selected.length !== 1 ) {
  928. selectItem($dir_table_row.filter('.img').last() );
  929. } else {
  930. selectItem($prev_image);
  931. }
  932. } else if ( $content_image.hasClass('visible') ) {
  933. $prev_image.find('a').click();
  934. } else {
  935. $dir_table_row.filter('.img').last().find('a').click();
  936. }
  937. break;
  938.  
  939. case 'ArrowRight':
  940.  
  941. if ( (e.ctrl || e.metaKey) || ( e.ctrl && e.metaKey ) ) {
  942. return;
  943. } else if ( $sidebar_wrapper.hasClass('unfocused') || $selected.hasClass('ignore') ) {
  944. return;
  945. }
  946. // Navigate Grid or Images
  947. if ( $selected.hasClass('dir') ) {
  948. window.location = $selected_href; // Open directory
  949. } else if ( $content_grid.hasClass('visible') ) {
  950. if ( $selected.length == 0 ) {
  951. selectItem($dir_table_row.filter('.img').first() );
  952. } else {
  953. selectItem($next_image);
  954. }
  955. } else if ( $content_image.hasClass('visible') ) {
  956. $next_image.find('a').click();
  957. } else {
  958. $dir_table_row.filter('.img').first().find('a').click();
  959. }
  960. break;
  961.  
  962. case 'Enter':
  963. // Open directories (or ignore)
  964. if ( $selected.hasClass('app') && $settings.apps_as_dirs === false ) {
  965. break;
  966. } else if ( $selected.hasClass('dir') ) {
  967. window.location = $selected_href;
  968. } else if ( $selected.hasClass('file') ) {
  969. $selected.find('a').click();
  970. } else {
  971. return;
  972. }
  973. break;
  974.  
  975. case 'i':
  976. // Toggle Invisibles with Command-i
  977. if ( (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) ) {
  978. e.preventDefault();
  979. e.stopPropagation();
  980. $inv_checkbox.click();
  981. }
  982. break;
  983.  
  984. case 'w':
  985. // Close content pane if Close button visible with Command-w
  986. if ( (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) && $content_close_btn.is(':visible') ) {
  987. e.preventDefault();
  988. e.stopPropagation();
  989. $content_close_btn.click();
  990. }
  991. break;
  992.  
  993. case 'Tab':
  994. // Tab cannot blur focussed iframe
  995. $(':focus').blur();
  996. if ( $sidebar_wrapper.hasClass('focused') ) {
  997. contentFocus();
  998. } else if ( $sidebar_wrapper.hasClass('unfocused') ) {
  999. $('.selected').click();
  1000. sidebarFocus();
  1001. }
  1002. break;
  1003.  
  1004. } // end switch
  1005.  
  1006. // Cmd/Ctrl + g: Show image Grid, with override for default browser binding
  1007. if ( e.key == "g" && (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) ) {
  1008. $content_grid_btn.click();
  1009. e.preventDefault();
  1010. e.stopPropagation();
  1011. }
  1012.  
  1013. // Cmd/Ctrl + Shift + O: Open selected item in new window
  1014. if ( e.key == "o" && (navigator.platform.match("Mac") ? e.metaKey && e.shiftKey : e.ctrlKey && e.shiftKey ) ) {
  1015. window.open($selected_href);
  1016. }
  1017. });
  1018.  
  1019. // ***** END KEYBOARD EVENTS ***** //
  1020.  
  1021. // ***** IMAGE NAVIGATION ***** //
  1022.  
  1023. $prev_btn.on('mouseenter',function() {
  1024. $(this).css({'opacity':'1'});
  1025. }).on('mouseleave',function() {
  1026. $(this).css({'opacity':'0.6'});
  1027. });
  1028.  
  1029. $next_btn.on('mouseenter',function() {
  1030. $(this).css({'opacity':'1'});
  1031. }).on('mouseleave',function() {
  1032. $(this).css({'opacity':'0.6'});
  1033. });
  1034.  
  1035. $prev_btn.on( 'click', function(event) {
  1036. var e = $.Event("keydown");
  1037. e.key = 'ArrowLeft';
  1038. $dir_table.trigger(e);
  1039. });
  1040.  
  1041. $next_btn.on( 'click', function(event) {
  1042. var e = $.Event("keydown");
  1043. e.key = 'ArrowRight';
  1044. $dir_table.trigger(e);
  1045. });
  1046.  
  1047. // Edit Button -- not implemented
  1048. // $content_edit_btn.on('click',function() {
  1049. // var $selected_url = $('.selected').find('a').attr('href');
  1050. // window.open($selected_url);
  1051. // });
  1052.  
  1053. // GRID BUTTON & IMAGE GRID
  1054. // Show grid button only if images are found in directory
  1055. function show_grid_btn() {
  1056. if ( $dir_table.find('.img').length ) {
  1057. $content_grid_btn.show();
  1058. }
  1059. }
  1060. show_grid_btn();
  1061.  
  1062. // create grid items
  1063. var $grid_item_el = $(
  1064. '<div class="grid_item" style="display:inline-block;width:150px;height:150px;float:left;text-align:center;vertical-align:middle;">' +
  1065. '<a href="">' +
  1066. '<img src="" style="width:auto;height:auto;max-width:128px;max-height:128px;position:relative;top:50%;transform:translateY(-50%);opacity:0.8;" />' +
  1067. '</a>' +
  1068. '</div>'
  1069. );
  1070. var $grid_items_arr;
  1071. function gridItems() {
  1072. $grid_items_arr = [];
  1073. $dir_table_link.each(function() {
  1074. var $this_link = $(this).attr('href');
  1075. var $this_ext = $this_link.toLowerCase().slice($this_link.lastIndexOf('.'));
  1076. if ( $.inArray($this_ext,$image_ext_arr) != -1 ) {
  1077. $grid_item_el.find('a').attr('href',$this_link).find('img').attr('src',$this_link);
  1078. $grid_items_arr.push($grid_item_el.clone());
  1079. }
  1080. return $grid_items_arr;
  1081. });
  1082. }
  1083.  
  1084. // Grid Button Click
  1085. $content_grid_btn.on('click',function() {
  1086. gridItems();
  1087. $content_grid.empty().append( $grid_items_arr );
  1088. selectItem( $dir_table.find('.selected') );
  1089. hideThis($('.visible') );
  1090. unhideThis($content_grid);
  1091. setContentHeight();
  1092. });
  1093.  
  1094. // GRID ITEMS
  1095. // Grid Item Hover
  1096. $content_grid.on('mouseenter','> div:not(".selected")',function() {
  1097. var $this_link = $(this).find('a').attr('href');
  1098. $(this).css({'background':'#555'});
  1099. $dir_table.find('a[href="' + $this_link + '"]').css({'background':'#BBB'});
  1100. }).on('mouseleave','> div:not(".selected")',function() {
  1101. var $this_link = $(this).find('a').attr('href');
  1102. $(this).css({'background':'transparent'});
  1103. $dir_table.find('a[href="' + $this_link + '"]').css({'background':'transparent'});
  1104. });
  1105.  
  1106. // Grid Item Click
  1107. $content_grid.on('click','> div',function(e) {
  1108. e.preventDefault();
  1109. var $this_link = $(this).find('a').attr('href');
  1110. $dir_table.find('a[href="' + $this_link + '"]').click();
  1111. });
  1112.  
  1113.  
  1114. // ***** END IMAGE GRID ***** //
  1115.  
  1116. // Resize Sidebar/Content Pane
  1117. $handle.on('mousedown',function(f) {
  1118. f.stopPropagation();
  1119. var $startX = f.pageX;
  1120. var $sidebar_width = $sidebar_wrapper.width();
  1121. var $window_width = window.innerWidth;
  1122. $content_mask.show(); // needed to prevent interactions with iframe
  1123. $sidebar_wrapper.css({'-webkit-user-select':'none','-moz-user-select':'none','user-select':'none'});
  1124.  
  1125. $(document).on('mousemove',function(e) {
  1126. e.stopPropagation();
  1127. var $deltaX = e.pageX - $startX;
  1128. if ( e.pageX > 200 && e.pageX < $window_width - 200 ) {
  1129. $sidebar_wrapper.css({'width':$sidebar_width + $deltaX + 'px'});
  1130. $content_pane.css({'width':($window_width - $sidebar_width) - $deltaX + 'px'});
  1131. }
  1132. headerHeight();
  1133. setContentHeight();
  1134. });
  1135. $(document).on('mouseup',function() {
  1136. $content_mask.hide();
  1137. $sidebar_wrapper.css({'-webkit-user-select':'auto','-moz-user-select':'auto','user-select':'auto'});
  1138. $(document).off('mousemove');
  1139. });
  1140. });
  1141.  
  1142. })();