Custom cursor and menu at caret

by kthornbloom

HTML

<div class="editable" spellcheck="false">
     <h1>Editor Test</h1>

    <ul>
        <li>Fix moving menu on close</li>
        <li>Account for line height where your caret is</li>
        <li>make formating menu vs insert menu</li>
    </ul>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Maxime at minus <b>ratione</b> fugit mollitia debitis architecto omnis autem. Exercitationem cumque aspernatur ipsa omnis quisquam repudiandae ipsam iusto quidem sequi eveniet.Lorem ipsum dolor sit amet, consectetur adipisicing elit. Maxime at minus ratione fugit mollitia debitis architecto omnis autem. Exercitationem cumque aspernatur ipsa omnis quisquam repudiandae ipsam iusto quidem sequi eveniet.Lorem ipsum dolor sit amet, consectetur adipisicing elit. Maxime at minus ratione fugit mollitia debitis architecto omnis autem. Exercitationem cumque aspernatur ipsa omnis quisquam repudiandae ipsam iusto quidem sequi eveniet.Lorem ipsum dolor sit amet, consectetur adipisicing elit. Maxime at minus <a href="#">Here's some</a>  <b>HTML</b>  <i>to</i>  <u>play with</u>. ratione fugit mollitia debitis architecto omnis autem. Exercitationem cumque aspernatur ipsa omnis quisquam repudiandae ipsam iusto quidem sequi eveniet.Lorem ipsum dolor sit amet, consectetur adipisicing elit. Maxime at minus ratione fugit mollitia debitis architecto omnis autem. Exercitationem cumque aspernatur ipsa omnis quisquam repudiandae ipsam iusto quidem sequi eveniet.Lorem ipsum dolor sit amet, consectetur adipisicing elit. Maxime at minus ratione fugit mollitia debitis architecto omnis autem. Exercitationem cumque aspernatur ipsa omnis quisquam repudiandae ipsam iusto quidem sequi eveniet.Lorem ipsum dolor sit amet, consectetur adipisicing elit. Maxime at minus ratione fugit mollitia debitis architecto omnis autem. Exercitationem cumque aspernatur ipsa omnis quisquam repudiandae ipsam iusto quidem sequi eveniet. <b>Test</b>.</div>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
    <defs>
  ...

CSS

body {
    padding:30px;
    font-family:sans-serif;
}
.editable {
    padding:30px;
    outline:none;
    background:#efefef;
    font-size:13px;
    line-height:1.8em;
}
@keyframes blink {
    0% {
        background:rgba(0, 139, 255, 1);
    }
    40% {
        background:rgba(0, 139, 255, 1);
    }
    60% {
        background:rgba(0, 139, 255, 0);
    }
    100% {
        background:rgba(0, 139, 255, 0);
    }
}
.custom-caret {
    position: absolute;
    width: 2px;
    height: 16px;
    top: -22px;
    left: 14px;
    background: #008BFF;
    animation:blink 1s infinite;
}
.custom-caret:before {
    content:'';
    width:6px;
    height:6px;
    border-radius:3px;
    background: #008BFF;
    animation:blink 1s infinite;
    position:absolute;
    top:-4px;
    left:-2px;
}
.circle-menu-wrap {
    -webkit-filter:url('#goo');
    filter:url('#goo');
    position:absolute;
    margin:15px;
    display:inline-block;
    width:0;
    height:0;
    transition:.5s cubic-bezier(0, 0, 0, 1);
}
.circle-menu-wrap a:link, .circle-menu-wrap a:visited {
    display:block;
    width:40px;
    height:40px;
    line-height:40px;
    border-radius:18px;
    text-align:center;
    text-decoration:none;
    background:#8DAB45;
    color:#fff;
    position:absolute;
    left:50%;
    top:50%;
    margin:-20px 0 0 -20px;
    transform-origin:center;
    transform:rotate(0deg) translate(0px) rotate(0deg) scale(.95);
    transition:1s cubic-bezier(0, 0, 0, 1.3), rotate;
    font-weight: bold;
    font-size: 0px;
}
.circle-menu-trigger {
    position:absolute;
    background:#8DAB45;
    color:#585858;
    line-height:40px;
    border-radius:20px;
    text-align:center;
    text-decoration:none;
    font-weight:bold;
    width:40px;
    height:40px;
    left:50%;
    top:50%;
    margin:-20px 0 0 -20px;
    transition:1s ease-out;
}
.circle-menu-trigger:hover {
    cursor:pointer;
}
.rotate, .rotate:hover {
    transform: rotate(-45deg) scale(.9);
}

JavaScript

jQuery.fn.reverse = [].reverse;

// Set font color to transparent, and then use text-shadow to re-apply color. This makes the default cursor invisible in most situations
var textColor = $('.editable').css('color');
$('.editable').css({
    color: 'rgba(0,0,0,0)',
    textShadow: '0 0 0 ' + textColor
}).attr('contentEditable', true);

// Function to paste html at the caret
function pasteHtmlAtCaret(html, selectPastedContent) {
    var sel, range;
    if (window.getSelection) {
        // IE9 and non-IE
        sel = window.getSelection();
        if (sel.getRangeAt && sel.rangeCount) {
            range = sel.getRangeAt(0);
            range.deleteContents();

            var el = document.createElement("div");
            el.innerHTML = html;
            var frag = document.createDocumentFragment(),
                node, lastNode;
            while ((node = el.firstChild)) {
                lastNode = frag.appendChild(node);
            }
            var firstNode = frag.firstChild;
            range.insertNode(frag);

            // Preserve the selection
            if (lastNode) {
                range = range.cloneRange();
                range.setStartAfter(lastNode);
                if (selectPastedContent) {
                    range.setStartBefore(firstNode);
                } else {
                    range.collapse(true);
                }
                sel.removeAllRanges();
                sel.addRange(range);
            }
        }
    } else if ((sel = document.selection) && sel.type != "Control") {
        // IE < 9
        var originalRange = sel.createRange();
        originalRange.collapse(true);
        sel.createRange().pasteHTML(html);
        if (selectPastedContent) {
            range = sel.createRange();
            range.setEndPoint("StartToStart", originalRange);
            range.select();
        }
    }
}

// Function to return selected HTML
function getSelectionHtml() {
    var html = "";
    if (typeof window.getSelection !=...