/* eslint-disable no-mixed-spaces-and-tabs */ /* eslint-disable no-useless-escape */ /* eslint-disable no-fallthrough */ /* eslint-disable indent */ // ==UserScript== // @name Supercharged Local Directory File Browser // @version 5.0.1 // @description Makes file:/// directory ("Index of...") pages (and many server-generated index pages) actually useful. Adds sidebar and preview pane; keyboard navigation and sorting; media playback with shuffle, loop, and playlist (m3u) support; preview, edit, and save markdown/plain text files; preview images and fonts; grid views for images and fonts; user-defined bookmarks; more. // @author gaspar_schot // @license GPL-3.0-or-later // @homepageURL https://openuserjs.org/scripts/gaspar_schot/Supercharged_Local_Directory_File_Browser // @contributionURLhttps://paypal.me/mschrauzer // @include file://* // @include about:blank // @require https://code.jquery.com/jquery-latest.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/markdown-it/10.0.0/markdown-it.js // @require https://cdnjs.cloudflare.com/ajax/libs/markdown-it-footnote/3.0.2/markdown-it-footnote.min.js // @require https://cdn.jsdelivr.net/npm/markdown-it-toc-done-right@2.1.0/dist/markdown-it-toc-made-right.min.js // @require https://cdn.jsdelivr.net/npm/markdown-it-sub@1.0.0/dist/markdown-it-sub.min.js // @require https://cdn.jsdelivr.net/npm/markdown-it-sup@1.0.0/dist/markdown-it-sup.min.js // @require https://cdn.jsdelivr.net/npm/markdown-it-deflist@2.0.3/dist/markdown-it-deflist.min.js // @require https://cdn.jsdelivr.net/npm/markdown-it-multimd-table@3.2.3/dist/markdown-it-multimd-table.min.js // @require https://cdn.jsdelivr.net/npm/markdown-it-center-text@1.0.4/dist/markdown-it-center-text.min.js // @require https://cdn.jsdelivr.net/npm/opentype.js@latest/dist/opentype.min.js // UPDATE URL // NOTE: This script was developed in the lastest version of Vivaldi, running on Mac OS High Sierra. It has been tested in various Chrome- and Gecko-based browsers. // It has been not been tested in any other browsers or OSes. It should work, but please report any issues. // The script does not work on local directories in Safari because Safari does not allow local directories to be browsed, but it will work on remote directories (or on local directories through a local server). // NOTE: By default, Greasemonkey and Tampermonkey will not run scripts on file:/// urls, so for this script to work, you will have to enable that option first. // @namespace https://greasyfork.org/users/16170 // @downloadURL none // ==/UserScript== (function() { 'use strict'; const $ = window.jQuery; // ***** USER SETTINGS ***** // const $settings = { // NOTE: These settings will be overwritten whenever the script is updated. Use the "Export User Settings" menu item to save them. // You can paste the exported settings between the two lines below: //--------------------------------------------------------// bookmarks: // N.B.: Directory links must end with "/", file links must end with another character. // You may add as many menus and links as you like; just copy the example below and edit as needed. // Local directory bookmarks must begin with "file:///"; external bookmarks must begin with the correct protocol ("http://" or "ftp://", etc.). [ // { 'menu_title':'My Sample Menu', // 'links': [ // { 'link_name':'My Directory Link 1', 'link':'file:///Path/To/My/Directory/' }, // { 'link_name':'My Directory Link 2', 'link':'file:///Path/To/My/Directory_2/' }, // { 'link_name':'My External Link', 'link':'https://www.mywebpage.com/' }, // { 'link_name':'My File Link', 'link':'file:///Path/To/My/File.ext' }, // ]}, ], // GENERAL USER SETTINGS alternate_background: true, // If true (default true), alternate sidebar row background color. apps_as_dirs: false, // Un*x/Mac OS only: if true, treat apps as directories; allows app contents to be browsed. This is the default behavior for Chrome. // If false (default), treat apps as ignored files. autoload_media: true, // If true (default: true), the first audio or video file found in a directory will be automatically selected and loaded for playback; also, cover art (if any, will be loaded in the preview pane). autoload_index_files: false, // If true (default: false), automatically select first "index.xxx" (.xxx !== .htm) file found in directory. // Note: the browser will automatically load any index.html files it finds in the directory, so the script will not work properly in such cases. theme: 'light', // Options: 'light' or 'dark' editor_theme: 'light', // Options: if not set (default), uses "theme" setting; otherwise 'light' or 'dark'. sort_by: 'default', // Choose from: 'name', 'size', 'date', 'kind', 'ext', 'default'. // default = Chrome sorting: dirs on top, files alphabetical. dirs_on_top: false, // If true, directories will always be listed firs except when sorting by "name" (since otherwise sorting by "name" would equal "default"). // 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.) grid_font_size: 1, // Default = 1 grid_image_size: 184, // Default = 184 (200px - 16px) show_details: true, // If true (default), hide file and directory details; if false, show them. show_ignored_files: false, // If true, ignored files will appear greyed-out. // If false (default), ignored files will be completely hidden from the file list; ignore_ignored_files: true, // If true (default), ignored file types (see $row_settings below) will be hidden. // If false, ignored files will be shown, but will not be downloaded when navigated. show_invisibles: true, // Un*x/Mac OS only: If true (default), files or directories beginning with a "." will be hidden. show_numbers: true, // If true (default true), number index items UI_font: 'system-ui, sans-serif', // Choose an installed font for the UI; if undefined, use browser defaults instead. UI_font_size: '13px', // Choose a default UI font size; use any standard CSS units. use_custom_icons: true, // if true (default), use custom icons for dirs and files // if false, use browser/server default icons // TEXT EDITING SETTINGS disable_text_editing: false, // If false (default), allow plain text files to be edited. default_text_view: 'preview_text', // Options: 'source_text' or 'preview_text' for text editor. // Note that split_view = true overrides this setting. split_view: true, // If true, show split view on plain text file load. // if true (default), use default preview_text setting. sync_scroll: true // If true (default: true), show split view on plain text file load // if false, use default preview_text setting. //--------------------------------------------------------// // Paste your exported settings between the above two lines. }; // $ROW_TYPES: // DO NOT DELETE ANY EXISTING CATEGORIES BELOW! // Add file extensions for sorting and custom icon display to the existing categories. // You can also define your own new categories, but do not add an extension to more than one row_type category. // Do not add leading "." to the extensions. const $row_types = { // myRowType: ['ext1','ext2'], dir: ['/'], app: ['app/','app','bat','cgi','com','exe','jar','msi','wsf'], alias: ['alias','desktop','directory','lnk','symlink'], archive: ['7z','archive','b6z','bin','bzip','bz2','cbr','dmg','gz','iso','mpkg','pkg','rar','sit','sitx','tar','tar.gz','zip','zipx'], audio: ['aac','aif','aiff','ape','flac','m4a','mp3','ogg','opus','wav'], bin: ['a','dll','dylib','icc','msi','o'], code: ['bak','bash','bash_profile','bashrc','c','cfg','cnf','codes','coffee','conf','csh','cshrc','cson','css','custom_aliases','default','dist','editorconfig','emacs','example','gemspec','gitconfig','gitignore','gitignore_global','h','hd','ini','js','json','jsx','less','list','local','login','logout','lua','mkshrc','old','php','pl','plist','pre-oh-my-zsh','profile','pth','py','rb','rc','rdoc','sass','settings','sh','strings','taskrc','tcl','viminfo','vimrc','vue','xml','yaml','yml','zlogin','zlogout','zpreztorc','zprofile','zsh','zshenv','zshrc'], database: ['accdb','db','dbf','mdb','pdb','sql', 'sqlite','sqlitedb','sqlite3'], ebook: ['azw','azw1','azw3','azw4','epub','ibook','kfx','mobi','tpz'], font: ['otf','ttf','woff','woff2','afm','pfb','pfm','tfm'], graphics: ['afdesign','ai','book','dtp','eps','fm','icml','idml','indd','indt','inx','mif','pmd','pub','qxb','qxd','qxp','sla','swf'], htm: ['htm','html','xhtm','xhtml'], image: ['apng','bmp','gif','ico','jpeg','jpg','png','svg','webp'], ignored_image: ['ai','arw','cr2','dng','eps','jpf','nef','psd','psd','raw','tif','tiff'], markdown: ['md','markdown','mdown','mkdn','mkd','mdwn','mdtxt','mdtext'], office: ['csv','doc','docx','epub','key','numbers','odf','ods','odt','pages','rtf','scriv','wpd','wps','xlr','xls','xlsx','xlm'], pdf: ['pdf'], system: ['DS_Store','ds_store','icon','ics'], text: ['log','nfo','txt'], video: ['m4v','mov','mp4','mpeg','webm'] }; // $ROW_SETTINGS: Ignore or Exclude files by extension const $row_settings = { // Ignore: $row_types or files with extensions added here will not be loaded if selected in the sidebar (prevents the browser from attempting to download the file). ignore: $row_types.archive.concat( 'alias', $row_types.bin, $row_types.database, $row_types.graphics, $row_types.ignored_image, $row_types.office, $row_types.system), // Exclude: Files with these exensions will not be inverted in dark mode exclude: ['htm','html','xhtm','xhtml'] }; // ***** END USER SETTINGS ***** // // ## FEATURES INCLUDE: // - Resizable sidebar and directory/file preview pane. // - Arrow navigation in sidebar: // - Up and Down Arrows select next/prev item. // - Left and Right Arrows select next/prev item of same type. // - Navigate sidebar by typed string. // - Show/Hide file details (size (if avail), date modified (if avail), kind, extension). // - Sort sidebar items by name or file details. // - Default sort = sort by name with folders on top. // - Preview all file types supported natively by browser (html, text, images, pdf, audio, video, etc.) and preview fonts. // - Preview and edit markdown (if your browser supports it) and plain text files, with option to save files locally. // - Markdown rendered with markdownit.js ( https://github.com/markdown-it/markdown-it ). // - Uses Github Markdown styles for preview ( https://github.com/sindresorhus/github-markdown-css ), with a few customizations. // - Support for: // - TOC creation ( `${toc}` ) ( https://github.com/nagaozen/markdown-it-toc-done-right ) // - Multimarkdown table syntax ( https://github.com/RedBug312/markdown-it-multimd-table ) // - Live checkboxes ( `\[ ], [x]` ), allowed in lists and deflists. // - Superscript ( `^sup^` ) ( https://github.com/markdown-it/markdown-it-sup ) // - Subscript ( `~sub~` ) ( https://github.com/markdown-it/markdown-it-sub ) // - Definition lists ( https://github.com/markdown-it/markdown-it-deflist; for syntax, see http://pandoc.org/MANUAL.html#definition-lists ) // - Centered text ( `->centered<-` ) ( https://github.com/jay-hodgson/markdown-it-center-text ) // - Footnotes ( https://github.com/markdown-it/markdown-it-footnote ) // - View source text, preview, or split pane with proportional sync scroll. // - Save edited source text or previewed HTML. // - Create and edit text in separate text editor. // - Audio and video playback, with shuffle, loop, skip audio +/- 10 or 30 sec via keyboard. // - Preview other files (e.g., lyrics or cover art) in same directory while playing audio. // - User setting to autoload cover art (if any images in directory, load "cover.ext" or first image found) // - Grid views for images and fonts. // - Playlists and filelists. // - Open standard .m3u media files; save .m3u file of all media items in current directory. // - Open and save custom .m3u files that may contain directories and _any_ file type. // - Click file or audio title to display .m3u listing for that item. // - User settings (see $settings in code; some settings can be changed via the main menu in the UI and will be remembered in URL query): // - Light or Dark theme. // - Bookmarks for local or remote directories. // - Default image grid size. // - Default UI font size and font-family. // - Default UI font and font-size. // - Default file sorting. // - Sort with directories on top. // - Treat apps as directories (MacOS and *nix only) // - Show or hide invisible files. // - Show or hide ignored files in the ignored files list (see $row_settings in code below $settings). // - Show or hide file details. // - Use custom file icons or browser defaults. // - Autoload index.ext files. // - Autoload cover art in directories with audio files. // - Text editing default view: split, source, or preview. // - Text editing sync scroll: on or off. // ************ J + M + J ************* // // ************************************ // // DON'T EDIT ANYTHING BELOW THIS LINE. // // ************************************ // // ***** GENERAL SETUP ***** // function getBrowser() { switch(true) { case navigator.userAgent.search('Chrome') >= 0: return 'chrome'; case navigator.userAgent.search('Firefox') >= 0: return 'firefox'; case navigator.userAgent.search('MSIE') >= 0: return 'explorer'; case navigator.userAgent.search('Opera') >= 0: return 'opera'; case navigator.userAgent.search('Safari') >= 0 && navigator.userAgent.search('Chrome') < 0: return 'safari'; } } function getOS() { // modded from https://stackoverflow.com/questions/38241480/detect-macos-ios-windows-android-and-linux-os-with-js var platform = window.navigator.platform, macosPlatforms = ['Macintosh', 'MacIntel', 'MacPPC', 'Mac68K'], windowsPlatforms = ['Win32', 'Win64', 'Windows', 'WinCE'], os = null; switch(true) { case macosPlatforms.indexOf(platform) !== -1: os = 'macos'; break; case windowsPlatforms.indexOf(platform) !== -1: os = 'windows'; break; // case iosPlatforms.indexOf(platform) !== -1: os = 'ios'; break; // just in case; // case /Android/.test(userAgent): os = 'android'; break; // just in case; case !os && /Linux/.test(platform): os = 'linux'; break; } return os; } // OTHER // var context = new AudioContext(); // needed to allow continuous playback of audio // PATHS function decodeURIComponentSafe(s) { // Fix "%" error in file name; see https://stackoverflow.com/questions/7449588/why-does-decodeuricomponent-lock-up-my-browser if ( !s ) { return s; } try { return decodeURIComponent(s.replace(/%(?![0-9a-fA-F]{2})/g, '%25') ); // replace % with %25 if not followed by two a-f/number } catch(e) { return s; } } const $protocol = window.location.protocol; const $origin = $protocol +'//'+ window.location.host; let $location = decodeURIComponentSafe( [location.protocol, '//', location.host, location.pathname].join('') ); const $current_dir_path = $location.replace(/([/|_|—])/g,'$1').replace(/\\/g,'/'); // URL w/o query string for display const $current_dir = $location.split('/').slice(-2,-1).toString(); function escapeStr(str) { str = str.replace(/([$?*+()[]|^])/g,'\$1'); return str; } // if URL is a file, change window location to parent dir, add querystring of file name; then autoload file. function loadFile() { let $query_prefs = getQueryPrefs(); $query_prefs.set( 'file', $location.slice($location.lastIndexOf('/') + 1) ); window.location = $location.slice(0,$location.lastIndexOf('/') + 1) +'?'+ $query_prefs; return; } if ( $location.slice($location.lastIndexOf('/')).indexOf('.') !== -1 && !$location.endsWith('/') && window.top === window.self ) { loadFile(); } // QUERY PREFS function getQueryPrefs() { return new URL(window.location).searchParams; } // const initialQueryPrefs = getQueryPrefs(); // set query key/value function setQuery(key, value) { let $query_prefs = getQueryPrefs(); $query_prefs.set( key, value ); updateQuery($query_prefs); } // get query value function getQuery(key) { let $query_prefs = getQueryPrefs(); let value = ''; if ( key === 'width' ) { value = ( !$query_prefs.has(key) || window.innerWidth === 0 ? 30 : Math.round(100 * Number.parseInt($query_prefs.get('width'))/window.innerWidth) ); // percentage } else { value = ( $query_prefs.has(key) ? $query_prefs.get(key) : $settings[key] !== undefined ? $settings[key].toString() : '' ); value = value.replace('%2F','').replace('/',''); // some servers add a '/' to end of query string } return value; } // toggle query key function toggleQuery(key) { let $query_prefs = getQueryPrefs(); let nonBoolPrefs = { 'theme_light': {'theme':'dark'}, 'theme_dark': {'theme':'light'}, 'source_text': {'default_text_view':'preview_text'}, 'preview_text': {'default_text_view':'source_text'}, 'sort_by_default': {'sort_by':'default'}, 'sort_by_name': {'sort_by':'name'}, 'sort_by_size': {'sort_by':'size'}, 'sort_by_date': {'sort_by':'date'}, 'sort_by_kind': {'sort_by':'kind'}, 'sort_by_ext': {'sort_by':'ext'} }; var value, queryValue, settingsValue; if ( nonBoolPrefs[key] !== undefined ) { value = Object.values(nonBoolPrefs[key]).toString(); key = Object.keys(nonBoolPrefs[key]).toString(); // must come after value: i.e., don't redefine key before getting value if ( $settings[key] === value ) { $query_prefs.delete( key ); } else { $query_prefs.set( key, value ); } } else { queryValue = $query_prefs.get(key); settingsValue = $settings[key]; value = ( queryValue === null ? settingsValue.toString() : queryValue.toString() ); value = ( value === 'true' ? 'false' : 'true' ); if ( ( queryValue !== null && queryValue !== settingsValue ) ) { $query_prefs.delete( key ); } else { $query_prefs.set( key, value ); } } updateQuery($query_prefs); } // remove query key function removeQuery(key) { let $query_prefs = getQueryPrefs(); $query_prefs.delete(key); updateQuery($query_prefs); } // update query string function updateQuery(querystr) { querystr = querystr.toString().replace('%2F','').replace('/',''); window.history.replaceState({}, document.title, window.location.pathname +'?'+ querystr); updateParentLinks(); } // Test some array 1 in array 2 or all array 1 in array 2 function arr1InArr2(arr1,arr2,bool) { // bool 'true' = some in array; else all in array if ( bool !== true ) { // 'some' return arr1.some(r => Array.from(arr2).includes(r)); } else { // 'all' return arr1.every(r => Array.from(arr2).includes(r)); } } // ***** SET UP UI ELEMENTS ***** // // Parent and Parents Menus // UTILITIES function updateQueryStr(str) { str = str.replace(/([^\?]*)selected=[^&]*(.*)$/m,'$1$2') // delete current selected query, if any .replace(/([^\?]*)history=(\d+)\+*([^&]*)(&*)(.*)/m,'$1$4$5&selected=$2&history=$3').replace(/&{2,}/g,'&').replace(/\?&/m,'\?'); // format query with selected and history at end if ( str.endsWith('&history=') ) { str = str.replace(/(.+)&history=$/m,'$1'); } // if no history, delete query return str; } // create links function updateParentLinks() { $('#parents_dir_nav').siblings('ul').empty().append( createParentLinkItems() ); } function createParentLinks() { let links = []; let str = decodeURIComponentSafe(window.location.search); str = str.replace('/','').replace('%2F',''); let linkPieces = $location.split('/'); linkPieces = linkPieces.slice(2,-2); // remove beginning and ending empty elements and current directory while ( linkPieces.length > 0 ) { // while there are link pieces... str = updateQueryStr(str); // update selected and history let link = $protocol +'//'+ linkPieces.join('/') +'/'+ str; // assemble link links.push(link); // add to link array linkPieces.pop(); // remove last link piece and repeat... } if ( links.length === 0 ) { links.push($protocol +'///'+ str); } // fix for top level local pages return links; } // create menu items function createParentLinkItems() { let $parent_link_menu_items = []; let $links = createParentLinks(); $('#parent_dir_nav').find('a').attr( 'href', $links[0] ); // set parent link for ( let i = 0; i < $links.length; i++ ) { let display_name = $links[i].slice(0,$links[i].lastIndexOf('?') - 1); display_name = display_name.replace(/\//g,'\/'); let menu_item = '
  • ' + display_name + '/
  • '; $parent_link_menu_items.push(menu_item); } return $parent_link_menu_items; // return parents link items } // MENUS: User bookmarks function bookmarksMenuItems() { const $bookmarks = $settings.bookmarks; let menu_items = []; let $links_arr = []; let $links_arr_str = ''; let $links; if ( $bookmarks.length > 0 ) { for ( let i = 0; i < $bookmarks.length; i+=1 ) { $links = $bookmarks[i].links; // make array of links for ( let j = 0; j < $links.length; j+=1 ) { if ( !$links[j].link.endsWith('/') ) { $links[j].link = $links[j].link.slice(0,$links[j].link.lastIndexOf('/') + 1) + '?file=' + $links[j].link.slice($links[j].link.lastIndexOf('/') + 1) ; } $links[j].link_name = $links[j].link_name.split('/').join('/'); $links_arr[j] = '
  • ' + $links[j].link_name + '
  • '; } $links_arr_str = $links_arr.join(''); menu_items[i] = '
  • '+ $bookmarks[i].menu_title +'
  • '; } menu_items = menu_items.join(''); } return menu_items; } // MENUS: Other menu items // #menu li a::before, .toggle_UI_pref, a, a:, div, li, span, span::before, span::after, #dir_list a.icon span, #warnings h3::before, #content_audio_title td:before const SidebarMenuItems = function() { let sort_by = '
  • Sort by…'; let autoload_media = '
  • Autoload Media
  • '; let theme = '
  • Theme
  • '; let alternate_background = '
  • Alternate Backgrounds
  • '; let show_numbers = '
  • Show Numbers
  • '; let ignore_ignored_files = '
  • Hide Ignored Files
  • '; let text_editing = '
  • Text Editing'; let disable_text_editing = '
  • Text Editing
  • '; let open_playlist = '
  • '; let make_playlist = '
  • Make Playlist/Filelist…
  • '; let open_font = '
  • '; let default_settings = '
  • Default User Settings
  • '; let export_settings = '
  • Export User Settings
  • '; let help_link = '
  • Help
  • '; let contact_link = '
  • Contact
  • '; let donate_link = ''; return sort_by + theme + alternate_background + show_numbers + ignore_ignored_files + autoload_media + text_editing + disable_text_editing + open_playlist + make_playlist + open_font + default_settings + export_settings + help_link + contact_link + donate_link; }; const SidebarHeaderEls = function() { let checked = ''; if ( getQuery('show_invisibles') === 'true' ) { checked = 'checked'; } let parent_link_items = createParentLinkItems(); let parent_link = $(parent_link_items[0]).find('a').attr('href'); parent_link_items = parent_link_items.toString().replace(/<\/li>,
  • /g,'
  • '); let parent_dir_nav = ''; let parents_dir_nav = ''; let menu_nav = ''; let show_details = ''; // "Show/Hide" prefixed in pseudo-element let inv_checkbox = ''; let grid_btn = '
    '; let sorting = '
    Name
    Default
    Size
    Date
    Kind
    Ext
    '; let text_editor_row = '
    Text Editor
    '; let sidebar_header_head = ''; let sidebar_menus = ''; let sidebar_buttons = ''; let sidebar_header_body = ''; let sidebar_header_els = ''; return sidebar_header_els; }; // Sidebar Footer (Stats) const SidebarFooterEls = function() { const stats_summary = '
     
    '; const stats_summary_detailed_total = '
    '; const stats_summary_detailed_dirs = '
    '; const stats_summary_detailed_files = '
    '; const stats_summary_playlist_container = '
    '; const stats_summary_detailed_container = '
    ' + stats_summary_detailed_total + stats_summary_detailed_dirs + stats_summary_detailed_files +'
    '; const stats_details_container = '
    '; const stats = stats_summary + stats_summary_detailed_container + stats_summary_playlist_container + stats_details_container; let dir_list_foot = '
    '+ stats +'
    '; return dir_list_foot; }; // Dir List Elements const SidebarDirListEls = function() { let dir_list_body = ''; let sidebar_dir_list_els = '
    '+ dir_list_body +'
    '; return sidebar_dir_list_els; }; // CONTENT PANE ELEMENTS const ContentHeaderEls = function() { let title_buttons_left = '
    '; let title = '
    '; let title_buttons_right = '
    '; let content_playlist = '
    '; const textPreview = '
    '; const htmlPreview = '
    '; const textEditingUI = toolbar +'
    '+ textSource + textPreview + htmlPreview +'
    '; return textEditingUI; }; // ASSEMBLE SIBEBAR & CONTENT PANE ELEMENTS const MainContent = function() { let width = Number(getQuery('width')); let handle = '
    '; let toggle_sidebar = '
    '; let sidebar = ''; let sidebar_wrapper = ''; let content_pane = '
    '+ ContentHeaderEls() + ContentEls() +'
    '; let utilities = '
    '+ Warnings() +'
    '+ $content_help +'
    '; let main_content = '
    '+ sidebar_wrapper + content_pane + utilities +'
    '; return $(main_content); }; // DEFINE Content Elements const $main_content = MainContent(); const $dir_list_body = $main_content.find('#tbody'); const $dir_list = $main_content.find('#dir_list'); const $content_pane = $main_content.find('#content_pane'); const $audio_player = $main_content.find('#audio'); // const $content_text = $main_content.find('#content_text'); const $content_grid = $main_content.find('#content_grid'); const $content_font = $main_content.find('#content_font'); const $content_image = $main_content.find('#content_image'); // const $content_pdf = $main_content.find('#content_pdf'); const $content_video = $main_content.find('#content_video'); const $content_iframe = $main_content.find('#content_iframe'); // SVG UI ICONS // auto format dark icon colr function SVG_UI_Icon(icon_name) { let svg = SVG_UI_Icons[icon_name]; return 'url("data:image/svg+xml;utf8,'+ svg +'")'; } const SVG_UI_Icons = { 'arrow': '', 'arrow_dark': '', 'bookmark': '', 'bookmark_dark': '', 'check_mark': '', 'chevron_up': '', 'chevron_right': '', 'chevron_down': '', 'chevron_left': '', 'document': '', 'error': '', 'folder': '', 'grid': '', 'grid_dark': '', 'grid_loaded': '', 'grid_loaded_dark': '', 'ignored': '', 'ignored_dark': '', 'menu': ' ', 'minus': '', 'multiply': '', 'music': '', 'plus': '', 'prev_next_track': '', 'prev_next_track_ff': '', 'spinner': '', 'toggle': '', }; function SVG_UI_File_Icon(icon_name) { if ( icon_name === 'file_icon_dir_default' ) { // default chrome dir and file icons return '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= ")'; } else if ( icon_name === 'file_icon_file_default' ) { return 'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABnRSTlMAAAAAAABupgeRAAABHUlEQVR42o2RMW7DIBiF3498iHRJD5JKHurL+CRVBp+i2T16tTynF2gO0KSb5ZrBBl4HHDBuK/WXACH4eO9/CAAAbdvijzLGNE1TVZXfZuHg6XCAQESAZXbOKaXO57eiKG6ft9PrKQIkCQqFoIiQFBGlFIB5nvM8t9aOX2Nd18oDzjnPgCDpn/BH4zh2XZdlWVmWiUK4IgCBoFMUz9eP6zRN75cLgEQhcmTQIbl72O0f9865qLAAsURAAgKBJKEtgLXWvyjLuFsThCSstb8rBCaAQhDYWgIZ7myM+TUBjDHrHlZcbMYYk34cN0YSLcgS+wL0fe9TXDMbY33fR2AYBvyQ8L0Gk8MwREBrTfKe4TpTzwhArXWi8HI84h/1DfwI5mhxJamFAAAAAElFTkSuQmCC ")'; } else { // custom icons return 'url("data:image/svg+xml;utf8,'+ SVG_UI_File_Icons[icon_name] +'")'; } } const SVG_UI_File_Icons = { // n.b.: order is important 'file_icon_dir': '', 'file_icon_file': '', 'file_icon_invisible': '', 'file_icon_ignored': '', 'file_icon_dirinvisible': '', 'file_icon_alias': '', 'file_icon_archive': '', 'file_icon_app': '', 'file_icon_audio': '', 'file_icon_code': '', 'file_icon_database': '', 'file_icon_ebook': '', 'file_icon_font': '', 'file_icon_graphics': '', 'file_icon_htm': '', 'file_icon_ignoredimage': '', 'file_icon_image': '', 'file_icon_markdown': '', 'file_icon_office': '', 'file_icon_pdf': '', 'file_icon_text': '', 'file_icon_video': '', // the following are the same: 'file_icon_bin': '', 'file_icon_other': '', 'file_icon_system': '' }; // Programatically add File icon CSS rules function CSS_UI_Icon_Rules() { let rules = '', kind, classname; rules += '#menu ul a::before { background-image:'+ SVG_UI_File_Icon('file_icon_file') +'; }'; rules += '#menu ul a[href^="file"]::before { background-image:'+ SVG_UI_File_Icon('file_icon_dir') +'; }'; rules += '#menu ul a[href^="http"]::before { background-image:'+ SVG_UI_File_Icon('file_icon_htm') +'; }'; rules += 'body:not(.use_custom_icons) #dir_list tr.dir a.icon span::before { background-image:'+ SVG_UI_File_Icon('file_icon_dir_default') + '; background-size:auto 13px; }'; rules += 'body:not(.use_custom_icons) #dir_list tr.file:not(.app) a.icon span::before { background-image:'+ SVG_UI_File_Icon('file_icon_file_default') + '; background-size:auto 13px; }'; for ( let icon in SVG_UI_File_Icons ) { kind = icon.slice(icon.lastIndexOf('_') + 1); classname = kind; if ( kind === 'dirinvisible' ) { classname = 'dir.invisible'; } if ( kind === 'ignoredimage' ) { classname = 'ignored_image'; } // add rules for dir_list items, content_header, stats details: rules += 'body.use_custom_icons #dir_list tr.'+ classname +' a.icon span::before, #content_pane[data-content="has_'+ classname +'"] #title span::before, body.use_custom_icons #stats .stats_kind.'+ classname +' span.has_icon_before::before { background-image: url("data:image/svg+xml;utf8,'+ SVG_UI_File_Icons['file_icon_'+kind] +'"); }'; } return rules; } // Text Editing UI Icons function SVG_Text_Editing_UI_Icon(icon_name) { let svg = SVG_Text_Editing_UI_Icons[icon_name]; return 'url("data:image/svg+xml;utf8,'+ svg +'")'; } const SVG_Text_Editing_UI_Icons = { 'toggle_theme': '', 'show_markdown': '', 'show_source': '', 'show_preview': '', 'show_html': '', 'toggle_split': '', 'save_btn': '', 'save_btn_edited': '' }; //***** END UI ELEMENTS //***** STYLES *****// // DEFINE STYLES const $warning_styles = // WARNINGS '#warnings_container { width:26em; transform:translate(-50%, 0); display:none; flex-direction:column; border-radius:0 0 3px 3px; position:absolute; top:0; left:50%; z-index:9999; box-shadow:0px 2px 12px 0 #333; font-size:0.875em; color:#111; overflow:hidden; }' + // table 'body.has_warning #warnings_container { display:flex; }' + // warnings thead '#warnings_header { padding:1rem 1.5rem; background-position:left 1.25rem center; background-repeat:no-repeat; background-size:24px; }' + '#warnings_container:not(.warning_make_playlist) #warnings_header { background-image:'+ SVG_UI_Icon('error') +'; }' + '#warnings_header h3 { display:none; margin:0; text-indent:2.25em; }' + '#warnings_container:not(.warning_make_playlist) h3#warning_header, #warnings_container.warning_make_playlist h3#make_playlist_header { display:block;}' + 'body#top.edited #warnings_container.unloading h3::before { content: "Text Editor: " }' + // warnings tbody '#warnings .warning { padding: 0 1.5rem 1rem; display:none; hyphens:none; }' + // tbody // warnings tfoot '#warning_buttons_container { padding:1rem 1.5rem; }' + '#warning_buttons { display:flex; flex-direction:row; }' + '#warning_buttons button { min-width:4em; display:none; }' + 'button.focus, button:focus { background-color: #0E4399; color: #EEE; outline:none; }' + '#warning_btn_dont_save { margin-right: auto; }' + // button '#warning_btn_cancel, #warning_btn_clear, #warning_btn_save { margin-left: 0.5rem; }'+ // button '#warning_btn_ok { margin-left:auto; }'+ // button '#warnings_container.warning_close_font #warning_close_font, #warnings_container.warning_close_playlist #warning_close_playlist, #warnings_container.unloading #warning_unsaved_text, #warnings_container.unloading #warning_btn_dont_save, #warnings_container.unloading #warning_btn_cancel, #warnings_container.unloading #warning_btn_save { display:inline-block; }'+ // button '#warnings_container.clear #warning_buttons { justify-content:space-between; }'+ '#warnings_container.clear #warning_clear_text, #warnings_container.clear #warning_btn_cancel, #warnings_container.clear #warning_btn_clear { display:inline-block; }'+ '#warnings_container.local_bookmark #warning_local_bookmark, #warnings_container.local_bookmark #warning_btn_ok { display:inline-block; margin-left:auto; }'+ '#warnings_container.warning_close_font #warning_btn_ok { display:inline-block; margin-left:auto; }'+ '#warnings_container.warning_close_font #warning_btn_cancel { display:inline-block; }'+ '#warnings_container.warning_close_playlist #warning_btn_ok { display:inline-block; margin-left:auto; }'+ '#warnings_container.warning_close_playlist #warning_btn_cancel { display:inline-block; }'+ '#warning_make_playlist fieldset { margin:0; padding:0; border:0; }'+ '#warning_make_playlist fieldset div { padding:0 0 2px; }'+ '#warning_make_playlist .indent { text-indent:2em; }'+ '#warning_make_playlist input { margin-right:6px; }'+ '#warnings_container.warning_make_playlist #warning_make_playlist { display:flex; flex-direction:column; }'+ '#warnings_container.warning_make_playlist #warning_btn_ok, #warnings_container.warning_make_playlist #warning_btn_cancel { display: inline-block; }' + '#warnings_container.warning_no_playlist #warning_no_playlist, #warnings_container.warning_no_playlist #warning_btn_ok { display: inline-block; }' + '#warnings_container.warning_local_playlist #warning_local_playlist, #warnings_container.warning_local_playlist #warning_btn_ok { display:inline-block; margin-left:auto; }' ; var $main_styles = ':root, html, body { margin:0; padding:0; width:100%; max-width:100%; height:100vh; overflow:hidden; border:0; border-radius:0; font-family:'+ $settings.UI_font +'; font-size:'+ $settings.UI_font_size +'; hyphens:auto; display:flex; }' + // 'body + div { top:-1em !important; }' + // for vivaldi picture-in-picture div after body 'table { width:100%; border:0; border-collapse: collapse; }' + '#sidebar_wrapper li, #toolbar li { list-style:none; }' + // 'a, a:hover { text-decoration: none !important; }' + 'button { padding:2px 6px; border:solid 1px #333; border-radius:3px; cursor:pointer; height:18px; line-height:0; }' + 'button:focus, textarea:focus, audio:focus { outline:none; }' + '#main_content { width:100%; display:flex; flex-direction:row; overflow:hidden; }' + // table with initial height '#handle { position:absolute; top:0; bottom:0; z-index:1; cursor:col-resize; right:-4px; width:7px; }' + // OVERLAY 'body.has_overlay #handle { z-index:9999; }' + 'body.has_warning::before, body.has_overlay::before { content:""; position:absolute; top:0; right:0; bottom:0; left:0; z-index:9998; -webkit-user-select:none; -moz-user-select:none; user-select:none; }' + // WARNING STYLES $warning_styles + // HELP STYLES '#help_container { display:none; padding: 0 1em 1em; overflow:scroll; position:absolute; top:0; right:0; bottom:0; left:0; z-index:9998; contain:strict; }' + // tr '#help_container header { font-size:0.875rem; text-align:center; margin-right:calc(-1em - 2px); margin-left:calc(-1em - 2px); }' + // '#help_container header > span { display:inline-block; padding:6px; font-weight:bold; }' + // '#help_container tr { display:flex; border-bottom:solid 1px #666; }' + // td '#help_container td { vertical-align:top; }' + // td '#content_help { margin:1em auto; width:auto; overflow:auto; }' + // table '#close_help { float:right; margin-left:-100% !important; margin:4px 6px; }' + // '#content_help tbody { font-size:0.875rem; padding:8px; overflow:auto; }' + // '#content_help td.kbd_shortcut { text-align:right; width:33%; padding:4px 9px 4px 6px; }' + // td '#content_help td.help_description { width:66%; padding:4px 6px 4px 12px; }' + // td '#content_help kbd { display:inline-block; min-width:1em; margin:2px; padding:2px 6px; border:solid 1px #888; border-radius: 3px; text-align:center; font-family:inherit; font-size:0.875em; }' + //***** SIDEBAR STYLES *****// '#sidebar_wrapper { min-width:220px; padding:0; position:relative; z-index:1; display:flex; flex-direction:column; }' + '#sidebar { overflow:hidden; font-size:0.875rem; display:flex; flex-direction:column; flex-basis:100%; }' + '#sidebar_header { font-size:0.875rem; position:relative; z-index:3; user-select:none; -webkit-user-select:none; display:flex; flex-direction:column; }' + '#sidebar_title { font-weight:normal; display:flex; flex-direction:row; }' + '#sidebar_title div { padding: 4px 6px; text-align:center; letter-spacing:0.5em; text-indent:0.75em; flex-basis:100%; }' + '#sidebar_header_body { display:flex; flex-direction:column; }' + '#toggle_sidebar { position:absolute; top:0; right:0; cursor:pointer; width:24px; height:21px; z-index:9997; background-image:'+ SVG_UI_Icon('toggle') +'; background-size:18px; background-position:center; }' + '#sidebar ul { -webkit-margin-before:0em !important; -webkit-margin-after:0em !important; -webkit-padding-start:0em; }' + '#sidebar_menus { cursor:pointer; display:flex; flex-direction:row; }' + // PARENT MENU '#parent_dir_menu { flex-basis:24px; max-width:24px; min-width:24px; padding:0; position:relative; }' + '#parent_dir_nav { margin:0; padding:0; display:block; position:absolute; top:0; right:0; bottom:0; left:0; }' + '#parent_dir_nav a { height:100%; padding:0; text-align:center; background-position:center; background-repeat:no-repeat; background-size:12px; }' + // PARENTS MENU '#parents_dir_menu { padding:0; flex-grow:1; }' + '#parents_dir_nav { margin:0; padding:0; display:block; line-height:1.4; }' + // nav '#current_dir_path { cursor:pointer; font-weight:bold; hyphens:none; padding:4px 6px 4px 6px; text-align:center; z-index:9998; }' + '#parents_links { margin:0; padding:0; display:none; position:absolute; right:0; left:0; box-shadow: 0px 4px 6px -3px #333; z-index:9998; }' + // ul '#parents_links a { margin:0; padding:4px 8px; display:block; }' + // MAIN MENU '#menu_container { display:flex; flex-direction:column; justify-content:center; flex-basis:24px; max-width:24px; min-width:24px; padding:0; }' + '#menu_nav { margin:0; padding:0; display:block; cursor:pointer; }' + // nav '#menu_nav div { width:24px; background-image:'+ SVG_UI_Icon('menu') + '; background-position:center; background-repeat:no-repeat; background-size:13px; }' + '#menu { display:none; margin:0; padding:0; position:absolute; right:0;left:0; zindex:9998; box-shadow: 0px 4px 6px -3px #333; z-index:9998; }' + // ul '#menu li.has_submenu { position:relative; background-position:right 6px center; background-repeat:no-repeat; background-size:12px; }' + // right triangle icon '#menu li.bookmark a::before { content:""; width:24px; height: 12px; background-size:12px; }' + // bookmark icon '.submenu { display:none; margin:0; padding:0; position:absolute; top:-1px !important; left:100%; right:0; width:100%; box-shadow:0px 4px 6px -3px #333; width:100%; max-width: 240px; }' + // ul '#menu ul.submenu li a { margin:0; padding:6px 8px 6px 0; }' + '#menu input { width:0; float:left; }' + '.menu_item { margin:0; padding:6px 8px 6px 0; display:flex; }' + '.menu_item::before { content:""; width:24px; height:9px; margin:2px 0 -2px; display:inline-block; background-position:center; background-repeat:no-repeat; }' + // SIDEBAR BUTTONS '#sidebar_buttons { position:relative; display:flex; flex-direction:row; }' + '#sidebar_buttons_left { padding:6px; }' + '#show_details { margin-top:0; margin-right:0.5em; padding:0 4px 0 4px; }' + '#show::before { content:"Show "; }' + '#show_invisibles_container { cursor:pointer; }' + '#show_invisibles { cursor:pointer; }' + '#show_invisibles:hover span { font-weight:bold; }' + // Grid Btn '#grid_btn { display:none; margin:0 0 0 auto; padding:0; width:24px; position:relative; z-index:9997; background-color:inherit; background-position:center; background-size:14px; cursor:pointer; outline:none; }' + '#grid_btn ul { display:none; margin:0; padding:0; padding-right:24px; padding-left:0px; position:absolute; top:-1px; right:-1px; background-position:right 5px top 8px; background-repeat:no-repeat; background-size:14px; box-shadow:0px 4px 6px -3px #333; }' + '#grid_btn ul li { padding:4px 6px; text-align:right; width:100%; white-space:pre; box-sizing:border-box; }' + // SORTING ITEMS '#sorting_row_1 { display:flex; cursor:pointer; justify-content:space-between; flex-direction:row; }' + '#sorting_row_2 { display:none; cursor:pointer; justify-content:space-between; flex-direction:row; }' + '#sorting_row_1 div { width:50%; }' + '#sorting_row_2 div { width:25%; }' + '#sorting_row_1 div, #sorting_row_2 div { flex-grow:1; padding:0; }' + '#sorting .sorting { white-space:pre; }' + '#sorting_row_1 span { display:inline-block; padding:6px 0; }' + '#sorting_row_2 span { display:inline-block; padding:0 0 6px 0; }' + '.sorting:hover span { font-weight:bold; }' + // '.sorting span::before, .sorting span::after { content:""; width:16px; height:8px; display:inline-block; position:relative; color:#CCC; background-position:center; background-repeat:no-repeat; background-size:10px; }' +// content '.sorting.down span::after { transform:rotate(180deg) }' + '#sort_by_name input { display:none; margin:0 4px 0 2px; }' + '#sort_by_name, #sort_by_size { text-align:left; }' + '#sort_by_date, #sort_by_kind { text-align:center; }' + '#sort_by_default, #sort_by_ext { text-align:right; }' + '#sorting div.up span::after { transform:rotate(180deg) !important; }' + // TEXT EDITOR ROW '#text_editor_row { display:none; }' + '#text_editor_row a { padding:6px 0 6px 16px; text-align:left; font-weight:bold; }' + //***** DIR_LIST STYLES *****// '#dir_list_wrapper { overflow:scroll; position:relative; }' + '#dir_list { overflow:hidden; position:relative; font-size:0.875rem; }' + '#tbody { counter-reset:row; height:100%; overflow:hidden; transition:opacity .125s; background-position:center; background-repeat:no-repeat; background-size:50%; }' + '#tbody > tr { margin-inline-start:0; display:grid; grid-gap:0; grid-template-columns:minmax(auto,5rem) 1fr minmax(auto,7em); }' + // tr '.tbody_row_cell { padding:0; }' + // td '.tbody_row_cell_name { grid-column: 1 / span 3; font-variant-numeric:tabular-nums; }' + '.tbody_row_cell_name_a { display:flex; text-decoration:none; -webkit-padding-start:0; -moz-padding-start:0; }' + // td.name a.icon '.tbody_row_cell_name_a::before { display:none; counter-increment: row; content:counter(row); width:28px; min-width:28px; height:14px; max-height:14px; min-height:14px; text-align:right; padding-right:1px; }' + // numbers '.tbody_row_cell_name_a_span { display:flex; line-height:1.4; text-align:left; word-break:break-word; }' + // req: .has_background_before '.tbody_row_cell_name_a_span input { display:none; margin:2px 6px 0 0; }' + // media checkboxes '.tbody_row_cell_details { display:none; text-align:right; white-space:pre; max-height:1em; font-variant-numeric:tabular-nums; }' + '.tbody_row_cell_details.size { grid-column: 1; grid-row: 2; padding:0 8px 3px 8px; }' + '.tbody_row_cell_details.date { grid-column: 2; grid-row: 2; text-align:right; overflow:hidden; overflow-wrap:break-word; text-overflow:""; padding-right:8px; }' + '.tbody_row_cell_details.date span { white-space:pre; display:inline-block; }' + '.tbody_row_cell_details.kind { grid-column: 3; grid-row: 2; overflow:hidden; text-overflow:ellipsis; }' + '.tbody_row_cell_name_a { padding:5px 8px 5px 0; }' + // '.tbody_row_cell_name_a::before { margin-top:1px; }' + // '.tbody_row_cell_details { padding:0 16px 3px 0; }' + // padding:4px 8px 4px 0; 'tr:hover a, tr.selected a, tr.playing a { font-weight:bold; }' + 'tr.disabled, tr.disabled a, tr.disabled span, tr.ignore, tr.ignore a { cursor:not-allowed; }' + 'tr.invisible { display:none; }' + // PLAYLIST ITEMS 'body[class$="list"] #tbody tr { display: grid; grid-gap:0; grid-template-columns: 48px minmax(8rem,18rem) minmax(auto,8rem); }' + 'body[class$="list"] #tbody td.name { grid-column: 1 / span 4; }' + 'body[class$="list"] #tbody td.size { grid-column: 1 / span 2; grid-row: 2; padding:0 8px 6px 8px; text-align:left; text-indent: 40px; }' + 'body[class$="list"] tbody td.kind { grid-column: 3 / span 2; grid-row: 2; padding:0 8px 6px 8px; }' + 'body[class$="list"] #tbody td.details.ext { display:none; }' + 'body.show_numbers[class$="list"] #tbody td.size { text-indent: 48px; }' + //***** SIDEBAR FOOTER *****// '#tfoot { margin-top:auto; padding:0; position:relative; font-size:0.875rem; user-select:none; -webkit-user-select:none; display:flex; flex-direction:column; }' + '#tfoot:hover { box-shadow: 0px 4px 6px 3px #333; }' + // tr // STATS '#stats { cursor:pointer; float:left; width:100%; overflow:hidden; flex-direction:column; line-height:1.6; font-size:0.875rem; }' + // td '#stats_summary, #stats_summary_playlist_container { padding:2px 8px; overflow:hidden; white-space:pre; }' + // tr '#stats_summary div > div { padding:0 6px; }' + // tr '#stats_summary_playlist_container, #stats_summary_detailed_container, #stats_details_container { display:none; flex-direction:column; }' + // tbody '#stats_summary_playlist_container > div, #stats_summary_detailed_container > div, #stats_details_container > div { flex-direction:row; }' + // tbody '.summary_detailed { display:none; }' + '.stats_count { display:inline-block; padding:2px 2px 2px 6px; text-align:right; min-width: 22px; }' + '.stats_kind { display:inline-block; padding:2px 6px 2px 0; }' + '#stats_summary_detailed_total { padding-left:4px; font-weight:bold; }' + '#stats_summary_detailed_dirs span { padding-bottom:0; }' + '#stats_summary_detailed_dirs .stats_kind::before { background-image:'+ SVG_UI_File_Icon('file_icon_dir_default') +'; }' + '#stats_summary_detailed_files .stats_kind::before { background-image:'+ SVG_UI_File_Icon('file_icon_file_default') +'; }' + '#stats_details_container > div:first-of-type span { padding-top:4px; }' + '#stats_details_container > div:last-of-type span { padding-bottom:4px; }' + '#stats_summary_detailed_dirs .stats_kind, #stats_summary_detailed_files .stats_kind { display:flex; }' + '#stats_summary_detailed_dirs:hover, #stats_summary_detailed_files:hover, #stats_details_container div:hover { font-weight:bold; }' + // SIDEBAR FOOTER LINKS '#footer_links { margin-top:-1px; position:absolute; z-index:1; right:0; cursor:pointer; float:right; width:24px; height:100%; background-image:'+ SVG_UI_Icon('toggle') +'; transform:rotate(180deg) !important; background-size:18px; background-position:2px center; }' + // td '#footer_links ul { display:none; padding:0; position:absolute; top:calc(100% - 1px); right:unset; left:calc(100% - 27px); white-space:pre; box-shadow:-0px -3px 6px -3px #333; transform:rotate(180deg) !important; }' + // ul '#open_in_content_pane { padding:4px 6px; }' + '#view_directory_source { padding:4px 6px; }' + // CLASSES AND ELEMENTS '.has_icon_before::before { content:""; display:block; float:left; width:28px; min-width:28px; height:14px; max-height:14px; min-height:14px; margin-top:1px; background-position:center; background-repeat:no-repeat; background-size:14px; }' + '.has_checkmark_before::before { content:""; height:9px; background-position:center; background-repeat:no-repeat; }' + 'ul.has_popout_menu, .editor_theme_light #toolbar ul.has_popout_menu { background-color:#C0C0C0; border:solid 1px #666; }' + 'ul.has_popout_menu li { background-color:#D0D0D0; }' + 'ul.has_popout_menu li:hover { background-color:#E0E0E0; }' + '.theme_dark #sidebar_wrapper ul.has_popout_menu { border:solid 1px #111; }' + '.theme_dark #sidebar_wrapper ul.has_popout_menu, .theme_dark #sidebar_wrapper ul.has_popout_menu li { background-color:#505050; }' + '.editor_theme_dark #toolbar ul.has_popout_menu li { background-color:#C0C0C0; }' + '.theme_dark #sidebar_wrapper ul.has_popout_menu li:hover { background-color:#686868; }' + //***** END SIDEBAR STYLES *****// //***** CONTENT STYLES *****// '#content_pane { height:100%; padding:0; position:relative; transform:scale(1); vertical-align:top; display:flex; flex-direction:column; contain:strict; }' + // td '#content_header { display:block; font-size:0.875rem; position:relative; z-index:3; }' + // header '#content_header table { font-size:0.875rem; z-index:2; }' + '#content_title { text-align:center; display:flex; flex-direction:row; justify-content:space-between; }' + // tr // CONTENT TITLE BUTTONS LEFT '#title_buttons_left { padding:4px 6px; text-align:left; width:4rem; max-width:9.5em; vertical-align:top; }' + // td '#reload_btn { float:left; width:52px; }' + '#reload_btn::before { content:"Reload"; }' + '#prev_next_btns { margin-left:8px; cursor:pointer; float:left; line-height:1; padding:0; position:relative; }' + // button '#prev_next_btns span { width:2em; height:18px; }' + '#prev_btn, #next_btn { background-position:center 36%; background-repeat:no-repeat; background-size:33%; }' + '#prev_btn { background-image:'+ SVG_UI_Icon('chevron_left') + '; }' + '#next_btn { background-image:'+ SVG_UI_Icon('chevron_right') + '; }' + // CONTENT TITLE '#title { line-height:1.4; min-width:10em; min-height:18px; padding:4px 8px; text-align:center; word-break:break-word; vertical-align:top; cursor:pointer; }' + // td '#title span { font-weight:bold; }' + '#title span::before { content:""; margin-top:1px; margin-bottom:-2px; width:24px; height:14px; display:inline-block; background-position:center; background-repeat:no-repeat; font-weight:normal; }' + '#title span::after { font-weight:bold; }' + '#content_pane[data-content="has_image"] #title span::after { font-weight:normal; }' + // CONTENT TITLE BUTTONS RIGHT '#title_buttons_right { padding:4px 6px; text-align:right; width:4rem; max-width:9.5em; vertical-align:top; }' + // td '#scale { cursor:pointer; float:right; line-height:1; margin-right:8px; padding:0 4px; position:relative; background-color:#FFF; }' + // button '#scale span { width:2em; height:18px; background-position:center 36%; background-repeat:no-repeat; background-size:10px; }' + '#decrease { margin-left:-4px; background-image:'+ SVG_UI_Icon('minus') +'; }' + '#increase { margin-right:-4px; background-image:'+ SVG_UI_Icon('plus') +'; }' + '#close_btn { float:right; padding:0px; width:52px; }' + // close button '#close_btn::before { content:"Close"; }' + // '.split_btn { display:none; }' + '.split_btn span { display:inline-flex; }' + '.split_btn::after { content:""; position: absolute; top: 0; bottom: 0; left: 50%; }' + // CONTENT AUDIO TITLE '#content_audio_title { display:none;}' + // tr '#content_audio_title span { display:block; width:100%; cursor:pointer; padding:4px 6px 6px; text-align:center; line-height:1.4; }' + // td '#content_audio_title span::before { content:""; padding-right:18px; font-weight:normal; background-position:center; background-position:right 4px center; background-repeat:no-repeat; }' + // td // CONTENT AUDIO PLAYER '#content_audio { display:none; justify-content:center; padding-bottom:6px; }' + // tr '#content_audio > div > div { text-align:center; font-weight:bold;}' + // td '#audio_container { padding:0 4px; height:32px; display:flex; background-color:rgb(241, 243, 244); flex-direction:row; }' + // div; background-color is for chrome browsers '#prev_track, #next_track { width:2rem; padding:0; display:inline-block; overflow:auto; background-image:'+ SVG_UI_Icon('prev_next_track') +'; background-position:center; background-repeat:no-repeat; }' + // div '#prev_track { transform:rotate(180deg) !important; }' + // div '#audio { height:32px; }' + // audio // 'body:not(.is_gecko) #audio { margin-top: -9px; }' + // hacky fix for PIP button in Vivaldi '#close_audio { width:2rem; padding:0; position:relative; display:inline-block; background-image:'+ SVG_UI_Icon('multiply') +'; background-position:center; background-repeat:no-repeat; background-size:14px; }' + // div , .audio_controls, #glyph_viewer_info div '#audio_options { margin-top:0; margin-right:calc(-6em - 8px); padding:0 4px; width:6em; display:flex; flex-direction:column; justify-content:center; }' + // div '#loop_label input { margin:0px 4px 2px}' + // input '#shuffle_label input { margin:2px 4px 0px}' + // input '#audio_iframe { margin:0; padding:0; border:0; }' + // CONTENT TITLE PLAYLIST ENTRY (#content_playlist and #content_audio_playlist) '.playlist_entry_container { display:none; padding:4px 6px; text-align:center; flex-direction:row; }' + // td '.playlist_entry_container textarea { width:100%; padding:0 6px; border:0; resize:vertical; }' + // tr // CONTENT_CONTAINER (section) '#content_container { box-sizing:border-box; justify-content:center; padding:0; position:relative; bottom:0; overflow:auto; width:100%; background-position:center; background-repeat:no-repeat; background-size: 50%; display:flex; flex-basis:100%; contain:strict; }' + // section '.content { display:none; overflow:scroll; width:100%; height:100% }' + // hide content by default // CONTENT GRID (div) '#content_grid { display:none; position:absolute; padding:0; width:100%; font-size:1rem; grid-gap:0; grid-template-columns:repeat(auto-fill, minmax('+ ( $settings.grid_image_size + 16) +'px, auto)); grid-auto-rows:minmax(min-content, max-content); }' + '#content_grid::after { content:""; width:1px; position:absolute; top:0; right:0; bottom:0; }' + '#content_grid a { display:block; }' + // Image Grid Items '.image_grid_item { padding:6px; grid-column:auto; display:flex; align-items:center; justify-content:center; line-height:0; }' + '.image_grid_item img { width:auto; max-width:'+ ($settings.grid_image_size).toString() +'px; max-height:'+ ($settings.grid_image_size) +'px; position:relative; }' + '.image_grid_item img[src$=".svg"] { width: 100%; height:100%; }' + // Font Grid Items '.font_grid_item { line-height:1; padding:8px 20px; grid-column: 1 / -1; }' + '.font_grid_item p { margin:0; padding:0 0 6px 0; line-height:1; font-size:1rem; letter-spacing:0.1em; text-indent:0.1em; }' + '.font_grid_item h2 { margin:0; font-weight:normal; font-size:'+ $settings.grid_font_size * 4 +'em; }' + '.image_grid_item + .font_grid_item { margin-top:-1px; }' + // mask double borders // CONTENT TEXT EDITOR '#content_text { display:none; width:100%; max-width:100%; height:100%; overflow:hidden; padding:0; position:absolute; z-index:1; flex-direction:column; }' + // div // CONTENT FONT.content '#content_font { hyphens:none; padding:0; position:relative; font-size:'+ $settings.grid_font_size +'em; overflow-wrap:break-word; }' + // div '#font_specimen { max-width:100%; display:none; line-height:1.2; flex-direction:column; }' + // div '#specimen { padding: 20px; font-size:4em; word-break:break-all; line-height:1.2; }' + // div '#specimen_2 { margin:0; font-weight:normal; font-size:8em; overflow:hidden; text-overflow:ellipsis; white-space:pre; }' + '#specimen_2H4 { margin:0; font-weight:normal; font-size:1.618em; overflow:hidden; text-overflow:ellipsis; }' + '#specimen_3 { margin:0; font-weight:normal; font-size:6em; text-align:justify; overflow:hidden; text-overflow:ellipsis; white-space:pre; }' + '#specimen_3H3 { margin:0; font-size:2em; hyphens:auto; }' + '#specimen_string_2 { padding:20px; text-align:justify; hyphens:auto; }' + // div '#specimen_string_3 { padding:20px; text-align:justify; }' + // div '.lorem { text-align:justify; hyphens:auto; font-size:1em; line-height:1.4; column-gap:1.5em; overflow-wrap:normal; word-break:normal; }' + // div '#lorem { padding:20px 20px 0; }' + // div '#lorem::first-line { letter-spacing:0.1em; text-indent:0.1em; font-size:'+ $settings.grid_font_size * 1.33 +'em; font-variant:small-caps; }' + '#lorem_2 { padding:12px 20px 0; columns:2; }' + // div '#lorem_3 { padding:12px 20px 40px; columns:3; }' + // div // FONT GLYPHS '#font_viewer { display:none; position:relative; font-family:unset; width:100%; }' + // div '#content_pane[data-content="has_font_file"] #font_viewer { overflow-y:auto; }' + // div '#content_pane[data-content="has_glyph"] #font_viewer { overflow:auto; }' + '#content_pane[data-content="has_glyph"] #glyphs_container { visibility:hidden; }' + '#glyphs_container { padding:0; position:relative; text-align:center; display:grid; grid-gap:0; grid-template-columns:repeat(auto-fill, minmax(120px,auto)); }' + // div '.glyph_container { padding:0; position:relative; }' + // div '.glyph_info { padding:2px; position:absolute; right:0; bottom:0; left:0; font-size:0.75rem; }' + '#glyph_viewer { display:none; padding:0; z-index:1; position:absolute; top:0; right:0; bottom:0; left:0; background-color:#FFF; background-position:center; background-repeat:no-repeat; background-size:contain; }' + // div '#glyph_viewer_info { padding:4px 6px; position:fixed; right:0; left:0; text-align:center; height:18px; line-height:1.6; font-size:0.875rem; }' + // div '#content_font svg { width: 100%; }' + '#save_svg_hidden { float:left; visibility:hidden; }' + // button '#glyph_viewer_info div { padding:0; display:inline-block; }' + // button '#glyph_viewer_info div::before { content:"Glyph "; }' + '#save_svg { float:right; }' + // button '#font_info { max-height:'+ (window.innerHeight * 0.75) +'px; font-size:0.875rem; position:fixed; bottom:0; z-index:2; }' + '#font_info:hover { box-shadow:0px 4px 6px 3px #333; }' + // tr '#font_info th { padding:4px 6px 5px; letter-spacing:0.1em; text-indent:0.1em; }' + '#font_info_body { display:none; max-height:'+ ((window.innerHeight * 0.75) - 64) +'px; font-size:0.875rem; overflow:auto; }' + '.font_info_name { padding:4px 6px; text-align:right; font-weight:bold; width:33%; }' + '.font_info_value { padding:4px 6px; width_66%; }' + '.font_info_value a { font-weight:bold; }' + // CONTENT IMAGE.content '#content_image { display:none; margin:0; padding:2rem 2.5rem; position:relative; overflow:auto; box-sizing:border-box; }' + // div '#content_image img { margin:auto; width:auto; max-width:100%; max-height:100%; position:relative; object-fit:contain; cursor:zoom-in; }' + // img '#content_pane.has_zoom_image #content_image { padding:0; }' + // div // OTHER CONTENT ELEMENTS '#content_pdf { height:100%; padding:0; position:relative; width:100%; }' + // embed.content '#content_video { padding:0; position:absolute; background:transparent; }' + // video.content '#content_iframe { border:0; height:100%; padding:0; position:relative; width:100%; background-color: #FFF; }' // iframe.content ; var $color_and_background_styles = // added to #top and #iframe_body // BACKGROUND COLORS // 'body.theme_light .background_color_A0_20 { background-color: #A0A0A0; }' + // 'body.theme_dark .background_color_A0_20 { background-color: #202020; }' + 'body.theme_light, body.theme_light .background_color_B0_30 { background-color: #B0B0B0; }' + 'body.theme_dark, body.theme_dark .background_color_B0_30 { background-color: #303030; }' + 'body.theme_light .background_color_C0_40 { background-color: #C0C0C0; }' + 'body.theme_dark .background_color_C0_40 { background-color: #404040; }' + 'body.theme_light .background_color_D0_50 { background-color: #D0D0D0; }' + 'body.theme_dark .background_color_D0_50 { background-color: #505050; }' + 'body.theme_light .background_color_E0_60 { background-color: #E0E0E0; }' + 'body.theme_dark .background_color_E0_60 { background-color: #606060; }' + 'body.theme_light .background_color_DD_44 { background-color: #DDDDDD; }' + // tbody background 'body.theme_dark .background_color_DD_44 { background-color: #383838; }' + 'body.theme_light:not(.editor_theme_dark) .background_color_DD_33, body.editor_theme_light .background_color_DD_33 { background-color: #DDDDDD; }' + 'body.theme_dark:not(.editor_theme_light) .background_color_DD_33, body.editor_theme_dark .background_color_DD_33 { background-color: #333333; }' + 'body.theme_light .background_color_EE_22, body.editor_theme_light .background_color_DD_33:focus, body.theme_light #content_container:hover, body.theme_light #content_grid::after { background-color: #EEEEEE; }' + 'body.theme_dark .background_color_EE_22, body.editor_theme_dark .background_color_DD_33:focus, body.theme_dark #content_container:hover, body.theme_dark #content_grid::after { background-color: #222222; }' + 'body.theme_light .background_color_FF_11 { background-color: #EFEFEF; }' + 'body.theme_dark .background_color_FF_11 { background-color: #0F0F0F; }' + 'body.theme_light .background_color_EE_22:hover, body.theme_light .background_color_EE_22.hovered, body.theme_light .background_color_EE_22.selected { background-color: #FFFFFF; }' + 'body.theme_dark .background_color_EE_22:hover, body.theme_dark .background_color_EE_22.hovered, body.theme_dark .background_color_EE_22.selected { background-color: #000000; }' + 'body.theme_light .background_color_22_EE { background-color: #222222; }' + 'body.theme_dark .background_color_22_EE { background-color: #EEEEEE; }' + 'body.theme_light .background_color_11_FF, body.theme_light .background_color_22_EE:hover, body.theme_light .background_color_22_EE.hovered { background-color: #0F0F0F; }' + 'body.theme_dark .background_color_11_FF, body.theme_dark .background_color_22_EE:hover, body.theme_dark .background_color_22_EE.hovered { background-color: #EFEFEF; }' + // DIR LIST ROWS: .alternate_background 'body.theme_dark.alternate_background #tbody tr:nth-of-type(odd) { background-color: #505050; }' + 'body.theme_light.alternate_background #tbody tr:nth-of-type(odd) { background-color: #D0D0D0; }' + 'body.theme_dark.alternate_background #tbody tr:nth-of-type(even) { background-color: #404040; }' + 'body.theme_light.alternate_background #tbody tr:nth-of-type(even) { background-color: #E8E8E8; }' + 'body.theme_dark #content_help tr:nth-of-type(even) { background-color: #484848; }' + 'body.theme_light #content_help tr:nth-of-type(even) { background-color: #CCCCCC; }' + // NON-MEDIA ROWS .selected, .loaded, :hover ("light cyan") 'body.theme_light tr:not(.media).selected, body.theme_light tr:not(.media).selected.loaded, body.theme_light tr.selected.loaded:hover, body.theme_light #menu li.selected'+ '{ background-color: rgba(172,202,235,1.00) !important; }' + 'body.theme_light tr:not(.media).loaded, body.theme_light #tbody tr:not(.media):hover, body.theme_light.alternate_background #tbody tr:not(.media):hover, body.theme_light tr:not(.media).hovered'+ '{ background-color: rgba(172,202,235,0.60) !important; }' + 'body.theme_dark tr:not(.media).selected, body.theme_dark tr:not(.media).selected.loaded, body.theme_dark tr.selected.loaded:hover, body.theme_dark #menu li.selected'+ '{ background-color: rgba(101,140,179,0.80) !important; }' + //#658CB3 'body.theme_dark tr:not(.media).loaded, body.theme_dark #tbody tr:not(.media):hover, body.theme_dark.alternate_background #tbody tr:not(.media):hover, body.theme_dark tr:not(.media).hovered'+ '{ background-color: rgba(101,140,179,0.60) !important; }' + // MEDIA ROWS .playing, .selected, :hover 'body.theme_light tr.media.playing { background-color: rgba(130,196,196,1) !important; }' + // #82C4C4 'body.theme_light tr.media.selected:not(.playing), body.theme_light tr.media.hovered' + '{ background-color: rgba(116,190,190,0.60) !important; }' + 'body.theme_light tr.media:not(.playing):hover { background-color: rgba(116,190,190,0.40) !important; }' + 'body.theme_dark tr.media.playing { background-color: rgba(076,143,143,0.75) !important; }' + 'body.theme_dark tr.media.selected:not(.playing), body.theme_dark tr.media.hovered' + '{ background-color: rgba(076,143,143,0.55) !important; }' + 'body.theme_dark tr.media:hover { background-color: rgba(076,143,143,0.45) !important; }' + // text editor row ("purple") 'body.theme_light #text_editor_row.has_text_editor, body.theme_light.edited #text_editor_row { background-color: rgba(160,160,230,1.00); }' + // #A0A0E6 'body.theme_dark #text_editor_row.has_text_editor, body.theme_dark.edited #text_editor_row { background-color: rgba(100,100,160,1.00); }' + // #6464A0 // menu items :hover, content_iframe 'body.theme_dark .menu li:hover { background-color: #686868; }' + 'body.theme_light .menu li:hover { background-color: #B8B8B8; }' + // BACKGROUND IMAGES '.has_background, .has_background_before::before, .has_background_after::after { background-repeat:no-repeat; background-position:center; background-color:transparent !important; }' + 'body.theme_light .bookmark > a::before { background-image: '+ SVG_UI_Icon('bookmark') +'; }' + 'body.theme_dark .bookmark > a::before { background-image: '+ SVG_UI_Icon('bookmark_dark') +'; }' + 'body.theme_light li.has_submenu { background-image: '+ SVG_UI_Icon('arrow') +'; }' + 'body.theme_dark li.has_submenu { background-image: '+ SVG_UI_Icon('arrow_dark') +'; }' + '.background_color_check_mark::before, #theme::before, body.sort_by_default #default span::before, body.sort_by_name #name span::before, body.sort_by_size #size span::before, body.sort_by_date #date span::before, body.sort_by_kind #kind span::before, body.sort_by_ext #ext span::before, body.alternate_background #alternate_background span::before, body.show_numbers #show_numbers span::before, body.autoload_media #autoload_media span::before, body.split_view #split_view_menu_item, body.split_view #split_view span::before, body.source_text:not(.preview_text) #source_text::before, body.preview_text:not(.source_text) #preview_text::before, body.sort_by_default #sort_by_default span::before, body.sort_by_name #sort_by_name span::before, body.sort_by_size #sort_by_size span::before, body.sort_by_date #sort_by_date span::before, body.sort_by_kind #sort_by_kind span::before, body.sort_by_ext #sort_by_ext span::before, body.ignore_ignored_files #ignore_ignored_files span::before, #disable_text_editing::before'+ '{ background-image:'+ SVG_UI_Icon('check_mark') +'; }' + 'body.sort_by_default #sort_by_default span::after, body.sort_by_name #sort_by_name span::after, body.sort_by_size #sort_by_size span::after, body.sort_by_date #sort_by_date span::after, body.sort_by_kind #sort_by_kind span::after, body.sort_by_ext #sort_by_ext span::after'+ '{ background-image:'+ SVG_UI_Icon('chevron_down') +'; }' + '#parent_dir_nav a { background-image:'+ SVG_UI_Icon('chevron_up') +'; }' + 'body.is_error #tbody { background-image: '+ SVG_UI_Icon('error') +'; }' + 'body.theme_light #content_pane[data-content="has_ignored"] #content_container'+ '{ background-image:'+ SVG_UI_Icon('ignored') +'; }' + 'body.theme_dark #content_pane[data-content="has_ignored"] #content_container'+ '{ background-image:'+ SVG_UI_Icon('ignored_dark') +'; }' + 'body.has_audio #content_pane:not([data-content^="has_"]) #content_container'+ '{ background-image:'+ SVG_UI_Icon('music') +'; }' + CSS_UI_Icon_Rules() + 'body.theme_light #grid_btn:not(.has_grid), body.theme_light #grid_btn:not(.has_grid) .menu'+ '{ background-image:'+ SVG_UI_Icon('grid') +'; }' + 'body.theme_dark #grid_btn:not(.has_grid), body.theme_dark #grid_btn:not(.has_grid) .menu'+ '{ background-image:'+ SVG_UI_Icon('grid_dark') +'; }' + 'body.theme_light #grid_btn.has_grid, body.theme_light #grid_btn.has_grid .menu'+ '{ background-image:'+ SVG_UI_Icon('grid_loaded') +'; }' + 'body.theme_dark #grid_btn.has_grid, body.theme_dark #grid_btn.has_grid .menu'+ '{ background-image:'+ SVG_UI_Icon('grid_loaded_dark') +'; }' + '#content_pane[data-content="has_text_editor"] #title span::before { background-image: '+ SVG_UI_File_Icon('file_icon_markdown') +'; }' + '#content_pane[data-content="has_font_file"] #title span::before { background-image: '+ SVG_UI_File_Icon('file_icon_font') +'; }' + '#content_audio_title span::before { background-image: '+ SVG_UI_File_Icon('file_icon_audio') +'; height:14px !important; }' + '#content_pane[data-content="has_grid"] #title span::before { background-image: '+ SVG_UI_File_Icon('file_icon_dir') +'; height:14px !important; }' + '#content_pane[data-content="has_view_directory_source"] #title span::before { background-image: '+ SVG_UI_File_Icon('file_icon_dir_default') +'; height:14px !important; background-size:contain; }' + // BORDERS 'body.theme_dark .border_all { border: solid 1px #111; }' + 'body.theme_light .border_all { border: solid 1px #666; }' + 'body.theme_dark .border_top { border-top: solid 1px #111; }' + 'body.theme_light .border_top { border-top: solid 1px #666; }' + 'body.theme_dark .border_right { border-right: solid 1px #111; }' + 'body.theme_light .border_right { border-right: solid 1px #666; }' + 'body.theme_dark .border_bottom, body.theme_dark #tbody tr:last-of-type:not(.invisible) { border-bottom: solid 1px #111; }' + 'body.theme_light .border_bottom, body.theme_light #tbody tr:not(.invisible):last-of-type { border-bottom: solid 1px #666; }' + 'body.theme_dark .border_left { border-left: solid 1px #111; }' + 'body.theme_light .border_left { border-left: solid 1px #666; }' + '.border_top_x { border-top: solid 1px #666; }' + // "x" = inverted for theme_dark '.border_right_x { border-right: solid 1px #666; }' + '.border_bottom_x { border-bottom: solid 1px #666; }' + '.border_left_x { border-left: solid 1px #666; }' + '.split_btn::after { border-left: solid 1px #333; }' + // TEXT COLORS 'body.theme_light a.text_color_111:hover, body.theme_light .text_color_111.selected, body.theme_light .selected .text_color_111 { color: #000; }' + 'body.theme_dark a.text_color_111:hover, body.theme_dark .text_color_111.selected, body.theme_dark .selected .text_color_111 { color: #FFF; }' + 'body.theme_light .text_color_111, body.editor_theme_light #content_text .text_color_111 { color: #111; }' + 'body.theme_dark .text_color_111, body.editor_theme_dark #content_text .text_color_111 { color: #EEE; }' + 'body.theme_light .text_color_333 { color: #333; }' + 'body.theme_dark .text_color_333 { color: #CCC; }' + 'body.theme_light .ignore, body.theme_light .ignore a { color: #555 !important; }' + 'body.theme_dark .ignore, body.theme_dark .ignore a { color: #AAA !important; }' ; var $conditional_styles = // PSEUDO-ELEMENTS '#content_pane[data-content="has_image"] #title span::after' + '{content: attr(data-after); }' + '#content_pane[data-content="has_font"] #title::before { content: "Font:" }' + '#content_pane[data-content="has_font_file"] #title::before { content: "Glyphs from font:" }' + 'body.has_directory_source #title::before { content: "Source of: " }' + '#content_pane[data-content="has_grid"] #title::before{ content: "Fonts and Images from:"; }' + '#content_pane[data-content="has_grid"].has_font_grid #title::before { content: "Fonts from:"; }' + '#content_pane[data-content="has_grid"].has_image_grid #title::before { content: "Images from:"; }' + '#content_pane[data-content="has_grid"] #title::after { content: "'+ $current_dir +'"; }' + '#content_pane[data-content="has_ignored"] #title::before{ content: "Ignored content:"; }' + '#content_pane[data-content="has_dir"] #title::before {content: "Index of:"; }' + 'body #sidebar_title div:before { content: "INDEX OF"; }' + 'body.has_playlist #sidebar_title div:before { content: "PLAYLIST"; }' + 'body.has_filelist #sidebar_title div:before { content: "FILELIST"; }' + '#content_pane.has_audio #content_audio_title span::before, body #content_pane[data-content="has_video"] #title::before' + '{content: "Playing: "; }' + '#reload_btn.reset::before, #content_pane.has_zoom_image #reload_btn::before, #content_pane.has_scaled_image #reload_btn::before { content: "Reset"; }' + // reload button '#content_pane[data-content="has_text_editor"] #close_btn::before { content: "Hide"; }' + // close button '#content_pane[data-content="has_text_editor"] #title::after { content: "Text Editor"; }' + '#content_pane[data-content="has_text_editor"].edited #title::after { content: "Text Editor (edited)"; }' + 'body.edited #text_editor_row a:after, body.iframe_edited:not(.has_text_editor) #content_pane.has_iframe #title::after' + '{content: " (edited)"; }' + 'body.theme_light #theme span::before { content: "Light "; }' + 'body.theme_dark #theme span::before { content: "Dark "; }' + 'body.show_details #show::before { content: "Hide "; }' + '#disable::after { content: "Enabled"; }' + 'body.disable_text_editing #disable::after { content: "Disabled"; }' + '#content_pane[data-content="has_text_editor"] #title::before { content: "" !important; }' + // DISPLAY 'body.has_hidden_sidebar #handle, body.has_stats #stats_summary, body[class$="list"] #stats_summary, body.has_stats #footer_links, body[class$="list"] #footer_links, body[class$="list"] #sidebar_buttons label, body.is_error #sidebar_header_body > div:not(:first-of-type), body.is_error #tfoot, body.is_non_local #show_invisibles_container, body.is_windows #show_invisibles_container' + '{ display:none; }' + 'body.ignore_ignored_files tr.ignore { display:none !important; }' + 'body.has_media #play_toggle, body[class$="list"] #play_toggle, .media .tbody_row_cell_name_a_span input, body.theme_dark #theme_dark, body.theme_light #theme_light, body.show_details .tbody_row_cell_details, #content_pane[class^="has_"] #close_btn, #content_pane[data-content="has_text_editor"] #close_btn, body[class$="list"] #close_btn' + '{ display:unset; }' + '#font_info:hover #font_info_body { display:table-row-group; }' + 'body[class$="list"] #stats_summary_playlist_files, body.has_help #help_container' + '{ display:table-row; }' + 'body.has_images #grid_btn, body.has_fonts #grid_btn' + '{ display:table-cell; }' + 'body.has_menu #menu, body.has_menu_parents #parents_links, #sidebar_menus .has_submenu:hover .submenu, #menu .has_submenu.hovered .submenu, #footer_links:hover ul, body.has_images.has_fonts #grid_btn:hover ul.menu, #content_pane[data-content="has_font_file"] #font_viewer, #content_pane[data-content="has_glyph"] #font_viewer, #tfoot tr, body.has_warning #overlay_container' + '{ display:block; }' + 'body.show_details #dir_list td.details, body.show_numbers .tbody_row_cell_name_a::before, #content_pane[data-content="has_grid"] .split_btn, #content_pane[data-content="has_image"] .split_btn, #content_pane[data-content="has_font"] .split_btn, #content_pane[data-content="has_font_file"] #title_buttons_left .split_btn, #content_pane[data-content="has_glyph"] #title_buttons_right .split_btn' + '{ display:inline-block; }' + 'body.show_invisibles tr.invisible, body.has_stats tr.invisible, body.has_stats tr.ignore, body.has_stats tr.ignore.hovered, body.ignore_ignored_files.has_stats tr.ignore' + '{ display:grid !important; }' + 'body.show_details #sorting_row_2, body.show_details #text_editor_row, #text_editor_row.has_text_editor, #content_pane.has_audio #content_audio_title, #content_pane.has_audio #content_audio, .playlist_entry_container.has_content, #content_pane .content.has_content, #content_pane[data-content="has_text_editor"] #content_text, #content_pane[data-content="has_font"] #font_specimen, #content_pane[data-content="has_image"] #content_container, #content_pane[data-content="has_video"] #content_container, body.has_stats #stats_summary_detailed_container, body.has_stats #stats_details_container, body.has_stats .summary_detailed, body[class$="list"] #stats_summary_playlist_container' + '{ display:flex; }' + '#content_pane[data-content="has_text_editor"] .content.has_content, #content_pane[data-content="has_text_editor"] #content_grid, #content_pane[data-content="has_grid"] #content_text, #content_pane[data-content="has_grid"] .content.has_content' + '{display:none !important; }'+ 'body.theme_dark .invert, body.theme_dark #menu li > span::before, body.theme_dark #sorting span::before, body.theme_dark #sorting span::after' + '{ filter:invert(1); }' + '#content_pane[data-content="has_ignored"]::before { opacity:0.3; }' + 'body.has_warning #sidebar_wrapper, body.has_warning #content_pane, #close_audio:hover::after, body.has_menu_parents #tbody, body.has_menu #tbody, body.faded:not(.has_stats) #tbody tr:not(.hovered), body.has_stats:not(.faded) #tbody tr:not(.hovered), #parent_dir_menu:not(:hover), #menu_container:not(:hover) nav, #toggle_sidebar, #grid_btn, #dir_list.has_dir:not(:hover), #footer_links, .split_btn span, .disabled, body.focus_content #tbody tr:not(.hovered)' + '{ opacity: 0.6; }' + '#grid_btn:hover, #prev_next_btns span:hover, #toggle_sidebar:hover, #grid_btn:hover, #footer_links:hover, .split_btn span:hover' + '{ opacity: 1.0; }' + // HAS HIDDEN SIDEBAR 'body.has_hidden_sidebar #toggle_sidebar { left:2px; transform:rotate(180deg); }' + 'body.has_hidden_sidebar #sidebar_wrapper { width:0 !important; min-width:0; position:absolute; top:2px; left:-1px; }' + 'body.has_hidden_sidebar #sidebar_header { z-index:unset; display:none; }' + 'body.has_hidden_sidebar #sidebar { visibility:hidden; }' + // allows hidden sidebar to still be navigated by arrows 'body.has_hidden_sidebar #dir_list { min-width:0; }' + 'body.has_hidden_sidebar #content_pane { width:100% !important; }' + 'body.has_hidden_sidebar #title_buttons_left { padding-left:24px; }' + // HAS GRID '#content_pane[data-content="has_grid"] #content_grid { display: grid; }' + '#content_pane.has_hidden_grid #content_grid { max-height:100%; display:grid; overflow:hidden; }' + // HAS ZOOM IMAGE '#content_pane.has_scaled_image #content_image { display:grid; padding:0; }' + '#content_pane.has_zoom_image #content_image img { width:unset; max-width:unset; max-height:unset; cursor: zoom-out; }' + // FIGURE OUT HOW TO DELETE THESE: '#content_pane[data-content="has_image"] #title_buttons_left, #content_pane[data-content="has_image"] #title_buttons_right, #content_pane[data-content="has_grid"] #title_buttons_left, #content_pane[data-content="has_grid"] #title_buttons_right, #content_pane[data-content^="has_font"] #title_buttons_left, #content_pane[data-content^="has_font"] #title_buttons_right, #content_pane[data-content^="has_glyph"] #title_buttons_right'+ '{ min-width: 9.5em; }' ; var $text_editor_styles = 'html, body, #iframe_body { margin:0; padding:0; height:100%; position:relative; font-family:'+ $settings.UI_font +'; font-size: '+ $settings.UI_font_size +'; }' + '#iframe_body { display:flex; flex-direction:column; position:relative; overflow:hidden; }' + '#iframe_body #content_text { display:flex; height:100%; padding:0; position:absolute; z-index:1; width:100%; overflow:hidden; flex-direction:column; }' + // div $warning_styles + // TOOLBAR '#toolbar { width:100%; background:#C0C0C0; position:relative; z-index:100; border-bottom: solid 1px #999; border-collapse:collapse; font-family:'+ $settings.UI_font +'; font-size:'+ parseFloat($settings.UI_font_size) * 0.875 + $settings.UI_font_size.replace(/\d*/,'') +'; -webkit-user-select: none; -moz-user-select: none; user-select:none; position:relative; }' + // table '#toolbar td { padding:2px 0 0; }' + '#toolbar_buttons { margin:0; padding:0; list-style:none; }' + // ul '.toolbar_icon { margin:0 4px; padding:4px; width:16px; height:16px; float:left; background-size:14px; background-repeat:no-repeat; background-position:center; cursor:pointer; display:block; opacity:0.6; color:#444; }' + // li '#toggle_theme { background-image:'+ SVG_Text_Editing_UI_Icon('toggle_theme') +'; }' + '#show_source { background-image:'+ SVG_Text_Editing_UI_Icon('show_markdown') +'; background-size:18px; }' + '#show_preview { background-image:'+ SVG_Text_Editing_UI_Icon('show_preview') +'; }' + '#show_html { background-image:'+ SVG_Text_Editing_UI_Icon('show_html') +'; background-size:20px; }' + '#toggle_split { background-image:'+ SVG_Text_Editing_UI_Icon('toggle_split') +'; }' + '#sync_scroll { padding:4px; float:left; opacity:1 !important; }' + // li '#sync_scroll input { float:left; }' + // '#sync_scroll label { width:8em; display:block; font-size:inherit; line-height:1.5; }' + // '#save_btn { margin:0; padding:4px 4px 4px 8px; width:20px; height:16px; float:right; position:relative; z-index:9997; background-repeat:no-repeat; background-position:center right 7px; background-image:'+ SVG_Text_Editing_UI_Icon('save_btn') +'; background-size:16px; cursor:pointer; outline:none; }' + '#save_btn ul { display:none; margin:0; padding:0 32px 0 0; position:absolute; top:-4px; right:-2px; list-style:none; box-shadow:0px 4px 6px -3px #333; background-repeat:no-repeat; background-position:right 8px top 7px; background-image:'+ SVG_Text_Editing_UI_Icon('save_btn') +'; background-size:16px; background-color:#C0C0C0; }' + '.edited #save_btn, .edited #save_btn ul { background-image:'+ SVG_Text_Editing_UI_Icon('save_btn_edited') +'; }' + '#save_btn ul li { margin:0; padding:4px 6px; text-align:right; width:100%; white-space:pre; box-sizing:border-box; }' + '#save_btn a { font-weight:normal; }' + '#save_btn:hover ul { display:block; }' + '#save_btn li:hover { background-color:#DDDDDD !important; }' + '.editor_theme_dark #save_btn ul { border-color:#EEE; box-shadow:0px 4px 6px -3px #EEE;}' + '.editor_theme_dark #save_btn li { border-color:#EEE; color:#111; background-color:#AEAEAE !important; }' + '.editor_theme_dark #save_btn li:hover { background-color:#989898 !important; }' + '.editor_theme_dark #save_text { border-color:#EEE; color:#111; }' + '#clear_text { margin:0 4px; padding:4px; height:16px; float:right; cursor:pointer; }' + // li '#toolbar li:hover, .preview_text:not(.split) #show_preview, body.source_text #show_source, .split_view #toggle_split, .edited #save_btn'+ '{ opacity:1; }' + // TEXT CONTENT CONTAINERS '#text_container { display:flex; flex-grow:1; overflow:hidden; }' + // li '#text_source { margin:0; padding:1em; display:block; border:0; line-height:1.2; overflow-y:scroll; resize:none; font-family:monospace; box-sizing:border-box; z-index:1; font-size:'+ parseFloat($settings.UI_font_size) + $settings.UI_font_size.replace(/\d*/,'') +'; }' + // textarea '#text_preview, #html_preview { margin:0; padding:1em; overflow-y:scroll; box-sizing:border-box; z-index:1; }' + // div '#html_preview { display:none; white-space:pre-wrap; word-break:break-word; font-family:monospace; }' + 'body.has_html #html_preview { display:block; }' + // RESIZE HANDLE '#text_editing_handle { display:none; width:7px; position:absolute; top:0; bottom:0; left:calc(50% - 4px); cursor:col-resize; z-index:3; }' + // '.split_view #text_editing_handle { display:block; }' + // TEXT SOURCE '#text_source, #text_preview, #html_preview { height:100%; }' + '#text_source:focus, #text_preview:focus, #html_preview:focus { outline:none; }' + '#text_source:focus { box-shadow: inset 0 0 4px #666; }' + '.editor_theme_dark #text_source:focus { box-shadow: inset 0 0 6px #000; }' + // TEXT PREVIEW '#text_preview, #html_preview { padding: 14px; box-sizing:border-box; z-index:1; overflow-y:scroll; font-size:'+ parseFloat($settings.UI_font_size) + $settings.UI_font_size.replace(/\d*/,'') +'; }' + // dark theme '.editor_theme_dark #toolbar { background: #404040; color:#EEEEEE; border-bottom: solid 1px #111; }' + '.editor_theme_dark.split_view #text_preview, .editor_theme_dark.split_view #html_preview { border-left: solid 1px #111; }' + '.editor_theme_dark #toolbar .has_background:not(#save_btn), .editor_theme_dark:not(.edited) #save_btn, .editor_theme_dark.edited #save_btn li' + '{ filter: invert(1); }' + 'body.theme_light:not(.editor_theme_dark) .background_color_DD_33, body.editor_theme_light .background_color_DD_33 { background-color: #DDDDDD; }' + 'body.theme_dark:not(.editor_theme_light) .background_color_DD_33, body.editor_theme_dark .background_color_DD_33 { background-color: #333333; }' + 'body.theme_light .background_color_EE_22, body.editor_theme_light .background_color_DD_33:focus, body.theme_light #content_container:hover, body.theme_light #content_grid::after { background-color: #EEEEEE; }' + 'body.theme_dark .background_color_EE_22, body.editor_theme_dark .background_color_DD_33:focus, body.theme_dark #content_container:hover, body.theme_dark #content_grid::after { background-color: #222222; }' + // SPLIT VIEWS '.preview_text:not(.split_view) #text_source, .has_html:not(.split_view):not(.source_text) #text_source, .source_text:not(.split_view) #text_preview, .source_text:not(.split_view) #html_preview, body.has_html #text_preview, #html_preview, li#save_btn div, .comment'+ '{ display:none; }' + '.preview_text:not(.split_view) #text_preview, .source_text:not(.split_view) #text_source, .source_text:not(.split_view) #html_preview, .has_html.preview_text:not(.split_view) #html_preview'+ '{ width:100% !important; }' + '.split_view #text_preview, .split_view #html_preview { width:50%; border-left:solid 1px #999; }' + '.split_view #text_source { width:50%; }' + // custom previewed text styles '#text_preview pre { border:solid 1px #CCC; border-radius:3px; white-space:pre-wrap; word-break:break-word; font-size:'+ parseFloat($settings.UI_font_size) + $settings.UI_font_size.replace(/\d*/,'') +'; }' + '#text_preview th, #text_preview td { vertical-align:top; }' + '#text_preview blockquote { margin-top:1em; margin-bottom:1em; color: #555; }' + '#text_preview blockquote + blockquote { margin-top:0; }' + '.markdown-body input[type="checkbox"] { margin-top:0.375em; margin-right:6px; float:left; }' + 'h1 .uplink,h2 .uplink,h3 .uplink,h4 .uplink,h5 .uplink,h6 .uplink'+ '{ display:inline-block; font-size:0.875em; transition: opacity 0.25s; opacity:0; cursor:pointer; margin:0; padding:0; }' + 'h1:hover .uplink,h2:hover .uplink,h3:hover .uplink,h4:hover .uplink,h5:hover .uplink,h6:hover .uplink'+ '{ transition: opacity 0.25s; opacity:0.5; }' + '#text_preview table { font-size:inherit; }' + '#text_preview table th { background-color:#EEE; }' + '.markdown-body::before, .markdown-body::after { display:none !important; background:transparent; }' + // disabled text editing '#iframe_body.text_editing_disabled #toolbar, #iframe_body.text_editing_disabled #preview_text, #iframe_body.text_editing_disabled #text_editing_handle'+ '{ display:none; }' + '#iframe_body.text_editing_disabled #text_source.disabled { top:0; background:white; }' + '#iframe_body.editor_theme_light .background_color_D0_50 { background-color: #D0D0D0; }' + '#iframe_body.editor_theme_dark .background_color_D0_50 { background-color: #505050; }' + '#iframe_body.editor_theme_light .background_color_E0_60 { background-color: #E0E0E0; }' + '#iframe_body.editor_theme_dark .background_color_E0_60 { background-color: #606060; }' + '#iframe_body.editor_theme_light .text_color_111, #iframe_body.editor_theme_light #content_text .text_color_111 { color: #111; }' + '#iframe_body.editor_theme_dark .text_color_111, #iframe_body.editor_theme_dark #content_text .text_color_111 { color: #EEE; }' + '#iframe_body.has_warning::after { content:""; position:absolute; top:0; right:0; bottom:0; left:0; background:rgba(0,0,0,0.33); z-index:9998; }' ; var $iframe_dir_styles = ':root, html, body { margin:0; padding:0; width:100%; max-width:100%; height:100%; border:0; border-radius:0; font-family:'+ $settings.UI_font +'; font-size:'+ $settings.UI_font_size +'; hyphens:auto; overflow:hidden; }' + '#iframe_body { width:100%; display:flex; flex-direction:column; font-size:'+ (parseFloat($settings.UI_font_size) * 0.875) + $settings.UI_font_size.replace(/\d*/,'') +'; }' + // '#iframe_body.theme_dark { background-color:#333; }' + 'a, a:hover { text-decoration: none !important; }' + // iframe header '#thead { flex-direction:row; font-size: 0.875rem; user-select:none; -webkit-user-select:none; }' + '#change_dirs { display:flex; flex-direction:row; justify-content:space-between; white-space:pre; }' + '#change_dirs a { padding-right:0 !important; padding-left:0 !important;}' + '#change_dirs span:hover { font-weight:bold; }' + '#parent { padding:5px 3px 5px 0; text-align:left; flex-grow:1; }' + '#open_in_sidebar { padding:5px 0 5px 3px; text-align:right; flex-grow:1; }' + '#parent a::before, #open_in_sidebar a::after { content:""; width:16px; height:10px; display:inline-block; }' + '#open_in_sidebar a::after { background-image:'+ SVG_UI_Icon('chevron_left') +'; background-repeat: no-repeat; background-size:6px; background-position: center; }' + '.theme_dark #parent a::before, .theme_dark #open_in_sidebar a::after { filter:invert(1); }' + // sorting '#sorting_row_1 div, #sorting_row_2 div { flex-grow:1; padding:0; }' + '#sorting_row_1 { display:flex; cursor:pointer; justify-content:space-between; flex-direction:row; }' + '#sorting_row_1 div { width:50%; }' + '.sorting span { display:inline-block; padding:3px 0; }' + '.sorting:hover span { font-weight:bold; }' + // '.sorting span::before, .sorting span::after { content:""; width:16px; height:8px; display:inline-block; position:relative; color:#CCC; background-repeat:no-repeat; background-position:center; background-size:10px; }' + '.sorting.down span::after { transform:rotate(180deg) }' + '.theme_dark .sorting span::before, .theme_dark .sorting span::after { filter:invert(1); }' + '#sort_by_name, #sort_by_ext { text-align:left; }' + '#sort_by_size, #sort_by_date, #sort_by_default, #sort_by_kind { text-align:right; }' + // iframe dir list '#iframe_dir_list_wrapper { width:100%; height:100%; display:block; position:relative; overflow:scroll; }' + // '#dir_list { width:100%; height:100%; line-height:1.5; position:relative; overflow:hidden; border-collapse:collapse; }' + // '#tbody { font-size:0.875rem; counter-reset:row; height:100%; overflow:hidden; }' + // '#tbody tr, #sorting_row_2 { display: grid; grid-gap:0; grid-template-columns: minmax(24em,100%) minmax(5.5em,8em) minmax(6.5em,14em) minmax(5.5em,5.5em); }' + '.tbody_row_cell_details { white-space:pre; font-variant-numeric:tabular-nums; padding:3px 0; }' + '.tbody_row_cell_name_a { padding:3px 0; }' + '.tbody_row_cell_name_a::before { display:none; counter-increment:row; content:counter(row); width:28px; min-width:28px; height:14px; max-height:14px; min-height:14px; margin-top:2px; padding:0; float:left; text-align:right; font-variant-numeric:tabular-nums; }' + '.show_numbers #tbody td.name a::before { display:inline-block; }' + '.tbody_row_cell_name_a_span { display:flex; padding:2px 0; word-break:break-word; }' + '.tbody_row_cell_name_a_span input { margin:2px 6px 0 0; }' + '.tbody_row_cell_details.name { grid-column: 1; text-align:left; }' + '.tbody_row_cell_details.size { grid-column: 2; padding-right:16px; padding-left:0; text-align:right; }' + '.tbody_row_cell_details.date { grid-column: 3; padding-right:16px; padding-left:0; white-space:normal; overflow:hidden; max-height:1em; overflow-wrap:break-word; text-align:right; }' + '.tbody_row_cell_details.date span { white-space:pre; display:inline-block; }' + '.tbody_row_cell_details.kind { grid-column: 4; text-align:right; padding-right:16px; }' + '.tbody_row_cell_details.ext { display:none; }' + 'tr:hover .tbody_row_cell_name_a, tr.selected .tbody_row_cell_name_a, tr.playing .tbody_row_cell_name_a { font-weight:bold; }' + 'tr.ignore, tr.ignore a { cursor:not-allowed; }' + 'tr.invisible, #iframe_body.ignore_ignored_files tr.ignore { display:none !important; }' + // display '#iframe_body.show_invisibles tr.invisible { display:grid; }' + // display // '#iframe_body tr.dimmed { background-color: rgba(172,202,235,0.50) !important; }' + CSS_UI_Icon_Rules() + // background icons '.has_icon_before::before { content:""; display:block; float:left; width:28px; min-width:28px; height:14px; max-height:14px; min-height:14px; margin-top:1px; background-position:center; background-repeat:no-repeat; background-size:14px; }' + '#dir_list.sort_by_default #sort_by_default span::before, #dir_list.sort_by_name #sort_by_name span::before, #dir_list.sort_by_size #sort_by_size span::before, #dir_list.sort_by_date #sort_by_date span::before, #dir_list.sort_by_kind #sort_by_kind span::before, #dir_list.sort_by_ext #sort_by_ext span::before'+ '{ content:""; height:9px; background-image:'+ SVG_UI_Icon('check_mark') +'; background-repeat: no-repeat; background-size:10px; background-position: center; }' + '#dir_list.sort_by_default #sort_by_default span::after, #dir_list.sort_by_name #sort_by_name span::after, #dir_list.sort_by_size #sort_by_size span::after, #dir_list.sort_by_date #sort_by_date span::after, #dir_list.sort_by_kind #sort_by_kind span::after, #dir_list.sort_by_ext #sort_by_ext span::after, #parent a::before'+ '{ background-image:'+ SVG_UI_Icon('chevron_up') +'; background-repeat: no-repeat; background-size:10px; background-position: center; }' + // GECKO STYLES: 'body.is_gecko #dir_list tr td { min-width:calc(100% - 24px); }' + 'body.is_gecko #dir_list tr td.name span { display:-webkit-box; width:calc(100% - 64px); }' + 'body.is_gecko .dir::before { content:"" !important; display:none !important; }' ; // Gecko Styles: const $gecko_style_rules = 'html, body.is_gecko { border: solid 1px gray !important; }' + '.dir::before { content:"" !important; display:none !important; }' + 'body.is_gecko button { padding:revert; }' + 'body.is_gecko #grid_btn .menu { top:-7px; left:-120px; }' + 'body.is_gecko thead { font-size:100%; }' + 'body.is_gecko #dir_list .dir::before { position:absolute; }' + 'body.is_gecko #dir_list tr td.name span { display:-webkit-box; width:calc(100% - 56px); }' + 'body.is_gecko.use_default_icons:not(.is_converted_list) #dir_list .file .name .icon { padding-left:4px; background:none; }' + 'body.is_gecko.use_default_icons #dir_list .file .name .icon img { margin-right:6px; height:14px; }' + 'body.is_gecko #tbody > tr > td:not(:first-of-type){ float:left }' + 'body.is_gecko #content_audio_title span { padding-top: 6px;, padding-bottom: 0; }' + 'body.is_gecko #audio,body.is_gecko #audio_container { background-color: transparent; }' + 'body.is_gecko.theme_dark #prev_track, body.is_gecko.theme_dark #next_track, body.is_gecko.theme_dark #close_audio { filter: invert(1); }' + 'body.is_gecko #content_pane.has_zoom_image #content_image { display:block !important;}' ; const $safari_style_rules = 'body.is_safari button { background-color: #FFF; }' + 'body.is_safari.theme_dark #prev_track, body.is_safari.theme_dark #next_track, body.is_safari.theme_dark #close_audio { filter: invert(1); }' ; // ADD STYLES const $font_styles = document.createElement('style'); const $font_grid_styles = document.createElement('style'); function addStyles(el) { el.find('style, link[rel="stylesheet"], link[href$="css"]').remove(); // remove any existing stylesheets el.append(''); el.append($font_styles); // empty until font is previewed el.append($font_grid_styles); // empty until font grid is made if ( getBrowser() === 'firefox' ) { el.append(''); } if ( getBrowser() === 'safari' ) { el.append(''); } } // ***** END STYLES ***** // // ***** INDEX PREP ***** // // Try to determine index type from parent directory link container, with fallbacks for indexes that don't have parent directories, or for parent directory links that aren't siblings or ancestors of the index itself. function getIndexType() { let parentLinkParent; // Possible elements: pre, li, td, th, div -- used to determine index type // Try to find parent directory link: let parentLink = $('a:contains("Parent Directory"), a:contains("parent directory"), a[href="../"], a[href="/"], a[href^="?"], img[alt*="PARENTDIR"]'); if ( parentLink.length === 0 ) { parentLinkParent = document.querySelectorAll('body > ul li, body > pre, body > table:last-of-type tr td'); } else { parentLinkParent = parentLink.parent(); // use original found parentLink } // If no parentLinkParent found, type = error; else return parent node name let nodeName = ( parentLinkParent[0] !== undefined ? parentLinkParent[0].nodeName.toLowerCase() : ''); switch(true) { case parentLinkParent.length === 0: nodeName = 'error'; break; case $protocol.startsWith('file'): if ( getBrowser() === 'firefox' ) { nodeName = 'gecko'; } break; } let types = {'gecko':'gecko','li':'list','pre':'pre','th':'table','td':'table','div':'default','error':'error'}; let type = types[nodeName]; return type; } // Return Index items, Index type, remove parent directory link, and add body class. function getIndexItems() { const type = getIndexType(); let items; switch(type) { case 'gecko': items = $('body').find('> table > tbody'); break; case 'list': items = $('body').find('> ul'); break; case 'pre': items = $('body').find('> pre').html(); break; case 'table': case 'td': if ( $('table > tbody').length === 1 ) { items = $('body').find('table > tbody'); } else { items = $('body').find('table'); // tables without tbody } // if ( $('table.PARENTTABLE > tbody').length === 1 ) { // items = $('body').find('table.PARENTTABLE > tbody'); // } else { // items = $('body').find('table.PARENTTABLE'); // tables without tbody // } items.find('tr th, tr td:contains(Parent Directory), a[href="../"], hr').parent('tr').remove(); break; case 'default': items = $('body').find('> table').find('> tbody'); break; case 'error': items = $('body').html(); break; } return [items,type]; } // Index Prep: convert rows and return array of rows, with link, size, date-modified function convertIndexItems(type,items) { let converted = []; switch(type) { case 'gecko': converted = convertGeckoType(items); break; case 'list': converted = convertListType(items); break; case 'pre': converted = convertPreType(items); break; case 'table': case 'default': converted = convertTableType(type,items); break; case 'error': converted = ''; break; } return converted; } // Index Prep: convert list type function function convertGeckoType(items) { let preppedIndex = []; const rows = Array.from(items.find('> tr')); for ( let row of rows ) { let preppedRow = [], cellContents = '', cells = Array.from( $(row).find('> td') ), link = ($(cells).find('a').attr('href')); for ( let cell of cells ) { cellContents = cell.innerText; cellContents = ( cellContents !== undefined ? cellContents.trim() : ''); preppedRow.push(cellContents); } preppedRow[1] = preppedRow[1].replace(/\s*KB/,'000'); // convert reported size in KB to total bytes preppedRow[2] = preppedRow[2] + ' '+ preppedRow[3]; preppedRow = preppedRow.slice(1,-1); if ( link.length > 0 && link !== '/' && link !== '../' ) { preppedRow.unshift(link); } if ( preppedRow.length > 0 ) { preppedIndex.push(preppedRow); } } return preppedIndex; } // Index Prep: convert list type function function convertListType(items) { let preppedIndex = []; const rows = items[0].children; for ( let i = rows.length; i--; ) { let row = rows[i]; if ( row.innerHTML.indexOf('Parent Directory') === -1 ) { let preppedRow = []; let link = $(row).find('a').attr('href'); row = row.innerHTML.replace(/\s*/,''); let cells = row.split(' '); for ( let cell of cells ) { if ( cell.trim().length > 0 ) { preppedRow.push(cell); } } if ( link.length > 0 && link !== '/' && link !== '../' ) { preppedRow.unshift(link); } if ( preppedRow.length > 0 ) { preppedIndex.push(preppedRow); } } } return preppedIndex; } // Index Prep: convert pre type function function convertPreType(items) { let preppedIndex = []; const spaces = /\s{2,}/; items = items.replace(/\[\s*?\]/g,'[]') // remove empty 'alt=[ ]' .replace(/\s*<(hr|img[^>])>\s*/g,'') // remove hr and images .replace(/\s*]+?>[^<]+?<\/a>\s*/g,'') // remove sorting links (href beginning with "?") .replace(/(]+?>)[^<]+?<\/a>/g,' $1 ') // remove link display text and add two spaces after .replace(/(\%20){2,}/g,'\%20'); // remove extra spaces in hrefs const rows = items.split('\n'); // create array of rows from items for ( let i = rows.length; i--; ) { let row = rows[i]; let preppedRow = []; let cells = row.split(spaces); // assumes pre-type only uses two or more spaces between "columns" let link; for ( let j = cells.length; j--; ) { let cell = cells[j]; if ( cell.trim().length > 0 ) { if ( !cell.startsWith(' 1 ) { preppedIndex.push(preppedRow); } // add prepped row to index } return preppedIndex; } // Index Prep: convert table type function function convertTableType(type,items) { // for local chrome indexes and server-generated table-type indexes let preppedIndex = []; const rows = items[0].children; for ( let i = rows.length; i--; ) { let row = rows[i], preppedRow = [], link, cell, cells = row.cells; for ( let j = cells.length; j--; ) { switch(true) { case cells[j].children[0] !== undefined && cells[j].children[0].nodeName === 'A': link = cells[j].children[0].attributes.href.value; break; case cells[j].innerHTML !== ' ' && cells[j].innerHTML.indexOf(' 0 ) { preppedRow.push( cell ); } break; } } if ( link !== undefined ) { preppedRow.unshift(link); } if ( preppedRow.length > 1 ) { preppedIndex.push(preppedRow); } // preppedRow.length > 2 in order to omit parent directory row } return preppedIndex; } // Index Prep: Build new Index from prepped rows function buildNewIndex(id,preppedIndex,sort,type) { let newIndexItems = [], bodyClasses = new Set(), rowLinkPrefix = ''; if (type === 'gecko' ) { rowLinkPrefix = document.baseURI; } // because FF only uses the let stats = { total:0, dir:0, file:0, hidden:0, invisible:0, ignore:0, dir_ignore:0, dir_invisible:0, file_invisible:0, file_ignore:0 }; let selected = ( getQuery('selected').length > 0 ? Number(getQuery('selected')) : null); for ( let i = preppedIndex.length; i--; ) { let row = preppedIndex[i]; // Get row attrs and text const rowLink = row[0] !== undefined ? row[0].replace(/&/g,'&') : '';// .replace(//,''); // decode some reserved characters let url = new URL(rowLink.replace(/^\//,''),document.baseURI); const rowName = getItemName(rowLink).replace(/^\s/m,'\ ').replace(/^\//m,'').replace(/([-_——])/g,'$1'); // display name, with word breaks added after unbreakable chars const rowSortName = getItemName(rowLink).replace(/^\//m,'').replace('/','').toLocaleLowerCase(); const sizesAndDates = getItemSizeAndDate(row); const rowSize = sizesAndDates[0]; const rowSortSize = sizesAndDates[1]; const rowDate = sizesAndDates[2]; const rowSortDate = sizesAndDates[3]; const rowExt = getItemExt(rowLink); const rowSortKind = getItemKind(rowExt); const rowKind = rowSortKind.slice(0,1).toUpperCase() + rowSortKind.slice(1); const rowClasses = getItemClasses(rowName,rowSortKind,rowExt); // Assemble row elements let newRow = $('').attr('id','rowid-'+ i).addClass(rowClasses).attr('data-kind',rowSortKind).attr('data-ext',rowExt); newRow.data('data-url',url); if ( window.self === window.top && selected !== undefined && i === selected ) { newRow.addClass('selected'); } // autoSelect file const checkbox = ( /media/.test(rowClasses) ? $('') : '' ); const nameSpan = $('').append(checkbox, rowName); const cellLink = $('').addClass('icon tbody_row_cell_name_a text_color_111').attr('href',rowLinkPrefix + rowLink).append(nameSpan); const cellName = $('').addClass('tbody_row_cell tbody_row_cell_name name').attr('data-name',rowSortName).append(cellLink); const cellSize = $('').addClass('tbody_row_cell_details size details').attr('data-size',rowSortSize).append(rowSize); const cellDate = $('').addClass('tbody_row_cell_details date details').attr('data-date',rowSortDate).append(rowDate); const cellKind = $('').addClass('tbody_row_cell_details kind details').attr('data-kind',rowSortKind).append(rowKind); const cellExt = $('').attr('data-ext',rowExt).addClass('tbody_row_cell_details ext details'); // Assemble row newRow.append(cellName, cellSize, cellDate, cellKind, cellExt); newIndexItems.push(newRow[0]); // body classes (set object: unique values) switch(rowSortKind) { case 'audio': bodyClasses.add('has_media').add('has_audio'); break; case 'video': bodyClasses.add('has_media').add('has_video'); break; case 'font' : bodyClasses.add('has_fonts'); break; case 'image': bodyClasses.add('has_images'); break; } switch(type) { case 'gecko': bodyClasses.add('is_converted_gecko'); break; case 'list': bodyClasses.add('is_converted_list'); break; case 'pre': bodyClasses.add('is_converted_pre'); break; case 'table': case 'td': bodyClasses.add('is_converted_table'); break; case 'default': bodyClasses.add('is_default'); break; case 'error': bodyClasses.add('is_error'); break; } // Stats for visible files stats.total = ++stats.total; // increment total number of items if ( rowSortKind === 'dir' ) { ++stats.dir; } else { ++stats.file; } // increment total number of dirs or files if ( !stats[rowKind] && !/invisible|ignore/.test(rowClasses) ) { stats[rowKind] = 1; } else { ++stats[rowKind]; } // increment rowKind or add it to the stats if it is not already there // stats for invisible/ignored files if ( /invisible|ignore/.test(rowClasses) ) { // if it's invisible or ignored --> why is this so complicated? ++stats.hidden; if ( /invisible/.test(rowClasses) ) { // if it's invisible... ++stats.invisible; // increment the invisible count if ( !stats[rowKind +'_invisible'] ) { stats[rowKind +'_invisible'] = 1; } else { ++stats[rowKind +'_invisible']; } // increment rowKind or add it if ( /dir/.test(rowClasses) ) { // if it's an invisible directory... if ( !stats.dir_invisible ) { stats.dir_invisible = 1; } else { ++stats.dir_invisible; } // increment count or add it to the list } else { // or an invisible file if ( !stats.file_invisible ) { stats.file_invisible = 1; } else { ++stats.file_invisible; } // increment count or add it to the list } } else { // else it is ignored ++stats.ignore; // increment the ignored count if ( !stats[rowKind +'_ignore'] ) { stats[rowKind +'_ignore'] = 1; } else { ++stats[rowKind +'_ignore']; } if ( /dir/.test(rowClasses) ) { // if it's an ignored directory... (is there such a thing?) if ( !stats.dir_ignore ) { stats.dir_ignore = 1; } else { ++stats.dir_ignore; } } else { // on an ignored file... if ( !stats.file_ignore ) { stats.file_ignore = 1; } else { ++stats.file_ignore; } } } } } $('body').addClass(Array.from(bodyClasses).join(' ')).data('stats',stats); // add stats data to body and body classes if ( sort === undefined ) { sort = getQuery('sort_by'); } // get sorting pref let sort_direction = ( getQuery('sort_direction') === 'up' ? -1 : 1 ); // get sort direction let sortedIndexItems = sortDirList($(newIndexItems), 'sort_by_'+ sort, sort_direction); // make initial sort return sortedIndexItems; } // Index Prep: get row name function getItemName(link) { let name; try { name = decodeURIComponentSafe(link); } catch (error) { name = link.replace(/%20/g,' ').replace(/\s{2,}/,' '); } if ( name.split('/').length > 2 ) { // get name only if x = full path let arr = name.split('/'); if ( name.startsWith('/') && name.endsWith('/') ) { // dirs name = arr[arr.length - 2] + '/'; } else { name = arr[arr.length - 1]; // files } } return name; } // Index Prep: get row classes function getItemClasses(name,kind,ext) { if ( ext === undefined ) { ext = ''; } let itemClasses = []; itemClasses.push(kind); if ( !/dir|app/.test(ext) ) { itemClasses.push('file'); } if ( ext === 'app' ) { if ( $settings.apps_as_dirs === false ) { itemClasses.push('file'); } else { itemClasses.push('dir'); } } if ( name.endsWith('symlink') || name.endsWith('alias') || name.endsWith('symbolic link') ) { itemClasses.push('alias'); } if ( name.startsWith('.') ) { itemClasses.push('invisible'); } if ( !JSON.stringify($row_types).match( escapeStr(ext) ) ) { // else classify as "other" if extension is not in $row_types. itemClasses.push('other'); //itemType = 'Other'; itemKind = 'other'; } else if ( kind === '' ) { // itemClasses.push('code'); //itemType = 'Code'; itemKind = 'code'; } else { itemClasses.push(ext); } if ( $row_settings.ignore.includes( ext ) ) { itemClasses.push('ignore'); } if ( $row_settings.exclude.includes( ext ) ) { itemClasses.push('exclude'); } if ( kind === 'audio' || kind === 'video' ) { itemClasses.push('media'); } itemClasses = Array.from(new Set(itemClasses)); // remove dupe classes return itemClasses.join(' '); } // Index Prep: get formatted row size and date function getItemSizeAndDate(cells) { let sizesAndDates = [], rowDisplaySize, rowSortSize, rowDisplayDate, rowSortDate; if ( cells.length > 1 ) { if ( cells[1].search(/[-:\/]/) !== -1 ) { // test for typical date/time separators. rowDisplayDate = cells[1]; rowDisplaySize = cells[2]; } else { rowDisplayDate = cells[2]; rowDisplaySize = cells[1]; } } // size let sizeUnits = /[BYTES|B|K|KB|MB|GB|TB|PB|EB|ZB|YB]/; if ( /undefined|—|-|,/.test(rowDisplaySize) || rowDisplaySize === '' ) { // if size is undefined, empty, or punctuation rowDisplaySize = '—'; rowSortSize = '0'; // if no size supplied, use these defaults } else { rowSortSize = getItemSortSize(rowDisplaySize); if ( !rowDisplaySize.toUpperCase().match(sizeUnits) ) { // if provided size is only numeric rowDisplaySize = formatBytes(rowDisplaySize,1); // format byte size } else { rowDisplaySize = rowDisplaySize.replace('K','k').replace(/(\d+)\s*([A-z])/,'$1 $2'); // ensure display size has space between number and units } } // date if ( [undefined,'','-'].includes(rowDisplayDate) ) { rowDisplayDate = '—'; rowSortDate = '0'; } else { rowSortDate = getItemDate(rowDisplayDate); } rowDisplayDate = rowDisplayDate.replace(/, (.+)/,', $1'); // add span for short date sizesAndDates.push(rowDisplaySize,rowSortSize,rowDisplayDate,rowSortDate); return sizesAndDates; } // Index Prep: get row size for sorting function getItemSortSize(val) { let sortSize, values = val.replace(/(\d+)\s*([A-z]+)/,'$1 $2').split(' '), size = values[0], unit = values[1]; if ( unit !== undefined ) { unit = unit.toUpperCase(); } const factor = { '':1, B:1, K:1000, KB:1000, M:1000000, MB:1000000, G:1000000000, GB:1000000000, T:1000000000000, TB:1000000000000, P:1000000000000000, PB:1000000000000000, E:1000000000000000000, EB:1000000000000000000, Z:1000000000000000000000, ZB:1000000000000000000000 }; // unit to file size sortSize = size * factor[unit]; // convert byte size to multiplication factor return sortSize; } // convert numeric sizes to display format function formatBytes(val, decimals) { if (val === 0) return '0 Bytes'; const k = 1024, dm = (decimals < 0 ? 0 : decimals), sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], i = Math.floor(Math.log(val) / Math.log(k)); return parseFloat((val / Math.pow(k, i)).toFixed(dm)) +' '+ sizes[i]; } // process date function processDate(match,p1,p2,p3) { //date formats: 2017-10-09 13:12 || 2015-07-25T02:02:57.000Z || 12-Mon-2017 21:11 const mo = 'JanFebMarAprMayJunJulAugSepOctNovDec'.indexOf(p2)/3 + 1; // e.g., convert month into number, or use number return p3 +'-'+ mo +'-'+ p1; } // Index Prep: get row date 2015-07-25T02:22:00.000Z function getItemDate(val) { let sortDate = val.replace(/^(\d{2})-(\w{3})-(\d{4})/m, processDate) // convert Month to number .replace(/\b(\d{1})[-:/]/g,'0$1/') // add leading 0 for single digit numbers .replace(/(\d{2})\/(\d{2})\/(\d{2}),/,'$3$1$2') // reorder MM/DD/YY dates .replace(/-|:|\s+|\//g,''); // remove spacing characters return sortDate; } // Index Prep: get row kind function getItemKind(ext) { let kind = ''; switch(true) { case ext === 'dir' || ext === 'app': kind = ext; break; default: for ( let types in $row_types ) { if ( $row_types[types].includes( ext ) ) { kind = types; return kind; } else { kind = 'other'; } } } return kind; } // Index Prep: get row extension function getItemExt(link) { let ext = ''; switch(true) { case link.endsWith('app/') || link.endsWith('exe'): ext = 'app'; break; case link.endsWith('/'): ext = 'dir'; break; case !/\./.test(link): // if no '.' in link (typical for bin files), ... break; default: // find the last . and get the remaining characters ext = link.slice(link.lastIndexOf('.') + 1).toLowerCase(); } return ext; } // END INDEX PREP // ***** MAKE NEW INDEX ***** // function makeNewIndex(el,sort) { const indexItems = getIndexItems(el), items = indexItems[0], type = indexItems[1]; const convertedIndex = convertIndexItems( type, items ); // = array of rows: ["link","date","size"] if ( type === 'error' ) { return [convertedIndex]; } else { let newIndex = buildNewIndex( $(el).attr('id'), convertedIndex, sort, type ); return [newIndex]; } } // get and set index stats function sortObject(obj) { return Object.keys(obj).sort().reduce((r, k) => (r[k] = obj[k], r), {}); } function getIndexStats() { if ( $('body').data('stats') === undefined ) { return; } var stats = $('body').data('stats'), i = 1, padding_style = ''; stats = sortObject(stats); // sort by kind const totalHidden = '('+ stats.hidden +' invisible or ignored)'; const hiddenDirs = ' ('+ (stats.dir_invisible + stats.dir_ignore) +' invisible or ignored)'; const hiddenFiles = ' ('+ (stats.file_invisible + stats.file_ignore) +' invisible or ignored)'; const dirsLabel = ( stats.dir === 1 ? ' Directory' : ' Directories' ); const filesLabel = ( stats.file === 1 ? ' File' : ' Files' ); let details_rows = ''; var displayName = '', hiddenName = ''; for ( let stat in stats ) { if ( i !== 1 ) { padding_style = 'first'; } if ( Number.isNaN(stats[stat]) ) { stats[stat] = 0; } displayName = stat.replace(/_(.+)/,' ($1)'); if ( !/total|dir|file|^hidden|^invisible|^ignore/m.test(stat) ) { // don't include these stats in details rows details_rows += (``); // create list of kinds and counts } i = 0; } $('#stats_summary').html(stats.total +' Items: '+ stats.dir + dirsLabel +', '+ stats.file + filesLabel); $('#stats_summary_detailed_total').html( ''+ stats.total +' Items '+ totalHidden +'' ); $('#stats_summary_detailed_dirs').html( ''+ stats.dir +''+ dirsLabel + hiddenDirs +'' ); $('#stats_summary_detailed_files').html( ''+ stats.file +''+ filesLabel + hiddenFiles +'' ); $('#total').html( stats.total +' Items' ); $('#total_hidden').html( totalHidden ); $('#dirs_length').html( stats.dir ); $('#dirs_label_hidden_dirs').html( dirsLabel + hiddenDirs ); $('#files_length').html( stats.file ); $('#files_label_hidden_files').html( filesLabel + hiddenFiles ); $('#stats_details_container').empty().append( details_rows ); } // ***** END DIR_LIST SETUP ***** // const $body = $('body'); // ***** UI SETUP ***** // // UI Setup: build UI and add body classes and other initial settings function setupUIprefs() { for ( let key in $settings ) { switch(true) { case getQuery(key) === 'true': $body.addClass(key); if ( getQuery('toggle_sidebar') === 'true' ) { $body.addClass( 'has_hidden_sidebar' ); } if ( getQuery('show_invisibles') === 'true' ) { $('#show_invisibles').attr('checked','checked'); } break; default: // non-boolean settings and others switch(true) { case key === 'theme': $body.addClass( 'theme_'+ getQuery('theme') ); break; case key === 'sort_by': $body.addClass( 'sort_by_'+ getQuery(key) ); $('#sort_by_'+ getQuery(key) ).addClass('selected'); if ( getQuery('sort_direction') === 'up' ) { $('#sort_by_'+ getQuery(key) ).addClass('up'); } break; case key === 'default_text_view': if ( getQuery(key) === 'source_text' ) { $body.addClass('source_text'); } else { $body.addClass('preview_text'); } break; } if ( getQuery('editor_theme') !== '' ) { $body.addClass( 'editor_theme_'+ getQuery('editor_theme') ); } } } switch( getBrowser() ) { case 'chrome': $body.addClass('is_chrome'); break; case 'firefox': $body.addClass('is_gecko'); break; case 'safari': $body.addClass('is_safari'); break; } if ( navigator.platform.indexOf('Win') > -1 ) { $body.addClass('is_windows'); } if ( $protocol !== 'file:' ) { $body.addClass('is_non_local'); } } // Build UI: Append all assembled elements to $body function buildUI() { if ( window.self === window.top ) { // if it's not an iframe... document.title = 'Index of: '+ $location; $('head').prepend('');// .find('#title').removeAttr('id'); if ( $location.startsWith('file') ) { // add favicon for local directories let favicon = 'iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAgMAAAC+UIlYAAAACVBMVEUmRcmZzP8zmf8pVcWPAAAAAXRSTlMAQObYZgAAAFBJREFUeF7tyqERwDAMBEE3mX5UiqDmqwwziTPHjG7xrmzrLFtRaApDIRiKQlMYCsFQFJrCUAiGotAU5hTA1WB4fhkMBsOJwWAwgHvB8CHpBcTbpxy4RZNvAAAAAElFTkSuQmCC'; $('head').prepend(''); } const newIndex = makeNewIndex('body'); // make index $main_content.find('#tbody').append(newIndex); // append index to body $('body').empty().attr('id','top').attr('lang','en').append($main_content); // setup if ( !$('body').hasClass('is_error') ) { getIndexStats(); setupUIprefs(); addStyles( $('head') ); initMedia(); autoLoadFile(); setContentTitle( $('tr.loaded').attr('data-kind') ); } else { $('#content_pane').addClass('has_iframe'); $('#content_iframe').attr('src',$location +'/?is_error=true').addClass('has_content'); } } else { // set up iframe setUpIframeUI(); } } buildUI(); // Get DOMTokenList of body classes function getClassList(id) { return document.getElementById(id).classList; } // Set up iFrame UI // SET UI TO DEFAULT SETTINGS: remove queries; function defaultSettings() { let query_str = '?'; if ( getQuery('selected') !== undefined ) { query_str += 'selected='+ getQuery('selected'); } if ( getQuery('history') !== undefined ) { query_str += 'history='+ getQuery('history'); } query_str = query_str.replace(/\s/g,'+'); window.location.assign($location + query_str); } $('#default_settings').on('click', function(e) { e.preventDefault(); e.stopPropagation(); $('body').removeClass('has_menu'); if (window.confirm( 'Are you sure you want to remove all your temporary UI settings from the URL query string?' ) ) { defaultSettings(); } }); // SAVE USER SETTINGS function saveSettings(filename, data) { saveFile(data,'application/json',filename); } $('#export_settings').on('click',function(e) { e.preventDefault(); e.stopPropagation(); $('body').removeClass('has_menu'); const $settings_string = ( JSON.stringify($settings,null,'\t')); saveSettings('settings.json',$settings_string); }); // Show/Close Help function showHelp() { $('body#top').addClass('has_help'); } $('#show_help').on('click','span', function(e) { e.stopPropagation(); showHelp(); }); $('#close_help').on('click', function(e) { e.preventDefault(); $('body').removeClass('has_help'); }); $('#help_container').on('click', function(e) { e.stopPropagation(); }); // Click Menu Bookmark (with warning) function setLocation(link) { window.location = link; } $('#parent_dir_nav, #parents_dir_nav + .menu, #menu .bookmark').on('click','a', function(e) { e.preventDefault(); switch(true) { case $(this).attr('href').indexOf('file://') > -1 && $protocol !== 'file:': $body.addClass('has_warning').find('#warnings_container').addClass('local_bookmark'); break; case $body.hasClass('has_playlist'): case $body.hasClass('has_filelist'): closePlaylist(); break; default: showWarning( 'setLocation', thisLink($(this)) ); } }); // Show Menus function showMenus(el) { sendMessage('iframe','top_has_menu'); $('body').removeClass('has_hidden_sidebar'); let position = $(el).position(), id = $(el).attr('id'); if ( $('body').hasClass('has_stats') ) { $('#stats').click(); } $(el).find('> ul').css({'top':position.top + $(el).innerHeight() + 1 + 'px'}); if ( id === 'parents_dir_menu' ) { if ( $('body').hasClass('has_menu_parents') ) { $('body').removeClass('has_menu_parents'); } else { $('body').addClass('has_menu_parents').removeClass('has_menu'); } } if ( id === 'menu_container' ) { if ( $('body').hasClass('has_menu') ) { $('body').removeClass('has_menu'); sendMessage('iframe','top_closed_menu'); } else { $('body').addClass('has_menu').removeClass('has_menu_parents'); sendMessage('iframe','top_has_menu'); } } } $('#parents_dir_menu, #menu_container').on('click',function(e) { e.stopPropagation(); showMenus($(this)); }); // Click menu function clickMenu() { $('#menu').find('.selected:not(.hovered)').removeClass('selected').find('a,> span,label').click(); $('#menu').find('.hovered').removeClass('selected hovered'); $('body').removeClass('has_menu'); if ( $('body').hasClass('focus_content') ) { sendMessage('iframe','close_menu'); $('#content_iframe').focus(); } if ( $body.hasClass('focus_content') ) { focusContent(); } } // Close menus function closeMenus() { if ( !$('body').hasClass('has_menu') && !$('body').hasClass('has_menu_parents') && !$('body').hasClass('has_stats') ) { return; } else { $('body').removeClass('has_menu has_menu_parents faded has_stats has_help'); } if ( $body.hasClass('focus_content') ) { focusContent(); } } // Show Stats function showStats() { $('body').addClass('has_stats').removeClass('has_menu has_menu_parents'); } $('#stats_summary').on('click', function(e) { e.stopPropagation(); showStats(); }); $(document).on('click', function(e) { e.stopPropagation(); closeMenus(); }); // Toggle UI Preferences function toggleUIpref(prefID) { let prefClass = prefID; switch(prefID) { case 'sort_by_name': case 'sort_by_default': case 'sort_by_size': case 'sort_by_date': case 'sort_by_kind': case 'sort_by_ext': // sorting $('body').removeClass('has_menu sort_by_default sort_by_name sort_by_size sort_by_date sort_by_kind sort_by_ext'); break; case 'theme': // theme if ( $('body').hasClass('theme_dark') ) { prefID = 'theme_dark'; } else { prefID = 'theme_light'; } prefClass = 'theme_dark theme_light'; sendMessage('iframe',prefID); break; case 'source_text': case 'preview_text': // text editing default view prefClass = 'preview_text source_text'; prefID = 'default_text_view'; break; case 'split_view': case 'alternate_background': case 'ignore_ignored_files': case 'show_invisibles': case 'show_numbers': sendMessage('iframe',prefID); break; } toggleQuery(prefID); $('body#top').toggleClass(prefClass); if ( prefID === 'disable_text_editing' ) { // needs to be here and not in switch above $('.selected.text, .selected.markdown, .selected.code').find('a').click(); } scrollThis('tbody','selected',false); // true = instant scroll $('#stats').html(getIndexStats($('#dir_list').find('#tbody tr'))); if ( $('body').hasClass('focus_content') ) { focusContent(); } } // Click Toggle UI Pref elements $('.toggle_UI_pref').on('click',function(e) { if ( !$(this).is('input') ) { e.preventDefault(); } // allow checkboxes to be checked if ( !$(this).hasClass('disabled') ) { toggleUIpref( $(this).attr('id') ); } }); // Toggle Sidebar function toggleSidebar() { $body.toggleClass('has_hidden_sidebar'); if ( $body.hasClass('has_hidden_sidebar') ) { setQuery('toggle_sidebar','true'); } else { removeQuery('toggle_sidebar'); } if ( $body.hasClass('focus_content') ) { focusContent(); } scrollThis('tbody','selected',true); // true = instant scroll } $('#toggle_sidebar').on('click', function(e) { e.stopPropagation(); toggleSidebar(); }); // RESIZE Sidebar/Content Pane function resizeSidebar(f) { f.stopPropagation(); const $sidebar_wrapper = $('#sidebar_wrapper'); const $startX = f.pageX; let $window_width = window.innerWidth; let $sidebar_width = $sidebar_wrapper.width(); $('body').addClass('has_overlay'); $(document).on('mousemove',function(e) { e.stopPropagation(); e.preventDefault(); const $deltaX = e.pageX - $startX; if ( e.pageX > 230 && e.pageX < $window_width - 200 ) { $sidebar_wrapper.css({'width':$sidebar_width + $deltaX + 'px'}); $content_pane.css({'width':($window_width - $sidebar_width) - $deltaX + 'px'}); } scrollThis('tbody','selected',false); // true = instant scroll }); $(document).on('mouseup',function() { $('body').removeClass('has_overlay'); $(document).off('mousemove'); $sidebar_width = $sidebar_wrapper.width(); setQuery('width',$sidebar_width); if ( $body.hasClass('focus_content') ) { focusContent(); } }); } $('#handle').on('mousedown', function(f) { f.stopPropagation(); resizeSidebar(f); }); // ***** BASIC UI FUNCTIONS ***** // // Get dir_list item row function thisRow(x) { return $(x).closest('#dir_list > tbody > tr').length > 0 ? $(x).closest('#dir_list > tbody > tr') : $(x).closest('#dir_list > li'); } function thisID(x) { return thisRow(x).attr('id'); } // Get row link function thisLink(x) { return $(x).find('a').length > 0 ? $(x).find('a').attr('href') : $(x).attr('href'); } // Get row name function thisText(x) { return $(x).find('a span').length > 0 ? decodeURIComponentSafe( $(x).find('a span').text().toLocaleLowerCase() ) : decodeURIComponentSafe( $(x).text().toLocaleLowerCase() ); } // get row text function thisExt(x) { let $this_name = thisText(x); return $this_name.endsWith('app/') ? 'app' : $this_name.endsWith('/') ? '/' : $this_name.lastIndexOf('.') === -1 ? undefined : $this_name.toLocaleLowerCase().slice( $this_name.lastIndexOf('.') + 1 ); } // get Element by ID alias function getElById(id) { return $(document.getElementById(id) ); } // SET CONTENT HEIGHT // window.addEventListener('resize', setContentHeight ); // SET CONTENT TITLE function setContentTitle(kind) { if ( kind === undefined ) { return; } let $title = $('#title span'); let $title_text;// = $('tr.loaded').not('.audio').find('td.name span').text(); switch(true) { case kind === 'audio': // if audio, don't set content title break; case $body.hasClass('has_directory_source'): $title_text = $current_dir_path; break; default: $title_text = ( $('tr.selected:not(.audio)').length ? $('tr.selected:not(.audio)').find('.name span').text() : $('tr.loaded:not(.audio)').find('.name span').text() ); break; } if ( kind === 'image' ) { setImageDimensions(); } $title.empty().html($title_text); // set text } // Get Image Dimensions function getDimensions(link, callback) { if ( link !== undefined ) { let img = new Image(); img.src = link; img.onload = function() { callback( this.width, this.height ); }; } } function setImageDimensions() { if ( window.top !== window.self ) { return; } else if ( $content_pane.attr('data-content') === 'has_image' ) { let link = $content_image.find('img').attr('src'); getDimensions( link, function( width, height ) { $('#title span').attr('data-after',' (' + width + 'px × ' + height + 'px) ('+ ( ($content_image.find('img').width()/width)*100 ).toFixed(1) +'%)'); }); } else { $('#title span').removeAttr('data-after'); // remove image dimensions } } // Scroll Selected Items function scrollThis(containerID,scrollElClass,bool) { const container = document.getElementById(containerID); if ( container.height === 0 ) { return; } // don't scroll hidden elements let $scrollEl = ( scrollElClass !== undefined ? container.getElementsByClassName(scrollElClass) : null ); let $behavior = ( ( bool !== undefined || bool === true || $content_pane.attr('data-content') === 'has_grid' ) ? 'instant' : 'smooth' ); // instant allows sidebar & grid to scroll simultaneously let $block = ( $('body').hasClass('is_gecko') ? 'start' : 'nearest' ); if ( $scrollEl !== undefined && $scrollEl.length === 1 ) { $scrollEl[0].scrollIntoView({ behavior:$behavior, block:$block, inline:'nearest' }); } } // ***** SORTING ***** // function sortIndex(els,id,sortDirection) { // id = sort type let sorted = []; const newSort = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }); // needs to be above makeNewIndex() sorted = els.removeClass('sorted border_top').sort((a, b) => { let aName, bName; if ( $('body').hasClass('has_playlist') || $('body').hasClass('has_filelist') ) { aName = $(a).attr('id'); bName = $(b).attr('id'); } else { aName = $(a).find('td.name').data('name'); bName = $(b).find('td.name').data('name'); } let aData = $(a).find('td[data-'+ id +']').data(id); let bData = $(b).find('td[data-'+ id +']').data(id); if ( sortDirection === 1 ) { // sort by detail data value, then by name (i.e., if data values are the same, sort by name) if ( newSort.compare(aData, bData) === 0 ) { return newSort.compare(aName, bName); } else { return newSort.compare(aData, bData); } } else { // reverse sort if ( newSort.compare(bData, aData) === 0 ) { return newSort.compare(bName, aName); } else { return newSort.compare(bData, aData); } } }); sorted = sorted.sort((a, b) => { // add sorted style (rules between different rows) if ( id === 'kind' || id === 'ext' && sortDirection === 1 ) { if ( $(a).find('td[data-'+ id +']').data(id) !== $(b).find('td[data-'+ id +']').data(id) ) { $(a).addClass('sorted border_top'); } } if ( id === 'kind' || id === 'ext' && sortDirection === -1 ) { if ( $(b).find('td[data-'+ id +']').data(id) !== $(a).find('td[data-'+ id +']').data(id) ) { $(a).addClass('sorted border_top'); } } }); return sorted; } // Sort the Dir List on click function sortDirList(rows, id, sortDirection) { const $sort_all = rows; const $sort_dirs = $sort_all.filter('.dir:not(.app)'); const $sort_files = $sort_all.filter('.file,.app'); const $sort_id = id.slice(8);// === 'default' ? 'name' : id; let $sorted = []; if ( $sort_id === 'default' || ( $sort_id !== 'name' && $settings.dirs_on_top === true )) { const $sorted_dirs = sortIndex($sort_dirs, $sort_id, sortDirection); const $sorted_files = sortIndex( $sort_files, $sort_id, sortDirection ); if (sortDirection === 1) { $sorted = $.merge($sorted_dirs,$sorted_files); } else { $sorted = $.merge($sorted_files,$sorted_dirs); } } else { $sorted = sortIndex( $sort_all, $sort_id, sortDirection ); } return $sorted; } // Sort on click function clickSort(id) { const el = $(getElById(id)); // id from th sort item = 'sort_by_xxx' let sortDirection, querySortDirection; switch(true) { case !el.hasClass('selected'): // el is not selected $('#sorting_row_1,#sorting_row_2').find('> div').removeClass('up selected'); el.addClass('selected'); sortDirection = 1; break; case el.hasClass('selected'): switch(true) { case !el.hasClass('up'): el.addClass('up'); sortDirection = -1; break; case el.hasClass('up'): el.removeClass('up'); sortDirection = 1; break; } } querySortDirection = ( sortDirection !== -1 ? 'default' : 'up' ); setQuery('sort_direction',querySortDirection); // SORT EM! let rows = $('#tbody').find('tr'); const $sorted_index = sortDirList( rows, id, sortDirection ); $dir_list_body.empty().append($sorted_index); switch(true) { case $content_pane.hasClass('has_playlist'): case $content_pane.hasClass('has_filelist'): break; case $content_pane.hasClass('has_font_grid'): $('#show_font_grid').click(); break; case $content_pane.hasClass('has_image_grid'): $('#show_image_grid').click(); break; case $content_pane.attr('data-content') === 'has_grid': $('#grid_btn').click(); break; } scrollThis('tbody','selected',false); // true = instant scroll } // Sort on clicking dir_list sort item $('#sorting').on('click','.sorting, #play_toggle',function(e) { e.preventDefault(); e.stopPropagation(); switch(true) { case $(this).attr('id') === 'play_toggle': toggleAllChecked(); break; // toggle media checkboxes case !$(this).hasClass('disabled'): clickSort( $(this).attr('id') ); break; // sort dir_list } }); // Sort Menu: click the list header that contains the selected menu text. $('#sort_menu').on('click','li:not(.disabled)',function(e) { e.preventDefault(); e.stopPropagation(); $('#sorting div[id="sort_by_'+ $(this).attr('id') +'"]').click(); // click corresponding sidebar sort item }); // ***** END SORTING ***** // // ***** END BASIC UI FUNCTIONS ***** // // ***** CONTENT PANE ***** // // Focus Sidebar function focusSidebar() { document.activeElement.blur(); $('body').removeClass('faded focus_content').find('#sidebar').focus(); } // Focus content function focusContent(id) { $('body').addClass('focus_content'); switch(true) { case $content_pane.attr('data-content') === 'has_dir': case $content_iframe.hasClass('has_content'): $('#content_iframe').focus(); break; case $content_pane.attr('data-content') === undefined: case $content_pane.attr('data-content') === 'has_video': case $content_pane.attr('data-content') === 'has_pdf': case $('body').hasClass('has_playlist'): $('body').removeClass('focus_content'); // don't focus content in the above cases break; case id !== undefined: document.getElementById(id).focus(); break; case $content_pane.attr('data-content') === 'has_grid': $('#content_grid').focus(); break; case $content_pane.attr('data-content') === 'has_text_editor': $('body').addClass('split_view'); $('#text_source').focus(); break; case $content_pane.attr('data-content') === 'has_image': $('#content_image').find('img').focus(); break; case $content_pane.attr('data-content') === 'has_font': $('#specimen').focus(); break; case $content_pane.attr('data-content') === 'has_text': sendMessage('iframe','tab_iframe'); // enter iframe and focus text_source break; case $content_pane.attr('data-content') === 'has_text': case $content_pane.attr('data-content') === 'has_markdown': sendMessage('iframe','iframe_focus_text'); break; } } function focusButton(id) { let el = document.getElementById(id); el.classList.add('focus'); el.focus(); } // Show Grid function showGrid(id) { if ( id !== undefined ) { makeGrids(id); } // initial make grid items; otherwise, just unhide existing grid (see below) closeContent(); const selectedID = $('#tbody').find('.selected.image, .selected.font').attr('id'); $('#grid_btn').addClass('has_grid'); $('#content_pane').removeClass('has_hidden_grid').attr('data-content','has_grid').find('div[data-id="'+ selectedID +'"]').addClass('selected').siblings().removeClass('selected hovered'); focusContent(); } // Hide Grid function hideGrid() { if ( $content_pane.attr('data-content') === 'has_grid' ) { $content_pane.removeAttr('data-content').addClass('has_hidden_grid'); } } // Show Hidden Grid function showHiddenGrid() { if ( $content_pane.hasClass('has_hidden_grid') ) { showGrid(); } } // Close Grid (remove grid elements, etc.) function closeGrid() { $('#grid_btn').removeClass('has_grid'); if ( $content_pane.attr('data-content') === 'has_grid' ) { $content_pane.removeAttr('data-content'); } $content_pane.removeClass('has_image_grid has_font_grid').find('#content_grid').attr('style','').empty(); } // Show Text Editor function showTextEditor() { switch(true) { case $content_pane.attr('data-content') === 'has_text_editor': // hide open text editor hideTextEditor(); break; case !$body.hasClass('has_text_editor_UI'): // add the text editor UI if loading text editor for first time setUpTextEditorUI(); default: // show editor closeContent(); $content_pane.removeClass('has_hidden_text_editor').attr('data-content','has_text_editor'); // empty title $('#text_editor_row').addClass('has_text_editor'); focusContent(); } } // Hide Text Editor function hideTextEditor() { if ( $content_pane.attr('data-content') === 'has_text_editor' ) { $content_pane.removeAttr('data-content').addClass('has_hidden_text_editor'); } } // Show Hidden Text Editor function showHiddenTextEditor() { if ( $content_pane.hasClass('has_hidden_text_editor') ) { showTextEditor(); } } // showIndexSource(); //***** SHOW CONTENT *****// function showAudio(rowID) { closeMedia('video'); let row = getElById(rowID), link = row[0].children[0].children[0].href, title = row[0].children[0].children[0].innerText; row.addClass('playing selected').siblings('.media').removeClass('playing selected'); $audio_player.attr('src', link ); $content_pane.addClass('has_audio').removeClass('has_iframe_audio'); $('#content_audio_title').find('span').empty().text( title ); $('#content_audio_playlist').removeClass('has_content'); } // Show Font (and create font items) function showFont(row,bool,sheet,link) { // bool = true if for show font grid, sheet defined at fontGridItems(); link = from previewed directory let fontStyles = $font_styles.sheet; const $font_family = (row !== '' ? thisText(row) : link.slice(link.lastIndexOf('/') + 1,link.lastIndexOf('.')) ); const $font_url = (row !== '' ? thisLink(row) : link); if ( bool === false ) { // just show selected font if ( fontStyles.cssRules.length > 0 ) { fontStyles.deleteRule(0); } // delete previous @font-face rule fontStyles.insertRule('@font-face { font-family: "'+ $font_family +'"; src: url("'+ $font_url +'"); }'); $content_font.css({ 'font-family':'"'+ $font_family +'"' }); // set content font style } else { // else make font grid items const $font_grid_item_el = $('
    '); let grid_item = $font_grid_item_el.clone(); const $display_name = $font_family; sheet.insertRule('@font-face { font-family: "'+ $font_family +'"; src: url("'+ $font_url +'"); }'); grid_item.append('

    '+ $display_name.toUpperCase() +'

    '+ $display_name.slice(0,$font_family.lastIndexOf('.')) +'

    ' ); return grid_item; } } // OPEN FONT FILE (req: opentype.js font parsing) $('#open_font_label').on('click',function(e) { e.stopPropagation(); }); // Open font $('#menu').on('change','#open_font',function(e) { $('body').removeClass('has_menu faded'); openFile(e,'font'); }); // Open Font File function openFontFile(files,reader) { closeContent(); hideGrid(); $content_pane.attr('data-content','has_font_file'); $content_font.addClass('has_content'); parseFont(reader.result); $('#title span').html( files.name ); $('#open_font').val(''); // reset input to allow same font to be reopened immediately after closing. focusContent(); } // Parse font function parseFont(fontblob) { let font = window.opentype.parse(fontblob); getFontInfo(font); let $glyphs_container = $('#glyphs_container'); $glyphs_container.empty(); let $glyph_container = $('
    '); let $glyph_canvas = $(''); let $glyph_info = $('
    '); let $glyph_viewer = $('#glyph_viewer'); $glyph_viewer.data('data-font-name',font.names.fullName.en); let glyphs = font.glyphs; $content_font.data('data-glyphs',glyphs); // add glyphs data to $content_font // Draw glyphs for ( let i = 0; i < glyphs.length; i++ ) { let glyph = glyphs.glyphs[i]; // Glyph width let boundingBox = glyph.getBoundingBox(); let glyphWidth = boundingBox.x2 - boundingBox.x1; let contextX = (60 - glyphWidth/24); // Add glyph info and append elements let glyphUnicode = ( glyph.unicode !== undefined ? '#'+ glyph.unicode : glyph.unicode ); let glyphContainer = $glyph_container.clone(); let glyphCanvas = $glyph_canvas.clone(); let glyphInfo = $glyph_info.clone(); glyphInfo.text(glyph.index +': '+ glyph.name +', '+ glyphUnicode); glyphContainer.attr('id','glyph_container_'+ glyph.index ).attr('data-id','glyph_container_'+ glyph.index); glyphContainer.append( glyphCanvas.clone().attr('id','glyph_'+ glyph.index ) ).append(glyphInfo); $glyphs_container.append( glyphContainer ); // Draw glyph let thisGlyph = document.getElementById('glyph_'+ glyph.index); $(thisGlyph).data('contextX',contextX); let context = thisGlyph.getContext('2d'); glyph.draw(context, contextX, 84, 72); } } // Get font info function getFontInfo(font) { let $font_names = font.names; let $font_info = $('
    FONT INFO: '+ font.names.fullName.en.toUpperCase() +'
    '); for ( let name in $font_names ) { let value = $font_names[name].en; if ( name.endsWith('URL') ) { let href = value; if ( !value.startsWith('http') ) { href = 'http://'+ value; } // in case url without protocol is used value = ''+ value +''; } $font_info.find('tbody').append(''+ name +': '+ value +''); } let numGlyphs = font.numGlyphs; // glyph count $font_info.find('tbody').append('numGlyphs: '+ numGlyphs +''); $content_font.find('#font_viewer').prepend($font_info); } // Show glyph viewer $content_font.on('click','.glyph',function(e) { e.stopPropagation(); let id = $(this).attr('id'); $(this).parent('div').addClass('selected').siblings().removeClass('selected'); showGlyph( id ); }); function showGlyph(id) { let $glyph_viewer = $('#glyph_viewer'); let glyphX = $('#'+ id).data('contextX'); id = id.slice(id.lastIndexOf('_') + 1); // convert id to number only let glyphs = $content_font.data('data-glyphs'); // get glyphs let glyph = glyphs.get(id); let glyphName = glyph.name; let glyphPath = glyph.getPath(glyphX,84,72); let glyphSVG = glyphPath.toSVG().replace(/"/g,'\''); // set the background SVG image: let SVGprefix = 'url("data:image/svg+xml;utf8,'; $glyph_viewer.show().css({'background-image': SVGprefix + glyphSVG +'")'}); $glyph_viewer.data('data-raw-svg',glyphSVG).data('data-glyph-name',glyphName).find('#glyph_viewer_info div').text(id +': '+ glyphName +', #'+ glyph.unicode ); // for saving SVG $content_pane.attr('data-content','has_glyph'); } // Save glyph svg function saveGlyph() { let filename = $('#glyph_viewer').data('data-font-name') +'—'+ $('#glyph_viewer').data('data-glyph-name') +'.svg'; let data_prefix = ''; let data_suffix = ''; let data = data_prefix + $('#glyph_viewer').data('data-raw-svg') + data_suffix; saveFile(data,'image/svg+xml',filename); } $content_font.on('click','#save_svg',function(e) { e.stopPropagation(); saveGlyph(); }); // END OPEN FONT // show PDF function showPDF(link) { let $embed = ''; $('#content_pdf').remove(); $content_image.after($embed); // replace existing embed element to allow new pdf content $('#content_pdf').addClass('has_content').attr('src',link); } // Set Content Pane classes function setContentClasses(content_el,kind) { if ( content_el === 'audio' ) { $content_pane.addClass('has_audio'); } else { $content_pane.attr('data-content','has_'+ kind); // remove classes, excluding grid and text classes; add content class } $dir_list.removeClass('has_dir'); } // get query string for various content kinds function getLinkQueries(kind) { let query_str = ''; switch(kind) { case 'code': case 'markdown': case 'text': query_str = '?split_view='+ getQuery('split_view') +'&default_text_view='+ getQuery('default_text_view') +'&theme='+ getQuery('theme') +'&editor_theme='+ getQuery('editor_theme'); if ( !$body.hasClass('disable_text_editing') ) { query_str += '&disable_text_editing=false'; } else { query_str += '&disable_text_editing=true'; } break; case 'app': case 'dir': query_str = '?sort_by=' + getQuery('sort_by') +'&show_numbers='+ getQuery('show_numbers') +'&use_custom_icons='+ getQuery('use_custom_icons') +'&show_invisibles='+ getQuery('show_invisibles') +'&ignore_ignored_files='+ getQuery('ignore_ignored_files') +'&alternate_background='+ getQuery('alternate_background') +'&theme='+ getQuery('theme'); break; case 'view_directory_source': query_str = '?&view_directory_source=true'; break; case 'pdf': query_str = '#view=fitB&scrollbar=1&toolbar=1&navpanes=1'; break; } return query_str; } // create link for various content kinds function setContentSources(row,link,kind,content_el) { let query_str = getLinkQueries(kind); switch(true) { // create source link for content case kind === 'font' && $('body').attr('id') === 'top': showFont(row,false); break; case kind === 'code': case kind === 'markdown': case kind === 'text': link = thisLink(row) + query_str; break; case kind === 'app': case kind === 'dir': $content_pane.toggleClass('has_iframe has_dir'); link = link + query_str; $content_iframe.attr('data-dir_url',link.slice(0,link.lastIndexOf('/?') + 1)).attr('data-query_str',query_str); // is this needed? break; case kind === 'view_directory_source': link = $location + query_str; break; case kind === 'pdf': link = link + query_str; showPDF(link); break; } switch(true) { // add selected, loaded, or playing classes for directory items case kind === 'audio': case kind === 'video': row.addClass('playing').siblings('.media').removeClass('playing'); break; default: row.addClass('loaded').siblings().removeClass('loaded'); } switch(true) { // add content element source and class case kind !== 'image': $('#content_'+ content_el).addClass('has_content').attr('src',link); break; default: $('#content_image').addClass('has_content').find('img').attr('src',link); } } // SELECT ROW on click and set classes for $content_pane function selectThis(rowID) { let row = $(getElById(rowID)); switch(true) { case row.hasClass('disabled'): break; case $content_pane.attr('data-content') === 'has_grid': case $content_pane.hasClass('has_hidden_grid'): {// Select corresponding grid item const $grid_selected = $content_grid.find('> div[data-id="'+ rowID +'"]'); row.addClass('selected').siblings().removeClass('selected hovered'); $grid_selected.addClass('selected').siblings().removeClass('selected'); break; } default: $body.removeClass('has_directory_source'); row.addClass('selected').siblings().removeClass('selected hovered'); // remove classes from sibling, but leave .audio with playing row.siblings('.video').removeClass('playing'); } } // Show Content, hide text editor and grid function showContent(id) { // show any content on row click let row = getElById(id); let kind = row.attr('data-kind'); switch(true) { case row.length === -1 || row.hasClass('disabled'): break; // needed for left/right arrow nav when there are no valid items (i.e. images or fonts) case row.hasClass('audio') && $('body').attr('id') === 'top': // show audio showAudio(id); break; case id === 'text_editor' || id === 'text_editor_row': // show text editor showTextEditor(); break; case id === 'grid_btn' || id === 'show_image_grid' || id === 'show_font_grid': // show grid showGrid(id); break; case ( /code|text|markdown/.test('kind') ): // send stored text selection data to iframe sendMessage('iframe','get_text_selection','',[row.attr('data-selection_start'),row.attr('data-selection_end')]); case row.hasClass('video'): // close audio when showing video closeMedia('audio'); default: {// all other content: font, image, pdf, video, iframe, dir let link = thisLink(row); // load dirs and any other kind of content except audio, font, image, pdf, or video into iframe let content_el = ( row.hasClass('ignore') ? 'ignored' : !['audio','font','image','pdf','video'].includes(kind) || kind === 'dir' || kind === 'info' ? 'iframe' : kind ); closeContent(); // Close all content and hide grid or texteditor setContentClasses(content_el,kind); // Add "has_[kind]" class to content_pane (replace with data-kind="...") setContentSources(row,link,kind,content_el); // Set the src attribute for the content element setContentTitle(kind); // Set the title } } if ( $('body').hasClass('focus_content') ) { focusContent(); } } // Show iframe Dir function showIframeDir(args) { $('body#top').addClass('faded'); $content_pane.attr('data-content','has_dir').removeClass('has_iframe_audio').addClass('has_iframe_dir'); // Prevent attempt to play next audio track after dir change. $content_iframe.addClass('has_dir').attr('data-iframe_dir_url',args).attr('src',args + getLinkQueries('dir')); // store path of current navigated iframe directory and set iframe source $('#title span').empty().html(decodeURIComponentSafe(args)); } // Show Iframe File function showIframeFile(args) { let link = args[0], kind = args[1], title = decodeURIComponentSafe(link); switch(true) { case kind === 'audio': // load audio files closeMedia('video'); $audio_player.attr('src', link ); $content_pane.addClass('has_audio has_iframe_audio'); $('#content_audio_title span').empty().text( title ); playPauseMedia(); break; case ['font','image','pdf','video'].includes(kind): // load non-iframe content types closeContent(); switch(kind) { case 'font': showFont('',false,'',link); $('#content_font').addClass('has_content').css({'font-family':link.slice(link.lastIndexOf('/'),link.lastIndexOf('.')) }); break; case 'image': $('#content_image').addClass('has_content').find('img').attr('src',link); setImageDimensions(); break; case 'pdf': showPDF(link); break; case 'video': closeMedia('audio'); $('#content_video'+ kind).addClass('has_content').attr('src',link); break; } $content_pane.removeClass('has_dir has_iframe_dir').addClass('has_iframe_file').attr('data-content','has_'+ kind); $('#title span').text(decodeURIComponentSafe(link)); break; default: // load iframe content $content_pane.removeClass('has_dir has_iframe_dir').addClass('has_iframe_file').attr('data-content','has_'+ kind); $content_iframe.addClass('has_content').attr('src',link); // set file container link $('#title span').text( title ); focusContent(); break; } focusContent(); } // Show source of current sidebar dir in content pane function showDirectorySource() { if ( $body.hasClass('has_directory_source') ) { $('#close_btn').click(); } else { $body.addClass('has_directory_source'); showContent('view_directory_source'); } } $('#view_directory_source').on('click',function(e) { e.stopPropagation(); showWarning( 'showDirectorySource' ); }); // toggle show directory source // Show current sidebar dir in content pane function openInContentPane() { let link = $location + '?sort_by=' + getQuery('sort_by') +'&show_numbers='+ getQuery('show_numbers') +'&use_custom_icons='+ getQuery('use_custom_icons') +'&show_invisibles='+ getQuery('show_invisibles') +'&ignore_ignored_files='+ getQuery('ignore_ignored_files') +'&alternate_background='+ getQuery('alternate_background'); closeContent(); $content_pane.attr('data-content','has_dir'); $('#title').find('span').html($current_dir_path); $content_iframe.addClass('has_content has_dir').attr('src',link); focusContent(); } $('#open_in_content_pane').on('click',function() { showWarning( 'openInContentPane' ); }); // Open Previewed Dir in Sidebar (@ receiveMessage() ) function openInSidebar(args) { let $query_str = getQueryPrefs(); if ($query_str.has('selected') ) { $query_str.delete('selected'); } if ($query_str.has('history') ) { $query_str.delete('history'); } window.location = args.slice(0,args.lastIndexOf('/?')+1) +'?'+ $query_str.toString(); } //***** CLOSE CONTENT (Close button or Cmd/Ctrl + W) *****// // Close Audio/Video function closeMedia(type) { // type === audio || video let $mediaEl = ( type === 'audio' ? $audio_player : $content_video ); $mediaEl.trigger('pause').removeAttr('src'); switch(true) { case type === 'audio': $('tr.audio.playing').removeClass('playing'); $('#content_audio_title span').empty(); $('#content_audio_playlist').removeClass('has_content'); if ( $content_pane.hasClass('has_iframe_audio') ) { focusContent('content_iframe'); } $content_pane.removeClass('has_audio has_iframe_audio'); sendMessage('iframe','close_iframe_audio'); break; case type === 'video' && $('#content_pane').attr('data-content') === 'has_video': $('tr.video.playing').removeClass('playing'); $('#content_pane').removeAttr('data-content').find('#content_video').removeClass('has_content'); setContentTitle( $('tr.selected:not(.audio)').attr('data-kind') ); break; } } $('#close_audio').on('click',function(e) { e.stopPropagation(); closeMedia('audio'); }); // Close Audio button click // Close Playlist function closePlaylist() { $('#tbody').empty().html($('#tbody').data('dir_list')); // restore original dir_list $body.removeClass('has_playlist has_filelist has_media has_audio has_video has_fonts has_images').addClass( $('#tbody').data('dataClasses').join(' ') ).find('#sort_by_date,li#date').removeClass('disabled'); // restore orignal body "has" classes $('head').find('title').text('Index of: '+ $location); // change window title back to default $('#parents_dir_nav').find('> div').empty().html($current_dir_path); $dir_list.find('#stats').html(getIndexStats($dir_list.find('#tbody tr'))); if ( $('#tbody').find('.media').length < 1 ) { $body.removeClass('has_media has_audio has_video'); } } // Close index source preview function closeIndexSource() { $body.removeClass('has_directory_source'); $('.dir.loaded').click(); } // Close opened font file function closeFontFile() { $body.removeClass('faded focus_content'); $content_pane.removeAttr('data-content').find('#glyphs_container').empty(); $('#font_viewer').remove().end().append(ContentFontViewer()); $('tr.loaded:not(.audio)').click(); } // Close glyph function closeGlyph() { $('#glyph_viewer').hide().attr('style',''); $content_pane.attr('data-content','has_font_file'); $content_font.addClass('has_content'); scrollThis('glyphs_container','selected'); } // Close iframe File function closeIframeFile() { let iframe_dir_url = decodeURIComponentSafe($('#content_iframe').attr('data-iframe_dir_url')); // for iframe navigation if ( iframe_dir_url === undefined ) { iframe_dir_url = decodeURIComponentSafe($('#content_iframe').attr('data-dir_url')); } // for current sidebar dir let query_str ='?sort_by=' + getQuery('sort_by') +'&show_numbers='+ getQuery('show_numbers') +'&use_custom_icons='+ getQuery('use_custom_icons') +'&show_invisibles='+ getQuery('show_invisibles') +'&ignore_ignored_files='+ getQuery('ignore_ignored_files') +'&theme='+ getQuery('theme') +'&alternate_background='+ getQuery('alternate_background'); $content_pane.removeClass('has_iframe_file '+ $content_pane.attr('data-content')).addClass('has_dir has_iframe_dir').attr('data-content','has_dir').find('.content.has_content').removeClass('has_content'); $('#content_iframe').attr('src',iframe_dir_url + query_str).addClass('has_content'); // load source directory from data $('#title span').html(iframe_dir_url); focusContent('content_iframe'); } // Close all .content elements before opening any new .content from sidebar; leave grid and text editor hidden. function closeContent() { switch(true) { case window.top !== window.self: break; default: // remove sources, data-content, styles, and classes from #content_pane $('#title span').removeAttr('data-after').empty(); // empty content title hideGrid(); hideTextEditor(); $content_pane.removeClass('has_zoom_image has_scaled_image').removeAttr('data-content').find('.content.has_content, #content_image img').removeAttr('data-content style src').removeClass('has_content'); $content_font.find('#font_viewer').remove().end().append(ContentFontViewer()); closeMedia('video'); $body.removeClass('faded focus_content'); $('.playlist_entry_container').removeClass('has_content').find('textarea').val(''); } } //////// NEW CLOSE BUTTON FUNCTION: replaces OLDcloseContent() function closeButton() { switch(true) { case $content_pane.attr('data-content') === 'has_grid': // close grid closeGrid(); if ( $content_pane.hasClass('has_hidden_text_editor') ) { showHiddenTextEditor(); } else { focusSidebar(); }// else { showContent( $('tr:not(.audio).selected').attr('id') ); } break; case $content_pane.attr('data-content') === 'has_text_editor': // hide text editor hideTextEditor(); if ( $content_pane.hasClass('has_hidden_grid') ) { showHiddenGrid(); } else { focusSidebar(); }// else { showContent( $('tr:not(.audio).selected').attr('id') ); } break; case $body.hasClass('iframe_edited'): // close edited iframe file showWarning('closeContent'); break; case $content_pane.hasClass('has_iframe_file'): // close iframe file and reopen parent directory or selected sidebar directory closeIframeFile(); break; case $content_pane.hasClass('has_iframe_dir'): // close iframe dir and reopen selected sidebar directory $content_pane.removeAttr('data-content').removeClass('has_iframe_dir'); $('#content_iframe').removeAttr('data-iframe_dir_url').removeAttr('data-iframe_query_str'); $dir_list.find('.dir.selected a').click(); focusContent('content_iframe'); break; case $body.hasClass('has_directory_source'): // close directory source closeIndexSource(); break; case $content_pane.attr('data-content') === 'has_glyph': // close glyph showWarning( 'closeGlyph' ); break; case $content_pane.attr('data-content') === 'has_font_file': // close font file preview showWarning( 'closeFontFile' ); break; case $content_pane.hasClass('has_ignored'): // close ignored content case $('.content').hasClass('has_content'): // close all other content closeContent(); // false = show hidden text editor or grid after closing other content focusSidebar(); $('.loaded').removeClass('loaded'); // don't remove selected class; allows navigation to continue from last selection switch(true) { case $content_pane.hasClass('has_hidden_grid'): showGrid(); break; case $content_pane.hasClass('has_hidden_text_editor'): showTextEditor(); break; } break; case $content_pane.hasClass('has_audio'): // close audio closeMedia('audio'); focusSidebar(); break; case $body.hasClass('has_playlist'): // close playlist case $body.hasClass('has_filelist'): // close filelist showWarning( 'closePlaylist' ); break; } // show hidden texteditor or grid } $('#close_btn').on('click', function(e) { e.stopPropagation(); e.preventDefault(); showWarning('closeButton'); $(this).blur(); }); //***** RESET CONTENT (Reset button or Cmd/Ctrl + R) *****// function resetContent() { $audio_player.prop('currentTime', 0).trigger('pause'); $('#content_audio_playlist').removeClass('has_content'); switch(true) { case $content_pane.attr('data-content') === 'has_grid': $('#grid_btn').click(); break; case $content_pane.attr('data-content') === 'has_glyph': $('#glyph_viewer').css({'width':'100%','height':'100%','background-size':'100%'}).attr('data-scale',1); break; case $content_pane.attr('data-content') === 'has_font': $content_font.css({'font-size':'1em'}); break; case $content_pane.attr('data-content') === 'has_image': $content_pane.removeClass('has_zoom_image has_scaled_image').find('#content_image').removeAttr('style').find('img').removeAttr('style'); break; case $content_pane.attr('data-content') === 'has_text_editor': break; case $content_pane.attr('data-content') === 'has_video': $content_video.prop('currentTime',0).trigger('pause'); break; case $content_pane.hasClass('has_iframe') || $content_pane.hasClass('has_dir'): case $content_pane.attr('data-content') === 'has_iframe' || $content_pane.attr('data-content') === 'has_dir': $('#tbody').find('.selected').find('a').click(); break; default: window.location = window.location.href; } } $('#reload_btn').on('click', function(e) { e.stopPropagation(); e.preventDefault(); showWarning('resetContent'); $(this).blur().removeClass('reset'); }); //**********************// //***** NAVIGATION *****// function firstRowID(className) { return ( $('#tbody').find('> tr:visible').filter(className).length ? $('#tbody').find('> tr:visible').filter(className).first().attr('id') : null ); } function prevRowID(className) { let $selected, rowID; if ( /audio|video|media/.test(className) ) { $selected = $('#tbody').find('> tr.playing'); } else { $selected = $('#tbody').find('> tr.selected'); } if ( !$selected.length || !$selected.prevAll('tr:visible:not(.disabled)').filter(className).length ) { rowID = $('#tbody').find('> tr:visible:not(.disabled)').filter(className).last().attr('id'); // select last item if nothing selected or selected is first item } else { rowID = $selected.prevAll('tr:visible:not(.disabled)').filter(className).first().attr('id'); } return rowID; } function nextRowID(className) { // if nothing selected, or if no next row with classname, return first row with classname, else return next row with classname let row; if ( /audio|video|media/.test(className) ) { row = $('tr.playing'); } else { row = $('#tbody').find('.selected'); } return ( !row.length || !row.nextAll('tr:visible').not('.disabled').filter(className).length ) ? $('#tbody').find('> tr:visible').not('.disabled').filter(className).first().attr('id') : row.nextAll('tr:visible').not('.disabled').filter(className).first().attr('id'); } // get selected row id function selectRowID(className,key) { let id = ''; switch(key) { case 'ArrowUp': id = prevRowID('.dir,.file'); break; case 'ArrowLeft': id = prevRowID(className); break; case 'ArrowRight': id = nextRowID(className); break; case 'ArrowDown': id = nextRowID('.dir,.file'); break; } return id; } // NAVIGATION: Menu Arrow Navigation function menuNavigation(key) { switch(key) { case 'ArrowUp': switch(true) { case $('#menu').find('.hovered').length > 0: // if submenu visible if ( !$('#menu').find('ul li.selected').length ) { // if no menu item selected, select last submenu item $('#menu').find('ul li:last-of-type').addClass('selected'); } else { // else select previous submenu item $('#menu').find('ul li.selected').removeClass('selected').prev('li').addClass('selected'); } break; case !$('#menu').find('.selected').length: $('#menu').find('> li:last-of-type').addClass('selected'); break; // select last item default: $('#menu').find('.selected:not(.hovered)').removeClass('selected').prev('li').addClass('selected'); } break; case 'ArrowDown': switch(true) { case $('#menu').find('.hovered').length > 0: // if submenu visible if ( !$('#menu').find('ul li.selected').length ) { // if no menu item selected, select first submenu item $('#menu').find('ul li:first-of-type').addClass('selected'); } else { // else select next submenu item $('#menu').find('ul li.selected').removeClass('selected').next('li').addClass('selected'); } break; case !$('#menu').find('.selected').length: $('#menu').find('> li:first-of-type').addClass('selected'); break; default: $('#menu').find('.selected:not(.hovered)').removeClass('selected').next('li').addClass('selected'); } break; case 'ArrowLeft': switch(true) { case !$('#menu').find('.selected').length: $('#menu').find('> li:last-of-type').addClass('selected'); break; case $('#menu').find('li.selected').hasClass('hovered'): $('#menu').find('.hovered').removeClass('hovered'); $('#menu').find('ul li').removeClass('selected'); break; } break; case 'ArrowRight': switch(true) { case !$('#menu').find('.selected').length: $('#menu').find('> li:first-of-type').addClass('selected'); break; case $('#menu').find('li.selected').hasClass('has_submenu') && !$('.submenu').find('.selected').length: $('#menu').find('.selected').addClass('hovered').find('ul li:first-of-type').addClass('selected'); break; } break; } } // NAVIGATION: select GRID items by left/right arrow keys @ arrowNavigation(); function gridNavigation(elId,key) { const $container_el = $(elId); let $selected = $container_el.find('> .selected'); let grid_item_id; // = grid item data-id = corresponding dir_list item id. let rowLength = 0; // $content_grid.hasClass('has_grid') || $('#content_font').hasClass('has_font_grid') rowLength = ( Math.round( $container_el.innerWidth() / $container_el.find('> div').outerWidth()) - 1 ); switch(true) { case $selected.length === 0: // nothing selected switch(true) { case key === 'ArrowRight': case key === 'ArrowDown': $container_el.find('> div').first().addClass('selected'); break; case key === 'ArrowLeft': case key === 'ArrowUp': $container_el.find('> div').last().addClass('selected'); break; } grid_item_id = $container_el.find('.selected').attr('data-id'); break; case key === 'ArrowDown': switch(true) { case $selected.nextAll().eq(rowLength).length === 1: grid_item_id = $selected.nextAll().eq(rowLength).attr('data-id'); break; case $container_el.find('> div').length - 1 === rowLength: gridNavigation(elId,'ArrowRight'); break; // for single grid row, select next item default: grid_item_id = $container_el.find('> div').first().attr('data-id'); break; } break; case key === 'ArrowUp': switch(true) { case $selected.prevAll().eq(rowLength).length === 1: grid_item_id = $selected.prevAll().eq(rowLength).attr('data-id'); break; case $container_el.find('> div').length - 1 === rowLength: gridNavigation(elId,'ArrowLeft'); break; // for single grid row, select prev item default: grid_item_id = $container_el.find('> div').last().attr('data-id'); break; } break; case key === 'ArrowLeft': switch(true) { case $selected.prev().length === 1: grid_item_id = $selected.prev().attr('data-id'); break; default: grid_item_id = $container_el.find('> div').last().attr('data-id'); break; } break; case key === 'ArrowRight': switch(true) { case $selected.next().length === 1: grid_item_id = $selected.next().attr('data-id'); break; default: grid_item_id = $container_el.find('> div').first().attr('data-id'); break; } break; } selectThis(grid_item_id); switch(true) { case $content_pane.attr('data-content') === 'has_glyph': $('.glyph_container.selected .glyph').click(); break; // show the selected glyph case $content_pane.hasClass('has_hidden_grid'): $('#content_grid > div[data-id="'+ grid_item_id +'"]').click(); break; } } // NAVIGATION: FONTS and IMAGES by prev/next buttons $content_pane.on( 'click','#prev_btn, #next_btn', function(e) { e.stopPropagation(); e.preventDefault(); let key = $(this).attr('id') === 'prev_btn' ? 'ArrowLeft' : 'ArrowRight'; if ( $content_pane.attr('data-content') === 'has_font_viewer' ) { gridNavigation('#glyphs_container',key); } else { clickRow( selectRowID('.font,.image',key)); } if ( $('body').hasClass('focus_content') ) { focusContent(); } }); // NAVIGATION: MEDIA tracks by arrow left/right @ leftRightArrowNavigation() & button click; // handle shuffle or loop play, as well as normal continuous playback after end of track with ArrowRight function playPrevNextTrack(key) { // context.resume(); switch(true) { case $('#audio').attr('src') === 'unknown' && $('tr.selected').hasClass('audio'): showAudio( $('tr.selected').attr('id') ); break; case $content_pane.hasClass('has_iframe_audio'): // play track from navigated directory sendMessage('iframe','play_prev_next_iframe_audio','',key); break; case $('.playing').length === 0: // Arrow L/R selects first/last audio file if nothing selected clickRow( selectRowID('.media:not(.unchecked)',key) ); playMedia('play'); break; case $('.playing').length === 1: { let mediaClass = ( $('.playing').hasClass('audio') ? '.audio:not(.unchecked)' : '.video:not(.unchecked)' ); switch(true) { case $body.hasClass('shuffle_audio'): { // If shuffle play... let trackRowID = $audio_player.data('shufflelist').pop(); // remove track from list if ( trackRowID !== undefined ) { // if shuffle list is not empty... showAudio( trackRowID ); playMedia('play'); // load media and play } else if ( trackRowID === undefined ) { // if end of shufflelist... if ( $body.hasClass('loop_audio') ) { // and if loop audio, update the shufflelist and play updateShuffleList(); playMedia('play'); } else { // else just load the first track showAudio( firstRowID( mediaClass )); } } break; } case $(mediaClass).filter('.selected').length === 1 && !$('.media.selected').hasClass('playing'): // else if there is another media file selected, play it next: showAudio( $('.media.selected').attr('id') ); playMedia('play'); break; default: switch(true) { case selectRowID( mediaClass,key ) !== firstRowID( mediaClass ): // show and play next track if next track !== first track case $body.hasClass('loop_audio'): // if loop audio, always show and play showAudio( selectRowID( mediaClass,key ) ); playMedia('play'); break; case selectRowID( mediaClass,key ) === firstRowID( mediaClass ): // if next track is first track, just show it showAudio( selectRowID( mediaClass,key ) ); break; } break; } } } } // NAVIGATION: Audio by prev/next audio buttons $('.prev_next_track_btn').on('click',function() { let key = ( $(this).attr('id') === 'prev_track' ? 'ArrowLeft' : 'ArrowRight' ); playPrevNextTrack(key); }); function upDownArrowNavigation(e) { e.preventDefault(); let rowID = selectRowID('.dir:visible,.file:visible',e.key); let row = $(document.getElementById(rowID)); let $selected = $('#tbody').find('.selected'); switch(true) { case window.self !== window.top && cmdKey(e): // iframe && cmdKey switch(true) { case $('#iframe_body #dir_list').length === 0: break; case e.key === 'ArrowDown': // open selected iframe item $selected.find('a').trigger('dblclick'); break; case e.key === 'ArrowUp': // open selected iframe item $('#parent a').trigger('click'); break; } break; case cmdKey(e) && e.key === 'ArrowDown' && $('#tbody').find('.selected').hasClass('dir'): // open selected directory with cmdKey if ( $selected.hasClass('app') && $settings.apps_as_dirs === false ) { break; } else { $selected.find('a').trigger('dblclick'); break; } case cmdKey(e) && e.key === 'ArrowUp': // Cmd/Ctrl + up arrow = go to parent directory showWarning( 'clickThis', $('#parent_dir_nav').attr('id') ); break; case $body.hasClass('has_directory_source'): // if viewing directory source if ( $('#tbody .loaded').length > 0 ) { clickRow( $('#tbody .loaded').attr('id') ); } else { clickRow( rowID ); } scrollThis('tbody','selected'); break; default: // click prev/next item selectThis(rowID); // don't use $selected after this switch(true) { // don't show content for media; only select row case row.hasClass('audio'): case row.hasClass('video') && $('.playing').hasClass('video'): break; default: showContent(rowID); } if ( $('.selected').length ) { scrollThis('tbody','selected'); } } } // NAVIGATION: Prev/Next Audio or Prev/Next File of same data-kind by arrow left/right function leftRightArrowNavigation(className,e) { if ( cmdAltKey(e) && e.key === 'ArrowLeft' || cmdAltKey(e) && e.key === 'ArrowRight' ) { // don't start audio tracks when changing tabs return; } else { e.preventDefault(); let $selected = $('#tbody').find('.selected'); switch(true) { case e.altKey && e.shiftKey: case e.altKey && !e.metaKey && !e.ctrlKey: { // Skip audio 30s/10s: Opt-Shift-Arrow/Opt-Arrow let args = [e.key]; if ( e.shiftKey ) { args.push(30); } else { args.push(10); } switch(true) { case window.top !== window.self: sendMessage('top','mediaSkip','mediaSkip',args); break; default: mediaSkip(e); } break; } case $('#content_pane').hasClass('has_audio') && window.top === window.self: playPrevNextTrack(e.key); break; // play prev/next audio track case $body.hasClass('has_directory_source'): // if viewing directory source if ( $('#tbody .loaded').length > 0 ) { clickRow( $('#tbody .loaded').attr('id') ); } else { clickRow( selectRowID('.dir,.file',e.key) ); } scrollThis('tbody','selected'); break; default: switch(true) { case $selected.length === 0: clickRow( selectRowID('.dir,.file',e.key) ); break; // click first/last row case $selected.hasClass('audio'): clickRow( selectRowID('.audio',e.key) ); break; // click audio case !$selected.hasClass('audio'): clickRow( selectRowID('.'+ $selected.attr('data-kind'),e.key) ); break; } if (className === 'video') { playMedia('play'); } scrollThis('tbody','selected'); } } } // NAVIGATE directory index items by arrow up/down or left/right keys, with/without grid @ indexNavigation() function arrowNavigation(e) { switch(true) { case window.top !== window.self && document.activeElement.getAttribute('id') === 'iframe_body' && $('#iframe_body').find('#dir_list').length === 0: // allow arrows to work normally in iframes (but not iframe dirs) case ( /img|textarea|embed/.test( document.activeElement.tagName.toLowerCase() ) ): // allow arrows to work normally in images and textareas case document.activeElement.hasAttribute('contentEditable'): // allow arrows to work normally in contentEditable elements break; default: e.preventDefault(); // otherwise prevent default arrow behavior $('.button').blur(); // Need this to able to select grid items after clicking close button (or other buttons). case $('body').hasClass('has_menu'): // menu navigation if ( window.top !== window.self ) { sendMessage('top','menu_navigation','menuNavigation',e.key); } else { menuNavigation(e.key); } break; case $content_pane.attr('data-content') === 'has_grid' && $('body').hasClass('focus_content'): // navigate grid case $content_pane.attr('data-content') === 'has_image' && $content_pane.hasClass('has_hidden_grid'): // navigate hidden grid case $content_pane.attr('data-content') === 'has_font' && $content_pane.hasClass('has_hidden_grid'): // navigate hidden grid gridNavigation('#content_grid',e.key); if ( !$content_pane.hasClass('has_hidden_grid') ) { scrollThis('content_grid','selected'); } scrollThis('tbody','selected'); break; case $content_pane.attr('data-content') === 'has_font_file': // navigate font file glyphs case $content_pane.attr('data-content') === 'has_glyph': // navigate font file glyphs if ( $('body').hasClass('focus_content') ) { gridNavigation('#glyphs_container',e.key); scrollThis('content_container','selected'); } else { showWarning('warning_close_font','warning_btn_cancel'); // show close font warning if sidebar focused } break; case e.key === 'ArrowUp' || e.key === 'ArrowDown': // up/down arrow navigation upDownArrowNavigation(e); break; case e.key === 'ArrowLeft' || e.key === 'ArrowRight': { // left/right arrow navigation let className = ( $('.selected[data-kind]') === undefined ? $('#dir_list').find('tbody tr:visible').first().attr('data-kind') : $('.selected[data-kind]').attr('data-kind') ); leftRightArrowNavigation(className,e); break; } } } // NAVIGATION: by typed string var str = ''; function timeoutID() { return window.setTimeout( function() { str = ''; }, 1500 ); } function alphaNav(e) { // Select Dir_List row by typed string; Todo: select next row by nearest letter if ( document.activeElement.tagName.toLowerCase() === 'textarea' || document.activeElement.getAttribute('contentEditable') === true ) { return; } else { let timer = timeoutID(); if ( typeof timer === 'number' ) { window.clearTimeout( timer ); timer = 0; // id } timeoutID(); str += e.key; str = str.toLowerCase(); if ( $('#dir_list').find('td.name[data-name^="'+ str +'"]').length ) { $('#dir_list').find('td.name[data-name^="'+ str +'"]').first().find('a').click(); scrollThis('tbody','selected'); // } else { // null; // replace this with some sort of fuzzy match function? TBD } } } //***** END NAVIGATION *****// // CLICK element by id function clickThis(id) { let el = $(document.getElementById(id)); if ( el.find('a').length > 0 ) { el.find('a').click(); } else { el.click(); } } // CLICK Row to show content function clickRow(id) { selectThis(id); showContent(id); } // On click row, play/pause media or clickRow $('#top #tbody').on('click','tr', function(e) { e.preventDefault(); e.stopPropagation(); focusSidebar(); if ( $(this).hasClass('playing') ) { playPauseMedia(); } else { showWarning( 'clickRow', $(this).attr('id') ); } }); // DOUBLE-CLICK Row to open directory function doubleClickRow(args) { // row.dir only let id = args[0].slice(args[0].indexOf('-') + 1), href = args[1]; let $query_str = decodeURIComponentSafe(window.location.search); if ( $query_str === '' ) { $query_str = '?'; } if ( $query_str.indexOf('history') !== -1 ) { $query_str = $query_str.replace(/&selected=\d+/,'').replace(/&history=/,'&history='+ id +'+'); } else { $query_str = $query_str.replace(/&selected=\d+/,'') + '&history='+ id; } window.location = href + $query_str; } $('#top').on('dblclick', '#tbody tr.dir', function(e) { e.preventDefault(); e.stopPropagation(); showWarning( 'doubleClickRow', [$(this).attr('id'), $(this).find('a').attr('href')] ); }); // click content header or audio to remove faded body class $('#sidebar_title,#sidebar_buttons,#sidebar,#sorting .sorting').on('click',function(e) { focusSidebar(); }); // CLICK grid item $content_grid.on('click','div', function(e) { e.preventDefault(); let id = $(this).attr('data-id'); $(this).addClass('selected').siblings().removeClass('selected'); hideGrid(); getElById(id).click(); }); // HOVER Grid Item $content_grid.on('mouseenter','> div:not(.selected)',function() { thisRow($dir_list.find('a[href*="'+ thisLink(this) +'"]')).addClass('hovered'); }).on('mouseleave','> div:not(.selected)',function() { thisRow($dir_list.find('a[href*="'+ thisLink(this) +'"]')).removeClass('hovered'); }); // HOVER Dir_list_row: highlight corresponding grid item $dir_list_body.find('> tr').hover(function() { if ( $content_grid.is(':visible') ) { $content_grid.find('[href*="'+ thisLink(this) +'"]').closest('div').addClass('hovered'); } }, function() { if ( $content_grid.is(':visible') ) { $content_grid.find('.hovered').removeClass('hovered'); } }); // HOVER Footer Links: Fade dir_list $('#footer_links').on('mouseenter',function() { $('body:not(.has_menu), body:not(.has_menu_parents)').addClass('faded'); }).on('mouseleave',function() { $('body:not(.has_menu), body:not(.has_menu_parents)').removeClass('faded'); }); // HOVER Stats Items: highlight dir_list items $('#stats').on('mouseenter','#stats_details_container > div, #stats_summary_detailed_dirs, #stats_summary_detailed_files', function() { let thisClass = $(this).attr('class'); switch(true) { case $(this).attr('id') === 'stats_summary_detailed_dirs': thisClass = '.dir'; break; case $(this).attr('id') === 'stats_summary_detailed_files': thisClass = '.file'; break; case /_/.test(thisClass): thisClass = '.'+ thisClass.replace('_','.'); break; default: thisClass = '.'+ thisClass +':not(.invisible):not(.ignore)'; } $('#tbody').find(thisClass).addClass('hovered'); }).on('mouseleave','div',function() { $('#tbody tr').removeClass('hovered').removeAttr('style'); }); // END CLICK & HOVER EVENTS //***** AUTOLOAD CONTENT: index files or files from the file shortcut list *****// function autoLoadFile() { let selectedID = ( getQuery('selected').length > 0 ? 'rowid-'+ getQuery('selected') : undefined); switch(true) { case window.self !== window.top: // do nothing for iframes break; case $body.hasClass('has_audio') && getQuery('autoload_media') === 'true': // load audio clickRow( $('.audio').first().attr('id') ); // load audio if ( $('body').hasClass('has_images') ) { autoLoadCoverArt(); } else if ( selectedID !== undefined ) { clickRow( selectedID ); } // autoload cover art or select dir break; case $body.hasClass('has_video') && getQuery('autoload_media') === 'true': // load video (if no audio) clickRow( firstRowID('.video') ); break; case getQuery('file') !== undefined && getQuery('file').length > 0: // load files (from bookmark or url) clickRow( $dir_list.find('a[href*="'+ getQuery('file') +'"]').closest('tr').attr('id') ); removeQuery('file'); break; case getQuery('autoload_index_files') === 'true' && $dir_list.find( 'a[href*="/index."]').length > 0: // else load index file clickRow($('a[href*="/index."]').closest('tr').attr('id')); break; default: if ( selectedID !== undefined) { showContent( selectedID ); } } if ( $('#tbody').find('.selected').length > 0 ) { scrollThis('tbody','selected'); } } // Autoload Cover Art function getImageNames() { let image_names = []; $dir_list_body.find('.image').each(function() { let name = $(this).find('td.name').attr('data-name'); name = name.slice(0,name.lastIndexOf('.')); image_names.push( name ); }); return image_names; } function getCoverArtID() { const cover_names = ['cover','front']; const image_names = getImageNames(); const $image_files = $dir_list_body.find('tr.image'); for ( let cover_name of cover_names ) { switch(true) { case image_names.includes(cover_name): // file name === a cover_name return $image_files.eq( image_names.indexOf(cover_name) ).attr('id'); case image_names.some( name => name.startsWith(cover_name)): // file name starts with a cover name return $image_files.find('td.name[data-name^="'+ cover_name +'"]').closest('tr').attr('id'); case image_names.some( name => name.indexOf(cover_name) > 0 ): // file name includes a cover name return $image_files.find('td.name[data-name*="'+ cover_name +'"]').closest('tr').attr('id'); default: // else use first image return $image_files.first().attr('id'); } } } function autoLoadCoverArt() { // autoload cover art for audio if dir contains audio and images, and audio is loaded and non-image content is not loaded let $coverID = getCoverArtID(); if ( $coverID !== undefined ) { let row = getElById($coverID); row.addClass('loaded'); $content_pane.attr('data-content','has_image').find('#content_image').addClass('has_content').find('img').attr('src',row.find('a').attr('href')); setImageDimensions(); $('#title').find('span').html(row.find('.name').text()); } } //***** KEYBOARD EVENTS *****// function metaKey(e) { return ( navigator.platform.match('Mac') ? e.metaKey && !e.ctrlKey : e.ctrlKey); } function cmdKey(e) { return ( metaKey(e) && !e.altKey && !e.shiftKey ); } function cmdAltKey(e) { return ( metaKey(e) && e.altKey && !e.shiftKey ); } // function cmdAltShiftKey(e) { return ( metaKey(e) && e.altKey && e.shiftKey ); } function cmdShiftKey(e) { return ( metaKey(e) && !e.altKey && e.shiftKey ); } $('#top, #iframe_body').on('keydown', function(e) { const $selected = ( $('#content_pane').attr('data-content') === 'has_font_file' ? $('.glyph_container.selected .glyph') : $('#tbody').find('.selected') ); // Disable all keydown events except return and tab when help or warning is shown, or when textarea is focused if ( ($('body').hasClass('has_warning') || $('body').hasClass('has_help') ) && !( cmdKey(e) || metaKey(e) || e.key === 'shiftKey' || e.key === 'Tab' || e.key === 'Enter' || e.key === 'Escape') ) { return; } switch ( e.key ) { case 'shiftKey': switch(true) { case $('body').hasClass('has_warning') || $('body').hasClass('has_help'): if (e.key !== 'Enter' && e.key !== 'Tab') { e.preventDefault(); return false; } break; } break; case 'ArrowUp': case 'ArrowDown': case 'ArrowLeft': case 'ArrowRight': showWarning( 'arrowNavigation',e ); // arrow navigation break; case ' ': // space if ( $('#content_pane').hasClass('has_audio') || $('#content_pane').attr('data-content') === 'has_video' ) { // Play/pause media (space bar) e.preventDefault(); e.stopPropagation(); playPauseMedia(); } else if ( window.top !== window.self && $('tr.audio.playing').length === 1 ) { e.preventDefault(); e.stopPropagation(); sendMessage('top','playPauseMedia'); } else { alphaNav(e); } break; case 'Enter': // Open directories (or ignore) switch(true) { case $('body').hasClass('has_warning') || $('body').hasClass('has_help'): e.preventDefault(); $('button.focus, button:focus').click(); // click focused warning button in #top or #iframe break; case window.self !== window.top: // if iframe... switch(true) { case $('body').hasClass('has_menu'): sendMessage('top','clickMenu'); // close main menu break; case $('tr.selected').hasClass('dir') && cmdKey(e): case $('tr.selected').hasClass('file') && cmdKey(e): // open iframe dir or file $('tr.selected').find('a').trigger('dblclick'); break; case !$('.audio.selected').hasClass('playing'): $('.audio.selected').find('a').trigger('dblclick'); break; case $('.audio.playing').length === 1 && !$('.selected').hasClass('playing'): // play/pause media e.preventDefault(); e.stopPropagation(); playPauseMedia(); break; } break; case $('body').hasClass('has_menu'): e.preventDefault(); clickMenu(); sendMessage('iframe','close_menu'); // click selected menu item break; case $selected.hasClass('app') && $settings.apps_as_dirs === false: // don't open app folders break; default: switch(true) { case $selected.hasClass('.disabled'): case $content_pane.attr('data-content') === 'has_text_editor': break; case $selected.hasClass('media') && !$selected.hasClass('playing'): // load selected media file $('.media.selected a').click(); break; case $selected.hasClass('media'): // else play/pause playing media e.preventDefault(); e.stopPropagation(); playPauseMedia(); break; case $selected.hasClass('dir'): // else open dir $selected.find('a').trigger('dblclick'); break; default: $selected.click(); } } break; // Alphabetical navigation case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'f': case 'h': case 'j': case 'k': case 'l': case 'm': case 'n': case 'p': case 'q': case 's': case 't': case 'u': case 'v': case 'x': case 'y': case 'z': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '~': case '!': case '@': case '#': case '$': case '%': case '^': case '&': case '*': case '(': case ')': case '`': case '+': case '[': case ']': case '{': case '}': /*case '\\':*/ case '|': case '/': case ',': case '?': case '–': case '—': case '\'': case '"': case '“': case '”': case '‘': case '’': case '…': case 'π': case '∫': case '∂': case 'ƒ': case '©': case '˙': case '∆': case '˚': case 'µ': case '®': case 'ß': case '†': case '√': case '∑': case '≈': case '¥': case 'Ω': case '¡': case '™': case '£': case '¢': case '∞': case '§': case '¶': case '•': case 'ª': case 'º': case '≠': case '⁄': case '€': case '‹': case '›': case 'fl': case '‡': case '°': case '·': case '‚': case '±': case '¯': case '˘': case '¿': case 'ı': case '': case '´': case '˝': case 'ˆ': case '': case '˜': case '‰': case 'ˇ': case '¨': case '◊': case '„': case '˛': case '¸': // case 'α': case 'β': case 'γ': case 'δ': case 'ε': case 'ζ': case 'η': case 'θ': case 'ι': case 'κ': case 'λ': case 'μ': case 'ν': case 'ξ': case 'ο': case 'π': case 'ρ': case 'ς': case 'σ': case 'τ': case 'υ': case 'φ': case 'χ': case 'ψ': case 'ω': case 'ϑ': case 'ϒ': case 'ϖ': // case 'Α': case 'Β': case 'Γ': case 'Δ': case 'Ε': case 'Ζ': case 'Η': case 'Θ': case 'Ι': case 'Κ': case 'Λ': case 'Μ': case 'Ν': case 'Ξ': case 'Ο': case 'Π': case 'Ρ': case 'Σ': case 'Σ': case 'Τ': case 'Υ': case 'Φ': case 'Χ': case 'Ψ': case 'Ω': case 'Θ': case 'ϒ': if ( !e.metaKey && !e.ctrlKey && !e.altKey ) { alphaNav(e); } break; case 'd': // Cmd/Ctrl + D: Toggle Details if ( cmdKey(e) && !$body.hasClass('has_warning') ) { e.preventDefault(); $('#show_details').click(); } else { alphaNav(e); } break; case 'e': // Cmd/Ctrl + E: Toggle Main Menu or Text Editor switch(true) { case $body.hasClass('has_warning'): break; case cmdShiftKey(e): // toggle text editor e.preventDefault(); if ( window.self !== window.top ) { sendMessage('top','toggle_text_editor'); } else { $('#text_editor_row').find('a').click(); } $('body').addClass('faded'); break; case cmdKey(e): // toggle main menu e.preventDefault(); if ( window.self !== window.top ) { sendMessage('top','toggle_menu'); } else { $('#menu_container').click(); } break; default: alphaNav(e); } break; case 'g': // Cmd/Ctrl + G: Show image Grid if ( cmdKey(e) && arr1InArr2(['has_images','has_fonts'],getClassList('top')) ) { e.preventDefault(); $('#grid_btn').click(); } else { alphaNav(e); } break; case 'i': // Cmd/Ctrl + I: Toggle Invisibles if ( cmdKey(e) ) { e.preventDefault(); $('#show_invisibles_container').find('input').click(); } else { alphaNav(e); } break; case 'o': // Cmd/Ctrl + Shift + O: Open selected item in new window if ( cmdShiftKey(e) ) { window.open( thisLink($('#tbody').find('.selected')) ); } else { alphaNav(e); } break; case 'r': // Cmd/Ctrl + Shift + R: Refresh switch(true) { case cmdKey(e) && window.top !== window.self: e.preventDefault(); sendMessage('top','reload'); // send reload message to top break; case cmdKey(e): e.preventDefault(); $('#reload_btn').click(); // click reload/reset button break; default: alphaNav(e); } break; case 'w': // Cmd/Crtl + W: if content pane has content, click close button; otherwise default behavior (close tab/window); Firefox does not allow scripts to override this behavior switch(true) { case cmdKey(e): switch(true) { case window.top !== window.self: e.preventDefault(); sendMessage('top','close'); // send close message to top break; case $content_pane.hasClass('has_audio'): case $content_pane.attr('data-content') !== undefined: e.preventDefault(); $('#close_btn').click(); // click close button break; default: return true; // else close window (or normal behavior) } break; default: alphaNav(e); } break; case '=': // equals sign switch(true) { case cmdKey(e) && /has_grid|has_image|has_font|has_glyph|has_font_file/.test($content_pane.attr('data-content')): e.preventDefault(); $('#increase').click(); break; case cmdKey: // allow normal Cmd + behavior return; default: alphaNav(e); break; } break; case '-': // hyphen switch(true) { case cmdKey(e) && /has_grid|has_image|has_font|has_glyph|has_font_file/.test($content_pane.attr('data-content')): e.preventDefault(); $('#decrease').click(); break; case cmdKey: // allow normal Cmd - behavior return; default: alphaNav(e); break; } break; case '\\': // \ backslash switch(true) { case cmdShiftKey(e): // Cmd Shift + \ : toggle split switch(true) { case window.top === window.self && $content_pane.attr('data-content') === 'has_iframe': sendMessage('iframe','toggle_split'); // send toggle split message to top break; case $('#toggle_split').height() > 0: $('#toggle_split').click(); // click toggle split break; } break; case cmdKey(e): // Cmd + \ : toggle sidebar if ( window.top !== window.self ) { sendMessage('top','toggle_sidebar'); } else { $('#toggle_sidebar').click(); } break; } break; case 'Tab': if ( document.activeElement.hasAttribute('contenteditable') ) { return; } e.preventDefault(); switch(true) { case $('body').hasClass('has_warning'): tabWarningButtons(e); break; case window.top === window.self: // If iframe is not focused if ( $('body').hasClass('focus_content') ) { focusSidebar(); } else { focusContent(); } break; case window.top !== window.self: // If iframe is focused if ( document.activeElement.tagName.toLowerCase() === 'textarea' ) { return; } else { sendMessage('top','tab'); } if ( !$('#toolbar').length ) { // if not text editor (i.e., is a dir), focus sidebar. $('tr.selected').toggleClass('selected dimmed'); } else { focusText(); } break; } break; case 'Escape': window.getSelection().removeAllRanges(); $('#content_playlist, #content_audio_playlist').removeClass('has_content'); $('#warning_btn_cancel,#close_help').click(); if ( window.top !== window.self ) { sendMessage('top','escape'); } else { closeMenus(); focusSidebar(); } break; case '.': switch(true) { case cmdKey(e): if ( $('body').hasClass('has_warning') ) { e.preventDefault(); $('#warning_btn_cancel,#close_help').click(); // click cancel buttons } break; default: break; } } // end switch }); // ***** END KEYBOARD EVENTS ***** // // ***** GRID SETUP ***** // // Create Font Grid Items function fontGridItems() { let $font_grid_items_arr = [], $font_files = $dir_list_body.find('.font'), fontGridStyles = $font_grid_styles.sheet; for ( let i = $font_files.length; i--; ) { let newGridItem = showFont( $font_files[i], true, fontGridStyles ); $font_grid_items_arr.unshift( newGridItem ); } $font_grid_items_arr[$font_grid_items_arr.length - 1].addClass('border_bottom_x'); // add bottom border to last return $font_grid_items_arr; } // Create Image Grid Items function imageGridItems() { let $image_grid_items_arr = [], $image_files = $dir_list_body.find('.image:not(.ignore)'), classes = 'image_grid_item border_right_x border_bottom_x'; for ( let i = $image_files.length; i--; ) { const id = $image_files.eq(i).attr('id'); const $this_link = thisLink($image_files[i]); const exts = $row_types.image.filter( ext => $.inArray(ext, $row_settings.ignore) == -1 ); // decide which image files can be displayed const $title_name = $this_link.slice($this_link.lastIndexOf('/') + 1); if ( $.inArray( thisExt($image_files[i]), exts ) > -1 ) { // if this row file ext is in the image extension array let item = '
    '; $image_grid_items_arr.unshift( item ); } } return $image_grid_items_arr; } // Make Grids function makeGrids(id) { closeGrid(); // remove previous grid items $content_pane.removeClass('has_hidden_grid has_image_grid has_font_grid'); // reset content_pane grid classes switch(true) { case id === 'show_font_grid' || !$body.hasClass('has_images'): // only show font grid $content_pane.addClass('has_font_grid'); $content_grid.append( fontGridItems() ); break; case id === 'show_image_grid' || !$body.hasClass('has_fonts'): // only show image grid $content_pane.addClass('has_image_grid'); $content_grid.append( imageGridItems() ); break; default: // show grid of both images and fonts $content_grid.append( imageGridItems(), fontGridItems() ); } } // Click grid button: make grid and show grid $('#sidebar_header').on('click', '#grid_btn, #show_font_grid, #show_image_grid', function(e) { e.stopPropagation(); showContent($(this).attr('id')); }); // ***** SCALE PREVIEWED IMAGES & FONTS ***** // // Scale Fonts function scaleFonts(e, incr, id) { const fontSize = parseInt(getComputedStyle(document.body).fontSize); // pts/em const getFontSize = function(el) { return parseFloat(el.css('font-size')); }; if ( id === 'decrease' ) { incr = 1/incr; } if ( $content_pane.attr('data-content') === 'has_grid' ) { $content_grid.css({'font-size':( getFontSize($content_grid)/fontSize * incr ) +'em'}); return; } if ( $content_pane.attr('data-content') === 'has_font' ) { $content_font.css({'font-size':( getFontSize($content_font)/fontSize * incr ) +'em'}); return; } scrollThis('content_container','content_font'); } // Scale Glyphs function scaleGlyphs(e, incr, id) { if ( id === 'decrease' ) { incr = 1/incr; } let scale = ( $('#glyph_viewer').attr('data-scale') === undefined ? incr : incr * $('#glyph_viewer').attr('data-scale') ); if ( scale >= 1 ) { $('#glyph_viewer').css({'width': scale * 100 +'%','height': scale * 100 +'%'}); document.getElementById('content_font').scrollLeft = -( Math.round( $('#font_viewer').outerWidth(true) - $('#glyph_viewer').width() ) / 2 ); document.getElementById('content_font').scrollTop = -( Math.round( $('#font_viewer').outerHeight(true) - $('#glyph_viewer').height() ) / 2 ); } $('#glyph_viewer').css({'background-size': scale * 100 +'%'}).attr('data-scale',scale.toFixed(2)); } // Scale Image Grid items function scaleGridImages(incr,id) { if ( id === 'decrease' ) { incr = 1/incr; } const $imageGridItem = $('.image_grid_item img'); let $image_grid_item_width = Number.parseFloat( $imageGridItem.width(),10) * incr; let $image_grid_item_height = Number.parseFloat( $imageGridItem.height(),10) * incr; let $image_grid_item_maxwidth = Number.parseFloat( $imageGridItem.css('maxWidth'),10) * incr; let $image_grid_item_maxheight = Number.parseFloat( $imageGridItem.css('maxHeight'),10) * incr; // prevent reducing grid image size on first scale click: if ( $image_grid_item_width < $image_grid_item_maxwidth ) { $image_grid_item_width = $image_grid_item_maxwidth; } if ( $image_grid_item_height < $image_grid_item_maxheight ) { $image_grid_item_height = $image_grid_item_maxheight; } // set grid properties $content_grid.css({'grid-template-columns':'repeat(auto-fill, minmax('+ ($image_grid_item_width +16) +'px, auto ) )'}); $content_grid.find('img').css({'max-width':( $image_grid_item_width ) +'px', 'max-height':( $image_grid_item_height ) +'px'}); return; } // Zoom Images on click function scaleImages(e,incr,id) { const $this_image = ( $('#iframe_body > img').length === 1 ? $('#iframe_body > img') : $content_image.find('img') ); const $this_link = $this_image.attr('src'); const $content_container = ( $('#iframe_body > img').length === 1 ? $('#iframe_body') : $('#content_container') ); let CC_width = function() { return Math.round($content_container.width()); }; let CC_height = function() { return Math.round($content_container.height()); }; const $this_width = Math.round($this_image.width()); const $this_height = Math.round($this_image.height()); const $iframe_delta = ( $('#iframe_body > img').length === 1 ? Number.parseInt($('#iframe_body').css('padding')) : 0 ); // scale grid images if ( $content_pane.attr('data-content') === 'has_grid' ) { scaleGridImages(incr,id); } else { // scale single images getDimensions( $this_link, function( width, height ) { if ( incr !== undefined && id !== undefined ) { // scale images by increment $content_pane.addClass('has_scaled_image').removeClass('has_zoom_image'); // remove zoom classes in case window resized after zoom if ( id === 'increase' ) {// && $this_width < width && $this_height < height) { $this_image.css({'width':$this_width * incr, 'height': $this_height * incr}); $this_image.css({'width':$this_width * incr, 'height': 'auto', 'max-width':'none', 'max-height':'none' }); } if ( id === 'decrease' && ( $this_width >= 1 && $this_height >= 1 ) ) { $this_image.css({'width':$this_width / incr, 'height': 'auto', 'max-width':'none', 'max-height':'none' }); } // keep images centered when scaling if ( Math.round($this_image.width()) >= CC_width() ) { $content_image.scrollLeft( ( Math.round( ( $this_image.outerWidth(true) ) - CC_width() ) )/2 ) ; } if ( Math.round($this_image.height()) <= CC_height() ) { $content_image.scrollTop( ( CC_height() - Math.round( $this_image.height() ) )/2 ); } else { $content_image.scrollTop( ( Math.round($this_image.outerHeight(true)) - CC_height())/2 ) ; } } else { // else zoom single image on click $this_image.removeAttr('style'); if ( width <= CC_width() && height <= CC_height() ) { // but don't zoom small images: $content_pane.removeClass('has_zoom_image has_scaled_image'); return; } // otherwise, zoom image: const $CC_offset = $content_container.offset(); const $img_offset = $this_image.offset(); // x,y coordinates of zoom click as percentage of image width/height const percentX = (e.pageX - $img_offset.left)/$this_width; const percentY = (e.pageY - $img_offset.top)/$this_height; // calculate scroll by pixel coordinates of full-size image - click coordinates and content_container offsets const scrollX = (width * percentX) - e.pageX + $CC_offset.left - ($iframe_delta * width / $this_width); const scrollY = (height * percentY) - e.pageY + $CC_offset.top - ($iframe_delta * height / $this_height); $content_pane.removeClass('has_scaled_image').toggleClass('has_zoom_image' ); $content_image.scrollLeft( scrollX ); $content_image.scrollTop( scrollY ); } }); setImageDimensions(); } } // Zoom single image on click $content_image.on('click','img', function(e) { scaleImages(e); focusContent('content_image'); }); // Scale Fonts and Images function scalePreviewItems(e,id) { // combine scaling into one function if ( $content_pane.attr('data-content') === 'has_glyph' ) { scaleGlyphs(e, 1.125, id); return; } scaleImages(e, 1.125, id ); scaleFonts(e, 1.125, id ); } // Scale Buttons $('#scale').on('click','span', function(e) { e.preventDefault(); e.stopPropagation(); scalePreviewItems(e, $(this).attr('id') ); $('#reload_btn').addClass('reset'); if ( $('body').hasClass('focus_content') ) { focusContent(); } }); // ***** END SCALE PREVIEW ITEMS ***** // // ***** AUDIO CONTENT ***** // // Update Playlist function updatePlaylist() { let playlist = []; $dir_list_body.find('.audio').not('.unchecked').not('.disabled').each(function() { playlist.push( thisID( $(this) ) ); }); return playlist; } // Randomize Shuffle List function shuffleArray(array) { for ( let i = array.length - 1; i > 0; i-- ) { const j = Math.floor(Math.random() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; } return array; } // Attach Shuffle List data to $audio_player function updateShuffleList(id) { let shuffleList; switch(true) { case !$body.hasClass('shuffle_audio'): break; case id !== undefined: // don't include .playing and .unchecked track in shufflelist shuffleList = $audio_player.data('shufflelist'); switch(true) { case $(document.getElementById(id)).hasClass('unchecked') || $(document.getElementById(id)).hasClass('playing'): shuffleList.splice(shuffleList.indexOf(id), 1); $audio_player.data('shufflelist',shuffleList); break; default: shuffleList.push(id); shuffleList = shuffleArray( shuffleList ); } break; default: shuffleList = shuffleArray( updatePlaylist() ); $audio_player.data('shufflelist',shuffleList); } } // Check/Uncheck single Audio/Video Files and update shufflelist function toggleChecked(el) { $(el).blur(); thisRow(el).toggleClass('unchecked'); updateShuffleList(thisRow(el).attr('id')); } $('#tbody').on('click','.media input', function(e) { e.stopPropagation(); toggleChecked($(this)); }); // Check/Uncheck all Audio/Video Files and update shufflelist function toggleAllChecked() { $dir_list_body.find('> tr').find('input').trigger('click'); updateShuffleList(); } // Is Playing; returns true if all conditions are true function isPlaying(el) { return (el !== undefined && el.get(0).currentTime > 0 && !el.get(0).paused && !el.get(0).ended); } // Play Media function playMedia(task) { if ( $content_pane.hasClass('has_audio') ) { $audio_player.trigger(task); } else { $content_video.trigger(task); } } // Skip media tracks +/-10/30 seconds function mediaSkip(e,args) { let factor, skip; switch(true) { case e !== undefined: // from top factor = ( e.key === 'ArrowLeft' ? -1 : 1 ); // forward or backward? skip = ( e.altKey && e.shiftKey ? 30 : e.altKey ? 10 : null ); // 30s or 10s? break; case args !== undefined: // from iframe factor = ( args[0] === 'ArrowLeft' ? -1 : 1 ); // forward or backward? skip = args[1]; // 30s or 10s? break; } const $player = ( $('#content_pane').hasClass('has_audio') ? $audio_player : $content_video ); // audio or video? const time = $player.prop('currentTime'); // current time $player.prop('currentTime', time + factor*(skip)); // set time } // Play/Pause Audio/Video function playPauseMedia() { let $player = $('#audio'); if ( $content_pane.attr('data-content') === 'has_video' ) { $player = $('#content_video'); } if ( isPlaying( $player ) ) { $player.trigger('pause'); } else { $player.trigger('play'); } } // Toggle Audio Playback Options (shuffle, loop) function audioPlaybackOptions(id) { if ( id === 'shuffle' ) { $body.toggleClass('shuffle_audio'); updateShuffleList(); if ( $body.hasClass('shuffle_audio') && $('.playing').length === 0 ) { playPrevNextTrack('ArrowRight'); } else { /* do nothing: i.e., allow current track to continue playing */ } } else { $body.toggleClass('loop_audio'); document.getElementById('audio').toggleAttribute('loop'); } } // click loop or shuffle audio options $('#audio_options').on('click','input', function() { audioPlaybackOptions( $(this).attr('id') ); }); // Initialize Media: play next track on ended and scroll to playing item function initMedia() { $('#audio, #content_video').on('ended', function() { $('#next_track').click(); scrollThis('tbody','playing'); }); } // ***** END AUDIO PLAYBACK ***** // // ***** IFRAME SETUP ***** // function setUpIframeUI() { // setup iframe directory UI or iframe text editor $('body').css({'background-color':'rgb(241, 243, 244)'}); // add iframe body id $('video').css({'width':'100%'}); $('body').attr('id','iframe_body'); // add iframe body id const $textFiles = $row_types.markdown.concat($row_types.text, $row_types.code); // define which files are editable switch(true) { case window.location.pathname.endsWith('/'): // set up iframe directory setUpIframeDirUI(); break; case $textFiles.includes( window.location.pathname.slice( window.location.pathname.lastIndexOf('.') + 1 ) ): // else set up iframe text editor setUpTextEditorUI(); break; } } // IFRAME DIRECTORY Prep function setUpIframeDirUI() { let link = decodeURIComponentSafe(window.location.href); let parentLink = link.split('/').slice(0,-2).join('/') +'/';//+ window.location.search; let queryPrefs = window.location.search; queryPrefs = queryPrefs.slice(1).split('&'); queryPrefs = queryPrefs.map(x => x.split('=')); let prefs = Object.fromEntries(queryPrefs); // convert query array of arrays to object let sortPref = prefs.sort_by; // sort determined by parent's current sorting pref let sortDirection = ( getQuery('sort_direction') === 'up' ? -1 : 1 ); // get sort direction let viewSourcePref = prefs.view_directory_source; let $iframeBody = $('#iframe_body'), $iframeHead = $('head'); let bodyClasses = []; // make array of classes to add all at once if ( viewSourcePref === 'true' || prefs.is_error === 'true' ) { // show raw directory index if error or viewing directory source return; } else { // else set up iframe directory if ( prefs.alternate_background === 'true' ) { bodyClasses.push('alternate_background'); } if ( prefs.ignore_ignored_files === 'true' ) { bodyClasses.push('ignore_ignored_files'); } if ( prefs.show_invisibles === 'true' ) { bodyClasses.push('show_invisibles'); } if ( prefs.show_numbers === 'true' ) { bodyClasses.push('show_numbers'); } if ( prefs.theme === 'dark' ) { bodyClasses.push('theme_dark'); } else { bodyClasses.push('theme_light'); } if ( prefs.use_custom_icons === 'true' ) { bodyClasses.push('use_custom_icons'); } if ( getBrowser() === 'firefox' ) { bodyClasses.push('is_gecko'); } bodyClasses.push('sort_by_'+ sortPref); $iframeBody.data('sort_direction',sortDirection); // initial directory sort $iframeHead.find('style').remove(); // remove any existing directory index styles $iframeHead.append(''); let iFrameTable = ContentIframeDirEls(parentLink); // get iframe directory UI elements const preppedIndex = makeNewIndex($iframeBody,sortPref); // prepare directory index $iframeBody.removeAttr('style').addClass( bodyClasses.join(' ') ).empty().append(iFrameTable).find('#tbody').append(preppedIndex); // append UI and prepped index } } // IFRAME Directory sorting function iframeSorting(el) { const $iframe_dir_list_rows = $('#iframe_body').find('#tbody').find('tr'); const id = el.attr('id'); // = new sort class let current_sort = document.getElementById('iframe_body').classList.value.match(/sort_by_\w+/)[0]; if ( $('#iframe_body').data('sorting') !== id ) { // if clicking sorting item for the first time $('#iframe_body').data('sorting',id); $('#iframe_body').data('sorting',id).data('sort_direction', 1 ); el.removeClass('up'); } else { // clicking the same sorting item again -- reverse sort order $('#iframe_body').data('sort_direction', $('#iframe_body').data('sort_direction') * -1 ); el.toggleClass('up'); } const sort_direction = $('#iframe_body').data('sort_direction'); const $sorted_index = sortDirList( $iframe_dir_list_rows, id, sort_direction ); $('#iframe_body').removeClass(current_sort).addClass(id).find('#tbody').empty().append($sorted_index); } $('#iframe_body').on('click','.sorting', function(e) { e.preventDefault(); iframeSorting($(this)); }); // IFRAME Click to select item $('#iframe_body').on('click', function() { sendMessage('top','iframe_click'); }); // IFRAME Links $('#iframe_body').on('click','a', function(e) { let link = $(this).attr('href'), url; if ( link.startsWith('#') ) { return true; } else { e.preventDefault(); url = newURL(link); } switch(true) { case $(this).parents('#tbody').length: case url === undefined: case link.startsWith('#'): break; case $(this).parent().attr('id') === 'parent': // open parent directory in iframe sendMessage('top','top_show_iframe_dir','',link); // adds data:iframe_link to $content_iframe break; case url.protocol !== 'file:' && window.location.protocol === 'file:': // open remote link from local file in new tab/window window.open(link,'_blank'); break; case url.protocol === 'file:' && window.location.protocol === 'file:': // open local links on local files in iframe case url.protocol === 'about:': // document #links case RegExp(url.hostname).test(window.location.hostname): // same origin links (might not include TLD) (just covering bases here) case RegExp(window.location.hostname).test(url.hostname): // same origin links (might not include TLD) (just covering bases here) // window.location = link; if ( getItemKind( getItemExt(link) ) === 'dir' ) { sendMessage('top','top_show_iframe_dir','',link); } else { sendMessage('top','top_show_iframe_file','',[link, getItemKind( getItemExt(link) ) ]); } break; default: window.open(link,'_blank'); break; // else open external document links in new tab } }); // IFRAME Click item to add selected class or play/pause media $('#iframe_body').on('click','#tbody tr', function(e) { e.preventDefault(); $(this).addClass('selected').siblings('tr').removeClass('dimmed selected'); if ( $(this).hasClass('audio') && $(this).hasClass('playing') ) { sendMessage('top','iframe_play_pause_media'); } else { sendMessage('top','iframe_click'); } // play/pause media or send message }); // IFRAME Doubleclick for directory and file navigation $('#iframe_body').on('dblclick','#tbody tr a', function(e) { e.preventDefault(); e.stopPropagation(); let link = $(this).attr('href'); if ( window.location.protocol === 'file:' ) { // send full path for non-local files link = 'file://'+ link; } else { link = window.location.protocol +'//'+ window.location.hostname + window.location.pathname + link; } let kind = $(this).closest('tr').attr('data-kind'); if ( kind === 'dir' ) { sendMessage('top','top_show_iframe_dir','',link); // adds current navigated iframe directory link data:iframe_link to $content_iframe } else { // display audio, font, image, pdf, video, and other files in corresponding content container if ( kind === 'audio' ) { $(this).closest('tr').addClass('playing selected').siblings('.audio').removeClass('playing selected'); } let args = [link,kind]; sendMessage('top','top_show_iframe_file','',args); // tell #top what to play } }); // Open IFRAME directory in sidebar function openDirInSidebar() { // let link = decodeURIComponentSafe(window.location.href.toString()); let link = decodeURIComponentSafe($('#open_in_sidebar').find('a').attr('href')); sendMessage('top','top_open_in_sidebar','',link); } $('#open_in_sidebar').on('click',function(e) { e.preventDefault(); openDirInSidebar(); }); //***** TEXT EDITING PANE *****// function setUpTextEditorUI() { let sourceText, bodyClasses = []; if ( !$('body').hasClass('has_text_editor_UI') ) { // add classes, styles, and scripts; only add once $('head').append(''); $('head').append(''); $('#title span').empty().text('Text Editor'); } switch(true) { // get source text and append UI elements case window.self === window.top: // top level text editor if ( !$('body').hasClass('has_text_editor_UI') ) { $('body').addClass('has_text_editor_UI'); $('#content_text').append( TextEditingUIEls() ); if ( getQuery('sync_scroll') === 'true' ) { $('#sync_scroll input').prop({checked:true}); } focusText(); } break; case window.self !== window.top: // iframe text editing UI $('head').prepend(''); sourceText = decodeURIComponentSafe($('body').find('> pre').text()); // get source text and decode Unicode chars. bodyClasses.push('has_text_editor_UI'); if ( getQuery('disable_text_editing') === 'true' ) { $body.removeClass('split_view source_text'); bodyClasses.push('text_editing_disabled preview_text'); } else { if ( getQuery('split_view') === 'true' ) { bodyClasses.push('split_view'); } if ( getQuery('default_text_view') === 'preview' ) { bodyClasses.push('preview_text'); } else { bodyClasses.push('source_text'); } } if ( getQuery('editor_theme') !== '') { bodyClasses.push('editor_theme_'+ getQuery('editor_theme')); } else { bodyClasses.push('editor_theme_'+ getQuery('theme')); } // add text editor ui and warnings $('head').append(''); $('body').addClass( bodyClasses.join(' ') ).empty().append('
    '+ TextEditingUIEls() +'
    '+ Warnings() +'
    '); $('#text_source').val(sourceText); // set the source text value if ( getQuery('sync_scroll') === 'true' ) { $('#sync_scroll input').prop({checked:true}); } break; } TextEditing(); // call text editing functions } // setup and show top level text editor $('#text_editor, #text_editor_row').on('click', function(e) { e.preventDefault(); showContent( $(this).attr('id') ); }); // Main Text Editing Function function TextEditing() { const $toolbar = $('#toolbar'); const $source = $('#text_source'); const $preview = $('#text_preview'); const $html = $('#html_preview'); const $MDhandle = $('#text_editing_handle'); // Toolbar button functions $toolbar.on('click','li,span', function(e) { e.stopPropagation(); MDtoolBarFunctions($(this).attr('id')); }); $toolbar.on('mousedown', function(e) { e.preventDefault(); }); // prevent textarea from losing focus when clicking sidebar // Resize $MDhandle.on('mousedown', function(e) { e.stopPropagation(); MDresizeSplit($MDhandle,$source); }); $(window).on('resize', function() { $source.add($preview).add($MDhandle).attr('style',''); }); // reset split to 50/50 on window resize; // Click labels to toggle checkboxes $preview.add($toolbar).on('click','label', function(e) { e.stopPropagation(); $(this).siblings('input').click(); }); // Sync scroll $source.on('scroll', function() { MDsyncScroll(this); }); $preview.on('scroll', function() { MDsyncScroll(this); }); $html.on('scroll', function() { MDsyncScroll(this); }); // Generate Preview const $source_text = ( $source.length === 0 ? '' : $source.val() ); MDmarkdown( $source_text, $preview ); // Live preview update, and set edited classes for unsaved warning $source.on('input', function() { // only add class or send message once after editing if ( !$('body').hasClass('edited') ) { $('body').addClass('edited'); // add edited class if ( window.top !== window.self ) { sendMessage('top','iframe_edited','',''); } // send edited message to top } MDlivePreview($source,$preview); }); // Checklists MDsetChecklistClass(); // Live checkboxes $preview.on('click','.checklist input', function(e) { e.stopPropagation(); MDliveCheckBoxes($(this),$source,$preview); }); // Preview TOC click navigation $preview.on('click','.table-of-contents a', function(e) { e.preventDefault(); MDtocClick($(this),$preview); }); // Click header uplinks $preview.on('click','.uplink', function(e) { e.stopPropagation(); MDheaderClick($preview); }); } ///// END MAIN MD FUNCTION // MARKDOWN Functions // Focus Text function focusText() { switch(true) { case $('body').hasClass('split_view'): case $('body').hasClass('source_text'): $('#text_source').focus(); break; case $('body').hasClass('has_html'): $('#html_preview').focus(); break; case $('body').hasClass('preview_text'): $('#text_preview').focus(); break } } // MD focus or blur text editor $('#text_source').on('click',function(e) { e.preventDefault(); }); // Restore text selection on focus $('#content_text').on('focus','#text_source',function() { let selectionStart, selectionEnd; if ( window.self === window.top ) { selectionStart = $('#text_source').attr('data-selection_start'); selectionEnd = $('#text_source').attr('data-selection_end'); } else { selectionStart = $('#text_source').attr('data-selection_start'); selectionEnd = $('#text_source').attr('data-selection_end'); } if ( selectionStart !== undefined && selectionEnd !== undefined ) { document.getElementById('text_source').setSelectionRange(selectionStart, selectionEnd); //, selectionDirection } }); // Save text range or cursor location $('#content_text').on('blur','#text_source',function() { let selectionStart = $(this).prop('selectionStart'); let selectionEnd = $(this).prop('selectionEnd'); if ( window.self === window.top ) { $(this).attr('data-selection_start',selectionStart).attr('data-selection_end',selectionEnd); } else { sendMessage('top','save_text_selection','',[selectionStart,selectionEnd]); } }); // Select Textarea Content function selectTextareaContent(id) { let textarea = document.getElementById(id); textarea.focus(); textarea.select(); textarea.scrollTop = 0; } // MD UI Buttons functions function MDtoolBarFunctions(id) { let $thisFileName; let sourceEl = $('#text_source'); let previewEl = $('#text_preview'); if ( $('#content_pane').attr('data-content') === 'has_text_editor' ) { $thisFileName = 'untitled'; } else { $thisFileName = decodeURI(window.location.pathname.slice(window.location.pathname.lastIndexOf('/') + 1)); } const $saveHTMLOpen = ''; const $saveHTMLClose = ''; switch (id) { case 'toggle_theme': $('body').toggleClass('editor_theme_dark editor_theme_light'); sourceEl.focus(); break; case 'toggle_split': $('body').toggleClass('split_view').find('#text_source,#text_preview,#html_preview,#text_editing_handle').attr('style',''); focusText(); break; case 'show_source': $('body').toggleClass('split_view').removeClass('preview_text').addClass('source_text'); focusText(); break; case 'show_preview': $('body').toggleClass('split_view').removeClass('source_text').addClass('preview_text'); focusText(); break; case 'show_html': // toggle html and preview $('body').removeClass('source_text'); if ( $('body').hasClass('has_html') ) { $('body').removeClass('has_html'); if ( document.getElementById('text_source') !== document.activeElement ) { $('#text_preview').focus(); } } else { let sourceHTML = previewEl.html().toString(); $('#html_preview').empty().text(sourceHTML); $('body').addClass('has_html'); if ( document.getElementById('text_source') !== document.activeElement ) { $('#html_preview').focus(); } } break; case 'clear_text': if ( $('#text_source').val().length > 0 ) { $('body').addClass('has_warning').find('#warnings_container').removeClass().addClass('clear'); focusButton('warning_btn_cancel'); } else { focusText(); } break; case 'save_text': saveMD( $thisFileName +'.md', sourceEl.val() ); break; case 'save_HTML': saveMD( $thisFileName +'.html', $saveHTMLOpen + MDprepHTML(previewEl.html()) + $saveHTMLClose ); break; } } // MD Custom pre- and post-processing for text. function MDaddHeaderIDs(match, p1, p2, p3) { return ''+ p3; } // create header ids for TOC function MDcustomPreProcess(src) { return src; } // we're not doing anything here just yet... function MDcustomPostProcess(html) { html = html.replace(/<(p|li|dt|dd)>-*\s*\[\s*x\s*\]\s*(.+?)<\/(p|li|dt|dd)>$/gm,'<$1 class="checklist">') // checkboxes in p,li,dt,dd .replace(/<(p|li|dt|dd)>-*\s*\[\s{1,}\]\s*(.+?)<\/(p|li|dt|dd)>$/gm,'<$1 class="checklist">') // checkboxes // .replace(/
  • "/g,'

  • ') .replace(/^]*)>([^<]+)/gm, MDaddHeaderIDs) // add header IDs; .replace(/<\/h(\d)>/g,''); return html; } //MD Render markdown from preprocessed source text function MDmarkdown(sourceText,previewEl) { const MDit = window.markdownit({linkify:false, typography:false, html:true}) .use(window.markdownitMultimdTable, {enableMultilineRows: true}) .use(window.markdownitSub) .use(window.markdownitSup) .use(window.markdownitFootnote) .use(window.markdownitCentertext) .use(window.markdownitDeflist) .use(window.markdownitTocDoneRight) ; let MDpreview = MDit.render( MDcustomPreProcess( sourceText ) ); previewEl.html( MDcustomPostProcess( MDpreview ) ); // set previewed html } // MD Live preview, add edited warning function MDlivePreview(sourceEl,previewEl) { MDmarkdown( sourceEl.val(),previewEl ); MDsetChecklistClass(); let sourceHTML = previewEl.html().toString(); $('#html_preview').empty().text(sourceHTML); } // MD Live Checkboxes prep: find each instance of [ ] or [x] and replace text in index = to clicked checkbox in Preview. function MDreplaceAt(str, replacement, position) { str = str.substring(0, position) + replacement + str.substring(position + replacement.length); return str; } function MDreplaceNthSubStr(str,substr,replacement,index) { let count = 0; let found = substr.exec(str); while ( found !== null ) { if ( count === index ) { return MDreplaceAt(str, replacement, found.index ); } else { count++; found = substr.exec(str); } } } // MD Live Checkboxes function MDliveCheckBoxes(checkbox,sourceEl,previewEl) { $('.checklist').removeClass('clicked'); checkbox.closest('p,li,dt,dd').addClass('clicked'); const thisIndex = previewEl.find('.checklist').index( $('.clicked') ); const srctext = sourceEl.val(); const substr = new RegExp(/\[\s*.\s*\]/g); const replacement = ( checkbox.is(':checked') ? '[x]' : '[ ]' ); sourceEl.val( MDreplaceNthSubStr(srctext, substr, replacement, thisIndex) ); } // MD Checkbox list class: Prevent checkbox lists from having list bullets function MDsetChecklistClass() { $('input[type="checkbox"]').closest('ul').css({'list-style':'none','padding':'0'}); } // MD Resize Split View function MDresizeSplit(handle,sourceEl) { let $sidebarWidth = $('#sidebar').outerWidth(); let $pageWidth = window.innerWidth; $(document).on('mousemove',function(e) { e.stopPropagation(); e.preventDefault(); let pageX = e.pageX; if ( pageX > $sidebarWidth + 100 && pageX < $pageWidth - 100 ) { // min widths $('#text_source').css({'width': pageX - $sidebarWidth + 'px'}); $('#html_preview').css({'width': $('#content_text').outerWidth() - sourceEl.outerWidth() + 'px'}); $('#text_preview').css({'width': $('#content_text').outerWidth() - sourceEl.outerWidth() + 'px'}); handle.css({'left': sourceEl.outerWidth() - 4 + 'px'}); } }); handle.on('mouseup', function() { $(document).off('mousemove'); focusText(); }); } // MD UI Sync Scroll function MDpercentage(el) { return (el.scrollTop / (el.scrollHeight - el.offsetHeight)); } function MDsyncScroll(el1) { let el2; // scroll this element when scrolling el1 switch(true) { case el1.getAttribute('id') === 'text_source': el2 = ( $('body').hasClass('has_html') ? document.getElementById('html_preview') : document.getElementById('text_preview') ); break; case el1.getAttribute('id') === 'text_preview': case el1.getAttribute('id') === 'html_preview': el2 = document.getElementById('text_source'); break; } if ( document.querySelector('input[name="sync_scroll"').checked ) { el2.scrollTo( 0, (MDpercentage(el1) * (el2.scrollHeight - el2.offsetHeight)).toFixed(0) ); // toFixed(0) prevents scrolling feedback loop } } // click TOC anchors function MDtocClick(el,previewEl) { let thisId = el.attr('href'); if ( thisId ) { previewEl.scrollTop( $(thisId).offset().top - 48 ); } } // click Headers to return to TOC or top function MDheaderClick(previewEl) { if ( previewEl.find('.table-of-contents').length > 0 ) { document.getElementsByClassName('table-of-contents')[0].scrollIntoView(true); } else { document.getElementById('preview').scroll(0,0); } } // MD Clear text source function clearText() { if ( window.self !== window.top ) { sendMessage('top','iframe_edited'); } $('body').removeClass('edited'); $('#text_preview,#html_preview').empty(); $('#text_source').val('').show().focus(); } // MD SAVE SOURCE or HTML function MDprepHTML(data) { data = data.replace(/.<\/span>/g,''); return data; } function saveMD(filename, data) { saveFile(data,'text/plain',filename); if ( window.top !== window.self ) { sendMessage('top','clear'); } // if iframe, send message to top $('body,#text_source,#content_text').removeClass('edited'); } // END TEXT EDITING // MESSAGES // Send a message to iframe or parent function sendMessage(target,message,funcName,args) { var messageObj = { 'messageContent': message, 'functionName': funcName, 'arguments': args }; if ( target === 'iframe' ) { let contentIFrame = document.getElementById('content_iframe'); contentIFrame.contentWindow.postMessage( messageObj, '*' ); } if ( target === 'top' ) { window.parent.postMessage( messageObj, '*'); } } // Receive a message from iframe or parent, do appropriate action function receiveMessage(e) { if ( e.origin === 'null' || e.origin === $origin ) { let $message = e.data.messageContent; let funcName = e.data.functionName; let args = e.data.arguments; switch( $message ) { case 'arrowNavigation': arrowNavigation(args); // classname, key break; case 'toggle_sidebar': // toggle sidebar $('#toggle_sidebar').click(); break; case 'toggle_menu': // toggle menu $('#menu_container').click(); break; case 'close_menu': // close menu case 'top_closed_menu': $('#iframe_body').removeClass('has_menu'); break; case 'top_has_menu': $('#iframe_body').addClass('has_menu'); break; case 'menu_navigation': // menu navigation from iframe menuNavigation(args); // classname, key break; case 'menu_selection': case 'clickMenu': // show menu e.preventDefault(); e.stopPropagation(); clickMenu(); break; case 'escape': if ( $('#top').hasClass('focus_content') && $('#top').hasClass('has_menu') ) { closeMenus(); focusContent(); } else { focusSidebar(); } break; case 'tab': // escape content_iframe with tab or escape focusSidebar(); break; case 'iframe_click': // close menus and fade sidebar closeMenus(); $('body').addClass('focus_content'); break; case 'tab_iframe': // after tabbing into iframe if ( $('#text_source:visible').length === 1 ) { $('#text_source:visible').focus(); // focus textarea when tabbing into iframe let selection = window.getSelection(); // if data-selection !== 0 if ( selection.anchorOffset > 0 ) { // restore cursor position or text selection // document.getElementById('text_source').setSelectionRange(x,y); // document.getElementById('text_source').scrollTop = [position of x]; return; } else { document.getElementById('text_source').setSelectionRange(0,0); document.getElementById('text_source').scrollTop = 0; } } else if ( $('#text_preview:visible').length === 1 ) { $('#text_preview').attr('tabindex',-1).focus().click(); // else focus preview to allow arrow scrolling.... } if ( $('#tbody').length === 1 ) { if ( $('tr.selected, tr.dimmed').length === 0 ) { $('#tbody').find('tr:visible a').first().click(); // select first row when tabbing into directory } else { $('.dimmed').toggleClass('selected dimmed'); } } break; case 'reload': // escape content_iframe and close content $('#reload_btn').click(); break; case 'close': // escape content_iframe and close content $('#close_btn').click(); break; case 'iframe_play_pause_media': playPauseMedia(); break; case 'mediaSkip': // allow mediaskip from focuses iframe directory mediaSkip(undefined,args); break; case 'play_prev_next_iframe_audio': // play next iframe track if ( args === 'ArrowRight' ) { if ( $('.audio.playing').nextAll('.audio.selected').length === 1 ) { $('.audio.playing').nextAll('.audio.selected').first().find('a').dblclick(); } else { $('.audio.playing').nextAll('.audio').first().find('a').dblclick(); } } break; case 'close_iframe_audio': $('.playing').removeClass('playing'); break; case 'iframe_edited': // let top know iframe text has been edited if ( !$('body#top').hasClass('iframe_edited') ) { $('body#top').addClass('iframe_edited'); } break; case 'clear': $('body#top').addClass('iframe_edited'); break; // toggleUIprefs: case 'show_numbers': case 'show_invisibles': case 'alternate_background': case 'ignore_ignored_files': $('#iframe_body').toggleClass($message); break; // MESSAGES FROM IFRAME to TOP case 'save_text_selection': // from iframe $('.text.selected,.code.selected,.markdown.selected').attr('data-selection_start',args[0]).attr('data-selection_end',args[1]); break; case 'get_text_selection': // from top to iframe $('#content_text').attr('data-selection_start',args[0]).attr('data-selection_end',args[1]); break; // MESSAGES FROM TOP to IFRAME case 'theme_light': case 'theme_dark': $('#iframe_body').toggleClass('theme_dark theme_light'); break; case 'split_view': case 'toggle_split': $('#toggle_split').click(); break; case 'default_text_view': $('#iframe_body').toggleClass('preview_text source_text').removeClass('split_view'); break; case 'toggle_text_editor': showTextEditor(); break; case 'iframe_focus_text': focusText(); break case 'unloading': // warn iframe that user wants to change iframes if ( !$('#iframe_body').hasClass('has_warning') ) { $('#iframe_body').addClass('has_warning').find('#warnings_container').removeClass().addClass('unloading').attr('data-function_name',funcName).attr('data-args',args); } break; case 'ignore':// from iframe "ignore" button $('#content_iframe').removeAttr('src').removeClass('has_content'); $('body').removeClass('iframe_edited faded focus_content'); $content_iframe.blur(); // doFunction(funcName,args); break; case 'top_show_iframe_dir': // focus iframe, add data new directory url, set title and content height showIframeDir(args); break; case 'top_show_iframe_file': // from doubleclicking iframe dir file showIframeFile(args); // replace this with default showContent function? break; case 'top_open_in_sidebar': // from iframe dir header openInSidebar(args); break; } } } window.addEventListener('message',receiveMessage,false); // END MESSAGES // WARNINGS // list of functions to remember while sending messages and then execute after warning button click function doFunction(funcName,args) { var funcDictionary = { 'arrowNavigation':arrowNavigation, 'clickRow':clickRow, 'doubleClickRow':doubleClickRow, 'null':null, 'clickMenu':clickMenu, 'clickThis':clickThis, 'clearText':clearText, 'closeButton':closeButton, 'closeContent':closeContent, 'closeFontFile':closeFontFile, 'closePlaylist':closePlaylist, 'closeGlyph':closeGlyph, 'openInContentPane':openInContentPane, 'mediaSkip':mediaSkip, 'resetContent':resetContent, 'setLocation':setLocation, 'showDirectorySource':showDirectorySource }; return funcName === 'null' ? null : funcDictionary[funcName](args); } // Open and Close Warning alert, focus default button function openWarning(id,buttonid) { $('body').addClass('has_warning').find('#warnings_container').removeClass().addClass(id); focusButton(buttonid); } function closeWarning() { $('body').removeClass('has_warning').find('#warnings_container, #warning_buttons button').removeClass(); } // Show warning after in certain conditions (edited text, open playlist, open font file, etc.; otherwise do the action. function showWarning(funcName,args) { switch(true) { case ( /arrowNavigation|clickRow/.test(funcName) ): // warnings for arrow navigation and row clicks switch(true) { case $('body').hasClass('iframe_edited'): sendMessage('iframe','unloading',funcName,args.key); // upon receipt of message, iframe will show its warning message, based on the funcName focusButton('warning_btn_save'); break; case !$('body').hasClass('focus_content') && /has_font_file|has_glyph/.test( $content_pane.attr('data-content') ): // warn with open font file and focused sidebar openWarning('warning_close_font','warning_btn_cancel'); break; default: doFunction(funcName,args); break; } break; case ( !/arrowNavigation|clickRow/.test(funcName) ): // warnings for other functions switch(true) { case $content_pane.attr('data-content') === 'has_font_file': // warn with open font file and close button. openWarning('warning_close_font','warning_btn_cancel'); break; case $('body').hasClass('has_playlist') && $content_pane.attr('data-content') === undefined && !$content_pane.hasClass('has_audio'): case $('body').hasClass('has_filelist') && $content_pane.attr('data-content') === undefined && !$content_pane.hasClass('has_audio'): openWarning('warning_close_playlist','warning_btn_cancel'); break; case $('body').hasClass('edited'): $('#content_pane').removeClass('has_hidden_text_editor').attr('data-content','has_text_editor'); openWarning('unloading','warning_btn_save'); break; default: doFunction(funcName,args); break; } } } // Warning buttons: what to do when the user clicks a warning button function warningButtons(id) { let btn = $(document.getElementById(id)); let container_el = btn.closest('body'); switch(id) { case 'warning_btn_dont_save': // do the user initiated func without saving the edited text if ( window.self !== window.top ) { if ( $('#warnings_container').hasClass('unloading') ) { clearText(container_el); sendMessage('top','ignore'); // remove the irame src and body.iframe_edited class } } else { clearText(container_el); $content_pane.removeAttr('data-content'); $('#content_iframe').removeAttr('src').removeClass('has_content'); $dir_list.find('.dir.selected a').click(); // reload selected sidebar directory } closeWarning(); break; case 'warning_btn_cancel': closeWarning(); if ( $body.hasClass('focus_content') ) { focusContent(); } break; case 'warning_btn_clear': // clear text editor closeWarning(); clearText(); break; case 'warning_btn_save': // save text if ( window.top !== window.self ) { sendMessage('top','clear'); } container_el.removeClass('edited'); $('#save_text').click(); closeWarning(); break; case 'warning_btn_ok': switch(true) { case $('#warnings_container').hasClass('warning_close_font'): closeFontFile(); closeWarning(); break; case $('#warnings_container').hasClass('warning_close_playlist'): closePlaylist(); closeWarning(); break; case $('#warnings_container').hasClass('warning_make_playlist'): makePlaylist(); break; case $('#warnings_container').hasClass('warning_local_playlist'): closeWarning(); break; case $('#warnings_container').hasClass('warning_no_playlist'): closeWarning(); break; } break; } } // Click Edited Warning Buttons $('#warnings_container').on('click','button', function(e) { e.preventDefault(); e.stopPropagation(); warningButtons( $(this).attr('id') ); }); // Edited Warning overlay: prevent user clicks on rest of UI $('body.has_overlay, body.has_warning').on('click mousedown mouseup', function(e) { e.preventDefault(); e.stopPropagation(); return; }); // Tab Warning Buttons (keyboard event) function tabWarningButtons(e) { switch(true) { case e.shiftKey: if ( !$('#warning_buttons').find(':focus,.focus').length || !$('#warning_buttons').find(':focus,.focus').prevAll('button:visible').length ) { $('#warning_buttons').find('button:visible').removeClass('focus').last().focus().addClass('focus'); } else { $('#warning_buttons').find(':focus,.focus').removeClass('focus').prevAll('button:visible').first().addClass('focus').focus(); } break; default: if ( !$('#warning_buttons').find(':focus,.focus').length || !$('#warning_buttons').find(':focus,.focus').nextAll('button:visible').length ) { $('#warning_buttons').find('button:visible').removeClass('focus').first().focus().addClass('focus'); } else { $('#warning_buttons').find(':focus,.focus').removeClass('focus').nextAll('button:visible').first().addClass('focus').focus(); } } } // END WARNINGS // PLAYLISTS // Open playlist $('#menu').on('click','#open_playlist_label', function(e) { e.stopPropagation(); }); $('#menu').on('change','#open_playlist', function(e) { openFile(e,'playlist'); }); // Create Playlist items const newURL = function(link) { try { return new URL(link,document.baseURL); } catch(error) { return console.log('This link is invalid. Please check the file.'); } }; function convertPlaylist(items) { let preppedIndex = ''; let preppedRow = ''; let rows, info, title, type, time = '0', display_time = '—', link, url, kind = '', display_kind, ext; items = items.replace(/\s*#EXTM3U.*\s*/g,'').replace(/^\*\n{2,}/gm,'\n'); // remove header comment and multiple returns switch(true) { // create rows based on m3u type; case ( /#EXTINF:/.test(items) ): type = 'extm3u'; rows = items.split('#EXTINF:'); break; // need to add cases for other extm3u directives default: type = 'm3u'; rows = items.split('\n'); break; } for ( let i = 0; i < rows.length; i++ ) { let row = rows[i], classes = []; switch(true) { // get entry information: title, link, etc. case row.length === 0: break; case type === 'extm3u' && row.trim().length > 0: // extm3u file row = row.trim().split('\n'); info = row[0]; time = info.slice(0,info.indexOf(',')).replace('undefined',''); display_time = new Date(time * 1000).toISOString().substr(11, 8); if ( display_time === '00:00:00' ) { display_time = '—'; } title = info.slice(info.indexOf(',') + 1); link = row[1]; break; default: // m3u with urls only title = decodeURIComponentSafe(row.slice(row.lastIndexOf('/') + 1)); link = row; break; } if ( link !== undefined ) { url = newURL(link); } switch(true) { case url === undefined && i !== 0: // add notice for invalid url and row class closeMenus(); classes.push('ignored','invalid','disabled'); title += ' [invalid url]'; case url === undefined: break; case url.search.length: // remove query string from dirs link = link.replace(/\/\?.+/,'/'); case url.pathname.endsWith('/'): // directory ext = ' dir'; kind = 'dir'; classes.push('dir'); break; case /file:/.test(link) && !/file:/.test($protocol): classes.push('disabled'); // disable local files on non-local pages case link.indexOf('pdf#') > -1: // remove pdf parameters link = link.replace(/pdf#.+/,'pdf'); default: // files ext = url.pathname.slice(url.pathname.lastIndexOf('.') + 1); kind = getItemKind(ext); switch(true) { case kind === 'audio': $('body').addClass('has_media has_audio'); classes.push('file','media',kind,ext); $body.removeClass('has_filelist').addClass('has_playlist'); break; case kind === 'video': $('body').addClass('has_media has_video'); classes.push('file','media',kind,ext); $body.removeClass('has_filelist').addClass('has_playlist'); break; case kind === 'image': $('body').addClass('has_images'); case kind === 'font': $('body').addClass('has_fonts'); default: classes.push('file',kind,ext); $body.removeClass('has_playlist').addClass('has_filelist'); } } let display_kind = kind.slice(0,1).toUpperCase() + kind.slice(1); if ( i !== 0 ) { // don't list first item twice preppedRow = ''+ title +''+ display_time +''+ display_kind +''; preppedIndex += preppedRow; } } return preppedIndex; } // Open Playlist/Filelist function openPlaylist(files,reader) { if ( !$body.hasClass('has_playlist') && !$body.hasClass('has_filelist') ) { // store original dir_list and body "has_" classes as data if body does not already have playlist or filelist let bodyClasses = document.getElementById('top').classList, dataClasses = []; for ( let bodyClass of bodyClasses.values() ) { if ( bodyClass.startsWith('has') ) { dataClasses.push( bodyClass ); } } // add original body classes to dataclasses $('#tbody').data('dir_list',$('#tbody').html() ).data('dataClasses',dataClasses); $body.removeClass(dataClasses.join(' ')); } closeContent(); // close all existing content, because it shouldn't be navigable or selectable with playlist loaded let preppedIndex = convertPlaylist(reader.result); // get the new index $('#tbody').empty().append(preppedIndex); // append the prepared playlist $('#top,#content_pane').removeClass('has_menu faded'); if ( !$('#sort_by_name').hasClass('selected' ) ) { $('#sort_by_name').click(); } // sort by name $('#stats_summary_playlist_files').empty().html('Playlist: '+ $('#tbody tr.media').length +' Files'); // update stats if ( /file:/.test(preppedIndex) && !/file:/.test($protocol) ) { // show warning about local files on non-local page $body.addClass('has_warning'); $('tbody').addClass('local'); openWarning('warning_local_playlist','warning_btn_ok'); } if ( $body.hasClass('autoload_media') ) { $('#tbody tr.media:not(.disabled)').first().click(); // select first item if ( $body.hasClass('has_images') ) { autoLoadCoverArt(); } } scrollThis('tbody','selected',false); document.title = 'Playlist: '+ files.name; $('#parents_dir_nav').find('> div').empty().html( files.name ); $('#open_playlist').val(''); } // Make and save playlist function makePlaylistLink(link) { link = link.replace(/\/\?.+/,'/').replace(/\.pdf\#.+/,'pdf'); switch(true) { // we need to handle various cases like this in case the link is being from a loaded playlist which might include links to other pages case link.startsWith('http'): // remote files case link.startsWith('file'): // local files link = link; break; case link.startsWith('/'): link = $protocol +'//' + link; break; case !link.startsWith('file'): link = $protocol +'//'+ window.location.host + window.location.pathname + link; break; } return link; } function makePlaylist() { let items = $('#tbody'), rows, playlist = [], playlistEntry = ''; let playlist_type = $('#make_playlist_form').find('input:checked').attr('id'); switch(playlist_type) { case 'media_files_only': rows = items.children('tr.media:not(.unchecked)'); break; case 'audio_files_only': rows = items.children('tr.audio:not(.unchecked)'); break; case 'video_files_only': rows = items.children('tr.video:not(.unchecked)'); break; case 'all_non_media_files': rows = items.children('tr:not(.media)'); break; case 'all_items': rows = items.children('tr'); break; case 'directories_only': rows = items.children('tr.dir'); break; case 'files_only': rows = items.children('tr.file'); break; } switch(true) { // show warning if no qualifying items found or make playlist case rows.length === 0: openWarning('warning_no_playlist','warning_btn_ok'); break; default: for ( let i = 0; i < rows.length; i++ ) { let row = $(rows[i]); let link = row.find('a').attr('href'); link = makePlaylistLink(link); let timing = ''; playlistEntry = '#EXTINF:'+ timing +','+ row.find('a').text() +'\n'+ link; playlist.push(playlistEntry); } playlist = '#EXTM3U\n'+ playlist.join('\n'); // add playlist header id saveFile(playlist,'audio/mpeg-url','untitled.m3u'); // show save file dialogue closeWarning(); } } // Select "Make Playlist" menu item: show make playlist warning list $('#make_playlist').on('click',function(e) { e.preventDefault(); e.stopPropagation(); closeMenus(); openWarning('warning_make_playlist','warning_btn_ok'); }); // Get playlist entry function makePlaylistEntry(id) { let title, link, duration = ''; switch(true) { case id === 'title': // make link for non-audio items title = $('#title span').text(); switch(true) { case $('.content.has_content').attr('id') === 'content_image': // link for images link = $('.content.has_content').find('img').attr('src'); break; case $('.content.has_content').attr('id') !== 'content_image': // all other content link = $('.content.has_content').attr('src'); if ($('.content.has_content').attr('id') === 'content_video') { duration = Number.parseInt(getElById('video')[0].duration); } break; } link = makePlaylistLink(link); $('#content_playlist').find('textarea').val('#EXTINF:'+ duration +','+ title +'\n'+ link +'\n').focus(); break; case id === 'content_audio_title': title = $('#content_audio_title span').text(); link = $('#audio').attr('src'); link = makePlaylistLink(link); duration = Number.parseInt(getElById('audio')[0].duration); $('#content_audio_playlist').find('textarea').val('#EXTINF:'+ duration +','+ title +'\n'+ link +'\n').focus(); break; } } // Show Playlist Entry function showPlaylistEntry(id) { makePlaylistEntry(id); switch(true) { case id === 'title': document.getElementById('content_playlist').classList.toggle('has_content'); selectTextareaContent('content_playlist_textarea'); break; case id === 'content_audio_title': document.getElementById('content_audio_playlist').classList.toggle('has_content'); selectTextareaContent('content_audio_playlist_textarea'); break; } } $('#title, #content_audio_title').on('click',function() { if ( $content_pane.attr('data-content') !== 'has_font_file' && $content_pane.attr('data-content') !== 'has_grid' ) { showPlaylistEntry($(this).attr('id')); } }); // Open File function openFile(e,type) { // type: font or playlist. if (window.File && window.FileReader && window.FileList && window.Blob) { let files = e.target.files[0]; let reader = new FileReader(); if ( type === 'font' ) { reader.readAsArrayBuffer(files); } if ( type === 'playlist' ) { reader.readAsText(files); } reader.onload = function() { if ( type === 'font' ) { openFontFile(files,reader); } if ( type === 'playlist' ) { openPlaylist(files,reader); } return true; }; } else { alert('Can\'t open file: file APIs are not fully supported in this browser.'); } } // Save File function saveFile(content,mimetype,filename) { let blob = new Blob([content], {type: mimetype}); let downloadEl = window.document.createElement('a'); downloadEl.href = window.URL.createObjectURL(blob); downloadEl.download = filename; document.body.appendChild(downloadEl); downloadEl.click(); document.body.removeChild(downloadEl); URL.revokeObjectURL(blob); } })(); // FINIS! + DEO GRATIAS + //