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");}());