Giter Site home page Giter Site logo

cubiclesoft / js-fileexplorer Goto Github PK

View Code? Open in Web Editor NEW
227.0 7.0 36.0 612 KB

A zero dependencies, customizable, pure Javascript widget for navigating, managing, uploading, and downloading files and folders or other hierarchical object structures on any modern web browser.

PLSQL 2.87% CSS 0.79% HTML 3.33% JavaScript 59.70% PHP 33.31%
filemanager file-manager folder-viewer file-uploader file-download file-explorer widget

js-fileexplorer's Introduction

Folder and File Explorer

A zero dependencies, customizable, pure Javascript widget for navigating, managing (move, copy, delete), uploading, and downloading files and folders or other hierarchical object structures on any modern web browser. Choose from a MIT or LGPL license.

Screenshot of CubicleSoft File Explorer

Live Demo | The making of this widget

Experience a clean, elegant presentation of folders and files in a mobile-friendly layout that looks and feels great on all devices. CubicleSoft File Explorer is easily connected to any web application that needs to manage hierarchical objects (folders and files, database records, or even JSON, XML, etc).

Check out a working product that uses this widget: PHP File Manager and Editor

Donate Discord

If you use this project, don't forget to donate to support its development!

Features

  • Clean, elegant presentation of folders and files in a mobile-friendly layout.
  • Zero dependencies. No other libraries required!
  • Can handle displaying thousands of files in a folder.
  • Full keyboard, mouse, and touch support. Secure from fake/simulated keyboard, mouse, and touch events.
  • Keyboard shortcuts. Lots of keyboard shortcuts.
  • Folder history tracking and navigation. Optionally navigate through folder history in the widget using the back and forward buttons on the mouse.
  • Customizable toolbar.
  • Create new files and folders.
  • Easily rename and delete/recycle files and folders.
  • Double-click/tap or press Enter to open items in your application-defined manner.
  • Powerful file and folder selection tools. Selection boxes with variable-speed scrolling on desktop browsers.
  • Multi-select checkboxes on touch devices. Can be enabled for non-touch devices too.
  • Cut/Copy/Paste items using: Keyboard shortcuts, mouse right-click menu, or toolbar buttons.
  • Cut/Copy/Paste items between compatible widget instances...even across different web browsers!
  • Drag-and-drop support between compatible widget instances.
  • Drag-and-drop bulk uploading support directly from the OS for both files and folders.
  • Drag-and-drop direct URL downloading support (Chromium only).
  • Chunked file upload support.
  • Download files and folders via a toolbar button.
  • Items can be displayed as a thumbnail image, including folders. Thumbnails are intelligently bulk loaded in the background using a custom lazy loader.
  • Items can have detailed tooltips, overlays, size information, etc.
  • Automatic item sorting (can be disabled per folder).
  • Provides useful feedback on the status bar - items in a folder, number selected, active upload information, etc.
  • Can easily be connected to a WebSocket backend (e.g. Data Relay Center) to efficiently watch for folder updates.
  • Event-handler style callbacks. Way more callbacks than you'll probably need.
  • Plenty of automation possibilities too via the extensive FileExplorer API.
  • Multilingual support.
  • And much, much more.

Getting Started

To use this widget, you should be quite comfortable with writing both client-side Javascript and secure server-side code. Client-side Javascript is never a proper defense mechanism for proper server-side security and assume someone will try to break into your server by sending bad folder and file paths to the server to read/write data they shouldn't have access to.

Also note that this widget only provides the client-side (web browser) portion of the equation. You will need to supply your own server-side handlers (e.g. PHP) but that's the comparatively easy part.

With those caveats out of the way, download/clone this repo, put the file-explorer directory on a server, and add these lines to your page to load the widget core:

<link rel="stylesheet" type="text/css" href="file-explorer/file-explorer.css">
<script type="text/javascript" src="file-explorer/file-explorer.js"></script>

Next, create an instance of the FileExplorer class inside a closure for security reasons:

<div id="filemanager" style="height: 50vh; max-height: 400px; position: relative;"></div>

