/* * Copyright (c) Codiad & Kent Safranski (codiad.com), * Isaac Brown ( telaaedifex.com ) distributed as-is and without * warranty under the MIT License. See [root]/license.txt for more. * This information must remain intact. */ ( function( global, $ ) { var EditSession = ace.require( 'ace/edit_session' ) .EditSession; var UndoManager = ace.require( 'ace/undomanager' ) .UndoManager; var codiad = global.codiad; $( function() { codiad.active.init(); }); ////////////////////////////////////////////////////////////////// // // Active Files Component for Codiad // --------------------------------- // Track and manage EditSession instaces of files being edited. // ////////////////////////////////////////////////////////////////// codiad.active = { controller: 'components/active/controller.php', // Path to EditSession instance mapping sessions: {}, // History of opened files history: [], // List of active file positions positions: {}, ////////////////////////////////////////////////////////////////// // // Check if a file is open. // // Parameters: // path - {String} // ////////////////////////////////////////////////////////////////// isOpen: function( path ) { return !!this.sessions[path]; }, open: function( path, content, mtime, inBackground, focus ) { //if( this. ) { //} /* Notify listeners. */ amplify.publish( 'active.onFileWillOpen', { path: path, content: content }); if( focus === undefined ) { focus = true; } var _this = this; if( this.isOpen( path ) ) { if( focus ) this.focus( path ); return; } var ext = codiad.filemanager.getExtension( path ); var mode = codiad.editor.selectMode( path ); var fn = function() { //var Mode = require('ace/mode/' + mode) // .Mode; // TODO: Ask for user confirmation before recovering // And maybe show a diff var draft = _this.checkDraft( path ); if( draft ) { content = draft; codiad.message.success( i18n( 'Recovered unsaved content for: ' ) + path ); } //var session = new EditSession(content, new Mode()); var session = new EditSession( content ); session.setMode( mode.mode ); session.setUndoManager( new UndoManager() ); session.path = path; session.serverMTime = mtime; _this.sessions[path] = session; session.untainted = content.slice( 0 ); if( !inBackground && focus ) { codiad.editor.setSession( session ); } _this.add( path, session, focus ); if( !( _this.positions[`${path}`] === undefined ) && focus ) { _this.setPosition( _this.positions[`${path}`] ); } /* Notify listeners. */ amplify.publish( 'active.onOpen', path ); }; // Assuming the mode file has no dependencies $.loadScript( 'components/editor/ace-editor/mode-' + mode.name + '.js', fn ); }, init: function() { var _this = this; _this.initTabDropdownMenu(); _this.updateTabDropdownVisibility(); // Focus from list. $( '#list-active-files a' ) .live( 'click', function( e ) { e.stopPropagation(); _this.focus( $( this ).parent( 'li' ).attr( 'data-path' ) ); }); // Focus on left button click from dropdown. $( '#dropdown-list-active-files a' ) .live( 'click', function( e ) { if( e.which == 1 ) { /* Do not stop propagation of the event, * it will be catch by the dropdown menu * and close it. */ _this.focus( $( this ).parent( 'li' ).attr( 'data-path' ) ); } }); // Focus on left button mousedown from tab. $( '#tab-list-active-files li.tab-item>a.label' ) .live( 'mousedown', function( e ) { if( e.which == 1 ) { e.stopPropagation(); _this.focus( $( this ).parent( 'li' ).attr( 'data-path' ) ); } }); // Remove from list. $( '#list-active-files a>span' ) .live( 'click', function( e ) { e.stopPropagation(); _this.remove( $( this ) .parent( 'a' ) .parent( 'li' ) .attr( 'data-path' ) ); }); // Remove from dropdown. $( '#dropdown-list-active-files a>span' ) .live( 'click', function( e ) { e.stopPropagation(); /* Get the active editor before removing anything. Remove the * tab, then put back the focus on the previously active * editor if it was not removed. */ var activePath = _this.getPath(); var pathToRemove = $( this ).parents( 'li' ).attr( 'data-path' ); _this.remove( pathToRemove ); if( activePath !== null && activePath !== pathToRemove ) { _this.focus( activePath ); } _this.updateTabDropdownVisibility(); }); // Remove from tab. $( '#tab-list-active-files a.close' ) .live( 'click', function( e ) { e.stopPropagation(); /* Get the active editor before removing anything. Remove the * tab, then put back the focus on the previously active * editor if it was not removed. */ var activePath = _this.getPath(); var pathToRemove = $( this ).parent( 'li' ).attr( 'data-path' ); _this.remove( pathToRemove ); if( activePath !== null && activePath !== pathToRemove ) { _this.focus( activePath ); } _this.updateTabDropdownVisibility(); }); // Remove from middle button click on dropdown. $( '#dropdown-list-active-files li' ) .live( 'mouseup', function( e ) { if( e.which == 2 ) { e.stopPropagation(); /* Get the active editor before removing anything. Remove the * tab, then put back the focus on the previously active * editor if it was not removed. */ var activePath = _this.getPath(); var pathToRemove = $( this ).attr( 'data-path' ); _this.remove( pathToRemove ); if( activePath !== null && activePath !== pathToRemove ) { _this.focus( activePath ); } _this.updateTabDropdownVisibility(); } }); // Remove from middle button click on tab. $( '.tab-item' ) .live( 'mouseup', function( e ) { if( e.which == 2 ) { e.stopPropagation(); /* Get the active editor before removing anything. Remove the * tab, then put back the focus on the previously active * editor if it was not removed. */ var activePath = _this.getPath(); var pathToRemove = $( this ).attr( 'data-path' ); _this.remove( pathToRemove ); if( activePath !== null && activePath !== pathToRemove ) { _this.focus( activePath ); } _this.updateTabDropdownVisibility(); } }); // Make list sortable $( '#list-active-files' ) .sortable( { placeholder: 'active-sort-placeholder', tolerance: 'intersect', start: function( e, ui ) { ui.placeholder.height( ui.item.height() ); } }); // Make dropdown sortable. $( '#dropdown-list-active-files' ) .sortable( { axis: 'y', tolerance: 'pointer', start: function( e, ui ) { ui.placeholder.height( ui.item.height() ); } }); // Make tabs sortable. $( '#tab-list-active-files' ) .sortable( { items: '> li', axis: 'x', tolerance: 'pointer', containment: 'parent', start: function( e, ui ) { ui.placeholder.css( 'background', 'transparent' ); ui.helper.css( 'width', '200px' ); }, stop: function( e, ui ) { // Reset css ui.item.css( 'z-index', '' ) ui.item.css( 'position', '' ) } }); /* Woaw, so tricky! At initialization, the tab-list is empty, so * it is not marked as float so it is not detected as an horizontal * list by the sortable plugin. Workaround is to mark it as * floating at initialization time. See bug report * http://bugs.jqueryui.com/ticket/6702. */ $( '#tab-list-active-files' ).data( 'sortable' ).floating = true; // Open saved-state active files on load $.get( _this.controller + '?action=list', function( data ) { var listResponse = codiad.jsend.parse( data ); if( listResponse !== null ) { $.each( listResponse, function( index, data ) { codiad.active.positions[`${data.path}`] = JSON.parse( data.position ); codiad.filemanager.openFile( data.path, data.focused ); }); } }); // Prompt if a user tries to close window without saving all filess window.onbeforeunload = function( e ) { codiad.active.uploadPositions(); if( $( '#list-active-files li.changed' ) .length > 0 ) { var e = e || window.event; var errMsg = i18n( 'You have unsaved files.' ); // For IE and Firefox prior to version 4 if( e ) { e.returnValue = errMsg; } // For rest return errMsg; } }; }, ////////////////////////////////////////////////////////////////// // Drafts ////////////////////////////////////////////////////////////////// checkDraft: function( path ) { var draft = localStorage.getItem( path ); if( draft !== null ) { return draft; } else { return false; } }, removeDraft: function( path ) { localStorage.removeItem( path ); }, ////////////////////////////////////////////////////////////////// // Get active editor path ////////////////////////////////////////////////////////////////// getPath: function() { try { return codiad.editor.getActive() .getSession() .path; } catch ( e ) { return null; } }, ////////////////////////////////////////////////////////////////// // Check if opened by another user ////////////////////////////////////////////////////////////////// check: function( path ) { $.get( this.controller + '?action=check&path=' + encodeURIComponent( path ), function( data ) { var checkResponse = codiad.jsend.parse( data ); }); }, ////////////////////////////////////////////////////////////////// // Add newly opened file to list ////////////////////////////////////////////////////////////////// add: function( path, session, focus ) { if( focus === undefined ) { focus = true; } var listThumb = this.createListThumb( path ); session.listThumb = listThumb; $( '#list-active-files' ).append( listThumb ); /* If the tab list would overflow with the new tab. Move the * first tab to dropdown, then add a new tab. */ if( this.isTabListOverflowed( true ) ) { var tab = $( '#tab-list-active-files li:first-child' ); this.moveTabToDropdownMenu( tab ); } var tabThumb = this.createTabThumb( path ); $( '#tab-list-active-files' ).append( tabThumb ); session.tabThumb = tabThumb; this.updateTabDropdownVisibility(); $.get( this.controller + '?action=add&path=' + encodeURIComponent( path ) ); if( focus ) { this.focus( path ); } // Mark draft as changed if( this.checkDraft( path ) ) { this.markChanged( path ); } }, ////////////////////////////////////////////////////////////////// // Focus on opened file ////////////////////////////////////////////////////////////////// focus: function( path, moveToTabList ) { if( moveToTabList === undefined ) { moveToTabList = true; } /* Notify listeners. */ amplify.publish( 'active.onWillFocus', path ); this.highlightEntry( path, moveToTabList ); if( path != this.getPath() ) { let _this = this; codiad.editor.setSession( this.sessions[path] ); this.history.push( path ); $.get( this.controller, { 'action': 'focused', 'path': path }, function() { if( !( _this.positions[`${path}`] === undefined ) ) { _this.setPosition( _this.positions[`${path}`] ); } }); } /* Check for users registered on the file. */ this.check( path ); /* Notify listeners. */ amplify.publish( 'active.onFocus', path ); }, highlightEntry: function( path, moveToTabList ) { if( moveToTabList === undefined ) { moveToTabList = true; } $( '#list-active-files li' ) .removeClass( 'active' ); $( '#tab-list-active-files li' ) .removeClass( 'active' ); $( '#dropdown-list-active-files li' ) .removeClass( 'active' ); var session = this.sessions[path]; if( $( '#dropdown-list-active-files' ).has( session.tabThumb ).length > 0 ) { if( moveToTabList ) { /* Get the menu item as a tab, and put the last tab in * dropdown. */ var menuItem = session.tabThumb; this.moveDropdownMenuItemToTab( menuItem, true ); var tab = $( '#tab-list-active-files li:last-child' ); this.moveTabToDropdownMenu( tab ); } else { /* Show the dropdown menu if needed */ this.showTabDropdownMenu(); } } else if( this.history.length > 0 ) { var prevPath = this.history[this.history.length - 1]; var prevSession = this.sessions[prevPath]; if( $( '#dropdown-list-active-files' ).has( prevSession.tabThumb ).length > 0 ) { /* Hide the dropdown menu if needed */ this.hideTabDropdownMenu(); } } session.tabThumb.addClass( 'active' ); session.listThumb.addClass( 'active' ); }, ////////////////////////////////////////////////////////////////// // Mark changed ////////////////////////////////////////////////////////////////// markChanged: function( path ) { this.sessions[path].listThumb.addClass( 'changed' ); this.sessions[path].tabThumb.addClass( 'changed' ); }, ////////////////////////////////////////////////////////////////// // Save active editor ////////////////////////////////////////////////////////////////// save: function( path, alerts = true ) { /* Notify listeners. */ amplify.publish( 'active.onSave', path ); var _this = this; if( ( path && !this.isOpen( path ) ) || ( !path && !codiad.editor.getActive() ) ) { codiad.message.error( i18n( 'No Open Files to save' ) ); return; } var session; if( path ) session = this.sessions[path]; else session = codiad.editor.getActive() .getSession(); var content = session.getValue(); var path = session.path; var handleSuccess = function( mtime ) { var session = codiad.active.sessions[path]; if( typeof session != 'undefined' ) { session.untainted = newContent; session.serverMTime = mtime; if( session.listThumb ) session.listThumb.removeClass( 'changed' ); if( session.tabThumb ) session.tabThumb.removeClass( 'changed' ); } _this.removeDraft( path ); } // Replicate the current content so as to avoid // discrepancies due to content changes during // computation of diff var newContent = content.slice( 0 ); if( session.serverMTime && session.untainted ) { codiad.workerManager.addTask( { taskType: 'diff', id: path, original: session.untainted, changed: newContent }, function( success, patch ) { if( success ) { codiad.filemanager.savePatch( path, patch, session.serverMTime, { success: handleSuccess }, alerts ); } else { codiad.filemanager.saveFile( path, newContent, { success: handleSuccess }, alerts ); } }, this ); } else { codiad.filemanager.saveFile( path, newContent, { success: handleSuccess }, alert ); } }, ////////////////////////////////////////////////////////////////// // Save all files ////////////////////////////////////////////////////////////////// saveAll: function() { var _this = this; for( var session in _this.sessions ) { if( _this.sessions[session].listThumb.hasClass( 'changed' ) ) { codiad.active.save( session ); } } }, ////////////////////////////////////////////////////////////////// // Remove file ////////////////////////////////////////////////////////////////// remove: function( path ) { if( !this.isOpen( path ) ) return; var session = this.sessions[path]; var closeFile = true; if( session.listThumb.hasClass( 'changed' ) ) { codiad.modal.load( 450, 'components/active/dialog.php?action=confirm&path=' + encodeURIComponent( path ) ); closeFile = false; } if( closeFile ) { this.close( path ); } }, removeAll: function( discard ) { discard = discard || false; /* Notify listeners. */ amplify.publish( 'active.onRemoveAll' ); var _this = this; var changed = false; var opentabs = new Array(); for( var session in _this.sessions ) { opentabs[session] = session; if( _this.sessions[session].listThumb.hasClass( 'changed' ) ) { changed = true; } } if( changed && !discard ) { codiad.modal.load( 450, 'components/active/dialog.php?action=confirmAll' ); return; } for( var tab in opentabs ) { var session = this.sessions[tab]; session.tabThumb.remove(); _this.updateTabDropdownVisibility(); session.listThumb.remove(); /* Remove closed path from history */ var history = []; $.each( this.history, function( index ) { if( this != tab ) history.push( this ); }) this.history = history delete this.sessions[tab]; this.removeDraft( tab ); } codiad.editor.exterminate(); $( '#list-active-files' ).html( '' ); $.get( this.controller + '?action=removeall' ); }, close: function( path ) { /* Notify listeners. */ amplify.publish( 'active.onClose', path ); var _this = this; var session = this.sessions[path]; /* Animate only if the tabThumb if a tab, not a dropdown item. */ if( session.tabThumb.hasClass( 'tab-item' ) ) { session.tabThumb.css( { 'z-index': 1 }); session.tabThumb.animate( { top: $( '#editor-top-bar' ).height() + 'px' }, 300, function() { session.tabThumb.remove(); _this.updateTabDropdownVisibility(); }); } else { session.tabThumb.remove(); _this.updateTabDropdownVisibility(); } session.listThumb.remove(); /* Remove closed path from history */ var history = []; $.each( this.history, function( index ) { if( this != path ) history.push( this ); }) this.history = history /* Select all the tab tumbs except the one which is to be removed. */ var tabThumbs = $( '#tab-list-active-files li[data-path!="' + path + '"]' ); if( tabThumbs.length == 0 ) { codiad.editor.exterminate(); } else { var nextPath = ''; if( this.history.length > 0 ) { nextPath = this.history[this.history.length - 1]; } else { nextPath = $( tabThumbs[0] ).attr( 'data-path' ); } var nextSession = this.sessions[nextPath]; codiad.editor.removeSession( session, nextSession ); this.focus( nextPath ); } delete this.sessions[path]; $.get( this.controller + '?action=remove&path=' + encodeURIComponent( path ) ); this.removeDraft( path ); }, ////////////////////////////////////////////////////////////////// // Process rename ////////////////////////////////////////////////////////////////// rename: function( oldPath, newPath ) { var switchSessions = function( oldPath, newPath ) { var tabThumb = this.sessions[oldPath].tabThumb; tabThumb.attr( 'data-path', newPath ); var title = newPath; if( codiad.project.isAbsPath( newPath ) ) { title = newPath.substring( 1 ); } tabThumb.find( '.label' ) .text( title ); this.sessions[newPath] = this.sessions[oldPath]; this.sessions[newPath].path = newPath; delete this.sessions[oldPath]; //Rename history for( var i = 0; i < this.history.length; i++ ) { if( this.history[i] === oldPath ) { this.history[i] = newPath; } } }; if( this.sessions[oldPath] ) { // A file was renamed switchSessions.apply( this, [oldPath, newPath] ); // pass new sessions instance to setactive for( var k = 0; k < codiad.editor.instances.length; k++ ) { if( codiad.editor.instances[k].getSession().path === newPath ) { codiad.editor.setActive( codiad.editor.instances[k] ); } } var newSession = this.sessions[newPath]; // Change Editor Mode var mode = codiad.editor.selectMode( newPath ); // handle async mode change var fn = function() { codiad.editor.setModeDisplay( newSession ); newSession.removeListener( 'changeMode', fn ); } newSession.on( "changeMode", fn ); newSession.setMode( mode.mode ); } else { // A folder was renamed var newKey; for( var key in this.sessions ) { newKey = key.replace( oldPath, newPath ); if( newKey !== key ) { switchSessions.apply( this, [key, newKey] ); } } } $.get( this.controller + '?action=rename&old_path=' + encodeURIComponent( oldPath ) + '&new_path=' + encodeURIComponent( newPath ), function() { /* Notify listeners. */ amplify.publish( 'active.onRename', { "oldPath": oldPath, "newPath": newPath }); }); }, ////////////////////////////////////////////////////////////////// // Open in Browser ////////////////////////////////////////////////////////////////// openInBrowser: function() { var path = this.getPath(); if( path ) { codiad.filemanager.openInBrowser( path ); } else { codiad.message.error( 'No Open Files' ); } }, ////////////////////////////////////////////////////////////////// // Get Selected Text ////////////////////////////////////////////////////////////////// getSelectedText: function() { var path = this.getPath(); var session = this.sessions[path]; if( path && this.isOpen( path ) ) { return session.getTextRange( codiad.editor.getActive() .getSelectionRange() ); } else { codiad.message.error( i18n( 'No Open Files or Selected Text' ) ); } }, ////////////////////////////////////////////////////////////////// // Insert Text ////////////////////////////////////////////////////////////////// insertText: function( val ) { codiad.editor.getActive() .insert( val ); }, ////////////////////////////////////////////////////////////////// // Goto Line ////////////////////////////////////////////////////////////////// gotoLine: function( line ) { codiad.editor.getActive() .gotoLine( line, 0, true ); }, ////////////////////////////////////////////////////////////////// // Move Up (Key Combo) ////////////////////////////////////////////////////////////////// move: function( dir ) { var num = $( '#tab-list-active-files li' ).length; if( num === 0 ) return; var newActive = null; var active = null; if( dir == 'up' ) { // If active is in the tab list active = $( '#tab-list-active-files li.active' ); if( active.length > 0 ) { // Previous or rotate to the end newActive = active.prev( 'li' ); if( newActive.length === 0 ) { newActive = $( '#dropdown-list-active-files li:last-child' ) if( newActive.length === 0 ) { newActive = $( '#tab-list-active-files li:last-child' ) } } } // If active is in the dropdown list active = $( '#dropdown-list-active-files li.active' ); if( active.length > 0 ) { // Previous newActive = active.prev( 'li' ); if( newActive.length === 0 ) { newActive = $( '#tab-list-active-files li:last-child' ) } } } else { // If active is in the tab list active = $( '#tab-list-active-files li.active' ); if( active.length > 0 ) { // Next or rotate to the beginning newActive = active.next( 'li' ); if( newActive.length === 0 ) { newActive = $( '#dropdown-list-active-files li:first-child' ); if( newActive.length === 0 ) { newActive = $( '#tab-list-active-files li:first-child' ) } } } // If active is in the dropdown list active = $( '#dropdown-list-active-files li.active' ); if( active.length > 0 ) { // Next or rotate to the beginning newActive = active.next( 'li' ); if( newActive.length === 0 ) { newActive = $( '#tab-list-active-files li:first-child' ) } } } if( newActive ) this.focus( newActive.attr( 'data-path' ), false ); }, ////////////////////////////////////////////////////////////////// // Dropdown Menu ////////////////////////////////////////////////////////////////// initTabDropdownMenu: function() { var _this = this; var menu = $( '#dropdown-list-active-files' ); var button = $( '#tab-dropdown-button' ); var closebutton = $( '#tab-close-button' ); menu.appendTo( $( 'body' ) ); button.click( function( e ) { e.stopPropagation(); _this.toggleTabDropdownMenu(); }); closebutton.click( function( e ) { e.stopPropagation(); _this.removeAll(); }); }, showTabDropdownMenu: function() { var menu = $( '#dropdown-list-active-files' ); if( !menu.is( ':visible' ) ) this.toggleTabDropdownMenu(); }, hideTabDropdownMenu: function() { var menu = $( '#dropdown-list-active-files' ); if( menu.is( ':visible' ) ) this.toggleTabDropdownMenu(); }, toggleTabDropdownMenu: function() { var _this = this; var menu = $( '#dropdown-list-active-files' ); menu.css( { top: $( "#editor-top-bar" ).height() + 'px', right: '20px', width: '200px' }); menu.slideToggle( 'fast' ); if( menu.is( ':visible' ) ) { // handle click-out autoclosing var fn = function() { menu.hide(); $( window ).off( 'click', fn ) } $( window ).on( 'click', fn ); } }, moveTabToDropdownMenu: function( tab, prepend ) { if( prepend === undefined ) { prepend = false; } tab.remove(); path = tab.attr( 'data-path' ); var tabThumb = this.createMenuItemThumb( path ); if( prepend ) $( '#dropdown-list-active-files' ).prepend( tabThumb ); else $( '#dropdown-list-active-files' ).append( tabThumb ); if( tab.hasClass( "changed" ) ) { tabThumb.addClass( "changed" ); } if( tab.hasClass( "active" ) ) { tabThumb.addClass( "active" ); } this.sessions[path].tabThumb = tabThumb; }, moveDropdownMenuItemToTab: function( menuItem, prepend ) { if( prepend === undefined ) { prepend = false; } menuItem.remove(); path = menuItem.attr( 'data-path' ); var tabThumb = this.createTabThumb( path ); if( prepend ) $( '#tab-list-active-files' ).prepend( tabThumb ); else $( '#tab-list-active-files' ).append( tabThumb ); if( menuItem.hasClass( "changed" ) ) { tabThumb.addClass( "changed" ); } if( menuItem.hasClass( "active" ) ) { tabThumb.addClass( "active" ); } this.sessions[path].tabThumb = tabThumb; }, isTabListOverflowed: function( includeFictiveTab ) { if( includeFictiveTab === undefined ) { includeFictiveTab = false; } var tabs = $( '#tab-list-active-files li' ); var count = tabs.length if( includeFictiveTab ) count += 1; if( count <= 1 ) return false; var width = 0; tabs.each( function( index ) { width += $( this ).outerWidth( true ); }) if( includeFictiveTab ) { width += $( tabs[tabs.length - 1] ).outerWidth( true ); } /* If we subtract the width of the left side bar, of the right side * bar handle and of the tab dropdown handle to the window width, * do we have enough room for the tab list? Its kind of complicated * to handle all the offsets, so afterwards we add a fixed offset * just t be sure. */ var lsbarWidth = $( ".sidebar-handle" ).width(); if( codiad.sidebars.isLeftSidebarOpen ) { lsbarWidth = $( "#sb-left" ).width(); } var rsbarWidth = $( ".sidebar-handle" ).width(); if( codiad.sidebars.isRightSidebarOpen ) { rsbarWidth = $( "#sb-right" ).width(); } var tabListWidth = $( "#tab-list-active-files" ).width(); var dropdownWidth = $( '#tab-dropdown' ).width(); var closeWidth = $( '#tab-close' ).width(); var room = window.innerWidth - lsbarWidth - rsbarWidth - dropdownWidth - closeWidth - width - 30; return ( room < 0 ); }, updateTabDropdownVisibility: function() { while( this.isTabListOverflowed() ) { var tab = $( '#tab-list-active-files li:last-child' ); if( tab.length == 1 ) this.moveTabToDropdownMenu( tab, true ); else break; } while( !this.isTabListOverflowed( true ) ) { var menuItem = $( '#dropdown-list-active-files li:first-child' ); if( menuItem.length == 1 ) this.moveDropdownMenuItemToTab( menuItem ); else break; } if( $( '#dropdown-list-active-files li' ).length > 0 ) { $( '#tab-dropdown' ).show(); } else { $( '#tab-dropdown' ).hide(); // Be sure to hide the menu if it is opened. $( '#dropdown-list-active-files' ).hide(); } if( $( '#tab-list-active-files li' ).length > 1 ) { $( '#tab-close' ).show(); } else { $( '#tab-close' ).hide(); } }, uploadPositions: function() { $.ajax( { type: 'POST', url: codiad.active.controller + '?action=save_positions', data: { positions: ( JSON.stringify( codiad.active.positions ) ) }, success: function( data ) {}, }); }, savePosition: function() { let editor = codiad.editor.getActive(); let session = editor.getSession(); let path = session.path; let position = this.getPosition( path, true ); this.positions[path] = position; }, getPosition: function( path = null, live = false ) { if( path === null ) { path = this.getPath(); } let editor = null; let position = null; let session = codiad.active.sessions[path]; for( let i = codiad.editor.instances.length; i--; ) { if( codiad.editor.instances[i].getSession().path == path ) { editor = codiad.editor.instances[i]; } } if( live ) { position = editor.getCursorPosition(); } else { if( !this.positions[path] === undefined ) { position = this.positions[path].position; } } if( position == null ) { position = { column: 0, row: 0 }; } return position; }, setPosition: function( cursor = null ) { let editor = codiad.editor.getActive(); if( cursor == null ) { cursor = this.getPosition(); } editor.scrollToLine( cursor.row, true, true, function() {}); editor.moveCursorTo( cursor.row, cursor.column ); }, ////////////////////////////////////////////////////////////////// // Factory ////////////////////////////////////////////////////////////////// splitDirectoryAndFileName: function( path ) { var index = path.lastIndexOf( '/' ); return { fileName: path.substring( index + 1 ), directory: ( path.indexOf( '/' ) == 0 ) ? path.substring( 1, index + 1 ) : path.substring( 0, index + 1 ) } }, createListThumb: function( path ) { return $( '