mirror of
https://github.com/xevidos/codiad.git
synced 2024-11-10 21:26:35 +01:00
1115 lines
39 KiB
JavaScript
Executable File
1115 lines
39 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) {
|
|
|
|
//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 $('<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);
|