/* * Copyright (c) Codiad & Kent Safranski (codiad.com), distributed * as-is and without warranty under the MIT License. See * [root]/license.txt for more. This information must remain intact. */ (function(global, $) { // Classes from Ace var VirtualRenderer = ace.require('ace/virtual_renderer').VirtualRenderer; var Editor = ace.require('ace/editor').Editor; var EditSession = ace.require('ace/edit_session').EditSession; var ModeList = ace.require("ace/ext/modelist"); var UndoManager = ace.require("ace/undomanager").UndoManager; // Editor modes that have been loaded var editorModes = {}; var codiad = global.codiad; codiad._cursorPoll = null; var separatorWidth = 3; $(function(){ codiad.editor.init(); }); function SplitContainer(root, children, splitType) { var _this = this; this.root = root; this.splitType = splitType; this.childContainers = {}; this.childElements = {}; this.splitProp = 0.5; this.setChild(0, children[0]); this.setChild(1, children[1]); this.splitter = $('
') .addClass('splitter') .appendTo(root) .draggable({ axis: (splitType === 'horizontal' ? 'x' : 'y'), drag: function(e, ui){ if (_this.splitType === 'horizontal') { var w1, w2; w1 = ui.position.left - separatorWidth/2; w2 = _this.root.width() - ui.position.left - separatorWidth/2; _this.splitProp = w1 / _this.root.width(); _this.childElements[0] .width(w1) .trigger('h-resize', [true, true]); _this.childElements[1] .width(w2) .css('left', w1 + separatorWidth + 'px') .trigger('h-resize', [true, true]); _this.splitProp = ui.position.left / _this.root.width(); } else { var h1, h2; h1 = ui.position.top - separatorWidth/2; h2 = _this.root.width() - ui.position.top - separatorWidth/2; _this.splitProp = h1 / _this.root.height(); _this.childElements[0] .height(h1) .trigger('v-resize', [true, true]); _this.childElements[1] .height(h2) .css('top', h1 + separatorWidth + 'px') .trigger('v-resize', [true, true]); } } }); if (splitType === 'horizontal') { this.splitter .addClass('h-splitter') .width(separatorWidth) .height(root.height()); } else if (splitType === 'vertical') { this.splitter .addClass('v-splitter') .height(separatorWidth) .width(root.width()); } this.root.on('h-resize', function(e, percolateUp, percolateDown){ e.stopPropagation(); if (_this.splitType === 'horizontal') { var w1, w2; w1 = _this.root.width() * _this.splitProp - separatorWidth / 2; w2 = _this.root.width() * (1 - _this.splitProp) - separatorWidth / 2; _this.childElements[0] .width(w1); _this.childElements[1] .width(w2) .css('left', w1 + separatorWidth); _this.splitter.css('left', w1); } else if (_this.splitType === 'vertical') { var w = _this.root.width(); _this.childElements[0] .width(w); _this.childElements[1] .width(w); _this.splitter.width(w); } if (percolateUp) { _this.root.parent('.editor-wrapper') .trigger('h-resize', [true ,false]); } if (! percolateDown) return; if (_this.childContainers[0]) { _this.childContainers[0].root .trigger('h-resize', [false, true]); } else if (_this.childContainers[1]) { _this.childContainers[1].root .trigger('h-resize', [false, true]); } }); this.root.on('v-resize', function(e, percolateUp, percolateDown){ e.stopPropagation(); if (_this.splitType === 'horizontal') { var h = _this.root.height(); _this.childElements[0] .height(h); _this.childElements[1] .height(h); _this.splitter.height(h); } else if (_this.splitType === 'vertical') { var h1 = _this.root.height() * _this.splitProp - separatorWidth / 2; var h2 = _this.root.height() * (1 - _this.splitProp) - separatorWidth / 2; _this.childElements[0] .height(h1); _this.childElements[1] .height(h2) .css('top', h1+separatorWidth); _this.splitter.css('top', h1); } if (percolateUp) { _this.root.parent('.editor-wrapper') .trigger('v-resize', [true ,false]); } if (! percolateDown) return; if (_this.childContainers[0]) { _this.childContainers[0].root .trigger('v-resize', [false, true]); } else if (_this.childContainers[1]) { _this.childContainers[1].root .trigger('v-resize', [false, true]); } }); this.root .trigger('h-resize', [false, false]) .trigger('v-resize', [false, false]); } SplitContainer.prototype = { setChild: function(idx, el){ if (el instanceof SplitContainer) { this.childElements[idx] = el.root; this.childContainers[idx] = el; el.idx = idx; } else { this.childElements[idx] = el; } this.childElements[idx].appendTo(this.root); this.cssInit(this.childElements[idx], idx); }, cssInit: function(el, idx){ var props = {}; var h1, h2, w1, w2, rh, rw; rh = this.root.height(); rw = this.root.width(); if (this.splitType === 'horizontal') { w1 = rw * this.splitProp - separatorWidth / 2; w2 = rw * (1 - this.splitProp) - separatorWidth / 2; if (idx === 0) { props = { left: 0, width: w1, height: rh, top: 0 }; } else { props = { left: w1 + separatorWidth, width: w2, height: rh, top: 0 }; } } else if (this.splitType === 'vertical') { h1 = rh * this.splitProp - separatorWidth / 2; h2 = rh * (1 - this.splitProp) - separatorWidth / 2; if (idx === 0) { props = { top: 0, height: h1, width: rw, left: 0 }; } else { props = { top: h1 + separatorWidth, height: h2, width: rw, left: 0 }; } } el.css(props); } }; ////////////////////////////////////////////////////////////////// // // Editor Component for Codiad // --------------------------- // Manage the lifecycle of Editor instances // ////////////////////////////////////////////////////////////////// codiad.editor = { /// Editor instances - One instance corresponds to an editor /// pane in the user interface. Different EditSessions /// (ace/edit_session) instances: [], /// Currently focussed editor activeInstance: null, // Settings for Editor instances settings: { autocomplete: false, theme: 'twilight', fontSize: '13px', printMargin: false, printMarginColumn: 80, highlightLine: true, indentGuides: true, wrapMode: false, softTabs: false, persistentModal: true, rightSidebarTrigger: false, fileManagerTrigger: false, tabSize: 4 }, multi_line: false, rootContainer: null, fileExtensionTextMode: {}, init: function(){ this.createSplitMenu(); this.createModeMenu(); var er = $('#editor-region'); er.on('h-resize-init', function(){ $('#editor-region > .editor-wrapper') .width($(this).width()) .trigger('h-resize'); }).on('v-resize-init', function(){ $('#editor-region > .editor-wrapper') .height($(this).height()) .trigger('v-resize'); }); $("#root-editor-wrapper").live("contextmenu", function( e ) { // Context Menu e.preventDefault(); _this = codiad.editor if( _this.getActive() ) { let path = codiad.active.getPath(); $(e.target).attr('data-path', path); codiad.filemanager.contextMenuShow( e, path, 'editor', 'editor'); $(this).addClass('context-menu-active'); } }); $(window).resize(function(){ $('#editor-region') .trigger('h-resize-init') .trigger('v-resize-init'); }); //Load settings when initially starting up that way the first editor //we open doesn't have the default settings. this.getSettings(); }, ////////////////////////////////////////////////////////////////// // // Retrieve editor settings from database // ////////////////////////////////////////////////////////////////// getSettings: function() { var boolVal = null; var _this = this; var options = [ 'editor.fontSize', 'editor.overScroll', 'editor.printMarginColumn', 'editor.tabSize', 'editor.theme', ]; var bool_options = [ 'editor.autocomplete', 'settings.autosave', 'editor.printMargin', 'editor.highlightLine', 'editor.indentGuides', 'editor.wrapMode', 'editor.rightSidebarTrigger', 'editor.fileManagerTrigger', 'editor.softTabs', 'editor.persistentModal', ]; $.each( options, async function( idx, key ) { let localValue = await codiad.settings.get_option( 'codiad.' + key ); if ( localValue != null ) { _this.settings[key.split('.').pop()] = localValue; } }); $.each( bool_options, async function(idx, key) { let localValue = await codiad.settings.get_option( 'codiad.' + key ); if ( localValue != null ) { _this.settings[key.split('.').pop()] = (localValue == 'true'); } }); }, ///////////////////////////////////////////////////////////////// // // Apply configuration settings // // Parameters: // i - {Editor} // ///////////////////////////////////////////////////////////////// applySettings: function(i) { // Check user-specified settings this.getSettings(); // Apply the current configuration settings: i.setTheme('ace/theme/' + this.settings.theme); i.setFontSize(this.settings.fontSize); i.setPrintMarginColumn(this.settings.printMarginColumn); i.setShowPrintMargin(this.settings.printMargin); i.setHighlightActiveLine(this.settings.highlightLine); i.setDisplayIndentGuides(this.settings.indentGuides); i.getSession().setUseWrapMode(this.settings.wrapMode); this.setTabSize(this.settings.tabSize, i); this.setSoftTabs(this.settings.softTabs, i); this.setOverScroll(this.settings.overScroll, i); i.setOptions({ enableBasicAutocompletion: true, enableSnippets: true, enableLiveAutocompletion: this.settings.autocomplete }); }, ////////////////////////////////////////////////////////////////// // // Create a new editor instance attached to given session // // Parameters: // session - {EditSession} Session to be used for new Editor instance // ////////////////////////////////////////////////////////////////// addInstance: function(session, where) { var el = $('
'); var chType, chArr = [], sc = null, chIdx = null; var _this = this; if (this.instances.length == 0) { // el.appendTo($('#editor-region')); el.appendTo($('#root-editor-wrapper')); } else { var ch = this.activeInstance.el; var root; chIdx = (where === 'top' || where === 'left') ? 0 : 1; chType = (where === 'top' || where === 'bottom') ? 'vertical' : 'horizontal'; chArr[chIdx] = el; chArr[1 - chIdx] = ch; root = $('
') .height(ch.height()) .width(ch.width()) .addClass('editor-wrapper-' + chType) .appendTo(ch.parent()); sc = new SplitContainer(root, chArr, chType); if (this.instances.length > 1) { var pContainer = this.activeInstance.splitContainer; var idx = this.activeInstance.splitIdx; pContainer.setChild(idx, sc); } } ace.require("ace/ext/language_tools"); var i = ace.edit(el[0]); var resizeEditor = function(){ i.resize(); }; if (sc) { i.splitContainer = sc; i.splitIdx = chIdx; this.activeInstance.splitContainer = sc; this.activeInstance.splitIdx = 1 - chIdx; sc.root .on('h-resize', resizeEditor) .on('v-resize', resizeEditor); if (this.instances.length === 1) { var re = function(){ _this.instances[0].resize(); }; sc.root .on('h-resize', re) .on('v-resize', re); } } i.el = el; this.setSession(session, i); this.changeListener(i); this.cursorTracking(i); this.bindKeys(i); this.instances.push(i); i.on('focus', function(){ _this.focus(i); }); return i; }, createSplitMenu: function(){ var _this = this; var _splitOptionsMenu = $('#split-options-menu'); this.initMenuHandler($('#split'),_splitOptionsMenu); $('#split-horizontally a').click(function(e){ e.stopPropagation(); _this.addInstance(_this.activeInstance.getSession(), 'bottom'); _splitOptionsMenu.hide(); }); $('#split-vertically a').click(function(e){ e.stopPropagation(); _this.addInstance(_this.activeInstance.getSession(), 'right'); _splitOptionsMenu.hide(); }); $('#merge-all a').click(function(e){ e.stopPropagation(); var s = _this.activeInstance.getSession(); _this.exterminate(); _this.addInstance(s); _splitOptionsMenu.hide(); }); }, createModeMenu: function(){ var _this = this; var _thisMenu = $('#changemode-menu'); var modeColumns = new Array(); var modeOptions = new Array(); var maxOptionsColumn = 15; var firstOption = 0; this.initMenuHandler($('#current-mode'),_thisMenu); var modes = Object.keys( ModeList.modesByName ).sort(); $.each(modes, function(i){ modeOptions.push('
  • '+modes[i]+'
  • '); }); var html = ''; while(true) { html += ''; firstOption = firstOption + maxOptionsColumn; if(firstOption >= modeOptions.length) { break; } } html += '
      '; if ((modeOptions.length-firstOption) < maxOptionsColumn) { max = modeOptions.length; } else { max = firstOption + maxOptionsColumn; } var currentcolumn = modeOptions.slice(firstOption, max); for (var option in currentcolumn) { html += currentcolumn[option]; } html += '
    '; _thisMenu.html(html); $('#changemode-menu a').click(function(e){ e.stopPropagation(); var newMode = "ace/mode/" + $(e.currentTarget).text(); var actSession = _this.activeInstance.getSession(); // handle async mode change var fn = function(){ _this.setModeDisplay(actSession); actSession.removeListener('changeMode', fn); }; actSession.on("changeMode", fn); actSession.setMode(newMode); _thisMenu.hide(); }); }, initMenuHandler: function(button,menu){ var _this = this; var thisButton = button; var thisMenu = menu; thisMenu.appendTo($('body')); thisButton.click(function(e){ var wh = $(window).height(); e.stopPropagation(); // close other menus _this.closeMenus(thisMenu); thisMenu.css({ // display: 'block', bottom: ( (wh - $(this).offset().top) + 8) + 'px', left: ($(this).offset().left - 13) + 'px' }); thisMenu.slideToggle('fast'); // handle click-out autoclosing var fn = function(){ thisMenu.hide(); $(window).off('click', fn); }; $(window).on('click', fn); }); }, closeMenus: function(exclude){ var menuId = exclude.attr("id"); if(menuId != 'split-options-menu') $('#split-options-menu').hide(); if(menuId != 'changemode-menu') $('#changemode-menu').hide(); }, setModeDisplay: function(session){ var currMode = session.getMode().$id; if(currMode){ currMode = currMode.substring(currMode.lastIndexOf('/') + 1); $('#current-mode').html(currMode); } else { $('#current-mode').html('text'); } }, ////////////////////////////////////////////////////////////////// // // Remove all Editor instances and clean up the DOM // ////////////////////////////////////////////////////////////////// exterminate: function() { $('.editor').remove(); $('.editor-wrapper').remove(); $('#editor-region').append($('
    ').attr('id', 'editor')); $('#current-file').html(''); $('#current-mode').html(''); this.instances = []; this.activeInstance = null; }, ////////////////////////////////////////////////////////////////// // // Detach EditSession session from all Editor instances replacing // them with replacementSession // ////////////////////////////////////////////////////////////////// removeSession: function(session, replacementSession) { for (var k = 0; k < this.instances.length; k++) { if (this.instances[k].getSession().path === session.path) { this.instances[k].setSession(replacementSession); } } if ($('#current-file').text() === session.path) { $('#current-file').text(replacementSession.path); } this.setModeDisplay(replacementSession); }, isOpen: function(session){ for (var k = 0; k < this.instances.length; k++) { if (this.instances[k].getSession().path === session.path) { return true; } } return false; }, ///////////////////////////////////////////////////////////////// // // Convenience function to iterate over Editor instances // // Parameters: // fn - {Function} callback called with each member as an argument // ///////////////////////////////////////////////////////////////// forEach: function(fn) { for (var k = 0; k < this.instances.length; k++) { fn.call(this, this.instances[k]); } }, ///////////////////////////////////////////////////////////////// // // Get the currently active Editor instance // // In a multi-pane setup this would correspond to the // editor pane user is currently working on. // ///////////////////////////////////////////////////////////////// getActive: function() { return this.activeInstance; }, ///////////////////////////////////////////////////////////////// // // Set an editor instance as active // // Parameters: // i - {Editor} // ///////////////////////////////////////////////////////////////// setActive: function(i) { if (! i) return; this.activeInstance = i; $('#current-file').text(i.getSession().path); this.setModeDisplay(i.getSession()); }, ///////////////////////////////////////////////////////////////// // // Change the EditSession of Editor instance // // Parameters: // session - {EditSession} // i - {Editor} // ///////////////////////////////////////////////////////////////// setSession: function(session, i) { i = i || this.getActive(); if (! this.isOpen(session)) { if (! i) { i = this.addInstance(session); } else { i.setSession(session); } } else { // Proxy session is required because scroll-position and // cursor position etc. are shared among sessions. var proxySession = new EditSession(session.getDocument(), session.getMode()); proxySession.setUndoManager(new UndoManager()); proxySession.path = session.path; proxySession.listThumb = session.listThumb; proxySession.tabThumb = session.tabThumb; if (! i) { i = this.addInstance(proxySession); } else { i.setSession(proxySession); } } this.applySettings(i); this.setActive(i); }, ///////////////////////////////////////////////////////////////// // // Select file mode by extension case insensitive // // Parameters: // e - {String} File extension // ///////////////////////////////////////////////////////////////// selectMode: function(e) { if(typeof(e) != 'string'){ return 'text'; } e = e.toLowerCase(); return( ModeList.getModeForPath( e ) ); }, ///////////////////////////////////////////////////////////////// // // Add an text mode for an extension // // Parameters: // extension - {String} File Extension // mode - {String} TextMode for this extension // ///////////////////////////////////////////////////////////////// addFileExtensionTextMode: function(extension, mode){ if(typeof(extension) != 'string' || typeof(mode) != 'string'){ if (console){ console.warn('wrong usage of addFileExtensionTextMode, both parameters need to be string'); } return; } mode = mode.toLowerCase(); this.fileExtensionTextMode[extension] = mode; }, ///////////////////////////////////////////////////////////////// // // clear all extension-text mode joins // ///////////////////////////////////////////////////////////////// clearFileExtensionTextMode: function(){ this.fileExtensionTextMode = {}; }, ///////////////////////////////////////////////////////////////// // // Set the editor mode // // Parameters: // m - {TextMode} mode // i - {Editor} Editor (Defaults to active editor) // ///////////////////////////////////////////////////////////////// setMode: function(m, i) { i = i || this.getActive(); // Check if mode is already loaded if (! editorModes[m]) { // Load the Mode var modeFile = 'components/editor/ace-editor/mode-' + m + '.js'; $.loadScript(modeFile, function() { // Mark the mode as loaded editorModes[m] = true; var EditorMode = ace.require('ace/mode/' + m).Mode; i.getSession().setMode(new EditorMode()); }, true); } else { var EditorMode = ace.require('ace/mode/' + m).Mode; i.getSession().setMode(new EditorMode()); } }, ///////////////////////////////////////////////////////////////// // // Set the editor theme // // Parameters: // t - {String} theme eg. twilight, cobalt etc. // i - {Editor} Editor instance (If omitted, Defaults to all editors) // // For a list of themes supported by Ace - refer : // https://github.com/ajaxorg/ace/tree/master/lib/ace/theme // // TODO: Provide support for custom themes // ///////////////////////////////////////////////////////////////// setTheme: function(t, i) { if (i) { // If a specific instance is specified, change the theme for // this instance i.setTheme('ace/theme/'+ t); } else { // Change the theme for the existing editor instances // and make it the default for new instances this.settings.theme = t; for (var k = 0; k < this.instances.length; k++) { this.instances[k].setTheme('ace/theme/'+ t); } } //Database codiad.settings.update_option( 'codiad.editor.theme', t ); }, ///////////////////////////////////////////////////////////////// // // Set contents of the editor // // Parameters: // c - {String} content // i - {Editor} (Defaults to active editor) // ///////////////////////////////////////////////////////////////// setContent: function(c, i) { i = i || this.getActive(); i.getSession().setValue(c); }, ///////////////////////////////////////////////////////////////// // // Set Font Size // // Set the font for all Editor instances and remember // the value for Editor instances to be created in // future // // Parameters: // s - {Number} font size // i - {Editor} Editor instance (If omitted, Defaults to all editors) // ///////////////////////////////////////////////////////////////// setFontSize: function(s, i) { if (i) { i.setFontSize(s); } else { this.settings.fontSize = s; this.forEach(function(i) { i.setFontSize(s); }); } //Database codiad.settings.update_option( 'codiad.editor.fontSize', s ); }, ///////////////////////////////////////////////////////////////// // // Enable/disable Highlighting of active line // // Parameters: // h - {Boolean} // i - {Editor} Editor instance ( If left out, setting is // applied to all editors ) // ///////////////////////////////////////////////////////////////// setHighlightLine: function(h, i) { if (i) { i.setHighlightActiveLine(h); } else { this.settings.highlightLine = h; this.forEach(function(i) { i.setHighlightActiveLine(h); }); } //Database codiad.settings.update_option( 'codiad.editor.highlightLine', h ); }, ////////////////////////////////////////////////////////////////// // // Show/Hide print margin indicator // // Parameters: // p - {Number} print margin column // i - {Editor} (If omitted, Defaults to all editors) // ////////////////////////////////////////////////////////////////// setPrintMargin: function(p, i) { if (i) { i.setShowPrintMargin(p); } else { this.settings.printMargin = p; this.forEach(function(i) { i.setShowPrintMargin(p); }); } //Database codiad.settings.update_option( 'codiad.editor.printMargin', p ); }, ////////////////////////////////////////////////////////////////// // // Set print margin column // // Parameters: // p - {Number} print margin column // i - {Editor} (If omitted, Defaults to all editors) // ////////////////////////////////////////////////////////////////// setPrintMarginColumn: function(p, i) { if (i) { i.setPrintMarginColumn(p); } else { this.settings.printMarginColumn = p; this.forEach(function(i) { i.setPrintMarginColumn(p); }); } //Database codiad.settings.update_option( 'codiad.editor.printMarginColumn', p ); }, ////////////////////////////////////////////////////////////////// // // Show/Hide indent guides // // Parameters: // g - {Boolean} // i - {Editor} (If omitted, Defaults to all editors) // ////////////////////////////////////////////////////////////////// setIndentGuides: function(g, i) { if (i) { i.setDisplayIndentGuides(g); } else { this.settings.indentGuides = g; this.forEach(function(i) { i.setDisplayIndentGuides(g); }); } //Database codiad.settings.update_option( 'codiad.editor.indentGuides', g ); }, ////////////////////////////////////////////////////////////////// // // Enable/Disable Code Folding // // Parameters: // f - {Boolean} // i - {Editor} (If omitted, Defaults to all editors) // ////////////////////////////////////////////////////////////////// setCodeFolding: function(f, i) { if (i) { i.setFoldStyle(f); } else { this.forEach(function(i) { i.setFoldStyle(f); }); } }, ////////////////////////////////////////////////////////////////// // // Enable/Disable Line Wrapping // // Parameters: // w - {Boolean} // i - {Editor} (If omitted, Defaults to all editors) // ////////////////////////////////////////////////////////////////// setWrapMode: function(w, i) { if (i) { i.getSession().setUseWrapMode(w); } else { this.forEach(function(i) { i.getSession().setUseWrapMode(w); }); } //Database codiad.settings.update_option( 'codiad.editor.wrapMode', w ); }, ////////////////////////////////////////////////////////////////// // // Set last position of modal to be saved // // Parameters: // t - {Boolean} (false for Automatic Position, true for Last Position) // i - {Editor} (If omitted, Defaults to all editors) // ////////////////////////////////////////////////////////////////// setPersistentModal: function(t, i) { this.settings.persistentModal = t; //Database codiad.settings.update_option( 'codiad.editor.persistentModal', t ); }, ////////////////////////////////////////////////////////////////// // // Set trigger for opening the right sidebar // // Parameters: // t - {Boolean} (false for Hover, true for Click) // i - {Editor} (If omitted, Defaults to all editors) // ////////////////////////////////////////////////////////////////// setRightSidebarTrigger: function(t, i) { this.settings.rightSidebarTrigger = t; //Database codiad.settings.update_option( 'codiad.editor.rightSidebarTrigger', t ); }, ////////////////////////////////////////////////////////////////// // // Set trigger for clicking on the filemanager // // Parameters: // t - {Boolean} (false for Hover, true for Click) // i - {Editor} (If omitted, Defaults to all editors) // ////////////////////////////////////////////////////////////////// setFileManagerTrigger: function(t, i) { this.settings.fileManagerTrigger = t; //Database codiad.settings.update_option( 'codiad.editor.fileManagerTrigger', t ); codiad.project.loadSide(); }, ////////////////////////////////////////////////////////////////// // // set Tab Size // // Parameters: // s - size // i - {Editor} (If omitted, Defaults to all editors) // ////////////////////////////////////////////////////////////////// setTabSize: function(s, i) { if (i) { i.getSession().setTabSize(parseInt(s)); } else { this.forEach(function(i) { i.getSession().setTabSize(parseInt(s)); }); } //Database codiad.settings.update_option( 'codiad.editor.tabSize', s ); }, ////////////////////////////////////////////////////////////////// // // Enable or disable Soft Tabs // // Parameters: // t - true / false // i - {Editor} (If omitted, Defaults to all editors) // ////////////////////////////////////////////////////////////////// setSoftTabs: function(t, i) { if (i) { i.getSession().setUseSoftTabs(t); } else { this.forEach(function(i) { i.getSession().setUseSoftTabs(t); }); } //Database codiad.settings.update_option( 'codiad.editor.softTabs', t ); }, ////////////////////////////////////////////////////////////////// // // Get content from editor // // Parameters: // i - {Editor} (Defaults to active editor) // ////////////////////////////////////////////////////////////////// getContent: function(i) { i = i || this.getActive(); if (! i) return; var content = i.getSession().getValue(); if (!content) { content = ' '; } // Pass something through return content; }, ////////////////////////////////////////////////////////////////// // // Resize the editor - Trigger the editor to readjust its layout // esp if the container has been resized manually. // // Parameters: // i - {Editor} (Defaults to active editor) // ////////////////////////////////////////////////////////////////// resize: function(i) { i = i || this.getActive(); if (! i) return; i.resize(); }, ////////////////////////////////////////////////////////////////// // // Mark the instance as changed (in the user interface) // upon change in the document content. // // Parameters: // i - {Editor} // ////////////////////////////////////////////////////////////////// changeListener: function(i) { var _this = this; i.on('change', function() { codiad.active.markChanged(_this.getActive().getSession().path); }); }, ////////////////////////////////////////////////////////////////// // // Get Selected Text // // Parameters: // i - {Editor} (Defaults to active editor) // ////////////////////////////////////////////////////////////////// getSelectedText: function(i) { i = i || this.getActive(); if (! i) return; return i.getCopyText(); }, ////////////////////////////////////////////////////////////////// // // Insert text // // Parameters: // val - {String} Text to be inserted // i - {Editor} (Defaults to active editor) // ////////////////////////////////////////////////////////////////// insertText: function(val, i) { i = i || this.getActive(); if (! i) return; i.insert(val); }, ////////////////////////////////////////////////////////////////// // // Move the cursor to a particular line // // Parameters: // line - {Number} Line number // i - {Editor} Editor instance // ////////////////////////////////////////////////////////////////// gotoLine: function(line, i) { i = i || this.getActive(); if (! i) return; i.gotoLine(line, 0, true); }, ////////////////////////////////////////////////////////////////// // // Focus an editor // // Parameters: // i - {Editor} Editor instance (Defaults to current editor) // ////////////////////////////////////////////////////////////////// focus: function(i) { i = i || this.getActive(); this.setActive(i); if (! i) return; i.focus(); codiad.active.focus(i.getSession().path); this.cursorTracking(i); }, ////////////////////////////////////////////////////////////////// // // Setup Cursor Tracking // // Parameters: // i - {Editor} (Defaults to active editor) // ////////////////////////////////////////////////////////////////// cursorTracking: function(i) { i = i || this.getActive(); if (! i) return; clearInterval(codiad._cursorPoll); codiad._cursorPoll = setInterval(function() { $('#cursor-position') .html(i18n('Ln') + ': ' + (i.getCursorPosition().row + 1) + ' · ' + i18n('Col') + ': ' + i.getCursorPosition().column ); }, 100); }, ////////////////////////////////////////////////////////////////// // // Setup Key bindings // // Parameters: // i - {Editor} // ////////////////////////////////////////////////////////////////// bindKeys: function(i) { var _this = this; // Find i.commands.addCommand({ name: 'Find', bindKey: { win: 'Ctrl-F', mac: 'Command-F' }, exec: function(e) { _this.openSearch('find'); } }); // Find + Replace i.commands.addCommand({ name: 'Replace', bindKey: { win: 'Ctrl-R', mac: 'Command-R' }, exec: function(e) { _this.openSearch('replace'); } }); i.commands.addCommand({ name: 'Move Up', bindKey: { win: 'Ctrl-up', mac: 'Command-up' }, exec: function(e) { codiad.active.move('up'); } }); i.commands.addCommand({ name: 'Move Down', bindKey: { win: 'Ctrl-down', mac: 'Command-up' }, exec: function(e) { codiad.active.move('down'); } }); }, ////////////////////////////////////////////////////////////////// // // Present the Search (Find + Replace) dialog box // // Parameters: // type - {String} Optional, defaults to find. Provide 'replace' for replace dialog. // ////////////////////////////////////////////////////////////////// openSearch: function(type) { if (this.getActive()) { codiad.modal.load(400, 'components/editor/dialog.php?action=search&type=' + type); codiad.modal.hideOverlay(); } else { codiad.message.error('No Open Files'); } }, ////////////////////////////////////////////////////////////////// // // Perform Search (Find + Replace) operation // // Parameters: // action - {String} find | replace | replaceAll // i - {Editor} Defaults to active Editor instance // ////////////////////////////////////////////////////////////////// search: function(action, i) { i = i || this.getActive(); if (! i) return; if( this.multi_line ) { var find = $('#modal textarea[name="find"]') .val(); var replace = $('#modal textarea[name="replace"]') .val(); } else { var find = $('#modal input[name="find"]') .val(); var replace = $('#modal input[name="replace"]') .val(); } switch (action) { case 'find': i.find(find, { backwards: false, wrap: true, caseSensitive: false, wholeWord: false, regExp: false }); break; case 'replace': i.find(find, { backwards: false, wrap: true, caseSensitive: false, wholeWord: false, regExp: false }); i.replace(replace); break; case 'replaceAll': i.find(find, { backwards: false, wrap: true, caseSensitive: false, wholeWord: false, regExp: false }); i.replaceAll(replace); break; } }, ////////////////////////////////////////////////////////////////// // // Enable editor // // Parameters: // i - {Editor} (Defaults to active editor) // ////////////////////////////////////////////////////////////////// enableEditing: function(i) { i = i || this.getActive(); if (! i) return; i.textInput.setReadOnly( false ); }, ////////////////////////////////////////////////////////////////// // // Disable editor // // Parameters: // i - {Editor} (Defaults to active editor) // ////////////////////////////////////////////////////////////////// disableEditing: function(i) { i = i || this.getActive(); if (! i) return; i.textInput.setReadOnly( true ); }, ////////////////////////////////////////////////////////////////// // // Set Overscroll // // Parameters: // i - {Editor} (Defaults to active editor) // ////////////////////////////////////////////////////////////////// setOverScroll: function( s, i ) { if (i) { i.setOption( "scrollPastEnd", s ); } else { this.settings.overScroll = s; this.forEach(function(i) { i.setOption( "scrollPastEnd", s ); }); } //Database codiad.settings.update_option( 'codiad.editor.overScroll', s ); }, setLiveAutocomplete: function( s, i ) { if (i) { i.setOptions({ enableLiveAutocompletion: s }); } else { this.settings.autocomplete = s; this.forEach(function(i) { i.setOptions({ enableLiveAutocompletion: s }); }); } //Database codiad.settings.update_option( 'codiad.editor.autocomplete', s ); }, toggleMultiLine: function( e ) { if( e.innerText === "Multi Line" ) { this.multi_line = true; e.innerText = "Single Line"; $('input[name="find"]').hide(); $('textarea[name="find"]').show(); $('textarea[name="find"]').val( $('input[name="find"]').val() ); $('input[name="replace"]').hide(); $('textarea[name="replace"]').show(); $('textarea[name="replace"]').val( $('input[name="replace"]').val() ); } else { this.multi_line = false; e.innerText = "Multi Line"; $('input[name="find"]').show(); $('textarea[name="find"]').hide(); $('input[name="find"]').val( $('textarea[name="find"]').val() ); $('input[name="replace"]').show(); $('textarea[name="replace"]').hide(); $('input[name="replace"]').val( $('textarea[name="replace"]').val() ); } }, paste: function( ) { navigator.clipboard.readText().then(text => {codiad.editor.getActive().insert( text )}); }, openSort: function() { if ( this.getActive() && codiad.active.getSelectedText() != "" ) { codiad.modal.load( 400, 'components/editor/dialog.php?action=sort' ); codiad.modal.hideOverlay(); } else { codiad.message.error('No text selected'); } }, sort: function( eol ) { let text = $('#modal textarea[name="sort"]').val(); let array = text.split( eol ); array = array.sort( codiad.editor.sort_a ); let sorted = array.join( eol ); console.log( text, eol, array, sorted ); codiad.modal.unload(); codiad.editor.getActive().insert( sorted ); codiad.editor.getActive().focus(); }, sort_a: function( a, b ) { let pos = 0; let case_sensitive = $( '#modal input[name="case_sensitive"]' ).prop( 'checked' ) if( ! case_sensitive ) { a = a.toLowerCase(); b = b.toLowerCase(); } if( a < b ) { pos = -1; } else if( a > b ) { pos = 1; } return pos; } }; })(this, jQuery);