Text Editor

by zzzzBov

HTML

<div data-editable=""></div>

CSS

@keyframes caret {
    0% {
        background-color: rgba(0, 0, 0, 0.5);
        border-left-color: rgba(0, 0, 0, 1);
    }
    49.9% {
        background-color: rgba(0, 0, 0, 0.5);
        border-left-color: rgba(0, 0, 0, 1);
    }
    50% {
        background-color: rgba(0, 0, 0, 0);
        border-left-color: rgba(0, 0, 0, 0);
    }
    100% {
        background-color: rgba(0, 0, 0, 0);
        border-left-color: rgba(0, 0, 0, 0);
    }
}

[data-editable] {
    border: 1px solid #888;
    cursor: text;
    font-family: "Courier New", monospace;
    overflow: auto;
    padding: 2px 0 0 2px;
    min-height: 100px;
    white-space: pre;
}

.caret {
    background-color: transparent;
    border-left: 1px solid transparent;
    box-sizing: border-box;
    display: inline-block;
    height: 1em;
    margin-right: -1px;
    position: relative;
    width: 0;
    z-index: -1;
}
[data-editable~="insert"] .caret {
    margin-right: -0.5em;
    width: 0.5em;
}

[data-editable]:focus .caret {
    animation-duration: 1.2s;
    animation-name: caret;
    animation-iteration-count: infinite;
}

JavaScript

/**
 * jquery-editable.js
 * By: zzzzBov
 * 
 * Generated using jQuery Widget Template
 */
(function ($, widget) {
    "use strict";
    $[widget] = function (element, opts) {
        if (!(this instanceof $[widget])) {
            return new $[widget](element, opts);
        }
        this._carets = this._caret().appendTo(element);
        this._element = element;
        this._options = opts;
        element.attr('tabindex', 0);
    };
    $[widget].prototype = {
        _options: {},
        _init: function () {
            $(this._element).on({
                'keydown': $.proxy(this, '_keydownHandler'),
                'keypress': $.proxy(this, '_keypressHandler')
            }).on('mousedown', '[data-char]', $.proxy(this, '_mousedownHandler'));
        },
        destroy: function () {
            
        },
        _caret: function () {
            return $('<span class="caret"> </span>');
        },
        _keydownHandler: function (e) {
            console.log(e.which, e.ctrlKey, e.altKey, e.shiftKey);
            switch (e.which) {
                case 8:
                    this._backspace();
                    break;
                case 9:
                    this._insertChar(9);
                    break;
                case 37:
                    this._caretLeft();
                    break;
                case 39:
                    this._caretRight();
                    break;
                case 46:
                    this._delete();
                    break;
                default:
                    return;
            }
            e.preventDefault();
        },
        _keypressHandler: function (e) {
            this._insertChar(e.which);
        },
        _mousedownHandler: function (e) {
            var $this,
                offset,
                width,
                x,
                y;
            $this = $(e.currentTarget);
            offset = $this.offset();
            x = e.pageX - offset.left;
            y =...