在Math Editor中实现Undo-Redo

时间:2013-02-07 09:20:53

标签: browser cursor wysiwyg text-editor undo-redo

我需要在Web应用程序中实现Undo / Redo系统,该系统使用MathQuill JavaScript Library在文本编辑器中定义事件。虽然我可以使用一组包含状态和推/弹调用等的对象来自己实现自定义解决方案,但是在处理所有状态和光标位置时存在很多故障。无论如何我可以在Mathquill Library中实现它,或者我可以在我的编辑器中启用浏览器内置的undo-redo功能。

1 个答案:

答案 0 :(得分:0)

我也需要这个功能,需要在不修改原始 MathQuill 库的情况下拥有它。

我就是这样做的:

/* * * * * * * * * * * * * * * 
 * File: UndoRedoManager.js  *
 * * * * * * * * * * * * * * */

const TAB_KEYCODE = 9;
const ENTER_KEYCODE = 13;
const SHIFT_KEYCODE = 16;
const CTRL_KEYCODE = 17;
const ALT_KEYCODE = 18;
const CAPSLOCK_KEYCODE = 20;
const ESCAPE_KEYCODE = 27;
const PAGEUP_KEYCODE = 33;
const PAGEDOWN_KEYCODE = 34;
const END_KEYCODE = 35;
const HOME_KEYCODE = 36;
const LEFTARROW_KEYCODE = 37;
const UPARROW_KEYCODE = 38;
const RIGHTARROW_KEYCODE = 39;
const DOWNARROW_KEYCODE = 40;
const V_KEYCODE = 86;
const Y_KEYCODE = 89;
const Z_KEYCODE = 90;
const ALTGR_KEYCODE = 225;

const unaffectingKeys = [TAB_KEYCODE,
                        ENTER_KEYCODE,
                        SHIFT_KEYCODE,
                        CTRL_KEYCODE,
                        ALT_KEYCODE,
                        CAPSLOCK_KEYCODE,
                        ESCAPE_KEYCODE,
                        LEFTARROW_KEYCODE,
                        PAGEUP_KEYCODE,
                        PAGEDOWN_KEYCODE,
                        END_KEYCODE,
                        HOME_KEYCODE,
                        LEFTARROW_KEYCODE,
                        UPARROW_KEYCODE,
                        RIGHTARROW_KEYCODE,
                        DOWNARROW_KEYCODE,
                        ALTGR_KEYCODE];

function UndoRedoManager(pMathField, pElement) {
    this.mathField = pMathField;
    this.contentEl = pElement;
    this.typedHistory = [this.mathField.latex()];
    this.ctrlIsDown = false;
    this.YIsDown = false;
    this.ZIsDown = false;
    this.currentState = 0;
    this.buffSize = 50;

    this.rearrangeTypedArray = () => {
        if (this.typedHistory.length > this.buffSize) {
            let sizeOverflow = this.typedHistory.length - this.buffSize;
            this.currentState = this.currentState - sizeOverflow;
            this.typedHistory = this.typedHistory.slice(this.buffSize * (-1));
        }
    };

    this.isKeyIsUnaffecting = (pKey) => {
        return unaffectingKeys.includes(pKey);
    };

    this.checkIfSpecialKeysAreUpAndSetStates = (pUppedKey) => {
        switch (pUppedKey) {
            case CTRL_KEYCODE:
                this.ctrlIsDown = false;
                break;

            case Y_KEYCODE:
                this.YIsDown = false;
                break;

            case Z_KEYCODE:
                this.ZIsDown = false;
                break;
        }
    }

    this.checkIfSpecialKeysAreDownAndSetStates = (pDownedKey) => {
        switch (pDownedKey) {
            case CTRL_KEYCODE:
                this.ctrlIsDown = true;
                break;

            case Y_KEYCODE:
                this.YIsDown = true;
                break;

            case Z_KEYCODE:
                this.ZIsDown = true;
                break;
        }
    }

    this.saveState = () => {
        if (this.currentState !== (this.typedHistory.length - 1)) {
            this.typedHistory = this.typedHistory.slice(0, (this.currentState + 1));
        }   
        
        this.typedHistory.push(this.mathField.latex());
        this.rearrangeTypedArray();
        this.currentState++;
    };

    this.undo = () => {
        if (this.currentState !== 0) {
            this.currentState--;
            this.mathField.latex(this.typedHistory[this.currentState]);
        }  else {
            //console.log('do nothing');
        }
    };

    this.redo = () => {
        if (this.currentState < (this.typedHistory.length - 1)) {
            this.currentState++;
            this.mathField.latex(this.typedHistory[this.currentState]);
        } else {
            //console.log('do nothing');
        }
    };

    this.contentEl.on('keyup', (e) => {
        this.checkIfSpecialKeysAreUpAndSetStates(e.which);
        
        //log in typedHistory
        if ((this.isKeyIsUnaffecting(e.which) === false)
            && (this.ctrlIsDown === false || (this.ctrlIsDown && e.which === V_KEYCODE))) {
            this.saveState();
        }
    });

    this.contentEl.on('keydown', (e) => {
        this.checkIfSpecialKeysAreDownAndSetStates(e.which);

        //ctrl+z
        if (this.ctrlIsDown && this.ZIsDown) {
            this.undo();
        }

        //ctrl + y
        if (this.ctrlIsDown && this.YIsDown) {
            this.redo();
        }
    });
}

然后我们就这样使用它:

/* * * * * * * * * *  
 * File: index.js  *
 * * * * * * * * * */

/* we consider that there is a DOM element we want to write Math in, with the id "math_content", like <div id="mathContent"></div> */

// Here nothing special, we use the lib as usual
const MQ = MathQuill.getInterface(2);
const mathContentEl = $('#mathContent');
const mathField = MQ.MathField(mathContentEl, {
    handlers: {
        edit: () => {
            
        },
    
        enter: () => {
                
        }
    }
});

// And we add the undo/redo feature like this
const undoManager = new UndoRedoManager(mathField, mathContentEl);

对我来说效果很好