Merge pull request #15 from xwiki-labs/diffdom

merge diffdom into netflux
pull/1/head
ansuz 9 years ago
commit 7782069dbd

@ -28,13 +28,16 @@ define([
toolbar; toolbar;
var module = window.REALTIME_MODULE = { var module = window.REALTIME_MODULE = {
localChangeInProgress: 0 localChangeInProgress: 0,
Hyperjson: Hyperjson,
Hyperscript: Hyperscript
}; };
var isNotMagicLine = function (el) { var isNotMagicLine = function (el) {
// factor as: // factor as:
// return !(el.tagName === 'SPAN' && el.contentEditable === 'false'); // return !(el.tagName === 'SPAN' && el.contentEditable === 'false');
var filter = (el.tagName === 'SPAN' && el.contentEditable === 'false'); var filter = (el.tagName === 'SPAN' &&
el.getAttribute('contentEditable') === 'false');
if (filter) { if (filter) {
console.log("[hyperjson.serializer] prevented an element" + console.log("[hyperjson.serializer] prevented an element" +
"from being serialized:", el); "from being serialized:", el);
@ -95,7 +98,7 @@ define([
we should check when such an element is going to be we should check when such an element is going to be
removed, and prevent that from happening. */ removed, and prevent that from happening. */
if (info.node && info.node.tagName === 'SPAN' && if (info.node && info.node.tagName === 'SPAN' &&
info.node.contentEditable === "true") { info.node.getAttribute('contentEditable') === "false") {
// it seems to be a magicline plugin element... // it seems to be a magicline plugin element...
if (info.diff.action === 'removeElement') { if (info.diff.action === 'removeElement') {
// and you're about to remove it... // and you're about to remove it...
@ -163,7 +166,8 @@ define([
doc: inner, doc: inner,
// provide initialstate... // provide initialstate...
initialState: JSON.stringify(Hyperjson.fromDOM(inner, isNotMagicLine)), initialState: JSON.stringify(Hyperjson
.fromDOM(inner, isNotMagicLine)) || '{}',
// really basic operational transform // really basic operational transform
// reject patch if it results in invalid JSON // reject patch if it results in invalid JSON
@ -186,8 +190,7 @@ define([
var localWorkInProgress = function (stage) { var localWorkInProgress = function (stage) {
if (module.localChangeInProgress) { if (module.localChangeInProgress) {
console.error("Applied a change while a local patch was in progress"); console.error("Applied a change while a local patch was in progress");
alert("local work was interrupted at stage: " + stage); console.error("local work was interrupted at stage: " + stage);
//module.realtimeInput.onLocal();
return true; return true;
} }
return false; return false;
@ -200,6 +203,21 @@ define([
var userDocStateDom = hjsonToDom(JSON.parse(shjson)); var userDocStateDom = hjsonToDom(JSON.parse(shjson));
localWorkInProgress(2); // check again localWorkInProgress(2); // check again
/* in the DOM contentEditable is "false"
while "contenteditable" is undefined.
When it goes over the wire, it seems hyperjson transforms it.
of course, hyperjson simply gets attributes from the DOM.
el.attributes returns 'contenteditable', so we have to correct for that
There are quite possibly all sorts of other attributes which might lose
information, and we won't know what they are until after we've lost them.
this comes from hyperscript line 101. FIXME maybe
*/
userDocStateDom.setAttribute("contenteditable", "true"); // lol wtf userDocStateDom.setAttribute("contenteditable", "true"); // lol wtf
localWorkInProgress(3); // check again localWorkInProgress(3); // check again
var patch = (DD).diff(inner, userDocStateDom); var patch = (DD).diff(inner, userDocStateDom);

@ -147,7 +147,8 @@ define([
passwd, // password, to be deprecated (maybe) passwd, // password, to be deprecated (maybe)
channel, // the channel we're to connect to channel, // the channel we're to connect to
// initialState argument. (optional) /* optional unless your application expects JSON
from getUserDoc */
config.initialState || '', config.initialState || '',
// transform function (optional), which handles conflicts // transform function (optional), which handles conflicts
@ -160,7 +161,6 @@ define([
// this is a problem // this is a problem
warn("realtime.getUserDoc() !== newText"); warn("realtime.getUserDoc() !== newText");
} }
//try{throw new Error();}catch(e){console.log(e.stack);}
}; };
// pass your shiny new realtime into initialization functions // pass your shiny new realtime into initialization functions

@ -220,10 +220,9 @@ var transform = Patch.transform = function (origToTransform, transformBy, doc, t
Common.assert(origToTransform.parentHash === transformBy.parentHash); Common.assert(origToTransform.parentHash === transformBy.parentHash);
var resultOfTransformBy = apply(transformBy, doc); var resultOfTransformBy = apply(transformBy, doc);
toTransform = clone(origToTransform); var toTransform = clone(origToTransform);
var text = doc; var text = doc;
for (var i = toTransform.operations.length-1; i >= 0; i--) { for (var i = toTransform.operations.length-1; i >= 0; i--) {
text = Operation.apply(toTransform.operations[i], text);
for (var j = transformBy.operations.length-1; j >= 0; j--) { for (var j = transformBy.operations.length-1; j >= 0; j--) {
toTransform.operations[i] = Operation.transform(text, toTransform.operations[i] = Operation.transform(text,
toTransform.operations[i], toTransform.operations[i],

@ -1,5 +1,4 @@
define([], function () { define([], function () {
// this makes recursing a lot simpler // this makes recursing a lot simpler
var isArray = function (A) { var isArray = function (A) {
return Object.prototype.toString.call(A)==='[object Array]'; return Object.prototype.toString.call(A)==='[object Array]';
@ -39,8 +38,14 @@ define([], function () {
return cb(hj[0], hj[1], children); return cb(hj[0], hj[1], children);
}; };
var prependDot = function (token) { var classify = function (token) {
return '.' + token; return '.' + token.trim();
};
var isValidClass = function (x) {
if (x && /\S/.test(x)) {
return true;
}
}; };
var isTruthy = function (x) { var isTruthy = function (x) {
@ -61,13 +66,13 @@ define([], function () {
if(!el.attributes){ if(!el.attributes){
return; return;
} }
if (predicate) { if (predicate) {
if (!predicate(el)) { if (!predicate(el)) {
// shortcircuit // shortcircuit
return; return;
} }
} }
var attributes = {}; var attributes = {};
var i = 0; var i = 0;
@ -91,19 +96,25 @@ define([], function () {
var sel = el.tagName; var sel = el.tagName;
if(attributes.id){ if(attributes.id){
// we don't have to do much to validate IDs because the browser
// will only permit one id to exist
// unless we come across a strange browser in the wild
sel = sel +'#'+ attributes.id; sel = sel +'#'+ attributes.id;
delete attributes.id; delete attributes.id;
} }
if(attributes.class){ if(attributes.class){
// actually parse out classes so that we produce a valid selector
// string. leading or trailing spaces would have caused it to choke
// these are really common in generated html
/* TODO this can be done with RegExps alone, and it will be faster /* TODO this can be done with RegExps alone, and it will be faster
but this works and is a little less error prone, albeit slower but this works and is a little less error prone, albeit slower
come back and speed it up when it comes time to optimize */ come back and speed it up when it comes time to optimize */
sel = sel + attributes.class sel = sel + attributes.class
.split(/\s+/) .split(/\s+/g)
.filter(isTruthy) .filter(isValidClass)
.map(prependDot) .map(classify)
.join(''); .join('')
.replace(/\.\./g, '.');
delete attributes.class; delete attributes.class;
} }
result.push(sel); result.push(sel);
@ -116,11 +127,9 @@ define([], function () {
// js hint complains if we use 'var' here // js hint complains if we use 'var' here
i = 0; i = 0;
for(; i < el.childNodes.length; i++){ for(; i < el.childNodes.length; i++){
children.push(DOM2HyperJSON(el.childNodes[i], predicate, filter)); children.push(DOM2HyperJSON(el.childNodes[i], predicate, filter));
} }
result.push(children.filter(isTruthy)); result.push(children.filter(isTruthy));
if (filter) { if (filter) {

@ -6,6 +6,11 @@ define([
var validate = JsonOT.validate = function (text, toTransform, transformBy) { var validate = JsonOT.validate = function (text, toTransform, transformBy) {
try { try {
// text = O (mutual common ancestor)
// toTransform = A (the first incoming operation)
// transformBy = B (the second incoming operation)
// threeway merge (0, A, B)
var resultOp = ChainPad.Operation.transform0(text, toTransform, transformBy); var resultOp = ChainPad.Operation.transform0(text, toTransform, transformBy);
var text2 = ChainPad.Operation.apply(transformBy, text); var text2 = ChainPad.Operation.apply(transformBy, text);
var text3 = ChainPad.Operation.apply(resultOp, text2); var text3 = ChainPad.Operation.apply(resultOp, text2);

Loading…
Cancel
Save