Skip to content

Instantly share code, notes, and snippets.

@notcome
Created March 16, 2016 05:25
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 notcome/8300c04d1007d5791bd3 to your computer and use it in GitHub Desktop.
Save notcome/8300c04d1007d5791bd3 to your computer and use it in GitHub Desktop.
A simple “bidirectional” text editor prototype/proof-of-concept
logOnPageTarget = document.getElementById('log-on-page-target');
function resetOnPageLog () {
logOnPageTarget.innerText = '';
}
function logOnPage (text) {
logOnPageTarget.innerText = text + '\n' + logOnPageTarget.innerText;
}
// Force Safari to evaluate the mutation observer.
document.addEventListener('keyup', function () {});
function refresh () {
setTimeout(function () {
refresh();
}, 100);
}
refresh();
function onSelectionChange () {
var selection = getSelection();
if (!selection.isCollapsed) {
logOnPage('not collapsed');
return;
}
if (window.lastSelectedNode == selection.anchorNode) {
logOnPage('selected');
return;
}
if (!doesSelectTarget(selection)) {
logOnPage('not inside the target');
return;
}
logOnPage('select a node');
logOnPage(selection.anchorNode.textContent);
window.currentPath = determinePath(selection.anchorNode);
window.lastSelectedNode = selection.anchorNode;
observer.disconnect();
observer.observe(lastSelectedNode, observerConfig);
}
var observerConfig = {
characterData : true,
characterDataOldValue : true
};
var observer = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
if (mutation.type != 'characterData') {
logOnPage('unexpected mutation ' + mutation.type);
return;
}
logOnPage('from ' + mutation.oldValue + ' to ' + mutation.target.textContent);
updateTree(window.currentPath, docTree, mutation.target.textContent);
});
});
function updateTree (path, tree, newValue) {
if (path.length == 0) {
tree.content = newValue;
return;
}
updateTree(path.slice(1), tree.children[path[0]], newValue);
}
function doesSelectTarget (selection) {
if (selection.anchorNode == null)
return false;
var target = document.getElementById('target');
function traverse (elem) {
if (elem == document)
return false;
if (elem == target)
return true;
return traverse(elem.parentNode);
}
return traverse(selection.anchorNode);
}
function determinePath (node) {
var target = document.getElementById('target');
function traverse (elem) {
if (elem == target)
return []
var parentPath = traverse(elem.parentNode);
var siblings = elem.parentNode.childNodes;
var i = 0;
while (i < siblings.length) {
if (siblings[i] == elem)
break;
i ++;
}
parentPath.push(i);
return parentPath;
}
return traverse(node);
}
document.addEventListener('selectionchange', onSelectionChange);
var docTree = {
children: [{
tagName: 'p',
children: [
{
tagName: 'text',
content: 'Hello, '
},
{
tagName: 'b',
children: [{
tagName: 'text',
content: 'world'
}]
},
{
tagName: 'text',
content: '. '
},
{
tagName: 'i',
children: [{
tagName: 'text',
content: 'Another'
}]
},
{
tagName: 'text',
content: '.'
}
]
}]
};
function buildDocTree (tree) {
if (tree.tagName == 'text') {
return document.createTextNode(tree.content)
} else {
var node = document.createElement(tree.tagName);
tree.children.forEach(function (child) {
var child_ = buildDocTree(child);
node.appendChild(child_);
});
return node;
}
}
document.getElementById('target').appendChild(buildDocTree(docTree.children[0]));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment