/***** BEGIN LICENSE BLOCK ***** {{{ * * Mozilla Public License Notice * * The contents of this file are subject to the Mozilla Public License * Version 1.1 (the "License"); you may not use this file except in * compliance with the License. You may obtain a copy of the License * at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See * the License for the specific language governing rights and * limitations under the License. * }}} ***** END LICENSE BLOCK *****/ // command names taken from: // http://developer.mozilla.org/en/docs/Editor_Embedding_Guide function Editor() //{{{ { // store our last search with f,F,t or T var last_findChar = null; var last_findChar_func = null; function editor() { return window.document.commandDispatcher.focusedElement; } function getController() { var el = window.document.commandDispatcher.focusedElement; if (!el || !el.controllers) return null; return el.controllers.getControllerAt(0); } this.line = function() { var line = 1; var text = editor().value; for (var i = 0; i < editor().selectionStart; i++) if (text[i] == "\n") line++; return line; } this.col = function() { var col = 1; var text = editor().value; for (var i = 0; i < editor().selectionStart; i++) { col++; if (text[i] == "\n") col = 1; } return col; } this.unselectText = function() { var elt = window.document.commandDispatcher.focusedElement; elt.selectionEnd = elt.selectionStart; return true; } this.selectedText = function() { var text = editor().value; return text.substring(editor().selectionStart, editor().selectionEnd); } this.pasteClipboard = function() { var elt = window.document.commandDispatcher.focusedElement; if (elt.setSelectionRange && readFromClipboard()) // readFromClipboard would return 'undefined' if not checked // dunno about .setSelectionRange { var rangeStart = elt.selectionStart; // caret position var rangeEnd = elt.selectionEnd; var tempStr1 = elt.value.substring(0,rangeStart); var tempStr2 = readFromClipboard(); var tempStr3 = elt.value.substring(rangeEnd); elt.value = tempStr1 + tempStr2 + tempStr3; elt.selectionStart = rangeStart + tempStr2.length; elt.selectionEnd = elt.selectionStart; } } // count is optional, defaults to 1 this.executeCommand = function(cmd, count) { var controller = getController(); var el = window.document.commandDispatcher.focusedElement; if (!controller || !el) return false; if (!controller.supportsCommand(cmd) || !controller.isCommandEnabled(cmd)) { vimperator.beep(); return false; } if (typeof count != "number" || count < 1) count = 1; var did_command = false; while(count--) { // some commands need this try/catch workaround, because a cmd_charPrevious triggered // at the beginning of the textarea, would hang the doCommand() // good thing is, we need this code anyway for proper beeping try { controller.doCommand(cmd); did_command = true; } catch(e) { if (!did_command) vimperator.beep(); return false; } } return true; } // cmd = y, d, c // motion = b, 0, gg, G, etc. this.executeCommandWithMotion = function(cmd, motion, count) { if (!typeof count == "number" || count < 1) count = 1; if (cmd == motion) { motion = "j"; count--; } switch (motion) { case "j": this.executeCommand("cmd_beginLine", 1); this.executeCommand("cmd_selectLineNext", count+1); break; case "k": this.executeCommand("cmd_beginLine", 1); this.executeCommand("cmd_lineNext", 1); this.executeCommand("cmd_selectLinePrevious", count+1); break; case "h": this.executeCommand("cmd_selectCharPrevious", count); break; case "l": this.executeCommand("cmd_selectCharNext", count); break; case "e": case "w": this.executeCommand("cmd_selectWordNext", count); break; case "b": this.executeCommand("cmd_selectWordPrevious", count); break; case "0": case "^": this.executeCommand("cmd_selectBeginLine", 1); break; case "$": this.executeCommand("cmd_selectEndLine", 1); break; case "gg": this.executeCommand("cmd_endLine", 1); this.executeCommand("cmd_selectTop", 1); this.executeCommand("cmd_selectBeginLine", 1); break; case "G": this.executeCommand("cmd_beginLine", 1); this.executeCommand("cmd_selectBottom", 1); this.executeCommand("cmd_selectEndLine", 1); break; default: vimperator.beep(); return false; } switch (cmd) { case "d": this.executeCommand("cmd_delete", 1); // need to reset the mode as the visual selection changes it vimperator.setMode(vimperator.modes.TEXTAREA); break; case "c": this.executeCommand("cmd_delete", 1); this.startInsert(); break; case "y": this.executeCommand("cmd_copy", 1); this.unselectText(); break; default: vimperator.beep(); return false; } return true; } this.startNormal = function() { vimperator.setMode(vimperator.modes.TEXTAREA); //var self = this; //editor().addEventListener("mouseclick", function() { // setTimeout(function() { // pos = editor().selectionStart; // self.moveCaret(pos); // }, 10); //}, false); } this.startInsert = function() { vimperator.setMode(vimperator.modes.INSERT, vimperator.modes.TEXTAREA); } // This function will move/select up to given "pos" // Simple setSelectionRange() would be better, but we want to maintain the correct // order of selectionStart/End (a firefox bug always makes selectionStart <= selectionEnd) // Use only for small movements! this.moveToPosition = function(pos, forward, select) { if (!select) { editor().setSelectionRange(pos, pos); return; } if (forward) { if (pos <= editor().selectionEnd || pos > editor().value.length) return false; do // TODO: test code for endless loops { this.executeCommand("cmd_selectCharNext", 1); } while ( editor().selectionEnd != pos ); } else { if (pos >= editor().selectionStart || pos < 0) return false; do // TODO: test code for endless loops { this.executeCommand("cmd_selectCharPrevious", 1); } while ( editor().selectionStart != pos ); } } // returns the position of char this.findCharForward = function(char, count) { if (!editor()) return -1; last_findChar = char; last_findChar_func = this.findCharForward; var text = editor().value; if (!typeof count == "number" || count < 1) count = 1; for (var i = editor().selectionEnd + 1; i < text.length; i++) { if (text[i] == "\n") break; if (text[i] == char) count--; if (count == 0) return i + 1; // always position the cursor after the char } vimperator.beep(); return -1; } // returns the position of char this.findCharBackward = function(char, count) { if (!editor()) return -1; last_findChar = char; last_findChar_func = this.findCharBackward; var text = editor().value; if (!typeof count == "number" || count < 1) count = 1; for (var i = editor().selectionStart - 1; i >= 0; i--) { if (text[i] == "\n") break; if (text[i] == char) count--; if (count == 0) return i; } vimperator.beep(); return -1; } } //}}} // vim: set fdm=marker sw=4 ts=4 et: