Skip to content

Instantly share code, notes, and snippets.

@uhop
Created May 14, 2018 19:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save uhop/2c99272c5fe723375687900c7492c51e to your computer and use it in GitHub Desktop.
Save uhop/2c99272c5fe723375687900c7492c51e to your computer and use it in GitHub Desktop.
Rich text editor sketch
<!doctype html>
<html>
<head>
<title>Rich text editor demo</title>
<script src="./rich.js" defer></script>
<style>
#editor[contentEditable=true] {
padding: 0.5em;
margin: 1em;
}
#editor[contentEditable=true] {
background-color: #ffc;
border: 1px solid #ccc;
}
#tools {
margin: 1em;
}
#tools span {
display: inline-block;
border-radius: 0.25em;
margin: 0.1em 0.25em;
padding: 0.25em;
color: #ccc;
cursor: not-allowed;
}
#tools span.separator {
color: black;
}
#tools .enabled {
color: black;
cursor: pointer;
}
#tools .active {
background-color: #aaa;
}
</style>
</head>
<body>
<h1>Rich text editor demo</h1>
<div id="tools"></div>
<div id="editor"><p>Hello, wonderful world!</p></div>
</body>
</html>
(function () {
'use strict';
var toolbar = ['bold', 'italic', 'strikeThrough', 'underline', '|', 'subscript', 'superscript', '|', 'increaseFontSize', 'decreaseFontSize', '|',
'createLink', 'unlink', '|', 'insertParagraph', 'insertOrderedList', 'insertUnorderedList', 'insertImage', 'insertHorizontalRule', 'insertHTML', 'insertText', '|',
'undo', 'redo', 'copy', 'cut', 'delete', 'forwardDelete', 'paste', 'selectAll', '|', 'foreColor', 'backColor', 'hiliteColor', 'fontName', 'fontSize', 'heading', '|',
'indent', 'outdent', 'justifyRight', 'justifyCenter', 'justifyLeft', 'justifyFull', '|', 'formatBlock', 'removeFormat'];
var commandDict = {};
function refreshState () {
toolbar.filter(function (cmd) { return cmd !== '|'; }).forEach(function (cmd) {
var supported = document.queryCommandSupported(cmd),
enabled = document.queryCommandEnabled(cmd),
state = document.queryCommandState(cmd);
commandDict[cmd] = (supported ? 1 : 0) + (enabled ? 2 : 0) + (state ? 4 : 0);
});
}
function updateState (selector) {
refreshState();
Array.prototype.slice.call(document.querySelector(selector).querySelectorAll('[command]')).forEach(function (element) {
var cmd = element.getAttribute('command');
if (!isNaN(commandDict[cmd])) {
if (commandDict[cmd] & 1) {
element.classList.remove('unsupported');
element.classList.add('supported');
} else {
element.classList.remove('supported');
element.classList.add('unsupported');
}
if (commandDict[cmd] & 2) {
element.classList.remove('disabled');
element.classList.add('enabled');
} else {
element.classList.remove('enabled');
element.classList.add('disabled');
}
if (commandDict[cmd] & 4) {
element.classList.remove('inactive');
element.classList.add('active');
} else {
element.classList.remove('active');
element.classList.add('inactive');
}
}
});
}
document.addEventListener('DOMContentLoaded', function () {
// setup tools
refreshState();
var tools = document.getElementById('tools'), nonEmptyTool = false;
toolbar.forEach(function (cmd) {
if (cmd === '|') {
if (nonEmptyTool) {
nonEmptyTool = false;
var tool = document.createElement('span');
tool.className = 'separator';
tool.appendChild(document.createTextNode('|'));
tools.appendChild(tool);
}
return;
}
if (!(commandDict[cmd] & 1)) {
return;
}
nonEmptyTool = true;
var tool = document.createElement('span');
tool.setAttribute('command', cmd);
tool.classList.add(commandDict[cmd] & 2 ? 'enabled' : 'disabled');
tool.classList.add(commandDict[cmd] & 4 ? 'active' : 'inactive');
tool.appendChild(document.createTextNode(cmd));
tools.appendChild(tool);
});
if (commandDict.styleWithCSS & 1) {
document.execCommand('styleWithCSS', false, 'true');
}
if (commandDict.defaultParagraphSeparator & 1) {
document.execCommand('defaultParagraphSeparator', false, 'p');
}
tools.addEventListener('mousedown', function (evt) {
evt.stopPropagation();
evt.preventDefault();
for (var element = evt.target; element !== tools; element = element.parentElement) {
if (element.getAttribute) {
var cmd = element.getAttribute('command');
if (cmd) {
if (element.classList.contains('enabled')) {
document.execCommand(cmd, false, null);
updateState('#tools');
}
break;
}
}
}
});
// setup the editor
var editor = document.getElementById('editor');
editor.contentEditable = 'true';
editor.addEventListener('click', function () { updateState('#tools'); });
editor.addEventListener('focus', function () { updateState('#tools'); });
editor.addEventListener('keyup', function () { updateState('#tools'); });
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment