/* * Copyright (c) Codiad & Kent Safranski (codiad.com), distributed * as-is and without warranty under the MIT License. See * [root]/license.txt for more. This information must remain intact. */ ( function( global, $ ) { let codiad = global.codiad; $( window ).load( function() { codiad.filemanager.init(); }); codiad.filemanager = { auto_reload: false, clipboard: '', controller: 'components/filemanager/controller.php', dialog: 'components/filemanager/dialog.php', file_preview_list: { audio: [ 'aac', 'aif', 'mp3', 'mp4', 'wav', 'ogg', ], files: [ 'exe', 'pdf', 'zip', 'tar', 'tar.gz', ], image: [ 'ico', 'icon', 'jpg', 'jpeg', 'png', 'gif', 'bmp', ], }, file_reader: null, files: [], node: { accept: function( e, i ) { return true; }, drag: function( e, i ) { }, drop: function( e, i ) { let _this = codiad.filemanager; let drag = i.helper[0]; let drop = e.target; $( drop ).removeClass( "drag_over" ); if( ! $( drop ).attr( "data-path" ) ) { drop = $( drop ).children( 'a' ); } console.log( drop ); console.log( drag ); let drop_path = $( drop ).attr( "data-path" ); let drag_path = $( drag ).children( "a" ).attr( "data-path" ); let path = drag_path; let newPath = `${drop_path}/` + path.split( "/" ).pop(); console.log( path, newPath ); _this.rename_node( path, newPath ); }, out: function( e, i ) { let drag = i.helper[0]; let drop = e.target; $( drop ).removeClass( "drag_over" ); }, over: function( e, i ) { let drag = i.helper[0]; let drop = e.target; $( drop ).addClass( "drag_over" ); }, start: function( e, i ) { let drag = i.helper[0]; $( drag ).addClass( "drag_start" ); $( drag ).children( 'a' ).removeClass( "a:hover" ); }, stop: function( e, i ) { let drag = i.helper[0]; $( drag ).removeClass( "drag_start" ); //$( drag ).removeClass( "hover" ); }, }, opened_folders: [], post_max_size: ( 1024*1024 ), preview_window: null, refresh_interval: null, selected: [], init: async function() { let _this = codiad.filemanager; /* Reload the page when saving auto reload preview */ amplify.subscribe( 'settings.save', async function() { let option = ( await codiad.settings.get_option( "codiad.filemanager.autoReloadPreview" ) == "true" ); if( option != codiad.filemanager.auto_reload ) { window.location.reload( true ); } }); /* Subscribe to know when a file become active. */ amplify.subscribe( 'active.onFocus', async function( path ) { let editor = codiad.editor.getActive(); if( _this.auto_reload && editor !== null ) { codiad.editor.getActive().addEventListener( "change", _this.refresh_preview ); } }); /* maybe we should have this calcualted as the file is being uploaded. this may allow for a more dynamic upload speed for faster connections and a more stable upload for slower connections. */ _this.calculate_upload_variables(); _this.node_listeners(); $( document ).on( 'dragenter', function( e ) { let d = e.originalEvent.dataTransfer; if( d && e.target.className !== "ace_scroller" ) { let files = d.files; let items = d.items; console.log( 'dragenter', e, e.target, files, items, items[0].webkitGetAsEntry() ); _this.upload_overlay_on(); } }); $( '.drop-overlay' ).on( 'drag dragstart dragend dragover dragenter dragleave drop', function( e ) { //console.log( 'drag dragstart dragend dragover dragenter dragleave drop', e ); }) .on( 'dragover dragenter', function( e ) { e.preventDefault(); e.stopPropagation(); //console.log( 'dragover dragenter', e ); }) .on( 'dragleave dragend drop dragdrop', function( e ) { //e.preventDefault(); //e.stopPropagation(); _this.upload_overlay_off(); }) .on( 'drop dragdrop', async function( e ) { e.preventDefault(); e.stopPropagation(); let d = e.originalEvent.dataTransfer; if( d ) { let files = []; let items = []; let file_list = d.files; let item_list = d.items; let total_items = item_list.length; if( item_list && item_list[0].webkitGetAsEntry ) { for( let i = 0;i < total_items;i++ ) { items.push( item_list[i].webkitGetAsEntry() ); } } console.log( files, items ); let project = $( '#file-manager a[data-type="root"]' ).attr( 'data-path' ); let path = await _this.open_file_selector( project, {type: "directory"}, 1, {} ); if( items.length ) { for( let i = items.length;i--; ) { let j = await _this.upload_load_files( items[i], path ); files = files.concat( j ); } } else { files = file_list; } for( let i = files.length;i--; ) { let status = await _this.upload( files[i] ); //console.log( status ); } console.log( 'drop', files, items ); } else { codiad.message.error( i18n( 'Your browser does not seem to support dragging and dropping files' ) ); } }); $( '.drop-overlay' ).on( 'click', _this.upload_overlay_off ); }, archive: function( path ) { let _this = this; $.get( _this.controller + '?action=archive&path=' + encodeURIComponent( path ), function( data ) { console.log( data ); let response = codiad.jsend.parse( data ); parent = path.split( '/' ); parent.pop(); _this.rescan( parent.join( '/' ) ); console.log( response ); }); }, calculate_upload_variables: async function() { let _this = codiad.filemanager; let result = await codiad.system.get_ini_setting( 'post_max_size' ); result = result.toLowerCase() console.log( result, result.includes( 'g' ), result.includes( 'm' ), result.includes( 'k' ) ); if( result.includes( 'g' ) ) { let integer = result.replace( /^\D+/g, '' ); console.log( integer, 1024*1024*1024*integer ); result = 1024*1024*1024*integer; } else if( result.includes( 'm' ) ) { let integer = result.replace( /^\D+/g, '' ); console.log( integer, 1024*1024*integer ); result = 1024*1024*integer; } else if( result.includes( 'k' ) ) { let integer = result.replace( /^\D+/g, '' ); console.log( integer, 1024*integer ); result = 1024*integer; } _this.post_max_size = result; console.log( _this.post_max_size ); }, context_menu_track_mouse: function( e ) { let _this = codiad.filemanager; let offset = $( '#context-menu' ).offset(); let bottom = offset.top + $( '#context-menu' ).outerHeight( true ) + 20; let left = offset.left - 20; let right = offset.left + $( '#context-menu' ).outerWidth( true ) + 20; let top = offset.top - 20; if( ( e.clientX > right || e.clientX < left ) || ( e.clientY > bottom || e.clientY < top ) ) { $( '#file-manager, #editor-region' ).off( 'mousemove', codiad.filemanager.context_menu_track_mouse ); $( '#context-menu, #editor-region' ).off( 'paste', codiad.editor.paste ); _this.hide_context_menu(); } }, ////////////////////////////////////////////////////////////////// // Copy to Clipboard ////////////////////////////////////////////////////////////////// copy_node: function( path ) { this.clipboard = path; codiad.message.success( i18n( 'Copied to Clipboard' ) ); }, create_node: function( path, type ) { codiad.modal.load( 250, this.dialog, { action: 'create', type: type, path: path }) .then( function( container ) { $( '#modal-content form' ) .on( 'submit', function( e ) { e.preventDefault(); e.stopPropagation(); let shortName = $( '#modal-content form input[name="object_name"]' ).val(); let path = $( '#modal-content form input[name="path"]' ).val(); let type = $( '#modal-content form input[name="type"]' ).val(); let createPath = path + '/' + shortName; $.get( codiad.filemanager.controller + '?action=create&path=' + encodeURIComponent( createPath ) + '&type=' + type, function( data ) { let createResponse = codiad.jsend.parse( data ); if( createResponse != 'error' ) { codiad.message.success( type.charAt( 0 ) .toUpperCase() + type.slice( 1 ) + ' Created' ); codiad.modal.unload(); if( type == 'file' ) { codiad.filemanager.openFile( createPath, true ); } codiad.filemanager.rescan( path ); /* Notify listeners. */ amplify.publish( 'filemanager.onCreate', { createPath: createPath, path: path, shortName: shortName, type: type }); } }); }); }); }, delete_node: function( path ) { let _this = this; codiad.modal.load( 400, this.dialog, { action: 'delete', path: path }) .then( function( container ) { $( '#modal-content form' ) .on( 'submit', function( e ) { e.preventDefault(); $.get( _this.controller + '?action=delete&path=' + encodeURIComponent( path ), function( data ) { let response = codiad.jsend.parse( data ); if( response != 'error' ) { let node = $( '#file-manager a[data-path="' + path + '"]' ); let parent_path = node.parent().parent().children( 'a' ).attr( 'data-path' ); node.parent( 'li' ).remove(); // Close any active files $( '#active-files a' ).each( function() { let curPath = $( this ).attr( 'data-path' ); console.log( curPath, curPath.indexOf( path ) ); if( curPath.indexOf( path ) == 0 ) { codiad.active.remove( curPath ); } }); /* Notify listeners. */ amplify.publish( 'filemanager.onDelete', { deletePath: path, path: parent_path }); } codiad.modal.unload(); }); }); }) }, delete_children_nodes: function( path ) { let _this = this; codiad.modal.load( 400, this.dialog, { action: 'delete', path: path }) .then( function() { $( '#modal-content form' ) .on( 'submit', function( e ) { e.preventDefault(); $.get( _this.controller + '?action=delete_children&path=' + encodeURIComponent( path ), function( data ) { console.log( data ); let response = codiad.jsend.parse( data ); if( response != 'error' ) { let node = $( '#file-manager a[data-path="' + path + '"]' ); let parent_path = node.parent().parent().prev().attr( 'data-path' ); _this.toggle_directory( node, [], [], true ); node.parent( 'li' ).children( 'span' ).remove(); // Close any active files $( '#active-files a' ).each( function() { let curPath = $( this ).attr( 'data-path' ); console.log( curPath, curPath.indexOf( path ) ); if( path.indexOf( curPath ) == 0 ) { codiad.active.remove( curPath ); } }); /* Notify listeners. */ amplify.publish( 'filemanager.onDelete', { path: path }); } codiad.modal.unload(); }); }); }); }, display_context_menu: function( e, path, type, name ) { let _this = this; let top = e.pageY; $( '#context-menu a, #context-menu hr' ).hide(); // Selective options switch ( type ) { case 'directory': $( '#context-menu .directory-only, #context-menu .non-root, #context-menu .both' ).show(); break; case 'file': $( '#context-menu .file-only, #context-menu .non-root, #context-menu .both' ).show(); break; case 'root': $( '#context-menu .directory-only, #context-menu .root-only' ).show(); $( '#context-menu .non-root' ).hide(); break; case 'editor': $( '#context-menu .editor-only' ).show(); break; } if( codiad.project.isAbsPath( $( '#file-manager a[data-type="root"]' ).attr( 'data-path' ) ) ) { $( '#context-menu .no-external' ).hide(); } else if( type == "editor" ) { $( '#context-menu .no-external' ).hide(); } else { $( '#context-menu .no-external' ).show(); } // Show menu if( top > $( window ).height() - $( '#context-menu' ).height() ) { top -= $( '#context-menu' ).height(); } if( top < 10 ) { top = 10; } let max = $( window ).height() - top - 10; $( '#context-menu' ) .css( { 'top': top + 'px', 'left': e.pageX + 'px', 'max-height': max + 'px' }) .fadeIn( 200 ) .attr( 'data-path', path ) .attr( 'data-type', type ) .attr( 'data-name', name ); // Show faded 'paste' if nothing in clipboard if( this.clipboard === '' ) { $( '#context-menu a[content="Paste"]' ) .addClass( 'disabled' ); } else { $( '#context-menu a[data-action="paste"]' ) .removeClass( 'disabled' ); } // Hide menu /** * make sure that the user has moved their mouse far enough * away from the context menu to warrant a close. */ $( '#file-manager, #editor-region' ).on( 'mousemove', codiad.filemanager.context_menu_track_mouse ); $( '#context-menu, #editor-region' ).on( 'paste', codiad.editor.paste ); $( '#context-menu, #editor-region' ).on( 'click', _this.hide_context_menu ); /* Notify listeners. */ amplify.publish( 'context-menu.onShow', { e: e, path: path, type: type }); // Hide on click $( '#context-menu a' ).on( 'click', _this.hide_context_menu ); }, download: function( path ) { let type = this.getType( path ); $( '#download' ) .attr( 'src', 'components/filemanager/download.php?path=' + encodeURIComponent( path ) + '&type=' + type ); }, get_extension: function( path ) { return path.split( '.' ).pop(); }, get_index: function( path, files ) { let _this = codiad.filemanager; if( ! files ) { files = _this.files; } return new Promise( async function( resolve, reject ) { let index = {}; let total = ( !!files ) ? files.length : 0; for( let i = 0;i < total;i++ ) { if( path == files[i].dirname ) { index = files[i]; break; } else { if( files[i].children !== undefined && files[i].children !== null ) { //console.log( path ); //console.log( files[i] ); index = await _this.get_index( path, files[i].children ); if( Object.keys( index ).length > 0 ) { break; } } } } resolve( index ); }); }, get_indexes: async function( path ) { let r = await $.get( this.controller + '?action=index&path=' + encodeURIComponent( path ) ); return r; }, get_opened_indexes: async function( files ) { let _this = codiad.filemanager; for( let i = files.length;i--; ) { files[i].name = files[i].path; if( files[i].type == "directory" ) { let existing_data = await _this.get_index( files[i].path ); //console.log( "opened?", existing_data, files[i] ); if( existing_data.open ) { files[i].open = true; let data = await _this.get_indexes( files[i].path ); let response = codiad.jsend.parse( data ); _this.set_children( files[i].path, files, response.index ); let children = await _this.get_opened_indexes( response.index ); } else { files[i].open = false; } } } return files; }, get_parent: function( path ) { let parent = path.split( '/' ); parent.pop(); return parent.join( '/' ); }, get_file_reader: function() { let _this = codiad.filemanager; if( ! _this.reader ) { _this.reader = new FileReader(); } return _this.reader; }, get_short_name: function( path ) { return path.split( '/' ).pop(); }, get_type: function( path ) { if( path.match( /\\/g ) ) { path = path.replace( '\\', '\\\\' ); } return $( '#file-manager a[data-path="' + path + '"]' ).attr( 'data-type' ); }, hide_context_menu: function() { $( '#context-menu' ).fadeOut( 200 ); $( '#file-manager a' ).removeClass( 'context-menu-active' ); /* Notify listeners. */ amplify.publish( 'context-menu.onHide' ); }, index: async function( path, rescan = false, node = null, filters = {}, callbacks = {} ) { path.replace( /\/$/, '' ); let _this = codiad.filemanager; let children = 0; let container = $( '' ); let files = []; let root = false; let total_saved = _this.files.length; let file = await _this.get_index( path ); rescan = !!rescan; if( node === null ) { node = $( '#file-manager a[data-path="' + path + '"]' ); } let parentNode = node.parent(); let span = node.prev(); if( node.attr( 'data-type' ) == "root" ) { node.droppable({ accept: _this.node.accept, drop: _this.node.drop, over: _this.node.over, out: _this.node.out }); } if( ! callbacks.directory ) { callbacks.directory = [_this.index_directory_callback]; } if( ! callbacks.file ) { callbacks.file = [_this.index_file_callback]; } node.addClass( 'loading' ); if( Object.keys( file ).length == 0 ) { children = file.children; } //console.log( file.children, file ) if( rescan || total_saved == 0 || ! children ) { let data = await _this.get_indexes( path ); //console.log( data ); let response = codiad.jsend.parse( data ); let result = []; if( response != 'error' ) { result = response.index; } files = result; } else { files = file.children; } files = await _this.get_opened_indexes( files ); if( total_saved == 0 ) { _this.files = files; } else { _this.set_children( path, _this.files, files ); } //console.log( _this.files, files ) _this.index_nodes( path, node, files, filters, callbacks, ); /* Notify listener */ amplify.publish( "filemanager.onIndex", { path: path, files: files }); return files; }, index_directory_callback: function( entry, container, i, files ) { let _this = codiad.filemanager; entry.children( 'a' ).droppable({ accept: _this.node.accept, drop: _this.node.drop, over: _this.node.over, out: _this.node.out, start: _this.node.start, stop: _this.node.stop, }); entry.draggable({ opacity: 0.85, revert: true, start: _this.node.start, stop: _this.node.stop, zIndex: 100, }); }, index_file_callback: function( entry, container, i, files ) { let _this = codiad.filemanager; entry.draggable({ opacity: 0.85, revert: true, start: _this.node.start, stop: _this.node.stop, zIndex: 100, }); }, index_nodes: function( path, node, files, filters, callbacks ) { let _this = codiad.filemanager; let container = $( '' ); let total_files = files.length; let parent = node.parent(); let ul = parent.children( 'ul' ); for( let i = 0;i < total_files;i++ ) { let v = files[i]; let ext = ''; let name = ''; let node_class = 'none'; let entry = $( "
  • " ); let span = $( "" ); let link = $( "" ); let type = null; entry.append( span, link ); if( v.type == "file" ) { if( filters.type == "directory" ) { continue; } ext = "ext-" + v.extension; name = v.basename; type = 'file'; link.addClass( ext ); } else if( v.type == "directory" ) { if( filters.type == "file" ) { continue; } if( v.children ) { if( v.open ) { node_class = "minus"; console.log( "loading children", v.path, link, v.children, filters, callbacks, link.parent() ); _this.index_nodes( v.path, link, v.children, filters, callbacks ); } else { node_class = "plus"; } } name = v.basename; type = 'directory'; } //console.log( v.path, v.type ); span.addClass( node_class ); link.addClass( type ); link.attr( "data-type", type ); link.attr( "data-path", v.path ); link.text( name ); container.append( entry ); if( typeof callbacks == "function" ) { callbacks( entry, container, v, files ); } else if( Array.isArray( callbacks ) ) { let total_callbacks = callbacks.length; for( let j = 0;j < total_callbacks;j++ ) { callbacks[j]( entry, container, v, files ); } } else if( callbacks === Object( callbacks ) ) { if( typeof callbacks[v.type] == "function" ) { callbacks[v.type]( entry, container, v, files ); } else if( Array.isArray( callbacks[v.type] ) ) { let total_callbacks = callbacks[v.type].length; for( let j = 0;j < total_callbacks;j++ ) { callbacks[v.type][j]( entry, container, v, files ); } } } } if( ul.length ) { ul.replaceWith( container ); } else { container.insertAfter( node ); } node.removeClass( 'loading' ); }, is_child: function( parent, child ) { if( child === parent ) { return false; } let parentTokens = parent.split( '/' ).filter( i => i.length ); return parentTokens.every( ( t, i ) => child.split( '/' )[i] === t ) }, node_listeners: function() { let _this = this; $( '#file-manager' ) .on( 'click', 'a', function() { // Open or Expand if( codiad.editor.settings.fileManagerTrigger ) { if( $( this ).hasClass( 'directory' ) ) { _this.toggle_directory( $( this ) ); } else { _this.open_file( $( this ).attr( 'data-path' ) ); } } }) .on( 'click', 'span', function() { // Open or Expand if( $( this ).parent().children( "a" ).attr( 'data-type' ) == 'directory' ) { _this.toggle_directory( $( this ).parent().children( "a" ) ); } else { _this.open_file( $( this ).parent().children( "a" ).attr( 'data-path' ) ); } }) .on( "contextmenu", 'a', function( e ) { // Context Menu e.preventDefault(); _this.display_context_menu( e, $( this ).attr( 'data-path' ), $( this ).attr( 'data-type' ), $( this ).html() ); $( this ).addClass( 'context-menu-active' ); }) .on( 'dblclick', 'a', function() { // Open or Expand if( ! codiad.editor.settings.fileManagerTrigger ) { if( $( this ).hasClass( 'directory' ) ) { _this.toggle_directory( $( this ) ); } else { _this.open_file( $( this ).attr( 'data-path' ) ); } } }) .on( 'selectstart', false ); }, open_file: function( path, focus = true ) { /* Notify listeners. */ amplify.publish( 'filemanager.onFileWillOpen', { path: path }); let _this = codiad.filemanager; let node = $( '#file-manager a[data-path="' + path + '"]' ); let ext = _this.get_extension( path ); let preview = []; $.each( _this.file_preview_list, function( id, value ) { preview.concat( value ); }); console.log( ext, preview ); if( $.inArray( ext.toLowerCase(), preview ) < 0 ) { node.addClass( 'loading' ); $.get( _this.controller + '?action=open&path=' + encodeURIComponent( path ), function( data ) { let openResponse = codiad.jsend.parse( data ); if( openResponse != 'error' ) { node.removeClass( 'loading' ); codiad.active.open( path, openResponse.content, openResponse.mtime, false, focus, openResponse.read_only ); } }); } else { if( ! codiad.project.isAbsPath( path ) ) { let download = []; download.concat( files ); if( $.inArray( ext.toLowerCase(), download ) < 0 ) { _this.download( path ); } else { _this.open_in_modal( path ); } } else { codiad.message.error( i18n( 'Unable to open file in Browser while using absolute path.' ) ); } } }, open_file_selector: function( path, filters = {}, limit = 1, callbacks ) { let _this = this; return new Promise( function( resolve, reject ) { codiad.modal.load( 300, _this.dialog, { action: 'selector', } ) .then( async function( container ) { let _this = codiad.filemanager; let div = null; let select = $( '' ); let cancel = $( '' ); if( ! path ) { path = codiad.project.getCurrent(); } if( Object.keys( filters ).length == 0 ) { filters = { type: 'directory', } } div = $( '#file-manager a[data-path="' + path + '"]' ).parent().parent().parent().clone(); let node = $( div ).find( 'a[data-path="' + path + '"]' ); console.log( div, node ); node.off(); node.children().off(); _this.selector_listeners( div, limit, filters, callbacks ); let result = await _this.index( path, false, node, filters, callbacks, ); console.log( result ); container.html( div ); container.append( select ); container.append( cancel ); select.on( 'click', function( e ) { codiad.modal.unload(); resolve( _this.selected ); }); cancel.on( 'click', function( e ) { codiad.modal.unload(); reject({ status: "error", message: "User canceled action." }); }); }) .catch( reject ); }); }, open_in_modal: function( path ) { let type = ""; let ext = this.getExtension( path ).toLowerCase(); if( this.file_preview_list.images.includes( ext ) ) { type = 'music_preview'; } else if( this.file_preview_list.images.includes( ext ) ) { type = 'preview'; } codiad.modal.load( 250, this.dialog, { action: type, path: path }); }, open_rename: function( path ) { let _this = codiad.filemanager; let shortName = this.get_short_name( path ); let type = this.getType( path ); codiad.modal.load( 250, this.dialog, { action: 'rename', path: path, short_name: shortName, type: type }) .then( function( content ) { $( content ).find( 'form' ) .on( 'submit', function( e ) { e.preventDefault(); let new_name = $( '#modal-content form input[name="object_name"]' ).val(); let arr = path.split( '/' ); let temp = new Array(); for( i = 0; i < arr.length - 1; i++ ) { temp.push( arr[i] ) } let new_path = temp.join( '/' ) + '/' + new_name; _this.rename_node( path, new_path ); codiad.modal.unload(); }); }); }, paste_node: function( path ) { let _this = this; let replace = false; let clipboard = this.clipboard; console.log( path, clipboard ) if( clipboard == '' ) { codiad.message.error( i18n( 'Nothing in Your Clipboard' ) ); } else if( path == clipboard ) { codiad.message.error( i18n( 'Cannot Paste Directory Into Itself' ) ); } else { let short_name = _this.get_short_name( clipboard ); let new_path = path + '/' + short_name let existing_node = $( '#file-manager a[data-path="' + new_path + '"]' ); console.log( existing_node ); if( existing_node.length ) { // Confirm replace? codiad.modal.load( 400, this.dialog, { action: 'replace', path: new_path }) .then( function( container ) { $( '#modal-content form' ) .on( 'submit', function( e ) { e.preventDefault(); codiad.modal.unload(); if( $( '#modal-content form select[name="or_action"]' ).val() == 1 ) { replace = true; } $.ajax({ type: 'POST', url: _this.controller + '?action=copy', data: { path: clipboard, destination: new_path, replace: replace, }, success: async function( data ) { amplify.publish( 'filemanager.onPaste', { path: path, shortName: shortName, duplicate: duplicate }); let dir = await _this.is_dir( new_path ); if( dir ) { _this.rescan( new_path ); } else { _this.rescan( _this.get_parent( new_path ) ); } if( path !== new_path ) { _this.rescan( path ); } }, error: function( data ) { console.log( data ); }, }); }); }); } else { $.ajax({ type: 'POST', url: _this.controller + '?action=copy', data: { path: clipboard, destination: new_path, replace: replace, }, success: async function( data ) { amplify.publish( 'filemanager.onPaste', { path: path, shortName: shortName, duplicate: duplicate }); let dir = await _this.is_dir( new_path ); if( dir ) { _this.rescan( new_path ); } else { _this.rescan( _this.get_parent( new_path ) ); } if( path !== new_path ) { _this.rescan( path ); } }, error: function( data ) { console.log( data ); }, }); } } }, refresh_preview: function( event ) { let _this = codiad.filemanager; /** * When reloading after every change, we encounter performance issues * in the editor. Therefore, we implement the same logic as the * auto_save module where we only reload after the user has finished * changing their document. */ if( _this.refresh_interval !== null ) { clearTimeout( _this.refresh_interval ); _this.refresh_interval = null; } _this.refresh_interval = setTimeout( function() { if( _this.preview_window == null ) { return; } try { if( ( typeof _this.preview_window.location.reload ) == "undefined" ) { _this.preview_window = null; codiad.editor.getActive().removeEventListener( "change", _this.refresh_preview ); return; } _this.preview_window.location.reload( true ); } catch ( e ) { console.log( e ); codiad.message.error( 'Please close your previously opened preview window.' ); _this.preview_window = null; codiad.editor.getActive().removeEventListener( "change", _this.refresh_preview ); } }, 1000 ); }, rename: function( path, new_path ) { let _this = codiad.filemanager; return new Promise( function( resolve, reject ) { $.ajax({ type: 'POST', url: _this.controller + '?action=rename', data: { path: path, destination: new_path }, success: function( data ) { resolve( data ); }, error: function( data ) { reject( data ); }, }); }); }, rename_node: async function( path, new_path ) { let short_name = this.get_short_name( path ); let type = this.getType( path ); let _this = this; let project = codiad.project.getCurrent(); let arr = path.split( '/' ); let message = "Successfully Renamed." let new_parent = new_path.split( '/' ); let parent = path.split( '/' ); let temp = []; parent.pop(); new_parent.pop(); for( i = 0; i < arr.length - 1; i++ ) { temp.push( arr[i] ) } let result = codiad.jsend.parse( await _this.rename( path, new_path ) ); if( result != 'error' ) { if( type !== undefined ) { let node = $( '#file-manager a[data-path="' + path + '"]' ); node.attr( 'data-path', new_path ).html( new_path.split( "/" ).pop() ); message = type.charAt( 0 ).toUpperCase() + type.slice( 1 ) + ' Renamed' codiad.message.success( message ); // Change icons for file let current_class = 'ext-' + _this.get_extension( path ); let new_class = 'ext-' + _this.get_extension( new_path ); $( '#file-manager a[data-path="' + new_path + '"]' ) .removeClass( current_class ) .addClass( new_class ); codiad.active.rename( path, new_path ); codiad.filemanager.rescan( parent.join( '/' ) ); if( parent !== new_parent ) { console.log( parent, new_parent ); codiad.filemanager.rescan( new_parent.join( '/' ) ); } /* Notify listeners. */ amplify.publish( 'filemanager.onRename', { path: path, newPath: new_path, project: project }); } } }, preview: function( path ) { let _this = this; $.ajax({ type: 'GET', url: _this.controller + '?action=preview&path=' + encodeURIComponent( path ), success: function( data ) { console.log( data ); let r = JSON.parse( data ); if( r.status === "success" ) { _this.preview = window.open( r.data, '_newtab' ); let editor = codiad.editor.getActive(); if( _this.auto_reload && editor !== null ) { editor.addEventListener( "change", _this.refresh_preview ); } } else { codiad.message.error( i18n( r.message ) ); } }, error: function( data ) { codiad.message.error( i18n( r.message ) ); }, }); }, rescan: function( path ) { let _this = codiad.filemanager; return _this.index( path, true ); }, save_file: function( path, data, display_messages = true ) { return new Promise( function( resolve, reject ) { let _this = codiad.filemanager; $.ajax({ type: 'POST', url: _this.controller + '?action=modify', data: { path: path, data: JSON.stringify( data ), }, success: function( data ) { console.log( data ); let r = JSON.parse( data ); if( r.status === "success" ) { if( display_messages === true ) { codiad.message.success( i18n( 'File saved' ) ); } resolve( r ); } else if( r.message == 'Client is out of sync' ) { let reload = confirm( "Server has a more updated copy of the file. Would " + "you like to refresh the contents ? Pressing no will " + "cause your changes to override the server's copy upon " + "next save." ); if( reload ) { codiad.active.close( path ); codiad.active.removeDraft( path ); _this.openFile( path ); } else { let session = codiad.editor.getActive().getSession(); session.serverMTime = null; session.untainted = null; } resolve( r ); } else { codiad.message.error( i18n( r.message ) ); reject( data ); } }, error: function( data ) { codiad.message.error( i18n( 'File could not be saved' ) ); reject( data ); }, }); }); }, search: async function( path ) { let _this = this; let container = await codiad.modal.load( 500, this.dialog, { action: 'search', path: path }); codiad.modal.hideOverlay(); $( '#modal-content form' ) .on( 'submit', function( e ) { $( '#filemanager-search-processing' ).show(); e.preventDefault(); searchString = $( '#modal-content form input[name="search_string"]' ).val(); fileExtensions = $( '#modal-content form input[name="search_file_type"]' ).val(); searchFileType = $.trim( fileExtensions ); if( searchFileType != '' ) { //season the string to use in find command searchFileType = "\\(" + searchFileType.replace( /\s+/g, "\\|" ) + "\\)"; } searchType = $( '#modal-content form select[name="search_type"]' ) .val(); let options = { filetype: fileExtensions, }; $.post( _this.controller + '?action=search', { path: path, query: searchString, options: JSON.stringify( options ) }, function( data ) { let searchResponse = codiad.jsend.parse( data ); let results = ''; console.log( data ); console.log( searchResponse ); if( searchResponse != 'error' ) { $.each( searchResponse.index, function( key, val ) { // Cleanup file format if( val['file'].substr( -1 ) == '/' ) { val['file'] = val['file'].substr( 0, str.length - 1 ); } val['file'] = val['file'].replace( '//', '/' ); // Add result results += '
    Line ' + val['line'] + ': ' + val['file'] + '
    '; }); $( '#filemanager-search-results' ) .slideDown() .html( results ); } else { $( '#filemanager-search-results' ) .slideUp(); } $( '#filemanager-search-processing' ) .hide(); }); }); }, set_children: function( path, files, children ) { let _this = this; let index = {}; let total = ( !!files ) ? files.length : 0; for( let i = 0;i < total;i++ ) { if( path == files[i].dirname ) { files[i].children = children; index = files[i]; break; } else { if( files[i].children !== undefined ) { index = _this.set_children( path, files[i].children, children ); if( Object.keys( index ).length > 0 ) { break; } } } } return index; }, selector_listeners: function( node, limit, filters, callbacks ) { let _this = codiad.filemanager; $( node ) .on( 'click', 'a', async function( e ) { let i = $( e.target ); // Select or Expand if( codiad.editor.settings.fileManagerTrigger ) { _this.toggle_directory( i, filters, callbacks ); } else { _this.toggle_select_node( $( e.target ), limit ); } }) .on( 'click', 'span', async function( e ) { let i = $( e.target ).parent().children( 'a' ); _this.toggle_directory( i, {type: 'directory'} ); }) .on( 'dblclick', 'a', async function( e ) { let i = $( e.target ); if( ! codiad.editor.settings.fileManagerTrigger ) { _this.toggle_directory( i, {type: 'directory'} ); } else { _this.toggle_select_node( i, limit ); } }) .on( 'selectstart', false ); }, set_index: function( path, files, data ) { let _this = codiad.filemanager; let index = false; let total = ( !!files ) ? files.length : 0; for( let i = 0;i < total;i++ ) { if( path == files[i].dirname ) { files[i] = data; index = files[i]; break; } else { if( files[i].children !== undefined ) { index = _this.set_index( path, files[i].children, data ); if( index ) { break; } } } } return index; }, toggle_directory: async function( node, filters, callbacks, open ) { let _this = codiad.filemanager; node.addClass( "loading" ); let path = node.attr( "data-path" ); let i = await _this.get_index( path, _this.files ); let span = node.parent().children( 'span' ); let link = node.parent().children( 'a' ); console.log( i ); if( Object.keys( i ).length == 0 ) { let result = await _this.index( path ); i = { open: false, } } if( i.open || open ) { node.parent().children( 'ul' ) .slideUp( 300, function() { $( this ).remove(); span.removeClass( 'minus' ); node.removeClass( 'open' ); span.addClass( 'plus' ); if( typeof close_callback == "function" ) { close_callback(); } }); if( typeof open_callback == "function" ) { close_callback( node ); } } else { span.removeClass( 'plus' ); span.addClass( 'minus' ); link.addClass( 'open' ); _this.index( path ); } i.open = !i.open _this.set_index( path, _this.files, i ); console.log( i, await _this.get_index( path, _this.files ) ); node.removeClass( "loading" ); }, toggle_select_node: function( node, limit = 0 ) { let _this = codiad.filemanager; let selected = false; let path = node.attr( 'data-path' ); let i = 1; for( i = _this.selected.length;i--; ) { if( _this.selected[i] == path ) { selected = true; break; } } if( limit > 0 && _this.selected.length >= limit ) { for( i = _this.selected.length;i--; ) { $( `[data-path='${_this.selected[i]}']` ).css( "background", "" ); } _this.selected = []; _this.selected.push( path ); } else { if( selected ) { _this.selected.splice( i, 1 ); } else { _this.selected.push( path ); } } console.log( path, _this.selected ); }, unarchive: function( path ) { let _this = this; $.get( _this.controller + '?action=unarchive&path=' + encodeURIComponent( path ), function( data ) { console.log( data ); let response = codiad.jsend.parse( data ); console.log( response ); parent = path.split( '/' ); parent.pop(); _this.rescan( parent.join( '/' ) ); }); }, upload: function( file ) { let _this = codiad.filemanager; let blob_size = 1024*1024*4; let total_size = file.size; let upload_status = null; let total_blobs = 0; let current = 0; let index = 0; console.log( file, file.size ); return new Promise( function( resolve, reject ) { if( total_size < blob_size ) { blob_size = total_size; total_blobs = 1; } else { total_blobs = ( Math.round( ( total_size / blob_size ) ) ); } console.log( blob_size, _this.uploads.cache, total_size ); let timeout = setInterval( function() { if( ( index == total_blobs || current == total_size ) && _this.uploads.cache.length == 0 ) { _this.upload_stitch( file.path ); clearTimeout( timeout ); resolve( true ); } else if( ( index != total_blobs && current != total_size ) && _this.uploads.cache.length < _this.uploads.max ) { console.log( "Adding new blob: ", ( index + 1 ) + "/" + total_blobs, current + blob_size + "/" + total_size ); let reader = new FileReader(); let blob = file.slice( current, current + blob_size ); reader.onload = async function( e ) { current = current + blob_size; _this.uploads.cache.push({ blob: e.target.result, path: file.path, status: "queued", index: index, }); index++; _this.upload_clear_cache(); } reader.readAsDataURL( blob ); } else { _this.upload_clear_cache(); } }, 100 ); }); }, uploads: { cache: [], max: 4, }, upload_blob: function( blob, path, index, o ) { return new Promise( function( resolve, reject ) { let _this = codiad.filemanager; let form = new FormData(); form.append( 'path', path ); form.append( 'index', index ); form.append( 'data', blob ); $.ajax({ type: 'POST', url: _this.controller + "?action=upload_blob", data: form, processData: false, contentType: false, success: function( data ) { console.log( data ); let d = JSON.parse( data ); if( d.status == "success" ) { o.status = "success"; resolve( data ); } else { o.status = "error"; reject( data ); } }, error: function( data ) { o.status = "error"; reject( data ); }, }); }); }, upload_clear_cache: function() { let _this = codiad.filemanager; for( let i = _this.uploads.cache.length;i--; ) { if( _this.uploads.cache[i].status == "success" ) { _this.uploads.cache.splice( i, 1 ); } else if( _this.uploads.cache[i].status == "error" ) { _this.uploads.cache[i].status = "retrying"; _this.upload_blob( _this.uploads.cache[i].blob, _this.uploads.cache[i].path, _this.uploads.cache[i].index, _this.uploads.cache[i] ); } else if( _this.uploads.cache[i].status == "queued" ) { _this.uploads.cache[i].status = "uploading"; _this.upload_blob( _this.uploads.cache[i].blob, _this.uploads.cache[i].path, _this.uploads.cache[i].index, _this.uploads.cache[i] ); } } }, upload_load_files: async function( item, path, files = null ) { let _this = codiad.filemanager; if( files === null ) { files = []; } return new Promise( function( resolve, reject ) { if( item.isFile ) { // Get file item.file( function( file ) { if( ( `${path}` ).charAt( path.length - 1 ) != "/" ) { path += "/"; } file.path = path + file.name; files.push( file ); resolve( files ); }); } else if( item.isDirectory ) { // Get folder contents let dirReader = item.createReader(); dirReader.readEntries( async function( entries ) { if( ( `${path}` ).charAt( path.length - 1 ) != "/" ) { path += "/"; } let total = entries.length; for( let i = 0;i < total; i++ ) { let children = await _this.upload_load_files( entries[i], path + item.name + "/", files ); files.concat( children ); } resolve( files ); }, reject ); } }); }, upload_overlay_off: function() { $( '.drop-overlay' ).css( 'display', 'none' ); }, upload_overlay_on: function( e ) { $( '.drop-overlay' ).css( 'display', 'block' ); }, upload_stitch: function( path ) { let _this = codiad.filemanager; let form = new FormData(); form.append( 'path', path ); $.ajax({ type: 'POST', url: _this.controller + "?action=upload_stitch", data: form, processData: false, contentType: false, success: function( data ) { console.log( data ); parent = path.split( '/' ); parent.pop(); parent.pop(); console.log( path, parent.join( '/' ) ); _this.rescan( parent.join( '/' ) ); }, error: function( data ) { console.log( data ); }, }); }, upload_to_node: function( path ) { let _this = codiad.filemanager; codiad.modal.load( 500, this.dialog, { action: 'upload', } ) .then( function( container ) { let text = $( '

    ' ); let input = $( '' ); text.html( `

    Drag and drop files or folders anywhere or click here to upload a file!

    ` ); input.on( 'change', function( e ) { console.log( e ); let items = e.target.files; _this.upload( items, path ); }); text.on( 'click', function( e ) { input.click(); }); container.html( '' ); container.append( text ); container.append( input ); }); }, //Compatibility functions //these may be needed more after updating the new functions to newer standards copyNode: function( path ) {return this.copy_node( path )}, createNode: function( path, type ) {return this.create_node( path, type )}, deleteNode: function( path ) {return this.delete_node( path )}, getExtension: function( path ) {return this.get_extension( path )}, getShortName: function( path ) {return this.get_short_name( path )}, getType: function( path ) {return this.get_type( path )}, openFile: function( path ) {return this.open_file( path )}, openInBrowser: function( path ) {return this.preview( path )}, pasteNode: function( path ) {return this.paste_node( path )}, renameNode: function( path ) {return this.rename_node( path )}, saveFile: function( path, data, display_messages ) {return this.save_file( path, data, display_messages )}, uploadToNode: function( path ) {return this.upload_to_node( path )}, }; })( this, jQuery );