Supercharged Local Directory File Browser

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

Verze ze dne 23. 08. 2018. Zobrazit nejnovější verzi.

  1. // ==UserScript==
  2. // @name Supercharged Local Directory File Browser
  3. // @version 2.4.2
  4. // @description Makes file:/// directory ("Index of...") pages (and many server-generated index pages) actually useful. Adds navigation links, file preview pane, keyboard navigation, user-defined shortcuts, filtering, 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 various Chrome and Gecko-based browsers. It has only been minimally tested on Windows, however. It should work, but please report any issues.
  11. // It does not work in Safari because Safari does not allow local directories to be browsed.
  12.  
  13. // 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.
  14. // For Greasemonkey, open about:config and change greasemonkey.fileIsGreaseable to true.
  15. // For Tampermonkey, go to Chrome extension page, and tick the 'Allow access to file URLs' checkbox at the Tampermonkey extension section.
  16.  
  17. // CHANGELOG:
  18.  
  19. // 2.4.2
  20. // More audio file enhancements:
  21. // Added: Checkboxes to play or skip specific audio files. (Such a seemingly small thing, such a pain to implement.)
  22. // Added: Start/stop play by clicking file name.
  23. // Added: Use Reload button and reload keybinding (Cmd/Ctr-R) to return audio to beginning of track.
  24. // Changed: Don't autoplay audio when using left and right arrows to navigate tracks.
  25. // Fixed: Various issues with shuffle play.
  26. // Fixed: Completely rewrote autoload cover art code so that it actually works.
  27. // Fixed: Ignored files weren't being ignored in list-based index pages.
  28. // Many other bug fixes.
  29. // Minor UI adjustments (e.g., better prev and next track buttons).
  30.  
  31. // 2.4.1
  32. // Added: Loop and shuffle playback for audio files.
  33. // Added: Wrap-around keyboard navigation for audio tracks.
  34. // Added: Default background image for audio directories containing no image files.
  35. // Fixed some scrollIntoView issues.
  36. // Fixed auto-cover image selection issue in Firefox.
  37. // Bug-fixes. Yep, bug-fixes.
  38.  
  39. // 2.4.0
  40. // Major update for audio files!
  41. // New: Play all audio files in a directory.
  42. // New: Autoload cover art files (if available) in the preview pane. The script will look in the directory for image files titled "cover.[ext]" or "front.[ext]"; if they are not found, it will load the first image file it finds.
  43. // New: Audio file keyboard controls: Left/Right Arrows to navigate tracks, ignoring other files in the directory. Up/Down Arrows to navigate and preview all non-audio files in the directory (e.g., cover art or lyrics). Space bar to pause/play. (Note: grid-view is available in directories containing audio files, but the left/right arrow keys will not select grid items.)
  44. // New user setting: autoplay audio files. Default is false, but if true, the first audio file will start playing as soon as the directory is loaded. Again, don't delete it when pasting in exported settings.
  45. // Some code cleanup and bugfixes.
  46.  
  47. // 2.3.0
  48. // Added: New user settings: default grid image and font sizes. Don't delete them when pasting in your old exported settings!
  49. // Added: Scale grid image items.
  50. // Improved: Better image zooming. Zoomed images now scroll to the area clicked.
  51. // Fixed: Keybinding to toggle invisibles wasn't working.
  52. // Fixed: Image zoom not working.
  53. // Fixed: Hovered background for grid items in dark mode.
  54. // Fixed: Font scaling in dual grids.
  55.  
  56. // 2.2.0
  57. // Added: Export settings menu item. This will make it easier to save your user settings before updating the script. (Remember, scripts running on local files cannot set cookies or use localStorage, which would make it possible to save user settings automatic.) Unfortunately, there is no mechanism for importing the settings: you'll still have to open the script editor and paste in the exported data.
  58. // Fixed: File extensions were case sensitive, so some valid files wouldn't load if the extension contained capital letters.
  59. // Fixed: File and Dir names were also being lowercased.
  60.  
  61. // v. 2.1.1
  62. // Fixed: Can't type "r" in font previews.
  63. // Other minor bug fixes.
  64.  
  65. // v. 2.1.0
  66. // Added: Toggle Default/Dark modes menu item while browsing current directory; mode reverts to saved setting on dir change.
  67. // Added: White background for images so that images with transparency are visible against the grey background
  68. // Removed trailing "/" from directory display names.
  69. // Fixed an issue where font and image grid display got confused about what content to display.
  70. // Fixed directory names display in dark mode in server pages that use lists instead of tables.
  71. // Better styling for grid button.
  72. // Better styling for grid items in dark mode.
  73. // Bug fixes.
  74. // Remember to copy your user settings before updating!
  75.  
  76. // v. 2.0.2
  77. // Various fixes suggested by "Zorg":
  78. // Dark mode now ignores html files (and media files), but inverts all plain text files. This solution is not perfect: there is a flash of inverted content when navigating from an html or media file to a non-html or media file; will look for a fix.
  79. // Fixed display of non-Latin characters in sidebar header.
  80. // Add support for linux "home" directory for user profile shortcuts
  81. // Made entire up directory button clickable.
  82.  
  83. // v. 2.0.1
  84. // Fixed wonky font preview scaling.
  85.  
  86. // v. 2.0
  87. // NEW! Not just for local directories anymore! Added support for *many* server-generated directory index pages (It's impossible to check every server configuration, needless to say, so it might not work in every case; let me know if you encounter problems.// NEW! Preview font files (otf, ttf, woff, woff2). Great for designers: preview fonts without installing them. Hint: To install previewed fonts, just type Cmd/Ctrl + O or right-click the link and save it to your fonts folder. Font preview pane is content-editable.
  88. // NEW! Preview font files (otf, ttf, woff, woff2). Great for designers: preview fonts without installing them. Hint: To install previewed fonts, just type Cmd/Ctrl + Shift + O or right-click the link and save it to your fonts folder. Font preview pane is content-editable.
  89. // Added: Now use the Left and Right arrow keys to navigate through images and fonts in a folder, skipping other files. Use up and down arrow keys to navigate all files normally.
  90. // Added: Basic dark mode user setting.
  91. // Added: Wrap-around keyboard navigation.
  92. // Added: Cmd/Ctr-D to toggle details.
  93. // Added: Cmd/Ctr-Shift-. and Cmd/Ctr-Shift-, to scale font previews text size.
  94. // Changed: Moved dynamically-added in-line styles to appended stylesheet.
  95. // Changed: Extensive refactoring of code for better separation of concerns. (More needs to be done.... Sigh.)
  96. // Removed: ScrollIntoView for Grid views. Scrolling didn't work reliably when the sidebar was also being scrolled. Will restore when bug fixed.
  97. // Many small bug fixes.
  98.  
  99. // v. 1.4
  100. // Added: Initial support for Firefox. Tested in Firefox 59, Waterfox 56.
  101. // Changed: Use SVG for menu icons
  102. // Changed: Code cleanup, reorganization
  103. // Renamed script to "Supercharged Local Directory File Browser"
  104.  
  105. // v. 1.3
  106. // Fixed: Keyboard navigation of ignored or invisible items fails if "Hide Invisibles" is toggled off after loading a directory.
  107. // Added: Also hide ignored files if "Hide Invisibles" is checked.
  108. // Added: Content reload button.
  109. // Changed: Reorganized settings to reduce confusion about how ignored files are treated.
  110.  
  111. // v. 1.2
  112. // Click to show menus instead of hover.
  113. // Added Cmd/Crl+Shift+O keybinding to open selected item in new window
  114. // Arrow navigation bugfixes
  115.  
  116. // TO-DO:
  117. // Fix grid scrollIntoView
  118. // Sort by file extension?
  119.  
  120. // @namespace https://greatest.deepsurf.us/users/16170
  121. // ==/UserScript==
  122.  
  123. (function() {
  124. 'use strict';
  125. var $ = jQuery;
  126.  
  127. // ***** USER SETTINGS ***** //
  128.  
  129. var $settings = {
  130.  
  131. // Paste your exported settings between the followeing two lines:
  132. //----------------------------------------------------//
  133.  
  134. user_name: // Your computer user name
  135. 'MacBookPro',
  136. // Shortcuts: add directories and files here. (You can use your browser's bookmarks, of course.)
  137. root_shortcuts: // Root directories: add or remove as you please (but at leave empty brackets). These defaults are applicable to Mac OS.
  138. ['Applications','Library','Users','Volumes'],
  139. // ['C:/Users','C:/Program Files','C:/Windows'],
  140. user_shortcuts: // User directories; you must enter your user_name above.
  141. ['Documents','Downloads','Library','Movies','Music','Pictures'],
  142. file_shortcuts: // Add specific file paths, e.g.: 'Users/MyUserName/Documents/MyDocument.html'
  143. // These files will be selected (loaded) automatically when their containing directory is loaded
  144. // Limitations: only works for one file per directory; if more than one file per directory is listed, the last item will be selected.
  145. ['path/to/file.ext','path/to/another/file.ext'],
  146. ignore_files: // If true (default), ignored files (see below) will be greyed-out (default) in the file list and will not be loaded in the content pane when selected;
  147. // 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).
  148. true,
  149. ignore_file_types: // Files with these extensions will be ignored:
  150. ['exe','.doc','.docx','ppt','pptx','xls','xlsx','odt','odp','.csv','msi','dll','rtf','indd','idml','.pages','.tif','tiff','.eps','.psd','.ai','.afm','.pfb','.pfm','.tfm','.zip','pkg','.swf','.pls','.ics','.DS_Store','.ds_store','ds_store','alias','.dmg','.gz','.qxp','icon.jpg','thumbs.db','.ape','.srf'], // lowercase
  151. hide_ignored_files: // If true, ignored files will be hidden in the file list;
  152. // if false (default), they will appear greyed-out.
  153. false,
  154. hide_invisibles: // Mac OS only: If true (default), files or directories beginning with a "." will be hidden.
  155. true,
  156. 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.
  157. // If false (default), treat apps as ignored files.
  158. false,
  159. dark_mode: // If true, gives the content pane a dark background, and inverts html and text content.
  160. false,
  161. grid_image_size: // Default = 150
  162. 150,
  163. grid_font_size: // Default = 1
  164. 1,
  165. // Automatically play media (Default: false)
  166. autoplay: false
  167.  
  168. //----------------------------------------------------//
  169. // Paste your exported settings between the above two lines.
  170.  
  171. };
  172.  
  173. // ***** END USER SETTINGS ***** //
  174.  
  175. // ***** SETUP ***** //
  176.  
  177. var $userAgent = navigator.userAgent;
  178.  
  179. function platformIsMac() {
  180. return navigator.platform.indexOf('Mac') > -1;
  181. }
  182. function platformIsWin() {
  183. return navigator.platform.indexOf('Win') > -1;
  184. }
  185. function platformIsLinux() {
  186. return navigator.platform.indexOf('Linux') > -1;
  187. }
  188.  
  189. // Don't run script in iframes or files (only directories)
  190. // if ( window.top != window.self ) {
  191. if ( window.frameElement !== null ) {
  192. return;
  193. }
  194. if ( window.location.pathname.slice(-1) != '/') {
  195. // $('body').attr('contentEditable','true');
  196. return;
  197. }
  198.  
  199. // Arrays of supported file types
  200. var $audio_ext_arr = ['.mp3','.mp4','.m4a','.ogg','.flac','.wav','webm'];
  201. var $video_ext_arr = ['.mpeg','.mov'];
  202. var $image_ext_arr = ['.jpg','.jpeg','.png','apng','.gif','.bmp','webp'];
  203. var $font_ext_arr = ['.otf','.ttf','.woff','.woff2'];
  204. var $font_family_arr = [];
  205.  
  206. var $body = $('body');
  207. // add lang attr, remove some unneeded default elements
  208. $body.attr('lang','en').find('> h1:contains("Index of"),> #parentDirLinkBox,> #UI_goUp,#UI_showHidden').remove();
  209.  
  210. var $location = decodeURIComponent(window.location.pathname);
  211. var $current_dir_path = $location.replace(/%20/g,' ').replace(/\//g,'/<wbr>').replace(/_/g,'_<wbr>').replace(/—/g,'—<wbr>').replace(/\\/g,'/');
  212. var $current_dir_name = $location.replace(/%20/g,' ').slice(0,-1);
  213. $current_dir_name = $current_dir_name.slice($current_dir_name.lastIndexOf('/') + 1);
  214. var $location_arr = $location.split('/');
  215. var $parent_dir_link = $location_arr.slice(0,-2).join('/') + '/';
  216.  
  217. var e, i, n;
  218. var $block;
  219. var $this_text;
  220. var $this_link;
  221. var $this_ext;
  222. var $this_row;
  223.  
  224. var $dir_table;
  225. var $dir_table_type;
  226. var $dir_table_head;
  227. var $dir_table_head_cell;
  228. var $dir_table_head_name;
  229. var $dir_table_head_details;
  230. var $dir_table_body;
  231. var $dir_table_row;
  232. var $dir_table_cell;
  233. var $dir_table_item_name;
  234. var $dir_table_details;
  235. var $dir_table_item_icon;
  236. var $dir_table_link;
  237. var $dir_table_rule;
  238. var $selected;
  239. var $playing;
  240.  
  241. if ( $body.find('> table').length > 0 ) {
  242. $dir_table = $body.find('> table');
  243. $dir_table.addClass('table');
  244. } else {
  245. $dir_table = $body.find('> ul');
  246. $dir_table.add('body').addClass('list');
  247. }
  248.  
  249. // ***** BUILD UI ELEMENTS ***** //
  250.  
  251. // ***** SIDEBAR ELEMENTS ***** //
  252.  
  253. // 1. SIDEBAR: Parent Directory Menu
  254. var $parent_dir_menu = $('<nav id="parent_dir_menu"><a href="">&nbsp;</a></nav>');
  255. $parent_dir_menu.find('a').attr('href',$parent_dir_link);
  256. // 2. SIDEBAR: Current Directory Name and Parents Directory Menu
  257. var $parents_dir_menu = $('<nav id="parents_dir_menu"><div></div></nav><ul class="menu"></ul>');
  258. $parents_dir_menu.find('div').append( $current_dir_path );
  259. // 3A. SIDEBAR: Shortcuts Menu
  260. var $divider = $('<li><hr></li>');
  261. var $shortcuts_menu = $('<nav id="shortcuts_menu"><div>&nbsp;</div></nav><ul class="menu"></ul>');
  262. // 3B. SIDEBAR: Other Menu Items
  263. var $toggle_dark_mode = $('<li id="toggle_dark_mode_menu_item"><a href="#"><span id="dark_theme">Dark Theme</span><span id="default_theme">Default Theme</span></a></li>');
  264. var $export_settings = $('<li><a id="export_settings" href="#">Export Settings</a></li>');
  265. // 4. SIDEBAR: Details Button
  266. var $details_btn = $('<button id="details_btn" tabindex="-1"><span>Show details</span><span>Hide details</span></button>');
  267. // 5. SIDEBAR: Invisibles Checkbox
  268. var $inv_checkbox = $('<label ><input type="checkbox" id="inv_checkbox" for="inv_checkbox" name="inv_checkbox" tabindex="-1" />Hide Invisibles</label>');
  269. // 6. SIDEBAR: Image Grid Button
  270. var $grid_btn = $('<div id="grid_btn" tabindex="-1" title="Show Grid"><ul class="menu"><li id="show_image_grid">Show Image Grid</li><li id="show_font_grid">Show Font Grid</li></ul></div>');
  271. // 7. SIDEBAR: Header Element
  272. var $sidebar_header = $('<table id="sidebar_header"><thead><tr><th colspan="3">INDEX OF</th></tr></thead><tbody><tr><td></td><td></td><td></td></tr><tr><td colspan="3"></td></tr></tbody></table>');
  273. $sidebar_header.find('tbody tr:nth-child(1)').find('td').first().append( $parent_dir_menu ).next().append( $parents_dir_menu ).next().append( $shortcuts_menu );
  274. $sidebar_header.find('tbody tr:last-child td').append( $details_btn, $inv_checkbox, $grid_btn );
  275.  
  276. var $dir_table_wrapper = $('<div id="dir_table_wrapper"></div>');
  277. // 8. SIDEBAR:
  278. var $sidebar = $('<div id="sidebar"></div>');
  279. $sidebar.append($sidebar_header);
  280. // 9. SIDEBAR: Resize Handle
  281. var $handle = $('<div id="handle"></div>');
  282. // 10. ASSEMBLE Sidebar Elements
  283. var $sidebar_wrapper = $('<td id="sidebar_wrapper"></td>');
  284. $sidebar_wrapper.append( $sidebar, $handle );
  285.  
  286. // ***** END SIDEBAR ELEMENTS ***** //
  287.  
  288. // ***** CONTENT PANE ELEMENTS ***** //
  289.  
  290. // 1. CONTENT: Overlay Element
  291. var $content_overlay = $('<div id="content_overlay""></div>');
  292. // 2. CONTENT: Image Grid Element
  293. var $content_grid = $('<div id="content_grid" data-grid-scale-factor="0"></div>');
  294. // 3. CONTENT: grid items
  295. var $image_grid_item_el = $('<div class="image_grid_item"><a href=""><img src="" /></a></div>');
  296. var $font_grid_item_el = $('<div class="font_grid_item"></div>');
  297. var $content_font_size = $('<div id="font_size"><span id="increase"></span><span id="decrease"></span></div>');
  298. // 4. CONTENT: Image Element
  299. var $image = $('<img src="" class="" />');
  300. var $content_image = $('<div id="content_image"></div>');
  301. $content_image.append($image);
  302. // 5. CONTENT: Embed (pdfs) Element
  303. var $content_embed = $('<embed id="content_embed" name="plugin" type="application/pdf" tabindex="0"></embed>');
  304. // 6. CONTENT: Iframe Element
  305. var $content_iframe = $('<iframe id="content_iframe" sandbox="allow-scripts allow-same-origin allow-modals" tabindex="0"></iframe>');
  306. // 7. CONTENT: Media Elements
  307. var $content_media = $('<td id="content_media" colspan="3"></td>');
  308. var $prev_track = $('<div id="prev_track"></div>');
  309. var $next_track = $('<div id="next_track"></div>');
  310. var $audio = $('<audio id="audio" preload="auto" tabindex="0" controls><source id="sound_src" src="" type="audio/mpeg"><source type="audio/mp3" src="">Sorry, your browser does not support HTML5 audio.</audio>');
  311. var $loop = $('<label><input type="checkbox" id="loop" for="loop" name="loop" tabindex="0" />Loop</label>');
  312. var $shuffle = $('<label><input type="checkbox" id="shuffle" for="shuffle" name="shuffle" tabindex="0" />Shuffle</label>');
  313. var $checkbox_cont = $('<div id="checkbox_div"></div>');
  314. $checkbox_cont.append( $loop, $shuffle );
  315. $content_media.append( $prev_track, $next_track, $audio, $checkbox_cont );
  316. // 8. CONTENT: Font Elements
  317. var $sample_string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ<br />abcdefghijklmnopqrstuvwxyz<br />0123456789 [(!@#$%^&*;:)]';
  318. var $lorem_string = 'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.';
  319. var $specimen = $('<div class="specimen" style="font-size:3em;">'+ $sample_string +'</div><hr><div class="lorem first" style="font-size:1em;">'+ $lorem_string +'</div><div class="lorem" style="font-size:1em;">'+ $lorem_string +'</div><div class="lorem" style="font-size:1em;">'+ $lorem_string +'</div>');
  320. var $lorem = $('.lorem');
  321. var $content_font = $('<div id="content_font" spellcheck="false" contenteditable="true"></div>');
  322. $content_font.append($specimen);
  323. // 9. CONTENT: Header Reload Button Element
  324. var $content_reload_btn = $('<td><button id="reload_btn" tabindex="-1">Reload</button></td>');
  325. // 10. CONTENT: Header Title Element
  326. var $content_title = $('<td id="content_title"></td>');
  327. // 11. CONTENT: Header Close Button Element
  328. var $content_close_btn = $('<td><button id="close_btn" tabindex="-1">Close</button></td>');
  329. // 12. CONTENT: Header Element
  330. var $content_header = $('<header id="content_header"><table><tbody><tr></tr><tr></tr></tbody></table></header>');
  331. $content_header.find('tr').first().append($content_reload_btn, $content_title, $content_close_btn);
  332. $content_header.find('tr').last().append($content_media);
  333. // 13. CONTENT: Next/Prev Elements
  334. var $prev_btn = $('<div id="prev_btn"></div>');
  335. var $next_btn = $('<div id="next_btn"></div>');
  336.  
  337. // ASSEMBLE Content Container Elements
  338. var $content_container = $('<section id="content_container"></section>');
  339. $content_container.append( $content_header, $content_font_size, $content_overlay, $content_grid, $content_image, $content_embed, $content_iframe, $content_font );
  340.  
  341. // ASSEMBLE Content Pane Elements
  342. var $content_pane = $('<td id="content_pane"></td>');
  343. $content_pane.append( $content_container, $prev_btn, $next_btn );
  344.  
  345. // ***** END BUILD UI ELEMENTS ***** //
  346.  
  347. // ***** STYLES ***** //
  348.  
  349. // Create Custom Font Styles element
  350. var $custom_font_styles = document.createElement('style');
  351. $custom_font_styles.appendChild(document.createTextNode(""));
  352. document.head.append($custom_font_styles);
  353.  
  354. var $font_styles = '';
  355. $custom_font_styles.append( $font_styles );
  356.  
  357. // Create Custom Styles element
  358. var $custom_styles = document.createElement("style");
  359. $custom_styles.appendChild(document.createTextNode(""));
  360.  
  361. // SVG IMAGES
  362. 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>")';
  363. var $svg_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=\'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>")';
  364. 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>")';
  365. 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>")';
  366. var $plus_sign = 'url("data:image/svg+xml;utf8,<svg version=\'1.1\' baseProfile=\'basic\' 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\' xml:space=\'preserve\'><polygon points=\'16,6.5 9.5,6.5 9.5,0 6.5,0 6.5,6.5 0,6.5 0,9.5 6.5,9.5 6.5,16 9.5,16 9.5,9.5 16,9.5 \'/></svg>")';
  367. var $minus_sign = 'url("data:image/svg+xml;utf8,<svg version=\'1.1\' baseProfile=\'basic\' 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\' xml:space=\'preserve\'> <rect x=\'1\' y=\'6.499\' width=\'14\' height=\'3.001\'/> </svg>")';
  368. var $next_track_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.873px\' height=\'14.018px\' viewBox=\'-2 0 12.873 14.018\' enable-background=\'new -2 0 12.873 14.018\' xml:space=\'preserve\'><polygon fill=\'%23464547\' points=\'10.873,14.017 0,7.01 10.872,0 \'/><rect x=\'-2\' y=\'0\' fill=\'%23464547\' width=\'2\' height=\'14\'/></svg>")';
  369. var $next_track_arrow_gecko = '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.873px\' height=\'14.018px\' viewBox=\'-2 0 12.873 14.018\' enable-background=\'new -2 0 12.873 14.018\' xml:space=\'preserve\'><polygon fill=\'white\' points=\'10.873,14.017 0,7.01 10.872,0 \'/><rect x=\'-2\' y=\'0\' fill=\'white\' width=\'2\' height=\'14\'/></svg>")';
  370. var $next_track_arrow_gecko_hover = '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.873px\' height=\'14.018px\' viewBox=\'-2 0 12.873 14.018\' enable-background=\'new -2 0 12.873 14.018\' xml:space=\'preserve\'><polygon fill=\'%236bb5ff\' points=\'10.873,14.017 0,7.01 10.872,0 \'/><rect x=\'-2\' y=\'0\' fill=\'%236bb5ff\' width=\'2\' height=\'14\'/></svg>")';
  371. var $music = '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=\'143.717px\' height=\'199.404px\' viewBox=\'0 0 143.717 199.404\' enable-background=\'new 0 0 143.717 199.404\' xml:space=\'preserve\'><g opacity=\'0.2\'> <path fill=\'%23757679\' d=\'M143.717,143.82c0,10.033-4.573,18.425-13.717,25.183c-8.394,6.143-17.776,9.211-28.149,9.211 c-6.074,0-11.056-1.432-14.943-4.297c-4.301-3.275-6.45-7.849-6.45-13.719c0-9.279,4.403-17.438,13.204-24.466 c8.326-6.616,17.266-9.93,26.82-9.93c8.052,0,13.922,1.605,17.606,4.812V25.487L63.26,45.654v119.354 c0,10.03-4.573,18.427-13.717,25.181c-8.394,6.142-17.778,9.215-28.148,9.215c-6.077,0-11.055-1.437-14.947-4.302 C2.151,191.827,0,187.253,0,181.386c0-9.282,4.401-17.436,13.206-24.465c8.323-6.615,17.262-9.929,26.817-9.929 c8.051,0,13.921,1.605,17.606,4.812V23.237L143.717,0V143.82z\'/></g></svg>")';
  372.  
  373. // DEFINE STYLES
  374. var $styles = '';
  375.  
  376. $styles += 'html, body, :root { margin:0; padding:0; max-width:100%; height:100%; font-family:lucidagrande,"fira sans",helvetica,sans-serif; font-size:13px !important; hyphens:auto; overflow:hidden; border-radius:0; box-sizing:border-box; }';
  377. $styles += 'ul { -webkit-margin-before:0em !important; -webkit-margin-after:0em !important; -webkit-padding-start:0em; }';
  378. $styles += 'hr { margin:0; border-bottom:0; }';
  379.  
  380. // SIDEBAR
  381. $styles += '#sidebar_wrapper { width:25%; min-width:220px; will-change:width; padding:0; position:relative; border:0; background:lightgray; overflow:hidden; }';
  382. $styles += '#sidebar { 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; overflow:hidden; }';
  383. $styles += '#handle { width:8px; position:absolute; top:0; right:-4px; bottom:0; z-index:1000; cursor:col-resize; }';
  384.  
  385. // Sidebar Header
  386. $styles += '#sidebar_header { width:100%; height:auto; position:relative; border:0; user-select:none; border-collapse:collapse; font-size:0.875rem; }';
  387. $styles += '#sidebar_header thead tr, #sidebar_header tbody tr:first-of-type { border-bottom:solid 1px grey; background-color:#BBB; }';
  388. $styles += '#sidebar_header thead th { padding:4px; font-weight:normal; font-size:0.875em; letter-spacing:0.5em; cursor:default; }';
  389. $styles += '#sidebar_header tbody tr { position:relative; }';
  390. $styles += '#sidebar_header tbody tr:first-of-type td:hover { cursor:pointer; }';
  391. $styles += '#sidebar_header tbody tr:first-of-type td:first-of-type { position:relative; }';
  392. $styles += '#sidebar_header tbody tr:first-of-type td:nth-of-type(odd) { width:24px; max-width:24px; min-width:24px; padding:0; }';
  393. $styles += '#sidebar_header tbody tr:first-of-type td:nth-of-type(even) { width:100%; padding:0; border-left:solid 1px grey; border-right:solid 1px grey; }';
  394. $styles += '#sidebar_header tbody tr:last-of-type td { padding:1em 0; vertical-align:middle; position:relative; white-space:normal; }';
  395.  
  396. // Sidebar Parent Menu
  397. $styles += '#parent_dir_menu { margin:0; padding:0; display:block; position:absolute; top:0; right:0; bottom:0; left:0; }';
  398. $styles += '#parent_dir_menu a { width:100%; height:100%; padding:0; display:block; text-align:center; vertical-align:middle; text-decoration:none; opacity:0.7; background:' + $up_arrow + 'center no-repeat; }';
  399. // Sidebar Parents Menu
  400. $styles += '#parents_dir_menu { margin:0; padding:0; height:auto; text-align:center; }';
  401. $styles += '#parents_dir_menu div { padding:4px 6px; height:auto; display:inline-block; text-align:center; vertical-align:middle; overflow:hidden; cursor:pointer; hyphens:none; white-space:normal; }';
  402. $styles += '#parents_dir_menu + ul { 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; }';
  403. $styles += '#parents_dir_menu + ul li:hover, #shortcuts_menu + ul li:hover { background:#BBB; }';
  404. $styles += '#parents_dir_menu + ul li a, #shortcuts_menu + ul li a { margin:0; padding:4px 6px; display:block; text-indent:0; text-decoration:none; color:#333; white-space:normal; }';
  405. // Sidebar Shortcuts Menu
  406. $styles += '#shortcuts_menu { margin:0; padding:0; }';
  407. $styles += '#shortcuts_menu div { width:6em; display:table-cell; text-align:center; vertical-align:middle; font-size:18px; cursor:pointer; opacity:0.7; background:'+ $menu_icon + 'center no-repeat; }';
  408. $styles += '#shortcuts_menu + ul { margin:0; padding:0; -webkit-margin-before:0em !important; -webkit-margin-after:0em !important; -webkit-padding-start:0em; 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; display:none; }';
  409. $styles += '#shortcuts_menu div, #parent_dir_menu, #prev_btn, #next_btn { opacity:0.7; }';
  410. $styles += '#dark_theme { display:block; }';
  411. $styles += '#default_theme { display:none; }';
  412.  
  413. // Sidebar Details Button
  414. $styles += '#details_btn { margin:0 0.5em; }';
  415. $styles += '#details_btn span:last-of-type { display:none; }';
  416. $styles += 'body.list #details_btn { color:#999; background:#EEE; outline:none; }';
  417.  
  418. // Sidebar Grid Button
  419. $styles += '#grid_btn { margin:0; width:28px; height:18px; display:none; float:right; cursor:pointer; outline:0; background:'+ $grid_icon +' no-repeat center 100%; }';
  420. $styles += '#grid_btn .menu { padding-right:29px; padding-left:0; position:absolute; top:-1px; right:0; border:solid grey; border-width: 1px 0 1px 1px; display:none; }';
  421. $styles += '#grid_btn .menu li { width:100%; padding:4px 6px; background:#CCC; display:block; float:right; clear:both; text-align:right; list-style:none; border-right:solid 1px grey; box-sizing:border-box; white-space:pre;}';
  422. $styles += '#grid_btn .menu li:first-of-type { border-bottom:solid 1px grey; }';
  423. $styles += '#grid_btn:hover, #sidebar_header tbody tr:first-of-type td:last-of-type:hover div, #parent_dir_menu:hover, #prev_btn:hover, #next_btn:hover { opacity:1; }';
  424. $styles += '#grid_btn.has_images, #grid_btn.has_fonts { display:inline-block; }';
  425. $styles += '#grid_btn.has_images.has_fonts:hover ul.menu { display:block !important; }';
  426. $styles += '#grid_btn.has_images.has_fonts:hover ul.menu li:hover { background:#BBB; }';
  427.  
  428. // DIR_TABLE.table
  429. $styles += '#dir_table.table { width:100%; min-wdth:100px; border:0; position:relative; overflow:hidden; table-layout:fixed; border-collapse:collapse; font-size:0.875rem; }';
  430. $styles += '#dir_table.table thead { width:100%; position:absolute; left:0; right:0; text-align:left; }';
  431. $styles += '#dir_table.table thead th { padding:0 24px 4px; }';
  432. $styles += '#dir_table.table thead .name { padding-left:2em; display:block; }';
  433. $styles += '#dir_table.table > tbody { width:100%; position:absolute; right:0; bottom:0; left:0; overflow-y:auto; outline:0; }';
  434. $styles += '#dir_table.table tbody .name { display:block; clear:right; text-align:left; }';
  435. $styles += '#dir_table.table tbody tr { display:block; margin-inline-start:0; clear:both; }';
  436. $styles += '#dir_table.table tbody a { margin:0; display:block; background-size:auto 13px; -webkit-padding-start:2m; padding: 4px 6px 4px 24px; color:#333; text-decoration:none; outline:none; overflow:hidden; background-position:6px 4px; white-space:normal; }';
  437. $styles += '#dir_table.table #thead .name a { padding:0; }';
  438. $styles += '#dir_table.table .icon img { margin-left:1rem; height:16px; }';
  439. $styles += '#dir_table.table .icon + td.name a { -webkit-padding-start:1em; padding-left:6px; }';
  440. $styles += '#dir_table.table .details, #dir_table.table.firefox #tbody > tr > td:not(:first-of-type) { display:none; padding:0 0 4px 24px; font-size:0.875em; text-align:left; vertical-align:top; float:left; }';
  441. $styles += '#dir_table.table .details a { padding:0; }';
  442. $styles += '#dir_table.table .rule { display:none; }';
  443. $styles += '#dir_table.table.show_details .details, #dir_table.table.firefox.show_details #tbody > tr > td:not(:first-of-type) { display:block; }';
  444. $styles += '#dir_table.table.headless > tbody { top:0 !important; }';
  445. $styles += '#dir_table.table.headless .icon { vertical-align:middle; float:left; }';
  446.  
  447. // DIR_TABLE.list
  448. $styles += '#dir_table_wrapper { width:100%; min-width:100px; border:0; position:relative; overflow-y:auto; }';
  449. $styles += '#dir_table.list #dir_table_wrapper { padding-top:6px; }';
  450. $styles += '#dir_table.list { width:100%; position:absolute; overflow-y:auto; list-style:none; -webkit-margin-before:0; -webkit-margin-after:0; -webkit-padding-start:0; font-size:0.875rem; line-height:1.4; }';
  451. $styles += '#dir_table.list li a { display:block; padding:3px 1rem; text-decoration:none; color:#333; }';
  452. $styles += '#dir_table.list li.dir a { padding-left:24px; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAd5JREFUeNqMU79rFUEQ/vbuodFEEkzAImBpkUabFP4ldpaJhZXYm/RiZWsv/hkWFglBUyTIgyAIIfgIRjHv3r39MePM7N3LcbxAFvZ2b2bn22/mm3XMjF+HL3YW7q28YSIw8mBKoBihhhgCsoORot9d3/ywg3YowMXwNde/PzGnk2vn6PitrT+/PGeNaecg4+qNY3D43vy16A5wDDd4Aqg/ngmrjl/GoN0U5V1QquHQG3q+TPDVhVwyBffcmQGJmSVfyZk7R3SngI4JKfwDJ2+05zIg8gbiereTZRHhJ5KCMOwDFLjhoBTn2g0ghagfKeIYJDPFyibJVBtTREwq60SpYvh5++PpwatHsxSm9QRLSQpEVSd7/TYJUb49TX7gztpjjEffnoVw66+Ytovs14Yp7HaKmUXeX9rKUoMoLNW3srqI5fWn8JejrVkK0QcrkFLOgS39yoKUQe292WJ1guUHG8K2o8K00oO1BTvXoW4yasclUTgZYJY9aFNfAThX5CZRmczAV52oAPoupHhWRIUUAOoyUIlYVaAa/VbLbyiZUiyFbjQFNwiZQSGl4IDy9sO5Wrty0QLKhdZPxmgGcDo8ejn+c/6eiK9poz15Kw7Dr/vN/z6W7q++091/AQYA5mZ8GYJ9K0AAAAAASUVORK5CYII= ") 6px 4px no-repeat; background-size:auto 13px; }';
  453. $styles += '#dir_table.list li.file a { padding-left:24px; background: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABnRSTlMAAAAAAABupgeRAAABHUlEQVR42o2RMW7DIBiF3498iHRJD5JKHurL+CRVBp+i2T16tTynF2gO0KSb5ZrBBl4HHDBuK/WXACH4eO9/CAAAbdvijzLGNE1TVZXfZuHg6XCAQESAZXbOKaXO57eiKG6ft9PrKQIkCQqFoIiQFBGlFIB5nvM8t9aOX2Nd18oDzjnPgCDpn/BH4zh2XZdlWVmWiUK4IgCBoFMUz9eP6zRN75cLgEQhcmTQIbl72O0f9865qLAAsURAAgKBJKEtgLXWvyjLuFsThCSstb8rBCaAQhDYWgIZ7myM+TUBjDHrHlZcbMYYk34cN0YSLcgS+wL0fe9TXDMbY33fR2AYBvyQ8L0Gk8MwREBrTfKe4TpTzwhArXWi8HI84h/1DfwI5mhxJamFAAAAAElFTkSuQmCC") 6px 4px no-repeat; background-size:auto 13px; }';
  454.  
  455. // dir_table: .table and .list
  456. $styles += '#dir_table tr, #dir_table li { background:transparent; }';
  457. $styles += '#dir_table > tbody tr:hover, #dir_table li:hover, #dir_table .hovered { background:#BBB; }';
  458.  
  459. // Remove icons for audio files
  460. $styles += 'body.has_audio #dir_table .audio a { padding-left:4px; background:none; }';
  461. $styles += 'body.has_audio #dir_table .audio input { margin-top:1px; margin-bottom:1px; }';
  462.  
  463. // dir_table: Selected and Playing items
  464. $styles += '#dir_table .selected, #dir_table .playing { background:lightsteelblue; }';
  465. $styles += '#dir_table .selected a, #dir_table .playing a { font-weight:bold; color:#333; }';
  466. $styles += 'body.blur_sidebar .selected { background-color:#BBB; }';
  467. $styles += 'body.blur_sidebar .selected a { font-weight:normal; color:#444; }';
  468. $styles += '#dir_table .playing { background:#9FC4C6; }';
  469.  
  470. // dir_table: Invisibles and Ignored items
  471. $styles += '#dir_table tr.ignore a, #dir_table tr.ignore.app a { color:#888; }';
  472. $styles += 'body.hide_invisibles .invisible { display:none !important; }';
  473. $styles += 'body.hide_ignored .ignore { display:none !important; }';
  474.  
  475.  
  476. // ***** CONTENT PANE STYLES ***** //
  477.  
  478. $styles += '#content_pane { width:75%; will-change:width; padding:0; border:0; background:#FFF; position:relative; }';
  479. $styles += '#content_container { width:100%; height:100%; top:0; overflow:visible; }';
  480.  
  481. // CONTENT: Header
  482. $styles += '#content_header { 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; }';
  483. $styles += '#content_header table { width:100%; padding:7px 12px 5px; border-collapse:collapse; font-size:0.875rem; }';
  484. $styles += '#content_header tr:first-of-type td:first-of-type, #content_header tr:first-of-type td:last-of-type { width:6em; padding:4px 6px 3px; vertical-align:middle; }';
  485. $styles += '#content_header tr:first-of-type td:first-of-type { float:left; text-align:left; }';
  486. $styles += '#content_header tr:first-of-type td:last-of-type { float:right; text-align:right; }';
  487. $styles += '#content_header tr:last-of-type { border-top:solid #888 1px; }';
  488. $styles += '#content_header td button { word-break:none; hyphens:none; }';
  489. $styles += '#content_title { padding:4px 1em 3px; vertical-align:middle; word-break:break-word; text-align:center; }';
  490. $styles += '#content_pane[class*="content"] #content_header { display:block !important; }';
  491.  
  492. // CONTENT: Overlay
  493. $styles += '#content_overlay { height:'+ window.innerHeight + 'px; position:absolute; top:0; right:0; bottom:0; left:0; display:none; z-index:9998; }';
  494.  
  495. // CONTENT: Grid styles
  496. // Default Image and Font sizes
  497. var $grid_image_size = $settings.grid_image_size ? $settings.grid_image_size : 150;
  498. var $grid_font_size = $settings.grid_font_size ? $settings.grid_font_size : 1;
  499. $styles += '#content_grid { width:auto; display:none; position:absolute; right:0; bottom:0; left:0; overflow:auto; font-size:1em; background:#333; grid-gap:0; grid-template-columns:repeat(auto-fit, minmax('+ $grid_image_size +'px, 1fr)); grid-template-rows:repeat(auto, minmax('+ $grid_image_size +'px, 1fr)); z-index:1; }';
  500. $styles += '#content_grid .image_grid_item { display:inline-block; width:'+ $grid_image_size +'px; height:'+ $grid_image_size +'px; float:left; text-align:center; vertical-align:middle; }';
  501. $styles += '#content_grid.not(.has_grid) .image_grid_item.selected { background:#666; }';
  502. $styles += '#content_grid div img { width:auto; height:auto; max-width:'+ ($grid_image_size - $grid_image_size/8) +'px; max-height:'+ ($grid_image_size - $grid_image_size/8) +'px; position:relative; top:50%; transform:translateY(-50%); opacity:0.8; } ';
  503. $styles += '#content_grid:not(.has_grid) .image_grid_item:hover, #content_grid:not(.has_grid) div.image_grid_item.hovered { background:#555; }';
  504. $styles += '#content_grid .font_grid_item { width:calc(100% - 2rem); padding:0rem 1rem 0.5rem; display:block; font-size:'+ $grid_font_size * 3 +'em; text-align:left; outline:none; clear:both; }';
  505. $styles += 'body #content_grid .font_grid_item:hover, body #content_grid div.font_grid_item.hovered { background:#DDD; }';
  506. $styles += 'body #content_grid .font_grid_item.selected { background:#CCC; }';
  507. $styles += 'body #content_grid div.selected { background:#CCC; }';
  508. $styles += 'body #content_grid div:hover, body #content_grid div.hovered { background:#DDD; }';
  509.  
  510. // CONTENT: Image preview styles
  511. $styles += '#content_image {padding:2em; position:absolute; top:0; right:0; bottom:0;left:0; display:none; background:#333; overflow:auto; text-align:center;}';
  512. $styles += '#content_image img:not(.zoom_img) {width:auto; height:auto; max-width:100%; max-height:100%; max-height:calc(100% - 3px); cursor:zoom-in; -webkit-user-select:none; position:relative; top:50%; transform:translateY(-50%); background:#FFF; };';
  513. $styles += '#content_image img.zoom_img { max-width:none; max-height:none; cursor:zoom-out; top:0; transform:translateY(0); background:"FFF" }';
  514.  
  515. // CONTENT: Image Prev and Next buttons
  516. $styles += '#prev_btn, #next_btn { padding:0 1em; display:none; top:0; bottom:0; z-index:100; opacity:0.6; filter:invert(50%); background: ' + $svg_arrow + ' no-repeat center; }';
  517. $styles += '#prev_btn { position:absolute; left:0 !important; }';
  518. $styles += '#next_btn { position:absolute; right:0; transform:rotate(180deg); }';
  519. $styles += 'body:not(.has_audio) #content_pane.has_image_content #prev_btn, body:not(.has_audio) #content_pane.has_image_content #next_btn { display:block; }';
  520.  
  521. // CONTENT: Embed (pdf) and iframe styles
  522. $styles += '#content_embed, #content_iframe { width:100%; height:100%; padding:0; position:absolute; top:0; right:0; bottom:0; left:0; border:0; display:none; }';
  523.  
  524. // CONTENT: Font preview styles
  525. $styles += '#content_font { padding:1rem; display:none; position:absolute; right:0; bottom:0; left:0; overflow:auto; font-size:'+ $grid_font_size +'em; word-break:break-all; overflow-wrap:break-word; hyphens:none; outline:none; }';
  526. $styles += '.specimen { padding-bottom:0.25em; white-space:normal; text-align:left; }';
  527. $styles += '.lorem { margin-bottom:1em; text-align:justify; word-break:normal; white-space:normal; overflow-wrap:normal; hyphens:auto; line-height: 1.4; }';
  528. $styles += '.lorem.first { margin-top:0.5em; }';
  529. $styles += '.lorem.first:first-line { letter-spacing:1pt; font-variant:small-caps; font-size:'+ $grid_font_size*1.33 +'em; }';
  530. $styles += '.lorem + .lorem { columns:2; column-gap:1em;}';
  531. $styles += '.lorem + .lorem + .lorem { margin-bottom:2em; columns:3; }';
  532.  
  533. // CONTENT: Font size button
  534. $styles += '#font_size { background:rgba(128,128,128,0.3); position:absolute; right:1rem; opacity:0; transition:opacity 1s ease-in-out; display:block; z-index:10; }';
  535. $styles += '#font_size span { width:2rem; height:2rem; font-size:2rem; display:block; text-align:center; cursor:pointer; }';
  536. $styles += '#font_size span:first-of-type { opacity:0.3; background:'+ $plus_sign +' center no-repeat; }';
  537. $styles += '#font_size span:last-of-type { opacity:0.3; background:'+ $minus_sign +' center no-repeat; }';
  538. $styles += '#font_size span:hover { opacity:0.5; filter:invert(100%); background-color:#666; }';
  539. $styles += '#content_pane.has_font_content:hover #font_size, #content_pane.has_grid_content:hover #font_size { display:block; opacity:1; }';
  540.  
  541. // CONTENT: Media Content
  542. $styles += '#content_media { padding: 6px; text-align:center; }';
  543. $styles += '#checkbox_div { padding-left:6px; display:inline-block; }';
  544. $styles += '#checkbox_div label { display:block; float:left; clear:both; }';
  545. $styles += '#checkbox_div label input { margin-top:0; }';
  546. $styles += 'body:not(.has_audio) #content_media { display:none; }';
  547. $styles += 'body.has_audio #content_header { display:table-cell; }';
  548. $styles += 'body.has_audio #content_pane:not(.has_image_content) #content_image { display:block; background:'+ $music +' no-repeat center; background-size:33.33%; }';
  549.  
  550. // CONTENT: Media Prev and Next Track buttons
  551. $styles += '#prev_track, #next_track { width:2em; height:2em; padding:0 0 0 8px; display:inline-block; background: #F8F8F8 '+ $next_track_arrow +' no-repeat center; background-blend-mode:difference; }';
  552. $styles += '#next_track { transform:rotate(180deg); }';
  553. $styles += '#prev_track:hover, #next_track:hover { background-blend-mode:normal; }';
  554.  
  555. // CONTENT: Display preview content
  556. $styles += '#content_pane.has_font_content #content_font { display:block; }';
  557. $styles += '#content_pane.has_image_content #content_image { display:block; }';
  558. $styles += '#content_pane.has_pdf_content #content_embed { display:block; }';
  559. $styles += '#content_pane.has_file_content #content_iframe { display:block; background:white; }';
  560. $styles += '#content_grid.has_image_grid { padding-bottom:1rem; display:grid !important; }';
  561. $styles += '#content_grid.has_font_grid, #content_grid.has_grid { background:#FFF; display:block !important; }';
  562. $styles += '#content_pane[class*="hidden"] #content_grid { display:none !important; z-index:auto; }';
  563.  
  564.  
  565. // DARK THEME STYLES
  566. $styles += 'body.dark_mode #sidebar, body.dark_mode #content_header { background:#555; }';
  567. $styles += 'body.dark_mode #sidebar ul.menu { background:#444; box-shadow-color:#111; }';
  568. $styles += 'body.dark_mode #sidebar ul.menu li:hover { background:#666; }';
  569. $styles += 'body.dark_mode #sidebar { border-right-color:#111; }';
  570. $styles += 'body.dark_mode #sidebar_header thead tr, body.dark_mode #sidebar_header tbody tr:first-of-type { border-bottom:solid 1px black; background-color:#444; }';
  571. $styles += 'body.dark_mode #sidebar_header tbody tr:first-of-type td:nth-of-type(even) { border-left-color:#111; border-right-color:#111; }';
  572. $styles += 'body.dark_mode #sidebar_header .menu { border-top-color:#111; }';
  573. $styles += 'body.dark_mode #sidebar_header .menu, body.dark_mode #content_header { border-bottom-color:#111; }';
  574. $styles += 'body.dark_mode #sidebar tr *, body.dark_mode #sidebar li, body.dark_mode #sidebar li a, body.dark_mode #content_header tr { color:#EEE !important; }';
  575. $styles += 'body.dark_mode #details_btn span { color:#333 !important; }';
  576. $styles += 'body.dark_mode #dark_theme { display:none; }';
  577. $styles += 'body.dark_mode #default_theme { display:block; }';
  578.  
  579. // SIDEBAR DARK: Grid Buttons
  580. $styles += 'body.dark_mode #sidebar #grid_btn, body.dark_mode #grid_btn .menu, body.dark_mode #parent_dir_menu, body.dark_mode #shortcuts_menu { filter:invert(100%); }';
  581. $styles += 'body.dark_mode #sidebar #grid_btn ul.menu { background:transparent; border: solid #111; border-width: 1px 0 1px 1px; box-shadow:none !important; }';
  582. $styles += 'body.dark_mode #sidebar #grid_btn .menu li { background:#555; border-right-color: #111; }';
  583. $styles += 'body.dark_mode #sidebar #grid_btn .menu li:first-of-type { border-bottom-color: #111; }';
  584. $styles += 'body.dark_mode #sidebar #grid_btn .menu li:hover { background:#777; }';
  585.  
  586. // SIDEBAR DARK: Selected and Playing items
  587. $styles += 'body.dark_mode #dir_table .selected { background:slategray !important; }';
  588. $styles += 'body.dark_mode #dir_table tr:hover, body.dark_mode #dir_table li:hover { background:#777 !important; }';
  589. $styles += 'body.dark_mode #dir_table .playing { background: #4C7E80 !important; }';
  590.  
  591. // CONTENT DARK
  592. $styles += 'body.dark_mode #content_pane, body.dark_mode #content_grid { background:#333; }';
  593. $styles += 'body.dark_mode #content_header tr:last-of-type { border-top: solid #333 1px; }';
  594. $styles += 'body.dark_mode #content_font, body.dark_mode .font_grid_item { color:#CCC; }';
  595. $styles += 'body.dark_mode #content_grid .font_grid_item.selected, body.dark_mode #content_grid .image_grid_item.selected { background:#666 !important }';
  596. $styles += 'body.dark_mode .font_grid_item:hover, body.dark_mode .font_grid_item.hovered, body.dark_mode .image_grid_item:hover, body.dark_mode .image_grid_item.hovered { background:#555 !important; }';
  597. $styles += 'body.dark_mode #content_pane.has_font_content hr { border-bottom-color:#CCC; }';
  598. $styles += 'body.dark_mode #content_iframe { filter:invert(87.5%); }';
  599. var $excluded = ['.htm','.xhtm','.mp3','.m4a','.m4v','.mp4','.ogg','.ogm','.oga','.webm','.wav','.mpeg','.flac']; // Files with these extensions in iframes should not be inverted in dark mode:
  600. for ( i = 0, n = $excluded.length; i < n; i += 1) {
  601. $styles += 'body.dark_mode #content_iframe[src*="'+ $excluded[i] +'" i] { background:white; filter:unset; }';
  602. }
  603. // MAIN CONTENT
  604. $styles += '#main_content { width:100%; height:100%; border:0; border-collapse:collapse; overflow:hidden; }';
  605.  
  606. // APPEND CUSTOM STYLES
  607. $custom_styles.append($styles);
  608. document.head.appendChild($custom_styles);
  609.  
  610. // Conditional Styles:
  611. var $custom_gecko_styles = document.createElement("style");
  612. $custom_gecko_styles.appendChild(document.createTextNode(""));
  613.  
  614. var $gecko_styles = '';
  615. $gecko_styles += 'html, body { border: solid 1px gray !important; }';
  616. $gecko_styles += 'button { padding:0; }';
  617. $gecko_styles += '#grid_btn .menu {top:-24px;}';
  618. $gecko_styles += 'thead {font-size:100%;}';
  619. $gecko_styles += '#dir_table .dir::before {position:absolute;}';
  620. $gecko_styles += '#dir_table .audio a img { display:none; }';
  621. $gecko_styles += '#dir_table.table.firefox #tbody > tr > td:not(:first-of-type) { padding:0 24px 4px; }';
  622. $gecko_styles += '#dir_table.table.firefox #tbody > tr > td:last-of-type { text-indent:6px; }';
  623. $gecko_styles += '#prev_track, #next_track { background: #3F3F3F '+ $next_track_arrow_gecko +' no-repeat center; }';
  624. $gecko_styles += 'body.dark_mode #prev_track, body.dark_mode #next_track { background: #252525 '+ $next_track_arrow_gecko +' no-repeat center; }';
  625. $gecko_styles += 'body #prev_track:hover, body #next_track:hover { background: #252525 '+ $next_track_arrow_gecko_hover +' no-repeat center; }';
  626. $gecko_styles += '#prev_track:hover, #next_track:hover { background-blend-mode:difference; }';
  627.  
  628. $custom_gecko_styles.append($gecko_styles);
  629.  
  630. if( $userAgent.indexOf('Firefox') > -1 ){
  631. document.head.appendChild($custom_gecko_styles);
  632. $block = 'start';
  633. } else {
  634. $block = 'nearest';
  635. }
  636. // if( $userAgent.indexOf('Chrome') > -1 ){
  637. // Do something
  638. // }
  639.  
  640. // Sidebar Header: Hide invisibles checkbox user setting
  641. if ( $settings.hide_invisibles === true ) {
  642. $inv_checkbox.find('input').prop('checked',true);
  643. $body.addClass('hide_invisibles');
  644. }
  645. // Sidebar Header: Toggle Invisibles checkbox
  646. $inv_checkbox.on('click','input', function(e){
  647. $body.toggleClass('hide_invisibles');
  648. $('.selected').removeClass('selected');
  649. });
  650.  
  651. // Dir_Table: Hide Ignored Files
  652. if ( $settings.hide_ignored_files === true ) {
  653. $body.addClass('hide_ignored');
  654. }
  655.  
  656. // Dark Mode
  657. if ( $settings.dark_mode === true ) {
  658. $body.addClass('dark_mode');
  659. }
  660. $toggle_dark_mode.on('click',function(e) {
  661. e.preventDefault();
  662. $body.toggleClass('dark_mode');
  663. });
  664.  
  665. // Hide hide invisibles chceckbox
  666. if ( platformIsWin() || window.location.href.indexOf('file:') < 0 ) {
  667. $inv_checkbox.hide();
  668. }
  669.  
  670. // ***** END STYLES ***** //
  671.  
  672. // HELPER FUNCTIONS
  673.  
  674. // get item link
  675. var thisLink = function(x) {
  676. return $(x).find('a').attr('href') != undefined ? $(x).find('a').attr('href') : $(x).attr('href') ;
  677. };
  678.  
  679. var thisText = function(x) {
  680. return $(x).find('a').text() ? $(x).find('a').text() : $(x).text();
  681. };
  682.  
  683. var thisRow = function(x) {
  684. return $(x).closest('#dir_table > tbody > tr').length ? $(x).closest('#dir_table > tbody > tr') : $(x).closest('#dir_table > li');
  685. };
  686.  
  687.  
  688. // ***** BUILD MENUS ***** //
  689.  
  690. // MENUS: Parents Link Menu Items
  691.  
  692. var parentLinksArr = function() {
  693. var $paths_arr = [];
  694. for ( i = 1, n = $location_arr.length; i < n - 1; i+=1 ) {
  695. $paths_arr[0] = ''; // root
  696. $paths_arr[i] = $paths_arr[i - 1] + $location_arr[i] + '/';
  697. }
  698. return $paths_arr;
  699. };
  700.  
  701. // MENUS: function to build menu list items
  702. var menuItems = function(x,y,i) { // (link, name, count)
  703. var $menu_item = '<li><a href="file:///' + x[i] + '">' + y[i] + '</a></li>';
  704. return $menu_item;
  705. };
  706. // MENUS: Parents Directory Menu Items
  707. var parents_dir_menu_arr = function() {
  708. var $parents_dir_menu_items = [];
  709. for ( var i = 1, n = parentLinksArr().length; i < n; i+=1 ) {
  710. $parents_dir_menu_items[0] = menuItems('/','/',0); // root
  711. $parents_dir_menu_items[i] = menuItems( parentLinksArr(), parentLinksArr(), i);
  712. }
  713. $parents_dir_menu_items.pop(); // remove current directory
  714. $parents_dir_menu_items = $parents_dir_menu_items.reverse().join('').replace(/%20/g,' ');
  715. return $parents_dir_menu_items;
  716. };
  717. $parents_dir_menu.siblings('ul').append( parents_dir_menu_arr() );
  718.  
  719. // MENUS: Root Shortcuts Menu Items
  720. $settings.root_shortcuts = $settings.root_shortcuts.map(i => i + '/'); // append '/' to directory name
  721.  
  722. var root_shortcuts_menu_arr = function() {
  723. if ( $settings.root_shortcuts.length ) {
  724. var $root_shortcut_items = [];
  725. for ( i = 0, n = $settings.root_shortcuts.length; i < n; i+=1 ) {
  726. $root_shortcut_items[i] = menuItems($settings.root_shortcuts,$settings.root_shortcuts,i);
  727. }
  728. $root_shortcut_items = $root_shortcut_items.join('');
  729. return $root_shortcut_items;
  730. }
  731. };
  732.  
  733. // MENUS: User Shortcuts Menu Items
  734. var $working_user_shortcuts = $settings.user_shortcuts;
  735.  
  736. var $user_shortcuts_display_name = $working_user_shortcuts.map(i => $settings.user_name + '/' + i + '/' ); // build display names
  737. var $home = '';
  738. if ( platformIsLinux() ) {
  739. $home = 'home/';
  740. } else {
  741. $home = 'users/';
  742. }
  743. $working_user_shortcuts = $working_user_shortcuts.map(i => $home + $settings.user_name + '/' + i + '/'); // build link fragments
  744.  
  745. var userShortcutsMenuArr = function() {
  746. if ( $settings.user_name && $working_user_shortcuts.length ) {
  747. var $user_shortcut_items = [];
  748. for ( i = 0, n = $working_user_shortcuts.length; i < n; i+=1 ) {
  749. $user_shortcut_items[i] = menuItems( $working_user_shortcuts, $user_shortcuts_display_name, i );
  750. }
  751. $user_shortcut_items = $user_shortcut_items.join('');
  752. return $user_shortcut_items;
  753. }
  754. };
  755.  
  756. // MENUS: File Shortcuts Menu Items
  757. var $file_shortcuts_display_name = $settings.file_shortcuts.map(i => i.split('/').pop()); // get file names from paths
  758.  
  759. var fileShortcutsMenuArr = function() {
  760. if ( $settings.file_shortcuts.length ) {
  761. var $file_shortcut_items = [];
  762. for ( i = 0, n = $settings.file_shortcuts.length; i < n; i+=1 ) {
  763. $file_shortcut_items[i] = menuItems( $settings.file_shortcuts, $file_shortcuts_display_name, i );
  764. }
  765. $file_shortcut_items = $file_shortcut_items.join('').replace(/\/<\/a>/g,'<\/a>').replace(/<a\s/g,'<a class="file_shortcut" ').replace(/%20/g,' ');
  766. return $file_shortcut_items;
  767. }
  768. };
  769. $shortcuts_menu.siblings('ul').append( root_shortcuts_menu_arr(), $divider, userShortcutsMenuArr(), $divider.clone(), fileShortcutsMenuArr(), $divider.clone() );
  770.  
  771. // MENUS: Additional Menu Items
  772. $shortcuts_menu.siblings('ul').append($toggle_dark_mode, $export_settings);
  773.  
  774. // MENUS: Show Menu on click
  775. function showMenus(el) {
  776. var $position = $(el).position();
  777. $(el).find('ul').css({'top':$position.top + $(el).innerHeight() + 'px'}).toggle().parent('td').siblings('td').find('.menu').hide();
  778. }
  779. $parents_dir_menu.add($shortcuts_menu).parent('td').on('click',function(e) {
  780. e.stopPropagation();
  781. showMenus(this);
  782. });
  783.  
  784. // ***** END BUILD MENUS ***** //
  785.  
  786. // ***** END BUILD UI ***** //
  787.  
  788. // ***** SIDEBAR ***** //
  789.  
  790. // DIRECTORY TABLE
  791.  
  792. // DIRECTORY TABLE VARIABLES
  793. if ( $dir_table.hasClass('table') ) {
  794.  
  795. $dir_table_head = $dir_table.find('> thead').length ? $dir_table.find('> thead') : $dir_table.addClass('headless').find('> tbody > tr:first-of-type') ;
  796. $dir_table_head.attr('id','thead');
  797. $dir_table_head_cell = $dir_table_head.find('th');
  798. $dir_table_head_name = $dir_table_head.find('th:contains("Name")');
  799. $dir_table_head_name.addClass('name');
  800. $dir_table_head_details = $dir_table_head_name.nextAll();
  801. $dir_table_head_details.addClass('details');
  802.  
  803. $dir_table_body = $dir_table.find('> tbody');
  804. $dir_table_body.attr('id','tbody');
  805. $dir_table_rule = $dir_table_body.find('hr').closest('tr');
  806. $dir_table_rule.addClass('rule');
  807. $dir_table_row = $dir_table_body.find('> tr').not('#thead').not('.rule');
  808. $dir_table_cell = $dir_table_row.find('td');
  809. $dir_table_link = $dir_table_cell.find('a');
  810. $dir_table_item_name = $dir_table_link.parent('td');
  811. $dir_table_item_name.addClass('name');
  812. $dir_table_item_icon = $dir_table_item_name.add($dir_table_head_name).prev();
  813. $dir_table_item_icon.addClass('icon'); // for directory lists with separate td for icons
  814. $dir_table_details = $dir_table_item_name.nextAll();
  815. $dir_table_details.addClass('details');
  816. }
  817. if( $userAgent.indexOf('Firefox') > -1 ) {
  818. $dir_table.addClass('firefox');
  819. }
  820. if ( $dir_table.hasClass('list') ) { // Apache server
  821. $dir_table_wrapper.addClass('headless');
  822. $dir_table_head = '';
  823. $dir_table_head_cell = '';
  824. $dir_table_head_name = '';
  825. $dir_table_head_details = '';
  826. $dir_table_body = '';
  827. $dir_table_row = $dir_table.find('li');
  828. $dir_table_cell = '';
  829. $dir_table_details = '';
  830. $dir_table_link = $dir_table_row.find('a');
  831. // $dir_table_link.addClass('name');
  832. $dir_table_item_name = thisText($dir_table_link);
  833. $sidebar.append($dir_table_wrapper);
  834. }
  835. $dir_table.detach().attr('id','dir_table');
  836.  
  837. // ***** DIR_TABLE SETUP ***** //
  838.  
  839. // Sidebar Header: Show details button click function
  840. function detailsButton() {
  841. $dir_table.toggleClass('show_details');
  842. $dir_table_body.css({'top':$dir_table_head.height() + 1 +'px'});
  843. $details_btn.find('span').toggle();
  844. }
  845. $details_btn.on('click', detailsButton );
  846.  
  847. // Dir_table: Row hover effects
  848. $dir_table_row.hover(function() {
  849. // Highlight corresponding grid item
  850. if ( $content_grid.is(':visible') ) {
  851. $this_link = thisLink(this);
  852. $content_grid.find('[href="' + $this_link + '"]').closest('div').addClass('hovered');
  853. }
  854. }, function() {
  855. if ( $content_grid.is(':visible') ) {
  856. $content_grid.find('.hovered').removeClass('hovered');
  857. }
  858. });
  859.  
  860. // Dir_table: create link arrays
  861. var $dir_table_dir_link_arr = [];
  862. var $dir_table_file_link_arr = [];
  863. var $dir_table_file_ext_arr = [];
  864. $dir_table_row.not('.ignore,.invisible').find('a').each(function() {
  865. $this_link = thisLink(this).toLowerCase();
  866. if ( $this_link.endsWith('/') ) {
  867. $dir_table_dir_link_arr.push($this_link);
  868. return $dir_table_dir_link_arr;
  869. } else {
  870. var $this_link_ext = $this_link.slice($this_link.lastIndexOf('.'));
  871. $dir_table_file_link_arr.push($this_link);
  872. if ( $dir_table_file_ext_arr.indexOf($this_link_ext) < 0 ) {
  873. $dir_table_file_ext_arr.push($this_link_ext);
  874. }
  875. return $dir_table_file_link_arr, $dir_table_file_ext_arr;
  876. }
  877. });
  878. // Dir_table: array of all dir_table links
  879. var $dir_table_link_arr = [];
  880. $dir_table_link_arr = $dir_table_dir_link_arr.concat($dir_table_file_link_arr);
  881.  
  882. // Dir_table: Classify items
  883. $dir_table_row.each(function() {
  884.  
  885. $this_link = thisLink(this);
  886. $this_text = thisText(this);
  887. $this_ext = $this_text.toLowerCase().slice($this_text.lastIndexOf('.'));
  888.  
  889. if ( $this_link !== undefined ) {
  890.  
  891. $this_link = $this_link.toString().toLowerCase();
  892.  
  893. // Directories or files
  894. if ( $this_link.endsWith('/') ) {
  895. $(this).addClass('dir');
  896. } else {
  897. $(this).addClass('file');
  898. }
  899. // File types
  900. if ( $this_link.endsWith('.pdf') ) {
  901. $(this).addClass('pdf');
  902. } else if ( $.inArray( $this_ext, $image_ext_arr ) != -1 ) {
  903. $(this).addClass('img');
  904. } else if ( $.inArray( $this_ext, $audio_ext_arr ) != -1 ) {
  905. $(this).addClass('audio').find('a').prepend('<input type="checkbox" tabindex="-1" checked="true" />');
  906. } else if ( $.inArray( $this_ext, $font_ext_arr ) != -1 ) {
  907. $(this).addClass('font');
  908. }
  909. // Invisibles
  910. if ( $this_link.slice($this_link.lastIndexOf('/') + 1 ) === '.' || $this_link.startsWith('.') || thisText(this).startsWith('.') ) {
  911. $(this).addClass('invisible');
  912. }
  913. // Ignored
  914. if ( $settings.ignore_files === true ) {
  915. for ( i = 0, n = $settings.ignore_file_types.length; i < n; i+=1 ) {
  916. if ( $this_link.endsWith( $settings.ignore_file_types[i] ) ) {
  917. thisRow(this).addClass('ignore');
  918. }
  919. }
  920. }
  921.  
  922. // directories as Files and hide ignored files
  923. if ( $this_text.endsWith('.app') ) {
  924. $(this).addClass('app');
  925. if ( $settings.apps_as_dirs === false ) {
  926. $(this).addClass('ignore');
  927. }
  928. }
  929.  
  930. if ( $(this).hasClass('dir') && $(this).hasClass('app') && $this_text.endsWith('/') ) {
  931. $(this).find('a').text($this_text.slice(0,$this_text.lastIndexOf('/')));
  932. }
  933. }
  934. }); // end classify dir_table items
  935.  
  936. // Append dir_table to sidebar or wrapper, depending on directory type
  937. if ( $dir_table.hasClass('table') ) {
  938. $dir_table.appendTo($sidebar);
  939. } else {
  940. $dir_table.appendTo($dir_table_wrapper);
  941. }
  942.  
  943. // Add body classes depending on content
  944. if ( $dir_table.find('.img').length ) {
  945. $body.add($grid_btn).addClass('has_images');
  946. }
  947. if ( $dir_table.find('.font').length ) {
  948. $body.add($grid_btn).addClass('has_fonts');
  949. }
  950. if ( $dir_table.find('.audio').length ) {
  951. $dir_table.add($body).addClass('has_audio');
  952. $dir_table.find('thead th').first().prepend('<input id="play_toggle" type="checkbox" tabindex="-1" checked="true" />');
  953. $dir_table.find('li').first().after('<li><a><input id="play_toggle" type="checkbox" tabindex="-1" checked="true" />Name</a><li>');
  954. }
  955.  
  956. // ***** End dir_table setup ***** //
  957.  
  958. // Cereate Main Content element and append Sidebar and Content Pane.
  959. var $main_content = $('<table id="main_content"><tbody><tr></tr></tbody></table>');
  960. $main_content.find('tr').append( $sidebar_wrapper, $content_pane );
  961.  
  962. // Append everything to body
  963. $body.prepend($main_content);
  964.  
  965. // ***************************** //
  966.  
  967. // ***** SHOW/HIDE CONTENT ***** //
  968.  
  969. // MENUS: Hide Menu function
  970. function hideMenu() {
  971. $('.menu').hide();
  972. }
  973. $(document).on('click', hideMenu );
  974.  
  975. // Set content height
  976. function setContentHeight() {
  977. var $dir_table_head_height = $dir_table_head.length ? $dir_table_head.height() : 0;
  978. var $content_headerHeight = $content_header.outerHeight();
  979. $dir_table.add($dir_table_wrapper).css({'height':window.innerHeight - $sidebar_header.outerHeight() });
  980. $dir_table.find($dir_table_body).css({'top': $dir_table_head_height });
  981. $content_image.css({'top':$content_headerHeight });
  982. $content_grid.add($content_embed).add($content_iframe).add($content_font).css({'height':window.innerHeight - $content_headerHeight,'top':$content_headerHeight });
  983. $content_font_size.css({'top':$content_headerHeight + 13 });
  984. $content_media.find('> div').css({'height':$audio.height() });
  985. $('#checkbox_div').css({'margin-right': - $('#checkbox_div').outerWidth() });
  986. }
  987. setContentHeight();
  988. $('window').on('resize', setContentHeight );
  989.  
  990. function setContentTitle() {
  991. if ( $content_pane.hasClass('has_grid_content') && $content_grid.hasClass('has_grid') ) {
  992. $content_title.empty().prepend('Images and Fonts from: ' + $current_dir_name);
  993. } else if ( $content_pane.hasClass('has_grid_content') && $content_grid.hasClass('has_image_grid') ) {
  994. $content_title.empty().prepend('Images from: ' + $current_dir_name);
  995. } else if ( $content_pane.hasClass('has_grid_content') && $content_grid.hasClass('has_font_grid') ) {
  996. $content_title.empty().prepend('Fonts from: ' + $current_dir_name);
  997. } else if ( $body.hasClass('has_audio') ) {
  998. $content_title.empty().prepend( thisText($playing) );
  999. } else {
  1000. $content_title.empty().prepend( thisText($('.selected')) );
  1001. }
  1002. if ( $('.selected').hasClass('ignore') ) {
  1003. $content_title.append(' (Ignored content)' );
  1004. }
  1005. }
  1006.  
  1007. // Get image dimensions
  1008. function getDimensions(link, callback) {
  1009. var img = new Image();
  1010. img.src = link;
  1011. img.onload = function() { callback( this.width, this.height ); };
  1012. }
  1013.  
  1014. function scrollSidebar(row) {
  1015. row[0].scrollIntoView({ behavior:'smooth', block:$block, inline:'nearest' });
  1016. }
  1017. function scrollGrid(item) {
  1018. item[0].scrollIntoView({ behavior:'smooth', block:$block, inline:'nearest' });
  1019. }
  1020.  
  1021. // Select row on click and set classes for $content_pane
  1022. function selectThis(row) {
  1023. // Audio vs other
  1024. if ( row.hasClass('audio') ) {
  1025. row.addClass('playing').siblings().removeClass('playing hovered');
  1026. } else
  1027. if ( row.not('.audio') ) {
  1028. row.addClass('selected').siblings().removeClass('selected hovered');
  1029. }
  1030.  
  1031. $selected = $dir_table.find('.selected');
  1032. $playing = $dir_table.find('.playing');
  1033.  
  1034. // Scroll selected/playing items
  1035. if ( $selected.length && $body.not('.has_audio') ) {
  1036. // scrollSidebar( $selected );
  1037. }
  1038. if ( $playing.length ) {
  1039. scrollSidebar( $playing );
  1040. }
  1041.  
  1042. // Directory rows
  1043. if ( row.hasClass('dir') ) {
  1044. closeThis(); // empty content pane
  1045. setContentTitle();
  1046. $content_pane.removeClass().addClass('has_dir_content');
  1047. }
  1048.  
  1049. // Select corresponding grid item
  1050. var $grid_selected = $content_grid.find('div.font_grid_item[href="'+ thisLink(row) +'"]').add('div.image_grid_item a[href="'+ thisLink(row) +'"]').parent('div').addBack();
  1051. if ( $content_pane.hasClass('has_grid_content') ) {
  1052. $grid_selected.addClass('selected').siblings().removeClass('selected');
  1053. $grid_selected = $content_grid.find('.selected');
  1054. // scrollGrid($grid_selected); // grid scroll is not working reliably
  1055. }
  1056. }
  1057.  
  1058. function showIgnored() {
  1059. closeThis();
  1060. $content_pane.addClass('has_ignored_content');
  1061. $content_title.append(' (Ignored content)' );
  1062. }
  1063.  
  1064. function showImage(row,link) {
  1065. $content_pane.addClass('has_image_content');
  1066. $content_image.find('img').removeClass('zoom_img').attr('src',link);
  1067. getDimensions( link, function( width, height ) {
  1068. if ( !$body.is('.has_audio') ) {
  1069. $content_title.append(' <span style="text-transform:lowercase;">(' + width + 'px &times; ' + height + 'px</span>)' );
  1070. }
  1071. });
  1072. $content_grid.find('a[href="' + thisLink(row) + '"]').parent('div').addClass('selected').siblings().removeClass('selected');
  1073. }
  1074.  
  1075. function showPdf(link) {
  1076. $content_pane.addClass('has_pdf_content');
  1077. $content_embed.attr('type','application/pdf').attr('src',link + '?#zoom=100&scrollbar=1&toolbar=1&navpanes=1');
  1078. }
  1079.  
  1080. function showFile(link) {
  1081. $content_pane.addClass('has_file_content');
  1082. $content_iframe.attr('src',link);
  1083. }
  1084.  
  1085. function showFont(row,link) {
  1086. var $font_family = row.find('.name').text();
  1087.  
  1088. addCustomStyle($font_family,link);
  1089.  
  1090. $content_pane.addClass('has_font_content');
  1091. $content_font.css({ 'font-family':'"'+ $font_family +'"' });
  1092. }
  1093.  
  1094. function addCustomStyle(font_family,link) {
  1095. if ( $font_family_arr.indexOf(font_family) == -1 ) {
  1096. $font_family_arr.push(font_family);
  1097. $custom_font_styles.append('@font-face { font-family: "'+ font_family +'"; src: url("'+ link +'"); }'); // only add style if it doesn't exist
  1098. }
  1099. }
  1100.  
  1101. // Show selected content
  1102. function showThis(row,link) {
  1103.  
  1104. if ( $content_pane.hasClass('has_grid_content') ) {
  1105. $content_pane.removeClass().addClass('has_hidden_grid'); // hide grid when showing new content
  1106. } else
  1107. if ( $content_pane.hasClass('has_hidden_grid') ) {
  1108. $content_pane.removeClass().addClass('has_hidden_grid'); // keep grid hidden when showing new content
  1109. } else
  1110. if ( !row.hasClass('audio') ) {
  1111. $content_pane.removeClass();
  1112. }
  1113.  
  1114. if ( row.hasClass('ignore') ) {
  1115. showIgnored();
  1116. return;
  1117. }
  1118. if ( row.hasClass('img') ) {
  1119. showImage(row,link);
  1120. return;
  1121. }
  1122. if ( row.hasClass('pdf') ) {
  1123. showPdf(link);
  1124. return;
  1125. }
  1126. if ( row.hasClass('font') ) {
  1127. showFont(row,link);
  1128. return;
  1129. }
  1130. if ( row.hasClass('unchecked') ) {
  1131. return;
  1132. } else
  1133. if ( row.hasClass('playing') ) {
  1134. playPause();
  1135. return;
  1136. } else
  1137. if ( row.hasClass('audio') && !row.hasClass('.unchecked') ) {
  1138. $count = row.filter('.audio:not(.unchecked)').index() - 1;
  1139. loadAudio(row.index('.audio:not(.unchecked)'));
  1140. return;
  1141. }
  1142. if ( row.hasClass('file') ) {
  1143. showFile(link);
  1144. return;
  1145. }
  1146. }
  1147.  
  1148. // ***** MAIN CLICK FUNCTION FOR SHOWING CONTENT ***** //
  1149.  
  1150. function clickDirTableLink(link) {
  1151. $this_row = thisRow(link);
  1152. $this_link = thisLink(link);
  1153.  
  1154. if ( $this_row.hasClass('dir') ) {
  1155. window.location = $this_link;
  1156. }
  1157. hideMenu();
  1158. showThis( $this_row, $this_link );
  1159. selectThis( $this_row );
  1160. setContentTitle();
  1161. setContentHeight();
  1162. }
  1163.  
  1164. $dir_table_row.on('click','a',function(e) {
  1165. e.preventDefault();
  1166. e.stopPropagation();
  1167. clickDirTableLink($(this));
  1168. });
  1169.  
  1170. // Auto-select file from file shortcut list;
  1171. // Limitations: only loads last file from list found in directory, doesn't know anything about which actual file shortcut was selected
  1172. function autoSelectFile() {
  1173. if ( $settings.file_shortcuts.length ) {
  1174. for ( i = 0, n = $settings.file_shortcuts.length; i < n; i+=1 ) {
  1175. if ( $.inArray($settings.file_shortcuts[i], $dir_table_link_arr ) ) {
  1176. $dir_table.find( 'a[href*="/' + $settings.file_shortcuts[i] + '"]').click();
  1177. }
  1178. }
  1179. }
  1180. }
  1181. autoSelectFile();
  1182.  
  1183. // File shortcuts: load directory then auto-select file
  1184. function showFileShortcut(item) {
  1185. e.preventDefault();
  1186. $this_link = thisLink(item);
  1187. var $this_dir = $this_link.slice(0,$this_link.lastIndexOf('/') );
  1188. window.location = $this_dir;
  1189. }
  1190. $('.file_shortcut').on('click',showFileShortcut );
  1191.  
  1192. function reloadThis() {
  1193. if ( $audio.find('source').attr('src') ) {
  1194. var $this_src = $audio.find('source').attr('src');
  1195. $audio.find('source').attr('src',$this_src);
  1196. $audio.trigger('load');
  1197. //$audio.trigger('play');
  1198. return;
  1199. } else
  1200. if ( $content_pane.hasClass('has_grid_content') ) {
  1201. $grid_btn.click();
  1202. $content_grid.css({'font-size':'1em','grid-template-columns':'repeat(auto-fit, minmax(150px, 1fr))'});
  1203. return;
  1204. }
  1205. if ( $content_pane.is('[class*="content"]') ) {
  1206. $('.selected').find('a').click();
  1207. $content_font.css({'font-size':'1em'});
  1208. } else {
  1209. return;
  1210. }
  1211. }
  1212. $content_reload_btn.on('click', reloadThis );
  1213.  
  1214. function closeContent() {
  1215. $content_image.find('img').removeAttr('src');
  1216. $content_embed.removeAttr('src');
  1217. $content_iframe.removeAttr('src');
  1218. $content_font.css({'font-family':''});
  1219. }
  1220.  
  1221. function closeThis() {
  1222. hideMenu();
  1223. if ( $content_pane.hasClass('has_grid_content') ) {
  1224. $content_pane.removeClass('has_grid_content');
  1225. $content_grid.removeClass().empty().css({'font-size':'1em'});
  1226. } else if ( $content_pane.hasClass('has_hidden_grid') ) {
  1227. $content_pane.removeClass().addClass('has_grid_content');
  1228. closeContent();
  1229. } else {
  1230. $content_pane.removeClass();
  1231. closeContent();
  1232. }
  1233. setContentTitle();
  1234. }
  1235. // Close content button
  1236. $content_close_btn.on( 'click', closeThis );
  1237.  
  1238.  
  1239. // ***** KEYBOARD EVENTS ***** //
  1240. function navigateToThis(row) {
  1241. if ( $content_pane.hasClass('has_grid_content') ) {
  1242. selectThis(row);
  1243. } else {
  1244. row.find('a').click();
  1245. }
  1246. }
  1247.  
  1248. $body.on('keydown',$dir_table,function(e) {
  1249.  
  1250. var $selected = $dir_table_row.filter('.selected');
  1251. var $selected_href = thisLink($selected);
  1252. var $playing = $dir_table_row.filter('.playing');
  1253. var $first_item = $dir_table_row.filter(':visible').not('.audio').first();
  1254. var $last_item = $dir_table_row.filter(':visible').not('.audio').last();
  1255. var $prev_item = $selected.prevAll(':visible').not('.audio').first();
  1256. var $next_item = $selected.nextAll(':visible').not('.audio').first();
  1257. var $first_image = $dir_table_row.filter('.img:visible').first();
  1258. var $last_image = $dir_table_row.filter('.img:visible').last();
  1259. var $prev_image = $selected.prevAll('.img:visible').first();
  1260. var $next_image = $selected.nextAll('.img:visible').first();
  1261. var $first_font = $dir_table_row.filter('.font:visible').first();
  1262. var $last_font = $dir_table_row.filter('.font:visible').last();
  1263. var $prev_font = $selected.prevAll('.font:visible').first();
  1264. var $next_font = $selected.nextAll('.font:visible').first();
  1265.  
  1266. switch ( e.key ) {
  1267.  
  1268. case 'ArrowUp':
  1269.  
  1270. // Go to parent folder
  1271. if ( (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) ) {
  1272. window.location = $parent_dir_link;
  1273. break;
  1274. }
  1275. // Allow arrow navigation within content_editable elemnets
  1276. if ( $('*[contentEditable="true"').is(':focus') ) {
  1277. return;
  1278. }
  1279.  
  1280. e.preventDefault();
  1281. if ( $first_item.hasClass('selected') || $selected.length < 1 ) {
  1282. $last_item.hasClass('dir') ? selectThis($last_item) : $last_item.find('a').click();
  1283. } else {
  1284. $prev_item.hasClass('dir') ? selectThis($prev_item) : $prev_item.find('a').click();
  1285. }
  1286. scrollSidebar( $selected.length ? $selected : $last_item );
  1287.  
  1288. break;
  1289.  
  1290. case 'ArrowDown':
  1291.  
  1292. if ( (e.ctrl || e.metaKey) && $selected.hasClass('app') && $settings.apps_as_dirs === false ) {
  1293. return;
  1294. } else if ( $('*[contentEditable="true"').is(':focus') ) {
  1295. return;
  1296. } else if ( (e.ctrl || e.metaKey) && $selected.hasClass('dir') ) {
  1297. window.location = $selected_href;
  1298. break;
  1299. }
  1300.  
  1301. e.preventDefault();
  1302. if ( $last_item.hasClass('selected') || $selected.length < 1 ) {
  1303. $first_item.hasClass('dir') ? selectThis($first_item) : $first_item.find('a').click();
  1304. } else {
  1305. $next_item.hasClass('dir') ? selectThis($next_item) : $next_item.find('a').click();
  1306. }
  1307. scrollSidebar( $selected.length ? $selected : $first_item );
  1308.  
  1309. break;
  1310.  
  1311. case 'ArrowLeft':
  1312.  
  1313. if ( (e.ctrl || e.metaKey) || ( e.ctrl && e.metaKey ) ) {
  1314. return;
  1315. } else if ( $('*[contentEditable="true"').is(':focus') ) {
  1316. return;
  1317. }
  1318. // Navigate Grid or Images
  1319. if ( $playing.length ) {
  1320. prevTrack();
  1321. $audio.trigger('pause');
  1322. return;
  1323. } else if ( $selected.length < 1 ) {
  1324. $last_image.length ? navigateToThis($last_image) : navigateToThis($last_font);
  1325. } else if ( $first_image.hasClass('selected') || $selected.length < 1 ) {
  1326. $last_font.length ? navigateToThis($last_font) : navigateToThis($last_image);
  1327. } else if ( $first_font.hasClass('selected') || $selected.length < 1 ) {
  1328. $last_image.length ? navigateToThis($last_image) : navigateToThis($last_font);
  1329. } else if ( $selected.hasClass('img') && $prev_image.length ) {
  1330. navigateToThis($prev_image);
  1331. } else {
  1332. navigateToThis($prev_font);
  1333. }
  1334. break;
  1335.  
  1336. case 'ArrowRight':
  1337.  
  1338. if ( (e.ctrl || e.metaKey) || ( e.ctrl && e.metaKey ) ) {
  1339. return;
  1340. } else if ( $('*[contentEditable="true"').is(':focus') || $selected.hasClass('dir ignore') ) {
  1341. return;
  1342. }
  1343. // Navigate Grid or Images
  1344. if ( $selected.hasClass('dir') && !$body.hasClass('has_audio') ) {
  1345. window.location = $selected_href; // Open directory
  1346. } else if ( $playing.length ) {
  1347. nextTrack();
  1348. $audio.trigger('pause');
  1349. } else if ( $selected.length < 1 ) {
  1350. $first_image.length ? navigateToThis($first_image) : navigateToThis($first_font);
  1351. } else if ( $last_image.hasClass('selected') || $selected.length < 1 ) {
  1352. $first_font.length ? navigateToThis($first_font) : navigateToThis($first_image);
  1353. } else if ( $last_font.hasClass('selected') || $selected.length < 1 ) {
  1354. $first_image.length ? navigateToThis($first_image) : navigateToThis($first_font);
  1355. } else if ( $selected.hasClass('img') && $next_image.length ) {
  1356. navigateToThis($next_image);
  1357. } else {
  1358. navigateToThis($next_font);
  1359. }
  1360. break;
  1361.  
  1362. case ' ':
  1363. // Play/pause audio
  1364. if ( $body.hasClass('has_audio') ) {
  1365. playPause();
  1366. }
  1367. break;
  1368.  
  1369. case 'Enter':
  1370. // Open directories (or ignore)
  1371. if ( $selected.hasClass('app') && $settings.apps_as_dirs === false ) {
  1372. break;
  1373. } else {
  1374. $selected.find('a').click();
  1375. }
  1376. break;
  1377.  
  1378. case 'd':
  1379. // Toggle Invisibles with Command-i
  1380. if ( (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) ) {
  1381. e.preventDefault();
  1382. e.stopPropagation();
  1383. $details_btn.click();
  1384. }
  1385. break;
  1386.  
  1387. case 'g':
  1388. // Show image Grid
  1389. if ( (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) ) {
  1390. e.preventDefault();
  1391. e.stopPropagation();
  1392. $grid_btn.click();
  1393. }
  1394. break;
  1395.  
  1396. case 'i':
  1397. // Toggle Invisibles with Command-i
  1398. if ( (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) ) {
  1399. e.preventDefault();
  1400. e.stopPropagation();
  1401. $inv_checkbox.find('input').click();
  1402. }
  1403. break;
  1404.  
  1405. case 'o':
  1406. // Cmd/Ctrl + Shift + O: Open selected item in new window
  1407. if ( (navigator.platform.match("Mac") ? e.metaKey && e.shiftKey : e.ctrlKey && e.shiftKey ) ) {
  1408. window.open($selected_href);
  1409. }
  1410. break;
  1411.  
  1412. case 'r':
  1413. // Cmd/Ctrl + Shift + R: Refresh
  1414. if ( (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) ) {
  1415. if ( $content_pane.is('[class*="has_"]') ) {
  1416. e.preventDefault();
  1417. $content_reload_btn.click();
  1418. } else {
  1419. return;
  1420. }
  1421. }
  1422. break;
  1423.  
  1424. case 'w':
  1425. // Close content pane if Close button visible with Command-w
  1426. // Doesn't work in Firefox: can't override default keybinding
  1427. if ( (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) && ($content_pane.is('[class*="has_"]')) ) {
  1428. e.preventDefault();
  1429. e.stopPropagation();
  1430. $content_close_btn.click();
  1431. }
  1432. break;
  1433.  
  1434. case '.':
  1435. // Increase font preview size
  1436. if ( (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) && e.shiftKey ) {
  1437. $('#increase').click();
  1438. }
  1439. break;
  1440.  
  1441. case ',':
  1442. // Dencrease font preview size
  1443. if ( (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) && e.shiftKey ) {
  1444. $('#decrease').click();
  1445. }
  1446. break;
  1447.  
  1448. case 'tab':
  1449. break;
  1450.  
  1451. } // end switch
  1452.  
  1453. });
  1454.  
  1455. // ***** END KEYBOARD EVENTS ***** //
  1456.  
  1457. // ***** IMAGE NAVIGATION ***** //
  1458.  
  1459. $prev_btn.on( 'click', function(event) {
  1460. e = $.Event("keydown");
  1461. $playing.length ? e.key = 'ArrowUp' : e.key = 'ArrowLeft';
  1462. $dir_table.trigger(e);
  1463. });
  1464.  
  1465. $next_btn.on( 'click', function(event) {
  1466. e = $.Event("keydown");
  1467. $playing.length ? e.key = 'ArrowDown' : e.key = 'ArrowRight';
  1468. $dir_table.trigger(e);
  1469. });
  1470.  
  1471. // Zoom Images
  1472. $content_image.find('img').on('click',function(e) {
  1473. $this_link = $(this).attr('src');
  1474. var $offset = $(this).offset();
  1475. var $this_width = $(this).width();
  1476. var $this_height = $(this).height();
  1477.  
  1478. $(this).toggleClass('zoom_img');
  1479.  
  1480. getDimensions( $this_link, function( width, height ) {
  1481. $content_image.scrollLeft( (e.pageX - $offset.left) * width/($this_width + 13) - ( e.pageX - $offset.left ) - 52 ) ;
  1482. $content_image.scrollTop( (e.pageY - $offset.top) * height/($this_height + 13) - ( e.pageY - $offset.top ) - 52 );
  1483. });
  1484.  
  1485. });
  1486.  
  1487. // ***** GRIDS ***** //
  1488.  
  1489. var $font_family;
  1490.  
  1491. var imageGridItems = function() {
  1492. var $image_grid_items_arr = [];
  1493. $dir_table_row.filter('.img').each(function() {
  1494.  
  1495. $this_link = thisLink(this);
  1496. $this_ext = $this_link.toLowerCase().slice($this_link.lastIndexOf('.'));
  1497.  
  1498. if ( $.inArray( $this_ext, $image_ext_arr ) != -1 ) { // if this row file ext is in the image extension array
  1499. $image_grid_item_el.find('a').attr('href',$this_link).find('img').attr('src',$this_link);
  1500. $image_grid_items_arr.push( $image_grid_item_el.clone() );
  1501. }
  1502. });
  1503. return $image_grid_items_arr;
  1504. };
  1505.  
  1506. var fontGridItems = function() {
  1507. var $font_grid_items_arr = [];
  1508. $dir_table_row.filter('.font').each(function() {
  1509.  
  1510. $this_link = thisLink(this);
  1511. $font_family = $(this).find('.name').text();
  1512.  
  1513. addCustomStyle( $font_family, $this_link );
  1514.  
  1515. $font_grid_item_el.attr('href',$this_link).css({ 'font-family':'"'+ $font_family +'"' }).empty().append( $font_family.slice( 0,$font_family.lastIndexOf('.') ) );
  1516. $font_grid_items_arr.push($font_grid_item_el.clone());
  1517. });
  1518. return $font_grid_items_arr;
  1519. };
  1520.  
  1521. // Grid Button Click
  1522. function showGrid() {
  1523. $content_pane.removeClass('has_hidden_grid').addClass('has_grid_content');
  1524.  
  1525. if ( $body.hasClass('has_images') && ( $body.hasClass('has_fonts') ) ) {
  1526. $content_grid.empty().append( imageGridItems() ).append( fontGridItems() );
  1527. $content_grid.removeClass().addClass('has_grid');
  1528. } else if ( $body.hasClass('has_images') ) {
  1529. $content_grid.empty().append( imageGridItems() );
  1530. $content_grid.removeClass().addClass('has_image_grid');
  1531. } else {
  1532. $content_grid.empty().append( fontGridItems() );
  1533. $content_grid.removeClass().addClass('has_font_grid');
  1534. }
  1535. setContentTitle();
  1536. setContentHeight();
  1537. }
  1538. $grid_btn.on('click', showGrid );
  1539.  
  1540. $('#show_image_grid').on('click',function(e) {
  1541. e.stopPropagation();
  1542. $content_pane.removeClass('has_hidden_grid').addClass('has_grid_content');
  1543. $content_grid.empty().append( imageGridItems() );
  1544. $content_grid.removeClass().addClass('has_image_grid');
  1545. setContentTitle();
  1546. setContentHeight();
  1547. });
  1548.  
  1549. $('#show_font_grid').on('click',function(e) {
  1550. e.stopPropagation();
  1551. $content_pane.removeClass('has_hidden_grid').addClass('has_grid_content');
  1552. $content_grid.empty().append( fontGridItems() );
  1553. $content_grid.removeClass().addClass('has_font_grid');
  1554. setContentTitle();
  1555. setContentHeight();
  1556. });
  1557.  
  1558. // GRID ITEMS
  1559.  
  1560. // Grid Item Hover
  1561. $content_grid.on('mouseenter','> div:not(".selected")',function() {
  1562. thisRow($dir_table.find('a[href="' + thisLink(this) + '"]')).addClass('hovered');
  1563. }).on('mouseleave','> div:not(".selected")',function() {
  1564. thisRow($dir_table.find('a[href="' + thisLink(this) + '"]')).removeClass('hovered');
  1565. });
  1566.  
  1567. // Grid Item Click
  1568. $content_grid.on('click','> div[class*="item"]',function(e) {
  1569. e.preventDefault();
  1570. $dir_table.find('a[href="' + thisLink(this) + '"]').click();
  1571. });
  1572.  
  1573. // ***** FONT PREVIEWS ***** //
  1574.  
  1575. var fontSize = function(el) {
  1576. return parseFloat(el.css('font-size'));
  1577. };
  1578.  
  1579. function setFontSize(el,x) {
  1580. el.css({'font-size':( fontSize(el)/$em * x ) +'em'});
  1581. }
  1582.  
  1583. var $em = parseInt(getComputedStyle(document.body).fontSize); // pts/em
  1584. var $factor = 1.125;
  1585.  
  1586. // Scale Image Grid Items
  1587. var $image_grid_item_size;
  1588. var $grid_scale_factor;
  1589.  
  1590. function setImageSize(x) {
  1591. $image_grid_item_size = parseInt($('.image_grid_item').width()) + x;
  1592. $grid_scale_factor = parseInt($content_grid.attr('data-grid-scale-factor'));
  1593. $content_grid.attr('data-grid-scale-factor',parseInt($grid_scale_factor) + x).css({'grid-template-columns':'repeat(auto-fit, minmax('+ ($image_grid_item_size) +'px, 1fr))', 'grid-template-rows':'repeat(auto, minmax('+ ($image_grid_item_size) +'px))' });
  1594. $content_grid.find('.image_grid_item').css({'width':$image_grid_item_size +'px', 'height':$image_grid_item_size +'px'});
  1595. $content_grid.find('img').css({'max-width':($image_grid_item_size - $image_grid_item_size/8) +'px', 'max-height':($image_grid_item_size - $image_grid_item_size/8) +'px'});
  1596. }
  1597.  
  1598. function enlargeGridItems() {
  1599. if ( $content_pane.hasClass('has_grid_content') && $content_grid.hasClass('has_font_grid') || $content_pane.hasClass('has_grid_content') && $content_grid.hasClass('has_grid') ) {
  1600. setFontSize($content_grid,$factor);
  1601. setImageSize(25);
  1602. return;
  1603. }
  1604.  
  1605. if ( $content_pane.attr('class').match('/^(^has_hidden_grid|^has_font_content)/') ) { // = has_hidden_grid && has_font_content
  1606. setFontSize($content_font,$factor);
  1607. return;
  1608. }
  1609. if ( $content_pane.hasClass('has_font_content') ) {
  1610. setFontSize($content_font,$factor);
  1611. return;
  1612. }
  1613. if ( $content_pane.attr('class').match('/^(^has_grid_content|^has_image_grid)/') ) { // = has_grid_content && has_image_grid
  1614. setImageSize(25);
  1615. return;
  1616. }
  1617. }
  1618.  
  1619. function reduceGridItems() {
  1620. // "/^(^A|^B)/"
  1621.  
  1622. if ( $content_pane.hasClass('has_grid_content') && $content_grid.hasClass('has_font_grid') || $content_pane.hasClass('has_grid_content') && $content_grid.hasClass('has_grid') ) {
  1623. setFontSize($content_grid,1/$factor);
  1624. setImageSize(-25);
  1625. return;
  1626. }
  1627. if ( $content_pane.attr('class').match('/^(^has_hidden_grid|^has_font_content)/') ) { // = has_hidden_grid && has_font_content
  1628. setFontSize($content_font,1/$factor);
  1629. return;
  1630. }
  1631. if ( $content_pane.attr('class').match('has_font_content') ) {
  1632. setFontSize($content_font,1/$factor);
  1633. return;
  1634. }
  1635. if ( $content_pane.attr('class').match('/^(^has_grid_content|^has_image_grid)/') ) { // = has_grid_content && has_image_grid
  1636. setImageSize(-25);
  1637. return;
  1638. }
  1639. }
  1640.  
  1641. $('#font_size').on('click','span',function() {
  1642. if ( $(this).attr('id') === 'increase' ) {
  1643. enlargeGridItems();
  1644. }
  1645. if ( $(this).attr('id') === 'decrease' ) {
  1646. reduceGridItems();
  1647. }
  1648. });
  1649.  
  1650. // ***** END FONT PREVIEWS ***** //
  1651.  
  1652. // ***** AUDIO CONTENT ***** //
  1653.  
  1654. var $count = 0;
  1655. var $playlist;
  1656. var $shuffleList = [];
  1657. var $reset = [];
  1658. var $next;
  1659. var $task;
  1660.  
  1661. function updatePlaylist() {
  1662. $playlist = [];
  1663. $dir_table_row.filter('.audio:not(".unchecked")').each(function() {
  1664. $this_link = thisLink(this);
  1665. $playlist.push($this_link);
  1666. });
  1667. return $playlist;
  1668. }
  1669.  
  1670. function initShuffleList() {
  1671. updatePlaylist();
  1672. $shuffleList = [];
  1673. for ( i = 0; i < $playlist.length; i += 1 ) {
  1674. $shuffleList.push(i);
  1675. }
  1676. return $shuffleList;
  1677. }
  1678.  
  1679. function updateCount() {
  1680. $count = thisRow($playing).index('.audio:not(.unchecked)');
  1681. }
  1682.  
  1683. // Check/uncheck individual audio files
  1684. $dir_table_row.filter('.audio').on('click','input',function(e) {
  1685. e.stopPropagation();
  1686. $(this).blur();
  1687. thisRow(this).toggleClass('unchecked');
  1688. updateCount();
  1689. updatePlaylist();
  1690. initShuffleList();
  1691. });
  1692.  
  1693. // Check/uncheck all audio files
  1694. $dir_table.find('#play_toggle').on('click',function(e) {
  1695. e.stopPropagation();
  1696. $dir_table_row.find('input').trigger('click');
  1697. updatePlaylist();
  1698. initShuffleList();
  1699. });
  1700.  
  1701. function loadAudio( count ) {
  1702. updatePlaylist();
  1703. $audio.find('source').attr('src', $playlist[count] );
  1704. $audio.trigger('load');
  1705. selectThis( thisRow( $dir_table.find('a[href="' + $playlist[count] + '"]') ) );
  1706. setContentTitle();
  1707. }
  1708.  
  1709. // Trigger play or stop based on task
  1710. function playAudio( task ) {
  1711. if ( task == 'play' ) {
  1712. $audio.trigger('play');
  1713. }
  1714. if ( task == 'stop' ) {
  1715. $audio.trigger('pause');
  1716. $audio.prop('currentTime',0);
  1717. }
  1718. }
  1719.  
  1720. function playThis( count, task ) {
  1721. loadAudio(count);
  1722. playAudio(task);
  1723. }
  1724.  
  1725. // Toggle play/pause
  1726. function playPause() {
  1727. $audio.get(0).paused ? $audio.get(0).play() : $audio.get(0).pause();
  1728. }
  1729.  
  1730. var $unchecked;
  1731. var $playable;
  1732.  
  1733. function nextTrack() {
  1734. updatePlaylist();
  1735. if ( $shuffle.find('input').prop('checked') ) { // shuffle
  1736. shuffle();
  1737. } else if ( $count === -1 ) { // if playing is unchecked
  1738. $playing.next('.audio:not(.unchecked)').length === 1 ? $count = $playing.next('.audio:not(".unchecked")').index('.audio:not(".unchecked")') : $count = 0;
  1739. $task = 'play';
  1740. } else if ( $count === $playlist.length - 1) { // loop to beginning and stop
  1741. $count = 0;
  1742. if ( $loop.find('input').prop('checked') === false ) {
  1743. $task = 'stop';
  1744. } else {
  1745. $task = 'play';
  1746. }
  1747. } else if ( $count < $playlist.length - 1 ) { // play next track
  1748. $count += 1;
  1749. $task = 'play';
  1750. }
  1751. playThis($count, $task);
  1752. }
  1753.  
  1754. function prevTrack() {
  1755. updatePlaylist();
  1756. if ( $shuffle.find('input').prop('checked') ) { // shuffle
  1757. shuffle();
  1758. } else if ( $count === -1 ) { // if playing is unchecked
  1759. $playing.prev('.audio:not(.unchecked)').length === 1 ? $count = $playing.prev('.audio:not(.unchecked)').index('.audio:not(.unchecked)') : $count = $playlist.length - 1;
  1760. } else if ( $count === 0 ) { // loop to end and play
  1761. $count = ($playlist.length - 1);
  1762. $task = 'play';
  1763. } else if ( !$playing.prevAll('.audio:not(.unchecked)').length ) {
  1764. $task = 'stop';
  1765. } else if ( $count > 0 ) { // play prev track
  1766. $count -= 1;
  1767. $task = 'play';
  1768. }
  1769. playThis($count,$task);
  1770. }
  1771.  
  1772. $prev_track.on( 'click', prevTrack );
  1773. $next_track.on( 'click', nextTrack );
  1774.  
  1775. $shuffle.on('click',function() {
  1776. if ( $shuffle.find('input').prop('checked') ) {
  1777. updatePlaylist();
  1778. initShuffleList();
  1779. $count = Math.floor( Math.random() * $shuffleList.length );
  1780. playThis($count,'stop');
  1781. }
  1782. });
  1783.  
  1784. function shuffle() {
  1785. if ( $shuffleList.length <= 1 && $loop.find('input').prop('checked') ) { // If loop is checked, and all songs have been shuffled...
  1786. initShuffleList();
  1787. $next = Math.floor( Math.random() * $shuffleList.length ) ; // choose a random index number from the remaining unplayed songs,
  1788. $count = $shuffleList[ $next ]; // set the playing $count to the index
  1789. $task = 'play';
  1790. } else if ( $shuffleList.length === 0 ) { // else if all songs have been shuffled, select the first song and stop;
  1791. $count = 0;
  1792. $task = 'stop';
  1793. } else { // else
  1794. $next = Math.floor( Math.random() * $shuffleList.length ) ; // choose a random index number from the remaining unplayed songs,
  1795. $count = $shuffleList[ $next ]; // set the playing $count to the index
  1796. $shuffleList.splice( $next, 1 ); // remove the indexed value from the unplayed list,
  1797. $task = 'play';
  1798. }
  1799. }
  1800.  
  1801. // Audio setup
  1802. function initAudio() {
  1803. if ( $body.hasClass('has_audio') ) {
  1804. loadAudio(0);
  1805. autoLoadCoverArt();
  1806. // If autoplay, play file
  1807. if ( $settings.autoplay == true ) {
  1808. $audio.trigger('play');
  1809. }
  1810. // Play next track
  1811. $audio.on('ended', nextTrack );
  1812. }
  1813. }
  1814. initAudio();
  1815.  
  1816. // Autoload cover art for audio files
  1817. function autoLoadCoverArt() {
  1818. var $matched;
  1819. var $image_titles = [];
  1820. $dir_table_row.filter('.img').each(function() {
  1821. $image_titles.push( thisText(this).toLowerCase() );
  1822. });
  1823. // Find image file with name matching default cover art name
  1824. for ( i = 0; i < $image_titles.length; i++) {
  1825. if ( $image_titles[i].match(/(cover\.|front\.)/) ) {
  1826. $matched = $(document.getElementById('dir_table').getElementsByClassName('img').item(i));
  1827. $matched.addClass('selected');
  1828. showImage( $matched, thisLink( $matched ) );
  1829. return true;
  1830. }
  1831. }
  1832. // If no default cover art found, select first image in dir, if any.
  1833. if ( $('.selected').length < 1 && $dir_table_row.filter('.img').length > 0 ) {
  1834. $matched = $(document.getElementById('dir_table').getElementsByClassName('img').item(0));
  1835. $matched.addClass('selected').siblings().removeClass('selected');
  1836. showImage( $matched, thisLink( $matched ) );
  1837. }
  1838. }
  1839.  
  1840. // ***** END AUDIO CONTENT ***** //
  1841.  
  1842. // OTHER FUNCTIONS
  1843.  
  1844. // Resize Sidebar/Content Pane
  1845. $handle.on('mousedown',function(f) {
  1846. f.stopPropagation();
  1847. var $startX = f.pageX;
  1848. var $sidebar_width = $sidebar_wrapper.width();
  1849. var $window_width = window.innerWidth;
  1850. $content_overlay.show(); // needed to prevent interactions with iframe
  1851. $sidebar_wrapper.css({'-webkit-user-select':'none','-moz-user-select':'none','user-select':'none'});
  1852.  
  1853. $(document).on('mousemove',function(e) {
  1854. e.stopPropagation();
  1855. var $deltaX = e.pageX - $startX;
  1856. if ( e.pageX > 200 && e.pageX < $window_width - 200 ) {
  1857. $sidebar_wrapper.css({'width':$sidebar_width + $deltaX + 'px'});
  1858. $content_pane.css({'width':($window_width - $sidebar_width) - $deltaX + 'px'});
  1859. }
  1860. setContentHeight();
  1861. });
  1862. $(document).on('mouseup',function() {
  1863. $content_overlay.hide();
  1864. $sidebar_wrapper.css({'-webkit-user-select':'auto','-moz-user-select':'auto','user-select':'auto'});
  1865. $(document).off('mousemove');
  1866. });
  1867. });
  1868.  
  1869.  
  1870. // Export Settings
  1871. var $settings_string = unescape( $settings.toSource() );
  1872. $settings_string = $settings_string.replace('JSON.parse\(unescape\("\{','').replace('\}"))','').replace(/\/",/g,'",').replace(/\/"],/g,'"],').replace(/"(\w+?)":/g,'\n\n$1:\t');
  1873.  
  1874. var save = function(filename, data) {
  1875. var blob = new Blob([data], {type: 'text/html'});
  1876. if(window.navigator.msSaveOrOpenBlob) {
  1877. window.navigator.msSaveBlob(blob, filename);
  1878. }
  1879. else{
  1880. var elem = window.document.createElement('a');
  1881. elem.href = window.URL.createObjectURL(blob);
  1882. elem.download = filename;
  1883. document.body.appendChild(elem);
  1884. elem.click();
  1885. document.body.removeChild(elem);
  1886. URL.revokeObjectURL(blob);
  1887. }
  1888. };
  1889.  
  1890. $export_settings.on('click','a',function(e) {
  1891. e.preventDefault();
  1892. save("settings.txt",$settings_string);
  1893. });
  1894.  
  1895.  
  1896. })();