<script type="text/javascript">
(function() {
	var elem = document.getElementById('filemanager');

	var options = {
		initpath: [
			[ '', 'Projects (/)', { canmodify: false } ]
		],

		onrefresh: function(folder, required) {
			// Optional:  Ignore non-required refresh requests.  By default, folders are refreshed every 5 minutes so the widget has up-to-date information.
//			if (!required)  return;

			// Maybe notify a connected WebSocket here to watch the folder on the server for changes.
			if (folder === this.GetCurrentFolder())
			{
			}

			// Make a call to your server here to get some entries to diplay.
			// this.PrepareXHR(options) could be useful for doing that.  Example:

			var $this = this;

			var xhr = new this.PrepareXHR({
				url: '/yourapp/',
				params: {
					action: 'file_explorer_refresh',
					path: JSON.stringify(folder.GetPathIDs()),
					xsrftoken: 'asdfasdf'
				},
				onsuccess: function(e) {
					var data = JSON.parse(e.target.response);
console.log(data);

					if (data.success)  folder.SetEntries(data.entries);
					else if (required)  $this.SetNamedStatusBarText('folder', $this.EscapeHTML('Failed to load folder.  ' + data.error));
				},
				onerror: function(e) {
					// Maybe output a nice message if the request fails for some reason.
//					if (required)  $this.SetNamedStatusBarText('folder', 'Failed to load folder.  Server error.');

console.log(e);
				}
			});

			xhr.Send();
		},

		// This will be covered in a moment...
//		onrename: function(renamed, folder, entry, newname) {
//		},
	};

	var fe = new window.FileExplorer(elem, options);
})();
</script>

That code will produce a less-than-exciting 'Loading...' view and also doesn't show the toolbar since there are no event listeners for the tools (yet):

A loading screen

The next step is to connect the widget to the backend server. There are many ways to do that. The widget instance itself exports the PrepareXHR class to make AJAX calls. Or you could use a framework specific mechanism of your choice (jQuery, Vue, whatever) or talk to a WebSocket server or whatever makes sense for your application. The entries returned from the server should ideally be compatible with Folder.SetEntries.

If using PHP on a server and this widget will reflect a physical file system on the same server, there is the useful FileExplorerFSHelper PHP class that can simplify connecting the widget to the backend server:

<?php
	require_once "server-side-helpers/file_explorer_fs_helper.php";

	$options = array(
		"base_url" => "https://yoursite.com/yourapp/files/",
		"protect_depth" => 1,  // Protects base_dir + additional directory depth.
		"recycle_to" => "Recycle Bin",
		"temp_dir" => "/tmp",
		"dot_folders" => false,  // Set to true to allow things like:  .git, .svn, .DS_Store
		"allowed_exts" => ".jpg, .jpeg, .png, .gif, .svg, .txt",
		"allow_empty_ext" => true,
		"thumbs_dir" => "/var/www/yourapp/thumbs",
		"thumbs_url" => "https://yoursite.com/yourapp/thumbs/",
		"thumb_create_url" => "https://yoursite.com/yourapp/?action=file_explorer_thumbnail&xsrftoken=qwerasdf",
		"refresh" => true,
		"rename" => true,
		"file_info" => false,
		"load_file" => false,
		"save_file" => false,
		"new_folder" => true,
		"new_file" => ".txt",
		"upload" => true,
		"upload_limit" => 20000000,  // -1 for unlimited or an integer
		"download" => true,
		"copy" => true,
		"move" => true,
		"delete" => true
	);

	FileExplorerFSHelper::HandleActions("action", "file_explorer_", "/var/www/yourapp/files", $options);

Once entries are populating in the widget, various bits of navigation functionality should start working. The widget is starting to come to life but is mostly read only. Let's add an onrename handler so users can press F2 or click on the text of a selected item to rename the item:

	// Note:  'entry' is a copy of the original, so it is okay to modify any aspect of it, including 'id'.
	onrename: function(renamed, folder, entry, newname) {
		var xhr = new this.PrepareXHR({
			url: '/yourapp/',
			params: {
				action: 'file_explorer_rename',
				path: JSON.stringify(folder.GetPathIDs()),
				id: entry.id,
				newname: newname,
				xsrftoken: 'asdfasdf'
			},
			onsuccess: function(e) {
				var data = JSON.parse(e.target.response);
console.log(data);

				// Updating the existing entry or passing in a completely new entry to the renamed() callback are okay.
				if (data.success)  renamed(data.entry);
				else  renamed(data.error);
			},
			onerror: function(e) {
console.log(e);
				renamed('Server/network error.');
			}
		});

		xhr.Send();
	},

