// ==UserScript==
// @name Supercharged Local Directory File Browser
// @version 1.4
// @description Makes file:/// directory ("Index of...") pages actually useful. Adds navigation links, file preview pane, user-defined shortcuts, filtering, keyboard navigation, more.
// @author Gaspar Schott
// @license GPL-3.0
// @match file:///*
// @require http://code.jquery.com/jquery-latest.min.js
// This script was developed in Vivaldi, running on Mac OS High Sierra. It has been tested in Chromium, Opera Next, Iridium, and in general it should work in all Chrome-based browsers.
// It does not work in Safari because Safari does not allow local directories to be browsed.
// In theory, it should work in Firefox, but because Firefox uses a different DOM for building the Index pages, the script would have to be rewritten.
// 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 Greasemonkey, open about:config and change greasemonkey.fileIsGreaseable to true.
// For Tampermonkey, go to Chrome extension page, and tick the 'Allow access to file URLs' checkbox at the Tampermonkey extension section.
// CHANGELOG:
// v. 1.4
// Added: Initial support for Firefox. Tested in Firefox 59, Waterfox 56.
// Changed: Use SVG for menu icons
// Changed: Code cleanup, reorganization
// v. 1.3
// Fixed: Keyboard navigation of ignored or invisible items fails if "Hide Invisibles" is toggled off after loading a directory.
// Added: Also hide ignored files if "Hide Invisibles" is checked.
// Added: Content reload button.
// Changed: Reorganized settings to reduce confusion about how ignored files are treated.
// v. 1.2
// Click to show menus instead of hover.
// Added Cmd/Crl+Shift+O keybinding to open selected item in new window
// Arrow navigation bugfixes
// TODO:
// Resize elements on window resize (e.g. menus)
// Add dark mode: better to use UserStyle instead?
// Fix Scroll into view -- dir_table doesn't scroll
// Support for Apache index pages (uses ul and li instead of tables)
// Support for Firefox index pages (uses embedded tables)
// if invisible item is selected when "hide invisibles" is unchecked, select nearest previous visible item when hide invisible is checked agagin?
// @namespace https://greatest.deepsurf.us/users/16170
// ==/UserScript==
(function() {
'use strict';
var $ = jQuery;
function platformIsMac() {
return navigator.platform.indexOf('Mac') > -1;
}
function platformIsWin() {
return navigator.platform.indexOf('Win') > -1;
}
// Don't run script in iframes or files (only directories)
// if ( window.top != window.self ) {
if ( window.frameElement !== null ) {
return;
} else if ( window.location.pathname.slice(-1) != '/') {
return;
}
// ***** USER SETTINGS ***** //
var $settings = {
user_name: // Your computer user name
'',
// Shortcuts: add directories and files here. (You can use your browser's bookmarks, of course.)
root_shortcuts: // Root directories: add or remove as you please (but at leave empty brackets). These defaults are applicable to Mac OS.
['Applications','Library','Users','Volumes'],
// ['C:/Users','C:/Program Files','C:/Windows'],
user_shortcuts: // User directories; you must enter your user_name above.
['Documents','Photos'],
file_shortcuts: // Add specific file paths, e.g.: 'Users/MyUserName/Documents/MyDocument.html'
// These files will be selected (loaded) automatically when their containing directory is loaded
// Limitations: only works for one file per directory; if more than one file per directory is listed, the last item will be selected.
['path/to/file.ext'],
ignore_files: // If true, ignored files (see below) 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 hidden in the file list;
// if false, they will appear greyed-out (default).
false,
ignore_file_types: // ignore files with these extensions:
['exe','.doc','.docx','ppt','pptx','xls','xlsx','odt','odp','msi','dll','rtf','indd','idml','.pages','.tif','tiff','.eps','.psd','.ai','.zip','pkg','.swf','.pls','.ics','.ds_store','alias','.dmg','.gz','.qxp','icon.jpg','thumbs.db'], // lowercase
hide_invisibles: // Mac OS only: Files beginning with a "." will be ignored.
true,
apps_as_dirs: // Mac OS only: if true, treat apps as directories; allows app contents to be browsed. This is the default behavior for Chrome.
// If false, treat apps as ignored files.
true,
dark_mode: // To be implemented
false
};
// ***** END USER SETTINGS ***** //
// ***** BUILD MENUS ***** //
// PATHS
var $location = window.location.pathname;
var $current_dir_path = $location.replace(/%20/g,' ').replace(/\//g,'/<wbr>').replace(/_/g,'_<wbr>').replace(/—/g,'—<wbr>').replace(/\\/g,'/');
var $current_dir_name = $location.replace(/%20/g,' ').slice(0,-1);
$current_dir_name = $current_dir_name.slice($current_dir_name.lastIndexOf('/') + 1);
var $location_arr = $location.split('/');
var $parent_dir_link = $location_arr.slice(0,-2).join('/') + '/';
// Parents Link Menu Items
var $parent_links_arr = function() {
var $paths_arr = [];
for ( var i = 1; i < $location_arr.length - 1; i++ ) {
$paths_arr[0] = ''; // root
$paths_arr[i] = $paths_arr[i - 1] + $location_arr[i] + '/';
}
return $paths_arr;
};
// function to build menu list items
function menu_items(x,y,i) {
var $menu_item = '<li><a href="file:///' + x[i] + '" style="margin:0;padding:4px 6px;display:block;text-indent:0;text-decoration:none;color:#333;white-space:normal;">' + y[i] + '</a></li>';
return $menu_item;
}
// Parents Directory Menu Items
var $parents_dir_menu_arr = function() {
var $parents_dir_menu_items = [];
for ( var i = 1; i < $parent_links_arr().length; i++ ) {
$parents_dir_menu_items[0] = menu_items('/','/',0); // root
$parents_dir_menu_items[i] = menu_items($parent_links_arr(),$parent_links_arr(),i);
}
$parents_dir_menu_items.pop(); // remove current directory
$parents_dir_menu_items = $parents_dir_menu_items.reverse().join('').replace(/%20/g,' ');
return $parents_dir_menu_items;
};
// Root Shortcuts Menu Items
$settings.root_shortcuts = $settings.root_shortcuts.map(i => i + '/'); // add '/'
var $root_shortcuts_menu_arr = function() {
if ( $settings.root_shortcuts.length ) {
var $root_shortcut_items = [];
for ( var i = 0; i < $settings.root_shortcuts.length; i++ ) {
$root_shortcut_items[i] = menu_items($settings.root_shortcuts,$settings.root_shortcuts,i);
}
$root_shortcut_items = $root_shortcut_items.join('');
return $root_shortcut_items;
}
};
// User Shortcuts Menu Items
var $user_shortcuts_display_name = $settings.user_shortcuts.map(i => $settings.user_name + '/' + i + '/' ); // build display names
$settings.user_shortcuts = $settings.user_shortcuts.map(i => 'users/' + $settings.user_name + '/' + i + '/'); // build link fragments
var $user_shortcuts_menu_arr = function() {
if ( $settings.user_name && $settings.user_shortcuts.length ) {
var $user_shortcut_items = [];
for ( var i = 0; i < $settings.user_shortcuts.length; i++ ) {
$user_shortcut_items[i] = menu_items($settings.user_shortcuts,$user_shortcuts_display_name,i);
}
$user_shortcut_items = $user_shortcut_items.join('');
return $user_shortcut_items;
}
};
// File Shortcuts Menu Items
var $file_shortcuts_display_name = $settings.file_shortcuts.map(i => i.split('/').pop()); // get file names from paths
var $file_shortcuts_menu_arr = function() {
if ( $settings.file_shortcuts.length ) {
var $file_shortcut_items = [];
for ( var i = 0; i < $settings.file_shortcuts.length; i++ ) {
$file_shortcut_items[i] = menu_items($settings.file_shortcuts,$file_shortcuts_display_name,i);
}
$file_shortcut_items = $file_shortcut_items.join('').replace(/\/<\/a>/g,'<\/a>').replace(/<a /g,'<a class="file_shortcut" ').replace(/%20/g,' ');
return $file_shortcut_items;
}
};
// ***** END BUILD MENUS ***** //
// ***** BUILD UI ELEMENTS ***** //
// ***** SIDEBAR ELEMENTS ***** //
// 1. Parent Directory Menu
var $up_arrow = 'url("data:image/svg+xml;utf8,<svg version=\'1.1\' id=\'Layer_1\' xmlns=\'http://www.w3.org/2000/svg\' xmlns:xlink=\'http://www.w3.org/1999/xlink\' x=\'0px\' y=\'0px\' width=\'12.728px\' height=\'7.779px\' viewBox=\'0 0 12.728 7.779\' enable-background=\'new 0 0 12.728 7.779\' xml:space=\'preserve\'><path fill=\'%23444444\' d=\'M6.364,2.828l4.95,4.949l1.414-1.414L6.364,0l0,0L0,6.363l1.413,1.416L6.364,2.828\'/></svg>")';
var $parent_dir_menu = $(
'<nav id="parent_dir_menu" style="margin:0;padding:0;display:table;width:100%;">' +
'<a href="" style="width:100%;padding:0;display:table-cell;text-align:center;vertical-align:middle;text-decoration:none;opacity:0.7"></a>' +
'</nav>'
);
$parent_dir_menu.find('a').attr('href',$parent_dir_link).css({'background':$up_arrow + 'center no-repeat'});
// 2. Current Directory Name and Parents Directory Menu
var $parents_dir_menu = $(
'<nav id="parents_dir_menu" style="margin:0;padding:0;text-align:center;">' +
'<div style="padding:4px 6px;display:inline-block;text-align:center;vertical-align:middle;overflow:hidden;cursor:pointer;hyphens:none;white-space:normal;"></div>' +
'<ul class="menu" style="display:none;margin:0;padding:0;position:absolute;top:;right:0;left:0;z-index:100;text-indent:0;text-align:left;background:lightgray;border-top:solid 1px gray;border-bottom:solid 1px gray;list-style-type:none;box-shadow: 0px 2px 3px -2px #888;"></ul>' +
'</nav>'
);
$parents_dir_menu.find('div').append( $current_dir_path );
$parents_dir_menu.find('ul').append( $parents_dir_menu_arr() );
// 3. Shortcuts Menu
var $divider = $(
'<li><hr style="margin:0;border-bottom:0;"></li>'
);
var $menu_icon = 'url("data:image/svg+xml;utf8,<svg version=\'1.1\' id=\'Layer_1\' xmlns=\'http://www.w3.org/2000/svg\' xmlns:xlink=\'http://www.w3.org/1999/xlink\' x=\'0px\' y=\'0px\' width=\'13px\' height=\'10px\' viewBox=\'0 0 13 10\' enable-background=\'new 0 0 13 10\' xml:space=\'preserve\'><rect fill=\'%23444444\' width=\'13\' height=\'2\'/><rect y=\'4\' fill=\'%23444444\' width=\'13\' height=\'2\'/><rect y=\'8\' fill=\'%23444444\' width=\'13\' height=\'2\'/></svg>")';
var $shortcuts_menu = $(
'<nav id="shortcuts_menu" style="margin:0;padding:0;">' +
'<div style="width:6em;display:table-cell;text-align:center;vertical-align:middle;font-size:18px;cursor:pointer;opacity:0.7"></div>' +
'<ul class="menu" style="display:none;margin:0;padding:0;position:absolute;right:0;left:0;z-index:100;text-indent:0;text-align:left;background:lightgray;border-top:solid 1px gray;border-bottom:solid 1px gray;list-style-type:none;box-shadow: 0px 2px 3px -2px #888;"></ul>' +
'</nav>'
);
$shortcuts_menu.find('div').css({'background':$menu_icon + 'center no-repeat'});
$shortcuts_menu.find('ul').append( $root_shortcuts_menu_arr(), $divider, $user_shortcuts_menu_arr(), $divider.clone(), $file_shortcuts_menu_arr(), $divider.clone() );
// 4. Details Button
var $details_btn = $(
'<button id="details_btn" style="margin:1em 0.5em 0.5em;clear:both" tabindex="-1">' +
'<span id="show">Show details</span><span id="hide" style="display:none">' +
'Hide details' +
'</span>' +
'</button>'
);
// 5. Invisibles Checkbox
var $inv_checkbox = $(
'<label id="inv_checkbox" for="inv_checkbox">' +
'<input type="checkbox" name="inv_checkbox" tabindex="-1" />' +
'Hide Invisibles' +
'</label>'
);
// 6. Image Grid Button
var $grid_icon = 'url("data:image/svg+xml;utf8,<svg version=\'1.1\' id=\'Layer_1\' xmlns=\'http://www.w3.org/2000/svg\' xmlns:xlink=\'http://www.w3.org/1999/xlink\' x=\'0px\' y=\'0px\' width=\'16px\' height=\'16px\' viewBox=\'0 0 16 16\' enable-background=\'new 0 0 16 16\' xml:space=\'preserve\'><g><path fill=\'%23666666\' d=\'M5,2v3H2V2H5 M7,0H0v7h7V0L7,0z\'/></g><g><path fill=\'%23666666\' d=\'M14,2v3h-3V2H14 M16,0H9v7h7V0L16,0z\'/></g><g><path fill=\'%23666666\' d=\'M5,11v3H2v-3H5 M7,9H0v7h7V9L7,9z\'/></g><g><path fill=\'%23666666\' d=\'M14,11v3h-3v-3H14 M16,9H9v7h7V9L16,9z\'/></g></svg>")';
var $content_grid_btn = $(
'<div id="grid_btn" style="margin:0 0 -0.5em 1em;width:17px;height:17px;display:none;position:absolute;top:1em;right:0.5em;line-height:0;cursor:pointer;outline:0;opacity:0.7;" tabindex="-1" title="Show Image Grid">' +
'</div>'
);
$content_grid_btn.css({'background':$grid_icon + 'no-repeat center 100%'});
// ASSEMBLE SIDEBAR HEADER
var $sidebar_header = $(
'<table id="sidebar_header" style="width:100%;position:relative;border:0;user-select:none;border-collapse:collapse;">' +
'<thead>' +
'<tr style="border-bottom:solid 1px grey;background-color:#BBB;">' +
'<th colspan="3" style="padding:4px;font-weight:normal;font-size:0.875em;letter-spacing:0.5em;cursor:default;">' +
'INDEX OF' +
'</th>' +
'</tr>' +
'</thead>' +
'<tbody>' +
'<tr style="border-bottom:solid 1px grey;background-color:#BBB;">' +
'<td style="width:24px;max-width:24px;min-width:24px;padding:0;float:left;"></td>' +
'<td style="width:100%;padding:0;border-left:solid 1px grey;border-right:solid 1px grey;"></td>' +
'<td style="width:24px;max-width:24px;min-width:24px;padding:0;float:right;"></td>' +
'</tr>' +
'<tr>' +
'<td colspan="3" style="position:relative;"></td>' +
'</tr>' +
'</tbody>' +
'</table>'
);
$sidebar_header.find('tbody tr:nth-child(1)').find('td:nth-child(1)').append( $parent_dir_menu );
$sidebar_header.find('tbody tr:nth-child(1)').find('td:nth-child(2)').append( $parents_dir_menu );
$sidebar_header.find('tbody tr:nth-child(1)').find('td:nth-child(3)').append( $shortcuts_menu );
$sidebar_header.find('tbody tr:last-child td').append( $details_btn, $inv_checkbox, $content_grid_btn );
if ( platformIsWin() ) { $inv_checkbox.hide(); }
// 7. Sidebar
var $sidebar = $(
'<div id="sidebar" style="background-color:lightgray;height:'+ window.innerHeight +'px;min-height:100%;overflow-wrap:break-word;box-sizing:border-box;border-right:solid 1px gray;font-size:0.875em;color:#333;"></div>'
);
$sidebar.append($sidebar_header);
// 8. Resize Handle
var $handle = $(
'<div id="handle" style="width:8px;position:absolute;top:0;right:-4px;bottom:0;z-index:1000;cursor:col-resize"></div>'
);
// Assemble Sidebar Elements
var $sidebar_wrapper = $(
'<td id="sidebar_wrapper" class="focused" style="width:25%;min-width:220px;will-change:width;padding:0;position:relative;border:0;background:lightgray"></td>'
);
$sidebar_wrapper.append( $sidebar, $handle );
// ***** END SIDEBAR ELEMENTS ***** //
// ***** BUILD CONTENT PANE ELEMENTS ***** //
// 1. Reload Button Element
var $content_reload_btn = $(
'<td style="width:6em;padding:4px 6px 3px;vertical-align:middle;float:left;">' +
'<button id="reload_btn" style="hyphens:none;" tabindex="-1">Reload</button>' +
'</td>'
);
// 2. Title Element
var $content_title = $(
'<td id="content_title" style="padding:4px 1em 3px;vertical-align:middle;word-break:break-word;text-transform:uppercase;"></td>'
);
// 3. Close Button Element
var $content_close_btn = $(
'<td style="width:6em;padding:4px 6px 3px;vertical-align:middle;float:right;">' +
'<button id="close_btn" tabindex="-1">Close</button>' +
'</td>'
);
// 4. Content Header Element
var $content_header = $(
'<header id="content_header" style="width:100%;display:none;position:absolute;top:0;right:0;left:0;z-index:200;background:lightgray;border-bottom:solid 1px #AAA;font-size:0.875em;color:#333;text-align:center;">' +
'<table style="width:100%;padding:7px 12px 5px;border-collapse:collapse;"><tbody><tr></tr></tbody></table>' +
'</header>'
);
$content_header.find('tr').append($content_reload_btn, $content_title, $content_close_btn);
// 5. Content Mask Element
var $content_mask = $(
'<div id="content_mask" style="position:absolute;top:0;right:0;bottom:0;left:0;display:none;z-index:2000;"></div>'
);
$content_mask.css({'height':window.innerHeight + 'px'});
// 6. Image Grid Element
var $content_grid = $(
'<div id="content_grid" style="width:auto;padding:0;display:none;position:absolute;right:0;bottom:0;left:0;overflow:auto;background:#333;grid-gap:0;grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));grid-template-rows: repeat(auto, 150px);"></div>'
);
// 7. Image Element
var $image = $(
'<img class="default" style="width:auto;height:auto;max-width:100%;max-height:calc(100% - 3px);cursor:zoom-in;-webkit-user-select:none;position:relative;top:50%;transform:translateY(-50%);" />'
);
var $content_image = $(
'<div id="content_image" style="padding:2em;position:absolute;top:0;right:0;bottom:0;left:0;display:none;background:#333;overflow:auto;text-align:center;"></div>'
);
$content_image.append($image);
// 8. Pdf (embed) Element
var $content_embed = $(
'<embed id="content_embed" style="width:100%;height:100%;position:absolute;0;right:0;bottom:0;left:0;display:none;" name="plugin" id="plugin" type="application/pdf" tabindex="0"></embed>'
);
// 9. Iframe Element
var $content_iframe = $(
'<iframe id="content_iframe" style="width:100%;height:100%;padding:0;position:absolute;top:0;right:0;bottom:0;left:0;display:none;border:0;" sandbox="allow-scripts allow-same-origin allow-modals" tabindex="0"></iframe>'
);
// 10. Object Element
// var $content_object = $(
// '<object id="content_object" style="width:100%;height:100%;padding:0;position:absolute;top:0;right:0;bottom:0;left:0;display:none;border:0;" tabindex="0"></object>'
// );
// 9. Next/Prev Elements
var $prev_btn = $('<div id="prev_btn" style="padding:0 1em;display:none;position:absolute;top:0;bottom:0;left:0;z-index:100;opacity:0.6;"></div>');
var $next_btn = $('<div id="next_btn" style="padding:0 1em;display:none;position:absolute;top:0;bottom:0;right:0;z-index:100;opacity:0.6;"></div>');
var $svg_arrow = 'url("data:image/svg+xml;utf8,<svg version=\'1.1\' id=\'Layer_1\' xmlns=\'http://www.w3.org/2000/svg\' x=\'0px\' y=\'0px\' width=\'11px\' height=\'16px\' viewBox=\'234.5 248 11 16\' enable-background=\'new 234.5 248 11 16\' xml:space=\'preserve\'><path d=\'M245.5,261l-3,3l-8-8l8-8l3,3l-5,5L245.5,261z\'/></svg>")';
$prev_btn.css({'filter':'invert(50%)','background':$svg_arrow + ' no-repeat center'});
$next_btn.css({'filter':'invert(50%)','background':$svg_arrow + ' no-repeat center','transform':'rotate(180deg)'});
// 10. Content container
var $content_container = $('<section id="content_container" style="width:100%;height:100%;top:0;overflow:visible;"></section>');
$content_container.append( $content_header, $content_mask, $content_grid, $content_image, $content_embed, $content_iframe );
// Assemble Content Pane Elements
var $content_pane = $('<td id="content_pane" class="unfocused" style="width:75%;will-change:width;padding:0;border:0;background:white;position:relative;"></td>');
$content_pane.append( $content_container, $prev_btn, $next_btn );
// Set content height
function setContentHeight() {
var $content_headerHeight = $content_header.outerHeight();
$content_image.css({'top':$content_headerHeight });
$content_grid.add($content_embed).add($content_iframe).css({'height':window.innerHeight - $content_headerHeight,'top':$content_headerHeight });
}
setContentHeight();
// resize head and content header
$('window').on('resize', function() {
headerHeight();
setContentHeight();
});
// ***** END BUILD CONTENT PANE ELEMENTS ***** //
// ASSEMBLE MAIN CONTENT = SIDEBAR + CONTENT PANE
var $main_content = $('<table id="main_content" style="width:100%;height:100%;border:0;border-collapse:collapse;"><tbody><tr></tr></tbody></table>');
$main_content.find('tr').append( $sidebar_wrapper, $content_pane );
// END BUILD UI ELEMENTS
// DEFAULT HTML, BODY STYLES
// Conditional Styles:
var $userAgent = navigator.userAgent;
var $gecko_styles = '<style type="text/css">button { padding:0; } thead {font-size:100%;} #dir_table .dir::before {position:absolute;} </style>';
if( $userAgent.indexOf('Firefox') > -1 ){
document.querySelector('head').innerHTML += $gecko_styles;
}
if( $userAgent.indexOf('Chrome') > -1 ){
}
var $body = $('body');
var $dir_table = $body.find('> table');
$body.find('> h1:contains("Index of"),> #parentDirLinkBox,> #UI_goUp').remove();
$body.attr('lang','en').parents('html').addBack().css({'margin':'0','padding':'0','max-width':'100%','height':'100%','font-family':'lucidagrande,"fira sans",helvetica,sans-serif','font-size':'13px','hyphens':'auto','overflow':'hidden','border-radius':0});
$body.prepend($main_content);
// ***** SIDEBAR ***** //
// ***** SIDEBAR HEADER ***** //
// Sidebar Header: Set menu container heights
function headerHeight() {
$sidebar_header.find('nav').find('*').addBack().css({'height': 'auto' });
$shortcuts_menu.find('div').add($parent_dir_menu).find('a').addBack().css({'height': ($shortcuts_menu.closest('tr').height() ) +'px' });
}
headerHeight();
// Sidebar Header: Show Menu function
function showMenu(x) {
if ( $(x).parents('td').siblings('td').find('.clicked').length ) {
hideMenu();
}
$(x).toggleClass('clicked').find('.menu').toggle();
}
// Sidebar Header: Show Menu on click
$parents_dir_menu.add($shortcuts_menu).on('click',function(e) {
e.stopPropagation();
showMenu(this);
});
// Sidebar Header: Hide Menu function
function hideMenu() {
$('.clicked').removeClass('clicked').find('.menu').hide();
}
// Sidebar Header: Hide Menu on click
$(document).on('click',function() {
hideMenu();
});
// Sidebar Header: Menu Icons Hover
$('#grid_btn,#shortcuts_menu div,#parent_dir_menu').hover(function() {
$(this).css({'opacity':1});
}, function() {
$(this).css({'opacity':0.7});
});
// Sidebar Header: Menu item Hover
$parents_dir_menu.add($shortcuts_menu).find('li').hover(function() {
$(this).css({'background-color':'#BBB'});
}, function() {
$(this).css({'background-color':'lightgrey'});
});
// Sidebar Header: Hide invisibles checkbox user setting
if ( $settings.hide_invisibles === true ) {
$inv_checkbox.find('input').prop('checked',true);
}
// Sidebar Header: Toggle Invisibles checkbox
$inv_checkbox.on('click', function(){
if ( $(this).find('input').is(':checked') ) {
$(this).find('input').prop('checked',false);
} else {
$(this).find('input').prop('checked',true);
}
$('.invisible').add('.ignore').toggle().filter('.selected').removeClass('selected');
});
// ***** DIR_TABLE SETUP ***** //
$dir_table.detach().attr('id','dir_table');
// DIR_TABLE: Variables
var $dir_table_head = $dir_table.find('> thead');
var $dir_table_head_cell = $dir_table_head.find('th');
var $dir_table_head_name = $dir_table_head_cell.filter(':first-of-type');
var $dir_table_head_details = $dir_table_head_name.nextAll();
var $dir_table_body = $dir_table.find('> tbody');
var $dir_table_row = $dir_table_body.find('> tr');
var $dir_table_cell = $dir_table_row.find('td');
var $dir_table_item_name = $dir_table_cell.filter(':nth-of-type(1)');
var $dir_table_details = $dir_table_item_name.nextAll();
var $dir_table_link = $dir_table_item_name.find('a');
// Dir_table: Styles
$dir_table.css({'width':'100%','min-width':'100px','height':'calc(100% - ' + $sidebar_header.height() + 'px)','border':'0','position':'relative','overflow':'hidden','table-layout':'fixed','border-collapse':'collapse'});
$dir_table_head.css({'text-align':'left','position':'absolute'});
$dir_table_head_name.css({'padding-left':'2em'});
$dir_table_head_cell.css({'padding':'0 24px 4px;'});
$dir_table_body.css({'width':'100%','position':'absolute','top':'18px','right':'0','bottom':'0','left':'0','overflow-y':'auto','outline':'0'});//.attr('tabindex','0');
$dir_table_row.css({'display':'block','margin-inline-start':'0','clear':'both'});
$dir_table_cell.css({'padding':'0px'});
$dir_table_link.css({'margin':'0px','display':'block','background-size':'auto 13px','-webkit-padding-start':'2em','padding':'4px 6px 4px 24px','color':'#333','text-decoration':'none','overflow':'hidden','background-position':'6px 4px','white-space':'normal'});
$dir_table_item_name.css({'display':'block','clear':'right'});
$dir_table_details.add($dir_table_head_details).css({'font-size':'0.875em','text-align':'left','vertical-align':'top'}).hide();
$dir_table_details.not('th').css({'padding':'0 24px 4px','float':'left'});
// Sidebar Header: Show details button click function
$details_btn.on('click',function() {
$dir_table_details.add($dir_table_head_details).toggle();
$dir_table_body.css({'top':$dir_table_head.height() + 1 + 'px'});
$(this).find('span').toggle();
});
// Dir_table: Row hover effects
$dir_table_row.hover(function() {
$(this).not('.selected').css({'background-color':'#BBB'});
// Highlight corresponding grid item
if ( $content_grid.hasClass('visible') ) {
var $this_href = $(this).find('a').attr('href');
$content_grid.find('a[href="' + $this_href + '"]').parent('div').css({'background':'#555'}).siblings('div:not(".selected")').css({'background':'transparent'});
}
}, function() {
$(this).not('.selected').css({'background-color':'transparent'});
if ( $content_grid.is(':visible') ) {
$content_grid.find('div:not(".selected")').css({'background':'transparent'});
}
});
// Dir_table: create link arrays
var $dir_table_dir_link_arr = [];
var $dir_table_file_link_arr = [];
var $dir_table_file_ext_arr = [];
$dir_table_row.not('.ignore,.invisible').find('a').each(function() {
var $this_link = $(this).attr('href').toLowerCase();
if ( $this_link.endsWith('/') ) {
$dir_table_dir_link_arr.push($this_link);
return $dir_table_dir_link_arr;
} else {
var $this_link_ext = $this_link.slice($this_link.lastIndexOf('.'));
$dir_table_file_link_arr.push($this_link);
if ( $dir_table_file_ext_arr.indexOf($this_link_ext) < 0 ) {
$dir_table_file_ext_arr.push($this_link_ext);
}
return $dir_table_file_link_arr, $dir_table_file_ext_arr;
}
});
// Dir_table: array of all dir_table links
var $dir_table_link_arr = [];
$dir_table_link_arr = $dir_table_dir_link_arr.concat($dir_table_file_link_arr);
// Dir_table: array of image types
var $image_ext_arr = ['.jpg','.jpeg','.png','apng','.gif','.bmp','webp'];
// Dir_table: Classify items
$dir_table_row.each(function() {
var $this_href = $(this).find('a').attr('href').toString().toLowerCase();
// Directories or files
if ( $this_href.endsWith('/') ) {
$(this).addClass('dir');
} else {
$(this).addClass('file');
}
// pdf
if ( $this_href.endsWith('.pdf') ) {
$(this).addClass('pdf');
} else
// images
if ( $.inArray( $this_href.slice($this_href.lastIndexOf('.') ), $image_ext_arr ) != -1 ) {
$(this).addClass('img');
}
// invisibles
if ( $this_href.slice($this_href.lastIndexOf('/') + 1 ) === '.' || $this_href.startsWith('.') || $(this).find('a').text().startsWith('.') ) {
$(this).addClass('invisible');
if ( $settings.hide_invisibles === true ) {
$(this).hide();
}
}
// ignored
if ( $settings.ignore_files === true ) {
for ( var i = 0; i < $settings.ignore_file_types.length; i++ ) {
if ( $this_href.endsWith( $settings.ignore_file_types[i] ) ) {
$(this).closest('#dir_table > tbody > tr').addClass('ignore').find('a').css({'color':'#888'});
if ( $settings.hide_ignored_files === true || $settings.hide_invisibles == true ) {
$(this).hide();
}
}
}
}
// directories as Files and hide ignored files
if ( $settings.apps_as_dirs === false ) {
if ( $this_href.endsWith('.app/') ) {
$(this).addClass('ignore app').find('a').css({'color':'#888'});
var $app_name = $(this).find('a').text().slice(0,-1);
$(this).find('a').text($app_name);
}
}
}); // end classify dir_table items
$dir_table.appendTo('#sidebar');
// ***** End dir_table setup ***** //
// ***************************** //
// ***** SHOW/HIDE CONTENT ***** //
function setContentTitle(el) {
if ( el == $content_grid ) {
$content_title.empty().prepend('Images from: /' + $current_dir_name);
} else {
$content_title.empty().prepend( $('.selected').find('a').text() );
}
if ( $('.selected').hasClass('ignore') ) {
$content_title.append(' (Ignored content)' );
}
}
function getDimensions(link, callback) {
var img = new Image();
img.src = link;
img.onload = function() { callback( this.width, this.height ); };
}
function showThis(el,link) {
if ( el == $content_image ) {
el.find('img').attr('src',link);
getDimensions( link, function( width, height ) {
$content_title.append(' <span style="text-transform:lowercase;">(' + width + 'px × ' + height + 'px</span>)' );
});
} else if ( el == $content_embed ) {
el.attr('type','application/pdf').attr('src',link + '?#zoom=100&scrollbar=1&toolbar=1&navpanes=1');
} else {
el.attr('src',link);
}
unhideThis(el);
}
function showContentHeader() {
$content_header.css({'display':'table'});
}
function showPrevNextBtns() {
if ( $dir_table.find('.img').length > 1 ) {
$prev_btn.add($next_btn).css({'display':'block'});
}
}
function hidePrevNextBtns() {
$prev_btn.add($next_btn).css({'display':'none'});
}
function hideThis(el) {
el.removeClass('visible').addClass('hidden').css({'z-index':'-1'}).hide();
}
function unhideThis(el) {
el.removeClass('hidden').addClass('visible').css({'z-index':'auto'});
if ( el == $content_grid ) {
el.css({'display':'grid'});
showPrevNextBtns();
} else if ( el == $content_image) {
el.show();
showPrevNextBtns();
} else {
el.show();
}
setContentTitle(el);
showContentHeader();
}
function closeThis(el) {
if ( el == $content_grid ) {
$content_grid.hide().empty();
} else if ( el == $content_image ) {
$content_image.hide().find('img').removeAttr('src');
} else {
el.removeClass('visible').hide().removeAttr('src');
hidePrevNextBtns();
$content_header.hide();
}
}
function emptyContentPane() {
closeThis($content_image);
closeThis($content_embed);
closeThis($content_iframe);
}
$content_close_btn.on('click',function() {
var $visible = $content_container.find('.visible');
var $hidden = $content_container.find('.hidden');
hideMenu();
if ( $visible == $content_grid && ( $hidden == $content_iframe || $hidden == $content_embed ) ) {
var $this_content_src = $hidden.attr('src');//.replace(/%20/g,' ');
$dir_table.find('a[href*="' + $this_content_src + '"]').each(function() {
$(this).click();
});
} else if ( $visible == $content_image && $hidden == $content_grid ) {
$content_grid.find('a[href="' + $dir_table.find('.selected').find('a').attr('href') + '"]').parent('div').addClass('selected').css({'background':'#666'}).siblings('div').removeClass('selected').css({'background':'transparent'});
}
closeThis( $visible );
if ( $hidden.length ) {
unhideThis($hidden);
}
scrollSidebar();
});
// ***** MAIN CLICK FUNCTION FOR SHOWING CONTENT ***** //
$dir_table_row.on('click','a',function(e) {
var $this_row = $(this).closest('#dir_table > tbody > tr');
var $this_link = 'file://' + $(this).attr('href');
$this_link = $this_link.slice($this_link.lastIndexOf('/') + 1 );
if ( $sidebar_wrapper.hasClass('unfocused') ) {
sidebarFocus();
}
hideMenu();
// if app, ignore or treat as directory
if ( $this_row.hasClass('app') && $settings.apps_as_dirs === false ) {
e.preventDefault();
} else if ( $this_row.hasClass('dir') ) {
return;
} else {
e.preventDefault();
selectItem($this_row);
if ( $content_grid.hasClass('visible') ) {
hideThis($content_grid);
}
if ( $this_row.hasClass('ignore') ) {
emptyContentPane();
showContentHeader();
} else if ( $this_row.hasClass('img') ) {
closeThis($content_embed);
closeThis($content_iframe);
showThis($content_image,$this_link);
showPrevNextBtns();
} else if ( $this_row.hasClass('pdf') ) {
emptyContentPane();
showThis($content_embed,$this_link);
} else { // iframe
emptyContentPane();
showThis($content_iframe,$this_link);
}
}
setContentTitle();
setContentHeight();
});
// File shortcuts: load directory then auto-select file
$('.file_shortcut').on('click',function(e) {
e.preventDefault();
var $this_link = $(this).attr('href');
var $this_dir = $this_link.slice(0,$this_link.lastIndexOf('/') );
window.location = $this_dir;
});
$('#reload_btn').on('click',function() {
$('.selected').find('a').click();
});
// END MAIN CLICK FUNCTIONS
// Auto-select file from file shortcut list;
// Limitations: only loads last file from list found in directory, doesn't know anything about which actual file shortcut was selected
function autoSelectFile() {
if ( $settings.file_shortcuts.length ) {
for ( var i = 0; i < $settings.file_shortcuts.length; i++ ) {
if ( $.inArray($settings.file_shortcuts[i], $dir_table_link_arr ) ) {
$dir_table.find( 'a[href*="/' + $settings.file_shortcuts[i] + '"]').click();
}
}
}
}
autoSelectFile();
function selectItem(el) {
$(el).addClass('selected').css({'background-color':'lightsteelblue'}).find('a').css({'font-weight':'bold','color':'#333'});
$(el).siblings().removeClass('selected').css({'background-color':'transparent'}).find('a').css({'font-weight':'normal'});
$(el).siblings('.ignore').find('a').css({'color':'#888'});
// select grid item
var $selected_link = $(el).find('a').attr('href');
$content_grid.find('a[href="' + $selected_link + '"]').parent('div').addClass('selected').css({'background':'#666'}).siblings().removeClass('selected').css({'background':'transparent'});
scrollSidebar();
scrollGrid();
}
function selectBlur(el) { $(el).css({'background-color':'#BBB'}).find('a').css({'font-weight':'normal','color':'#444'}); }
function sidebarFocus() {
$sidebar_wrapper.removeClass('unfocused').addClass('focused');
$content_pane.removeClass('focused').addClass('unfocused');
selectItem('.selected');
}
function contentFocus() {
$sidebar_wrapper.removeClass('focused').addClass('unfocused');
$content_pane.removeClass('unfocused').addClass('focused');
if ( $content_image.find('img').attr('src') !== undefined ) {
$content_image.focus();
}
selectBlur('.selected');
}
// Scroll Behavior
var $block = '';
if( $userAgent.indexOf('Firefox') > -1 ){
$block = 'start';
} else {
$block = 'nearest';
}
function scrollGrid() {
if ( $content_grid.hasClass('visible') && $content_grid.find('.selected').length ) {
$content_grid.find('.selected')[0].scrollIntoView({ behavior:'smooth',block:$block,inline:'nearest' });
}
}
function scrollSidebar() {
if ( $sidebar.find('.selected').length ) {
$sidebar.find('.selected')[0].scrollIntoView({ behavior:'smooth',block:$block,inline:'nearest' });
}
}
// Zoom Images
$content_image.find('img').on('click',function() {
if ( $(this).hasClass('default') ) {
$(this).removeClass('default').css({'max-width':'','max-height':'','cursor':'zoom-out','top':'0','transform':'translateY(0%)'});
} else {
$(this).addClass('default').css({'max-width':'100%','max-height':'calc(100% - 3px)','cursor':'zoom-in','top':'50%','transform':'translateY(-50%)'});
}
});
// Focus content pane on click
// $content_image.add($content_iframe).add($content_embed).on('click', contentFocus() );
// ***** KEYBOARD EVENTS ***** //
$body.on('keydown',$dir_table,function(e) {
var $selected = $dir_table.find('.selected');
var $selected_href = $selected.find('a').attr('href');
var $first_item = $dir_table_row.filter(':visible').first();
var $last_item = $dir_table_row.filter(':visible').last();
var $prev_item = $selected.prevAll('tr:visible').first();
var $next_item = $selected.nextAll('tr:visible').first();
var $prev_image = $selected.prevAll('tr.img:visible').first();
var $next_image = $selected.nextAll('tr.img:visible').first();
switch ( e.key ) {
case 'ArrowUp':
// Go to parent folder
if ( (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) ) {
window.location = $parent_dir_link;
break;
}
// Default action when content pane is focused
if ( $sidebar_wrapper.hasClass('unfocused') ) {
return;
}
if ( $first_item.hasClass('selected') ) {
break;
} else if ( $selected.length < 1 && $last_item.hasClass('dir') ) {
emptyContentPane();
selectItem($last_item);
} else if ( $prev_item.length && $prev_item.hasClass('dir') ) {
emptyContentPane();
selectItem($prev_item);
} else if ( $selected.length < 1 ) {
$last_item.find('a').click();
} else {
e.preventDefault();
$prev_item.find('a').click();
}
break;
case 'ArrowDown':
if ( (e.ctrl || e.metaKey) && $selected.hasClass('app') && $settings.apps_as_dirs === false ) {
return;
} else if ( $sidebar_wrapper.hasClass('unfocused') ) {
return;
} else if ( (e.ctrl || e.metaKey) && $selected.hasClass('dir') ) {
window.location = $selected_href;
break;
}
if ( $last_item.hasClass('selected') ) {
e.preventDefault();
} else if ( $selected.length < 1 && $first_item.hasClass('dir') ) {
selectItem($first_item);
} else if ( $selected.length < 1 ) {
$first_item.find('a').click();
} else if ( $next_item.length > 0 && $next_item.hasClass('dir') ) {
e.preventDefault();
emptyContentPane();
selectItem($next_item);
} else {
e.preventDefault();
$next_item.find('a').click();
}
break;
case 'ArrowLeft':
if ( (e.ctrl || e.metaKey) || ( e.ctrl && e.metaKey ) ) {
return;
} else if ( $sidebar_wrapper.hasClass('unfocused') ) {
return;
}
// Navigate Grid or Images
if ( $content_grid.hasClass('visible') ) {
if ( $selected.length !== 1 ) {
selectItem($dir_table_row.filter('.img').last() );
} else {
selectItem($prev_image);
}
} else if ( $content_image.hasClass('visible') ) {
$prev_image.find('a').click();
} else {
$dir_table_row.filter('.img').last().find('a').click();
}
break;
case 'ArrowRight':
if ( (e.ctrl || e.metaKey) || ( e.ctrl && e.metaKey ) ) {
return;
} else if ( $sidebar_wrapper.hasClass('unfocused') || $selected.hasClass('ignore') ) {
return;
}
// Navigate Grid or Images
if ( $selected.hasClass('dir') ) {
window.location = $selected_href; // Open directory
} else if ( $content_grid.hasClass('visible') ) {
if ( $selected.length == 0 ) {
selectItem($dir_table_row.filter('.img').first() );
} else {
selectItem($next_image);
}
} else if ( $content_image.hasClass('visible') ) {
$next_image.find('a').click();
} else {
$dir_table_row.filter('.img').first().find('a').click();
}
break;
case 'Enter':
// Open directories (or ignore)
if ( $selected.hasClass('app') && $settings.apps_as_dirs === false ) {
break;
} else if ( $selected.hasClass('dir') ) {
window.location = $selected_href;
} else if ( $selected.hasClass('file') ) {
$selected.find('a').click();
} else {
return;
}
break;
case 'i':
// Toggle Invisibles with Command-i
if ( (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) ) {
e.preventDefault();
e.stopPropagation();
$inv_checkbox.click();
}
break;
case 'w':
// Close content pane if Close button visible with Command-w
if ( (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) && $content_close_btn.is(':visible') ) {
e.preventDefault();
e.stopPropagation();
$content_close_btn.click();
}
break;
case 'Tab':
// Tab cannot blur focussed iframe
$(':focus').blur();
if ( $sidebar_wrapper.hasClass('focused') ) {
contentFocus();
} else if ( $sidebar_wrapper.hasClass('unfocused') ) {
$('.selected').click();
sidebarFocus();
}
break;
} // end switch
// Cmd/Ctrl + g: Show image Grid, with override for default browser binding
if ( e.key == "g" && (navigator.platform.match("Mac") ? e.metaKey : e.ctrlKey) ) {
$content_grid_btn.click();
e.preventDefault();
e.stopPropagation();
}
// Cmd/Ctrl + Shift + O: Open selected item in new window
if ( e.key == "o" && (navigator.platform.match("Mac") ? e.metaKey && e.shiftKey : e.ctrlKey && e.shiftKey ) ) {
window.open($selected_href);
}
});
// ***** END KEYBOARD EVENTS ***** //
// ***** IMAGE NAVIGATION ***** //
$prev_btn.on('mouseenter',function() {
$(this).css({'opacity':'1'});
}).on('mouseleave',function() {
$(this).css({'opacity':'0.6'});
});
$next_btn.on('mouseenter',function() {
$(this).css({'opacity':'1'});
}).on('mouseleave',function() {
$(this).css({'opacity':'0.6'});
});
$prev_btn.on( 'click', function(event) {
var e = $.Event("keydown");
e.key = 'ArrowLeft';
$dir_table.trigger(e);
});
$next_btn.on( 'click', function(event) {
var e = $.Event("keydown");
e.key = 'ArrowRight';
$dir_table.trigger(e);
});
// Edit Button -- not implemented
// $content_edit_btn.on('click',function() {
// var $selected_url = $('.selected').find('a').attr('href');
// window.open($selected_url);
// });
// GRID BUTTON & IMAGE GRID
// Show grid button only if images are found in directory
function show_grid_btn() {
if ( $dir_table.find('.img').length ) {
$content_grid_btn.show();
}
}
show_grid_btn();
// create grid items
var $grid_item_el = $(
'<div class="grid_item" style="display:inline-block;width:150px;height:150px;float:left;text-align:center;vertical-align:middle;">' +
'<a href="">' +
'<img src="" style="width:auto;height:auto;max-width:128px;max-height:128px;position:relative;top:50%;transform:translateY(-50%);opacity:0.8;" />' +
'</a>' +
'</div>'
);
var $grid_items_arr;
function gridItems() {
$grid_items_arr = [];
$dir_table_link.each(function() {
var $this_link = $(this).attr('href');
var $this_ext = $this_link.toLowerCase().slice($this_link.lastIndexOf('.'));
if ( $.inArray($this_ext,$image_ext_arr) != -1 ) {
$grid_item_el.find('a').attr('href',$this_link).find('img').attr('src',$this_link);
$grid_items_arr.push($grid_item_el.clone());
}
return $grid_items_arr;
});
}
// Grid Button Click
$content_grid_btn.on('click',function() {
gridItems();
$content_grid.empty().append( $grid_items_arr );
selectItem( $dir_table.find('.selected') );
hideThis($('.visible') );
unhideThis($content_grid);
setContentHeight();
});
// GRID ITEMS
// Grid Item Hover
$content_grid.on('mouseenter','> div:not(".selected")',function() {
var $this_link = $(this).find('a').attr('href');
$(this).css({'background':'#555'});
$dir_table.find('a[href="' + $this_link + '"]').css({'background':'#BBB'});
}).on('mouseleave','> div:not(".selected")',function() {
var $this_link = $(this).find('a').attr('href');
$(this).css({'background':'transparent'});
$dir_table.find('a[href="' + $this_link + '"]').css({'background':'transparent'});
});
// Grid Item Click
$content_grid.on('click','> div',function(e) {
e.preventDefault();
var $this_link = $(this).find('a').attr('href');
$dir_table.find('a[href="' + $this_link + '"]').click();
});
// ***** END IMAGE GRID ***** //
// Resize Sidebar/Content Pane
$handle.on('mousedown',function(f) {
f.stopPropagation();
var $startX = f.pageX;
var $sidebar_width = $sidebar_wrapper.width();
var $window_width = window.innerWidth;
$content_mask.show(); // needed to prevent interactions with iframe
$sidebar_wrapper.css({'-webkit-user-select':'none','-moz-user-select':'none','user-select':'none'});
$(document).on('mousemove',function(e) {
e.stopPropagation();
var $deltaX = e.pageX - $startX;
if ( e.pageX > 200 && e.pageX < $window_width - 200 ) {
$sidebar_wrapper.css({'width':$sidebar_width + $deltaX + 'px'});
$content_pane.css({'width':($window_width - $sidebar_width) - $deltaX + 'px'});
}
headerHeight();
setContentHeight();
});
$(document).on('mouseup',function() {
$content_mask.hide();
$sidebar_wrapper.css({'-webkit-user-select':'auto','-moz-user-select':'auto','user-select':'auto'});
$(document).off('mousemove');
});
});
})();