start on a vdom-based wysiwyg realtime

There are still a few conditions that cause it to 'explode'
when different browsers disagree on element semantics.

Overall it's a much cleaner approach, but it will require
extensive tuning to improve performance, as there are still
many complete dom traversals which will not scale for large documents
This commit is contained in:
ansuz 2016-01-29 12:40:45 +01:00
parent 1f7f90165f
commit d14e42fedd
3 changed files with 150 additions and 0 deletions

41
www/vdom/index.html Normal file
View File

@ -0,0 +1,41 @@
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<script data-main="main" src="/bower_components/requirejs/require.js"></script>
<style>
html, body {
margin: 0px;
padding: 0px;
}
#pad-iframe {
position:fixed;
top:0px;
left:0px;
bottom:0px;
right:0px;
width:50%;
height:100%;
border:none;
margin:0;
padding:0;
overflow:hidden;
}
#feedback {
position: fixed;
top: 0px;
right: 0px;
border: 0px;
height: 100vh;
width: 50vw;
background-color: #222;
color: #ccc;
}
</style>
</head>
<body>
<iframe id="pad-iframe" src="inner.html"></iframe>
<textarea id="feedback"></textarea>
</body>
</html>

12
www/vdom/inner.html Normal file
View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta content="text/html; charset=utf-8" http-equiv="content-type"/>
<script src="/bower_components/jquery/dist/jquery.min.js"></script>
<script src="/bower_components/ckeditor/ckeditor.js"></script>
</head>
<body>
<textarea style="display:none" id="editor1" name="editor1"></textarea>
</body>
</html>

97
www/vdom/main.js Normal file
View File

@ -0,0 +1,97 @@
define([
'/api/config?cb=' + Math.random().toString(16).substring(2),
'/common/messages.js',
'/common/crypto.js',
'/common/realtime-input.js',
'/common/convert.js',
'/bower_components/jquery/dist/jquery.min.js',
'/customize/pad.js'
], function (Config, Messages, Crypto, realtimeInput, Convert) {
var $ = window.jQuery;
var ifrw = $('#pad-iframe')[0].contentWindow;
var Ckeditor = ifrw.CKEDITOR;
var Vdom = Convert.core.vdom,
Hyperjson = Convert.core.hyperjson;
window.Hyperjson = Hyperjson;
var andThen = function () {
$(window).on('hashchange', function() {
window.location.reload();
});
if (window.location.href.indexOf('#') === -1) {
window.location.href = window.location.href + '#' + Crypto.genKey();
return;
}
var fixThings = false;
var key = Crypto.parseKey(window.location.hash.substring(1));
var editor = Ckeditor.replace('editor1', {
// https://dev.ckeditor.com/ticket/10907
needsBrFiller: fixThings,
needsNbspFiller: fixThings,
removeButtons: 'Source,Maximize',
// magicline plugin inserts html crap into the document which is not part of the
// document itself and causes problems when it's sent across the wire and reflected back
removePlugins: 'magicline,resize'
});
window.editor = editor;
editor.on('instanceReady', function () {
editor.execCommand('maximize');
ifrw.$('iframe')[0].contentDocument.body.innerHTML = Messages.initialState;
var inner = ifrw.$('iframe')[0].contentDocument.body;
window.inner = inner;
var $textarea = $('#feedback');
var vdom1 = Convert.dom.to.vdom(inner);
window.rti = realtimeInput.start($textarea[0],
Config.websocketURL,
Crypto.rand64(8),
key.channel,
key.cryptKey,
inner,
function (shjson) {
var authDoc = JSON.parse(shjson);
var vdom2 = Convert.hjson.to.vdom(authDoc);
var patches = Vdom.diff(vdom1, vdom2);
Vdom.patch(inner, patches);
vdom1 = vdom2;
});
$textarea.val(JSON.stringify(Convert.dom.to.hjson(inner)));
editor.on('change', function () {
var hjson = Hyperjson.fromDOM(inner);
/*
hjson = Hyperjson.callOn(hjson, function (a, b, c) {
Object.keys(b).forEach(function (k) {
if (a === "BR" && b[k] === '_moz') {
delete b[k];
}
});
return [a,b,c];
});*/
$textarea.val(JSON.stringify(hjson));
rti.bumpSharejs();
});
});
};
var interval = 100;
var first = function () {
if (Ckeditor = ifrw.CKEDITOR) {
andThen();
} else {
console.log("Ckeditor was not defined. Trying again in %sms",interval);
setTimeout(first, interval);
}
};
$(first);
});