/* eslint-disable no-mixed-spaces-and-tabs */ /* eslint-disable no-useless-escape */ /* eslint-disable no-fallthrough */ /* eslint-disable no-case-declarations */ /* eslint-disable indent */ /* eslint-disable quotes */ // ==UserScript== // @name Supercharged Local Directory File Browser // @version 6.1.2 // @description Makes directory index pages (either local or remote open directories) actually useful. Adds sidebar and preview pane; keyboard navigation; sorting; light & dark UI themes; browse subdirectories without reloading page; media playback, shuffle and loop options, support for playlists (m3u, extm3u), cuesheets (.cue); preview images & fonts singly or in navigable grids; create, preview, edit, 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 // @contributionURL https://paypal.me/mschrauzer // @include file://* // @include about:blank // @include https://www.example.com/path/to/directory/* // @require https://code.jquery.com/jquery-latest.min.js // @require https://cdn.jsdelivr.net/npm/markdown-it@11.0.0/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@latest/dist/opentype.min.js // UPDATE URL // NOTE: This script was developed in Vivaldi, running on Mac OS Mojave. It has been tested in various Chrome and Gecko-based browsers. // It has been minimally tested on Windows and not at all on other OSes. It should work, but please report any issues. // The script does not work on local directories in Safari because Safari does not allow local directories to be browsed, but it will work on remote directories (or on local directories through a local server). // NOTE: By default, Greasemonkey and Tampermonkey will not run scripts on file:/// urls, so for this script to work, you will have to enable it first. // For Tampermonkey, go to Chrome extension page, and tick the 'Allow access to file URLs' checkbox at the Tampermonkey extension section. // For Greasemonkey, open about:config and change greasemonkey.fileIsGreaseable to true. // @namespace https://greasyfork.org/users/16170 // @downloadURL none // ==/UserScript== (function() { 'use strict'; const $ = window.jQuery; // ***** USER SETTINGS ***** // const $settings = { // NOTE: These settings will be overwritten whenever the script is updated. Use the "Export User Settings" menu item to save them. // You can paste the exported settings between the two lines below: //--------------------------------------------------------// bookmarks: // N.B.: Directory links must end with "/", file links must end with another character. // You may add as many menus and links as you like; just copy the example below and edit as needed. // Local directory bookmarks must begin with "file:///"; external bookmarks must begin with the correct protocol ("http://" or "ftp://", etc.). [ // { 'menu_title':'My Sample Menu', // 'links': [ // { 'link_name':'My Directory Link 1', 'link':'file:///Path/To/My/Directory/' }, // { 'link_name':'My Directory Link 2', 'link':'file:///Path/To/My/Directory_2/' }, // { 'link_name':'My External Link', 'link':'https://www.mywebpage.com/' }, // { 'link_name':'My File Link', 'link':'file:///Path/To/My/File.ext' }, // ]}, { 'menu_title':'My Sample Bookmark Menu', 'links': [ { 'link_name':'My Directory Link 1', 'link':'file:///Path/To/My/Directory/' }, { 'link_name':'My Directory Link 2', 'link':'file:///Path/To/My/Directory_2/' }, { 'link_name':'My External Link', 'link':'https://www.mywebpage.com/' }, { 'link_name':'My File Link', 'link':'file:///Path/To/My/File.ext' }, ]}, ], // GENERAL USER SETTINGS alternate_background: true, // If true (default true), alternate sidebar row background color. apps_as_dirs: true, // Un*x/Mac OS only: if true, treat apps as directories; allows app contents to be browsed. This is the default behavior for Chrome. // If false (default), treat apps as ignored files. autoload_media: true, // If true (default), the first audio or video file found in a directory will be automatically selected and loaded for playback. // Also, cover art files (if any) will be loaded in the preview pane. // Files with 'cover','front','album','jacket','sleeve','cd','disc','insert','liner','notes' in the title will be loaded first in that order, with exact matches having preference. // Note that there can be false positives because a file will be matched whenever there is no exact match and one of these words appears in an image name. // Otherwise the first image file in directory will be loaded. autoload_index_files: true, // If true (default: false), automatically select first "index.xxx" (.xxx !== .htm) file found in directory. // Note: the browser will automatically load any index.html files it finds in the directory, so the script will not work properly in such cases. theme: 'light', // Options: 'light' or 'dark' sort_by: 'default', // Choose from: 'name', 'size', 'date', 'kind', 'ext', 'default'. // default = Chrome sorting: dirs on top, files alphabetical. sort_direction: 'down', // Choose from: 'up' or 'down' (default) (i.e., ascending or descending). dirs_on_top: false, // If true, directories will always be listed firs except when sorting by "name" (since otherwise sorting by "name" would equal "default"). // If false (default), directories and files will be sorted together. (In practice, dirs will typically still be separated when sorting by size, kind, and extension.) grid_font_size: 1, // Default = 1 grid_image_size: 184, // Default = 184 (200px - 16px) show_details: true, // If true (default), hide file and directory details; if false, show them. hide_ignored_items: false, // If true, ignored files (= files the browser cannot natively open, e.g., common office and graphics files) will be hidden. // If false (default), ignored files will appear greyed-out. ignore_ignored_items: true, // If true (default), clicking ignored file types (see $row_settings below) in the directory list will not cause the browser to attempt to open the file. // It is recommended to leave the default unchanged. // Ignored items can still be downloaded with ctrl-click or right-click. show_invisibles: true, // Un*x/Mac OS only: If true (default), files or directories beginning with a "." will be hidden. show_numbers: true, // If true (default true), number index items UI_font: 'system-ui, sans-serif', // Choose an installed font for the UI; if undefined, use browser defaults instead. UI_font_size: '13px', // Choose a default UI font size; use any standard CSS units. use_custom_icons: true, // if true (default), use custom icons for dirs and files // if false, use browser/server default icons // TEXT EDITING SETTINGS enable_text_editing: true, // If true (default), allow plain text files to be edited. editor_theme: 'default', // Options: 'default' = use "theme" setting, else always 'light' or 'dark'. default_text_view: 'source_text', // Options: 'source_text','preview_text','preview_html' // Note that split_view = true overrides this setting. split_view: false, // If true, show split view on plain text file load. // if false (default), use default preview_text setting. sync_scroll: true, // If true (default: true), show split view on plain text file load // if false, use default preview_text setting. hide_sidebar: false, // play_all_media: true // Continuous playback of all media files (i.e., both audio and video), not just selected media type. //--------------------------------------------------------// // Paste your exported settings between the above two lines. }; // $ITEM_KIND: // DO NOT DELETE ANY EXISTING CATEGORIES! // Add file extensions for sorting and custom icon display to the existing categories. // You can also define your own new categories, but do not add an extension to more than one row_type category. // Do not add leading "." to the extensions. const $item_kind = { // myRowType: ['ext1','ext2'], dir: ['/'], // loaded in iframe#content_iframe app: ['app/','app','bat','cgi','com','exe','jar','msi','wsf'], // generally ignored; 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','mp3','ogg','opus','wav'], // loaded in audio#audio bin: ['a','bundle','dll','dyld','dylib','gem','icc','msi','pyc','pyo','o','rakefile','ri','so','xml','2'], // ignored code: ['bak','bash','bash_profile','bashrc','c','cfg','cnf','codes','coffee','conf','csh','cshrc','cson','css','cue','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','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','idml','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','bmp','gif','ico','jpeg','jpg','png','svg','webp'], link: ['url','webloc','inetloc'], markdown: ['md','markdown','mdown','mkdn','mkd','mdwn','mdtxt','mdtext'], // treated as text, opened in iframe#content_iframe text editor office: ['csv','doc','docx','epub','key','numbers','odf','ods','odt','pages','rtf','scriv','wpd','wps','xlr','xls','xlsx','xlm'], // ignored pdf: ['pdf'], // open in embed#content_pdf system: ['DS_Store','ds_store','icon','ics','spotlight-v100/','temporaryitems/','documentrevisions-v100/','trashes/','fseventsd/','dbfseventsd','file','localized','programdata'], // ignored system items text: ['log','nfo','txt','readme'], // opened in iframe#content_iframe text editor video: ['m4v','mov','mp4','mpeg','webm'] // loaded in video#content_video }; // $ROW_SETTINGS: Ignore or Exclude files by extension const $row_settings = { // ignored: $item_kind or files with extensions added here will not be loaded if selected in the sidebar (prevents the browser from attempting to download the file). ignored: $item_kind.archive.concat( 'alias','m3u','m3u8', $item_kind.bin, $item_kind.database, $item_kind.graphics, $item_kind.ignored_image, $item_kind.office, $item_kind.system) }; // ***** END USER SETTINGS ***** // // ************ J + M + J ************* // // ************************************ // // DON'T EDIT ANYTHING BELOW THIS LINE. // // ************************************ // // If window.location points to a file, change window location to file container dir, add search_param of file name; then load file container directory and load file in content pane. function loadFileURL() { let search_params = getSearchParams(); 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 // ***** GENERAL SETUP ***** // function getBrowser() { switch(true) { case navigator.userAgent.search('Chrome') >= 0: return 'is_chrome'; case navigator.userAgent.search('Firefox') >= 0: return 'is_gecko'; case navigator.userAgent.search('MSIE') >= 0: return 'is_explorer'; case navigator.userAgent.search('Opera') >= 0: return 'is_opera'; case navigator.userAgent.search('Safari') >= 0 && navigator.userAgent.search('Chrome') < 0: return 'is_safari'; } } function getOS() { // modded from https://stackoverflow.com/questions/38241480/detect-macos-ios-windows-android-and-linux-os-with-js var platform = window.navigator.platform, macos_platforms = ['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; } // PATHS const newURL = function(link) { try { return new URL(link,document.baseURI); } catch(error) { return; } //console.log('This link is invalid. Please check the file.'); } }; function decodeURIComponentSafe(str) { // Fix "%" error in file name; see https://stackoverflow.com/questions/7449588/why-does-decodeuricomponent-lock-up-my-browser if ( !str ) { return str; } try { return decodeURIComponent(str.replace(/%(?![0-9a-fA-F]{2})/g,'%25') ); // replace % with %25 if not followed by two a-f/number } catch(e) { return str; } } const $protocol = window.location.protocol; const $origin = $protocol +'//'+ window.location.host; let current_location = decodeURIComponentSafe( [location.protocol, '//', location.host, location.pathname].join('') ); const current_dir_path = current_location.replace(/([/|_|—])/g,'$1').replace(/\\/g,'/'); // URL w/o query string for display const current_dir = current_location.split('/').slice(-2,-1).toString(); function escapeStr(str) { str = str.replace(/([$?*+()[]|^])/g,'\\$1'); return str; } // QUERY PREFS function getSearchParams() { return new URL(window.location).searchParams; } // set query key/value function setSearchParam(key, value) { let search_params = getSearchParams(); search_params.set( key, value ); updateSearchParams(search_params); } // get query value function getSearchParam(key) { let search_params = getSearchParams(), value = ''; switch(true) { case key === 'width': // set the stored sidebar width or use 30% value = ( !search_params.has(key) || window.innerWidth === 0 ? 30 : Math.round(100 * Number.parseInt(search_params.get('width'))/window.innerWidth) ); // percentage break; default: // if the query_string has a key/value pair, use it, otherwise use the key/value pair from the $settings value = ( search_params.has(key) ? search_params.get(key) : $settings[key] !== undefined ? $settings[key].toString() : '' ); value = value.replace('%2F','').replace('/',''); // some servers add a '/' to end of query string } return value; } // toggle query key function toggleSearchParam(key) { // set search parameter based on key let search_params = getSearchParams(); let non_bool_prefs = { // 'body_class,data-ui_pref': {'pref_name':'value'} 'theme_light': {'theme':'light'}, 'theme_dark': {'theme':'dark'}, // 'editor_theme': {'editor_theme':( getSearchParam('editor_theme') === 'default' ? getSearchParam('theme') : getSearchParam('editor_theme') === 'light' ? 'dark' : 'light') }, 'editor_theme': {'editor_theme':(getSearchParam('editor_theme') === 'light' ? 'light' : 'dark') }, 'editor_theme_default': {'editor_theme':getSearchParam('theme') }, 'editor_theme_light': {'editor_theme':'light'}, 'editor_theme_dark': {'editor_theme':'dark'}, 'source_text': {'default_text_view':'source_text'}, 'preview_text': {'default_text_view':'preview_text'}, 'preview_html': {'default_text_view':'preview_html'}, 'sort_by_default': {'sort_by':'default'}, 'sort_by_name': {'sort_by':'name'}, 'sort_by_size': {'sort_by':'size'}, 'sort_by_date': {'sort_by':'date'}, 'sort_by_kind': {'sort_by':'kind'}, 'sort_by_ext': {'sort_by':'ext'}, // 'sort_by_time': {'sort_by':'time'}, // don't add sort by time because times aren't available on initial page load 'sort_direction_down': {'sort_direction':'down'}, 'sort_direction_up': {'sort_direction':'up'}, }; var value, param_value, settings_value; switch(true) { case non_bool_prefs[key] !== undefined: value = Object.values( non_bool_prefs[key] ).toString(); // get the value for the key key = Object.keys(non_bool_prefs[key]).toString(); // must come after value: i.e., don't redefine key before getting value search_params.set( key,value ); // if the setting[key] = value, delete it from search_params; else set query_pref break; default: // boolean prefs settings_value = $settings[key]; // get the default pref value from $settings param_value = search_params.get(key); // see if pref is set in query prefs key = key.replace('toggle_',''); value = ( param_value === null ? settings_value.toString() : param_value.toString() ); // if pref is not set in queries, use the default value; else use the query value value = ( value === 'true' ? 'false' : 'true' ); // toggle value if ( ( param_value !== null && param_value !== settings_value ) ) { search_params.delete( key ); } else { search_params.set( key,value ); } } updateSearchParams(search_params); } // remove search param by key function removeSearchParam(key) { let search_params = getSearchParams(); search_params.delete(key); updateSearchParams(search_params); } // update search params function updateSearchParams(search_params) { let search_params_str = search_params.toString().replace('%2F','').replace('/',''); let new_location = ( search_params_str.length === 0 ? window.location.pathname : window.location.pathname +'?'+ search_params_str ); // don't add ? if no search params if ( search_params_str.length > -1 ) { window.history.replaceState({}, document.title, new_location); } updateParentLinks(); } // ***** SET UP UI ELEMENTS ***** // // Parent and Parents Menus // UTILITIES function updateParentSearchParams(str) { // decrement selected and history values let query_str = new URLSearchParams(str); // make new search params from window.location.search let history = ( query_str.has('history') ? query_str.get('history') : undefined ); if ( 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; } } else { query_str.delete('selected'); } str = decodeURIComponentSafe(query_str.toString()); return str; } function updateParentLinks() { $('#parents_dir_nav').siblings('ul').empty().append( createParentLinkItems()[0] ); } // create links function createParentLinks() { let link, links = [], query_str = window.location.search.toString().slice(1); let link_pieces = current_location.split('/'); // make array of parent directories link_pieces = link_pieces.slice(2,-2); // remove beginning and ending empty elements and current directory while ( link_pieces.length > 0 ) { // while there are link pieces... query_str = updateParentSearchParams(query_str); // update selected and history link = $protocol +'//'+ link_pieces.join('/') + '/?' + query_str; // assemble link links.push(link); // add to link array link_pieces.pop(); // remove last link piece and repeat... } return links; } // create menu items function createParentLinkItems() { let parent_link_menu_items = [], links = createParentLinks(); $('#parent_dir_nav').find('a').attr( 'href', links[0] ); // set parent link for ( let i = 0; i < links.length; i++ ) { let display_name = links[i].split('/?')[0]; display_name = display_name.replace(/\//g,'\/'); let menu_item = `
  • ${ display_name }/
  • `; parent_link_menu_items.push(menu_item); } return [parent_link_menu_items.join(''),(window.location.pathname === '/' ? window.location.href : links[0] )]; // return parents link items } // MENUS: User bookmarks function bookmarksMenuItems() { const bookmarks = $settings.bookmarks; let menu_items = [], links_arr = [], links_arr_str = '', links; if ( bookmarks.length > 0 ) { for ( let i = 0; i < bookmarks.length; i+=1 ) { links = bookmarks[i].links; // make array of links for ( let j = 0; j < links.length; j+=1 ) { if ( !links[j].link.endsWith('/') ) { links[j].link = links[j].link.slice(0,links[j].link.lastIndexOf('/') + 1) + '?file=' + links[j].link.slice(links[j].link.lastIndexOf('/') + 1) ; } links[j].link_name = links[j].link_name.split('/').join('/'); links_arr[j] = `
  • ${ links[j].link_name }
  • `; } links_arr_str = links_arr.join(''); menu_items[i] = `
  • ${ bookmarks[i].menu_title }
  • `; } menu_items = menu_items.join(''); } return menu_items; } // MENUS: Other menu items // #menu li a::before, .toggle_UI_pref, a, a:, div, li, span, span::before, span::after, #dir_list a.icon span, #warnings h3::before, #content_audio_title td:before const sidebar_menu_items = `
  • Sort by…
  • Theme
  • Alternate Backgrounds
  • Show Numbers
  • Ignored Items
  • Autoload Files
  • Media Files
  • Playlists
  • Text Editing
  • Text Editing Options
  • User Settings
  • About
  • Help
  • Contact
  • `; const SidebarHeaderEls = function() { let checked = '', parent_links = createParentLinkItems(); if ( getSearchParam('show_invisibles') === 'true' ) { checked = 'checked'; } return ``; }; // Sidebar Footer (Stats) const sidebar_footer_els = `
    `; // Dir List Elements const sidebar_dir_list_els = `
    `; // CONTENT PANE ELEMENTS // Content Audio Els const content_audio_els = `
     
     
    `; // ASSEMBLE SIBEBAR & CONTENT PANE ELEMENTS const MainContent = `
    ${ content_header_els } ${ content_els }
    ${ warnings }
    ${ content_help }
    `; // DEFINE Content Elements const $main_content = $(MainContent); const $dir_list_body = $main_content.find('#tbody'); const $dir_list = $main_content.find('#dir_list'); const $content_pane = $main_content.find('#content_pane'); const $audio_player = $main_content.find('#audio'); const $content_grid = $main_content.find('#content_grid'); const $content_font = $main_content.find('#content_font'); const $content_image_container = $main_content.find('#content_image_container'); const $content_image = $content_image_container.find('img'); const $content_video = $main_content.find('#content_video'); const $content_iframe = $main_content.find('#content_iframe'); // SVG UI ICONS // auto format dark icon colr function SVG_UI_Icon(icon_name) { return `url("data:image/svg+xml;utf8,${ SVG_UI_Icons[icon_name] }")`; } // get svg UI icon by name const SVG_UI_Icons = { 'arrow': '', 'arrow_dark': '', 'bookmark': '', 'bookmark_dark': '', 'check_mark': '', 'chevron_up': '', 'chevron_right': '', 'chevron_down': '', 'chevron_left': '', 'document': '', 'error': '', 'folder': '', 'grid': '', 'grid_dark': '', 'grid_loaded': '', 'grid_loaded_dark': '', 'ignored': '', 'ignored_dark': '', 'menu': ' ', 'minus': '', 'multiply': '', 'music': '', 'plus': '', 'prev_next_track': '', 'prev_next_track_ff': '', 'spinner': '', 'toggle': '', }; function SVG_UI_File_Icon(icon_name) { switch(icon_name) { case 'favicon': return 'iVBORw0KGgoAAAANSUhEUgAAAIAAAACAAgMAAAC+UIlYAAAACVBMVEUmRcmZzP8zmf8pVcWPAAAAAXRSTlMAQObYZgAAAFBJREFUeF7tyqERwDAMBEE3mX5UiqDmqwwziTPHjG7xrmzrLFtRaApDIRiKQlMYCsFQFJrCUAiGotAU5hTA1WB4fhkMBsOJwWAwgHvB8CHpBcTbpxy4RZNvAAAAAElFTkSuQmCC'; 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] +'")'; } } 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': '', // the following are the same: 'file_icon_bin': '', 'file_icon_other': '', 'file_icon_system': '' }; // Programatically add File icon CSS rules function CSS_UI_Icon_Rules() { let rules = '', kind, class_name; rules = ` #menu ul a::before { background-image:${ SVG_UI_File_Icon('file_icon_file') }; } #menu ul a[href^="file"]::before { background-image:${ SVG_UI_File_Icon('file_icon_dir') }; } #menu ul a[href^="http"]::before { background-image:${ SVG_UI_File_Icon('file_icon_htm') }; } body:not(.use_custom_icons) #dir_list tr.dir a.icon span::before { background-image:${ SVG_UI_File_Icon('file_icon_dir_default') }; background-size:auto 13px; } body:not(.use_custom_icons) #dir_list tr.file:not(.app) a.icon span::before { background-image:${ SVG_UI_File_Icon('file_icon_file_default') }; background-size:auto 13px; }`; for ( let icon in SVG_UI_File_Icons ) { kind = icon.slice(icon.lastIndexOf('_') + 1); class_name = kind; // 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'; } // add rules for dir_list items, content_header, stats details: rules += `body.use_custom_icons #dir_list tr.${ class_name } a.icon span.has_icon_before_before, #content_pane[data-content="has_${ class_name }"] #title span::before, body.use_custom_icons #stats tbody tr.${ class_name } .stats_kind.has_icon_before::before { background-image: url("data:image/svg+xml;utf8,${ SVG_UI_File_Icons['file_icon_'+kind] }"); }`; } return rules; } // Text Editing UI Icons function SVG_Text_Editing_UI_Icon(icon_name) { return `url("data:image/svg+xml;utf8,${ SVG_Text_Editing_UI_Icons[icon_name] }")`; } const SVG_Text_Editing_UI_Icons = { 'toggle_theme': '', 'show_markdown': '', 'show_source': '', 'show_preview': '', 'show_html': '', 'toggle_split': '', 'save_btn': '', 'save_btn_edited': '' }; //***** END UI ELEMENTS //***** STYLES *****// const $warning_styles = ` #warnings_container { width:26em; transform:translate(-50%, 0); display:none; flex-direction:column; border-radius:0 0 3px 3px; position:absolute; top:0; left:50%; z-index:9999; box-shadow:0px 2px 12px 0 #333; font-size:0.875em; color:#111; overflow:hidden; } body.has_warning #warnings_container { display:flex; } #warnings_header { padding:1rem 1.5rem; background-position:left 1.25rem center; background-repeat:no-repeat; background-size:24px; } #warnings_container:not(.warning_make_playlist) #warnings_header { background-image:${ SVG_UI_Icon("error") }; } #warnings_header h3 { display:none; margin:0; text-indent:2.25em; } #warnings_container:not(.warning_make_playlist) h3#warning_header, #warnings_container.warning_make_playlist h3#make_playlist_header { display:block;} body#top.edited #warnings_container.unloading h3::before { content: "Text Editor: " } #warnings .warning { padding: 0 1.5rem 1rem; display:none; hyphens:none; } #warning_buttons_container { padding:1rem 1.5rem; } #warning_buttons { display:flex; flex-direction:row; } #warning_buttons button { min-width:4em; display:none; font-size:1em; } button.focus, button:focus { background-color: #0E4399; color: #EEE; outline:none; } #warning_btn_dont_save { margin-right: auto; } #warning_btn_cancel, #warning_btn_clear, #warning_btn_save { margin-left: 0.5rem; } #warning_btn_ok { margin-left:auto; } #warnings_container.warning_close_font #warning_close_font, #warnings_container.warning_close_playlist #warning_close_playlist, #warnings_container.unloading #warning_unsaved_text, #warnings_container.unloading #warning_btn_dont_save, #warnings_container.unloading #warning_btn_cancel, #warnings_container.unloading #warning_btn_save { display:inline-block; } #warnings_container.clear #warning_buttons { justify-content:space-between; } #warnings_container.clear #warning_clear_text, #warnings_container.clear #warning_btn_cancel, #warnings_container.clear #warning_btn_clear { display:inline-block; } #warnings_container.warning_local_bookmark #warning_local_bookmark, #warnings_container.warning_local_bookmark #warning_btn_ok { display:inline-block; margin-left:auto; } #warnings_container.warning_local_file #warning_local_file, #warnings_container.warning_local_file #warning_btn_ok { display:inline-block; margin-left:auto; } #warnings_container.warning_close_font #warning_btn_ok { display:inline-block; margin-left:auto; } #warnings_container.warning_close_font #warning_btn_cancel { display:inline-block; } #warnings_container.warning_close_playlist #warning_btn_ok { display:inline-block; margin-left:auto; } #warnings_container.warning_close_playlist #warning_btn_cancel { display:inline-block; } #warning_make_playlist fieldset { margin:0; padding:0; border:0; } #warning_make_playlist fieldset div { padding:0 0 2px; } #warning_make_playlist .indent { text-indent:2em; } #warning_make_playlist input { margin-right:6px; } #warnings_container.warning_make_playlist #warning_make_playlist { display:flex; flex-direction:column; } #warnings_container.warning_make_playlist #warning_btn_ok, #warnings_container.warning_make_playlist #warning_btn_cancel { display: inline-block; } #warnings_container.warning_no_playlist #warning_no_playlist, #warnings_container.warning_no_playlist #warning_btn_ok { display: inline-block; } #warnings_container.warning_local_playlist #warning_local_playlist, #warnings_container.warning_local_playlist #warning_btn_ok { display:inline-block; margin-left:auto; } `; var $main_styles = ` :root, html, body { margin:0; padding:0; width:100%; max-width:100%; height:100vh; overflow:hidden; border:0; border-radius:0; font-family: ${$settings.UI_font}; font-size: ${ $settings.UI_font_size}; hyphens:auto; display:flex; } table { width:100%; border:0; border-collapse: collapse; } #sidebar_wrapper li, #toolbar li { list-style:none; } a, a:hover { text-decoration: none !important; } button { padding:2px 6px; border:solid 1px #333; border-radius:3px; cursor:pointer; height:18px; font-family:${$settings.UI_font} !important; font-size:1em !important; line-height:0;} button.focus, button:focus { outline:none; border-radius:3px !important; border-style:solid !important; border-width:1px !important; border-color:#222 !important; } textarea:focus, audio:focus { outline:none; } #main_content { width:100%; display:flex; flex-direction:row; overflow:hidden; } /***** SIDEBAR STYLES *****/ #sidebar_wrapper { min-width:220px; padding:0; position:relative; z-index:1; display:flex; flex-direction:column; } #sidebar { overflow:hidden; font-size:0.875rem; display:flex; flex-direction:column; flex-basis:100%; } #sidebar_header { font-size:0.875rem; position:relative; z-index:3; user-select:none; -webkit-user-select:none; display:flex; flex-direction:column; } #sidebar_title { font-weight:normal; display:flex; flex-direction:row; } #sidebar_title div { padding: 4px 6px; text-align:center; letter-spacing:0.5em; text-indent:0.75em; flex-basis:100%; } #sidebar_header_body { display:flex; flex-direction:column; } #hide_sidebar { position:absolute; top:0; right:0; cursor:pointer; width:24px; height:21px; z-index:9997; background-image: ${ SVG_UI_Icon("toggle") }; background-size:18px; background-position:center; } #sidebar ul { -webkit-margin-before:0em !important; -webkit-margin-after:0em !important; -webkit-padding-start:0em; } #sidebar_menus { cursor:pointer; display:flex; flex-direction:row; } /* PARENT MENU */ #parent_dir_menu { flex-basis:24px; max-width:24px; min-width:24px; padding:0; position:relative; } #parent_dir_nav { margin:0; padding:0; display:block; position:absolute; top:0; right:0; bottom:0; left:0; } #parent_dir_nav a { height:100%; padding:0; text-align:center; background-position:center; background-repeat:no-repeat; background-size:12px; } /* PARENTS MENU */ #parents_dir_menu { padding:0; flex-grow:1; } #parents_dir_nav { margin:0; padding:0; display:flex; line-height:1.4; } #current_dir_path { cursor:pointer; flex:auto; font-weight:bold; hyphens:none; padding:4px 6px 4px 6px; text-align:center; word-break:break-word; z-index:9998; } #close_playlist_btn_container { display:none; flex-direction:column; justify-content:center; flex-basis:24px; max-width:24px; min-width:24px; padding:0; } #close_playlist_btn { background-image:${ SVG_UI_Icon("multiply") }; background-position:center; background-size:12px; display:flex; flex-direction:column; justify-content:center; flex-basis:24px; max-width:24px; min-width:24px; padding:0; opacity:0.55; } #parents_links { margin:0; padding:0; display:none; position:absolute; right:0; left:0; box-shadow: 0px 4px 6px -3px #333; z-index:9998; } #parents_links a { margin:0; padding:4px 8px; display:block; } /* MAIN MENU */ #menu_container { display:flex; flex-direction:column; justify-content:center; flex-basis:24px; max-width:24px; min-width:24px; padding:0; } #menu_nav { margin:0; padding:0; display:block; cursor:pointer; } #menu_nav div { width:24px; background-image: ${ SVG_UI_Icon("menu")}; background-position:center; background-repeat:no-repeat; background-size:13px; } #menu { display:none; margin:0; padding:0; position:absolute; right:0;left:0; zindex:9998; box-shadow: 0px 4px 6px -3px #333; z-index:9998; } #menu li.has_submenu { position:relative; background-position:right 6px center; background-repeat:no-repeat; background-size:12px; } #menu li.bookmark a::before { content:""; width:24px; height: 12px; background-size:12px; } .submenu { display:none; margin:0; padding:0; position:absolute; top:-1px !important; left:100%; right:0; width:100%; box-shadow:0px 4px 6px -3px #333; width:100%; max-width:240px; } #menu ul.submenu li a { margin:0; padding:6px 8px 6px 0; } #menu input { width:0; float:left; } .menu_item { margin:0; padding:6px 8px 6px 0; display:flex; } .menu_item::before { content:""; width:24px; height:9px; margin:2px 0 -2px; display:inline-block; background-position:center; background-repeat:no-repeat; } li.has_submenu:hover > span, .menu_item:hover, #sidebar_menus li.selected > span { font-weight:bold; } /* SIDEBAR BUTTONS */ #sidebar_buttons { position:relative; display:flex; flex-direction:row; } #sidebar_buttons_left { padding:6px; } #show_details { margin-top:0; margin-right:0.5em; padding:0 4px 0 4px; } #show::before { content:"Show "; } #show_invisibles_container { cursor:pointer; display:inline-flex; vertical-align:text-top; padding:0; height:100%; } #show_invisibles { cursor:pointer; margin:0 4px 0 0; } #show_invisibles:hover span { font-weight:bold; } /* GRID BTN */ #grid_btn { display:none; margin:0 0 0 auto; padding:0; width:24px; position:relative; z-index:9997; background-color:inherit; background-position:center; background-size:14px; cursor:pointer; outline:none; } #grid_btn ul { display:none; margin:0; padding:0; padding-right:24px; padding-left:0px; position:absolute; top:-1px; right:-1px; background-position:right 5px top 8px; background-repeat:no-repeat; background-size:14px; box-shadow:0px 4px 6px -3px #333; } #grid_btn ul li { padding:4px 6px; text-align:right; width:100%; white-space:pre; box-sizing:border-box; } /* SORTING ITEMS */ #sorting_row_1 { display:flex; cursor:pointer; justify-content:space-between; flex-direction:row; } #sorting_row_2 { display:none; cursor:pointer; justify-content:space-between; flex-direction:row; } #sorting_row_1 div, #sorting_row_2 div { flex-grow:1; padding:0; } #sorting .sorting { white-space:pre; } #sorting_row_1 span { display:inline-block; padding:6px 0; } #sorting_row_2 span { display:inline-block; padding:0 0 6px 0; } .sorting:hover span { font-weight:bold; } .sorting span::before, .sorting span::after { content:""; width:16px; height:8px; display:inline-block; position:relative; color:#CCC; background-position:center; background-repeat:no-repeat; background-size:10px; } .sorting.down span::after { transform:rotate(180deg) } #sort_by_name input { display:none; margin:0 4px 0 2px; } #sort_by_name, #sort_by_size { text-align:left; } #sort_by_date, #sort_by_kind { text-align:center; } #sort_by_default, #sort_by_time, #sort_by_ext { text-align:right; } body.sort_direction_up .sorting span::after { transform:rotate(180deg) !important; } #sort_by_time { display:none; } body.has_media #sort_by_name span { padding:4px 0; } body.has_media #sort_by_name input { position:relative; bottom:-2px; } body.has_media #sort_by_default { text-align:center; } body.has_media #sort_by_time, body.has_playlist #sort_by_time { display:block; } /* TEXT EDITOR ROW */ #text_editor_row { display:none; } #text_editor_row a { padding:6px 0 6px 16px; text-align:left; font-weight:bold; } /***** DIR_LIST STYLES *****/ #dir_list_wrapper { overflow:scroll; position:relative; margin-bottom:-1px; } #dir_list { overflow:hidden; position:relative; font-size:0.875rem; } #tbody { counter-reset:row; height:100%; overflow:hidden; transition:opacity .125s; background-position:center; background-repeat:no-repeat; background-size:50%; } #tbody > tr { margin-inline-start:0; display:none; grid-gap:0; grid-template-columns:minmax(auto,6rem) minmax(12em,1fr) minmax(auto,6em); } .tbody_row_cell { padding:0; } .tbody_row_cell_name { grid-column: 1 / span 3; font-variant-numeric:tabular-nums; } .tbody_row_cell_name_a { display:flex; text-decoration:none; -webkit-padding-start:0; -moz-padding-start:0; } .tbody_row_cell_name_a::before { display:none; counter-increment:row; content:counter(row); width:36px; min-width:36px; height:14px; max-height:14px; min-height:14px; text-align:right; padding-right:1px; } .tbody_row_cell_name_a_span { display:flex; line-height:1.4; text-align:left; word-break:break-word; } .tbody_row_cell_name_a_span input { display:none; margin:2px 6px 0 0; } .tbody_row_cell_details { display:none; text-align:right; white-space:nowrap; max-height:1em; font-variant-numeric:tabular-nums; padding:0 12px 3px 0; } .tbody_row_cell_details.size { grid-column: 1; grid-row: 2; padding:0 8px 3px 8px; } .tbody_row_cell_details.date { grid-column: 2; grid-row: 2; text-align:right; overflow:hidden; overflow-wrap:break-word; text-overflow:""; padding-right:8px; } .tbody_row_cell_details.date span { white-space:pre; display:block; float:right; } .tbody_row_cell_details.kind { grid-column: 3; grid-row: 2; overflow:hidden; text-overflow:ellipsis; } .tbody_row_cell_details.kind::first-letter { text-transform:uppercase; } .tbody_row_cell_name_a { padding:5px 12px 5px 0; } .tbody_row_cell_name_a::before { margin-top:1px; } .tbody_row_cell_media_duration { display:none; grid-column: 3 / span 1; font-variant-numeric:tabular-nums; padding:6px 12px 3px 6px; text-align:right !important; } .has_subdirectory .dir_list_subdir { display:block; grid-column:1/ span 3; grid-row:3; width:100%; border:0; } tr.media:not(.local) .tbody_row_cell_name { grid-column: 1 / span 2; } tr.media:not(.local) .tbody_row_cell_name_a { padding-right:0; } tr.media:not(.local) .tbody_row_cell_media_duration { display:inline-block; } tr.media.local input { cursor:not-allowed; opacity:0.5; outline:solid #666 1px; } #tbody tr:hover a, tr.selected a, tr.media[class*="_loaded"] a { font-weight:bold; } tr.disabled, tr.disabled a, tr.disabled span, body.ignore_ignored_items tr.ignored, body.ignore_ignored_items tr.ignored a { cursor:not-allowed; } #dir_list #tbody .error { display:block; padding:6px 8px; } #dir_list #tbody .error::before { display:none; } /***** SIDEBAR FOOTER *****/ #tfoot { margin-top:auto; padding:0; position:relative; font-size:0.875rem; user-select:none; -webkit-user-select:none; display:flex; flex-direction:column; } /* STATS */ #stats_container { display:block; max-height: ${ (window.innerHeight * 0.33) }px; } #stats { cursor:pointer; float:left; width:100%; overflow:hidden;font-size:0.875rem; } body:not(.has_stats) #stats tbody, body.has_stats #stats_summary, body:not(.has_stats) #stats_summary_detailed_container, #stats .stats_kind span.file, #stats .stats_kind span.media { display:none; } body.theme_light #footer_links:hover ul, body.theme_light #tfoot:hover, body.theme_light #stats_summary_detailed_container { box-shadow: 0px -4px 4px 0px rgba(128,128,128,0.6); } body.theme_dark #footer_links:hover ul, body.theme_dark #tfoot:hover, body.theme_dark #stats_summary_detailed_container { box-shadow: 0px -4px 4px 0px rgba(32,32,32,0.6); } #stats_summary_detailed_container { overflow-y:scroll; } #stats_summary_detailed_dirs .stats_kind::before { background-image:${ SVG_UI_File_Icon("file_icon_dir_default") }; } #stats_summary_detailed_files .stats_kind::before { background-image:${ SVG_UI_File_Icon("file_icon_file_default") }; } #stats_details_container { display:block; position:relative; overflow-y:scroll; } #stats_details { display:block; } #stats_summary th { font-weight:normal; } #stats_summary_detailed_container tr:hover, #stats_details tr:hover { font-weight:bold; } #stats tr { line-height:1.2; } #stats a { padding:4px 12px 4px 0; } #stats_summary th, #stats_summary_detailed_total th, #stats_summary_playlist_container { padding:5px 8px; overflow:hidden; white-space:pre; } #stats a::before { content:attr(data-count); } #stats .stats_kind span { display:inline-block; margin-right:0.5em; white-space:nowrap; } #stats .stats_kind span::first-letter { text-transform:uppercase } /* SIDEBAR FOOTER LINKS */ #footer_links { margin-top:-1px; position:absolute; z-index:1; right:0; cursor:pointer; float:right; width:24px; height:100%; background-image:${ SVG_UI_Icon("toggle") }; transform:rotate(180deg) !important; background-size:18px; background-position:2px center; } #footer_links ul { display:none; margin:0; padding:0; position:absolute; top:calc(100% - 1px); right:unset; left:calc(100% - 27px); white-space:nowrap; box-shadow:-0px -3px 6px -3px #333; transform:rotate(180deg) !important; } #open_in_content_pane { padding:4px 6px; text-align:right; } #view_directory_source { padding:4px 6px; text-align:right; } /* CLASSES AND ELEMENTS */ .has_icon_before::before, .has_icon_before_before { content:""; display:block; float:left; max-width:28px; width:28px; min-width:28px; height:14px; max-height:14px; min-height:14px; margin-top:1px; background-position:center; background-repeat:no-repeat; background-size:14px; } tr.ignored .has_icon_before::before, tr.ignored .has_icon_before_before { opacity:0.75; filter:grayscale(100%); } .has_checkmark_before::before { content:""; height:9px; background-position:center; background-repeat:no-repeat; } ul.has_popout_menu, .editor_theme_light #toolbar ul.has_popout_menu { background-color:#C0C0C0; border:solid 1px #666; } ul.has_popout_menu li { background-color:#D0D0D0; } ul.has_popout_menu li:hover { background-color:#E0E0E0; } .theme_dark #sidebar_wrapper ul.has_popout_menu { border:solid 1px #111; } .theme_dark #sidebar_wrapper ul.has_popout_menu, .theme_dark #sidebar_wrapper ul.has_popout_menu li { background-color:#505050; } body.editor_theme_dark #toolbar ul.has_popout_menu li { background-color:#C0C0C0; } .theme_dark #sidebar_wrapper ul.has_popout_menu li:hover { background-color:#686868; } /***** END SIDEBAR STYLES *****/ /***** CONTENT STYLES *****/ #content_pane { height:100%; padding:0; position:relative; transform:scale(1); vertical-align:top; display:flex; flex-direction:column; contain:strict; } #content_header { display:block; font-size:0.875rem; position:relative; z-index:3; } #content_header table { font-size:0.875rem; z-index:2; } #content_title { text-align:center; display:flex; flex-direction:row; justify-content:space-between; } /* CONTENT TITLE BUTTONS LEFT */ #title_buttons_left, #title_buttons_right { display:flex; padding:4px 6px; text-align:left; width:auto; white-space:nowrap; } #reload_btn { width:52px; } #reload_btn::before { content:"Reload"; } #prev_next_btns { margin-left:4px; cursor:pointer; line-height:1; padding:0; position:relative; } #prev_next_btns span { width:2em; height:18px; } #prev_btn, #next_btn { background-position:center 36%; background-repeat:no-repeat; background-size:33%; } #prev_btn { background-image:${ SVG_UI_Icon("chevron_left") }; } #next_btn { background-image:${ SVG_UI_Icon("chevron_right") }; } /***** CONTENT TITLE *****/ #title { line-height:1.4; min-width:10em; min-height:18px; padding:4px 8px; text-align:center; word-break:break-word; vertical-align:top; cursor:pointer; } #title span { font-weight:bold; hyphens:none; } #title span::before { content:""; margin-top:1px; margin-bottom:-2px; width:24px; height:14px; display:inline-block; background-position:center; background-repeat:no-repeat; font-weight:normal; } #title span::after { font-weight:bold; } #content_pane[data-content="has_image"] #title span::after { font-weight:normal; } /* CONTENT TITLE BUTTONS RIGHT */ #title_buttons_right { text-align:right; } #scale { cursor:pointer; line-height:1; margin-right:4px; padding:0 4px; position:relative; background-color:#FFF; } #scale span { width:2em; height:18px; background-position:center 36%; background-repeat:no-repeat; background-size:10px; } #decrease { margin-left:-4px; background-image:${ SVG_UI_Icon("minus") }; } #increase { margin-right:-4px; background-image:${ SVG_UI_Icon("plus") }; } #close_btn { padding:0px; width:52px; } #close_btn::before { content:"Close"; } .split_btn { display:none; } .split_btn span { display:inline-flex; } .split_btn::after { content:""; position: absolute; top: 0; bottom: 0; left: 50%; } #open_in_text_editor { display:none; margin-right:4px; } /* CONTENT AUDIO TITLE */ #content_audio_title { display:none;} #content_audio_title span { display:block; width:100%; cursor:pointer; padding:4px 6px 6px; font-weight:bold; text-align:center; line-height:1.4; } #content_audio_title span::before { content:""; padding-right:18px; font-weight:normal; background-position:center; background-position:right 4px center; background-repeat:no-repeat; } /* CONTENT AUDIO PLAYER */ #content_audio { display:none; justify-content:center; padding-bottom:6px; position:relative; } #content_audio > div > div { text-align:center; font-weight:bold;} #audio_container { padding:0; height:32px; display:flex; background-color:rgb(241, 243, 244); flex-direction:row; } #prev_track, #next_track { width:2rem; padding:0; display:inline-block; overflow:auto; background-image:${ SVG_UI_Icon("prev_next_track") }; background-position:center; background-repeat:no-repeat; } #prev_track { transform:rotate(180deg) !important; } #audio { height:32px; } audio::-webkit-media-controls-panel { padding:0; } audio::-webkit-media-controls-enclosure { border-radius:0; } #close_audio { width:32px; padding:0; position:relative; display:inline-block; background-image:${ SVG_UI_Icon("multiply") }; background-position:center; background-repeat:no-repeat; background-size:14px; } #audio_options { margin-top:0; margin-right:calc(-6em - 8px); padding:0 4px; width:6em; display:flex; flex-direction:column; justify-content:center; } #loop_label input { margin:0px 4px 2px} #shuffle_label input { margin:2px 4px 0px} #audio_iframe { margin:0; padding:0; border:0; } /* CUE SHEET MENU */ .cue_sheet_track_list_container { display:none; width:32px; background-image:${ SVG_UI_File_Icon("file_icon_playlist") }; background-position:center; background-repeat:no-repeat; background-size:18px; background-blend-mode:luminosity; background-color:inherit; } .cue_sheet_track_list { display:none; position:absolute; width:100%; margin:0; padding:0; left:0; right:0; z-index:1; overflow:scroll; } #cue_sheet_track_list_container_audio .cue_sheet_track_list { top:33px; border-top:solid 7px transparent; } .cue_sheet_track_list_container:hover .cue_sheet_track_list { display:block; } .cue_sheet_track { list-style:none; display:grid; grid-gap:0; grid-template-columns:minmax(4em,6em) minmax(25%,75%) minmax(25%,75%) minmax(6em,8em); } .cue_sheet_track.header { font-weight:bold; cursor:default; } .cue_sheet_track:not(.header):hover { font-weight:bold; cursor:pointer; } .cue_sheet_track.selected { font-weight:bold; } .cue_sheet_track.selected .cue_track_id::before { content:""; width:16px; height:8px; display:inline-block; } .cue_sheet_track span { padding:4px 8px; font-variant-numeric:tabular-nums; } .cue_sheet_track span.cue_track_id { text-align:right; } .cue_sheet_track span.cue_index { text-align:right; } #cue_sheet_background { position:absolute; top:0; right:0; bottom:0; left:0; z-index:-1; } #cue_sheet_title { display:block; padding:4px 8px; font-variant-numeric:tabular-nums; text-align:center; } /* CONTENT TITLE PLAYLIST ENTRY (#content_playlist and #content_audio_playlist) */ .playlist_entry_container { display:none; padding:4px 6px; text-align:center; flex-direction:row; } .playlist_entry_container textarea { width:100%; padding:0 6px; border:0; resize:vertical; } /***** CONTENT_CONTAINER *****/ #content_container { box-sizing:border-box; justify-content:center; padding:0; position:relative; bottom:0; overflow:auto; width:100%; background-position:center; background-repeat:no-repeat; background-size: 50%; display:flex; flex-basis:100%; contain:strict; } .content { display:none; overflow:scroll; width:100%; height:100% } /* CONTENT GRID (div) */ #content_grid { display:none; position:absolute; padding:0; width:100%; font-size:1rem; grid-gap:0; grid-template-columns:repeat(auto-fill, minmax(${ ( $settings.grid_image_size + 16) }px, auto)); grid-auto-rows:minmax(min-content, max-content); } #content_grid::after { content:""; width:1px; position:absolute; top:0; right:0; bottom:0; } #content_grid a { display:block; } /* IMAGE GRID ITEMS */ .image_grid_item { padding:6px; grid-column:auto; display:flex; align-items:center; justify-content:center; line-height:0; } .image_grid_item img { width:auto; max-width:${ ($settings.grid_image_size).toString() }px; max-height:${ ($settings.grid_image_size) }px; position:relative; } .image_grid_item img[src$=".svg"] { width: 100%; height:100%; } /* FONT GRID ITEMS */ .font_grid_item { line-height:1; padding:8px 20px; grid-column: 1 / -1; } .font_grid_item p { margin:0; padding:0 0 6px 0; line-height:1; font-size:1rem; letter-spacing:0.1em; text-indent:0.1em; } .font_grid_item h2 { margin:0; font-weight:normal; font-size:${ $settings.grid_font_size * 4 }em; } .image_grid_item + .font_grid_item { margin-top:-1px; } /* CONTENT FONT.content */ #content_font { hyphens:none; padding:0; position:relative; font-size:${ $settings.grid_font_size }em; overflow-wrap:break-word; } #font_specimen { max-width:100%; display:none; line-height:1.2; flex-direction:column; } #specimen { padding: 20px; font-size:4em; word-break:break-all; line-height:1.2; } #specimen_2 { margin:0; font-weight:normal; font-size:8em; overflow:hidden; text-overflow:ellipsis; white-space:pre; } #specimen_2H4 { margin:0; font-weight:normal; font-size:1.618em; overflow:hidden; text-overflow:ellipsis; } #specimen_3 { margin:0; font-weight:normal; font-size:6em; text-align:justify; overflow:hidden; text-overflow:ellipsis; white-space:pre; } #specimen_3H3 { margin:0; font-size:2em; hyphens:auto; } #specimen_string_2 { padding:20px; text-align:justify; hyphens:auto; } #specimen_string_3 { padding:20px; text-align:justify; } .lorem { text-align:justify; hyphens:auto; font-size:1em; line-height:1.4; column-gap:1.5em; overflow-wrap:normal; word-break:normal; } #lorem { padding:20px 20px 0; } #lorem::first-line { letter-spacing:0.1em; text-indent:0.1em; font-size:${ $settings.grid_font_size * 1.33 }em; font-variant:small-caps; } #lorem_2 { padding:12px 20px 0; columns:2; } #lorem_3 { padding:12px 20px 40px; columns:3; } /* FONT GLYPHS */ #font_viewer { display:none; position:relative; font-family:unset; width:100%; } #content_pane[data-content="has_font_file"] #font_viewer { overflow-y:auto; } #content_pane[data-content="has_glyph"] #font_viewer { overflow:auto; } #content_pane[data-content="has_glyph"] #glyphs_container { visibility:hidden; } #glyphs_container { padding:0; position:relative; text-align:center; display:grid; grid-gap:0; grid-template-columns:repeat(auto-fill, minmax(120px,auto)); } .glyph_container { padding:0; position:relative; } .glyph_info { padding:2px; position:absolute; right:0; bottom:0; left:0; font-size:0.75rem; } #glyph_viewer { display:none; padding:0; z-index:1; position:absolute; top:0; right:0; bottom:0; left:0; background-color:#FFF; background-position:center; background-repeat:no-repeat; background-size:contain; } #glyph_viewer_info { padding:4px 6px; position:fixed; right:0; left:0; text-align:center; height:18px; line-height:1.6; font-size:0.875rem; } #content_font svg { width: 100%; } #save_svg_hidden { float:left; visibility:hidden; } #glyph_viewer_info div { padding:0; display:inline-block; } #glyph_viewer_info div::before { content:"Glyph "; } #save_svg { float:right; } #font_info { max-height:${ (window.innerHeight * 0.75) }px; font-size:0.875rem; position:fixed; bottom:0; z-index:2; } #font_info:hover { box-shadow:0px 4px 6px 3px #333; } #font_info th { padding:4px 6px 5px; letter-spacing:0.1em; text-indent:0.1em; } #font_info_body { display:none; max-height:${ ((window.innerHeight * 0.75) - 64) }px; font-size:0.875rem; overflow:auto; } .font_info_name { padding:4px 6px; text-align:right; font-weight:bold; width:33%; } .font_info_value { padding:4px 6px; width_66%; } .font_info_value a { font-weight:bold; } /* CONTENT TEXT EDITOR */ #content_text { display:none; width:100%; max-width:100%; height:100%; overflow:hidden; padding:0; position:absolute; z-index:1; flex-direction:column; } /* CONTENT IMAGE.content */ #content_image_container { display:none; margin:0; padding:2rem 2.5rem; position:relative; overflow:auto; box-sizing:border-box; } #content_image { margin:auto; width:auto; max-width:100%; max-height:100%; position:relative; object-fit:contain; cursor:zoom-in; } #content_pane.has_zoom_image #content_image_container { padding:0; } /* OTHER CONTENT ELEMENTS */ #content_pdf { height:100%; padding:0; position:relative; width:100%; } #content_video { padding:0; position:absolute; background:transparent; } #content_iframe { display:none; width:100%; height:100%; padding:0; position:relative; background:white; border:0; } #content_iframe_utility { display:none; } /* WARNING STYLES */ ${ $warning_styles } /* HELP STYLES */ #help_container { display:none; padding: 0 1em 1em; overflow:scroll; position:absolute; top:0; right:0; bottom:0; left:0; z-index:9998; contain:strict; } #help_container header { font-size:0.875rem; text-align:center; margin-right:calc(-1em - 2px); margin-left:calc(-1em - 2px); } #help_container header > span { display:inline-block; padding:6px; font-weight:bold; } #help_container tr { display:flex !important; border-bottom:solid 1px #666; } #help_container td { vertical-align:top; } #help_container dd { margin-inline-start:1em; } #help_container dd:before { content:"\u2219"; margin-right:6px; } #content_help { margin:1em auto; width:auto; overflow:auto; } #close_help { float:right; margin-left:-100% !important; margin:4px 6px; } #content_help tbody { font-size:0.875rem; padding:8px; overflow:auto; } #content_help td.kbd_shortcut { text-align:right; width:33%; padding:4px 9px 4px 6px; } #content_help td.help_description { width:66%; padding:4px 6px 4px 12px; } #content_help kbd { display:inline-block; min-width:1em; margin:2px; padding:2px 6px; border:solid 1px #888; border-radius: 3px; text-align:center; font-family:inherit; font-size:0.875em; } /* HANDLE & OVERLAY */ #handle { position:absolute; top:0; bottom:0; z-index:1; cursor:col-resize; right:-4px; width:7px; } body.has_overlay #handle { z-index:9999; } body.has_warning::before, body.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; } `; var $color_and_background_styles = // added to #top and #iframe_body `/* BACKGROUND COLORS */ /* body.theme_light .background_color_A0_20 { background-color: #A0A0A0; } */ /* body.theme_dark .background_color_A0_20 { background-color: #202020; } */ body.theme_light .background_color_B0_30, body.theme_light { background-color: #B0B0B0; } body.theme_dark .background_color_B0_30, body.theme_dark { background-color: #303030; } body.theme_light .background_color_C0_40 { background-color: #C0C0C0; } body.theme_dark .background_color_C0_40 { background-color: #404040; } body.theme_light .background_color_D0_50 { background-color: #D0D0D0; } body.theme_dark .background_color_D0_50 { background-color: #505050; } body.theme_light .background_color_E0_60 { background-color: #E0E0E0 !important; } body.theme_dark .background_color_E0_60 { background-color: #606060 !important; } body.theme_light .background_color_DD_44 { background-color: #DDDDDD; } body.theme_dark .background_color_DD_44 { background-color: #383838; } body.theme_light .background_color_DD_33, body.editor_theme_light .background_color_DD_33, body.theme_light.editor_theme_default .background_color_DD_33 { background-color: #DDDDDD; } body.theme_dark .background_color_DD_33, body.editor_theme_dark .background_color_DD_33, body.theme_dark.editor_theme_default .background_color_DD_33 { background-color: #333333; } body.theme_light .background_color_EE_22, body.editor_theme_light .background_color_DD_33:focus, body.theme_light.editor_theme_default .background_color_DD_33:focus, body.theme_light #content_container:hover, body.theme_light #content_grid::after { background-color: #EEEEEE; } body.theme_dark .background_color_EE_22, body.editor_theme_dark .background_color_DD_33:focus, body.theme_dark #content_container:hover, body.theme_dark #content_grid::after { background-color: #222222; } body.theme_light .background_color_FF_11 { background-color: #EFEFEF; } body.theme_dark .background_color_FF_11 { background-color: #0F0F0F; } body.theme_light .background_color_EE_22:hover, body.theme_light .background_color_EE_22.hovered, body.theme_light .background_color_EE_22.selected { background-color: #FFFFFF; } body.theme_dark .background_color_EE_22:hover, body.theme_dark .background_color_EE_22.hovered, body.theme_dark .background_color_EE_22.selected { background-color: #000000; } body.theme_light .background_color_22_EE { background-color: #222222; } body.theme_dark .background_color_22_EE { background-color: #EEEEEE; } body.theme_light .background_color_11_FF, body.theme_light .background_color_22_EE:hover, body.theme_light .background_color_22_EE.hovered { background-color: #0F0F0F; } body.theme_dark .background_color_11_FF, body.theme_dark .background_color_22_EE:hover, body.theme_dark .background_color_22_EE.hovered { background-color: #EFEFEF; } /* DIR LIST ROWS: .alternate_background */ body.theme_dark.alternate_background #tbody tr:nth-of-type(odd), body.theme_dark.alternate_background .cue_sheet_track:not(.header):nth-of-type(odd) { background-color: #505050; } body.theme_light.alternate_background #tbody tr:nth-of-type(odd), body.theme_light.alternate_background .cue_sheet_track:not(.header):nth-of-type(odd) { background-color: #D0D0D0; } body.theme_dark.alternate_background #tbody tr:nth-of-type(even), body.theme_dark.alternate_background .cue_sheet_track:not(.header):nth-of-type(even) { background-color: #404040; } body.theme_light.alternate_background #tbody tr:nth-of-type(even), body.theme_light.alternate_background .cue_sheet_track:not(.header):nth-of-type(even) { background-color: #E8E8E8; } body.theme_dark #content_help tr:nth-of-type(even) { background-color: #484848; } body.theme_light #content_help tr:nth-of-type(even) { background-color: #CCCCCC; } /* NON-MEDIA ROWS .selected, .loaded, :hover ("light cyan") */ body.theme_light:not(.has_stats) tr:not(.media).selected, body.theme_light:not(.has_stats) tr.content_loaded:not(video), body.theme_light:not(.has_stats) tr.content_loaded:not(.video):hover, body.theme_light #menu li.selected { background-color: rgba(172,202,235,1.00) !important; } body.theme_light:not(.has_stats) tr.content_loaded:not(.video), body.theme_light:not(.has_stats) #tbody tr:not(.media):hover, body.theme_light.alternate_background:not(.has_stats) #tbody tr:not(.media):hover, body.theme_light tr:not(.media).hovered { background-color: rgba(172,202,235,0.60) !important; } body.theme_dark:not(.has_stats) tr:not(.media).selected, body.theme_dark:not(.has_stats) tr.content_loaded:not(.video), body.theme_dark:not(.has_stats) tr.selected.content_loaded:not(.video):hover, body.theme_dark #menu li.selected { background-color: rgba(101,140,179,0.80) !important; } /* #658CB3 */ body.theme_dark:not(.has_stats) tr:not(.video).content_loaded, body.theme_dark:not(.has_stats) #tbody tr:not(.media):hover, body.theme_dark.alternate_background:not(.has_stats) #tbody tr:not(.media):hover, body.theme_dark tr:not(.media).hovered { background-color: rgba(101,140,179,0.60) !important; } /* MEDIA ROWS .audio_loaded, .video.content_loaded, .selected, :hover */ body.theme_light:not(.has_stats) tr.media[class*="_loaded"], body.theme_light .cue_sheet_track.selected { background-color: rgba(130,196,196,1) !important; } /* #82C4C4 */ body.theme_light:not(.has_stats) tr.media.selected:not([class*="_loaded"]), body.theme_light tr.media.hovered, body.theme_light .cue_sheet_track:not(.header):hover { background-color: rgba(116,190,190,0.60) !important; } body.theme_light:not(.has_stats) tr.media:not([class*="_loaded"]):hover { background-color: rgba(116,190,190,0.40) !important; } body.theme_dark:not(.has_stats) tr.media[class*="_loaded"], body.theme_dark .cue_sheet_track.selected { background-color: rgba(076,143,143,0.75) !important; } body.theme_dark:not(.has_stats) tr.media.selected:not([class*="_loaded"]), body.theme_dark tr.media.hovered, body.theme_dark .cue_sheet_track:not(.header):hover { background-color: rgba(076,143,143,0.55) !important; } body.theme_dark:not(.has_stats) tr.media:hover { background-color: rgba(076,143,143,0.45) !important; } /* text editor row ("purple") */ body.theme_light #text_editor_row.has_text_editor, body.theme_light.edited #text_editor_row { background-color: rgba(160,160,230,1.00); } /* #A0A0E6 */ body.theme_dark #text_editor_row.has_text_editor, body.theme_dark.edited #text_editor_row { background-color: rgba(100,100,160,1.00); } /* #6464A0 */ /* menu items :hover, with exceptions for default text editor view */ body.theme_dark .menu li:hover, body.theme_dark .menu li#preview_text_menu_item span:hover { background-color: #686868; } body.theme_light .menu li:hover, body.theme_light .menu li#preview_text_menu_item span:hover { background-color: #B8B8B8; } #preview_text_menu_item:hover { background-color: initial; } /* BACKGROUND IMAGES */ .has_background, .has_background_before::before, .has_background_after::after { background-repeat:no-repeat; background-position:center; background-color:transparent !important; } body.theme_light .bookmark > a::before { background-image: ${ SVG_UI_Icon("bookmark") }; } body.theme_dark .bookmark > a::before { background-image: ${ SVG_UI_Icon("bookmark_dark") }; } body.theme_light li.has_submenu { background-image: ${ SVG_UI_Icon("arrow") }; } body.theme_dark li.has_submenu { background-image: ${ SVG_UI_Icon("arrow_dark") }; } .background_color_check_mark::before, #theme::before, body.sort_by_default #default span::before, body.sort_by_name #name span::before, body.sort_by_time #time span::before, body.sort_by_size #size span::before, body.sort_by_date #date span::before, body.sort_by_kind #kind span::before, body.sort_by_ext #ext span::before, body.alternate_background #alternate_background span::before, body.show_numbers #show_numbers span::before, body.autoload_media #autoload_media span::before, body.autoload_index_files #autoload_index_files span::before, body.play_all_media #play_all_media span::before, body.split_view #split_view_menu_item, body.split_view #split_view span::before, body.source_text #source_text::before, body.preview_text #preview_text::before, body.sort_by_default #sort_by_default span::before, body.loop_media #loop_media_menu::before, body.shuffle_media #shuffle_media_menu::before, body.sort_by_name #sort_by_name span::before, body.sort_by_time #sort_by_time span::before, body.sort_by_size #sort_by_size span::before, body.sort_by_date #sort_by_date span::before, body.sort_by_kind #sort_by_kind span::before, body.sort_by_ext #sort_by_ext span::before, body.hide_ignored_items #hide_ignored_items span::before, body.ignore_ignored_items #ignore_ignored_items span::before, body.editor_theme_default #editor_theme_default::before, body.editor_theme_light #editor_theme_light::before, body.editor_theme_dark #editor_theme_dark::before, #enable_text_editing::before, body.source_text #source_text::before, body.preview_text #preview_text::before, body.preview_html #preview_html::before, .cue_sheet_track.selected .cue_track_id::before { background-image:${ SVG_UI_Icon("check_mark") }; } body.sort_by_default #sort_by_default span::after, body.sort_by_name #sort_by_name span::after, body.sort_by_time #sort_by_time span::after, body.sort_by_size #sort_by_size span::after, body.sort_by_date #sort_by_date span::after, body.sort_by_kind #sort_by_kind span::after, body.sort_by_ext #sort_by_ext span::after { background-image: ${ SVG_UI_Icon("chevron_down") }; } #parent_dir_nav a { background-image: ${ SVG_UI_Icon("chevron_up") }; } body.theme_light #content_pane[data-content="has_ignored"] #content_container { background-image: ${ SVG_UI_Icon("ignored") }; background-size:25%; } body.theme_dark #content_pane[data-content="has_ignored"] #content_container { background-image: ${ SVG_UI_Icon("ignored_dark") }; background-size:25%; } body.has_audio #content_pane:not([data-content^="has_"]):not([data-loaded="unloaded"]) #content_container { background-image:${ SVG_UI_Icon("music") }; } body.is_error #content_container { background-image: ${ SVG_UI_Icon("error") }; } ${ CSS_UI_Icon_Rules() } body.theme_light #grid_btn:not(.has_grid), body.theme_light #grid_btn:not(.has_grid) .menu { background-image:${ SVG_UI_Icon("grid") }; } body.theme_dark #grid_btn:not(.has_grid), body.theme_dark #grid_btn:not(.has_grid) .menu { background-image:${ SVG_UI_Icon("grid_dark") }; } body.theme_light #grid_btn.has_grid, body.theme_light #grid_btn.has_grid .menu { background-image: ${ SVG_UI_Icon("grid_loaded") }; } body.theme_dark #grid_btn.has_grid, body.theme_dark #grid_btn.has_grid .menu { background-image: ${ SVG_UI_Icon("grid_loaded_dark") }; } #current_dir_path span::before { background-image: ${ SVG_UI_File_Icon("file_icon_dir") }; float:none; display:inline-block; margin:0 0 -2px 0; width:24px; } body.has_playlist #current_dir_path span::before { background-image: ${ SVG_UI_File_Icon("file_icon_playlist") }; float:none; display:inline-block; margin:0 0 -2px 0; width:24px; } body.is_error #current_dir_path span::before { background-image: ${ SVG_UI_Icon("error") }; float:none; display:inline-block; margin:0 0 -2px 0; width:24px; } #content_pane[data-content="has_text_editor"] #title span::before { background-image: ${ SVG_UI_File_Icon("file_icon_markdown") }; } #content_pane[data-content="has_font_file"] #title span::before { background-image: ${ SVG_UI_File_Icon("file_icon_font") }; } #content_audio_title span::before { background-image: ${ SVG_UI_File_Icon("file_icon_audio") }; height:14px !important; } #content_pane[data-content="has_grid"] #title span::before { background-image: ${ SVG_UI_File_Icon("file_icon_dir") }; height:14px !important; } #content_pane[data-content="has_view_directory_source"] #title span::before { background-image: ${ SVG_UI_File_Icon("file_icon_dir_default") }; height:14px !important;background-size:contain;} #dir_list tr.ignored.local a.icon span::after { content: "\\00a0[local file]"; display:contents; font-style:italic; } /* BORDERS */ { border-top:solid 1px #666 !important; font-weight:bold; } body.theme_dark .border_all { border: solid 1px #111; } body.theme_light .border_all { border: solid 1px #666; } body.theme_dark .border_top { border-top: solid 1px #111; } body.theme_light .border_top { border-top: solid 1px #666; } body.theme_dark .border_right { border-right: solid 1px #111; } body.theme_light .border_right { border-right: solid 1px #666; } body.theme_dark .border_bottom { border-bottom: solid 1px #111; } body.theme_light .border_bottom { border-bottom: solid 1px #666; } body.theme_dark .border_left { border-left: solid 1px #111; } body.theme_light .border_left { border-left: solid 1px #666; } .border_top_x { border-top: solid 1px #666; } /* "x" = inverted for theme_dark */ .border_right_x { border-right: solid 1px #666; } .border_bottom_x { border-bottom: solid 1px #666; } .border_left_x { border-left: solid 1px #666; } .split_btn::after { border-left: solid 1px #333; } /* TEXT COLORS */ body.theme_light a.text_color_111:hover, body.theme_light .text_color_111.selected, body.theme_light .selected .text_color_111 { color: #000; } body.theme_dark a.text_color_111:hover, body.theme_dark .text_color_111.selected, body.theme_dark .selected .text_color_111 { color: #FFF; } body.theme_light .text_color_111, body.editor_theme_light #content_text .text_color_111, body.theme_light #help_container, body.theme_light #help_container a { color: #111; } body.theme_dark .text_color_111, body.editor_theme_dark #content_text .text_color_111, body.theme_dark #help_container, body.theme_dark #help_container a { color: #EEE; } body.theme_light .text_color_333 { color: #333; } body.theme_dark .text_color_333 { color: #CCC; } body.theme_light.ignore_ignored_items .ignored, body.theme_light.ignore_ignored_items .ignored a { color: #555 !important; } body.theme_dark.ignore_ignored_items .ignored, body.theme_dark.ignore_ignored_items .ignored a { color: #AAA !important; }` ; var $conditional_styles = ` /* PSEUDO-ELEMENTS */ #content_pane[data-content="has_image"] #title span::after { content: attr(data-after); } #content_pane[data-content="has_font"] #title::before { content: "Font:" } #content_pane[data-content="has_font_file"] #title::before { content: "Glyphs from font:" } body.has_directory_source #title::before { content: "Source of: " } #content_pane[data-content="has_grid"] #title::before { content: "Fonts and Images from:"; } #content_pane[data-content="has_grid"].has_font_grid #title::before { content: "Fonts from:"; } #content_pane[data-content="has_grid"].has_image_grid #title::before { content: "Images from:"; } #content_pane[data-content="has_grid"] #title::after { content: "${ current_dir }"; } #content_pane[data-content="has_ignored"] #title::before { content: "Ignored content:"; } #content_pane[data-content="has_dir"] #title::before { content: "Index of:"; } #content_pane[data-loaded="unloaded"] #content_container { background-image:${SVG_UI_Icon("spinner")}; background-repeat:no-repeat; background-position:center;background-size:32px;} body #sidebar_title div:before { content: "INDEX OF"; } body.has_playlist #sidebar_title div:before { content: "PLAYLIST"; } body.has_filelist #sidebar_title div:before { content: "FILELIST"; } #content_pane.has_audio #content_audio_title span::before, body #content_pane[data-content="has_video"] #title::before { content: "Playing: "; } #reload_btn.reset::before, #content_pane.has_zoom_image #reload_btn::before, #content_pane.has_scaled_image #reload_btn::before { content: "Reset"; } #content_pane[data-content="has_text_editor"] #close_btn::before { content: "Hide"; } #content_pane[data-content="has_text_editor"] #title span::after { content: "Text Editor"; } #content_pane[data-content="has_text_editor"].edited #title span::after { content: "Text Editor (edited)"; } body.source_text:not(.split_view) #content_pane[data-content="has_text_editor"] #title::after { content: " (Source Text)"; } body.preview_text:not(.split_view) #content_pane[data-content="has_text_editor"] #title::after { content: " (Text Preview)"; } body.preview_html:not(.split_view) #content_pane[data-content="has_text_editor"] #title::after { content: " (HTML Preview)"; } body.edited #text_editor_row a:after, body.iframe_edited:not(.has_text_editor) #content_pane.has_iframe #title::after { content: " (edited)"; } body.theme_light #theme span::before { content: "Light "; } body.theme_dark #theme span::before { content: "Dark "; } body.show_details #show::before { content: "Hide "; } #disable::after { content: "Enabled"; } body.disable_text_editing #disable::after { content: "Disabled"; } #disable::after { content: "Enabled"; } body.is_error #sidebar_header_body { border-bottom:0; } body.is_error #title span::before { content:"ERROR"; width:auto; } #stats_details span.ignored::before,#stats_details span.invisible::before { content:"("; } #stats_details span.ignored::after,#stats_details span.invisible::after { content:")"; } /* DISPLAY */ body.hide_sidebar #handle, body.has_playlist #stats_summary, body.has_filelist #stats_summary, body.has_stats #footer_links, body.has_playlist #footer_links, body.has_filelist #footer_links, body.has_playlist #sidebar_buttons label, body.has_filelist #sidebar_buttons label, body.is_error #sidebar_header_body > div:not(:first-of-type), body.is_error #tfoot, body.is_non_local #show_invisibles_container, tr.invisible, tr.ignored, body.hide_ignored_items #tbody tr.ignored, body.hide_ignored_items #tbody tr.ignored, body.hide_ignored_items:not(.show_invisibles) #tbody tr.ignored.invisible, body.hide_ignored_items:not(.show_invisibles) #tbody tr.ignored.invisible, body:not(.show_invisibles) #tbody tr.invisible, body:not(.has_media) #time, #close_playlist_container { display:none; } body.has_media #play_toggle, .media .tbody_row_cell_name_a_span input, body.theme_dark #theme_dark, body.theme_light #theme_light, body.show_details .tbody_row_cell_details, #content_pane[class^="has_"] #close_btn, #content_pane[data-content="has_text_editor"] #close_btn { display:unset; } #font_info:hover #font_info_body { display:table-row-group; } body.has_playlist #stats_summary_playlist_files, body.has_filelist #stats_summary_playlist_files, body.has_help #help_container { display:table-row; } body.has_images #grid_btn, body.has_fonts #grid_btn { display:table-cell; } body.has_menu #menu, body.has_menu_parents #parents_links, #sidebar_menus .has_submenu:hover .submenu, #menu .has_submenu.hovered .submenu, #footer_links:hover ul, body.has_images.has_fonts #grid_btn:hover ul.menu, #content_pane[data-content="has_font_file"] #font_viewer, #content_pane[data-content="has_glyph"] #font_viewer, #tfoot tr, body.has_warning #overlay_container, .cue_sheet_track_list_container.has_cue_sheet, body.has_playlist #close_playlist_container, body.has_filelist #close_playlist_container { display:block; } body.show_details #dir_list td.details, body.show_numbers .tbody_row_cell_name_a::before, #content_pane[data-content="has_grid"] .split_btn, #content_pane[data-content="has_image"] .split_btn, #content_pane[data-content="has_font"] .split_btn, #content_pane[data-content="has_font_file"] #title_buttons_left .split_btn, #content_pane[data-content="has_glyph"] #title_buttons_right .split_btn, #content_pane[data-content="has_htm"] #open_in_text_editor { display:inline-block; } #tbody tr:not(.ignored):not(.invisible), body.show_invisibles #tbody tr.invisible, body.has_stats #tbody tr.invisible, body.has_stats #tbody tr.ignored, body.has_stats #tbody tr.ignored.hovered, body.hide_ignored_items.has_stats #tbody tr.ignored, body:not(.hide_ignored_items) #tbody tr.ignored:not(.invisible) { display:grid; } body.show_details #sorting_row_2, body.show_details #text_editor_row, #text_editor_row.has_text_editor, #content_pane.has_audio #content_audio_title, #content_pane.has_audio #content_audio, .playlist_entry_container.has_content, #content_pane .content.has_content:not(#content_iframe), #content_pane[data-loaded="loaded"] #content_iframe, #content_pane[data-content="has_text_editor"] #content_text, #content_pane[data-content="has_font"] #font_specimen, #content_pane[data-content="has_image"] #content_container, #content_pane[data-content="has_image"] #content_image_container, #content_pane[data-content="has_video"] #content_container, body.has_playlist #close_playlist_btn_container, body.has_filelist #close_playlist_btn_container { display:flex; } #content_pane[data-content="has_text_editor"] .content.has_content, #content_pane[data-content="has_text_editor"] #content_grid, #content_pane[data-content="has_grid"] #content_text, #content_pane[data-content="has_grid"] .content.has_content { display:none !important; } body.theme_dark .invert, body.theme_dark #menu li > span::before, body.theme_dark #sorting span::before, body.theme_dark #sorting span::after { filter:invert(1); } #content_pane[data-content="has_ignored"]::before { opacity:0.3; } body.has_warning #sidebar_wrapper, body.has_warning #content_pane, #close_audio:hover::after, body.has_menu_parents #tbody, body.has_menu #tbody, body.faded:not(.has_stats) #tbody tr:not(.hovered), body.has_stats:not(.faded) #tbody tr:not(.hovered), #parent_dir_menu:not(:hover), #menu_container:not(:hover) nav, #hide_sidebar, #grid_btn, #dir_list.has_dir:not(:hover), #footer_links, .split_btn span, .disabled, body.focus_content #tbody tr:not(.hovered) { opacity: 0.6; } #grid_btn:hover, #prev_next_btns span:hover, #hide_sidebar:hover, #grid_btn:hover, #footer_links:hover, .split_btn span:hover { opacity: 1.0; } /* HAS HIDDEN SIDEBAR */ body.hide_sidebar #hide_sidebar { left:2px; transform:rotate(180deg); } body.hide_sidebar #sidebar_wrapper { width:0 !important; min-width:0; position:absolute; top:2px; left:-1px; } body.hide_sidebar #sidebar_header { z-index:unset; display:none; } body.hide_sidebar #sidebar { visibility:hidden; } /* allows hidden sidebar to still be navigated by arrows */ body.hide_sidebar #dir_list { min-width:0; } body.hide_sidebar #content_pane { width:100% !important; } body.hide_sidebar #title_buttons_left { padding-left:24px; } /* HAS GRID */ #content_pane[data-content="has_grid"] #content_grid { display: grid; } #content_pane.has_hidden_grid #content_grid { max-height:100%; display:grid; overflow:hidden; } /* HAS ZOOM IMAGE */ #content_pane.has_scaled_image #content_image_container { display:grid; padding:0; } #content_pane.has_zoom_image #content_image { width:unset; max-width:unset; max-height:unset; cursor: zoom-out; } `; var $text_editor_styles = ` html, body, #iframe_body { margin:0; padding:0; height:100%; position:relative; font-family:${ $settings.UI_font }; font-size: ${ $settings.UI_font_size }; } button.focus, button:focus { outline:none; border-radius:3px !important; border-style:solid !important; border-width:1px !important; border-color:#222 !important; } #iframe_body { display:flex; flex-direction:column; position:relative; overflow:hidden; } #iframe_body #content_text { display:flex; height:100%; padding:0; position:absolute; z-index:1; width:100%; overflow:hidden; flex-direction:column; } body.editor_theme_dark .border_right { border-right: solid 1px #111; } .editor_theme_light .border_right { border-right: solid 1px #666; } body.editor_theme_dark .border_bottom { border-bottom: solid 1px #111; } .editor_theme_light .border_bottom { border-bottom: solid 1px #666; } ul.has_popout_menu, .editor_theme_light #toolbar ul.has_popout_menu { background-color:#C0C0C0; border:solid 1px #666; } ul.has_popout_menu link { background-color:#D0D0D0; } ul.has_popout_menu li:hover { background-color:#E0E0E0; } body.editor_theme_dark #toolbar ul.has_popout_menu li { background-color:#C0C0C0; } ${ $warning_styles } /* TOOLBAR */ #toolbar { width:100%; background:#C0C0C0; position:relative; z-index:100; border-bottom: solid 1px #999; border-collapse:collapse; font-family:${ $settings.UI_font }; font-size:${ parseFloat($settings.UI_font_size) * 0.875 + $settings.UI_font_size.replace(/\d*/,'') }; -webkit-user-select: none; -moz-user-select: none; user-select:none; position:relative; } #toolbar td { padding:2px 0 0; } #toolbar_buttons { margin:0; padding:0; list-style:none; } .toolbar_icon { margin:0 4px; padding:4px; width:16px; height:16px; float:left; background-size:14px; background-repeat:no-repeat; background-position:center; cursor:pointer; display:block; opacity:0.5; color:#444; } #editor_theme { background-image:${ SVG_Text_Editing_UI_Icon("toggle_theme") }; } #toggle_source_text { background-image:${ SVG_Text_Editing_UI_Icon("show_markdown") }; background-size:18px; } #toggle_preview_text { background-image:${ SVG_Text_Editing_UI_Icon("show_preview") }; } #toggle_preview_html { background-image:${ SVG_Text_Editing_UI_Icon("show_html") }; background-size:20px; } #toggle_split_view { background-image:${ SVG_Text_Editing_UI_Icon("toggle_split") }; } #sync_scroll { padding:4px; float:left; opacity:1 !important; } #sync_scroll input { float:left; } #sync_scroll label { width:8em; display:block; font-size:inherit; line-height:1.5; } #save_btn { margin:0; padding:4px 4px 4px 8px; width:20px; height:16px; float:right; position:relative; z-index:9997; background-repeat:no-repeat; background-position:center right 7px; background-image:${ SVG_Text_Editing_UI_Icon("save_btn") }; background-size:16px; cursor:pointer; outline:none; } #save_btn ul { display:none; margin:0; padding:0 32px 0 0; position:absolute; top:-4px; right:-2px; list-style:none; box-shadow:0px 4px 6px -3px #333; background-repeat:no-repeat; background-position:right 8px top 7px; background-image:${ SVG_Text_Editing_UI_Icon("save_btn") }; background-size:16px; background-color:#C0C0C0; } .edited #save_btn, .edited #save_btn ul { background-image:${ SVG_Text_Editing_UI_Icon("save_btn_edited") }; } #save_btn ul li { margin:0; padding:4px 6px; text-align:right; width:100%; white-space:pre; box-sizing:border-box; } #save_btn a { font-weight:normal; } #save_btn:hover ul { display:block; } #save_btn li:hover { background-color:#DDDDDD !important; } body.editor_theme_dark #save_btn ul { border-color:#EEE; box-shadow:0px 4px 6px -3px #EEE;} body.editor_theme_dark #save_btn li { border-color:#EEE; color:#111; background-color:#AEAEAE !important; } body.editor_theme_dark #save_btn li:hover { background-color:#989898 !important; } body.editor_theme_dark #save_text { border-color:#EEE; color:#111; } #clear_text { margin:0 4px; padding:4px; height:16px; float:right; cursor:pointer; } #toolbar li:hover, body.source_text #toggle_source_text, body.split_view #toggle_source_text, body.split_view:not(.preview_html) #toggle_preview_text, body.preview_text #toggle_preview_text, body.preview_html #toggle_preview_html, .split_view #toggle_split_view, .edited #save_btn { opacity:1; } /* TEXT CONTENT CONTAINERS */ #text_container { display:flex; flex-grow:1; overflow:hidden; } #text_source, #text_preview, #html_preview { margin:0; padding:1em; display:none; width:100%; height:100%; overflow-y:scroll; box-sizing:border-box; border:0; z-index:1; background:transparent; line-height:1.2; font-size:${ parseFloat($settings.UI_font_size) + $settings.UI_font_size.replace(/\d*/,'') }; } #text_preview { padding:0; } #text_source { border-right-width:1px; resize:none; font-family:monospace; } #html_preview { white-space:pre-wrap; word-break:break-word; font-family:monospace; } /* TEXT SOURCE */ #text_source:focus, #text_preview:focus, #html_preview:focus { outline:none; } #text_source:focus { box-shadow: inset 0 0 4px #666; } body.editor_theme_dark #text_source:focus, body.theme_dark.editor_theme_default #text_source:focus { box-shadow: inset 0 0 6px #000; } /* SPLIT VIEWS */ .split_view #text_source, .split_view:not(.preview_html) #text_preview, .split_view.preview_html #html_preview { width:50%; display:block; } body.preview_text:not(.split_view):not(.preview_html) #text_preview, body.source_text:not(.split_view) #text_source, body.preview_html:not(.split_view) #html_preview { display:block; } body.source_text.preview_text:not(.split_view) #text_preview, body.source_text.preview_html:not(.split_view) #html_preview { display:none !important; } /* TEXT PREVIEW */ body.editor_theme_dark #toolbar, body.theme_dark.editor_theme_default #toolbar { background: #404040; color:#EEEEEE; border-bottom: solid 1px #111; } body.editor_theme_dark #toolbar .has_background:not(#save_btn), body.editor_theme_dark:not(.edited) #save_btn, body.editor_theme_dark.edited #save_btn li, body.theme_dark.editor_theme_default #toolbar .has_background:not(#save_btn), body.theme_dark.editor_theme_default:not(.edited) #save_btn, body.theme_dark.editor_theme_default.edited #save_btn li, body.editor_theme_dark #text_preview { filter: invert(1); } body.theme_light.editor_theme_default .background_color_DD_33, body.editor_theme_light .background_color_DD_33 { background-color: #DDDDDD; } body.editor_theme_dark .background_color_DD_33, body.theme_dark.editor_theme_default .background_color_DD_33 { background-color: #333333; } body.theme_light.editor_theme_default .background_color_EE_22, body.editor_theme_light .background_color_DD_33:focus, body.theme_light #content_container:hover, body.theme_light #content_grid::after, #text_preview table th, body.editor_theme_light #text_container, #top:not(.editor_theme_dark) #text_container { background-color: #F6F6F6; } body.theme_dark.editor_theme_default .background_color_EE_22, body.editor_theme_dark .background_color_DD_33:focus, body.theme_dark.editor_theme_default .background_color_DD_33:focus, body.theme_dark #content_container:hover, body.theme_dark #content_grid::after, body.editor_theme_dark #text_container { background-color: #222222; } /* custom previewed text styles */ #text_preview pre { border:solid 1px #CCC; border-radius:3px; white-space:pre-wrap; word-break:break-word; font-size:${ parseFloat($settings.UI_font_size) + $settings.UI_font_size.replace(/\d*/,'') }; } #text_preview th, #text_preview td { vertical-align:top; } #text_preview blockquote { margin-top:1em; margin-bottom:1em; color: #555; } #text_preview blockquote + blockquote { margin-top:0; } .markdown-body input[type="checkbox"] { margin-top:0.375em; margin-right:6px; float:left; } h1 .uplink,h2 .uplink,h3 .uplink,h4 .uplink,h5 .uplink,h6 .uplink { display:inline-block; font-size:0.875em; transition: opacity 0.25s; opacity:0; cursor:pointer; margin:0; padding:0; } h1:hover .uplink,h2:hover .uplink,h3:hover .uplink,h4:hover .uplink,h5:hover .uplink,h6:hover .uplink { transition: opacity 0.25s; opacity:0.5; } #text_preview table { font-size:inherit; } .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; } /* disabled text editing */ li#save_btn div, .comment, #iframe_body.disable_text_editing #toolbar, #iframe_body.disable_text_editing #preview_text, #iframe_body.disable_text_editing #text_editing_handle { display:none; } #iframe_body.disable_text_editing #text_source.disabled { top:0; background:white; } #iframe_body.editor_theme_light .background_color_D0_50, #iframe_body.theme_light.editor_theme_default .background_color_D0_50 { background-color: #D0D0D0; } #iframe_body.editor_theme_dark .background_color_D0_50, #iframe_body.theme_dark.editor_theme_default .background_color_D0_50 { background-color: #505050; } #iframe_body.editor_theme_light .background_color_E0_60, #iframe_body.theme_light.editor_theme_default .background_color_E0_60 { background-color: #E0E0E0; } #iframe_body.editor_theme_dark .background_color_E0_60, #iframe_body.theme_dark.editor_theme_default .background_color_E0_60 { background-color: #606060; } #iframe_body.editor_theme_light .text_color_111, #iframe_body.editor_theme_light #content_text .text_color_111, #iframe_body.theme_light.editor_theme_default .text_color_111, #iframe_body.theme_light.editor_theme_default #content_text .text_color_111 { color: #111; } #iframe_body.editor_theme_dark .text_color_111, #iframe_body.editor_theme_dark #content_text .text_color_111 { color: #EEE; } #iframe_body.has_warning::after { content:""; position:absolute; top:0; right:0; bottom:0; left:0; background:rgba(0,0,0,0.33); z-index:9998; } /* TEXT EDITOR RESIZE HANDLE */ #text_editing_handle { display:none; width:8px; position:absolute; top:0; bottom:0; left:calc(50% - 4px); cursor:col-resize; z-index:3; } #text_editing_handle::before { content:""; width:1px; background:#666; position:absolute; top:0; bottom:0; left:calc(50%); } #iframe_body.editor_theme_dark #text_editing_handle::before { background:#111; } .split_view #text_editing_handle { display:block; } `; var $iframe_dir_styles = ` :root, html, body { margin:0; padding:0; width:100%; max-width:100%; height:100%; border:0; border-radius:0; font-family:${ $settings.UI_font }; font-size:${ $settings.UI_font_size }; hyphens:auto; overflow:hidden; } #iframe_body { width:100%; display:flex; flex-direction:column; font-size:${ (parseFloat($settings.UI_font_size) * 0.875) + $settings.UI_font_size.replace(/\d*/,'') }; } #iframe_body.theme_dark { background-color:#333; } a, a:hover { text-decoration: none !important; } /* IFRAME HEADER */ #thead { flex-direction:row; font-size: 0.875rem; user-select:none; -webkit-user-select:none; } #change_dirs { display:flex; flex-direction:row; justify-content:space-between; white-space:pre; } #change_dirs a { padding-right:0 !important; padding-left:0 !important;} #change_dirs span:hover { font-weight:bold; } #parent { padding:5px 3px 5px 0; text-align:left; flex-grow:1; } #open_in_sidebar { padding:5px 0 5px 3px; text-align:right; flex-grow:1; } #parent a::before, #open_in_sidebar a::after { content:""; width:16px; height:10px; display:inline-block; } #open_in_sidebar a::after { background-image:${ SVG_UI_Icon('chevron_left') }; background-repeat: no-repeat; background-size:6px; background-position: center; } .theme_dark #parent a::before, .theme_dark #open_in_sidebar a::after { filter:invert(1); } /* IFRAME SORTING */ #sorting_row_1 div, #sorting_row_2 div { flex-grow:1; padding:0; } #sorting_row_1 { display:flex; cursor:pointer; justify-content:space-between; flex-direction:row; } #sorting_row_1 div { width:50%; } #sorting_row_2 { overflow-x:scroll; } .sorting span { display:inline-block; padding:3px 0; } .sorting:hover span { font-weight:bold; } .sorting span::before, .sorting span::after { content:""; width:16px; height:8px; display:inline-block; position:relative; color:#CCC; background-repeat:no-repeat; background-position:center; background-size:10px; } .sorting.down span::after { transform:rotate(180deg) } .theme_dark .sorting span::before, .theme_dark .sorting span::after { filter:invert(1); } #sort_by_name, #sort_by_ext { text-align:left; } #sort_by_time, #sort_by_size, #sort_by_date, #sort_by_default, #sort_by_kind { text-align:right; } body:not(.has_media) #sort_by_time { display:none; } body.sort_direction_up .sorting span::after { transform:rotate(180deg) !important; } /* IFRAME DIR LIST */ #iframe_dir_list_wrapper { width:100%; height:100%; display:block; position:relative; overflow:scroll; margin-bottom:-1px; } #dir_list { width:100%; height:100%; line-height:1.5; position:relative; overflow:hidden; border-collapse:collapse; } #tbody { font-size:0.875rem; counter-reset:row; overflow:hidden; display:block; } #tbody tr, #sorting_row_2 { display: grid; grid-gap:0; grid-template-columns: minmax(16em,100%) minmax(5.5em,6em) minmax(5.5em,14em) minmax(5.5em,6em); } .tbody_row_cell_details { white-space:pre; font-variant-numeric:tabular-nums; padding:3px 0; } .tbody_row_cell_name_a { padding:3px 0; } .tbody_row_cell_name_a::before { display:none; counter-increment:row; content:counter(row); width:36px; min-width:36px; height:14px; max-height:14px; min-height:14px; margin-top:2px; padding:0; float:left; text-align:right; font-variant-numeric:tabular-nums; } .show_numbers #tbody td.name a::before { display:inline-block; } .tbody_row_cell_name_a_span { display:flex; padding:2px 0; word-break:break-word; } .tbody_row_cell_name_a_span input { margin:2px 6px 0 0; display:none; } .tbody_row_cell_details.name { grid-column:1; text-align:left; } .tbody_row_cell_details.size { grid-column:2; padding-right:12px; padding-left:0; text-align:right; } .tbody_row_cell_details.date { grid-column:3; padding-right:12px; padding-left:0; white-space:normal; overflow:hidden; max-height:1em; overflow-wrap:break-word; text-align:right; } .tbody_row_cell_details.date span { white-space:pre; display:inline-block; } .tbody_row_cell_details.kind { grid-column: 4; text-align:right; padding-right:12px; } .tbody_row_cell_details.kind::first-letter { text-transform:uppercase; } .tbody_row_cell_details.ext { display:none; } .tbody_row_cell_media_duration { display:none; padding:3px 12px 3px 0; font-variant-numeric:tabular-nums; } tr.media, body.has_media tr.media #sorting_row_2 { grid-template-columns: minmax(24em,100%) minmax(4.5em,8em) minmax(5.5em,14em) minmax(5.5em,5.5em); } tr.media .tbody_row_cell_name_a_span input { display:inline-block; } body.has_media #tbody tr, body.has_media #sorting_row_2 { grid-template-columns: minmax(16em,100%) minmax(4em,6em) minmax(5.5em,6em) minmax(5.5em,14em) minmax(4em,6em); } body.has_media .tbody_row_cell_media_duration { display:inline-block; grid-column: 2; text-align:right; } body.has_media .tbody_row_cell_details.size { grid-column: 3; } body.has_media .tbody_row_cell_details.date { grid-column: 4; } body.has_media .tbody_row_cell_details.kind { grid-column: 5; text-align:right; padding-right:12px; } body.has_media .tbody_row_cell_details.ext { display:none; } body.has_media tr:not(.media) .tbody_row_cell_name { grid-column: 1 / span 2; } body.has_media tr:not(.media) .tbody_row_cell_media_duration { display:none; } tr:hover .tbody_row_cell_name_a, tr.selected .tbody_row_cell_name_a, tr.media[class*="_loaded"] .tbody_row_cell_name_a { font-weight:bold; } body.ignore_ignored_items tr.ignored, body.ignore_ignored_items tr.ignored a { cursor:not-allowed; } tr.invisible, tr.ignored, body.hide_ignored_items #tbody tr.ignored, body.hide_ignored_items #tbody tr.ignored, body.hide_ignored_items:not(.show_invisibles) #tbody tr.ignored.invisible, body.hide_ignored_items:not(.show_invisibles) #tbody tr.ignored.invisible, body:not(.show_invisibles) #tbody tr.invisible { display:none !important; } body.show_invisibles tr.invisible, #tbody tr:not(.ignored):not(.invisible), body.show_invisibles #tbody tr.invisible, body.has_stats #tbody tr.invisible, body.has_stats #tbody tr.ignored, body.has_stats #tbody tr.ignored.hovered, body.hide_ignored_items.has_stats #tbody tr.ignored, body:not(.hide_ignored_items) #tbody tr.ignored:not(.invisible) { display:grid !important; } #iframe_body.is_blurred #dir_list { opacity:0.75; } #iframe_body tr.is_blurred { background-color: rgba(172,202,235,0.66) !important; } ${ CSS_UI_Icon_Rules() } /* background icons */ .has_icon_before::before, .has_icon_before_before { content:""; display:block; float:left; max-width:28px; width:28px; min-width:28px; height:14px; max-height:14px; min-height:14px; margin-top:4px; background-position:center; background-repeat:no-repeat; background-size:14px; } #dir_list.sort_by_default #sort_by_default span::before, #dir_list.sort_by_name #sort_by_name span::before, #dir_list.sort_by_size #sort_by_size span::before, #dir_list.sort_by_date #sort_by_date span::before, #dir_list.sort_by_kind #sort_by_kind span::before, #dir_list.sort_by_ext #sort_by_ext span::before { content:""; height:9px; background-image:${ SVG_UI_Icon("check_mark") }; background-repeat: no-repeat; background-size:10px; background-position: center; } #dir_list.sort_by_default #sort_by_default span::after, #dir_list.sort_by_name #sort_by_name span::after, #dir_list.sort_by_size #sort_by_size span::after, #dir_list.sort_by_date #sort_by_date span::after, #dir_list.sort_by_kind #sort_by_kind span::after, #dir_list.sort_by_ext #sort_by_ext span::after, #parent a::before { background-image:${ SVG_UI_Icon("chevron_up") }; background-repeat: no-repeat; background-size:10px; background-position: center; } .sidebar #tbody tr { grid-template-columns: minmax(8em,100%) minmax(5.5em,6em) minmax(5.5em,6em) minmax(5.5em,6em); } .has_subdirectory .dir_list_subdir { display:block; grid-column:1/ span 4; grid-row:3; width:100%; border:0; } .tr[data-level] .tbody_row_cell_name_a::before { width:${ ( ( Number(getSearchParam("level") ) + 1) * 22 ) + 28 }px; } `; // Gecko (Firefos) Styles: const $gecko_style_rules = ` .dir::before { content:"" !important; display:none !important; } body.is_gecko button { padding:revert; } body.is_gecko #grid_btn .menu { top:-7px; left:-120px; } body.is_gecko thead { font-size:100%; } body.is_gecko #dir_list .dir::before { position:absolute; } body.is_gecko #dir_list tr td.name span { display:-webkit-box; width:auto; white-space:normal; } tr.dir td:not(:first-child), tr.file td:not(:first-child) { width:unset !important; } body.is_gecko #dir_list tr td { min-width:calc(100% - 24px); } body.is_gecko .dir::before { content:"" !important; display:none !important; } body.is_gecko.use_default_icons:not(.is_converted_list) #dir_list .file .name .icon { padding-left:4px; background:none; } body.is_gecko.use_default_icons #dir_list .file .name .icon img { margin-right:6px; height:14px; } body.is_gecko #tbody > tr > td:not(:first-of-type) { float:left } body.is_gecko #content_audio_title span { padding-top: 6px;, padding-bottom: 0; } body.is_gecko #audio,body.is_gecko #audio_container { background-color: rgba(26,26,26,1); } body.is_gecko #prev_track, body.is_gecko #next_track, body.is_gecko #close_audio { filter: invert(1); border:none !important; } body.is_gecko #content_pane.has_zoom_image #content_image_container { display:block !important;} `; const $safari_style_rules = ` body.is_safari button { background-color: #FFF; } body.is_safari.theme_dark #prev_track, body.is_safari.theme_dark #next_track, body.is_safari.theme_dark #close_audio { filter: invert(1); } `; const $chrome_style_rules = ` video::-webkit-media-controls-enclosure { border-radius:0 !important; } `; // ADD STYLES function addStyles() { $('head').find('style, link[rel="stylesheet"], link[href$="css"]').remove(); // remove any existing stylesheets let default_styles = ` `; if ( getBrowser() === 'is_gecko' ) { default_styles += ``; } if ( getBrowser() === 'is_safari' ) { default_styles += ``; } if ( getBrowser() === 'is_chrome' ) { default_styles += ``; } $('head').append( default_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() { let title = $('title').html(); let parent_link_parent, type; // Possible elements: pre, li, td, th, div -- used to determine index type // Try to find parent directory link: let parent_link = $('a:contains("Parent Directory"), a:contains("parent directory"), a[href="../"], a[href="/"], a[href^="?"], img[alt*="PARENTDIR"]'); switch(true) { case parent_link.length === 0: parent_link_parent = document.querySelectorAll('body > ul li, body > pre, body > table:last-of-type tr td'); break; default: parent_link_parent = parent_link.parent(); // use original found parent_link } // If no parent_link_parent found, type = error; else return parent node name let node_name = ( parent_link_parent[0] !== undefined ? parent_link_parent[0].nodeName.toLowerCase() : ''); switch(true) { case ( /Error|404|Not Found/.test(title) ): case $protocol.startsWith('file'): if ( getBrowser() === 'is_gecko' ) { node_name = 'gecko'; } break; } let types = {'gecko':'gecko','li':'list','pre':'pre','th':'table','td':'table','div':'default','error':'error','permission_denied':'permission_denied'}; type = types[node_name]; return type; } // Return Index items, Index type, remove parent directory link, and add body class. function getIndexItems() { let type = getIndexType(), items; switch(type) { case 'gecko': items = $('body').find('> table > tbody'); break; case 'list': items = $('body').find('> ul'); break; case 'pre': items = $('body').find('> pre').html(); break; case 'table' || 'td': switch(true) { case $('table > tbody').length === 1: items = $('body').find('table > tbody'); break; case $('table > tbody').length < 1: items = $('body').find('table'); // tables without tbody } items.find('tr th, tr td:contains(Parent Directory), a[href="../"], hr').parent('tr').remove(); break; case 'default': items = $('body').find('> table').find('> tbody'); break; case 'error': items = $('body').html(); break; } return [items,type]; } // Index Prep: convert list type function function convertGeckoType(items) { let prepped_index = []; const rows = Array.from(items.find('> tr')); for ( let row of rows ) { let prepped_row = [], cellContents = '', cells = Array.from( $(row).find('> td') ), link = ($(cells).find('a').attr('href')); for ( let cell of cells ) { cellContents = cell.innerText; cellContents = ( cellContents !== undefined ? cellContents.trim() : ''); prepped_row.push(cellContents); } prepped_row[1] = prepped_row[1].replace(/\s*KB/,'000'); // convert reported size in KB to total bytes prepped_row[2] = prepped_row[2] + ' '+ prepped_row[3]; prepped_row = prepped_row.slice(1,-1); if ( link.length > 0 && link !== '/' && link !== '../' ) { prepped_row.unshift(link); } if ( prepped_row.length > 0 ) { prepped_index.push(prepped_row); } } return prepped_index; } // Index Prep: convert list type function function convertListType(items) { let prepped_index = []; const rows = items[0].children; for ( let i = rows.length; i--; ) { let row = rows[i]; if ( row.innerHTML.indexOf('Parent Directory') === -1 ) { let prepped_row = []; let link = $(row).find('a').attr('href'); row = row.innerHTML.replace(/\s*/,''); let cells = row.split(' '); for ( let cell of cells ) { if ( cell.trim().length > 0 ) { prepped_row.push(cell); } } if ( link.length > 0 && link !== '/' && link !== '../' ) { prepped_row.unshift(link); } if ( prepped_row.length > 0 ) { prepped_index.push(prepped_row); } } } return prepped_index; } // Index Prep: convert pre type function function convertPreType(items) { let prepped_index = []; const spaces = /\s{2,}/; items = items.replace(/\[\s*?\]/g,'[]') // remove empty 'alt=[ ]' .replace(/\s*<(hr|img[^>])>\s*/g,'') // remove hr and images .replace(/\s*]+?>[^<]+?<\/a>\s*/g,'') // remove sorting links (href beginning with "?") .replace(/(]+?>)[^<]+?<\/a>/g,' $1 ') // remove link display text and add two spaces after .replace(/(\%20){2,}/g,'\%20'); // remove extra spaces in hrefs const rows = items.split('\n'); // create array of rows from items for ( let i = rows.length; i--; ) { let row = rows[i]; let prepped_row = []; let cells = row.split(spaces); // assumes pre-type only uses two or more spaces between "columns" let link; for ( let j = cells.length; j--; ) { let cell = cells[j]; if ( cell.trim().length > 0 ) { if ( !cell.startsWith(' 1 ) { prepped_index.push(prepped_row); } // add prepped row to index } return prepped_index; } // Index Prep: convert table type function function convertTableType(type,items) { // for local chrome indexes and server-generated table-type indexes let prepped_index = []; const rows = items[0].children; for ( let i = rows.length; i--; ) { let row = rows[i], prepped_row = [], link, cell, cells = row.cells; for ( let j = cells.length; j--; ) { switch(true) { case cells[j].children[0] !== undefined && cells[j].children[0].nodeName === 'A': link = cells[j].children[0].attributes.href.value; break; case cells[j].innerHTML !== ' ' && cells[j].innerHTML.indexOf(' 0 ) { prepped_row.push( cell ); } break; } } if ( link !== undefined ) { prepped_row.unshift(link); } if ( prepped_row.length > 1 ) { prepped_index.push(prepped_row); } // prepped_row.length > 2 in order to omit parent directory row } return prepped_index; } // Convert Errot Type function convertErrorType(items) { $('body').addClass('is_error'); items = '
    '+items+'
    '; return items; } // Create Playlist items function convertPlaylist(items) { let prepped_index = []; let prepped_row = []; let rows, 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:/.test(items) ): type = 'extm3u'; rows = items.split('#EXTINF:'); break; // rows made by splitting at "#EXTIMG:" prefix default: type = 'm3u'; rows = items.split('\n'); break; // rows are just naked links } let rows_length = rows.length; for ( let i = 0; i < rows_length; i++ ) { let row = rows[i]; switch(true) { // get entry information: title, link, etc. case type === 'extm3u': row = row.trim().split('\n'); prepped_row = row[1]; break; // split row into info ( = row[0] ) and link, but we're only using the link anyway.... case type === 'm3u': prepped_row = row; break; // m3u with urls only } if ( row.length > 1 ) { prepped_index.push([prepped_row]); } } return prepped_index; } // Index Prep: convert rows and return array of rows, with link, size, date-modified function convertIndexItems(type,items) { let converted = []; switch(type) { case 'gecko': converted = convertGeckoType(items); break; case 'list': converted = convertListType(items); break; case 'pre': converted = convertPreType(items); break; case 'table': case 'default': converted = convertTableType(type,items); break; case 'error': converted = convertErrorType(items); break; } return converted; } // INDEX PREP: Build new Index from prepped rows function buildNewIndex(id,prepped_index,sort,type) { let new_index_items = [], body_classes = new Set(); let new_row, 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_sort_kind, item_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; let stats_classes = [], stats_kinds = []; let parent_id = ( getSearchParam('parent_id') || '' ), connector = ( getSearchParam('parent_id') ? '_' : '' ), level = ( Number(getSearchParam('level')) || 0 ); // ensure unique ids for subdirectory items // add body classes according to index type switch(type) { case 'error': body_classes.add('is_error'); break; case 'gecko': body_classes.add('is_converted_gecko'); break; case 'list': body_classes.add('is_converted_list'); break; case 'pre': body_classes.add('is_converted_pre'); break; case 'table': case 'td': body_classes.add('is_converted_table'); break; case 'default': body_classes.add('is_default'); break; } for ( let i = prepped_index_length; i--; ) { item = prepped_index[i]; item_info = getLinkInfo(item[0]); // = [link,name,ext,kind,item_classes,body_classes]; item_link = item_info[0].trim(); item_name = item_info[1].replace(/^\s/m,'\ ').replace(/^\//m,'').replace(/([-_——])/g,'$1'); // prep display name, with word breaks added after unbreakable chars item_sort_name = escapeStr( item_info[1].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_kind = item_info[3]; item_sort_kind = item_info[3]; item_classes = item_info[4]; item_disabled = ( item_classes.indexOf('local') > -1 || type === 'gecko' ? ' disabled="disabled"' : '' ); // Assemble row elements name_span = ` ${ item_name }`; cell_link = `
    ${ name_span }`; cell_name = `${ cell_link }`; cell_time = ``; cell_size = `${ item_size }`; cell_date = `${ item_date }`; cell_kind = `${ item_kind }`; cell_ext = ``; // Assemble row new_row = `${ cell_name } ${ cell_time } ${ cell_size } ${ cell_date } ${ cell_kind } ${ cell_ext }`; new_index_items.push($(new_row)[0]); body_classes.add(item_info[5].join(' ')); if ( ( item_sort_kind === 'audio' || item_sort_kind === 'video' ) && item_classes.indexOf('local') === -1 ) { // get media file duration getMediaDuration( item_link, function( duration ) { // what to do when duration is returned... // display duration let query_str = window.location.href; if ( query_str.indexOf('subdirectory') > -1 ) { // send data to top for subdirectories sendMessage('top','set_media_duration','',['#'+ parent_id + connector +'rowid-'+ ( prepped_index.length - i),duration]); } else { setMediaDuration('#'+ parent_id + connector +'rowid-'+ ( prepped_index.length - i),duration); } if ( getSearchParam('sort_by') === 'time' ) { let time_sort = sortDirList($('#tbody').find('tr'), 'sort_by_time', 1); // sort directory again when durations are returned $('#tbody').empty().html(time_sort); } }); } stats_kinds.push(item_kind); stats_classes.push(item_classes); // add item classes for stats } let stats = buildStats(stats_classes,stats_kinds); // Sort items if ( sort === undefined ) { sort = getSearchParam('sort_by'); } // get sorting pref let sort_direction = ( getSearchParam('sort_direction') === 'down' ? 'sort_direction_down' : 'sort_direction_up' ); // get sort direction let sorted_index_items = sortDirList($(new_index_items), 'sort_by_'+ sort, sort_direction); // make initial sort return [sorted_index_items, Array.from(body_classes).join(' '),stats]; } function setMediaDuration(id,duration) { $(id).find('td.tbody_row_cell_media_duration').attr('data-time',duration).text( duration ); // add time to details if ( duration.toString().startsWith('Error') ) { $(id).addClass('ignored').find('input').attr('disabled','disabled'); } // on error, disable track } // GET CONTENT SRC LINK, NAME, KIND, and EXT function getLinkInfo(link) { if ( link === undefined) { return; } if ( link.startsWith('/') ) { link = 'file://'+ link; } let URL = newURL(decodeURIComponentSafe(encodeURIComponent(link))); let prepped_link, name, kind, ext, item_classes = [], body_classes = []; switch(true) { case $protocol !== 'file:': // non-local pages switch(true) { case URL.protocol === 'file:': case URL.protocol === undefined: prepped_link = link; item_classes.push('local','ignored'); break; default: prepped_link = URL.href; // non-local pages } break; default: // local pages; switch(true) { case URL.protocol !== 'file:': prepped_link = URL.href; break; default: prepped_link = URL.pathname; } } switch(true) { case URL.pathname.endsWith('/'): name = URL.pathname.split('/').reverse()[1] + '/'; switch(true) { case name.endsWith('.app') || name.endsWith('.app/') || name.endsWith('.exe'): // apps ext = 'app'; kind = ext; if ( $settings.apps_as_dirs === false ) { item_classes.sort().unshift('file','app'); } else { item_classes.sort().unshift('dir','app'); } break; default: kind = 'dir'; ext = 'dir'; // dirs item_classes.unshift(kind); } if ( name.startsWith('.') ) { item_classes.push('invisible'); } break; default: // files name = prepped_link.trim().split('/').reverse()[0]; switch(true) { case name.toLowerCase().endsWith('symlink'): ext = 'symlink'; break; case !/\./.test(name): ext = name.toLowerCase(); break; // if no '.' in link (typical for bin files), ... default: // find the last . and get the remaining characters ext = name.toLowerCase().slice(name.toLowerCase().lastIndexOf('.') + 1); for ( let item_kind in $item_kind ) { if ( $item_kind[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 ( $row_settings.ignored.includes( ext ) ) { item_classes.push('ignored'); } if ( name.startsWith('.') ) { item_classes.push('invisible'); } } if ( kind === undefined ) { kind = 'other'; } item_classes = item_classes.sort(); item_classes.unshift(kind); item_classes.unshift('file'); } if ( ext === undefined ) { ext = ''; } if ( name.endsWith('symlink') || name.endsWith('alias') || name.endsWith('symbolic link') ) { item_classes.push('alias'); } for ( let item_kind_system of $item_kind.system ) { if ( name.endsWith(item_kind_system) ) { item_classes.push('ignored'); } } // ignore various system items item_classes = Array.from(new Set(item_classes)).join(' '); // remove dupe classes return [decodeURIComponentSafe(encodeURIComponent(prepped_link)),decodeURIComponentSafe(name),ext,kind,item_classes,body_classes]; } // Index Prep: get formatted row size and date function getItemSizeAndDate(cells) { let item_size_and_date = [], row_display_size, item_sort_size, row_display_date, item_sort_date, size_units = /[BYTES|B|K|KB|MB|GB|TB|PB|EB|ZB|YB]/; if ( cells.length > 1 ) { if ( cells[1].search(/[-:\/]/) !== -1 ) { // test for typical date/time separators. row_display_date = cells[1]; row_display_size = cells[2]; } else { row_display_date = cells[2]; row_display_size = cells[1]; } } // size if ( /undefined|—|-|,/.test(row_display_size) || row_display_size === '' ) { // if size is undefined, empty, or punctuation row_display_size = '—'; item_sort_size = '0'; // if no size supplied, use these defaults } else { item_sort_size = getItemSortSize(row_display_size); if ( !row_display_size.toUpperCase().match(size_units) ) { // if provided size is only numeric row_display_size = formatBytes(row_display_size,1); // format byte size } else { row_display_size = row_display_size.replace('K','k').replace(/(\d+)\s*([A-z])/,'$1 $2'); // ensure display size has space between number and units } } // date if ( [undefined,'','-'].includes(row_display_date) ) { row_display_date = '—'; item_sort_date = '0'; } else { item_sort_date = getItemDate(row_display_date); } row_display_date = row_display_date.replace(/, (.+)/,', $1'); // add spans for short date display item_size_and_date.push( row_display_size, item_sort_size, row_display_date, item_sort_date ); return item_size_and_date; } // Index Prep: get row size for sorting function getItemSortSize(val) { let sort_size, values = val.replace(/(\d+)\s*([A-z]+)/,'$1 $2').split(' '), size = values[0], unit = values[1]; const factor = { '':1, B:1, K:1000, KB:1000, M:1000000, MB:1000000, G:1000000000, GB:1000000000, T:1000000000000, TB:1000000000000, P:1000000000000000, PB:1000000000000000, E:1000000000000000000, EB:1000000000000000000, Z:1000000000000000000000, ZB:1000000000000000000000 }; // unit to file size if ( unit !== undefined ) { unit = unit.toUpperCase(); } sort_size = size * factor[unit]; // convert byte size to multiplication factor return sort_size; } // convert numeric sizes to display format function formatBytes(val, decimals) { if (val === 0) return '0 Bytes'; const k = 1024, dm = (decimals < 0 ? 0 : decimals), sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'], i = Math.floor(Math.log(val) / Math.log(k)); return parseFloat((val / Math.pow(k, i)).toFixed(dm)) +' '+ sizes[i]; } // process date function processDate(match,p1,p2,p3) { //date formats: 2017-10-09 13:12 || 2015-07-25T02:02:57.000Z || 12-Mon-2017 21:11 const mo = 'JanFebMarAprMayJunJulAugSepOctNovDec'.indexOf(p2)/3 + 1; // e.g., convert month into number, or use number return p3 +'-'+ mo +'-'+ p1; } // Index Prep: get row date 2015-07-25T02:22:00.000Z function getItemDate(val) { let 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 .replace(/-|:|\s+|\//g,''); // remove spacing characters return sort_date; } // Stats function buildStats(stats_classes,stats_kinds) { stats_classes.sort(); let total = stats_classes.length, counts = [], kinds = [], stats_rows = [], total_dirs_invisible = 0, total_files_invisible = 0; for ( let i = 0; i < total; i++ ) { // get key/value pairs for item_classes/total counts counts[stats_classes[i]] = 1 + ( counts[stats_classes[i]] || 0 ); if ( /invisible|ignored/.test(stats_classes[i]) ) { if ( /dir/.test(stats_classes[i]) ) { total_dirs_invisible++; } if ( /file/.test(stats_classes[i]) ) { total_files_invisible++; } } } 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 let total_dirs = ( kinds.dir || 0 ); let total_files = ( total - total_dirs ); for ( let count in counts ) { // assemble stats row for each kind of item let kinds_items = count.split(' '), stats_row_kinds = ''; kinds_items.forEach( item => stats_row_kinds += (`${ item }`)); let stats_row = `${ stats_row_kinds }`; stats_rows.push(stats_row); } let stats = `
    '); } let num_glyphs = font.numGlyphs; // glyph count $font_info.find('tbody') .append(''); $content_font.find('#font_viewer').prepend($font_info); } // Show glyph viewer $content_font.on('click','.glyph',function(e) { e.stopPropagation(); let id = $(this).attr('id'); $(this).parent('div').addClass('selected').siblings().removeClass('selected'); showFontGlyph(id); }); function showFontGlyph(id) { let $glyph_viewer = $('#glyph_viewer'); let glyphX = $('#'+ id).data('contextX'); id = id.slice(id.lastIndexOf('_') + 1); // convert id to number only let glyphs = $content_font.data('data-glyphs'); // get glyphs let glyph = glyphs.get(id); let glyph_name = glyph.name; let glyph_path = glyph.getPath(glyphX,84,72); let glyph_SVG = glyph_path.toSVG().replace(/"/g,'\''); // set the background SVG image: let SVG_prefix = 'url("data:image/svg+xml;utf8,'; $glyph_viewer.show().css({'background-image': SVG_prefix + glyph_SVG +'")'}); $glyph_viewer.data('data-raw-svg',glyph_SVG).data('data-glyph-name',glyph_name).find('#glyph_viewer_info div').text(id +': '+ glyph_name +', #'+ glyph.unicode ); // for saving SVG $content_pane.attr('data-content','has_glyph'); } // Save glyph svg function saveGlyph() { let file_name = $('#glyph_viewer').data('data-font-name') +'—'+ $('#glyph_viewer').data('data-glyph-name') +'.svg'; let data_prefix = ''; let data_suffix = ''; let data = data_prefix + $('#glyph_viewer').data('data-raw-svg') + data_suffix; saveFile(data,'image/svg+xml',file_name); } $content_font.on('click','#save_svg',function(e) { e.stopPropagation(); saveGlyph(); }); // END OPEN FONT FILE Operations // Create new #content_pdf function setupContentPDF() { let $embed = ''; $('#content_pdf').remove(); $content_image_container.after($embed); } // OPEN LINK FILES (webloc, url) function openLink(ext) { let link, content = ( $protocol === 'file:' ? $('#iframe_body pre').text() : document.getElementsByTagName('iframe')[0].contentWindow.document.children[0].children[1].innerHTML ); switch(true) { case content === undefined: break; case ( /webloc|inetloc/.test(ext) ): link = content.split('')[1].split('')[0]; break; case ext === 'url': link = content.split('URL=')[1].split('\n')[0]; } let url = ( link.startsWith('file') ? link : newURL(link) ); switch(true) { case url === undefined: break; case link.startsWith('file') && window.location.protocol !== 'file:': // local files and links $body.addClass('has_warning').find('#warnings_container').addClass('warning_local_file'); $('#warning_btn_ok').focus(); break; case link.startsWith('file') && window.location.protocol === 'file:': // local files and links case url.origin === window.location.origin: // same origin sendMessage('top','show_content','',[link,getLinkInfo(link)[3]]); break; default: window.open(link,'_blank'); // open non-local links in new tab } } // END SHOW INDIVIDUAL CONTENT TYPES // ***** MAIN SHOW CONTENT FUNCTIONS ***** // // Set Content Pane classes function setContentClasses(id, kind, content_el) { switch(true) { // add loaded or playing classes case kind === 'audio': // case kind === 'video': getElById(id).addClass('selected').siblings('.audio').removeClass('audio_loaded selected'); break; default: // non-media items getElById(id).addClass('content_loaded').siblings().removeClass('content_loaded'); } switch(true) { case kind === 'audio': if ( id === 'content_iframe_file' ) { $content_pane.addClass('has_iframe_audio'); } else { $content_pane.addClass('has_audio'); } break; case ( /app|dir/.test(kind) ): // apps and directories if ( id === 'content_iframe_file' ) { $content_pane.attr('data-content','has_dir').addClass('has_iframe_dir'); } else { $content_pane.attr('data-content','has_dir').addClass('has_dir').removeClass('has_iframe_dir has_iframe_file'); } break; default: if ( id === 'content_iframe_file' ) { $content_pane.removeClass('has_dir').addClass('has_iframe_file').attr('data-content','has_'+ kind);} else { $content_pane.attr('data-content','has_'+ kind).removeClass('has_dir has_iframe_dir has_iframe_file'); } } $('#content_'+ content_el).addClass('has_content'); } // add body classes // function addBodyClasses(params) { // for ( let param of params ) { // switch(true) { // case getSearchParam(param) === '': case getSearchParam(param) === undefined: break; // do nothing if query undefined // case getSearchParam(param) === 'true': document.getElementsByTagName('body')[0].classList.add(param); break; // else add the query key if true // default: document.getElementsByTagName('body')[0].classList.add(getSearchParam(param)); // or add the query value // } // } // } // LINKS, SEARCH PARAMS, AND QUERIES // get query string for various content kinds function makeSearchParams(params) { let query_str = new URLSearchParams(); for ( let param of params ) { query_str.append(param,getSearchParam(param)); } return query_str; } function getLinkQueries(kind) { let query_str, params; switch(true) { case kind === 'pdf': // pdfs query_str = '#view=fitB&scrollbar=1&toolbar=1&navpanes=1'; break; case ( /text|markdown|code/.test(kind) ): // editable text files params = ['split_view','default_text_view','theme','sync_scroll']; query_str = '?'+ makeSearchParams(params).toString() +"&editor_theme="+ ( getSearchParam('editor_theme') === 'default' ? getSearchParam('theme') : getSearchParam('editor_theme') === 'light' ? 'light' : 'dark') + "&enable_text_editing="+ ( $('body').hasClass('enable_text_editing') ? 'true' : 'false' ); break; case ( /app|dir/.test(kind) ): // apps and directories params = ['sort_by','sort_direction','show_numbers','use_custom_icons','show_invisibles','hide_ignored_items','ignore_ignored_items','alternate_background','theme']; query_str = '?'+ makeSearchParams(params).toString(); } return query_str; } // CREATE LINKS FOR VARIOUS CONTENT KINDS function setContentSources(id, kind, link, content_el) { switch(true) { // make source link for content elements; default is link case kind === 'font': setContentFontSource(id,false,'',link); break; } $('#content_'+ content_el).attr('src',link); // set source for content element } // SET CONTENT TITLE function setContentTitle(link) { // kind = dir or files let $title = $('#title span'), title_text = ''; let $selected = $('#tbody tr.selected'),selected_link, selected_title, content_link;//, content_title; selected_link = ( $selected.length === 1 ? decodeURIComponentSafe($selected.find('a').attr('href')).trim() : '' ); // selected sidebar item; link = href selected_title = ( $selected.length === 1 ? $selected.find('.tbody_row_cell_name_a_span').text() : '' ); // title from stored data let content_link_info = getLinkInfo(link); // actual loaded content src; if iframe, link is from iframe data-content_name = full path of current item w/o queries content_link = ( content_link_info !== undefined ? decodeURIComponentSafe(content_link_info[0]).trim() : '' ); // content_title = ( content_link_info !== undefined ? content_link_info[1] : '' ); switch(true) { case selected_link !== content_link: title_text = content_link; break; // unsynced iframe items default: title_text = selected_title; // synced iframe items } title_text = title_text.split('/').join('/').split('_').join('_'); // allow nice line breaks in title $title.empty().html(title_text); // set title text } // Get Image Dimensions function getImageDimensions(link, callback) { if ( link !== undefined ) { let img = new Image(); img.src = link; img.onload = function() { callback( this.width, this.height ); }; } } function setImageDimensions() { switch(true) { case window.top !== window.self: break; // ignore iframe content case $content_pane.attr('data-content') === 'has_image': getImageDimensions( $content_image.attr('src'), function( width,height ) { $('#title span').attr('data-after',' ('+ width +'px × '+ height +'px) ('+ ( ($content_image.width()/width)*100 ).toFixed(1) +'%)'); // callback }); break; default: $('#title span').removeAttr('data-after'); // remove image dimensions } } // SHOW IFRAME CONTENT called when iframe loads function showIframeContent(args) { let link = $content_iframe.attr('data-content_name'), kind = $content_iframe.attr('data-kind'); switch(true) { // make source link for content elements; default is link case ( /app|dir/.test(kind) ): $content_pane.attr('data-content','has_dir'); break; default: $content_pane.attr('data-content','has_'+kind); } if ( args[0].indexOf('subdirectory=true') < 0 ) { $content_pane.attr('data-loaded','loaded'); setContentTitle(link,kind); } // show iframe, hide loading spinner, set title } // SHOW OTHER CONTENT function showContent(id, args) { // show any content on row click; last two arguments are only from clicking iframe links; args = [link,kind] let row, link_info, link, kind; switch(true) { // set vars and content_iframe attributes for iframe content or sidebar content case ( /grid_btn|text_editor|grid_btn|show_image_grid|show_font_grid/.test(id) ): break; // ignore these ids (i.e, don't get links) case id === 'view_directory_source': break; case ( /content_iframe_dir|open_sidebar_in_content_pane/.test(id) ): // prep for iframe dirs $('body').addClass('focus_content'); link_info = getLinkInfo(args[0]); link = link_info[0]; // set links for default (chrome) and gecko browsers link += getLinkQueries('dir'), kind = link_info[3]; $content_pane.addClass('has_iframe_dir').removeClass('has_iframe_file'); $content_iframe.removeAttr('data-iframe_file_src'); // because it's not needed after opening a dir break; case id === 'content_iframe_file': // prep for iframe files $('body').addClass('focus_content'); link_info = getLinkInfo(args[0]); link = link_info[0]; // set links for default (chrome) and gecko browsers kind = link_info[3]; switch(true) { case kind === 'audio': $content_pane.addClass('has_iframe_audio'); break; default: $content_pane.addClass('has_iframe_file'); $content_iframe.attr('data-iframe_file_src',$content_iframe.attr('src')); // remember iframe file parent directory } break; case id !== '': // show content from sidebar items or iframe items (from above) and get link, kind row = getElById(id); // link = getFormattedLink(row.find('a').attr('href'),row.attr('data-kind')); link_info = getLinkInfo(row.find('a').attr('href')); kind = link_info[3]; link = link_info[0]; // set links for default (chrome) and gecko browsers switch(true) { case kind === 'dir': case kind === 'app': case ( /code|text|markdown/.test(kind) ): // add link queries for dirs and text link = link + getLinkQueries(kind); break; case ( /text|markdown|code/.test(kind) ): // sendMessage('iframe','get_text_selection','',[row.attr('data-selection_start'),row.attr('data-selection_end')]); break; } break; } let content_el = ( ['audio','font','image','pdf','video'].includes(kind) ? kind : 'iframe' ); switch(true) { case window.top !== window.self: break; // don't attempt to show content for iframe items when navigating case row !== undefined && row.hasClass('ignored') && getSearchParam('ignore_ignored_items') === 'false': // try to open ignored files if they are not to be ignored window.location = row.find('a').attr('href'); break; case row !== undefined && row.hasClass('ignored') && getSearchParam('ignore_ignored_items') === 'true': // don't open ignored files if they are to be ignored closeContent(); setContentTitle($('tr.selected').find('a').attr('href')); $content_pane.attr('data-content','has_ignored'); break; case kind === 'link': closeContent(); $content_pane.attr('data-loaded','loaded'); $content_iframe.addClass('has_content').attr('src',link).attr('data-kind',kind); break; case ( /grid_btn|show_image_grid|show_font_grid/.test(id) ): showGrid(id); break; // show grid case id === 'text_editor' || id === 'text_editor_row': showTextEditor(); break; // show the text editor case kind === 'audio': showAudio(id,link); break; // show audio case kind === 'video': showVideo(id,link); // no break; close audio when showing video ???? test if needed? case ['font','image','pdf','video'].includes(kind): closeContent(); // Close all open content setContentSources(id, kind, link, content_el); // Set the src attribute for the content element setContentClasses(id, kind, content_el); // Add "data=has_" to content_pane; test id for content_iframe setContentTitle($('#content_container .has_content:not(#content_iframe)').attr('src')); // no break; Set the title for non-iframe content case kind === 'image': setImageDimensions(); break; case id === 'view_directory_source': kind = 'view_directory_source'; link = current_location + '?&view_directory_source=true'; // no break; case kind === 'dir' && id !== 'content_iframe_dir': // no break; case kind === 'app' && id !== 'content_iframe_dir': // no break; case content_el === 'iframe': // set iframe source and hide until iframe loaded closeContent(); // Close all open content $content_pane.attr('data-loaded','unloaded'); // hide iframe until loaded, show loading spinner $content_iframe.addClass('has_content').attr('src',link).attr('data-kind',kind); // set iframe source, data-kind if ( id !== 'view_directory_source' ) { $content_iframe.attr('data-content_name',link_info[0]); } break; } if ( $('body').hasClass('focus_content') || id === 'content_iframe' ) { sendMessage('iframe','focus_iframe'); } } // Show source of current sidebar dir in content pane function showDirectorySource() { switch(true) { case $body.hasClass('has_directory_source'): $('#close_btn').click(); break; // close if open default: $body.addClass('has_directory_source'); showContent('view_directory_source',[current_location]); } } $('#view_directory_source').on('click',function(e) { e.stopPropagation(); showWarning( 'showDirectorySource' ); }); // toggle show directory source // Show current sidebar dir in content pane function openSidebarInContentPane() { showContent('open_sidebar_in_content_pane',[current_location,'dir']); focusContent(); } $('#open_in_content_pane').on('click',function() { showWarning( 'openSidebarInContentPane' ); }); function openInTextEditor() { let scroll_script = ''; let html = $('#content_text').data('edit_html'); closeContent(); showTextEditor(); if ( html !== undefined ) { $('#text_source').val(html); $('#text_preview').attr( 'srcdoc',scroll_script + html ); $('#html_preview').val(html); } // set previewed text $('#content_text').removeData('edit_html'); } $('#open_in_text_editor').on('click',function() { sendMessage('iframe','get_html_content'); }); // send message to iframe, receive message from iframe, and then call openInTextEditor(). //***** CLOSE CONTENT (Close button or Cmd/Ctrl + W) *****// // Close Audio/Video function closeMedia(kind) { // type === audio || video let $media_el = ( kind === 'audio' ? $audio_player : $content_video ); $media_el.trigger('pause').removeAttr('src'); switch(true) { case kind === 'audio': $('tr.audio_loaded').removeClass('audio_loaded'); $('#content_audio_title span').empty(); $('#content_audio_playlist').removeClass('has_content'); $('body').removeClass('is_playing is_paused'); $content_pane.removeClass('has_audio has_iframe_audio'); sendMessage('iframe','close_iframe_audio'); break; case kind === 'video' && $('#content_pane').attr('data-content') === 'has_video': $('tr.video.content_loaded').removeClass('content_loaded'); $('#content_pane').removeAttr('data-content').find('#content_video').removeClass('has_content'); break; } } $('#close_audio').on('click',function(e) { e.stopPropagation(); closeMedia('audio'); }); // Close Audio button click // Close Playlist function closePlaylist(bool) { // bool === true: close all content $('#tbody').empty().html($('#tbody').data('dir_list')); // restore original dir_list $body.removeClass('has_playlist has_filelist has_media has_audio has_video has_fonts has_images has_menu') .addClass( $('#tbody').data('data_classes').join(' ') ).find('#sort_by_date,li#date').removeClass('disabled'); // restore orignal body "has" classes $('head').find('title').text('Index of: '+ current_location); // change window title back to default $('#current_dir_path').find('span').empty().html(current_dir_path); if ( $('#tbody').find('.media').length < 1 ) { $body.removeClass('has_media has_audio has_video'); } if ( bool === true ) { closeContent(); } $('tr.selected.playlist').find('a').click(); updateStats(); } $('#close_playlist,#close_playlist_btn').on('click', function(e) { e.preventDefault(); e.stopPropagation(); showWarning('closePlaylist',true); }); // true === close all content // Close index source preview function closeIndexSource() { $body.removeClass('has_directory_source'); $content_pane.removeAttr('data-content').removeAttr('data-loaded'); // $content_iframe.removeAttr('src').removeClass('has_content'); // remove iframe src $('#title span').empty(); // empty content title if ( $('#tbody tr.content_loaded').length === 1 ) { $('#tbody tr.content_loaded').click(); } focusSidebar(); } // Close opened font file function closeFontFile() { $body.removeClass('faded focus_content'); $content_pane.removeAttr('data-content').find('#glyphs_container').empty(); $('#font_viewer').remove().end().append( content_font_viewer ); $('#title span').empty(); $('tr.content_loaded').click(); } // Close glyph function closeGlyph() { $('#glyph_viewer').hide().attr('style',''); $content_pane.attr('data-content','has_font_file'); $content_font.addClass('has_content'); scrollThis('glyphs_container','selected'); } // Close all .content elements before opening any new .content from sidebar; leave grid and text editor hidden. function closeContent() { switch(true) { case window.top !== window.self: break; default: // remove sources, data-content, styles, and classes from #content_pane $('#title span').removeAttr('data-after').empty(); // empty content title hideGrid(); hideTextEditor(true); // hide grid and texteditor $content_pane.removeClass('has_zoom_image has_scaled_image').removeAttr('data-content').removeAttr('data-loaded'); // remove content_pane classes and data $content_pane.find('.has_content').removeClass('has_content').removeAttr('src').removeAttr('style'); // remove .content classes, data, and attributes $content_font.find('#font_viewer').remove().end().append( content_font_viewer ); closeMedia('video'); $('#cue_sheet_track_list_container_video').removeClass('has_cue_sheet').empty(); // remove video cue sheet menu setupContentPDF(); $('.playlist_entry_container').removeClass('has_content').find('textarea').val(''); } } //////// NEW CLOSE BUTTON FUNCTION: replaces OLDcloseContent() function closeButton() { switch(true) { // CLOSE GRIDS & TEXT EDITOR case $content_pane.attr('data-content') === 'has_grid': // close grid closeGrid(); if ( $content_pane.hasClass('has_hidden_text_editor') ) { showHiddenTextEditor(); } else { focusSidebar(); } break; case $content_pane.attr('data-content') === 'has_text_editor': // hide text editor hideTextEditor(); if ( $content_pane.hasClass('has_hidden_grid') ) { showHiddenGrid(); } else { focusSidebar(); } break; case $body.hasClass('iframe_edited'): // close edited iframe file sendMessage('iframe','unloading'); break; // CLOSE FILES AND DIRECTORIES case $body.hasClass('has_directory_source'): // close directory source closeIndexSource(); break; case $content_pane.hasClass('has_iframe_file') && $content_pane.hasClass('has_iframe_dir'): { // close iframe file and reopen navigated container directory showContent('content_iframe_dir',[$content_iframe.attr('data-iframe_file_src'),'source_dir']); sendMessage('iframe','focus_iframe'); break; } case $content_pane.hasClass('has_iframe_file') && !$content_pane.hasClass('has_iframe_dir'): // close iframe file opened from previewed sidebar directory (not navigated directory) and reopen selected sidebar directory $content_pane.removeClass('has_iframe_file').removeAttr('data-content'); // no break; case $content_pane.hasClass('has_iframe_dir'): // close navigated iframe directory and reopen selected sidebar directory $content_pane.removeClass('has_iframe_dir'); clickRow($('#tbody tr.selected').attr('id')); // click selected sidebar item sendMessage('iframe','focus_iframe'); break; case $content_pane.attr('data-content') === 'has_dir': // close selected sidebar directory $('tr.selected:not(.audio)').removeClass('selected content_loaded'); $content_pane.removeAttr('data-content').removeAttr('data-loaded'); // $content_iframe.removeAttr('src').removeClass('has_content'); // remove iframe src $('#title span').empty(); // empty content title focusSidebar(); break; // CLOSE FONTS & GLYPHS case $content_pane.attr('data-content') === 'has_glyph': // close glyph showWarning( 'closeGlyph' ); break; case $content_pane.attr('data-content') === 'has_font_file': // close font file preview showWarning( 'closeFontFile' ); break; // CLOSE ALL OTHER CONTENT EXCEPT AUDIO case $content_pane.attr('data-content') === 'has_ignored': // close ignored content case $('#content_container .content').hasClass('has_content'): // close all other content case $('#content_image').hasClass('has_content'): // close all other content $('tr.selected').removeClass('content_loaded'); // don't remove selected in order to allow sidebar navigation to continue from closed file $content_iframe.removeAttr('data-iframe_file_src').removeAttr('data-kind'); closeContent(); focusSidebar(); switch(true) { case $content_pane.hasClass('has_hidden_grid'): showGrid(); break; case $content_pane.hasClass('has_hidden_text_editor'): showTextEditor(); break; } break; // CLOSE AUDIO & PLAYLISTS case $content_pane.hasClass('has_audio'): // close audio closeMedia('audio'); focusSidebar(); break; case $body.hasClass('has_playlist'): // close playlist case $body.hasClass('has_filelist'): // close filelist showWarning( 'closePlaylist' ); break; } // show hidden texteditor or grid } $('#close_btn').on('click', function(e) { e.stopPropagation(); e.preventDefault(); showWarning('closeButton'); $(this).blur(); }); //***** RESET CONTENT (Reset button or Cmd/Ctrl + R) *****// function reloadImage() { $content_pane.removeClass('has_zoom_image has_scaled_image').find('#content_image_container,#content_image').removeAttr('style'); $('#content_image').removeAttr('src'); $('#tbody').find('.image.selected,.image.content_loaded').find('a').click(); // clicking allows changed content to be reloaded instead of just being reset } function resetContent() { switch(true) { case $content_pane.hasClass('has_audio'): // pause and reset media case $content_pane.attr('data-content') === 'has_video': $audio_player.prop('currentTime', 0).trigger('pause'); $content_video.prop('currentTime',0).trigger('pause'); break; } switch(true) { case $content_pane.attr('data-content') === 'has_text_editor': case $body.hasClass('has_playlist'): break; // don't do anything else for audio, video, text editor, playlist content. case $content_pane.attr('data-content') === 'has_grid': $('#grid_btn').click(); break; case $content_pane.attr('data-content') === 'has_glyph': $('#glyph_viewer').css({'width':'100%','height':'100%','background-size':'100%'}).attr('data-scale',1); break; case $content_pane.attr('data-content') === 'has_font': $content_font.css({'font-size':'1em'}); break; case $content_pane.attr('data-content') === 'has_image': reloadImage(); break; case ( /has_markdown|has_htm|has_iframe|has_dir/.test( $content_pane.attr('data-content') ) ): $('#tbody').find('.selected').find('a').click(); // clicking allows changed content to be reloaded instead of just being reset break; case $content_pane.hasClass('has_audio'): case $content_pane.hasClass('has_video'): break; // don't do anything else for audio, video, text editor, playlist content. case ( /has_ignored|undefined/.test( $content_pane.attr('data-content') ) ): window.location = window.location.href; break; } } $('#reload_btn').on('click', function(e) { e.stopPropagation(); e.preventDefault(); showWarning('resetContent'); $(this).blur().removeClass('reset'); }); //**********************// //***** NAVIGATION *****// function firstRowID(class_name) { return ( $('#tbody').find('> tr:visible').filter(class_name).length ? $('#tbody').find('> tr:visible').filter(class_name).first().attr('id') : null ); } function lastRowID(class_name) { return ( $('#tbody').find('> tr:visible').filter(class_name).length ? $('#tbody').find('> tr:visible').filter(class_name).last().attr('id') : null ); } function prevRowID(class_name) { let $selected, row_ID; if ( /audio/.test(class_name) ) { $selected = $('tr.audio.selected,tr.audio_loaded'); } else { $selected = $('#tbody').find('.selected'); } switch(true) { case !$selected.length: case !$selected.prevAll('tr:visible:not(.disabled):not(.unchecked)').filter(class_name).length: row_ID = $('#tbody').find('> tr:visible:not(.disabled):not(.unchecked)').filter(class_name).last().attr('id'); // select last item if nothing selected or selected is first item break; default: row_ID = $selected.prevAll('tr:visible:not(.disabled):not(.unchecked)').filter(class_name).first().attr('id'); } return row_ID; } function nextRowID(class_name,bool) { // if nothing selected, or if no next row with class_name, return first row with class_name, else return next row with class_name; bool --> autoplay audio let $selected, row_ID; if ( bool === 'true' ) { $selected = $('tr.audio.selected,tr.audio_loaded'); class_name === 'audio'; } else { $selected = $('#tbody').find('tr.selected'); } switch(true) { case !$selected.length: case !$selected.nextAll('tr:visible:not(.disabled):not(.unchecked)').filter(class_name).length: // row_ID = $('#tbody').find('> tr:visible:not(.disabled):not(.unchecked)').filter(class_name).first().attr('id'); // select last item if nothing selected or selected is first item break; default: row_ID = $selected.nextAll('tr:visible:not(.disabled):not(.unchecked)').filter(class_name).first().attr('id'); } return row_ID; } // get selected row id function selectRowID(class_name,key,bool) { // bool === true --> autoplay media let id = ''; switch(key) { case 'ArrowUp': id = prevRowID('.dir,.file'); break; case 'ArrowLeft': id = prevRowID(class_name); break; case 'ArrowRight': id = nextRowID(class_name,bool); break; case 'ArrowDown': id = nextRowID('.dir,.file'); break; } return id; } // NAVIGATION: Menu Arrow Navigation function menuNavigation(key) { let $selected_menu_item = $('#menu').find('ul li.selected'); switch(key) { case 'ArrowUp': switch(true) { case $('#menu').find('.hovered').length > 0: // if submenu visible switch(true) { // if no menu item selected, select last submenu item, else select previous submenu item case !$selected_menu_item.length: $('#menu').find('ul li:last-of-type').addClass('selected'); break; default: $selected_menu_item.removeClass('selected').prevAll('li:visible').first().addClass('selected'); } break; case !$('#menu').find('.selected').length: $('#menu').find('> li:last-of-type').addClass('selected'); break; // select last item default: $('#menu').find('.selected:not(.hovered)').removeClass('selected').prevAll('li:visible').first().addClass('selected'); } break; case 'ArrowDown': switch(true) { case $('#menu').find('.hovered').length > 0: // if submenu visible switch(true) { // if no menu item selected, select first submenu item, else select next submenu item case !$selected_menu_item.length: $('#menu').find('ul li:first-of-type').addClass('selected'); break; default: $selected_menu_item.removeClass('selected').nextAll('li:visible').first().addClass('selected'); } break; case !$('#menu').find('.selected').length: $('#menu').find('> li:first-of-type').addClass('selected'); break; default: $('#menu').find('.selected:not(.hovered)').removeClass('selected').nextAll('li:visible').first().addClass('selected'); } break; case 'ArrowLeft': switch(true) { case !$('#menu').find('.selected').length: $('#menu').find('> li:last-of-type').addClass('selected'); break; case $('#menu').find('li.selected').hasClass('hovered'): $('#menu').find('.hovered').removeClass('hovered'); $('#menu').find('ul li').removeClass('selected'); break; } break; case 'ArrowRight': switch(true) { case !$('#menu').find('.selected').length: $('#menu').find('> li:first-of-type').addClass('selected'); break; case $('#menu').find('li.selected').hasClass('has_submenu') && !$('.submenu').find('.selected').length: $('#menu').find('.selected').addClass('hovered').find('ul li:first-of-type').addClass('selected'); break; } break; } } // NAVIGATION: select GRID items by left/right arrow keys @ arrowNavigation(); function gridNavigation(elId,key) { const $container_el = $(elId); let $selected = $container_el.find('> .selected'); let grid_item_id; // = grid item data-id = corresponding dir_list item id. let row_length = 0; // $content_grid.hasClass('has_grid') || $('#content_font').hasClass('has_font_grid') row_length = ( Math.round( $container_el.innerWidth() / $container_el.find('> div').outerWidth()) - 1 ); switch(true) { case $selected.length === 0: // nothing selected switch(true) { case key === 'ArrowRight': case key === 'ArrowDown': $container_el.find('> div').first().addClass('selected'); break; case key === 'ArrowLeft': case key === 'ArrowUp': $container_el.find('> div').last().addClass('selected'); break; } grid_item_id = $container_el.find('.selected').attr('data-id'); break; case key === 'ArrowUp': switch(true) { case $selected.prevAll().eq(row_length).length === 1: grid_item_id = $selected.prevAll().eq(row_length).attr('data-id'); break; case $container_el.find('> div').length - 1 === row_length: gridNavigation(elId,'ArrowLeft'); break; // for single grid row, select prev item default: grid_item_id = $container_el.find('> div').last().attr('data-id'); break; } break; case key === 'ArrowDown': switch(true) { case $selected.nextAll().eq(row_length).length === 1: grid_item_id = $selected.nextAll().eq(row_length).attr('data-id'); break; case $container_el.find('> div').length - 1 === row_length: gridNavigation(elId,'ArrowRight'); break; // for single grid row, select next item default: grid_item_id = $container_el.find('> div').first().attr('data-id'); break; } break; case key === 'ArrowLeft': switch(true) { case $selected.prev().length === 1: grid_item_id = $selected.prev().attr('data-id'); break; default: grid_item_id = $container_el.find('> div').last().attr('data-id'); break; } break; case key === 'ArrowRight': switch(true) { case $selected.next().length === 1: grid_item_id = $selected.next().attr('data-id'); break; default: grid_item_id = $container_el.find('> div').first().attr('data-id'); break; } break; } selectThis(grid_item_id); switch(true) { case $content_pane.attr('data-content') === 'has_glyph': $('.glyph_container.selected .glyph').click(); break; // show the selected glyph case $content_pane.hasClass('has_hidden_grid'): $('#content_grid > div[data-id="'+ grid_item_id +'"]').click(); break; } } // NAVIGATION: FONTS and IMAGES by prev/next buttons $content_pane.on( 'click','#prev_btn, #next_btn', function(e) { e.stopPropagation(); e.preventDefault(); let key = $(this).attr('id') === 'prev_btn' ? 'ArrowLeft' : 'ArrowRight'; switch(true) { case $content_pane.attr('data-content') === 'has_font_viewer': gridNavigation('#glyphs_container',key); break; default: clickRow( selectRowID('.font,.image',key)); } if ( $('body').hasClass('focus_content') ) { focusContent(); } }); // NAVIGATION: Audio by prev/next audio buttons $('.prev_next_track_btn').on('click',function() { let key = ( $(this).attr('id') === 'prev_track' ? 'ArrowLeft' : 'ArrowRight' ); playPrevNextMediaTrack(key); }); function upDownArrowNavigation(e) { switch(true) { case ( /a|button|input|select|textarea/.test(document.activeElement.tagName.toLowerCase() ) && window.top !== window.self ): case ( /text_preview|html_preview/.test(document.activeElement.id) ): return; // don't navigate if text editor previews are focussed default: document.activeElement.blur(); } e.preventDefault(); document.activeElement.blur(); $('body').focus(); // Need this to able to select grid items after clicking close button (or other buttons). let row_ID = selectRowID('.dir:visible,.file:visible',e.key); let row = $(document.getElementById(row_ID)); let $selected = $('#tbody').find('.selected'); switch(true) { case cmdKey(e) && e.key === 'ArrowUp': // Cmd/Ctrl + up arrow switch(true) { case window.self !== window.top: $('#iframe_body #parent').find('a').click(); break; // go to iframe parent case window.self === window.top && $('body').hasClass('focus_content'): // fallback for go to iframe parent in case top is incorrectly focused sendMessage('iframe','open_iframe_parent_dir'); break; default: showWarning( 'clickThis', $('#parent_dir_nav').attr('id') ); break; // go to parent (with warning) } break; case cmdKey(e) && e.key === 'ArrowDown': // Cmd/Ctrl + down arrow switch(true) { case window.self === window.top && $('body').hasClass('focus_content'): focusContent('content_iframe',e); break; // select first item if nothing selected in iframe case window.self === window.top && $selected.hasClass('file') && !$selected.hasClass('link'): break; // do nothing for #top files case window.self === window.top && $selected.hasClass('dir') && $selected.hasClass('app') && $settings.apps_as_dirs === false: break; // do nothing for apps if not viewing as dirs default: $selected.find('a').trigger('dblclick'); // else double-click directories and all iframe items to open them } break; case $body.hasClass('has_directory_source'): // if viewing directory source, arrows will reopen selected sidebar item if ( $('#tbody .content_loaded').length > 0 ) { clickRow( $('#tbody .content_loaded').attr('id') ); } else { clickRow( row_ID ); } scrollThis('tbody','selected'); break; case cmdKey(e) && window.self !== window.top: // iframe && cmdKey switch(true) { case $('#iframe_body #dir_list').length === 0: break; } break; case window.top === window.self && $('body').hasClass('focus_content'): $content_iframe.focus(); focusContent('iframe',e); sendMessage('iframe','iframe_arrow_navigation','',e.key); $('#iframe_body a').click(); break; default: // click prev/next item selectThis(row_ID); // don't use $selected after this switch(true) { case row.hasClass('audio'): break; // only select row default: showContent(row_ID); // otherwise show content } if ( $('.selected').length ) { scrollThis('tbody','selected'); } } } // NAVIGATION: Prev/Next Audio or Prev/Next File of same data-kind by arrow left/right function leftRightArrowNavigation(e) { switch(true) { case ( /a|button|input|select|textarea/.test(document.activeElement.tagName.toLowerCase() ) && window.top !== window.self ): // no break case ( /text_preview|html_preview/.test(document.activeElement.id) ): return; // don't navigate if text editor previews are focussed default: // document.activeElement.blur(); // $('tr.selected').find('a').focus(); } switch(true) { case cmdKey(e): switch(true) { case e.key === 'ArrowRight' && $('tr.selected').hasClass('dir'): $('tr.dir.selected').find('.has_icon_before_before').click(); break; // open subdirectory case e.key === 'ArrowLeft' && $('tr.selected').hasClass('has_subdirectory'): closeSubdirectory($('tr.dir.selected').attr('id') ); break; // close subdir case e.key === 'ArrowLeft' && !$('tr.selected').hasClass('has_subdirectory') && $('tr.has_subdirectory').length > 0: clickRow($('tr.selected').prevAll('.has_subdirectory').first().attr('id')); break; // jump to parent directory default: clickRow($('tr:visible').first().attr('id')); // jump to first visible item } scrollThis('tbody','selected'); break; case cmdAltKey(e) && e.key === 'ArrowLeft': case cmdAltKey(e) && e.key === 'ArrowRight': // don't start audio tracks when changing tabs break; default: e.preventDefault(); let $selected = $('#tbody').find('.selected'); switch(true) { // L/R Arrow + Alt + Shift case e.altKey && e.shiftKey: case e.altKey && !e.metaKey && !e.ctrlKey: { // Skip audio 30s/10s: Opt-Shift-Arrow/Opt-Arrow let args = [e.key]; if ( e.shiftKey ) { args.push(30); } else { args.push(10); } switch(true) { case window.top !== window.self: sendMessage('top','mediaSkip','mediaSkip',args); break; default: mediaSkip(e); } break; } case $body.hasClass('has_directory_source'): // if viewing directory source switch(true) { case $('#tbody .content_loaded').length > 0: clickRow( $('#tbody .content_loaded').attr('id') ); break; default: clickRow( selectRowID('.dir,.file',e.key) ); } scrollThis('tbody','selected'); break; default: switch(true) { case $selected.length === 0: clickRow( selectRowID('.dir,.file',e.key) ); break; // if nothing selected, click first/last row case $selected.hasClass('media') && $body.hasClass('play_all_media'): // no break; play all media case $selected.hasClass('audio'): playPrevNextMediaTrack(e.key); break; // play prev/next media track default: clickRow( selectRowID('.'+ $selected.attr('data-kind'),e.key) ); // click prev/next item with same data-kind if ( $selected.hasClass('video') ) { playMedia('play'); } } scrollThis('tbody','selected'); } } } // NAVIGATION: AUDIO tracks by arrow left/right @ leftRightArrowNavigation() & button click; // handle shuffle or loop play, as well as normal continuous playback after end of track with ArrowRight function playPrevNextMediaTrack(key,bool) { // bool === 'true' if from initMedia let track_row_id, $selected_media; switch(true) { case bool === 'true': // autoplay media (if selected audio !== playing audio, select selected audio, else select loaded media) $selected_media = ( $('#tbody tr.audio.selected').length === 1 && $('#tbody tr.audio_loaded') !== $('#tbody tr.audio.selected') ? $('#tbody tr.audio.selected') : $('#tbody tr.audio_loaded,#tbody tr.video.content_loaded') ); break; default: $selected_media = $('#tbody tr.media.selected'); // normal l/r arrow play media } let media_class = ( $('body').hasClass('play_all_media') ? '.media:not(.ignored):not(.unchecked)' : '.'+ $selected_media.attr('data-kind') +':not(.ignored):not(.unchecked)' ); // if play all media, use .media, else use selected let selected_row_id = $selected_media.attr('id'), last_row_id = lastRowID( media_class ); switch(true) { case $selected_media.length === 1 && !/loaded/.test($selected_media[0].classList.value): // if selected media track is not playing, click and play switch(true) { case window.self !== window.top: $selected_media.find('a').trigger('dblclick'); sendMessage('top','iframe_play_pause_media'); break; default: if ( media_class.indexOf('audio') > -1 ) { showAudio( selected_row_id ); } else { clickRow( selected_row_id ); } playMedia('play'); } break; case $('.audio_loaded').length === 0 && $('.video.content_loaded').length === 0: // Arrow L/R selects first/last media file if nothing selected clickRow( selectRowID('.audio_loaded',key) ); playMedia('play'); break; case $selected_media.length === 1: // if an audio file is loaded... switch(true) { case $body.hasClass('shuffle_media'): // and if shuffle play is checked... track_row_id = $audio_player.data('shufflelist').pop(); // remove track from shuffleTrackList switch(true) { case track_row_id !== undefined: // if shuffle list is not empty... if ( document.getElementById(track_row_id).classList.contains('audio') ) { showAudio( track_row_id ); } else { clickRow( track_row_id ); } playMedia('play'); break; // load media and play case track_row_id === undefined: // else if end of shufflelist... updateShuffleList(); switch(true) { case $body.hasClass('loop_media'): playMedia('play'); break; // and if loop audio, update the shufflelist and play default: clickRow( $('tr.media:not(.ignored)').first().attr('id') ); // else just load the first track } } break; default: // ordinary playback: play prev/next track if ( $selected_media.hasClass('audio') ) { showAudio( selectRowID( media_class,key,bool ) ); } else { clickRow( selectRowID( media_class,key ) ); } // show audio or click video switch(true) { case window.self !== window.top: $selected_media.find('a').trigger('dblclick'); sendMessage('top','iframe_play_pause_media'); break; // show iframe audio case key === 'ArrowRight' && selected_row_id === last_row_id && bool === 'true': // loop audio: autoplay first track at end of last track if ( $body.hasClass('loop_media') ) { playMedia('play'); } break; default: playMedia('play'); break; // play next track, don't play first track after last } break; } break; } scrollThis('tbody','audio_loaded'); scrollThis('tbody','.video.content_loaded'); } // NAVIGATE directory index items by arrow up/down or left/right keys, with/without grid @ indexNavigation() function arrowNavigation(e) { if ( $('body').hasClass('focus_content') && !$('body').hasClass('has_menu') ) { focusContent(); } switch(true) { case window.top !== window.self && document.activeElement.getAttribute('id') === 'iframe_body' && $('#iframe_body').find('#dir_list').length === 0: // allow arrows to work normally in iframes (but not iframe dirs) case ( /img|textarea|embed/.test( document.activeElement.tagName.toLowerCase() ) ): // allow arrows to work normally in images and textareas case document.activeElement.hasAttribute('contentEditable'): // allow arrows to work normally in contentEditable elements break; default: e.preventDefault(); // otherwise prevent default arrow behavior document.activeElement.blur(); // Need this to able to select grid items after clicking close button (or other buttons). case $('body').hasClass('has_menu'): // menu navigation if ( window.top !== window.self ) { sendMessage('top','menu_navigation','menuNavigation',e.key); } else { menuNavigation(e.key); } break; case $content_pane.attr('data-content') === 'has_grid' && $('body').hasClass('focus_content') && !cmdKey(e): // navigate grid case $content_pane.attr('data-content') === 'has_image' && $content_pane.hasClass('has_hidden_grid') && !cmdKey(e): // navigate hidden grid case $content_pane.attr('data-content') === 'has_font' && $content_pane.hasClass('has_hidden_grid') && !cmdKey(e): // navigate hidden grid gridNavigation('#content_grid',e.key); if ( !$content_pane.hasClass('has_hidden_grid') ) { scrollThis('content_grid','selected'); } scrollThis('tbody','selected'); break; case $content_pane.attr('data-content') === 'has_font_file' && !cmdKey(e): // navigate font file glyphs case $content_pane.attr('data-content') === 'has_glyph' && !cmdKey(e): // navigate font file glyphs if ( $('body').hasClass('focus_content') ) { gridNavigation('#glyphs_container',e.key); scrollThis('content_container','selected'); } else { showWarning('warning_close_font','warning_btn_cancel'); // show close font warning if sidebar focused } break; case e.key === 'ArrowUp' || e.key === 'ArrowDown': // up/down arrow navigation upDownArrowNavigation(e); break; case e.key === 'ArrowLeft' || e.key === 'ArrowRight': { // left/right arrow navigation leftRightArrowNavigation(e); break; } } } // NAVIGATION: by typed string var str = ''; function timeoutID() { return window.setTimeout( function() { str = ''; }, 1500 ); } function alphaNav(e) { // Select Dir_List row by typed string; Todo: select next row by nearest letter switch(true) { case document.activeElement.tagName.toLowerCase() === 'textarea' || document.activeElement.getAttribute('contentEditable') === true: break; default: let timer = timeoutID(); if ( typeof timer === 'number' ) { window.clearTimeout( timer ); timer = 0; } // id timeoutID(); str += e.key; str = str.toLowerCase(); if ( $('#dir_list').find('td.name[data-name^="'+ str +'"]').length ) { $('#dir_list').find('td.name[data-name^="'+ str +'"]').first().find('a').click(); scrollThis('tbody','selected'); // } else { // null; // replace this with some sort of fuzzy match function? TBD } } } //***** END NAVIGATION *****// // SELECT ROW on click and set classes for $content_pane function selectThis(row_ID) { let row = $(getElById(row_ID)); switch(true) { case row.hasClass('disabled'): break; case $content_pane.attr('data-content') === 'has_grid': case $content_pane.hasClass('has_hidden_grid'): {// Select corresponding grid item const $grid_selected = $content_grid.find('> div[data-id="'+ row_ID +'"]'); row.addClass('selected').siblings().removeClass('selected hovered'); $grid_selected.addClass('selected').siblings().removeClass('selected'); break; } case row.hasClass('audio'): row.addClass('selected').siblings().removeClass('selected'); break; default: $body.removeClass('has_directory_source'); row.addClass('content_loaded').siblings().removeClass('content_loaded'); row.addClass('selected').siblings().removeClass('selected hovered'); // remove classes from sibling, but leave .audio with playing } } // CLICK element by id function clickThis(id) { let el = $(document.getElementById(id)); if ( el.find('a').length > 0 ) { el.find('a').click(); } else { el.click(); } } // CLICK Row to show content function clickRow(id) { selectThis(id); showContent(id); } // On click row, play/pause media or clickRow $('#top #tbody').on('click','tr', function(e) { e.preventDefault(); e.stopPropagation(); focusSidebar(); switch(true) { case $(this).hasClass('audio'): if ( !$(this).hasClass('selected') ) { showContent($(this).attr('id')); } else { playPauseMedia(); } break; case $(this).hasClass('video'): if ( !$(this).hasClass('selected') ) { showWarning( 'clickRow', $(this).attr('id') ); } else { playPauseMedia(); } break; default: showWarning( 'clickRow', $(this).attr('id') ); } }); // SUBDIRECTORIES function openSubdirectory(parent) { // use the clicked subdirectory href as src for utility iframe, which loads that directory, processes the index and sends it back to the top iframe let this_id = parent.attr('id'), parent_link = parent.find('a').attr('href'), level = Number(parent.attr('data-level')) + 1; let content_iframe_utility_src = parent_link + getLinkQueries('dir') + '&parent_id='+ this_id +'&subdirectory=true&level='+ level; $('#content_iframe_utility').attr('src',content_iframe_utility_src ); parent.addClass('dir_list_subdir_loading'); // removed when content_iframe_utility sends loaded message with subdirectory data } function closeSubdirectory(id) { if ( id === undefined ) { // close all subdirectories if ( $('#tbody tr.has_subdirectory').length > 0 ) { closeContent(); } $('#tbody tr.dir').removeClass('has_subdirectory dir_list_subdir_loading'); $('#tbody').find('tr[id*="_"]').remove(); // remove any row whose id contains an underscore (= subdirectory row) } else { document.getElementById(id).classList.remove('has_subdirectory'); $('#tbody').find('tr').each(function() { if ( $(this).attr('id').startsWith(id + '_') ) { $(this).remove(); } // remove all rows whose id begins with the subdirectory parent id }); } updateStats(); } $('#tbody').on('click','tr.dir span.has_icon_before_before',function(e) { // open/close subdirectories e.preventDefault(); e.stopPropagation(); e.stopImmediatePropagation(); let $parent = $(this).closest('tr'); switch(true) { case ( /\.trashes|\.temporaryitems|\.spotlight-v\d+/.test($parent.find('.name').attr('data-name') ) ): $parent.removeClass('dir_list_subdir_loading'); break; default: selectThis($parent.attr('id')); if ( $parent.hasClass('has_subdirectory') ) { closeSubdirectory( $parent.attr('id') ); } else { openSubdirectory($parent); } if ( window.top === window.self ) { focusSidebar(); } else { document.activeElement.blur(); $('body').focus(); }// else { focusSidebar(); } } }); // DOUBLE-CLICK Row to open directory function doubleClickRow(args) { // row.dir only let row = document.getElementById(args[0]); if ( row.classList.contains('dir') && row.classList.contains('invisible') && row.classList.contains('ignored') ) { return; } // don't attempt to open ignored invisible dirs (chiefly system dirs) let id = args[0].slice(args[0].indexOf('-') + 1), href = args[1]; let query_str = decodeURIComponentSafe(window.location.search); if ( query_str === '' ) { query_str = '?'; } if ( query_str.indexOf('history') !== -1 ) { query_str = query_str.replace(/&selected=\d+/,'').replace(/&history=/,'&history='+ id +'+'); } else { query_str = query_str.replace(/&selected=\d+/,'') + '&history='+ id; } window.location = href + query_str; } $('#top').on('dblclick', '#tbody tr.dir,#tbody tr.link,#tbody tr.playlist', function(e) { e.preventDefault(); e.stopPropagation(); switch(true) { case $(this).hasClass('link'): if ( $protocol === 'file:' ) { sendMessage('iframe','open_link_file','',$(this).attr('data-ext')); } else { openLink($(this).attr('data-ext')); } break; case $(this).hasClass('playlist'): { let playlist_data = $(this).data('playlist'); openPlaylist('','',playlist_data); } break; default: showWarning('doubleClickRow', [$(this).attr('id'), $(this).find('a').attr('href')] ); } }); // click content header or audio to remove faded body class $('#sidebar_title,#sidebar_buttons,#sidebar,#sorting .sorting').on('click',function() { focusSidebar(); }); // CLICK grid item $content_grid.on('click','div', function(e) { e.preventDefault(); let id = $(this).attr('data-id'); $(this).addClass('selected').siblings().removeClass('selected'); hideGrid(); getElById(id).click(); }); // HOVER Grid Item and highlight dir_list row $content_grid.on('mouseenter','> div:not(.selected)',function() { document.getElementById(this.getAttribute('data-id')).classList.add('hovered'); }).on('mouseleave','> div:not(.selected)',function() { document.getElementById(this.getAttribute('data-id')).classList.remove('hovered'); }); // HOVER Dir_list_row: highlight corresponding grid item $dir_list_body.on('mouseenter','> tr', function() { if ( $content_pane.attr('data-content') === 'has_grid' ) { $content_grid.find('> div[data-id="'+ $(this).attr('id') +'"]').addClass('hovered'); } }).on('mouseleave','> tr', function() { if ( $content_pane.attr('data-content') === 'has_grid' ) { $content_grid.find('> div[data-id="'+ $(this).attr('id') +'"]').removeClass('hovered'); } }); // HOVER Footer Links: Fade dir_list $('#footer_links').on('mouseenter',function() { $('body:not(.has_menu), body:not(.has_menu_parents)').addClass('faded'); }).on('mouseleave',function() { $('body:not(.has_menu), body:not(.has_menu_parents)').removeClass('faded'); }); // CLICK/HOVER Stats Items: get the hovered stats item class function getHoveredStatsListClass(stats_item) { stats_item.classList.remove('file','media'); let this_class = '.'+ Array.from(stats_item.classList).join('.'); switch(true) { case $(stats_item).attr('id') === 'stats_summary_detailed_dirs': this_class = '.dir'; break; case $(stats_item).attr('id') === 'stats_summary_detailed_files': this_class = '.file'; break; // case /_/.test(this_class): this_class = this_class.replace('_','.'); break; case this_class === '.dir': this_class = '.dir:not(.ignored):not(.invisible):not(.app)'; break; case this_class === '.dir.app': this_class = '.dir.app:not(.ignored):not(.invisible)'; break; } return this_class; } // HOVER Stats Items: highlight dir_list items $('#tfoot').on('mouseenter','#stats_details tr, #stats_summary_detailed_dirs, #stats_summary_detailed_files', function() { $('#tbody').find(getHoveredStatsListClass(this)).addClass('hovered'); // add the hovered class if ( $('#tbody .hovered').length > 0 ) { $('#tbody .hovered')[0].scrollIntoView({ behavior:'smooth', block:'start', inline:'nearest' }); } // scroll first matching element into view }).on('mouseleave','tr',function() { $('#tbody tr').removeClass('hovered'); }); // CLICK Stats items: Select the first matched kind $('#tfoot').on('click','#stats_details tr, #stats_summary_detailed_dirs, #stats_summary_detailed_files', function() { $('#tbody').find(getHoveredStatsListClass(this)).first().find('a').click(); }); // END CLICK & HOVER EVENTS //***** AUTOLOAD CONTENT: index files or files from the file shortcut list *****// function autoLoadFile() { if ( window.self === window.top && getSearchParams().has('selected') ) { // add selected class $('#tbody tr[id="rowid-'+ Number(getSearchParam("selected")) +'"]').addClass('selected'); } let $selected = $('tr.selected'); if ( $selected.length === 0 ) { removeSearchParam('selected'); } // remove selected searchParam if item not found switch(true) { case $selected.length === 1 && $selected.hasClass('local'): case window.self !== window.top: // do nothing for iframes break; case getSearchParams().has('file'): {// load files (from bookmark or url) let $file = $('td.name span:contains("'+ decodeURIComponentSafe(getSearchParam('file')) +'")'); clickRow( $file.closest('tr').attr('id') ); removeSearchParam('file'); } break; case $('body').hasClass('has_audio') && getSearchParam('autoload_media') === 'true': // load audio; must be above next case // showAudio( $('.audio').first().attr('id'),'', 'true' ); // load audio clickRow( $('.audio').first().attr('id') ); // load audio autoLoadCoverArt(); // load image break; case $('body').hasClass('has_video') && getSearchParam('autoload_media') === 'true': clickRow( firstRowID('.video') ); break; // load video (if no audio) case $('body').hasClass('has_playlist'): case $('body').hasClass('has_filelist'): break; // do nothing for playlists and filelists ... but what about autoload media? case $selected.length === 1: showContent($selected.attr('id')); break; } if ( $selected.length === 1 ) { clickRow( $selected.attr('id')); } // click selected history item --> replaces auto-loaded cover art if ( getSearchParam('autoload_index_files') !== 'false' && $('tr.file.htm').find( 'a[href*="/index."]').length > 0 ) { // else load index file clickRow($('tr.file.htm').find('a[href*="/index."]').closest('tr').attr('id')); } if ( $('tr.selected').length === 1 ) { scrollThis('tbody','selected'); } } // get cover art names function getImageNames() { let images = $dir_list_body.find('.image'), image_names = [], image_id, image_name; for ( let i = 0; i < images.length; i++ ) { image_id = images.eq(i).attr('id'); image_name = images.eq(i).find('.name').attr('data-name'); image_name = image_name.slice(0,image_name.lastIndexOf('.') ); // remove extension image_names.push({'id':image_id,'name':image_name}); // add id and name } return image_names; } // get cover art id function getCoverArtID() { const cover_names = ['cover','front','album','jacket','sleeve','cd','disc','insert','liner','notes']; const image_names = getImageNames(); let id, exact_match, match, cover_name; for ( cover_name of cover_names ) { // test available image names against cover names exact_match = image_names.filter( el => el.name === cover_name ); // check for exact match (w/o extension) match = image_names.filter( el => el.name.indexOf(cover_name) > -1 ); // check for partial match if ( exact_match.length > 0 ) { return id = exact_match[0].id; } else if ( match.length > 0 ) { return id = match[0].id; } } if (id === undefined ) { return id = image_names[0].id; } // if no matches, return first image id } // Autoload Cover Art function autoLoadCoverArt() { // autoload cover art for audio if dir contains audio and images, and audio is loaded and non-image content is not loaded if ( !$('body').hasClass('has_images') ) { return; } // do nothing if no images found let cover_ID = getCoverArtID(); if ( cover_ID !== undefined ) { let row = getElById(cover_ID); row.addClass('content_loaded'); $content_pane.attr('data-content','has_image').find('#content_image').addClass('has_content').attr('src',row.find('a').attr('href')); $('#title span').empty().html(decodeURIComponentSafe(row.find('.name a span').text() ) ); // name setImageDimensions(); } let selected_ID = ( getSearchParam('selected').length > 0 ? 'rowid-'+ getSearchParam('selected') : undefined); if ( selected_ID !== undefined ) { document.getElementById(selected_ID).classList.add('selected'); $('.media').removeClass('selected'); } } //***** KEYBOARD EVENTS *****// function metaKey(e) { return ( navigator.platform.match('Mac') ? e.metaKey && !e.ctrlKey : e.ctrlKey); } function cmdKey(e) { return ( metaKey(e) && !e.altKey && !e.shiftKey ); } function cmdAltKey(e) { return ( metaKey(e) && e.altKey && !e.shiftKey ); } function cmdShiftKey(e) { return ( metaKey(e) && !e.altKey && e.shiftKey ); } $('#top, #iframe_body').on('keydown', function(e) { const $selected = ( $('#content_pane').attr('data-content') === 'has_font_file' ? $('.glyph_container.selected .glyph') : $('#tbody').find('.selected') ); switch(true) { // Disable all keydown events except escape, return, and tab when help or warning is shown, or when textarea is focused case document.activeElement.hasAttribute('contentEditable') && !/escape|tab/.test(e.key.toLowerCase()) && !( cmdKey(e) && e.key === 'w'): case ( /button|input|select|textarea/.test(document.activeElement.tagName.toLowerCase() ) && !/escape|tab|shiftkey|metakey|altkey/.test(e.key.toLowerCase()) ): case ($('body').hasClass('has_warning') || $('body').hasClass('has_help') ) && !( cmdKey(e) || metaKey(e) || (/escape|tab|shiftkey|enter/.test(e.key.toLowerCase()) ) ): return; } // MAIN KEYDOWN EVENTS switch ( e.key ) { case 'shiftKey': switch(true) { case $('body').hasClass('has_warning') || $('body').hasClass('has_help'): if (e.key !== 'Enter' && e.key !== 'Tab') { e.preventDefault(); return false; } break; } break; case 'ArrowUp': case 'ArrowDown': case 'ArrowLeft': case 'ArrowRight': switch(true) { case $('tr.selected').hasClass('link') && cmdKey(e) && e.key === 'ArrowDown': // webloc or url file case $('tr.selected').hasClass('playlist') && cmdKey(e) && e.key === 'ArrowDown': // webloc or url file $('tr.selected').dblclick(); break; default: showWarning( 'arrowNavigation',e ); // arrow navigation } break; case ' ': // space switch(true) { case $('#content_pane').hasClass('has_audio') || $('#content_pane').attr('data-content') === 'has_video': // Play/pause media (space bar) e.preventDefault(); e.stopPropagation(); playPauseMedia(); break; case window.top !== window.self && $('tr.audio_loaded').length === 1: e.preventDefault(); e.stopPropagation(); sendMessage('top','iframe_play_pause_media'); break; default: alphaNav(e); } break; case 'Enter': // Open directories (or ignored) switch(true) { case $('body').hasClass('has_warning') || $('body').hasClass('has_help'): e.preventDefault(); $('button.focus, button:focus').click(); // click focused warning button in #top or #iframe break; case window.self !== window.top: // if iframe... switch(true) { case $('body').hasClass('has_menu'): sendMessage('top','clickMenu'); // close main menu break; case $('tr.selected').hasClass('dir') && cmdKey(e): case $('tr.selected').hasClass('file') && cmdKey(e): // open iframe dir or file case $('tr.selected').hasClass('link') && cmdKey(e): // webloc or url file $('tr.selected').find('a').trigger('dblclick'); break; case !$('.audio.selected').hasClass('audio_loaded'): $('.audio.selected').find('a').trigger('dblclick'); break; case $('.audio_loaded').length === 1 && !$('.selected').hasClass('audio_loaded'): // play/pause media e.preventDefault(); e.stopPropagation(); playPauseMedia(); break; } break; case $('body').hasClass('has_menu'): e.preventDefault(); clickMenu(); sendMessage('iframe','close_menu'); // click selected menu item break; case $selected.hasClass('app') && $settings.apps_as_dirs === false: // don't open app folders break; default: switch(true) { case $selected.hasClass('.disabled'): case $content_pane.attr('data-content') === 'has_text_editor': break; case $selected.hasClass('audio') && !$selected.hasClass('audio_loaded'): // load selected audio file showAudio($('.audio.selected').attr('id')); break; case $selected.hasClass('media'): // else play/pause playing media e.preventDefault(); e.stopPropagation(); playPauseMedia(); break; case $selected.hasClass('link'): case $selected.hasClass('dir'): // else open dir case $('tr.selected').hasClass('link') && cmdKey(e): // webloc or url file case $('tr.selected').hasClass('playlist') && cmdKey(e): // webloc or url file $selected.find('a').trigger('dblclick'); break; default: $selected.click(); } } break; // Alphabetical navigation case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'X': case 'Y': case 'Z': case 'a': case 'b': case 'c': case 'f': case 'h': case 'j': case 'k': case 'l': case 'm': case 'n': case 'p': case 'q': case 's': case 't': case 'u': case 'v': case 'x': case 'y': case 'z': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': case '~': case '!': case '@': case '#': case '$': case '%': case '^': case '&': case '*': case '(': case ')': case '`': case '+': case '[': case ']': case '{': case '}': /*case '\\':*/ case '|': case '/': case ',': case '?': case '–': case '—': case '\'': case '"': case '“': case '”': case '‘': case '’': case '…': case 'π': case '∫': case '∂': case 'ƒ': case '©': case '˙': case '∆': case '˚': case 'µ': case '®': case 'ß': case '†': case '√': case '∑': case '≈': case '¥': case 'Ω': case '¡': case '™': case '£': case '¢': case '∞': case '§': case '¶': case '•': case 'ª': case 'º': case '≠': case '⁄': case '€': case '‹': case '›': case 'fl': case '‡': case '°': case '·': case '‚': case '±': case '¯': case '˘': case '¿': case 'ı': case '': case '´': case '˝': case 'ˆ': case '': case '˜': case '‰': case 'ˇ': case '¨': case '◊': case '„': case '˛': case '¸': // case 'α': case 'β': case 'γ': case 'δ': case 'ε': case 'ζ': case 'η': case 'θ': case 'ι': case 'κ': case 'λ': case 'μ': case 'ν': case 'ξ': case 'ο': case 'π': case 'ρ': case 'ς': case 'σ': case 'τ': case 'υ': case 'φ': case 'χ': case 'ψ': case 'ω': case 'ϑ': case 'ϒ': case 'ϖ': // case 'Α': case 'Β': case 'Γ': case 'Δ': case 'Ε': case 'Ζ': case 'Η': case 'Θ': case 'Ι': case 'Κ': case 'Λ': case 'Μ': case 'Ν': case 'Ξ': case 'Ο': case 'Π': case 'Ρ': case 'Σ': case 'Σ': case 'Τ': case 'Υ': case 'Φ': case 'Χ': case 'Ψ': case 'Ω': case 'Θ': case 'ϒ': if ( !e.metaKey && !e.ctrlKey && !e.altKey ) { alphaNav(e); } break; case 'd': // Cmd/Ctrl + D: Toggle Details if ( cmdKey(e) && !$body.hasClass('has_warning') ) { e.preventDefault(); $('#show_details').click(); } else { alphaNav(e); } break; case 'e': // Cmd/Ctrl + E: Toggle Main Menu or Text Editor switch(true) { case $body.hasClass('has_warning'): break; case cmdShiftKey(e): // toggle text editor e.preventDefault(); if ( window.self !== window.top ) { sendMessage('top','toggle_text_editor'); } else { $('#text_editor_row').find('a').click(); } $('body').addClass('faded'); break; case cmdKey(e): // toggle main menu e.preventDefault(); if ( window.self !== window.top ) { sendMessage('top','toggle_menu'); } else { $('#menu_container').click(); } break; default: alphaNav(e); } break; case 'g': // Cmd/Ctrl + G: Show image Grid if ( cmdKey(e) && ( $('#top').hasClass('has_images') || $('#top').hasClass('has_fonts') ) ) { e.preventDefault(); $('#grid_btn').click(); } else { alphaNav(e); } break; case 'i': // Cmd/Ctrl + I: Toggle Invisibles switch(true) { case cmdKey(e): e.preventDefault(); if ( window.self !== window.top ) { sendMessage('top','toggle_invisibles'); } else { $('#show_invisibles_container').find('input').click(); } break; default: alphaNav(e); } break; case 'o': // Cmd/Ctrl + Shift + O: Open selected item in new window if ( cmdShiftKey(e) ) { window.open( $('#tbody').find('.selected').find('a').attr('href') ); } else { alphaNav(e); } break; case 'r': // Cmd/Ctrl + Shift + R: Refresh switch(true) { case cmdKey(e) && window.top !== window.self: e.preventDefault(); sendMessage('top','reload'); break; // send reload message to top case cmdKey(e): e.preventDefault(); $('#reload_btn').click(); break; // click reload/reset button default: alphaNav(e); } break; case 'w': // Cmd/Crtl + W: if content pane has content, click close button; otherwise default behavior (close tab/window); Firefox does not allow scripts to override this behavior switch(true) { case cmdKey(e): switch(true) { case window.top !== window.self: e.preventDefault(); sendMessage('top','close'); // send close message to top break; case $content_pane.hasClass('has_audio'): case $body.hasClass('has_playlist'): case $body.hasClass('has_filelist'): case $body.hasClass('iframe_edited'): // close edited iframe file case $content_pane.attr('data-content') !== undefined: e.preventDefault(); $('#close_btn').click(); // click close button break; default: return true; // else close window (or normal behavior) } break; default: alphaNav(e); } break; case '=': // equals sign switch(true) { case cmdKey(e) && /has_grid|has_image|has_font|has_glyph|has_font_file/.test($content_pane.attr('data-content')): e.preventDefault(); $('#increase').click(); break; case cmdKey: return; // allow normal Cmd + behavior default: alphaNav(e); } break; case '-': // hyphen switch(true) { case cmdKey(e) && /has_grid|has_image|has_font|has_glyph|has_font_file/.test($content_pane.attr('data-content')): e.preventDefault(); $('#decrease').click(); break; case cmdKey: return; // allow normal Cmd - behavior default: alphaNav(e); } break; case '\\': // \ backslash switch(true) { case cmdShiftKey(e): // Cmd Shift + \ : toggle split switch(true) { case window.top === window.self && $content_pane.attr('data-content') === 'has_iframe': sendMessage('iframe','split_view'); break; // send toggle split message to top case $('#split_view').height() > 0: $('#split_view').click(); break; // if split view visible...click toggle split } break; case cmdKey(e): // Cmd + \ : toggle sidebar if ( window.top !== window.self ) { sendMessage('top','hide_sidebar'); } else { $('#hide_sidebar').click(); } break; } break; case 'Tab': e.preventDefault(); switch(true) { case $('body').hasClass('has_warning'): tabWarningButtons(e); break; // focus warning buttons case document.activeElement.id === 'content_image': focusSidebar(); break; // focus sidebar from image case $content_pane.attr('data-content') === 'has_text_editor': focusTextEditorPanes(); break; // focus text editor case window.top !== window.self && /\.html|\.htm/.test(window.location.pathname) && $('a,button,input,select,textarea').length > 0: focusFocusableEls(e); break; case (/has_markdown|has_text|has_code|has_htm/.test($content_pane.attr('data-content')) && window.top === window.self ): $('body').addClass('focus_content'); $content_iframe.focus(); sendMessage('iframe','focus_iframe'); break; // focus iframe text editor or htm files from top case ( /text_preview|html_preview|text_source/.test(document.activeElement.id) ): toggleTextEditorPanes(); break; // toggle focused text editor panes case $(document.activeElement).closest('#text_preview').length === 1: focusFocusableEls(e,'#text_preview'); break; // focus focusable elements in text preview case $(document.activeElement).closest('#font_specimen').length === 1: focusFocusableEls(e,'#font_specimen'); break; // focus focusable elements in font specimen case document.activeElement.hasAttribute('contenteditable'): case window.top !== window.self: // If iframe is focused, focus sidebar, dim selected iframe dir list item e.preventDefault(); switch(true) { case $('#iframe_dir_list_wrapper').length === 1: case !/a|button|input|select|textarea/.test(document.activeElement.tagName.toLowerCase() ): // allow form elements to be focused $('#iframe_body,#dir_list tr.selected').addClass('is_blurred'); // dim iframe dir_list sendMessage('top','tab'); // focus sidebar break; case $('a,button,input,select,textarea').length > 0: // allow tabbing to form elements and textareas (e.g., in previewed html pages) if ( $('body').hasClass('has_text_editor_UI') ) { focusFocusableEls(e,'#text_container'); } else { focusFocusableEls(e); } break; } break; case window.top === window.self: // Either focus sidebar or refocus content pane (after clicking menu, for example) switch(true) { case $('body').hasClass('focus_content') && $('body').hasClass('has_menu'): case !$('body').hasClass('focus_content'): focusContent('',e); break; case $('body').hasClass('focus_content'): case $('body').hasClass('has_menu'): focusSidebar(); break; } break; } break; case 'Escape': // remove text selections, close warnings or help, close menus (and refocus content), or focus sidebar. if ( $content_pane.attr('data-loaded') !== 'loaded' ) { $content_pane.removeAttr('data-loaded'); $content_iframe.removeAttr('src'); $('#font_styles').empty(); } // close loading iframe if ( window.top !== window.self ) { $('#iframe_body').addClass('is_blurred'); sendMessage('top','escape'); } else { focusSidebar(); } window.getSelection().removeAllRanges(); $('#content_playlist, #content_audio_playlist').removeClass('has_content'); $('#warning_btn_cancel,#close_help').click(); $(document).off('mousemove'); $('body').removeClass('has_overlay'); break; case '.': switch(true) { case cmdKey(e): if ( $('body').hasClass('has_warning') ) { e.preventDefault(); $('#warning_btn_cancel,#close_help').click(); // click cancel buttons } break; } } // end switch }); // ***** END KEYBOARD EVENTS ***** // // ***** GRID SETUP ***** // // Create Font Grid Items function fontGridItems() { let $font_grid_items_arr = [], $font_files = $dir_list_body.find('.font'), new_grid_item; $('#font_grid_styles').empty(); for ( let i = $font_files.length; i--; ) { new_grid_item = setContentFontSource( $font_files.eq(i).attr('id'), true, 'font_grid' ); $font_grid_items_arr.unshift( new_grid_item ); } $font_grid_items_arr[$font_grid_items_arr.length - 1].addClass('border_bottom_x'); // add bottom border to last return $font_grid_items_arr; } // Create Image Grid Items function imageGridItems() { let $image_grid_items_arr = [], $image_files = $dir_list_body.find('.image:not(.ignored)'), classes = 'image_grid_item border_right_x border_bottom_x', image_files_length = $image_files.length; for ( let i = image_files_length; i--; ) { const id = $image_files.eq(i).attr('id'); const this_link = $image_files.eq(i).find('a').attr('href'); const exts = $item_kind.image.filter( ext => $.inArray(ext, $row_settings.ignored) == -1 ); // decide which image files can be displayed const title_name = this_link.slice(this_link.lastIndexOf('/') + 1); if ( $.inArray( $image_files.eq(i).attr('data-ext'), exts ) > -1 ) { // if this row file ext is in the image extension array let item = `
    `; $image_grid_items_arr.unshift( item ); } } return $image_grid_items_arr; } // Make Grids function makeGrids(id) { closeGrid(); // remove previous grid items $content_pane.removeClass('has_hidden_grid has_image_grid has_font_grid'); // reset content_pane grid classes switch(true) { case id === 'show_font_grid' || !$body.hasClass('has_images'): $content_pane.addClass('has_font_grid'); $content_grid.append( fontGridItems() ); break; // only show font grid case id === 'show_image_grid' || !$body.hasClass('has_fonts'): $content_pane.addClass('has_image_grid'); $content_grid.append( imageGridItems() ); break; // only show image grid default: $content_grid.append( imageGridItems(), fontGridItems() ); // show grid of both images and fonts } } // Click grid button: make grid and show grid $('#sidebar_header').on('click', '#grid_btn, #show_font_grid, #show_image_grid', function(e) { e.stopPropagation(); showContent($(this).attr('id')); }); // ***** SCALE PREVIEWED IMAGES & FONTS ***** // // Scale Fonts function scaleFonts(e, incr, id) { const font_size = parseInt(getComputedStyle(document.body).fontSize); // pts/em const getFontSize = function(el) { return parseFloat(el.css('font-size')); }; if ( id === 'decrease' ) { incr = 1/incr; } if ( $content_pane.attr('data-content') === 'has_grid' ) { $content_grid.css({'font-size':( getFontSize($content_grid)/font_size * incr ) +'em'}); return; } if ( $content_pane.attr('data-content') === 'has_font' ) { $content_font.css({'font-size':( getFontSize($content_font)/font_size * incr ) +'em'}); return; } scrollThis('content_container','content_font'); } // Scale Glyphs function scaleGlyphs(e, incr, id) { if ( id === 'decrease' ) { incr = 1/incr; } let scale = ( $('#glyph_viewer').attr('data-scale') === undefined ? incr : incr * $('#glyph_viewer').attr('data-scale') ); if ( scale >= 1 ) { $('#glyph_viewer').css({'width': scale * 100 +'%','height': scale * 100 +'%'}); document.getElementById('content_font').scrollLeft = -( Math.round( $('#font_viewer').outerWidth(true) - $('#glyph_viewer').width() ) / 2 ); document.getElementById('content_font').scrollTop = -( Math.round( $('#font_viewer').outerHeight(true) - $('#glyph_viewer').height() ) / 2 ); } $('#glyph_viewer').css({'background-size': scale * 100 +'%'}).attr('data-scale',scale.toFixed(2)); } // Scale Image Grid items function scaleGridImages(incr,id) { if ( id === 'decrease' ) { incr = 1/incr; } const $image_grid_item = $('.image_grid_item img'); let image_grid_item_width = Number.parseFloat( $image_grid_item.width(),10) * incr; let image_grid_item_height = Number.parseFloat( $image_grid_item.height(),10) * incr; let image_grid_item_max_width = Number.parseFloat( $image_grid_item.css('maxWidth'),10) * incr; let image_grid_item_max_height = Number.parseFloat( $image_grid_item.css('maxHeight'),10) * incr; // prevent reducing grid image size on first scale click: if ( image_grid_item_width < image_grid_item_max_width ) { image_grid_item_width = image_grid_item_max_width; } if ( image_grid_item_height < image_grid_item_max_height ) { image_grid_item_height = image_grid_item_max_height; } // set grid properties $content_grid.css({'grid-template-columns':'repeat(auto-fill, minmax('+ (image_grid_item_width +16) +'px, auto ) )'}); $content_grid.find('img').css({'max-width':( image_grid_item_width ) +'px', 'max-height':( image_grid_item_height ) +'px'}); return; } // Zoom Images on click function scaleImages(e,incr,id) { const $content_container = ( $('#iframe_body > img').length === 1 ? $('#iframe_body') : $('#content_container') ); const $this_image = ( $('#iframe_body > img').length === 1 ? $('#iframe_body > img') : $content_image ); const this_link = $this_image.attr('src'); let CC_width = function() { return Math.round($content_container.width()); }; let CC_height = function() { return Math.round($content_container.height()); }; const this_width = Math.round($this_image.width()); const this_height = Math.round($this_image.height()); const iframe_delta = ( $('#iframe_body > img').length === 1 ? Number.parseInt($('#iframe_body').css('padding')) : 0 ); // scale grid images if ( $content_pane.attr('data-content') === 'has_grid' ) { scaleGridImages(incr,id); } else { // scale single images getImageDimensions( this_link, function( width, height ) { switch(true) { case incr !== undefined && id !== undefined: // scale images by increment $content_pane.addClass('has_scaled_image').removeClass('has_zoom_image'); // remove zoom classes in case window resized after zoom if ( id === 'increase' ) {// && this_width < width && this_height < height) { $this_image.css({'width':this_width * incr, 'height': this_height * incr}); $this_image.css({'width':this_width * incr, 'height': 'auto', 'max-width':'none', 'max-height':'none' }); } if ( id === 'decrease' && ( this_width >= 1 && this_height >= 1 ) ) { $this_image.css({'width':this_width / incr, 'height': 'auto', 'max-width':'none', 'max-height':'none' }); } // keep images centered when scaling if ( Math.round($this_image.width()) >= CC_width() ) { $content_image_container.scrollLeft( ( Math.round( ( $this_image.outerWidth(true) ) - CC_width() ) )/2 ) ; } if ( Math.round($this_image.height()) <= CC_height() ) { $content_image_container.scrollTop( ( CC_height() - Math.round( $this_image.height() ) )/2 ); } else { $content_image_container.scrollTop( ( Math.round($this_image.outerHeight(true)) - CC_height())/2 ) ; } break; default: { // else zoom single image on click $this_image.removeAttr('style'); if ( width <= CC_width() && height <= CC_height() ) { // but don't zoom small images: $content_pane.removeClass('has_zoom_image has_scaled_image'); return; } // otherwise, zoom image: const $CC_offset = $content_container.offset(); const $img_offset = $this_image.offset(); // x,y coordinates of zoom click as percentage of image width/height const percentX = (e.pageX - $img_offset.left)/this_width; const percentY = (e.pageY - $img_offset.top)/this_height; // calculate scroll by pixel coordinates of full-size image - click coordinates and content_container offsets const scrollX = (width * percentX) - e.pageX + $CC_offset.left - (iframe_delta * width / this_width); const scrollY = (height * percentY) - e.pageY + $CC_offset.top - (iframe_delta * height / this_height); $content_pane.removeClass('has_scaled_image').toggleClass('has_zoom_image' ); $content_image_container.scrollLeft( scrollX ); $content_image_container.scrollTop( scrollY ); } } }); setImageDimensions(); } } // Zoom single image on click $content_image_container.on('click','img', function(e) { scaleImages(e); focusContent('content_image_container'); }); // Scale Fonts and Images function scalePreviewItems(e,id) { // combine scaling into one function if ( $content_pane.attr('data-content') === 'has_glyph' ) { scaleGlyphs(e, 1.125, id); return; } scaleImages(e, 1.125, id ); scaleFonts(e, 1.125, id ); } // Scale Buttons $('#scale').on('click','span', function(e) { e.preventDefault(); e.stopPropagation(); scalePreviewItems(e, $(this).attr('id') ); $('#reload_btn').addClass('reset'); if ( $('body').hasClass('focus_content') ) { focusContent(); } }); // ***** END SCALE PREVIEW ITEMS ***** // // ***** AUDIO CONTENT ***** // // Update Playlist function updateTrackList() { let playlist = []; $dir_list_body.find('.media').not('.unchecked').not('.disabled').not('.audio_loaded').not('.content_loaded').not('.selected').each(function() { playlist.push( $(this).attr('id') ); }); return playlist; } // Randomize Shuffle List function shuffleArray(array) { for ( let i = array.length - 1; i > 0; i-- ) { const j = Math.floor(Math.random() * (i + 1)); [array[i], array[j]] = [array[j], array[i]]; } return array; } // Attach Shuffle List data to $audio_player function updateShuffleList(id) { let shuffle_list; switch(true) { case !$body.hasClass('shuffle_media'): break; case id !== undefined: // don't include .playing and .unchecked track in shufflelist shuffle_list = $audio_player.data('shufflelist'); switch(true) { case $(document.getElementById(id)).hasClass('unchecked') || ( /audio_loaded|content_loaded/.test(document.getElementById(id).classList.value) ): shuffle_list.splice(shuffle_list.indexOf(id), 1); $audio_player.data('shufflelist',shuffle_list); break; default: shuffle_list.push(id); shuffle_list = shuffleArray( shuffle_list ); } break; default: shuffle_list = shuffleArray( updateTrackList() ); $audio_player.data('shufflelist',shuffle_list); } } // Click media checkboxes $('#tbody').on('click','tr.media input', function(e) { if (getBrowser() === 'is_gecko') { e.preventDefault(); } // because Firefox sucks e.stopPropagation(); toggleChecked(this); }); // Toggle single media checkboxes and update shufflelist function toggleChecked(el) { el.blur(); $(el).closest('tr').toggleClass('unchecked'); updateShuffleList($(el).closest('tr').attr('id')); } // toggle all media checkboxes and update shufflelist function toggleAllChecked() { $dir_list_body.find('> tr').find('input').trigger('click'); updateShuffleList(); } $('#play_toggle').on('click',function(e) { e.preventDefault(); e.stopPropagation(); toggleAllChecked(); }); // Is Playing; returns true if all conditions are true function isPlaying(id) { switch(true) { case window.top !== window.self: return ( $('body').hasClass('is_playing') ? true : false ); default: return ( id !== undefined && document.getElementById(id).currentTime > 0 && !document.getElementById(id).paused && !document.getElementById(id).ended ); } } // Play Media function playMedia(task) { if ( $content_pane.hasClass('has_audio') ) { $audio_player.trigger(task); } else { $content_video.trigger(task); } setPlayerStatus(task); } function setPlayerStatus(task) { if ( task === 'play' ) { $('body').removeClass('is_paused').addClass('is_playing'); } else { $('body').removeClass('is_playing').addClass('is_paused'); } if ( $content_pane.hasClass('has_iframe_audio') ) { sendMessage('iframe','setIframePlayerStatus','',task); } } // Skip media tracks +/-10/30 seconds function mediaSkip(e,args) { let factor, skip; switch(true) { case e !== undefined: factor = ( e.key === 'ArrowLeft' ? -1 : 1 ); skip = ( e.altKey && e.shiftKey ? 30 : e.altKey ? 10 : null ); break; // from top case args !== undefined: factor = ( args[0] === 'ArrowLeft' ? -1 : 1 ); skip = args[1]; break; // from iframe } const $player = ( $('#content_pane').hasClass('has_audio') ? $audio_player : $content_video ); // audio or video? const time = $player.prop('currentTime'); // current time $player.prop('currentTime', time + factor*(skip)); // set time } // Play/Pause Audio/Video function playPauseMedia() { let playerID = ( $content_pane.attr('data-content') === 'has_video' ? 'content_video' : 'audio' ); let player = document.getElementById( playerID ); switch(true) { case isPlaying( playerID ): player.pause(); $('body').removeClass('is_playing').addClass('is_paused'); break; default: player.play(); $('body').removeClass('is_paused').addClass('is_playing'); } } // Toggle Audio Playback Options (shuffle, loop) function audioPlaybackOptions(id) { switch(true) { case id === 'shuffle': case id === 'shuffle_media_files': $body.toggleClass('shuffle_media'); updateShuffleList(); break; case id === 'loop': case id === 'loop_media_files': $body.toggleClass('loop_media'); break; } switch(true) { // change audio checkboxes prop case id === 'shuffle_media_files': ( $('#shuffle').prop('checked') === true ? $('#shuffle').prop({'checked':false}) : $('#shuffle').prop({'checked':true}) ); break; case id === 'loop_media_files': ( $('#loop').prop('checked') === true ? $('#loop').prop({'checked':false}) : $('#loop').prop({'checked':true}) ); break; } } // click loop or shuffle audio options $('#media_files_menu').on('click','#loop_media_files,#shuffle_media_files', function() { audioPlaybackOptions( $(this).attr('id') ); closeMenus(); }); $('#audio_options').on('click','input', function() { audioPlaybackOptions( $(this).attr('id') ); $(this).blur(); document.getElementById('top').focus(); }); // Initialize Media: play next track on ended and scroll to playing item function initMedia() { $('#audio,#content_video').on('ended', function() { switch(true) { // play track from iframe case $content_pane.hasClass('has_iframe_audio'): sendMessage('iframe','play_prev_next_iframe_audio','','ArrowRight'); break; case $content_pane.hasClass('has_audio'): playPrevNextMediaTrack('ArrowRight','true'); break; case $content_pane.attr('data-content') === 'has_video': playPrevNextMediaTrack('ArrowRight','true'); break; } scrollThis('tbody','.audio_loaded'); scrollThis('tbody','.video.content_loaded'); }); } // ***** END AUDIO PLAYBACK ***** // // ***** IFRAME SETUP ***** // function setUpIframeUI() { // set up iframe directory UI or iframe text editor let iframe_location = decodeURIComponentSafe(window.location.href), content = ''; $('body').attr('id','iframe_body'); // add iframe body id $('video').css({'width':'100%'}); const text_files = $item_kind.markdown.concat($item_kind.text, $item_kind.code); // define which files are editable const link_files = $item_kind.link; switch(true) { case window.location.pathname.indexOf('/?') > 0: // set up iframe directory case window.location.pathname.endsWith('/'): // set up iframe directory setUpIframeDirUI(iframe_location); break; case text_files.includes( window.location.pathname.slice( window.location.pathname.lastIndexOf('.') + 1 ) ): // else set up iframe text editor setUpTextEditorUI(); if ( iframe_location.endsWith('.cue') ) { content = $('#text_source').val(); } // get cue sheet text for processing if ( link_files.includes( iframe_location.slice(iframe_location.lastIndexOf('.') + 1 ) ) ) { content = $('#iframe_body').find('> pre').html(); } // get link file content for processing break; default: // $('body').prepend(text_editing_UI_els); break; // all other iframe content (e.g., html files) } let args = [iframe_location,content]; // args[0] = link, args[1] = iframe_content sendMessage('top','iframe_loaded','',args); } // IFRAME DIRECTORY Prep function setUpIframeDirUI(link) { // set up iframe dir_list UI let parent_link = link.split('/').slice(0,-2).join('/') +'/';//+ window.location.search; let query_str = new URLSearchParams(window.location.search.toString().slice(1)); let $iframe_body = $('#iframe_body'), $iframe_head = $('head'), $iframe_utility_iframe = ''; let body_classes = []; // make array of classes to add all at once if ( getBrowser() === 'is_gecko' ) { body_classes.push('is_gecko'); } // append gecko styles and fix links switch(true) { case query_str.get('view_directory_source') === 'true': case query_str.get('is_error') === 'true': break; // show raw directory index if error or viewing directory source default: // else set up iframe directory for ( let key of query_str.keys() ) { switch(true) { case query_str.get(key) === 'true': body_classes.push(key); break; // add body classes for boolean params case query_str.get(key) !== 'false': body_classes.push(key+'_'+getSearchParam(key)); break; // non-boolean params (theme, sort, sort_direction) } } $iframe_head.find('style').remove(); // remove any existing directory index styles $iframe_head.append(''); $iframe_head.append(''); // no break; // if opening a subdirectory, use dir_list_subdir to contain prepped index (i.e., we don't need the sidebar or other UI elements. let dir_list_subdir = '
    ${ total } Items: ${ total_dirs } Directories, ${ total_files } Files
    ${ total } Items (${ total_dirs_invisible + total_files_invisible } ignored or invisible)
    Dirs (${ total_dirs_invisible } ignored or invisible)
    Files (${ total_files_invisible } ignored or invisible)
    ${ stats_rows.join('\n') }
    `; return stats; } function updateStats() { let items = document.getElementById('tbody').getElementsByTagName('tr'); let stats_classes = [], stats_kinds = [], item_classlist; for ( let i= 0; i < items.length; i++ ) { // get classes and kind for each item item_classlist = items[i].className; item_classlist = item_classlist.replace(/file|media|content_loaded|has_subdirectory|selected/g,'').trim(); // remove unwanted classes stats_classes.push(item_classlist); stats_kinds.push(items[i].getAttribute('data-kind') ); } $('#stats_container').remove(); $('#tfoot').prepend( buildStats(stats_classes,stats_kinds) ); } // END INDEX PREP // ***** MAKE NEW INDEX ***** // function makeNewIndex(el,sort) { const index_items = getIndexItems(el), items = index_items[0], type = index_items[1]; const converted_index = convertIndexItems( type, items ); // = array of rows: ["link","date","size"] switch(type) { case 'error': return [converted_index]; default: { let new_index = buildNewIndex( $(el).attr('id'), converted_index, sort, type ); return [new_index]; } } } // ***** END DIR_LIST SETUP ***** // const $body = $('body'); // ***** UI SETUP ***** // function prepDocHead() { document.title = 'Index of: '+ current_location; // change the doc title to current location // $('head').prepend('');// .find('#title').removeAttr('id'); if ( window.location.protocol.startsWith('file') ) { // add custom favicon for local directories $('head').prepend(''); } addStyles(); // add css } // UI Setup: build UI and add body classes and other initial settings function getUIPrefBodyClasses() { let queries = new URLSearchParams(window.location.search).entries(); // make new search params from window.location.search queries = Object.fromEntries(queries); let body_classes = [], settings = Object.assign({},queries,$settings); // merge $settings and query settings for ( let key in settings ) { switch(true) { case ['bookmarks','grid_font_size','grid_image_size','UI_font','UI_font_size'].includes(key): break; // ignore these keys (values are set in css or by setUpTextEditorUI) case key === 'enable_text_editing': if ( getSearchParam(key) === 'true') { body_classes.push(key); } else { body_classes.push('disable_text_editing'); } break; case ['default_text_view'].includes(key): body_classes.push( getSearchParam(key) ); break; case key === 'editor_theme': switch(true) { case getSearchParam('editor_theme') === 'default': body_classes.push( 'editor_theme_default' ); break; // case getSearchParam('editor_theme') === 'default': body_classes.push( 'editor_theme_'+ getSearchParam('theme') ); case getSearchParam('editor_theme') === 'light': body_classes.push( 'editor_theme_light' ); break; case getSearchParam('editor_theme') === 'dark': body_classes.push( 'editor_theme_dark' ); break; } break; case ['theme','sort_by','sort_direction'].includes(key): // non-boolean prefs: add key_value to body classes body_classes.push( key +'_'+ getSearchParam(key) ); break; case getSearchParam(key) === 'true': // booleans: only add the key to body classes body_classes.push(key); break; } } body_classes.push(getBrowser()); body_classes.push('is_'+getOS()); // add browser and os classes return body_classes.join(' '); } // Build UI: Append all assembled elements to $body function buildUI() { switch(true) { case window.self === window.top: { // if it's not an iframe... const make_new_index = makeNewIndex('body')[0]; // make index const new_index = make_new_index[0]; const body_classes = make_new_index[1] +' '+ getUIPrefBodyClasses(); $main_content.find('#tbody').append(new_index); // append index to #main_content el $main_content.find('#tfoot').prepend(make_new_index[2]); // append stats $('body').empty(); prepDocHead(); // add styles, title, favicon, meta tags $('body').addClass(body_classes).attr('id','top').attr('lang','en').append($main_content); // add body classes, id, lang attr, and append the main content initMedia(); // initialize media autoLoadFile(); // autoload media and cover art, if any break; } case window.self !== window.top && !window.location.pathname.endsWith('.pdf'): setUpIframeUI(); break; } } buildUI(); // SET UI TO DEFAULT SETTINGS: remove queries; function defaultSettings() { let query_str = ''; if ( getSearchParam('selected') !== undefined ) { query_str += 'selected='+ getSearchParam('selected'); } if ( getSearchParam('history') !== undefined ) { query_str += 'history='+ getSearchParam('history'); } if ( query_str.length > 0 ) { query_str = '?' + query_str.replace(/\s/g,'+'); } window.location.assign(current_location + query_str); } $('#default_settings').on('click', function(e) { e.preventDefault(); e.stopPropagation(); $('body').removeClass('has_menu'); if (window.confirm( 'Are you sure you want to remove all your temporary UI settings from the URL query string?' ) ) { defaultSettings(); } }); // SAVE USER SETTINGS function saveSettings(file_name, data) { saveFile(data,'application/json',file_name); } $('#export_settings').on('click',function(e) { e.preventDefault(); e.stopPropagation(); $('body').removeClass('has_menu'); const settings_string = ( JSON.stringify($settings,null,'\t')); saveSettings('settings.json',settings_string); }); // Show/Close Help function showHelp() { $('body#top').addClass('has_help'); } $('#show_help').on('click','span', function(e) { e.stopPropagation(); showHelp(); }); $('#close_help').on('click', function(e) { e.preventDefault(); $('body').removeClass('has_help'); }); $('#help_container').on('click', function(e) { e.stopPropagation(); }); // Click Menu Bookmark (with warning) function setLocation(link) { window.location = link; } $('#parent_dir_nav, #parents_dir_nav + .menu, #menu .bookmark').on('click','a', function(e) { e.preventDefault(); switch(true) { case $(this).attr('href').indexOf('file://') > -1 && $protocol !== 'file:': $body.addClass('has_warning').find('#warnings_container').addClass('warning_local_bookmark'); $('#warning_btn_ok').focus(); break; case $body.hasClass('has_playlist'): case $body.hasClass('has_filelist'): closePlaylist(); break; default: showWarning( 'setLocation', $(this).attr('href') ); } }); // Show Menus function showMenus(el) { sendMessage('iframe','top_has_menu'); $('body').removeClass('hide_sidebar'); let position = $(el).position(), id = $(el).attr('id'); if ( $('body').hasClass('has_stats') ) { $('#stats').click(); } $(el).find('> ul').css({'top':position.top + $(el).innerHeight() + 1 + 'px'}); switch(id) { case 'parents_dir_menu': switch(true) { case $('body').hasClass('has_menu_parents'): $('body').removeClass('has_menu_parents'); break; default: $('body').addClass('has_menu_parents').removeClass('has_menu'); } break; case 'menu_container': switch(true) { case $('body').hasClass('has_menu'): $('body').removeClass('has_menu'); sendMessage('iframe','top_closed_menu'); break; default: $('body').addClass('has_menu').removeClass('has_menu_parents'); sendMessage('iframe','top_has_menu'); } } } $('#parents_dir_menu, #menu_container').on('click',function(e) { e.stopPropagation(); showMenus($(this)); }); // Click menu function clickMenu() { $('#menu').find('.selected:not(.hovered)').removeClass('selected').find('a,> span,label').click(); $('#menu').find('.hovered').removeClass('selected hovered'); $('body').removeClass('has_menu'); if ( $('body').hasClass('focus_content') ) { sendMessage('iframe','close_menu'); $('#content_iframe').focus(); } if ( $body.hasClass('focus_content') ) { focusContent(); } } // Close menus function closeMenus() { switch(true) { case !$('body').hasClass('has_menu') && !$('body').hasClass('has_menu_parents') && !$('body').hasClass('has_stats'): break; default: $('body').removeClass('has_menu has_menu_parents faded has_stats has_help'); $('#menu').find('.selected,.hovered').removeClass('selected hovered'); } } // Show Stats function showStats() { $('body').addClass('has_stats').removeClass('has_menu has_menu_parents'); $('#stats_details').css({'height':$('#stats_container').height() - $('#stats_summary_detailed_container').height() - 4 }); } $('#tfoot').on('click','#stats_summary', function(e) { e.stopPropagation(); showStats(); }); $(document).on('click', function(e) { e.stopPropagation(); closeMenus(); }); // Toggle UI Preferences: after clicking .toggle_UI_pref items (menu, buttons, etc.), update searchParams function toggleUIpref(pref_ID) { let message_target = ( window.self === window.top ? 'iframe' : 'top' ), sorted, sort_direction; let bodyClassNames = ( message_target === 'iframe' ? document.getElementById('top').className.split(' ') : null ); if ( pref_ID === 'theme' && $('body').hasClass('theme_dark') ) { pref_ID = 'theme_light'; } if ( pref_ID === 'theme' && $('body').hasClass('theme_light') ) { pref_ID = 'theme_dark'; } switch(true) { // Text editor prefs case pref_ID === 'editor_theme_default': pref_ID = 'editor_'+ bodyClassNames.find( el => el.startsWith('theme') ); // no break /* falls through */ case ( /editor_theme|editor_theme_light|editor_theme_dark/.test(pref_ID) ): case ( /split_view|source_text|toggle_source|toggle_preview|toggle_html|preview_text|preview_html/.test(pref_ID) ): toggleTextEditorPrefs(pref_ID); // Text Editor Preferences if ( window.top === window.self ) { sendMessage('iframe',pref_ID); } break; // Enable/Disable Text editing case pref_ID === 'enable_text_editing': // needs to be here and not in switch above -- why? $('body').toggleClass('enable_text_editing disable_text_editing'); $('.selected.text, .selected.markdown, .selected.code').find('a').click(); break; // Sorting case ( /sort_by_name|sort_by_default|sort_by_time|sort_by_size|sort_by_date|sort_by_kind|sort_by_ext/.test(pref_ID) ): // toggle sorting closeSubdirectory(); // subdirectory sorting not supported switch(true) { case $('body').hasClass(pref_ID): // pref_ID = current sort, reverse sort direction sort_direction = document.getElementsByTagName('body')[0].classList.value.match(/sort_direction_\w+/)[0]; sort_direction = ( sort_direction === 'sort_direction_up' ? 'sort_direction_down' : 'sort_direction_up' ); $('body').removeClass('sort_direction_up sort_direction_down').addClass(sort_direction); toggleSearchParam( sort_direction ); break; case !$('body').hasClass(pref_ID): sort_direction = 'sort_direction_'+ $settings.sort_direction; $('body').removeClass('has_menu sort_by_default sort_by_name sort_by_time sort_by_size sort_by_date sort_by_kind sort_by_ext').addClass(pref_ID); $('body').removeClass('sort_direction_up sort_direction_down').addClass(sort_direction); break; } sorted = sortDirList( $('#tbody').find('tr'), pref_ID, sort_direction ); // make initial sort $('#tbody').empty().append(sorted); scrollThis('tbody','selected',false); // true = instant scroll // if ( $('body').hasClass('sort_by_default') ) { $('#tbody').find('#tbody tr.dir.border_top,#tbody tr.dir.border_bottom').toggleClass('border_top border_bottom'); } switch(true) { // sort grids --> change this to actual sort, not reload case $content_pane.hasClass('has_font_grid'): $('#show_font_grid').click(); break; case $content_pane.hasClass('has_image_grid'): $('#show_image_grid').click(); break; case $content_pane.attr('data-content') === 'has_grid': $('#grid_btn').click(); break; } break; // various UI prefs case ( /alternate_background|hide_ignored_items|ignore_ignored_items|show_invisibles|show_numbers|hide_sidebar|play_all_media|show_details/.test(pref_ID) ): // toggle other boolean UI settings $('body').toggleClass(pref_ID); if ( pref_ID === 'show_details' ) { $('#show_details').blur(); $('body').focus(); } if ( window.top === window.self ) { sendMessage('iframe',pref_ID); } break; // toggle main UI theme case ( /theme_light|theme_dark/.test(pref_ID) ): $('body').removeClass('theme_dark theme_light').addClass(pref_ID); if ( $('body').hasClass('editor_theme_default') ) { $('body').removeClass('editor_theme_light editor_theme_dark').addClass('editor_'+ pref_ID); } else { toggleUIpref('editor_theme'); } if (window.top === window.self) { sendMessage(message_target,pref_ID); } break; } toggleSearchParam(pref_ID); // nobreak if ( window.top === window.self ) { if ( $('.selected').length) { scrollThis('tbody','selected',false); } // true = instant scroll } if ( $('body').hasClass('focus_content') ) { focusContent(); } } // set Text Editor Preferences (from menus or toolbar buttons) function toggleTextEditorPrefs(pref_ID) { let message_target = ( window.self === window.top ? 'iframe' : 'top' ); switch(true) { case ( /editor_theme|editor_theme_default|editor_theme_light|editor_theme_dark/.test(pref_ID) ): // toggle text editor theme switch(true) { case pref_ID === 'editor_theme': $('body').toggleClass('editor_theme_light editor_theme_dark'); pref_ID = ( $('body').hasClass('editor_theme_light') ? 'editor_theme_light' : 'editor_theme_dark' ); break; case pref_ID === 'editor_theme_default': pref_ID = ( $('body').hasClass('editor_theme_light') ? 'editor_theme_light' : 'editor_theme_dark' ); // arg sent to iframe to set editor theme $('body').removeClass('editor_theme_light editor_theme_dark').addClass('editor_theme_default');// editor_theme_'+ getSearchParam('theme') ); break; case pref_ID === 'editor_theme_light': $('body').removeClass('editor_theme_default editor_theme_dark').addClass('editor_theme_light'); break; case pref_ID === 'editor_theme_dark': $('body').removeClass('editor_theme_default editor_theme_light').addClass('editor_theme_dark'); break; } break; case pref_ID === 'source_text': switch(true) { case $('body').hasClass(getSearchParam('default_text_view')) : // if split view, remove split, show source $('body').removeClass(getSearchParam('default_text_view')).addClass('source_text'); break; default: // show split, show default text view $('body').addClass(getSearchParam('default_text_view') +' source_text '); } $('#toggle_split_view').click(); break; case ( /toggle_preview|toggle_html|preview_text|preview_html/.test(pref_ID) ): // toggle default text editor view if ( !$('body').hasClass(pref_ID) ) { $('body').removeClass('source_text preview_text preview_html').addClass(pref_ID); } break; case ( /split_view/.test(pref_ID) ): // toggle other boolean UI settings $('body').toggleClass(pref_ID); } switch(true) { //pref_ID += '_'+ getSearchParam('editor_theme'); // case pref_ID === 'editor_theme': sendMessage('top','toggle_text_editor_theme','',pref_ID); break; // case ( /editor_theme_default|editor_theme_light|editor_theme_dark/.test(pref_ID) ): sendMessage('iframe','text_editor_toolbar_button','',pref_ID); break; default: sendMessage(message_target,'text_editor_toolbar_button','',pref_ID); } } // Click Toggle UI Pref elements function toggleUIPrefOnClick(e,el) { e.stopPropagation(); if ( !$(el).is('input') ) { e.preventDefault(); } // allow checkboxes to be checked if ( !$(el).hasClass('disabled') ) { toggleUIpref( $(el).attr('data-ui_pref') ); } closeMenus(); } $('.toggle_UI_pref').on('click',function(e) { toggleUIPrefOnClick(e,this); }); $('#content_text').on('click','.toggle_UI_pref',function(e) { toggleUIPrefOnClick(e,this);}); // text editing UI is not in DOM on page load; why doesn't $('body').on('click','.toggle_UI_pref'... work? // RESIZE Sidebar/Content Pane function resizeSidebar(f) { f.stopPropagation(); let $sidebar_wrapper = $('#sidebar_wrapper'), startX = f.pageX, window_width = window.innerWidth, sidebar_width = $sidebar_wrapper.width(); $('body').addClass('has_overlay'); // prevent interference from the rest of ui $(document).on('mousemove',function(e) { e.stopPropagation(); e.preventDefault(); let deltaX = e.pageX - startX; if ( e.pageX > 230 && e.pageX < window_width - 200 ) { $sidebar_wrapper.css({'width':sidebar_width + deltaX + 'px'}); $content_pane.css({'width':(window_width - sidebar_width) - deltaX + 'px'}); } // scrollThis('tbody','selected',false); // true = instant scroll }); $(document).on('mouseup',function() { $('body').removeClass('has_overlay'); // remove the overlay $(document).off('mousemove'); // remove eventlistener setSearchParam('width',$sidebar_wrapper.width()); // set the sidebar width query if ( $body.hasClass('focus_content') ) { focusContent(); } // refocus content if necessary }); } $('#handle').on('mousedown', function(f) { f.stopPropagation(); resizeSidebar(f); }); // ***** BASIC UI FUNCTIONS ***** // // getElementById alias function getElById(id) { return $(document.getElementById(id) ); } // Scroll Selected Items function scrollThis(container_ID, scroll_el_class, bool) { let $container = document.getElementById(container_ID); if ( $container.height === 0 ) { return; } // don't scroll hidden elements let $scroll_el = ( scroll_el_class !== undefined ? $container.getElementsByClassName(scroll_el_class) : null ); let scroll_behavior = ( ( bool !== undefined || bool === true || $content_pane.attr('data-content') === 'has_grid' ) ? 'instant' : 'smooth' ); // instant allows sidebar & grid to scroll simultaneously let scroll_block = ( $('body').hasClass('is_gecko') ? 'start' : 'nearest' ); if ( $scroll_el !== undefined && $scroll_el.length === 1 ) { $scroll_el[0].scrollIntoView({ behavior:scroll_behavior, block:scroll_block, inline:'nearest' }); } } // ***** SORTING ***** // function sortIndex(els,sort_type,sort_direction) { // sort_id = sort type let sort_id = sort_type.split('_').reverse()[0]; const new_sort = new Intl.Collator(undefined, { numeric: true, sensitivity: 'base' }); let sorted = [], aName, bName, aData, bData;//, aLevel, bLevel, aKind, bKind; sorted = els.removeClass('sorted border_top').sort((a, b) => { // aLevel = $(a).attr('data-level'); bLevel = $(b).attr('data-level'); // subdirectory level aName = $(a).find('td.name').attr('data-name'); bName = $(b).find('td.name').attr('data-name'); // aName, bName = item name aData = $(a).find('td[data-'+ sort_id +']').attr('data-'+ sort_id); bData = $(b).find('td[data-'+ sort_id +']').attr('data-'+ sort_id); // aData, bData = size, date, kind, ext, time switch(true) { case sort_direction === 'sort_direction_down': switch(true) { case new_sort.compare(aData, bData) === 0: return new_sort.compare(aName, bName); // if same data kind, sort by name default: return new_sort.compare(aData, bData); // otherwise sort by data kind } case sort_direction === 'sort_direction_up': // reverse sort switch(true) { case new_sort.compare(bData, aData) === 0: return new_sort.compare(bName, aName); default: return new_sort.compare(bData, aData); } } }); switch(true) { // add sorted border style case ( /default/.test(sort_id) ): // dirs and files break; case ( /kind|ext/.test(sort_id) ): // different item kinds or extensions sorted = sorted.sort((a, b) => { if ( $(a).find('td[data-'+ sort_id +']').attr('data-'+ sort_id) !== $(b).find('td[data-'+ sort_id +']').attr('data-'+ sort_id) ) { $(a).addClass('sorted border_top'); } }); break; case ( /time/.test(sort_id) ): // if sorting by time, add rule between dirs and files sorted = sorted.sort((a, b) => { if ( $(a).attr('data-kind') !== $(b).attr('data-kind') ) { $(a).addClass('sorted border_top'); } }); break; } return sorted; } // Sort the Dir List on click function sortDirList(rows, sort_type, sort_direction) { let $sorted = [], $sort_all = rows, $sort_dirs = $sort_all.filter('.dir:not(.app)'), $sort_files = $sort_all.filter('.file,.app');// === 'default' ? 'name' : id; switch(true) { case sort_type === 'sort_by_default' || ( sort_type !== 'sort_by_name' && $settings.dirs_on_top === true ): const $sorted_dirs = sortIndex($sort_dirs, sort_type, sort_direction); const $sorted_files = sortIndex( $sort_files, sort_type, sort_direction ); if (sort_direction === 'sort_direction_down') { $sorted = $.merge($sorted_dirs,$sorted_files); } else { $sorted = $.merge($sorted_files,$sorted_dirs); } break; default: $sorted = sortIndex( $sort_all, sort_type, sort_direction ); } return $sorted; } // ***** END SORTING ***** // // ***** END BASIC UI FUNCTIONS ***** // // ***** CONTENT PANE ***** // // Focus Sidebar function focusSidebar() { document.activeElement.blur(); closeMenus(); $('body').removeClass('faded focus_content').find('#sidebar').focus(); sendMessage('iframe','blur_iframe'); } // Focus content function focusContent(id,e) { $('body').addClass('focus_content'); closeMenus(); switch(true) { case id === 'content_iframe' && cmdKey(e) && e.key === 'ArrowDown' && window.self === window.top && $('body').hasClass('focus_content'): $content_iframe.focus(); sendMessage('iframe','iframe_arrow_navigation','','ArrowDown'); $('#iframe_body a').click(); break; case $content_pane.attr('data-content') === 'has_font': $('#specimen').focus(); break; case document.activeElement.id.toLowerCase() === 'text_preview' && getFocusableEls('#text_preview').length > 0: getFocusableEls('#text_preview').first().focus(); break; case $content_pane.attr('data-content') === 'has_text_editor': focusTextEditorPanes(); break; // focus text editor case ( /has_text|has_markdown|has_code|has_htm|has_dir|has_app/.test($content_pane.attr('data-content') ) ): // no break; focus iframe text editor case $content_iframe.hasClass('has_content'): // focus iframe and/or iframe text editor $('#content_iframe').focus(); if ( e !== undefined && e.shiftKey ) { sendMessage('iframe','shift_focus_iframe'); } else { sendMessage('iframe','focus_iframe'); } break; case ( /has_video|has_pdf/.test($content_pane.attr('data-content') ) ): // no break; video, pdf case $content_pane.attr('data-content') === undefined: // no break; data-content undefined case $('body').hasClass('has_playlist'): $('body').removeClass('focus_content'); break; // don't focus content in this and the above two cases case $content_pane.attr('data-content') === 'has_grid': $('#content_grid').focus(); break; // grids case $content_pane.attr('data-content') === 'has_image': $('#content_image').focus(); break; // images case id !== undefined: document.getElementById(id).focus(); break; } } // get focusable elements function getFocusableEls(id) { let focusableEls = ( id !== undefined ? $(id).find('a,button,input,select,textarea,div[contenteditable]').filter(':visible') : $('a,button,input,select,textarea,div[contenteditable]').filter(':visible') ); return focusableEls; } // focus focusable elements in iframe files or text editor preview function focusFocusableEls(e,id) { e.preventDefault(); let els = Array.from(getFocusableEls(id)); switch(true) { case e.shiftKey: // tab + shift: focus prev switch(true) { case els.indexOf(document.activeElement) === 0: sendMessage('top','focus_sidebar'); break; case !els.includes(document.activeElement): els[els.length - 1].focus(); break; // if nothing focused...focus last el default: els[els.indexOf(document.activeElement) - 1].focus(); // else focus previous } break; case document.activeElement === els[els.length - 1]: sendMessage('top','focus_sidebar'); break; // if last focussable el, focus sidebar default: els[els.indexOf(document.activeElement) + 1].focus(); // tab: select next focusable element } } // focus button (for warnings) function focusButton(id) { let el = document.getElementById(id); el.classList.add('focus'); el.focus(); } //***** SHOW INDIVIDUAL CONTENT TYPES *****// // Show Grid function showGrid(id) { if ( id !== undefined ) { makeGrids(id); } // initial make grid items; otherwise, just unhide existing grid (see below) closeContent(); const selected_ID = $('#tbody').find('.selected.image, .selected.font').attr('id'); $('#grid_btn').addClass('has_grid'); $('#content_pane').removeClass('has_hidden_grid').attr('data-content','has_grid').find('div[data-id="'+ selected_ID +'"]').addClass('selected').siblings().removeClass('selected hovered'); focusContent(); } // Hide Grid function hideGrid() { if ( $content_pane.attr('data-content') === 'has_grid' ) { $content_pane.removeAttr('data-content').addClass('has_hidden_grid'); } } // Show Hidden Grid function showHiddenGrid() { if ( $content_pane.hasClass('has_hidden_grid') ) { showGrid(); } } // Close Grid (remove grid elements, etc.) function closeGrid() { $('#grid_btn').removeClass('has_grid'); if ( $content_pane.attr('data-content') === 'has_grid' ) { $content_pane.removeAttr('data-content'); } $content_pane.removeClass('has_image_grid has_font_grid').find('#content_grid').attr('style','').empty(); } // Show Text Editor function showTextEditor() { switch(true) { case $content_pane.attr('data-content') === 'has_text_editor': // hide open text editor hideTextEditor(true); break; case !$body.hasClass('has_text_editor_UI'): // add the text editor UI if loading text editor for first time setUpTextEditorUI(); default: // show editor // closeContent(); $('#content_video').trigger('pause'); $content_pane.removeClass('has_hidden_text_editor').attr('data-content','has_text_editor'); // empty title $('#text_editor_row').addClass('has_text_editor'); setContentTitle(); focusContent(); } } // Hide Text Editor function hideTextEditor(bool) { if ( $content_pane.attr('data-content') === 'has_text_editor' ) { $content_pane.removeAttr('data-content'); if ( bool === true ) { $content_pane.addClass('has_hidden_text_editor'); } if ( $('tr.selected').length > 0 ) { clickRow($('tr.selected').attr('id')); } focusSidebar(); } } // Show Hidden Text Editor function showHiddenTextEditor() { if ( $content_pane.hasClass('has_hidden_text_editor') ) { showTextEditor(); } } // showIndexSource(); // Show Audio function showAudio(id,iframe_audio_link,bool) { closeMedia('video'); document.activeElement.blur(); $('body').focus(); let row, link, title; switch(true) { case id === 'content_iframe_file': // clicked iframe audio files link = decodeURIComponentSafe(iframe_audio_link); title = link.slice(link.lastIndexOf('/') + 1); $content_pane.addClass('has_audio has_iframe_audio'); $('#content_iframe_utility').attr('src',link.slice(0,link.lastIndexOf('.') )+ '.cue' ); // load cue sheet in utility iframe break; default: // dir_list audio files row = getElById(id); switch(true) { case row.hasClass('local'): break; case bool === 'true': // bool !== undefined: if from autoLoadFile, just select file (don't add .audio_loaded class) row.addClass('selected'); if ( $('.dir.content_loaded').length === 1 ) { $('.dir.content_loaded').addClass('selected').siblings().removeClass('selected'); } // select dir.selected instead of media break; default: row.addClass('audio_loaded selected').siblings().removeClass('audio_loaded selected'); // otherwise select loaded media } link = row.find('a').attr('href'); title = row.find('.name').find('.tbody_row_cell_name_a_span').text(); $content_pane.addClass('has_audio').removeClass('has_iframe_audio'); getCueSheet(id,link,'audio'); } $audio_player.attr('src', link ); $('#content_audio_title').find('span').empty().text( title ); $('#content_audio_playlist').removeClass('has_content'); //setPlayerStatus('play'); } // Show Video function showVideo(id,link) { closeMedia('audio'); getCueSheet(id,link); document.activeElement.blur(); $content_video.focus(); } // rest of processing is done in showContent(); // GET CUE SHEET function getCueSheet(id,link,kind) { // id = 'content_iframe_file' or selected dir_list media id, link = selected href, kind = audio or video $('.cue_sheet_track_list_container').removeClass('has_cue_sheet').empty(); let media_file_name = getElById(id).find('.name').attr('data-name'); media_file_name = (media_file_name !== undefined ? media_file_name.slice(0,media_file_name.lastIndexOf('.')) + '.cue' : null); // undefined === from iframe, can't search for cuesheet let $cue_files; if ( $('#tbody tr[data-ext="cue"]').length === 0 ) { return; } else { $cue_files = $('#tbody tr[data-ext="cue"]'); }// abort if no cue files found let cue_file = $cue_files.filter( function(i) { return $cue_files[i].cells[0].dataset.name === media_file_name; }); // find matching cue sheet if ( cue_file.length === 1 ) { $(cue_file[0]).addClass('content_loaded'); $('#content_iframe_utility').attr('src',$(cue_file[0]).find('a').attr('href').trim() ); // load cue sheet in utility iframe } switch(kind) { // get durations case 'audio': $('#cue_sheet_track_list_container_audio').attr('data-duration',document.getElementById('audio').duration); break; case 'video': $('#cue_sheet_track_list_container_video').attr('data-duration',document.getElementById('content_video').duration); break; } } // TESTING: Get Media Duration (add to cue list, add to tr.audio var getFormattedTime = (secs) => { 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,''); // remove initial 0 return formattedTime; }; function getMediaDuration(link, callback) { if ( link !== undefined ) { let audio_file = new Audio(); audio_file.src = link; audio_file.onloadedmetadata = function() { callback( getFormattedTime( audio_file.duration )); }; audio_file.onerror = function() { callback( new Error("File not found")); }; } } // Process Cue Sheet function processCueSheet(cue_sheet_text) { let cue_sheet_tracks, track, command, prepped_track_list = [], track_id, display_time, index, index_sec; cue_sheet_text = cue_sheet_text.replace(/\t/g,' '); if ( !cue_sheet_text.startsWith('TRACK') ) { // let cue_sheet_info = cue_sheet_text.slice(0,cue_sheet_text.indexOf('TRACK ')); // if there is a file info header, remove it; cue_sheet_tracks = cue_sheet_text.slice(cue_sheet_text.indexOf('TRACK ')).split('TRACK ').reverse(); } else { cue_sheet_tracks = cue_sheet_text.split('TRACK ').reverse(); } for ( track of cue_sheet_tracks ) { // for each track in the cue sheet... let prepped_track = []; track = track.trim().split(/[\n\r]/); track_id = track.shift().split(' ')[0]; for ( command of track ) { // for each command (e.g.: PERFORMER "Artist"), trim and split it into command and value command = command.replace(/\"$/m,'').trim(); switch(true) { case ( /^PERFORMER\s+/.test(command) ): prepped_track[1] = ''+ command.split(/\s+\"/)[1] +''; break; // artist name case ( /^TITLE\s+/.test(command) ): prepped_track[2] = ''+ command.split(/\s+\"/)[1] +''; break; // track title case ( /^INDEX\s+01\s+/.test(command) ): // index display_time = command.split(/^INDEX\s+01\s+/)[1]; index = display_time.split(':').reverse(); // cue time format is mm:ss:ff (ff = frames @ 75 frames/sec) index_sec = index[0]/75 + index[1]*1 + index[2]*60; // convert index to seconds prepped_track[3] = ''+ display_time +''; break; } } prepped_track[0] = '
  • ' + ''+ track_id +''; if ( prepped_track[1] === undefined || ( /cue_performer"><\//.test(prepped_track[1]) ) ) { prepped_track[1] = '[missing]'; } if ( prepped_track[2] === undefined || ( /cue_title"><\//.test(prepped_track[2]) ) ) { prepped_track[2] = '[missing]'; } if ( prepped_track.length > 3 ) { prepped_track_list.push(prepped_track.join('')); } // prepped_track.length > 3 to prevent adding empty track } prepped_track_list = `
    • ${ getLinkInfo( $('#content_iframe_utility').attr("src") )[1] }
    • TrackArtist Title Time
    • `+ prepped_track_list.reverse().join('') +'
    '; switch(true) { case $('tr.audio_loaded').length > 0: case $content_pane.hasClass('has_iframe_audio'): $('#cue_sheet_track_list_container_audio').addClass('has_cue_sheet').append(prepped_track_list); // add cue sheet track list to menu break; case $('tr.video.content_loaded').length > 0: case $content_pane.hasClass('has_iframe_file'): $('#cue_sheet_track_list_container_video').addClass('has_cue_sheet').prepend(prepped_track_list); // add cue sheet track list to menu break; } } // Set track time on click $('.cue_sheet_track_list_container').on('click','li:not(.header)',function() { let $parent_nav_id = $(this).closest('nav').attr('id'); switch(true) { case $(this).hasClass('selected'): playPauseMedia(); break; default: $(this).addClass('selected').siblings().removeClass('selected'); let time = $(this).attr('data-time'); // cue time format is mm:ss:ff (ff = frames, 75 frames/sec) switch($parent_nav_id) { case 'cue_sheet_track_list_container_audio': if ( time < document.getElementById('audio').duration ) { $('#audio').prop('currentTime',time); } break; case 'cue_sheet_track_list_container_video': if ( time < document.getElementById('content_video').duration ) { $('#content_video').prop('currentTime',time); } break; } } }); $('.cue_sheet_track_list_container').on('mouseenter',function() { // add selected class to first track whose time is less than currentTime if ( $(this).attr('id') === 'cue_sheet_track_list_container_video' ) { $(this).find('.cue_sheet_track_list').css({'top':$(this).height() }); } let currentTime = document.getElementById('audio').currentTime; $(this).find('.cue_sheet_track_list').attr('data-time',currentTime).css({'height':$('#content_container').outerHeight() + $('#content_title').outerHeight() }); let currentTrack = $(this).find('.cue_sheet_track_list').find('li').filter(function() { return parseInt( $(this).attr('data-time')) <= Math.round(currentTime); }).last(); currentTrack.addClass('selected').siblings().removeClass('selected'); }); // SHOW FONT (and create font items) row function setContentFontSource(id,bool,font_grid,link) { // bool = true if for show font grid, id from fontGridItems(); link = from previewed directory let font_styles = $('#font_styles'); const font_family = ( link !== undefined ? link.slice(link.lastIndexOf('/') + 1,link.lastIndexOf('.')) : getElById(id).find('.name').attr('data-name') ); const font_url = ( link !== undefined ? link : getElById(id).find('a').attr('href') ); switch(true) { case bool === false: // just show selected font if ( font_styles.html().length > 0 ) { font_styles.empty(); } // delete previous @font-face rule font_styles.append( `@font-face { font-family: "${ font_family }"; src: url("${ font_url }"); }` ); $content_font.css({ 'font-family':'"'+ font_family +'"' }); // set content font style case font_grid === 'font_grid': // else make font grid items const $font_grid_item_el = $( `
    ` ); let $grid_item = $font_grid_item_el.clone(); const display_name = font_family; $('#font_grid_styles').append( `@font-face { font-family: "${ font_family }"; src: url("${ font_url }"); }`); $grid_item.append( `

    ${ display_name.toUpperCase() }

    ${ display_name.slice(0,font_family.lastIndexOf(".")) }

    ` ); return $grid_item; } } // Open Font File (req: opentype.js font parsing) $('#open_font_label').on('click',function(e) { e.stopPropagation(); }); // Open font $('#menu').on('change','#open_font',function(e) { $('body').removeClass('has_menu faded'); openFile(e,'font'); }); // Open Font File function openFontFile(files,reader) { closeContent(); hideGrid(); $content_pane.attr('data-content','has_font_file'); $content_font.addClass('has_content'); parseFont(reader.result); $('#title span').html( files.name ); $('#open_font').val(''); // reset input to allow same font to be reopened immediately after closing. focusContent(); } // Parse font (req opentype.js) function parseFont(fontblob) { let font = window.opentype.parse(fontblob); getFontInfo(font); let $glyphs_container = $('#glyphs_container'); $glyphs_container.empty(); let $glyph_container_el = $('
    '); let $glyph_canvas_el = $(''); let $glyph_info_el = $('
    '); let $glyph_viewer = $('#glyph_viewer'); $glyph_viewer.data('data-font-name',font.names.fullName.en); let glyphs = font.glyphs; $content_font.data('data-glyphs',glyphs); // add glyphs data to $content_font // Draw glyphs let glyph, glyph_width, context_X, bounding_box, glyph_unicode, $glyph_container, this_glyph, context, $glyph_info; for ( let i = 0; i < glyphs.length; i++ ) { glyph = glyphs.glyphs[i]; // Glyph width bounding_box = glyph.getBoundingBox(); glyph_width = bounding_box.x2 - bounding_box.x1; context_X = (60 - glyph_width/24); // Add glyph info and append elements glyph_unicode = ( glyph.unicode !== undefined ? '#'+ glyph.unicode : glyph.unicode ); $glyph_container = $glyph_container_el.clone(); $glyph_info = $glyph_info_el.clone(); $glyph_info.text(glyph.index +': '+ glyph.name +', '+ glyph_unicode); $glyph_container.attr('id','glyph_container_'+ glyph.index ).attr('data-id','glyph_container_'+ glyph.index); $glyph_container.append( $glyph_canvas_el.clone().attr('id','glyph_'+ glyph.index ) ).append($glyph_info); $glyphs_container.append( $glyph_container ); // Draw glyph this_glyph = document.getElementById('glyph_'+ glyph.index); $(this_glyph).data('contextX',context_X); context = this_glyph.getContext('2d'); glyph.draw(context, context_X, 84, 72); } } // Get font info function getFontInfo(font) { let font_names = font.names; let $font_info = $('
    FONT INFO: '+ font.names.fullName.en.toUpperCase() +'
    '); for ( let name in font_names ) { let value = font_names[name].en; if ( name.endsWith('URL') ) { let href = value; if ( !value.startsWith('http') ) { href = 'http://'+ value; } // in case url without protocol is used value = ''+ value +''; } $font_info.find('tbody') .append('
  • '+ name +': '+ value +'
    numGlyphs: '+ num_glyphs +'
    '; // dir_list_subdir let iframe_table = ( query_str.get('subdirectory') === 'true' ? dir_list_subdir : ContentIframeDirEls(parent_link) ); // get iframe directory UI for regular iframe or subdirectory const make_new_index = makeNewIndex($iframe_body, query_str.get('sort_by'))[0]; // make new index const prepped_index = make_new_index[0]; // define prepped index const additional_classes = make_new_index[1]; // additional body classes $iframe_body.removeAttr('style').addClass(additional_classes).addClass(body_classes.join(' ')).empty().append(iframe_table).find('#tbody').append(prepped_index); // add UI & prepped_index if ( query_str.get('subdirectory') === null ) { $iframe_body.append($iframe_utility_iframe); } // append utility iframe only in top iframe; i.e., don't nest utility iframes if ( query_str.get('subdirectory') === 'true' ) { // subdirectories sendMessage('top','dir_list_subdir_loaded','',$('#tbody').html() ); // if content_iframe_utility, send #top prepped directory for subdirectories } } } // IFRAME Click $('#iframe_body, #iframe_body #toolbar, #iframe_body #toolbar li, #iframe_body #tbody, #iframe_body .has_icon_before_before').on('click', function() { $('body').removeClass('is_blurred'); sendMessage('top','iframe_click'); // tell top to close menus, focus content }); // IFRAME Select iframe row on click or play/pause iframe audio function iframeSelectRow(row) { $('body').removeClass('is_blurred'); row.addClass('selected').removeClass('is_blurred').siblings('tr').removeClass('is_blurred selected'); // set selected classes if ( row.hasClass('audio_loaded') ) { sendMessage('top','iframe_play_pause_media'); } // play/pause media } $('#iframe_body').on('click','#tbody tr', function(e) { e.preventDefault(); e.stopImmediatePropagation(); iframeSelectRow($(this)); }); // IFRAME Click links from html files (should really combine with iframeDoubleClickRow()) function iframeClickLink(e,link,id) { let url, kind; if ( !link.startsWith('#') ) { e.preventDefault(); url = newURL(link); } // if link is not a link fragment switch(true) { case url === undefined: case link.startsWith('#'): break; // allow default link fragment behavior case id === 'tbody': e.preventDefault(); window.location = link + '?&view_directory_source=true'; break; // case id === 'parent': sendMessage('top','show_iframe_dir','',[link,'dir']); break; // open parent directory in iframe; add data-iframe_link to $content_iframe case url.protocol === 'file:' && window.location.protocol !== 'file:': sendMessage('top','local_link'); break; // show warning when attempting to open local links from non-local pages case url.protocol !== 'file:' && window.location.protocol === 'file:': window.open(link,'_blank'); break; // open remote link from local page in new tab/window case url.protocol === 'file:' && window.location.protocol === 'file:': // no break; open local links on local files in iframe case url.protocol === 'about:': // no break; document #link fragments case RegExp(url.hostname).test(window.location.hostname): // no break; same origin links (might not include TLD) (just covering bases here) case RegExp(window.location.hostname).test(url.hostname): // no break; same origin links (might not include TLD) (just covering bases here) kind = getLinkInfo(url.href)[3]; if ( /dir|app/.test(kind) ) { sendMessage('top','show_iframe_dir','',[link,kind] ); } else { sendMessage('top','show_iframe_file','',[link,kind] ); } break; default: window.open(link,'_blank'); break; // else open external document links in new tab } } // click link in html files (i.e., not iframe dir index) $('#iframe_body').on('click','a', function(e) { if ( $(this).closest('table').attr('id') !== 'dir_list' ) { iframeClickLink(e,$(this).attr('href'),$(this).closest('span,tbody').attr('id')); } }); // IFRAME Doubleclick iframe dir_list items (files and dirs) function iframeDoubleClickRow(row) { let kind = row.closest('tr').attr('data-kind'); // get item kind if ( kind === 'audio' ) { row.closest('tr').addClass('audio_loaded selected').siblings('.audio').removeClass('audio_loaded selected'); } let link = row.attr('href'); // get item link switch(true) { case ( /dir|app/.test(kind) ): sendMessage('top','show_iframe_dir','',[link,kind]); break; // dirs: tell top what to open; ignore ignored files when dblclicked default: sendMessage('top','show_iframe_file','',[link,kind]); // files: tell top what to open; ignore ignored files when dblclicked } } $('#iframe_body').on('dblclick','#tbody tr:not(.ignored) a', function(e) { e.preventDefault(); e.stopPropagation(); iframeDoubleClickRow($(this)); }); // Open IFRAME directory in sidebar function openDirInSidebar() { let link = window.location.href; sendMessage('top','open_iframe_dir_in_sidebar','',link); } $('#open_in_sidebar').on('click',function(e) { e.preventDefault(); openDirInSidebar(); }); // parent directory link for source directory view $('#iframe_body').on('click','#parentDirText,#UI_goUp a.up', function(e) { e.preventDefault(); e.stopPropagation(); if ( window.location.search.indexOf('view_directory_source') > -1 ) { window.location = $(this).closest('a').attr('href') + '?&view_directory_source=true'; } }); //***** TEXT EDITING PANE *****// function setUpTextEditorUI() { let source_text, body_classes = [], content; if ( !$('body').hasClass('has_text_editor_UI') ) { // add classes, styles, and scripts; only add once $('head').append(''); $('head').append(''); $('#title span').empty(); body_classes.push('has_text_editor_UI', getSearchParam('default_text_view')); } switch(true) { // get source text and append UI elements case window.self === window.top: // top level text editor if ( !$('body').hasClass('has_text_editor_UI') ) { $('#content_text').append( text_editing_UI_els ); } // only add the text editor UI if it hasn't been added already break; case window.self !== window.top: // iframe text editing UI $('head').prepend('') .append(''); // add iframe text editing styles source_text = decodeURIComponentSafe($('body').find('> pre').text()); // get source text and decode Unicode chars. $('body').empty().append('
    '+ text_editing_UI_els +'
    '+ warnings +'
    '); // add the UI $('#text_source').val(source_text); // set the source text value document.getElementById('text_source').setSelectionRange(0,0); // set the insertion point to the beginning of the text // no break case $('#text_source').val().trim().startsWith('#EXTM3U'): // playlists and filelists content = $('#text_source').val().trim(); // get m3u.txt file for processing sendMessage('top','iframe_playlist','',content); break; } switch(true) { // assemble text editing body classes case getSearchParam('enable_text_editing') === 'false' && window.top !== window.self: // text editing disabled $('body').removeClass('split_view preview_text preview_html'); body_classes.push('disable_text_editing source_text'); // show the source text $('#text_source').prop('disabled','disabled'); // diable textarea editing break; default: if ( getSearchParam('split_view') === 'true' ) { body_classes.push('split_view'); } if ( getSearchParam('sync_scroll') === 'true' ) { body_classes.push('sync_scroll'); $('#sync_scroll input').prop({'checked':true}); } if ( getSearchParam('editor_theme') === 'default' ) { body_classes.push('editor_theme_'+ getSearchParam('theme')); } else { body_classes.push( 'editor_theme_'+ getSearchParam('editor_theme') ); } } $('body').addClass( body_classes.join(' ') ); // add text editor body classes focusTextEditorPanes(); TextEditing(); // call text editing functions } // setup and show top level text editor $('#text_editor, #text_editor_row').on('click', function(e) { e.preventDefault(); showContent( $(this).attr('id') ); }); // Main Text Editing Function function TextEditing() { let $toolbar = $('#toolbar'), $source = $('#text_source'), $preview = $('#text_preview'), $html = $('#html_preview'), $MDhandle = $('#text_editing_handle'); // Toolbar button functions $toolbar.on('mousedown', function(e) { e.preventDefault(); }); // prevent textarea from losing focus when clicking sidebar // Resize $MDhandle.on('mousedown', function(e) { e.stopPropagation(); MDresizeSplit(); }); // resize text editor panes $MDhandle.on('dblclick', function(e) { e.stopPropagation(); $source.add($preview).add($MDhandle).add($html).removeAttr('style'); }); // reset text editor panes width $(window).on('resize', function() { $source.add($preview).add($MDhandle).add($html).removeAttr('style'); }); // reset split to 50/50 on window resize; // Click labels to toggle checkboxes $preview.add($toolbar).on('click','label', function(e) { e.stopPropagation(); $(this).siblings('input').click(); }); // Sync scroll // convert this so that function is triggered only by hovered element, not when targeted el is scrolled $source.on('scroll', function(e) { MDsyncScroll(e,'text_source'); }); $html.on('scroll', function(e) { MDsyncScroll(e,'html_preview'); }); // $preview.on('scroll'): see "scroll_iframe" in "scroll_script" added to iframe src_doc // Generate Markdown Preview let source_text = ( $source.length === 0 ? '' : $source.val() ); MDmarkdown( source_text, $preview ); // Live preview update, and set edited classes for unsaved warning $source.on('input', function() { // only add class or send message once after editing if ( !$('body').hasClass('edited') ) { $('body').addClass('edited'); // add edited class if ( window.top !== window.self ) { sendMessage('top','iframe_edited','',''); } // send edited message to top } MDlivePreview($source,$preview); }); // Checklists MDsetChecklistClass(); $preview.on('click','.checklist input', function(e) { e.stopPropagation(); MDliveCheckBoxes($(this),$source,$preview); }); // Live checkboxes $preview.on('click','.table-of-contents a', function(e) { e.preventDefault(); MDtocClick($(this),$preview); }); // Preview TOC click navigation $preview.on('click','.uplink', function(e) { e.stopPropagation(); MDheaderClick($preview); }); // Click header uplinks } ///// END MAIN MD FUNCTION // MARKDOWN Functions // Focus Text function focusTextEditorPanes() { switch(true) { case $('body').hasClass('split_view'): case $('body').hasClass('source_text'): $('#text_source').focus(); break; case $('body').hasClass('preview_html'): $('#html_preview').focus(); break; case $('body').hasClass('preview_text'): $('#text_preview').focus(); break; } } // toggle text editor panes (on tab) function toggleTextEditorPanes() { switch(true) { case document.activeElement.id === 'text_preview' && getFocusableEls('#text_preview').length > 0: // focus focusable elements in text preview getFocusableEls('#text_preview').first().focus(); break; case ( /text_preview|html_preview|text_source]/.test(document.activeElement.id) && !$('body').hasClass('split_view') ): // text editor: if not split view, focus sidebar sendMessage('top','focus_sidebar'); break; case $('body').hasClass('split_view'): switch(true) { case document.activeElement.id === 'text_source': // text editor: if text source has focus with split, focus the other pane if ( $('body').hasClass('preview_html') ) { $('#html_preview').focus(); } else { $('#text_preview').focus(); } break; case ( /text_preview|html_preview/.test(document.activeElement.id) ): // text editor: if text preview has focus with split, focus text source $('#text_source').focus(); break; } break; } } // Select Textarea Content function selectTextareaContent(id) { let $textarea = document.getElementById(id); $textarea.focus(); $textarea.select(); $textarea.scrollTop = 0; } // TOOLBAR Clear text source function clearText() { if ( window.self !== window.top ) { sendMessage('top','iframe_edited'); } $('body').removeClass('edited'); $('#text_source').val('').show().focus(); $('#text_preview').removeAttr('srcdoc'); $('#html_preview').val(''); } $('body').on('click','#clear_text',function() { showWarning('clearText'); }); // TOOLBAR Save Button function saveBtn(id) { let data, ext, file_name; switch(true) { case $('#content_pane').attr('data-content') === 'has_text_editor': file_name = 'untitled'; break; default: file_name = decodeURIComponentSafe(window.location.pathname.split('/').reverse()[0]); file_name = file_name.slice(0,file_name.lastIndexOf('.')); } switch(true) { case id === 'save_text': data = $('#text_source').val(); ext = '.md'; break; case id === 'save_HTML': data = MDprepHTML($('#text_preview').html()); ext = '.html'; } saveMD( data, file_name + ext ); } $('#content_text').on('click','#save_btn li',function() { saveBtn($(this).attr('id')); }); // save text editor content // MD SAVE SOURCE or HTML function MDprepHTML(data) { const save_HTML_open = ` `; const save_HTML_close = ''; data = data.replace(/.<\/span>/g,''); return save_HTML_open + data + save_HTML_close; } function saveMD(data,file_name) { // #top must save text, otherwise a new window is opened that contains the blob content if ( window.top !== window.self ) { sendMessage('top','save_text','',[data,file_name]); } else { saveFile(data,'text/plain',file_name); } $('body,#text_source,#content_text').removeClass('edited'); } // MD Custom pre- and post-processing for text. function MDaddHeaderIDs(match, p1, p2, p3) { return ''+ p3; } // create header ids for TOC function MDcustomPreProcess(src) { return src; } // we're not doing anything here just yet... function MDcustomPostProcess(html) { html = html.replace(/<(p|li|dt|dd)>-*\s*\[\s*x\s*\]\s*(.+?)<\/(p|li|dt|dd)>$/gm,'<$1 class="checklist">') // checkboxes in p,li,dt,dd .replace(/<(p|li|dt|dd)>-*\s*\[\s{1,}\]\s*(.+?)<\/(p|li|dt|dd)>$/gm,'<$1 class="checklist">') // checkboxes // .replace(/
  • "/g,'

  • ') .replace(/^]*)>([^<]+)/gm, MDaddHeaderIDs) // add header IDs; .replace(/<\/h(\d)>/g,''); return html; } //MD Render markdown from preprocessed source text function MDmarkdown(source_text,$preview_el) { const MDit = window.markdownit({linkify:false, typography:false, html:true}).use(window.markdownitMultimdTable, {enableMultilineRows: true}) .use(window.markdownitSub).use(window.markdownitSup).use(window.markdownitFootnote).use(window.markdownitCentertext).use(window.markdownitDeflist).use(window.markdownitTocDoneRight); let MD_Preview = MDcustomPostProcess( MDit.render( MDcustomPreProcess( source_text ) ) ); let MD_script = ''; $preview_el.attr('srcdoc',MD_script + MD_Preview); // set previewed text let source_HTML = MD_Preview.toString(); $('#html_preview').empty().val(source_HTML); // set raw html } // MD Live preview, add edited warning function MDlivePreview($source_el,$preview_el) { MDmarkdown( $source_el.val(),$preview_el ); MDsetChecklistClass(); } // MD Live Checkboxes prep: find each instance of [ ] or [x] and replace text in index = to clicked checkbox in Preview. function MDreplaceAt(str, replacement, position) { str = str.substring(0, position) + replacement + str.substring(position + replacement.length); return str; } function MDreplaceNthSubStr(str,substr,replacement,index) { let count = 0, found = substr.exec(str); while ( found !== null ) { if ( count === index ) { return MDreplaceAt(str, replacement, found.index ); } else { count++; found = substr.exec(str); } } } // MD Live Checkboxes function MDliveCheckBoxes(checkbox,$source_el,$preview_el) { $('.checklist').removeClass('clicked'); checkbox.closest('p,li,dt,dd').addClass('clicked'); const this_index = $preview_el.find('.checklist').index( $('.clicked') ); const src_text = $source_el.val(); const substr = new RegExp(/\[\s*.\s*\]/g); const replacement = ( checkbox.is(':checked') ? '[x]' : '[ ]' ); $source_el.val( MDreplaceNthSubStr(src_text, substr, replacement, this_index) ); } // MD Checkbox list class: Prevent checkbox lists from having list bullets function MDsetChecklistClass() { $('input[type="checkbox"]').closest('ul').css({'list-style':'none','padding':'0'}); } // MD Resize Split View function MDresizeSplit() { let page_width = window.innerWidth, editor_width = document.getElementById('content_text').offsetWidth, editor_offsetLeft = ( document.getElementsByTagName('body')[0].id === 'top' ? document.getElementById('content_pane').offsetLeft : 0); $(document).on('mousemove',function(e) { e.stopPropagation(); e.preventDefault(); let pageX = e.pageX; if ( pageX > editor_offsetLeft + 150 && pageX < page_width - 150 ) { // min widths $('#text_editing_handle').css({'left': pageX - editor_offsetLeft - 4 + 'px'}); $('#text_source').css({'width': pageX - editor_offsetLeft + 'px'}); $('#text_preview').css({'width': editor_width + editor_offsetLeft - pageX + 'px'}); $('#html_preview').css({'width': editor_width + editor_offsetLeft - pageX + 'px'}); } }); document.onmouseup = function() { $(document).off('mousemove'); focusTextEditorPanes(); }; } // MD UI Sync Scroll function MDsyncScroll(e,id,iframe_scrollTop,iframe_scrollHeight) { if ( !document.querySelector('input[name="sync_scroll"').checked || !$('body').hasClass('split_view') ) { return; } // ignore if no split or no sync scroll let editor_height = document.getElementById('text_container').offsetHeight; let scrolled = e.currentTarget, scrolled_scrollTop = iframe_scrollTop || scrolled.scrollTop, scrolled_height = iframe_scrollHeight || scrolled.scrollHeight, scrolled_percentage = (scrolled_scrollTop/scrolled_height).toFixed(4); // the element to be sync scrolled: remove the target element id and the hidden editor pane from the array of editor pane ids let synced_id = ['text_source','html_preview'].filter(el => el !== scrolled.id).filter(el => document.getElementById(el).offsetHeight > 0).toString(); let synced = document.getElementById(synced_id) || document.getElementById('text_preview').contentWindow.document.documentElement; // the element to be sync scrolled synced.scrollTo(0, (scrolled_percentage * synced.scrollHeight).toFixed(0), {behavior:'smooth'}); // if ( (scrolled.scrollTop + editor_height) === scrolled_height) { synced.scrollTo(0,synced.scrollHeight, {behavior:'smooth'}); }; // force synced element to bottom } // click TOC anchors function MDtocClick(el,$preview_el) { let thisId = el.attr('href'); if ( thisId ) { $preview_el.scrollTop( $(thisId).offset().top - 48 ); } } // click Headers to return to TOC or top function MDheaderClick($preview_el) { switch(true) { case $preview_el.find('.table-of-contents').length > 0: document.getElementsByClassName('table-of-contents')[0].scrollIntoView(true); break; default: document.getElementById('preview').scroll(0,0); } } //***********************// // MESSAGES // Send a message to iframe or parent function sendMessage(target,message,funcName,args) { let messageObj = { 'messageContent': message, 'functionName': funcName, 'arguments': args }, content_iframe; switch(target) { case 'iframe': content_iframe = document.getElementById('content_iframe'); content_iframe.contentWindow.postMessage( messageObj, '*' ); break; case 'top': window.parent.postMessage( messageObj, '*'); break; } } // Receive a message from iframe or parent, do appropriate action function receiveMessage(e) { if ( e.origin === 'null' || e.origin === $origin ) { let message = e.data.messageContent, funcName = e.data.functionName, args = e.data.arguments; switch( message ) { case 'arrowNavigation': arrowNavigation(args); break; // class_name, key case 'hide_sidebar': $('#hide_sidebar').click(); break; case 'toggle_menu': $('#menu_container').click(); break; case 'close_menu': case 'top_closed_menu': $('#iframe_body').removeClass('has_menu is_blurred'); break; case 'top_has_menu': $('#iframe_body').addClass('has_menu is_blurred'); break; // tell iframe top has menu case 'menu_navigation': menuNavigation(args); break; // menu navigation from iframe case 'menu_selection': case 'clickMenu': e.preventDefault(); e.stopPropagation(); clickMenu(); break; // show menu case 'toggle_invisibles': $('#show_invisibles_container').find('input').click(); focusContent(); break; case 'focus_sidebar': focusSidebar(); // no break case 'escape': case 'tab': // close menus and refocus content or focus sidebar switch(true) { case $('#top').hasClass('focus_content') && $('#top').hasClass('has_menu'): focusContent(); break; default: focusSidebar(); scrollThis('tbody','selected'); break; } case 'shift_focus_iframe': switch(true) { case $('#iframe_dir_list_wrapper').length === 1: // if iframe dir_list visible... $('body').removeClass('is_blurred'); switch(true) { case $('tr.selected, tr.is_blurred').length > 0: $('tr.is_blurred').removeClass('is_blurred').addClass('selected'); break; default: $('#tbody').find('tr:visible').last().addClass('selected'); // select last row when tabbing into directory } break; default: getFocusableEls('#iframe_body').last().focus(); break; } break; case 'focus_iframe': // after tabbing into iframe $('body').removeClass('has_menu is_blurred'); switch(true) { case $('#iframe_dir_list_wrapper').length === 1: // if iframe dir_list visible... switch(true) { case $('tr.selected, tr.is_blurred').length > 0: $('tr.is_blurred').removeClass('is_blurred').addClass('selected'); break; default: $('#tbody').find('tr:visible').first().addClass('selected'); // select first row when tabbing into directory } break; case $('body').hasClass('has_text_editor_UI'): // if text editor visible... switch(true) { case document.activeElement.id === 'iframe_body': focusTextEditorPanes(); break; } switch(true) { // ... and restore text selection: NOT IMPLEMENTED YET -- but should only be used for text editor, not editable file? case $('body').hasClass('split_view'): case $('body').hasClass('source_text'): { let selection = window.getSelection(); if ( selection.anchorOffset > 0 ) { // restore cursor position or text selection // document.getElementById('text_source').setSelectionRange(x,y); // document.getElementById('text_source').scrollTop = [position of x]; // return; } else { document.getElementById('text_source').setSelectionRange(0,0); document.getElementById('text_source').scrollTop = 0; } break; } } break; case document.activeElement.id === 'iframe_body': // focus form elements and textareas in iframe files // getFocusableEls('#iframe_body').first().focus(); } break; case 'editor_theme_default': $('#iframe_body').removeClass('editor_theme_light editor_theme_dark').addClass(message); break; case 'editor_theme_light': case 'editor_theme_dark': $('#iframe_body').removeClass('editor_theme_light editor_theme_dark').addClass(message); break; case 'theme_light': case 'theme_dark': // toggle iframe UI theme and iframe Text Editor theme $('#iframe_body').toggleClass('theme_dark theme_light').removeClass('editor_theme_light editor_theme_dark').addClass('editor_'+ args ); break; // change iframe dir theme case 'blur_iframe': $('body').addClass('is_blurred'); break; case 'iframe_click': closeMenus(); $('body').addClass('focus_content'); break; // close menus and fade sidebar case 'show_iframe_dir': showContent('content_iframe_dir',args); break; // args[0] === item link, args[1] === item kind case 'show_iframe_file': showContent('content_iframe_file',args); break; // args[0] === item link, args[1] === item kind case 'show_content': // mainly for opening webloc and url files from if ( args[1] === 'dir' ) { showContent('content_iframe_dir',args); } else { showContent('content_iframe_file',args); } break; case 'open_iframe_dir_in_sidebar': window.location = args; break; // tell top to open iframe directory in sidebar; args === iframe directory url case 'open_iframe_parent_dir': $('#iframe_body #parent').find('a').click(); break; case 'iframe_arrow_navigation': switch(args) { case 'ArrowUp': $('#tbody').find('tr:visible').last().find('a').click(); break; case 'ArrowDown': $('#tbody').find('tr:visible').first().find('a').click(); break; } break; case 'reload': $('#reload_btn').click(); break; // reload content case 'close': $('#close_btn').click(); break; // escape content_iframe and close content // toggle iframe dir_list UI prefs from main menu: case 'show_numbers': case 'show_invisibles': case 'alternate_background': case 'hide_ignored_items': case 'ignore_ignored_items': $('#iframe_body').toggleClass(message); break; // AUDIO MESSAGES case 'iframe_play_pause_media': playPauseMedia(); break; // tell top to play/pause audio from iframe click case 'mediaSkip': mediaSkip(undefined,args); break; // tell top to mediaskip from focused iframe case 'play_prev_next_iframe_audio': playPrevNextMediaTrack(args); break; // play next iframe track case 'close_iframe_audio': $('.audio_loaded').removeClass('audio_loaded'); break; case 'set_media_duration': setMediaDuration(args[0],args[1]); break; // set media durations for subdirectories // TEXT EDITING MESSAGES case 'iframe_edited': if ( !$('body#top').hasClass('iframe_edited') ) { $('body#top').addClass('iframe_edited'); } break; // let top know iframe text has been edited case 'save_text_selection': $('.text.selected,.code.selected,.markdown.selected').attr('data-selection_start',args[0]).attr('data-selection_end',args[1]); break; // from iframe case 'get_text_selection': $('#content_text').attr('data-selection_start',args[0]).attr('data-selection_end',args[1]); break; case 'scroll_iframe': MDsyncScroll(e,'text_preview',args,document.getElementById('text_preview').contentWindow.document.documentElement.scrollHeight); break; case 'text_editor_toolbar_button': if ( window.top !== window.self ) { $('body').toggleClass(args); } else { toggleSearchParam(args); } break; case 'clear': $('body#top').addClass('iframe_edited'); break; // add edited class after clearing text from edited iframe file case 'save_text': saveFile(args[0],'text/plain',args[1]); break; case 'toggle_text_editor': showTextEditor(); break; case 'focus_text_preview': $('#text_preview').focus(); break; case 'unloading': // warn iframe that user wants to change or close iframe if ( !$('#iframe_body').hasClass('has_warning') ) { $('#iframe_body').addClass('has_warning').find('#warnings_container').removeClass().addClass('unloading').attr('data-function_name',funcName).attr('data-args',args); } break; case 'dont_save':// from iframe "Don't Save" button $body.removeClass('iframe_edited'); $('tr.selected:not(.audio),tr.content_loaded').removeClass('selected content_loaded'); closeContent(); focusSidebar(); break; // OTHERS case 'open_link_file': openLink(args); break; // open webloc and url files case 'iframe_loaded': switch(true) { case args[1] !== '' && !$('tr.selected').hasClass('cue'): processCueSheet(args[1]); break; // cue sheet default: showIframeContent(args); // if message received by top, iframe loaded successfully; otherwise, data-loaded remains 'unloaded' } break; case 'dir_list_subdir_loaded': $('.dir_list_subdir_loading').toggleClass('dir_list_subdir_loading has_subdirectory').after(args); $('#content_iframe_utility').removeAttr('src'); updateStats(); break; // subdirectory loaded case 'iframe_playlist': $('tr.text').removeData('playlist').removeClass('playlist').filter('.selected').data('playlist',args).addClass('playlist'); break; case 'get_html_content': sendMessage('top','open_in_text_editor','',document.getElementsByTagName('html')[0].outerHTML ); break; // send iframe html to top case 'open_in_text_editor': $('#content_text').data('edit_html',args); showWarning('openInTextEditor'); break; // open text editor to edit iframe html case 'local_link': openWarning('warning_local_file','warning_btn_ok'); break; case 'setIframePlayerStatus': // for iframe audio playback if ( args === 'play' ) { $('body').removeClass('is_paused').addClass('is_playing'); } else { $('body').removeClass('is_playing').addClass('is_paused'); } break; } } } window.addEventListener('message',receiveMessage,false); // END MESSAGES // WARNINGS // list of functions to remember while sending messages and then execute after warning button click function doFunction(funcName,args) { var funcDictionary = { 'arrowNavigation':arrowNavigation, 'clickRow':clickRow, 'doubleClickRow':doubleClickRow, 'null':null, 'clickMenu':clickMenu, 'clickThis':clickThis, 'clearText':clearText, 'closeButton':closeButton, 'closeContent':closeContent, 'closeFontFile':closeFontFile, 'closePlaylist':closePlaylist, 'closeGlyph':closeGlyph, 'mediaSkip':mediaSkip, 'openSidebarInContentPane':openSidebarInContentPane, 'resetContent':resetContent, 'setLocation':setLocation, 'showDirectorySource':showDirectorySource, 'openInTextEditor':openInTextEditor }; return funcName === 'null' ? null : funcDictionary[funcName](args); // return the function and call it with args } // Open and Close Warning alert, focus default button function openWarning(id,buttonid) { $('body').addClass('has_warning').find('#warnings_container').removeClass().addClass(id); focusButton(buttonid); } function closeWarning() { $('body').removeClass('has_warning').find('#warnings_container, #warning_buttons button').removeClass(); } // Show warning after in certain conditions (edited text, open playlist, open font file, etc.; otherwise do the action. function showWarning(funcName,args) { switch(true) { case ( /arrowNavigation|clickRow/.test(funcName) ): // warnings for arrow navigation and row clicks switch(true) { // upon receipt of message, iframe will show its warning message, based on the funcName case $('body').hasClass('iframe_edited'): sendMessage('iframe','unloading',funcName,args.key); focusButton('warning_btn_save'); break; // warn with open font file and focused sidebar case !$('body').hasClass('focus_content') && /has_font_file|has_glyph/.test( $content_pane.attr('data-content') ): openWarning('warning_close_font','warning_btn_cancel'); break; default: doFunction(funcName,args); break; } break; case ( !/arrowNavigation|clickRow/.test(funcName) ): // warnings for other functions switch(true) { case $content_pane.attr('data-content') === 'has_font_file': openWarning('warning_close_font','warning_btn_cancel'); break; // warn with open font file and close button. case $('body').hasClass('has_playlist') && $content_pane.attr('data-content') === undefined && !$content_pane.hasClass('has_audio'): case $('body').hasClass('has_filelist') && $content_pane.attr('data-content') === undefined && !$content_pane.hasClass('has_audio'): openWarning('warning_close_playlist','warning_btn_cancel'); break; case $('body').hasClass('edited'): case funcName === 'clearText': $('#content_pane').removeClass('has_hidden_text_editor').attr('data-content','has_text_editor'); openWarning('unloading','warning_btn_save'); break; default: doFunction(funcName,args); break; } } } // Warning buttons: what to do when the user clicks a warning button function warningButtons(id) { let btn = $(document.getElementById(id)), container_el = btn.closest('body'); switch(id) { case 'warning_btn_dont_save': // do the user initiated func without saving the edited text switch(true) { case window.self !== window.top: if ( $('#warnings_container').hasClass('unloading') ) { clearText(container_el); sendMessage('top','dont_save'); } break;// remove the irame src and body.iframe_edited class ignore case window.self === window.top: clearText(container_el); $content_pane.removeAttr('data-content'); $('#content_iframe').removeAttr('src').removeClass('has_content'); $dir_list.find('.dir.selected a').click(); openInTextEditor(); } closeWarning(); break; case 'warning_btn_cancel': closeWarning(); if ( $body.hasClass('focus_content') ) { focusContent(); } break; case 'warning_btn_clear': closeWarning(); clearText(); break; // clear text editor case 'warning_btn_save': if ( window.top !== window.self ) { sendMessage('top','clear'); } container_el.removeClass('edited'); $('#save_text_link').click(); closeWarning(); openInTextEditor(); break; case 'warning_btn_ok': switch(true) { case $('#warnings_container').hasClass('warning_close_font'): closeFontFile(); closeWarning(); break; case $('#warnings_container').hasClass('warning_close_playlist'): closePlaylist(); closeWarning(); break; case $('#warnings_container').hasClass('warning_make_playlist'): makePlaylist(); break; case $('#warnings_container').hasClass('warning_local_bookmark'): // no break case $('#warnings_container').hasClass('warning_local_file'): // no break case $('#warnings_container').hasClass('warning_local_playlist'): // no break case $('#warnings_container').hasClass('warning_no_playlist'): closeWarning(); break; } break; } } // Click Edited Warning Buttons $('#warnings_container').on('click','button', function(e) { e.preventDefault(); e.stopPropagation(); warningButtons( $(this).attr('id') ); }); // Edited Warning overlay: prevent user clicks on rest of UI $('body.has_overlay, body.has_warning').on('click mousedown mouseup', function(e) { e.preventDefault(); e.stopPropagation(); return; }); // Tab Warning Buttons (keyboard event) function tabWarningButtons(e) { switch(true) { case e.shiftKey: switch(true) { case !$('#warning_buttons').find(':focus,.focus').length || !$('#warning_buttons').find(':focus,.focus').prevAll('button:visible').length: $('#warning_buttons').find('button:visible').removeClass('focus').last().focus().addClass('focus'); break; default: $('#warning_buttons').find(':focus,.focus').removeClass('focus').prevAll('button:visible').first().addClass('focus').focus(); } break; default: switch(true) { case !$('#warning_buttons').find(':focus,.focus').length || !$('#warning_buttons').find(':focus,.focus').nextAll('button:visible').length: $('#warning_buttons').find('button:visible').removeClass('focus').first().focus().addClass('focus'); break; default: $('#warning_buttons').find(':focus,.focus').removeClass('focus').nextAll('button:visible').first().addClass('focus').focus(); } } } // END WARNINGS // PLAYLISTS // Open playlist $('#menu').on('click','#open_playlist_label', function(e) { e.stopPropagation(); }); $('#menu').on('change','#open_playlist', function(e) { openFile(e,'playlist'); }); // Open Playlist/Filelist function openPlaylist(files,reader,data) { // files and reader = open .m3u file; data = contents of m3u.txt file if ( !$body.hasClass('has_playlist') && !$body.hasClass('has_filelist') ) { // store original dir_list and body "has_" classes as data if body does not already have playlist or filelist let body_classes = document.getElementById('top').classList, data_classes = []; for ( let bodyClass of body_classes.values() ) { if ( bodyClass.startsWith('has') ) { data_classes.push( bodyClass ); } } // add original body classes to dataclasses $('#tbody').data('dir_list', $('#tbody').html() ).data('data_classes',data_classes); // store the original dir_list and classes $body.removeClass(data_classes.join(' ')); // remove the original body classes } $body.removeClass('has_playlist has_filelist'); closeMenus(); let file_name = ( files !== '' ? files.name : $('tr.selected.playlist').find('.tbody_row_cell_name_a_span').text() ); // get the file name for the title and current_dir_path let playlist_items = ( reader.result || data ); // get the playlist items from the file let new_index = buildNewIndex( '', convertPlaylist(playlist_items) ); closeContent(); // if normal .m3u file, close all existing content; otherwise, leave m3u.txt files open; but what about filelists? $('#tbody').empty().append(new_index[0]); // append the prepared playlist if ( new_index[1].split(' ').filter(function(e) { return e !== 'has_media' && e !== 'has_audio' && e !== 'has_video' && e !==''; }).length > 0 || $('#tbody').find('tr.dir').length > 0 ) { $body.addClass('has_filelist'); // if playlist contains non-media files or dirs } else { $body.addClass('has_playlist has_media'); // else normal media playlist } // if ( !$('#sort_by_name').hasClass('selected' ) ) { $('#sort_by_name').click(); } // sort by name updateStats(); if ( /file:/.test(new_index) && !/file:/.test($protocol) ) { // show warning about local files on non-local page $body.addClass('has_warning'); $('tbody').addClass('local'); openWarning('warning_local_playlist','warning_btn_ok'); } if ( $body.hasClass('autoload_media') ) { autoLoadFile(); } // autoload media scrollThis('tbody','selected',false); document.title = 'Playlist: '+ file_name; $('#current_dir_path').find('span').empty().html( file_name ); $('#open_playlist').val(''); // clear form to allow new list to be loaded } // Make and save playlist function makePlaylist() { let items = $('#tbody'), rows, playlist = [], playlistEntry = ''; let playlist_type = $('#make_playlist_form').find('input:checked').attr('id'); switch(playlist_type) { case 'media_files_only': rows = items.children('tr.media:not(.unchecked)'); break; case 'audio_files_only': rows = items.children('tr.audio:not(.unchecked)'); break; case 'video_files_only': rows = items.children('tr.video:not(.unchecked)'); break; case 'all_non_media_files': rows = items.children('tr:not(.media)'); break; case 'all_items': rows = items.children('tr'); break; case 'directories_only': rows = items.children('tr.dir'); break; case 'files_only': rows = items.children('tr.file'); break; } switch(true) { // show warning if no qualifying items found or make playlist case rows.length === 0: openWarning('warning_no_playlist','warning_btn_ok'); break; default: for ( let i = 0; i < rows.length; i++ ) { let row = $(rows[i]); let link = getLinkInfo( row.find('a').attr('href') )[0].trim(); let timing = ''; playlistEntry = '#EXTINF:'+ timing +','+ row.find('a').text() +'\n'+ link; playlist.push(playlistEntry); } playlist = '#EXTM3U\n'+ playlist.join('\n'); // add playlist header id saveFile(playlist,'audio/mpeg-url','untitled.m3u'); // show save file dialogue closeWarning(); } } // Select "Make Playlist" menu item: show make playlist warning list $('#make_playlist').on('click',function(e) { e.preventDefault(); e.stopPropagation(); closeMenus(); openWarning('warning_make_playlist','warning_btn_ok'); }); // Get playlist entry function makePlaylistEntry(id) { let title, link, duration = ''; switch(true) { case id === 'title': // make link for non-audio items title = $('#title span').text(); switch(true) { case $content_pane.attr('data-content') === 'has_image': // link for images link = $('#content_image').attr('src'); break; default: // all other content link = $('.content.has_content').attr('src'); if ($('.content.has_content').attr('id') === 'content_video') { duration = Number.parseInt(getElById('video')[0].duration); } break; } link = getLinkInfo(link)[0].trim(); $('#content_playlist').find('textarea').val('#EXTINF:'+ duration +','+ title +'\n'+ link +'\n').focus(); break; case id === 'content_audio_title': title = $('#content_audio_title span').text(); link = getLinkInfo($('#audio').attr('src'))[0].trim(); duration = Number.parseInt(getElById('audio')[0].duration); // get the track duration in secs from the media element $('#content_audio_playlist').find('textarea').val('#EXTINF:'+ duration +','+ title +'\n'+ link +'\n').focus(); // add the entry to the textarea break; } } // Show Playlist Entry function showPlaylistEntry(id) { makePlaylistEntry(id); switch(true) { case id === 'title': document.getElementById('content_playlist').classList.toggle('has_content'); selectTextareaContent('content_playlist_textarea'); break; case id === 'content_audio_title': document.getElementById('content_audio_playlist').classList.toggle('has_content'); selectTextareaContent('content_audio_playlist_textarea'); break; } } $('#title, #content_audio_title').on('click',function() { if ( $content_pane.attr('data-content') !== 'has_font_file' && $content_pane.attr('data-content') !== 'has_grid' ) { showPlaylistEntry($(this).attr('id')); } }); // Open File function openFile(e,type) { // type: font or playlist. if (window.File && window.FileReader && window.FileList && window.Blob) { let files = e.target.files[0]; let reader = new FileReader(); if ( type === 'font' ) { reader.readAsArrayBuffer(files); } if ( type === 'playlist' ) { reader.readAsText(files); } reader.onload = function() { if ( type === 'font' ) { openFontFile(files,reader); } if ( type === 'playlist' ) { openPlaylist(files,reader); } return true; }; } else { alert('Can\'t open file: file APIs are not fully supported in this browser.'); } } // Save File function saveFile(content,mimetype,file_name) { 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; document.body.appendChild($download_el); $download_el.click(); document.body.removeChild($download_el); URL.revokeObjectURL(blob); } })(); // FINIS! † DEO GRATIAS † //