editor.js

来自「在线编辑器」· JavaScript 代码 · 共 1,381 行 · 第 1/4 页

JS
1,381
字号
/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1 * * 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. * * The Original Code is Bespin. * * The Initial Developer of the Original Code is Mozilla. * Portions created by the Initial Developer are Copyright (C) 2009 * the Initial Developer. All Rights Reserved. * * Contributor(s): *   Bespin Team (bespin@mozilla.com) * * ***** END LICENSE BLOCK ***** */dojo.provide("bespin.editor.editor");dojo.require("bespin.util.clipboard");// = Editor =//// This is the guts. The metal. The core editor has most of its classes living in here://// * {{{bespin.editor.API}}} : The editor API itself// * {{{bespin.editor.UI}}} : Knowledge of the UI pieces of the editor is here, bulk of the code. paints!// * {{{bespin.editor.Scrollbar}}} : The custom scrollbar (to be factored out and use TH scrollbar instead)// * {{{bespin.editor.SelectionHelper}}} : Handle text selection// * {{{bespin.editor.DefaultEditorKeyListener}}} : Key listener operations// * {{{bespin.editor.Rect}}} : Helper to hold a rectangle// * {{{bespin.editor.Events}}} : Helper to hold a rectangle// * {{{bespin.editor.Utils}}} : Blobby utility object to do common things//// * {{{bespin.editor.Actions}}} : The actions that the editor can do (can be added too) are are in actions.js// ** {{{ bespin.editor.Scrollbar }}} **//// some state mgmt. for scrollbars; not a true componentdojo.declare("bespin.editor.Scrollbar", null, {    HORIZONTAL: "horizontal",    VERTICAL: "vertical",    MINIMUM_HANDLE_SIZE: 20,    constructor: function(ui, orientation, rect, value, min, max, extent) {        this.ui = ui;        this.orientation = orientation; // "horizontal" or "vertical"        this.rect = rect;       // position/size of the scrollbar track        this.value = value;     // current offset value        this.min = min;         // minimum offset value        this.max = max;         // maximum offset value        this.extent = extent;   // size of the current visible subset        this.mousedownScreenPoint;    // used for scroll bar dragging tracking; point at which the mousedown first occurred        this.mousedownValue;          // value at time of scroll drag start    },    // return a Rect for the scrollbar handle    getHandleBounds: function() {        var sx = (this.isH()) ? this.rect.x : this.rect.y;        var sw = (this.isH()) ? this.rect.w : this.rect.h;        var smultiple = this.extent / (this.max + this.extent);        var asw = smultiple * sw;        if (asw < this.MINIMUM_HANDLE_SIZE) asw = this.MINIMUM_HANDLE_SIZE;        sx += (sw - asw) * (this.value / (this.max - this.min));        return (this.isH()) ? new bespin.editor.Rect(Math.floor(sx), this.rect.y, asw, this.rect.h) : new bespin.editor.Rect(this.rect.x, sx, this.rect.w, asw);    },    isH: function() {        return (!(this.orientation == this.VERTICAL));    },    fixValue: function(value) {        if (value < this.min) value = this.min;        if (value > this.max) value = this.max;        return value;    },    onmousewheel: function(e) {                   var wheel = bespin.util.mousewheelevent.wheel(e);        var axis = bespin.util.mousewheelevent.axis(e);         if (this.orientation == this.VERTICAL && axis == this.VERTICAL) {            this.setValue(this.value + (wheel * this.ui.lineHeight));        } else if (this.orientation == this.HORIZONTAL && axis == this.HORIZONTAL) {            this.setValue(this.value + (wheel * this.ui.charWidth));        }      },        onmousedown: function(e) {        var clientY = e.clientY - this.ui.getTopOffset();        var clientX = e.clientX - this.ui.getLeftOffset();        var bar = this.getHandleBounds();        if (bar.contains({ x: clientX, y: clientY })) {            this.mousedownScreenPoint = (this.isH()) ? e.screenX : e.screenY;            this.mousedownValue = this.value;        } else {            var p = (this.isH()) ? clientX : clientY;            var b1 = (this.isH()) ? bar.x : bar.y;            var b2 = (this.isH()) ? bar.x2 : bar.y2;            if (p < b1) {                this.setValue(this.value -= this.extent);            } else if (p > b2) {                this.setValue(this.value += this.extent);            }        }    },    onmouseup: function(e) {        this.mousedownScreenPoint = null;        this.mousedownValue = null;        if (this.valueChanged) this.valueChanged(); // make the UI responsive when the user releases the mouse button (in case arrow no longer hovers over scrollbar)    },    onmousemove: function(e) {        if (this.mousedownScreenPoint) {            var diff = ((this.isH()) ? e.screenX : e.screenY) - this.mousedownScreenPoint;            var multiplier = diff / (this.isH() ? this.rect.w : this.rect.h);            this.setValue(this.mousedownValue + Math.floor(((this.max + this.extent) - this.min) * multiplier));        }    },    setValue: function(value) {        this.value = this.fixValue(value);        if (this.valueChanged) this.valueChanged();    }});// ** {{{ bespin.editor.Rect }}} **//// treat as immutable (pretty please)dojo.declare("bespin.editor.Rect", null, {    constructor: function(x, y, w, h) {        this.x = x;        this.y = y;        this.w = w;        this.h = h;        this.x2 = x + w;        this.y2 = y + h;    },    // inclusive of bounding lines    contains: function(point) {        if (!this.x) return false;        return ((this.x <= point.x) && ((this.x + this.w) >= point.x) && (this.y <= point.y) && ((this.y + this.h) >= point.y));    }});// ** {{{ bespin.editor.SelectionHelper }}} **dojo.declare("bespin.editor.SelectionHelper", null, {    constructor: function(editor) {        this.editor = editor;    },    // returns an object with the startCol and endCol of the selection. If the col is -1 on the endPos, the selection goes for the entire line    // returns undefined if the row has no selection    getRowSelectionPositions: function(rowIndex) {        var startCol;        var endCol;        var selection = this.editor.getSelection();        if (!selection) return undefined;        if ((selection.endPos.row < rowIndex) || (selection.startPos.row > rowIndex)) return undefined;        startCol = (selection.startPos.row < rowIndex) ? 0 : selection.startPos.col;        endCol = (selection.endPos.row > rowIndex) ? -1 : selection.endPos.col;        return { startCol: startCol, endCol: endCol }    }}); // ** {{{ bespin.editor.utils }}} **//// Mess with positions mainly dojo.mixin(bespin.editor, { utils: {    buildArgs: function(oldPos) {        return { pos: bespin.editor.utils.copyPos(oldPos || _editor.cursorPosition) };        },    changePos: function(args, pos) {        ar        return { pos: bespin.editor.utils.copyPos(oldPos || _editor.cursorPosition) };        },        copyPos: function(oldPos) {        return { row: oldPos.row, col: oldPos.col };    },    posEquals: function(pos1, pos2) {        if (pos1 == pos2) return true;        if (!pos1 || !pos2) return false;        return (pos1.col == pos2.col) && (pos1.row == pos2.row);    },    diffObjects: function(o1, o2) {        var diffs = {};        if (!o1 || !o2) return undefined;                for (var key in o1) {            if (o2[key]) {                if (o1[key] != o2[key]) {                    diffs[key] = o1[key] + " => " + o2[key];                }            } else {                diffs[key] = "o1: " + key + " = " + o1[key];            }        }        for (var key2 in o2) {            if (!o1[key2]) {                diffs[key2] = "o2: " + key2 + " = " + o2[key2];            }        }        return diffs;    }}});// ** {{{ bespin.editor.DefaultEditorKeyListener }}} **// // Core key listener to decide which actions to rundojo.declare("bespin.editor.DefaultEditorKeyListener", null, {    constructor: function(editor) {        this.editor = editor;        this.actions = editor.ui.actions;        this.skipKeypress = false;        this.defaultKeyMap = {};        // Allow for multiple key maps to be defined        this.keyMap = this.defaultKeyMap;    },    bindKey: function(keyCode, metaKey, ctrlKey, altKey, shiftKey, action) {        this.defaultKeyMap[[keyCode, metaKey, ctrlKey, altKey, shiftKey]] =             (typeof action == "string") ?                function() {                     var toFire = bespin.events.toFire(action);                    bespin.publish(toFire.name, toFire.args);                } : dojo.hitch(this.actions, action);    },    bindKeyString: function(modifiers, keyCode, action) {        var ctrlKey = (modifiers.toUpperCase().indexOf("CTRL") != -1);        var altKey = (modifiers.toUpperCase().indexOf("ALT") != -1);        var metaKey = (modifiers.toUpperCase().indexOf("META") != -1) || (modifiers.toUpperCase().indexOf("APPLE") != -1) || (modifiers.toUpperCase().indexOf("CMD") != -1);        var shiftKey = (modifiers.toUpperCase().indexOf("SHIFT") != -1);        return this.bindKey(keyCode, metaKey, ctrlKey, altKey, shiftKey, action);    },        bindKeyStringSelectable: function(modifiers, keyCode, action) {        this.bindKeyString(modifiers, keyCode, action);        this.bindKeyString("SHIFT " + modifiers, keyCode, action);    },    onkeydown: function(e) {        var handled = _commandLine.handleCommandLineFocus(e);        if (handled) return false;        var args = { event: e, pos: bespin.editor.utils.copyPos(this.editor.cursorPosition) }        this.skipKeypress = false;        this.returnValue = false;        var action = this.keyMap[[e.keyCode, e.metaKey, e.ctrlKey, e.altKey, e.shiftKey]];        var hasAction = false;        if (dojo.isFunction(action)) {            hasAction = true;            action(args);            this.lastAction = action;        }        // If a special key is pressed OR if an action is assigned to a given key (e.g. TAB or BACKSPACE)        if (e.metaKey || e.ctrlKey || e.altKey) {            this.skipKeypress = true;            this.returnValue = true;        }        // stop going, but allow special strokes to get to the browser        if (hasAction || !bespin.util.keys.passThroughToBrowser(e)) dojo.stopEvent(e);    },    onkeypress: function(e) {        var handled = _commandLine.handleCommandLineFocus(e);        if (handled) return false;                // This is to get around the Firefox bug that happens the first time of jumping between command line and editor        // Bug https://bugzilla.mozilla.org/show_bug.cgi?id=478686        if (e.charCode == 'j'.charCodeAt() && e.ctrlKey) {            dojo.stopEvent(e);            return false;        }        // If key should be skipped, BUT there are some chars like "@|{}[]\" that NEED the ALT- or CTRL-key to be accessable        // on some platforms and keyboardlayouts (german?). This is not working for "^"        if ([64 /*@*/, 91/*[*/, 92/*\*/, 93/*]*/, 94/*^*/, 123/*{*/, 124/*|*/, 125/*}*/, 126/*~*/ ].indexOf(e.charCode) != -1) {            this.skipKeypress = false;        } else if (this.skipKeypress) {            if (!bespin.util.keys.passThroughToBrowser(e)) dojo.stopEvent(e);            return this.returnValue;        }        var args = { event: e, pos: bespin.editor.utils.copyPos(this.editor.cursorPosition) };        var actions = this.editor.ui.actions;        // Only allow ascii through        if ((e.charCode >= 32) && (e.charCode <= 126) || e.charCode >= 160) {            args.newchar = String.fromCharCode(e.charCode);            actions.insertCharacter(args);        } else { // Allow user to move with the arrow continuously            var action = this.keyMap[[e.keyCode, e.metaKey, e.ctrlKey, e.altKey, e.shiftKey]];            if (this.lastAction == action) {                delete this.lastAction;            } else if (typeof action == "function") {               action(args);            }        }        dojo.stopEvent(e);    }});// ** {{{ bespin.editor.UI }}} **//// Holds the UI. The editor itself, the syntax highlighter, the actions, and moredojo.declare("bespin.editor.UI", null, {    constructor: function(editor) {        this.editor = editor;        this.syntaxModel = new bespin.syntax.Model(editor);        this.selectionHelper = new bespin.editor.SelectionHelper(editor);        this.actions = new bespin.editor.Actions(editor);

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?