diff --git a/www/_socket/main.js b/www/_socket/main.js index 67bfaff89..042bd3c20 100644 --- a/www/_socket/main.js +++ b/www/_socket/main.js @@ -45,6 +45,16 @@ define([ return true; }; + /* catch `type="_moz"` before it goes over the wire */ + var brFilter = function (hj) { + if (hj[1].type === '_moz') { hj[1].type = undefined; } + return hj; + }; + + var stringifyDOM = function (dom) { + return JSON.stringify(Hyperjson.fromDOM(dom, isNotMagicLine, brFilter)); + }; + var andThen = function (Ckeditor) { $(window).on('hashchange', function() { window.location.reload(); @@ -76,6 +86,8 @@ define([ var inner = window.inner = documentBody; var cursor = window.cursor = Cursor(inner); + + var setEditable = function (bool) { // careful about putting attributes onto the DOM // they get put into the chain, and you can have trouble @@ -165,8 +177,7 @@ define([ doc: inner, // provide initialstate... - initialState: JSON.stringify(Hyperjson - .fromDOM(inner, isNotMagicLine)) || '{}', + initialState: stringifyDOM(inner) || '{}', // really basic operational transform // reject patch if it results in invalid JSON @@ -221,7 +232,8 @@ define([ // build a dom from HJSON, diff, and patch the editor applyHjson(shjson); - var shjson2 = JSON.stringify(Hyperjson.fromDOM(inner)); + var shjson2 = stringifyDOM(inner); + if (shjson2 !== shjson) { console.error("shjson2 !== shjson"); module.realtimeInput.patchText(shjson2); @@ -255,11 +267,6 @@ define([ var rti = module.realtimeInput = realtimeInput.start(realtimeOptions); - /* catch `type="_moz"` before it goes over the wire */ - var brFilter = function (hj) { - if (hj[1].type === '_moz') { hj[1].type = undefined; } - return hj; - }; /* It's incredibly important that you assign 'rti.onLocal' It's used inside of realtimeInput to make sure that all changes @@ -270,7 +277,7 @@ define([ the code less extensible. */ var propogate = rti.onLocal = function () { - var shjson = JSON.stringify(Hyperjson.fromDOM(inner, isNotMagicLine, brFilter)); + var shjson = stringifyDOM(inner); if (!rti.patchText(shjson)) { return; } diff --git a/www/common/RealtimeTextSocket.js b/www/common/RealtimeTextSocket.js index edb8e3791..4821ce454 100644 --- a/www/common/RealtimeTextSocket.js +++ b/www/common/RealtimeTextSocket.js @@ -264,7 +264,8 @@ define([ }, 200); toReturn.patchText = TextPatcher.create({ - realtime: realtime + realtime: realtime, + logging: true }); realtime.start(); diff --git a/www/common/TextPatcher.js b/www/common/TextPatcher.js index 66858826d..98ccea65a 100644 --- a/www/common/TextPatcher.js +++ b/www/common/TextPatcher.js @@ -23,8 +23,8 @@ var diff = function (oldval, newval) { commonEnd++; } - var toRemove; - var toInsert; + var toRemove = 0; + var toInsert = ''; /* throw some assertions in here before dropping patches into the realtime */ if (oldval.length !== commonStart + commonEnd) { @@ -79,14 +79,15 @@ var log = function (text, op) { Due to its reliance on patch, applyChange has side effects on the supplied realtime facade. */ -var applyChange = function(ctx, oldval, newval) { +var applyChange = function(ctx, oldval, newval, logging) { var op = diff(oldval, newval); - // log(oldval, op) + if (logging) { log(oldval, op) } patch(ctx, op); }; var create = function(config) { var ctx = config.realtime; + var logging = config.logging; // initial state will always fail the !== check in genop. // because nothing will equal this object @@ -100,7 +101,7 @@ var create = function(config) { // propogate() return function (newContent) { if (newContent !== content) { - applyChange(ctx, ctx.getUserDoc(), newContent); + applyChange(ctx, ctx.getUserDoc(), newContent, logging); if (ctx.getUserDoc() !== newContent) { console.log("Expected that: `ctx.getUserDoc() === newContent`!"); } diff --git a/www/common/chainpad.js b/www/common/chainpad.js index 9d9dcc071..134d80ce2 100644 --- a/www/common/chainpad.js +++ b/www/common/chainpad.js @@ -368,7 +368,7 @@ var random = Patch.random = function (doc, opCount) { * along with this program. If not, see . */ -var PARANOIA = module.exports.PARANOIA = false; +var PARANOIA = module.exports.PARANOIA = true; /* throw errors over non-compliant messages which would otherwise be treated as invalid */ var TESTING = module.exports.TESTING = true; @@ -832,7 +832,7 @@ var check = ChainPad.check = function(realtime) { Common.assert(uiDoc === realtime.userInterfaceContent); } - var doc = realtime.authDoc; + /*var doc = realtime.authDoc; var patchMsg = realtime.best; Common.assert(patchMsg.content.inverseOf.parentHash === realtime.uncommitted.parentHash); var patches = []; @@ -844,7 +844,7 @@ var check = ChainPad.check = function(realtime) { while ((patchMsg = patches.pop())) { doc = Patch.apply(patchMsg.content, doc); } - Common.assert(doc === realtime.authDoc); + Common.assert(doc === realtime.authDoc);*/ }; var doOperation = ChainPad.doOperation = function (realtime, op) { diff --git a/www/hack/index.html b/www/hack/index.html index 4fa7e6065..2e8df970f 100644 --- a/www/hack/index.html +++ b/www/hack/index.html @@ -12,8 +12,14 @@ box-sizing: border-box; } textarea{ + position: absolute; + top: 5vh; + left: 0px; + border: 0px; + + padding-top: 15px; width: 100%; - height: 100vh; + height: 95vh; max-width: 100%; max-height: 100vh; @@ -32,26 +38,41 @@ color: #637476; } - #run { + #panel { position: fixed; top: 0px; right: 0px; - - z-index: 100; - width: 5vw; + width: 100%; height: 5vh; + z-index: 95; + background-color: #777; + /* min-height: 75px; */ + } + #run { + display: block; + float: right; + height: 100%; + width: 10vw; + z-index: 100; + line-height: 5vw; + font-size: 1.5em; background-color: #222; color: #CCC; - - display: block; text-align: center; + border-radius: 5%; + border: 0px; } - RUN +
+ + + + RUN +
diff --git a/www/hack/main.js b/www/hack/main.js index 1f5f88ea9..d63aadbfa 100644 --- a/www/hack/main.js +++ b/www/hack/main.js @@ -3,9 +3,10 @@ define([ '/common/realtime-input.js', '/common/messages.js', '/common/crypto.js', + '/common/cursor.js', '/bower_components/jquery/dist/jquery.min.js', '/customize/pad.js' -], function (Config, Realtime, Messages, Crypto) { +], function (Config, Realtime, Messages, Crypto, Cursor) { var $ = window.jQuery; $(window).on('hashchange', function() { window.location.reload(); @@ -58,7 +59,75 @@ define([ window.alert("Server Connection Lost"); }; - var rt = Realtime.start(config); + var rt = window.rt = Realtime.start(config); + + var cursor = Cursor($textarea[0]); + + var splice = function (str, index, chars) { + var count = chars.length; + return str.slice(0, index) + chars + str.slice((index -1) + count); + }; + + var setSelectionRange = function (input, start, end) { + if (input.setSelectionRange) { + input.focus(); + input.setSelectionRange(start, end); + } else if (input.createTextRange) { + var range = input.createTextRange(); + range.collapse(true); + range.moveEnd('character', end); + range.moveStart('character', start); + range.select(); + } + }; + + var setCursor = function (el, pos) { + setSelectionRange(el, pos, pos); + }; + + var state = {}; + + // TODO + $textarea.on('keydown', function (e) { + // track when control keys are pushed down + //switch (e.key) { } + }); + + // TODO + $textarea.on('keyup', function (e) { + // track when control keys are released + }); + + $textarea.on('keypress', function (e) { + switch (e.key) { + case 'Tab': + // insert a tab wherever the cursor is... + var start = $textarea.prop('selectionStart'); + var end = $textarea.prop('selectionEnd'); + if (typeof start !== 'undefined') { + if (start === end) { + $textarea.val(function (i, val) { + return splice(val, start, "\t"); + }); + setCursor($textarea[0], start +1); + } else { + // indentation?? this ought to be fun. + + } + } + // simulate a keypress so the event goes through.. + // prevent default behaviour for tab + e.preventDefault(); + rt.bumpSharejs(); + break; + default: + break; + } + }); + + $textarea.on('change', function () { + rt.bumpSharejs(); + }); $run.click(function (e) { e.preventDefault();