A lot is going on here. When the user finishes renaming an item, onrename is called, which hands the request off to an AJAX request to the server to handle. During this operation, the textarea is marked read only and the busy state on the folder is enabled. When the AJAX operation completes, it must call renamed() to let the widget know that the operation has been completed and indicate success by passing in a compatible entry object - either a modified entry or a new entry from the server. On failure, a boolean of false or a string that is passed to renamed() to be used as part of the error message displayed to the user.

The above examples and documentation should be enough to get the ball rolling. Most event handler callbacks utilize a similar approach: Receive an event callback, make a server call or two, and finally call the completion callback function with the result of the operation. Callbacks always have the 'this' context as the FileExplorer instance.

See a complete, functional implementation of all of the important callbacks in the FileExplorerFSHelper PHP class documentation. It's an excellent starting point when utilizing the FileExplorerFSHelper class.

FileExplorer Options

The options object passed to the FileExplorer class accepts the following options:

  • group - An optional string containing a group name. When specified, this must be a unique string that indicates a compatible widget backend to allow cross-widget drag-and-drop and cut/copy/paste to function properly. When not specified, the instance receives a group name unique to the instance that disables the aforementioned cross-widget features.
  • alwaysfocused - A boolean that indicates whether or not the widget's appearance never loses focus (Default is false). This only affects visual appearance and does not stop onfocus/onblur event callbacks from firing.
  • capturebrowser - A boolean that indicates whether or not to capture the hardware back and forward mouse buttons when the mouse is hovering over the widget (Default is false). Read the 'Known Limitations' section before enabling this feature.
  • messagetimeout - An integer containing the number of milliseconds to show short-lived messages in the status bar (Default is 2000).
  • displayunits - A string containing one of 'iec_windows', 'iec_formal', or 'si' to specify what units to use when displaying file sizes to the user (Default is 'iec_windows').
  • adjustprecision - A boolean indicating whether or not to adjust the final precision when displaying file sizes to the user (Default is true).
  • initpath - An optional array of arrays containing the initial path segments to set. Each path segment is an array consisting of [id, value, attrs]. This path is passed to FileExplorer.SetPath.
  • onfocus(e) - An optional callback function that is called when the widget or one of its components receives focus.
    • e - The browser event object that triggered the event.
  • onblur(e) - An optional callback function that is called when the widget loses focus. This callback can be used to hide or Destroy() the widget instance if it is used in a popup overlay. Note that onblur will not fire if the window itself loses focus - only if the widget loses focus to another element on the page.
    • e - The browser event object that triggered the event.
  • onrefresh(folder, required) - An optional callback function that is called when a folder refresh should happen.
    • folder - A Folder instance to refresh.
    • refresh - A boolean indicating that the refresh is required for the widget to function properly.
  • onselchanged(folder, selecteditemsmap, numselecteditems) - An optional callback function that is called whenever the user changes the item selections. Rarely used.
    • folder - The current Folder instance.
    • selecteditemsmap - The internal selecteditemsmap object. Do not modify.
    • numselecteditems - An integer containing the number of selected items in the selecteditemsmap.
  • onrename(renamed, folder, entry, newname) - An optional callback function that is called when the user renames an item.
    • renamed(entry) - A callback function to call upon completion of renaming or on failure.
    • folder - The Folder in which the entry to rename is in.
    • entry - The entry to rename.
    • newname - A string containing the name that was entered by the user.
  • onopenfile(folder, entry) - An optional callback function that is called when the user double-clicks/taps on an item.
    • folder - The Folder that the entry to open is in.
    • entry - The entry to open.
  • tools - An optional object containing key-value pairs where the value is a boolean that indicates whether or not to show the tool and the keys are: new_folder, new_file, upload, download, copy, paste, cut, delete, item_checkboxes. Most toolbar tools show up automatically when an onhandler is defined and override any "don't show" setting. The only tool that doesn't show as a result of a handler being defined is 'item_checkboxes'.
  • onnewfolder(created, folder) - An optional callback function that is called when the user clicks the New Folder icon or presses Ctrl + Ins.
    • created(success) - A callback function to call upon completion of creating a new folder or on failure.
    • folder - The Folder in which to create a new folder.
  • onnewfile(created, folder) - An optional callback function that is called when the user clicks the New File icon or presses Ins.
    • created(success) - A callback function to call upon completion of creating a new file or on failure.
    • folder - The Folder in which to create a new file.
  • oninitupload(startupload, fileinfo, queuestarted) - An optional callback function that is called when the user clicks the Upload icon or presses Ctrl + U.
    • startupload(fileinfo, process) - A callback function to call upon completion of initializing the upload or on failure. The 'process' option can be used to tell the callback to process the upload itself or skip it if it was handled in the callback (e.g. creating an empty directory). When 'process' is a string, it is treated as an error message to display to the user.
    • fileinfo - The file information object of the file to initialize for uploading.
    • queuestarted - An integer containing a UNIX timestamp of when the current upload queue was started. Can be useful to pass to a server that can use the value to copy files that are going to be overwritten to a recycling bin folder before the overwrite happens.
  • onfinishedupload(finalize, fileinfo) - An optional callback function that is called when the upload finishes successfully. Can be used to finalize an uploaded file on a server (e.g. move from a temporary directory to its final location).
    • finalize(success, entry) - A callback function to call upon completion of finalizing the upload or on failure. The 'entry' parameter is an optional Folder entry to set in the current folder.
    • fileinfo - The file information object of the file upload to finalize.
  • onuploaderror(fileinfo, e) - An optional callback function that is called when the upload fails. Can be used to remove an associated temporary file that was created during initialization.
    • fileinfo - The file information object of the file upload that had an error.
    • e - An optional error event object (not all failure conditions include this parameter).
  • concurrentuploads - An integer containing the maximum number of concurrent uploads to perform simultaneously (Default is 4).
  • oninitdownload(startdownload, folder, ids, entries) - An optional callback function that is called when the user clicks the Download icon. Since browsers don't do well with downloading a lot of individual files, it is recommended that folders and files be sent from the server in a single, compressed file format (e.g. ZIP).
    • startdownload(xhroptions) - A callback function to call upon completion of preparing the download or on failure. When an object is passed for xhroptions, it must be a set of options compatible with PrepareXHR.
    • folder - The Folder in which the ids are located.
    • ids - An array containing the selected entry IDs to download.
    • entries - An array containing the selected entries to download.
  • ondownloadstarted(options) - An optional callback function that is called when the download has started.
    • options - The xhroptions object passed to startdownload().
  • ondownloaderror(options) - An optional callback function to that is called if the download failed to start.
    • options - The xhroptions object passed to startdownload().
  • ondownloadurl(result, folder, ids, name) - An optional synchronous callback function that is called when a user starts a drag operation or cuts/copies one or more selected items. This applies a DownloadURL MIME type to the content. This allows for dragging/pasting folders and files out of the browser and onto the desktop. Currently only supported in Chromium browsers and may not use asynchronous methods like AJAX to calculate the URL.
    • result - An object that should have 'name' and 'url' strings assigned to it.
    • folder - The Folder in which the ids are located.
    • ids - An array containing the selected entry IDs to download.
    • entry - The first folder entry in the associated IDs list. Can be used to set the 'name' of the file.
  • oncopy(copied, srcpath, srcids, destfolder) - An optional callback function that is called when the user copies folders/files from one folder to another.
    • copied(success, entries) - A callback function to call upon completion of copying the folders and files or on failure. The 'entries' should contain the successfully copied entries to add regardless of success/failure.
    • srcpath - An array of arrays representing the source path of the IDs.
    • srcids - An array of entry IDs in the source path to copy.
    • destfolder - A Folder to which the entries are to be copied. Note that the destination folder may be the same as the source, in which case the server may choose to copy the files to new files with different names or reject the request.
  • onmove(moved, srcpath, srcids, destfolder) - An optional callback function that is called when the user moves folders/files from one folder to another.
    • moved(success, entries) - A callback function to call upon completion of moving the folders and files or on failure. The 'entries' should contain the successfully moved entries to add regardless of success/failure.
    • srcpath - An array of arrays representing the source path of the IDs.
    • srcids - An array of entry IDs in the source path to move.
    • destfolder - A Folder to which the entries are to be moved. Note that the destination folder may be the same as the source, in which case the server should reject the request.
  • ondelete(deleted, folder, ids, entries, recycle) - An optional callback function that is called when the user deletes folders/files.
    • deleted(success) - A callback function to call upon completion of the deletion operation.
    • folder - The Folder in which the ids/entries are located.
    • ids - An array containing the selected entry IDs to delete.
    • entries - An array containing the selected entries to delete.
    • recycle - A boolean hinting at whether to send items to a recycling bin (Delete) or permanently delete them (Shift + Delete). The server is free to ignore this parameter and do whatever makes the most sense.
  • langmap - An object containing translation strings. Support exists for most of the user interface (Default is an empty object).

