/** * IFM constructor * * @param object params - object with some configuration values, currently you only can set the api url */ function IFM( params ) { var self = this; // reference to ourself, because "this" does not work within callbacks // set the backend for the application params = params || {}; self.api = params.api || window.location.pathname; this.editor = null; // global ace editor this.fileChanged = false; // flag for check if file was changed already this.currentDir = ""; // this is the global variable for the current directory; it is used for AJAX requests this.rootElement = ""; /** * Shows a bootstrap modal * * @param string content - content of the modal * @param object options - options for the modal */ this.showModal = function( content, options = {} ) { var modal = $( document.createElement( 'div' ) ) .addClass( "modal fade" ) .attr( 'id', 'ifmmodal' ) .attr( 'role', 'dialog' ); var modalDialog = $( document.createElement( 'div' ) ) .addClass( "modal-dialog" ) .attr( 'role', 'document' ); if( options.large == true ) modalDialog.addClass( 'modal-lg' ); var modalContent = $(document.createElement('div')) .addClass("modal-content") .append( content ); modalDialog.append( modalContent ); modal.append( modalDialog ); $( document.body ).append( modal ); modal.on('hide.bs.modal', function () { $(this).remove(); }); modal.on('shown.bs.modal', function () { var formElements = $(this).find('input, button'); if( formElements.length > 0 ) { formElements.first().focus(); } }); modal.modal('show'); }; /** * Hides a bootstrap modal */ this.hideModal = function() { $('#ifmmodal').modal('hide'); }; /** * Reloads the file table */ this.refreshFileTable = function () { var id = self.generateGuid(); self.task_add( "Refresh", id ); $.ajax({ url: self.api, type: "POST", data: { api: "getFiles", dir: self.currentDir }, dataType: "json", success: self.rebuildFileTable, error: function( response ) { self.showMessage( "General error occured: No or broken response", "e" ); }, complete: function() { self.task_done( id ); } }); }; /** * Rebuilds the file table with fetched items * * @param object data - object with items */ this.rebuildFileTable = function( data ) { data.forEach( function( item ) { item.guid = self.generateGuid(); item.linkname = ( item.name == ".." ) ? "[ up ]" : item.name; item.download = {}; item.download.name = ( item.name == ".." ) ? "." : item.name; item.download.allowed = self.config.download; item.download.currentDir = self.currentDir; if( ! self.config.chmod ) item.readonly = "readonly"; if( self.config.edit || self.config.rename || self.config.delete || self.config.extract || self.config.copymove ) { item.ftbuttons = true; item.button = []; } if( item.type == "dir" ) { item.download.action = "zipnload"; item.download.icon = "icon icon-download-cloud"; item.rowclasses = "isDir"; } else { item.download.action = "download"; item.download.icon = "icon icon-download"; if( item.icon.indexOf( 'file-image' ) !== -1 && self.config.isDocroot ) item.tooltip = 'data-toggle="tooltip" title=""'; if( item.name.toLowerCase().substr(-4) == ".zip" ) item.eaction = "extract"; else item.eaction = "edit"; if( self.config.edit && item.name.toLowerCase().substr(-4) != ".zip" ) item.button.push({ action: "edit", icon: "icon icon-pencil", title: "edit" }); else item.button.push({ action: "extract", icon: "icon icon-archive", title: "extract" }); } if( ! self.inArray( item.name, [".", ".."] ) ) { if( self.config.copymove ) item.button.push({ action: "copymove", icon: "icon icon-folder-open-empty", title: "copy/move" }); if( self.config.rename ) item.button.push({ action: "rename", icon: "icon icon-terminal", title: "rename" }); if( self.config.delete ) item.button.push({ action: "delete", icon: "icon icon-trash", title: "delete" }); } }); var newTBody = Mustache.render( self.templates.filetable, { items: data, config: self.config } ); $( "#filetable tbody" ).remove(); $( "#filetable" ).append( $(newTBody) ); $( '.clickable-row' ).click( function( event ) { if( event.ctrlKey ) { $( this ).toggleClass( 'selectedItem' ); } }); $( 'a[data-toggle="tooltip"]' ).tooltip({ animated: 'fade', placement: 'right', html: true }); $( 'a.ifmitem' ).each( function() { if( $(this).data( "type" ) == "dir" ) { $(this).on( 'click', function( e ) { e.stopPropagation(); self.changeDirectory( $(this).parent().parent().data( 'filename' ) ); return false; }); } else { if( self.config.isDocroot ) $(this).attr( "href", self.pathCombine( self.currentDir, $(this).parent().parent().data( 'filename' ) ) ); else $(this).on( 'click', function() { $( '#d_' + this.id ).submit(); return false; }); } }); $( 'a[name="start_download"]' ).on( 'click', function(e) { e.stopPropagation(); $( '#d_' + $(this).data( 'guid' ) ).submit(); return false; }); $( 'input[name="newpermissions"]' ).on( 'keypress', function( e ) { if( e.key == "Enter" ) { e.stopPropagation(); self.changePermissions( $( this ).data( 'filename' ), $( this ).val() ); return false; } }); $( 'a[name^="do-"]' ).on( 'click', function() { var action = this.name.substr( this.name.indexOf( '-' ) + 1 ); switch( action ) { case "rename": self.showRenameFileDialog( $(this).data( 'name' ) ); break; case "extract": self.showExtractFileDialog( $(this).data( 'name' ) ); break; case "edit": self.editFile( $(this).data( 'name' ) ); break; case "delete": self.showDeleteFileDialog( $(this).data( 'name' ) ); break; case "copymove": self.showCopyMoveDialog( $(this).data( 'name' ) ); break; } }); }; /** * Changes the current directory * * @param string newdir - target directory * @param object options - options for changing the directory */ this.changeDirectory = function( newdir, options={} ) { config = { absolute: false, pushState: true }; jQuery.extend( config, options ); if( ! config.absolute ) newdir = self.pathCombine( self.currentDir, newdir ); $.ajax({ url: self.api, type: "POST", data: ({ api: "getRealpath", dir: newdir }), dataType: "json", success: function( data ) { self.currentDir = data.realpath; self.refreshFileTable(); $( "#currentDir" ).val( self.currentDir ); if( config.pushState ) history.pushState( { dir: self.currentDir }, self.currentDir, "#"+self.currentDir ); }, error: function() { self.showMessage( "General error occured: No or broken response", "e" ); } }); }; /** * Shows a file, either a new file or an existing */ this.showFileDialog = function () { var filename = arguments.length > 0 ? arguments[0] : "newfile.txt"; var content = arguments.length > 1 ? arguments[1] : ""; self.showModal( Mustache.render( self.templates.file, { filename: filename } ), { large: true } ); var form = $('#formFile'); form.find('input[name="filename"]').on( 'keypress', self.preventEnter ); form.find('#buttonSave').on( 'click', function() { self.saveFile( form.find('input[name=filename]').val(), self.editor.getValue() ); self.hideModal(); return false; }); form.find('#buttonSaveNotClose').on( 'click', function() { self.saveFile( form.find('input[name=filename]').val(), self.editor.getValue() ); return false; }); form.find('#buttonClose').on( 'click', function() { self.hideModal(); return false; }); form.find('#editoroptions').popover({ html: true, title: function() { return $('#editoroptions-head').html(); }, content: function() { var content = $('#editoroptions-content').clone() var aceSession = self.editor.getSession(); content.removeClass( 'hide' ); content.find( '#editor-wordwrap' ) .prop( 'checked', ( aceSession.getOption( 'wrap' ) == 'off' ? false : true ) ) .on( 'change', function() { self.editor.setOption( 'wrap', $( this ).is( ':checked' ) ); }); content.find( '#editor-softtabs' ) .prop( 'checked', aceSession.getOption( 'useSoftTabs' ) ) .on( 'change', function() { self.editor.setOption( 'useSoftTabs', $( this ).is( ':checked' ) ); }); content.find( '#editor-tabsize' ) .val( aceSession.getOption( 'tabSize' ) ) .on( 'keydown', function( e ) { if( e.key == 'Enter' ) { self.editor.setOption( 'tabSize', $( this ).val() ); } }); return content; } }); form.on( 'remove', function () { self.editor = null; self.fileChanged = false; }); // Start ACE self.editor = ace.edit("content"); self.editor.$blockScrolling = 'Infinity'; self.editor.getSession().setValue(content); self.editor.focus(); self.editor.on("change", function() { self.fileChanged = true; }); // word wrap checkbox $('#aceWordWrap').on( 'change', function (event) { self.editor.getSession().setUseWrapMode( $(this).is(':checked') ); }); }; /** * Saves a file */ this.saveFile = function( filename, content ) { $.ajax({ url: self.api, type: "POST", data: ({ api: "saveFile", dir: self.currentDir, filename: filename, content: content }), dataType: "json", success: function( data ) { if( data.status == "OK" ) { self.showMessage( "File successfully edited/created.", "s" ); self.refreshFileTable(); } else self.showMessage( "File could not be edited/created:" + data.message, "e" ); }, error: function() { self.showMessage( "General error occured", "e" ); } }); self.fileChanged = false; }; /** * Edit a file * * @params string name - name of the file */ this.editFile = function( name ) { $.ajax({ url: self.api, type: "POST", dataType: "json", data: ({ api: "getContent", dir: self.currentDir, filename: name }), success: function( data ) { if( data.status == "OK" && data.data.content != null ) { self.showFileDialog( data.data.filename, data.data.content ); } else if( data.status == "OK" && data.data.content == null ) { self.showMessage( "The content of this file cannot be fetched.", "e" ); } else self.showMessage( "Error: "+data.message, "e" ); }, error: function() { self.showMessage( "This file can not be displayed or edited.", "e" ); } }); }; /** * Shows the create directory dialog */ this.showCreateDirDialog = function() { self.showModal( self.templates.createdir ); var form = $( '#formCreateDir' ); form.find( 'input[name=dirname]' ).on( 'keypress', self.preventEnter ); form.find( '#buttonSave' ).on( 'click', function() { self.createDir( form.find( 'input[name=dirname] ').val() ); self.hideModal(); return false; }); form.find( '#buttonCancel' ).on( 'click', function() { self.hideModal(); return false; }); }; /** * Create a directory */ this.createDir = function( dirname ) { $.ajax({ url: self.api, type: "POST", data: ({ api: "createDir", dir: self.currentDir, dirname: dirname }), dataType: "json", success: function( data ){ if( data.status == "OK" ) { self.showMessage( "Directory sucessfully created.", "s" ); self.refreshFileTable(); } else { self.showMessage( "Directory could not be created: "+data.message, "e" ); } }, error: function() { self.showMessage( "General error occured.", "e" ); } }); }; /** * Shows the delete file dialog * * @param string name - name of the file */ this.showDeleteFileDialog = function( filename ) { self.showModal( Mustache.render( self.templates.deletefile, { filename: name } ) ); var form = $( '#formDeleteFile' ); form.find( '#buttonYes' ).on( 'click', function() { self.deleteFile( self.JSEncode( filename ) ); self.hideModal(); return false; }); form.find( '#buttonNo' ).on( 'click', function() { self.hideModal(); return false; }); }; /** * Deletes a file * * @params string name - name of the file */ this.deleteFile = function( filename ) { $.ajax({ url: self.api, type: "POST", data: ({ api: "delete", dir: self.currentDir, filename: filename }), dataType: "json", success: function(data) { if(data.status == "OK") { self.showMessage("File successfully deleted", "s"); self.refreshFileTable(); } else self.showMessage("File could not be deleted", "e"); }, error: function() { self.showMessage("General error occured", "e"); } }); }; /** * Show the rename file dialog * * @params string name - name of the file */ this.showRenameFileDialog = function( filename ) { self.showModal( Mustache.render( self.templates.renamefile, { filename: filename } ) ); var form = $( '#formRenameFile' ); form.find( 'input[name=newname]' ).on( 'keypress', self.preventEnter ); form.find( '#buttonRename' ).on( 'click', function() { self.renameFile( filename, form.find( 'input[name=newname]' ).val() ); self.hideModal(); return false; }); form.find( '#buttonCancel' ).on( 'click', function() { self.hideModal(); return false; }); }; /** * Renames a file * * @params string name - name of the file */ this.renameFile = function( filename, newname ) { $.ajax({ url: ifm.api, type: "POST", data: ({ api: "rename", dir: ifm.currentDir, filename: filename, newname: newname }), dataType: "json", success: function(data) { if(data.status == "OK") { ifm.showMessage("File successfully renamed", "s"); ifm.refreshFileTable(); } else ifm.showMessage("File could not be renamed: "+data.message, "e"); }, error: function() { ifm.showMessage("General error occured", "e"); } }); }; /** * Show the copy/move dialog * * @params string name - name of the file */ this.showCopyMoveDialog = function( name ) { self.showModal( self.templates.copymove ); $.ajax({ url: self.api, type: "POST", data: ({ api: "getFolderTree", dir: self.currentDir }), dataType: "json", success: function( data ) { $( '#copyMoveTree' ).treeview( { data: data, levels: 0, expandIcon: "icon icon-folder-empty", collapseIcon: "icon icon-folder-open-empty" } ); }, error: function() { self.hideModal(); self.showMessage( "Error while fetching the folder tree.", "e" ) } }); $( '#copyButton' ).on( 'click', function() { self.copyMove( name, $('#copyMoveTree .node-selected').data('path'), 'copy' ); self.hideModal(); return false; }); $( '#moveButton' ).on( 'click', function() { self.copyMove( name, $('#copyMoveTree .node-selected').data('path'), 'move' ); self.hideModal(); return false; }); $( '#cancelButton' ).on( 'click', function() { self.hideModal(); return false; }); }; /** * Copy or moves a file * * @params string name - name of the file */ this.copyMove = function( source, destination, action ) { var id = self.generateGuid(); self.task_add( action.charAt(0).toUpperCase() + action.slice(1) + " " + source + " to " + destination, id ); $.ajax({ url: self.api, type: "POST", data: { dir: self.currentDir, api: "copyMove", action: action, filename: source, destination: destination }, dataType: "json", success: function(data) { if( data.status == "OK" ) { self.showMessage( data.message, "s" ); } else { self.showMessage( data.message, "e" ); } self.refreshFileTable(); }, error: function() { self.showMessage( "General error occured.", "e" ); }, complete: function() { self.task_done( id ); } }); }; /** * Shows the extract file dialog * * @param string name - name of the file */ this.showExtractFileDialog = function( filename ) { var targetDirSuggestion = ''; if( filename.lastIndexOf( '.' ) > 1 ) targetDirSuggestion = filename.substr( 0, filename.lastIndexOf( '.' ) ); else targetDirSuggestion = filename; self.showModal( Mustache.render( self.templates.extractfile, { filename: filename, destination: targetDirSuggestion } ) ); var form = $('#formExtractFile'); form.find('#buttonExtract').on( 'click', function() { var t = form.find('input[name=extractTargetLocation]:checked').val(); if( t == "custom" ) t = form.find('#extractCustomLocation').val(); self.extractFile( self.JSEncode( filename ), t ); self.hideModal(); return false; }); form.find('#buttonCancel').on( 'click', function() { self.hideModal(); return false; }); form.find('#extractCustomLocation').on( 'click', function(e) { $(e.target).prev().children().first().prop( 'checked', true ); }); }; /** * Extracts a file * * @param string filename - name of the file * @param string destination - name of the target directory */ this.extractFile = function( filename, destination ) { $.ajax({ url: self.api, type: "POST", data: { api: "extract", dir: self.currentDir, filename: filename, targetdir: destination }, dataType: "json", success: function( data ) { if( data.status == "OK" ) { self.showMessage( "File successfully extracted", "s" ); self.refreshFileTable(); } else self.showMessage( "File could not be extracted. Error: " + data.message, "e" ); }, error: function() { self.showMessage( "General error occured", "e" ); } }); }; /** * Shows the upload file dialog */ this.showUploadFileDialog = function() { self.showModal( self.templates.uploadfile ); var form = $('#formUploadFile'); form.find( 'input[name=newfilename]' ).on( 'keypress', self.preventEnter ); form.find( '#buttonUpload' ).on( 'click', function() { self.uploadFile(); self.hideModal(); return false; }); form.find( '#buttonCancel' ).on( 'click', function() { self.hideModal(); return false; }); }; /** * Uploads a file */ this.uploadFile = function() { var ufile = document.getElementById( 'ufile' ).files[0]; var data = new FormData(); var newfilename = $("#formUploadFile input[name^=newfilename]").val(); data.append('api', 'upload'); data.append('dir', self.currentDir); data.append('file', ufile); data.append('newfilename', newfilename); var id = self.generateGuid(); $.ajax({ url: self.api, type: "POST", data: data, processData: false, contentType: false, dataType: "json", xhr: function(){ var xhr = $.ajaxSettings.xhr() ; xhr.upload.onprogress = function(evt){ self.task_update(evt.loaded/evt.total*100,id); } ; xhr.upload.onload = function(){ console.log('Uploading '+newfilename+' done.') } ; return xhr ; }, success: function(data) { if(data.status == "OK") { self.showMessage("File successfully uploaded", "s"); if(data.cd == self.currentDir) self.refreshFileTable(); } else self.showMessage("File could not be uploaded: "+data.message, "e"); }, error: function() { self.showMessage("General error occured", "e"); }, complete: function() { self.task_done(id); } }); self.task_add("Upload "+ufile.name, id); }; /** * Change the permissions of a file * * @params object e - event object * @params string name - name of the file */ this.changePermissions = function( filename, newperms) { $.ajax({ url: self.api, type: "POST", data: ({ api: "changePermissions", dir: self.currentDir, filename: filename, chmod: newperms }), dataType: "json", success: function( data ){ if( data.status == "OK" ) { self.showMessage( "Permissions successfully changed.", "s" ); self.refreshFileTable(); } else { self.showMessage( "Permissions could not be changed: "+data.message, "e"); } }, error: function() { self.showMessage("General error occured.", "e"); } }); }; /** * Show the remote upload dialog */ this.showRemoteUploadDialog = function() { self.showModal( self.templates.remoteupload ); var form = $('#formRemoteUpload'); form.find( '#url' ) .on( 'keypress', self.preventEnter ) .on( 'change keyup', function() { $("#filename").val($(this).val().substr($(this).val().lastIndexOf("/")+1)); }); form.find( '#filename' ) .on( 'keypress', self.preventEnter ) .on( 'keyup', function() { $("#url").off( 'change keyup' ); }); form.find( '#buttonUpload' ).on( 'click', function() { self.remoteUpload(); self.hideModal(); return false; }); form.find( '#buttonCancel' ).on( 'click', function() { self.hideModal(); return false; }); }; /** * Remote uploads a file */ this.remoteUpload = function() { var filename = $("#formRemoteUpload #filename").val(); var id = ifm.generateGuid(); $.ajax({ url: ifm.api, type: "POST", data: ({ api: "remoteUpload", dir: ifm.currentDir, filename: filename, method: $("#formRemoteUpload input[name=method]:checked").val(), url: encodeURI($("#url").val()) }), dataType: "json", success: function(data) { if(data.status == "OK") { ifm.showMessage("File successfully uploaded", "s"); ifm.refreshFileTable(); } else ifm.showMessage("File could not be uploaded:
"+data.message, "e"); }, error: function() { ifm.showMessage("General error occured", "e"); }, complete: function() { ifm.task_done(id); } }); ifm.task_add("Remote upload: "+filename, id); }; /** * Shows the ajax request dialog */ this.showAjaxRequestDialog = function() { self.showModal( self.templates.ajaxrequest ); var form = $('#formAjaxRequest'); form.find( '#ajaxurl' ).on( 'keypress', self.preventEnter ); form.find( '#buttonRequest' ).on( 'click', function() { self.ajaxRequest(); return false; }); form.find( '#buttonClose' ).on( 'click', function() { self.hideModal(); return false; }); }; /** * Performs an ajax request */ this.ajaxRequest = function() { $.ajax({ url : $("#ajaxurl").val(), cache : false, data : $('#ajaxdata').val().replace(/\n/g,"&"), type : $('#ajaxrequest input[name=arMethod]:checked').val(), success : function(response) { $("#ajaxresponse").text(response); }, error : function(e) { self.showMessage("Error: "+e, "e"); console.log(e); } }); }; /** * Shows the delete dialog for multiple files */ this.showMultiDeleteDialog = function() { self.showModal( Mustache.render( self.templates.multidelete, { count: $('#filetable tr.selectedItem').length } ) ); var form = $('#formDeleteFiles'); form.find( '#buttonYes' ).on( 'click', function() { self.multiDelete(); self.hideModal(); return false; }); form.find( '#buttonNo' ).on( 'click', function() { self.hideModal(); return false; }); }; /** * Deletes multiple files */ this.multiDelete = function() { var elements = $('#filetable tr.selectedItem'); var filenames = []; for(var i=0;typeof(elements[i])!='undefined';filenames.push(elements[i++].getAttribute('data-filename'))); $.ajax({ url: self.api, type: "POST", data: ({ api: "multidelete", dir: self.currentDir, filenames: filenames }), dataType: "json", success: function(data) { if(data.status == "OK") { if(data.errflag == 1) ifm.showMessage("All files successfully deleted.", "s"); else if(data.errflag == 0) ifm.showMessage("Some files successfully deleted. "+data.message); else ifm.showMessage("Files could not be deleted. "+data.message, "e"); ifm.refreshFileTable(); } else ifm.showMessage("Files could not be deleted:
"+data.message, "e"); }, error: function() { ifm.showMessage("General error occured", "e"); } }); }; // -------------------- // helper functions // -------------------- /** * Shows a notification * * @param string m - message text * @param string t - message type (e: error, s: success) */ this.showMessage = function(m, t) { var msgType = (t == "e")?"danger":(t == "s")?"success":"info"; var element = ( self.config.inline ) ? self.rootElement : "body"; $.notify( { message: m }, { type: msgType, delay: 5000, mouse_over: 'pause', offset: { x: 15, y: 65 }, element: element } ); }; /** * Combines two path components * * @param string a - component 1 * @param string b - component 2 */ this.pathCombine = function(a, b) { if(a == "" && b == "") return ""; if(b[0] == "/") b = b.substring(1); if(a == "") return b; if(a[a.length-1] == "/") a = a.substring(0, a.length-1); if(b == "") return a; return a+"/"+b; }; /** * Prevents a user to submit a form via clicking enter * * @param object e - click event */ this.preventEnter = function(e) { if( e.keyCode == 13 ) return false; else return true; } /** * Checks if an element is part of an array * * @param obj needle - search item * @param array haystack - array to search */ this.inArray = function(needle, haystack) { for(var i = 0; i < haystack.length; i++) { if(haystack[i] == needle) return true; } return false; }; /** * Adds a task to the taskbar. * * @param string name - description of the task * @param string id - identifier for the task */ this.task_add = function( name, id ) { if( ! document.getElementById( "waitqueue" ) ) { $( document.body ).prepend( '
' ); } $( "#waitqueue" ).prepend('\
\
\
\
\ '+name+'\
\
\
\ '); }; /** * Removes a task from the taskbar * * @param string id - task identifier */ this.task_done = function(id) { $("#"+id).remove(); if($("#waitqueue>div").length == 0) { $("#waitqueue").remove(); } }; /** * Updates a task * * @param integer progress - percentage of status * @param string id - task identifier */ this.task_update = function(progress, id) { $('#'+id+' .progress-bar').css('width', progress+'%').attr('aria-valuenow', progress); }; /** * Highlights an item in the file table * * @param object param - either an element id or a jQuery object */ this.highlightItem = function( param ) { var highlight = function( el ) { el.addClass( 'highlightedItem' ).siblings().removeClass( 'highlightedItem' ); el.find( 'a' ).first().focus(); if( ! self.isElementInViewport( el ) ) { var scrollOffset = 0; if( param=="prev" ) scrollOffset = el.offset().top - ( window.innerHeight || document.documentElement.clientHeight ) + el.height() + 15; else scrollOffset = el.offset().top - 55; $('html, body').animate( { scrollTop: scrollOffset }, 200 ); } }; if( param.jquery ) { highlight( param ); } else { var highlightedItem = $('.highlightedItem'); if( ! highlightedItem.length ) { highlight( $('#filetable tbody tr:first-child') ); } else { var newItem = ( param=="next" ? highlightedItem.next() : highlightedItem.prev() ); if( newItem.is( 'tr' ) ) { highlight( newItem ); } } } }; /** * Checks if an element is within the viewport * * @param object el - element object */ this.isElementInViewport = function (el) { if (typeof jQuery === "function" && el instanceof jQuery) { el = el[0]; } var rect = el.getBoundingClientRect(); return ( rect.top >= 60 && rect.left >= 0 && rect.bottom <= ( (window.innerHeight || document.documentElement.clientHeight) ) && rect.right <= (window.innerWidth || document.documentElement.clientWidth) ); } /** * Generates a GUID */ this.generateGuid = function() { var result, i, j; result = ''; for(j=0; j<20; j++) { i = Math.floor(Math.random()*16).toString(16).toUpperCase(); result = result + i; } return result; }; /** * Logs a message if debug mode is on * * @param string m - message text */ this.log = function( m ) { if( self.config.debug ) { console.log( "IFM (debug): " + m ); } }; /** * Encodes a string for use within javascript * * @param string s - encoding string */ this.JSEncode = function(s) { return s.replace(/'/g, '\\x27').replace(/"/g, '\\x22'); }; /** * Handles the javascript pop states * * @param object event - event object */ this.historyPopstateHandler = function(event) { var dir = ""; if( event.state && event.state.dir ) dir = event.state.dir; self.changeDirectory( dir, { pushState: false, absolute: true } ); }; /** * Handles keystrokes * * @param object e - event object */ this.handleKeystrokes = function( e ) { // bind 'del' key if( $(e.target).closest('input')[0] || $(e.target).closest('textarea')[0] ) { return; } switch( e.key ) { case 'Delete': if( self.config.delete ) { if( $('#filetable tr.selectedItem').length > 0 ) { e.preventDefault(); self.showMultiDeleteDialog(); } else { var item = $('.highlightedItem'); if( item.length ) self.showDeleteFileDialog( item.data( 'filename' ) ); } } break; case 'e': if( self.config.edit ) { var item = $('.highlightedItem'); if( item.length && ! item.hasClass( 'isDir' ) ) { e.preventDefault(); var action = item.data( 'eaction' ); switch( action ) { case 'extract': self.showExtractFileDialog( item.data( 'filename' ) ); break; case 'edit': self.editFile( item.data( 'filename' ) ); } } } break; case 'g': e.preventDefault(); $('#currentDir').focus(); break; case 'r': e.preventDefault(); self.refreshFileTable(); break; case 'u': if( self.config.upload ) { e.preventDefault(); self.showUploadFileDialog(); } break; case 'o': if( self.config.remoteupload ) { e.preventDefault(); self.showRemoteUploadDialog(); } break; case 'a': if( self.config.ajaxrequest ) { e.preventDefault(); self.showAjaxRequestDialog(); } break; case 'F': if( self.config.createfile ) { e.preventDefault(); self.showFileDialog(); } break; case 'D': if( self.config.createdir ) { e.preventDefault(); self.showCreateDirDialog(); } break; case 'h': case 'ArrowLeft': e.preventDefault(); self.changeDirectory( '..' ); break; case 'l': case 'ArrowRight': e.preventDefault(); var item = $('.highlightedItem'); if( item.hasClass('isDir') ) self.changeDirectory( item.data( 'filename' ) ); break; case 'j': case 'ArrowDown': e.preventDefault(); self.highlightItem('next'); break; case 'k': case 'ArrowUp': e.preventDefault(); self.highlightItem('prev'); break; case 'Escape': if( $(':focus').is( '.clickable-row td:first-child a:first-child' ) && $('.highlightedItem').length ) { e.preventDefault(); $('.highlightedItem').removeClass( 'highlightedItem' ); } break; case ' ': // todo: make it work only when noting other is focused case 'Enter': if( $(':focus').is( '.clickable-row td:first-child a:first-child' ) ) { var trParent = $(':focus').parent().parent(); if( e.key == 'Enter' && trParent.hasClass( 'isDir' ) ) { e.preventDefault(); e.stopPropagation(); self.changeDirectory( trParent.data( 'filename' ) ); } else if( e.key == ' ' && ! trParent.is( ':first-child' ) ) { e.preventDefault(); e.stopPropagation(); var item = $('.highlightedItem'); if( item.is( 'tr' ) ) item.toggleClass( 'selectedItem' ); } } break; } } /** * Initializes the application */ this.initLoadConfig = function() { $.ajax({ url: self.api, type: "POST", data: { api: "getConfig" }, dataType: "json", success: function(d) { self.config = d; self.log( "configuration loaded" ); self.initLoadTemplates(); }, error: function() { throw new Error( "IFM: could not load configuration" ); } }); }; this.initLoadTemplates = function() { // load the templates from the backend $.ajax({ url: self.api, type: "POST", data: { api: "getTemplates" }, dataType: "json", success: function(d) { self.templates = d; self.log( "templates loaded" ); self.initApplication(); }, error: function() { throw new Error( "IFM: could not load templates" ); } }); }; this.initApplication = function() { self.rootElement.html( Mustache.render( self.templates.app, { showpath: "/", config: self.config, ftbuttons: function(){ return ( self.config.edit || self.config.rename || self.config.delete || self.config.zipnload || self.config.extract ); } } ) ); // bind static buttons $("#refresh").click(function(){ self.refreshFileTable(); }); $("#createFile").click(function(){ self.showFileDialog(); }); $("#createDir").click(function(){ self.showCreateDirDialog(); }); $("#upload").click(function(){ self.showUploadFileDialog(); }); $('#currentDir').on( 'keypress', function (event) { if( event.keyCode == 13 ) { event.preventDefault(); self.changeDirectory( $(this).val(), { absolute: true } ); } }); $('#buttonRemoteUpload').on( 'click', function() { self.showRemoteUploadDialog(); return false; }); $('#buttonAjaxRequest').on( 'click', function() { self.showAjaxRequestDialog(); return false; }); // handle keystrokes $(document).on( 'keydown', self.handleKeystrokes ); // handle history manipulation window.onpopstate = self.historyPopstateHandler; // load initial file table if( window.location.hash ) { self.changeDirectory( window.location.hash.substring( 1 ) ); } else { this.refreshFileTable(); } }; this.init = function( id ) { self.rootElement = $('#'+id); this.initLoadConfig(); }; }