codiad/components/active/init.js

1148 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( result ) {
var session = codiad.active.sessions[path];
if( typeof session != 'undefined' ) {
session.untainted = newContent;
session.serverMTime = result.data.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 ) {
console.log( "diff test", success, patch );
if( success ) {
if( patch.length > 0 ) {
codiad.filemanager.save_file( path, {
patch: patch,
mtime: session.serverMTime
}, alerts )
.then( handleSuccess );
}
} else {
console.log( "calling save while failed diff", path, newContent, alerts );
codiad.filemanager.save_file( path, {content: newContent}, alerts )
.then( handleSuccess );
}
}, this );
} else {
console.log( "calling save without mtime and untainted", path, newContent, alerts );
codiad.filemanager.save_file( path, {content: newContent}, alerts )
.then( handleSuccess );
}
},
//////////////////////////////////////////////////////////////////
// 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 ) {
console.log( "remove file", this.isOpen( path ) );
if( !this.isOpen( path ) ) return;
let session = this.sessions[path];
let 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 );