The Live Demo utilizes nearly all of the available callbacks. The Live Demo source code was designed so as keep this documentation to a minimum and to provide decent example usage without incurring AJAX calls.

Making Custom Tools

While most of the widget is not really intended to be modified externally, the toolbar is designed to be extensible. Building a new tool involves:

  • An icon to display and an associated CSS class.
  • Some Javascript that handles clicks and possibly a keyboard shortcut.
  • Being efficient. The toolbar buttons are updated regularly via toolbar events that fire from FileExplorer.
  • Cleaning up when the 'destroy' event fires.
  • Registering the new tool with the registered tools.

The simplest approach to developing a new tool is to look at the existing tools. However, the Delete tool is fairly simple and short:

(function() {
	// Tools receive the FileExplorer instance as the only option passed to the tool.
	var FileExplorerTool_Delete = function(fe) {
		if (!(this instanceof FileExplorerTool_Delete))  return new FileExplorerTool_Delete(fe);

		// Do not create the tool if deleting is disabled.
		if (!fe.hasEventListener('delete') && !fe.settings.tools.delete)  return;

		var enabled = false;

		// Register a toolbar button with File Explorer.
		var node = fe.AddToolbarButton('fe_fileexplorer_folder_tool_delete', fe.Translate('Delete (Del)'));

		// Handle clicks.
		var ClickHandler = function(e) {
			if (e.isTrusted && enabled)  fe.DeleteSelectedItems(!e.shiftKey);
		};

		node.addEventListener('click', ClickHandler);

		// Efficiently handle toolbar updates - only adding/removing the disabled class if it is different from its previous state.
		var UpdateToolHandler = function(currfolder, attrs) {
			var prevenabled = enabled;

			enabled = (!currfolder.waiting && (!('canmodify' in attrs) || attrs.canmodify) && fe.GetNumSelectedItems());

			if (prevenabled !== enabled)
			{
				if (enabled)  node.classList.remove('fe_fileexplorer_disabled');
				else  node.classList.add('fe_fileexplorer_disabled');

				// Notify File Explorer that the state was updated.
				// When the event callback finishes, it will know that there is some work to do.
				fe.ToolStateUpdated();
			}
		};

		fe.addEventListener('update_tool', UpdateToolHandler);

		// Cleanly handle the destroy event.
		var DestroyToolHandler = function() {
			node.removeEventListener('click', ClickHandler);
		};

		fe.addEventListener('destroy', DestroyToolHandler);
	};

	// Register the tool in the second group of tools (0-based).
	window.FileExplorer.RegisterTool(1, FileExplorerTool_Delete);
})();

