// ==UserScript== // @name Supercharged Local Directory File Browser // @version 3.2.2 // @description Makes file:/// directory ("Index of...") pages (and many server-generated index pages) actually useful. Adds sidebar and preview pane, keyboard navigation, audio and video player, markdown/plain text preview and editing, image and font previews and grid views, sorting, user-defined shortcuts, more. // @author Gaspar Schott (Michael Schrauzer) mshroud@gmail.com // @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 file://*/?* // @include *!localhost*/ // @require https://code.jquery.com/jquery-latest.min.js // @require https://cdnjs.cloudflare.com/ajax/libs/markdown-it/8.4.2/markdown-it.js // NOTE: By default, userscripts will not run on file:/// urls, so for this script to work, you will have to enable it in your browser extension settings. // E.G.: For Tampermonkey in Chrome, go to Chrome the extension page, click the details button on the Tampermonkey pane and check 'Allow access to file URLs'. // NOTE: Safari does not allow local directories to be browsed, so the script will not work on local directories, but it will work on remote directories (or on local directories through a local server). // NOTE: This script was developed in Vivaldi, running on Mac OS Mojave. It has been tested in several other Chrome and Gecko-based browsers. // It has been very minimally tested on Windows and not at all on other OSes or browsers. It should work, but please report any issues. // @namespace https://greasyfork.org/users/16170 // @downloadURL none // ==/UserScript== (function() { 'use strict'; var $ = jQuery; // ***** USER SETTINGS ***** // const $settings = { // Paste your exported settings between the two lines below: //--------------------------------------------------------// shortcuts: // 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 shortcuts must begin with "file:///"; external shortcuts must begin with the correct protocol ("http://" or "ftp://", etc.). // Hint: you can add query strings to your urls to override user settings on a site by site basis. // Note that because of same-origin security concerns, the browser will not allow you to navigate from an external webpage to a local directory. // (But see this page for possible workarounds: https://stackoverflow.com/questions/39007243/cannot-open-local-file-chrome-not-allowed-to-load-local-resource) [ { "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 Second 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" }, ] }, ], UI_font: // Choose an installed font for the UI. 'lucidagrande, sans-serif', UI_font_size: // Choose a default UI font size; use any standard CSS units. '13px', alternate_background: // If true (default true), alternate sidebar row background color. true, ignore_files: // If true (default), ignored files (see "$row_settings" below, after Keybindings and Changelog) will be greyed-out (default) in the file list and will not be loaded in the content pane when selected; // If false, they will be treated as normal files, so if they are selected, the browser will attempt to download any file types it can't handle (which makes keyboard navigation inconvenient). true, hide_ignored_files: // If true, ignored files will be completely hidden from the file list; // If false (default), ignored files will appear greyed-out. false, hide_invisibles: // Un*x/Mac OS only: If true (default), files or directories beginning with a "." will be hidden. true, hide_details: // If true (default), hide file and directory details; if false, show them. true, default_sort: // Choose from: 'name', 'size', 'date', 'kind', 'ext', 'default'. // default = Chrome sorting: dirs on top, files alphabetical. 'default', dirs_on_top: // 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.) false, apps_as_dirs: // Un*x/Mac OS only: if true, treat apps as directories; allows app contents to be browsed. This is the default behavior for Chrome. // If false (default), treat apps as ignored files. false, dark_theme: // If true (default: false), gives the content pane a dark background, and inverts html and text content. false, grid_image_size: // Default = 150 150, grid_font_size: // Default = 1 1, autoload_media: // If true (defautl: true), the first audio or video file found in a directory will be automatically selected and loaded for playback; also, cover art (if any, will be loaded in the preview pane). true, autoload_index_files: // If true (default: false), automatically select first "index.ext" file found in directory. false, use_custom_icons: // if true (default), use custom icons for dirs and files // if false, use browser/server default icons true, // TEXT EDITING SETTINGS preview_text: // If true (default), show rendered plain text and markdown file load; // if false, show editable source text on file load. // Note that split_view = true overrides this setting. true, split_view: // If true (default: false), show split view on plain text file load // if false, use default preview_text setting. false, sync_scroll: // Sync scrolling in split view. false //--------------------------------------------------------// // Paste your exported settings between the above two lines. } // $ROW_TYPES: // 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 $row_types = { // myCategory: ['ext1','ext2',], dir: ['/'], app: ['app/','app','exe','msi'], archive: ['zip','rar','cbr','7z','tar','gz','dmg','pkg','archive'], audio: ['mp3','m4a','aac','aif','aiff','ape','flac','ogg','wav'], font: ['otf','ttf','woff','woff2','afm','pfb','pfm','tfm'], graphics: ['indd','idml','indt','icml','ai','eps','pages','qxp','qxb','qxd','mif','sla','dtp','pmd','pub','fm','book','inx'], htm: ['htm','html','xhtm','xhtml'], image: ['jpg','jpeg','png','apng','gif','bmp','webp','svg','tif','tiff','psd','raw','dng','cr2','nef','arw'], markdown: ['md','markdown','mdown','mkdn','mkd','mdwn','mdtxt','mdtext'], office: ['doc','docx','epub','xls','xlsx','xlm','odt','odf','rtf'], pdf: ['pdf'], text: ['txt','log','nfo'], code: ['bak','bash','c','cnf','codes','conf','csh','cshrc','css','default','dist','example','gemspec','h','hd','ini','js','json','jsx','less','list','local','lock','login','logout','lua','old','php','pl','plist','pth','py','rb','rc','rdoc','sass','sh','strings','tcl','xml','yaml','yml'], video: ['mpeg','mov','m4v','webm','mp4'], }; // $ROW_SETTINGS: Ignore or Exclude files by extension const $row_settings = { ignore: // Files with these extensions will not be loaded if selected in the sidebar (prevents the browser from attempting to download the file). ['exe','doc','docx','rtf','xls','xlsx','odt','odp','csv','msi','dll','indd','idml','pages','numbers','tif','tiff','raw','dng','cr2','nef','arw','eps','psd','ai','afm','pfb','pfm','tfm','zip','pkg','swf','pls','ics','DS_Store','ds_store','ds_store','alias','dmg','gz','qxp','icon.jpg','thumbs.db','ape','srf','epub'], exclude: // Files with these exensions will not be inverted in dark mode ['htm','html','xhtm','xhtml'], }; // ***** END USER SETTINGS ***** // // FEATURES INCLUDE // Resizable sidebar and file preview pane. // Keyboard navigation in sidebar. // Sort files in sidebar. // Preview all file types supported by browser (html, text, images, pdf, audio, video) plus fonts. // Preview and edit markdown and plain text files. // Audio and video playback, with shuffle, loop, skip ahead and behind; view other files in directory while playing audio. // Grid view for images and fonts. // Light and Dark theme. // Show/Hide file details. // User-definable bookmarks for directories. // User settings for various UI features. // KEYBINDINGS (These don't work in all browsers): // Arrow Up/Down: Select prev/next item. If audio is playing, and prev/next file is also audio, it will be selected but not loaded in the audio player; press return to load it. // Arrow Left/Right: Select prev/next row of the same kind as the current selection, except if current selection is a media file, select and begin playback of that item. // Cmd/Ctrl + Arrow Up: Go to parent directory // Opt/Alt + Arrow Left/Right: Skip audio ±10s // Shift + Opt/Alt + Arrow Left/Right: Skip audio ±30s // Space: Pause/Play media files // Return: Open selected directory, select file, or pause/play media. // Cmd/Ctrl + D: Toggle file details (size, date modified) in some index page types. // Cmd/Ctrl + G: Show or Reset Grid // Cmd/Ctrl + I: Toggle Invisibles // Cmd/Ctrl + Shift + O: Open selected item in new window/tab // Cmd/Ctrl + R: Reload grids and previewed content, reset scaled images/fonts, reset media files to beginning. // Cmd/Ctrl + W: Close previewed content (doesn't work in all browsers; use close button instead), or close window if no content is being previewed. // Cmd/Ctrl + Shift + : Scale preview items and grids. // CHANGELOG: //• 3.2.2 More bug fixes. //• FIXED: Source text was being interpreted as HTML. //• FIXED: Checkboxes added to markdown weren't "live." //• IMPROVED: Live preview update on input. //• IMPROVED: Nested blockquote styling. //• 3.2.1 //• FIXED: A bad bug with saving source text. //• ADDED: Made text split-view resizable. //• IMPROVED: Various UI tweaks for text editing. //• 3.2.0 //• NEW!: Preview, edit, and save Markdown and plain-text files. (Now how much would you pay? Or donate?) //• Basic Markdown syntax is rendered via markdownit.js (https://github.com/markdown-it/markdown-it). //• View editable source text or rendered HTML preview, or both in split pane. //• Live preview of source edits in split-pane view. //• Render Checkboxes/Checklists with [x] and [ ], with live updating of source text. //• Save edited source text or rendered HTML. //• More to come. //• Caveat: You must manually save your work if you want to keep it; autosave is not possible. //• Moreover, because of security concerns with local files, the editing iframe cannot communicate with the parent index page, or vice versa. //• * It is therefore entirely possible to navigate away from the page and LOSE YOUR WORK. * //• To help prevent loss of work, the script will attempt to warn you when the editing pane loses focus (if you have made any edits), //• e.g., when you click in the script sidebar or in the browser UI, or when the browser tab/window or the browser itself loses focus. //• But note that if the editing pane is already unfocused, the warning will not be issued. //• ADDED: New user settings: set default view for markdown/text files: source or preview, or split pane view. See user settings for details. //• IMPROVED: Plain text and code file type detection. //• CHANGED: Treat files without extensions as text (code) files. (Extension-less binary files will still initiate a download.) //• FIXED: Malformed URI error if file name included "%". //• FIXED: Some issues with image zoom. //• FIXED: An issue with content pane sizing. //• FIXED: An issue with parent directory navigation. // ***** GENERAL SETUP ***** // // ************************************ // // DON'T EDIT ANYTHING BELOW THIS LINE. // // ************************************ // const $userAgent = navigator.userAgent; // PATHS // Fix "%" error in file name; see https://stackoverflow.com/questions/7449588/why-does-decodeuricomponent-lock-up-my-browser function decodeURIComponentSafe(uri, mod) { var out = new String(), arr, i = 0, l, x; typeof mod === "undefined" ? mod = 0 : 0; arr = uri.split(/(%(?:d0|d1)%.{2})/); for (l = arr.length; i < l; i++) { try { x = decodeURIComponent(arr[i]); } catch (e) { x = mod ? arr[i].replace(/%(?!\d+)/g, '%25') : arr[i]; } out += x; } return out; } var $href = decodeURIComponentSafe(window.location.href); // complete URL including query string: http://www.host.com/path/to/my_dir/?query_string var $location = decodeURIComponentSafe( [location.protocol, '//', location.host, location.pathname].join('') ); var $location_arr = $location.split('/'); var $current_dir_path = $location.replace(/\//g,'/').replace(/_/g,'_').replace(/—/g,'—').replace(/\\/g,'/'); // URL w/o query string for display var $current_dir_name = $location.replace(/%20/g,' ').slice(0,-1); $current_dir_name = $current_dir_name.slice($current_dir_name.lastIndexOf('/') + 1); // Dir name without parents for display: "my_dir" // QUERY PREFS var getQueryPrefs = function() { return new URLSearchParams( window.location.search ); }; var $query_prefs = getQueryPrefs(); var $UI_pref_width = $query_prefs.get('width') === null ? '25' : (Math.round(100*($query_prefs.get('width'))/window.innerWidth)).toString(); // number string var $UI_pref_theme = $query_prefs.get('dark_theme') === null ? $settings.dark_theme : JSON.parse( $query_prefs.get('dark_theme') ); // bool var $UI_pref_background = $query_prefs.get('alternate_background') === null ? $settings.alternate_background : JSON.parse( $query_prefs.get('alternate_background') ); // bool var $UI_pref_details = $query_prefs.get('hide_details') === null ? $settings.hide_details : JSON.parse( $query_prefs.get('hide_details') ); var $UI_pref_sort = $query_prefs.get('sort') === null ? $settings.default_sort.toLowerCase() : $query_prefs.get('sort'); var $UI_pref_file = $query_prefs.get('file') === null ? '' : $query_prefs.get('file'); var $UI_pref_autoload_media = $query_prefs.get('autoload_media') == null ? $settings.autoload_media : JSON.parse( $query_prefs.get('autoload_media') ); var $UI_pref_split_view = $settings.split_view; var $UI_pref_preview_text = $settings.preview_text; // var $UI_pref_invisibles = $query_prefs.get('hide_invisibles') == null ? $settings.hide_invisibles.toLowerCase() : $query_prefs.get('hide_invisibles'); var $UI_pref_selected = $query_prefs.get('selected') === null ? '' : JSON.parse( $query_prefs.get('selected') ); var $UI_pref_history = $query_prefs.get('history') === null ? '' : $query_prefs.get('history'); var $UI_pref_history_arr; var $grid_image_size = $settings.grid_image_size ? $settings.grid_image_size : 150; var $grid_font_size = $settings.grid_font_size ? $settings.grid_font_size : 1; // set query key/value function setQuery(key, value) { $query_prefs = getQueryPrefs(); $query_prefs.set( key, value ); updateQuery(); } // toggle query key function toggleQuery(key) { var value; $query_prefs = getQueryPrefs(); value = $query_prefs.has(key) ? JSON.parse( $query_prefs.get(key) ) : $settings[key]; value === true ? $query_prefs.set( key, 'false' ) : $query_prefs.set( key, 'true' ); updateQuery(); } // update query string function updateQuery() { $query_prefs = decodeURIComponent($query_prefs); window.history.replaceState({}, document.title, window.location.pathname +'?'+ $query_prefs); } // Styles for iFrame Directory Index pages const $fileName = window.location.pathname.slice(window.location.pathname.lastIndexOf('/') + 1); const $textFiles = $row_types.markdown.concat($row_types.text, $row_types.code); if ( ( window.frameElement !== null || window.top !== window.self ) && window.location.href.endsWith('/') ) { const $custom_iframe_styles = document.createElement("style"); var $iframe_styles = ''; $iframe_styles += 'a.up' + '{ background:transparent; }'; $iframe_styles += ':root, html, body' + '{ background-color:#BBB; }'; $iframe_styles += 'body > table > thead, body > table > tbody > tr, body ul li' + '{ background:rgba(221,221,221,0.5); }'; $iframe_styles += 'body > table > tbody > tr:nth-of-type(odd), body > ul > li:nth-of-type(odd)' + '{ background:rgba(221,221,221,1); }'; $iframe_styles += 'body > table > tbody > tr:hover, body > ul > li:hover:not(:first-of-type)' + '{ background:#ABABAB; }'; $iframe_styles += ':root, html, body' + '{ border:0 !important; }'; $iframe_styles += 'body > table' + '{ border-top:solid 1px #888888; }'; $iframe_styles += 'body > ul > li:first-of-type' + '{ border-bottom:solid 1px #888888; }'; $iframe_styles += 'html, body' + '{ border-radius:0; }'; $iframe_styles += 'body > table' + '{ border-collapse:collapse; }'; $iframe_styles += 'html, body' + '{ box-sizing:border-box; }'; $iframe_styles += '#parentDirLinkBox span' + '{ color:transparent; }'; $iframe_styles += 'a:hover' + '{ color:#000000; }'; $iframe_styles += 'a, #parentDirLinkBox span:before' + '{ color:#111111; }'; $iframe_styles += '#parentDirLinkBox span:before' + '{ content:"Parent Directory"; }'; $iframe_styles += '#UI_goUp a.up:before' + '{ display:none; }'; $iframe_styles += 'a, li' + '{ display:block; }'; $iframe_styles += '#UI_goUp, #UI_showHidden' + '{ display:inline-table; }'; $iframe_styles += 'body > table table a:before' + '{ float:left; }'; $iframe_styles += ':root, html, body, *' + '{ font-family:'+ $settings.UI_font +'; }'; const $font_size_units = $settings.UI_font_size.replace(/\d*/,''); $iframe_styles += 'html, body' + '{ font-size:'+ parseFloat($settings.UI_font_size) * 0.875 + $font_size_units +'; }'; $iframe_styles += 'body > div, body > table, thead, body p' + '{ font-size: 1em; }'; $iframe_styles += 'body > ul > li:first-of-type, #parentDirLinkBox span:before, #UI_goUp a' + '{ font-weight:bold; }'; $iframe_styles += 'html' + '{ height:100%; }'; $iframe_styles += 'body' + '{ height:auto; }'; $iframe_styles += '#UI_goUp, #UI_showHidden, #UI_showHidden label' + '{ height:1.5em !important; }'; $iframe_styles += 'body > table > tbody > tr, body > ul > li' + '{ line-height:1.5; }'; $iframe_styles += '#UI_goUp, #UI_showHidden' + '{ line-height:2; }'; $iframe_styles += 'body > ul' + '{ list-style:none; }'; $iframe_styles += 'html, body, body div, body > ul > li a, #UI_goUp, #UI_showHidden, #parentDirLink, #parentDirLinkBox' + '{ margin:0; }'; $iframe_styles += 'body > div' + '{ margin:1em; }'; $iframe_styles += 'body p' + '{ margin:1rem; }'; $iframe_styles += 'body > ul > li' + '{ margin-bottom:0; }'; $iframe_styles += '.up' + '{ margin-inline-start:0; }'; $iframe_styles += '.dir::before, .file > img' + // firefox '{ margin-inline-end: 6px; }'; $iframe_styles += ':root, html, body, body > div, body > ul > li, .up' + '{ padding:0; }'; $iframe_styles += 'body > table > tbody > tr > td, body > table > thead > tr > th, body > ul > li a, #parentDirLinkBox, #UI_goUp, #UI_showHidden' + '{ padding-top:3px; }'; $iframe_styles += 'body > table > tbody > tr > td:first-of-type, body > table > thead > tr > th:first-of-type, body > ul > li a' + '{ padding-right:0; }'; $iframe_styles += 'body > table th:last-of-type, body > table > tbody > tr > td:last-of-type, body > ul > li a, #parentDirLinkBox, #UI_showHidden' + '{ padding-right:1em; }'; $iframe_styles += '#parentDirLinkBox' + '{ padding-bottom:0; }'; $iframe_styles += 'body > table > tbody > tr > td, body > table > thead > tr > th, body > ul > li a, #parentDirLinkBox, #UI_goUp, #UI_showHidden' + '{ padding-bottom:3px; }'; $iframe_styles += 'body > ul' + '{ padding-left:0; }'; $iframe_styles += 'body > table > tbody > tr > td:first-of-type, body > table > thead > tr > th:first-of-type, body > ul > li a, #parentDirLinkBox, #UI_goUp' + '{ padding-left:1em; }'; $iframe_styles += 'th:not(:first-of-type)' + '{ text-align:right; }'; $iframe_styles += 'th:first-of-type' + '{ text-align:left; }'; $iframe_styles += 'a, a:hover, td:hover, li:hover' + '{ text-decoration:none !important; }'; $iframe_styles += 'a.icon' + '{ text-indent:2px; }'; $iframe_styles += '#UI_goUp, #UI_showHidden' + '{ vertical-align:middle; }'; $iframe_styles += 'html, body, body > table' + '{ width:100%; }'; $iframe_styles += 'thead th:first-of-type' + '{ width:50%; }'; $iframe_styles += 'html, body' + '{ max-width:100%; }'; $iframe_styles += 'html, body' + '{ min-width:100%; }'; $iframe_styles += 'body table tr, body ul li, body > table > tbody > tr:hover' + '{ outline:0; }'; $('body').css({'font-family':$settings.UI_font}); $('body > h1, address, body > hr').remove(); $('body > table > tbody').find('hr').closest('tr').remove(); $('body > table th:contains("Name")').css({'text-align':'left'}); $('body > table th:contains("Size")').css({'text-align':'right'}); // pre type if ( $('body > pre').length ) { let $list_items = $('body pre').html(); let $parent_dir = $('body pre').find('a:contains("Parent")').first().attr('href'); $list_items = '' + $list_items.replace(/Parent Directory<\/a>/,'').replace(/^\s*
/g,'').replace(/
').replace(/\s{2,}/g,'
') + '
'; $('body').empty().append($list_items).find('tr:first-of-type').remove(); $('body').prepend(''); } $custom_iframe_styles.appendChild(document.createTextNode("")); $custom_iframe_styles.append($iframe_styles); document.head.appendChild($custom_iframe_styles); return; // MARKDOWN FILES $row_types.markdown } else if ( ( window.frameElement !== null || window.top !== window.self ) && ( $textFiles.includes( window.location.href.slice( window.location.href.lastIndexOf('.') + 1 ) ) ) ) { const $custom_iframe_MDstyles = document.createElement("style"); var $iframe_MDstyles = ''; $iframe_MDstyles += 'body' + '{ margin:0;padding:32px 0 0; font-size:1rem; position:relative; }'; $iframe_MDstyles += 'body #source:focus' + '{ outline:none; background:white; }'; $iframe_MDstyles += 'body #source, body #preview' + '{ margin:0; padding:1rem; height:100%; box-sizing:border-box; position:relative; z-index:1; font-size:1rem; border:0; overflow-y:scroll; }'; $iframe_MDstyles += 'body.preview:not(.split) #source, body.source:not(.split) #preview, li#saveBtn div, .comment' + '{ display:none; }'; $iframe_MDstyles += 'body.split #preview, body.split #source' + '{ width:50%; float:left; }'; $iframe_MDstyles += 'body.split #preview' + '{ border-left:solid 1px #999; }'; $iframe_MDstyles += '#MDhandle' + '{ width:8px; position:absolute; top:0; bottom:0; left:calc(50% - 4px); cursor:col-resize; z-index:11; }'; // buttons $iframe_MDstyles += '#buttons' + //ul '{ margin:-32px 0 0; padding:0; display:block; width:100%; position:fixed; z-index:100; background:#EEE; border-bottom: solid 1px #999; font-size:0.875em; -webkit-user-select: none; -moz-user-select: none; user-select:none; }'; $iframe_MDstyles += '#buttons li' + '{ margin:4px; padding:4px; width:3.5em; height:100%; display:block; opacity:0.5; list-style-type:none; cursor:pointer; }'; $iframe_MDstyles += '#buttons li:hover, body.preview:not(.split) #buttons li#showPreview, body.source:not(.split) #buttons li#showSRC, body.split #buttons li#toggleSplit, body.edited #saveBtn' + '{ opacity:1; }'; $iframe_MDstyles += '#buttons li#toggleSplit' + // toggle split '{ float:left; width:16px; height: 16px; margin: 4px 0 4px 4px; background:url("data:image/svg+xml;utf8,") center no-repeat; }'; $iframe_MDstyles += '#buttons li#syncScroll' + // sync scroll '{ width:8em; float:left; opacity:1; }'; $iframe_MDstyles += '#buttons li#syncScroll label' + // sync scroll '{ width:8em; display:block; font-size:0.875em; line-height:1.5; }'; $iframe_MDstyles += '#buttons li#showSRC' + // show source '{ float:left; width:16px; height: 16px; margin: 4px 0 4px 8px; background:url("data:image/svg+xml;utf8, ") left 5px no-repeat; }'; $iframe_MDstyles += '#buttons li#showPreview' + // show preview '{ float:left; width:16px; height: 16px; margin: 4px 0 4px 4px; background:url("data:image/svg+xml;utf8, ") -24px 5px no-repeat; }'; // Save button $iframe_MDstyles += '#buttons li#saveBtn' + // show preview '{ float:right; width:20px; height: 16px; background: url("data:image/svg+xml;utf8, ") 8px 4px no-repeat; position:relative; }'; $iframe_MDstyles += 'body.edited #buttons li#saveBtn' + // show preview '{ background: url("data:image/svg+xml;utf8, ") 8px 4px no-repeat; position:relative; }'; $iframe_MDstyles += '#buttons li#saveBtn div' + // show preview '{ position:relative; top:-9px; left:-24px; color:#999; text-align:right; }'; $iframe_MDstyles += '#buttons li#saveBtn:hover div' + // show preview '{ display:block; }'; $iframe_MDstyles += '#buttons li#saveBtn span' + // show preview '{ width:6rem; padding:4px 6px; background: #EEE; float:right; display:block; border:solid 1px #999; }'; $iframe_MDstyles += '#buttons li#saveBtn span:first-of-type' + // show preview '{ border-bottom-width:0px; }'; $iframe_MDstyles += '#buttons li#saveBtn span:hover' + // show preview '{ color:#444; }'; // textarea source $iframe_MDstyles += '#source' + '{ width:100%; height:100%; min-height:100%; line-height:1.2; overflow-y:visible; font-family:monospace; resize: none; position:relative; top:0;bottom:0; background:#EEE; }'; $iframe_MDstyles += '#source:focus' + '{ background:#FFF; }'; // preview styles $iframe_MDstyles += '#preview' + '{ background:#FFF; }'; $iframe_MDstyles += '#preview p' + '{ margin:1em 0; }'; $iframe_MDstyles += '#preview blockquote blockquote' + '{ padding-top:0; padding-bottom:0; }'; $iframe_MDstyles += '#preview img' + '{ width:auto; height:auto; max-width:100%; max-height:100%; margin:1em 0; }'; $iframe_MDstyles += '#preview pre, #preview blockquote' + '{ padding:1em; background:#EEE; }'; $iframe_MDstyles += '#preview pre' + '{ display:block; border:solid 1px #BBB; border-radius:3px; white-space:pre-wrap; word-break:break-word; }'; $iframe_MDstyles += '#preview blockquote' + '{ margin:1em 0; border-left:solid 2px #999; }'; $iframe_MDstyles += '#preview .no_list' + '{ list-style:none; }'; $iframe_MDstyles += '#preview > .no_list' + '{ padding:0; }'; $iframe_MDstyles += '#preview kbd' + '{ padding:2px 4px; background:#eee; }'; $iframe_MDstyles += 'input[type="checkbox"]' + '{ margin-right:6px; float:left; }'; $custom_iframe_MDstyles.appendChild(document.createTextNode("")); $custom_iframe_MDstyles.append($iframe_MDstyles); document.head.appendChild($custom_iframe_MDstyles); // Attempt to prettify dark theme styles; doesn't respond to user changing // $settings.dark_theme === true ? $('input,a').css({'filter':'invert(87.5%)'}) : null; // UI Elements: source let $md = ''; $md = document.querySelectorAll('pre')[0].innerText; $('body > pre').first().remove(); const sourceEl = $(''); const previewEl = $('
') const MDhandle = $('
'); // UI Elements: Buttons const toggleSplitBtn = $('
  • '); const syncScrollEl = $('
  • ') const toggleSrcBtn = $('
  • '); const togglePreviewBtn = $('
  • '); const saveBtn = $('
  • Save SourceSave HTML
  • '); const buttonsCont = $('