mirror of
https://github.com/xevidos/codiad.git
synced 2024-11-14 07:41:14 +01:00
1135 lines
32 KiB
JavaScript
Executable file
1135 lines
32 KiB
JavaScript
Executable file
/*
|
|
* 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, read_only=false ) {
|
|
|
|
/* 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 );
|
|
session.read_only = read_only;
|
|
|
|
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 ) {
|
|
console.log( 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 ), function( data ) {
|
|
|
|
//console.log( data );
|
|
});
|
|
|
|
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( session && session.tabThumb && $( '#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() {
|
|
|
|
let path = this.getPath();
|
|
let session = this.sessions[path];
|
|
|
|
if( path && this.isOpen( path ) ) {
|
|
|
|
console.log( "Session:", session );
|
|
console.log( "selection: ", codiad.editor.getActive().getSelectionRange() )
|
|
console.log( "text: ", session.getTextRange( codiad.editor.getActive().getSelectionRange() ) )
|
|
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() {
|
|
|
|
if( Object.keys( codiad.active.positions ).length > 0 ) {
|
|
|
|
$.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 $( '<li data-path="' + path + '"><a title="' + path + '"><span></span><div>' + path + '</div></a></li>' );
|
|
},
|
|
|
|
createTabThumb: function( path ) {
|
|
split = this.splitDirectoryAndFileName( path );
|
|
return $( '<li class="tab-item" data-path="' + path + '"><a class="label" title="' + path + '">' +
|
|
split.directory + '<span class="file-name">' + split.fileName + '</span>' +
|
|
'</a><a class="close">x</a></li>' );
|
|
},
|
|
|
|
createMenuItemThumb: function( path ) {
|
|
split = this.splitDirectoryAndFileName( path );
|
|
return $( '<li data-path="' + path + '"><a title="' + path + '"><span class="label"></span><div class="label">' +
|
|
split.directory + '<span class="file-name">' + split.fileName + '</span>' +
|
|
'</div></a></li>' );
|
|
},
|
|
|
|
};
|
|
|
|
})( this, jQuery );
|