Some additional comments were added to the code above to aid in understanding what is going on. There are more complex tools to look at in the FileExplorer source code (e.g. FileExplorerTool_Download). The Class Documentation section below will be quite useful when developing a custom tool.

For custom tools, you might want to prefix custom settings object keys with something like a company abbreviation so the likelihood of a naming conflict is reduced.

One good idea for a custom tool might be a HTML embed tool. If a user selects a single item and clicks the embed tool, the clipboard receives some HTML code that can be pasted into another website to embed the item into a post.

Class Documentation

  • FileExplorer class - The core FileExplorer class + tools. Does most of the heavy-lifting. Exported as window.FileExplorer.
  • DebounceAttributes class - Debounces/throttles attribute changes. More efficient than most debounce functions that rely on events.
  • PrepareXHR class - A basic and convenient wrapper around a XMLHttpRequest (XHR) object.
  • ImageLoader class - A multi-queue delayed image loader that only loads images when it is told to. Also exported as window.FileExplorer.ImageLoader for reusability purposes.
  • PopupMenu class - Displays a popup menu of items. Used for Recent Locations and path segment expansion. Full keyboard, mouse, and touch support. Also exported as window.FileExplorer.PopupMenu for reusability purposes.
  • TextareaOverlay class - Displays a positionable textarea with optional text with text selection. Full keyboard, mouse, and touch support. Also exported as window.FileExplorer.TextareaOverlay for reusability purposes.
  • Folder class - Tracks the contents of a single folder in the mapped folders of the FileExplorer class. The Folder class cannot be instantiated except from within the FileExplorer but instances of this class can be accessed primarily via callbacks from FileExplorer.
  • FileExplorerFSHelper class - PHP server-side helper class to simplify interacting with server-local, physical file systems.

