diff --git a/www/debug/chainpad.dist.js b/www/debug/chainpad.dist.js
new file mode 100644
index 000000000..595e5d175
--- /dev/null
+++ b/www/debug/chainpad.dist.js
@@ -0,0 +1,4198 @@
+(function(){
+var r=function(){var e="function"==typeof require&&require,r=function(i,o,u){o||(o=0);var n=r.resolve(i,o),t=r.m[o][n];if(!t&&e){if(t=e(n))return t}else if(t&&t.c&&(o=t.c,n=t.m,t=r.m[o][t.m],!t))throw new Error('failed to require "'+n+'" from '+o);if(!t)throw new Error('failed to require "'+i+'" from '+u);return t.exports||(t.exports={},t.call(t.exports,t,t.exports,r.relative(n,o))),t.exports};return r.resolve=function(e,n){var i=e,t=e+".js",o=e+"/index.js";return r.m[n][t]&&t?t:r.m[n][o]&&o?o:i},r.relative=function(e,t){return function(n){if("."!=n.charAt(0))return r(n,t,e);var o=e.split("/"),f=n.split("/");o.pop();for(var i=0;i.
+ */
+"use strict";
+
+var Operation = require('./Operation');
+var Common = require('./Common');
+
+/*::
+import type { Operation_t } from './Operation';
+*/
+
+var DEFAULT_BLOCKSIZE = module.exports.DEFAULT_BLOCKSIZE = 8;
+
+var hashScan = function (str, blockSize) {
+ var out = {};
+ for (var i = 0; i + blockSize <= str.length; i++) {
+ var slice = str.slice(i, i + blockSize);
+ (out[slice] = out[slice] || []).push(i);
+ }
+ return out;
+};
+
+// return true if two segments do not overlap, else false
+var isCompatible = function (m1, m2) {
+ if (m1.oldIndex < m2.oldIndex) {
+ if (m1.oldIndex + m1.length > m2.oldIndex) { return false; }
+ if (m1.newIndex + m1.length > m2.newIndex) { return false; }
+ } else if (m2.oldIndex < m1.oldIndex) {
+ if (m2.oldIndex + m2.length > m1.oldIndex) { return false; }
+ if (m2.newIndex + m2.length > m1.newIndex) { return false; }
+ } else {
+ return false;
+ }
+ return true;
+};
+
+var scoreMatch = function (m) {
+ return (m.length * 2) - m.oldIndex - m.newIndex;
+};
+
+/* iterate backwards through and array, splicing out indices to remove
+ the indices to remove MUST be in ascending order
+ otherwise this could remove the wrong values
+ operates strictly via side-effects */
+var removeAscendingIndices = function (A, toRemove) {
+ if (!toRemove.length) { return; }
+ for (var j = toRemove.length - 1; j > -1; j--) {
+ A.splice(toRemove[j], 1);
+ }
+};
+
+/* given a candidate match and the list of pending matches
+ evaluate whether the candidate conflicts with existing matches
+ if the candidate is determined to be a worse match than existing matches
+ return false
+ otherwise return the list of candidates which should be replaced
+
+ returns either:
+ false => the candidate is incompatible, and its conflicts are more valuable
+ empty array => truthy, but there is nothing to remove (no conflicts)
+ array => conflicting elements to replace with the candidate
+*/
+var listInferiorCandidates = function (current, pending) {
+ var score_m = scoreMatch(current);
+ var score_rest = 0;
+ var toRemove = [];
+
+ var l = pending.length;
+ for (var i = 0; i < l; i++) {
+ if (isCompatible(current, pending[i])) { continue; }
+ toRemove.push(i);
+ score_rest += scoreMatch(pending[i]);
+ if (score_rest > score_m) { return false; }
+ }
+
+ return toRemove;
+};
+
+/* called with all the matches, including the common start and common end, if they exist...
+
+ A: Common start (should not be replaced)
+ B: potential operations
+ B': satisfactory set of operations
+ C: Common end (should not be replaced)
+
+ this implementation does not do anything special to protect A and C
+ it is believed that the way matches are produced, they should not be removed.
+*/
+var reduceMatches = function (matches) {
+ // ascending sort
+ matches.sort(function (a, b) { return (a.oldIndex + a.newIndex) - (b.oldIndex + b.newIndex); });
+ var out = [];
+
+ var l_m = matches.length;
+ var toRemove;
+ for (var i = 0; i < l_m; i++) {
+ toRemove = listInferiorCandidates(matches[i], out);
+ if (toRemove) {
+ removeAscendingIndices(out, toRemove);
+ out.push(matches[i]);
+ }
+ }
+ return out;
+};
+
+var resolve = function (str, hash, blockSize) {
+ var matches = [];
+ var candidates = [];
+ // do the same thing as was done in hashscan, but for the new string
+ // look for commonalities between new and old data
+ for (var i = 0; i + blockSize <= str.length; i++) {
+ var slice = str.slice(i, i + blockSize);
+ var instances = (hash[slice] || []).slice(0);
+ for (var j = candidates.length - 1; j >= 0; j--) {
+ var c = candidates[j];
+ var ii = instances.indexOf(c.oldIndex + c.length - blockSize + 1);
+ if (ii > -1) {
+ c.length++;
+ instances.splice(ii, 1);
+ } else {
+ // We're pushing all of the candidates as "matches" and then we're going to sort them
+ // by length and pull out only ones which are non-intersecting because the result
+ // of this function needs to be a set of sequencial non-intersecting matches.
+ matches.push(candidates[j]);
+ //if (candidates.length === 1) { matches.push(candidates[j]); }
+
+ candidates.splice(j, 1);
+ }
+ }
+ for (var k = 0; k < instances.length; k++) {
+ candidates.push({
+ newIndex: i,
+ oldIndex: instances[k],
+ length: blockSize
+ });
+ }
+ //console.log(JSON.stringify(candidates));
+ }
+
+ // Normally we would only take one candidate, since they're equal value we just pick one and
+ // use it. However since we need all possible candidates which we will feed to our reduce
+ // function in order to get a list of sequencial non-intersecting matches.
+ // like concat, but destructive
+ Array.prototype.push.apply(matches, candidates);
+ //if (candidates[0]) { matches.push(candidates[0]); }
+
+ return matches;
+};
+
+var matchesToOps = function (oldS, newS, matches) {
+ // ascending sort
+ matches.sort(function (a, b) { return a.oldIndex - b.oldIndex; });
+ var oldI = 0;
+ var newI = 0;
+ var out = [];
+ for (var i = 0; i < matches.length; i++) {
+ var m = matches[i];
+ out.push(Operation.create(oldI, m.oldIndex - oldI, newS.slice(newI, m.newIndex)));
+ oldI = m.oldIndex + m.length;
+ newI = m.newIndex + m.length;
+ }
+ out.push(Operation.create(oldI, oldS.length - oldI, newS.slice(newI))); // does not check ops
+
+ if (Common.PARANOIA) {
+ out.forEach(function (op) {
+ if (!op.toRemove || !op.toInsert) { return; }
+ try { Operation.check(op); }
+ catch (e) {
+ console.log('\nINVALID OPERATION');
+ console.log(oldS);
+ console.log(newS);
+ //console.log(m);
+
+ console.log('\nMATCHES');
+ console.log(matches);
+ console.log('\nOPS');
+ console.log(out);
+
+ throw e;
+ }
+ });
+ }
+
+ return out.filter(function (x) { return x.toRemove || x.toInsert; });
+};
+
+var getCommonBeginning = function (oldS, newS) {
+ var commonStart = 0;
+ // This could be Math.min ?
+ var limit = oldS.length < newS.length ? oldS.length : newS.length;
+ while (oldS.charAt(commonStart) === newS.charAt(commonStart) && commonStart < limit) {
+ commonStart++;
+ }
+ return { newIndex: 0, oldIndex: 0, length: commonStart };
+};
+
+var getCommonEnd = function (oldS, newS, commonBeginning) {
+ var oldEnd = oldS.length - 1;
+ var newEnd = newS.length - 1;
+ var limit = Math.min(oldEnd, newEnd) - commonBeginning;
+ var commonEnd = 0;
+ while (oldS.charAt(oldEnd) === newS.charAt(newEnd) && limit >= 0) {
+ oldEnd--;
+ newEnd--;
+ commonEnd++;
+ limit--;
+ }
+ return { newIndex: newEnd + 1, oldIndex: oldEnd + 1, length: commonEnd };
+};
+
+module.exports.diff = function (
+ oldS /*:string*/,
+ newS /*:string*/,
+ blockSize /*:?number*/ ) /*:Array*/
+{
+ blockSize = blockSize || DEFAULT_BLOCKSIZE;
+ var cb = getCommonBeginning(oldS, newS);
+ if (cb.length === oldS.length && oldS.length === newS.length) { return []; }
+ var ce = getCommonEnd(oldS, newS, cb.length);
+ var oldST = oldS;
+ var newST = newS;
+ if (ce.length) {
+ oldST = oldST.slice(0, ce.oldIndex+1);
+ newST = newST.slice(0, ce.newIndex+1);
+ }
+ if (cb.length) {
+ oldST = oldST.slice(cb.length);
+ newST = newST.slice(cb.length);
+ }
+ var matches = resolve(newST, hashScan(oldST, blockSize), blockSize);
+ if (cb.length) {
+ for (var i = 0; i < matches.length; i++) {
+ matches[i].oldIndex += cb.length;
+ matches[i].newIndex += cb.length;
+ }
+ matches.push(cb);
+ }
+ if (ce.length) { matches.push(ce); }
+ var reduced = reduceMatches(matches);
+ var ops = matchesToOps(oldS, newS, reduced); // HERE produced operation with negative toRemove
+ if (Operation.applyMulti(ops, oldS) !== newS) {
+ // use 'self' instead of 'window' for node and webworkers
+ var x = (typeof(global) !== 'undefined'? global: self).ChainPad_Diff_DEBUG = {
+ oldS: oldS,
+ newS: newS,
+ matches: matches,
+ reduced: reduced,
+ ops: ops
+ };
+ console.log(x);
+ console.log("diff did not make a sane patch, check window.ChainPad_Diff_DEBUG");
+ ops = matchesToOps(oldS, newS, [cb, ce]);
+ if (Operation.applyMulti(ops, oldS) !== newS) {
+ throw new Error("diff is unrecoverable");
+ }
+ }
+ return ops;
+};
+
+
+
+},
+"Patch.js": function(module, exports, require){
+/*@flow*/
+/*
+ * Copyright 2014 XWiki SAS
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+"use strict";
+var Common = require('./Common');
+var Operation = require('./Operation');
+var Sha = require('./sha256');
+
+var Patch = module.exports;
+
+/*::
+import type {
+ Operation_t,
+ Operation_Packed_t,
+ Operation_Simplify_t,
+ Operation_Transform_t
+} from './Operation';
+import type { Sha256_t } from './sha256';
+export type Patch_t = {
+ type: 'Patch',
+ operations: Array,
+ parentHash: Sha256_t,
+ isCheckpoint: boolean,
+ mut: {
+ inverseOf: ?Patch_t,
+ }
+};
+export type Patch_Packed_t = Array;
+export type Patch_Transform_t = (
+ toTransform:Array,
+ transformBy:Array,
+ state0:string
+) => Array;
+*/
+
+var create = Patch.create = function (parentHash /*:Sha256_t*/, isCheckpoint /*:?boolean*/) {
+ var out = Object.freeze({
+ type: 'Patch',
+ operations: [],
+ parentHash: parentHash,
+ isCheckpoint: !!isCheckpoint,
+ mut: {
+ inverseOf: undefined
+ }
+ });
+ if (isCheckpoint) {
+ out.mut.inverseOf = out;
+ }
+ return out;
+};
+
+var check = Patch.check = function (patch /*:any*/, docLength_opt /*:?number*/) /*:Patch_t*/ {
+ Common.assert(patch.type === 'Patch');
+ Common.assert(Array.isArray(patch.operations));
+ Common.assert(/^[0-9a-f]{64}$/.test(patch.parentHash));
+ for (var i = patch.operations.length - 1; i >= 0; i--) {
+ Operation.check(patch.operations[i], docLength_opt);
+ if (i > 0) {
+ Common.assert(!Operation.shouldMerge(patch.operations[i], patch.operations[i-1]));
+ }
+ if (typeof(docLength_opt) === 'number') {
+ docLength_opt += Operation.lengthChange(patch.operations[i]);
+ }
+ }
+ if (patch.isCheckpoint) {
+ Common.assert(patch.operations.length === 1);
+ Common.assert(patch.operations[0].offset === 0);
+ if (typeof(docLength_opt) === 'number') {
+ Common.assert(!docLength_opt || patch.operations[0].toRemove === docLength_opt);
+ }
+ }
+ return patch;
+};
+
+Patch.toObj = function (patch /*:Patch_t*/) {
+ if (Common.PARANOIA) { check(patch); }
+ var out /*:Array*/ = new Array(patch.operations.length+1);
+ var i;
+ for (i = 0; i < patch.operations.length; i++) {
+ out[i] = Operation.toObj(patch.operations[i]);
+ }
+ out[i] = patch.parentHash;
+ return out;
+};
+
+Patch.fromObj = function (obj /*:Patch_Packed_t*/, isCheckpoint /*:?boolean*/) {
+ Common.assert(Array.isArray(obj) && obj.length > 0);
+ var patch = create(Sha.check(obj[obj.length-1]), isCheckpoint);
+ var i;
+ for (i = 0; i < obj.length-1; i++) {
+ patch.operations[i] = Operation.fromObj(obj[i]);
+ }
+ if (Common.PARANOIA) { check(patch); }
+ return patch;
+};
+
+var hash = function (text) {
+ return Sha.hex_sha256(text);
+};
+
+var addOperation = Patch.addOperation = function (patch /*:Patch_t*/, op /*:Operation_t*/) {
+ if (Common.PARANOIA) {
+ check(patch);
+ Operation.check(op);
+ }
+ for (var i = 0; i < patch.operations.length; i++) {
+ if (Operation.shouldMerge(patch.operations[i], op)) {
+ var maybeOp = Operation.merge(patch.operations[i], op);
+ patch.operations.splice(i,1);
+ if (maybeOp === null) { return; }
+ op = maybeOp;
+ i--;
+ } else {
+ var out = Operation.rebase(patch.operations[i], op);
+ if (out === op) {
+ // op could not be rebased further, insert it here to keep the list ordered.
+ patch.operations.splice(i,0,op);
+ return;
+ } else {
+ op = out;
+ // op was rebased, try rebasing it against the next operation.
+ }
+ }
+ }
+ patch.operations.push(op);
+ if (Common.PARANOIA) { check(patch); }
+};
+
+Patch.createCheckpoint = function (
+ parentContent /*:string*/,
+ checkpointContent /*:string*/,
+ parentContentHash_opt /*:?string*/)
+{
+ var op = Operation.create(0, parentContent.length, checkpointContent);
+ if (Common.PARANOIA && parentContentHash_opt) {
+ Common.assert(parentContentHash_opt === hash(parentContent));
+ }
+ parentContentHash_opt = parentContentHash_opt || hash(parentContent);
+ var out = create(parentContentHash_opt, true);
+ out.operations[0] = op;
+ return out;
+};
+
+var clone = Patch.clone = function (patch /*:Patch_t*/) {
+ if (Common.PARANOIA) { check(patch); }
+ var out = create(patch.parentHash, patch.isCheckpoint);
+ for (var i = 0; i < patch.operations.length; i++) {
+ out.operations[i] = patch.operations[i];
+ }
+ return out;
+};
+
+Patch.merge = function (oldPatch /*:Patch_t*/, newPatch /*:Patch_t*/) {
+ if (Common.PARANOIA) {
+ check(oldPatch);
+ check(newPatch);
+ }
+ if (oldPatch.isCheckpoint) {
+ Common.assert(newPatch.parentHash === oldPatch.parentHash);
+ if (newPatch.isCheckpoint) {
+ return create(oldPatch.parentHash);
+ }
+ return clone(newPatch);
+ } else if (newPatch.isCheckpoint) {
+ return clone(oldPatch);
+ }
+ oldPatch = clone(oldPatch);
+ for (var i = newPatch.operations.length-1; i >= 0; i--) {
+ addOperation(oldPatch, newPatch.operations[i]);
+ }
+ return oldPatch;
+};
+
+Patch.apply = function (patch /*:Patch_t*/, doc /*:string*/)
+{
+ if (Common.PARANOIA) {
+ check(patch);
+ Common.assert(typeof(doc) === 'string');
+ Common.assert(Sha.hex_sha256(doc) === patch.parentHash);
+ }
+ var newDoc = doc;
+ for (var i = patch.operations.length-1; i >= 0; i--) {
+ newDoc = Operation.apply(patch.operations[i], newDoc);
+ }
+ return newDoc;
+};
+
+Patch.lengthChange = function (patch /*:Patch_t*/)
+{
+ if (Common.PARANOIA) { check(patch); }
+ var out = 0;
+ for (var i = 0; i < patch.operations.length; i++) {
+ out += Operation.lengthChange(patch.operations[i]);
+ }
+ return out;
+};
+
+Patch.invert = function (patch /*:Patch_t*/, doc /*:string*/)
+{
+ if (Common.PARANOIA) {
+ check(patch);
+ Common.assert(typeof(doc) === 'string');
+ Common.assert(Sha.hex_sha256(doc) === patch.parentHash);
+ }
+ var newDoc = doc;
+ var operations = new Array(patch.operations.length);
+ for (var i = patch.operations.length-1; i >= 0; i--) {
+ operations[i] = Operation.invert(patch.operations[i], newDoc);
+ newDoc = Operation.apply(patch.operations[i], newDoc);
+ }
+ var opOffsets = new Array(patch.operations.length);
+ (function () {
+ for (var i = operations.length-1; i >= 0; i--) {
+ opOffsets[i] = operations[i].offset;
+ for (var j = i - 1; j >= 0; j--) {
+ opOffsets[i] += operations[j].toRemove - operations[j].toInsert.length;
+ }
+ }
+ }());
+ var rpatch = create(Sha.hex_sha256(newDoc), patch.isCheckpoint);
+ rpatch.operations.splice(0, rpatch.operations.length);
+ for (var j = 0; j < operations.length; j++) {
+ rpatch.operations[j] =
+ Operation.create(opOffsets[j], operations[j].toRemove, operations[j].toInsert);
+ }
+ if (Common.PARANOIA) { check(rpatch); }
+ return rpatch;
+};
+
+Patch.simplify = function (
+ patch /*:Patch_t*/,
+ doc /*:string*/,
+ operationSimplify /*:Operation_Simplify_t*/ )
+{
+ if (Common.PARANOIA) {
+ check(patch);
+ Common.assert(typeof(doc) === 'string');
+ Common.assert(Sha.hex_sha256(doc) === patch.parentHash);
+ }
+ var spatch = create(patch.parentHash);
+ var newDoc = doc;
+ var outOps = [];
+ var j = 0;
+ for (var i = patch.operations.length-1; i >= 0; i--) {
+ var outOp = operationSimplify(patch.operations[i], newDoc, Operation.simplify);
+ if (outOp) {
+ newDoc = Operation.apply(outOp, newDoc);
+ outOps[j++] = outOp;
+ }
+ }
+ Array.prototype.push.apply(spatch.operations, outOps.reverse());
+ if (!spatch.operations[0]) {
+ spatch.operations.shift();
+ }
+ if (Common.PARANOIA) {
+ check(spatch);
+ }
+ return spatch;
+};
+
+Patch.equals = function (patchA /*:Patch_t*/, patchB /*:Patch_t*/) {
+ if (patchA.operations.length !== patchB.operations.length) { return false; }
+ for (var i = 0; i < patchA.operations.length; i++) {
+ if (!Operation.equals(patchA.operations[i], patchB.operations[i])) { return false; }
+ }
+ return true;
+};
+
+var isCheckpointOp = function (op, text) {
+ return op.offset === 0 && op.toRemove === text.length && op.toInsert === text;
+};
+
+Patch.transform = function (
+ toTransform /*:Patch_t*/,
+ transformBy /*:Patch_t*/,
+ doc /*:string*/,
+ patchTransformer /*:Patch_Transform_t*/ )
+{
+ if (Common.PARANOIA) {
+ check(toTransform, doc.length);
+ check(transformBy, doc.length);
+ if (Sha.hex_sha256(doc) !== toTransform.parentHash) { throw new Error("wrong hash"); }
+ }
+ if (toTransform.parentHash !== transformBy.parentHash) { throw new Error(); }
+
+ var afterTransformBy = Patch.apply(transformBy, doc);
+ var out = create(transformBy.mut.inverseOf
+ ? transformBy.mut.inverseOf.parentHash
+ : Sha.hex_sha256(afterTransformBy),
+ toTransform.isCheckpoint
+ );
+
+ if (transformBy.operations.length === 0) { return clone(toTransform); }
+ if (toTransform.operations.length === 0) {
+ if (toTransform.isCheckpoint) { throw new Error(); }
+ return out;
+ }
+
+ if (toTransform.isCheckpoint ||
+ (toTransform.operations.length === 1 && isCheckpointOp(toTransform.operations[0], doc)))
+ {
+ throw new Error("Attempting to transform a checkpoint, this should not happen");
+ }
+
+ if (transformBy.operations.length === 1 && isCheckpointOp(transformBy.operations[0], doc)) {
+ if (!transformBy.isCheckpoint) { throw new Error(); }
+ return toTransform;
+ }
+
+ if (transformBy.isCheckpoint) { throw new Error(); }
+
+ var ops = patchTransformer(toTransform.operations, transformBy.operations, doc);
+ Array.prototype.push.apply(out.operations, ops);
+
+ if (Common.PARANOIA) {
+ check(out, afterTransformBy.length);
+ }
+
+ return out;
+};
+
+Patch.random = function (doc /*:string*/, opCount /*:?number*/) {
+ Common.assert(typeof(doc) === 'string');
+ opCount = opCount || (Math.floor(Math.random() * 30) + 1);
+ var patch = create(Sha.hex_sha256(doc));
+ var docLength = doc.length;
+ while (opCount-- > 0) {
+ var op = Operation.random(docLength);
+ docLength += Operation.lengthChange(op);
+ addOperation(patch, op);
+ }
+ check(patch);
+ return patch;
+};
+
+Object.freeze(module.exports);
+
+},
+"SHA256.js": function(module, exports, require){
+/* A JavaScript implementation of the Secure Hash Algorithm, SHA-256
+ * Version 0.3 Copyright Angel Marin 2003-2004 - http://anmar.eu.org/
+ * Distributed under the BSD License
+ * Some bits taken from Paul Johnston's SHA-1 implementation
+ */
+(function () {
+ var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
+ function safe_add (x, y) {
+ var lsw = (x & 0xFFFF) + (y & 0xFFFF);
+ var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+ return (msw << 16) | (lsw & 0xFFFF);
+ }
+ function S (X, n) {return ( X >>> n ) | (X << (32 - n));}
+ function R (X, n) {return ( X >>> n );}
+ function Ch(x, y, z) {return ((x & y) ^ ((~x) & z));}
+ function Maj(x, y, z) {return ((x & y) ^ (x & z) ^ (y & z));}
+ function Sigma0256(x) {return (S(x, 2) ^ S(x, 13) ^ S(x, 22));}
+ function Sigma1256(x) {return (S(x, 6) ^ S(x, 11) ^ S(x, 25));}
+ function Gamma0256(x) {return (S(x, 7) ^ S(x, 18) ^ R(x, 3));}
+ function Gamma1256(x) {return (S(x, 17) ^ S(x, 19) ^ R(x, 10));}
+ function newArray (n) {
+ var a = [];
+ for (;n>0;n--) {
+ a.push(undefined);
+ }
+ return a;
+ }
+ function core_sha256 (m, l) {
+ var K = [0x428A2F98,0x71374491,0xB5C0FBCF,0xE9B5DBA5,0x3956C25B,0x59F111F1,0x923F82A4,0xAB1C5ED5,0xD807AA98,0x12835B01,0x243185BE,0x550C7DC3,0x72BE5D74,0x80DEB1FE,0x9BDC06A7,0xC19BF174,0xE49B69C1,0xEFBE4786,0xFC19DC6,0x240CA1CC,0x2DE92C6F,0x4A7484AA,0x5CB0A9DC,0x76F988DA,0x983E5152,0xA831C66D,0xB00327C8,0xBF597FC7,0xC6E00BF3,0xD5A79147,0x6CA6351,0x14292967,0x27B70A85,0x2E1B2138,0x4D2C6DFC,0x53380D13,0x650A7354,0x766A0ABB,0x81C2C92E,0x92722C85,0xA2BFE8A1,0xA81A664B,0xC24B8B70,0xC76C51A3,0xD192E819,0xD6990624,0xF40E3585,0x106AA070,0x19A4C116,0x1E376C08,0x2748774C,0x34B0BCB5,0x391C0CB3,0x4ED8AA4A,0x5B9CCA4F,0x682E6FF3,0x748F82EE,0x78A5636F,0x84C87814,0x8CC70208,0x90BEFFFA,0xA4506CEB,0xBEF9A3F7,0xC67178F2];
+ var HASH = [0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A, 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19];
+ var W = newArray(64);
+ var a, b, c, d, e, f, g, h, i, j;
+ var T1, T2;
+ /* append padding */
+ m[l >> 5] |= 0x80 << (24 - l % 32);
+ m[((l + 64 >> 9) << 4) + 15] = l;
+ for ( var i = 0; i>5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i%32);
+ return bin;
+ }
+ function binb2hex (binarray) {
+ var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
+ var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
+ var str = "";
+ for (var i = 0; i < binarray.length * 4; i++) {
+ str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
+ hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF);
+ }
+ return str;
+ }
+ function hex_sha256(s){
+ return binb2hex(core_sha256(str2binb(s),s.length * chrsz));
+ }
+ module.exports.hex_sha256 = hex_sha256;
+}());
+
+},
+"Common.js": function(module, exports, require){
+/*@flow*/
+/* globals localStorage, window */
+/*
+ * Copyright 2014 XWiki SAS
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+"use strict";
+
+module.exports.global = (function () {
+ if (typeof(self) !== 'undefined') { return self; }
+ if (typeof(global) !== 'undefined') { return global; }
+ if (typeof(window) !== 'undefined') { return window; }
+ throw new Error("no self, nor global, nor window");
+}());
+
+var cfg = function (name) {
+ if (typeof(localStorage) !== 'undefined' && localStorage[name]) {
+ return localStorage[name];
+ }
+ // flow thinks global may be undefined
+ return module.exports.global[name];
+};
+
+var PARANOIA = module.exports.PARANOIA = cfg("ChainPad_PARANOIA");
+
+/* Good testing but slooooooooooow */
+module.exports.VALIDATE_ENTIRE_CHAIN_EACH_MSG = cfg("ChainPad_VALIDATE_ENTIRE_CHAIN_EACH_MSG");
+
+/* throw errors over non-compliant messages which would otherwise be treated as invalid */
+module.exports.TESTING = cfg("ChainPad_TESTING");
+
+module.exports.assert = function (expr /*:any*/) {
+ if (!expr) { throw new Error("Failed assertion"); }
+};
+
+module.exports.isUint = function (integer /*:number*/) {
+ return (typeof(integer) === 'number') &&
+ (Math.floor(integer) === integer) &&
+ (integer >= 0);
+};
+
+module.exports.randomASCII = function (length /*:number*/) {
+ var content = [];
+ for (var i = 0; i < length; i++) {
+ content[i] = String.fromCharCode( Math.floor(Math.random()*256) % 57 + 65 );
+ }
+ return content.join('');
+};
+
+module.exports.strcmp = function (a /*:string*/, b /*:string*/) {
+ if (PARANOIA && typeof(a) !== 'string') { throw new Error(); }
+ if (PARANOIA && typeof(b) !== 'string') { throw new Error(); }
+ return ( (a === b) ? 0 : ( (a > b) ? 1 : -1 ) );
+};
+
+Object.freeze(module.exports);
+
+},
+"sha256.js": function(module, exports, require){
+/*@flow*/
+/*
+ * Copyright 2014 XWiki SAS
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+var asm_sha256 = require('./sha256/exports.js');
+var old = require('./SHA256.js');
+var Common = require('./Common');
+
+/*::
+export type Sha256_t = string;
+*/
+
+var brokenTextEncode = function (str) {
+ var out = new Uint8Array(str.length);
+ for (var i = 0; i < str.length; i++) {
+ out[i] = str.charCodeAt(i) & 0xff;
+ }
+ return out;
+};
+
+module.exports.check = function (hex /*:any*/) /*:Sha256_t*/ {
+ if (typeof(hex) !== 'string') { throw new Error(); }
+ if (!/[a-f0-9]{64}/.test(hex)) { throw new Error(); }
+ return hex;
+};
+
+module.exports.hex_sha256 = function (d /*:string*/) /*:Sha256_t*/ {
+ d = d+'';
+ var ret = asm_sha256.hex(brokenTextEncode(d));
+ if (Common.PARANOIA) {
+ var oldHash = old.hex_sha256(d);
+ if (oldHash !== ret) {
+ try {
+ throw new Error();
+ } catch (e) {
+ console.log({
+ hashErr: e,
+ badHash: d,
+ asmHasher: asm_sha256.hex,
+ oldHasher: old.hex_sha256
+ });
+ }
+ return oldHash;
+ }
+ }
+ return ret;
+};
+
+Object.freeze(module.exports);
+
+},
+"Message.js": function(module, exports, require){
+/*@flow*/
+/*
+ * Copyright 2014 XWiki SAS
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+"use strict";
+var Common = require('./Common');
+//var Operation = require('./Operation');
+var Patch = require('./Patch');
+var Sha = require('./sha256');
+
+var Message = module.exports;
+
+var PATCH = Message.PATCH = 2;
+var CHECKPOINT = Message.CHECKPOINT = 4;
+
+/*::
+import type { Sha256_t } from './sha256'
+import type { Patch_t } from './Patch'
+export type Message_Type_t = 2 | 4;
+export type Message_Status_t =
+ 'accepted'|'initial_state'|'duplicate'|'failed_content_validation'|
+ 'can_be_simplified'|'checkpoint_wrong_parentcount'|'parent_hash_invalid'|'unhandled';
+export type Message_t = {
+ type: 'Message',
+ messageType: Message_Type_t,
+ content: Patch_t,
+ lastMsgHash: Sha256_t,
+ hashOf: Sha256_t,
+ mut: {
+ parentCount: ?number,
+ isInitialMessage: boolean,
+ parent: ?Message_t,
+ isFromMe: boolean,
+ recvOrder: number,
+ status: Message_Status_t
+ }
+}
+*/
+
+var check = Message.check = function(msg /*:any*/) /*:Message_t*/ {
+ Common.assert(msg.type === 'Message');
+ Common.assert(msg.messageType === PATCH || msg.messageType === CHECKPOINT);
+ Patch.check(msg.content);
+ Common.assert(typeof(msg.lastMsgHash) === 'string');
+ return msg;
+};
+
+var DUMMY_HASH /*:Sha256_t*/ = "";
+
+var create = Message.create = function (
+ type /*:Message_Type_t*/,
+ content /*:Patch_t*/,
+ lastMsgHash /*:Sha256_t*/) /*:Message_t*/
+{
+ var msg = {
+ type: 'Message',
+ messageType: type,
+ content: content,
+ lastMsgHash: lastMsgHash,
+ hashOf: DUMMY_HASH,
+ mut: {
+ parentCount: undefined,
+ isInitialMessage: false,
+ isFromMe: false,
+ parent: undefined,
+ recvOrder: -1,
+ status: "unhandled"
+ }
+ };
+ msg.hashOf = hashOf(msg);
+ if (Common.PARANOIA) { check(msg); }
+ return Object.freeze(msg);
+};
+
+// $FlowFixMe doesn't like the toString()
+var toString = Message.toStr = Message.toString = function (msg /*:Message_t*/) {
+ if (Common.PARANOIA) { check(msg); }
+ if (msg.messageType === PATCH || msg.messageType === CHECKPOINT) {
+ if (!msg.content) { throw new Error(); }
+ return JSON.stringify([msg.messageType, Patch.toObj(msg.content), msg.lastMsgHash]);
+ } else {
+ throw new Error();
+ }
+};
+
+Message.fromString = function (str /*:string*/) /*:Message_t*/ {
+ var m = JSON.parse(str);
+ if (m[0] !== CHECKPOINT && m[0] !== PATCH) { throw new Error("invalid message type " + m[0]); }
+ var msg = create(m[0], Patch.fromObj(m[1], (m[0] === CHECKPOINT)), m[2]);
+ return Object.freeze(msg);
+};
+
+var hashOf = Message.hashOf = function (msg /*:Message_t*/) {
+ if (Common.PARANOIA) { check(msg); }
+ var hash = Sha.hex_sha256(toString(msg));
+ return hash;
+};
+
+Object.freeze(module.exports);
+
+},
+"ChainPad.js": function(module, exports, require){
+/*@flow*/
+/*
+ * Copyright 2014 XWiki SAS
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+"use strict";
+var Common = module.exports.Common = require('./Common');
+var Operation = module.exports.Operation = require('./Operation');
+var Patch = module.exports.Patch = require('./Patch');
+var Message = module.exports.Message = require('./Message');
+var Sha = module.exports.Sha = require('./sha256');
+var Diff = module.exports.Diff = require('./Diff');
+
+var TextTransformer = module.exports.TextTransformer = require('./transform/TextTransformer');
+module.exports.NaiveJSONTransformer = require('./transform/NaiveJSONTransformer');
+module.exports.SmartJSONTransformer = require('./transform/SmartJSONTransformer');
+
+// hex_sha256('')
+var EMPTY_STR_HASH = module.exports.EMPTY_STR_HASH =
+ 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855';
+var ZERO = '0000000000000000000000000000000000000000000000000000000000000000';
+
+// Default number of patches between checkpoints (patches older than this will be pruned)
+// default for realtime.config.checkpointInterval
+var DEFAULT_CHECKPOINT_INTERVAL = 50;
+
+// Default number of milliseconds to wait before syncing to the server
+var DEFAULT_AVERAGE_SYNC_MILLISECONDS = 300;
+
+// By default, we allow checkpoints at any place but if this is set true, we will blow up on chains
+// which have checkpoints not where we expect them to be.
+var DEFAULT_STRICT_CHECKPOINT_VALIDATION = false;
+
+var debug = function (realtime, msg) {
+ if (realtime.logLevel > 1) {
+ console.log("[" + realtime.userName + "] " + msg);
+ }
+};
+
+var warn = function (realtime, msg) {
+ if (realtime.logLevel > 0) {
+ console.error("[" + realtime.userName + "] " + msg);
+ }
+};
+
+var schedule = function (realtime, func, timeout) {
+ if (realtime.aborted) { return; }
+ if (!timeout) {
+ timeout = Math.floor(Math.random() * 2 * realtime.config.avgSyncMilliseconds);
+ }
+ var to = setTimeout(function () {
+ realtime.schedules.splice(realtime.schedules.indexOf(to), 1);
+ func();
+ }, timeout);
+ realtime.schedules.push(to);
+ return to;
+};
+
+var unschedule = function (realtime, schedule) {
+ var index = realtime.schedules.indexOf(schedule);
+ if (index > -1) {
+ realtime.schedules.splice(index, 1);
+ }
+ clearTimeout(schedule);
+};
+
+var onMessage = function (realtime, message, callback /*:(?string)=>void*/) {
+ if (!realtime.messageHandlers.length) {
+ callback("no onMessage() handler registered");
+ }
+ try {
+ realtime.messageHandlers.forEach(function (handler) {
+ handler(message, function () {
+ callback.apply(null, arguments);
+ callback = function () { };
+ });
+ });
+ } catch (e) {
+ callback(e.stack);
+ }
+};
+
+var sendMessage = function (realtime, msg, callback, timeSent) {
+ var strMsg = Message.toStr(msg);
+
+ onMessage(realtime, strMsg, function (err) {
+ if (err) {
+ debug(realtime, "Posting to server failed [" + err + "]");
+ realtime.pending = null;
+ } else {
+ var pending = realtime.pending;
+ realtime.pending = null;
+ if (!pending) { throw new Error(); }
+ Common.assert(pending.hash === msg.hashOf);
+ if (handleMessage(realtime, strMsg, true)) {
+ realtime.timeOfLastSuccess = +new Date();
+ realtime.lag = +new Date() - pending.timeSent;
+ } else {
+ debug(realtime, "Our message [" + msg.hashOf + "] failed validation");
+ }
+ pending.callback();
+ }
+ });
+
+ var timeout = schedule(realtime, function () {
+ debug(realtime, "Failed to send message [" + msg.hashOf + "] to server");
+ var pending = realtime.pending;
+ if (pending) {
+ //var timeSent = pending.timeSent;
+ realtime.pending = null;
+ realtime.syncSchedule = -1;
+ }
+ sync(realtime, 0);
+ if (!pending) {
+ throw new Error("INTERNAL ERROR: Message timed out but no realtime.pending");
+ }
+ }, 10000 + (Math.random() * 5000));
+
+ if (realtime.pending) { throw new Error("there is already a pending message"); }
+ if (realtime.timeOfLastSuccess === -1) { realtime.timeOfLastSuccess = +new Date(); }
+ realtime.pending = {
+ hash: msg.hashOf,
+ timeSent: timeSent || +new Date(),
+ callback: function () {
+ unschedule(realtime, timeout);
+ realtime.syncSchedule = schedule(realtime, function () { sync(realtime); }, 0);
+ callback();
+ }
+ };
+ if (Common.PARANOIA) { check(realtime); }
+};
+
+var settle = function (realtime) {
+ var onSettle = realtime.onSettle;
+ realtime.onSettle = [];
+ onSettle.forEach(function (handler) {
+ try {
+ handler();
+ } catch (e) {
+ warn(realtime, "Error in onSettle handler [" + e.stack + "]");
+ }
+ });
+};
+
+var inversePatch = function (patch) {
+ if (!patch.mut.inverseOf) { throw new Error(); }
+ return patch.mut.inverseOf;
+};
+
+var sync = function (realtime, timeSent) {
+ if (Common.PARANOIA) { check(realtime); }
+ if (realtime.syncSchedule && !realtime.pending) {
+ unschedule(realtime, realtime.syncSchedule);
+ realtime.syncSchedule = null;
+ } else {
+ //debug(realtime, "already syncing...");
+ // we're currently waiting on something from the server.
+ return;
+ }
+
+ realtime.uncommitted = Patch.simplify(
+ realtime.uncommitted, realtime.authDoc, realtime.config.operationSimplify);
+
+ if (realtime.uncommitted.operations.length === 0) {
+ //debug(realtime, "No data to sync to the server, sleeping");
+ settle(realtime);
+ realtime.timeOfLastSuccess = +new Date();
+ realtime.syncSchedule = schedule(realtime, function () { sync(realtime); });
+ return;
+ }
+
+ var pc = parentCount(realtime, realtime.best) + 1;
+ if ((pc % realtime.config.checkpointInterval) === 0) {
+ var best = realtime.best;
+ debug(realtime, "Sending checkpoint (interval [" + realtime.config.checkpointInterval +
+ "]) patch no [" + pc + "]");
+ debug(realtime, parentCount(realtime, realtime.best));
+ if (!best || !best.content || !inversePatch(best.content)) { throw new Error(); }
+ var cpp = Patch.createCheckpoint(realtime.authDoc,
+ realtime.authDoc,
+ inversePatch(best.content).parentHash);
+ var cp = Message.create(Message.CHECKPOINT, cpp, best.hashOf);
+ sendMessage(realtime, cp, function () {
+ debug(realtime, "Checkpoint sent and accepted");
+ }, timeSent);
+ return;
+ }
+
+ var msg;
+ if (realtime.setContentPatch) {
+ msg = realtime.setContentPatch;
+ } else {
+ msg = Message.create(Message.PATCH, realtime.uncommitted, realtime.best.hashOf);
+ }
+
+ sendMessage(realtime, msg, function () {
+ //debug(realtime, "patch sent");
+ if (realtime.setContentPatch) {
+ debug(realtime, "initial Ack received [" + msg.hashOf + "]");
+ realtime.setContentPatch = null;
+ }
+ }, timeSent);
+};
+
+var storeMessage = function (realtime, msg) {
+ Common.assert(msg.lastMsgHash);
+ Common.assert(msg.hashOf);
+ realtime.messages[msg.hashOf] = msg;
+ (realtime.messagesByParent[msg.lastMsgHash] =
+ realtime.messagesByParent[msg.lastMsgHash] || []).push(msg);
+ msg.mut.status = "accepted";
+};
+
+var forgetMessage = function (realtime, msg, reason) {
+ Common.assert(msg.lastMsgHash);
+ Common.assert(msg.hashOf);
+ if (reason) {
+ msg.mut.status = reason;
+ realtime.rejectedBlocks.push(msg);
+ }
+ delete realtime.messages[msg.hashOf];
+ var list = realtime.messagesByParent[msg.lastMsgHash];
+ Common.assert(list.indexOf(msg) > -1);
+ list.splice(list.indexOf(msg), 1);
+ if (list.length === 0) {
+ delete realtime.messagesByParent[msg.lastMsgHash];
+ }
+ var children = realtime.messagesByParent[msg.hashOf];
+ if (children) {
+ for (var i = 0; i < children.length; i++) {
+ delete children[i].mut.parent;
+ }
+ }
+};
+
+var create = function (config) {
+
+ var zeroPatch = Patch.create(EMPTY_STR_HASH);
+ mkInverse(zeroPatch, '');
+ var zeroMsg = Message.create(Message.PATCH, zeroPatch, ZERO);
+ zeroMsg.mut.parentCount = 0;
+ zeroMsg.mut.isInitialMessage = true;
+ var best = zeroMsg;
+
+ var initMsg;
+ if (config.initialState !== '') {
+ var initPatch = Patch.create(EMPTY_STR_HASH);
+ Patch.addOperation(initPatch, Operation.create(0, 0, config.initialState));
+ mkInverse(initPatch, '');
+ initMsg = Message.create(Message.PATCH, initPatch, zeroMsg.hashOf);
+ initMsg.mut.isInitialMessage = true;
+ best = initMsg;
+ }
+
+ var realtime = {
+ type: 'ChainPad',
+
+ authDoc: config.initialState,
+
+ config: config,
+
+ logLevel: config.logLevel,
+
+ /** A patch representing all uncommitted work. */
+ uncommitted: Patch.create(inversePatch(best.content).parentHash),
+
+ patchHandlers: [],
+ changeHandlers: [],
+
+ messageHandlers: [],
+
+ schedules: [],
+ aborted: false,
+
+ syncSchedule: -2,
+
+ // this is only used if PARANOIA is enabled.
+ userInterfaceContent: config.initialState,
+
+ // If we want to set the content to a particular thing, this patch will be sent across the
+ // wire. If the patch is not accepted we will not try to recover it. This is used for
+ // setting initial state.
+ setContentPatch: initMsg,
+
+ // hash and callback for previously send patch, currently in flight.
+ pending: undefined,
+
+ messages: {},
+ messagesByParent: {},
+
+ rootMessage: zeroMsg,
+
+ onSettle: [],
+
+ userName: config.userName,
+
+ best: best,
+
+ lag: 0,
+ timeOfLastSuccess: -1,
+
+ // Incremented every time a message comes in, valid or invalid, used to number messages.
+ recvCounter: 0,
+
+ // All of the messages which were discarded because they were faulty
+ rejectedBlocks: []
+ };
+ storeMessage(realtime, zeroMsg);
+ if (initMsg) {
+ storeMessage(realtime, initMsg);
+ }
+ return realtime;
+};
+
+var getParent = function (realtime, message) {
+ var parent = message.mut.parent = message.mut.parent || realtime.messages[message.lastMsgHash];
+ return parent;
+};
+
+var check = function(realtime) {
+ Common.assert(realtime.type === 'ChainPad');
+ Common.assert(typeof(realtime.authDoc) === 'string');
+
+ Patch.check(realtime.uncommitted, realtime.authDoc.length);
+
+ var uiDoc = Patch.apply(realtime.uncommitted, realtime.authDoc);
+ Common.assert(uiDoc.length === uncommittedDocLength(realtime));
+ if (realtime.userInterfaceContent !== '') {
+ Common.assert(uiDoc === realtime.userInterfaceContent);
+ }
+
+ if (!Common.VALIDATE_ENTIRE_CHAIN_EACH_MSG) { return; }
+
+ var doc = realtime.authDoc;
+ var patchMsg = realtime.best;
+ Common.assert(inversePatch(patchMsg.content).parentHash === realtime.uncommitted.parentHash);
+ var patches = [];
+ do {
+ patches.push(patchMsg);
+ doc = Patch.apply(inversePatch(patchMsg.content), doc);
+ } while ((patchMsg = getParent(realtime, patchMsg)));
+ if (realtime.rootMessage.content.isCheckpoint) {
+ if (doc !== realtime.rootMessage.content.operations[0].toInsert) { throw new Error(); }
+ } else if (doc !== '') { throw new Error(); }
+ while ((patchMsg = patches.pop())) {
+ doc = Patch.apply(patchMsg.content, doc);
+ }
+ Common.assert(doc === realtime.authDoc);
+};
+
+var uncommittedDocLength = function (realtime) {
+ return realtime.authDoc.length + Patch.lengthChange(realtime.uncommitted);
+};
+
+var doOperation = function (realtime, op) {
+ if (Common.PARANOIA) {
+ check(realtime);
+ realtime.userInterfaceContent = Operation.apply(op, realtime.userInterfaceContent);
+ }
+ Operation.check(op, uncommittedDocLength(realtime));
+ Patch.addOperation(realtime.uncommitted, op);
+};
+
+var doPatch = function (realtime, patch) {
+ if (Common.PARANOIA) {
+ check(realtime);
+ Common.assert(Patch.invert(realtime.uncommitted, realtime.authDoc).parentHash ===
+ patch.parentHash);
+ realtime.userInterfaceContent = Patch.apply(patch, realtime.userInterfaceContent);
+ }
+ Patch.check(patch, uncommittedDocLength(realtime));
+ realtime.uncommitted = Patch.merge(realtime.uncommitted, patch);
+};
+
+var isAncestorOf = function (realtime, ancestor, decendent) {
+ if (!ancestor) { return false; }
+ for (;;) {
+ if (!decendent) { return false; }
+ if (ancestor === decendent) { return true; }
+ decendent = getParent(realtime, decendent);
+ }
+};
+
+var parentCount = function (realtime, message) {
+ if (typeof(message.mut.parentCount) === 'number') { return message.mut.parentCount; }
+ var msgs = [];
+ for (; (typeof(message.mut.parentCount) !== 'number'); message = getParent(realtime, message)) {
+ if (!message) {
+ if (message === realtime.rootMessage) {
+ throw new Error("root message does not have parent count");
+ }
+ throw new Error("parentCount called on unlinked message");
+ }
+ msgs.unshift(message);
+ }
+ var pc = message.mut.parentCount;
+ for (var i = 0; i < msgs.length; i++) {
+ msgs[i].mut.parentCount = ++pc;
+ }
+ return pc;
+};
+
+var applyPatch = function (realtime, isFromMe, patch) {
+ Common.assert(patch);
+ var newAuthDoc;
+ if (isFromMe) {
+ // Case 1: We're applying a patch which we originally created (yay our work was accepted)
+ // We will merge the inverse of the patch with our uncommitted work in order that
+ // we do not try to commit that work over again.
+ // Case 2: We're reverting a patch which had originally come from us, a.k.a. we're applying
+ // the inverse of that patch.
+ //
+ // In either scenario, we want to apply the inverse of the patch we are applying, to the
+ // uncommitted work. Whatever we "add" to the authDoc we "remove" from the uncommittedWork.
+ //
+ Common.assert(patch.parentHash === realtime.uncommitted.parentHash);
+ realtime.uncommitted = Patch.merge(inversePatch(patch), realtime.uncommitted);
+
+ } else {
+ // It's someone else's patch which was received, we need to *transform* out uncommitted
+ // work over their patch in order to preserve intent as much as possible.
+ //debug(realtime, "Transforming patch " + JSON.stringify(realtime.uncommitted.operations));
+ realtime.uncommitted = Patch.transform(
+ realtime.uncommitted,
+ patch,
+ realtime.authDoc,
+ realtime.config.patchTransformer
+ );
+ //debug(realtime, "By " + JSON.stringify(patch.operations) +
+ // "\nResult " + JSON.stringify(realtime.uncommitted.operations));
+ if (realtime.config.validateContent) {
+ newAuthDoc = Patch.apply(patch, realtime.authDoc);
+ var userDoc = Patch.apply(realtime.uncommitted, newAuthDoc);
+ if (!realtime.config.validateContent(userDoc)) {
+ warn(realtime, "Transformed patch is not valid");
+ // big hammer
+ realtime.uncommitted = Patch.create(Sha.hex_sha256(realtime.authDoc));
+ }
+ }
+ }
+ Common.assert(realtime.uncommitted.parentHash === inversePatch(patch).parentHash);
+
+ realtime.authDoc = newAuthDoc || Patch.apply(patch, realtime.authDoc);
+
+ if (Common.PARANOIA) {
+ Common.assert(realtime.uncommitted.parentHash === inversePatch(patch).parentHash);
+ Common.assert(Sha.hex_sha256(realtime.authDoc) === realtime.uncommitted.parentHash);
+ realtime.userInterfaceContent = Patch.apply(realtime.uncommitted, realtime.authDoc);
+ }
+};
+
+var revertPatch = function (realtime, isFromMe, patch) {
+ applyPatch(realtime, isFromMe, inversePatch(patch));
+};
+
+var getBestChild = function (realtime, msg) {
+ var best = msg;
+ (realtime.messagesByParent[msg.hashOf] || []).forEach(function (child) {
+ Common.assert(child.lastMsgHash === msg.hashOf);
+ child = getBestChild(realtime, child);
+ if (parentCount(realtime, child) > parentCount(realtime, best)) { best = child; }
+ });
+ return best;
+};
+
+var pushUIPatch = function (realtime, patch) {
+ if (!patch.operations.length) { return; }
+ // push the uncommittedPatch out to the user interface.
+ realtime.patchHandlers.forEach(function (handler) { handler(patch); });
+ realtime.changeHandlers.forEach(function (handler) {
+ patch.operations.forEach(function (op) {
+ handler(op.offset, op.toRemove, op.toInsert);
+ });
+ });
+};
+
+var validContent = function (realtime, contentGetter) {
+ try {
+ return realtime.config.validateContent(contentGetter());
+ } catch (e) {
+ warn(realtime, "Error in content validator [" + e.stack + "]");
+ }
+ return false;
+};
+
+var forEachParent = function (realtime, patch, callback) {
+ for (var m = getParent(realtime, patch); m; m = getParent(realtime, m)) {
+ if (callback(m) === false) { return; }
+ }
+};
+
+var mkInverse = function (patch, content) {
+ if (patch.mut.inverseOf) { return; }
+ var inverse = patch.mut.inverseOf = Patch.invert(patch, content);
+ inverse.mut.inverseOf = patch;
+};
+
+var handleMessage = function (realtime, msgStr, isFromMe) {
+
+ if (Common.PARANOIA) { check(realtime); }
+ var msg = Message.fromString(msgStr);
+ msg.mut.recvOrder = realtime.recvCounter++;
+
+ debug(realtime, JSON.stringify([msg.hashOf, msg.content.operations]));
+
+ if (realtime.messages[msg.hashOf]) {
+ if (realtime.setContentPatch && realtime.setContentPatch.hashOf === msg.hashOf) {
+ // We got the initial state patch, channel already has a pad, no need to send it.
+ realtime.setContentPatch = null;
+ } else {
+ msg.mut.status = "duplicate";
+ realtime.rejectedBlocks.push(msg);
+ if (msg.content.isCheckpoint) {
+ debug(realtime, "[" +
+ (isFromMe ? "our" : "their") +
+ "] Checkpoint [" + msg.hashOf + "] is already known");
+ return true;
+ }
+ debug(realtime, "Patch [" + msg.hashOf + "] is already known");
+ }
+ if (Common.PARANOIA) { check(realtime); }
+ return;
+ }
+
+ if (msg.content.isCheckpoint &&
+ !validContent(realtime, function () { return msg.content.operations[0].toInsert; }))
+ {
+ // If it's not a checkpoint, we verify it later on...
+ debug(realtime, "Checkpoint [" + msg.hashOf + "] failed content validation");
+ msg.mut.status = "failed_content_validation";
+ realtime.rejectedBlocks.push(msg);
+ return;
+ }
+
+ storeMessage(realtime, msg);
+
+ if (!isAncestorOf(realtime, realtime.rootMessage, msg)) {
+ if (msg.content.isCheckpoint && realtime.best.mut.isInitialMessage) {
+ // We're starting with a trucated chain from a checkpoint, we will adopt this
+ // as the root message and go with it...
+ debug(realtime, 'applying checkpoint [' + msg.hashOf + ']');
+ var userDoc = Patch.apply(realtime.uncommitted, realtime.authDoc);
+ Common.assert(!Common.PARANOIA || realtime.userInterfaceContent === userDoc);
+ var fixUserDocPatch = Patch.invert(realtime.uncommitted, realtime.authDoc);
+ Patch.addOperation(fixUserDocPatch,
+ Operation.create(0, realtime.authDoc.length, msg.content.operations[0].toInsert));
+ fixUserDocPatch =
+ Patch.simplify(fixUserDocPatch, userDoc, realtime.config.operationSimplify);
+
+ msg.mut.parentCount = 0;
+ realtime.rootMessage = realtime.best = msg;
+
+ realtime.authDoc = msg.content.operations[0].toInsert;
+ realtime.uncommitted = Patch.create(Sha.hex_sha256(realtime.authDoc));
+ pushUIPatch(realtime, fixUserDocPatch);
+
+ if (Common.PARANOIA) { realtime.userInterfaceContent = realtime.authDoc; }
+ return true;
+ } else {
+ // we'll probably find the missing parent later.
+ debug(realtime, "Patch [" + msg.hashOf + "] not connected to root (parent: [" +
+ msg.lastMsgHash + "])");
+ if (Common.PARANOIA) { check(realtime); }
+ return;
+ }
+ }
+
+ // of this message fills in a hole in the chain which makes another patch better, swap to the
+ // best child of this patch since longest chain always wins.
+ msg = getBestChild(realtime, msg);
+ msg.mut.isFromMe = isFromMe;
+ var patch = msg.content;
+
+ // Find the ancestor of this patch which is in the main chain, reverting as necessary
+ var toRevert = [];
+ var commonAncestor = realtime.best;
+ if (!isAncestorOf(realtime, realtime.best, msg)) {
+ var pcBest = parentCount(realtime, realtime.best);
+ var pcMsg = parentCount(realtime, msg);
+ if (pcBest < pcMsg
+ || (pcBest === pcMsg
+ && Common.strcmp(realtime.best.hashOf, msg.hashOf) > 0))
+ {
+ // switch chains
+ while (commonAncestor && !isAncestorOf(realtime, commonAncestor, msg)) {
+ toRevert.push(commonAncestor);
+ commonAncestor = getParent(realtime, commonAncestor);
+ }
+ Common.assert(commonAncestor);
+ debug(realtime, "Patch [" + msg.hashOf + "] better than best chain, switching");
+ } else {
+ debug(realtime, "Patch [" + msg.hashOf + "] chain is [" + pcMsg + "] best chain is [" +
+ pcBest + "]");
+ if (Common.PARANOIA) { check(realtime); }
+ return true;
+ }
+ }
+
+ // Find the parents of this patch which are not in the main chain.
+ var toApply = [];
+ var current = msg;
+ do {
+ toApply.unshift(current);
+ current = getParent(realtime, current);
+ Common.assert(current);
+ } while (current !== commonAncestor);
+
+
+ var authDocAtTimeOfPatch = realtime.authDoc;
+
+ toRevert.forEach(function (tr) {
+ authDocAtTimeOfPatch = Patch.apply(inversePatch(tr.content), authDocAtTimeOfPatch);
+ });
+
+ toApply.forEach(function (ta, i) {
+ // toApply.length-1 because we do not want to apply the new patch.
+ if (i === toApply.length - 1) { return; }
+ mkInverse(ta.content, authDocAtTimeOfPatch);
+ authDocAtTimeOfPatch = Patch.apply(ta.content, authDocAtTimeOfPatch);
+ });
+
+ var headAtTimeOfPatch = realtime.best;
+ if (toApply.length > 1) {
+ headAtTimeOfPatch = toApply[toApply.length-2];
+ Common.assert(headAtTimeOfPatch);
+ } else if (toRevert.length) {
+ headAtTimeOfPatch = getParent(realtime, toRevert[toRevert.length-1]);
+ Common.assert(headAtTimeOfPatch);
+ }
+ Common.assert(inversePatch(headAtTimeOfPatch.content).parentHash);
+ Common.assert(!Common.PARANOIA ||
+ inversePatch(headAtTimeOfPatch.content).parentHash ===
+ Sha.hex_sha256(authDocAtTimeOfPatch));
+
+ if (inversePatch(headAtTimeOfPatch.content).parentHash !== patch.parentHash) {
+ debug(realtime, "patch [" + msg.hashOf + "] parentHash is not valid");
+ if (Common.PARANOIA) { check(realtime); }
+ if (Common.TESTING) { throw new Error(); }
+ forgetMessage(realtime, msg, "parent_hash_invalid");
+ return;
+ }
+
+ if (patch.isCheckpoint && realtime.config.noPrune) {
+ // do nothing, just fall through
+ } else if (patch.isCheckpoint) {
+ // Ok, we have a checkpoint patch.
+ // If the chain length is not equal to checkpointInterval then this patch is invalid.
+ var checkpointP;
+ forEachParent(realtime, msg, function (m) {
+ if (m.content.isCheckpoint) {
+ if (checkpointP) {
+ checkpointP = m;
+ return false;
+ }
+ checkpointP = m;
+ }
+ });
+ if (checkpointP && checkpointP !== realtime.rootMessage) {
+ var point = parentCount(realtime, checkpointP);
+ if (realtime.config.strictCheckpointValidation &&
+ (point % realtime.config.checkpointInterval) !== 0)
+ {
+ debug(realtime, "checkpoint [" + msg.hashOf + "] at invalid point [" + point + "]");
+ if (Common.PARANOIA) { check(realtime); }
+ if (Common.TESTING) { throw new Error(); }
+ forgetMessage(realtime, msg, "checkpoint_wrong_parentcount");
+ return;
+ }
+
+ // Time to prune some old messages from the chain
+ debug(realtime, "checkpoint [" + msg.hashOf + "]");
+ forEachParent(realtime, checkpointP, function (m) {
+ debug(realtime, "pruning [" + m.hashOf + "]");
+ forgetMessage(realtime, m);
+ });
+ realtime.rootMessage = checkpointP;
+ }
+ } else {
+ var simplePatch =
+ Patch.simplify(patch, authDocAtTimeOfPatch, realtime.config.operationSimplify);
+ if (!Patch.equals(simplePatch, patch)) {
+ debug(realtime, "patch [" + msg.hashOf + "] can be simplified");
+ if (Common.PARANOIA) { check(realtime); }
+ if (Common.TESTING) { throw new Error(); }
+ forgetMessage(realtime, msg, "can_be_simplified");
+ return;
+ }
+
+ if (!validContent(realtime,
+ function () { return Patch.apply(patch, authDocAtTimeOfPatch); }))
+ {
+ debug(realtime, "Patch [" + msg.hashOf + "] failed content validation");
+ return;
+ }
+ }
+
+ mkInverse(patch, authDocAtTimeOfPatch);
+
+ realtime.uncommitted = Patch.simplify(
+ realtime.uncommitted, realtime.authDoc, realtime.config.operationSimplify);
+ var oldUserInterfaceContent = Patch.apply(realtime.uncommitted, realtime.authDoc);
+ if (Common.PARANOIA) {
+ Common.assert(oldUserInterfaceContent === realtime.userInterfaceContent);
+ }
+
+ // Derive the patch for the user's uncommitted work
+ var uncommittedPatch = Patch.invert(realtime.uncommitted, realtime.authDoc);
+
+ toRevert.forEach(function (tr) {
+ debug(realtime, "reverting [" + tr.hashOf + "]");
+ if (tr.mut.isFromMe) {
+ debug(realtime, "reverting patch 'from me' [" + JSON.stringify(tr.content.operations) + "]");
+ }
+ uncommittedPatch = Patch.merge(uncommittedPatch, inversePatch(tr.content));
+ revertPatch(realtime, tr.mut.isFromMe, tr.content);
+ });
+
+ toApply.forEach(function (ta) {
+ debug(realtime, "applying [" + ta.hashOf + "]");
+ uncommittedPatch = Patch.merge(uncommittedPatch, ta.content);
+ applyPatch(realtime, ta.mut.isFromMe, ta.content);
+ });
+
+ uncommittedPatch = Patch.merge(uncommittedPatch, realtime.uncommitted);
+ uncommittedPatch = Patch.simplify(
+ uncommittedPatch, oldUserInterfaceContent, realtime.config.operationSimplify);
+
+ realtime.best = msg;
+
+ if (Common.PARANOIA) {
+ // apply the uncommittedPatch to the userInterface content.
+ var newUserInterfaceContent = Patch.apply(uncommittedPatch, oldUserInterfaceContent);
+ Common.assert(realtime.userInterfaceContent.length === uncommittedDocLength(realtime));
+ Common.assert(newUserInterfaceContent === realtime.userInterfaceContent);
+ }
+
+ pushUIPatch(realtime, uncommittedPatch);
+
+ if (!realtime.uncommitted.operations.length) {
+ settle(realtime);
+ }
+
+ if (Common.PARANOIA) { check(realtime); }
+
+ return true;
+};
+
+var getDepthOfState = function (content, minDepth, realtime) {
+ Common.assert(typeof(content) === 'string');
+
+ // minimum depth is an optional argument which defaults to zero
+ minDepth = minDepth || 0;
+
+ if (minDepth === 0 && realtime.authDoc === content) {
+ return 0;
+ }
+
+ var hash = Sha.hex_sha256(content);
+
+ var patchMsg = realtime.best;
+ var depth = 0;
+
+ do {
+ if (depth < minDepth) {
+ // you haven't exceeded the minimum depth
+ } else {
+ //console.log("Exceeded minimum depth");
+ // you *have* exceeded the minimum depth
+ if (patchMsg.content.parentHash === hash) {
+ // you found it!
+ return depth + 1;
+ }
+ }
+ depth++;
+ } while ((patchMsg = getParent(realtime, patchMsg)));
+ return -1;
+};
+
+var getContentAtState = function (realtime, msg) {
+ var patches = [ msg ];
+ while (patches[0] !== realtime.rootMessage) {
+ var parent = getParent(realtime, patches[0]);
+ if (!parent) {
+ return { error: 'not connected to root', doc: undefined };
+ }
+ patches.unshift(parent);
+ }
+ var doc = '';
+ if (realtime.rootMessage.content.operations.length) {
+ Common.assert(realtime.rootMessage.content.operations.length === 1);
+ doc = realtime.rootMessage.content.operations[0].toInsert;
+ }
+ for (var i = 1; i < patches.length; i++) {
+ doc = Patch.apply(patches[i].content, doc);
+ }
+ return { error: undefined, doc: doc };
+};
+
+/*::
+import type { Message_Status_t } from './Message.js';
+export type ChainPad_BlockContent_t = {
+ error: ?string,
+ doc: ?string
+};
+export type ChainPad_Block_t = {
+ type: 'Block',
+ hashOf: string,
+ lastMsgHash: string,
+ isCheckpoint: boolean,
+ status: Message_Status_t,
+ recvOrder: number,
+ parentCount: number,
+ getParent: ()=>?ChainPad_Block_t,
+ getChildren: ()=>Array,
+ getContent: ()=>{
+ error: ?string,
+ doc: ?string
+ },
+ getPatch: ()=>Patch_t,
+ getInversePatch: ()=>Patch_t,
+ equals: (?ChainPad_Block_t, ?any)=>boolean
+};
+*/
+
+var wrapMessage = function (realtime, msg) /*:ChainPad_Block_t*/ {
+ var pc = -1;
+ try { pc = parentCount(realtime, msg); } catch (e) { }
+ return Object.freeze({
+ type: 'Block',
+ hashOf: msg.hashOf,
+ lastMsgHash: msg.lastMsgHash,
+ isCheckpoint: !!msg.content.isCheckpoint,
+ status: msg.mut.status,
+ recvOrder: msg.mut.recvOrder,
+ parentCount: pc,
+ getParent: function () {
+ var parentMsg = getParent(realtime, msg);
+ if (parentMsg) { return wrapMessage(realtime, parentMsg); }
+ },
+ getChildren: function () {
+ return (realtime.messagesByParent[msg.hashOf] || []).map(function (x) {
+ return wrapMessage(realtime, x);
+ });
+ },
+ getContent: function () { return getContentAtState(realtime, msg); },
+ getPatch: function () { return Patch.clone(msg.content); },
+ getInversePatch: function () { return Patch.clone(inversePatch(msg.content)); },
+ equals: function (block, msgOpt) {
+ if (msgOpt) { return msg === msgOpt; }
+ if (!block || typeof(block) !== 'object' || block.type !== 'Block') { return false; }
+ return block.equals(block, msg);
+ }
+ });
+};
+
+var mkConfig = function (config) {
+ config = config || {};
+ if (config.transformFunction) {
+ throw new Error("chainpad config transformFunction is nolonger used");
+ }
+ return Object.freeze({
+ initialState: config.initialState || '',
+ checkpointInterval: config.checkpointInterval || DEFAULT_CHECKPOINT_INTERVAL,
+ avgSyncMilliseconds: config.avgSyncMilliseconds || DEFAULT_AVERAGE_SYNC_MILLISECONDS,
+ strictCheckpointValidation: config.strictCheckpointValidation ||
+ DEFAULT_STRICT_CHECKPOINT_VALIDATION,
+ operationSimplify: config.operationSimplify || Operation.simplify,
+ logLevel: (typeof(config.logLevel) === 'number') ? config.logLevel : 2,
+ noPrune: config.noPrune,
+ patchTransformer: config.patchTransformer || TextTransformer,
+ userName: config.userName || 'anonymous',
+ validateContent: config.validateContent || function (x) { x = x; return true; },
+ diffFunction: config.diffFunction ||
+ function (strA, strB /*:string*/) {
+ return Diff.diff(strA, strB, config.diffBlockSize);
+ },
+ });
+};
+
+/*::
+import type { Operation_Transform_t } from './Operation';
+import type { Operation_Simplify_t } from './Operation';
+import type { Operation_t } from './Operation';
+import type { Patch_t } from './Patch';
+import type { Patch_Transform_t } from './Patch';
+export type ChainPad_Config_t = {
+ initialState?: string,
+ checkpointInterval?: number,
+ avgSyncMilliseconds?: number,
+ validateContent?: (string)=>boolean,
+ strictCheckpointValidation?: boolean,
+ patchTransformer?: Patch_Transform_t,
+ operationSimplify?: Operation_Simplify_t,
+ logLevel?: number,
+ userName?: string,
+ noPrune?: boolean,
+ diffFunction?: (string, string)=>Array,
+ diffBlockSize?: number
+};
+*/
+module.exports.create = function (conf /*:ChainPad_Config_t*/) {
+ var realtime = create(mkConfig(conf));
+ var out = {
+ onPatch: function (handler /*:(Patch_t)=>void*/) {
+ Common.assert(typeof(handler) === 'function');
+ realtime.patchHandlers.push(handler);
+ },
+ patch: function (patch /*:Patch_t|number*/, x /*:?number*/, y /*:?string*/) {
+ if (typeof(patch) === 'number') {
+ // Actually they meant to call realtime.change()
+ if (typeof(x) !== 'number' || typeof(y) !== 'string') { throw new Error(); }
+ out.change(patch, x, y);
+ return;
+ }
+ doPatch(realtime, patch);
+ },
+
+ onChange: function (handler /*:(number, number, string)=>void*/) {
+ Common.assert(typeof(handler) === 'function');
+ realtime.changeHandlers.push(handler);
+ },
+
+ change: function (offset /*:number*/, count /*:number*/, chars /*:string*/) {
+ if (count === 0 && chars === '') { return; }
+ doOperation(realtime, Operation.create(offset, count, chars));
+ },
+
+ contentUpdate: function (newContent /*:string*/) {
+ var ops = realtime.config.diffFunction(realtime.authDoc, newContent);
+ var uncommitted = Patch.create(realtime.uncommitted.parentHash);
+ Array.prototype.push.apply(uncommitted.operations, ops);
+ realtime.uncommitted = uncommitted;
+ },
+
+ onMessage: function (handler /*:(string, ()=>void)=>void*/) {
+ Common.assert(typeof(handler) === 'function');
+ realtime.messageHandlers.push(handler);
+ },
+
+ message: function (message /*:string*/) {
+ handleMessage(realtime, message, false);
+ },
+
+ /// Control functions
+
+ start: function () {
+ realtime.aborted = false;
+ if (realtime.syncSchedule) { unschedule(realtime, realtime.syncSchedule); }
+ realtime.pending = null;
+ realtime.syncSchedule = schedule(realtime, function () { sync(realtime); });
+ },
+
+ abort: function () {
+ realtime.aborted = true;
+ realtime.schedules.forEach(function (s) { clearTimeout(s); });
+ },
+
+ sync: function () {
+ sync(realtime);
+ },
+
+ getAuthDoc: function () { return realtime.authDoc; },
+
+ getUserDoc: function () { return Patch.apply(realtime.uncommitted, realtime.authDoc); },
+
+ getDepthOfState: function (content /*:string*/, minDepth /*:?number*/) {
+ return getDepthOfState(content, minDepth, realtime);
+ },
+
+ onSettle: function (handler /*:()=>void*/) {
+ Common.assert(typeof(handler) === 'function');
+ realtime.onSettle.push(handler);
+ },
+
+ getAuthBlock: function () {
+ return wrapMessage(realtime, realtime.best);
+ },
+
+ getBlockForHash: function (hash /*:string*/) {
+ Common.assert(typeof(hash) === 'string');
+ var msg = realtime.messages[hash];
+ if (msg) { return wrapMessage(realtime, msg); }
+ },
+
+ getBlockHashes: function () {
+ return Object.keys(realtime.messages);
+ },
+
+ getRootBlock: function () {
+ return wrapMessage(realtime, realtime.rootMessage);
+ },
+
+ getRejectedBlock: function (number /*:number*/) {
+ var msg = realtime.rejectedBlocks[number];
+ return msg ? wrapMessage(realtime, msg) : undefined;
+ },
+
+ getLag: function () {
+ var isPending = !!realtime.pending;
+ var lag = realtime.lag;
+ if (realtime.pending) { lag = +new Date() - realtime.timeOfLastSuccess; }
+ return {
+ pending: isPending,
+ lag: lag,
+ active: (!realtime.aborted && realtime.syncSchedule !== -2)
+ };
+ },
+
+ _: undefined
+ };
+ out._ = realtime;
+ return Object.freeze(out);
+};
+
+Object.freeze(module.exports);
+
+},
+"Operation.js": function(module, exports, require){
+/*@flow*/
+/*
+ * Copyright 2014 XWiki SAS
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+"use strict";
+var Common = require('./Common');
+
+var Operation = module.exports;
+
+/*::
+export type Operation_t = {
+ type: 'Operation',
+ offset: number,
+ toRemove: number,
+ toInsert: string
+};
+export type Operation_Packed_t = [number, number, string];
+export type Operation_Simplify_t = (Operation_t, string, typeof(Operation.simplify))=>?Operation_t;
+export type Operation_Transform_t = (string, Operation_t, Operation_t)=>?Operation_t;
+*/
+
+var check = Operation.check = function (op /*:any*/, docLength_opt /*:?number*/) /*:Operation_t*/ {
+ Common.assert(op.type === 'Operation');
+ if (!Common.isUint(op.offset)) { throw new Error(); }
+ if (!Common.isUint(op.toRemove)) { throw new Error(); }
+ if (typeof(op.toInsert) !== 'string') { throw new Error(); }
+ if (op.toRemove < 1 && op.toInsert.length < 1) { throw new Error(); }
+ Common.assert(typeof(docLength_opt) !== 'number' || op.offset + op.toRemove <= docLength_opt);
+ return op;
+};
+
+var create = Operation.create = function (
+ offset /*:?number*/,
+ toRemove /*:?number*/,
+ toInsert /*:?string*/)
+{
+ var out = {
+ type: 'Operation',
+ offset: offset || 0,
+ toRemove: toRemove || 0,
+ toInsert: toInsert || '',
+ };
+ if (Common.PARANOIA) { check(out); }
+ return Object.freeze(out);
+};
+
+Operation.toObj = function (op /*:Operation_t*/) {
+ if (Common.PARANOIA) { check(op); }
+ return [op.offset,op.toRemove,op.toInsert];
+};
+
+ // Allow any as input because we assert its type internally..
+Operation.fromObj = function (obj /*:any*/) {
+ Common.assert(Array.isArray(obj) && obj.length === 3);
+ return create(obj[0], obj[1], obj[2]);
+};
+
+/**
+ * @param op the operation to apply.
+ * @param doc the content to apply the operation on
+ */
+var apply = Operation.apply = function (op /*:Operation_t*/, doc /*:string*/)
+{
+ if (Common.PARANOIA) {
+ Common.assert(typeof(doc) === 'string');
+ check(op, doc.length);
+ }
+ return doc.substring(0,op.offset) + op.toInsert + doc.substring(op.offset + op.toRemove);
+};
+
+Operation.applyMulti = function (ops /*:Array*/, doc /*:string*/)
+{
+ for (var i = ops.length - 1; i >= 0; i--) { doc = apply(ops[i], doc); }
+ return doc;
+};
+
+var invert = Operation.invert = function (op /*:Operation_t*/, doc /*:string*/) {
+ if (Common.PARANOIA) {
+ check(op);
+ Common.assert(typeof(doc) === 'string');
+ Common.assert(op.offset + op.toRemove <= doc.length);
+ }
+ return create(
+ op.offset,
+ op.toInsert.length,
+ doc.substring(op.offset, op.offset + op.toRemove)
+ );
+};
+
+// see http://unicode.org/faq/utf_bom.html#utf16-7
+var surrogatePattern = /[\uD800-\uDBFF]|[\uDC00-\uDFFF]/;
+var hasSurrogate = Operation.hasSurrogate = function(str /*:string*/) {
+ return surrogatePattern.test(str);
+};
+
+/**
+ * ATTENTION: This function is not just a neat way to make patches smaller, it's
+ * actually part of the ChainPad consensus rules, so if you have a clever
+ * idea to make it a bit faster, it is going to cause ChainPad to reject
+ * old patches, which means when you go to load the history of a pad, you're
+ * sunk.
+ * tl;dr can't touch this
+ */
+Operation.simplify = function (op /*:Operation_t*/, doc /*:string*/) {
+ if (Common.PARANOIA) {
+ check(op);
+ Common.assert(typeof(doc) === 'string');
+ Common.assert(op.offset + op.toRemove <= doc.length);
+ }
+ var rop = invert(op, doc);
+
+ var minLen = Math.min(op.toInsert.length, rop.toInsert.length);
+ var i = 0;
+ while (i < minLen && rop.toInsert[i] === op.toInsert[i]) {
+ if (hasSurrogate(rop.toInsert[i]) || hasSurrogate(op.toInsert[i])) {
+ if (op.toInsert[i + 1] === rop.toInsert[i + 1]) {
+ i++;
+ } else {
+ break;
+ }
+ }
+ i++;
+ }
+ var opOffset = op.offset + i;
+ var opToRemove = op.toRemove - i;
+ var opToInsert = op.toInsert.substring(i);
+ var ropToInsert = rop.toInsert.substring(i);
+
+ if (ropToInsert.length === opToInsert.length) {
+ for (i = ropToInsert.length-1; i >= 0 && ropToInsert[i] === opToInsert[i]; i--) ;
+ opToInsert = opToInsert.substring(0, i+1);
+ opToRemove = i+1;
+ }
+
+ if (opToRemove === 0 && opToInsert.length === 0) { return null; }
+ return create(opOffset, opToRemove, opToInsert);
+};
+
+Operation.equals = function (opA /*:Operation_t*/, opB /*:Operation_t*/) {
+ return (opA.toRemove === opB.toRemove
+ && opA.toInsert === opB.toInsert
+ && opA.offset === opB.offset);
+};
+
+Operation.lengthChange = function (op /*:Operation_t*/)
+{
+ if (Common.PARANOIA) { check(op); }
+ return op.toInsert.length - op.toRemove;
+};
+
+/*
+ * @return the merged operation OR null if the result of the merger is a noop.
+ */
+Operation.merge = function (oldOpOrig /*:Operation_t*/, newOpOrig /*:Operation_t*/) {
+ if (Common.PARANOIA) {
+ check(newOpOrig);
+ check(oldOpOrig);
+ }
+
+ var oldOp_offset = oldOpOrig.offset;
+ var oldOp_toRemove = oldOpOrig.toRemove;
+ var oldOp_toInsert = oldOpOrig.toInsert;
+
+ var newOp_offset = newOpOrig.offset;
+ var newOp_toRemove = newOpOrig.toRemove;
+ var newOp_toInsert = newOpOrig.toInsert;
+
+ var offsetDiff = newOp_offset - oldOp_offset;
+
+ if (newOp_toRemove > 0) {
+ var origOldInsert = oldOp_toInsert;
+ oldOp_toInsert = (
+ oldOp_toInsert.substring(0,offsetDiff)
+ + oldOp_toInsert.substring(offsetDiff + newOp_toRemove)
+ );
+ newOp_toRemove -= (origOldInsert.length - oldOp_toInsert.length);
+ if (newOp_toRemove < 0) { newOp_toRemove = 0; }
+
+ oldOp_toRemove += newOp_toRemove;
+ newOp_toRemove = 0;
+ }
+
+ if (offsetDiff < 0) {
+ oldOp_offset += offsetDiff;
+ oldOp_toInsert = newOp_toInsert + oldOp_toInsert;
+
+ } else if (oldOp_toInsert.length === offsetDiff) {
+ oldOp_toInsert = oldOp_toInsert + newOp_toInsert;
+
+ } else if (oldOp_toInsert.length > offsetDiff) {
+ oldOp_toInsert = (
+ oldOp_toInsert.substring(0,offsetDiff)
+ + newOp_toInsert
+ + oldOp_toInsert.substring(offsetDiff)
+ );
+ } else {
+ throw new Error("should never happen\n" +
+ JSON.stringify([oldOpOrig,newOpOrig], null, ' '));
+ }
+
+ if (oldOp_toInsert === '' && oldOp_toRemove === 0) { return null; }
+
+ return create(oldOp_offset, oldOp_toRemove, oldOp_toInsert);
+};
+
+/**
+ * If the new operation deletes what the old op inserted or inserts content in the middle of
+ * the old op's content or if they abbut one another, they should be merged.
+ */
+Operation.shouldMerge = function (oldOp /*:Operation_t*/, newOp /*:Operation_t*/)
+{
+ if (Common.PARANOIA) {
+ check(oldOp);
+ check(newOp);
+ }
+ if (newOp.offset < oldOp.offset) {
+ return (oldOp.offset <= (newOp.offset + newOp.toRemove));
+ } else {
+ return (newOp.offset <= (oldOp.offset + oldOp.toInsert.length));
+ }
+};
+
+/**
+ * Rebase newOp against oldOp.
+ *
+ * @param oldOp the eariler operation to have happened.
+ * @param newOp the later operation to have happened (in time).
+ * @return either the untouched newOp if it need not be rebased,
+ * the rebased clone of newOp if it needs rebasing, or
+ * null if newOp and oldOp must be merged.
+ */
+Operation.rebase = function (oldOp /*:Operation_t*/, newOp /*:Operation_t*/) {
+ if (Common.PARANOIA) {
+ check(oldOp);
+ check(newOp);
+ }
+ if (newOp.offset < oldOp.offset) { return newOp; }
+ return create(
+ newOp.offset + oldOp.toRemove - oldOp.toInsert.length,
+ newOp.toRemove,
+ newOp.toInsert
+ );
+};
+
+/** Used for testing. */
+Operation.random = function (docLength /*:number*/) {
+ Common.assert(Common.isUint(docLength));
+ var offset = Math.floor(Math.random() * 100000000 % docLength) || 0;
+ var toRemove = Math.floor(Math.random() * 100000000 % (docLength - offset)) || 0;
+ var toInsert = '';
+ do {
+ toInsert = Common.randomASCII(Math.floor(Math.random() * 20));
+ } while (toRemove === 0 && toInsert === '');
+ return create(offset, toRemove, toInsert);
+};
+
+Object.freeze(module.exports);
+
+},
+"sha256/hash.js": function(module, exports, require){
+var Utils = require('./utils.js');
+
+function hash_reset () {
+ this.result = null;
+ this.pos = 0;
+ this.len = 0;
+
+ this.asm.reset();
+
+ return this;
+}
+
+function hash_process ( data ) {
+ if ( this.result !== null )
+ throw new IllegalStateError("state must be reset before processing new data");
+
+ if ( Utils.is_string(data) )
+ data = Utils.string_to_bytes(data);
+
+ if ( Utils.is_buffer(data) )
+ data = new Uint8Array(data);
+
+ if ( !Utils.is_bytes(data) )
+ throw new TypeError("data isn't of expected type");
+
+ var asm = this.asm,
+ heap = this.heap,
+ hpos = this.pos,
+ hlen = this.len,
+ dpos = 0,
+ dlen = data.length,
+ wlen = 0;
+
+ while ( dlen > 0 ) {
+ wlen = Utils._heap_write( heap, hpos+hlen, data, dpos, dlen );
+ hlen += wlen;
+ dpos += wlen;
+ dlen -= wlen;
+
+ wlen = asm.process( hpos, hlen );
+
+ hpos += wlen;
+ hlen -= wlen;
+
+ if ( !hlen ) hpos = 0;
+ }
+
+ this.pos = hpos;
+ this.len = hlen;
+
+ return this;
+}
+
+function hash_finish () {
+ if ( this.result !== null )
+ throw new IllegalStateError("state must be reset before processing new data");
+
+ this.asm.finish( this.pos, this.len, 0 );
+
+ this.result = new Uint8Array(this.HASH_SIZE);
+ this.result.set( this.heap.subarray( 0, this.HASH_SIZE ) );
+
+ this.pos = 0;
+ this.len = 0;
+
+ return this;
+}
+
+module.exports.hash_reset = hash_reset;
+module.exports.hash_process = hash_process;
+module.exports.hash_finish = hash_finish;
+
+},
+"sha256/utils.js": function(module, exports, require){
+//var FloatArray = global.Float64Array || global.Float32Array; // make PhantomJS happy
+
+var string_to_bytes = module.exports.string_to_bytes = function( str, utf8 ) {
+ utf8 = !!utf8;
+
+ var len = str.length,
+ bytes = new Uint8Array( utf8 ? 4*len : len );
+
+ for ( var i = 0, j = 0; i < len; i++ ) {
+ var c = str.charCodeAt(i);
+
+ if ( utf8 && 0xd800 <= c && c <= 0xdbff ) {
+ if ( ++i >= len ) throw new Error( "Malformed string, low surrogate expected at position " + i );
+ c = ( (c ^ 0xd800) << 10 ) | 0x10000 | ( str.charCodeAt(i) ^ 0xdc00 );
+ }
+ else if ( !utf8 && c >>> 8 ) {
+ throw new Error("Wide characters are not allowed.");
+ }
+
+ if ( !utf8 || c <= 0x7f ) {
+ bytes[j++] = c;
+ }
+ else if ( c <= 0x7ff ) {
+ bytes[j++] = 0xc0 | (c >> 6);
+ bytes[j++] = 0x80 | (c & 0x3f);
+ }
+ else if ( c <= 0xffff ) {
+ bytes[j++] = 0xe0 | (c >> 12);
+ bytes[j++] = 0x80 | (c >> 6 & 0x3f);
+ bytes[j++] = 0x80 | (c & 0x3f);
+ }
+ else {
+ bytes[j++] = 0xf0 | (c >> 18);
+ bytes[j++] = 0x80 | (c >> 12 & 0x3f);
+ bytes[j++] = 0x80 | (c >> 6 & 0x3f);
+ bytes[j++] = 0x80 | (c & 0x3f);
+ }
+ }
+
+ return bytes.subarray(0, j);
+};
+
+var hex_to_bytes = module.exports.hex_to_bytes = function( str ) {
+ var len = str.length;
+ if ( len & 1 ) {
+ str = '0'+str;
+ len++;
+ }
+ var bytes = new Uint8Array(len>>1);
+ for ( var i = 0; i < len; i += 2 ) {
+ bytes[i>>1] = parseInt( str.substr( i, 2), 16 );
+ }
+ return bytes;
+};
+
+var base64_to_bytes = module.exports.base64_to_bytes = function( str ) {
+ return string_to_bytes( atob( str ) );
+};
+
+var bytes_to_string = module.exports.bytes_to_string = function( bytes, utf8 ) {
+ utf8 = !!utf8;
+
+ var len = bytes.length,
+ chars = new Array(len);
+
+ for ( var i = 0, j = 0; i < len; i++ ) {
+ var b = bytes[i];
+ if ( !utf8 || b < 128 ) {
+ chars[j++] = b;
+ }
+ else if ( b >= 192 && b < 224 && i+1 < len ) {
+ chars[j++] = ( (b & 0x1f) << 6 ) | (bytes[++i] & 0x3f);
+ }
+ else if ( b >= 224 && b < 240 && i+2 < len ) {
+ chars[j++] = ( (b & 0xf) << 12 ) | ( (bytes[++i] & 0x3f) << 6 ) | (bytes[++i] & 0x3f);
+ }
+ else if ( b >= 240 && b < 248 && i+3 < len ) {
+ var c = ( (b & 7) << 18 ) | ( (bytes[++i] & 0x3f) << 12 ) | ( (bytes[++i] & 0x3f) << 6 ) | (bytes[++i] & 0x3f);
+ if ( c <= 0xffff ) {
+ chars[j++] = c;
+ }
+ else {
+ c ^= 0x10000;
+ chars[j++] = 0xd800 | (c >> 10);
+ chars[j++] = 0xdc00 | (c & 0x3ff);
+ }
+ }
+ else {
+ throw new Error("Malformed UTF8 character at byte offset " + i);
+ }
+ }
+
+ var str = '',
+ bs = 16384;
+ for ( var _i = 0; _i < j; _i += bs ) {
+ str += String.fromCharCode.apply( String, chars.slice( _i, _i+bs <= j ? _i+bs : j ) );
+ }
+
+ return str;
+};
+
+var bytes_to_hex = module.exports.bytes_to_hex = function( arr ) {
+ var str = '';
+ for ( var i = 0; i < arr.length; i++ ) {
+ var h = ( arr[i] & 0xff ).toString(16);
+ if ( h.length < 2 ) str += '0';
+ str += h;
+ }
+ return str;
+};
+
+var bytes_to_base64 = module.exports.bytes_to_base64 = function( arr ) {
+ return btoa( bytes_to_string(arr) );
+};
+
+var pow2_ceil = module.exports.pow2_ceil = function( a ) {
+ a -= 1;
+ a |= a >>> 1;
+ a |= a >>> 2;
+ a |= a >>> 4;
+ a |= a >>> 8;
+ a |= a >>> 16;
+ a += 1;
+ return a;
+};
+
+var is_number = module.exports.is_number = function( a ) {
+ return ( typeof a === 'number' );
+};
+
+var is_string = module.exports.is_string = function( a ) {
+ return ( typeof a === 'string' );
+};
+
+var is_buffer = module.exports.is_buffer = function( a ) {
+ return ( a instanceof ArrayBuffer );
+};
+
+var is_bytes = module.exports.is_bytes = function( a ) {
+ return ( a instanceof Uint8Array );
+};
+
+var is_typed_array = module.exports.is_typed_array = function( a ) {
+ return ( a instanceof Int8Array ) || ( a instanceof Uint8Array )
+ || ( a instanceof Int16Array ) || ( a instanceof Uint16Array )
+ || ( a instanceof Int32Array ) || ( a instanceof Uint32Array )
+ || ( a instanceof Float32Array )
+ || ( a instanceof Float64Array );
+};
+
+var _heap_init = module.exports._heap_init = function( constructor, options ) {
+ var heap = options.heap,
+ size = heap ? heap.byteLength : options.heapSize || 65536;
+
+ if ( size & 0xfff || size <= 0 )
+ throw new Error("heap size must be a positive integer and a multiple of 4096");
+
+ heap = heap || new constructor( new ArrayBuffer(size) );
+
+ return heap;
+};
+
+var _heap_write = module.exports._heap_write = function( heap, hpos, data, dpos, dlen ) {
+ var hlen = heap.length - hpos,
+ wlen = ( hlen < dlen ) ? hlen : dlen;
+
+ heap.set( data.subarray( dpos, dpos+wlen ), hpos );
+
+ return wlen;
+};
+
+},
+"sha256/sha256.js": function(module, exports, require){
+var Utils = require('./utils.js');
+var Hash = require('./hash.js');
+var Asm = require('./sha256.asm.js');
+
+var _sha256_block_size = 64,
+ _sha256_hash_size = 32;
+
+function sha256_constructor ( options ) {
+ options = options || {};
+
+ this.heap = Utils._heap_init( Uint8Array, options );
+ this.asm = options.asm || Asm.sha256_asm( { Uint8Array: Uint8Array }, null, this.heap.buffer );
+
+ this.BLOCK_SIZE = _sha256_block_size;
+ this.HASH_SIZE = _sha256_hash_size;
+
+ this.reset();
+}
+
+sha256_constructor.BLOCK_SIZE = _sha256_block_size;
+sha256_constructor.HASH_SIZE = _sha256_hash_size;
+var sha256_prototype = sha256_constructor.prototype;
+sha256_prototype.reset = Hash.hash_reset;
+sha256_prototype.process = Hash.hash_process;
+sha256_prototype.finish = Hash.hash_finish;
+
+var sha256_instance = null;
+
+function get_sha256_instance () {
+ if ( sha256_instance === null ) sha256_instance = new sha256_constructor( { heapSize: 0x100000 } );
+ return sha256_instance;
+}
+
+module.exports.get_sha256_instance = get_sha256_instance;
+module.exports.sha256_constructor = sha256_constructor;
+
+},
+"sha256/exports.js": function(module, exports, require){
+var Sha256 = require('./sha256.js');
+var Utils = require('./utils.js');
+
+/**
+ * SHA256 exports
+ */
+
+function sha256_bytes ( data ) {
+ if ( data === undefined ) throw new SyntaxError("data required");
+ return Sha256.get_sha256_instance().reset().process(data).finish().result;
+}
+
+function sha256_hex ( data ) {
+ var result = sha256_bytes(data);
+ return Utils.bytes_to_hex(result);
+}
+
+function sha256_base64 ( data ) {
+ var result = sha256_bytes(data);
+ return Utils.bytes_to_base64(result);
+}
+
+Sha256.sha256_constructor.bytes = sha256_bytes;
+Sha256.sha256_constructor.hex = sha256_hex;
+Sha256.sha256_constructor.base64 = sha256_base64;
+
+//exports.SHA256 = sha256_constructor;
+module.exports = Sha256.sha256_constructor;
+
+},
+"sha256/sha256.asm.js": function(module, exports, require){
+module.exports.sha256_asm = function sha256_asm ( stdlib, foreign, buffer ) {
+ "use asm";
+
+ // SHA256 state
+ var H0 = 0, H1 = 0, H2 = 0, H3 = 0, H4 = 0, H5 = 0, H6 = 0, H7 = 0,
+ TOTAL0 = 0, TOTAL1 = 0;
+
+ // HMAC state
+ var I0 = 0, I1 = 0, I2 = 0, I3 = 0, I4 = 0, I5 = 0, I6 = 0, I7 = 0,
+ O0 = 0, O1 = 0, O2 = 0, O3 = 0, O4 = 0, O5 = 0, O6 = 0, O7 = 0;
+
+ // I/O buffer
+ var HEAP = new stdlib.Uint8Array(buffer);
+
+ function _core ( w0, w1, w2, w3, w4, w5, w6, w7, w8, w9, w10, w11, w12, w13, w14, w15 ) {
+ w0 = w0|0;
+ w1 = w1|0;
+ w2 = w2|0;
+ w3 = w3|0;
+ w4 = w4|0;
+ w5 = w5|0;
+ w6 = w6|0;
+ w7 = w7|0;
+ w8 = w8|0;
+ w9 = w9|0;
+ w10 = w10|0;
+ w11 = w11|0;
+ w12 = w12|0;
+ w13 = w13|0;
+ w14 = w14|0;
+ w15 = w15|0;
+
+ var a = 0, b = 0, c = 0, d = 0, e = 0, f = 0, g = 0, h = 0,
+ t = 0;
+
+ a = H0;
+ b = H1;
+ c = H2;
+ d = H3;
+ e = H4;
+ f = H5;
+ g = H6;
+ h = H7;
+
+ // 0
+ t = ( w0 + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x428a2f98 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 1
+ t = ( w1 + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x71374491 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 2
+ t = ( w2 + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0xb5c0fbcf )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 3
+ t = ( w3 + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0xe9b5dba5 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 4
+ t = ( w4 + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x3956c25b )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 5
+ t = ( w5 + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x59f111f1 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 6
+ t = ( w6 + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x923f82a4 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 7
+ t = ( w7 + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0xab1c5ed5 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 8
+ t = ( w8 + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0xd807aa98 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 9
+ t = ( w9 + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x12835b01 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 10
+ t = ( w10 + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x243185be )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 11
+ t = ( w11 + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x550c7dc3 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 12
+ t = ( w12 + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x72be5d74 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 13
+ t = ( w13 + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x80deb1fe )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 14
+ t = ( w14 + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x9bdc06a7 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 15
+ t = ( w15 + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0xc19bf174 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 16
+ w0 = t = ( ( w1>>>7 ^ w1>>>18 ^ w1>>>3 ^ w1<<25 ^ w1<<14 ) + ( w14>>>17 ^ w14>>>19 ^ w14>>>10 ^ w14<<15 ^ w14<<13 ) + w0 + w9 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0xe49b69c1 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 17
+ w1 = t = ( ( w2>>>7 ^ w2>>>18 ^ w2>>>3 ^ w2<<25 ^ w2<<14 ) + ( w15>>>17 ^ w15>>>19 ^ w15>>>10 ^ w15<<15 ^ w15<<13 ) + w1 + w10 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0xefbe4786 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 18
+ w2 = t = ( ( w3>>>7 ^ w3>>>18 ^ w3>>>3 ^ w3<<25 ^ w3<<14 ) + ( w0>>>17 ^ w0>>>19 ^ w0>>>10 ^ w0<<15 ^ w0<<13 ) + w2 + w11 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x0fc19dc6 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 19
+ w3 = t = ( ( w4>>>7 ^ w4>>>18 ^ w4>>>3 ^ w4<<25 ^ w4<<14 ) + ( w1>>>17 ^ w1>>>19 ^ w1>>>10 ^ w1<<15 ^ w1<<13 ) + w3 + w12 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x240ca1cc )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 20
+ w4 = t = ( ( w5>>>7 ^ w5>>>18 ^ w5>>>3 ^ w5<<25 ^ w5<<14 ) + ( w2>>>17 ^ w2>>>19 ^ w2>>>10 ^ w2<<15 ^ w2<<13 ) + w4 + w13 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x2de92c6f )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 21
+ w5 = t = ( ( w6>>>7 ^ w6>>>18 ^ w6>>>3 ^ w6<<25 ^ w6<<14 ) + ( w3>>>17 ^ w3>>>19 ^ w3>>>10 ^ w3<<15 ^ w3<<13 ) + w5 + w14 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x4a7484aa )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 22
+ w6 = t = ( ( w7>>>7 ^ w7>>>18 ^ w7>>>3 ^ w7<<25 ^ w7<<14 ) + ( w4>>>17 ^ w4>>>19 ^ w4>>>10 ^ w4<<15 ^ w4<<13 ) + w6 + w15 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x5cb0a9dc )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 23
+ w7 = t = ( ( w8>>>7 ^ w8>>>18 ^ w8>>>3 ^ w8<<25 ^ w8<<14 ) + ( w5>>>17 ^ w5>>>19 ^ w5>>>10 ^ w5<<15 ^ w5<<13 ) + w7 + w0 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x76f988da )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 24
+ w8 = t = ( ( w9>>>7 ^ w9>>>18 ^ w9>>>3 ^ w9<<25 ^ w9<<14 ) + ( w6>>>17 ^ w6>>>19 ^ w6>>>10 ^ w6<<15 ^ w6<<13 ) + w8 + w1 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x983e5152 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 25
+ w9 = t = ( ( w10>>>7 ^ w10>>>18 ^ w10>>>3 ^ w10<<25 ^ w10<<14 ) + ( w7>>>17 ^ w7>>>19 ^ w7>>>10 ^ w7<<15 ^ w7<<13 ) + w9 + w2 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0xa831c66d )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 26
+ w10 = t = ( ( w11>>>7 ^ w11>>>18 ^ w11>>>3 ^ w11<<25 ^ w11<<14 ) + ( w8>>>17 ^ w8>>>19 ^ w8>>>10 ^ w8<<15 ^ w8<<13 ) + w10 + w3 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0xb00327c8 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 27
+ w11 = t = ( ( w12>>>7 ^ w12>>>18 ^ w12>>>3 ^ w12<<25 ^ w12<<14 ) + ( w9>>>17 ^ w9>>>19 ^ w9>>>10 ^ w9<<15 ^ w9<<13 ) + w11 + w4 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0xbf597fc7 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 28
+ w12 = t = ( ( w13>>>7 ^ w13>>>18 ^ w13>>>3 ^ w13<<25 ^ w13<<14 ) + ( w10>>>17 ^ w10>>>19 ^ w10>>>10 ^ w10<<15 ^ w10<<13 ) + w12 + w5 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0xc6e00bf3 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 29
+ w13 = t = ( ( w14>>>7 ^ w14>>>18 ^ w14>>>3 ^ w14<<25 ^ w14<<14 ) + ( w11>>>17 ^ w11>>>19 ^ w11>>>10 ^ w11<<15 ^ w11<<13 ) + w13 + w6 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0xd5a79147 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 30
+ w14 = t = ( ( w15>>>7 ^ w15>>>18 ^ w15>>>3 ^ w15<<25 ^ w15<<14 ) + ( w12>>>17 ^ w12>>>19 ^ w12>>>10 ^ w12<<15 ^ w12<<13 ) + w14 + w7 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x06ca6351 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 31
+ w15 = t = ( ( w0>>>7 ^ w0>>>18 ^ w0>>>3 ^ w0<<25 ^ w0<<14 ) + ( w13>>>17 ^ w13>>>19 ^ w13>>>10 ^ w13<<15 ^ w13<<13 ) + w15 + w8 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x14292967 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 32
+ w0 = t = ( ( w1>>>7 ^ w1>>>18 ^ w1>>>3 ^ w1<<25 ^ w1<<14 ) + ( w14>>>17 ^ w14>>>19 ^ w14>>>10 ^ w14<<15 ^ w14<<13 ) + w0 + w9 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x27b70a85 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 33
+ w1 = t = ( ( w2>>>7 ^ w2>>>18 ^ w2>>>3 ^ w2<<25 ^ w2<<14 ) + ( w15>>>17 ^ w15>>>19 ^ w15>>>10 ^ w15<<15 ^ w15<<13 ) + w1 + w10 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x2e1b2138 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 34
+ w2 = t = ( ( w3>>>7 ^ w3>>>18 ^ w3>>>3 ^ w3<<25 ^ w3<<14 ) + ( w0>>>17 ^ w0>>>19 ^ w0>>>10 ^ w0<<15 ^ w0<<13 ) + w2 + w11 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x4d2c6dfc )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 35
+ w3 = t = ( ( w4>>>7 ^ w4>>>18 ^ w4>>>3 ^ w4<<25 ^ w4<<14 ) + ( w1>>>17 ^ w1>>>19 ^ w1>>>10 ^ w1<<15 ^ w1<<13 ) + w3 + w12 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x53380d13 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 36
+ w4 = t = ( ( w5>>>7 ^ w5>>>18 ^ w5>>>3 ^ w5<<25 ^ w5<<14 ) + ( w2>>>17 ^ w2>>>19 ^ w2>>>10 ^ w2<<15 ^ w2<<13 ) + w4 + w13 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x650a7354 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 37
+ w5 = t = ( ( w6>>>7 ^ w6>>>18 ^ w6>>>3 ^ w6<<25 ^ w6<<14 ) + ( w3>>>17 ^ w3>>>19 ^ w3>>>10 ^ w3<<15 ^ w3<<13 ) + w5 + w14 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x766a0abb )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 38
+ w6 = t = ( ( w7>>>7 ^ w7>>>18 ^ w7>>>3 ^ w7<<25 ^ w7<<14 ) + ( w4>>>17 ^ w4>>>19 ^ w4>>>10 ^ w4<<15 ^ w4<<13 ) + w6 + w15 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x81c2c92e )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 39
+ w7 = t = ( ( w8>>>7 ^ w8>>>18 ^ w8>>>3 ^ w8<<25 ^ w8<<14 ) + ( w5>>>17 ^ w5>>>19 ^ w5>>>10 ^ w5<<15 ^ w5<<13 ) + w7 + w0 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x92722c85 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 40
+ w8 = t = ( ( w9>>>7 ^ w9>>>18 ^ w9>>>3 ^ w9<<25 ^ w9<<14 ) + ( w6>>>17 ^ w6>>>19 ^ w6>>>10 ^ w6<<15 ^ w6<<13 ) + w8 + w1 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0xa2bfe8a1 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 41
+ w9 = t = ( ( w10>>>7 ^ w10>>>18 ^ w10>>>3 ^ w10<<25 ^ w10<<14 ) + ( w7>>>17 ^ w7>>>19 ^ w7>>>10 ^ w7<<15 ^ w7<<13 ) + w9 + w2 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0xa81a664b )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 42
+ w10 = t = ( ( w11>>>7 ^ w11>>>18 ^ w11>>>3 ^ w11<<25 ^ w11<<14 ) + ( w8>>>17 ^ w8>>>19 ^ w8>>>10 ^ w8<<15 ^ w8<<13 ) + w10 + w3 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0xc24b8b70 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 43
+ w11 = t = ( ( w12>>>7 ^ w12>>>18 ^ w12>>>3 ^ w12<<25 ^ w12<<14 ) + ( w9>>>17 ^ w9>>>19 ^ w9>>>10 ^ w9<<15 ^ w9<<13 ) + w11 + w4 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0xc76c51a3 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 44
+ w12 = t = ( ( w13>>>7 ^ w13>>>18 ^ w13>>>3 ^ w13<<25 ^ w13<<14 ) + ( w10>>>17 ^ w10>>>19 ^ w10>>>10 ^ w10<<15 ^ w10<<13 ) + w12 + w5 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0xd192e819 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 45
+ w13 = t = ( ( w14>>>7 ^ w14>>>18 ^ w14>>>3 ^ w14<<25 ^ w14<<14 ) + ( w11>>>17 ^ w11>>>19 ^ w11>>>10 ^ w11<<15 ^ w11<<13 ) + w13 + w6 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0xd6990624 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 46
+ w14 = t = ( ( w15>>>7 ^ w15>>>18 ^ w15>>>3 ^ w15<<25 ^ w15<<14 ) + ( w12>>>17 ^ w12>>>19 ^ w12>>>10 ^ w12<<15 ^ w12<<13 ) + w14 + w7 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0xf40e3585 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 47
+ w15 = t = ( ( w0>>>7 ^ w0>>>18 ^ w0>>>3 ^ w0<<25 ^ w0<<14 ) + ( w13>>>17 ^ w13>>>19 ^ w13>>>10 ^ w13<<15 ^ w13<<13 ) + w15 + w8 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x106aa070 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 48
+ w0 = t = ( ( w1>>>7 ^ w1>>>18 ^ w1>>>3 ^ w1<<25 ^ w1<<14 ) + ( w14>>>17 ^ w14>>>19 ^ w14>>>10 ^ w14<<15 ^ w14<<13 ) + w0 + w9 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x19a4c116 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 49
+ w1 = t = ( ( w2>>>7 ^ w2>>>18 ^ w2>>>3 ^ w2<<25 ^ w2<<14 ) + ( w15>>>17 ^ w15>>>19 ^ w15>>>10 ^ w15<<15 ^ w15<<13 ) + w1 + w10 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x1e376c08 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 50
+ w2 = t = ( ( w3>>>7 ^ w3>>>18 ^ w3>>>3 ^ w3<<25 ^ w3<<14 ) + ( w0>>>17 ^ w0>>>19 ^ w0>>>10 ^ w0<<15 ^ w0<<13 ) + w2 + w11 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x2748774c )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 51
+ w3 = t = ( ( w4>>>7 ^ w4>>>18 ^ w4>>>3 ^ w4<<25 ^ w4<<14 ) + ( w1>>>17 ^ w1>>>19 ^ w1>>>10 ^ w1<<15 ^ w1<<13 ) + w3 + w12 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x34b0bcb5 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 52
+ w4 = t = ( ( w5>>>7 ^ w5>>>18 ^ w5>>>3 ^ w5<<25 ^ w5<<14 ) + ( w2>>>17 ^ w2>>>19 ^ w2>>>10 ^ w2<<15 ^ w2<<13 ) + w4 + w13 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x391c0cb3 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 53
+ w5 = t = ( ( w6>>>7 ^ w6>>>18 ^ w6>>>3 ^ w6<<25 ^ w6<<14 ) + ( w3>>>17 ^ w3>>>19 ^ w3>>>10 ^ w3<<15 ^ w3<<13 ) + w5 + w14 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x4ed8aa4a )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 54
+ w6 = t = ( ( w7>>>7 ^ w7>>>18 ^ w7>>>3 ^ w7<<25 ^ w7<<14 ) + ( w4>>>17 ^ w4>>>19 ^ w4>>>10 ^ w4<<15 ^ w4<<13 ) + w6 + w15 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x5b9cca4f )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 55
+ w7 = t = ( ( w8>>>7 ^ w8>>>18 ^ w8>>>3 ^ w8<<25 ^ w8<<14 ) + ( w5>>>17 ^ w5>>>19 ^ w5>>>10 ^ w5<<15 ^ w5<<13 ) + w7 + w0 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x682e6ff3 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 56
+ w8 = t = ( ( w9>>>7 ^ w9>>>18 ^ w9>>>3 ^ w9<<25 ^ w9<<14 ) + ( w6>>>17 ^ w6>>>19 ^ w6>>>10 ^ w6<<15 ^ w6<<13 ) + w8 + w1 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x748f82ee )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 57
+ w9 = t = ( ( w10>>>7 ^ w10>>>18 ^ w10>>>3 ^ w10<<25 ^ w10<<14 ) + ( w7>>>17 ^ w7>>>19 ^ w7>>>10 ^ w7<<15 ^ w7<<13 ) + w9 + w2 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x78a5636f )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 58
+ w10 = t = ( ( w11>>>7 ^ w11>>>18 ^ w11>>>3 ^ w11<<25 ^ w11<<14 ) + ( w8>>>17 ^ w8>>>19 ^ w8>>>10 ^ w8<<15 ^ w8<<13 ) + w10 + w3 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x84c87814 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 59
+ w11 = t = ( ( w12>>>7 ^ w12>>>18 ^ w12>>>3 ^ w12<<25 ^ w12<<14 ) + ( w9>>>17 ^ w9>>>19 ^ w9>>>10 ^ w9<<15 ^ w9<<13 ) + w11 + w4 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x8cc70208 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 60
+ w12 = t = ( ( w13>>>7 ^ w13>>>18 ^ w13>>>3 ^ w13<<25 ^ w13<<14 ) + ( w10>>>17 ^ w10>>>19 ^ w10>>>10 ^ w10<<15 ^ w10<<13 ) + w12 + w5 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0x90befffa )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 61
+ w13 = t = ( ( w14>>>7 ^ w14>>>18 ^ w14>>>3 ^ w14<<25 ^ w14<<14 ) + ( w11>>>17 ^ w11>>>19 ^ w11>>>10 ^ w11<<15 ^ w11<<13 ) + w13 + w6 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0xa4506ceb )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 62
+ w14 = t = ( ( w15>>>7 ^ w15>>>18 ^ w15>>>3 ^ w15<<25 ^ w15<<14 ) + ( w12>>>17 ^ w12>>>19 ^ w12>>>10 ^ w12<<15 ^ w12<<13 ) + w14 + w7 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0xbef9a3f7 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ // 63
+ w15 = t = ( ( w0>>>7 ^ w0>>>18 ^ w0>>>3 ^ w0<<25 ^ w0<<14 ) + ( w13>>>17 ^ w13>>>19 ^ w13>>>10 ^ w13<<15 ^ w13<<13 ) + w15 + w8 )|0;
+ t = ( t + h + ( e>>>6 ^ e>>>11 ^ e>>>25 ^ e<<26 ^ e<<21 ^ e<<7 ) + ( g ^ e & (f^g) ) + 0xc67178f2 )|0;
+ h = g; g = f; f = e; e = ( d + t )|0; d = c; c = b; b = a;
+ a = ( t + ( (b & c) ^ ( d & (b ^ c) ) ) + ( b>>>2 ^ b>>>13 ^ b>>>22 ^ b<<30 ^ b<<19 ^ b<<10 ) )|0;
+
+ H0 = ( H0 + a )|0;
+ H1 = ( H1 + b )|0;
+ H2 = ( H2 + c )|0;
+ H3 = ( H3 + d )|0;
+ H4 = ( H4 + e )|0;
+ H5 = ( H5 + f )|0;
+ H6 = ( H6 + g )|0;
+ H7 = ( H7 + h )|0;
+ }
+
+ function _core_heap ( offset ) {
+ offset = offset|0;
+
+ _core(
+ HEAP[offset|0]<<24 | HEAP[offset|1]<<16 | HEAP[offset|2]<<8 | HEAP[offset|3],
+ HEAP[offset|4]<<24 | HEAP[offset|5]<<16 | HEAP[offset|6]<<8 | HEAP[offset|7],
+ HEAP[offset|8]<<24 | HEAP[offset|9]<<16 | HEAP[offset|10]<<8 | HEAP[offset|11],
+ HEAP[offset|12]<<24 | HEAP[offset|13]<<16 | HEAP[offset|14]<<8 | HEAP[offset|15],
+ HEAP[offset|16]<<24 | HEAP[offset|17]<<16 | HEAP[offset|18]<<8 | HEAP[offset|19],
+ HEAP[offset|20]<<24 | HEAP[offset|21]<<16 | HEAP[offset|22]<<8 | HEAP[offset|23],
+ HEAP[offset|24]<<24 | HEAP[offset|25]<<16 | HEAP[offset|26]<<8 | HEAP[offset|27],
+ HEAP[offset|28]<<24 | HEAP[offset|29]<<16 | HEAP[offset|30]<<8 | HEAP[offset|31],
+ HEAP[offset|32]<<24 | HEAP[offset|33]<<16 | HEAP[offset|34]<<8 | HEAP[offset|35],
+ HEAP[offset|36]<<24 | HEAP[offset|37]<<16 | HEAP[offset|38]<<8 | HEAP[offset|39],
+ HEAP[offset|40]<<24 | HEAP[offset|41]<<16 | HEAP[offset|42]<<8 | HEAP[offset|43],
+ HEAP[offset|44]<<24 | HEAP[offset|45]<<16 | HEAP[offset|46]<<8 | HEAP[offset|47],
+ HEAP[offset|48]<<24 | HEAP[offset|49]<<16 | HEAP[offset|50]<<8 | HEAP[offset|51],
+ HEAP[offset|52]<<24 | HEAP[offset|53]<<16 | HEAP[offset|54]<<8 | HEAP[offset|55],
+ HEAP[offset|56]<<24 | HEAP[offset|57]<<16 | HEAP[offset|58]<<8 | HEAP[offset|59],
+ HEAP[offset|60]<<24 | HEAP[offset|61]<<16 | HEAP[offset|62]<<8 | HEAP[offset|63]
+ );
+ }
+
+ // offset — multiple of 32
+ function _state_to_heap ( output ) {
+ output = output|0;
+
+ HEAP[output|0] = H0>>>24;
+ HEAP[output|1] = H0>>>16&255;
+ HEAP[output|2] = H0>>>8&255;
+ HEAP[output|3] = H0&255;
+ HEAP[output|4] = H1>>>24;
+ HEAP[output|5] = H1>>>16&255;
+ HEAP[output|6] = H1>>>8&255;
+ HEAP[output|7] = H1&255;
+ HEAP[output|8] = H2>>>24;
+ HEAP[output|9] = H2>>>16&255;
+ HEAP[output|10] = H2>>>8&255;
+ HEAP[output|11] = H2&255;
+ HEAP[output|12] = H3>>>24;
+ HEAP[output|13] = H3>>>16&255;
+ HEAP[output|14] = H3>>>8&255;
+ HEAP[output|15] = H3&255;
+ HEAP[output|16] = H4>>>24;
+ HEAP[output|17] = H4>>>16&255;
+ HEAP[output|18] = H4>>>8&255;
+ HEAP[output|19] = H4&255;
+ HEAP[output|20] = H5>>>24;
+ HEAP[output|21] = H5>>>16&255;
+ HEAP[output|22] = H5>>>8&255;
+ HEAP[output|23] = H5&255;
+ HEAP[output|24] = H6>>>24;
+ HEAP[output|25] = H6>>>16&255;
+ HEAP[output|26] = H6>>>8&255;
+ HEAP[output|27] = H6&255;
+ HEAP[output|28] = H7>>>24;
+ HEAP[output|29] = H7>>>16&255;
+ HEAP[output|30] = H7>>>8&255;
+ HEAP[output|31] = H7&255;
+ }
+
+ function reset () {
+ H0 = 0x6a09e667;
+ H1 = 0xbb67ae85;
+ H2 = 0x3c6ef372;
+ H3 = 0xa54ff53a;
+ H4 = 0x510e527f;
+ H5 = 0x9b05688c;
+ H6 = 0x1f83d9ab;
+ H7 = 0x5be0cd19;
+ TOTAL0 = TOTAL1 = 0;
+ }
+
+ function init ( h0, h1, h2, h3, h4, h5, h6, h7, total0, total1 ) {
+ h0 = h0|0;
+ h1 = h1|0;
+ h2 = h2|0;
+ h3 = h3|0;
+ h4 = h4|0;
+ h5 = h5|0;
+ h6 = h6|0;
+ h7 = h7|0;
+ total0 = total0|0;
+ total1 = total1|0;
+
+ H0 = h0;
+ H1 = h1;
+ H2 = h2;
+ H3 = h3;
+ H4 = h4;
+ H5 = h5;
+ H6 = h6;
+ H7 = h7;
+ TOTAL0 = total0;
+ TOTAL1 = total1;
+ }
+
+ // offset — multiple of 64
+ function process ( offset, length ) {
+ offset = offset|0;
+ length = length|0;
+
+ var hashed = 0;
+
+ if ( offset & 63 )
+ return -1;
+
+ while ( (length|0) >= 64 ) {
+ _core_heap(offset);
+
+ offset = ( offset + 64 )|0;
+ length = ( length - 64 )|0;
+
+ hashed = ( hashed + 64 )|0;
+ }
+
+ TOTAL0 = ( TOTAL0 + hashed )|0;
+ if ( TOTAL0>>>0 < hashed>>>0 ) TOTAL1 = ( TOTAL1 + 1 )|0;
+
+ return hashed|0;
+ }
+
+ // offset — multiple of 64
+ // output — multiple of 32
+ function finish ( offset, length, output ) {
+ offset = offset|0;
+ length = length|0;
+ output = output|0;
+
+ var hashed = 0,
+ i = 0;
+
+ if ( offset & 63 )
+ return -1;
+
+ if ( ~output )
+ if ( output & 31 )
+ return -1;
+
+ if ( (length|0) >= 64 ) {
+ hashed = process( offset, length )|0;
+ if ( (hashed|0) == -1 )
+ return -1;
+
+ offset = ( offset + hashed )|0;
+ length = ( length - hashed )|0;
+ }
+
+ hashed = ( hashed + length )|0;
+ TOTAL0 = ( TOTAL0 + length )|0;
+ if ( TOTAL0>>>0 < length>>>0 ) TOTAL1 = ( TOTAL1 + 1 )|0;
+
+ HEAP[offset|length] = 0x80;
+
+ if ( (length|0) >= 56 ) {
+ for ( i = (length+1)|0; (i|0) < 64; i = (i+1)|0 )
+ HEAP[offset|i] = 0x00;
+
+ _core_heap(offset);
+
+ length = 0;
+
+ HEAP[offset|0] = 0;
+ }
+
+ for ( i = (length+1)|0; (i|0) < 59; i = (i+1)|0 )
+ HEAP[offset|i] = 0;
+
+ HEAP[offset|56] = TOTAL1>>>21&255;
+ HEAP[offset|57] = TOTAL1>>>13&255;
+ HEAP[offset|58] = TOTAL1>>>5&255;
+ HEAP[offset|59] = TOTAL1<<3&255 | TOTAL0>>>29;
+ HEAP[offset|60] = TOTAL0>>>21&255;
+ HEAP[offset|61] = TOTAL0>>>13&255;
+ HEAP[offset|62] = TOTAL0>>>5&255;
+ HEAP[offset|63] = TOTAL0<<3&255;
+ _core_heap(offset);
+
+ if ( ~output )
+ _state_to_heap(output);
+
+ return hashed|0;
+ }
+
+ function hmac_reset () {
+ H0 = I0;
+ H1 = I1;
+ H2 = I2;
+ H3 = I3;
+ H4 = I4;
+ H5 = I5;
+ H6 = I6;
+ H7 = I7;
+ TOTAL0 = 64;
+ TOTAL1 = 0;
+ }
+
+ function _hmac_opad () {
+ H0 = O0;
+ H1 = O1;
+ H2 = O2;
+ H3 = O3;
+ H4 = O4;
+ H5 = O5;
+ H6 = O6;
+ H7 = O7;
+ TOTAL0 = 64;
+ TOTAL1 = 0;
+ }
+
+ function hmac_init ( p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10, p11, p12, p13, p14, p15 ) {
+ p0 = p0|0;
+ p1 = p1|0;
+ p2 = p2|0;
+ p3 = p3|0;
+ p4 = p4|0;
+ p5 = p5|0;
+ p6 = p6|0;
+ p7 = p7|0;
+ p8 = p8|0;
+ p9 = p9|0;
+ p10 = p10|0;
+ p11 = p11|0;
+ p12 = p12|0;
+ p13 = p13|0;
+ p14 = p14|0;
+ p15 = p15|0;
+
+ // opad
+ reset();
+ _core(
+ p0 ^ 0x5c5c5c5c,
+ p1 ^ 0x5c5c5c5c,
+ p2 ^ 0x5c5c5c5c,
+ p3 ^ 0x5c5c5c5c,
+ p4 ^ 0x5c5c5c5c,
+ p5 ^ 0x5c5c5c5c,
+ p6 ^ 0x5c5c5c5c,
+ p7 ^ 0x5c5c5c5c,
+ p8 ^ 0x5c5c5c5c,
+ p9 ^ 0x5c5c5c5c,
+ p10 ^ 0x5c5c5c5c,
+ p11 ^ 0x5c5c5c5c,
+ p12 ^ 0x5c5c5c5c,
+ p13 ^ 0x5c5c5c5c,
+ p14 ^ 0x5c5c5c5c,
+ p15 ^ 0x5c5c5c5c
+ );
+ O0 = H0;
+ O1 = H1;
+ O2 = H2;
+ O3 = H3;
+ O4 = H4;
+ O5 = H5;
+ O6 = H6;
+ O7 = H7;
+
+ // ipad
+ reset();
+ _core(
+ p0 ^ 0x36363636,
+ p1 ^ 0x36363636,
+ p2 ^ 0x36363636,
+ p3 ^ 0x36363636,
+ p4 ^ 0x36363636,
+ p5 ^ 0x36363636,
+ p6 ^ 0x36363636,
+ p7 ^ 0x36363636,
+ p8 ^ 0x36363636,
+ p9 ^ 0x36363636,
+ p10 ^ 0x36363636,
+ p11 ^ 0x36363636,
+ p12 ^ 0x36363636,
+ p13 ^ 0x36363636,
+ p14 ^ 0x36363636,
+ p15 ^ 0x36363636
+ );
+ I0 = H0;
+ I1 = H1;
+ I2 = H2;
+ I3 = H3;
+ I4 = H4;
+ I5 = H5;
+ I6 = H6;
+ I7 = H7;
+
+ TOTAL0 = 64;
+ TOTAL1 = 0;
+ }
+
+ // offset — multiple of 64
+ // output — multiple of 32
+ function hmac_finish ( offset, length, output ) {
+ offset = offset|0;
+ length = length|0;
+ output = output|0;
+
+ var t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0,
+ hashed = 0;
+
+ if ( offset & 63 )
+ return -1;
+
+ if ( ~output )
+ if ( output & 31 )
+ return -1;
+
+ hashed = finish( offset, length, -1 )|0;
+ t0 = H0, t1 = H1, t2 = H2, t3 = H3, t4 = H4, t5 = H5, t6 = H6, t7 = H7;
+
+ _hmac_opad();
+ _core( t0, t1, t2, t3, t4, t5, t6, t7, 0x80000000, 0, 0, 0, 0, 0, 0, 768 );
+
+ if ( ~output )
+ _state_to_heap(output);
+
+ return hashed|0;
+ }
+
+ // salt is assumed to be already processed
+ // offset — multiple of 64
+ // output — multiple of 32
+ function pbkdf2_generate_block ( offset, length, block, count, output ) {
+ offset = offset|0;
+ length = length|0;
+ block = block|0;
+ count = count|0;
+ output = output|0;
+
+ var h0 = 0, h1 = 0, h2 = 0, h3 = 0, h4 = 0, h5 = 0, h6 = 0, h7 = 0,
+ t0 = 0, t1 = 0, t2 = 0, t3 = 0, t4 = 0, t5 = 0, t6 = 0, t7 = 0;
+
+ if ( offset & 63 )
+ return -1;
+
+ if ( ~output )
+ if ( output & 31 )
+ return -1;
+
+ // pad block number into heap
+ // FIXME probable OOB write
+ HEAP[(offset+length)|0] = block>>>24;
+ HEAP[(offset+length+1)|0] = block>>>16&255;
+ HEAP[(offset+length+2)|0] = block>>>8&255;
+ HEAP[(offset+length+3)|0] = block&255;
+
+ // finish first iteration
+ hmac_finish( offset, (length+4)|0, -1 )|0;
+ h0 = t0 = H0, h1 = t1 = H1, h2 = t2 = H2, h3 = t3 = H3, h4 = t4 = H4, h5 = t5 = H5, h6 = t6 = H6, h7 = t7 = H7;
+ count = (count-1)|0;
+
+ // perform the rest iterations
+ while ( (count|0) > 0 ) {
+ hmac_reset();
+ _core( t0, t1, t2, t3, t4, t5, t6, t7, 0x80000000, 0, 0, 0, 0, 0, 0, 768 );
+ t0 = H0, t1 = H1, t2 = H2, t3 = H3, t4 = H4, t5 = H5, t6 = H6, t7 = H7;
+
+ _hmac_opad();
+ _core( t0, t1, t2, t3, t4, t5, t6, t7, 0x80000000, 0, 0, 0, 0, 0, 0, 768 );
+ t0 = H0, t1 = H1, t2 = H2, t3 = H3, t4 = H4, t5 = H5, t6 = H6, t7 = H7;
+
+ h0 = h0 ^ H0;
+ h1 = h1 ^ H1;
+ h2 = h2 ^ H2;
+ h3 = h3 ^ H3;
+ h4 = h4 ^ H4;
+ h5 = h5 ^ H5;
+ h6 = h6 ^ H6;
+ h7 = h7 ^ H7;
+
+ count = (count-1)|0;
+ }
+
+ H0 = h0;
+ H1 = h1;
+ H2 = h2;
+ H3 = h3;
+ H4 = h4;
+ H5 = h5;
+ H6 = h6;
+ H7 = h7;
+
+ if ( ~output )
+ _state_to_heap(output);
+
+ return 0;
+ }
+
+ return {
+ // SHA256
+ reset: reset,
+ init: init,
+ process: process,
+ finish: finish,
+
+ // HMAC-SHA256
+ hmac_reset: hmac_reset,
+ hmac_init: hmac_init,
+ hmac_finish: hmac_finish,
+
+ // PBKDF2-HMAC-SHA256
+ pbkdf2_generate_block: pbkdf2_generate_block
+ }
+}
+
+},
+"transform/TextTransformer.js": function(module, exports, require){
+/*@flow*/
+/*
+ * Copyright 2014 XWiki SAS
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+"use strict";
+
+/*::
+import type { Operation_t } from '../Operation'
+*/
+var Operation = require('../Operation');
+var Common = require('../Common');
+
+var transformOp0 = function (
+ toTransform /*:Operation_t*/,
+ transformBy /*:Operation_t*/)
+{
+ if (toTransform.offset > transformBy.offset) {
+ if (toTransform.offset > transformBy.offset + transformBy.toRemove) {
+ // simple rebase
+ return Operation.create(
+ toTransform.offset - transformBy.toRemove + transformBy.toInsert.length,
+ toTransform.toRemove,
+ toTransform.toInsert
+ );
+ }
+ var newToRemove =
+ toTransform.toRemove - (transformBy.offset + transformBy.toRemove - toTransform.offset);
+ if (newToRemove < 0) { newToRemove = 0; }
+ if (newToRemove === 0 && toTransform.toInsert.length === 0) { return null; }
+ return Operation.create(
+ transformBy.offset + transformBy.toInsert.length,
+ newToRemove,
+ toTransform.toInsert
+ );
+ }
+ // they don't touch, yay
+ if (toTransform.offset + toTransform.toRemove < transformBy.offset) { return toTransform; }
+ // Truncate what will be deleted...
+ var _newToRemove = transformBy.offset - toTransform.offset;
+ if (_newToRemove === 0 && toTransform.toInsert.length === 0) { return null; }
+ return Operation.create(toTransform.offset, _newToRemove, toTransform.toInsert);
+};
+
+var transformOp = function (
+ toTransform /*:Operation_t*/,
+ transformBy /*:Operation_t*/)
+{
+ if (Common.PARANOIA) {
+ Operation.check(toTransform);
+ Operation.check(transformBy);
+ }
+ var result = transformOp0(toTransform, transformBy);
+ if (Common.PARANOIA && result) { Operation.check(result); }
+ return result;
+};
+
+module.exports = function (
+ opsToTransform /*:Array*/,
+ opsTransformBy /*:Array*/,
+ doc /*:string*/ ) /*:Array*/
+{
+ var resultOfTransformBy = doc;
+ var i;
+ for (i = opsTransformBy.length - 1; i >= 0; i--) {
+ resultOfTransformBy = Operation.apply(opsTransformBy[i], resultOfTransformBy);
+ }
+ var out = [];
+ for (i = opsToTransform.length - 1; i >= 0; i--) {
+ var tti = opsToTransform[i];
+ for (var j = opsTransformBy.length - 1; j >= 0; j--) {
+ try {
+ tti = transformOp(tti, opsTransformBy[j]);
+ } catch (e) {
+ console.error("The pluggable transform function threw an error, " +
+ "failing operational transformation");
+ console.error(e.stack);
+ return [];
+ }
+ if (!tti) {
+ break;
+ }
+ }
+ if (tti) {
+ if (Common.PARANOIA) { Operation.check(tti, resultOfTransformBy.length); }
+ out.unshift(tti);
+ }
+ }
+ return out;
+};
+
+},
+"transform/SmartJSONTransformer.js": function(module, exports, require){
+
+/*@flow*/
+/*
+ * Copyright 2014 XWiki SAS
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+"use strict";
+
+var Sortify = require('json.sortify');
+var Diff = require('../Diff');
+//var Patch = require('../Patch');
+var Operation = require('../Operation');
+var TextTransformer = require('./TextTransformer');
+//var Sha = require('../sha256');
+
+/*::
+import type { Operation_t } from '../Operation';
+*/
+
+var isArray = function (obj) {
+ return Object.prototype.toString.call(obj)==='[object Array]';
+};
+
+/* Arrays and nulls both register as 'object' when using native typeof
+ we need to distinguish them as their own types, so use this instead. */
+var type = function (dat) {
+ return dat === null? 'null': isArray(dat)?'array': typeof(dat);
+};
+
+var find = function (map, path) {
+ var l = path.length;
+ for (var i = 0; i < l; i++) {
+ if (typeof(map[path[i]]) === 'undefined') { return; }
+ map = map[path[i]];
+ }
+ return map;
+};
+
+var clone = function (val) {
+ return JSON.parse(JSON.stringify(val));
+};
+
+var deepEqual = function (A /*:any*/, B /*:any*/) {
+ var t_A = type(A);
+ var t_B = type(B);
+ if (t_A !== t_B) { return false; }
+ if (t_A === 'object') {
+ var k_A = Object.keys(A);
+ var k_B = Object.keys(B);
+ return k_A.length === k_B.length &&
+ !k_A.some(function (a) { return !deepEqual(A[a], B[a]); }) &&
+ !k_B.some(function (b) { return !(b in A); });
+ } else if (t_A === 'array') {
+ return A.length === B.length &&
+ !A.some(function (a, i) { return !deepEqual(a, B[i]); });
+ } else {
+ return A === B;
+ }
+};
+
+/*::
+export type SmartJSONTransformer_Replace_t = {
+ type: 'replace',
+ path: Array,
+ value: any,
+ prev: any
+};
+export type SmartJSONTransformer_Splice_t = {
+ type: 'splice',
+ path: Array,
+ value: any,
+ offset: number,
+ removals: number
+};
+export type SmartJSONTransformer_Remove_t = {
+ type: 'remove',
+ path: Array,
+ value: any
+};
+export type SmartJSONTransformer_Operation_t =
+ SmartJSONTransformer_Replace_t | SmartJSONTransformer_Splice_t | SmartJSONTransformer_Remove_t;
+*/
+
+var operation = function (type, path, value, prev, other) /*:SmartJSONTransformer_Operation_t*/ {
+ if (type === 'replace') {
+ return ({
+ type: 'replace',
+ path: path,
+ value: value,
+ prev: prev,
+ } /*:SmartJSONTransformer_Replace_t*/);
+ } else if (type === 'splice') {
+ if (typeof(prev) !== 'number') { throw new Error(); }
+ if (typeof(other) !== 'number') { throw new Error(); }
+ return ({
+ type: 'splice',
+ path: path,
+ value: value,
+ offset: prev,
+ removals: other
+ } /*:SmartJSONTransformer_Splice_t*/);
+ } else if (type !== 'remove') { throw new Error('expected a removal'); }
+ // if it's not a replace or splice, it's a 'remove'
+ return ({
+ type: 'remove',
+ path: path,
+ value: value,
+ } /*:SmartJSONTransformer_Remove_t*/);
+};
+
+var replace = function (ops, path, to, from) {
+ ops.push(operation('replace', path, to, from));
+};
+
+var remove = function (ops, path, val) {
+ ops.push(operation('remove', path, val));
+};
+
+
+// HERE
+var splice = function (ops, path, value, offset, removals) {
+ ops.push(operation('splice', path, value, offset, removals));
+};
+
+/*
+ all of A's path is at the beginning of B
+ roughly: B.indexOf(A) === 0
+*/
+var pathOverlaps = function (A /*:Array*/, B /*:Array*/) {
+ return !A.some(function (a, i) {
+ return a !== B[i];
+ });
+};
+
+// OT Case #1 replace->replace ✔
+// OT Case #2 replace->remove ✔
+// OT Case #3 replace->splice ✔
+// OT Case #4 remove->replace ✔
+// OT Case #5 remove->remove ✔
+// OT Case #6 remove->splice ✔
+// OT Case #7 splice->replace ✔
+// OT Case #8 splice->remove ✔
+// OT Case #9 splice->splice ✔
+var CASES = (function () {
+ var types = ['replace', 'remove', 'splice'];
+
+ var matrix = {};
+ var i = 1;
+
+ types.forEach(function (a) {
+ matrix[a] = {};
+ return types.forEach(function (b) { matrix[a][b] = i++; });
+ });
+ return matrix;
+}());
+
+// A and B are lists of operations which result from calling diff
+
+var resolve = function (A /*:any*/, B /*:any*/, arbiter /*:?function*/) {
+ if (!(type(A) === 'array' && type(B) === 'array')) {
+ throw new Error("[resolve] expected two arrays");
+ }
+
+ /* OVERVIEW
+ * B
+ * 1. filter removals at identical paths
+ *
+ */
+
+ B = B.filter(function (b) {
+ // if A removed part of the tree you were working on...
+ if (A.some(function (a) {
+ if (a.type === 'remove') {
+ if (pathOverlaps(a.path, b.path)) {
+ if (b.path.length - a.path.length > 1) { return true; }
+ }
+ }
+ })) {
+ // this is weird... FIXME
+ return false;
+ }
+
+ /* remove operations which would no longer make sense
+ for instance, if a replaces an array with a string,
+ that would invalidate a splice operation at that path */
+ if (b.type === 'splice' && A.some(function (a) {
+ if (a.type === 'splice' && pathOverlaps(a.path, b.path)) {
+ if (a.path.length - b.path.length < 0) {
+ if (!a.removals) { return; }
+
+ var start = a.offset;
+ var end = a.offset + a.removals;
+
+ for (;start < end; start++) {
+ if (start === b.path[a.path.length]) {
+ /*
+ if (typeof(arbiter) === 'function' &&
+ deepEqual(a.path, b.path) &&
+ a.value.length === 1 &&
+ b.value.length === 1 &&
+ typeof(a.value[0]) === 'string' &&
+ typeof(b.value[0]) === 'string') {
+ console.log('strings');
+
+ return arbiter(a, b, CASES.splice.splice);
+ }
+ */
+
+ // b is a descendant of a removal
+ return true;
+ }
+ }
+ }
+ }
+ })) { return false; }
+
+ if (!A.some(function (a) {
+ return b.type === 'remove' && deepEqual(a.path, b.path);
+ })) { return true; }
+ })
+ .filter(function (b) {
+ // let A win conflicts over b if no arbiter is supplied here
+
+ // Arbiter is required here
+ return !A.some(function (a) {
+ if (b.type === 'replace' && a.type === 'replace') {
+ // remove any operations which return true
+ if (deepEqual(a.path, b.path)) {
+ if (typeof(a.value) === 'string' && typeof(b.value) === 'string') {
+ if (arbiter && a.prev === b.prev && a.value !== b.value) {
+ return arbiter(a, b, CASES.replace.replace);
+ }
+ return true;
+ }
+ return true;
+ }
+ }
+ });
+ })
+ .map(function (b) {
+ // if a splice in A modifies the path to b
+ // update b's path to reflect that
+
+ A.forEach(function (a) {
+ if (a.type === 'splice') {
+ // TODO
+ // what if a.path == b.path
+ // what if a removes elements (splice) and b also removes elements
+ // (generally we merge these two together but it is probably best to allow the api customer to decide via a "strategy")
+ // Note that A might be removing *and* inserting because a splice is roughly equivilent to a ChainPad Operation
+ // Consult Transform0 :)
+
+ // resolve insertion overlaps array.push conflicts
+ // iterate over A such that each overlapping splice
+ // adjusts the path/offset of b
+
+ if (deepEqual(a.path, b.path)) {
+ if (b.type === 'splice') {
+ // what if the splice is a removal?
+ b.offset += (a.value.length - a.removals);
+ // if both A and B are removing the same thing
+ // be careful
+ } else {
+ // adjust the path of b to account for the splice
+ // TODO
+ }
+ return;
+ }
+
+ if (pathOverlaps(a.path, b.path)) {
+ // TODO validate that this isn't an off-by-one error
+ var pos = a.path.length;
+ if (typeof(b.path[pos]) === 'number' && a.offset <= b.path[pos]) { // FIXME a.value is undefined
+ b.path[pos] += (a.value.length - a.removals);
+ }
+ }
+ }
+ });
+
+ return b;
+ });
+
+ return B;
+};
+
+// A, B, f, path, ops
+var objects = function (A, B, path, ops) {
+ var Akeys = Object.keys(A);
+ var Bkeys = Object.keys(B);
+
+ Bkeys.forEach(function (b) {
+ var t_b = type(B[b]);
+ var old = A[b];
+
+ var nextPath = path.concat(b);
+
+ if (Akeys.indexOf(b) === -1) {
+ // there was an insertion
+
+ // mind the fallthrough behaviour
+ if (t_b === 'undefined') {
+ throw new Error("undefined type has key. this shouldn't happen?");
+ }
+ if (old) { throw new Error("no such key existed in b, so 'old' should be falsey"); }
+ replace(ops, nextPath, B[b], old);
+ return;
+ }
+
+ // else the key already existed
+ var t_a = type(old);
+ if (t_a !== t_b) {
+ // its type changed!
+ console.log("type changed from [%s] to [%s]", t_a, t_b);
+ // type changes always mean a change happened
+ if (t_b === 'undefined') {
+ throw new Error("first pass should never reveal undefined keys");
+ }
+ replace(ops, nextPath, B[b], old);
+ return;
+ }
+
+ if (t_a === 'object') {
+ // it's an object
+ objects(A[b], B[b], nextPath, ops);
+ } else if (t_a === 'array') {
+ // it's an array
+ arrays(A[b], B[b], nextPath, ops);
+ } else if (A[b] !== B[b]) {
+ // it's not an array or object, so we can do === comparison
+ replace(ops, nextPath, B[b], old);
+ }
+ });
+ Akeys.forEach(function (a) {
+ // the key was deleted
+ if (Bkeys.indexOf(a) === -1 || type(B[a]) === 'undefined') {
+ remove(ops, path.concat(a), A[a]);
+ }
+ });
+};
+
+var arrayShallowEquality = function (A, B) {
+ if (A.length !== B.length) { return false; }
+ for (var i = 0; i < A.length; i++) {
+ if (type(A[i]) !== type(B[i])) { return false; }
+ }
+ return true;
+};
+
+// When an element in an array (number, string, bool) is changed, instead of a replace we
+// will do a splice(offset, [element], 1)
+var arrays = function (A_orig, B, path, ops) {
+ var A = A_orig.slice(0); // shallow clone
+
+ if (A.length === 0) {
+ // A is zero length, this is going to be easy...
+ splice(ops, path, B, 0, 0);
+
+ } else if (arrayShallowEquality(A, B)) {
+ // This is a relatively simple case, the elements in A and B are all of the same type and if
+ // that type happens to be a primitive type, they are also equal.
+ // This means no change will be needed at the level of this array, only it's children.
+ A.forEach(function (a, i) {
+ var b = B[i];
+ if (b === a) { return; }
+ var old = a;
+ var nextPath = path.concat(i);
+
+ var t_a = type(a);
+ switch (t_a) {
+ case 'undefined':
+ throw new Error('existing key had type `undefined`. this should never happen');
+ case 'object':
+ objects(a, b, nextPath, ops);
+ break;
+ case 'array':
+ arrays(a, b, nextPath, ops);
+ break;
+ default:
+ //console.log('replace: ' + t_a);
+ //splice(ops, path, [b], i, 1);
+ replace(ops, nextPath, b, old);
+ }
+ });
+ } else {
+ // Something was changed in the length of the array or one of the primitives so we're going
+ // to make an actual change to this array, not only it's children.
+ var commonStart = 0;
+ var commonEnd = 0;
+ while (commonStart < A.length && deepEqual(A[commonStart], B[commonStart])) { commonStart++; }
+ while (deepEqual(A[A.length - 1 - commonEnd], B[B.length - 1 - commonEnd]) &&
+ commonEnd + commonStart < A.length && commonEnd + commonStart < B.length)
+ {
+ commonEnd++;
+ }
+ var toRemove = A.length - commonStart - commonEnd;
+ var toInsert = [];
+ if (B.length !== commonStart + commonEnd) {
+ toInsert = B.slice(commonStart, B.length - commonEnd);
+ }
+ splice(ops, path, toInsert, commonStart, toRemove);
+ }
+};
+
+var diff = function (A, B) {
+ var ops = [];
+
+ var t_A = type(A);
+ var t_B = type(B);
+
+ if (t_A !== t_B) {
+ throw new Error("Can't merge two objects of differing types");
+ }
+
+ if (t_B === 'array') {
+ arrays(A, B, [], ops);
+ } else if (t_B === 'object') {
+ objects(A, B, [], ops);
+ } else {
+ throw new Error("unsupported datatype" + t_B);
+ }
+ return ops;
+};
+
+var applyOp = function (O, op /*:SmartJSONTransformer_Operation_t*/) {
+ var path;
+ var key;
+ var result;
+ switch (op.type) {
+ case "replace":
+ key = op.path[op.path.length -1];
+ path = op.path.slice(0, op.path.length - 1);
+
+ var parent = find(O, path);
+
+ if (!parent) {
+ throw new Error("cannot apply change to non-existent element");
+ }
+ parent[key] = op.value;
+ break;
+ case "splice":
+ var found = find(O, op.path);
+ if (!found) {
+ console.error("[applyOp] expected path [%s] to exist in object", op.path.join(','));
+ throw new Error("Path did not exist");
+ }
+
+ if (type(found) !== 'array') {
+ throw new Error("Can't splice non-array");
+ }
+
+ Array.prototype.splice.apply(found, [op.offset, op.removals].concat(op.value));
+ break;
+ case "remove":
+ key = op.path[op.path.length -1];
+ path = op.path.slice(0, op.path.length - 1);
+ result = find(O, path);
+ if (typeof(result) !== 'undefined') { delete result[key]; }
+ break;
+ default:
+ throw new Error('unsupported operation type');
+ }
+};
+
+var patch = function (O, ops) {
+ ops.forEach(function (op) {
+ applyOp(O, op);
+ });
+ return O;
+};
+
+
+/////
+
+// We mutate b in this function
+// Our operation is p_b and the other person's operation is p_a.
+// If we return true here, it means our operation will die off.
+var arbiter = function (p_a, p_b, c) {
+ if (p_a.prev !== p_b.prev) { throw new Error("Parent values don't match!"); }
+
+ if (c === CASES.splice.splice) {
+ // We and the other person are both pushing strings to an array so
+ // we'll just accept both of them into the array.
+ console.log(p_a);
+ console.log(p_b);
+ console.log('\n\n\n\n\n\n\n\n\n');
+ // TODO: do we really want to kill off our operation in this case ?
+ return true;
+ }
+ var o = p_a.prev;
+
+ var ops_a = Diff.diff(o, p_a.value);
+ var ops_b = Diff.diff(o, p_b.value);
+
+ /* given the parent text, the op to transform, and the incoming op
+ return a transformed operation which takes the incoming
+ op into account */
+ var ops_x = TextTransformer(ops_b, ops_a, o);
+
+ /* Apply the incoming operation to the parent text
+ */
+ var x2 = Operation.applyMulti(ops_a, o);
+
+ /* Apply the transformed operation to the result of the incoming op
+ */
+ var x3 = Operation.applyMulti(ops_x, x2);
+
+ p_b.value = x3;
+};
+
+module.exports = function (
+ opsToTransform /*:Array*/,
+ opsTransformBy /*:Array*/,
+ s_orig /*:string*/ ) /*:Array*/
+{
+ var o_orig = JSON.parse(s_orig);
+ var s_transformBy = Operation.applyMulti(opsTransformBy, s_orig);
+ var o_transformBy = JSON.parse(s_transformBy);
+ // try whole patch at a time, see how it goes...
+ var s_toTransform = Operation.applyMulti(opsToTransform, s_orig);
+ var o_toTransform = JSON.parse(s_toTransform);
+
+ try {
+ var diffTTF = diff(o_orig, o_toTransform);
+ var diffTFB = diff(o_orig, o_transformBy);
+ var newDiffTTF = resolve(diffTFB, diffTTF, arbiter);
+
+ // mutates orig
+ patch(o_orig, diffTFB);
+ patch(o_orig, newDiffTTF);
+
+ var result = Sortify(o_orig);
+ var ret = Diff.diff(s_transformBy, result);
+ return ret;
+
+ } catch (err) {
+ console.error(err); // FIXME Path did not exist...
+ }
+ return [];
+};
+
+
+module.exports._ = {
+ clone: clone,
+ pathOverlaps: pathOverlaps,
+ deepEqual: deepEqual,
+ diff: diff,
+ resolve: resolve,
+ patch: patch,
+
+};
+
+},
+"transform/NaiveJSONTransformer.js": function(module, exports, require){
+/*@flow*/
+/*
+ * Copyright 2014 XWiki SAS
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+"use strict";
+
+var TextTransformer = require('./TextTransformer');
+//var ChainPad = require('../ChainPad');
+var Operation = require('../Operation');
+var Common = require('../Common');
+
+/*::
+import type { Operation_t } from '../Operation';
+*/
+
+module.exports = function (
+ opsToTransform /*:Array*/,
+ opsTransformBy /*:Array*/,
+ text /*:string*/ ) /*:Array*/
+{
+ var DEBUG = Common.global.REALTIME_DEBUG = Common.global.REALTIME_DEBUG || {};
+
+ var resultOps, text2, text3;
+ try {
+ // text = O (mutual common ancestor)
+ // toTransform = A (your own operation)
+ // transformBy = B (the incoming operation)
+ // threeway merge (0, A, B)
+
+ resultOps = TextTransformer(opsToTransform, opsTransformBy, text);
+
+ text2 = Operation.applyMulti(opsTransformBy, text);
+
+ text3 = Operation.applyMulti(resultOps, text2);
+ try {
+ JSON.parse(text3);
+ return resultOps;
+ } catch (e) {
+ console.error(e);
+ DEBUG.ot_parseError = {
+ type: 'resultParseError',
+ resultOps: resultOps,
+
+ toTransform: opsToTransform,
+ transformBy: opsTransformBy,
+
+ text1: text,
+ text2: text2,
+ text3: text3,
+ error: e
+ };
+ console.log('Debugging info available at `window.REALTIME_DEBUG.ot_parseError`');
+ }
+ } catch (x) {
+ console.error(x);
+ DEBUG.ot_applyError = {
+ type: 'resultParseError',
+ resultOps: resultOps,
+
+ toTransform: opsToTransform,
+ transformBy: opsTransformBy,
+
+ text1: text,
+ text2: text2,
+ text3: text3,
+ error: x
+ };
+ console.log('Debugging info available at `window.REALTIME_DEBUG.ot_applyError`');
+ }
+
+ // return an empty patch in case we can't do anything else
+ return [];
+};
+
+}
+};
+r.m[1] = {
+"dist/JSON.sortify.js": function(module, exports, require){
+"use strict";(function(factory){if(typeof module!=="undefined"&&module.exports)module.exports=factory();else if(typeof define=="function"&&typeof define.amd=="object")define("json.sortify",factory);else JSON.sortify=factory()})(function(){ /*!
+* Copyright 2015-2017 Thomas Rosenau
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/"use strict";var sortKeys=function sortKeys(o){if(Array.isArray(o)){return o.map(sortKeys)}else if(o instanceof Object){var _ret=function(){var numeric=[];var nonNumeric=[];Object.keys(o).forEach(function(key){if(/^(0|[1-9][0-9]*)$/.test(key)){numeric.push(+key)}else {nonNumeric.push(key)}});return {v:numeric.sort(function(a,b){return a-b}).concat(nonNumeric.sort()).reduce(function(result,key){result[key]=sortKeys(o[key]);return result},{})}}();if(typeof _ret==="object")return _ret.v}return o};var jsonStringify=JSON.stringify.bind(JSON);var sortify=function sortify(value,replacer,space){var nativeJson=jsonStringify(value,replacer,0);if(!nativeJson||nativeJson[0]!=="{"&&nativeJson[0]!=="["){return nativeJson}var cleanObj=JSON.parse(nativeJson);return jsonStringify(sortKeys(cleanObj),null,space)};return sortify});
+}
+};
+function umd(n,f){"object"==typeof exports&&(module.exports=n),"function"==typeof define&&define.amd&&define(function(){return n});var e;"undefined"!=typeof window?e=window:"undefined"!=typeof global?e=global:"undefined"!=typeof self&&(e=self),e[f]=n}umd(r("ChainPad.js"), "ChainPad");}());