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.

当前为 2018-09-30 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Supercharged Local Directory File Browser
  3. // @version 2.7.0
  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 (Michael Schrauzer) mshroud@gmail.com
  6. // @license GPL-3.0-or-later
  7. // @homepageURL https://openuserjs.org/scripts/gaspar_schot/Supercharged_Local_Directory_File_Browser
  8. // @contributionURL https://paypal.me/mschrauzer
  9. // @include file://*/
  10. // @include file://*/?*
  11. // @include *!localhost*/
  12. // @require http://code.jquery.com/jquery-latest.min.js
  13.  
  14. // NOTE: This script was developed in Vivaldi, running on Mac OS High Sierra. It has been tested in various Chrome and Gecko-based browsers.
  15. // It has been minimally tested on Windows and not at all on other OSes. It should work, but please report any issues.
  16. // The script does not work in Safari because Safari does not allow local directories to be browsed.
  17.  
  18. // 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.
  19. // For Tampermonkey, go to Chrome extension page, and tick the 'Allow access to file URLs' checkbox at the Tampermonkey extension section.
  20. // For Greasemonkey, open about:config and change greasemonkey.fileIsGreaseable to true.
  21.  
  22. // @namespace https://greasyfork.org/users/16170
  23. // ==/UserScript==
  24.  
  25. (function() {
  26. 'use strict';
  27. var $ = jQuery;
  28.  
  29. // ***** USER SETTINGS ***** //
  30.  
  31. const $settings = {
  32.  
  33. // Paste your exported settings between the two lines below:
  34. //--------------------------------------------------------//
  35.  
  36. shortcuts: // N.B.: Directory links must end with "/", file links must not.
  37. // You may add as many menus and links as you like. Just copy the curly-bracketed example and edit.
  38. [
  39. {
  40. "menu_title":"My Sample Menu",
  41. "links":
  42. [
  43. { "link_name":"My Directory Link 1", "link":"file:///Path/To/My/Directory/" },
  44. { "link_name":"My Directory Link 2", "link":"file:///Path/To/My/Directory_2/" },
  45. { "link_name":"My Directory Link 3", "link":"file:///Path/To/My/Directory_3/" },
  46. { "link_name":"My File Link 1", "link":"file:///Path/To/My/File.ext" },
  47. ]
  48. },
  49. ],
  50.  
  51. 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;
  52. // 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).
  53. true,
  54. hide_ignored_files: // If true, ignored files will be completely hidden from the file list;
  55. // If false (default), ignored files will appear greyed-out.
  56. false,
  57. hide_invisibles: // Un*x/Mac OS only: If true (default), files or directories beginning with a "." will be hidden.
  58. true,
  59. hide_details: // If true (default), hide file and directory details; if false, show them.
  60. true,
  61. default_sort: // Choose from: 'name', 'size', 'date', 'kind', 'ext', 'default'.
  62. // default = Chrome sorting: dirs on top, files alphabetical.
  63. 'default',
  64. dirs_on_top: // If true, directories will always be listed firs except when sorting by "name" (since otherwise sorting by "name" would equal "default").
  65. // If false (default), directories and files will be sorted together. (In practice, dirs will typically still be separated when sorting by size, kind, and extension.)
  66. false,
  67. apps_as_dirs: // Un*x/Mac OS only: if true, treat apps as directories; allows app contents to be browsed. This is the default behavior for Chrome.
  68. // If false (default), treat apps as ignored files.
  69. false,
  70. dark_theme: // If true (default: false), gives the content pane a dark background, and inverts html and text content.
  71. false,
  72. grid_image_size: // Default = 150
  73. 150,
  74. grid_font_size: // Default = 1
  75. 1,
  76. autoload_index_files: // If true (default: false), automatically select first "index.ext" file found in directory.
  77. false,
  78. autoload_audio: // If true, the first audio file found in a directory will be automatically selected and loaded for playback.
  79. // If true, use left/right arrows to select audio files, use up/down arrows to select non-audio files (including video) only.
  80. // If false (default), audio files will be treated like any other file for navigation purposes (i.e., up and down arrows will select them too).
  81. // Note: if false, using up/down arrows will automatically begin playback of selected audio files; left/right arrows will select audio files without playing them.
  82. false,
  83. use_custom_icons: // if true (default), use custom icons for dirs and files
  84. // if false, use browser/server default icons
  85. true
  86.  
  87. //--------------------------------------------------------//
  88. // Paste your exported settings between the above two lines.
  89.  
  90. };
  91.  
  92. // ***** END USER SETTINGS ***** //
  93.  
  94. // KEYBINDINGS:
  95. // Arrow Up/Down: Navigate up and down directory items
  96. // Cmd/Ctrl + Arrow Up: Go to parent directory
  97. // Arrow Left/Right: Navigate to prev/next image, font, or audio file, skipping other files, when files of these types are selected.
  98. // Opt/Alt + Arrow Left/Right: Skip audio ±10s
  99. // Shift + Opt/Alt + Arrow Left/Right: Skip audio ±30s
  100. // Arrow Right: Open selected directory.
  101. // Space: Pause/Play audio
  102. // Return: Open selected directory.
  103. // Cmd/Ctrl + D: Toggle file details (size, date modified) in some index page types.
  104. // Cmd/Ctrl + G: Show or Reset Grids
  105. // Cmd/Ctrl + I: Toggle Invisibles
  106. // Cmd/Ctrl + Shift + O: Open selected item in new window/tab
  107. // Cmd/Ctrl + R: Reload previewed content, reset scaled images/fonts.
  108. // Cmd/Ctrl + W: Close previewed content (not available in Firefox), or close window if no content is being previewed.
  109. // Cmd/Ctrl + Shift + </>: Scale preview items and grids.
  110.  
  111. //• Fixed: Ignored files weren't being sorted properly.
  112.  
  113.  
  114. // CHANGELOG HISTORY:
  115.  
  116. //• 2.7.0: New features and user settings. Please export your user_settings before updating and note the changes mentioned below.
  117. //• NEW (and experimental): Display a standard index of the selected sidebar directory in the preview pane.
  118. // This is very handy for browsing directories from the sidebar.
  119. // Clicking files and directories directly in the preview pane works as you'd expect, but it can be a little weird,
  120. // since you can navigate up and down the directory structure in the preview pane independently from the sidebar.
  121. //• NEW: Added URL query strings to remember directory selection history and various temporary UI settings between directory changes:
  122. // sort order, show/hide details, default/dark mode, sidebar width, and previously selected directory.
  123. //• NEW: Navigate to files and directories by typed string.
  124. //• Improved: Converted user shortcut settings to JSON for greater flexibility.
  125. // Now you can create and name your own shortcut menu categories.
  126. // IMPORTANT: You must re-enter your old shortcuts in the new settings format.
  127. //• Added: Menu item to reset/delete query strings and return to the default user_settings in the script.
  128. //• Added: New user setting to show/hide details by default. (Don't know why it took so long to add this.)
  129. //• Added: User customizable file kinds for sorting.
  130. // You may add or remove file kind categories and file extensions to suit your own files:
  131. // See "$row_types" in code below, at the beginning of "General Setup" and just after the Changelog, to add your own.
  132. //• Removed "ignore_file_types" from the user_settings, because the list of ignored file extensions has been moved:
  133. // See "$row_settings" in code below, at the beginning of "General Setup" and just after the Changelog, to add your own.
  134. //• Removed "user_name" from the user_settings, because it is no longer needed.
  135. //• Improved: File shortcuts work much more reliably now, and thanks to query strings, you can now bookmark more than one file in a directory.
  136. //• Improved: Various small UI changes.
  137. //• Fixed: Toggle sidebar wasn't working.
  138. //• Fixed: Ignored files weren't being sorted.
  139. //• Internals: Abstracted a bunch of functions, removed some unneeded variables and functions. More to be done.
  140. //• If you like this script, please consider making a donation. See the "Donate" menu item. Thanks!
  141.  
  142. //• 2.6.5
  143. //• Fixed: First and last audio files would repeat when ended instead of going to next track in some circumstances.
  144. //• Fixed: Various issues with file navigation.
  145.  
  146. //• 2.6.4
  147. //• Added: Nice custom icons for dirs and various file types. Other minor UI tweaks.
  148. //• Added: New user setting to use default file icons instead the new custom ones, in case you don't like them.
  149. //• Added: Separate close button for audio player, since there was no way to close it while leaving other previewed content open.
  150. //• Fixed: Really, really fixed reload button and reload shortcut not working (I hope).
  151. //• Fixed: Yet more issues with arrow navigation.
  152. //• Fixed: Various UI issues with Firefox.
  153. //• Improved: Sidebar resizing should be more reliable (drag event was interacting with sidebar).
  154. //• Other: Abstracted some styles and cleaned up code.
  155. //• Many other bug fixes.
  156.  
  157. //• 2.6.3: Minor UI improvements and bugfixes.
  158. //• Changed: Open dirs on double-click. (You can still open them by selecting and pressing "Return" or the Right Arrow Key.)
  159. //• Changed: Removed autoplay_audio preference.
  160. //• Fixed: Reload button and reload shortcut weren't working correctly.
  161. //• Fixed: Clicking audio file name didn't pause/play file.
  162. //• Fixed: Sidebar height didn't reset when changing sort order.
  163. //• Fixed: Directories with a "." in the name were being sorted as if the trailing text was an extension.
  164. //• Fixed: Corrected some code that prevented the script from working in Safari; while Safari still doesn't allow local directories to be browsed, the script will work on server-generated pages.
  165. //• Other: Removed some unneeded styles and functions, simplified arrow navigation code.
  166.  
  167. //• 2.6.2: Fixes for a few more pesky bugs. Just when you think you're safe.... Apologies.
  168. //• Fixed: Reload and Close buttons and keyboard combinations now work for audio files.
  169. //• Fixed: Preview pane titlebar wasn't showing in some circumstances.
  170.  
  171. //• 2.6.1: Hopefully fixed a bug when navigating folders containing audio files. Sorry about that!
  172.  
  173. //• 2.6.0: Sorting, new user settings, and much more! This is a pretty substantial update.
  174. //• Added: Donate link (paypal.me/mschrauzer) to the main shortcuts menu. If you like this script, please support its development. Thank you!
  175. //• Added: Support for additional common server-generated index types. The script should now work on index pages structured as unordered lists, "<pre>" text, and html tables.
  176. //• Added: Sorting by name, size, date modified, kind, file extension, and default Chrome sorting (i.e., dirs on top, files alphabetical).
  177. //• Click the "Details" button (or Cmd/Ctrl + D) to show the available sort options.
  178. //• Note: Some server configurations don't provide size or date information; on such sites, these sorting options will not be available.
  179. //• Note: Sorting by kind is based on the file extension, so it is rather coarse (at least for now); many non-image/audio/pdf files will simply be categorized as generic "Files".
  180. //• Added: Added "Sort by..." menu item in the main shortcuts menu.
  181. //• Added: New User Settings (gives you control over some behavior added in the last update):
  182. //• default_sort: Choose default sort method: by name, size, date, kind, extension, or Chrome default (dirs on top, files alphabetical).
  183. //• dirs_on_top: Choose whether directories should be sorted separately from files.
  184. //• autoload_index_files: If true (default false), autoload first index.[ext] file found in directory.
  185. //• autoload_audio:
  186. //• If false (default), treat audio files like other files for navigation purposes. Use up and down arrows as normal, only show audio controls when an audio file is selected.
  187. //• (Note that when false, only one file in the directory at a time can be selected.)
  188. //• If true, autoload first audio file in directory (and cover art, if any) and navigate audio with left and right arrows, other files with up and down arrows. This was the previous behavior of the script.
  189. //• Added: Alert reminder to add your computer user account name to the settings.
  190. //• Enhancement: UI: Added flyout submenus to main shortcuts menu.
  191. //• Enhancement: Added .aiff, .ape, .m4a to recognized audio format types.
  192. //• Enhancement: Added .svg to recognized image format types (they now are shown in an <image> element instead of an <iframe>).
  193. //• Enhancement: Moved the user settings to top of the code for easier access.
  194. //• Changed: Since the browser can't tell if an mp4 file is video or audio only, all mp4 files will load in the main preview area, not the audio player.
  195. //• Fixed an issue with image zoom.
  196. //• Fixed an issue with previewed content not being removed when content of another type was previewed.
  197. //• Internals: The script now converts server-generated index pages to the default Chrome table structure. Conversion happens in memory, so performance should be improved.
  198. //• Todo: Better video file handling.
  199.  
  200. // 2.5.0
  201. // A whole bunch of small and not-so-small additions, changes, bug fixes, and enhancements.
  202. // Added: Automatically load /index.[ext] files in the preview pane, if one exists in the directory.
  203. // Added: Initial support for AMMPS index pages; added "localhost" to @include rules.
  204. // Added: New user setting for external (non-local) shortcuts. Don't delete this item when pasting in your exported settings.
  205. // Added: Show/Hide sidebar. Hidden sidebar can still be navigated by the arrow keys.
  206. // Added: (Shift +) Alt/Option + Left/Right Arrows to skip audio ±10 or 30 secs.
  207. // Added: Also use the Prev and Next arrows to navigate font previews.
  208. // Added: Checkboxes for video files. Video file handling is still a bit of mess when audio files are also present in the directory; I will address this in a future update.
  209. // Added: Images can now be scaled (instead of just zoomed by clicking) with the +/– buttons and the scale keybindings.
  210. // Improved: Better scaling overall for images, fonts, and grids (use geometric instead of arithmetic scaling -- duh!).
  211. // Improved: Auto-file selection from user file shortcut list. First file found is loaded; user file shortcuts take precedence over autoloading index files.
  212. // Enhancement: Reload button also resets size of scaled images, fonts, and grids.
  213. // Fixed: Some lingering issues with arrow navigation and audio files selection.
  214. // Please read the updated list of keybindings in the description.
  215.  
  216. // 2.4.2
  217. // More audio file enhancements:
  218. // Added: Checkboxes to play or skip specific audio files. (Such a seemingly small thing, such a pain to implement.)
  219. // Added: Start/stop play by clicking file name.
  220. // Added: Use Reload button and reload keybinding (Cmd/Ctr-R) to return audio to beginning of track.
  221. // Changed: Don't autoplay audio when using left and right arrows to navigate tracks.
  222. // Fixed: Various issues with shuffle play.
  223. // Fixed: Completely rewrote autoload cover art code so that it actually works.
  224. // Fixed: Ignored files weren't being ignored in list-based index pages.
  225. // Many other bug fixes.
  226. // Minor UI adjustments (e.g., better prev and next track buttons).
  227.  
  228. // 2.4.1
  229. // Added: Loop and shuffle playback for audio files.
  230. // Added: Wrap-around keyboard navigation for audio tracks.
  231. // Added: Default background image for audio directories containing no image files.
  232. // Fixed some scrollIntoView issues.
  233. // Fixed auto-cover image selection issue in Firefox.
  234. // Bug-fixes. Yep, bug-fixes.
  235.  
  236. // 2.4.0
  237. // Major update for audio files!
  238. // New: Play all audio files in a directory.
  239. // 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.
  240. // 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.)
  241. // 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.
  242. // Some code cleanup and bugfixes.
  243.  
  244. // 2.3.0
  245. // Added: New user settings: default grid image and font sizes. Don't delete them when pasting in your old exported settings!
  246. // Added: Scale grid image items.
  247. // Improved: Better image zooming. Zoomed images now scroll to the area clicked.
  248. // Fixed: Keybinding to toggle invisibles wasn't working.
  249. // Fixed: Image zoom not working.
  250. // Fixed: Hovered background for grid items in dark mode.
  251. // Fixed: Font scaling in dual grids.
  252.  
  253. // 2.2.0
  254. // 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.
  255. // Fixed: File extensions were case sensitive, so some valid files wouldn't load if the extension contained capital letters.
  256. // Fixed: File and Dir names were also being lowercased.
  257.  
  258. // v. 2.1.1
  259. // Fixed: Can't type "r" in font previews.
  260. // Other minor bug fixes.
  261.  
  262. // v. 2.1.0
  263. // Added: Toggle Default/Dark modes menu item while browsing current directory; mode reverts to saved setting on dir change.
  264. // Added: White background for images so that images with transparency are visible against the grey background
  265. // Removed trailing "/" from directory display names.
  266. // Fixed an issue where font and image grid display got confused about what content to display.
  267. // Fixed directory names display in dark mode in server pages that use lists instead of tables.
  268. // Better styling for grid button.
  269. // Better styling for grid items in dark mode.
  270. // Bug fixes.
  271. // Remember to copy your user settings before updating!
  272.  
  273. // v. 2.0.2
  274. // Various fixes suggested by "Zorg":
  275. // 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.
  276. // Fixed display of non-Latin characters in sidebar header.
  277. // Add support for linux "home" directory for user profile shortcuts
  278. // Made entire up directory button clickable.
  279.  
  280. // v. 2.0.1
  281. // Fixed wonky font preview scaling.
  282.  
  283. // v. 2.0
  284. // 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.
  285. // 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.
  286. // 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.
  287. // Added: Basic dark mode user setting.
  288. // Added: Wrap-around keyboard navigation.
  289. // Added: Cmd/Ctr-D to toggle details.
  290. // Added: Cmd/Ctr-Shift-. and Cmd/Ctr-Shift-, to scale font previews text size.
  291. // Changed: Moved dynamically-added in-line styles to appended stylesheet.
  292. // Changed: Extensive refactoring of code for better separation of concerns. (More needs to be done.... Sigh.)
  293. // Removed: ScrollIntoView for Grid views. Scrolling didn't work reliably when the sidebar was also being scrolled. Will restore when bug fixed.
  294. // Many small bug fixes.
  295.  
  296. // v. 1.4
  297. // Added: Initial support for Firefox. Tested in Firefox 59, Waterfox 56.
  298. // Changed: Use SVG for menu icons
  299. // Changed: Code cleanup, reorganization
  300. // Renamed script to "Supercharged Local Directory File Browser"
  301.  
  302. // v. 1.3
  303. // Fixed: Keyboard navigation of ignored or invisible items fails if "Hide Invisibles" is toggled off after loading a directory.
  304. // Added: Also hide ignored files if "Hide Invisibles" is checked.
  305. // Added: Content reload button.
  306. // Changed: Reorganized settings to reduce confusion about how ignored files are treated.
  307.  
  308. // v. 1.2
  309. // Click to show menus instead of hover.
  310. // Added Cmd/Crl+Shift+O keybinding to open selected item in new window
  311. // Arrow navigation bugfixes
  312.  
  313. //TODO:
  314. // Update parents links with queries --> delete history/selected
  315. // Remove Gecko styles
  316. // Fix grid scrollIntoView
  317. // Preload next audio track to prevent gaps
  318.  
  319.  
  320. // ***** GENERAL SETUP ***** //
  321.  
  322. // $ROW_TYPES:
  323. // Add file extensions for sorting and custom icon display.
  324. // You can also add your own new row_type categories.
  325. // Note: no leading "." for extensions.
  326. // Note: Do not add an extension to more than one row_type category; only the last one will be used.
  327. const $row_types = {
  328. // myCategory: ['ext1','ext2',],
  329. dir: ['/'],
  330. app: ['app/','app','exe','msi'],
  331. archive: ['zip','rar','cbr','7z','tar','gz','dmg','pkg','archive'],
  332. audio: ['mp3','mp4','m4a','aac','aif','aiff','ape','flac','ogg','wav','webm'],
  333. font: ['otf','ttf','woff','woff2','afm','pfb','pfm','tfm'],
  334. graphics: ['indd','idml','indt','icml','ai','eps','pages','qxp','qxb','qxd','mif','sla','dtp','pmd','pub','fm','book','inx'],
  335. htm: ['htm','html','xhtm','xhtml'],
  336. image: ['jpg','jpeg','png','apng','gif','bmp','webp','svg','tif','tiff','psd','raw','dng','cr2','nef','arw'],
  337. office: ['doc','docx','epub','xls','xlsx','xlm','odt','odf','rtf'],
  338. pdf: ['pdf'],
  339. text: ['txt','log','md','nfo'],
  340. code: ['c','conf','css','h','ini','js','json','jsx','less','list','lock','php','plist','py','sass','strings','xml'],
  341. video: ['mpeg','mov','m4v','webm'],
  342. }
  343. // $ROW_SETTINGS
  344. const $row_settings = {
  345. // Files with these extensions will not be loaded if selected in the sidebar (prevents the browser from attempting to download the file).
  346. ignore: ['exe','doc','docx','xls','xlsx','odt','odp','csv','msi','dll','indd','idml','pages','tif','tiff','raw','dng','cr2','nef','arw','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','epub'],
  347. // Files with these exensions will not be inverted in dark mode
  348. exclude: ['htm','html','xhtm','xhtml','mp3','m4a','m4v','mp4','ogg','ogm','oga','webm','wav','mpeg','flac'],
  349. }
  350.  
  351. // ************************************ //
  352. // DON'T EDIT ANYTHING BELOW THIS LINE. //
  353. // ************************************ //
  354.  
  355. // Styles for iFrame Directory Index pages
  356. if ( window.frameElement !== null || window.top != window.self && window.location.href.endsWith('/') ) {
  357.  
  358. $('html').css({'margin':'0','padding':'0','width':'100%','max-width':'100%','height':'100%','background':'#CCCCCC','box-sizing':'border-box','font-size':'12px'});
  359. $('body').css({'width':'100%','max-width':'100%','min-width':'100%','height':'auto','margin':'0','padding':'0','background':'#CCCCCC','border':'0','border-radius':'0','box-sizing':'border-box','font-family':'lucidagrande,"fira sans",helvetica,sans-serif','font-size':'12px'});
  360. $('body > h1').css({'display':'none'});
  361. $('body > div').css({'margin':'1em','padding':'0','font-size':'1rem'});
  362. $('body p').css({'margin':'1rem','font-size':'1rem'});
  363. $('body > table').css({'width':'100%','border-collapse':'collapse','border-top':'solid 1px #888888','font-size':'1rem'});
  364. $('thead').css({'font-size':'1em'});
  365. $('thead th:first-of-type').css({'width':'50%','text-align':'left'});
  366. $('body table tr').css({'outline':'0'});
  367. $('body > table > tbody > tr > td:first-of-type, body > table > thead > tr > th:first-of-type, body > ul > li').css({'padding':'3px 0 3px 1em'})
  368. $('body > ul').css({'padding-left':'0','list-style':'none'})
  369. $('body > table > tbody > tr > td:last-of-type').css({'padding-right':'1em'})
  370. $('body > table > thead th').css({'padding-top':'6px'});
  371. $('body > table > tbody > tr, body > ul > li:not(:contains("Parent Directory"))').hover( function() {
  372. $(this).css({'background-color':'#ABABAB'});
  373. }, function() {
  374. $(this).css({'background-color':'transparent'});
  375. });
  376. $('a').css({'color':'#333','text-decoration':'none','display':'block'});
  377. $('address').remove();
  378. $('body > ul > li:contains("Parent Directory")').css({'font-weight':'bold','margin-bottom':'1em','padding':'3px 0 1em 1em','border-bottom':'solid 1px #888888'})
  379.  
  380. return;
  381. }
  382.  
  383. // Experimental: Don't run script in previewed contentEditable text and html files.
  384. if ( window.location.pathname.slice(-1) != '/') {
  385. // $('body').attr('contentEditable','true');
  386. return;
  387. }
  388.  
  389. const $userAgent = navigator.userAgent;
  390.  
  391. var $font_family_arr = [];
  392.  
  393. $('head').prepend('<meta charset="utf-8">');
  394.  
  395. const $body = $('body');
  396. $body.attr('lang','en').find('> h1:contains("Index of"),> #parentDirLinkBox,> #UI_goUp,#UI_showHidden').remove();
  397.  
  398. // PATHS
  399. var $location = decodeURIComponent(window.location);
  400. $location = $location.slice(0,$location.lastIndexOf('/?') + 1 ); // remove query string
  401. var $protocol = window.location.protocol;
  402. var $path_name = window.location.pathname;
  403. var $location_arr = $path_name.split('/');
  404.  
  405. var $current_dir_path = $protocol + $path_name.replace(/%20/g,' ').replace(/\//g,'/<wbr>').replace(/_/g,'_<wbr>').replace(/—/g,'—<wbr>').replace(/\\/g,'/');
  406. var $current_dir_name = $protocol + $path_name.replace(/%20/g,' ').slice(0,-1);
  407. $current_dir_name = $current_dir_name.slice($current_dir_name.lastIndexOf('/') + 1);
  408.  
  409. // URL QUERIES
  410. var getQueryPrefs = function() {
  411. return new URLSearchParams( window.location.search );
  412. }
  413. var $query_prefs = getQueryPrefs();
  414.  
  415. var $UI_pref_width = $query_prefs.get('width') == null ? '25' : (Math.round(100*($query_prefs.get('width'))/window.innerWidth)).toString(); // number string
  416. var $UI_pref_theme = $query_prefs.get('dark_theme') == null ? $settings.dark_theme : JSON.parse( $query_prefs.get('dark_theme') ); // bool
  417. var $UI_pref_details = $query_prefs.get('hide_details') == null ? $settings.hide_details : JSON.parse( $query_prefs.get('hide_details') );
  418. var $UI_pref_sort = $query_prefs.get('sort') == null ? $settings.default_sort.toLowerCase() : $query_prefs.get('sort');
  419. var $UI_pref_file = $query_prefs.get('file') == null ? '' : $query_prefs.get('file');
  420. var $UI_pref_selected = $query_prefs.get('selected') == null ? '' : JSON.parse( $query_prefs.get('selected') );
  421. var $UI_pref_history = $query_prefs.get('history') == null ? '' : $query_prefs.get('history');
  422. var $UI_pref_history_arr;
  423.  
  424. function toggleQuery(key) {
  425. var value;
  426. $query_prefs = getQueryPrefs();
  427. value = $query_prefs.has(key) ? JSON.parse( $query_prefs.get(key) ) : $settings[key];
  428. value === true ? $query_prefs.set( key, 'false' ) : $query_prefs.set( key, 'true' );
  429. updateQuery();
  430. }
  431.  
  432. function updateQuery() {
  433. $query_prefs = decodeURIComponent($query_prefs);
  434. window.history.replaceState({}, document.title, window.location.pathname +'?'+ $query_prefs);
  435. }
  436.  
  437. // Globals
  438. var e, i, j, n;
  439.  
  440. // ***** BUILD UI ELEMENTS ***** //
  441.  
  442. // ***** SIDEBAR ELEMENTS ***** //
  443. const $parent_dir_menu = $('<nav id="parent_dir_menu"><a href="">&nbsp;</a></nav>');
  444. const $parents_dir_menu = $('<nav id="parents_dir_menu"><div></div></nav><ul class="menu"></ul>');
  445. const $divider = $('<li><hr></li>');
  446. const $shortcuts_menu = $('<nav id="shortcuts_menu"><div>&nbsp;</div></nav><ul id="shortcuts" class="menu"></ul>');
  447. const $details_btn = $('<button id="details_btn" tabindex="-1"><span>Show details</span><span>Hide details</span></button>');
  448. const $inv_checkbox = $('<label ><input type="checkbox" id="inv_checkbox" for="inv_checkbox" name="inv_checkbox" tabindex="-1" />Hide Invisibles</label>');
  449. const $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>');
  450. const $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>');
  451. const $dir_list_head = $('<thead id="thead"><tr id="theader" class="header"><th id="name" class="name"><input id="play_toggle" type="checkbox" tabindex="-1" checked="true" />Name</th><th id="size" class="details">Size</th><th id="date" class="details">Date</th><th id="kind" class="details">Kind</th><th id="ext" class="details">Ext</th><th id="default" class="details">Default</th></tr></thead>');
  452. const $dir_list_body = $('<tbody id="tbody"></tbody>');
  453. const $dir_list = $('<table id="dir_list"></table>');
  454. const $dir_list_wrapper = $('<div id="dir_list_wrapper"></div>');
  455. const $sidebar = $('<div id="sidebar"></div>');
  456. const $handle = $('<div id="handle"></div>');
  457. const $sidebar_wrapper = $('<td id="sidebar_wrapper"></td>');
  458. const $toggle_sidebar = $('<div id="toggle_sidebar"></div>');
  459.  
  460. // ***** CONTENT PANE ELEMENTS ***** //
  461. const $sidebar_overlay = $('<div id="sidebar_overlay""></div>');
  462. const $content_overlay = $('<div id="content_overlay""></div>');
  463. const $content_grid = $('<div id="content_grid" data-grid-scale-factor="0"></div>');
  464. const $image_grid_item_el = $('<div class="image_grid_item"><a href=""><img src="/" /></a></div>');
  465. const $font_grid_item_el = $('<div class="font_grid_item"></div>');
  466. const $content_scale = $('<div id="scale"><span id="increase"></span><span id="decrease"></span></div>');
  467. const $sample_string = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ<br />abcdefghijklmnopqrstuvwxyz<br />0123456789 [(!@#$%^&*;:)]';
  468. const $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.';
  469. const $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>');
  470. const $content_font = $('<div id="content_font" spellcheck="false" contenteditable="true"></div>');
  471. const $image = $('<img src="/" class="" />');
  472. const $content_image = $('<div id="content_image" data-image-scale-factor="1"></div>');
  473. const $content_embed = $('<embed id="content_embed" name="plugin" type="application/pdf" tabindex="0"></embed>');
  474. const $content_iframe = $('<iframe id="content_iframe" sandbox="allow-scripts allow-same-origin allow-modals" tabindex="0"></iframe>');
  475. const $content_media = $('<td id="content_media" colspan="3"></td>');
  476. const $prev_track = $('<div id="prev_track"></div>');
  477. const $next_track = $('<div id="next_track"></div>');
  478. const $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>');
  479. const $loop = $('<label><input type="checkbox" id="loop" for="loop" name="loop" tabindex="0" />Loop</label>');
  480. const $shuffle = $('<label><input type="checkbox" id="shuffle" for="shuffle" name="shuffle" tabindex="0" />Shuffle</label>');
  481. const $close_audio = $('<div id="close_audio"></div>');
  482. const $checkbox_cont = $('<div id="checkbox_div"></div>');
  483. const $content_reload_btn = $('<td><button id="reload_btn" tabindex="-1">Reload</button></td>');
  484. const $content_title = $('<td id="content_title"></td>');
  485. const $content_close_btn = $('<td><button id="close_btn" tabindex="-1">Close</button></td>');
  486. const $content_header = $('<header id="content_header"><table><tbody><tr></tr><tr></tr></tbody></table></header>');
  487. const $prev_btn = $('<div id="prev_btn"></div>');
  488. const $next_btn = $('<div id="next_btn"></div>');
  489. const $content_container = $('<section id="content_container"></section>');
  490. const $content_pane = $('<td id="content_pane"></td>');
  491.  
  492. // SVG UI ICONS
  493. const $svg_prefix = '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\' ';
  494. const $up_arrow = $svg_prefix + '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>")';
  495. const $up_arrow_inv = $svg_prefix + '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=\'%23CCCCCC\' d=\'M6.364,2.828l4.95,4.949l1.414-1.414L6.364,0l0,0L0,6.363l1.413,1.416L6.364,2.828\'/></svg>")';
  496. const $svg_arrow = $svg_prefix + '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>")';
  497. const $toggle = $svg_prefix + 'width=\'13.779px\' height=\'12.729px\' viewBox=\'2.474 -2.475 13.779 12.729\' enable-background=\'new 2.474 -2.475 13.779 12.729\' xml:space=\'preserve\'> <path fill=\'%23444444\' d=\'M5.302,3.889l4.949-4.95L8.838-2.475L2.474,3.889l0,0l6.363,6.364l1.416-1.413L5.302,3.889\'/> <path fill=\'%23444444\' d=\'M11.302,3.889l4.949-4.95l-1.414-1.414L8.474,3.889l0,0l6.363,6.364l1.416-1.413L11.302,3.889\'/> </svg>")';
  498. const $check_mark = $svg_prefix + 'width=\'17px\' height=\'14px\' viewBox=\'250.182 490.01 17 14\' enable-background=\'new 250.182 490.01 16.971 14.143\' xml:space=\'preserve\'><polygon fill=\'%23444444\' points=\'255.839,498.495 253.011,495.667 250.182,498.496 255.839,504.152 267.152,492.838 264.323,490.01 \'/></svg>")';
  499. const $check_mark_inv = $svg_prefix + 'width=\'17px\' height=\'14px\' viewBox=\'250.182 490.01 17 14\' enable-background=\'new 250.182 490.01 16.971 14.143\' xml:space=\'preserve\'><polygon fill=\'%23CCCCCC\' points=\'255.839,498.495 253.011,495.667 250.182,498.496 255.839,504.152 267.152,492.838 264.323,490.01 \'/></svg>")';
  500. const $menu_arrow = $svg_prefix + 'width=\'24px\' height=\'16px\' viewBox=\'0 0 24 16\' enable-background=\'new 0 0 24 16\' xml:space=\'preserve\'> <polygon fill=\'%23444444\' points=\'0,0 13.873,8.008 0.001,16.017 \'/> </svg>")';
  501. const $menu_arrow_inv = $svg_prefix + 'width=\'24px\' height=\'16px\' viewBox=\'0 0 24 16\' enable-background=\'new 0 0 24 16\' xml:space=\'preserve\'> <polygon fill=\'%23CCCCCC\' points=\'0,0 13.873,8.008 0.001,16.017 \'/> </svg>")';
  502. const $menu_icon = $svg_prefix + '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>")';
  503. const $grid_icon = $svg_prefix + '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>")';
  504. const $plus_sign = $svg_prefix + '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>")';
  505. const $minus_sign = $svg_prefix + 'width=\'16px\' height=\'16px\' viewBox=\'0 0 16 16\' xml:space=\'preserve\'> <rect x=\'1\' y=\'6.499\' width=\'14\' height=\'3.001\'/> </svg>")';
  506. const $next_track_arrow = $svg_prefix + 'width=\'12.8px\' height=\'14px\' viewBox=\'-2 0 12.8 14\' enable-background=\'new -2 0 12.8 14\' 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>")';
  507. const $next_track_arrow_gecko = $svg_prefix + 'width=\'12.8px\' height=\'14px\' viewBox=\'-2 0 12.8 14\' enable-background=\'new -2 0 12.8 14\' 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>")';
  508. const $next_track_arrow_gecko_hover = $svg_prefix + 'width=\'12.8px\' height=\'14px\' viewBox=\'-2 0 12.8 14\' enable-background=\'new -2 0 12.8 14\' 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>")';
  509. const $music = $svg_prefix + '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>")';
  510. //SVG FILE ICONS
  511. // Chrome default icons
  512. const $file_icon_dir_default = '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= ")';
  513. const $file_icon_file_default = 'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABnRSTlMAAAAAAABupgeRAAABHUlEQVR42o2RMW7DIBiF3498iHRJD5JKHurL+CRVBp+i2T16tTynF2gO0KSb5ZrBBl4HHDBuK/WXACH4eO9/CAAAbdvijzLGNE1TVZXfZuHg6XCAQESAZXbOKaXO57eiKG6ft9PrKQIkCQqFoIiQFBGlFIB5nvM8t9aOX2Nd18oDzjnPgCDpn/BH4zh2XZdlWVmWiUK4IgCBoFMUz9eP6zRN75cLgEQhcmTQIbl72O0f9865qLAAsURAAgKBJKEtgLXWvyjLuFsThCSstb8rBCaAQhDYWgIZ7myM+TUBjDHrHlZcbMYYk34cN0YSLcgS+wL0fe9TXDMbY33fR2AYBvyQ8L0Gk8MwREBrTfKe4TpTzwhArXWi8HI84h/1DfwI5mhxJamFAAAAAElFTkSuQmCC ")';
  514. // Custom file icons
  515. const $svg_icon_prefix = '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=\'14px\' height=\'14px\' viewBox=\'0 0 14 14\' enable-background=\'new 0 0 14 14\' xml:space=\'preserve\'> ';
  516. const $file_icon_dir = $svg_icon_prefix + '<polygon fill=\'%233399FF\' points=\'6.1,2.7 4.8,1 0,1 0,13 14,13 14,2.7 \'/> <rect x=\'1.5\' y=\'4.2\' fill=\'%2399CCFF\' width=\'11\' height=\'7.3\'/> </svg> ")';
  517. const $file_icon_dir_invisible = $svg_icon_prefix + '<polygon fill=\'%23888888\' points=\'6.1,2.7 4.8,1 0,1 0,13 14,13 14,2.7 \'/> <rect x=\'1.5\' y=\'4.2\' fill=\'%23BBBBBB\' width=\'11\' height=\'7.3\'/> <circle fill=\'%23888888\' cx=\'7\' cy=\'7.9\' r=\'1\'/> </svg> ")';
  518. const $file_icon_app = $svg_icon_prefix + '<g> <polygon style=\'fill:%230066FF;\' points=\'14,0 0,0 0,14 14,14 \'/> </g> <path style=\'fill:%23FFFFFF;\' d=\'M6.466,3.696L5.854,3.421c-0.175-0.078-0.381,0.003-0.455,0.18L5.086,4.348l1.241,0.578l0.315-0.791 C6.71,3.965,6.632,3.771,6.466,3.696z\'/> <polygon style=\'fill:%23FFFFFF;\' points=\'4.955,4.663 2.755,9.922 4.091,10.544 6.201,5.243 \'/> <polygon style=\'fill:%23FFFFFF;\' points=\'2.625,10.237 2.563,12.166 3.955,10.856 \'/> <polygon style=\'fill:%23FFFFFF;\' points=\'9.998,6.663 10.569,8.027 13.143,8.027 13.143,6.663 \'/> <path style=\'fill:%23FFFFFF;\' d=\'M9.838,7.164L7.594,1.797C7.52,1.619,7.314,1.538,7.139,1.616L6.527,1.893 C6.36,1.968,6.282,2.16,6.35,2.329l2.17,5.449L9.838,7.164z\'/> <polygon style=\'fill:%23FFFFFF;\' points=\'9.97,7.479 8.646,8.096 9.021,9.035 10.367,8.43 \'/> <path style=\'fill:%23FFFFFF;\' d=\'M10.479,8.753l-1.3,0.585L9.178,9.339c-0.041,0.311-0.073,0.736,0.073,1.07 c0.35,0.798,1.045,1.264,0.923,1.959c0,0,0.887-1.152,0.989-1.896C11.286,9.579,10.82,9.05,10.479,8.753z\'/> <polygon style=\'fill:%23FFFFFF;\' points=\'5.459,8.027 8.251,8.027 7.708,6.663 6.003,6.663 \'/> <polygon style=\'fill:%23FFFFFF;\' points=\'3.749,6.663 0.857,6.663 0.857,8.027 3.178,8.027 \'/> </svg> ")';
  519. const $file_icon_file = $svg_icon_prefix + '<g> <polygon fill=\'%23888888\' points=\'8.3,0 1.5,0 1.5,14 12.5,14 12.5,4.2 \'/> <polygon fill=\'%23FFFFFF\' points=\'11,12.5 3,12.5 3,1.5 6.8,1.5 6.8,5.7 11,5.7 \'/> <polygon fill=\'%23FFFFFF\' points=\'8.3,4.2 10.2,4.2 8.3,2.2 \'/> </g> </svg> ")';
  520. const $file_icon_text = $svg_icon_prefix + '<g> <polygon style=\'fill:%238888AA;\' points=\'14,0 0,0 0,14 14,14 \'/> </g> <g> <rect x=\'2.155\' y=\'2.187\' style=\'fill:%23FFFFFF;\' width=\'9.69\' height=\'1.14\'/> </g> <g> <rect x=\'2.155\' y=\'5.036\' style=\'fill:%23FFFFFF;\' width=\'9.69\' height=\'1.14\'/> </g> <g> <rect x=\'2.155\' y=\'7.886\' style=\'fill:%23FFFFFF;\' width=\'9.69\' height=\'1.141\'/> </g> <g> <rect x=\'2.155\' y=\'10.736\' style=\'fill:%23FFFFFF;\' width=\'6.555\' height=\'1.14\'/> </g> </svg> ")';
  521. const $file_icon_image = $svg_icon_prefix + '<g id=\'Layer_2\'> </g> <circle fill=\'%23FFEE22\' cx=\'5.5\' cy=\'3.2\' r=\'1.5\'/> <g> <path fill=\'%23FFFFFF\' d=\'M5.6,7.5L3.8,6L0.2,8.7c0.1,0.6,0.6,1.5,0.5,1.3l3-2.4L5.6,9l4.7-4l3.6,3.2l0,0C14,7.8,14,7.4,14,7 c0-0.1,0-0.3,0-0.4l-3.6-3.2L5.6,7.5z\'/> </g> <path fill=\'%2399AADD\' d=\'M3.8,6l1.8,1.5l4.8-4.1L14,6.6C13.8,2.9,10.7,0,7,0C3.1,0,0,3.1,0,7c0,0.6,0.1,1.2,0.2,1.7L3.8,6z\'/> <path fill=\'%233366CC\' d=\'M10.3,5L5.6,9L3.7,7.6l-3,2.4c1.1,2.4,3.5,4,6.3,4c3.4,0,6.3-2.5,6.9-5.8L10.3,5z\'/> <circle fill=\'%23FFE650\' cx=\'5.5\' cy=\'3.2\' r=\'1.5\'/> </svg> ")';
  522. const $file_icon_pdf = $svg_icon_prefix + '<g> <polygon style=\'fill:%23D84444;\' points=\'14,0 0,0 0,14 14,14 14,0 \'/> </g> <path style=\'fill:%23FFFFFF;\' d=\'M12.634,9.094c-0.074,0.047-0.288,0.075-0.423,0.075c-0.439,0-0.981-0.202-1.745-0.529 c0.294-0.022,0.562-0.031,0.803-0.031c0.441,0,0.569,0,1.002,0.108C12.7,8.824,12.705,9.047,12.634,9.094z M4.99,9.162 c0.17-0.3,0.345-0.616,0.521-0.952c0.435-0.822,0.712-1.469,0.914-1.997c0.409,0.742,0.917,1.37,1.51,1.876 C8.011,8.151,8.09,8.212,8.174,8.276C6.962,8.519,5.914,8.809,4.99,9.162z M6.404,1.383c0.241,0,0.38,0.606,0.391,1.179 c0.011,0.568-0.12,0.965-0.287,1.265c-0.14-0.441-0.203-1.129-0.203-1.581C6.305,2.245,6.295,1.383,6.404,1.383z M1.663,12.3 c0.14-0.374,0.68-1.113,1.479-1.771c0.051-0.037,0.175-0.155,0.289-0.263C2.596,11.603,2.033,12.133,1.663,12.3z M12.864,8.31 c-0.24-0.238-0.781-0.363-1.599-0.373c-0.555-0.008-1.218,0.041-1.923,0.138C9.03,7.893,8.707,7.697,8.451,7.459 c-0.683-0.64-1.25-1.524-1.606-2.497c0.021-0.094,0.044-0.171,0.062-0.253c0,0,0.383-2.186,0.28-2.925 c-0.015-0.104-0.021-0.131-0.05-0.21L7.104,1.486c-0.103-0.241-0.31-0.497-0.633-0.483L6.283,0.997H6.28 c-0.358,0-0.654,0.184-0.729,0.456c-0.233,0.864,0.007,2.15,0.444,3.818L5.882,5.544c-0.312,0.76-0.704,1.527-1.048,2.203 L4.787,7.836c-0.362,0.71-0.693,1.315-0.99,1.825l-0.31,0.165c-0.021,0.014-0.551,0.292-0.675,0.367 c-1.053,0.628-1.752,1.343-1.868,1.91c-0.037,0.179-0.009,0.41,0.178,0.52l0.299,0.148c0.129,0.064,0.269,0.096,0.406,0.096 c0.75,0,1.621-0.931,2.817-3.023c1.387-0.452,2.965-0.828,4.347-1.035c1.052,0.595,2.346,1.006,3.163,1.006 c0.146,0,0.271-0.013,0.373-0.042c0.155-0.04,0.288-0.129,0.369-0.254c0.156-0.235,0.191-0.563,0.146-0.901 C13.032,8.519,12.95,8.395,12.864,8.31z\'/> </svg> ")';
  523. const $file_icon_font = $svg_icon_prefix + '<g><polygon style=\'fill:%23770099;\' points=\'14,0 0,0 0,14 14,14 \'/></g><g><path style=\'fill:%23FFFFFF;\' d=\'M4.599,11.321h1.44V2.774H3.334v1.088H1.83V1.222h10.34v2.641h-1.505V2.774H7.977v8.547h1.393v1.457 H4.599V11.321z\'/></g></svg>")';
  524. const $file_icon_code = $svg_icon_prefix + '<g> <polygon style=\'fill:%237722DD;\' points=\'14,0 0,0 0,14 14,14 \'/> </g> <g> <path style=\'fill:%23FFFFFF;\' d=\'M5.892,12.965c-1.049,0-1.784-0.161-2.209-0.48c-0.425-0.317-0.638-0.82-0.638-1.503V8.915 c0-0.446-0.146-0.764-0.438-0.95C2.315,7.777,1.898,7.684,1.351,7.684V6.316c0.547,0,0.967-0.094,1.259-0.28s0.438-0.5,0.438-0.938 V3.006c0-0.675,0.217-1.172,0.65-1.491C4.13,1.195,4.862,1.036,5.893,1.036v1.312c-0.401,0.01-0.718,0.09-0.952,0.24 c-0.233,0.15-0.348,0.426-0.348,0.827V5.4c0,0.876-0.511,1.396-1.532,1.559v0.083c1.021,0.154,1.532,0.67,1.532,1.544v1.997 c0,0.41,0.116,0.688,0.349,0.835c0.233,0.146,0.55,0.223,0.951,0.232L5.892,12.965L5.892,12.965z\'/> <path style=\'fill:%23FFFFFF;\' d=\'M8.045,12.965v-1.313c0.392-0.009,0.706-0.089,0.944-0.239c0.236-0.15,0.355-0.426,0.355-0.829 V8.588c0-0.867,0.511-1.382,1.531-1.545V6.959C9.855,6.795,9.345,6.28,9.345,5.413V3.416c0-0.41-0.116-0.688-0.349-0.834 C8.764,2.436,8.447,2.358,8.045,2.349V1.036c1.049,0,1.785,0.159,2.21,0.479c0.423,0.319,0.637,0.821,0.637,1.505v2.065 c0,0.447,0.146,0.765,0.438,0.951c0.292,0.187,0.711,0.28,1.257,0.28v1.367c-0.546,0.012-0.967,0.107-1.259,0.287 C11.035,8.153,10.89,8.47,10.89,8.915v2.08c0,0.674-0.217,1.172-0.65,1.491C9.808,12.805,9.075,12.965,8.045,12.965z\'/> </g> </svg>")';
  525. const $file_icon_html = $svg_icon_prefix + '<path style=\'fill:%23FFFFFF;\' d=\'M7,14c-3.9,0-7-3.1-7-7C0,3.2,3.1,0,7,0H7C8.9,0,10.6,0.7,12,2C13.3,3.4,14,5.1,14,7 c0,1.9-0.7,3.6-2,5C10.7,13.3,8.9,14,7,14C7,14,7,14,7,14z\'/> <g> <path style=\'fill:%23EE7700;\' d=\'M5.3,1.1C4.7,1.9,4.2,2.6,3.8,3.5C3.4,3.3,3,3.1,2.6,2.8C3.3,2.1,4.2,1.5,5.3,1.1z M2,3.6 c0.5,0.3,1,0.6,1.5,0.8C3.3,5,3.2,5.6,3.1,6.3c0,0.1,0,0.2,0,0.3H0.9C1,5.4,1.4,4.4,2,3.6z M2,10.4c-0.6-0.9-1-1.9-1.1-3h2.1 c0,0.8,0.2,1.5,0.4,2.2C2.9,9.9,2.5,10.1,2,10.4z M2.6,11.2c0.4-0.3,0.8-0.5,1.2-0.6c0.3,0.8,0.8,1.5,1.4,2.2c0,0,0.1,0.1,0.1,0.1 C4.2,12.5,3.3,11.9,2.6,11.2z M6.5,12.9c-0.2-0.2-0.5-0.5-0.7-0.7c-0.5-0.6-0.9-1.3-1.2-1.9C5.3,10,5.9,9.9,6.5,9.9V12.9z M6.5,9 C5.8,9,5,9.1,4.3,9.4C4.1,8.7,4,8.1,4,7.5h2.6V9z M6.5,6.5H4c0-0.1,0-0.1,0-0.2c0.1-0.6,0.2-1.2,0.3-1.7C5.1,4.9,5.8,5,6.5,5V6.5z M6.5,4.1C5.9,4.1,5.3,4,4.7,3.8c0.4-1,1.1-1.9,1.9-2.6V4.1z M12,3.6c0.6,0.9,1,1.9,1.1,3h-2.2c0-0.8-0.2-1.5-0.4-2.2 C11,4.1,11.5,3.9,12,3.6z M11.3,2.7c0,0,0.1,0.1,0.1,0.1c-0.4,0.3-0.8,0.5-1.2,0.7C9.9,2.7,9.4,2,8.8,1.3C8.8,1.3,8.7,1.2,8.7,1.1 C9.7,1.4,10.6,2,11.3,2.7z M7.5,1.2c0.2,0.2,0.5,0.5,0.7,0.7C8.6,2.5,9,3.1,9.3,3.8C8.7,4,8.1,4.1,7.5,4.1V1.2z M7.5,5 c0.7,0,1.5-0.2,2.2-0.4C9.9,5.3,10,5.9,10,6.5H7.5V5z M7.5,7.5H10c0,0.1,0,0.2,0,0.2c0,0.6-0.2,1.1-0.3,1.7C9,9.1,8.2,9,7.5,9V7.5z M7.5,12.9v-3c0.6,0,1.3,0.1,1.9,0.3C8.9,11.2,8.3,12.1,7.5,12.9z M11.3,11.3c-0.7,0.7-1.6,1.3-2.6,1.5c0.6-0.7,1.1-1.5,1.5-2.3 c0.4,0.2,0.8,0.4,1.2,0.6C11.4,11.2,11.4,11.2,11.3,11.3z M10.5,9.7c0.2-0.6,0.3-1.2,0.4-1.9c0-0.1,0-0.2,0-0.3h2.2 c-0.1,1.1-0.4,2.1-1.1,3C11.5,10.1,11,9.9,10.5,9.7z M7,0C3.1,0,0,3.1,0,7s3.1,7,7,7s7-3.1,7-7S10.9,0,7,0z\'/> </g> </svg>")';
  526. const $file_icon_ignored = $svg_icon_prefix + '<path fill=\'%23888888\' d=\'M7,0C3.1,0,0,3.1,0,7c0,3.9,3.1,7,7,7c3.9,0,7-3.1,7-7C14,3.1,10.9,0,7,0L7,0z\'/><path fill=\'%23BBBBBB\' d=\'M7,2c2.8,0,5,2.2,5,5s-2.2,5-5,5c-2.8,0-5-2.2-5-5S4.2,2,7,2\'/><rect x=\'0.8\' y=\'5.9\' transform=\'matrix(0.7071 -0.7071 0.7071 0.7071 -2.8818 7.0063)\' fill=\'%23888888\' width=\'12.5\' height=\'2.3\'/></svg>")';
  527. const $file_icon_invisible = $svg_icon_prefix + '<g> <polygon fill=\'%23888888\' points=\'8.3,0 1.5,0 1.5,14 12.5,14 12.5,4.2 \'/> <polygon fill=\'%23BBBBBB\' points=\'11,12.5 3,12.5 3,1.5 6.8,1.5 6.8,5.7 11,5.7 \'/> <polygon fill=\'%23BBBBBB\' points=\'8.3,4.2 10.2,4.2 8.3,2.2 \'/> </g> <circle fill=\'%23777777\' cx=\'7\' cy=\'9\' r=\'1\'/> </svg>")';
  528. // const $file_icon_video = 'url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 100 100\'><style type=\'text/css\'>.st0{fill:%23FFFFFF;}.st1{fill:%23EF6F2E;}</style><rect class=\'st0\' width=\'100\' height=\'100\'/><path class=\'st1\' d=\'M100 100H0V0h100V100zM9.7 90h80.7V10H9.7\'/><path class=\'st1\' d=\'M21 26.4v47.1h58V26.4H21zM31.9 69.9h-7.2v-7.2h7.2V69.9zM31.9 59.1h-7.2v-7.2h7.2V59.1zM31.9 48.2h-7.2v-7.2h7.2V48.2zM31.9 37.3h-7.2v-7.2h7.2V37.3zM42.8 62.7V37.3L60.9 50 42.8 62.7zM75.4 69.9h-7.2v-7.2h7.2V69.9zM75.4 59.1h-7.2v-7.2h7.2V59.1zM75.4 48.2h-7.2v-7.2h7.2V48.2zM75.4 37.3h-7.2v-7.2h7.2V37.3z\'/></svg>")';
  529. // const $file_icon_audio = 'url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 100 100\'><style type=\'text/css\'>.st0{fill:%23FFFFFF;}.st1{fill:%230E3693;}.st2{fill:%23003399;}</style><rect class=\'st0\' width=\'100\' height=\'100\'/><path class=\'st1\' d=\'M100 100H0V0h100V100zM9.7 90h80.7V10H9.7\'/><polyline class=\'st2\' points=\'32.5 37.5 23.5 37.5 23.5 62.5 32.5 62.5 53.6 77 53.6 23 32.5 37.5 \'/><path class=\'st2\' d=\'M71.9 50c0 6.8-3.7 12.7-9.1 15.8l2.8 4.9c7.1-4.1 11.9-11.8 11.9-20.7 0-8.8-4.8-16.6-11.9-20.7l-2.8 4.9C68.2 37.3 71.9 43.2 71.9 50z\'/><path class=\'st2\' d=\'M62.1 50c0 3.2-1.7 5.9-4.3 7.4l2.7 4.7c4.2-2.4 7-6.9 7-12.1 0-5.2-2.8-9.7-7-12.1l-2.7 4.7C60.4 44.1 62.1 46.8 62.1 50z\'/></svg>")';
  530. // const $file_icon_archive = 'url("data:image/svg+xml;utf8,<svg xmlns=\'http://www.w3.org/2000/svg\' viewBox=\'0 0 100 100\'><style type=\'text/css\'>.st0{fill:%23FFFFFF;}.st1{fill:%23D8A13F;}</style><rect class=\'st0\' width=\'100\' height=\'100\'/><path class=\'st1\' d=\'M100 100H0V0h100V100zM9.7 90h80.7V10H9.7\'/><path class=\'st1\' d=\'M72.4 38.5h-7.9v-7.9L72.4 38.5zM51.1 30.6v28.8h21.4v-19h-9.9v-9.9H51.1zM54.4 23H30.8v5.6h9.3l-5.9 4.5v4.8l8.6-6.6v-2.7h30.1v-2.3L54.4 23zM42.9 35.1l-8.6 6.6v4.8l8.6-6.6V35.1zM34.2 55.1l8.6-6.6v-4.8l-8.6 6.6V55.1zM42.9 57.1v-4.8l-8.6 6.6v2.6h-3.4v5.6h5.3v3.8H33c-0.6-1-1.6-1.6-2.8-1.6 -1.8 0-3.2 1.4-3.2 3.2s1.4 3.2 3.2 3.2c1.2 0 2.2-0.6 2.8-1.6h3.1V77h4.8v-2.9H44c0.6 1 1.6 1.6 2.8 1.6 1.8 0 3.2-1.4 3.2-3.2s-1.4-3.2-3.2-3.2c-1.2 0-2.2 0.6-2.8 1.6h-3.1v-3.8h13.5l18.5-3.3v-2.3H37.1L42.9 57.1z\'/></svg>")';
  531.  
  532.  
  533. function assembleUIElements() {
  534. // setParentLink();
  535.  
  536. $parents_dir_menu.find('div').append( $current_dir_path );
  537. $sidebar_header.find('thead th').append($toggle_sidebar);
  538. $sidebar_header.find('tbody tr').first().find('td').first().append( $parent_dir_menu ).next().append( $parents_dir_menu ).next().append( $shortcuts_menu );
  539. $sidebar_header.find('tbody tr:last-child td').append( $details_btn, $inv_checkbox, $grid_btn );
  540.  
  541. $dir_list.append($dir_list_head, $dir_list_body);
  542. $sidebar.append($sidebar_header, $dir_list);
  543. $sidebar_wrapper.append( $sidebar, $sidebar_overlay, $handle );
  544.  
  545. $content_image.append( $image );
  546. $checkbox_cont.append( $loop, $shuffle );
  547. $content_media.append( $prev_track, $next_track, $audio, $checkbox_cont, $close_audio );
  548. $content_font.append( $specimen );
  549. $content_header.find('tr').first().append( $content_reload_btn, $content_title, $content_close_btn );
  550. $content_header.find('tr').last().append( $content_media );
  551. $content_container.append( $content_header, $content_scale, $content_overlay, $content_grid, $content_image, $content_embed, $content_iframe, $content_font );
  552. $content_pane.append( $content_container, $prev_btn, $next_btn );
  553. }
  554. assembleUIElements();
  555.  
  556. // ***** STYLES ***** //
  557.  
  558. // DEFINE STYLES
  559. var $styles = '';
  560.  
  561. $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; }';
  562. $styles += 'table { width:100%; border-collapse:collapse; }';
  563.  
  564.  
  565. // SIDEBAR
  566. $styles += '#sidebar_wrapper { width:'+ $UI_pref_width +'%; min-width:220px; will-change:width; padding:0; position:relative; border:0; background:lightgray; }';
  567.  
  568. $styles += '#sidebar 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; text-align:left; text-indent:0; list-style-type:none; background:lightgray; border:solid 1px gray; box-shadow: 0px 2px 3px -2px #888; display:none; }';
  569. $styles += '#sidebar li { display:block; }';
  570. $styles += '#sidebar a { text-decoration:none; }';
  571.  
  572. $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; }';
  573. $styles += '#handle { width:8px; position:absolute; top:0; right:-4px; bottom:0; z-index:1000; cursor:col-resize; }';
  574. // SIDEBAR: Header
  575. $styles += '#sidebar_header { height:auto; position:relative; border:0; user-select:none; font-size:0.875rem; z-index:2000; }';
  576. $styles += '#sidebar_header thead tr, #sidebar_header tbody tr:first-of-type { border-bottom:solid 1px grey; background-color:#BBB; }';
  577. $styles += '#sidebar_header thead th { padding:4px; font-weight:normal; font-size:0.875em; letter-spacing:0.5em; cursor:default; }';
  578. $styles += '#sidebar_header tbody tr, #sidebar_header tbody tr:first-of-type td:first-of-type, #sidebar_header tbody tr:last-of-type td { position:relative; }';
  579. $styles += '#sidebar_header tbody tr td { padding:0; }';
  580. $styles += '#sidebar_header tbody tr:first-of-type td:hover { cursor:pointer; }';
  581. $styles += '#sidebar_header tbody tr:first-of-type td:nth-of-type(odd) { width:24px; max-width:24px; min-width:24px; }';
  582. $styles += '#sidebar_header tbody tr:first-of-type td:nth-of-type(even) { width:100%; border-left:solid 1px grey; border-right:solid 1px grey; }';
  583. $styles += '#sidebar_header tbody tr:last-of-type td { padding:6px 0; vertical-align:middle; white-space:normal; }';
  584. // SIDEBAR: Menu Parent
  585. $styles += '#parent_dir_menu { margin:0; padding:0; display:block; position:absolute; top:0; right:0; bottom:0; left:0; }';
  586. $styles += '#parent_dir_menu a { width:100%; height:100%; padding:0; display:block; text-align:center; vertical-align:middle; background:' + $up_arrow + 'center no-repeat; }';
  587. // SIDEBAR: Menu Parents
  588. $styles += '#parents_dir_menu { margin:0; padding:0; height:auto; text-align:center; }';
  589. $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; }';
  590. $styles += '#parents_dir_menu + ul { z-index:100; border-right:0; border-left:0; }';
  591. $styles += '#parents_dir_menu + ul li a, #shortcuts_menu + ul li a, #shortcuts_menu + ul li > span { margin:0; padding:6px 8px; display:block; text-indent:0; color:#333; white-space:normal; }';
  592. // SIDEBAR: Menu Shortcuts
  593. $styles += '#shortcuts_menu { margin:0; padding:0; overflow:hidden; }';
  594. $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; }';
  595. $styles += '#shortcuts_menu + ul { z-index:9999; border-right:0; border-left:0; }';
  596. $styles += '#shortcuts_menu + ul > li.has_submenu { position:relative; background:'+ $menu_arrow +' right no-repeat; background-size: 12px; }';
  597. $styles += '#shortcuts_menu + ul > li.has_submenu:hover { background:#BBB '+ $menu_arrow +' right no-repeat; background-size: 12px; }';
  598. $styles += '#shortcuts_menu + ul > li.has_submenu.ruled { border-bottom:solid 1px #999; }';
  599. $styles += '#sort_by, #donate { border-top:solid 1px #999; }';
  600. $styles += '#shortcuts_menu + ul > li > ul { width:100%; top:-1px; left:100%; }';
  601. $styles += '#shortcuts_menu div, #parent_dir_menu, #prev_btn, #next_btn { opacity:0.7; }';
  602. // SIDEBAR: Menu Shortcuts Sort
  603. $styles += '#sort_menu li span { padding-left: 2em !important; }';
  604. $styles += '#sort_menu li.checked span { background:'+ $check_mark +' 4px center no-repeat; background-size:12px; }';
  605. $styles += '#sort_menu .disabled span, #dir_list .disabled { color:#999 !important; cursor:default; }';
  606. // SIDEBAR: Menu Shortcuts Other items
  607. $styles += '#dark_theme { display:block; }';
  608. // SIDEBAR: Details Button
  609. $styles += '#details_btn { margin:0 0.5em; padding:2px 6px; border:solid 1px #333; border-radius: 3px; }';
  610. // SIDEBAR: Grid Button
  611. $styles += '#grid_btn { margin:0; width:28px; height:18px; float:right; cursor:pointer; outline:0; background:'+ $grid_icon +' center no-repeat; }';
  612. $styles += '#grid_btn .menu { padding-right:29px; padding-left:0; top:-1px; right:0; left:0; border:solid grey; border-width: 1px 0 1px 1px; }';
  613. $styles += '#grid_btn .menu li { width:100%; padding:4px 6px; background:#CCC; display:block; clear:both; text-align:right; list-style:none; border-right:solid 1px grey; box-sizing:border-box; white-space:pre;}';
  614. $styles += '#grid_btn .menu li:first-of-type { border-bottom:solid 1px grey; }';
  615. $styles += '#grid_btn.has_images, #grid_btn.has_fonts { display:inline-block; }';
  616. // SIDEBAR: Toggle Sidebar
  617. $styles += '#toggle_sidebar { width:16pt; height:14pt; position:absolute; top:1px; right:2px; z-index:9999; background:'+ $toggle +' center no-repeat; background-size:12px; opacity:0.7; }';
  618. $styles += 'body.has_hidden_sidebar #toggle_sidebar { left:0; transform:rotate(180deg); }';
  619. $styles += 'body.has_hidden_sidebar #sidebar_header { z-index:unset; }';
  620. $styles += 'body.has_hidden_sidebar #sidebar_wrapper { width:0%; top:2px; left:3px; position:absolute; }';
  621. $styles += 'body.has_hidden_sidebar #content_pane { width:100%; }';
  622. $styles += 'body.has_hidden_sidebar #reload_btn { margin-left: 16pt; }';
  623.  
  624. // DIR_LIST
  625. $styles += '#dir_list { min-wdth:100px; border:0; position:relative; overflow:hidden; table-layout:fixed; font-size:0.875rem; }';
  626. // DIR_LIST: tHead, tbody
  627. $styles += '#dir_list thead { white-space:pre-wrap; }';
  628. $styles += '#dir_list thead, #dir_list tbody { width:100%; position:absolute; left:0; right:0; text-align:left; }';
  629. // DIR_LIST: Sorting
  630. $styles += '#dir_list thead th { padding:0 24px 6px; cursor:pointer; }';
  631. $styles += '#dir_list thead th:last-of-type { margin-right:1em; }';
  632. $styles += '#dir_list thead th.details { padding-bottom: 9px !important; }';
  633. $styles += '#dir_list thead th.checked { margin-left:0 !important; margin-right:0 !important; background:'+ $check_mark +' 10px 2px no-repeat; position:relative; background-size:10px; }';
  634. $styles += '#dir_list thead th.checked:after { content:""; width:2em; height:8px; top:2px; display:inline-block; position:absolute; background:'+ $up_arrow +' center no-repeat; background-size:10px; }';
  635. $styles += '#dir_list thead th.checked:not(.up):after { transform:rotate(180deg); }';
  636. $styles += '#dir_list thead .name, #default { padding-left:2em; display:block; }';
  637. // DIR_LIST: tBody
  638. $styles += '#dir_list tbody { bottom:0; overflow-y:auto; border-top:solid 1px grey; outline:0; background-color:#DFDFDF; }';
  639. $styles += '#dir_list tbody .name { display:block; clear:right; text-align:left;}';
  640. $styles += '#dir_list tbody tr { padding-right:12px; display:block; margin-inline-start:0; clear:both; background:transparent; }';
  641. $styles += '#dir_list tbody a { margin:0; display:block; background-size:auto 13px; -webkit-padding-start:2m; padding: 4px 6px 4px 26px; color:#333; outline:none; overflow:hidden; background-position:6px 4px; white-space:normal; }';
  642. $styles += '#dir_list td.icon { height:16px; padding-left: 6px; }';
  643. $styles += '#dir_list .icon + td.name a { -webkit-padding-start:1em; padding-left:6px; }';
  644. $styles += '#dir_list .details { padding:0 0 4px 24px !important; font-size:0.875em; text-align:left; vertical-align:top; }';
  645. $styles += '#dir_list .details a { padding: 0; }';
  646. $styles += '#dir_list.show_details .details:not(.ext), body.is_gecko #dir_list.show_details #tbody > tr > td:not(:first-of-type) { display:inline-block; }';
  647. $styles += 'body.is_dirs_on_top #dir_list tbody tr.sorted:not(:last-of-type) { padding-bottom:2px; border-bottom:solid 1px #999; }';
  648. $styles += '#dir_list tbody tr.dir ~ tr:not(.dir,.invisible,.ignore) { border-top: solid 1px #999; }';
  649. // DIR_LIST: Audio/Video files
  650. $styles += 'body.has_audio #dir_list input { margin-top:1px; margin-right:8px; margin-bottom:1px; }';
  651. $styles += 'body.has_audio #dir_list thead input { display:inline-block; }';
  652. $styles += '#dir_list .audio a, #dir_list .video a { padding-left:4px; background:none; }';
  653. // DIR_LIST: Selected and Playing items
  654. $styles += '#dir_list .selected, #dir_list .selected.audio, #dir_list .playing { background:lightsteelblue; }';
  655. $styles += '#dir_list .selected a, #dir_list .playing a { font-weight:bold; color:#333; }';
  656. // DIR_LIST: Invisible and Ignored items
  657. $styles += '#dir_list tr.ignore a, #dir_list tr.ignore.app a, #dir_list tr.ignore td.details { color:#777; position:relative; }';
  658. // SIDEBAR: Hover styles
  659. $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, #toggle_sidebar:hover { opacity:1; }';
  660. $styles += '#shortcuts_menu + ul > li:hover > ul, #shortcuts_menu + ul > li > ul:hover, #grid_btn.has_images.has_fonts:hover ul.menu { display:block !important; }';
  661. $styles += '#parents_dir_menu + ul li:hover, #shortcuts_menu + ul li:not(.has_submenu):hover, #grid_btn.has_images.has_fonts:hover ul.menu li:hover, #dir_list tbody tr:hover, #dir_list .hovered { background:#BBB; }';
  662. // SIDEBAR: Hide stuff
  663. $styles += '#default_theme, #details_btn span:last-of-type, #grid_btn, #grid_btn .menu, #dir_list thead .name input, #dir_list .details { display:none; }';
  664. $styles += 'body.has_hidden_sidebar #handle, #dir_list thead th.checked:before, #dir_list thead td.icon, #dir_list tr:empty, #dir_list .audio td.icon, #dir_list .video td.icon, body.hide_invisibles .invisible, body.hide_ignored .ignore { content:""; display:none !important; }';
  665.  
  666. // ***** CONTENT PANE STYLES ***** //
  667. $styles += '#content_pane { width:'+ (100 - $UI_pref_width).toString() +'%; will-change:width; padding:0; border:0; background:#FFF; position:relative; }';
  668. $styles += '#content_container { width:100%; height:100%; top:0; overflow:visible; }';
  669. // CONTENT: Overlay
  670. $styles += '#sidebar_overlay, #content_overlay { height:'+ window.innerHeight + 'px; position:absolute; top:0; right:0; bottom:0; left:0; display:none; z-index:9998; }';
  671. // CONTENT: Header
  672. $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; }';
  673. $styles += '#content_header table { padding:7px 12px 5px; font-size:0.875rem; }';
  674. $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; }';
  675. $styles += '#content_header tr:first-of-type td:first-of-type { float:left; text-align:left; }';
  676. $styles += '#content_header tr:first-of-type td:last-of-type { float:right; text-align:right; }';
  677. $styles += '#content_header tr:last-of-type { border-top:solid #888 1px; }';
  678. $styles += '#content_header td button { word-break:none; hyphens:none; }';
  679. $styles += '#content_title { padding:4px 1em 3px; vertical-align:middle; word-break:break-word; text-align:center; }';
  680. $styles += '#content_pane[class*="content"] #content_header, body.is_playing #content_header { display:block !important; }';
  681. // CONTENT: Grid styles
  682. // Default Image and Font sizes
  683. var $grid_image_size = $settings.grid_image_size ? $settings.grid_image_size : 150;
  684. var $grid_font_size = $settings.grid_font_size ? $settings.grid_font_size : 1;
  685. $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; }';
  686. $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; }';
  687. $styles += '#content_grid:not(.has_grid) .image_grid_item.selected { background:#666; }';
  688. $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; } ';
  689. $styles += '#content_grid:not(.has_grid) .image_grid_item:hover, #content_grid:not(.has_grid) div.image_grid_item.hovered { background:#555; }';
  690. $styles += '#content_grid .font_grid_item { width:calc(100% - 5rem); padding:0rem 2.5rem 0.5rem; display:block; font-size:'+ $grid_font_size * 3 +'em; text-align:left; outline:none; clear:both; }';
  691. $styles += '#content_grid .font_grid_item:first-of-type { padding-top:2rem; }';
  692. $styles += '#content_grid .font_grid_item:last-of-type { padding-bottom:2rem; }';
  693. $styles += 'body #content_grid div:hover, body #content_grid div.hovered { background:#DDD; }';
  694. $styles += 'body #content_grid div.selected { background:#CCC; }';
  695. // CONTENT: Image preview styles
  696. $styles += '#content_image {padding:2rem 2.5rem; position:absolute; top:0; right:0; bottom:0;left:0; display:none; background:#333; overflow:auto; text-align:center;}';
  697. $styles += '#content_image img[src="/"] { display:none; }';
  698. $styles += '#content_image img:not(.zoom_img) {width:auto; height:auto; max-width:100%; max-height:calc(100% - 3px); cursor:zoom-in; -webkit-user-select:none; position:relative; top:50%; transform:translateY(-50%); background:#FFF; };';
  699. $styles += '#content_image img.zoom_img { max-width:none !important; max-height:none !important; cursor:zoom-out; top:0; transform:translateY(0); background:"FFF" }';
  700. $styles += '.vert_center { top:50% !important; transform:translateY(-50%); }';
  701. // CONTENT: Font preview styles
  702. $styles += '#content_font { padding:2rem 2.5rem; 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; }';
  703. $styles += '.specimen { padding-bottom:0.25em; white-space:normal; text-align:left; }';
  704. $styles += '.lorem { margin-bottom:1em; text-align:justify; word-break:normal; white-space:normal; overflow-wrap:normal; hyphens:auto; line-height: 1.4; }';
  705. $styles += '.lorem.first { margin-top:0.5em; }';
  706. $styles += '.lorem.first:first-line { letter-spacing:1pt; font-variant:small-caps; font-size:'+ $grid_font_size*1.33 +'em; }';
  707. $styles += '.lorem + .lorem { columns:2; column-gap:1em;}';
  708. $styles += '.lorem + .lorem + .lorem { margin-bottom:2em; columns:3; }';
  709. // CONTENT: PDF (embed) and iFrame styles
  710. $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; }';
  711. // CONTENT: Audio/Video Content
  712. $styles += '#content_media { padding: 6px; text-align:center; display:none; }';
  713. $styles += '#checkbox_div { padding-left:6px; display:inline-block; }';
  714. $styles += '#checkbox_div label { display:block; float:left; clear:both; }';
  715. $styles += '#checkbox_div label input { margin-top:0; }';
  716. $styles += 'body.has_audio.autoload_audio #content_media, body.is_playing #content_media, body.is_paused #content_media { display:table-cell; }';
  717. $styles += 'body.has_audio #content_pane:not(.has_image_content) #content_image { display:block; background:'+ $music +' center no-repeat; background-size:33.33%; }';
  718. $styles += 'body.has_audio #content_pane.has_ignored_content #content_image { background:transparent; }';
  719. $styles += 'body.is_chrome #audio { background:rgb(241, 243, 244); }'; // tweak for new Chrome audio element
  720. // CONTENT: Audio Prev/Next Track buttons
  721. $styles += '#prev_track, #next_track { width:2em; height:2em; padding:0 0 0 8px; display:inline-block; background: rgb(241, 243, 244) '+ $next_track_arrow +' center no-repeat; background-blend-mode:difference; }';
  722. $styles += '#next_track { transform:rotate(180deg); }';
  723. $styles += '#prev_track:hover, #next_track:hover { background-blend-mode:normal; }';
  724. $styles += '#close_audio { width:14px; height:14px; border:solid 1px #222; border-radius:100%; float:right; background:#EEE '+ $plus_sign +' center no-repeat; background-size:10px; transform:rotate(45deg); opacity:.75; }'; // tweak for new Chrome audio element
  725. // CONTENT: Scale Buttons
  726. $styles += '#scale { background:rgba(128,128,128,0.3); position:absolute; right:1rem; opacity:0; transition:opacity 1s ease-in-out; display:block; z-index:10; }';
  727. $styles += '#scale span { width:2rem; height:2rem; font-size:2rem; display:block; text-align:center; cursor:pointer; }';
  728. $styles += '#scale span:first-of-type { opacity:0.3; background:'+ $plus_sign +' center no-repeat; }';
  729. $styles += '#scale span:last-of-type { opacity:0.3; background:'+ $minus_sign +' center no-repeat; }';
  730. $styles += '#scale span:hover { opacity:0.5; filter:invert(100%); background-color:#666; }';
  731. $styles += '#content_pane.has_font_content:hover #scale, #content_pane.has_grid_content:hover #scale, #content_pane.has_image_content:hover #scale { display:block; opacity:1; }';
  732. // CONTENT: Prev and Next Item buttons
  733. $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 + ' center no-repeat; }';
  734. $styles += '#prev_btn { position:absolute; left:0 !important; }';
  735. $styles += '#next_btn { position:absolute; right:0; transform:rotate(180deg); }';
  736. $styles += 'body:not(.has_audio) #content_pane.has_image_content #prev_btn, body:not(.has_audio) #content_pane.has_image_content #next_btn, body:not(.has_audio) #content_pane.has_font_content #prev_btn, body:not(.has_audio) #content_pane.has_font_content #next_btn { display:block; }';
  737. $styles += '#content_pane.has_image_content.has_grid_content #next_btn, #content_pane.has_image_content.has_grid_content #prev_btn { display:none; }';
  738. // CONTENT: Preview content
  739. $styles += '#content_pane.has_font_content #content_font, #content_pane.has_image_content #content_image, #content_pane.has_pdf_content #content_embed, #content_pane.has_file_content #content_iframe { display:block; }';
  740. $styles += '#content_grid.has_image_grid { padding-bottom:1rem; display:grid !important; }';
  741. $styles += '#content_grid.has_font_grid, #content_grid.has_grid { background:#FFF; display:block !important; }';
  742. $styles += '#content_pane[class*="hidden"] #content_grid { display:none !important; z-index:auto; }';
  743. $styles += '#content_pane.has_file_content #content_iframe { background:white; }';
  744. $styles += '#content_pane.has_ignored_content:before { content:""; background-image:'+ $file_icon_ignored +'; background-position:center; background-repeat:no-repeat; background-size:50%; position:absolute; top:0; right:0; bottom:0; left:0; opacity:0.33}';
  745.  
  746.  
  747. // DARK THEME STYLES
  748. $styles += 'body.dark_theme #sidebar, body.dark_theme #content_header, body.dark_theme #dir_list #tbody { background:#555; }';
  749. $styles += 'body.dark_theme #sidebar ul { background:#444; box-shadow:0px 2px 3px -2px #111; }';
  750. $styles += 'body.dark_theme #sidebar ul li:hover { background:#666; }';
  751. $styles += 'body.dark_theme #sidebar { border-right-color:#111; }';
  752. // DARK THEME: SIDEBAR Header
  753. $styles += 'body.dark_theme #sidebar_header thead tr, body.dark_theme #sidebar_header tbody tr:first-of-type { border-bottom:solid 1px black; background-color:#444; }';
  754. $styles += 'body.dark_theme #sidebar_header tbody tr:first-of-type td:nth-of-type(even) { border-left-color:#111; border-right-color:#111; }';
  755. // DARK THEME: SIDEBAR Menus
  756. $styles += 'body.dark_theme #sidebar_header .menu { border-top-color:#111; }';
  757. $styles += 'body.dark_theme #sidebar_header .menu, body.dark_theme #content_header { border-bottom-color:#111; }';
  758. $styles += 'body.dark_theme #shortcuts_menu + ul > li.has_submenu { background:'+ $menu_arrow_inv +' right no-repeat; background-size: 12px; }';
  759. $styles += 'body.dark_theme #shortcuts_menu + ul > li.has_submenu:hover { background:#666 '+ $menu_arrow_inv +' right no-repeat; background-size: 12px; }';
  760. $styles += 'body.dark_theme #shortcuts_menu + ul > li > ul, body.dark_theme #shortcuts_menu + ul > li.has_submenu + li:not(.has_submenu) { border-color:#111; }';
  761. $styles += 'body.dark_theme #sort_menu li.checked a { background:'+ $check_mark_inv +' 4px center no-repeat; background-size:12px; }';
  762. $styles += 'body.dark_theme #sidebar tr *, body.dark_theme #content_header tr { color:#EEE; }';
  763. $styles += 'body.dark_theme #details_btn span { color:#333 !important; }';
  764. $styles += 'body.dark_theme #dark_theme { display:none; }';
  765. $styles += 'body.dark_theme #default_theme { display:block; }';
  766. // DARK THEME: SIDEBAR Grid Buttons
  767. $styles += 'body.dark_theme #sidebar #grid_btn, body.dark_theme #grid_btn .menu, body.dark_theme #parent_dir_menu, body.dark_theme #shortcuts_menu, body.dark_theme #toggle_sidebar { filter:invert(100%); }';
  768. $styles += 'body.dark_theme #sidebar #grid_btn ul.menu { background:transparent; border: solid #111; border-width: 1px 0 1px 1px; box-shadow:none !important; }';
  769. $styles += 'body.dark_theme #sidebar #grid_btn .menu li { background:#555; border-right-color: #111; }';
  770. $styles += 'body.dark_theme #sidebar #grid_btn .menu li:first-of-type { border-bottom-color: #111; }';
  771. $styles += 'body.dark_theme #sidebar #grid_btn .menu li:hover { background:#777; }';
  772. // DARK THEME: DIR_LIST
  773. $styles += 'body.dark_theme #dir_list #tbody { border-top:solid 1px #222; }';
  774. $styles += 'body.dark_theme #dir_list thead th.checked { color:#EEE; background:'+ $check_mark_inv +' 10px 2px no-repeat; background-size:10px; }';
  775. $styles += 'body.dark_theme #dir_list thead th.checked:after { content:" "; width:2em; height:1em; display:inline-block; background:'+ $up_arrow_inv +' center no-repeat; background-size:10px; }';
  776. $styles += 'body.dark_theme.is_dirs_on_top #dir_list tbody tr.sorted:not(:last-of-type) { border-bottom-color: #CCC; }';
  777. $styles += 'body.dark_theme.is_dirs_on_top #dir_list tbody tr.dir + tr.file { border-top-color: #CCC; }';
  778. $styles += 'body.dark_theme #dir_list tbody tr:hover, body.dark_theme #dir_list tbody li:hover, body.dark_theme #sidebar .hovered { background:#777 !important; }';
  779. $styles += 'body.dark_theme #dir_list tr.file.ignore a.icon, body.dark_theme #dir_list tr.ignore td.details { color:#BBBBBB; }';
  780. // DARK THEME: DIR_LIST Selected and Playing items
  781. $styles += 'body.dark_theme #dir_list .selected { background:slategray !important; }';
  782. $styles += 'body.dark_theme #dir_list .playing, body.dark_theme #dir_list .selected.audio { background: #4C7E80 !important; }';
  783. // DARK THEME: CONTENT
  784. $styles += 'body.dark_theme #content_pane, body.dark_theme #content_grid { background:#333; }';
  785. $styles += 'body.dark_theme #content_header tr:last-of-type { border-top: solid #333 1px; }';
  786. $styles += 'body.dark_theme #content_font, body.dark_theme .font_grid_item { color:#CCC; }';
  787. $styles += 'body.dark_theme #content_grid .font_grid_item.selected, body.dark_theme #content_grid .image_grid_item.selected { background:#666 !important }';
  788. $styles += 'body.dark_theme .font_grid_item:hover, body.dark_theme .font_grid_item.hovered, body.dark_theme .image_grid_item:hover, body.dark_theme .image_grid_item.hovered { background:#555 !important; }';
  789. $styles += 'body.dark_theme #content_pane.has_font_content hr { border-bottom-color:#CCC; }';
  790. $styles += 'body.dark_theme #content_iframe, body.dark_theme #close_audio { filter:invert(87.5%); }';
  791. // EXCLUDED: Prevent previewed files with these extensions from being inverted in dark mode
  792. for ( let i = 0, n = $row_settings.exclude.length; i < n; i += 1) {
  793. $styles += 'body.dark_theme #content_iframe[src*="'+ $row_settings.exclude[i] +'" i], body.dark_theme #content_iframe[src$="/" i] { background:white; filter:unset; }';
  794. }
  795.  
  796. // FILE ICON STYLES
  797. $styles += 'body.use_default_icons #dir_list .dir a.icon { background:'+ $file_icon_dir_default + ' 6px 4px no-repeat; background-size:auto 13px; }';
  798. $styles += 'body.use_default_icons #dir_list .file a.icon { background:'+ $file_icon_file_default + ' 6px 4px no-repeat; background-size:auto 13px; }';
  799. $styles += 'body #dir_list tr.file.audio a.icon, body #dir_list tr.file.video a.icon { background: none !important }';
  800. $styles += 'body.use_custom_icons #dir_list img, #dir_list tr.file.audio a img, #dir_list tr.file.video a img { display:none; }';
  801. $styles += 'body.use_custom_icons #dir_list tr.file:not(.dir) a.icon { background: '+ $file_icon_file +' 6px 4px no-repeat; background-size:14px 14px; }';
  802. $styles += 'body.use_custom_icons #dir_list tr.file.font a.icon { background: '+ $file_icon_font +' 6px 4px no-repeat; background-size:14px 14px; }';
  803. $styles += 'body.use_custom_icons #dir_list tr.file.image a.icon { background: '+ $file_icon_image +' 6px 4px no-repeat; background-size:14px 14px; }';
  804. $styles += 'body.use_custom_icons #dir_list tr.file.pdf a.icon { background: '+ $file_icon_pdf +' 6px 4px no-repeat; background-size:14px 14px; }';
  805. $styles += 'body.use_custom_icons #dir_list tr.file[class*="htm"] a.icon { background: '+ $file_icon_html +' 6px 4px no-repeat; background-size:14px 14px; }';
  806. $styles += 'body.use_custom_icons #dir_list tr.file.ignore a.icon { background: transparent !important; }';
  807. $styles += 'body.use_custom_icons #dir_list tr.file.ignore a.icon::before { content:""; background: '+ $file_icon_ignored +' 6px 4px no-repeat; position:absolute; top:0; left:0; width:20px; height:18px; display:block; opacity:0.66; }';
  808. $styles += 'body.use_custom_icons #dir_list tr.file.invisible a.icon { background: '+ $file_icon_invisible +' 6px 4px no-repeat; background-size:14px 14px; }';
  809. $styles += 'body.use_custom_icons #dir_list tr.dir.invisible a.icon { background: '+ $file_icon_dir_invisible +' 6px 4px no-repeat; background-size:14px 14px; }';
  810. $styles += 'body.use_custom_icons #dir_list tr.dir:not(.app) a.icon { background: '+ $file_icon_dir +' 6px 4px no-repeat; background-size:14px 14px; }';
  811. $styles += 'body.use_custom_icons #dir_list tr.app a { background: '+ $file_icon_app +' 6px 4px no-repeat !important; background-size:14px 14px; }';
  812. // Plain text icons
  813. for ( let i = 0, n = $row_types.text.length; i < n; i += 1) {
  814. $styles += 'body.use_custom_icons #dir_list tr.file.'+ $row_types.text[i] +' a.icon { background: '+ $file_icon_text +' 6px 4px no-repeat; background-size:14px 14px }';
  815. }
  816. // Code file icons
  817. for ( let i = 0, n = $row_types.code.length; i < n; i += 1) {
  818. $styles += 'body.use_custom_icons #dir_list tr.file.'+ $row_types.code[i] +' a.icon { background: '+ $file_icon_code +' 6px 4px no-repeat; background-size:14px 14px }';
  819. }
  820.  
  821. // MAIN CONTENT
  822. $styles += '#main_content { height:100%; border:0; overflow:hidden; }';
  823.  
  824. // Gecko Styles:
  825. var $gecko_styles = '';
  826.  
  827. $gecko_styles += 'html, body { border: solid 1px gray !important; }';
  828. $gecko_styles += 'button { padding:0; }';
  829. $gecko_styles += 'body.is_gecko #grid_btn .menu { top:-7px; left:-120px; }';
  830. $gecko_styles += 'thead {font-size:100%;}';
  831. $gecko_styles += '#dir_list th, #dir_list td {width:auto;}';
  832. $gecko_styles += '#dir_list .dir::before {position:absolute;}';
  833. $gecko_styles += 'body.is_gecko.use_default_icons:not(.is_converted_list) #dir_list .file .name .icon { padding-left:4px; background:none; }';
  834. $gecko_styles += 'body.is_gecko.use_default_icons #dir_list .file .name .icon img { margin-right:6px; height:14px; }';
  835. $gecko_styles += 'body.is_gecko #dir_list #tbody > tr > td:not(:first-of-type) { padding:0 24px 4px; }';
  836. $gecko_styles += 'body.is_gecko #dir_list #tbody > tr > td:last-of-type { text-indent:6px; }';
  837. $gecko_styles += '#prev_track, #next_track { background: #3F3F3F '+ $next_track_arrow_gecko +' center no-repeat; }';
  838. $gecko_styles += 'body.dark_theme #prev_track, body.dark_theme #next_track { background: #252525 '+ $next_track_arrow_gecko +' center no-repeat; }';
  839. $gecko_styles += 'body #prev_track:hover, body #next_track:hover { background: #252525 '+ $next_track_arrow_gecko_hover +' center no-repeat; }';
  840. $gecko_styles += '#prev_track:hover, #next_track:hover { background-blend-mode:difference; }';
  841. $gecko_styles += '#content_header tr:first-of-type td:first-of-type, #content_header tr:first-of-type td:last-of-type { padding-top:8px; }';
  842.  
  843. const $font_preview_styles = document.createElement('style');
  844. const $custom_styles = document.createElement("style");
  845. const $custom_gecko_styles = document.createElement("style");
  846.  
  847. function addStyles() {
  848. // for font previews
  849. $font_preview_styles.appendChild(document.createTextNode(""));
  850. document.head.append($font_preview_styles);
  851. var $font_styles = '';
  852. $font_preview_styles.append( $font_styles );
  853.  
  854. // basic UI styles
  855. $custom_styles.appendChild(document.createTextNode(""));
  856. $custom_styles.append($styles);
  857. document.head.appendChild($custom_styles);
  858.  
  859. // Firefox styles
  860. if ( $userAgent.indexOf('Firefox') > -1 ) {
  861. $custom_gecko_styles.appendChild(document.createTextNode(""));
  862. $custom_gecko_styles.append($gecko_styles);
  863. document.head.appendChild($custom_gecko_styles);
  864. }
  865. }
  866. // ***** END STYLES ***** //
  867.  
  868. // ***** BUILD MENUS ***** //
  869.  
  870. const parentLinksArr = function() {
  871. var $paths_arr = [];
  872. var $path = window.location.pathname;
  873. while ( $path.lastIndexOf('/') > 0 ) {
  874. $path = $path.slice( 0,$path.lastIndexOf('/') );
  875. $paths_arr.push($path);
  876. }
  877. if ( $userAgent.indexOf('Firefox') > -1 ) {
  878. $paths_arr.push('///'); }
  879. else {
  880. $paths_arr.push('//'); // root
  881. }
  882. return $paths_arr;
  883. };
  884.  
  885. // MENUS: Create menu list items
  886. var $menu_item;
  887. const menuItems = function(x,y,i,b) { // (link, name, count, boolean)
  888. let $qstr = '/?';
  889. if ( b === true ) {
  890. $protocol = '';
  891. $qstr = '';
  892. $query_prefs = '';
  893. }
  894. $menu_item = '<li><a href="'+ $protocol + x[i] + $qstr + $query_prefs +'">' + y[i] + '/</a></li>';
  895. return $menu_item;
  896. };
  897.  
  898. var $parent_dir_link;
  899. function setParentLink() {
  900. var $query_prefs = new URLSearchParams( window.location.search );
  901. $query_prefs.delete('file');
  902.  
  903. $UI_pref_history_arr = $query_prefs.get('history') !== null ? $query_prefs.get('history').split(' ') : [];
  904.  
  905. if ( $UI_pref_history_arr.length == 1 ) {
  906. $query_prefs.set('selected',$UI_pref_history_arr.shift(0));
  907. $query_prefs.delete('history');
  908. } else if ( $UI_pref_history_arr.length > 1 ) {
  909. $query_prefs.set('selected',$UI_pref_history_arr.shift(0));
  910. $query_prefs.set('history',$UI_pref_history_arr.join('+'));
  911. } else {
  912. $query_prefs.delete('selected');
  913. }
  914. let $qstr = $query_prefs.toString().length == 0 ? '' : '?';
  915. $query_prefs.sort();
  916. $parent_dir_link = $protocol + $location_arr.slice(0,-2).join('/') +'/'+ $qstr + decodeURIComponent($query_prefs);
  917.  
  918. $parent_dir_menu.find('a').attr( 'href', $parent_dir_link );
  919. }
  920. setParentLink();
  921.  
  922. $parent_dir_menu.on('click','a',function(e) {
  923. e.preventDefault();
  924. setParentLink();
  925. $('#parent_dir_menu').find('a').attr( 'href', $parent_dir_link );
  926. window.location = $parent_dir_link;
  927. });
  928.  
  929. // MENUS: Directory Parents Links
  930. const parents_dir_menu_arr = function() {
  931. var $parents_dir_menu_items = [];
  932. for ( let i = 1, n = parentLinksArr().length + 1; i < n; i+=1 ) {
  933. $parents_dir_menu_items[i] = menuItems( parentLinksArr(), parentLinksArr(), i, false);
  934. }
  935. $parents_dir_menu_items.pop(); // remove current directory
  936.  
  937. if ( $parents_dir_menu_items[1] !== undefined ) {
  938. let str = $parents_dir_menu_items[1].slice($parents_dir_menu_items[1].lastIndexOf('?'),$parents_dir_menu_items[1].lastIndexOf('">'));
  939.  
  940. for ( let i = 1; i < $parents_dir_menu_items.length; i+=1 ) {
  941. // recursively update selected and history query str
  942. str = str.replace(/selected=\d+&*/,'').replace(/history=(\d+)$/,'selected=$1').replace(/history=(\d+)\+/,'selected=$1&history=').replace(/&$/,'');
  943. $parents_dir_menu_items[i] = $parents_dir_menu_items[i].replace(/\?.+?">/,str +'">');
  944. }
  945. }
  946.  
  947. $parents_dir_menu_items = $parents_dir_menu_items.join('')
  948. .replace(/%20/g,' ').replace(/file=.+?&/g,'&').replace(/file=.+?/g,'').replace(/>\/(?!\/)/g,'>').replace(/>\/\/\/\/</,'>/<').replace(/>\/\/\/</,'>/<');
  949. return $parents_dir_menu_items;
  950. };
  951.  
  952. // MENUS: Shortcuts
  953. var $menu_items = [];
  954. var $shortcuts_links_arr = [];
  955.  
  956. const shortcutMenus = function() {
  957. const $shortcuts = $settings.shortcuts;
  958.  
  959. if ( $shortcuts.length > 0 ) {
  960.  
  961. for ( let i = 0; i < $shortcuts.length; i+=1 ) {
  962. var $links_arr = [];
  963. var $links = $shortcuts[i].links;
  964.  
  965. for ( let j = 0; j < $links.length; j+=1 ) {
  966. if ( !$links[j].link.endsWith('/') ) {
  967. $links[j].link = $links[j].link.slice(0,$links[j].link.lastIndexOf('/') + 1) + '?file=' + $links[j].link.slice($links[j].link.lastIndexOf('/') + 1) ;
  968. }
  969. $shortcuts_links_arr.push($links[j].link);
  970. $links_arr[j] = '<li><a href="'+ $links[j].link +'">' + $links[j].link_name + '</a></li>';
  971. }
  972.  
  973. var $links_arr_str = $links_arr.join('');
  974. $menu_items[i] = '<li class="has_submenu"><a>'+ $shortcuts[i].menu_title +'</a><ul>'+ $links_arr_str +'</ul></li>';
  975. }
  976. $menu_items = $menu_items.join('');
  977. }
  978. return ($shortcuts_links_arr,$menu_items);
  979. }
  980. shortcutMenus();
  981.  
  982. // MENUS: Other menu items
  983. // var $sort_by = $('<li id="sort_by" class="has_submenu ruled"><a href="">Sort by&hellip;</a><ul id="sort_menu"><li><a href="">Name</a></li><li id="sort_by_size"><a href="">Size</a></li><li id="sort_by_date"><a href="">Date</a></li><li><a href="">Kind</a></li><li><a href="">Extension</a></li><li><a href="">Default</a></li></ul>');
  984. var $sort_by = $('<li id="sort_by" class="has_submenu ruled"><span>Sort by&hellip;</span><ul id="sort_menu"><li><span>Name</span></li><li id="sort_by_size"><span>Size</span></li><li id="sort_by_date"><span>Date</span></li><li><span>Kind</span></li><li><span>Extension</span></li><li><span>Default</span></li></ul>');
  985. const $toggle_dark_theme = $('<li id="toggle_dark_theme_menu_item"><a href="#"><span id="dark_theme">Dark Theme</span><span id="default_theme">Default Theme</span></a></li>');
  986. const $default_settings = $('<li><a href="#" id="default_settings" title="Delete URL query string and reload page.">Default Settings</a></li>');
  987. const $export_settings = $('<li><a href="#" id="export_settings" title="Export user settings to text file.">Export Settings</a></li>');
  988. const $donate_link = $('<li><a href="#" id="donate" href="https://paypal.me/mschrauzer" target="_blank">Donate</a></li>');
  989.  
  990. function assembleMenus() { // called by buildUI();
  991. $parents_dir_menu.siblings('ul').append( parents_dir_menu_arr() );
  992. $shortcuts_menu.siblings('ul').append( $menu_items, $sort_by, $toggle_dark_theme, $default_settings, $export_settings, $donate_link );
  993. }
  994.  
  995. // ***** END BUILD MENUS ***** //
  996.  
  997. // ***** DIRECTORY LIST Set Up ***** //
  998.  
  999. // CONVERT VARIOUS SERVER-GENERATED INDEX PAGES TO DEFAULT (Chrome) STRUCTURE
  1000.  
  1001. var $list_items, $rows;
  1002. var yr, mo, dd, pref;
  1003. var factor = {k:1, kb:1, m:1000, mb:1000, g:1000000, gb:1000000, t:1000000000,tb:1000000000}
  1004. var sizeCalc = function(x,y) {
  1005. y === '' ? y = 'k' : y = y;
  1006. return x == '-' ? 0 : x * factor[ y.toString().toLowerCase() ]; // convert size abbreviation to multiplaction factor
  1007. }
  1008.  
  1009. // Convert matched date and size strings to default size and date format, calculate data-values for sorting
  1010. var sizeAndDate = function(match,p1,p2,p3,p4,p5,p6,offset,string) {
  1011. p1.toString().length == 4 ? (yr = p1, dd = p3) : (yr = p3, dd = p1);
  1012. p2.toString().length == 3 ? mo = "JanFebMarAprMayJunJulAugSepOctNovDec".indexOf(p2)/3 + 1 : mo = p2; // convert month into number, or use number
  1013. p6 = undefined|'' ? p6 = 1 : p6 = p6;
  1014. mo.toString().length == 1 ? pref = 0 : pref = null; // add a zero before single digit months (i.e., multiply year by 10)
  1015. return '<td class="size details" data-value="'+ sizeCalc(p5,p6) +'">' + [p5,p6].join('') + '</td><td class="date details" data-value="' + [yr, pref, mo, dd, p4.replace(/:/, '') ].join('') +'">'+ [yr, mo, dd].join('-') +' '+ [p4].join(':') +'</td></tr>\n';
  1016. };
  1017.  
  1018. // AMPPS index pages
  1019. if ( window.location.href.indexOf('localhost') > -1 && $body.find('> title:contains(AMPPS)').length ) {
  1020. $dir_list = $body.find('> center > div > table');
  1021. $dir_list.remove();
  1022. $body.addClass('is_ampps').empty().append($dir_list);
  1023. }
  1024.  
  1025. // Pre type
  1026. if ( window.location.href.indexOf('file:') == -1 && $body.find('> pre').length > 0 ) {
  1027. $list_items = $body.find('> pre').html();
  1028. $body.addClass('is_converted_pre').find('> pre').remove();
  1029.  
  1030. $list_items = $list_items
  1031. .replace(/^.+?\n/, '') // delete header row
  1032. .replace(/^.+?Parent Directory.+?\n/gm, '\n') // delete parent directory link
  1033. .replace(/^\s*<a.+?>\.\.\/<\/a>\s*$/gm, '') // delete parent directory link (list type)
  1034. .replace(/^\s*<img .+?>\s*/gm, '') // delete all images (icons)
  1035. .replace(/^\s*<address>.+?<\/address>\s*/,'') // delete address element
  1036. .replace(/^\s*<a href="(.+?)">(.+?)<\/a>\s*/gm, '<tr><td data-value="$2"><a class="icon" href="$1">$2</a></td>') // create name cell
  1037. .replace(/(\d{2}|\d{4})[\/-](\d{2}|\w{3})[\/-](\d{2}|\d{4})\s+?([\d:]+)\s*([\.\d]+)([A-z]{0,2})\s*$/gm, sizeAndDate); // dates (search dd-mmm-yyyy hh:mm) and sizes
  1038. }
  1039.  
  1040. // List type
  1041. if ( window.location.href.indexOf('file:') == -1 && $body.find('> ul').length > 0 ) {
  1042. $list_items = $body.find('> ul').html();
  1043. $body.addClass('is_converted_list').find('> ul').remove();
  1044.  
  1045. if ( $list_items.indexOf('</a></li>') != -1 ) {
  1046. $list_items = $list_items.replace(/<li>\s*/g,'<tr><td data-value="">').replace(/\s*<\/li>/g, '</td></tr>\n');
  1047. $list_items = $list_items.replace(/data-value=""><a href="(.+?)">\s+(.+?)<\/a>/g, 'data-value="$2"><a class="icon" href="$1">$2<\/a>');
  1048. $list_items = $list_items.replace(/<tr.+?Parent Directory.+?<\/tr>/,'');
  1049. }
  1050. }
  1051.  
  1052. // Table type
  1053. if ( window.location.href.indexOf('file:') == -1 && $body.find('> table').length > 0 ) {
  1054. $list_items = $body.find('> table tbody').length > -1 ? $body.find('> table tbody').html() : $body.find('> table');
  1055. $body.addClass('is_converted_table').find('> table').remove();
  1056.  
  1057. $list_items = $list_items
  1058. // .replace(/<img .+?>/g, '') // delete all images (icons)
  1059. .replace(/<tr><th.+?\/th><\/tr>/g, '') // delete empty tr
  1060. .replace(/<tr><td.+?Parent Dir.+?<\/td><\/tr>/, '') // delete empty tr
  1061. .replace(/(?:<th>|<td>|<td \w+="\w+">)(?:&nbsp;|\s*)(?:<\/th>|<\/td>)/g, '') // delete empty th or td
  1062. .replace(/<td>\s*<a href="(.+?)">(.+?)<\/a>\s*<\/td>/g, '<td class="name" data-value="\L$2"><a class="icon" href="$1">$2</a></td>')
  1063. .replace(/<tr><td.*?><img (.+?)><\/td><td (.+?)><a (.+?)>(.+?)<\/a>/g, '<tr><td $2><a $3><img $1>$4</a>') // move default image to .name td
  1064. .replace(/(?:<td>|<td \w+="\w+">)(\d{4}|\d{2})-(\d{2}|\w{3})-(\d{4}|\d{2})\s*([\d:-]*)\s*<\/td>(?:<td>|<td \w+="\w+">)\s*([-\.\d]*)([A-z]*)\s*<\/td><\/tr>/g, sizeAndDate)
  1065. .replace(/^\s*<address>.+?<\/address>\s*$/,''); // delete address line
  1066. }
  1067.  
  1068. // Convert Gecko-style local index pages
  1069. if ( window.location.href.indexOf('file:') > -1 && $userAgent.indexOf('Firefox') > -1 ) {
  1070. $body.find('> table').find('> thead').find('th[colspan="2"]').attr('colspan',1);
  1071. $list_items = $body.find('> table').find('> tbody').html();
  1072. $body.find('> table').remove();
  1073.  
  1074. $list_items = $list_items
  1075. .replace(/<table class="ellipsis"><tbody><tr><td>/g,'')
  1076. .replace(/<\/a><\/td><\/tr><\/tbody><\/table>/g,'')
  1077. .replace(/sortable-data="\d+?/g,'data-value="')
  1078. .replace(/<td data-value="(\d+)">([\d-]+)<\/td>\s*<td>(.+?)<\/td>/g,'<td data-value="$1">$2, $3</td>')
  1079. .replace(/<td(.+?)><a/g,'<td class="name" $1><a class="icon"');
  1080. }
  1081.  
  1082. // Default Chrome type
  1083. if ( window.location.href.indexOf('file:') > -1 && $body.find('> table').length > 0 && $userAgent.indexOf('Firefox') < 0) {
  1084. $list_items = $body.find('> table tbody').html();
  1085. $body.addClass('is_default_table').find('> table').remove();
  1086. }
  1087.  
  1088. // ***** END CONVERT INDEX PAGES ***** //
  1089.  
  1090. $dir_list_body.append($list_items);
  1091.  
  1092. var $dir_list_row = $dir_list_body.find('> tr');
  1093. var $dir_list_item_name = $dir_list_row.find('a').parent('td');
  1094. var $dir_list_details = $dir_list_item_name.nextAll();
  1095. $dir_list_details.addClass('details');
  1096.  
  1097. // ***** END CONVERT INDEX PAGES ***** //
  1098.  
  1099. // ***** PREPARE DIR LIST ***** //
  1100.  
  1101. var $this_link;
  1102. var $this_text;
  1103. var $this_ext;
  1104.  
  1105. // Get dir_list item row
  1106. var thisRow = function(x) {
  1107. return $(x).closest('#dir_list > tbody > tr').length ? $(x).closest('#dir_list > tbody > tr') : $(x).closest('#dir_list > li');
  1108. };
  1109. // Get dir_list item link
  1110. var thisLink = function(x) {
  1111. return $(x).find('a').length ? decodeURI( $(x).find('a').attr('href') ) : decodeURI( $(x).attr('href') ) ;
  1112. };
  1113. // Get dir_list item name
  1114. var thisText = function(x) {
  1115. return $(x).find('a').length ? decodeURIComponent( $(x).find('a').text().toLowerCase() ) : decodeURIComponent( $(x).text().toLowerCase() );
  1116. };
  1117. var thisExt = function(x) {
  1118. return thisLink(x).toLowerCase().endsWith('app/') ? 'app' : thisLink(x).toLowerCase().endsWith('/') ? '/' : $(x).find('a').attr('href').toLowerCase().slice( $(x).find('a').attr('href').lastIndexOf('.') + 1 );
  1119. }
  1120.  
  1121. // Dir_List: Classify items
  1122. function classifyDirListItems() {
  1123.  
  1124. $dir_list_row.each(function() {
  1125.  
  1126. let row = {};
  1127. $this_link = thisLink(this);
  1128. $this_text = thisText(this);
  1129. $this_ext = thisExt(this);
  1130.  
  1131. // Add ID and lowercase .name data
  1132. $(this).attr('id',$(this).index()).find('td').first().addClass('name').attr( 'data-value',$this_text.toLowerCase() ); // add ID
  1133.  
  1134. if ( $this_ext === '/' ) {
  1135. $(this).addClass('dir').attr('data-ext','dir');
  1136. } else {
  1137. $(this).addClass('file').attr('data-ext',$this_ext);
  1138. }
  1139. if ( $this_text.indexOf('.') === 0 ) {
  1140. $(this).addClass('invisible');
  1141. }
  1142.  
  1143. $(this).append('<td class="detailsColumn details kind" data-value="File">File</td>')
  1144. .append('<td class="detailsColumn details ext" data-value="'+ $this_ext +'">&nbsp;</td>');//attr('data-ext',$this_ext);
  1145.  
  1146. // For listed supported file types
  1147. for ( let key in $row_types ) {
  1148.  
  1149. if ( $row_types[key].includes($this_ext) ) {
  1150.  
  1151. let $display_key = key.charAt(0).toUpperCase() + key.slice(1);
  1152. let kind = $display_key === undefined ? 'File' : $display_key;
  1153. $(this).addClass(key).addClass($this_ext).attr('data-type',$display_key);
  1154.  
  1155. if ( key === 'audio' || key === 'video' ) {
  1156. $(this).find('a').prepend('<input type="checkbox" tabindex="-1" checked="true" />');
  1157. }
  1158. $(this).find('td.kind').attr('data-value',kind).text(kind);
  1159. }
  1160. };
  1161. // For ignored and excluded items
  1162. for ( let key in $row_settings ) {
  1163. if ( $row_settings[key].includes($this_ext) ) {
  1164. $(this).addClass(key);
  1165. }
  1166. if ( $row_settings[key].includes($this_ext) && key == 'ignore' ) {
  1167. }
  1168. }
  1169.  
  1170. }); // end classify dir_list items
  1171. }
  1172.  
  1173. // ***** END DIR_LIST SETUP ***** //
  1174.  
  1175. // ***** UI SETUP ***** //
  1176.  
  1177. // Add body classes depending on available content types
  1178. function bodyClasses() {
  1179. if ( $userAgent.indexOf('Chrome') > 0 ) {
  1180. $body.addClass('is_chrome');
  1181. }
  1182. if ( $userAgent.indexOf('Firefox') > 0 ) {
  1183. $body.addClass('is_gecko');
  1184. }
  1185. if ( $dir_list.find('.image').length ) {
  1186. $body.add($grid_btn).addClass('has_images');
  1187. }
  1188. if ( $dir_list.find('.font').length ) {
  1189. $body.add($grid_btn).addClass('has_fonts');
  1190. }
  1191. if ( $dir_list.find('.audio').length ) {
  1192. $dir_list.add($body).addClass('has_audio');
  1193. }
  1194. if ( $dir_list.find('.video').length ) {
  1195. $dir_list.add($body).addClass('has_video');
  1196. }
  1197. }
  1198.  
  1199. function init() {
  1200. addStyles();
  1201.  
  1202. if ( navigator.platform.indexOf('Win') > -1 || window.location.href.indexOf('file:') < 0 ) {
  1203. $inv_checkbox.hide();
  1204. }
  1205. if ( $settings.hide_invisibles === true ) {
  1206. $inv_checkbox.find('input').prop('checked',true);
  1207. $body.addClass('hide_invisibles');
  1208. }
  1209. if ( $settings.hide_ignored_files === true ) {
  1210. $body.addClass('hide_ignored');
  1211. }
  1212. if ( $UI_pref_details === false ) {
  1213. $dir_list.addClass('show_details');
  1214. $dir_list_body.css({'top':$dir_list_head.height() + 1 +'px'});
  1215. $details_btn.find('span').first().hide().next().show();
  1216. }
  1217. if ( $UI_pref_theme === true ) {
  1218. $body.addClass('dark_theme');
  1219. }
  1220. if ( $settings.use_custom_icons === true ) {
  1221. $body.addClass('use_custom_icons');
  1222. } else {
  1223. $body.addClass('use_default_icons');
  1224. }
  1225. // Hide audio controls if $settings.autoload_audio = false (but show when audio file is selected)
  1226. if ( $settings.autoload_audio === true ) {
  1227. $body.addClass('autoload_audio');
  1228. }
  1229. if ( $settings.autoload_index_files === true ) {
  1230. autoSelectFile();
  1231. }
  1232.  
  1233. }
  1234. init();
  1235.  
  1236. function buildUI() {
  1237. assembleMenus();
  1238. classifyDirListItems();
  1239. bodyClasses();
  1240.  
  1241. $dir_list.appendTo($sidebar);
  1242.  
  1243. var $main_content = $('<table id="main_content"><tbody><tr></tr></tbody></table>');
  1244. $main_content.find('tr').append( $sidebar_wrapper, $content_pane );
  1245.  
  1246. $body.empty().prepend($main_content);
  1247.  
  1248. if ( $body.hasClass('is_converted_list') ) {
  1249. $('#sort_by_size, #sort_by_date, #size, #date').addClass('disabled');
  1250. }
  1251.  
  1252. if ( $body.hasClass('is_converted_table') || $body.hasClass('is_converted_pre') ) {
  1253. if ( $dir_list.find('td.size').length < 1 ) {
  1254. $('#sort_by_size,th#size').addClass('disabled');
  1255. };
  1256. if ( $dir_list.find('td.date').length < 1 ) {
  1257. $('#sort_by_date,th#date').addClass('disabled');
  1258. };
  1259. }
  1260. }
  1261. buildUI();
  1262.  
  1263. // ***** BASIC UI FUNCTIONS ***** //
  1264.  
  1265. function showMenus(el) {
  1266. var $position = $(el).position();
  1267. $(el).find('> ul').css({'top':$position.top + $(el).innerHeight() + 'px'}).toggle().parent('td').siblings('td').find('.menu').hide();
  1268. }
  1269. $parents_dir_menu.add($shortcuts_menu).parent('td').on('click',function(e) {
  1270. e.stopPropagation();
  1271. showMenus(this);
  1272. });
  1273.  
  1274. function hideMenus() {
  1275. $('.menu').hide();
  1276. }
  1277. $(document).on('click', hideMenus );
  1278.  
  1279. function toggleInvisibles() {
  1280. $body.toggleClass('hide_invisibles');
  1281. }
  1282. $inv_checkbox.on('click','input', toggleInvisibles );
  1283.  
  1284. function toggleDarkMode(e) {
  1285. e.preventDefault();
  1286. $body.toggleClass('dark_theme');
  1287. toggleQuery('dark_theme');
  1288. }
  1289. $toggle_dark_theme.on('click', toggleDarkMode );
  1290.  
  1291. function toggleDetails() {
  1292. $dir_list.toggleClass('show_details');
  1293. $dir_list_body.css({'top':$dir_list_head.height() + 1 +'px'});
  1294. $details_btn.find('span').toggle();
  1295. toggleQuery('hide_details');
  1296. }
  1297. $details_btn.on('click', toggleDetails );
  1298.  
  1299. function toggleSidebar() {
  1300. $body.toggleClass('has_hidden_sidebar');
  1301. setContentHeight();
  1302. }
  1303. $toggle_sidebar.on('click', toggleSidebar);
  1304.  
  1305. // Default settings: remove queries;
  1306. function defaultSettings() {
  1307. $location = window.location.href;
  1308. $location = $location.slice(0,$location.lastIndexOf('?'));
  1309. window.location.assign($location);
  1310. }
  1311.  
  1312. $default_settings.on('click', function() {
  1313. if (window.confirm( "Are you sure you want to reset all your temporary UI settings to the defaults in your user_settings?" ) ) {
  1314. defaultSettings();
  1315. }
  1316. });
  1317.  
  1318. function setContentHeight() {
  1319. var $dir_list_head_height = $dir_list_head.length ? $dir_list_head.height() : 0;
  1320. var $content_headerHeight = $content_header.outerHeight();
  1321. $dir_list.add($dir_list_wrapper).css({'height':window.innerHeight - $sidebar_header.outerHeight() });
  1322. $dir_list.find($dir_list_body).css({'top': $dir_list_head_height });
  1323. $content_image.css({'top':$content_headerHeight });
  1324. $content_grid.add($content_embed).add($content_iframe).add($content_font).css({'height':window.innerHeight - $content_headerHeight,'top':$content_headerHeight });
  1325. $content_scale.css({'top':$content_headerHeight + 13 });
  1326. $content_media.find('> div:first-of-type').add('#content_media > div:nth-of-type(2)').css({'height':$audio.height() });
  1327. $('#checkbox_div').css({'margin-right': - $('#checkbox_div').outerWidth() });
  1328. }
  1329. setContentHeight();
  1330. $('window').on('resize', setContentHeight );
  1331.  
  1332. function setContentTitle() {
  1333.  
  1334. if ( $content_pane.hasClass('has_grid_content') && $content_grid.hasClass('has_grid') ) {
  1335. $content_title.empty().prepend('Images and Fonts from: ' + $current_dir_name);
  1336. return;
  1337. }
  1338. if ( $content_pane.hasClass('has_grid_content') && $content_grid.hasClass('has_image_grid') ) {
  1339. $content_title.empty().prepend('Images from: ' + $current_dir_name);
  1340. return;
  1341. }
  1342. if ( $content_pane.hasClass('has_grid_content') && $content_grid.hasClass('has_font_grid') ) {
  1343. $content_title.empty().prepend('Fonts from: ' + $current_dir_name);
  1344. return;
  1345. }
  1346. if ( $body.hasClass('has_audio') && $settings.autoload_audio === true ) {
  1347. $content_title.empty().prepend( thisText($('.playing')) );
  1348. return;
  1349. }
  1350. $content_title.empty().prepend( $('.selected').find('a').text() );
  1351.  
  1352. if ( $('.selected').hasClass('ignore') ) {
  1353. $content_title.prepend('Ignored content: ' );
  1354. }
  1355. if ( $('.selected').hasClass('dir') ) {
  1356. $content_title.prepend('Index of: ' );
  1357. }
  1358. }
  1359.  
  1360. function getDimensions(link, callback) {
  1361. var img = new Image();
  1362. img.src = link;
  1363. img.onload = function() { callback( this.width, this.height ); };
  1364. }
  1365.  
  1366. function scrollSidebar(row) {
  1367. var $block = $userAgent.indexOf('Firefox') > -1 ? $block = 'start' : $block = 'nearest';
  1368. if (row[0] !== undefined ) {
  1369. row[0].scrollIntoView({ behavior:'smooth', block:$block, inline:'nearest' });
  1370. }
  1371. }
  1372.  
  1373. function scrollGrid(item) {
  1374. var $block = $userAgent.indexOf('Firefox') > -1 ? $block = 'start' : $block = 'nearest';
  1375. if (item[0] !== undefined ) {
  1376. item[0].scrollIntoView({ behavior:'smooth', block:$block, inline:'nearest' });
  1377. }
  1378. }
  1379.  
  1380. // ***** SORTING ***** //
  1381.  
  1382. var $sort_all = $dir_list_body.find('tr');
  1383. var $sort_dirs = $dir_list_body.find('tr.dir').not('.app');
  1384. var $sort_files = $dir_list_body.find('tr').not('.dir').add('.app');
  1385. var currentIndex, prevIndex;
  1386. var $default_index = $('#thead').find('#default').index();
  1387. var $sort_direction = 1;
  1388.  
  1389. var dataName = function(x) {
  1390. return $(x).find('td').eq(0).data('value').toString();
  1391. };
  1392. var dataData = function(x,index) {
  1393. return $(x).find('td').eq(index).attr('data-value') === undefined ? '' : $(x).find('td').eq(index).attr('data-value').toString();
  1394. };
  1395.  
  1396. // comparison functions
  1397. var sortByName = function(x,y,sortDirection) {
  1398. return sortDirection === 1 ? dataName(x).localeCompare(dataName(y)) : dataName(y).localeCompare(dataName(x));
  1399. };
  1400.  
  1401. var sortByData = function(x,y,index,sortDirection) { // sort by data then by name
  1402. if ( sortDirection === 1 ) {
  1403. return dataData(x,index).localeCompare(dataData(y,index), undefined, { numeric:true }) === 0 ? dataName(x).localeCompare(dataName(y)) : dataData(x,index).localeCompare(dataData(y,index), undefined, { numeric:true });
  1404. } else {
  1405. return dataData(y,index).localeCompare(dataData(x,index), undefined, { numeric:true }) === 0 ? dataName(y).localeCompare(dataName(x)) : dataData(y,index).localeCompare(dataData(x,index), undefined, { numeric:true });
  1406. }
  1407. };
  1408.  
  1409. // sort elements: sort by name if index = 0, else sort by data-value.
  1410. var sortEls = function(els,index,sortDirection) {
  1411. els.detach().sort(function(a,b) {
  1412. return index === 0 ? sortByName(a,b,sortDirection) : sortByData(a,b,index,sortDirection);
  1413. });
  1414. };
  1415.  
  1416. function sortDirList(index, sortDirection) {
  1417. $dir_list_row.removeClass('sorted');
  1418.  
  1419. if ( index == $default_index ) {
  1420. if ( index == $default_index ) {
  1421. index = 0;
  1422. }
  1423.  
  1424. sortEls( $sort_dirs, index, sortDirection );
  1425. sortEls( $sort_files, index, sortDirection );
  1426.  
  1427. if (sortDirection === 1 ) {
  1428. $.each($sort_dirs, function() { $dir_list_body.append(this); });
  1429. $.each($sort_files, function() { $dir_list_body.append(this); });
  1430. } else {
  1431. $.each($sort_files, function() { $dir_list_body.append(this); });
  1432. $.each($sort_dirs, function() { $dir_list_body.append(this); });
  1433. }
  1434.  
  1435. } else if ( $settings.dirs_on_top === false || index == 0 && index !== $default_index ) {
  1436. sortEls( $sort_all, index, sortDirection );
  1437. $.each($sort_all, function() { $dir_list_body.append(this); });
  1438. $body.removeClass('is_dirs_on_top');
  1439.  
  1440. } else if ( $settings.dirs_on_top === true || index !== 0 ) {
  1441. sortEls( $sort_dirs, index, sortDirection );
  1442. sortEls( $sort_files, index, sortDirection );
  1443.  
  1444. if (sortDirection === 1 ) {
  1445. $.each($sort_dirs, function() { $dir_list_body.append(this); });
  1446. $.each($sort_files, function() { $dir_list_body.append(this); });
  1447. } else {
  1448. $.each($sort_files, function() { $dir_list_body.append(this); });
  1449. $.each($sort_dirs, function() { $dir_list_body.append(this); });
  1450. }
  1451. }
  1452. if ( index === $('#thead').find('#kind').index() || index === $('#thead').find('#ext').index() ) {
  1453. $body.addClass('is_dirs_on_top');
  1454. addSortStyle( index, sortDirection );
  1455. }
  1456. }
  1457.  
  1458. function addSortStyle(index, sortDirection) {
  1459. var $this_value, $next_value;
  1460. $dir_list_row.removeClass('sorted');
  1461.  
  1462. $dir_list_row.filter(':visible').not('.dir').add('.app').each(function() {
  1463. $this_value = $(this).find('td').eq(index).attr('data-value');
  1464. $next_value = $(this).next('tr:visible').find('td').eq(index).attr('data-value');
  1465.  
  1466. if ( $this_value !== $next_value ) {
  1467. $(this).addClass('sorted');
  1468. }
  1469.  
  1470. if ( $(this).attr('data-type') !== $(this).next('tr:visible').attr('data-type') ) {
  1471. $(this).addClass('sorted');
  1472. }
  1473. });
  1474.  
  1475. if ( sortDirection == 1 ) {
  1476. $dir_list_row.filter('.dir').last().addClass('sorted');
  1477. }
  1478. }
  1479.  
  1480. // Sort on click
  1481. function clickSort(x,y) { // x = clicked sort link, y = bool (false ? don't set query_prefs)
  1482. currentIndex = x.index();
  1483.  
  1484. if ( currentIndex != prevIndex ) { // Only reverse sort order after clicking a sort column once.
  1485. prevIndex = currentIndex;
  1486. $sort_direction = 1;
  1487. }
  1488.  
  1489. sortDirList(currentIndex, $sort_direction);
  1490. $sort_direction = -1 * $sort_direction;
  1491.  
  1492. x.toggleClass('up').addClass('checked').siblings().removeClass('checked up');
  1493. $('#sort_menu').find('li').eq(currentIndex).addClass('checked').siblings().removeClass('checked');
  1494.  
  1495. setContentHeight();
  1496.  
  1497. if ( y === true ) {
  1498. $query_prefs = getQueryPrefs();
  1499. $query_prefs.set('sort',$('#sort_menu').find('li').eq(currentIndex).text().toLowerCase() );
  1500. updateQuery();
  1501. }
  1502. }
  1503.  
  1504. // Sort on clicking dir_list sort header
  1505. $('#thead').on('click','th:not(.disabled)',function(e) {
  1506. e.preventDefault();
  1507. var i = $(this).index();
  1508. clickSort( $(this),true)
  1509. });
  1510.  
  1511. // Sort Menu: click the list header that contains the selected menu text.
  1512. $('#sort_menu').on('click','li:not(.disabled)',function(e) {
  1513. e.preventDefault();
  1514. e.stopPropagation();
  1515. $(this).addClass('checked').siblings().removeClass('checked');
  1516. $('#theader th[id*="'+ thisText($(this)).slice(0,2) +'" i]').click();
  1517. setContentHeight();
  1518. });
  1519.  
  1520. // Initialize page
  1521. // var $sort = $settings.default_sort.toLowerCase();
  1522. function initialSort() {
  1523. switch ( $UI_pref_sort ) {
  1524. case 'name':
  1525. i = 0;
  1526. break;
  1527. case 'size':
  1528. i = 1;
  1529. break;
  1530. case 'date':
  1531. i = 2;
  1532. break;
  1533. case 'kind':
  1534. i = 3;
  1535. break;
  1536. case 'ext':
  1537. i = 4;
  1538. break;
  1539. default:
  1540. i = 5;
  1541. }
  1542. $('#theader').find('th').eq(i).click();
  1543. }
  1544. initialSort();
  1545.  
  1546. // ***** END SORTING ***** //
  1547.  
  1548. // ***** END BASIC UI FUNCTIONS ***** //
  1549.  
  1550. // ***** CONTENT PREVIEW ***** //
  1551.  
  1552. var $selected = $dir_list_row.filter('.selected');
  1553. var $playing = $dir_list_row.filter('.playing');
  1554.  
  1555. // Select row on click and set classes for $content_pane
  1556. function selectThis(row) {
  1557. // If autoload_audio === true, navigate audio and other files separately; otherwise, treat audio files like any other
  1558. if ( $settings.autoload_audio == true ) {
  1559. if ( row.hasClass('audio') || row.hasClass('video') ) {
  1560. row.addClass('playing').siblings().removeClass('playing hovered');
  1561. $content_pane.addClass('has_audio_content');
  1562. } else {
  1563. row.addClass('selected').siblings().removeClass('selected hovered');
  1564. // previewThis( row, thisLink(row) );
  1565. }
  1566. }
  1567. if ( $settings.autoload_audio == false ) {
  1568. row.addClass('selected').siblings().removeClass('selected playing hovered');
  1569. if ( row.hasClass('audio') || row.hasClass('video') ) {
  1570. row.addClass('playing');
  1571. $content_pane.addClass('has_audio_content');
  1572. } else {
  1573. $content_pane.removeClass('has_audio_content');
  1574. }
  1575. }
  1576.  
  1577. if ( row.hasClass('dir') ) {
  1578. closeThis();
  1579. closeGrid();
  1580. $content_pane.removeClass().addClass('has_dir_content');
  1581. }
  1582.  
  1583. // Select corresponding grid item
  1584. 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();
  1585. if ( $content_pane.hasClass('has_grid_content') ) {
  1586. $grid_selected.addClass('selected').siblings().removeClass('selected');
  1587. $grid_selected = $content_grid.find('.selected');
  1588. // scrollGrid( $grid_selected ); // not working reliably together with scroll sidebar
  1589. }
  1590.  
  1591. setContentTitle();
  1592. scrollSidebar(row);
  1593. }
  1594.  
  1595. function showIgnored() {
  1596. closeThis();
  1597. $content_pane.addClass('has_ignored_content');
  1598. $content_title.append(' (Ignored content)' );
  1599. }
  1600.  
  1601. function showMedia(row) {
  1602. if ( $settings.autoload_audio == false ) { closeContent(); }
  1603. $content_pane.addClass('has_audio_content');
  1604. $body.hasClass('is_playing') ? $task = 'stop' : $task = 'play';
  1605. $playing = row;
  1606. playThis( $playing.index('.audio:not(.unchecked)'),$task );
  1607. }
  1608.  
  1609. function showImage(row,link) {
  1610. $content_pane.addClass('has_image_content');
  1611. $content_image.find('img').removeClass('zoom_img').attr('src',link).attr('style','');
  1612. getDimensions( link, function( width, height ) {
  1613. if ( !$body.is('.has_audio') ) {
  1614. $content_title.append(' <span style="text-transform:lowercase;">(' + width + 'px &times; ' + height + 'px</span>)' );
  1615. }
  1616. });
  1617. $content_grid.find('a[href="' + thisLink(row) + '"]').parent('div').addClass('selected').siblings().removeClass('selected');
  1618. }
  1619.  
  1620. function showPdf(link) {
  1621. $content_pane.addClass('has_pdf_content');
  1622. $content_embed.attr('type','application/pdf').attr('src',link + '?#zoom=100&scrollbar=1&toolbar=1&navpanes=1');
  1623. }
  1624.  
  1625. function showFile(link) {
  1626. $content_pane.addClass('has_file_content');
  1627. $content_iframe.attr('src',link);
  1628. }
  1629.  
  1630. function showFont(row,link) {
  1631. var $font_family = thisText(row);
  1632.  
  1633. if ( $font_family_arr.indexOf($font_family) == -1 ) {
  1634. $font_family_arr.push($font_family);
  1635. $font_preview_styles.append('@font-face { font-family: "'+ $font_family +'"; src: url("'+ link +'"); }'); // only add style if it doesn't exist
  1636. }
  1637.  
  1638. $content_pane.addClass('has_font_content');
  1639. $content_font.css({ 'font-family':'"'+ $font_family +'"' });
  1640. }
  1641.  
  1642. // Show selected content
  1643. function previewThis(row,link) {
  1644. if ( $content_pane.hasClass('has_grid_content') ) {
  1645. hideGrid();
  1646. }
  1647. if ( row.hasClass('unchecked') ) {
  1648. // return;
  1649. }
  1650. if ( row.hasClass('ignore') ) {
  1651. showIgnored();
  1652. return;
  1653. }
  1654. if ( row.hasClass('audio') ) {
  1655. showMedia(row);
  1656. return;
  1657. }
  1658. if ( row.hasClass('font') ) {
  1659. showFont(row,link);
  1660. return;
  1661. }
  1662. if ( row.hasClass('image') ) {
  1663. showImage(row,link);
  1664. return;
  1665. }
  1666. if ( row.hasClass('pdf') ) {
  1667. showPdf(link);
  1668. return;
  1669. }
  1670. if ( row.hasClass('file') || row.hasClass('dir')) {
  1671. showFile(link);
  1672. return;
  1673. }
  1674. }
  1675.  
  1676. // ***** MAIN CLICK FUNCTIONS FOR PREVIEWING CONTENT ***** //
  1677.  
  1678. // CLICK ROW & SET UI
  1679. function clickDirListLink(row) {
  1680. hideMenus();
  1681. closeContent();
  1682. selectThis( thisRow(row) );
  1683. previewThis( thisRow(row), thisLink(row) );
  1684. setContentTitle();
  1685. setContentHeight();
  1686. }
  1687.  
  1688. // CLICK ROW
  1689. function clickRow(row) {
  1690. // toggle play/pause, or click list item
  1691. if ( $body.hasClass('is_playing') && thisRow( row ).hasClass('playing') ) {
  1692. playAudio('pause');
  1693. } else if ( $body.hasClass('is_paused') && thisRow( row ).hasClass('playing') ) {
  1694. playAudio('play');
  1695. } else {
  1696. clickDirListLink( row );
  1697. }
  1698. }
  1699. $dir_list_row.on('click','a', function(e) {
  1700. e.preventDefault();
  1701. e.stopPropagation();
  1702. clickRow( $(this) );
  1703. });
  1704.  
  1705. // DOUBLE-CLICK DIRECTORY
  1706. function doubleClickRow(row) {
  1707. if ( row.hasClass('dir') ) {
  1708. $query_prefs = getQueryPrefs();
  1709. var $history = $query_prefs.get('history') === null ? '' : '+'+ $query_prefs.get('history');
  1710. var $current_index = row.prevAll('.dir:visible:not(.ignore)').length;
  1711. $history = $current_index + $history; // update history
  1712. $query_prefs.delete('selected');
  1713. $query_prefs.set('history',$history);
  1714. updateQuery();
  1715. window.location = row.find('a').attr('href') +'?'+ $query_prefs;
  1716. }
  1717. }
  1718. $dir_list_row.filter('.dir').on('dblclick', function(e) {
  1719. e.preventDefault();
  1720. e.stopPropagation();
  1721. doubleClickRow( $(this) );
  1722. });
  1723.  
  1724. // Prev and Next Buttons
  1725. function prevNextItem(e) {
  1726. e = $.Event("keydown");
  1727.  
  1728. if ( $(this).attr('id') === 'prev_btn' ) {
  1729. $playing.length ? e.key = 'ArrowUp' : e.key = 'ArrowLeft';
  1730. } else {
  1731. $playing.length ? e.key = 'ArrowDown' : e.key = 'ArrowRight';
  1732. }
  1733.  
  1734. $dir_list.trigger(e);
  1735. }
  1736. $content_pane.on( 'click','#prev_btn, #next_btn', prevNextItem );
  1737.  
  1738. // AUTOLOAD index files or files from the file shortcut list;
  1739. function autoSelectFile() {
  1740. if ( $UI_pref_selected !== '' ) {
  1741. let $selectThisRow = $dir_list.find('tbody tr:not(.invisible)').eq($UI_pref_selected);
  1742. clickThis( $selectThisRow );
  1743. } else
  1744. if ( $UI_pref_file !== '' ) {
  1745. let $selectThisRow = $dir_list_row.find('a:contains('+ $UI_pref_file +')');
  1746. clickThis( $selectThisRow );
  1747. } else
  1748. if ( $dir_list.find( 'a[href*="/index."]').length > -1 ) {
  1749. $dir_list.find( 'a[href*="/index."]').click();
  1750. return;
  1751. }
  1752. }
  1753. autoSelectFile();
  1754.  
  1755. // File shortcuts: load directory, then auto-select file
  1756. function showFileShortcut(item) {
  1757. $this_link = thisLink(item);
  1758. window.location = $this_link.slice( 0, $this_link.lastIndexOf('/') );
  1759. }
  1760.  
  1761. // RESET CONTENT
  1762.  
  1763. function resetAudio() {
  1764. var $this_src = $audio.find('source').attr('src');
  1765. $audio.find('source').attr('src',$this_src);
  1766. $audio.trigger('load');
  1767. return;
  1768. }
  1769.  
  1770. function resetFont() {
  1771. $content_font.css({'font-size':'1em'});
  1772. }
  1773.  
  1774. function resetGrid() {
  1775. $content_grid.attr('style','');
  1776. $content_grid.find('.image_grid_item, img').attr('style','');
  1777. setContentHeight();
  1778. }
  1779.  
  1780. function resetImage() {
  1781. $content_image.attr('data-image-scale-factor',1).find('img').attr('style','');
  1782. }
  1783.  
  1784. function reloadThis() {
  1785. if ( $content_pane.hasClass('has_dir_content') || $content_pane.attr('class') === '' || $('#content_pane[class]').length < 1 ) {
  1786. window.location = window.location.href;
  1787. }
  1788. if ( $content_pane.hasClass('has_font_content') || $content_pane.hasClass('has_image_content') ) {
  1789. // resetAudio();
  1790. resetFont();
  1791. resetImage();
  1792. }
  1793. if ( $content_pane.hasClass('has_grid_content') ) {
  1794. $grid_btn.click();
  1795. }
  1796. if ( $content_pane.hasClass('has_file_content') ) {
  1797. $('.selected').find('a').click();
  1798. }
  1799. if ( $content_pane.hasClass('has_audio_content') ) {
  1800. $audio.prop('currentTime', 0);
  1801. playAudio('stop');
  1802. }
  1803. setContentHeight();
  1804. }
  1805. $content_reload_btn.on('click', reloadThis );
  1806.  
  1807. // CLOSE CONTENT (Close button or Cmd/Ctrl + W)
  1808.  
  1809. function closeContent() {
  1810. $content_embed.removeAttr('src');
  1811. $content_iframe.removeAttr('src');
  1812. $content_font.css({'font-family':''});
  1813. $content_image.find('img').removeAttr('src');
  1814. resetImage();
  1815. $content_pane.removeClass('has_image_content has_font_content has_file_content has_pdf_content has_ignored_content')
  1816. if ( $settings.autoload_audio === true ) {
  1817. //$audio.trigger('pause').find('source').attr('src','');
  1818. } else {
  1819. $audio.trigger('pause').find('source').attr('src','');
  1820. $body.removeClass('is_playing').removeClass('is_paused');
  1821. $content_pane.removeClass('has_audio_content');
  1822. }
  1823. }
  1824.  
  1825. function closeThis() {
  1826. hideMenus();
  1827.  
  1828. if ( $content_pane.hasClass('has_grid_content') ) {
  1829. closeGrid();
  1830. }
  1831. else if ( $content_pane.hasClass('has_hidden_grid') ) {
  1832. closeContent();
  1833. $content_pane.removeClass().addClass('has_grid_content');
  1834. } else {
  1835. $content_pane.removeClass();
  1836. closeContent();
  1837. }
  1838. setContentTitle();
  1839. }
  1840. $content_close_btn.on( 'click', closeThis );
  1841.  
  1842. $('#close_audio').on('click',function() {
  1843. $audio.trigger('pause').find('source').attr('src','');
  1844. $body.removeClass('is_paused');
  1845. $content_pane.removeClass('has_audio_content');
  1846. setContentHeight();
  1847. });
  1848.  
  1849. // ***** KEYBOARD EVENTS AND NAVIGATION ***** //
  1850.  
  1851. function clickThis(el) {
  1852. el.find('a').length > 0 ? el.find('a').click() : el.click();
  1853. }
  1854.  
  1855. function navigateToThis(row) {
  1856. if ( $content_pane.hasClass('has_grid_content') ) {
  1857. selectThis(row);
  1858. } else {
  1859. clickThis(row);
  1860. }
  1861. }
  1862.  
  1863. // Select dir_list_row by typed string
  1864. var str = '',
  1865. timer;
  1866.  
  1867. function timeoutID() {
  1868. timer = window.setTimeout( function() {
  1869. str = '';
  1870. }, 1500 );
  1871. };
  1872.  
  1873. function alphaNav(e) {
  1874. if (typeof timer == 'number' ) {
  1875. window.clearTimeout( timer );
  1876. timer = 0; // id
  1877. }
  1878. timeoutID();
  1879. str += e.key
  1880. // Select row; todo: select next row by nearest letter
  1881. $('#dir_list').find('td.name[data-value^="'+ str +'"]').first().find('a').click();
  1882. }
  1883.  
  1884. // KEYBOARD EVENTS
  1885. $body.on('keydown',$dir_list,function(e) {
  1886.  
  1887. $dir_list_row = $dir_list.find('tbody tr:not(.unchecked)').filter(':visible');
  1888. $selected = $dir_list.find('tbody tr.selected');
  1889. var $selected_href = thisLink($selected);
  1890. $playing = $dir_list_row.filter('.playing');
  1891. var $time;
  1892.  
  1893. var firstRow = function(className) {
  1894. return className == undefined ? $dir_list_row.first() : $dir_list_row.filter( className ).first();
  1895. }
  1896. var lastRow = function(className) {
  1897. return className == undefined ? $dir_list_row.last() : $dir_list_row.filter( className ).last();
  1898. }
  1899. var nextRow = function(className) {
  1900. return className == undefined ? $selected.nextAll(':visible:not(.unchecked)').first() : $selected.nextAll( className ).filter(':visible:not(.unchecked)').first();
  1901. }
  1902. var prevRow = function(className) {
  1903. return className == undefined ? $selected.prevAll(':visible:not(.unchecked)').first() : $selected.prevAll( className ).filter(':visible:not(.unchecked)').first();
  1904. }
  1905.  
  1906. switch ( e.key ) {
  1907.  
  1908. case 'ArrowUp':
  1909.  
  1910. if ( (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) ) {
  1911. $('#parent_dir_menu a').trigger('click');
  1912. break;
  1913. }
  1914.  
  1915. if ( $('*[contentEditable="true"]').is(':focus') ) {
  1916. return; // Allow arrow navigation within content_editable elemnets
  1917. }
  1918.  
  1919. e.preventDefault();
  1920.  
  1921. if ( $settings.autoload_audio === true ) {
  1922. prevRow(':not(.audio, .video)').length < 1 || $selected.length < 1 ? clickThis( lastRow(':not(.audio, .video)') ) : clickThis( prevRow(':not(.audio, .video)') );
  1923. }
  1924. if ( $settings.autoload_audio === false ) {
  1925. prevRow().length < 1 || $selected.length < 1 ? ( scrollSidebar( lastRow() ), clickThis( lastRow() ) ) : clickThis( prevRow() );
  1926. }
  1927.  
  1928. ( prevRow().length < 1 || $selected.length < 1 ) && $settings.autoload_audio === false ? scrollSidebar( lastRow() ) : scrollSidebar( $('.selected') );
  1929. break;
  1930.  
  1931. case 'ArrowDown':
  1932.  
  1933. if ( (e.ctrl || e.metaKey) && $selected.hasClass('app') && $settings.apps_as_dirs === false ) {
  1934. return;
  1935. } else if ( $('*[contentEditable="true"]' ).is(':focus') ) {
  1936. return;
  1937. } else if ( (e.ctrl || e.metaKey) && $selected.hasClass('dir') ) {
  1938. $selected.find('a').trigger('dblclick');
  1939. break;
  1940. }
  1941.  
  1942. e.preventDefault();
  1943.  
  1944. if ( $settings.autoload_audio === true ) { // select non-audio items
  1945. nextRow(':not(.audio, .video)').length < 1 || $selected.length < 1 ? clickThis( firstRow(':not(.audio, .video)') ) : clickThis( nextRow(':not(.audio, .video)') );
  1946. }
  1947. if ( $settings.autoload_audio === false ) { // select any item
  1948. nextRow().length < 1 || $selected.length < 1 ? ( scrollSidebar( firstRow() ),clickThis( firstRow() ) ) : clickThis( nextRow() );
  1949. }
  1950.  
  1951. nextRow().length < 1 || $selected.length < 1 ? scrollSidebar( firstRow() ) : scrollSidebar( $('.selected') );
  1952. break;
  1953.  
  1954. case 'ArrowLeft':
  1955.  
  1956. if ( (e.ctrl || e.metaKey) || ( e.ctrl && e.metaKey ) ) {
  1957. return;
  1958. } else if ( $('*[contentEditable="true"]').is(':focus') ) {
  1959. return;
  1960. }
  1961.  
  1962. // Skip audio -30s
  1963. if ( e.altKey && e.shiftKey ) {
  1964. $time = $audio.prop('currentTime');
  1965. $audio.prop('currentTime', $time - 30);
  1966. return;
  1967. }
  1968. // Skip audio -10s
  1969. if ( e.altKey ) {
  1970. $time = $audio.prop('currentTime');
  1971. $audio.prop('currentTime', $time - 10);
  1972. return;
  1973. }
  1974.  
  1975. // Navigate Grid or Images
  1976. if ( $body.hasClass('has_audio') && !$content_grid.hasClass('has_grid') && $playing.length < 1 ) {
  1977. playThis( $dir_list.find('.audio:not(.unchecked)').length - 1,'stop');
  1978. setContentHeight();
  1979. return;
  1980. } else if ( $playing.length > 0 ) {
  1981. prevTrack('stop');
  1982. setContentHeight();
  1983. return;
  1984. } else if ( $selected.length < 1 || $selected.hasClass('dir') || firstRow().hasClass('selected') ) {
  1985. if ( $body.hasClass('has_fonts') ) {
  1986. navigateToThis( lastRow('.font') );
  1987. } else if ( $body.hasClass('has_images') ) {
  1988. navigateToThis( lastRow('.image') );
  1989. } else {
  1990. navigateToThis( lastRow() );
  1991. }
  1992. } else if ( $selected.hasClass('image') ) {
  1993. if ( prevRow('.image').length > 0 ) {
  1994. navigateToThis( prevRow('.image') );
  1995. } else if ( lastRow('.font').length > 0 ) {
  1996. navigateToThis( lastRow('.font') );
  1997. } else {
  1998. navigateToThis( lastRow('.image') )
  1999. }
  2000. } else if ( $selected.hasClass('font') ) {
  2001. if ( prevRow('.font').length > 0 ) {
  2002. navigateToThis( prevRow('.font') );
  2003. } else if ( lastRow('.image').length > 0 ) {
  2004. navigateToThis( lastRow('.image') );
  2005. } else {
  2006. navigateToThis( lastRow('.font') )
  2007. }
  2008. } else {
  2009. lastRow('.image').length ? navigateToThis( lastRow('.font') ) : navigateToThis( lastRow('.image') );
  2010. }
  2011.  
  2012. scrollSidebar( $('tr.selected') );
  2013. if ( $content_pane.hasClass('has_grid_content') ) {
  2014. scrollGrid( $('div.selected') );
  2015. }
  2016. scrollSidebar( $('tr.selected') );
  2017. resetImage();
  2018. break;
  2019.  
  2020. case 'ArrowRight':
  2021.  
  2022. if ( (e.ctrl || e.metaKey) || ( e.ctrl && e.metaKey ) ) {
  2023. return;
  2024. } else if ( $('*[contentEditable="true"]').is(':focus') || $selected.hasClass('dir ignore') ) {
  2025. return;
  2026. }
  2027.  
  2028. // Skip audio 30s
  2029. if ( e.altKey && e.shiftKey ) {
  2030. $time = $audio.prop('currentTime');
  2031. $audio.prop('currentTime', $time + 30);
  2032. return;
  2033. }
  2034. // Skip audio 10s
  2035. if ( e.altKey ) {
  2036. $time = $audio.prop('currentTime');
  2037. $audio.prop('currentTime', $time + 10);
  2038. return;
  2039. }
  2040.  
  2041. if ( $selected.hasClass('dir') && ( !$body.hasClass('has_audio') && !$content_pane.hasClass('has_grid_content') ) ) {
  2042. $selected.find('a').trigger('dblclick');
  2043. return;
  2044. }
  2045.  
  2046. // Navigate Grid or Images
  2047. if ( $body.hasClass('has_audio') && !$content_grid.hasClass('has_grid') && $playing.length < 1 ) {
  2048. playThis(0,'stop');
  2049. return;
  2050. } else if ( $playing.length > 0 ) {
  2051. nextTrack('stop');
  2052. return;
  2053. } else if ( $selected.length < 1 || $selected.hasClass('dir') || lastRow().hasClass('selected') ) {
  2054. if ( $body.hasClass('has_images') ) {
  2055. navigateToThis( firstRow('.image') );
  2056. } else if ( $body.hasClass('has_fonts') ) {
  2057. navigateToThis( firstRow('.font') );
  2058. } else {
  2059. navigateToThis( firstRow() );
  2060. }
  2061. } else if ( $selected.hasClass('font') ) {
  2062. if ( nextRow('.font').length > 0 ) {
  2063. navigateToThis( nextRow('.font') );
  2064. } else if ( firstRow('.image').length > 0 ) {
  2065. navigateToThis( firstRow('.image') );
  2066. } else {
  2067. navigateToThis( firstRow('.font') )
  2068. }
  2069. } else if ( $selected.hasClass('image') ) {
  2070. if ( nextRow('.image').length > 0 ) {
  2071. navigateToThis( nextRow('.image') );
  2072. } else if ( firstRow('.font').length > 0 ) {
  2073. navigateToThis( firstRow('.font') );
  2074. } else {
  2075. navigateToThis( firstRow('.image') )
  2076. }
  2077. } else {
  2078. firstRow('.image').length ? navigateToThis( firstRow('.image') ) : navigateToThis( firstRow('.font') );
  2079. }
  2080.  
  2081. setContentHeight();
  2082. scrollSidebar( $('tr.selected') );
  2083. if ( $content_pane.hasClass('has_grid_content') ) {
  2084. scrollGrid( $('div.selected') );
  2085. }
  2086. resetImage();
  2087. break;
  2088.  
  2089. case ' ': // space bar
  2090. if ( !e.metaKey && !e.ctrlKey && !e.altKey ) {
  2091. alphaNav(e);
  2092. }
  2093. // Play/pause audio
  2094. if ( $body.hasClass('has_audio') && $body.hasClass('is_playing') ) {
  2095. e.preventDefault();
  2096. playAudio('pause');
  2097. } else if ( $body.hasClass('has_audio') && $body.hasClass('is_paused') || $playing.length ) {
  2098. e.preventDefault();
  2099. playAudio('play');
  2100. }
  2101. break;
  2102.  
  2103. case 'Enter':
  2104. // Open directories (or ignore)
  2105. if ( $selected.hasClass('app') && $settings.apps_as_dirs === false ) {
  2106. break;
  2107. } else {
  2108. if ( $selected.hasClass('dir') ) {
  2109. $selected.find('a').trigger('dblclick');
  2110. } else {
  2111. $selected.find('a').click();
  2112. }
  2113. }
  2114. break;
  2115.  
  2116. case 'A': case 'B': case 'C': case '': case 'E': case 'F': case '': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case '': case 'P': case 'Q': case '': case 'S': case 'T': case 'U': case 'V': case '': case 'X': case 'Y': case 'Z':
  2117. case 'a': case 'b': case 'c': case '': case 'e': case 'f': case '': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case '': case 'p': case 'q': case '': case 's': case 't': case 'u': case 'v': case '': case 'x': case 'y': case 'z':
  2118. case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9':
  2119. if ( !e.metaKey && !e.ctrlKey && !e.altKey ) {
  2120. alphaNav(e);
  2121. }
  2122. break;
  2123.  
  2124. case 'd':
  2125. // Toggle Invisibles with Command-i
  2126. if ( (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) ) {
  2127. e.preventDefault();
  2128. e.stopPropagation();
  2129. $details_btn.click();
  2130. }
  2131. if ( !e.metaKey && !e.ctrlKey && !e.altKey ) {
  2132. alphaNav(e);
  2133. }
  2134. break;
  2135.  
  2136. case 'g':
  2137. // Show image Grid
  2138. if ( (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) ) {
  2139. e.preventDefault();
  2140. e.stopPropagation();
  2141. $grid_btn.click();
  2142. }
  2143. if ( !e.metaKey && !e.ctrlKey && !e.altKey ) {
  2144. alphaNav(e);
  2145. }
  2146. break;
  2147.  
  2148. case 'i':
  2149. // Cmd/Ctrl + I: Toggle Invisibles
  2150. if ( (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) ) {
  2151. e.preventDefault();
  2152. e.stopPropagation();
  2153. $inv_checkbox.find('input').click();
  2154. }
  2155. if ( !e.metaKey && !e.ctrlKey && !e.altKey ) {
  2156. alphaNav(e);
  2157. }
  2158. break;
  2159.  
  2160. case 'o':
  2161. // Cmd/Ctrl + Shift + O: Open selected item in new window
  2162. if ( !e.metaKey && !e.ctrlKey && !e.altKey ) {
  2163. alphaNav(e);
  2164. }
  2165. if ( (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) && e.shiftKey ) {
  2166. window.open($selected_href);
  2167. }
  2168. break;
  2169.  
  2170. case 'r':
  2171. // Cmd/Ctrl + Shift + R: Refresh
  2172. if ( (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) ) {
  2173. e.preventDefault();
  2174. $content_reload_btn.click();
  2175. }
  2176. if ( !e.metaKey && !e.ctrlKey && !e.altKey ) {
  2177. alphaNav(e);
  2178. }
  2179. break;
  2180.  
  2181. case 'w':
  2182. // Close content pane if Close button visible with Cmd/Crtl + W
  2183. // Doesn't work in Firefox: can't override default keybinding
  2184. if ( (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) && ( $content_pane.is('[class*="has_"]') ) ) {
  2185. e.preventDefault();
  2186. e.stopPropagation();
  2187. $content_close_btn.click();
  2188. }
  2189. if ( !e.metaKey && !e.ctrlKey && !e.altKey ) {
  2190. alphaNav(e);
  2191. }
  2192. break;
  2193.  
  2194. case '.':
  2195. if ( !e.metaKey && !e.ctrlKey && !e.altKey ) {
  2196. alphaNav(e);
  2197. }
  2198. // Increase font preview size
  2199. if ( (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) && e.shiftKey ) {
  2200. $('#increase').click();
  2201. }
  2202. break;
  2203.  
  2204. case ',':
  2205. if ( !e.metaKey && !e.ctrlKey && !e.altKey ) {
  2206. alphaNav(e);
  2207. }
  2208. // Decrease font preview size
  2209. if ( (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) && e.shiftKey ) {
  2210. $('#decrease').click();
  2211. }
  2212. break;
  2213.  
  2214. case 'tab':
  2215. break;
  2216.  
  2217. } // end switch
  2218.  
  2219. });
  2220.  
  2221. // ***** END KEYBOARD EVENTS ***** //
  2222.  
  2223.  
  2224. // ***** GRIDS ***** //
  2225.  
  2226. const fontGridItems = function() {
  2227. var $font_grid_items_arr = [];
  2228. $dir_list_row.filter('.font').each(function() {
  2229.  
  2230. let link = thisLink(this);
  2231. let $font_family = thisText($(this));
  2232.  
  2233. if ( $font_family_arr.indexOf($font_family) == -1 ) {
  2234. $font_family_arr.push($font_family);
  2235. $font_preview_styles.append('@font-face { font-family: "'+ $font_family +'"; src: url("'+ link +'"); }'); // only add style if it doesn't exist
  2236. }
  2237.  
  2238. $font_grid_item_el.attr('href',link).css({ 'font-family':'"'+ $font_family +'"' }).empty().append( $font_family.slice( 0,$font_family.lastIndexOf('.') ) );
  2239. $font_grid_items_arr.push($font_grid_item_el.clone());
  2240. });
  2241. return $font_grid_items_arr;
  2242. };
  2243.  
  2244. const imageGridItems = function() {
  2245. var $image_grid_items_arr = [];
  2246. $dir_list_row.filter('.image').each(function() {
  2247.  
  2248. $this_link = thisLink(this);
  2249. $this_ext = thisExt(this);
  2250. let exts = $row_types.image.filter( ext => $.inArray(ext, $row_settings.ignore) == -1 );
  2251.  
  2252. if ( $.inArray( $this_ext, exts ) > -1 ) { // if this row file ext is in the image extension array
  2253. $image_grid_item_el.find('a').attr('href',$this_link).find('img').attr('src',$this_link);
  2254. $image_grid_items_arr.push( $image_grid_item_el.clone() );
  2255. }
  2256. });
  2257. return $image_grid_items_arr;
  2258. };
  2259.  
  2260. function showFontGrid() {
  2261. $content_pane.removeClass('has_hidden_grid').addClass('has_grid_content');
  2262. $content_grid.empty().append( fontGridItems() );
  2263. $content_grid.removeClass().addClass('has_font_grid');
  2264. }
  2265.  
  2266. function showImageGrid() {
  2267. $content_pane.removeClass('has_hidden_grid').addClass('has_grid_content');
  2268. $content_grid.empty().append( imageGridItems() );
  2269. $content_grid.removeClass().addClass('has_image_grid');
  2270. }
  2271.  
  2272. function showGrids() {
  2273. $content_pane.removeClass('has_hidden_grid').addClass('has_grid_content');
  2274. $content_grid.empty().append( imageGridItems(), fontGridItems() );
  2275. $content_grid.removeClass().addClass('has_grid');
  2276. }
  2277.  
  2278. $grid_btn.on('click', function(e) {
  2279. e.stopPropagation();
  2280. resetGrid();
  2281. showGrids();
  2282. setContentTitle();
  2283. setContentHeight();
  2284. });
  2285.  
  2286. $('#show_image_grid').on('click', function(e) {
  2287. e.stopPropagation();
  2288. resetGrid();
  2289. showImageGrid();
  2290. setContentTitle();
  2291. setContentHeight();
  2292. });
  2293.  
  2294. $('#show_font_grid').on('click', function(e) {
  2295. e.stopPropagation();
  2296. resetGrid();
  2297. showFontGrid();
  2298. setContentTitle();
  2299. setContentHeight();
  2300. });
  2301.  
  2302. function hideGrid() {
  2303. $content_pane.removeClass('has_grid_content').addClass('has_hidden_grid');
  2304. }
  2305.  
  2306. function closeGrid() {
  2307. $content_pane.removeClass('has_grid_content');
  2308. $content_grid.removeClass().empty().css({'font-size':'1em'});
  2309. }
  2310.  
  2311. // GRID ITEMS
  2312.  
  2313. // Grid Item Hover
  2314. $content_grid.on('mouseenter','> div:not(".selected")',function() {
  2315. thisRow($dir_list.find('a[href*="' + thisLink(this) + '"]')).addClass('hovered');
  2316. }).on('mouseleave','> div:not(".selected")',function() {
  2317. thisRow($dir_list.find('a[href*="' + thisLink(this) + '"]')).removeClass('hovered');
  2318. });
  2319.  
  2320. $dir_list_row.hover(function() {
  2321. // Highlight corresponding grid item
  2322. if ( $content_grid.is(':visible') ) {
  2323. $this_link = thisLink(this);
  2324. $content_grid.find('[href="' + $this_link + '"]').closest('div').addClass('hovered');
  2325. }
  2326. }, function() {
  2327. if ( $content_grid.is(':visible') ) {
  2328. $content_grid.find('.hovered').removeClass('hovered');
  2329. }
  2330. });
  2331.  
  2332. function gridItemClick(e) {
  2333. e.preventDefault();
  2334. $dir_list.find('a[href*="' + thisLink(this) + '"]').click();
  2335. }
  2336. $content_grid.on('click','> div[class*="item"]', gridItemClick );
  2337.  
  2338.  
  2339. // ***** SCALE PREVIEW ITEMS ***** //
  2340.  
  2341. var $em = parseInt(getComputedStyle(document.body).fontSize); // pts/em
  2342. var $font_incr = 1.125; // scale factor for font size increase/decrease
  2343. var $image_incr = 1.125;
  2344. var $image_width;
  2345. var $image_height;
  2346. var $image_scale_factor;
  2347. var $image_grid_item_size;
  2348. var $grid_scale_factor;
  2349.  
  2350. var fontSize = function(el) {
  2351. return parseFloat(el.css('font-size'));
  2352. };
  2353.  
  2354. function scaleFonts(incr, y) {
  2355. if ( y === -1 ) { incr = 1/incr; }
  2356. if ( $content_pane.hasClass('has_grid_content') && ( $content_grid.hasClass('has_font_grid') || $content_grid.hasClass('has_grid') ) ) {
  2357. $content_grid.css({'font-size':( fontSize($content_grid)/$em * incr ) +'em'});
  2358. return;
  2359. }
  2360. if ( $content_pane.hasClass('has_font_content') ) {
  2361. $content_font.css({'font-size':( fontSize($content_font)/$em * incr ) +'em'});
  2362. return;
  2363. }
  2364. }
  2365. function scaleImages(incr, y) {
  2366. if (y === -1 ) { incr = 1/incr; }
  2367. if ( $content_pane.hasClass('has_image_content') && !$content_pane.hasClass('has_grid_content') ) {
  2368. $image_width = parseFloat( $content_image.find('img').width() ) * incr; // increment image size
  2369. $image_height = parseFloat( $content_image.find('img').height() ) * incr; // increment image size
  2370. $image_scale_factor = parseFloat( $content_image.attr('data-image-scale-factor') ); // get scale factor
  2371.  
  2372. $content_image.attr('data-image-scale-factor', parseFloat( $image_scale_factor ) * incr ).find('img').removeClass('zoom_img').css({'width':$image_width +'px', 'max-width':'none', 'max-height':'none','top':'0','transform':'translateY(0)'});
  2373.  
  2374. $content_image.scrollLeft( ( $image_width - $(window).width() )/2 ) ;
  2375. if ( ( ( $image_height - $(window).height() )/2 ) > 0 ) {
  2376. $content_image.scrollTop( ( $image_height - $(window).height() )/2 );
  2377. }
  2378. return;
  2379. }
  2380. if ( $content_pane.hasClass('has_grid_content') && ( $content_grid.hasClass('has_image_grid') || $content_grid.hasClass('has_grid') ) ) {
  2381. $image_grid_item_size = parseFloat( $('.image_grid_item').width() * incr);
  2382. $grid_scale_factor = parseFloat( $content_grid.attr('data-grid-scale-factor') );
  2383.  
  2384. $content_grid.attr('data-grid-scale-factor', parseFloat( $grid_scale_factor ) * incr ).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))' });
  2385. $content_grid.find('.image_grid_item').css({'width':$image_grid_item_size +'px', 'height':$image_grid_item_size +'px'}); // grid items are square, so width = height
  2386. $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'});
  2387. return;
  2388. }
  2389. }
  2390.  
  2391. function scalePreviewItems(y) { // combine scaling into one function
  2392. scaleImages( $image_incr, y );
  2393. scaleFonts( $font_incr, y );
  2394. }
  2395.  
  2396. $('#scale').on('click','span',function() {
  2397. if ( $(this).attr('id') === 'increase' ) {
  2398. scalePreviewItems(1);
  2399. }
  2400. if ( $(this).attr('id') === 'decrease' ) {
  2401. scalePreviewItems(-1);
  2402. }
  2403. });
  2404.  
  2405. function zoomImage(x,e) {
  2406. $this_link = $(x).attr('src');
  2407. var $offset = $(x).offset();
  2408. var $this_width = $(x).width();
  2409. var $this_height = $(x).height();
  2410.  
  2411. $(x).toggleClass('zoom_img');
  2412. if ( $(x).attr('style') !== '' || $(x).attr('style') !== undefined ) {
  2413. $(x).attr('style','');
  2414. }
  2415. if ( $this_link !== undefined ) {
  2416. getDimensions( $this_link, function( width, height ) {
  2417. $content_image.scrollLeft( (e.pageX - $offset.left) * width/($this_width + 13) - ( e.pageX - $offset.left ) - 52 ) ;
  2418. $content_image.scrollTop( (e.pageY - $offset.top) * height/($this_height + 13) - ( e.pageY - $offset.top ) - 52 );
  2419. });
  2420. }
  2421. }
  2422. $content_image.on('click', 'img', function(e) {
  2423. zoomImage( this,e )
  2424. });
  2425.  
  2426. // ***** END SCALE PREVIEW ITEMS ***** //
  2427.  
  2428.  
  2429. // ***** AUDIO CONTENT ***** //
  2430.  
  2431. var $count = 0;
  2432. var $playlist;
  2433. var $shuffleList = [];
  2434. var $next;
  2435. var $task;
  2436.  
  2437. function updatePlaylist() {
  2438. $playlist = [];
  2439. $dir_list_row.filter('.audio:not(".unchecked")').each(function() {
  2440. $this_link = thisLink(this);
  2441. $playlist.push($this_link);
  2442. });
  2443. return $playlist;
  2444. }
  2445.  
  2446. function initShuffleList() {
  2447. updatePlaylist();
  2448. $shuffleList = [];
  2449. for ( let i = 0; i < $playlist.length; i += 1 ) {
  2450. $shuffleList.push(i);
  2451. }
  2452. return $shuffleList;
  2453. }
  2454.  
  2455. function updateCount() {
  2456. $count = $playing.index('.audio:not(.unchecked)');
  2457. }
  2458.  
  2459. function toggleChecked(e) {
  2460. e.stopPropagation();
  2461. $(this).blur();
  2462. thisRow(this).toggleClass('unchecked');
  2463. updateCount();
  2464. updatePlaylist();
  2465. initShuffleList();
  2466. }
  2467. $dir_list_row.filter('.audio').on('click','input', toggleChecked );
  2468.  
  2469. function toggleAllChecked(e) {
  2470. e.stopPropagation();
  2471. $dir_list_row.find('input').trigger('click');
  2472. updatePlaylist();
  2473. initShuffleList();
  2474. }
  2475. $dir_list.find('#play_toggle').on('click', toggleAllChecked );
  2476.  
  2477. function loadAudio( count ) {
  2478. var $this_track = $dir_list_row.filter('.audio:not(.unchecked)').eq(count);
  2479. $audio.find('source').attr('src', thisLink($this_track) );
  2480. $audio.trigger('load');
  2481. selectThis( $this_track );
  2482. setContentTitle();
  2483. }
  2484.  
  2485. function playAudio( task ) { // Trigger play or stop based on task
  2486. if ( task === 'play' ) {
  2487. $audio.trigger('play');
  2488. $body.removeClass('is_paused').addClass('is_playing');
  2489. }
  2490. if ( task === 'pause' ) {
  2491. $audio.trigger('pause');
  2492. $body.addClass('is_paused').removeClass('is_playing');
  2493. }
  2494. if ( task === 'stop' ) {
  2495. $audio.trigger('pause');
  2496. $audio.prop('currentTime',0);
  2497. $body.addClass('is_paused').removeClass('is_playing');
  2498. }
  2499. }
  2500.  
  2501. function playThis( count, task ) {
  2502. loadAudio( count );
  2503. playAudio( task );
  2504. // scrollSidebar( $('.playing')[0] );
  2505. scrollSidebar( document.getElementsByClassName('playing') );
  2506. }
  2507.  
  2508. function playPause() {
  2509. if ( $body.hasClass('is_playing') ) { playAudio('pause') };
  2510. if ( $body.hasClass('is_paused') ) { playAudio('play') };
  2511. // if ( $('.playing').hasClass('audio') ) { // for audio files only, for now
  2512. // return $audio.get(0).paused ? $audio.get(0).play() : $audio.get(0).pause();
  2513. // }
  2514. }
  2515.  
  2516. function nextTrack(navTask) {
  2517. $('.playing').length == 1 ? $playing = $('.playing') : $playing = $dir_list_row.filter('.audio:not(".unchecked")').first();
  2518.  
  2519. var $next_track = $playing.nextAll('.audio:not(.unchecked)').first();
  2520. var $next_track_index = $next_track.index('.audio:not(".unchecked")');
  2521.  
  2522. if ( $shuffle.find('input').prop('checked') ) { // shuffle
  2523. shuffle();
  2524. } else if ( $playing.hasClass('unchecked') ) { // if playing is unchecked
  2525. $next_track.length === 1 ? $count = $next_track_index : $count = 0;
  2526. $task = 'play';
  2527. } else if ( $next_track.length !== 1 ) { // if no next track, loop to beginning
  2528. $count = 0;
  2529. $loop.find('input').prop('checked') === true ? $task = 'play' : $task = 'stop';
  2530. } else if ( $next_track.length === 1 ) {
  2531. $count = $next_track_index;
  2532. $task = 'play';
  2533. }
  2534. if ( navTask == 'stop' ) { $task = 'stop'; }
  2535.  
  2536. playThis($count, $task);
  2537. navTask = '';
  2538. }
  2539.  
  2540. function prevTrack(navTask) {
  2541. $('.playing').length == 1 ? $playing = $('.playing') : $playing = $dir_list_row.filter('.audio:not(".unchecked")').first();
  2542.  
  2543. var $prev_track = $playing.prevAll('.audio:not(".unchecked")').first();
  2544. var $prev_track_index = $prev_track.index('.audio:not(".unchecked")');
  2545.  
  2546. if ( $shuffle.find('input').prop('checked') ) { // shuffle
  2547. shuffle();
  2548. } else if ( $playing.hasClass('unchecked') ) { // if playing is unchecked
  2549. $prev_track.length === 1 ? $count = $prev_track_index : $count = $playlist.length - 1;
  2550. } else if ( $prev_track.length !== 1 ) { // loop to end and play
  2551. $count = ($('.audio:not(".unchecked")').length - 1);
  2552. $task = 'play';
  2553. } else if ( $prev_track.length != 1 ) { // loop to end and play
  2554. $task = 'stop';
  2555. } else if ( $count > 0 ) { // play prev track
  2556. $count = $prev_track_index;
  2557. $task = 'play';
  2558. }
  2559. if ( navTask == 'stop' ) { $task = 'stop'; }
  2560. playThis( $count, $task );
  2561. navTask = '';
  2562. }
  2563.  
  2564. // Prev/Next Track buttons
  2565. $prev_track.on( 'click', prevTrack );
  2566. $next_track.on( 'click', nextTrack );
  2567.  
  2568. function toggleShuffle() {
  2569. if ( $shuffle.find('input').prop('checked') ) {
  2570. updatePlaylist();
  2571. initShuffleList();
  2572. $count = Math.floor( Math.random() * $shuffleList.length );
  2573. playThis($count,'stop');
  2574. }
  2575. }
  2576. $shuffle.on('click', toggleShuffle );
  2577.  
  2578. function shuffle() {
  2579. if ( $shuffleList.length <= 1 && $loop.find('input').prop('checked') ) { // If loop is checked, and all songs have been shuffled...
  2580. initShuffleList();
  2581. $next = Math.floor( Math.random() * $shuffleList.length ); // choose a random index number from the remaining unplayed songs,
  2582. $count = $shuffleList[ $next ]; // set the playing $count to the index
  2583. $task = 'play';
  2584. } else if ( $shuffleList.length === 0 ) { // else if all songs have been shuffled, select the first song and stop;
  2585. $count = 0;
  2586. $task = 'stop';
  2587. } else {
  2588. $next = Math.floor( Math.random() * $shuffleList.length ); // else choose a random index number from the remaining unplayed songs,
  2589. $count = $shuffleList[ $next ]; // set the playing $count to the index
  2590. $shuffleList.splice( $next, 1 ); // remove the indexed value from the unplayed list,
  2591. $task = 'play';
  2592. }
  2593. }
  2594.  
  2595. function initAudio() {
  2596. if ( $body.hasClass('has_audio') ) {
  2597. if ( $settings.autoload_audio === true ) {
  2598. loadAudio(0);
  2599. autoLoadCoverArt();
  2600. }
  2601. $audio.on('ended', nextTrack );
  2602. }
  2603. }
  2604. initAudio();
  2605.  
  2606. function autoLoadCoverArt() {
  2607. var $matched;
  2608. var $image_titles = [];
  2609. $dir_list_row.filter('.image').each(function() {
  2610. $image_titles.push( thisText(this) );
  2611. });
  2612. // Find image file with name matching default cover art name
  2613. for ( let i = 0; i < $image_titles.length; i++) {
  2614. if ( $image_titles[i].match(/(cover\.|front\.)/) ) {
  2615. $matched = $(document.getElementById('dir_list').getElementsByClassName('image').item(i));
  2616. $matched.addClass('selected');
  2617. showImage( $matched, thisLink( $matched ) );
  2618. return true;
  2619. }
  2620. }
  2621. // If no default cover art found, select first image in dir, if any.
  2622. if ( $('.selected').length < 1 && $dir_list_row.filter('.image').length > 0 ) {
  2623. $matched = $(document.getElementById('dir_list').getElementsByClassName('image').item(0));
  2624. $matched.addClass('selected').siblings().removeClass('selected');
  2625. showImage( $matched, thisLink( $matched ) );
  2626. }
  2627. }
  2628.  
  2629. // ***** END AUDIO CONTENT ***** //
  2630.  
  2631. // ***** OTHER FUNCTIONS ***** //
  2632.  
  2633. // Resize Sidebar/Content Pane
  2634. function resizeSidebar(f) {
  2635. f.stopPropagation();
  2636. var $startX = f.pageX;
  2637. var $sidebar_width = $sidebar_wrapper.width();
  2638. var $window_width = window.innerWidth;
  2639. $sidebar_overlay.show(); // needed to prevent interactions with iframe
  2640. $content_overlay.show(); // needed to prevent interactions with iframe
  2641. $sidebar_wrapper.css({'-webkit-user-select':'none','-moz-user-select':'none','user-select':'none'});
  2642.  
  2643. $(document).on('mousemove',function(e) {
  2644. e.stopPropagation();
  2645. var $deltaX = e.pageX - $startX;
  2646. if ( e.pageX > 200 && e.pageX < $window_width - 200 ) {
  2647. $sidebar_wrapper.css({'width':$sidebar_width + $deltaX + 'px'});
  2648. $content_pane.css({'width':($window_width - $sidebar_width) - $deltaX + 'px'});
  2649. }
  2650. setContentHeight();
  2651. });
  2652. $(document).on('mouseup',function() {
  2653. $sidebar_overlay.hide(); // needed to prevent interactions with iframe
  2654. $content_overlay.hide();
  2655. $sidebar_wrapper.css({'-webkit-user-select':'auto','-moz-user-select':'auto','user-select':'auto'});
  2656. $(document).off('mousemove');
  2657. // update URI query
  2658. $sidebar_width = $sidebar_wrapper.width();
  2659. $query_prefs = getQueryPrefs();
  2660. $query_prefs.set('width',$sidebar_width);
  2661. updateQuery();
  2662. });
  2663. }
  2664. $handle.on('mousedown', resizeSidebar );
  2665.  
  2666. // EXPORT SETTINGS
  2667. var $settings_string = unescape( $settings.toSource() );
  2668. $settings_string = $settings_string.replace('JSON.parse\(unescape\("\{','').replace('\}"))','').replace(/\/",/g,'",').replace(/\/"],/g,'"],').replace(/"(\w+?)":/g,'\n\n$1:\t');
  2669.  
  2670. var save = function(filename, data) {
  2671. var blob = new Blob([data], {type: 'text/html'});
  2672. if ( window.navigator.msSaveOrOpenBlob ) {
  2673. window.navigator.msSaveBlob( blob, filename );
  2674. } else {
  2675. var elem = window.document.createElement('a');
  2676. elem.href = window.URL.createObjectURL(blob);
  2677. elem.download = filename;
  2678. document.body.appendChild(elem);
  2679. elem.click();
  2680. document.body.removeChild(elem);
  2681. URL.revokeObjectURL(blob);
  2682. }
  2683. };
  2684.  
  2685. $export_settings.on('click','a',function(e) {
  2686. e.preventDefault();
  2687. save("settings.txt",$settings_string);
  2688. });
  2689.  
  2690. })();