Known Limitations

CubicleSoft File Explorer is a complex piece of software written in Javascript. As with every complex piece of software written in Javascript, there are going to be problems with certain combinations of OS + device + web browser for a garden variety of reasons. Web browsers have lots of bugs and the specifications that browsers follow don't cover all edge cases, which leaves things open to interpretation for the browser vendor. What follows are known limitations of the widget due to conditions beyond nearly everyone's direct control and most of the listed problems have reasonable workarounds. Please do not open issues on the issue tracker for these items unless you actually solve them.

  • Some devices and web browsers lack HTML 5 drag-and-drop support, notably any browser on iOS as the iOS webview implementation lacks support. It is NOT recommended to use a polyfill with this widget to support HTML 5 drag-and-drop. There are several situations where a drag-and-drop polyfill will probably break certain touch-based features of this widget and/or break native drag-and-drop elsewhere. Instead, use the cut/copy/paste feature on devices/browsers where HTML 5 drag-and-drop is not natively supported.

  • The right-click context menu for cut/copy/paste can cause the main UI area to get stuck on some platforms until a click or keyboard event is registered, notably Mac OSX. There is a near-invisible textarea overlay that captures right-click events for cut/copy/paste operations but it doesn't always go away until a second mousedown/keyup event is registered. This happens because there is no such thing as an 'exitcontextmenu' event to detect that the context menu has closed and all known methods for detection are hacks. The solution is to either put up with the extra click or just not use right-click and use keyboard shortcuts or the toolbar buttons instead.

  • There is extremely limited "file paste" support from OS file systems into the widget. Raw image data that is stored on the clipboard (e.g. right-click on an image in a website then click Copy) can be pasted into the widget and it will be uploaded but files from the OS itself do not drop. Here's a Chromium bug that's been open since 2013 about the issue and has received almost no attention other than duplicates being merged into it. Dragging and dropping files from the OS into the browser is the most feature-complete file transfer mechanism available to date.

  • The iframe-based downloading method requires initiating a second request to the server for the same content. This kind of hack has to be done because there is no web browser event available to know that page navigation was cancelled when the download starts. The XHR request terminates as soon as it receives its first 'progress' event, usually in the first few KB and it assumes the iframe form submission equivalent, which started before the XHR request, completed in a similar amount of time and has started downloading the file. It is recommended that download requests that take more than a few seconds to process on the server (e.g. generating a compressed file) be run separately and the generated file stored on the server somewhere prior to calling startdownload(fileinfo) in the client so that both the form and XHR requests return quickly, the download begins, and the iframe is removed all within a few seconds.

  • The hardware back/forward mouse button capture logic modifies browser history. There is no way to capture the hardware mouse buttons except to modify browser history so that there are three distinct history push states (back, current, forward) while hovering over the widget and using mouseenter/mouseleave to enable/disable the back/forward button capture. There are a couple of extremely difficult to track down bugs as well. The first bug is that the window.history.scrollRestoration of the real page sometimes gets stuck on manual instead of being restored to auto, which causes page reload jumping issues and also causes Edge to jump during scrolling. That bug only seems to show up if lots of reloads happen. The second bug is the page the widget is on occasionally completely messes up the parent pushState and the widget decides it needs to back up too far in the history stack and ends up leaving the page altogether. I have no idea what causes either bug to occur. Also, during development of this specific feature, Firefox literally crashed a half-dozen times. Since browser history is modified, which may conflict with other software, the hardware back/forward mouse button capture feature is disabled by default. It is a pretty cool feature when it works.

