/* eslint-disable no-case-declarations, no-fallthrough, indent, no-mixed-spaces-and-tabs, no-multi-spaces, no-return-assign, no-useless-escape, quotes */ /* jshint esversion: 6 */ // ==UserScript== // @name Supercharged Local Directory File Browser // @version 8.1.4 // @description Makes directory index pages (either local or remote open directories) actually useful. Adds sidebar and content preview pane; keyboard navigation; sorting; light/dark UI; preview images/fonts in navigable grids; browse subdirectories w/o page reload (“tree view”); media playback, shuffle/loop options; basic playlist (m3u, extm3u) & cuesheet (.cue) support; create, edit, preview, save markdown/plain text files; open font files, view complete glyph repertoire, save glyphs as .svg; more. // @author gaspar_schot // @license GPL-3.0-or-later // @homepageURL https://openuserjs.org/scripts/gaspar_schot/Supercharged_Local_Directory_File_Browser // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAgMAAAC+UIlYAAAACVBMVEUmRcmZzP8zmf8pVcWPAAAAAXRSTlMAQObYZgAAAFBJREFUeF7tyqERwDAMBEE3mX5UiqDmqwwziTPHjG7xrmzrLFtRaApDIRiKQlMYCsFQFJrCUAiGotAU5hTA1WB4fhkMBsOJwWAwgHvB8CHpBcTbpxy4RZNvAAAAAElFTkSuQmCC // @match file://*/* // @match https://www.example.com/path/to/directory/* // @require https://cdn.jsdelivr.net/npm/markdown-it@13.0.1/dist/markdown-it.min.js // @require https://cdn.jsdelivr.net/npm/markdown-it-footnote@3.0.2/dist/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@4.0.2/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@1.3.4/dist/opentype.min.js // @require https://cdn.jsdelivr.net/npm/@gerhobbelt/markdown-it-checkbox@1.2.0-3/dist/markdownItCheckbox.umd.js // @namespace https://greasyfork.org/users/16170 // @downloadURL https://update.greasyfork.icu/scripts/38638/Supercharged%20Local%20Directory%20File%20Browser.user.js // @updateURL https://update.greasyfork.icu/scripts/38638/Supercharged%20Local%20Directory%20File%20Browser.meta.js // ==/UserScript== (function() { // ************ J + M + J ************* // 'use strict'; // ***** UI SETTINGS ***** // const UI_Prefs_Bool = { alternate_background:true, apps_as_dirs:true, autoload_index_files:true, iframe_theme:true, ignore_ignored_items:true, media_autoload:true, media_autoplay:true, media_play_all:true, show_details:true, show_ignored_items:true, show_image_thumbnails:true, show_large_image_thumbnails:true, show_invisibles:true, show_media_name_in_window_title:true, show_numbers:true, show_sidebar:true, text_editing_enable:true, texteditor_split_view:true, texteditor_sync_scroll:true, use_custom_icons:true, audio_player_on_top:true } const UI_Prefs_Non_Bool = { grid_font_size: 1, // Default = 1 grid_image_size: 184, // Default = 184 (200px - 16px) sort_by: 'default', // Choose from: 'name', 'size', 'date', 'kind', 'ext', 'default ( = Chrome sorting: dirs on top, files alphabetical). sort_direction: 'ascending', // Choose from: 'ascending' (A-Z) [default] or 'descending' (Z-A). texteditor_view: 'styled', // Options: 'raw','styled','html' theme: 'light', // Options: 'light' or 'dark' ui_font: 'system-ui, sans-serif', // Choose an installed font for the UI; if undefined, use browser defaults instead. [system-ui, sans-serif] ui_font_size: '13px', // Choose a default UI font size; use any standard CSS units. ui_scale: '100', } let UI_Settings = {...UI_Prefs_Bool, ...UI_Prefs_Non_Bool}; const Item_Kinds = { dir: ['/'], // loaded in iframe#content_iframe app: ['app/','app','appimage','apk','exe','ipa','ipk','jar','msi','wsf'], // generally ignored; some apps may be opened as directories alias: ['alias','desktop','directory','lnk','symlink','symlink/'], archive: ['7z','archive','b6z','bin','bzip','bz2','cbr','dmg','gz','iso','mpkg','pkg','rar','sit','sitx','tar','tar.gz','zip','zipx','zxp'], // ignored audio: ['aac','aif','aiff','ape','flac','m4a','mka','mp3','ogg','opus','wav'], // loaded in audio#audio bin: ['a','ase','bundle','dll','dyld','dylib','gem','icc','msi','profraw','pyc','pyo','o','rakefile','ri','so','torrent','xml','2','opml','qm','scpt','uo','vsix','zwc'], // ignored code: ['bak','bash','bash_profile','bashrc','bat','cgi','com','c','cfg','cnf','codes','coffee','conf','csh','cshrc','cson','css','cuetxt','custom_aliases','d','default','description','dist','editorconfig', 'emacs','example','gemspec','gitconfig','gitignore','gitignore_global','h','hd','ini','js','json','jsx','less','list','local','login','logout','lua','mkshrc','old','pc','php','pl','plist','pre-oh-my-zsh','profile','pth','py','rb','rc','rdoc','sass','settings','sh','strings','taskrc','tcl','viminfo','vimrc','vue','vtt','yaml','yml','zlogin','zlogout','zpreztorc','zprofile','zsh','zshenv','zshrc'], // treated as text, opened in iframe#content_iframe text editor database: ['accdb','db','dbf','mdb','pdb','sql', 'sqlite','sqlitedb','sqlite3'], // ignored ebook: ['azw','azw1','azw3','azw4','epub','ibook','kfx','mobi','tpz'], // ignored font: ['otf','ttf','woff','woff2','afm','pfb','pfm','tfm'], // opened in div#content_font graphics: ['afdesign','afpub','ai','book','dtp','eps','fm','icml','icns','idml','indb','indd','indt','inx','mif','pmd','pub','qxb','qxd','qxp','sla','swf','ai','arw','cr2','dng','eps','jpf','nef','psd','psd','raw', 'tif','tiff'], // ignored htm: ['htm','html','xhtm','xhtml'], // opened in iframe#content_iframe image: ['apng','avif','bmp','gif','ico','jpeg','jpg','png','svg','webp'], link: ['url','webloc','inetloc'], markdown: ['md','markdown','mdown','mkdn','mkd','mdwn','mdtxt','mdtext','mk'], // treated as text, opened in iframe#content_iframe text editor other_ignored: ['alias','cue','dat','dic','idx','xmp'], office: ['csv','doc','docx','key','numbers','odf','ods','odt','pages','rtf','scriv','wpd','wps','xlr','xls','xlsx','xlm'], // ignored playlist: ['m3u','m3u8','pls','asx','wpl','xspf'], pdf: ['pdf'], // open in #content_pdf system: ['DS_Store','ds_store','icon','ics','spotlight-v100/','temporaryitems/','documentrevisions-v100/','trashes/','fseventsd/','dbfseventsd','file','programdata','localized'], // ignored system items text: ['log','nfo','txt','text','readme'], // opened in iframe#content_iframe text editor video: ['m4v','mkv','mov','mp4','mpeg','webm'] // loaded in video#content_video }; const Item_Settings = { // ITEM_SETTINGS: Ignore or Exclude files by extension (prevents browser from attempting to download the file). ignored: [...Item_Kinds.archive,...Item_Kinds.bin,...Item_Kinds.database,...Item_Kinds.graphics,...Item_Kinds.other_ignored,...Item_Kinds.office,...Item_Kinds.playlist,...Item_Kinds.system] }; // ***** UTILITIES ***** // function loadFileURL() { // ===> LOAD FILE URL // if window.location points to a file, change the location to the file's container dir, add search_param of file name; then load the file's container dir and load file in content pane. let search_params = searchParamsGet(); search_params.set( 'file', window.location.pathname.split('/').reverse()[0]); window.location = window.location.pathname.slice( 0,window.location.pathname.lastIndexOf('/') ) +'/?'+ search_params ; return; } if ( !window.location.pathname.endsWith('/') && window.top === window.self ) { loadFileURL(); } // load file urls //==============================// function isTopWindow() { return ( window.top === window.self || false ) } // ===> TOP WINDOW OR IFRAME function getBrowser() { //*** needs testing for new userAgentData object --> what are possible brand names?; combine with getOS() // ===> GET BROWSER let brand = ( navigator.userAgentData !== undefined ? navigator.userAgentData.brands[1].brand.toLowerCase() : navigator.userAgent ); switch(true) { case brand === 'chromium' || ( /chrome?chromium/.test(brand) ): return 'is_chrome'; case brand === 'msie' || ( /msie/.test(brand) ): return 'is_explorer'; // case brand === 'edge' || ( /edge/.test(brand) ): return 'is_edge'; // need case for ms edge case brand === 'opera' || ( /opera/.test(brand) ): return 'is_opera'; case brand === 'safari' || ( /safari/.test(brand) ): return 'is_safari'; case brand === 'firefox' || ( !/chrome|chromium/.test(brand) ): return 'is_gecko'; } } function getOS() { // modded from https://***stackoverflow.com/questions/38241480/detect-macos-ios-windows-android-and-linux-os-with-js // ===> GET OS let platform = ( navigator.userAgentData !== undefined ? navigator.userAgentData.platform : window.navigator.platform ).toLowerCase(); let macos_platforms = ['macos','macintosh','macintel','macppc','mac68k'], windows_platforms = ['win32','win64','windows','wince'], os = null; switch(true) { case macos_platforms.indexOf(platform) !== -1: os = 'macos'; break; case windows_platforms.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; } function newURL(link) { try { return new URL(link,document.baseURI); } catch( error ) { return new URL(encodeURI(link) ); } } // ===> NEW URL function decodeURIComponentSafe(str) { if ( !str ) { return str; } // ===> DECODE URI COMPONENT SAFE; // Fix "%" error in file name; see stackoverflow.com/questions/7449588/why-does-decodeuricomponent-lock-up-my-browser try { return decodeURIComponent(str.replace(/%(?![0-9a-fA-F]{2})/g,'%25') ).replace(/\"/g,'\"'); } catch(e) { return str; } // replace % with %25 if not followed by two a-f/number; replace " with html entity } //function sanitizeReservedChars(str) { let chars = ['#','$','&','+',',','/',':',';','=','?','@','[',']']; } //function escapeStr(str) { str = str.replace(/([$?*+()[]|^])/g,'\\$1'); return str; } // ===> ESCAPE STRING function convertHex2Decimal(d) { return parseInt(d, 16); } function convertDecimal2Hex(d, padding) { let hex = Number(d).toString(16); hex = ( isNaN(hex) ? null : "000000".substr(0, padding - hex.length) + hex ); return hex; } //==============================// const window_protocol = window.location.protocol; // GLOBAL: protocol //const window_origin = window_protocol +'//'+ window.location.host; // GLOBAL: origin let window_location = decodeURIComponentSafe( [location.protocol, '//', location.host, location.pathname].join('') ); // GLOBAL: current location const current_dir_path = window_location.replace(/([/|_|—])/g,'$1').replace(/\\/g,'/'); // GLOBAL: current dir path w/o query string for display // const current_dir = window_location.split('/').slice(-2,-1).toString(); // GLOBAL: current dir //==============================// function setLocation(link) { window.location = link; } function changeLocation(args) { // args[0] === href, args[1] === 'external || ok' switch(true) { case args[1] === 'external': window.open(args[0]); break; // open external menu links: about, coffee, contact case args[1] === 'ok': window.location = args[0]; break; case ( /has_\w+list/.test(getClassNames('body'))): args = window.location.href; // nobreak; show playlist warning default: showWarning( 'setLocation',args.toString() ); } } function searchParamsGet() { let search_params = new URL(window.location)?.searchParams; search_params.sort(); return search_params; } // ===> GET SEARCH PARAMS function searchParamSet(key,value,bool) { let search_params = searchParamsGet(); search_params.set( key, value ); if ( bool !== false ) { updateSearchParams(search_params);} } // ===> SET SEARCH PARAM (bool false = don't update) function searchParamDelete(key) { let search_params = searchParamsGet(); search_params.delete(key); updateSearchParams(search_params); } // ===> REMOVE SEARCH PARAM function updateSearchParams(search_params) { // ===> UPDATE SEARCH PARAMS search_params = sanitizeSearchParams(search_params); search_params.sort(); // sort and sanitize params let search_params_str = search_params.toString().replace(/%2F/g,'').replace('/','').replace(/%2Cfalse/g,''); // further sanitization let new_location = ( search_params_str.length === 0 ? window.location.pathname : window.location.pathname +'?'+ search_params_str ); // don't add ? if no search params window.history.replaceState({}, document.title, new_location); // set new location if ( isTopWindow() ) { updateParentLinks(); } } function sanitizeSearchParams(search_params_str) { // remove search_params that are not in UI_Settings for ( let entry of search_params_str.entries() ) { if ( !UI_Settings[entry[0]] && !/selected|history|width/.test(entry[0]) ) { search_params_str.delete(entry[0]); } } return search_params_str; } function getCurrentUIPref(pref_id) { // ===> GET SEARCH PARAM value by key let search_params = searchParamsGet(), value = ''; switch(true) { case pref_id === 'width': if ( !isTopWindow() ) { return; } // width: set the stored sidebar width or use default 30% value = ( !search_params.has(pref_id) || window.innerWidth === 0 ? 30 : Math.round(100 * Number.parseInt(search_params.get('width'))/window.innerWidth) ); break; // percentage case pref_id === 'parent_id': value = ( search_params.has(pref_id) ? search_params.get(pref_id) : UI_Settings?.[pref_id] ? UI_Settings[pref_id]?.toString() : '' ); break; // default: // if query_string has key/value pair, use it, else use key/value pair from UI_Settings value = ( search_params.has(pref_id) ? search_params.get(pref_id) : UI_Settings?.[pref_id] ? UI_Settings[pref_id]?.toString() : pref_id )?.toString(); value = ( value?.replace('%2F','')?.replace('/','') || '' ); // some servers add a '/' to end of query string } return value; } function getNewUIPref(key) { let value, bool_prefs = Object.keys(UI_Prefs_Bool); let non_bool_prefs = { 'sort_direction_ascending': {'sort_direction':'descending'}, 'sort_direction_descending': {'sort_direction':'ascending'}, 'sort_by_name': {'sort_by':'name'}, 'sort_by_default': {'sort_by':'default'}, 'sort_by_duration': {'sort_by':'duration'}, 'sort_by_size': {'sort_by':'size'}, 'sort_by_date': {'sort_by':'date'}, 'sort_by_kind': {'sort_by':'kind'}, 'sort_by_ext': {'sort_by':'ext'}, 'texteditor_view': {'texteditor_view':getCurrentUIPref('texteditor_view')}, 'texteditor_view_raw': {'texteditor_view':'raw'}, 'texteditor_view_styled': {'texteditor_view':'styled'}, 'texteditor_view_html': {'texteditor_view':'html'}, 'theme': {'theme':(getCurrentUIPref('theme') === 'light' ? 'dark' : 'light') }, 'theme_dark': {'theme':'dark'}, 'theme_light': {'theme':'light'}, 'ui_font': {'ui_font':getCurrentUIPref('ui_font')},'ui_scale': {'ui_scale':getCurrentUIPref('ui_scale')} } switch(true) { case bool_prefs.includes(key): return ( getCurrentUIPref(key) === 'true' ? [key,'false'] : [key,'true']); // toggle bool prefs default: value = Object.values( non_bool_prefs[key] ).toString(); key = Object.keys(non_bool_prefs[key]).toString(); return [key,value]; // get value for key; then key (i.e., don't redefine key before getting value) } } let setWarningItemNotLoadedID; let str = ''; // global str var; function timeoutID() { return window.setTimeout( () => { str = ''; }, 1000 ); } // ===> TIMEOUT ID: reset typed string to '' after 1.5 sec. //==============================// function getEl(sel) { try { return document.querySelector(sel); } catch (error) { return null; } } function getEls(sel) { try { return document.querySelectorAll(sel); } catch (error) { return null; } } function elExists(sel) { return ( document.querySelector(sel) !== null ? true : false ); } function getVisibleElsBySelector(sel) { // ===> GET VISIBLE ELS BY SELECTOR // remove els with display:none or 0 width/height let els = Array.from( getEls(sel) ).filter( (el) => { let el_styles = window.getComputedStyle(el); return ( el_styles.getPropertyValue('display') !== 'none' || ( el?.offsetWidth > 0 || el?.offsetHeight > 0 ) ); }); return els; } function getContentPaneData() { return getAttr('#content_pane','data-content'); } // ===> GET CONTENT_PANE DATA content function hasContent(args) { // ===> HAS CONTENT? switch(true) { case args === undefined: return ( hasClass('#content_pane','has_audio') || getContentPaneData() !== 'has_null' ? true : false ); // has any content case args.includes('audio'): return ( hasClass('#content_pane','has_audio') && ( args.includes('ignore') || getContentPaneData() === 'has_null') ? true : false ); // has audio only or ignore other content case args.includes('_'): return hasClass('#content_pane','has_'+args); default: return ( args.split(',').includes( getContentPaneData()?.split('_')[1] ) ? true : false ); // has named content (e.g., pdf, iframe) } } //==============================// function getClassNames(sel) { return getEl(sel)?.className; } function hasClass(sel,classname) { let el = getEl(sel); return el?.classList.contains(classname); } // ===> HAS CLASS function addClass(sel,classname) { let els = Array.from(getEls(sel)), classes = classname.split(' ').filter(item => item); els?.forEach( el => el.classList.add(...classes) ); } // ===> ADD CLASS function removeClass(sel,classname) { // ===> REMOVE CLASS let els = Array.from(getEls(sel)), classes = ( classname?.split(' ')?.filter(item => item) || null ); if ( classname === undefined || classes === null ) { els?.forEach( el => el.removeAttribute('class') ); } else { els?.forEach( el => el.classList.remove(...classes) ); } // if no className, remove all classes } function addRemoveClassSiblings(sel,classname) { // ===> ADD/REMOVE CLASS SIBLINGS let el = document.querySelector(sel), siblings = el?.parentElement.children; if ( el !== null ) { Array.from(siblings).forEach( sibling => sibling.classList.remove(...classname.replace(/\s{2,}/g,' ').split(' ') ) ); addClass(sel,classname); } // remove class from els & add class to selected el } //==============================// function getAttr(sel,attributeName) { return getEl(sel)?.getAttribute(attributeName); } // ===> GET ATTRIBUTE function hasAttr(sel,attributeName) { return getEl(sel)?.hasAttribute(attributeName); } // ===> HAS ATTRIBUTE function setAttr(sel,attributeName,value) { getEl(sel)?.setAttribute(attributeName,value); } // ===> SET ATTRIBUTE function removeAttr(sel,attributeNamesArr) { // ===> REMOVE ATTRIBUTE if ( typeof attributeNamesArr === 'string' ) { attributeNamesArr = [attributeNamesArr]; } let els = getEls(sel); Array.from(els)?.forEach( el => attributeNamesArr.forEach( attributeName => el.removeAttribute(attributeName) ) ); } function getData(sel,keyname) { let el = ( typeof sel === 'string' ? getEl(sel) : sel ); return el?.getAttribute('data-'+keyname); } // ===> GET DATASET function setData(sel,keyname,value) { if ( elExists(sel) ) { setAttr(sel,'data-'+keyname, value); } } // ===> SET DATASET function deleteData(sel,keyname) { removeAttr(sel,'data-'+keyname); } // ===> REMOVE DATASET function setStyle(sel,property,value) { getEl(sel)?.style.setProperty(property,value); } // ===> SET STYLE function setValue(sel,value) { getEl(sel).value = value; } // ===> SET VALUE //==============================// function clickThis(sel) { let el = getEl(sel); ( el?.querySelector('a')?.click() || el?.click() ) } // ===> CLICK THIS by CSS selector // function dblclick(el,func) { var evt = new MouseEvent('dblclick'); el.addEventListener(evt,func); el.dispatchEvent(evt); } // ===> DOUBLE CLICK function altKey(e) { return ( !e.metaKey && !e.ctrlKey && e.altKey && !e.shiftKey ); } // ===> ALT KEY test function altShiftKey(e) { return ( !e.metaKey && !e.ctrlKey && e.altKey && e.shiftKey ); } // ===> ALT SHIFT KEY test function cmdKey(e) { return ( (e.metaKey || e.ctrlKey) && !e.altKey && !e.shiftKey ); } // ===> CMD/CTRL KEY test function cmdAltKey(e) { return ( (e.metaKey || e.ctrlKey) && e.altKey && !e.shiftKey ); } // ===> CMD/CTRL ALT KEY test function cmdShiftKey(e) { return ( (e.metaKey || e.ctrlKey) && !e.altKey && e.shiftKey ); } // ===> CMD/CTRL SHIFT KEY test // function cmdAltShiftKey(e) { return ( (e.metaKey || e.ctrlKey) && e.altKey && e.shiftKey; } // ===> CMD/CTRL ALT SHIFT KEY test (not used) function eStopPrevent(e) { e?.preventDefault(); e?.stopPropagation(); } //============================// // ***** BASIC UI FUNCTIONS ***** // function scrollThis(container_ID,sel,bool) { // ===> SCROLL to Selected Item let container = getEl(container_ID); const isInViewport = (sel) => { const rect = ( getEl(sel) !== null ? getEl(sel).getBoundingClientRect() : null ); if ( rect === null ) { return false; } return ( rect.top >= getEl('#sidebar_header').offsetHeight && rect.bottom <= (window.innerHeight - getEl('#sidebar_footer').offsetHeight || document.documentElement.clientHeight - getEl('#sidebar_footer').offsetHeight) ); } if ( container?.height === 0 || isInViewport(sel) ) { return; } // don't scroll hidden elements let scroll_el = container?.querySelector(sel), scroll_behavior = ( ( bool !== undefined || bool === true ) ? 'instant' : 'smooth' ), scroll_block = ( hasClass('body','is_gecko') ? 'start' : 'center' ); scroll_el?.scrollIntoView({ behavior:scroll_behavior, block:scroll_block, inline:'nearest' }); } function mouseMove(e,sel,startX,startY,elOffsetLeft,elOffsetTop) { // ===> Init events to allow glyphs to be dragged into view let scale_factor = ( sel === '#font_specimen_glyph' ? 2 : 1 ); // scale_factor needed for svg glyphs setStyle(sel,'left',elOffsetLeft + (e.pageX - startX)*scale_factor + 'px'); setStyle(sel,'top',elOffsetTop + (e.pageY - startY)*scale_factor +'px'); } //==============================// OPEN/SAVE FILES function openFile(args) { menuClose(); // ===> OPEN FILE; type: font or playlist. if ( window.File && window.FileReader && window.FileList && window.Blob ) { // if browser supports file API... let files = args[0].target.files[0], id = args[1], reader = new FileReader(); switch(id) { case 'open_font': reader.readAsArrayBuffer(files); break; case 'open_playlist': reader.readAsText(files); break; } // get the file reader reader.onload = () => { // on file reader load switch(true) { case id === 'open_font': openFontFile(files,reader); break; case id === 'open_playlist': openPlaylist(files.name,'',reader.result); break; } // open the file return true; } getEl('#'+id).value = ''; // reset input to allow same item to be reopened immediately after closing } else { alert('Can\'t open file: file APIs are not fully supported in this browser.'); } // else error } function saveFile(content,mimetype,file_name) { // ===> SAVE FILE let blob = new Blob([content], {type: mimetype}); let download_el = window.document.createElement('a'); download_el.style = "display:none"; download_el.href = window.URL.createObjectURL(blob); download_el.download = file_name; // define & style download_el document.body.appendChild(download_el); download_el.click(); document.body.removeChild(download_el); URL.revokeObjectURL(blob); // add download_el, click, & remove } // END UTILITIES //==============================// // ***** SET UP UI ELEMENTS ***** // function updateParentLinkSearchParams(str) { //*** decrement selected and history values ***// // ===> UPDATE PARENT LINK SEARCH PARAMS let query_str = new URLSearchParams(str); query_str.sort(); // make new search params from window.location.search let history = ( query_str.has('history') ? query_str.get('history') : undefined ); switch(true) { case history !== undefined: history = history.split(' '); switch(true) { case history.length > 1: query_str.set('selected',history[0]); history.shift(); query_str.set('history',history.join('+')); break; case history.length === 1: query_str.set('selected',history[0]); history.shift(); query_str.delete('history'); break; } break; default: query_str.delete('selected'); } return decodeURIComponentSafe(query_str.toString()); } function createParentLinks() { // ===> CREATE PARENT LINKS let link, links = [], search_params = searchParamsGet(); search_params.sort(); let query_str = search_params.toString(); let link_pieces = window_location.split('/'); link_pieces = link_pieces.slice(2,-2); // make array of parent directories; remove beginning and ending empty elements and current directory while ( link_pieces.length > 0 ) { // while there are link pieces... query_str = updateParentLinkSearchParams(query_str); // update selected and history link = window_protocol +'//'+ link_pieces.join('/') + '/?' + query_str; links.push(link); link_pieces.pop(); // assemble link; add to link array; remove last link piece and repeat... } return links; } function createParentLinkItems() { // ===> CREATE PARENT LINK ITEMS let parent_link_menu_items = [], links = createParentLinks(); for ( let i = 0; i < links.length; i++ ) { let display_name = links[i].split('/?')[0]; display_name = display_name.replace(/\//g,'\/'); let menu_item = `
  • ${ display_name }/
  • `; parent_link_menu_items.push(menu_item); } let parent_link = ( links[0] === undefined ? window.location.href : links[0]); parent_link = parent_link.replace(/parents_link_/,'parent_link_'); return [parent_link_menu_items.join(''),parent_link]; // return parents link items } function updateParentLinks() { // ===> UPDATE PARENT LINKS and init new item events let links = createParentLinkItems(); getEl('#parents_links').innerHTML = links[0]; getEl('#parent_dir_nav a').href = links[1]; // add the links getEls('#sidebar_menu_parent a,#parents_links a').forEach( el => el.onclick = function(e) { eStopPrevent(e); showWarning('changeLocation',[this.href,'false']); }); // reinit onclick } //==============================// // SVG UI ICONS const SVG_UI_Icons = { 'arrow': '', 'bookmark': '', 'check_mark': '', 'chevron': '', 'document': '', 'error': '', 'external_link': '', 'folder': '', 'grid': '', 'menu': '', 'minus': '', 'multiply': '', 'music': '', 'plus': '', 'prev_next_track': '', 'spinner': '', 'toggle': '', 'ui_layout': '' }; function get_SVG_UI_Icon(icon_name) { return `url("data:image/svg+xml;utf8,${ SVG_UI_Icons[icon_name] }")`; } const SVG_UI_File_Icons = { // n.b.: order is important 'file_icon_dir': '', 'file_icon_dir_open': '', '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_playlist': '', 'file_icon_text': '', 'file_icon_video': '', 'file_icon_bin': '', 'file_icon_other': '', // <-- these two use file_icon_system: 'file_icon_system': '' }; const SVG_Text_Editing_UI_Icons = { 'toggle_theme': '', 'text_editing': '', 'show_markdown': '', 'show_source': '', 'show_preview': '', 'show_html': '', 'toggle_split': '', 'save_btn': '', 'save_btn_edited': '' }; // ===> GET SVG UI ICON by name function get_SVG_UI_File_Icon(icon_name) { // ===> GET SVG UI FILE Icon by name switch(icon_name) { case 'favicon': return ''; case 'file_icon_dir_default': 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= ")'; case 'file_icon_file_default': return 'url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAAABnRSTlMAAAAAAABupgeRAAABHUlEQVR42o2RMW7DIBiF3498iHRJD5JKHurL+CRVBp+i2T16tTynF2gO0KSb5ZrBBl4HHDBuK/WXACH4eO9/CAAAbdvijzLGNE1TVZXfZuHg6XCAQESAZXbOKaXO57eiKG6ft9PrKQIkCQqFoIiQFBGlFIB5nvM8t9aOX2Nd18oDzjnPgCDpn/BH4zh2XZdlWVmWiUK4IgCBoFMUz9eP6zRN75cLgEQhcmTQIbl72O0f9865qLAAsURAAgKBJKEtgLXWvyjLuFsThCSstb8rBCaAQhDYWgIZ7myM+TUBjDHrHlZcbMYYk34cN0YSLcgS+wL0fe9TXDMbY33fR2AYBvyQ8L0Gk8MwREBrTfKe4TpTzwhArXWi8HI84h/1DfwI5mhxJamFAAAAAElFTkSuQmCC ")'; default: return 'url("data:image/svg+xml;utf8,'+ SVG_UI_File_Icons[icon_name] +'")'; } } function CSS_UI_Icon_Rules() { // programatically add File icon CSS rules // ===> CSS UI ICON RULES let rules = '', kind, class_name; for ( let icon in SVG_UI_File_Icons ) { kind = icon.slice(icon.lastIndexOf('_') + 1); class_name = kind; if ( class_name !== ('file') ) { // exceptions: if ( kind === 'dirinvisible' ) { class_name = 'dir.invisible'; } if ( kind === 'ignoredimage' ) { class_name = 'ignored_image'; } if ( kind === 'open' ) { class_name = 'has_subdirectory'; kind = 'dir_open'; } if ( /alias|symlink/.test(kind) ) { class_name = 'link'; } if ( /bin|other/.test(kind) ) { kind = 'system'; } // add rules for dir_list items, content_header, stats details: rules += `body:not(.use_custom_icons_false) .${ class_name } .has_icon_before_before, #content_pane[data-content^="has_${ class_name }"] #content_title span::before,body:not(.use_custom_icons_false) .${ class_name }.has_icon_before::before, .${ class_name } .has_icon_before::before { background-image: url("data:image/svg+xml;utf8,${ SVG_UI_File_Icons['file_icon_'+kind] }"); }`; // add custom file icons } } return rules; } // END SVG UI ICONS //==============================// UI HTML // SIDEBAR ELEMENTS function Sidebar_Elements(body_id,parent_link) { // Assemble directory elements for both top and iframe directories const sidebar_header_menu_elements = `
  • Go to item… (⌘⇧J)
  • UI Preferences${SVG_UI_Icons.arrow}
  • File Handling Preferences${SVG_UI_Icons.arrow}
  • Media Preferences${SVG_UI_Icons.arrow}
  • Text Editing Preferences${SVG_UI_Icons.arrow}
  • Playlists${SVG_UI_Icons.arrow}
  • `; const sidebar_header_elements = function(body_id,parent_link) { let parent_links = createParentLinkItems(), sidebar_header_title_element = '', sidebar_header_menus ='', sidebar_texteditor_element =''; let checked = ( getCurrentUIPref('show_invisibles') === 'true' ? 'checked="true"' : '' ); const sidebar_header_utilities_row_1 = ``; const sidebar_header_utilities_row_2 = ``; switch(body_id) { case 'top_body': sidebar_header_title_element = ``; sidebar_header_menus = ``; sidebar_texteditor_element = ``; break; case 'iframe': sidebar_header_menus = ``; break; } return ``; } let sidebar_footer_utilities = '', sidebar_utilities = ''; if ( body_id === 'top_body' ) { // various elements not needed in iframe directories sidebar_footer_utilities = ``; sidebar_utilities = ` `; } const sidebar_nav = ``; const sidebar_footer = ``; return ``; } //==============================// // CONTENT PANE ELEMENTS function Content_Pane_Elements(id) { const content_audio_elements = `
    ${ SVG_UI_Icons.prev_next_track }
    ${ SVG_UI_Icons.prev_next_track }
    ${ SVG_UI_Icons.multiply }
    `; const text_editing_ui_elements = `
    `; const content_text_elements = `
    ${ text_editing_ui_elements }
    `; const content_font_toolbar = `
    1. Text Color
    2. Text Stroke Color
    `; const content_font_sample_string = `ABCDEFGHIJKLMNOPQRSTUVWXYZ
    abcdefghijklmnopqrstuvwxyz
    0123456789
    !"#$%&'()*+,-./:;<=>?@[\\]^_\`{|}~`; const content_font_lorem_string = `Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.`; const content_font_viewer = '
      '; const content_font_elements = `
        ${ content_font_sample_string }

        Typography

        The art of using types to produce impressions on paper, vellum, &c.

        S P E C I M E N

        Typography is the work of typesetters (also known as compositors), typographers, graphic designers, art directors, manga artists, comic book artists, graffiti artists, and, now, anyone who arranges words, letters, numbers, and symbols for publication, display, or distribution.

        ${ content_font_lorem_string }
        ${ content_font_lorem_string }
        ${ content_font_lorem_string }
        ${ content_font_viewer }`; const content_header_elements = `
        ${ content_audio_elements }
        ${ content_font_toolbar }
        `; switch(true) { // ASSEMBLE CONTENT ELEMENTS case id === 'content_font_viewer': return content_font_viewer; case id === 'content_text_elements': return content_text_elements; default: return `
        ${ content_header_elements }
        ${ SVG_UI_Icons.spinner }
          ${ content_text_elements }
          ${ content_font_elements }
          `; } } //==============================// // UTILITIES HTML (warnings and help) function Utilities_Elements(body_id) { let utilities_warning_elements = `

          Warning:

          Make Playlist/Filelist (.m3u)

          `; let utilities_help_elements = `
          HELP

          I. ABOUT THIS SCRIPT

          Script home: openuserjs.org
          GENERAL INFORMATION
          This script works on local directories, as well as many remote server-generated index pages or “open directories”.
          By default, userscripts do not run on local file:/// urls, so for this script to work on local directories you will need to enable it in your browser’s extension settings (e.g.: For Tampermonkey in Chrome, open the Chrome extensions page, click the details button for Tampermonkey and check ‘Allow access to file URLs’).
          To make the script work on a remote open directory, you must add its URL to the list of allowed sites in the settings for this userscript, as provided by your userscript manager.
          Because server configurations vary, the script may not work perfectly (or at all) on some open directories. You may also need to allow—or block—javascript on some ODs, and/or allow cookies. Please let me know if you encounter any problems.
          This script was developed in the latest version of Vivaldi, running on the latest MacOS. It has been minimally tested in other Chrome-based browsers, Safari, and Firefox, and has been not been tested in any other browsers or OSes. No effort has been made to ensure compatibility with older browsers. Please report any issues.
          ${ SVG_UI_Icons.ui_layout }The UI consists of two main parts:
          (1) the directory list SIDEBAR on the left and
          (2) the CONTENT PANE on the right.
          The Sidebar shows all the items in the current directory, while the Content Pane shows a preview of items selected in the Sidebar.
          1. The SIDEBAR comprises a HEADER, the DIRECTORY LIST itself, and a FOOTER.
          The Sidebar is resizeable; it can be hidden completely by clicking the double-chevron icon at the Sidebar top right or typing ⌘\.
          1A. The SIDEBAR HEADER contains a Parent Directory button, a Parent Directories menu which displays separate links for all the parent directories, and the Main Menu.
          Below these are Show Details and Show Invisibles items, a Show Grid button (when appropriate), and sort by Name or Default items.
          If Show Details is selected, additional sorting options are shown, along with the Text Editor item.
          All of these items are also available in the Main Menu, and some can be toggled via keyboard shortcuts (see below).
          1B. The DIRECTORY LIST displays the items in the current directory.
          Directory items can be selected with the arrow keys or by clicking.
          Selecting an item will preview it in the content pane.
          Multiple directories, fonts, or images can be selected with shift+arrowkey, cmd+click, or shift+click.
          Directories can be previewed in the content pane or toggled open in the sidebar to create a “tree view” of the directory by clicking the folder icon or typing Cmd→.
          1C. The SIDEBAR FOOTER displays Stats for the items in the current directory.
          Detailed stats can be shown by clicking the footer.
          There is also a popup menu on the right of the footer with options to display the Sidebar directory or the raw directory index in the Content Pane.
          2. The CONTENT PANE displays the selected sidebar item.
          The content pane can be focused by tabbing from the sidebar or clicking. Links in HTML files can be navigated via the tab key.
          Clicking the title of the content pane title reveals an EXTM3U-formatted playlist item for use in an EXTM3U file.
          Previewed Content
          Previewed Directories
          Previewed directories in the Content Pane inherit the sorting and other UI preferences from the Sidebar directory list. They can be navigated independently from the Sidebar via the “Parent Directory” link in the header or Cmd-Up Arrow.
          An item in the content pane header allows previewed directories to be opened into the sidebar.
          A selected item can be previewed by pressing the spacebar. This is similar to the “quicklook” function in MacOS.
          Double-clicking a selected directory list item or typing Cmd-Down Arrow will open it in the content pane, replacing the previewed directory. Closing the item via the Close Button or Cmd-W will restore the original previewed directory.

          II. KEYBOARD SHORTCUTS

          III. USAGE

          ${ SVG_UI_Icons.menu } MAIN MENU (⌘+E)
          The Main Menu contains the following top-level items:

          1. Go to Item...
          Select a sidebar item by its row number (displayed if "Show Numbers" pref is set). Useful especially for large directories. Directory list can also be navigated by typed strings.

          2. SORT BY... ${SVG_UI_Icons.arrow}
          Sort directory items by Name, Size, Date, Kind, Extension, Duration (media items only). Default sort = sort items by Name, with directories on top.

          Clicking the sort preference again will reverse the sort.
          Sorting preferences are also available in the Sidebar Header.
          Note that many server configurations don’t report directory size, so when sorting by size, directories will be on top, sorted by name.

          3. UI PREFERENCES ${SVG_UI_Icons.arrow}
          Selecting these preferences items will add the new setting to the URL query string, so that the setting will persist as you navigate within the same window, or if you bookmark the page.

          a. Light Theme/Dark Theme
          Change the UI theme.
          b. Alternate Backgrounds
          Alternate background colors for directory list items.
          c. Show Numbers
          Show numbers for directory list items.
          d. Use Custom Icons
          If enabled, use custom file and directory icons provided by the script; if disabled, use the browser’s default icons.
          e. Show Image Thumbnails
          Replace image file icons with a thumbnail icon of the image itself. Enabling this setting may slow down page load, because each image file in the directory must be downloaded.
          f. Use Large Image Thumbnails
          If “Show Image Thumbnails” is also enabled, a larger version of the image thumbnail is displayed.
          g. Always Show Image Thumbnails
          Image thumbnails are always shown, no matter how many images are in the directory. This overrides the default behavior which automatically disables thumbnail display for directories containing more than 2000 items in order to improve performance.
          h. Audio Player at Top
          Disable this item in order to position the audio player at the bottom of the content pane.
          i. Set UI Font
          Set a custom font for the UI.
          j. Scale UI
          Scale the entire UI (50%–150%). Double-click the label to quickly reset the scale to 100%.

          4. FILE HANDLING PREFERENCES ${SVG_UI_Icons.arrow}

          a. Show/Hide Invisible Items
          It does what it says on the tin…
          b. Show/Hide Ignored Items
          Ignored items include files that the browser cannot handle natively (e.g., common Office and graphics files, various binary files, etc.). Hide them to reduce clutter.
          c. Ignore Ignored Items
          Prevent normal browser behavior for handling such files, which is to open a download file dialog.
          d. Autoload Index Files
          Automatically load “index.html” or similar files in the Content Pane when the directory loads.

          5. MEDIA PREFERENCES ${SVG_UI_Icons.arrow}

          a. Autoload Media
          If enabled, automatically load the first media file (audio or video) when the directory loads.
          For audio files, this will also automatically load any “cover art” (image file) found in the same directory. The script will first look for an image file with exactly the same name as the currently selected/playing audio file, followed in order by files containing the words “cover”, “front”, “album”, “jacket”, “sleeve”, “cd”, “disc”, “insert”, “liner”, or “notes.” If it finds no matching files, it will load the first image file it finds. Cover art will be automatically loaded whenever a new audio file is selected for playback.
          b. Autoplay Media
          If enabled, play the next media file when the currently playing media file ends.
          c. Play All Media Files
          If disabled (and Autoplay Media enabled), only play media of the same type (audio or video) as the currently playing media file.
          d. Loop Media Playback
          Loop media playback to the first media item when the last media item ends and continue playing. This option can also be enabled from the audio player.
          e. Shuffle Media Playback
          Randomize the order of media playback. This option can also be enabled from the audio player.
          A media item selected via the up/down arrow keys will be played after the currently playing item. This give you the option to play a specific item while continuing with shuffle play.

          6. TEXT EDITING PREFERENCES ${SVG_UI_Icons.arrow}

          a. Text Editing Enabled/Disabled
          If text editing is disabled, text files are displayed as normal files.
          b. Text Editing Options
          Toggle the Text Editor. Select Editor UI theme: Default = same as main UI. Toggle split view. Select view of raw text, preview text, rendered HTML.
          7. DEFAULT PREFERENCES
          Resets UI to defaults by removing manually set preferences from the URL query string.

          8. PLAYLISTS ${SVG_UI_Icons.arrow}

          a. Open Playlist/Filelist…
          Click to load a local .m3u file. See below for more details.
          b. Make Playlist/Filelist…
          Make an .m3u playlist/filelist of the items in the current sidebar directory, with option to include audio only, video only, all media, all non-media, all items, directories or files only.
          9. OPEN FONT FILE…
          Load a local font file to view its complete glyph repertoire in a grid and information about the font.
          Note that this function is different from previewing a font from the sidebar.
          Font file glyph grids can be navigated with the arrow keys. Individual glyphs can be selected by clicking them or pressing Return.
          Individual glyphs may be saved as .svg files.
          10. SCRIPT HOME
          openuserjs.org.
          11. HELP
          Show this help page.
          12. BUY ME A COFFEE
          13. CONTACT
          Email me about anything to do with the script.

          IV. OTHER SCRIPT FUNCTIONS

          NAVIGATION
          Use the up and down arrow keys to navigate items in the sidebar and previewed directories in the content pane.
          Use the left and right arrow keys to navigate items of the same kind as the currently selected item.
          Use the Tab key to toggle the focus between the sidebar and the content pane.
          Type a letter or letters to navigate the directory by file name.
          Type ⌘↓ to navigate to the selected directory.
          Click a directory icon in the sidebar or select it and type ⌘→ to open subdirectory; to close, click the icon again or type ⌘←.
          IMAGES, FONTS, and FONT GLYPHS
          These previewed items can be scaled with ⌘+/– keys.
          FONT and IMAGE GRIDS
          If a directory contains fonts and/or image files, the “Show Grid” icon will appear in the sidebar. Click it or type ⌘G to show a grid of the available items.
          Grids can be navigated with the arrow keys, and individual grid items may be viewed by clicking them or pressing Return.
          When a grid item is being viewed, the grid can still be navigated with the arrow keys.
          Closing a selected grid item will show the grid again.
          PLAYLISTS AND FILELISTS (m3u)
          The script supports basic .m3u playlists containing links to audio or video files, but it also has custom support for “filelists,” which are standard .m3u files that contain links to any type of file or directory.
          Playlist files can be opened via the menu item.
          However, for ease of use, if you change the extension of an ordinary .m3u file to .txt, the script will read it normally as an editable text file. Double-clicking such a file in the sidebar or typing ⌘↓ or ⌘+Return will open it as a playlist/filelist. Please note that the text file must begin with “#EXTM3U&sdquo; for this work.
          CUE SHEETS (cue)
          When a media file (audio or video) is loaded, the script will look for a .cue file in the same directory with EXACTLY the same name as the media file.
          If it finds one, it will load the Track ID, the PERFORMER, the TITLE, and the INDEX (time position) into a menu next to the audio player; there is no support for other commands.
          Tracks can be selected by clicking the item, and played or paused by clicking the selected item.
          .cue files can also be selected independently in the sidebar and edited and saved (locally). This may be handy for creating “on the fly” bookmarks for a long media track before closing the page.
          Note that you can also create and save (locally) a new .cue file by using the Text Editor.
          Note (MacOS): If you prefer not to clutter the sidebar with .cue files, you may make them invisible by adding a dot to beginning of the file name; the script will still find them.

          V. TROUBLESHOOTING

          The script doesn’t work with a specific directory.
          If you have a javascript blocker installed in your browser (and if you don’t, you should), try disabling some of the site-specific scripts and XHR requests, as they may be interfering with the execution of this script.
          Alternately, if you do have a javascript blocker installed, you may need to allow some scripts and XHR requests instead.
          If the open directory still does not display correctly, check to see if cookies from the site are blocked.
          Try deleting preferences from the Main Menu or removing the query string from the URL in the browser.
          A specific item in a directory does not display correctly.
          Confirm that the file is one that browser is capable of rendering. This script cannot display files that the browser itself cannot display.
          If the item is from a playlist (m3u) and links to a remote site (e.g., archive.org), check your javascript blocker and cookies for any that need to be allowed from that site.
          If you think you have found a bug, please contact me.

           

          `; let help_elements = ''; if ( body_id === 'top_body' ) { help_elements = ``; } return `
          ${ help_elements }
          `; } // ===> END UI HTML //==============================// // ===> STYLES const background_images = ` .menu_item::before { content:""; width:12px; max-width:12px; min-width:12px; height:9px; margin:2px 0 -2px; background-position:center; background-repeat:no-repeat; display:flex; } .submenu .menu_item::before { width:24px; max-width:24px; min-width:24px; } .has_background, .has_background_before::before, .has_background_after::after { background-repeat:no-repeat; background-position:center; background-color:transparent !important; } .bookmark > a::before { background-image:${ get_SVG_UI_Icon("bookmark") }; } :is(.sort_by_default #menu_sort_by_default, .sort_by_name #menu_sort_by_name, .sort_by_duration #menu_sort_by_duration, .sort_by_size #menu_sort_by_size, .sort_by_date #menu_sort_by_date, .sort_by_kind #menu_sort_by_kind, .sort_by_ext #menu_sort_by_ext, #menu_theme_container, #toggle_text_editing) .menu_item::before, :is(.sort_by_default #sort_by_default, .sort_by_name #sort_by_name, .sort_by_duration #sort_by_duration, .sort_by_size #sort_by_size, .sort_by_date #sort_by_date, .sort_by_kind #sort_by_kind, .sort_by_ext #sort_by_ext) span::before, .loop_media #loop_media_menu::before, .shuffle_media #shuffle_media_menu::before, .background_color_check_mark::before, .texteditor_view_raw #toggle_texteditor_raw::before, .texteditor_view_styled #toggle_texteditor_preview::before, .texteditor_view_html #toggle_texteditor_html::before, body:not(.text_editing_enable_false) #text_editing_enable::before, .texteditor_view_html #texteditor_view_html::before, .cuesheet_track.selected .cue_track_id::before, .menu_item.checkmark::before { background-image:${ get_SVG_UI_Icon("check_mark") }; } :is( .show_invisibles_false #show_invisible_items, .alternate_background_false #alternate_background, .show_numbers_false #show_numbers, .use_custom_icons_false #use_custom_icons, .show_image_thumbnails_false #show_image_thumbnails, .show_large_image_thumbnails_false #show_large_image_thumbnails, .show_ignored_items_false #show_ignored_items, .ignore_ignored_items_false #ignore_ignored_items, .autoload_index_files_false #autoload_index_files, .media_autoload_false #media_autoload, .media_autoplay_false #media_autoplay, .media_play_all_false #media_play_all, .texteditor_split_view_false:not(.has_texteditor) #texteditor_split_view, .show_media_name_in_window_title_false #show_media_name_in_window_title, .audio_player_on_top_false #audio_player_on_top, .iframe_theme_false #iframe_theme) .menu_item.checkmark::before { background-image:none; } .sort_by_default #sort_by_default span::after, .sort_by_name #sort_by_name span::after, .sort_by_duration #sort_by_duration span::after, .sort_by_size #sort_by_size span::after, .sort_by_date #sort_by_date span::after, .sort_by_kind #sort_by_kind span::after, .sort_by_ext #sort_by_ext span::after { background-image:${ get_SVG_UI_Icon("chevron") }; background-size:75%; transform:rotate(180deg); } .is_error #sidebar_nav, .is_error #current_dir_path span::before { background-image:${ get_SVG_UI_Icon("error") }; } .is_error #sidebar_nav { background-repeat:no-repeat; background-position:center top 6rem; background-size:6rem;} .is_error #current_dir_path span::before { float:none; display:inline-flex; margin:0 0 -2px 0; width:24px; } #content_pane[data-content="has_ignored"] #content_container { background-image:${ get_SVG_UI_File_Icon('file_icon_ignored') }; background-size:28px; } #content_pane.has_audio[data-content="has_null"]:not([data-loaded="unloaded"]) #content_container, #content_pane.has_audio:not([data-content]) #content_container, .has_audio #content_pane[data-content="has_null"]:not([data-loaded="unloaded"]) #content_container, .has_audio #content_pane:not([data-content])[data-loaded="loaded"] #content_container { background-image:${ get_SVG_UI_Icon("music") }; } ${ CSS_UI_Icon_Rules() } #sidebar_menu_main ul a::before { background-image:${ get_SVG_UI_File_Icon('file_icon_file') }; } #sidebar_menu_main ul a[href^="file"]::before, #current_dir_path span::before { background-image:${ get_SVG_UI_File_Icon('file_icon_dir') }; margin-bottom:-3px; } #sidebar_menu_main ul a[href^="http"]::before { background-image:${ get_SVG_UI_File_Icon('file_icon_htm') }; } body.use_custom_icons_false .dir .has_icon_before_before { background-image:${ get_SVG_UI_File_Icon('file_icon_dir_default') }; background-size:auto 13px; } body.use_custom_icons_false.show_image_thumbnails_false .file:not(.app) .has_icon_before_before, body.use_custom_icons_false:not(.show_image_thumbnails_false) .file:not(.app) .has_icon_before_before { background-image:${ get_SVG_UI_File_Icon('file_icon_file_default') }; background-size:auto 13px; } body:not(.use_custom_icons_false).show_image_thumbnails_false .image .has_icon_before_before { background-image:${ get_SVG_UI_File_Icon('file_icon_image') } } .has_playlist #current_dir_path span::before { background-image:${get_SVG_UI_File_Icon('file_icon_playlist')}; display:inline-flex; margin:-2px 0 0; width:24px; vertical-align:middle;} .dirlist_item.dir:not(.has_subdirectory) .has_icon_before_before:hover { background-image:${ get_SVG_UI_Icon('chevron') }; transform:rotate(90deg); filter:invert(1); } .dirlist_item.dir.has_subdirectory .has_icon_before_before:hover { background-image:${ get_SVG_UI_Icon('chevron') }; transform:rotate(180deg); filter:invert(1); } .dirlist_item.non_local .name_span span::before { background-image:${ get_SVG_UI_Icon('external_link') }; content:""; width:20px; min-width:20px; height:14px; margin-top:-3px; margin-bottom:-3px; background-position:left center; background-repeat:no-repeat; background-blend-mode:screen; display:inline-block; } .dirlist_item:is(.dir,.other,.system,.bin,.invisible,.markdown):is(.selected,:hover) a .has_icon_before_before { filter:brightness(var(--brightness_low)); } .theme_light .dirlist_item.audio:is(.selected,:hover) a .has_icon_before_before { mix-blend-mode: hard-light; filter:brightness(0.75) contrast(2) saturate(2.66); } .theme_dark .dirlist_item.audio:is(.selected,:hover) a .has_icon_before_before, .dirlist_item.content_loaded.non_local .name_span span::before { filter:brightness(1.33); } .dirlist_item.selected:is(.archive,.app) a .has_icon_before_before, .dirlist_item:is(.archive,.app):hover a .has_icon_before_before { filter:brightness(var(--brightness_high)) saturate(6); } .dirlist_item.non_local:is(.selected,.audio_loaded) .name_span span::before, body:not(.no_hover) .dirlist_item.non_local:hover .name_span span::before { filter:brightness(2); } .dirlist_subdir_loading .has_icon_before_before { background-image:${ get_SVG_UI_Icon('spinner') } !important; filter:invert(1); background-size:20px; } `; const global_styles = ` .theme_light { --percent_100:100%; --percent_95:95%; --percent_90:90%; --percent_85:85%; --percent_80:80%; --percent_75:75%; --percent_70:70%; --percent_65:65%; --percent_60:60%; --percent_55:55%; --percent_50:50%; --percent_45:45%; --percent_40:40%; --percent_35:35%; --percent_30:30%; --percent_25:25%; --percent_20:20%; --percent_15:15%; --percent_10:10%; --percent_05:05%; --percent_00:00%; --border_lum:40%; --border_lum_inverted:40%; --brightness_low:1.15; --brightness_med:1.33; --brightness_high:1.875; --non_media_item_background_h:216deg; --non_media_item_background_s:100%; --non_media_item_background_l:50%; --non_media_item_background_a:0.8; --media_item_background_h:180deg; --media_item_background_s:100%; --media_item_background_l:33%; --media_item_background_a:1; --texteditor_item_background_h:250deg; --texteditor_item_background_s:66%; --texteditor_item_background_l:66%; --texteditor_item_background_a:1.00; } .theme_dark { --percent_100:00%; --percent_95:05%; --percent_90:10%; --percent_85:15%; --percent_80:20%; --percent_75:25%; --percent_70:30%; --percent_65:35%; --percent_60:40%; --percent_55:45%; --percent_50:50%; --percent_45:55%; --percent_40:60%; --percent_35:65%; --percent_30:70%; --percent_25:75%; --percent_20:80%; --percent_15:85%; --percent_10:90%; --percent_05:95%; --percent_00:100%; --border_lum:05%; --border_lum_inverted:40%; --brightness_low:1.15; --brightness_med:1.5; --brightness_high:1.66; --non_media_item_background_h:216deg; --non_media_item_background_s:80%; --non_media_item_background_l:60%; --non_media_item_background_a:0.8; --media_item_background_h:180deg; --media_item_background_s:50%; --media_item_background_l:40%; --media_item_background_a:1; --texteditor_item_background_h:250deg; --texteditor_item_background_s:50%; --texteditor_item_background_l:60%; --texteditor_item_background_a:1.00; } li, div { --non_media_background: hsla(var(--non_media_item_background_h), var(--non_media_item_background_s), var(--non_media_item_background_l), var(--non_media_item_background_a)); --media_background: hsla(var(--media_item_background_h), var(--media_item_background_s), var(--media_item_background_l), var(--media_item_background_a)); --texteditor_item_background: hsla(var(--texteditor_item_background_h), var(--texteditor_item_background_s), var(--texteditor_item_background_l), var(--texteditor_item_background_a)); } :root { --font_size_small:0.875rem; --font_size_smaller:0.75rem; color-scheme:none; } :root, html, body { margin:0; padding:0; border:0; border-radius:0; overflow:hidden; display:flex; flex-direction:row; width:100%; height:100vh; font-family:${UI_Prefs_Non_Bool.ui_font}; font-size:${ UI_Prefs_Non_Bool.ui_font_size}; hyphens:auto; transform-origin:0 0; } a, a:hover { color:inherit; font-weight:inherit; text-decoration:none !important; } ul, li { list-style:none; } svg { margin:auto; } button, .warning_button { background-color:hsl(0,0%,95%); border:solid 1px #333; border-radius:3px; height:18px; font-size:0.875em; font-family:${UI_Prefs_Non_Bool.ui_font} !important; cursor:pointer; } button.focus, button:focus, .warning_button { border-radius:3px !important; border-style:solid !important; border-width:1px !important; border-color:#222 !important; } .selected, .audio_loaded { --background_opacity:1; } :hover, .hovered { --background_opacity:0.75; } .focus_content #sidebar .selected, .focus_content #sidebar .audio_loaded { --background_opacity:0.50; opacity:1; } .focus_content #sidebar :hover, .focus_content #sidebar .hovered { --background_opacity:0.25; } .align_left { text-align:left; } .align_center { text-align:center; } .align_right { text-align:right; } .align_justify { text-align:justify; text-justify:inter-character; hyphens:auto; } .background_grey_60 { background-color:hsl(0,0%,var(--percent_60)); } .background_grey_65 { background-color:hsl(0,0%,var(--percent_65)); } .background_grey_70 { background-color:hsl(0,0%,var(--percent_70)); } .background_grey_75, body { background-color:hsl(0,0%,var(--percent_75)); } .background_grey_80 { background-color:hsl(0,0%,var(--percent_80)); } .background_grey_85, .dirlist_item:nth-of-type(odd), .cuesheet_track:not(.header):nth-of-type(odd), #utilities_help li:nth-of-type(even), body.alternate_background_false .dirlist_item:nth-of-type(even) { background-color:hsl(0,0%,var(--percent_85)); } .background_grey_90, .dirlist_item:nth-of-type(even), .cuesheet_track:not(.header):nth-of-type(even), #stats li:nth-of-type(even) { background-color:hsl(0,0%,var(--percent_90)); } .background_grey_95, .background_grey_90:not(.no_hover):hover, .background_grey_90.hovered, .background_grey_90.selected, .background_grey_90:focus { background-color:hsl(0,0%,var(--percent_95)); } .background_grey_100, .background_grey_95:not(.no_hover):hover, .background_grey_95.hovered, .background_grey_95.selected { background-color:hsl(0,0%,var(--percent_100)); } ${ background_images } .border_0 { border: none; } .border_all { border: solid 1px hsl(0,0%,var(--border_lum)); } .border_top { border-top: solid 1px hsl(0,0%,var(--border_lum)); } .border_right { border-right: solid 1px hsl(0,0%,var(--border_lum)); } .border_bottom { border-bottom: solid 1px hsl(0,0%,var(--border_lum)); } .border_left { border-left: solid 1px hsl(0,0%,var(--border_lum)); } .border_top_x { border-top: solid 1px hsl(0,0%,var(--border_lum_inverted)); } .border_right_x { border-right: solid 1px hsl(0,0%,var(--border_lum_inverted)); } /* "x" = inverted for theme_dark */ .border_bottom_x { border-bottom: solid 1px hsl(0,0%,var(--border_lum_inverted)); } .border_left_x { border-left: solid 1px hsl(0,0%,var(--border_lum_inverted)); } .box_shadow_menu { box-shadow:0px 4px 6px -3px #000; } .display_grid, .info_list:hover li, .has_flyout_menu:hover ul { display:grid; } .display_none, .error_display_none { display:none; } .display_block { display:block; } .display_flex { display:flex; } .display_inline_flex { display:inline-flex; } .flex_column { flex-direction:column; } flex-direction:column; } .flex_row { flex-direction:row; } .flex_justify_center { display:flex; flex-direction:column; justify-content:center; flex-grow:1; align-items:center; align-self:stretch; text-align:center; } .flex_justify_center_row { display:flex; flex-direction:row; justify-content:center; flex-grow:1; align-items:center; } .flex_justify_contents { justify-content:space-between; } .flex_grow_1 { flex-grow:1; } .font_size_small { font-size:var(--font_size_small); } .has_flyout_menu { outline:none; justify-content:center; align-content:center; } .has_flyout_menu ul li { width:100%; margin:0; padding:4px 6px; text-align:right; box-sizing:border-box; white-space:pre; grid-column:1; } .has_flyout_menu ul div { grid-column:2; padding-top:8px; } .has_flyout_menu .item_1 { grid-row:1; } .has_flyout_menu .item_2 { grid-row:2; } .height_100 { height:100%; } .hyphens_none { hyphens:none; } .info_list { color:hsl(0,0%,var(--percent_10)); } .info_list li { grid-template-columns:minmax(33%,100%) min(66%); border-top:solid 1px hsl(0,0%,var(--border_lum)); } .info_list li.info_list_header { border-top:none; } .info_list li .col_1 { font-weight:bold; text-align:right; border-right:solid 1px hsl(0,0%,var(--border_lum)); } .info_list span { display:inline-block; padding:4px 6px; } .line_height_1 { line-height:1; } .line_height_1_2, .info_list span { line-height:1.2; } .line_height_1_4 { line-height:1.4; } .margin_0, header, footer, nav, ol, ul, li { margin:0; } .media.local input { cursor:not-allowed; } .normal { font-weight:normal; } .outline_none, .outline_none:focus, .outline_none:focus-visible { outline:none; } .overflow_auto { overflow:auto; } .overflow_hidden { overflow:hidden; } .overflow_visible { overflow:visible; } .overflow_x_hidden { overflow-x:hidden; } .padding_0, header, footer, nav, a, ol, ul, li { padding:0; } .padding_4_6 { padding:4px 6px; } .padding_4_8 { padding:4px 8px; } .padding_6_8 { padding:6px 8px; } .padding_top_1rem { padding-top:1rem; } .padding_bottom_1rem { padding-bottom:1rem; } .pointer, label, input { cursor:pointer; } .cursor_default { cursor:default; } div:has(> input[disabled]), input[disabled], input[disabled] + label { cursor:not-allowed; } .position_absolute { position:absolute; } .position_relative { position:relative; } .position_fixed { position:fixed; } .position_0 { top:0; right:0; bottom:0; left:0; } .position_LR_0 { left:0; right:0; } .resize_none { resize:none; } .theme_dark .invert { filter:invert(1); } .transform_rotate_90, .transform_rotate_90_contents > * { transform:rotate(90deg); } .transform_rotate_180, .transform_rotate_180_contents > * { transform:rotate(180deg); } .transform_rotate_270, .transform_rotate_270_contents > * { transform:rotate(270deg); } .user_select_none { -webkit-user-select:none; -moz-user-select:none; user-select:none; } .whitespace_pre { white-space:pre; } .width_10px, .width_10px_contents > * { width:10px; max-width:10px; min-width:10px; } .width_12px, .width_12px_contents > * { width:12px; max-width:12px; min-width:12px; } .width_14px, .width_14px_contents > * { width:14px; max-width:14px; min-width:14px; } .width_16px, .width_16px_contents > * { width:16px; max-width:16px; min-width:16px; } .width_18px, .width_18px_contents > * { width:18px; max-width:18px; min-width:18px; } .width_24px, .width_24px_contents > * { width:24px; max-width:24px; min-width:24px; } .width_100 { width:100% !important; } .z_index_1 { z-index:1; } .z_index_2 { z-index:2; } .z_index_3 { z-index:3; } .z_index_9997 { z-index:9997; } .z_index_9998 { z-index:9998; } .z_index_9999 { z-index:9999; } /* NON-MEDIA ITEMS BACKGROUND */ li.selected:not(.media), li:not(.media):not(.no_highlight):hover, li.hovered, li.content_loaded { background-color:var(--non_media_background) !important; } /* all selected items, hovered non-dirlist items */ li.grid_item:hover, li.grid_item.hovered { --non_media_item_background_a:0.40; background-color:var(--non_media_background); } /* hovered grid items */ #sidebar_nav li:not(.media):hover { --non_media_item_background_a:0.5; background-color:var(--non_media_background); } /* hovered dirlist items */ li.grid_item.selected { --non_media_item_background_a:0.75; background-color:var(--non_media_background); } /* selected grid items */ body.no_hover li.grid_item:is(:not(.selected):hover,.hovered), body.no_hover li.grid_item:is(:not(.selected):hover,.hovered) * { background-color:transparent !important; color:initial !important; } li.selected + li.selected:nth-of-type(even), li.dir.hovered + li.hovered:nth-of-type(even), li.file.hovered + li.hovered:nth-of-type(odd), .info_list li:nth-of-type(even) { --non_media_item_background_a:0.60; } /* alternate highlight background with multiple selections, show stats*/ /* MEDIA ITEMS BACKGROUND */ li.media[class*="loaded"] { --media_item_background_a:0.9; background-color:var(--media_background) !important; } /* loaded audio, selected video */ li.media.selected:not([class*="loaded"]) { --media_item_background_a:0.7; background-color:var(--media_background) !important; } /* selected audio */ li.media:hover { --media_item_background_a:0.5; background-color:var(--media_background) !important; } /* hovered media */ /* UNHIGHLIGHTED ITEMS: menu visible, .focus_content, .no_hover */ body[class*="has_menu"] #sidebar_nav, body.focus_content #sidebar_nav { --non_media_item_background_s:0%; --media_item_background_s:0%; } body.theme_light[class*="has_menu"] #sidebar_nav, body.focus_content #sidebar_nav { --media_item_background_l:50%; --non_media_item_background_l:50%; } body.theme_dark[class*="has_menu"] #sidebar_nav, body.focus_content #sidebar_nav { --media_item_background_l:40%; --non_media_item_background_l:30%; } body.no_hover #sidebar_menus li:not(.selected):not(.hovered):hover { background-color:inherit !important; color:unset !important; } body.no_hover #sidebar_nav li:nth-of-type(even):not(.selected):hover { background-color:hsl(0,0%,var(--percent_80)) !important; } body.no_hover #sidebar_nav li:nth-of-type(odd):not(.selected):hover { background-color:hsl(0,0%,var(--percent_85)) !important; } body.no_hover #sidebar_nav li:not(.selected):hover { color:unset !important; } /* TEXTEDITOR ITEMS*/ body:is(.has_texteditor,.texteditor_edited) #show_texteditor li { background-color:var(--texteditor_item_background); } #show_texteditor li:hover { --texteditor_item_background_a:0.7; background-color:var(--texteditor_item_background) !important; } /* TEXT COLOR */ li:where(.selected,:hover,.hovered), li:where(.hovered,:hover) li:is(.selected:hover,.selected), .content_loaded, .audio_loaded, body.has_texteditor #show_texteditor, body.texteditor_edited #show_texteditor, .no_hover .grid_item:is(.selected,:hover,.hovered), .no_hover .grid_item:is(.selected,:hover,.hovered) *, .grid_item:is(.selected,:hover,.hovered), .grid_item:is(.selected,:hover,.hovered) *, .grid_item.selected::before, .grid_item.selected::after { color:white !important; } /* white */ .text_color_default, body.no_hover.theme_dark li.grid_item:is(:not(.selected):hover,.hovered), body.no_hover.theme_dark li.grid_item:is(:not(.selected):hover,.hovered) *, li:is(.selected,:hover,.hovered) li:not(.selected), body.no_hover #sidebar_menus li:is(.selected,:hoever,.hovered) li:not(.selected):hover, .no_highlight, .no_highlight:hover, .no_highlight > li:hover, :hover:not(#svg_container) > svg, .font_glyph_item::before,.font_glyph_item::after,#font_specimen_viewer::before,.has_font_specimen_glyph #font_specimen_viewer::after,#font_file_glyph_viewer::before,#font_file_glyph_viewer::after { color:hsl(0,0%,var(--percent_10)) !important; } /* default */ /* FONT WEIGHT */ li:is(.selected,:hover,.hovered) li, body.no_hover li:hover, li:not(.grid_item).no_hover:hover, body.no_hover #sidebar_menus li.hovered li:not(.selected):hover, li:is(.selected,:hover,.hovered) li:not(.selected), body.no_hover #sidebar_menus li:is(.selected,:hoever,.hovered) li:not(.selected):hover { font-weight:normal !important; } .bold, li:not(.grid_item):hover,li.hovered,li.hovered li:is(:hover,.selected),li:not(.grid_item):not(.audio).selected,li.selected li:hover, li.media[class*="_loaded"], #show_image_grid:hover, #show_font_grid:hover,dt { font-weight:bold !important; } #content_pane[data-content="has_ignored"]::before { opacity:0.3; } .has_warning #sidebar, .has_warning #content_pane, .focus_content .dirlist_item, body:is(.has_menu,.has_menu_parents,.has_menu_stats,.has_menu_grid.has_images.has_fonts) .dirlist_item:not(.hovered), body:is(.has_menu,.has_menu_parents,.has_menu_stats,.has_menu_grid.has_images.has_fonts) #content_pane, #sidebar_menu_parent:not(:hover), #sidebar_menu_main_container:not(:hover) nav, #show_grid_btn, .split_btn span, .disabled:not(.local) { opacity:0.75; } body.has_menu_footer .dirlist_item.hovered, .disabled:not(.local).selected { opacity:0.84; } #show_grid_btn:hover, #prev_next_btns span:hover, #sidebar_footer_utilities:hover, .split_btn span:hover { opacity:1.0; } `; const utilities_styles = ` #utilities { top:0; justify-content:center; } #warnings_container { width:26em; flex-direction:column; border-radius:0 0 3px 3px; box-shadow:0px 2px 12px #333; font-size:0.875em; color:#111; display:none; } #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:${ get_SVG_UI_Icon("error") }; } #warnings_header h3 { text-indent:2.25em; } #warnings_container:not(.warning_make_playlist) h3#warning_header, #warnings_container.warning_make_playlist h3#make_playlist_header, .warning_button.show, #warnings div.show, .has_warning #utilities, .has_warning #warnings_container, .has_help #utilities, .has_help #help_container, .warning_open_font #warnings #warning_open_font, .warning_unsaved_text #warnings #warning_unsaved_text, .warning_clear_text #warnings #warning_clear_text, .warning_local_file #warnings #warning_local_file, .warning_close_playlist #warnings #warning_close_playlist, .warning_local_playlist #warnings #warning_local_playlist, .warning_non_local_file #warnings #warning_non_local_file, .warning_close_font #warnings #warning_close_font { display:flex;} #warnings .warning { padding:0 1.5rem 1rem; display:none; } #warning_buttons_container { padding:1rem 1.5rem; } button.focus, button:focus { background-color:#0E4399; color:#EEE; outline:none; } .warning_button { min-width:4em; display:none; font-size:1em; justify-content:center; } #warning_btn_cancel + #warning_btn_save, #warning_btn_dont_save + #warning_btn_cancel, #warning_btn_clear { margin-right:auto; } #warning_btn_cancel, #warning_btn_clear, #warning_btn_save { margin-left:0.5rem; } #warnings_container.clear #warning_buttons { justify-content:space-between; } #warning_make_playlist fieldset div { padding:0 0 2px; } #warning_make_playlist .indent { text-indent:2em; } #warning_make_playlist input { margin-right:6px; } .has_warning::before, .has_overlay::before { content:""; position:fixed; top:0; right:0; bottom:0; left:0; z-index:9998;-webkit-user-select:none;-moz-user-select:none; user-select:none; } #help_container { padding:0 1em 1em; overflow:auto; flex-direction:column; } #help_container > header { grid-template-columns:5em auto fit-content(100%); } #help_contents { margin: 1em -1em 0; padding:1em; } #help_container dt { color:var(--media_background); } #help_container h2 { color: var(--non_media_background); } #help_container dd { margin-inline-start:1em; } #help_container dd:before { content:"\u2219"; margin-right:6px; } #help_container dl + p { margin-top:1rem; padding-top:1rem; } #help_container ol li { list-style:decimal; } #help_container kbd { min-width:1em; height:fit-content; padding:2px 6px; display:inline-block; border:solid 1px #888; border-radius:3px; text-align:center; font-family:inherit; font-size:0.875em; background-color:hsl(0,0%,var(--percent_90)); } #help_main_menu svg { margin: 0 0 -2px; width:12px; } #help_container svg.icon_arrow { height:14px; margin-bottom:-2px; } #help_bookmarks::before { background-image: ${ get_SVG_UI_Icon("bookmark") }; } .has_help #utilities, .has_help #help_container { bottom:0; } `; const sidebar_header_menu_styles = ` /* PARENTS MENU */ #parent_dir_nav #svg_chevron { width:18px; } #parent_dir_nav #svg_multiply { width:14px; } #parent_dir_nav #svg_multiply, body:is(.has_playlist,.has_filelist) #svg_chevron, body:is(:not(.has_playlist),:not(.has_filelist)) #sidebar_header #close_playlist_container { display:none; } body.has_playlist #parent_dir_nav #svg_multiply, body.has_filelist #parent_dir_nav #svg_multiply, body:is(.has_playlist,.has_filelist) #sidebar_header #close_playlist_container { display:flex; } #current_dir_path { padding:3px 6px; word-break:break-word; } /* MAIN MENU */ #sidebar_menu_main li { display:flex; } .has_menu #sidebar_menu_main, .has_menu_parents #parents_links, body:not(.no_hover) #sidebar_menus .has_submenu:hover .submenu, #sidebar_menu_main .has_submenu.hovered .submenu, #sidebar_menu_main li.has_submenu.selected .submenu { display:block; } #sidebar_menu_main li.has_submenu { position:relative; justify-content:space-between; } #sidebar_menu_main li.bookmark a::before { content:""; width:24px; max-width:24px; min-width:24px; height:12px; background-size:12px; } .submenu { width:100%; max-width:240px; display:none; margin:0; padding:0; box-sizing:border-box; position:absolute; left:100%; } #sidebar_menu_main ul.submenu li a { padding:6px 8px 6px 0; } #sidebar_menu_main input { width:0; float:left; } .menu_item { margin:0; padding:5px 8px 5px 0; display:flex; flex-grow:1; text-align:left; } #sidebar_menu_main .selected ~ li:hover .submenu, #sidebar_menu_main .selected ~ li .submenu:hover, .has_open, #sidebar_menu_main .show_input span.menu_item, .text_editing_enable_false:not(.has_texteditor) #sidebar_menu_main li#texteditor_split_view, .text_editing_enable_false:not(.has_texteditor) #toggle_texteditor_html_menu { display:none; } #sidebar_menu_main .show_input input { display:unset; margin:2px 6px; width:100%; } /* menu right arrow */ #sidebar_menu_main svg { margin: 0 6px; width:12px; } #sidebar_menu_main li:is(.selected,.hovered:not(:hover),:hover) svg { filter:invert(1); } body.no_hover:not(.theme_dark) #sidebar_menu_main li:not(.selected):not(.hovered):hover svg { filter:invert(0); } #ui_scale span.menu_item::after { content:attr(data-value); } #ui_scale_input_container, #ui_scale.show_input span + span { display:flex; } #ui_scale_input_container { padding-left:18px; padding-right:8px; } /* IFRAME MENUS */ #parent { padding:5px 3px 5px 0; } #parent span { padding:0px 1px; } #open_in_sidebar { padding:5px 2px 5px 3px; } `; const sidebar_header_styles = ` /* for both sidebar and content_iframe */ #sidebar { font-variant-numeric:tabular-nums; } #sidebar.top_body { min-width:200px; } #sidebar.iframe { min-width:500px; flex-basis:100%; } #sidebar_header_title_div { letter-spacing:0.5em; text-indent:0.75em; flex-basis:100%; } #sidebar_header_title_div:before { content:"INDEX OF"; } .has_playlist #sidebar_header_title_div:before { content:"PLAYLIST"; } .has_filelist #sidebar_header_title_div:before { content:"FILELIST"; } ${ sidebar_header_menu_styles } /* SIDEBAR BUTTONS */ #directory_buttons_left { padding:6px; } #show_details { margin-top:0; margin-right:8px; padding:0 4px; } #show::before { content:"Show "; } /* GRID BTN ---> combine style with save_btn */ #show_grid_btn { margin:0 0 0 auto; } #show_grid_btn ul { padding-left:0px; top:-1px; right:-1px; } #show_grid_btn ul:after { content:""; position:absolute; z-index:-1; top:0; bottom:0; left:0; right:0; background-color:hsl(0,0%,var(--percent_80)); } #show_grid_btn ul:not(:has(li:hover)):hover { background-color:var(--non_media_background); } #show_grid_btn ul:not(:has(li:hover)):hover svg { color:white !important; } #show_grid_btn.has_grid div { color:#118888; } #top_body:is(.has_images,.has_fonts) #show_grid_btn { display:flex; } #top_body.has_images.has_fonts #show_grid_btn:hover ul { display:grid; } /* SORTING ITEMS */ #sidebar_header_utilities_row_2 .sorting { grid-row:1; } body:not(.show_details_false) #sorting_row_2 { display:grid; } #sorting_row_1 span, #sorting_row_2 span, .sorting span::before, .sorting span::after { display:inline-block; } #sorting_row_1 span, #sorting_row_2 span { padding:6px 0; } #sorting_row_1.iframe span, #sorting_row_2.iframe span { padding:4px 0; } #sidebar_header_utilities_row_2 span::before, #sidebar_header_utilities_row_2 span::after, .sorting .menu_item::after { content:""; width:16px; height:8px; color:#CCC; background-position:center; background-repeat:no-repeat; background-size:10px; } .sorting.down span::after, .sort_direction_descending .sorting span::after { transform:rotate(0deg) !important; } #sort_by_name input { margin:-2px 6px -2px 0; bottom:-2px; } #sort_by_ext { grid-column: span 2; } .has_media #sort_by_ext { grid-column: span 1; } .has_media #sort_by_default { text-align:center; } #sort_by_default.iframe, .iframe #sort_by_size, .iframe #sort_by_date { text-align:right; } .has_media #sort_by_duration, .has_playlist #sort_by_duration, #content_body.has_media #sorting_row_2 #sort_by_duration { display:unset; } #content_body #sorting_row_1 #sort_by_duration, #top_body #sorting_row_2 #sort_by_duration { display:none; } /* TEXT EDITOR ITEM */ body:not(.show_details_false) #show_texteditor, body.has_texteditor #show_texteditor { display:flex; } #show_texteditor a { padding-left:10px; } `; const sidebar_nav_styles = ` /* for both sidebar and content dirlists */ #sidebar_nav { overflow-y:hidden; flex-basis:100%; margin-bottom:-1px; } #dir_nav_inner { overflow:auto; } #sidebar_nav ol { -webkit-margin-before:0em !important; -webkit-margin-after:0em !important; -webkit-padding-start:0em; } #directory_list { counter-reset:item; transition:opacity .125s; } #directory_list:empty { border-bottom:0; padding:100%; } .dirlist_item_input, .dirlist_item_details, .dirlist_item_details span, .dirlist_item_media_duration, .details.ext, .dirlist_item.error::before, .dirlist_item_name_a::before, body.show_invisibles_false:not(.has_menu_stats) .dirlist_item.invisible.ignored, body.show_invisibles_false:not(.show_ignored_false):not(.has_menu_stats) .dirlist_item.invisible:not(.ignored), body.show_invisibles_false.show_ignored_items_false:not(.has_menu_stats) .dirlist_item.ignored, body.show_invisibles_false.show_ignored_items_false:not(.has_menu_stats) .dirlist_item.invisible, body.show_ignored_items_false:not(.show_invisibles_false):not(.has_menu_stats) .dirlist_item.ignored:not(.invisible) { display:none; } .dirlist_item { margin-inline-start:0; display:grid; grid-gap:0; } .top_item { grid-template-columns:minmax(8rem,auto) minmax(6em,1fr) minmax(auto,6em); } .dirlist_item_name { grid-row:1; display:flex; -webkit-padding-start:0; -moz-padding-start:0; word-break:break-word; } .top_item .dirlist_item_name { grid-column:1 / span 3; padding:6px 12px 6px 0; } .dirlist_item_name::before { counter-increment:item; content:counter(item); min-width:36px; height:14px; max-height:14px; min-height:14px; text-align:right; padding:0 3px 0 0; text-indent:6px; } .dirlist_item_input { margin:1px 6px 0 0; max-height:13px; } .dirlist_item .desc.dirlist_item_details { padding:0 6px 4px 40px; grid-column:1 / span 3; text-align:left; white-space:unset; } .has_icon_before::before, .has_icon_before_before, .has_icon_after::after { content:""; display:inline-block; background-position:center; background-repeat:no-repeat; background-size:14px,0px; } .has_icon_before::before, .has_icon_before_before, .show_large_image_thumbnails_false .dirlist_item.image .has_icon_before_before, .show_image_thumbnails_false .dirlist_item.image .has_icon_before_before, .has_icon_after:not([data-after])::after { width:14px; height:14px; max-width:20px; max-height:14px; min-width:14px; min-height:14px; margin:0 6px; } .dirlist_item.image .has_icon_before_before { width:56px; height:56px; max-width:56px; max-height:56px; min-width:56px; min-height:56px; margin:0 6px; background-position:top; background-size:contain,0px; } .ignored:not(.selected) .has_icon_before::before, .ignored:not(.selected) .has_icon_before_before, .focus_content .has_icon_before_before, .has_quicklook #sidebar { filter:grayscale(100%); } .focus_content li:is(.selected,.hovered,:hover) .has_icon_before_before { filter:grayscale(0%); } /* MEDIA ITEMS */ .top_item.media .dirlist_item_name { grid-column:1 / span 2; padding-right:0; } .top_item.media .dirlist_item_media_duration { grid-column:3; padding:6px 12px 6px 0; } .dirlist_item.media .dirlist_item_media_duration { grid-row:1; } .iframe_item.media .dirlist_item_media_duration { grid-column:2; } .media:not(.local) .dirlist_item_media_duration { display:unset; } .dirlist_item.media .dirlist_item_media_duration:not([data-duration="NaN"]):empty { background-image:${ get_SVG_UI_Icon('spinner') } !important; background-position:top 3px right 10px; background-repeat:no-repeat; background-size:20px; } .dirlist_item_media_duration[data-duration="NaN"]::after { content:"[Error]"; } /* SORTING BORDERS */ .sort_by_default:not(.show_invisibles_false) .dir.invisible + .dir:not(.invisible), .sort_by_default:not(.show_invisibles_false) .dir:not(.invisible) + .dir.invisible {border-top:solid 1px hsl(0,0%,var(--border_lum));} /* ITEM DETAILS */ .dirlist_item_details { text-align:right; white-space:nowrap; } .top_item .dirlist_item_details { padding:0 12px 4px 0; } .dirlist_item_details.size { padding-left:12px; } .dirlist_item_details.date { overflow:hidden; height:calc(1em - 2px); } .dirlist_item_details.kind::first-letter { text-transform:uppercase; } #content_body .iframe_item { grid-template-columns: minmax(20em,100%) minmax(4em,6em) minmax(6em,8em) minmax(6em,14em) minmax(6em,8em); } #content_body .iframe_item.non_media .dirlist_item_name { grid-column:1 / span 2; } #content_body .iframe_item .dirlist_item_details { grid-row:1; overflow:hidden; height:calc(1em - 2px); } #content_body .iframe_item .desc.dirlist_item_details { grid-row:2; grid-column:1 / span 6; height:auto; padding:0 6px 4px 40px; } #content_body .iframe_item .dirlist_item_name_a, #content_body .iframe_item > span { padding:5px 16px 5px 0; } body:not(.show_numbers_false) .dirlist_item_name_a::before { display:initial; } body:not(.show_details_false) .dirlist_item_details, .media .dirlist_item_input { display:unset; } .disabled, .ignore_ignored_items li.ignored, .has_filelist [id$="sort_by_size"], .has_playlist [id$="sort_by_size"], .has_filelist [id$="sort_by_date"], .has_playlist [id$="sort_by_date"] { cursor:not-allowed; opacity:0.75; } .dirlist_item.error { display:block; padding:6px 8px; } .dirlist_item.ignored.local .dirlist_item_name_a::after { content:"\\00a0[local file]"; display:contents; font-style:italic; } .is_error #is_error { display:block !important; grid:none !important; grid-template-columns:none !important; } .is_error #is_error_items { display:block; } `; const iframe_dir_styles = `${ global_styles } #content_body { overflow-x:auto; font-size:${ (parseFloat(UI_Prefs_Non_Bool.ui_font_size) * 0.875) + UI_Prefs_Non_Bool.ui_font_size.replace(/\d*/,'') }; } .theme_dark .sorting span::before, .theme_dark .sorting span::after { filter:invert(1); } #content_body:not(.show_details_false) #show::before { content:"Hide "; } #content_body.show_details_false .iframe_item { grid-template-columns:auto; } #content_body.show_details_false .media .dirlist_item_media_duration, #content_body #content_pane, #content_body .content_el, #content_body.has_quicklook div[id^="title_buttons"], #content_body.has_quicklook #content_pane.has_audio :is(#content_title_container,#content_container), #content_body.has_quicklook #content_pane[data-content="has_ignored"] #content_iframe, #content_body.has_quicklook #content_pane[data-content="has_pdf"] #content_pdf { display:none; } #content_body.has_quicklook #sidebar_nav { opacity:0.6; } #content_body.has_quicklook #content_pane { display:flex; padding:2em; position:absolute; z-index:1; left:0; right:0; top:0; bottom:0; justify-content:center; } #content_body.has_quicklook #content_header { background-color:hsl(0,0%,var(--percent_85)); border-radius: 3px 3px 0 0; border:solid 1px hsl(0,0%,var(--border_lum)); border-bottom:0; } #content_body.has_quicklook #content_container { padding:6px; background-color:hsl(0,0%,var(--percent_85)); box-shadow:0 0 12px #000; border-radius: 0 0 3px 3px; border:solid 1px hsl(0,0%,var(--border_lum)); border-top:0; contain:unset; flex-basis:unset; } #content_body.has_quicklook .content_el { width:100%; } #content_body.has_quicklook .content_el.has_content { border:solid 1px hsl(0,0%,var(--border_lum)); box-sizing:border-box; } #content_body.has_quicklook .content_el.has_content, #content_body.has_quicklook .content_el:has(.has_content), #content_body.has_quicklook #content_pane.has_audio #content_audio_container { display:flex; z-index:1; } #content_body.has_quicklook #content_pane.has_audio #content_header { margin:auto; } #content_body.has_quicklook #content_pane.has_audio #audio_wrapper { padding:0 6px; } #content_body.has_quicklook #content_pane.has_audio #content_audio_container { padding:0; } #content_body.has_quicklook #content_pane.has_audio #audio_options { display:none; } #content_body.has_quicklook #content_container:has(#content_font.has_content, #content_pdf.has_content,#content_iframe.has_content),#content_body.has_quicklook #content_pane[data-content="has_ignored"] #content_container { height:50%; flex-basis:unset; } #content_body.has_quicklook #content_container:has(#content_image.has_content) { display:table; flex-basis:unset; } #content_body.has_quicklook #content_video.has_content { position:static; } #content_body.has_quicklook #content_pane:not([data-content="has_ignored"]) #content_font.has_content { display:grid; } #content_body.has_quicklook #content_pane[class*="has_font_specimen"] #font_specimen_viewer { display:flex; } #content_body.has_quicklook #content_image_container, #content_body.has_quicklook #content_iframe { max-height:88vh; } #content_header, #content_body.has_quicklook #content_image_container, #content_body.has_quicklook #content_container:has(#content_video.has_content) { padding:0; z-index:11; } #content_pane[data-content="has_pdf"] #content_container { background-image:${ get_SVG_UI_File_Icon('file_icon_ignored') }; background-size:28px; } `; const sidebar_footer_styles = ` /* for both sidebar and content_iframe */ .has_menu_stats #stats_summary, .stats_kind span.file, .stats_kind span.media, .has_menu_stats #sidebar_footer_utilities { display:none; } #stats_container { max-height:33vh; } .theme_light #sidebar_footer_utilities:hover ul, .theme_light #sidebar_footer:hover, .theme_light #stats_details_summary { box-shadow:0px -4px 4px 0px rgba(128,128,128,0.6); } .theme_dark #sidebar_footer_utilities:hover ul, .theme_dark #sidebar_footer:hover, .theme_dark #stats_details_summary { box-shadow:0px -4px 4px 0px rgba(32,32,32,0.6); } #stats_details_summary_dirs .stats_kind::before { background-image:${ get_SVG_UI_File_Icon("file_icon_dir") }; } #stats_details_summary_files .stats_kind::before { background-image:${ get_SVG_UI_File_Icon("file_icon_file_default") }; } #stats_details_summary, #stats_details_items_container { overflow-y:scroll; } #stats_summary_totals, .has_media #total_duration { display:flex; text-align:left; white-space:normal; padding-right:1em; } #stats a { padding:3px 12px 3px 0; } #stats a::before { content:attr(data-count); width:36px; text-align:right; } .stats_kind span { margin-right:0.5em; white-space:pre; display:flex; } .stats_kind > span::first-letter { text-transform:uppercase; } #stats_details_items li.audio a span span::after { content:attr(data-audio_duration); white-space:pre; } #stats_details_items li.video a span span::after { content:attr(data-video_duration); white-space:pre; } #total_duration::after { content:attr(data-time_remaining); white-space:pre; } .has_media #total_duration::before { content:"Total Time:\\00a0"; } .stats_list_item_name_a { -webkit-padding-start:0; padding:1px 0; } .stats_list_item span::first-letter { text-transform:uppercase; } #stats_summary, #stats_details_summary { margin-block-start:0; margin-block-end:0; } #stats a.dirlist_item_name_a:before { display:inline-block; } #stats_details_items { max-height:25vh; } #stats_container, .has_menu_stats #stats_details_summary, .has_menu_stats #stats_details_items { display:block; } #sidebar_footer_utilities { right:-1px; } #sidebar_footer_utilities svg { margin:2px 2px 0 0; opacity:0.75; } #sidebar_footer_utilities ul { bottom:0; right:-1px; white-space:nowrap; box-shadow:-0px -3px 6px -3px #333; } #sidebar_footer_utilities:hover ul { display:block; } `; const sidebar_utilities_styles = ` #show_sidebar { top:0; right:0; height:21px; opacity:0.75; } #show_sidebar:hover { opacity:1; } body.show_sidebar_false #handle { display:none; } body.show_sidebar_false #show_sidebar { left:2px; transform:rotate(180deg); } body.show_sidebar_false #sidebar { width:0 !important; min-width:0; position:absolute; top:2px; left:-1px; } body.show_sidebar_false #sidebar_header, body.show_sidebar_false #sidebar_footer { z-index:unset; display:none; } body.show_sidebar_false #sidebar_nav { visibility:hidden; } body.show_sidebar_false #directory_list_outer { min-width:0; } body.show_sidebar_false #content_pane { width:100% !important; } body.show_sidebar_false #title_buttons_left { padding-left:24px; } #handle { top:0; bottom:0; right:-4px; width:7px; cursor:col-resize; } .has_overlay #handle { z-index:9999; } `; const sidebar_styles = `${ sidebar_header_styles } ${ sidebar_nav_styles } ${ sidebar_footer_styles } ${ sidebar_utilities_styles }`; /* CONTENT PANE STYLES */ const content_pane_header_styles = ` /***** CONTENT TITLE *****/ #content_title_container { overflow-x:scroll; } #content_title { min-width:16em; min-height:18px; padding:4px 8px; word-break:break-word; } #content_title *:empty { display:none; } #content_title span { pointer-events:none; } #content_title span::before, #content_title span::after { font-weight:normal; margin-bottom:-3px; } #content_pane.has_font_specimen #content_title div::before { content:"Font"; } #content_pane.has_font_file #content_title div::before, #content_pane.has_font_file_glyph #content_title div::before { content:"Glyphs from font"; } .has_directory_source #content_title div::before { content:"Source of" !important; } .has_directory_source #content_title span::before { background-image:${ get_SVG_UI_File_Icon("file_icon_dir_default") }; height:14px !important; background-size:contain; } #content_pane[data-content="has_grid"] #content_title div::before { content:"Fonts and Images from"; } #content_pane[data-content="has_grid"].has_font_grid #content_title div::before { content:"Fonts from"; } #content_pane[data-content="has_grid"].has_image_grid #content_title div::before { content:"Images from"; } #content_pane[data-content="has_ignored"] #content_title div::before { content:"Ignored content"; } #content_pane[data-content="has_dir"] #content_title div::before { content:"Index of"; } body.has_texteditor.texteditor_view_raw.texteditor_split_view_false #content_title div::after { content:" (Source Text)"; } body.has_texteditor.texteditor_view_styled.texteditor_split_view_false #content_title div::after { content:" (Text Preview)"; } body.has_texteditor.texteditor_view_html.texteditor_split_view_false #content_title div::after { content:" (HTML Preview)"; } body.has_texteditor #content_title div::before { content:"Text Editor" !important ; font-weight:bold; } body.texteditor_edited.has_texteditor #content_title div::before { content:"Text Editor (edited)" !important; font-weight:bold; } body.has_texteditor #content_title span { display:none; } body.has_texteditor #content_title span::before { background-image:${ get_SVG_UI_File_Icon("file_icon_markdown") }; } #content_pane:is([data-content="has_htm"]) #content_title span::after { display:none; } body:not(.text_editing_enable_false):not(.has_quicklook) #content_pane:is([data-content="has_text"],[data-content="has_code"],[data-content="has_markdown"]) #content_title span::after { background-image:url("data:image/svg+xml;utf8,${ SVG_UI_File_Icons.file_icon_ebook}"); pointer-events:all; opacity:0.66; } body.text_editing_enable_false #content_pane:is([data-content="has_text"],[data-content="has_code"],[data-content="has_markdown"]) #content_title span::after { background-image:url("data:image/svg+xml;utf8,${ SVG_Text_Editing_UI_Icons.text_editing }"); pointer-events:all; opacity:0.66; } #content_pane:is([data-content="has_text"],[data-content="has_code"],[data-content="has_markdown"]) #content_title:hover span::after { opacity:1 !important; } body.theme_dark #content_pane:not([data-content="has_image"]):not([data-content="has_grid"]) #content_title span::after { filter:invert(1) !important; } #content_pane[data-loaded="unloaded"] #content_title { display:flex; justify-content:center; align-items:center; } #content_pane[data-loaded="unloaded"] #content_title div::before { content:"Loading..." } #content_pane[data-loaded="unloaded"] #content_title span { display:none; } #content_pane[data-content="has_grid"] #content_title span::before { background-image:${ get_SVG_UI_File_Icon("file_icon_dir") }; height:14px !important;} #content_pane[data-content="has_grid"] #content_title span::after { content:attr(data-grid_item_count); font-weight:normal; white-space:pre; margin:0; } #content_pane[data-content="has_image"] #content_title span::after { content:attr(data-after); font-weight:normal; white-space:pre; margin:0; } #content_pane.content_error #content_title span::before, #content_pane.content_error #content_container, #content_pane.has_audio_error #content_audio_title span::before {background-image:${ get_SVG_UI_Icon("error")}; } body.is_error:not(.has_directory_source) #content_title span::before, #content_pane.content_error #content_title div::before { content:"ERROR:"; white-space:pre; display:inline; } /* CONTENT TITLE BUTTONS LEFT */ #reload_btn { width:52px; } #reload_btn::before { content:"Reload"; } #prev_next_btns { margin-left:4px; } #prev_next_btns span { width:2em; height:16px; } #prev_next_btns span:active { background-color:#0E4399; } #prev_next_btns:focus { background-color:white; } #prev_next_btns svg { width:12px; } /* CONTENT TITLE BUTTONS RIGHT */ #scale { margin-right:4px; background-color:#FFF; } #scale span { width:2em; } #close_btn { width:52px; } #close_btn::before { content:"Close"; } body.has_texteditor #close_btn::before { content:"Hide"; } #content_pane[data-content="has_null"] #close_btn { display:none !important; } .split_btn::after { content:""; position:absolute; top:0; bottom:0; left:calc(50% - 1px); border-left:solid 1px #333; } .split_btn span { display:inline-flex; } #open_in_texteditor { margin-right:4px; } `; const content_pane_audio_styles = ` /* CONTENT AUDIO TITLE */ #content_audio_title span { padding:4px 6px 0; } #content_audio_title span::before { content:""; padding-right:22px; height:14px !important; font-weight:normal; background-image:${ get_SVG_UI_File_Icon("file_icon_audio") }; background-position:center; background-position:right 4px center; background-repeat:no-repeat; } #content_pane.has_audio #content_audio_title span::before, #content_pane[data-content="has_video"] #content_title div::before { content:"Playing:"; } #content_pane.has_audio.has_audio_error #content_audio_title span::before { content:"ERROR:"; } #content_pane.has_audio.has_audio_error #content_audio_container { padding-top:0; } #content_pane.has_audio.has_audio_error #audio_container { display:none; } /* CONTENT AUDIO PLAYER */ #content_audio_container { justify-content:center; padding:2px 6px 6px; overflow-x:auto; flex-wrap:wrap; display:flex; } #audio_container { margin:0 100%; height:32px; background-color:rgb(241, 243, 244); } #prev_track, #next_track { width:2rem; } #content_audio { height:32px; } audio::-webkit-media-controls-enclosure { border-radius:0; } #close_audio { width:32px; } #audio_options { margin-top:0; margin-right:calc(-6em - 8px); padding:0 4px; width:6em; justify-content:start; } #loop_label input { margin:0px 4px 2px; } #shuffle_label input { margin:2px 4px 0px; } #shuffle_label::after { content:attr(data-shufflecount); } /* CUE SHEET MENU */ #content_pane.has_audio .cuesheet_track .icon.has_icon_before_before { background-image:${ get_SVG_UI_File_Icon("file_icon_audio") }; } #content_pane.has_video .cuesheet_track .icon.has_icon_before_before { background-image:${ get_SVG_UI_File_Icon("file_icon_video") }; } .cuesheet_track_list_container { background-image:${get_SVG_UI_File_Icon("file_icon_playlist")}; background-repeat:no-repeat; background-size:18px; background-color:inherit; display:none; } .cuesheet_track_list_container:hover > div, .cuesheet_track_list_container.has_menu > div { display:flex; flex-direction:column; margin-top:-1px; overflow:hidden; max-height:100%; } .cuesheet_track_list_container:hover .cuesheet_track_list, .cuesheet_track_list_container.has_menu, .cuesheet_track_list, #content_grid a { display:block; } #cuesheet_track_list_container_audio { width:32px; background-position:center; } #cuesheet_track_list_container_video { width:24px; background-position:top left; } #cuesheet_track_list_container_audio > div { padding-top:13px; } #cuesheet_track_list_container_video > div { padding-top:10px; } .cuesheet_track_list { overflow:scroll; } .cuesheet_track { justify-content:space-between; grid-template-columns: 2rem 20px minmax(6rem,1fr) minmax(6em,1fr) minmax(auto,6em) minmax(auto,6em); } .cuesheet_track.selected .cue_track_id::before { content:""; width:16px; height:8px; display:flex; } .cuesheet_track span { padding:4px 0 4px 8px; font-variant-numeric:tabular-nums; } .cue_index, .cue_position { text-align:right; } #cuesheet_title { padding:4px 8px; font-variant-numeric:tabular-nums; text-align:center; } .track_title_container::after { content:attr(data-track_title); padding-top:4px; text-align:center; color:hsl(0,0%,var(--percent_10)); } /* CONTENT TITLE PLAYLIST ENTRY (#content_playlist_item and #content_audio_playlist_item) */ .playlist_entry_container { flex-direction:row; } .playlist_entry_container textarea { resize:vertical; background-color:transparent; } .theme_light .playlist_entry_container:has(textarea:focus) { box-shadow:inset 0px 0px 2px 2px var(--non_media_item_background); } .theme_dark .playlist_entry_container:has(textarea:focus) { box-shadow:inset 0px 0px 4px 1px hsl(0,0%,var(--percent_95)); } #content_pane.has_audio #audio_wrapper, .playlist_entry_container.has_content { display:flex; flex-direction:column; } audio::-webkit-media-controls-panel { padding:0; } .audio_player_on_top_false #audio_wrapper { position:absolute; left:0; right:0; bottom:0; border-top:solid 1px hsl(0,0%,var(--border_lum)); } `; const content_pane_styles = `${ content_pane_header_styles } ${ content_pane_audio_styles } #content_pane { transform:scale(1); contain:strict; } #content_container { align-items:center; justify-content:center; bottom:0; background-position:center; background-repeat:no-repeat; background-size:33.33%; contain:strict; flex-basis:100%; overflow:auto; } .content_el { width:100%; height:100%; margin:0; padding:0; overflow:auto; display:none; z-index:11; } #content_pane[data-loaded="unloaded"] .content_el:not(#content_audio_container) { display:none !important; } #content_pane[data-loaded="unloaded"]:not([data-content="has_ignored"]) #loading_spinner { display:block; } /* CONTENT DISPLAY */ #content_pane:not([data-content="has_ignored"]) #content_font.has_content, #content_pane[data-content="has_image"] #content_image_container, body:not(.has_texteditor) #content_pane[data-content="has_grid"] #content_grid { display:grid; } #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_body:not(.text_editing_enable_false) #content_pane[data-content="has_htm"] #open_in_texteditor, #content_pane:not([data-content="has_ignored"]) .content_el:not(#content_font).has_content, body.has_texteditor #content_texteditor, #content_pane[class*="has_font_specimen"] #font_specimen_viewer, .has_font_specimen_glyph #font_specimen_glyph_viewer, #content_pane[class*="has_font_file"] #font_file_viewer, .has_font_file_glyph #font_file_glyph_viewer { display:flex; } /* CONTENT GRID (div) */ #content_grid { font-size:1rem; grid-gap:0; grid-template-columns:repeat(auto-fill, minmax(${ ( UI_Prefs_Non_Bool.grid_image_size + 16) }px, auto)); grid-auto-rows:minmax(min-content, max-content); counter-reset:count; } #content_pane.has_hidden_grid #content_grid { max-height:100%; overflow:hidden; position:absolute; display:grid; margin-left:-100%; } .grid_item { counter-increment:count; position:relative; } .image_grid_item::before, .font_grid_item_info:before { content:counter(count,decimal); color:hsl(0,0%,var(--percent_20)); font-size: var(--font_size_smaller); margin-right:6px; display:inline-block; background-color:hsl(0,0%,var(--percent_80)); z-index:10; } .image_grid_item::before { padding:8px 4px; } .font_grid_item_info:before { padding:4px; } .theme_dark .image_grid_item::before, .theme_dark .font_grid_item_info:before { color:hsl(0,0%,var(--percent_20)); } .image_grid_item::before { position:absolute; top:6px; left:6px; } /* FONT & IMAGE GRID ITEMS */ .image_grid_item { padding:6px; grid-column:auto; line-height:0; } .image_grid_item img { width:auto; max-width:${ (UI_Prefs_Non_Bool.grid_image_size).toString() }px; max-height:${ (UI_Prefs_Non_Bool.grid_image_size) }px; position:relative; opacity:0.9; } .image_grid_item img[src$=".svg"] { width:100%; height:100%; } .image_grid_item.selected { box-shadow:inset 0px 0px 4px hsl(0,0%,var(--percent_60)); } .font_grid_item.selected p { font-weight:bold; } .font_grid_item.selected a { font-weight:unset; } .image_grid_item.selected img, .font_grid_item.selected, .image_grid_item:hover, .font_grid_item:hover { opacity:1; } .font_grid { font-size:4em !important; margin:0 0 20px; grid-gap:0; grid-template-columns:repeat(auto-fit, minmax(max(60px,1.33em), 1.5fr)); grid-auto-rows:minmax(max(60px,1.33em), max-content); line-height:unset !important; letter-spacing:unset !important; } .font_grid_item { line-height:1; padding:8px 20px; grid-column:1 / -1; opacity:0.9; } .font_grid_item_info { padding:0 0 6px 0; letter-spacing:0.1em; text-indent:0.1em; } .font_grid_item h2 { font-size:${ UI_Prefs_Non_Bool.grid_font_size * 4 }em; font-weight:normal; } .image_grid_item + .font_grid_item { margin-top:-1px; border-top:solid 1px hsl(0,0%,var(--border_lum_inverted)); } /* CONTENT FONT.content */ #content_font { font-size:${ UI_Prefs_Non_Bool.grid_font_size }em; overflow-wrap:break-word; align-content:start; flex-direction:column; } #content_pane.has_font_specimen #content_container, .has_font_specimen_glyph #content_container, .has_font_specimen_glyph #font_specimen, .has_font_file_glyph #content_container { overflow:hidden; } #content_pane[data-content*="has_font"] #font_toolbar { display:grid; overflow-x:scroll; overflow-y:hidden; } #content_pane.has_font_specimen #font_specimen_variants { display:flex; } #content_pane.has_font_specimen #font_specimen_adjustments li.display_none { display:unset; } #content_pane.has_font_file_glyph #content_font { background-color:hsl(0,0%,var(--percent_100)); } #font_toolbar li { margin:2px; padding:0 4px; white-space:pre; } #font_toolbar li.text { width:50%; font-size:var(--font_size_small); } #font_variant_select { width:13em; } #unicode_char_ranges_select { width:12em; } #font_specimen_adjustments { font-size:0.75rem; padding-bottom:2px; } #font_specimen_adjustments input { width:8em; } #font_specimen_viewer { min-width:100%; flex-direction:column; } #font_specimen_viewer .specimen { padding:20px; outline:none; color:inherit; font-weight:normal; } #font_specimen_viewer .specimen:focus, #font_specimen_viewer .specimen:focus-visible { box-shadow:inset 0 0 2px 2px hsl(212deg 50% 60%); border-radius:3px; outline:none !important; } .specimen:focus, specimen:focus-visible { background-color: hsl(0,0%,var(--percent_100)); } #font_specimen_grid:empty, #font_specimen_glyph:empty, #font_specimen_grid:empty + hr { display:none; } .font_glyph_item { grid-column:auto; display:flex; justify-content:center; position:relative; cursor:pointer; -webkit-text-stroke:inherit !important; letter-spacing:initial; line-height:initial; } .font_glyph_item div { color:inherit; } .font_glyph_item::before, .font_glyph_item::after, #font_specimen_viewer::before, #font_specimen_viewer::after { display:inline-block; position:absolute; font-size:0.75rem; top:0; font-family:${UI_Prefs_Non_Bool.ui_font}; opacity:0.75; font-feature-settings:normal; font-variant:normal; line-height:1.2; letter-spacing:normal; -webkit-text-stroke:0 !important; } .font_glyph_item::before, #font_specimen_viewer::before { content:attr(data-unicode_dec); left:2px; } .font_glyph_item::after, #font_specimen_viewer::after { content:attr(data-unicode_hex); right:2px; } .has_font_specimen_glyph #font_specimen_viewer::before, .has_font_specimen_glyph #font_specimen_viewer::after, .has_font_file_glyph #font_file_glyph_viewer::before, .has_font_file_glyph #font_file_glyph_viewer::after { display:inline-block; padding:4px 6px; font-size:0.875rem; position:fixed; white-space:pre; opacity:1; z-index:2; } #font_specimen_1 { font-size:4em;} #specimen_2 { font-size:8em; } #specimen_3 { font-size:6em; } #specimen_2H4 { font-size:1.618em; } #specimen_3H3 { font-size:2em; } .lorem { font-size:1em; column-gap:1.5em; overflow-wrap:normal; } #lorem::first-line { letter-spacing:0.1em; text-indent:0.1em; font-size:${ UI_Prefs_Non_Bool.grid_font_size * 1.33 }em; font-variant:small-caps; } #lorem_2 { padding:12px 0 0; columns:2; } #lorem_3 { padding:12px 0 0; columns:3; } /* FONT GLYPHS */ #font_specimen_viewer, #font_file_viewer { line-height:1.5; background-color:inherit; } .glyph_container:hover, .glyph_container.selected { z-index:1; } #font_specimen_glyph, #font_specimen_glyph:not(:empty) + #font_specimen_glyph_overlay { display:flex; justify-content:center; font-size:64vw; overflow:visible; } #font_file_viewer { font-family:unset; flex-direction:column; } #content_pane.has_font_file_glyph #font_file_grid { visibility:hidden; } #content_pane.has_font_file_glyph #font_file_viewer { overflow:hidden; } #font_file_grid { margin-bottom:21px; } .font_glyph_item svg { width:1em; height:1em; display:block; overflow:visible; } .font_glyph_item g, #font_file_glyph_viewer svg g { transform-origin:center; } #font_file_glyph_viewer { background-color:#FFF; } #font_file_glyph_viewer::before, #font_file_glyph_viewer::after { position:absolute; color:hsl(0,0%,var(--percent_65)); } #font_file_glyph_viewer::before { content:attr(data-unicode_dec); } #font_file_glyph_viewer::after { content:attr(data-unicode_hex); right:0; } .has_font_file_glyph #save_svg { display:initial; margin-right:4px; } #font_info { max-height:${ (window.innerHeight * 0.75) }px; left:-1px; right:0; bottom:-1px; overflow-y:auto; } #font_info:hover { box-shadow:0px 4px 6px 3px #333; } /* OTHER CONTENT ELEMENTS */ #content_image_container { padding:2rem 2.5rem; box-sizing:border-box; } .has_zoom_image #content_image_container, .has_scaled_image #content_image_container { padding:0; } #content_image { margin:auto; width:auto; max-width:100%; height:auto; max-height:100%; object-fit:contain; cursor:zoom-in; } #content_image:focus-visible { outline:none; } #content_pane.has_zoom_image #content_image { width:fit-content; height:fit-content; max-width:unset; max-height:unset; cursor:zoom-out; } #content_pane[data-content="has_video"] #content_video_container { display:flex; flex-direction:column; justify-content:center; flex-grow:1; align-items:center; align-self:stretch; text-align:center; } #content_video { background:transparent; height:auto; max-width:calc(100% - 4em); max-height:calc(100% - 4em); } #content_pane[data-content="has_htm"] #content_iframe { background:rgba(255,255,255,0); } #content_pane.has_emptycontent #content_iframe { background:unset; } .theme_dark:not(.iframe_theme_false) #content_pane[data-content="has_htm"] #content_iframe, .theme_dark:not(.iframe_theme_false) #content_pane[data-content="has_pdf"] #content_pdf { filter:invert(1) hue-rotate(180deg) saturate(2); } `; const conditional_styles = ` /* PSEUDO-ELEMENTS */ #reload_btn.reset::before, #content_pane:is(.has_font_specimen,.has_font_specimen_glyph,.has_font_file,.has_font_file_glyph,.has_zoom_image,.has_scaled_image) #reload_btn::before { content:"Reset"; } .texteditor_edited #show_texteditor span:after, .iframe_edited:not(.has_texteditor) #content_pane.has_iframe #content_title div::after { content:" (edited)"; } .theme_light #menu_theme span::before { content:"Light "; } .theme_dark #menu_theme span::before { content:"Dark "; } body:not(.show_details_false) #show::before { content:"Hide "; } body.text_editing_enable_false #disable::after { content:"Disabled"; } #disable::after { content:"Enabled"; } .is_error #sidebar_header_utilities { border-bottom:0; } #is_error { display:block !important; grid:none !important; grid-template-columns:none !important; } .theme_dark #is_error_items, .theme_dark #sidebar_menu_main li > span::before, .theme_dark #sidebar_header_utilities_row_2 span::before, .theme_dark #sidebar_header_utilities_row_2 span::after, .theme_light #sidebar_menu_main li.selected > span::before { filter:invert(1); } /* CONDITIONAL DISPLAY */ body:not(.alternate_background_false).is_error #alternate_background, .is_error #sidebar_header_utilities > div:not(:first-of-type), .is_non_local #show_invisibles_container { display:none; } .has_media #play_toggle, .theme_dark #theme_dark, .theme_light #theme_light, #content_pane[class^="has_"] #close_btn, #content_pane[data-content="has_texteditor"] #close_btn { display:unset; } .has_playlist #stats_summary_playlist_files, .has_filelist #stats_summary_playlist_files { display:table-row; } #sidebar_footer li, .has_warning #overlay_container, .cuesheet_track_list_container.has_cue_sheet, .has_playlist #close_playlist_container, .has_filelist #close_playlist_container { display:block; } .has_menu_stats .dirlist_item.invisible, .has_menu_stats .dirlist_item.ignored, .has_menu_stats .dirlist_item.ignored.hovered, body:not(.show_ignored_items_false).has_menu_stats .dirlist_item.ignored, body:not(.show_ignored_items) .dirlist_item.ignored:not(.invisible) { display:grid; } #content_pane[data-content="has_texteditor"] .content_el.has_content, #content_pane[data-content="has_texteditor"] #content_grid, body:not(.has_texteditor) #content_pane[data-content="has_grid"] #content_texteditor, #content_pane[data-content="has_grid"] .content_el.has_content { display:none !important; } `; const texteditor_styles = ` html, body, #content_body { margin:0; padding:0; height:100%; overflow:hidden; position:relative; font-family:${ UI_Prefs_Non_Bool.ui_font }; font-size:${ UI_Prefs_Non_Bool.ui_font_size }; } button.focus, button:focus { outline:none; border-radius:3px !important; border-style:solid !important; border-width:1px !important; border-color:#222 !important; } .is_texteditor #content_texteditor, body.is_text #content_texteditor { display:flex; } /* TOOLBAR */ #texteditor_toolbar { overflow:visible; z-index:100; font-size:${ parseFloat(UI_Prefs_Non_Bool.ui_font_size) * 0.875 + UI_Prefs_Non_Bool.ui_font_size.replace(/\d*/,'') }; } .toolbar_icon { margin:0 4px; padding:4px; min-width:16px; height:16px; cursor:pointer; opacity:0.5; } #texteditor_sync_scroll { opacity:1; height:24px; padding:0 8px; flex-grow:unset; } #texteditor_sync_scroll input { margin:0 4px 0 0; z-index:-1; } #save_btn ul { top:-3px; right:-4px; } #save_html, #save_text { grid-column:1; } #save_btn_icon { grid-column:2; grid-row:span 2; width:32px; } #save_btn_icon svg { margin:3px; } .texteditor_edited #save_btn svg { color:red !important; } #texteditor_toolbar li:hover, .texteditor_view_raw #toggle_texteditor_view_raw, body:not(.texteditor_split_view_false) #toggle_texteditor_view_raw, body:not(.texteditor_split_view_false):not(.texteditor_view_html) #toggle_texteditor_view_styled, .texteditor_view_styled #toggle_texteditor_view_styled, .texteditor_view_html #toggle_texteditor_view_html, body:not(.texteditor_split_view_false) #toggle_texteditor_split_view { opacity:1; } /* TEXT CONTENT CONTAINERS */ .texteditor_pane { padding:1em; overflow-y:scroll; box-sizing:border-box; background:transparent; font-size:${ parseFloat(UI_Prefs_Non_Bool.ui_font_size) + UI_Prefs_Non_Bool.ui_font_size.replace(/\d*/,'') }; } body:not(.text_editing_enable_false) .texteditor_pane, body:not(.texteditor_split_view_false) .texteditor_pane { width:50%; } body:is(.text_editing_enable_false,.texteditor_split_view_false) .texteditor_pane { width:100% !important; } #text_container .texteditor_pane:focus { background-color:hsl(0,0%,var(--percent_90)); outline:none; box-shadow:inset 0px 0px 4px hsl(0,0%,var(--percent_95)); } #text_container textarea { font-family:monospace; } /* EDITOR PANES */ .texteditor_split_view_false.texteditor_view_raw #texteditor_raw_pane, .texteditor_split_view_false.texteditor_view_styled #texteditor_styled_pane, .texteditor_split_view_false.texteditor_view_html #texteditor_html_pane, .texteditor_split_view_false.texteditor_view_html.texteditor_view_raw #texteditor_raw_pane, .texteditor_split_view_false.texteditor_view_styled.texteditor_view_raw #texteditor_raw_pane, .texteditor_split_view_true #texteditor_raw_pane, .texteditor_split_view_true:not(.texteditor_view_styled):not(.texteditor_view_html) #texteditor_styled_pane, .texteditor_split_view_true.texteditor_view_styled #texteditor_styled_pane, .texteditor_split_view_true.texteditor_view_html #texteditor_html_pane, .text_editing_enable_true #reading_btn_icon, .text_editing_enable_false:not(.texteditor_view_styled) #texteditor_raw_pane, .text_editing_enable_false.texteditor_view_raw #texteditor_raw_pane, .text_editing_enable_false.texteditor_view_styled #texteditor_styled_pane { display:block !important; } .text_editing_enable_false:not(.has_texteditor) :is(#text_editing_handle,#texteditor_toolbar,#save_btn,#texteditor_sync_scroll,#toggle_texteditor_html_menu,#texteditor_html_pane), .texteditor_split_view_false :is(#texteditor_sync_scroll,#text_editing_handle), .texteditor_split_view_false.texteditor_view_raw :is(#texteditor_styled_pane,#texteditor_html_pane), .texteditor_split_view_false.texteditor_view_styled :is(#texteditor_raw_pane,#texteditor_html_pane), .texteditor_split_view_false.texteditor_view_html :is(#texteditor_raw_pane,#texteditor_styled_pane), .texteditor_split_view_true.texteditor_view_styled #texteditor_html_pane, .texteditor_split_view_true.texteditor_view_html #texteditor_styled_pane { display:none !important; } /* THEMES & COLORS */ .texteditor_theme_default #content_texteditor .background_grey_95, .background_grey_90:focus, #texteditor_styled_pane table th { background-color:hsl(0,0%,var(--percent_95)); } .theme_dark.texteditor_theme_light #content_texteditor .text_color_default, .theme_light.texteditor_theme_default #content_texteditor .text_color_default { color:hsl(0,0%,var(--percent_05)); } .theme_dark #content_texteditor .text_color_default, .texteditor_theme_dark #content_texteditor .text_color_default { color:#EEE; } /* custom previewed text styles */ #texteditor_styled_pane { word-break:break-word; } #texteditor_styled_pane pre { font-size:${ parseFloat(UI_Prefs_Non_Bool.ui_font_size) + UI_Prefs_Non_Bool.ui_font_size.replace(/\d*/,'') }; border:solid 1px #CCC; border-radius:3px; white-space:pre-wrap; word-break:break-word; } #texteditor_styled_pane th, #texteditor_styled_pane td { vertical-align:top; } #texteditor_styled_pane blockquote { margin-top:1em; margin-bottom:1em; color:#555; } #texteditor_styled_pane blockquote + blockquote { margin-top:0; } #texteditor_styled_pane img { max-width:100%; height:auto; } .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; } #texteditor_styled_pane table { font-size:inherit; } .markdown_body table tr, .markdown_body .highlight pre, .markdown_body pre { background-color:transparent !important; } .markdown_body::before, .markdown_body::after { display:none !important; background:transparent; } #content_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; } #text_editing_handle { width:8px; top:0; bottom:0; left:calc(50% - 4px); cursor:col-resize; } #text_editing_handle::before { content:""; width:1px; background:hsl(0,0%,var(--border_lum)); position:absolute; top:0; bottom:0; left:calc(50%); } .texteditor_theme_dark #text_editing_handle::before, .theme_dark.texteditor_theme_default #text_editing_handle::before { background:#111; } .is_link #texteditor_styled_pane a { font-size:0.875rem; } .is_link #texteditor_styled_pane a:hover { font-weight:bold; } `; // Gecko (Firefos) Styles: const gecko_style_rules = ` .dir::before { content:"" !important; display:none !important; } .is_gecko button { padding:revert; } .is_gecko #show_grid_btn .menu { top:-7px; left:-120px; } .is_gecko thead { font-size:100%; } .is_gecko .dirlist_item.dir::before { position:absolute; } .is_gecko .dirlist_item_name span { display:-webkit-box; width:auto; white-space:normal; } .dirlist_item.dir td:not(:first-child), .dirlist_item.file td:not(:first-child) { width:unset !important; } .is_gecko .dirlist_item td { min-width:calc(100% - 24px); } .is_gecko .dir::before { content:"" !important; display:none !important; } .is_gecko.use_default_icons:not(.is_converted_list) .dirlist_item.file .icon { padding-left:4px; background:none; } .is_gecko.use_default_icons .dirlist_item.file .icon img { margin-right:6px; height:14px; } .is_gecko #directory_list > tr > td:not(:first-of-type) { float:left } .is_gecko #content_audio_title span { padding-top:6px;, padding-bottom:0; } .is_gecko #content_audio,.is_gecko #audio_container { background-color:rgba(26,26,26,1); } .is_gecko #prev_track, .is_gecko #next_track, .is_gecko #close_audio { filter:invert(1); border:none !important; } .is_gecko #content_pane.has_zoom_image #content_image_container { display:block !important; } `; const safari_style_rules = ` .is_safari button { background-color:#FFF; } .is_safari.theme_dark #prev_track, .is_safari.theme_dark #next_track, .is_safari.theme_dark #close_audio { filter:invert(1); } `; const chrome_style_rules = `video::-webkit-media-controls-enclosure { border-radius:0 !important; }`; const html_style_rules = `a:focus, a:focus-visible { font-weight:bold; border-radius:1px; outline:currentcolor solid 1px; outline-offset:1px; display:inline-block; padding:0 2px; text-decoration:none; }` //==============================// function addStyles(user_agent) { // ===> ADD STYLES let default_styles = ` `; switch(user_agent) { case user_agent === 'is_gecko': default_styles += ``; break; case user_agent === 'is_safari': default_styles += ``; break; case user_agent === 'is_chrome': default_styles += ``; break; } return default_styles // return styles } // ***** 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() { // ===> GET INDEX TYPE let index_el = getEls('body > ul, body ul, body > pre, body > table:last-of-type, body div table'); if ( index_el.length > 1 ) { index_el = ( Array.from(index_el).filter( el => el?.nodeName?.toLowerCase() === 'table') || index_el.reverse()[0] ) } // some index pages have pre and table elements; list is usually table index_el = index_el[0]; let node_name = ( index_el !== undefined ? index_el.nodeName.toLowerCase() : 'body' ); // "body" is likely to be an error page let types = {'gecko':'gecko','ul':'list','pre':'pre','table':'table','th':'table','td':'table','div':'default','error':'error','body':'error','permission_denied':'permission_denied'}; // object array of types return types[node_name]; // return index type } function getIndexItems(agent) { let type = getIndexType(agent), items; // ===> GET INDEX ITEMS // get index type, define items switch(type) { case 'error': items = document.getElementsByTagName('html')[0].outerHTML; break; // error type case 'pre': items = getEl('body > pre').innerHTML; break; // pre type case 'list': items = getEls('body > ul li, body > * > ul li'); break; // list type case 'table': case 'td': // table types switch(true) { case elExists('table > tbody'): items = getEls('body table > tbody tr'); break; // ordinary tables case !elExists('table > tbody'): items = getEls('body table tr'); break; // tables without tbody element } break; case 'gecko': items = getEls('body > table > tbody > tr'); break; // gecko type case 'default': items = getEls('body > table > tbody tr'); break; // default: how is this different from table type? } return [items,type]; // return index items and index type } //==============================// function prepPreType(items_str) { // ===> PREP PRE TYPE let prepped_index = [], parser = new DOMParser(), items_HTML = parser.parseFromString(items_str, "text/html"); // convert items_str to DOM html items_HTML.querySelectorAll('hr,img').forEach( el => el.remove() ); // remove junk elements items_HTML.querySelectorAll('a').forEach( // remove junk links (sorting and parent links) or define item_link el => { if ( /^\?|^\./m.test(el.getAttribute('href')) || /^Parent$|^Parent Directory$|^\s*Up\s*$|^\s*Root\s*$/im.test(el.innerText) ) { el.remove(); } } ); items_str = items_HTML.querySelector('body').innerHTML; // convert DOM html back to str // remove header elements | link text nodes | links with empty text nodes (which are sometimes duplicated) | name, last modified, size, description) items_str = items_str.replace(/\<dir\>/gm,' ').replace(/
          /gi,'\n').replace(/[ ]*[^<]*<\/h\d>[ ]*/gmi,'').replace(/[ ]*(]+?>)[^<]*(<\/a>)/g,'$1$2 ').replace(/(\w) 0 && cell.trim() !== '-' ) { if ( !cell.startsWith(' 0 ) { prepped_index.push(prepped_item); } // add prepped item to index } return prepped_index; // return prepped index } function prepListType(items) { // ===> PREP LIST TYPE let prepped_index = []; for ( let i = items.length; i--; ) { let item = items[i]; if ( item.innerHTML.indexOf('Parent Directory') === -1 ) { let prepped_item = [], link = item.querySelector('a')?.href; item.querySelector('a')?.remove(); Array.from(item.children).forEach( child => { if ( child.innerText === '' ) { child.remove(); } }) // remove link and empty child elements let cells = item.innerHTML.split(' '); // create array from remaining elements for ( let cell of cells ) { prepped_item.push(cell); } if ( link === undefined || ( /^\.\.$|^\.\.\/$|^\/$|^\?|\?sort=|\?path=\&/mi.test(link) ) ) { prepped_item = []; } else { prepped_item.unshift(link); } // exclude some items (e.g., parent directory links) if ( prepped_item.length > 0 ) { prepped_index.push(prepped_item); } } } return prepped_index; // return prepped index } function prepGeckoType(items) { // ===> PREP GECKO TYPE let prepped_index = []; for ( let item of items ) { let prepped_item = [], cellContents = '', cells = item.cells, link = item.innerHTML.split('href=\"')[1].split('\">')[0]; for ( let cell of cells ) { cellContents = cell.innerText; cellContents = ( cellContents !== undefined ? cellContents.trim() : ''); prepped_item.push(cellContents); } prepped_item[1] = prepped_item[1].replace(/\s*KB/,'000'); // convert reported size in KB to total bytes prepped_item[2] = prepped_item[2] + ' '+ prepped_item[3]; prepped_item = prepped_item.slice(1,-1); if ( link === undefined || ( /^\.\.$|^\.\.\/$|^\/$|^\?|\?sort=|\?path=\&/mi.test(link) ) ) { prepped_item = []; } else { prepped_item.unshift(link); } // exclude some items (e.g., parent directory links) if ( prepped_item.length > 0 ) { prepped_index.push(prepped_item); } } return prepped_index; // return prepped index } function prepTableType(items) { //*** for local chrome indexes and server-generated table-type indexes // ===> PREP TABLE TYPE // const testString = new RegExp(/alt=\"\[PARENTDIR\]|>\s*\ \s*<|^\s*\ \s*$|^\s*-\s*$|\?sort=|\?path=\&/,'mi'); let prepped_index = [], prepped_item, item, cell, cell_text; for ( item of items ) { if ( item.querySelector('td a') !== null ) { let link; prepped_item = []; // get legitimate items (i.e., those containing a link) for ( cell of item.cells ) { // get text from remaining cells (date & size) switch(true) { case cell.querySelector('a') !== null && link === undefined: link = item.querySelector('a')?.getAttribute('href'); // get link; add to prepped_item; ignore if link already defined if ( !/^\?|^\.\.\/$|^\|\"\/\".$/m.test(link) && !/^\s*parent directory\*$|^\*up\s*$/m.test(item.innerText.toLowerCase()) ) { prepped_item.unshift(link); }// else { prepped_item.unshift(''); } break; default: cell_text = cell.innerText.trim().replace(/(^[ ]*-[ ]*$|[ ]*-[ ]*\ [ ]*$)|\ /m,''); // prep cells and clean cell text if ( !/]*>dir|directory|file<\/td>|>\w*\s*file<|>\w*\s*unknown 1 && prepped_item[0] !== '' ) { prepped_index.push(prepped_item); } // prepped_item.length > 2 in order to omit parent directory item } } return prepped_index; // return prepped index } function prepErrorType(items) { return items; } // ===> PREP ERROR TYPE; receives and returns html string function prepPlaylist(items) { // ===> PREP PLAYLIST items let prepped_index = []; let prepped_item, link, duration, name, info; let items_arr, type; items = items.replace(/\s*#EXTM3U.*\s*/g,'').replace(/^\*\n{2,}/gm,'\n').replace(/\.pdf\?.+?\n/g,'.pdf\n');//.replace(/\?/g,'%3F'); // remove header comment and multiple returns switch(true) { // determine playlist type; case ( /#EXTINF:/i.test(items) ): type = 'extm3u'; items_arr = items.split('#EXTINF:'); break; // rows made by splitting at "#EXTIMG:" prefix default: type = 'm3u'; items_arr = items.split('\n'); break; // rows are just naked links } items_arr.forEach( (item) => { switch(true) { // get entry information: title, link, etc. case type === 'extm3u': item = item.trim().split('\n'); link = item[1]; info = item[0].split(','); duration = info.shift(); name = info.join(','); if ( item[1] !== undefined ) { prepped_item = [link,duration,'',name]; } break; case type === 'm3u': prepped_item = [item,'','']; break; // m3u with urls only } if ( prepped_item !== undefined ) { prepped_index.push(prepped_item); } }); return prepped_index; // return prepped index } function convertIndexItems(items,type) { let converted = []; // ===> CONVERT INDEX ITEMS by type; returns [prepped_index] switch(type) { case 'gecko': converted = prepGeckoType(items); break; case 'list': converted = prepListType(items); break; case 'pre': converted = prepPreType(items); break; case 'table': case 'default': converted = prepTableType(items,type); break; case 'error': converted = prepErrorType(items); break; } return converted; } //==============================// function buildNewIndex(id,prepped_index,sort,type,body_id) { // ===> BUILD NEW INDEX from prepped rows let i, new_index_items = [], body_classes = new Set(); let index_html = ''; let new_item, item, item_info = [], item_link, item_name, item_sort_name, item_size_and_date, item_size, item_sort_size, item_date, item_sort_date, item_ext, item_description, item_sort_kind, item_classes; let name_span, cell_link, cell_name, cell_size, cell_date, cell_kind, cell_ext, cell_time, prepped_index_length = prepped_index.length, item_disabled, item_input; let stats, stats_classes = [], stats_kinds = [], stats_total_size = 0, media_count = 0; let dir_list_parent_class = ( (body_id === ( null || 'top_body') || type === 'playlist') ? 'top_item' : 'iframe_item' ), is_playlist = (type === 'playlist' ? type +'_' : ''); // id used to set dir list details style let parent_id = ( getCurrentUIPref('parent_id') || '' ), connector = ( getCurrentUIPref('parent_id') ? '_' : '' ), level = ( Number(getCurrentUIPref('level')) || 0 ), level_style = ( level === 0 ? '' : `style="padding-left:${ Number(level) * 22 }px;"` ); // ensure unique ids (with parent_id) and set indents for subdirectory items if ( prepped_index_length > 5000 ) { if ( confirm(`This directory contains ${prepped_index_length} items; it may take a long time to process and could cause your browser to crash. Are you sure you want to open it?`) === false ) { return } } switch(type) { // add body classes according to index type case 'error': body_classes.add('is_error'); break; case 'pre': body_classes.add('is_converted_pre'); break; case 'list': body_classes.add('is_converted_list'); break; case 'table': case 'td': body_classes.add('is_converted_table'); break; case 'default': body_classes.add('is_default'); break; } // create and format directory item for ( i = 0; i < prepped_index_length; i++ ) { item = prepped_index[i]; item_info = getLinkInfo(item[0]); // returns [link,name,ext,kind,item_classes,body_classes]; item_link = item_info[0]; item_name = item_info[1] || item[3]; // prep display name, with word breaks added after unbreakable chars item_sort_name = item_name.toLocaleLowerCase(); item_size_and_date = getItemSizeAndDate(item); item_size = item_size_and_date[0]; item_sort_size = item_size_and_date[1]; item_date = item_size_and_date[2]; item_sort_date = item_size_and_date[3]; item_ext = item_info[2]; item_sort_kind = item_info[3]; item_classes = item_info[4] +" "+ dir_list_parent_class; item_disabled = ( /local/.test(item_classes) ? ' disabled="disabled"' : '' ); // disable media if local file on non_local page or vice versa item_input = ( /audio|video/.test(item_sort_kind) && /top/.test(body_id) || type === 'playlist' ? `` : '' ); // Assemble item elements name_span = `${ item_input }${ item_name }`; cell_link = `${ name_span }`; cell_name = `${ cell_link }`; cell_time = ``; cell_size = `${ item_size }`; cell_date = `${ item_date }`; cell_kind = `${ item_sort_kind }`; cell_ext = ``; item_description = ( item[3] !== undefined ? `Description: ${ item[3] } ` : '' ); // some servers provide a description of the item // Assemble item new_item = `
        1. ${ cell_name } ${ cell_time } ${ cell_size } ${ cell_date } ${ cell_kind } ${ cell_ext } ${ item_description }
        2. \n`; if ( /audio|video/.test(item_sort_kind) ) { media_count += 1; // if media item... let media_kind = item_sort_kind, media_item_id = parent_id + connector + is_playlist + 'rowid-'+ ( prepped_index.length - i), is_subdir = ( /subdirectory/.test(window.location.search) ? true : false ); // get media duration, not in utility subdir (limit to 1000 calls): if ( media_count < 1000 && is_subdir !== true ) { getMediaDuration( item_link, media_kind, media_item_id, is_subdir ); } else { new_item = new_item.replace(/data-duration="">/,'data-duration="NaN">'); } } new_index_items.push(new_item); // add item to index items body_classes.add(item_info[5].join(' ')); // add item classes to body_classes stats_kinds.push(item_sort_kind); stats_total_size += Number(item_sort_size); stats_classes.push(item_info[6]); // STATS: add item kind; update total size; add to stats classes } body_classes = [...body_classes].filter(body_class => body_class).sort(); // BODY CLASSES: body_classes to array, filter empty items, sort stats = buildStats(stats_classes,stats_kinds,stats_total_size); // STATS: build stats if ( sort === '' || sort === undefined ) { sort = getCurrentUIPref('sort_by'); } // SORT ITEMS: get sort_by pref let sort_direction = getCurrentUIPref('sort_direction'); // get sort_direction pref let sorted_index_items = sortDirListItems(new_index_items, 'sort_by_'+ sort, sort_direction); // make initial sort return [sorted_index_items, body_classes.join(' '),stats,index_html]; // RETURN [sorted_index_items, body_classes, stats, index_html] } //==============================// function getLinkInfo(link) { // ===> GET LINK INFO; returns [link,name,ext,kind,item_classes,body_classes,link_protocol] switch(true) { case link === undefined: return; // return if link undefined case link === null: link = getEl('#content_iframe').src; break; // link from opening local link files links in iframe case link.startsWith('file://') && window.location.protocol === 'file:': link = link.split('file://')[1]; break; // local links case link.startsWith('/') && window.location.protocol === 'file:': link = 'file://'+ link; break; // local links case !link.startsWith('/') && !link.endsWith('/') && !/\./.test(link): link = '/'+ link +''; break; } link = link.replace(/%3C/g,'\<').replace(/\.pdf\..+/,'.pdf'); // fix and sanitize links // if ( /\.php\?(\w+)=/.test(link) ) { link = link.split('\.php')[1]; } // attempt to deal with some php links let URL = newURL(decodeURIComponentSafe(encodeURIComponent(link))); let prepped_link, display_name, kind, ext, item_classes = [], body_classes = [], stats_classes = [], aliases = new RegExp(/(symlink|alias|symbolic link)$/,'m'), link_protocol = URL.protocol; switch(true) { // prep link case window_protocol !== 'file:': // for non-local pages switch(true) { case URL.protocol === 'file:': case URL.protocol === undefined: prepped_link = link; item_classes.push('local','ignored'); break; // local links from non-local pages default: prepped_link = URL.href; // non-local pages } break; case window_protocol === 'file:': // for non-local pages switch(true) { case URL.protocol !== 'file:': prepped_link = URL.href; item_classes.push('non_local'); break; default: prepped_link = URL.pathname; } } switch(true) { // prepare display name, body_classes, and item_classes case ( /youtube.com|youtu.be/.test(link) && !link.indexOf('/.') ): prepped_link = link.replace(/watch%3F/,'watch?'); kind = 'video'; item_classes.push('video','media'); display_name = undefined; break; // youtube videos from playlists case URL.pathname.endsWith('/'): case ( /\.php\?/.test(link) ): // nobreak; dirs, apps and index.php? links display_name = ( URL.pathname.endsWith('/') ? URL.pathname.split('/').reverse()[1] + '/' : /\.php\?/.test(link) ? link : null ); switch(true) { case ( /\.app$|\.app\/$|\.exe$/m.test(display_name) ): ext = 'app'; kind = ext; // apps if ( UI_Prefs_Bool.apps_as_dirs === false ) { item_classes.sort().unshift('file','app'); } else { item_classes.sort().unshift('dir','app'); } break; default: ext = 'dir'; kind = 'dir'; item_classes.unshift(kind); // dirs; remove kind from item_classes } item_classes.push('non_media'); // add "non_media" to item_classes if ( display_name.startsWith('.') ) { item_classes.push('invisible'); stats_classes.push('invisible'); } break; default: // files display_name = prepped_link.trim().split('/?')[0].split('/').reverse()[0]; switch(true) { case display_name.toLowerCase().endsWith('symlink'): ext = 'symlink'; break; case !/\./.test(display_name): ext = display_name.toLowerCase(); break; // if no '.' in link (typical for bin files), ... default: // find the last . and get the remaining characters ext = display_name.slice(display_name.lastIndexOf('.') + 1).toLowerCase(); for ( let item_kind in Item_Kinds ) { if ( Item_Kinds[item_kind].includes( ext ) ) { kind = item_kind; } } // kind = types if ( /url|url\/|webloc|webloc\//.test(ext) ) { kind = 'link'; } // links switch(true) { case kind === 'audio': item_classes.push('media'); body_classes.push('has_media','has_audio'); break; case kind === 'video': item_classes.push('media'); body_classes.push('has_media','has_video'); break; case kind === 'font' : body_classes.push('has_fonts'); break; case kind === 'image': body_classes.push('has_images'); break; } if ( Item_Settings.ignored.includes( ext ) ) { item_classes.push('ignored'); stats_classes.push('ignored'); } if ( display_name.startsWith('.') ) { item_classes.push('invisible'); stats_classes.push('invisible'); } } if ( kind === undefined ) { kind = 'other'; } if ( !/audio|video/.test(kind) ) { item_classes.push('non_media'); } item_classes.unshift(kind); item_classes.unshift('file'); prepped_link = decodeURIComponentSafe(encodeURIComponent(prepped_link))?.trim(); } stats_classes.push(kind); if ( ext === undefined ) { ext = ''; } if ( aliases.test(display_name) ) { item_classes.push('alias'); } for ( let item_kind_system of Item_Kinds.system ) { if ( display_name?.endsWith(item_kind_system) ) { item_classes.push('ignored'); } } // ignore various system items item_classes = Array.from(new Set(item_classes)).filter(item => item).join(' '); // remove dupe or empty classes, join return [prepped_link,decodeURIComponentSafe(display_name)?.trim(),ext,kind,item_classes,body_classes,stats_classes.join(' '),link_protocol,URL.origin + URL.pathname]; } //==============================// function getItemSizeAndDate(item) { // ===> GET ITEM SIZE AND DATE let item_size_and_date = [], item_display_size, item_sort_size, item_display_date, item_sort_date, size_units = /[BYTES|B|K|KB|MB|GB|TB|PB|EB|ZB|YB]/; if ( item.length > 1 ) { // test for typical date/time separators. if ( /[-:\/]/.test(item[1]) ) { item_display_date = item[1]; item_display_size = item[2]; } else { item_display_date = item[2]; item_display_size = item[1]; } } switch(true) { // get size case item_display_size !== undefined && item_display_size.toLowerCase() === 'dir': case ( /undefined|—|-|,|\*/.test(item_display_size) ): case item_display_size === '': item_display_size = '—'; item_sort_size = '0'; break; // if size is undefined, empty, or punctuation, use these defaults default: item_sort_size = getItemSortSize(item_display_size); switch(true) { case !item_display_size.toUpperCase().match(size_units) : item_display_size = formatBytes(item_display_size,1); break; // if provided size is only numeric, format byte size default: item_display_size = item_display_size.replace('K','k').replace(/(\d+)\s*([A-z])/,'$1 $2'); // default: format and ensure display size has space between number and units } break; } if ( item_display_size === 'NaN undefined' ) { item_display_size = '0 B'; } // get date if ( [undefined,'','-'].includes(item_display_date) ) { item_display_date = '—'; item_sort_date = '0'; } else { item_sort_date = getItemDate(item_display_date); } item_display_date = item_display_date.replace(/, (.+)/,', $1').replace(/ (AM|PM)$/im,' $1').replace(/\s/g,' '); // ensure that time acts as a block for wrapping in narrow sidebar item_size_and_date.push( item_display_size, item_sort_size, item_display_date, item_sort_date ); return item_size_and_date; } function getItemSortSize(val) { // GET ITEM SORT SIZE let sort_size, values = val.replace(/(\d+)\s*([A-z]+)/,'$1 $2').split(' '), size = values[0], unit = values[1]; const factor = { undefined:1, '':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 if ( unit !== undefined ) { unit = unit.toUpperCase(); } sort_size = size * factor[unit]; // convert byte size to multiplication factor return sort_size; } function formatBytes(val, decimals) { // ===> FORMAT BYTES: format numeric sizes for display 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)); if (val === 0) { return '0 Bytes'; } else { return parseFloat((val / Math.pow(k, i)).toFixed(dm)) +' '+ sizes[i]; } } function processDate(match,p1,p2,p3) { //***date formats: 2017-10-09 13:12 || 2015-07-25T02:02:57.000Z || 12-Mon-2017 21:11 ***// // ===> PROCESS DATE const mo = 'JanFebMarAprMayJunJulAugSepOctNovDec'.indexOf(p2)/3 + 1; // e.g., convert month into number, or use number return p3 +'-'+ mo +'-'+ p1; // return assembled date: YYYY-MM-DD } function getItemDate(val) { // ===> GET ITEM DATE: for sorting (YYYY-MM-DD) let sort_date = 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 to YY/MM/DD .replace(/-|:|\s+|\//g,''); // remove spacing characters return sort_date; } //==============================// // FETCH MEDIA DURATIONS var getFormattedDuration = (secs) => { if ( isNaN(secs) ) { return Number.NaN; } // ===> GET FORMATTED TIME let sec_num = parseInt(secs, 10), hours = Math.floor(sec_num / 3600), minutes = Math.floor(sec_num / 60) % 60, seconds = sec_num % 60; let formattedTime = [hours,minutes,seconds].map( v => v < 10 ? "0" + v : v ).filter( (v,i) => v !== "00" || i > 0 ).join(":"); formattedTime = formattedTime.replace(/^0/m,''); return formattedTime; // remove initial 0 and return formatted time }; async function fetchMediaDuration(link,kind) { // ===> ASYNC FETCH MEDIA DURATION return new Promise((resolve, reject) => { const media = ( kind === 'audio' ? new Audio() : document.createElement('video') ); media.addEventListener('loadedmetadata', () => { resolve(media.duration); }); media.addEventListener('error', reject); media.src = link.replace(/\&/g,'&'); }); } async function getMediaDuration(link,media_kind,id) { // ===> ASYNC GET MEDIA DURATION (not utility iframe subdir items; see buildNewIndex) try { const duration = await fetchMediaDuration(link,media_kind); setMediaDuration( id,media_kind,duration ); // await media duration; set media duration } catch (error) { if ( id?.indexOf('playlist') && hasClass('body','has_playlist') || !id?.indexOf('playlist') && !hasClass('body','has_playlist') ) { setMediaDuration( id,media_kind,Number.NaN ); } // on error, set dur = NaN } } // SET MEDIA DURATIONS function getThisDuration(id) { let item_dur = Number(getData('#'+id +' .dirlist_item_media_duration','duration') ); if ( id !== undefined ) { return ( isNaN(item_dur) ? Number.NaN : item_dur ); } } // return dur or NaN function setThisDuration(id,dur) { getEl('#'+id).querySelector('.dirlist_item_media_duration').dataset.duration = dur; } function setMediaDuration(id,kind,duration) { let el; // ===> SET MEDIA DURATION if ( id === 'content_iframe_file' || /youtube.com|youtu.be/.test(getEl('#'+id+' a')?.href) ) { return; } // do not attempt to set duration for iframe files or youtube playlist items try { el = getEl('#'+id); setThisDuration(id,duration); switch(true) { case Number(duration) === 0: case isNaN(Number(duration)): el.classList.add('disabled'); break; // if duration is NaN, disable and show spinner default: el.querySelector('.dirlist_item_media_duration').innerText = getFormattedDuration(duration); el.classList.remove('disabled'); el.querySelector('input')?.removeAttribute('disabled'); statsSetTotalDuration(duration,kind); break; // if duration is a number, update stats } } catch (error) { null } } function statsSetTotalDuration(duration,kind) { let media_items = getEls('.dirlist_item.media'); if ( !media_items ) { return; } // if no media items, or total times already calculated, abort let total_duration = Number(getEl('#total_duration')?.dataset.total_duration), audio_duration = 0, video_duration = 0; switch(true) { case kind !== 'refresh_all': addClass('body','has_'+ kind); // after opening subdir total_duration = Number(total_duration) + Number(duration); getEl('#total_duration').dataset.total_duration = total_duration; getEl('#total_duration').innerText = getFormattedDuration(total_duration); break; // set display total duration default: for ( let i = 0; i < media_items.length; i++ ) { duration = getThisDuration(media_items[i].id); // get duration from dirlist item if ( !isNaN(Number(duration)) ) { total_duration = Number(total_duration) + Number(duration); } else { return } // update total duration switch(true) { // update audio and video total durations case kind === 'audio': audio_duration = Number(audio_duration) + Number(duration); // add has_audio class, increment total audio duration setAttr('#stats_details_items span.audio','data-audio_duration',' (Total Time: '+ getFormattedDuration(audio_duration) +')'); break; case kind === 'video': video_duration = Number(video_duration) + Number(duration); // add has_video class, increment total video duration setAttr('#stats_details_items span.video','data-video_duration',' (Total Time: '+ getFormattedDuration(video_duration) +')'); break; } setAttr('#total_duration','data-total_duration',total_duration); getEl('#total_duration').innerText = getFormattedDuration(total_duration); // set display total duration } } } // REFRESH MEDIA DURATIONS function refreshMediaDurations(id) { let media_items, item = getEl('#'+id), link = item?.querySelector('a')?.href, kind = item?.dataset?.kind; // ===> REFRESH MEDIA DURATIONS from menu or selecting [Error] media file switch(true) { case id === 'refresh_media_durations': // refresh all durations; from click refresh media durations menu item media_items = Array.from(getEls('.dirlist_item.media')); media_items = media_items.filter( (el) => { return ( isNaN(getThisDuration(el?.id)) || getThisDuration(el?.id) === 0) }); // only update if dur is falsey media_items.forEach( el => refreshMediaDurations(el?.id) ); // send each media item with id back to function for default processing if ( isTopWindow() ) { messageSend('iframe','refresh_media_durations'); } break; // send refresh message to iframe default: if ( isNaN(getThisDuration(id)) || getThisDuration(id) === 0 ) { setThisDuration(id,0); getMediaDuration( link,kind,id ); } break; // refresh dur by id; first set dur to 0 to show loading spinner } } //==============================// function buildStats(stats_classes,stats_kinds,stats_total_size) { //*** BUILD STATS stats_classes.sort(); let total_items = stats_classes.length, counts = {}, kinds = [], stats_items = [], total_dirs = 0, total_files = 0, total_dirs_invisible = 0, total_files_invisible = 0, total_invisibles = ''; for ( let i = 0; i < total_items; i++ ) { // Get counts stats_classes[i] = stats_classes[i].split(' ').reverse().join(' ') // reorder classes to make invisible/ignored last counts[stats_classes[i]] = 1 + ( counts[stats_classes[i]] || 0 ); // get key/value pairs for item_classes/total counts switch(true) { case ( !/invisible|ignored/.test(stats_classes[i]) ): break; // don't count :not(.invisible) and :not(.ignored) case (getCurrentUIPref('show_invisibles') === 'true' ) && ( getCurrentUIPref('show_ignored_items') === 'false' ): // show_invisibles && hide_ignored if ( /invisible/.test(stats_classes[i]) && /ignored/.test(stats_classes[i]) ) { break; } // don't count .invisible.ignored if ( /dir/.test(stats_classes[i]) ) { total_dirs_invisible++; } else { total_files_invisible++; } break; // else count .ignored case (getCurrentUIPref('show_invisibles') === 'false' ) && ( getCurrentUIPref('show_ignored_items') === 'false' ): // hide_invisibles && hide_ignored (hide all) if ( /dir/.test(stats_classes[i]) ) { total_dirs_invisible++; } else { total_files_invisible++; } break; // count .invisible and .ignored (count all) case (getCurrentUIPref('show_invisibles') === 'true' ) && ( getCurrentUIPref('show_ignored_items') === 'true' ): break; // don't count .invisible or .ignored (count none) case (getCurrentUIPref('show_invisibles') === 'false' ) && ( getCurrentUIPref('show_ignored_items') === 'true' ): // hide_invisibles && show_ignored if ( !/invisible/.test(stats_classes[i]) && /ignored/.test(stats_classes[i]) ) { break; } // don't count .ignored:not(.invisible) if ( /dir/.test(stats_classes[i]) ) { total_dirs_invisible++; } else { total_files_invisible++; } break; // else count .invisible and .invisible.ignored } } for ( let i = 0; i < stats_kinds.length; i++ ) { kinds[stats_kinds[i]] = 1 + ( kinds[stats_kinds[i]] || 0 ); } // get key/value pairs for item kinds/counts total_dirs = ( kinds.dir || 0 ); total_files = ( total_items - total_dirs ); // total dirs && files count if ( getCurrentUIPref('show_invisibles') === 'false' || getCurrentUIPref('show_ignored_items') === 'false' ) { total_invisibles = ' (+'+ (total_dirs_invisible + total_files_invisible) +')'; total_items = total_items - (total_dirs_invisible + total_files_invisible); total_dirs = total_dirs - total_dirs_invisible; total_files = total_files - total_files_invisible; } for ( let count in counts ) { // make detail item for each kind of dirlist item --> doesn't preserve order let kinds_items = count.split(' '), temp_items = [], stats_Item_Kinds = ''; kinds_items.forEach( item => ( !/ignored|invisible/.test(item) ? temp_items.unshift(item) : temp_items.push(item) ) ); kinds_items = temp_items; kinds_items.forEach( item => ( stats_Item_Kinds += (``+ (/ignored|invisible/.test(item) ? ' ('+item+')' : item.trim() ) +``)) ); let stats_item = `
        3. ${ stats_Item_Kinds }
        4. `; stats_items.push(stats_item); } stats_items.sort(); return ``; } function updateStats(bool) { // ===> UPDATE STATS (bool: add or subtract size from total) let items = getEls('.dirlist_item'), item_info, total_item_size, stats_classes = [], stats_kinds = [], item_classlist = [], total_size = 0; // get all dir_list items getEls('.dirlist_item_details.size').forEach(el => total_size += Number(el.dataset.size)); total_item_size = ( bool === false ? total_size : Number(getData('#stats_summary_totals','size')) ); for ( let i = 0; i < items.length; i++ ) { // get classes and kind for each item item_info = getLinkInfo( items[i].getElementsByClassName('dirlist_item_name_a')[0].href ); // get item info = [link,name,ext,kind,item_classes,body_classes]; item_classlist = item_info[4]; item_classlist = item_classlist.replace(/file|media|audio_loaded|content_loaded|has_subdirectory|selected|non_/g,'').trim(); // get item_classlist; remove unwanted classes stats_classes.push(item_classlist); stats_kinds.push( item_info[3] ); // add item_classlist to stats_classes; add Item_Kinds to stats_kinds total_item_size += Number(items[i].querySelector('.size').dataset.size); } getEl('#stats_container').remove(); getEl('#sidebar_footer').insertAdjacentHTML('afterbegin',buildStats(stats_classes,stats_kinds,total_item_size,2)); // remove old stats; build new stats and add to sidebar_footer statsSetTotalDuration(null,'refresh_all'); initStatsEvents(); // initial event listeners for new stats items } // ***** END DIR_LIST SETUP ***** // //============================// // ***** UI SETUP ***** // function prepDocHead(agent) { // ===> PREP DOC HEAD document.title = 'Index of '+ window_location; // change the doc title to current location for ( let attr_name of getEl('html').getAttributeNames() ) { getEl('html').removeAttribute(attr_name); } // remove html attributes, if any getEl('head title').removeAttribute('id'); getEls('head meta, head base, head link, head style, head script, head noscript').forEach( headEl => headEl.remove() ); // remove various head elements let head_content = '' + getEl('head').innerHTML.replace(//g,''); // add meta and remove conditional comments if ( window.location.protocol.startsWith('file') ) { head_content = get_SVG_UI_File_Icon('favicon') + head_content; } // add custom favicon for local directories getEl('head').innerHTML = head_content + addStyles(agent); // replace head content with prepped content } function getUIPrefBodyClasses(agent) { // ===> GET UI PREF BODY CLASSES and other initial settings let queries = new URLSearchParams(window.location.search).entries(); queries = Object.fromEntries(queries); // make new search params from window.location.search let body_classes = [], settings = Object.assign({},queries,UI_Settings); // merge UI_Settings and query settings for ( let key in settings ) { switch(true) { case ['grid_font_size','grid_image_size','ui_font','ui_scale','show_image_thumbnails'].includes(key): break; // ignore these keys (values set in css or by buildTextEditorUI) case ['sort_by','sort_direction','theme','texteditor_view'].includes(key): body_classes.push( key +'_'+ getCurrentUIPref(key) ); break; // other non-booleans: class = key + value case getCurrentUIPref(key) === 'false': body_classes.push( key +'_false' ); break; // booleans: only add false values } } body_classes.push(agent); body_classes.push('is_'+getOS()); // add browser and os classes return body_classes.sort().join(' '); } function makeNewIndex(el,sort,agent,body_id) { // ===> MAKE NEW INDEX const index_items = getIndexItems(agent), items = index_items[0], type = index_items[1]; const converted_index = convertIndexItems( items, type ); // = array of rows: ["link","date","size"] switch(type) { case 'error': return [[[''+ ( items === undefined ? '' : items ) +''],'is_error'],'','',index_items]; default: return [buildNewIndex( el.id, converted_index, sort, type, body_id )]; } } // ===> BUILD IFRAME DIR LIST UI, with utility iframe for subdirectories add function buildIframeUI(src,file_name,agent) { let parent_link = src.split('/').slice(0,-2).join('/') + '/', query_str = new URLSearchParams(window.location.search.toString().slice(1)); let subdirectory = query_str.get('subdirectory') || null, body_id = query_str.get('body_id'); let iframe_directory, iframe_head, iframe_dir_list, content_body, gecko_styles, body_classes, iframe_utility_iframe, new_index, make_new_index, additional_classes; window.onmessage = function(e) { messageReceive(e); return; } // init receive messages switch(true) { case window.location.search === "": // nobreak; case is true when opening dirs from sidebar source dir case ( query_str.get('show_directory_source') || query_str.get('is_error') ) === 'true': if ( elExists('#iframe_dir_styles') ) { getEl('#iframe_dir_styles').remove(); } break; // do nothing when viewing directory source or if error page... default: // ...else set up iframe directory: iframe_head = getEl('head'); content_body = getEl('body'); iframe_dir_list = ''; gecko_styles = ''; body_classes = []; iframe_utility_iframe = ''; if ( /\.php\?/.test(src) ) { query_str = new URLSearchParams(makeSrcSearchParams('dir')); } // define default params for index.php?folder=... pages for ( let key of query_str.keys() ) { // add various body_classes... switch(true) { case ( /show_details|ui_font/.test(key) ): break; // show details by default case query_str.get(key) === 'true': break; // ignore true booleans case query_str.get(key) === 'false': body_classes.push(key+'_false'); break; // add body classes for false boolean params default: body_classes.push(key+'_'+getCurrentUIPref(key)); break; // non-boolean params (theme, sort) } } if ( agent === 'is_gecko' ) { gecko_styles = (''); } new_index = makeNewIndex(content_body, query_str.get('sort_by'),'',body_id); // make new index make_new_index = new_index[0]; additional_classes = (new_index[0][1].trim().split(/\s+/)).concat(body_classes); // define additional body classes if ( !/is_error/.test(new_index[0][1]) ) { // if not an a error page...build the ui iframe_head.querySelectorAll('style,script,meta,link[rel="stylesheet"],link[href$="css"]').forEach( el => el.remove() ); // remove any existing directory index styles iframe_head.insertAdjacentHTML('beforeend',`${gecko_styles}`); // assemble the iframe head switch(true) { // Assemble content_iframe and utility_iframe content case subdirectory === 'true': iframe_dir_list = `
            ${ make_new_index[0] }
          `; break; default: iframe_directory = Sidebar_Elements('iframe',parent_link); // create iframe directory elements iframe_dir_list = iframe_directory.replace(/insert_prepped_index/,make_new_index[0]).replace(/insert_stats/,make_new_index[2]); // assemble iframe directory content_body.removeAttribute('style'); content_body.style.fontFamily = getCurrentUIPref('ui_font'); // remove any body inline styles; set ui_font content_body.classList.add(...additional_classes); // add body styles } content_body.innerHTML = iframe_dir_list + Content_Pane_Elements(); // append iframe_dir_list and content_pane for quicklook } if ( subdirectory === null ) { content_body.insertAdjacentHTML('beforeend',iframe_utility_iframe); initIframeEvents(); } // don't multiply utility_iframes; init iframe event listeners if ( subdirectory === 'true' ) { messageSend('top_body','dirlist_subdir_loaded','',[getEl('#directory_list').innerHTML,make_new_index[1],getCurrentUIPref('parent_id')] ); // send prepped subdir to parent window } else { messageSend('top_body','iframe_loaded','',[src,file_name,'dir']); // else send iframe_loaded message } } } // ===> BUILD TEXT EDITOR UI function buildTextEditorUI(kind) { let raw_markdown, body_classes = [], content; if ( document.body === null ) { // process remote webloc files content = document.documentElement.textContent; //messageSend('top_body','iframe_loaded','',[window_location,'',kind,null,content]); return; } if ( !hasClass('body','has_texteditorUI') ) { // add classes, styles, and scripts; only add once getEl('head').insertAdjacentHTML('beforeend',''); getEl('head').insertAdjacentHTML('beforeend',''); body_classes.push('has_texteditorUI', 'texteditor_view_'+getCurrentUIPref('texteditor_view')); } switch(true) { // get source text and append UI elements case !isTopWindow(): // iframe text editing UI window.onmessage = function(e) { messageReceive(e); return false; } // init receive messages is_link getEl('head').insertAdjacentHTML('afterbegin',''); getEl('head').insertAdjacentHTML('beforeend',''); // add iframe text editing styles if ( kind === 'link' ) { tempHideTexteditor(); } // prevent FOUC for some kinds of content... raw_markdown = decodeURIComponentSafe( getEl('body').innerText ); // get source text and decode Unicode chars. document.body.innerHTML = Content_Pane_Elements('content_text_elements') + Utilities_Elements('texteditor'); // add the UI getEl('#texteditor_raw_pane').value = raw_markdown; // set the source text value getEl('#texteditor_raw_pane').setSelectionRange(0,0); // set the insertion point to the beginning of the text if ( /^\#EXTM3U/m.test(getEl('#texteditor_raw_pane').value) ) { content = getEl('#texteditor_raw_pane').value.trim(); messageSend('top_body','iframe_playlist','',content); } // playlists & filelists addClass('body','is_text'); searchParamsGet().forEach((key, value) => { addClass('body',value+"_"+key); }); // add text editor body classes break; } switch(true) { // assemble text editing body classes case getCurrentUIPref('text_editing_enable') === 'false' && !isTopWindow(): // if text editing disabled... if ( hasClass('body','texteditor_view_html') ) { searchParamSet('texteditor_view','raw'); } removeClass('body','texteditor_view_html texteditor_split_view_true'); // remove split_view and view_html classes body_classes.push('text_editing_enable_false','texteditor_split_view_false','texteditor_view_'+getCurrentUIPref('texteditor_view'));// show the raw text, no split view setAttr('#texteditor_raw_pane','readonly',''); break; // disable textarea editing default: // ...otherwise set up text editing textEditorTogglePrefs('texteditor_split_view', ( getCurrentUIPref('texteditor_split_view') === 'false' ? 'false' : 'true' )); // set split view textEditorTogglePrefs('texteditor_sync_scroll',( getCurrentUIPref('texteditor_sync_scroll') === 'false' ? 'false' : 'true' )); // set sync scroll } TextEditing(); initTextEditorEvents(); // text editing functions & init text editor event listeners } // ===> BUILD UI: Append all assembled elements to body function buildUI() { let make_new_index, body_classes, main_content, agent = getBrowser(), link_info, file_name, kind, iframe_src; switch(true) { case isTopWindow(): // if it's not an iframe... make_new_index = makeNewIndex('body','',agent,'top_body'); // make index if ( make_new_index[0] === undefined ) { return; } // in case user cancels processing of large directory > 5000 items body_classes = make_new_index[0][1] +' '+ getUIPrefBodyClasses(agent); // delete extra spaces, create array of body class names main_content = `${ Sidebar_Elements('top_body') } ${ Content_Pane_Elements() } ${ Utilities_Elements('top_body') }`; // assemble html elements if ( make_new_index[0][1] !== 'is_error' ) { main_content = main_content.replace(/insert_prepped_index/,make_new_index[0][0]).replace(/insert_stats/,make_new_index[0][2]); // build dir_index and stats, add to MainContent } else { main_content = main_content.replace(/insert_prepped_index/,'').replace(/