Created
March 16, 2016 05:25
-
-
Save notcome/8300c04d1007d5791bd3 to your computer and use it in GitHub Desktop.
A simple “bidirectional” text editor prototype/proof-of-concept
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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