js-fileexplorer's People

Contributors

cubiclesoft avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

js-fileexplorer's Issues

onmove - get file names by ids

In my project i use my own backend code to handle refresh/delete/move/rename... requests.
Every operation requires the full path of a file.
For example a delete action GET-Request would look like this https://......../fileHandlerInterface.php?fileName=/home/user1/test.txt&op=delete.
As a gui i used your code and it worked pretty well, until i started working with the move event.
It gives the user the srcpath, which is an array (not a folder object!).
And the moved files as an array of ids.
I tried everything to parse the ids into the correct format, but failed.
Can you help me?

my code:

onmove: function(moved, srcpath, srcids, destfolder) {
                    
	var sourcepath = "";

	srcpath.forEach(element => {
		sourcepath += element[1];
	});
                    
                    
	var destpath = dataContainer.buildAbsolutePath(destfolder);//own function creates absolute path from folder object

	console.log("onmove {sourcepath: " + sourcepath + " srcids: " + srcids + " destfolder: " + destpath + "}");

	moved("Server/Network error");

},

ARIA implementation/ADA screen reader compliance

During the initial development, significant effort was made to make sure keyboard navigation worked well and hit on all the critical navigation areas. This is a very complex widget with lots of little details. The big question is: Is this Americans with Disabilities Act (ADA) compliant? If not, then what ARIA labels, code changes, etc. need to be applied to bring it into compliance?

Firefox has a built-in accessibility analyzer. When I run it, it shows a giant list of "problems" that it found. Here's an example:

Clickable elements must be focusable and should have interactive semantics.

The very first of several hundred potential accessibility issues that Firefox finds with the widget is in regards to the entire widget wrapper and keyboard navigation. Basically, Firefox is saying that while the mouse can click on the widget wrapper, the keyboard can't reach the same element. When the user clicks on the widget, it forces the keyboard focus area to a focusable element.

There are several questions to answer here for just this one issue:

  • Is the mouse-click -> focus-steal behavior the correct thing to do?
  • Does there need to be keyboard interaction on the entire widget wrapper for ADA compliance?
  • What ARIA items and/or code changes are needed to achieve ADA compliance?

I am not qualified (as in, I don't use a screen reader and, even if I did, I wouldn't use it the same way a person with vision deficiencies would) nor am I sufficiently knowledgeable in this area to figure out what the correct answers are despite reading a number of guides on ARIA. What happens now, IMO, makes the most sense under a wide range of circumstances, but the various choices made might not be the best choices.

As I mentioned, I've read a number of ARIA guides - clearly written by fully sighted people (who are also unqualified) - and there doesn't seem to be a consensus on how to approach building widgets like this one. Just broad, sweeping, hand-waving "here are the various ARIA attributes" and "good luck!" style messaging. Neither of which help to actually answer the earlier questions. So if you chance upon this issue and can provide laser-focused answers to those three questions (and whatever others I might have), it would provide a starting point to work through the accessibility issue list and ultimately bring the whole widget into complete ADA compliance.

License request

Hello, I'd like to use this project in one of my (FOSS) programs. Could I ask you if it would be possible to get a compatible license for this in the repository? Right now there is no indicator as to what the license is, which means it goes under GitHubs default licensing, which isn't great.

No tags, no npm/yarn, no composer ?

hey, it was a surprise to find a JS FileExplorer that is not overly priced/free.

So I was happy to try it out on my app,
but sadly there's no package manager to install that component.

Why 🤔

PNG vs. SVG icons

Currently, the icons in the widget are sourced from a single PNG sprite sheet. The switch to PNGs was done after attempting a SVG icon implementation that rendered rather badly on desktop browsers.

Unfortunately, devices with higher pixel densities scale up the PNG icons, which ends up looking blurry. This was discovered pretty late during development.

The quick fix was to apply the relatively new CSS image-rendering: pixelated feature to provide a hint to the scaler to increase the crispness of the images. However, there are now a few jaggy edges and supposedly mobile Safari (iOS) flips the meaning of pixelated vs. crisp-edges. I would prefer a more refined option like "edge-detect-pixelated" that combines nearest neighbor for the interior and smooths out the edges of the scaled image instead of blunt instruments like nearest neighbor vs. bicubic scaling.

Overall, this is a minor issue. If someone wants to tackle it, be my guest. All the artwork used to create the widget is in the 'artwork' directory. Desktop browsers are the most critical environment, which means there can't be odd visual artifacts that show up on desktop browsers. SVGs tend to not display consistently at small sizes across browsers.

How to empty recycle bin?

Hi there,

I found that I could not delete any files in Recycle Bin or empty the bin.

image

Is there any options for me to allow permanently deleting items in Recycle Bin or empty Recycle Bin?

Display details about each uploaded file in a separate UI

Currently, file upload details are limited to summary information in the upload queue and global cancellation controls. This issue is open to gauge interest in some sort of enhanced UI for tracking and cancelling individual uploads.

If someone wants to pick up the implementation of this feature, let me know. I have some ideas of what I'd like it to look like.

Error downloading multiple files

Hello. When downloading multiple files, it tells me that the zip file is not correct or contains errors.

It happens with PDF, sometimes with .docx

Unable to open the downloaded zip when selecting an entire folder or selecting multiple files.

How to get file object for file upload?

I didn't get the mechanism of how to upload a file. The function for oninitupload receives a fileinfo object.
However, this object does not contain the full path to the file on my local drive. I expected to receive something like a File-object as provided by a file input element like <input type="file" id="fileInput" />

Any help will be appreciated
Thanks

Cool project.

This looks great.
I would like to integrate with a backend file server on windows using c# and .net core framework.
I guess this can be done since I am comfortable with c# . Right ?
Thanks
giannis

Upload not working for files with size over chunksize

I'm using lighttpd 1.4.59 with PHP 7.4.27 on Gentoo Linux.

When I try to upload file with size over chunksize (in my case it seems to be 1MB), final stage of upload never happened. File is actually uploaded in full size, but stay with .tmp extension.

Removing .tmp and comparing file with original it's seems identical. If I put fixed chunksize to some very large number like 100GB for example make upload to work.

How to use without php?

I have a json to describing directories.
I want to use it without php server. (Only frontend)
how to emulate prepXhr with json file or object?

[
{n: "folder1",
icon:"cloudFolder",
url:"./folder1"
}
]

What are js-fileexplorer differences from WebDAV?

What are js-fileexplorer differences from WebDAV?

I am a lighttpd developer and lighttpd natively supports WebDAV with lighttpd mod_webdav. Files and folders can be manipulated in MacOS, Windows, Linux, and others using the client OS native file interfaces (as long as they support the WebDAV protocol)

I am curious where js-fileexplorer has advantages, and if these advantages are over the client OS WebDAV implementations or if there is something that lighttpd can do better on the server side.

Allow for list view

Hello, awesome project. Could you point me in the direction within the 1000s of beautiful lines of code where you set up the view for the files/folder grid? I'd like to take a crack at adding a list/detail styled view. Thanks!

Help following symlinks

Hi! I am trying to use this to access a symlinked folder and it says "failed to load folder. Invalid path specified" even though when I hover over the symlink folder it shows the folder's owner, group, etc, which makes me think I might be missing something silly, or maybe it's intended not to follow symlinks?!

Not sure if this is a bug or an intended feature so sorry if it's not appropriate to raise here as an issue!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.