diff options
author | Peter 'Pita' Martischka <petermartischka - googlemail - com> | 2012-02-14 09:21:45 -0800 |
---|---|---|
committer | Peter 'Pita' Martischka <petermartischka - googlemail - com> | 2012-02-14 09:21:45 -0800 |
commit | 5751a8e43450e96eac03a5007494fa0aca508076 (patch) | |
tree | 32220823b91d45c972d72fb5166623f642ddb35e /static | |
parent | da1fbaad9aec6d766142119ef97746638dfd7ad6 (diff) | |
parent | e0d23e3c5d9feb1fc4ff88fa923bb6d1fa1aae18 (diff) | |
download | etherpad-lite-5751a8e43450e96eac03a5007494fa0aca508076.zip |
Merge pull request #352 from cweider/modulize-share
Module Sharing
Diffstat (limited to 'static')
-rw-r--r-- | static/js/AttributePoolFactory.js | 90 | ||||
-rw-r--r-- | static/js/Changeset.js (renamed from static/js/easysync2.js) | 1693 | ||||
-rw-r--r-- | static/js/ace2_common.js | 10 | ||||
-rw-r--r-- | static/js/ace2_inner.js | 4 | ||||
-rw-r--r-- | static/js/broadcast.js | 10 | ||||
-rw-r--r-- | static/js/changesettracker.js | 4 | ||||
-rw-r--r-- | static/js/contentcollector.js | 2 | ||||
-rw-r--r-- | static/js/cssmanager_client.js | 118 | ||||
-rw-r--r-- | static/js/domline.js | 32 | ||||
-rw-r--r-- | static/js/domline_client.js | 309 | ||||
-rw-r--r-- | static/js/easysync2_client.js | 2274 | ||||
-rw-r--r-- | static/js/linestylefilter.js | 2 | ||||
-rw-r--r-- | static/js/linestylefilter_client.js | 343 | ||||
-rw-r--r-- | static/js/pad.js | 46 | ||||
-rw-r--r-- | static/js/pad_utils.js | 64 | ||||
-rw-r--r-- | static/js/security.js | 54 | ||||
-rw-r--r-- | static/js/timeslider.js | 38 | ||||
-rw-r--r-- | static/js/undomodule.js | 2 |
18 files changed, 800 insertions, 4295 deletions
diff --git a/static/js/AttributePoolFactory.js b/static/js/AttributePoolFactory.js new file mode 100644 index 00000000..00b58dbb --- /dev/null +++ b/static/js/AttributePoolFactory.js @@ -0,0 +1,90 @@ +/** + * This code represents the Attribute Pool Object of the original Etherpad. + * 90% of the code is still like in the original Etherpad + * Look at https://github.com/ether/pad/blob/master/infrastructure/ace/www/easysync2.js + * You can find a explanation what a attribute pool is here: + * https://github.com/Pita/etherpad-lite/blob/master/doc/easysync/easysync-notes.txt + */ + +/* + * Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd) + * + * 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. + */ + +exports.createAttributePool = function () { + var p = {}; + p.numToAttrib = {}; // e.g. {0: ['foo','bar']} + p.attribToNum = {}; // e.g. {'foo,bar': 0} + p.nextNum = 0; + + p.putAttrib = function (attrib, dontAddIfAbsent) { + var str = String(attrib); + if (str in p.attribToNum) { + return p.attribToNum[str]; + } + if (dontAddIfAbsent) { + return -1; + } + var num = p.nextNum++; + p.attribToNum[str] = num; + p.numToAttrib[num] = [String(attrib[0] || ''), String(attrib[1] || '')]; + return num; + }; + + p.getAttrib = function (num) { + var pair = p.numToAttrib[num]; + if (!pair) { + return pair; + } + return [pair[0], pair[1]]; // return a mutable copy + }; + + p.getAttribKey = function (num) { + var pair = p.numToAttrib[num]; + if (!pair) return ''; + return pair[0]; + }; + + p.getAttribValue = function (num) { + var pair = p.numToAttrib[num]; + if (!pair) return ''; + return pair[1]; + }; + + p.eachAttrib = function (func) { + for (var n in p.numToAttrib) { + var pair = p.numToAttrib[n]; + func(pair[0], pair[1]); + } + }; + + p.toJsonable = function () { + return { + numToAttrib: p.numToAttrib, + nextNum: p.nextNum + }; + }; + + p.fromJsonable = function (obj) { + p.numToAttrib = obj.numToAttrib; + p.nextNum = obj.nextNum; + p.attribToNum = {}; + for (var n in p.numToAttrib) { + p.attribToNum[String(p.numToAttrib[n])] = Number(n); + } + return p; + }; + + return p; +} diff --git a/static/js/easysync2.js b/static/js/Changeset.js index cef868a1..715836d5 100644 --- a/static/js/easysync2.js +++ b/static/js/Changeset.js @@ -1,13 +1,16 @@ +/* + * This is the Changeset library copied from the old Etherpad with some modifications to use it in node.js + * Can be found in https://github.com/ether/pad/blob/master/infrastructure/ace/www/easysync2.js + */ + /** - * This code is mostly from the old Etherpad. Please help us to comment this code. + * This code is mostly from the old Etherpad. Please help us to comment this code. * This helps other people to understand this code better and helps them to improve it. * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED */ -// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.easysync2 -// %APPJET%: jimport("com.etherpad.Easysync2Support"); -/** - * Copyright 2009 Google Inc. +/* + * Copyright 2009 Google Inc., 2011 Peter 'Pita' Martischka (Primary Technology Ltd) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,203 +25,101 @@ * limitations under the License. */ -//var _opt = (this.Easysync2Support || null); -var _opt = null; // disable optimization for now - -function AttribPool() -{ - var p = {}; - p.numToAttrib = {}; // e.g. {0: ['foo','bar']} - p.attribToNum = {}; // e.g. {'foo,bar': 0} - p.nextNum = 0; - - p.putAttrib = function(attrib, dontAddIfAbsent) - { - var str = String(attrib); - if (str in p.attribToNum) - { - return p.attribToNum[str]; - } - if (dontAddIfAbsent) - { - return -1; - } - var num = p.nextNum++; - p.attribToNum[str] = num; - p.numToAttrib[num] = [String(attrib[0] || ''), String(attrib[1] || '')]; - return num; - }; - - p.getAttrib = function(num) - { - var pair = p.numToAttrib[num]; - if (!pair) return pair; - return [pair[0], pair[1]]; // return a mutable copy - }; +var AttributePoolFactory = require("/AttributePoolFactory"); - p.getAttribKey = function(num) - { - var pair = p.numToAttrib[num]; - if (!pair) return ''; - return pair[0]; - }; - - p.getAttribValue = function(num) - { - var pair = p.numToAttrib[num]; - if (!pair) return ''; - return pair[1]; - }; - - p.eachAttrib = function(func) - { - for (var n in p.numToAttrib) - { - var pair = p.numToAttrib[n]; - func(pair[0], pair[1]); - } - }; - - p.toJsonable = function() - { - return { - numToAttrib: p.numToAttrib, - nextNum: p.nextNum - }; - }; - - p.fromJsonable = function(obj) - { - p.numToAttrib = obj.numToAttrib; - p.nextNum = obj.nextNum; - p.attribToNum = {}; - for (var n in p.numToAttrib) - { - p.attribToNum[String(p.numToAttrib[n])] = Number(n); - } - return p; - }; - - return p; -} +var _opt = null; -var Changeset = {}; - -Changeset.error = function error(msg) -{ +//var exports = {}; +exports.error = function error(msg) { var e = new Error(msg); e.easysync = true; throw e; }; -Changeset.assert = function assert(b, msgParts) -{ - if (!b) - { +exports.assert = function assert(b, msgParts) { + if (!b) { var msg = Array.prototype.slice.call(arguments, 1).join(''); - Changeset.error("Changeset: " + msg); + exports.error("exports: " + msg); } }; -Changeset.parseNum = function(str) -{ +exports.parseNum = function (str) { return parseInt(str, 36); }; -Changeset.numToString = function(num) -{ +exports.numToString = function (num) { return num.toString(36).toLowerCase(); }; -Changeset.toBaseTen = function(cs) -{ +exports.toBaseTen = function (cs) { var dollarIndex = cs.indexOf('$'); var beforeDollar = cs.substring(0, dollarIndex); var fromDollar = cs.substring(dollarIndex); - return beforeDollar.replace(/[0-9a-z]+/g, function(s) - { - return String(Changeset.parseNum(s)); + return beforeDollar.replace(/[0-9a-z]+/g, function (s) { + return String(exports.parseNum(s)); }) + fromDollar; }; -Changeset.oldLen = function(cs) -{ - return Changeset.unpack(cs).oldLen; +exports.oldLen = function (cs) { + return exports.unpack(cs).oldLen; }; -Changeset.newLen = function(cs) -{ - return Changeset.unpack(cs).newLen; +exports.newLen = function (cs) { + return exports.unpack(cs).newLen; }; -Changeset.opIterator = function(opsStr, optStartIndex) -{ +exports.opIterator = function (opsStr, optStartIndex) { //print(opsStr); var regex = /((?:\*[0-9a-z]+)*)(?:\|([0-9a-z]+))?([-+=])([0-9a-z]+)|\?|/g; var startIndex = (optStartIndex || 0); var curIndex = startIndex; var prevIndex = curIndex; - function nextRegexMatch() - { + function nextRegexMatch() { prevIndex = curIndex; var result; - if (_opt) - { + if (_opt) { result = _opt.nextOpInString(opsStr, curIndex); - if (result) - { - if (result.opcode() == '?') - { - Changeset.error("Hit error opcode in op stream"); + if (result) { + if (result.opcode() == '?') { + exports.error("Hit error opcode in op stream"); } curIndex = result.lastIndex(); } - } - else - { + } else { regex.lastIndex = curIndex; result = regex.exec(opsStr); curIndex = regex.lastIndex; - if (result[0] == '?') - { - Changeset.error("Hit error opcode in op stream"); + if (result[0] == '?') { + exports.error("Hit error opcode in op stream"); } } return result; } var regexResult = nextRegexMatch(); - var obj = Changeset.newOp(); + var obj = exports.newOp(); - function next(optObj) - { + function next(optObj) { var op = (optObj || obj); - if (_opt && regexResult) - { + if (_opt && regexResult) { op.attribs = regexResult.attribs(); op.lines = regexResult.lines(); op.chars = regexResult.chars(); op.opcode = regexResult.opcode(); regexResult = nextRegexMatch(); - } - else if ((!_opt) && regexResult[0]) - { + } else if ((!_opt) && regexResult[0]) { op.attribs = regexResult[1]; - op.lines = Changeset.parseNum(regexResult[2] || 0); + op.lines = exports.parseNum(regexResult[2] || 0); op.opcode = regexResult[3]; - op.chars = Changeset.parseNum(regexResult[4]); + op.chars = exports.parseNum(regexResult[4]); regexResult = nextRegexMatch(); - } - else - { - Changeset.clearOp(op); + } else { + exports.clearOp(op); } return op; } - function hasNext() - { + function hasNext() { return !!(_opt ? regexResult : regexResult[0]); } - function lastIndex() - { + function lastIndex() { return prevIndex; } return { @@ -228,15 +129,13 @@ Changeset.opIterator = function(opsStr, optStartIndex) }; }; -Changeset.clearOp = function(op) -{ +exports.clearOp = function (op) { op.opcode = ''; op.chars = 0; op.lines = 0; op.attribs = ''; }; -Changeset.newOp = function(optOpcode) -{ +exports.newOp = function (optOpcode) { return { opcode: (optOpcode || ''), chars: 0, @@ -244,8 +143,7 @@ Changeset.newOp = function(optOpcode) attribs: '' }; }; -Changeset.cloneOp = function(op) -{ +exports.cloneOp = function (op) { return { opcode: op.opcode, chars: op.chars, @@ -253,60 +151,54 @@ Changeset.cloneOp = function(op) attribs: op.attribs }; }; -Changeset.copyOp = function(op1, op2) -{ +exports.copyOp = function (op1, op2) { op2.opcode = op1.opcode; op2.chars = op1.chars; op2.lines = op1.lines; op2.attribs = op1.attribs; }; -Changeset.opString = function(op) -{ +exports.opString = function (op) { // just for debugging if (!op.opcode) return 'null'; - var assem = Changeset.opAssembler(); + var assem = exports.opAssembler(); assem.append(op); return assem.toString(); }; -Changeset.stringOp = function(str) -{ +exports.stringOp = function (str) { // just for debugging - return Changeset.opIterator(str).next(); + return exports.opIterator(str).next(); }; -Changeset.checkRep = function(cs) -{ +exports.checkRep = function (cs) { // doesn't check things that require access to attrib pool (e.g. attribute order) // or original string (e.g. newline positions) - var unpacked = Changeset.unpack(cs); + var unpacked = exports.unpack(cs); var oldLen = unpacked.oldLen; var newLen = unpacked.newLen; var ops = unpacked.ops; var charBank = unpacked.charBank; - var assem = Changeset.smartOpAssembler(); + var assem = exports.smartOpAssembler(); var oldPos = 0; var calcNewLen = 0; var numInserted = 0; - var iter = Changeset.opIterator(ops); - while (iter.hasNext()) - { + var iter = exports.opIterator(ops); + while (iter.hasNext()) { var o = iter.next(); - switch (o.opcode) - { + switch (o.opcode) { case '=': oldPos += o.chars; calcNewLen += o.chars; break; case '-': oldPos += o.chars; - Changeset.assert(oldPos < oldLen, oldPos, " >= ", oldLen, " in ", cs); + exports.assert(oldPos < oldLen, oldPos, " >= ", oldLen, " in ", cs); break; case '+': { calcNewLen += o.chars; numInserted += o.chars; - Changeset.assert(calcNewLen < newLen, calcNewLen, " >= ", newLen, " in ", cs); + exports.assert(calcNewLen < newLen, calcNewLen, " >= ", newLen, " in ", cs); break; } } @@ -315,75 +207,62 @@ Changeset.checkRep = function(cs) calcNewLen += oldLen - oldPos; charBank = charBank.substring(0, numInserted); - while (charBank.length < numInserted) - { + while (charBank.length < numInserted) { charBank += "?"; } assem.endDocument(); - var normalized = Changeset.pack(oldLen, calcNewLen, assem.toString(), charBank); - Changeset.assert(normalized == cs, normalized, ' != ', cs); + var normalized = exports.pack(oldLen, calcNewLen, assem.toString(), charBank); + exports.assert(normalized == cs, normalized, ' != ', cs); return cs; } -Changeset.smartOpAssembler = function() -{ - // Like opAssembler but able to produce conforming changesets +exports.smartOpAssembler = function () { + // Like opAssembler but able to produce conforming exportss // from slightly looser input, at the cost of speed. // Specifically: // - merges consecutive operations that can be merged // - strips final "=" // - ignores 0-length changes // - reorders consecutive + and - (which margingOpAssembler doesn't do) - var minusAssem = Changeset.mergingOpAssembler(); - var plusAssem = Changeset.mergingOpAssembler(); - var keepAssem = Changeset.mergingOpAssembler(); - var assem = Changeset.stringAssembler(); + var minusAssem = exports.mergingOpAssembler(); + var plusAssem = exports.mergingOpAssembler(); + var keepAssem = exports.mergingOpAssembler(); + var assem = exports.stringAssembler(); var lastOpcode = ''; var lengthChange = 0; - function flushKeeps() - { + function flushKeeps() { assem.append(keepAssem.toString()); keepAssem.clear(); } - function flushPlusMinus() - { + function flushPlusMinus() { assem.append(minusAssem.toString()); minusAssem.clear(); assem.append(plusAssem.toString()); plusAssem.clear(); } - function append(op) - { + function append(op) { if (!op.opcode) return; if (!op.chars) return; - if (op.opcode == '-') - { - if (lastOpcode == '=') - { + if (op.opcode == '-') { + if (lastOpcode == '=') { flushKeeps(); } minusAssem.append(op); lengthChange -= op.chars; - } - else if (op.opcode == '+') - { - if (lastOpcode == '=') - { + } else if (op.opcode == '+') { + if (lastOpcode == '=') { flushKeeps(); } plusAssem.append(op); lengthChange += op.chars; - } - else if (op.opcode == '=') - { - if (lastOpcode != '=') - { + } else if (op.opcode == '=') { + if (lastOpcode != '=') { flushPlusMinus(); } keepAssem.append(op); @@ -391,19 +270,15 @@ Changeset.smartOpAssembler = function() lastOpcode = op.opcode; } - function appendOpWithText(opcode, text, attribs, pool) - { - var op = Changeset.newOp(opcode); - op.attribs = Changeset.makeAttribsString(opcode, attribs, pool); + function appendOpWithText(opcode, text, attribs, pool) { + var op = exports.newOp(opcode); + op.attribs = exports.makeAttribsString(opcode, attribs, pool); var lastNewlinePos = text.lastIndexOf('\n'); - if (lastNewlinePos < 0) - { + if (lastNewlinePos < 0) { op.chars = text.length; op.lines = 0; append(op); - } - else - { + } else { op.chars = lastNewlinePos + 1; op.lines = text.match(/\n/g).length; append(op); @@ -413,15 +288,13 @@ Changeset.smartOpAssembler = function() } } - function toString() - { + function toString() { flushPlusMinus(); flushKeeps(); return assem.toString(); } - function clear() - { + function clear() { minusAssem.clear(); plusAssem.clear(); keepAssem.clear(); @@ -429,13 +302,11 @@ Changeset.smartOpAssembler = function() lengthChange = 0; } - function endDocument() - { + function endDocument() { keepAssem.endDocument(); } - function getLengthChange() - { + function getLengthChange() { return lengthChange; } @@ -449,29 +320,23 @@ Changeset.smartOpAssembler = function() }; }; -if (_opt) -{ - Changeset.mergingOpAssembler = function() - { +if (_opt) { + exports.mergingOpAssembler = function () { var assem = _opt.mergingOpAssembler(); - function append(op) - { + function append(op) { assem.append(op.opcode, op.chars, op.lines, op.attribs); } - function toString() - { + function toString() { return assem.toString(); } - function clear() - { + function clear() { assem.clear(); } - function endDocument() - { + function endDocument() { assem.endDocument(); } @@ -482,17 +347,14 @@ if (_opt) endDocument: endDocument }; }; -} -else -{ - Changeset.mergingOpAssembler = function() - { +} else { + exports.mergingOpAssembler = function () { // This assembler can be used in production; it efficiently // merges consecutive operations that are mergeable, ignores // no-ops, and drops final pure "keeps". It does not re-order // operations. - var assem = Changeset.opAssembler(); - var bufOp = Changeset.newOp(); + var assem = exports.opAssembler(); + var bufOp = exports.newOp(); // If we get, for example, insertions [xxx\n,yyy], those don't merge, // but if we get [xxx\n,yyy,zzz\n], that merges to [xxx\nyyyzzz\n]. @@ -500,19 +362,13 @@ else // ops immediately after it. var bufOpAdditionalCharsAfterNewline = 0; - function flush(isEndDocument) - { - if (bufOp.opcode) - { - if (isEndDocument && bufOp.opcode == '=' && !bufOp.attribs) - { + function flush(isEndDocument) { + if (bufOp.opcode) { + if (isEndDocument && bufOp.opcode == '=' && !bufOp.attribs) { // final merged keep, leave it implicit - } - else - { + } else { assem.append(bufOp); - if (bufOpAdditionalCharsAfterNewline) - { + if (bufOpAdditionalCharsAfterNewline) { bufOp.chars = bufOpAdditionalCharsAfterNewline; bufOp.lines = 0; assem.append(bufOp); @@ -523,53 +379,40 @@ else } } - function append(op) - { - if (op.chars > 0) - { - if (bufOp.opcode == op.opcode && bufOp.attribs == op.attribs) - { - if (op.lines > 0) - { + function append(op) { + if (op.chars > 0) { + if (bufOp.opcode == op.opcode && bufOp.attribs == op.attribs) { + if (op.lines > 0) { // bufOp and additional chars are all mergeable into a multi-line op bufOp.chars += bufOpAdditionalCharsAfterNewline + op.chars; bufOp.lines += op.lines; bufOpAdditionalCharsAfterNewline = 0; - } - else if (bufOp.lines == 0) - { + } else if (bufOp.lines == 0) { // both bufOp and op are in-line bufOp.chars += op.chars; - } - else - { + } else { // append in-line text to multi-line bufOp bufOpAdditionalCharsAfterNewline += op.chars; } - } - else - { + } else { flush(); - Changeset.copyOp(op, bufOp); + exports.copyOp(op, bufOp); } } } - function endDocument() - { + function endDocument() { flush(true); } - function toString() - { + function toString() { flush(); return assem.toString(); } - function clear() - { + function clear() { assem.clear(); - Changeset.clearOp(bufOp); + exports.clearOp(bufOp); } return { append: append, @@ -580,26 +423,20 @@ else }; } -if (_opt) -{ - Changeset.opAssembler = function() - { +if (_opt) { + exports.opAssembler = function () { var assem = _opt.opAssembler(); // this function allows op to be mutated later (doesn't keep a ref) - - function append(op) - { + function append(op) { assem.append(op.opcode, op.chars, op.lines, op.attribs); } - function toString() - { + function toString() { return assem.toString(); } - function clear() - { + function clear() { assem.clear(); } return { @@ -608,33 +445,25 @@ if (_opt) clear: clear }; }; -} -else -{ - Changeset.opAssembler = function() - { +} else { + exports.opAssembler = function () { var pieces = []; // this function allows op to be mutated later (doesn't keep a ref) - - function append(op) - { + function append(op) { pieces.push(op.attribs); - if (op.lines) - { - pieces.push('|', Changeset.numToString(op.lines)); + if (op.lines) { + pieces.push('|', exports.numToString(op.lines)); } pieces.push(op.opcode); - pieces.push(Changeset.numToString(op.chars)); + pieces.push(exports.numToString(op.chars)); } - function toString() - { + function toString() { return pieces.join(''); } - function clear() - { + function clear() { pieces.length = 0; } return { @@ -645,38 +474,32 @@ else }; } -Changeset.stringIterator = function(str) -{ +exports.stringIterator = function (str) { var curIndex = 0; - function assertRemaining(n) - { - Changeset.assert(n <= remaining(), "!(", n, " <= ", remaining(), ")"); + function assertRemaining(n) { + exports.assert(n <= remaining(), "!(", n, " <= ", remaining(), ")"); } - function take(n) - { + function take(n) { assertRemaining(n); var s = str.substr(curIndex, n); curIndex += n; return s; } - function peek(n) - { + function peek(n) { assertRemaining(n); var s = str.substr(curIndex, n); return s; } - function skip(n) - { + function skip(n) { assertRemaining(n); curIndex += n; } - function remaining() - { + function remaining() { return str.length - curIndex; } return { @@ -687,17 +510,14 @@ Changeset.stringIterator = function(str) }; }; -Changeset.stringAssembler = function() -{ +exports.stringAssembler = function () { var pieces = []; - function append(x) - { + function append(x) { pieces.push(String(x)); } - function toString() - { + function toString() { return pieces.join(''); } return { @@ -707,10 +527,9 @@ Changeset.stringAssembler = function() }; // "lines" need not be an array as long as it supports certain calls (lines_foo inside). -Changeset.textLinesMutator = function(lines) -{ +exports.textLinesMutator = function (lines) { // Mutates lines, an array of strings, in place. - // Mutation operations have the same constraints as changeset operations + // Mutation operations have the same constraints as exports operations // with respect to newlines, but not the other additional constraints // (i.e. ins/del ordering, forbidden no-ops, non-mergeability, final newline). // Can be used to mutate lists of strings where the last char of each string @@ -727,120 +546,87 @@ Changeset.textLinesMutator = function(lines) // invariant: if (inSplice && (curLine >= curSplice[0] + curSplice.length - 2)) then // curCol == 0 - function lines_applySplice(s) - { + function lines_applySplice(s) { lines.splice.apply(lines, s); } - function lines_toSource() - { + function lines_toSource() { return lines.toSource(); } - function lines_get(idx) - { - if (lines.get) - { + function lines_get(idx) { + if (lines.get) { return lines.get(idx); - } - else - { + } else { return lines[idx]; } } // can be unimplemented if removeLines's return value not needed - - function lines_slice(start, end) - { - if (lines.slice) - { + function lines_slice(start, end) { + if (lines.slice) { return lines.slice(start, end); - } - else - { + } else { return []; } } - function lines_length() - { - if ((typeof lines.length) == "number") - { + function lines_length() { + if ((typeof lines.length) == "number") { return lines.length; - } - else - { + } else { return lines.length(); } } - function enterSplice() - { + function enterSplice() { curSplice[0] = curLine; curSplice[1] = 0; - if (curCol > 0) - { + if (curCol > 0) { putCurLineInSplice(); } inSplice = true; } - function leaveSplice() - { + function leaveSplice() { lines_applySplice(curSplice); curSplice.length = 2; curSplice[0] = curSplice[1] = 0; inSplice = false; } - function isCurLineInSplice() - { + function isCurLineInSplice() { return (curLine - curSplice[0] < (curSplice.length - 2)); } - function debugPrint(typ) - { + function debugPrint(typ) { print(typ + ": " + curSplice.toSource() + " / " + curLine + "," + curCol + " / " + lines_toSource()); } - function putCurLineInSplice() - { - if (!isCurLineInSplice()) - { + function putCurLineInSplice() { + if (!isCurLineInSplice()) { curSplice.push(lines_get(curSplice[0] + curSplice[1])); curSplice[1]++; } return 2 + curLine - curSplice[0]; } - function skipLines(L, includeInSplice) - { - if (L) - { - if (includeInSplice) - { - if (!inSplice) - { + function skipLines(L, includeInSplice) { + if (L) { + if (includeInSplice) { + if (!inSplice) { enterSplice(); } - for (var i = 0; i < L; i++) - { + for (var i = 0; i < L; i++) { curCol = 0; putCurLineInSplice(); curLine++; } - } - else - { - if (inSplice) - { - if (L > 1) - { + } else { + if (inSplice) { + if (L > 1) { leaveSplice(); - } - else - { + } else { putCurLineInSplice(); } } @@ -857,22 +643,15 @@ Changeset.textLinesMutator = function(lines) //debugPrint("skip"); } - function skip(N, L, includeInSplice) - { - if (N) - { - if (L) - { + function skip(N, L, includeInSplice) { + if (N) { + if (L) { skipLines(L, includeInSplice); - } - else - { - if (includeInSplice && !inSplice) - { + } else { + if (includeInSplice && !inSplice) { enterSplice(); } - if (inSplice) - { + if (inSplice) { putCurLineInSplice(); } curCol += N; @@ -881,34 +660,26 @@ Changeset.textLinesMutator = function(lines) } } - function removeLines(L) - { + function removeLines(L) { var removed = ''; - if (L) - { - if (!inSplice) - { + if (L) { + if (!inSplice) { enterSplice(); } - function nextKLinesText(k) - { + function nextKLinesText(k) { var m = curSplice[0] + curSplice[1]; return lines_slice(m, m + k).join(''); } - if (isCurLineInSplice()) - { + if (isCurLineInSplice()) { //print(curCol); - if (curCol == 0) - { + if (curCol == 0) { removed = curSplice[curSplice.length - 1]; // print("FOO"); // case foo curSplice.length--; removed += nextKLinesText(L - 1); curSplice[1] += L - 1; - } - else - { + } else { removed = nextKLinesText(L - 1); curSplice[1] += L - 1; var sline = curSplice.length - 1; @@ -916,9 +687,7 @@ Changeset.textLinesMutator = function(lines) curSplice[sline] = curSplice[sline].substring(0, curCol) + lines_get(curSplice[0] + curSplice[1]); curSplice[1] += 1; } - } - else - { + } else { removed = nextKLinesText(L); curSplice[1] += L; } @@ -927,19 +696,13 @@ Changeset.textLinesMutator = function(lines) return removed; } - function remove(N, L) - { + function remove(N, L) { var removed = ''; - if (N) - { - if (L) - { + if (N) { + if (L) { return removeLines(L); - } - else - { - if (!inSplice) - { + } else { + if (!inSplice) { enterSplice(); } var sline = putCurLineInSplice(); @@ -951,19 +714,14 @@ Changeset.textLinesMutator = function(lines) return removed; } - function insert(text, L) - { - if (text) - { - if (!inSplice) - { + function insert(text, L) { + if (text) { + if (!inSplice) { enterSplice(); } - if (L) - { - var newLines = Changeset.splitTextLines(text); - if (isCurLineInSplice()) - { + if (L) { + var newLines = exports.splitTextLines(text); + if (isCurLineInSplice()) { //if (curCol == 0) { //curSplice.length--; //curSplice[1]--; @@ -982,15 +740,11 @@ Changeset.textLinesMutator = function(lines) curSplice.push(theLine.substring(lineCol)); curCol = 0; //} - } - else - { + } else { Array.prototype.push.apply(curSplice, newLines); curLine += newLines.length; } - } - else - { + } else { var sline = putCurLineInSplice(); curSplice[sline] = curSplice[sline].substring(0, curCol) + text + curSplice[sline].substring(curCol); curCol += text.length; @@ -999,21 +753,17 @@ Changeset.textLinesMutator = function(lines) } } - function hasMore() - { + function hasMore() { //print(lines.length+" / "+inSplice+" / "+(curSplice.length - 2)+" / "+curSplice[1]); var docLines = lines_length(); - if (inSplice) - { + if (inSplice) { docLines += curSplice.length - 2 - curSplice[1]; } return curLine < docLines; } - function close() - { - if (inSplice) - { + function close() { + if (inSplice) { leaveSplice(); } //debugPrint("close"); @@ -1031,21 +781,18 @@ Changeset.textLinesMutator = function(lines) return self; }; -Changeset.applyZip = function(in1, idx1, in2, idx2, func) -{ - var iter1 = Changeset.opIterator(in1, idx1); - var iter2 = Changeset.opIterator(in2, idx2); - var assem = Changeset.smartOpAssembler(); - var op1 = Changeset.newOp(); - var op2 = Changeset.newOp(); - var opOut = Changeset.newOp(); - while (op1.opcode || iter1.hasNext() || op2.opcode || iter2.hasNext()) - { +exports.applyZip = function (in1, idx1, in2, idx2, func) { + var iter1 = exports.opIterator(in1, idx1); + var iter2 = exports.opIterator(in2, idx2); + var assem = exports.smartOpAssembler(); + var op1 = exports.newOp(); + var op2 = exports.newOp(); + var opOut = exports.newOp(); + while (op1.opcode || iter1.hasNext() || op2.opcode || iter2.hasNext()) { if ((!op1.opcode) && iter1.hasNext()) iter1.next(op1); if ((!op2.opcode) && iter2.hasNext()) iter2.next(op2); func(op1, op2, opOut); - if (opOut.opcode) - { + if (opOut.opcode) { //print(opOut.toSource()); assem.append(opOut); opOut.opcode = ''; @@ -1055,17 +802,15 @@ Changeset.applyZip = function(in1, idx1, in2, idx2, func) return assem.toString(); }; -Changeset.unpack = function(cs) -{ +exports.unpack = function (cs) { var headerRegex = /Z:([0-9a-z]+)([><])([0-9a-z]+)|/; var headerMatch = headerRegex.exec(cs); - if ((!headerMatch) || (!headerMatch[0])) - { - Changeset.error("Not a changeset: " + cs); + if ((!headerMatch) || (!headerMatch[0])) { + exports.error("Not a exports: " + cs); } - var oldLen = Changeset.parseNum(headerMatch[1]); + var oldLen = exports.parseNum(headerMatch[1]); var changeSign = (headerMatch[2] == '>') ? 1 : -1; - var changeMag = Changeset.parseNum(headerMatch[3]); + var changeMag = exports.parseNum(headerMatch[3]); var newLen = oldLen + changeSign * changeMag; var opsStart = headerMatch[0].length; var opsEnd = cs.indexOf("$"); @@ -1078,28 +823,24 @@ Changeset.unpack = function(cs) }; }; -Changeset.pack = function(oldLen, newLen, opsStr, bank) -{ +exports.pack = function (oldLen, newLen, opsStr, bank) { var lenDiff = newLen - oldLen; - var lenDiffStr = (lenDiff >= 0 ? '>' + Changeset.numToString(lenDiff) : '<' + Changeset.numToString(-lenDiff)); + var lenDiffStr = (lenDiff >= 0 ? '>' + exports.numToString(lenDiff) : '<' + exports.numToString(-lenDiff)); var a = []; - a.push('Z:', Changeset.numToString(oldLen), lenDiffStr, opsStr, '$', bank); + a.push('Z:', exports.numToString(oldLen), lenDiffStr, opsStr, '$', bank); return a.join(''); }; -Changeset.applyToText = function(cs, str) -{ - var unpacked = Changeset.unpack(cs); - Changeset.assert(str.length == unpacked.oldLen, "mismatched apply: ", str.length, " / ", unpacked.oldLen); - var csIter = Changeset.opIterator(unpacked.ops); - var bankIter = Changeset.stringIterator(unpacked.charBank); - var strIter = Changeset.stringIterator(str); - var assem = Changeset.stringAssembler(); - while (csIter.hasNext()) - { +exports.applyToText = function (cs, str) { + var unpacked = exports.unpack(cs); + exports.assert(str.length == unpacked.oldLen, "mismatched apply: ", str.length, " / ", unpacked.oldLen); + var csIter = exports.opIterator(unpacked.ops); + var bankIter = exports.stringIterator(unpacked.charBank); + var strIter = exports.stringIterator(str); + var assem = exports.stringAssembler(); + while (csIter.hasNext()) { var op = csIter.next(); - switch (op.opcode) - { + switch (op.opcode) { case '+': assem.append(bankIter.take(op.chars)); break; @@ -1115,17 +856,14 @@ Changeset.applyToText = function(cs, str) return assem.toString(); }; -Changeset.mutateTextLines = function(cs, lines) -{ - var unpacked = Changeset.unpack(cs); - var csIter = Changeset.opIterator(unpacked.ops); - var bankIter = Changeset.stringIterator(unpacked.charBank); - var mut = Changeset.textLinesMutator(lines); - while (csIter.hasNext()) - { +exports.mutateTextLines = function (cs, lines) { + var unpacked = exports.unpack(cs); + var csIter = exports.opIterator(unpacked.ops); + var bankIter = exports.stringIterator(unpacked.charBank); + var mut = exports.textLinesMutator(lines); + while (csIter.hasNext()) { var op = csIter.next(); - switch (op.opcode) - { + switch (op.opcode) { case '+': mut.insert(bankIter.take(op.chars), op.lines); break; @@ -1140,8 +878,7 @@ Changeset.mutateTextLines = function(cs, lines) mut.close(); }; -Changeset.composeAttributes = function(att1, att2, resultIsMutation, pool) -{ +exports.composeAttributes = function (att1, att2, resultIsMutation, pool) { // att1 and att2 are strings like "*3*f*1c", asMutation is a boolean. // Sometimes attribute (key,value) pairs are treated as attribute presence // information, while other times they are treated as operations that @@ -1155,9 +892,8 @@ Changeset.composeAttributes = function(att1, att2, resultIsMutation, pool) // ([(bold, true)], [(bold, )], true) -> [(bold, )] // ([(bold, true)], [(bold, )], false) -> [] // pool can be null if att2 has no attributes. - if ((!att1) && resultIsMutation) - { - // In the case of a mutation (i.e. composing two changesets), + if ((!att1) && resultIsMutation) { + // In the case of a mutation (i.e. composing two exportss), // an att2 composed with an empy att1 is just att2. If att1 // is part of an attribution string, then att2 may remove // attributes that are already gone, so don't do this optimization. @@ -1165,76 +901,58 @@ Changeset.composeAttributes = function(att1, att2, resultIsMutation, pool) } if (!att2) return att1; var atts = []; - att1.replace(/\*([0-9a-z]+)/g, function(_, a) - { - atts.push(pool.getAttrib(Changeset.parseNum(a))); + att1.replace(/\*([0-9a-z]+)/g, function (_, a) { + atts.push(pool.getAttrib(exports.parseNum(a))); return ''; }); - att2.replace(/\*([0-9a-z]+)/g, function(_, a) - { - var pair = pool.getAttrib(Changeset.parseNum(a)); + att2.replace(/\*([0-9a-z]+)/g, function (_, a) { + var pair = pool.getAttrib(exports.parseNum(a)); var found = false; - for (var i = 0; i < atts.length; i++) - { + for (var i = 0; i < atts.length; i++) { var oldPair = atts[i]; - if (oldPair[0] == pair[0]) - { - if (pair[1] || resultIsMutation) - { + if (oldPair[0] == pair[0]) { + if (pair[1] || resultIsMutation) { oldPair[1] = pair[1]; - } - else - { + } else { atts.splice(i, 1); } found = true; break; } } - if ((!found) && (pair[1] || resultIsMutation)) - { + if ((!found) && (pair[1] || resultIsMutation)) { atts.push(pair); } return ''; }); atts.sort(); - var buf = Changeset.stringAssembler(); - for (var i = 0; i < atts.length; i++) - { + var buf = exports.stringAssembler(); + for (var i = 0; i < atts.length; i++) { buf.append('*'); - buf.append(Changeset.numToString(pool.putAttrib(atts[i]))); + buf.append(exports.numToString(pool.putAttrib(atts[i]))); } //print(att1+" / "+att2+" / "+buf.toString()); return buf.toString(); }; -Changeset._slicerZipperFunc = function(attOp, csOp, opOut, pool) -{ +exports._slicerZipperFunc = function (attOp, csOp, opOut, pool) { // attOp is the op from the sequence that is being operated on, either an - // attribution string or the earlier of two changesets being composed. + // attribution string or the earlier of two exportss being composed. // pool can be null if definitely not needed. //print(csOp.toSource()+" "+attOp.toSource()+" "+opOut.toSource()); - if (attOp.opcode == '-') - { - Changeset.copyOp(attOp, opOut); + if (attOp.opcode == '-') { + exports.copyOp(attOp, opOut); attOp.opcode = ''; - } - else if (!attOp.opcode) - { - Changeset.copyOp(csOp, opOut); + } else if (!attOp.opcode) { + exports.copyOp(csOp, opOut); csOp.opcode = ''; - } - else - { - switch (csOp.opcode) - { + } else { + switch (csOp.opcode) { case '-': { - if (csOp.chars <= attOp.chars) - { + if (csOp.chars <= attOp.chars) { // delete or delete part - if (attOp.opcode == '=') - { + if (attOp.opcode == '=') { opOut.opcode = '-'; opOut.chars = csOp.chars; opOut.lines = csOp.lines; @@ -1243,16 +961,12 @@ Changeset._slicerZipperFunc = function(attOp, csOp, opOut, pool) attOp.chars -= csOp.chars; attOp.lines -= csOp.lines; csOp.opcode = ''; - if (!attOp.chars) - { + if (!attOp.chars) { attOp.opcode = ''; } - } - else - { + } else { // delete and keep going - if (attOp.opcode == '=') - { + if (attOp.opcode == '=') { opOut.opcode = '-'; opOut.chars = attOp.chars; opOut.lines = attOp.lines; @@ -1267,34 +981,30 @@ Changeset._slicerZipperFunc = function(attOp, csOp, opOut, pool) case '+': { // insert - Changeset.copyOp(csOp, opOut); + exports.copyOp(csOp, opOut); csOp.opcode = ''; break; } case '=': { - if (csOp.chars <= attOp.chars) - { + if (csOp.chars <= attOp.chars) { // keep or keep part opOut.opcode = attOp.opcode; opOut.chars = csOp.chars; opOut.lines = csOp.lines; - opOut.attribs = Changeset.composeAttributes(attOp.attribs, csOp.attribs, attOp.opcode == '=', pool); + opOut.attribs = exports.composeAttributes(attOp.attribs, csOp.attribs, attOp.opcode == '=', pool); csOp.opcode = ''; attOp.chars -= csOp.chars; attOp.lines -= csOp.lines; - if (!attOp.chars) - { + if (!attOp.chars) { attOp.opcode = ''; } - } - else - { + } else { // keep and keep going opOut.opcode = attOp.opcode; opOut.chars = attOp.chars; opOut.lines = attOp.lines; - opOut.attribs = Changeset.composeAttributes(attOp.attribs, csOp.attribs, attOp.opcode == '=', pool); + opOut.attribs = exports.composeAttributes(attOp.attribs, csOp.attribs, attOp.opcode == '=', pool); attOp.opcode = ''; csOp.chars -= attOp.chars; csOp.lines -= attOp.lines; @@ -1303,7 +1013,7 @@ Changeset._slicerZipperFunc = function(attOp, csOp, opOut, pool) } case '': { - Changeset.copyOp(attOp, opOut); + exports.copyOp(attOp, opOut); attOp.opcode = ''; break; } @@ -1311,190 +1021,155 @@ Changeset._slicerZipperFunc = function(attOp, csOp, opOut, pool) } }; -Changeset.applyToAttribution = function(cs, astr, pool) -{ - var unpacked = Changeset.unpack(cs); +exports.applyToAttribution = function (cs, astr, pool) { + var unpacked = exports.unpack(cs); - return Changeset.applyZip(astr, 0, unpacked.ops, 0, function(op1, op2, opOut) - { - return Changeset._slicerZipperFunc(op1, op2, opOut, pool); + return exports.applyZip(astr, 0, unpacked.ops, 0, function (op1, op2, opOut) { + return exports._slicerZipperFunc(op1, op2, opOut, pool); }); }; -/*Changeset.oneInsertedLineAtATimeOpIterator = function(opsStr, optStartIndex, charBank) { - var iter = Changeset.opIterator(opsStr, optStartIndex); +/*exports.oneInsertedLineAtATimeOpIterator = function(opsStr, optStartIndex, charBank) { + var iter = exports.opIterator(opsStr, optStartIndex); var bankIndex = 0; };*/ -Changeset.mutateAttributionLines = function(cs, lines, pool) -{ +exports.mutateAttributionLines = function (cs, lines, pool) { //dmesg(cs); //dmesg(lines.toSource()+" ->"); - var unpacked = Changeset.unpack(cs); - var csIter = Changeset.opIterator(unpacked.ops); + var unpacked = exports.unpack(cs); + var csIter = exports.opIterator(unpacked.ops); var csBank = unpacked.charBank; var csBankIndex = 0; // treat the attribution lines as text lines, mutating a line at a time - var mut = Changeset.textLinesMutator(lines); + var mut = exports.textLinesMutator(lines); var lineIter = null; - function isNextMutOp() - { + function isNextMutOp() { return (lineIter && lineIter.hasNext()) || mut.hasMore(); } - function nextMutOp(destOp) - { - if ((!(lineIter && lineIter.hasNext())) && mut.hasMore()) - { + function nextMutOp(destOp) { + if ((!(lineIter && lineIter.hasNext())) && mut.hasMore()) { var line = mut.removeLines(1); - lineIter = Changeset.opIterator(line); + lineIter = exports.opIterator(line); } - if (lineIter && lineIter.hasNext()) - { + if (lineIter && lineIter.hasNext()) { lineIter.next(destOp); - } - else - { + } else { destOp.opcode = ''; } } var lineAssem = null; - function outputMutOp(op) - { + function outputMutOp(op) { //print("outputMutOp: "+op.toSource()); - if (!lineAssem) - { - lineAssem = Changeset.mergingOpAssembler(); + if (!lineAssem) { + lineAssem = exports.mergingOpAssembler(); } lineAssem.append(op); - if (op.lines > 0) - { - Changeset.assert(op.lines == 1, "Can't have op.lines of ", op.lines, " in attribution lines"); + if (op.lines > 0) { + exports.assert(op.lines == 1, "Can't have op.lines of ", op.lines, " in attribution lines"); // ship it to the mut mut.insert(lineAssem.toString(), 1); lineAssem = null; } } - var csOp = Changeset.newOp(); - var attOp = Changeset.newOp(); - var opOut = Changeset.newOp(); - while (csOp.opcode || csIter.hasNext() || attOp.opcode || isNextMutOp()) - { - if ((!csOp.opcode) && csIter.hasNext()) - { + var csOp = exports.newOp(); + var attOp = exports.newOp(); + var opOut = exports.newOp(); + while (csOp.opcode || csIter.hasNext() || attOp.opcode || isNextMutOp()) { + if ((!csOp.opcode) && csIter.hasNext()) { csIter.next(csOp); } //print(csOp.toSource()+" "+attOp.toSource()+" "+opOut.toSource()); //print(csOp.opcode+"/"+csOp.lines+"/"+csOp.attribs+"/"+lineAssem+"/"+lineIter+"/"+(lineIter?lineIter.hasNext():null)); //print("csOp: "+csOp.toSource()); - if ((!csOp.opcode) && (!attOp.opcode) && (!lineAssem) && (!(lineIter && lineIter.hasNext()))) - { + if ((!csOp.opcode) && (!attOp.opcode) && (!lineAssem) && (!(lineIter && lineIter.hasNext()))) { break; // done - } - else if (csOp.opcode == '=' && csOp.lines > 0 && (!csOp.attribs) && (!attOp.opcode) && (!lineAssem) && (!(lineIter && lineIter.hasNext()))) - { + } else if (csOp.opcode == '=' && csOp.lines > 0 && (!csOp.attribs) && (!attOp.opcode) && (!lineAssem) && (!(lineIter && lineIter.hasNext()))) { // skip multiple lines; this is what makes small changes not order of the document size mut.skipLines(csOp.lines); //print("skipped: "+csOp.lines); csOp.opcode = ''; - } - else if (csOp.opcode == '+') - { - if (csOp.lines > 1) - { + } else if (csOp.opcode == '+') { + if (csOp.lines > 1) { var firstLineLen = csBank.indexOf('\n', csBankIndex) + 1 - csBankIndex; - Changeset.copyOp(csOp, opOut); + exports.copyOp(csOp, opOut); csOp.chars -= firstLineLen; csOp.lines--; opOut.lines = 1; opOut.chars = firstLineLen; - } - else - { - Changeset.copyOp(csOp, opOut); + } else { + exports.copyOp(csOp, opOut); csOp.opcode = ''; } outputMutOp(opOut); csBankIndex += opOut.chars; opOut.opcode = ''; - } - else - { - if ((!attOp.opcode) && isNextMutOp()) - { + } else { + if ((!attOp.opcode) && isNextMutOp()) { nextMutOp(attOp); } //print("attOp: "+attOp.toSource()); - Changeset._slicerZipperFunc(attOp, csOp, opOut, pool); - if (opOut.opcode) - { + exports._slicerZipperFunc(attOp, csOp, opOut, pool); + if (opOut.opcode) { outputMutOp(opOut); opOut.opcode = ''; } } } - Changeset.assert(!lineAssem, "line assembler not finished"); + exports.assert(!lineAssem, "line assembler not finished"); mut.close(); //dmesg("-> "+lines.toSource()); }; -Changeset.joinAttributionLines = function(theAlines) -{ - var assem = Changeset.mergingOpAssembler(); - for (var i = 0; i < theAlines.length; i++) - { +exports.joinAttributionLines = function (theAlines) { + var assem = exports.mergingOpAssembler(); + for (var i = 0; i < theAlines.length; i++) { var aline = theAlines[i]; - var iter = Changeset.opIterator(aline); - while (iter.hasNext()) - { + var iter = exports.opIterator(aline); + while (iter.hasNext()) { assem.append(iter.next()); } } return assem.toString(); }; -Changeset.splitAttributionLines = function(attrOps, text) -{ - var iter = Changeset.opIterator(attrOps); - var assem = Changeset.mergingOpAssembler(); +exports.splitAttributionLines = function (attrOps, text) { + var iter = exports.opIterator(attrOps); + var assem = exports.mergingOpAssembler(); var lines = []; var pos = 0; - function appendOp(op) - { + function appendOp(op) { assem.append(op); - if (op.lines > 0) - { + if (op.lines > 0) { lines.push(assem.toString()); assem.clear(); } pos += op.chars; } - while (iter.hasNext()) - { + while (iter.hasNext()) { var op = iter.next(); var numChars = op.chars; var numLines = op.lines; - while (numLines > 1) - { + while (numLines > 1) { var newlineEnd = text.indexOf('\n', pos) + 1; - Changeset.assert(newlineEnd > 0, "newlineEnd <= 0 in splitAttributionLines"); + exports.assert(newlineEnd > 0, "newlineEnd <= 0 in splitAttributionLines"); op.chars = newlineEnd - pos; op.lines = 1; appendOp(op); numChars -= op.chars; numLines -= op.lines; } - if (numLines == 1) - { + if (numLines == 1) { op.chars = numChars; op.lines = 1; } @@ -1504,149 +1179,121 @@ Changeset.splitAttributionLines = function(attrOps, text) return lines; }; -Changeset.splitTextLines = function(text) -{ +exports.splitTextLines = function (text) { return text.match(/[^\n]*(?:\n|[^\n]$)/g); }; -Changeset.compose = function(cs1, cs2, pool) -{ - var unpacked1 = Changeset.unpack(cs1); - var unpacked2 = Changeset.unpack(cs2); +exports.compose = function (cs1, cs2, pool) { + var unpacked1 = exports.unpack(cs1); + var unpacked2 = exports.unpack(cs2); var len1 = unpacked1.oldLen; var len2 = unpacked1.newLen; - Changeset.assert(len2 == unpacked2.oldLen, "mismatched composition"); + exports.assert(len2 == unpacked2.oldLen, "mismatched composition"); var len3 = unpacked2.newLen; - var bankIter1 = Changeset.stringIterator(unpacked1.charBank); - var bankIter2 = Changeset.stringIterator(unpacked2.charBank); - var bankAssem = Changeset.stringAssembler(); - - var newOps = Changeset.applyZip(unpacked1.ops, 0, unpacked2.ops, 0, function(op1, op2, opOut) - { - //var debugBuilder = Changeset.stringAssembler(); - //debugBuilder.append(Changeset.opString(op1)); + var bankIter1 = exports.stringIterator(unpacked1.charBank); + var bankIter2 = exports.stringIterator(unpacked2.charBank); + var bankAssem = exports.stringAssembler(); + + var newOps = exports.applyZip(unpacked1.ops, 0, unpacked2.ops, 0, function (op1, op2, opOut) { + //var debugBuilder = exports.stringAssembler(); + //debugBuilder.append(exports.opString(op1)); //debugBuilder.append(','); - //debugBuilder.append(Changeset.opString(op2)); + //debugBuilder.append(exports.opString(op2)); //debugBuilder.append(' / '); var op1code = op1.opcode; var op2code = op2.opcode; - if (op1code == '+' && op2code == '-') - { + if (op1code == '+' && op2code == '-') { bankIter1.skip(Math.min(op1.chars, op2.chars)); } - Changeset._slicerZipperFunc(op1, op2, opOut, pool); - if (opOut.opcode == '+') - { - if (op2code == '+') - { + exports._slicerZipperFunc(op1, op2, opOut, pool); + if (opOut.opcode == '+') { + if (op2code == '+') { bankAssem.append(bankIter2.take(opOut.chars)); - } - else - { + } else { bankAssem.append(bankIter1.take(opOut.chars)); } } - //debugBuilder.append(Changeset.opString(op1)); + //debugBuilder.append(exports.opString(op1)); //debugBuilder.append(','); - //debugBuilder.append(Changeset.opString(op2)); + //debugBuilder.append(exports.opString(op2)); //debugBuilder.append(' -> '); - //debugBuilder.append(Changeset.opString(opOut)); + //debugBuilder.append(exports.opString(opOut)); //print(debugBuilder.toString()); }); - return Changeset.pack(len1, len3, newOps, bankAssem.toString()); + return exports.pack(len1, len3, newOps, bankAssem.toString()); }; -Changeset.attributeTester = function(attribPair, pool) -{ +exports.attributeTester = function (attribPair, pool) { // returns a function that tests if a string of attributes // (e.g. *3*4) contains a given attribute key,value that // is already present in the pool. - if (!pool) - { + if (!pool) { return never; } var attribNum = pool.putAttrib(attribPair, true); - if (attribNum < 0) - { + if (attribNum < 0) { return never; - } - else - { - var re = new RegExp('\\*' + Changeset.numToString(attribNum) + '(?!\\w)'); - return function(attribs) - { + } else { + var re = new RegExp('\\*' + exports.numToString(attribNum) + '(?!\\w)'); + return function (attribs) { return re.test(attribs); }; } - function never(attribs) - { + function never(attribs) { return false; } }; -Changeset.identity = function(N) -{ - return Changeset.pack(N, N, "", ""); +exports.identity = function (N) { + return exports.pack(N, N, "", ""); }; -Changeset.makeSplice = function(oldFullText, spliceStart, numRemoved, newText, optNewTextAPairs, pool) -{ +exports.makeSplice = function (oldFullText, spliceStart, numRemoved, newText, optNewTextAPairs, pool) { var oldLen = oldFullText.length; - if (spliceStart >= oldLen) - { + if (spliceStart >= oldLen) { spliceStart = oldLen - 1; } - if (numRemoved > oldFullText.length - spliceStart - 1) - { + if (numRemoved > oldFullText.length - spliceStart - 1) { numRemoved = oldFullText.length - spliceStart - 1; } var oldText = oldFullText.substring(spliceStart, spliceStart + numRemoved); var newLen = oldLen + newText.length - oldText.length; - var assem = Changeset.smartOpAssembler(); + var assem = exports.smartOpAssembler(); assem.appendOpWithText('=', oldFullText.substring(0, spliceStart)); assem.appendOpWithText('-', oldText); assem.appendOpWithText('+', newText, optNewTextAPairs, pool); assem.endDocument(); - return Changeset.pack(oldLen, newLen, assem.toString(), newText); + return exports.pack(oldLen, newLen, assem.toString(), newText); }; -Changeset.toSplices = function(cs) -{ +exports.toSplices = function (cs) { // get a list of splices, [startChar, endChar, newText] - var unpacked = Changeset.unpack(cs); + var unpacked = exports.unpack(cs); var splices = []; var oldPos = 0; - var iter = Changeset.opIterator(unpacked.ops); - var charIter = Changeset.stringIterator(unpacked.charBank); + var iter = exports.opIterator(unpacked.ops); + var charIter = exports.stringIterator(unpacked.charBank); var inSplice = false; - while (iter.hasNext()) - { + while (iter.hasNext()) { var op = iter.next(); - if (op.opcode == '=') - { + if (op.opcode == '=') { oldPos += op.chars; inSplice = false; - } - else - { - if (!inSplice) - { + } else { + if (!inSplice) { splices.push([oldPos, oldPos, ""]); inSplice = true; } - if (op.opcode == '-') - { + if (op.opcode == '-') { oldPos += op.chars; splices[splices.length - 1][1] += op.chars; - } - else if (op.opcode == '+') - { + } else if (op.opcode == '+') { splices[splices.length - 1][2] += charIter.take(op.chars); } } @@ -1655,56 +1302,40 @@ Changeset.toSplices = function(cs) return splices; }; -Changeset.characterRangeFollow = function(cs, startChar, endChar, insertionsAfter) -{ +exports.characterRangeFollow = function (cs, startChar, endChar, insertionsAfter) { var newStartChar = startChar; var newEndChar = endChar; - var splices = Changeset.toSplices(cs); + var splices = exports.toSplices(cs); var lengthChangeSoFar = 0; - for (var i = 0; i < splices.length; i++) - { + for (var i = 0; i < splices.length; i++) { var splice = splices[i]; var spliceStart = splice[0] + lengthChangeSoFar; var spliceEnd = splice[1] + lengthChangeSoFar; var newTextLength = splice[2].length; var thisLengthChange = newTextLength - (spliceEnd - spliceStart); - if (spliceStart <= newStartChar && spliceEnd >= newEndChar) - { + if (spliceStart <= newStartChar && spliceEnd >= newEndChar) { // splice fully replaces/deletes range // (also case that handles insertion at a collapsed selection) - if (insertionsAfter) - { + if (insertionsAfter) { newStartChar = newEndChar = spliceStart; - } - else - { + } else { newStartChar = newEndChar = spliceStart + newTextLength; } - } - else if (spliceEnd <= newStartChar) - { + } else if (spliceEnd <= newStartChar) { // splice is before range newStartChar += thisLengthChange; newEndChar += thisLengthChange; - } - else if (spliceStart >= newEndChar) - { + } else if (spliceStart >= newEndChar) { // splice is after range - } - else if (spliceStart >= newStartChar && spliceEnd <= newEndChar) - { + } else if (spliceStart >= newStartChar && spliceEnd <= newEndChar) { // splice is inside range newEndChar += thisLengthChange; - } - else if (spliceEnd < newEndChar) - { + } else if (spliceEnd < newEndChar) { // splice overlaps beginning of range newStartChar = spliceStart + newTextLength; newEndChar += thisLengthChange; - } - else - { + } else { // splice overlaps end of range newEndChar = spliceStart; } @@ -1715,79 +1346,63 @@ Changeset.characterRangeFollow = function(cs, startChar, endChar, insertionsAfte return [newStartChar, newEndChar]; }; -Changeset.moveOpsToNewPool = function(cs, oldPool, newPool) -{ - // works on changeset or attribution string +exports.moveOpsToNewPool = function (cs, oldPool, newPool) { + // works on exports or attribution string var dollarPos = cs.indexOf('$'); - if (dollarPos < 0) - { + if (dollarPos < 0) { dollarPos = cs.length; } var upToDollar = cs.substring(0, dollarPos); var fromDollar = cs.substring(dollarPos); // order of attribs stays the same - return upToDollar.replace(/\*([0-9a-z]+)/g, function(_, a) - { - var oldNum = Changeset.parseNum(a); + return upToDollar.replace(/\*([0-9a-z]+)/g, function (_, a) { + var oldNum = exports.parseNum(a); var pair = oldPool.getAttrib(oldNum); var newNum = newPool.putAttrib(pair); - return '*' + Changeset.numToString(newNum); + return '*' + exports.numToString(newNum); }) + fromDollar; }; -Changeset.makeAttribution = function(text) -{ - var assem = Changeset.smartOpAssembler(); +exports.makeAttribution = function (text) { + var assem = exports.smartOpAssembler(); assem.appendOpWithText('+', text); return assem.toString(); }; -// callable on a changeset, attribution string, or attribs property of an op -Changeset.eachAttribNumber = function(cs, func) -{ +// callable on a exports, attribution string, or attribs property of an op +exports.eachAttribNumber = function (cs, func) { var dollarPos = cs.indexOf('$'); - if (dollarPos < 0) - { + if (dollarPos < 0) { dollarPos = cs.length; } var upToDollar = cs.substring(0, dollarPos); - upToDollar.replace(/\*([0-9a-z]+)/g, function(_, a) - { - func(Changeset.parseNum(a)); + upToDollar.replace(/\*([0-9a-z]+)/g, function (_, a) { + func(exports.parseNum(a)); return ''; }); }; -// callable on a changeset, attribution string, or attribs property of an op, +// callable on a exports, attribution string, or attribs property of an op, // though it may easily create adjacent ops that can be merged. -Changeset.filterAttribNumbers = function(cs, filter) -{ - return Changeset.mapAttribNumbers(cs, filter); +exports.filterAttribNumbers = function (cs, filter) { + return exports.mapAttribNumbers(cs, filter); }; -Changeset.mapAttribNumbers = function(cs, func) -{ +exports.mapAttribNumbers = function (cs, func) { var dollarPos = cs.indexOf('$'); - if (dollarPos < 0) - { + if (dollarPos < 0) { dollarPos = cs.length; } var upToDollar = cs.substring(0, dollarPos); - var newUpToDollar = upToDollar.replace(/\*([0-9a-z]+)/g, function(s, a) - { - var n = func(Changeset.parseNum(a)); - if (n === true) - { + var newUpToDollar = upToDollar.replace(/\*([0-9a-z]+)/g, function (s, a) { + var n = func(exports.parseNum(a)); + if (n === true) { return s; - } - else if ((typeof n) === "number") - { - return '*' + Changeset.numToString(n); - } - else - { + } else if ((typeof n) === "number") { + return '*' + exports.numToString(n); + } else { return ''; } }); @@ -1795,108 +1410,88 @@ Changeset.mapAttribNumbers = function(cs, func) return newUpToDollar + cs.substring(dollarPos); }; -Changeset.makeAText = function(text, attribs) -{ +exports.makeAText = function (text, attribs) { return { text: text, - attribs: (attribs || Changeset.makeAttribution(text)) + attribs: (attribs || exports.makeAttribution(text)) }; }; -Changeset.applyToAText = function(cs, atext, pool) -{ +exports.applyToAText = function (cs, atext, pool) { return { - text: Changeset.applyToText(cs, atext.text), - attribs: Changeset.applyToAttribution(cs, atext.attribs, pool) + text: exports.applyToText(cs, atext.text), + attribs: exports.applyToAttribution(cs, atext.attribs, pool) }; }; -Changeset.cloneAText = function(atext) -{ +exports.cloneAText = function (atext) { return { text: atext.text, attribs: atext.attribs }; }; -Changeset.copyAText = function(atext1, atext2) -{ +exports.copyAText = function (atext1, atext2) { atext2.text = atext1.text; atext2.attribs = atext1.attribs; }; -Changeset.appendATextToAssembler = function(atext, assem) -{ +exports.appendATextToAssembler = function (atext, assem) { // intentionally skips last newline char of atext - var iter = Changeset.opIterator(atext.attribs); - var op = Changeset.newOp(); - while (iter.hasNext()) - { + var iter = exports.opIterator(atext.attribs); + var op = exports.newOp(); + while (iter.hasNext()) { iter.next(op); - if (!iter.hasNext()) - { + if (!iter.hasNext()) { // last op, exclude final newline - if (op.lines <= 1) - { + if (op.lines <= 1) { op.lines = 0; op.chars--; - if (op.chars) - { + if (op.chars) { assem.append(op); } - } - else - { - var nextToLastNewlineEnd = atext.text.lastIndexOf('\n', atext.text.length - 2) + 1; + } else { + var nextToLastNewlineEnd = + atext.text.lastIndexOf('\n', atext.text.length - 2) + 1; var lastLineLength = atext.text.length - nextToLastNewlineEnd - 1; op.lines--; op.chars -= (lastLineLength + 1); assem.append(op); op.lines = 0; op.chars = lastLineLength; - if (op.chars) - { + if (op.chars) { assem.append(op); } } - } - else - { + } else { assem.append(op); } } }; -Changeset.prepareForWire = function(cs, pool) -{ - var newPool = new AttribPool(); - var newCs = Changeset.moveOpsToNewPool(cs, pool, newPool); +exports.prepareForWire = function (cs, pool) { + var newPool = AttributePoolFactory.createAttributePool();; + var newCs = exports.moveOpsToNewPool(cs, pool, newPool); return { translated: newCs, pool: newPool }; }; -Changeset.isIdentity = function(cs) -{ - var unpacked = Changeset.unpack(cs); +exports.isIdentity = function (cs) { + var unpacked = exports.unpack(cs); return unpacked.ops == "" && unpacked.oldLen == unpacked.newLen; }; -Changeset.opAttributeValue = function(op, key, pool) -{ - return Changeset.attribsAttributeValue(op.attribs, key, pool); +exports.opAttributeValue = function (op, key, pool) { + return exports.attribsAttributeValue(op.attribs, key, pool); }; -Changeset.attribsAttributeValue = function(attribs, key, pool) -{ +exports.attribsAttributeValue = function (attribs, key, pool) { var value = ''; - if (attribs) - { - Changeset.eachAttribNumber(attribs, function(n) - { - if (pool.getAttribKey(n) == key) - { + if (attribs) { + exports.eachAttribNumber(attribs, function (n) { + if (pool.getAttribKey(n) == key) { value = pool.getAttribValue(n); } }); @@ -1904,36 +1499,31 @@ Changeset.attribsAttributeValue = function(attribs, key, pool) return value; }; -Changeset.builder = function(oldLen) -{ - var assem = Changeset.smartOpAssembler(); - var o = Changeset.newOp(); - var charBank = Changeset.stringAssembler(); +exports.builder = function (oldLen) { + var assem = exports.smartOpAssembler(); + var o = exports.newOp(); + var charBank = exports.stringAssembler(); var self = { // attribs are [[key1,value1],[key2,value2],...] or '*0*1...' (no pool needed in latter case) - keep: function(N, L, attribs, pool) - { + keep: function (N, L, attribs, pool) { o.opcode = '='; - o.attribs = (attribs && Changeset.makeAttribsString('=', attribs, pool)) || ''; + o.attribs = (attribs && exports.makeAttribsString('=', attribs, pool)) || ''; o.chars = N; o.lines = (L || 0); assem.append(o); return self; }, - keepText: function(text, attribs, pool) - { + keepText: function (text, attribs, pool) { assem.appendOpWithText('=', text, attribs, pool); return self; }, - insert: function(text, attribs, pool) - { + insert: function (text, attribs, pool) { assem.appendOpWithText('+', text, attribs, pool); charBank.append(text); return self; }, - remove: function(N, L) - { + remove: function (N, L) { o.opcode = '-'; o.attribs = ''; o.chars = N; @@ -1941,42 +1531,32 @@ Changeset.builder = function(oldLen) assem.append(o); return self; }, - toString: function() - { + toString: function () { assem.endDocument(); var newLen = oldLen + assem.getLengthChange(); - return Changeset.pack(oldLen, newLen, assem.toString(), charBank.toString()); + return exports.pack(oldLen, newLen, assem.toString(), charBank.toString()); } }; return self; }; -Changeset.makeAttribsString = function(opcode, attribs, pool) -{ +exports.makeAttribsString = function (opcode, attribs, pool) { // makeAttribsString(opcode, '*3') or makeAttribsString(opcode, [['foo','bar']], myPool) work - if (!attribs) - { + if (!attribs) { return ''; - } - else if ((typeof attribs) == "string") - { + } else if ((typeof attribs) == "string") { return attribs; - } - else if (pool && attribs && attribs.length) - { - if (attribs.length > 1) - { + } else if (pool && attribs && attribs.length) { + if (attribs.length > 1) { attribs = attribs.slice(); attribs.sort(); } var result = []; - for (var i = 0; i < attribs.length; i++) - { + for (var i = 0; i < attribs.length; i++) { var pair = attribs[i]; - if (opcode == '=' || (opcode == '+' && pair[1])) - { - result.push('*' + Changeset.numToString(pool.putAttrib(pair))); + if (opcode == '=' || (opcode == '+' && pair[1])) { + result.push('*' + exports.numToString(pool.putAttrib(pair))); } } return result.join(''); @@ -1984,30 +1564,24 @@ Changeset.makeAttribsString = function(opcode, attribs, pool) }; // like "substring" but on a single-line attribution string -Changeset.subattribution = function(astr, start, optEnd) -{ - var iter = Changeset.opIterator(astr, 0); - var assem = Changeset.smartOpAssembler(); - var attOp = Changeset.newOp(); - var csOp = Changeset.newOp(); - var opOut = Changeset.newOp(); - - function doCsOp() - { - if (csOp.chars) - { - while (csOp.opcode && (attOp.opcode || iter.hasNext())) - { +exports.subattribution = function (astr, start, optEnd) { + var iter = exports.opIterator(astr, 0); + var assem = exports.smartOpAssembler(); + var attOp = exports.newOp(); + var csOp = exports.newOp(); + var opOut = exports.newOp(); + + function doCsOp() { + if (csOp.chars) { + while (csOp.opcode && (attOp.opcode || iter.hasNext())) { if (!attOp.opcode) iter.next(attOp); - if (csOp.opcode && attOp.opcode && csOp.chars >= attOp.chars && attOp.lines > 0 && csOp.lines <= 0) - { + if (csOp.opcode && attOp.opcode && csOp.chars >= attOp.chars && attOp.lines > 0 && csOp.lines <= 0) { csOp.lines++; } - Changeset._slicerZipperFunc(attOp, csOp, opOut, null); - if (opOut.opcode) - { + exports._slicerZipperFunc(attOp, csOp, opOut, null); + if (opOut.opcode) { assem.append(opOut); opOut.opcode = ''; } @@ -2020,20 +1594,15 @@ Changeset.subattribution = function(astr, start, optEnd) doCsOp(); - if (optEnd === undefined) - { - if (attOp.opcode) - { + if (optEnd === undefined) { + if (attOp.opcode) { assem.append(attOp); } - while (iter.hasNext()) - { + while (iter.hasNext()) { iter.next(attOp); assem.append(attOp); } - } - else - { + } else { csOp.opcode = '='; csOp.chars = optEnd - start; doCsOp(); @@ -2042,57 +1611,39 @@ Changeset.subattribution = function(astr, start, optEnd) return assem.toString(); }; -Changeset.inverse = function(cs, lines, alines, pool) -{ - // lines and alines are what the changeset is meant to apply to. +exports.inverse = function (cs, lines, alines, pool) { + // lines and alines are what the exports is meant to apply to. // They may be arrays or objects with .get(i) and .length methods. // They include final newlines on lines. - - function lines_get(idx) - { - if (lines.get) - { + function lines_get(idx) { + if (lines.get) { return lines.get(idx); - } - else - { + } else { return lines[idx]; } } - function lines_length() - { - if ((typeof lines.length) == "number") - { + function lines_length() { + if ((typeof lines.length) == "number") { return lines.length; - } - else - { + } else { return lines.length(); } } - function alines_get(idx) - { - if (alines.get) - { + function alines_get(idx) { + if (alines.get) { return alines.get(idx); - } - else - { + } else { return alines[idx]; } } - function alines_length() - { - if ((typeof alines.length) == "number") - { + function alines_length() { + if ((typeof alines.length) == "number") { return alines.length; - } - else - { + } else { return alines.length(); } } @@ -2101,49 +1652,40 @@ Changeset.inverse = function(cs, lines, alines, pool) var curChar = 0; var curLineOpIter = null; var curLineOpIterLine; - var curLineNextOp = Changeset.newOp('+'); + var curLineNextOp = exports.newOp('+'); - var unpacked = Changeset.unpack(cs); - var csIter = Changeset.opIterator(unpacked.ops); - var builder = Changeset.builder(unpacked.newLen); + var unpacked = exports.unpack(cs); + var csIter = exports.opIterator(unpacked.ops); + var builder = exports.builder(unpacked.newLen); - function consumeAttribRuns(numChars, func /*(len, attribs, endsLine)*/ ) - { + function consumeAttribRuns(numChars, func /*(len, attribs, endsLine)*/ ) { - if ((!curLineOpIter) || (curLineOpIterLine != curLine)) - { + if ((!curLineOpIter) || (curLineOpIterLine != curLine)) { // create curLineOpIter and advance it to curChar - curLineOpIter = Changeset.opIterator(alines_get(curLine)); + curLineOpIter = exports.opIterator(alines_get(curLine)); curLineOpIterLine = curLine; var indexIntoLine = 0; var done = false; - while (!done) - { + while (!done) { curLineOpIter.next(curLineNextOp); - if (indexIntoLine + curLineNextOp.chars >= curChar) - { + if (indexIntoLine + curLineNextOp.chars >= curChar) { curLineNextOp.chars -= (curChar - indexIntoLine); done = true; - } - else - { + } else { indexIntoLine += curLineNextOp.chars; } } } - while (numChars > 0) - { - if ((!curLineNextOp.chars) && (!curLineOpIter.hasNext())) - { + while (numChars > 0) { + if ((!curLineNextOp.chars) && (!curLineOpIter.hasNext())) { curLine++; curChar = 0; curLineOpIterLine = curLine; curLineNextOp.chars = 0; - curLineOpIter = Changeset.opIterator(alines_get(curLine)); + curLineOpIter = exports.opIterator(alines_get(curLine)); } - if (!curLineNextOp.chars) - { + if (!curLineNextOp.chars) { curLineOpIter.next(curLineNextOp); } var charsToUse = Math.min(numChars, curLineNextOp.chars); @@ -2153,45 +1695,34 @@ Changeset.inverse = function(cs, lines, alines, pool) curChar += charsToUse; } - if ((!curLineNextOp.chars) && (!curLineOpIter.hasNext())) - { + if ((!curLineNextOp.chars) && (!curLineOpIter.hasNext())) { curLine++; curChar = 0; } } - function skip(N, L) - { - if (L) - { + function skip(N, L) { + if (L) { curLine += L; curChar = 0; - } - else - { - if (curLineOpIter && curLineOpIterLine == curLine) - { - consumeAttribRuns(N, function() - {}); - } - else - { + } else { + if (curLineOpIter && curLineOpIterLine == curLine) { + consumeAttribRuns(N, function () {}); + } else { curChar += N; } } } - function nextText(numChars) - { + function nextText(numChars) { var len = 0; - var assem = Changeset.stringAssembler(); + var assem = exports.stringAssembler(); var firstString = lines_get(curLine).substring(curChar); len += firstString.length; assem.append(firstString); var lineNum = curLine + 1; - while (len < numChars) - { + while (len < numChars) { var nextString = lines_get(lineNum); len += nextString.length; assem.append(nextString); @@ -2201,13 +1732,10 @@ Changeset.inverse = function(cs, lines, alines, pool) return assem.toString().substring(0, numChars); } - function cachedStrFunc(func) - { + function cachedStrFunc(func) { var cache = {}; - return function(s) - { - if (!cache[s]) - { + return function (s) { + if (!cache[s]) { cache[s] = func(s); } return cache[s]; @@ -2216,192 +1744,140 @@ Changeset.inverse = function(cs, lines, alines, pool) var attribKeys = []; var attribValues = []; - while (csIter.hasNext()) - { + while (csIter.hasNext()) { var csOp = csIter.next(); - if (csOp.opcode == '=') - { - if (csOp.attribs) - { + if (csOp.opcode == '=') { + if (csOp.attribs) { attribKeys.length = 0; attribValues.length = 0; - Changeset.eachAttribNumber(csOp.attribs, function(n) - { + exports.eachAttribNumber(csOp.attribs, function (n) { attribKeys.push(pool.getAttribKey(n)); attribValues.push(pool.getAttribValue(n)); }); - var undoBackToAttribs = cachedStrFunc(function(attribs) - { + var undoBackToAttribs = cachedStrFunc(function (attribs) { var backAttribs = []; - for (var i = 0; i < attribKeys.length; i++) - { + for (var i = 0; i < attribKeys.length; i++) { var appliedKey = attribKeys[i]; var appliedValue = attribValues[i]; - var oldValue = Changeset.attribsAttributeValue(attribs, appliedKey, pool); - if (appliedValue != oldValue) - { + var oldValue = exports.attribsAttributeValue(attribs, appliedKey, pool); + if (appliedValue != oldValue) { backAttribs.push([appliedKey, oldValue]); } } - return Changeset.makeAttribsString('=', backAttribs, pool); + return exports.makeAttribsString('=', backAttribs, pool); }); - consumeAttribRuns(csOp.chars, function(len, attribs, endsLine) - { + consumeAttribRuns(csOp.chars, function (len, attribs, endsLine) { builder.keep(len, endsLine ? 1 : 0, undoBackToAttribs(attribs)); }); - } - else - { + } else { skip(csOp.chars, csOp.lines); builder.keep(csOp.chars, csOp.lines); } - } - else if (csOp.opcode == '+') - { + } else if (csOp.opcode == '+') { builder.remove(csOp.chars, csOp.lines); - } - else if (csOp.opcode == '-') - { + } else if (csOp.opcode == '-') { var textBank = nextText(csOp.chars); var textBankIndex = 0; - consumeAttribRuns(csOp.chars, function(len, attribs, endsLine) - { + consumeAttribRuns(csOp.chars, function (len, attribs, endsLine) { builder.insert(textBank.substr(textBankIndex, len), attribs); textBankIndex += len; }); } } - return Changeset.checkRep(builder.toString()); + return exports.checkRep(builder.toString()); }; // %CLIENT FILE ENDS HERE% -Changeset.follow = function(cs1, cs2, reverseInsertOrder, pool) -{ - var unpacked1 = Changeset.unpack(cs1); - var unpacked2 = Changeset.unpack(cs2); +exports.follow = function (cs1, cs2, reverseInsertOrder, pool) { + var unpacked1 = exports.unpack(cs1); + var unpacked2 = exports.unpack(cs2); var len1 = unpacked1.oldLen; var len2 = unpacked2.oldLen; - Changeset.assert(len1 == len2, "mismatched follow"); - var chars1 = Changeset.stringIterator(unpacked1.charBank); - var chars2 = Changeset.stringIterator(unpacked2.charBank); + exports.assert(len1 == len2, "mismatched follow"); + var chars1 = exports.stringIterator(unpacked1.charBank); + var chars2 = exports.stringIterator(unpacked2.charBank); var oldLen = unpacked1.newLen; var oldPos = 0; var newLen = 0; - var hasInsertFirst = Changeset.attributeTester(['insertorder', 'first'], pool); + var hasInsertFirst = exports.attributeTester(['insertorder', 'first'], pool); - var newOps = Changeset.applyZip(unpacked1.ops, 0, unpacked2.ops, 0, function(op1, op2, opOut) - { - if (op1.opcode == '+' || op2.opcode == '+') - { + var newOps = exports.applyZip(unpacked1.ops, 0, unpacked2.ops, 0, function (op1, op2, opOut) { + if (op1.opcode == '+' || op2.opcode == '+') { var whichToDo; - if (op2.opcode != '+') - { + if (op2.opcode != '+') { whichToDo = 1; - } - else if (op1.opcode != '+') - { + } else if (op1.opcode != '+') { whichToDo = 2; - } - else - { + } else { // both + var firstChar1 = chars1.peek(1); var firstChar2 = chars2.peek(1); var insertFirst1 = hasInsertFirst(op1.attribs); var insertFirst2 = hasInsertFirst(op2.attribs); - if (insertFirst1 && !insertFirst2) - { + if (insertFirst1 && !insertFirst2) { whichToDo = 1; - } - else if (insertFirst2 && !insertFirst1) - { + } else if (insertFirst2 && !insertFirst1) { whichToDo = 2; } // insert string that doesn't start with a newline first so as not to break up lines - else if (firstChar1 == '\n' && firstChar2 != '\n') - { + else if (firstChar1 == '\n' && firstChar2 != '\n') { whichToDo = 2; - } - else if (firstChar1 != '\n' && firstChar2 == '\n') - { + } else if (firstChar1 != '\n' && firstChar2 == '\n') { whichToDo = 1; } // break symmetry: - else if (reverseInsertOrder) - { + else if (reverseInsertOrder) { whichToDo = 2; - } - else - { + } else { whichToDo = 1; } } - if (whichToDo == 1) - { + if (whichToDo == 1) { chars1.skip(op1.chars); opOut.opcode = '='; opOut.lines = op1.lines; opOut.chars = op1.chars; opOut.attribs = ''; op1.opcode = ''; - } - else - { + } else { // whichToDo == 2 chars2.skip(op2.chars); - Changeset.copyOp(op2, opOut); + exports.copyOp(op2, opOut); op2.opcode = ''; } - } - else if (op1.opcode == '-') - { - if (!op2.opcode) - { + } else if (op1.opcode == '-') { + if (!op2.opcode) { op1.opcode = ''; - } - else - { - if (op1.chars <= op2.chars) - { + } else { + if (op1.chars <= op2.chars) { op2.chars -= op1.chars; op2.lines -= op1.lines; op1.opcode = ''; - if (!op2.chars) - { + if (!op2.chars) { op2.opcode = ''; } - } - else - { + } else { op1.chars -= op2.chars; op1.lines -= op2.lines; op2.opcode = ''; } } - } - else if (op2.opcode == '-') - { - Changeset.copyOp(op2, opOut); - if (!op1.opcode) - { + } else if (op2.opcode == '-') { + exports.copyOp(op2, opOut); + if (!op1.opcode) { op2.opcode = ''; - } - else if (op2.chars <= op1.chars) - { + } else if (op2.chars <= op1.chars) { // delete part or all of a keep op1.chars -= op2.chars; op1.lines -= op2.lines; op2.opcode = ''; - if (!op1.chars) - { + if (!op1.chars) { op1.opcode = ''; } - } - else - { + } else { // delete all of a keep, and keep going opOut.lines = op1.lines; opOut.chars = op1.chars; @@ -2409,36 +1885,26 @@ Changeset.follow = function(cs1, cs2, reverseInsertOrder, pool) op2.chars -= op1.chars; op1.opcode = ''; } - } - else if (!op1.opcode) - { - Changeset.copyOp(op2, opOut); + } else if (!op1.opcode) { + exports.copyOp(op2, opOut); op2.opcode = ''; - } - else if (!op2.opcode) - { - Changeset.copyOp(op1, opOut); + } else if (!op2.opcode) { + exports.copyOp(op1, opOut); op1.opcode = ''; - } - else - { + } else { // both keeps opOut.opcode = '='; - opOut.attribs = Changeset.followAttributes(op1.attribs, op2.attribs, pool); - if (op1.chars <= op2.chars) - { + opOut.attribs = exports.followAttributes(op1.attribs, op2.attribs, pool); + if (op1.chars <= op2.chars) { opOut.chars = op1.chars; opOut.lines = op1.lines; op2.chars -= op1.chars; op2.lines -= op1.lines; op1.opcode = ''; - if (!op2.chars) - { + if (!op2.chars) { op2.opcode = ''; } - } - else - { + } else { opOut.chars = op2.chars; opOut.lines = op2.lines; op1.chars -= op2.chars; @@ -2446,8 +1912,7 @@ Changeset.follow = function(cs1, cs2, reverseInsertOrder, pool) op2.opcode = ''; } } - switch (opOut.opcode) - { + switch (opOut.opcode) { case '=': oldPos += opOut.chars; newLen += opOut.chars; @@ -2462,11 +1927,10 @@ Changeset.follow = function(cs1, cs2, reverseInsertOrder, pool) }); newLen += oldLen - oldPos; - return Changeset.pack(oldLen, newLen, newOps, unpacked2.charBank); + return exports.pack(oldLen, newLen, newOps, unpacked2.charBank); }; -Changeset.followAttributes = function(att1, att2, pool) -{ +exports.followAttributes = function (att1, att2, pool) { // The merge of two sets of attribute changes to the same text // takes the lexically-earlier value if there are two values // for the same key. Otherwise, all key/value changes from @@ -2476,21 +1940,16 @@ Changeset.followAttributes = function(att1, att2, pool) if ((!att2) || (!pool)) return ''; if (!att1) return att2; var atts = []; - att2.replace(/\*([0-9a-z]+)/g, function(_, a) - { - atts.push(pool.getAttrib(Changeset.parseNum(a))); + att2.replace(/\*([0-9a-z]+)/g, function (_, a) { + atts.push(pool.getAttrib(exports.parseNum(a))); return ''; }); - att1.replace(/\*([0-9a-z]+)/g, function(_, a) - { - var pair1 = pool.getAttrib(Changeset.parseNum(a)); - for (var i = 0; i < atts.length; i++) - { + att1.replace(/\*([0-9a-z]+)/g, function (_, a) { + var pair1 = pool.getAttrib(exports.parseNum(a)); + for (var i = 0; i < atts.length; i++) { var pair2 = atts[i]; - if (pair1[0] == pair2[0]) - { - if (pair1[1] <= pair2[1]) - { + if (pair1[0] == pair2[0]) { + if (pair1[1] <= pair2[1]) { // winner of merge is pair1, delete this attribute atts.splice(i, 1); } @@ -2500,14 +1959,10 @@ Changeset.followAttributes = function(att1, att2, pool) return ''; }); // we've only removed attributes, so they're already sorted - var buf = Changeset.stringAssembler(); - for (var i = 0; i < atts.length; i++) - { + var buf = exports.stringAssembler(); + for (var i = 0; i < atts.length; i++) { buf.append('*'); - buf.append(Changeset.numToString(pool.putAttrib(atts[i]))); + buf.append(exports.numToString(pool.putAttrib(atts[i]))); } return buf.toString(); }; - -exports.Changeset = Changeset; -exports.AttribPool = AttribPool; diff --git a/static/js/ace2_common.js b/static/js/ace2_common.js index 1ce7810a..b4c72a92 100644 --- a/static/js/ace2_common.js +++ b/static/js/ace2_common.js @@ -20,6 +20,7 @@ * limitations under the License. */ +var Security = require('/security'); function isNodeText(node) { @@ -137,14 +138,7 @@ function binarySearchInfinite(expectedLength, func) function htmlPrettyEscape(str) { - return str.replace(/[&"<>]/g, function (c) { - return { - '&': '&', - '"': '"', - '<': '<', - '>': '>' - }[c] || c; - }).replace(/\r?\n/g, '\\n'); + return Security.escapeHTML(str).replace(/\r?\n/g, '\\n'); } exports.isNodeText = isNodeText; diff --git a/static/js/ace2_inner.js b/static/js/ace2_inner.js index 31d4dac9..d2113574 100644 --- a/static/js/ace2_inner.js +++ b/static/js/ace2_inner.js @@ -42,8 +42,8 @@ var colorutils = require('/colorutils').colorutils; var makeContentCollector = require('/contentcollector').makeContentCollector; var makeCSSManager = require('/cssmanager').makeCSSManager; var domline = require('/domline').domline; -var AttribPool = require('/easysync2').AttribPool; -var Changeset = require('/easysync2').Changeset; +var AttribPool = require('/AttributePoolFactory').createAttributePool; +var Changeset = require('/Changeset'); var linestylefilter = require('/linestylefilter').linestylefilter; var newSkipList = require('/skiplist').newSkipList; var undoModule = require('/undomodule').undoModule; diff --git a/static/js/broadcast.js b/static/js/broadcast.js index 020f47e7..4a7b0168 100644 --- a/static/js/broadcast.js +++ b/static/js/broadcast.js @@ -20,11 +20,11 @@ * limitations under the License. */ -var makeCSSManager = require('/cssmanager_client').makeCSSManager; -var domline = require('/domline_client').domline; -var Changeset = require('/easysync2_client').Changeset; -var AttribPool = require('/easysync2_client').AttribPool; -var linestylefilter = require('/linestylefilter_client').linestylefilter; +var makeCSSManager = require('/cssmanager').makeCSSManager; +var domline = require('/domline').domline; +var AttribPool = require('/AttributePoolFactory').createAttributePool; +var Changeset = require('/Changeset'); +var linestylefilter = require('/linestylefilter').linestylefilter; var colorutils = require('/colorutils').colorutils; // These parameters were global, now they are injected. A reference to the diff --git a/static/js/changesettracker.js b/static/js/changesettracker.js index 7b0fb3e4..e34dc107 100644 --- a/static/js/changesettracker.js +++ b/static/js/changesettracker.js @@ -20,8 +20,8 @@ * limitations under the License. */ -var Changeset = require('/easysync2').Changeset; -var AttribPool = require('/easysync2').AttribPool; +var AttribPool = require('/AttributePoolFactory').createAttributePool; +var Changeset = require('/Changeset'); function makeChangesetTracker(scheduler, apool, aceCallbacksProvider) { diff --git a/static/js/contentcollector.js b/static/js/contentcollector.js index 0437ccd7..96dc4b7d 100644 --- a/static/js/contentcollector.js +++ b/static/js/contentcollector.js @@ -25,7 +25,7 @@ var _MAX_LIST_LEVEL = 8; -var Changeset = require('/easysync2').Changeset +var Changeset = require('/Changeset'); var plugins = require('/plugins').plugins; function sanitizeUnicode(s) diff --git a/static/js/cssmanager_client.js b/static/js/cssmanager_client.js deleted file mode 100644 index 6d9d989e..00000000 --- a/static/js/cssmanager_client.js +++ /dev/null @@ -1,118 +0,0 @@ -/** - * This code is mostly from the old Etherpad. Please help us to comment this code. - * This helps other people to understand this code better and helps them to improve it. - * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED - */ - -// DO NOT EDIT THIS FILE, edit infrastructure/ace/www/cssmanager.js -/** - * Copyright 2009 Google Inc. - * - * 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. - */ - -function makeCSSManager(emptyStylesheetTitle) -{ - - function getSheetByTitle(title) - { - var allSheets = document.styleSheets; - for (var i = 0; i < allSheets.length; i++) - { - var s = allSheets[i]; - if (s.title == title) - { - return s; - } - } - return null; - } - -/*function getSheetTagByTitle(title) { - var allStyleTags = document.getElementsByTagName("style"); - for(var i=0;i<allStyleTags.length;i++) { - var t = allStyleTags[i]; - if (t.title == title) { - return t; - } - } - return null; - }*/ - - var browserSheet = getSheetByTitle(emptyStylesheetTitle); - //var browserTag = getSheetTagByTitle(emptyStylesheetTitle); - - function browserRules() - { - return (browserSheet.cssRules || browserSheet.rules); - } - - function browserDeleteRule(i) - { - if (browserSheet.deleteRule) browserSheet.deleteRule(i); - else browserSheet.removeRule(i); - } - - function browserInsertRule(i, selector) - { - if (browserSheet.insertRule) browserSheet.insertRule(selector + ' {}', i); - else browserSheet.addRule(selector, null, i); - } - var selectorList = []; - - function indexOfSelector(selector) - { - for (var i = 0; i < selectorList.length; i++) - { - if (selectorList[i] == selector) - { - return i; - } - } - return -1; - } - - function selectorStyle(selector) - { - var i = indexOfSelector(selector); - if (i < 0) - { - // add selector - browserInsertRule(0, selector); - selectorList.splice(0, 0, selector); - i = 0; - } - return browserRules().item(i).style; - } - - function removeSelectorStyle(selector) - { - var i = indexOfSelector(selector); - if (i >= 0) - { - browserDeleteRule(i); - selectorList.splice(i, 1); - } - } - - return { - selectorStyle: selectorStyle, - removeSelectorStyle: removeSelectorStyle, - info: function() - { - return selectorList.length + ":" + browserRules().length; - } - }; -} - -exports.makeCSSManager = makeCSSManager; diff --git a/static/js/domline.js b/static/js/domline.js index 8d8c2ea9..15528bf7 100644 --- a/static/js/domline.js +++ b/static/js/domline.js @@ -26,6 +26,7 @@ // requires: plugins // requires: undefined +var Security = require('/security'); var plugins = require('/plugins').plugins; var map = require('/ace2_common').map; @@ -103,17 +104,17 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument) if (listType) { listType = listType[1]; - start = start?'start="'+start[1]+'"':''; + start = start?'start="'+Security.escapeHTMLAttribute(start[1])+'"':''; if (listType) { if(listType.indexOf("number") < 0) { - preHtml = '<ul class="list-' + listType + '"><li>'; + preHtml = '<ul class="list-' + Security.escapeHTMLAttribute(listType) + '"><li>'; postHtml = '</li></ul>'; } else { - preHtml = '<ol '+start+' class="list-' + listType + '"><li>'; + preHtml = '<ol '+start+' class="list-' + Security.escapeHTMLAttribute(listType) + '"><li>'; postHtml = '</li></ol>'; } } @@ -168,7 +169,7 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument) { href = "http://"+href; } - extraOpenTags = extraOpenTags + '<a href="' + domline.escapeHTML(href) + '">'; + extraOpenTags = extraOpenTags + '<a href="' + Security.escapeHTMLAttribute(href) + '">'; extraCloseTags = '</a>' + extraCloseTags; } if (simpleTags) @@ -178,7 +179,7 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument) simpleTags.reverse(); extraCloseTags = '</' + simpleTags.join('></') + '>' + extraCloseTags; } - html.push('<span class="', cls || '', '">', extraOpenTags, perTextNodeProcess(domline.escapeHTML(txt)), extraCloseTags, '</span>'); + html.push('<span class="', Security.escapeHTMLAttribute(cls || ''), '">', extraOpenTags, perTextNodeProcess(Security.escapeHTML(txt)), extraCloseTags, '</span>'); } }; result.clearSpans = function() @@ -224,27 +225,6 @@ domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument) return result; }; -domline.escapeHTML = function(s) -{ - var re = /[&<>'"]/g; - /']/; // stupid indentation thing - if (!re.MAP) - { - // persisted across function calls! - re.MAP = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''' - }; - } - return s.replace(re, function(c) - { - return re.MAP[c]; - }); -}; - domline.processSpaces = function(s, doesWrap) { if (s.indexOf("<") < 0 && !doesWrap) diff --git a/static/js/domline_client.js b/static/js/domline_client.js deleted file mode 100644 index 87b6ed55..00000000 --- a/static/js/domline_client.js +++ /dev/null @@ -1,309 +0,0 @@ -/** - * This code is mostly from the old Etherpad. Please help us to comment this code. - * This helps other people to understand this code better and helps them to improve it. - * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED - */ - -// DO NOT EDIT THIS FILE, edit infrastructure/ace/www/domline.js -// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.domline -/** - * Copyright 2009 Google Inc. - * - * 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. - */ -// requires: top -// requires: plugins -// requires: undefined - -var plugins = require('/plugins').plugins; -var map = require('/ace2_common').map; - -var domline = {}; -domline.noop = function() -{}; -domline.identity = function(x) -{ - return x; -}; - -domline.addToLineClass = function(lineClass, cls) -{ - // an "empty span" at any point can be used to add classes to - // the line, using line:className. otherwise, we ignore - // the span. - cls.replace(/\S+/g, function(c) - { - if (c.indexOf("line:") == 0) - { - // add class to line - lineClass = (lineClass ? lineClass + ' ' : '') + c.substring(5); - } - }); - return lineClass; -} - -// if "document" is falsy we don't create a DOM node, just -// an object with innerHTML and className -domline.createDomLine = function(nonEmpty, doesWrap, optBrowser, optDocument) -{ - var result = { - node: null, - appendSpan: domline.noop, - prepareForAdd: domline.noop, - notifyAdded: domline.noop, - clearSpans: domline.noop, - finishUpdate: domline.noop, - lineMarker: 0 - }; - - var browser = (optBrowser || {}); - var document = optDocument; - - if (document) - { - result.node = document.createElement("div"); - } - else - { - result.node = { - innerHTML: '', - className: '' - }; - } - - var html = []; - var preHtml, postHtml; - var curHTML = null; - - function processSpaces(s) - { - return domline.processSpaces(s, doesWrap); - } - var identity = domline.identity; - var perTextNodeProcess = (doesWrap ? identity : processSpaces); - var perHtmlLineProcess = (doesWrap ? processSpaces : identity); - var lineClass = 'ace-line'; - result.appendSpan = function(txt, cls) - { - if (cls.indexOf('list') >= 0) - { - var listType = /(?:^| )list:(\S+)/.exec(cls); - var start = /(?:^| )start:(\S+)/.exec(cls); - if (listType) - { - listType = listType[1]; - start = start?'start="'+start[1]+'"':''; - if (listType) - { - if(listType.indexOf("number") < 0) - { - preHtml = '<ul class="list-' + listType + '"><li>'; - postHtml = '</li></ul>'; - } - else - { - preHtml = '<ol '+start+' class="list-' + listType + '"><li>'; - postHtml = '</li></ol>'; - } - } - result.lineMarker += txt.length; - return; // don't append any text - } - } - var href = null; - var simpleTags = null; - if (cls.indexOf('url') >= 0) - { - cls = cls.replace(/(^| )url:(\S+)/g, function(x0, space, url) - { - href = url; - return space + "url"; - }); - } - if (cls.indexOf('tag') >= 0) - { - cls = cls.replace(/(^| )tag:(\S+)/g, function(x0, space, tag) - { - if (!simpleTags) simpleTags = []; - simpleTags.push(tag.toLowerCase()); - return space + tag; - }); - } - - var extraOpenTags = ""; - var extraCloseTags = ""; - - var plugins_ = plugins; - - map(plugins_.callHook("aceCreateDomLine", { - domline: domline, - cls: cls - }), function(modifier) - { - cls = modifier.cls; - extraOpenTags = extraOpenTags + modifier.extraOpenTags; - extraCloseTags = modifier.extraCloseTags + extraCloseTags; - }); - - if ((!txt) && cls) - { - lineClass = domline.addToLineClass(lineClass, cls); - } - else if (txt) - { - if (href) - { - if(!~href.indexOf("http")) // if the url doesn't include http or https etc prefix it. - { - href = "http://"+href; - } - extraOpenTags = extraOpenTags + '<a href="' + href.replace(/\"/g, '"') + '">'; - extraCloseTags = '</a>' + extraCloseTags; - } - if (simpleTags) - { - simpleTags.sort(); - extraOpenTags = extraOpenTags + '<' + simpleTags.join('><') + '>'; - simpleTags.reverse(); - extraCloseTags = '</' + simpleTags.join('></') + '>' + extraCloseTags; - } - html.push('<span class="', cls || '', '">', extraOpenTags, perTextNodeProcess(domline.escapeHTML(txt)), extraCloseTags, '</span>'); - } - }; - result.clearSpans = function() - { - html = []; - lineClass = ''; // non-null to cause update - result.lineMarker = 0; - }; - - function writeHTML() - { - var newHTML = perHtmlLineProcess(html.join('')); - if (!newHTML) - { - if ((!document) || (!optBrowser)) - { - newHTML += ' '; - } - else if (!browser.msie) - { - newHTML += '<br/>'; - } - } - if (nonEmpty) - { - newHTML = (preHtml || '') + newHTML + (postHtml || ''); - } - html = preHtml = postHtml = null; // free memory - if (newHTML !== curHTML) - { - curHTML = newHTML; - result.node.innerHTML = curHTML; - } - if (lineClass !== null) result.node.className = lineClass; - } - result.prepareForAdd = writeHTML; - result.finishUpdate = writeHTML; - result.getInnerHTML = function() - { - return curHTML || ''; - }; - - return result; -}; - -domline.escapeHTML = function(s) -{ - var re = /[&<>'"]/g; - /']/; // stupid indentation thing - if (!re.MAP) - { - // persisted across function calls! - re.MAP = { - '&': '&', - '<': '<', - '>': '>', - '"': '"', - "'": ''' - }; - } - return s.replace(re, function(c) - { - return re.MAP[c]; - }); -}; - -domline.processSpaces = function(s, doesWrap) -{ - if (s.indexOf("<") < 0 && !doesWrap) - { - // short-cut - return s.replace(/ /g, ' '); - } - var parts = []; - s.replace(/<[^>]*>?| |[^ <]+/g, function(m) - { - parts.push(m); - }); - if (doesWrap) - { - var endOfLine = true; - var beforeSpace = false; - // last space in a run is normal, others are nbsp, - // end of line is nbsp - for (var i = parts.length - 1; i >= 0; i--) - { - var p = parts[i]; - if (p == " ") - { - if (endOfLine || beforeSpace) parts[i] = ' '; - endOfLine = false; - beforeSpace = true; - } - else if (p.charAt(0) != "<") - { - endOfLine = false; - beforeSpace = false; - } - } - // beginning of line is nbsp - for (var i = 0; i < parts.length; i++) - { - var p = parts[i]; - if (p == " ") - { - parts[i] = ' '; - break; - } - else if (p.charAt(0) != "<") - { - break; - } - } - } - else - { - for (var i = 0; i < parts.length; i++) - { - var p = parts[i]; - if (p == " ") - { - parts[i] = ' '; - } - } - } - return parts.join(''); -}; - -exports.domline = domline; diff --git a/static/js/easysync2_client.js b/static/js/easysync2_client.js deleted file mode 100644 index f4f3d08f..00000000 --- a/static/js/easysync2_client.js +++ /dev/null @@ -1,2274 +0,0 @@ -/** - * This code is mostly from the old Etherpad. Please help us to comment this code. - * This helps other people to understand this code better and helps them to improve it. - * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED - */ - -// DO NOT EDIT THIS FILE, edit infrastructure/ace/www/easysync2.js -// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.easysync2 -/** - * Copyright 2009 Google Inc. - * - * 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. - */ -//var _opt = (this.Easysync2Support || null); -var _opt = null; // disable optimization for now - -function AttribPool() -{ - var p = {}; - p.numToAttrib = {}; // e.g. {0: ['foo','bar']} - p.attribToNum = {}; // e.g. {'foo,bar': 0} - p.nextNum = 0; - - p.putAttrib = function(attrib, dontAddIfAbsent) - { - var str = String(attrib); - if (str in p.attribToNum) - { - return p.attribToNum[str]; - } - if (dontAddIfAbsent) - { - return -1; - } - var num = p.nextNum++; - p.attribToNum[str] = num; - p.numToAttrib[num] = [String(attrib[0] || ''), String(attrib[1] || '')]; - return num; - }; - - p.getAttrib = function(num) - { - var pair = p.numToAttrib[num]; - if (!pair) return pair; - return [pair[0], pair[1]]; // return a mutable copy - }; - - p.getAttribKey = function(num) - { - var pair = p.numToAttrib[num]; - if (!pair) return ''; - return pair[0]; - }; - - p.getAttribValue = function(num) - { - var pair = p.numToAttrib[num]; - if (!pair) return ''; - return pair[1]; - }; - - p.eachAttrib = function(func) - { - for (var n in p.numToAttrib) - { - var pair = p.numToAttrib[n]; - func(pair[0], pair[1]); - } - }; - - p.toJsonable = function() - { - return { - numToAttrib: p.numToAttrib, - nextNum: p.nextNum - }; - }; - - p.fromJsonable = function(obj) - { - p.numToAttrib = obj.numToAttrib; - p.nextNum = obj.nextNum; - p.attribToNum = {}; - for (var n in p.numToAttrib) - { - p.attribToNum[String(p.numToAttrib[n])] = Number(n); - } - return p; - }; - - return p; -} - -var Changeset = {}; - -Changeset.error = function error(msg) -{ - var e = new Error(msg); - e.easysync = true; - throw e; -}; -Changeset.assert = function assert(b, msgParts) -{ - if (!b) - { - var msg = Array.prototype.slice.call(arguments, 1).join(''); - Changeset.error("Changeset: " + msg); - } -}; - -Changeset.parseNum = function(str) -{ - return parseInt(str, 36); -}; -Changeset.numToString = function(num) -{ - return num.toString(36).toLowerCase(); -}; -Changeset.toBaseTen = function(cs) -{ - var dollarIndex = cs.indexOf('$'); - var beforeDollar = cs.substring(0, dollarIndex); - var fromDollar = cs.substring(dollarIndex); - return beforeDollar.replace(/[0-9a-z]+/g, function(s) - { - return String(Changeset.parseNum(s)); - }) + fromDollar; -}; - -Changeset.oldLen = function(cs) -{ - return Changeset.unpack(cs).oldLen; -}; -Changeset.newLen = function(cs) -{ - return Changeset.unpack(cs).newLen; -}; - -Changeset.opIterator = function(opsStr, optStartIndex) -{ - //print(opsStr); - var regex = /((?:\*[0-9a-z]+)*)(?:\|([0-9a-z]+))?([-+=])([0-9a-z]+)|\?|/g; - var startIndex = (optStartIndex || 0); - var curIndex = startIndex; - var prevIndex = curIndex; - - function nextRegexMatch() - { - prevIndex = curIndex; - var result; - if (_opt) - { - result = _opt.nextOpInString(opsStr, curIndex); - if (result) - { - if (result.opcode() == '?') - { - Changeset.error("Hit error opcode in op stream"); - } - curIndex = result.lastIndex(); - } - } - else - { - regex.lastIndex = curIndex; - result = regex.exec(opsStr); - curIndex = regex.lastIndex; - if (result[0] == '?') - { - Changeset.error("Hit error opcode in op stream"); - } - } - return result; - } - var regexResult = nextRegexMatch(); - var obj = Changeset.newOp(); - - function next(optObj) - { - var op = (optObj || obj); - if (_opt && regexResult) - { - op.attribs = regexResult.attribs(); - op.lines = regexResult.lines(); - op.chars = regexResult.chars(); - op.opcode = regexResult.opcode(); - regexResult = nextRegexMatch(); - } - else if ((!_opt) && regexResult[0]) - { - op.attribs = regexResult[1]; - op.lines = Changeset.parseNum(regexResult[2] || 0); - op.opcode = regexResult[3]; - op.chars = Changeset.parseNum(regexResult[4]); - regexResult = nextRegexMatch(); - } - else - { - Changeset.clearOp(op); - } - return op; - } - - function hasNext() - { - return !!(_opt ? regexResult : regexResult[0]); - } - - function lastIndex() - { - return prevIndex; - } - return { - next: next, - hasNext: hasNext, - lastIndex: lastIndex - }; -}; - -Changeset.clearOp = function(op) -{ - op.opcode = ''; - op.chars = 0; - op.lines = 0; - op.attribs = ''; -}; -Changeset.newOp = function(optOpcode) -{ - return { - opcode: (optOpcode || ''), - chars: 0, - lines: 0, - attribs: '' - }; -}; -Changeset.cloneOp = function(op) -{ - return { - opcode: op.opcode, - chars: op.chars, - lines: op.lines, - attribs: op.attribs - }; -}; -Changeset.copyOp = function(op1, op2) -{ - op2.opcode = op1.opcode; - op2.chars = op1.chars; - op2.lines = op1.lines; - op2.attribs = op1.attribs; -}; -Changeset.opString = function(op) -{ - // just for debugging - if (!op.opcode) return 'null'; - var assem = Changeset.opAssembler(); - assem.append(op); - return assem.toString(); -}; -Changeset.stringOp = function(str) -{ - // just for debugging - return Changeset.opIterator(str).next(); -}; - -Changeset.checkRep = function(cs) -{ - // doesn't check things that require access to attrib pool (e.g. attribute order) - // or original string (e.g. newline positions) - var unpacked = Changeset.unpack(cs); - var oldLen = unpacked.oldLen; - var newLen = unpacked.newLen; - var ops = unpacked.ops; - var charBank = unpacked.charBank; - - var assem = Changeset.smartOpAssembler(); - var oldPos = 0; - var calcNewLen = 0; - var numInserted = 0; - var iter = Changeset.opIterator(ops); - while (iter.hasNext()) - { - var o = iter.next(); - switch (o.opcode) - { - case '=': - oldPos += o.chars; - calcNewLen += o.chars; - break; - case '-': - oldPos += o.chars; - Changeset.assert(oldPos < oldLen, oldPos, " >= ", oldLen, " in ", cs); - break; - case '+': - { - calcNewLen += o.chars; - numInserted += o.chars; - Changeset.assert(calcNewLen < newLen, calcNewLen, " >= ", newLen, " in ", cs); - break; - } - } - assem.append(o); - } - - calcNewLen += oldLen - oldPos; - charBank = charBank.substring(0, numInserted); - while (charBank.length < numInserted) - { - charBank += "?"; - } - - assem.endDocument(); - var normalized = Changeset.pack(oldLen, calcNewLen, assem.toString(), charBank); - Changeset.assert(normalized == cs, normalized, ' != ', cs); - - return cs; -} - -Changeset.smartOpAssembler = function() -{ - // Like opAssembler but able to produce conforming changesets - // from slightly looser input, at the cost of speed. - // Specifically: - // - merges consecutive operations that can be merged - // - strips final "=" - // - ignores 0-length changes - // - reorders consecutive + and - (which margingOpAssembler doesn't do) - var minusAssem = Changeset.mergingOpAssembler(); - var plusAssem = Changeset.mergingOpAssembler(); - var keepAssem = Changeset.mergingOpAssembler(); - var assem = Changeset.stringAssembler(); - var lastOpcode = ''; - var lengthChange = 0; - - function flushKeeps() - { - assem.append(keepAssem.toString()); - keepAssem.clear(); - } - - function flushPlusMinus() - { - assem.append(minusAssem.toString()); - minusAssem.clear(); - assem.append(plusAssem.toString()); - plusAssem.clear(); - } - - function append(op) - { - if (!op.opcode) return; - if (!op.chars) return; - - if (op.opcode == '-') - { - if (lastOpcode == '=') - { - flushKeeps(); - } - minusAssem.append(op); - lengthChange -= op.chars; - } - else if (op.opcode == '+') - { - if (lastOpcode == '=') - { - flushKeeps(); - } - plusAssem.append(op); - lengthChange += op.chars; - } - else if (op.opcode == '=') - { - if (lastOpcode != '=') - { - flushPlusMinus(); - } - keepAssem.append(op); - } - lastOpcode = op.opcode; - } - - function appendOpWithText(opcode, text, attribs, pool) - { - var op = Changeset.newOp(opcode); - op.attribs = Changeset.makeAttribsString(opcode, attribs, pool); - var lastNewlinePos = text.lastIndexOf('\n'); - if (lastNewlinePos < 0) - { - op.chars = text.length; - op.lines = 0; - append(op); - } - else - { - op.chars = lastNewlinePos + 1; - op.lines = text.match(/\n/g).length; - append(op); - op.chars = text.length - (lastNewlinePos + 1); - op.lines = 0; - append(op); - } - } - - function toString() - { - flushPlusMinus(); - flushKeeps(); - return assem.toString(); - } - - function clear() - { - minusAssem.clear(); - plusAssem.clear(); - keepAssem.clear(); - assem.clear(); - lengthChange = 0; - } - - function endDocument() - { - keepAssem.endDocument(); - } - - function getLengthChange() - { - return lengthChange; - } - - return { - append: append, - toString: toString, - clear: clear, - endDocument: endDocument, - appendOpWithText: appendOpWithText, - getLengthChange: getLengthChange - }; -}; - -if (_opt) -{ - Changeset.mergingOpAssembler = function() - { - var assem = _opt.mergingOpAssembler(); - - function append(op) - { - assem.append(op.opcode, op.chars, op.lines, op.attribs); - } - - function toString() - { - return assem.toString(); - } - - function clear() - { - assem.clear(); - } - - function endDocument() - { - assem.endDocument(); - } - - return { - append: append, - toString: toString, - clear: clear, - endDocument: endDocument - }; - }; -} -else -{ - Changeset.mergingOpAssembler = function() - { - // This assembler can be used in production; it efficiently - // merges consecutive operations that are mergeable, ignores - // no-ops, and drops final pure "keeps". It does not re-order - // operations. - var assem = Changeset.opAssembler(); - var bufOp = Changeset.newOp(); - - // If we get, for example, insertions [xxx\n,yyy], those don't merge, - // but if we get [xxx\n,yyy,zzz\n], that merges to [xxx\nyyyzzz\n]. - // This variable stores the length of yyy and any other newline-less - // ops immediately after it. - var bufOpAdditionalCharsAfterNewline = 0; - - function flush(isEndDocument) - { - if (bufOp.opcode) - { - if (isEndDocument && bufOp.opcode == '=' && !bufOp.attribs) - { - // final merged keep, leave it implicit - } - else - { - assem.append(bufOp); - if (bufOpAdditionalCharsAfterNewline) - { - bufOp.chars = bufOpAdditionalCharsAfterNewline; - bufOp.lines = 0; - assem.append(bufOp); - bufOpAdditionalCharsAfterNewline = 0; - } - } - bufOp.opcode = ''; - } - } - - function append(op) - { - if (op.chars > 0) - { - if (bufOp.opcode == op.opcode && bufOp.attribs == op.attribs) - { - if (op.lines > 0) - { - // bufOp and additional chars are all mergeable into a multi-line op - bufOp.chars += bufOpAdditionalCharsAfterNewline + op.chars; - bufOp.lines += op.lines; - bufOpAdditionalCharsAfterNewline = 0; - } - else if (bufOp.lines == 0) - { - // both bufOp and op are in-line - bufOp.chars += op.chars; - } - else - { - // append in-line text to multi-line bufOp - bufOpAdditionalCharsAfterNewline += op.chars; - } - } - else - { - flush(); - Changeset.copyOp(op, bufOp); - } - } - } - - function endDocument() - { - flush(true); - } - - function toString() - { - flush(); - return assem.toString(); - } - - function clear() - { - assem.clear(); - Changeset.clearOp(bufOp); - } - return { - append: append, - toString: toString, - clear: clear, - endDocument: endDocument - }; - }; -} - -if (_opt) -{ - Changeset.opAssembler = function() - { - var assem = _opt.opAssembler(); - // this function allows op to be mutated later (doesn't keep a ref) - - function append(op) - { - assem.append(op.opcode, op.chars, op.lines, op.attribs); - } - - function toString() - { - return assem.toString(); - } - - function clear() - { - assem.clear(); - } - return { - append: append, - toString: toString, - clear: clear - }; - }; -} -else -{ - Changeset.opAssembler = function() - { - var pieces = []; - // this function allows op to be mutated later (doesn't keep a ref) - - function append(op) - { - pieces.push(op.attribs); - if (op.lines) - { - pieces.push('|', Changeset.numToString(op.lines)); - } - pieces.push(op.opcode); - pieces.push(Changeset.numToString(op.chars)); - } - - function toString() - { - return pieces.join(''); - } - - function clear() - { - pieces.length = 0; - } - return { - append: append, - toString: toString, - clear: clear - }; - }; -} - -Changeset.stringIterator = function(str) -{ - var curIndex = 0; - - function assertRemaining(n) - { - Changeset.assert(n <= remaining(), "!(", n, " <= ", remaining(), ")"); - } - - function take(n) - { - assertRemaining(n); - var s = str.substr(curIndex, n); - curIndex += n; - return s; - } - - function peek(n) - { - assertRemaining(n); - var s = str.substr(curIndex, n); - return s; - } - - function skip(n) - { - assertRemaining(n); - curIndex += n; - } - - function remaining() - { - return str.length - curIndex; - } - return { - take: take, - skip: skip, - remaining: remaining, - peek: peek - }; -}; - -Changeset.stringAssembler = function() -{ - var pieces = []; - - function append(x) - { - pieces.push(String(x)); - } - - function toString() - { - return pieces.join(''); - } - return { - append: append, - toString: toString - }; -}; - -// "lines" need not be an array as long as it supports certain calls (lines_foo inside). -Changeset.textLinesMutator = function(lines) -{ - // Mutates lines, an array of strings, in place. - // Mutation operations have the same constraints as changeset operations - // with respect to newlines, but not the other additional constraints - // (i.e. ins/del ordering, forbidden no-ops, non-mergeability, final newline). - // Can be used to mutate lists of strings where the last char of each string - // is not actually a newline, but for the purposes of N and L values, - // the caller should pretend it is, and for things to work right in that case, the input - // to insert() should be a single line with no newlines. - var curSplice = [0, 0]; - var inSplice = false; - // position in document after curSplice is applied: - var curLine = 0, - curCol = 0; - // invariant: if (inSplice) then (curLine is in curSplice[0] + curSplice.length - {2,3}) && - // curLine >= curSplice[0] - // invariant: if (inSplice && (curLine >= curSplice[0] + curSplice.length - 2)) then - // curCol == 0 - - function lines_applySplice(s) - { - lines.splice.apply(lines, s); - } - - function lines_toSource() - { - return lines.toSource(); - } - - function lines_get(idx) - { - if (lines.get) - { - return lines.get(idx); - } - else - { - return lines[idx]; - } - } - // can be unimplemented if removeLines's return value not needed - - function lines_slice(start, end) - { - if (lines.slice) - { - return lines.slice(start, end); - } - else - { - return []; - } - } - - function lines_length() - { - if ((typeof lines.length) == "number") - { - return lines.length; - } - else - { - return lines.length(); - } - } - - function enterSplice() - { - curSplice[0] = curLine; - curSplice[1] = 0; - if (curCol > 0) - { - putCurLineInSplice(); - } - inSplice = true; - } - - function leaveSplice() - { - lines_applySplice(curSplice); - curSplice.length = 2; - curSplice[0] = curSplice[1] = 0; - inSplice = false; - } - - function isCurLineInSplice() - { - return (curLine - curSplice[0] < (curSplice.length - 2)); - } - - function debugPrint(typ) - { - print(typ + ": " + curSplice.toSource() + " / " + curLine + "," + curCol + " / " + lines_toSource()); - } - - function putCurLineInSplice() - { - if (!isCurLineInSplice()) - { - curSplice.push(lines_get(curSplice[0] + curSplice[1])); - curSplice[1]++; - } - return 2 + curLine - curSplice[0]; - } - - function skipLines(L, includeInSplice) - { - if (L) - { - if (includeInSplice) - { - if (!inSplice) - { - enterSplice(); - } - for (var i = 0; i < L; i++) - { - curCol = 0; - putCurLineInSplice(); - curLine++; - } - } - else - { - if (inSplice) - { - if (L > 1) - { - leaveSplice(); - } - else - { - putCurLineInSplice(); - } - } - curLine += L; - curCol = 0; - } - //print(inSplice+" / "+isCurLineInSplice()+" / "+curSplice[0]+" / "+curSplice[1]+" / "+lines.length); -/*if (inSplice && (! isCurLineInSplice()) && (curSplice[0] + curSplice[1] < lines.length)) { - print("BLAH"); - putCurLineInSplice(); - }*/ - // tests case foo in remove(), which isn't otherwise covered in current impl - } - //debugPrint("skip"); - } - - function skip(N, L, includeInSplice) - { - if (N) - { - if (L) - { - skipLines(L, includeInSplice); - } - else - { - if (includeInSplice && !inSplice) - { - enterSplice(); - } - if (inSplice) - { - putCurLineInSplice(); - } - curCol += N; - //debugPrint("skip"); - } - } - } - - function removeLines(L) - { - var removed = ''; - if (L) - { - if (!inSplice) - { - enterSplice(); - } - - function nextKLinesText(k) - { - var m = curSplice[0] + curSplice[1]; - return lines_slice(m, m + k).join(''); - } - if (isCurLineInSplice()) - { - //print(curCol); - if (curCol == 0) - { - removed = curSplice[curSplice.length - 1]; - // print("FOO"); // case foo - curSplice.length--; - removed += nextKLinesText(L - 1); - curSplice[1] += L - 1; - } - else - { - removed = nextKLinesText(L - 1); - curSplice[1] += L - 1; - var sline = curSplice.length - 1; - removed = curSplice[sline].substring(curCol) + removed; - curSplice[sline] = curSplice[sline].substring(0, curCol) + lines_get(curSplice[0] + curSplice[1]); - curSplice[1] += 1; - } - } - else - { - removed = nextKLinesText(L); - curSplice[1] += L; - } - //debugPrint("remove"); - } - return removed; - } - - function remove(N, L) - { - var removed = ''; - if (N) - { - if (L) - { - return removeLines(L); - } - else - { - if (!inSplice) - { - enterSplice(); - } - var sline = putCurLineInSplice(); - removed = curSplice[sline].substring(curCol, curCol + N); - curSplice[sline] = curSplice[sline].substring(0, curCol) + curSplice[sline].substring(curCol + N); - //debugPrint("remove"); - } - } - return removed; - } - - function insert(text, L) - { - if (text) - { - if (!inSplice) - { - enterSplice(); - } - if (L) - { - var newLines = Changeset.splitTextLines(text); - if (isCurLineInSplice()) - { - //if (curCol == 0) { - //curSplice.length--; - //curSplice[1]--; - //Array.prototype.push.apply(curSplice, newLines); - //curLine += newLines.length; - //} - //else { - var sline = curSplice.length - 1; - var theLine = curSplice[sline]; - var lineCol = curCol; - curSplice[sline] = theLine.substring(0, lineCol) + newLines[0]; - curLine++; - newLines.splice(0, 1); - Array.prototype.push.apply(curSplice, newLines); - curLine += newLines.length; - curSplice.push(theLine.substring(lineCol)); - curCol = 0; - //} - } - else - { - Array.prototype.push.apply(curSplice, newLines); - curLine += newLines.length; - } - } - else - { - var sline = putCurLineInSplice(); - curSplice[sline] = curSplice[sline].substring(0, curCol) + text + curSplice[sline].substring(curCol); - curCol += text.length; - } - //debugPrint("insert"); - } - } - - function hasMore() - { - //print(lines.length+" / "+inSplice+" / "+(curSplice.length - 2)+" / "+curSplice[1]); - var docLines = lines_length(); - if (inSplice) - { - docLines += curSplice.length - 2 - curSplice[1]; - } - return curLine < docLines; - } - - function close() - { - if (inSplice) - { - leaveSplice(); - } - //debugPrint("close"); - } - - var self = { - skip: skip, - remove: remove, - insert: insert, - close: close, - hasMore: hasMore, - removeLines: removeLines, - skipLines: skipLines - }; - return self; -}; - -Changeset.applyZip = function(in1, idx1, in2, idx2, func) -{ - var iter1 = Changeset.opIterator(in1, idx1); - var iter2 = Changeset.opIterator(in2, idx2); - var assem = Changeset.smartOpAssembler(); - var op1 = Changeset.newOp(); - var op2 = Changeset.newOp(); - var opOut = Changeset.newOp(); - while (op1.opcode || iter1.hasNext() || op2.opcode || iter2.hasNext()) - { - if ((!op1.opcode) && iter1.hasNext()) iter1.next(op1); - if ((!op2.opcode) && iter2.hasNext()) iter2.next(op2); - func(op1, op2, opOut); - if (opOut.opcode) - { - //print(opOut.toSource()); - assem.append(opOut); - opOut.opcode = ''; - } - } - assem.endDocument(); - return assem.toString(); -}; - -Changeset.unpack = function(cs) -{ - var headerRegex = /Z:([0-9a-z]+)([><])([0-9a-z]+)|/; - var headerMatch = headerRegex.exec(cs); - if ((!headerMatch) || (!headerMatch[0])) - { - Changeset.error("Not a changeset: " + cs); - } - var oldLen = Changeset.parseNum(headerMatch[1]); - var changeSign = (headerMatch[2] == '>') ? 1 : -1; - var changeMag = Changeset.parseNum(headerMatch[3]); - var newLen = oldLen + changeSign * changeMag; - var opsStart = headerMatch[0].length; - var opsEnd = cs.indexOf("$"); - if (opsEnd < 0) opsEnd = cs.length; - return { - oldLen: oldLen, - newLen: newLen, - ops: cs.substring(opsStart, opsEnd), - charBank: cs.substring(opsEnd + 1) - }; -}; - -Changeset.pack = function(oldLen, newLen, opsStr, bank) -{ - var lenDiff = newLen - oldLen; - var lenDiffStr = (lenDiff >= 0 ? '>' + Changeset.numToString(lenDiff) : '<' + Changeset.numToString(-lenDiff)); - var a = []; - a.push('Z:', Changeset.numToString(oldLen), lenDiffStr, opsStr, '$', bank); - return a.join(''); -}; - -Changeset.applyToText = function(cs, str) -{ - var unpacked = Changeset.unpack(cs); - Changeset.assert(str.length == unpacked.oldLen, "mismatched apply: ", str.length, " / ", unpacked.oldLen); - var csIter = Changeset.opIterator(unpacked.ops); - var bankIter = Changeset.stringIterator(unpacked.charBank); - var strIter = Changeset.stringIterator(str); - var assem = Changeset.stringAssembler(); - while (csIter.hasNext()) - { - var op = csIter.next(); - switch (op.opcode) - { - case '+': - assem.append(bankIter.take(op.chars)); - break; - case '-': - strIter.skip(op.chars); - break; - case '=': - assem.append(strIter.take(op.chars)); - break; - } - } - assem.append(strIter.take(strIter.remaining())); - return assem.toString(); -}; - -Changeset.mutateTextLines = function(cs, lines) -{ - var unpacked = Changeset.unpack(cs); - var csIter = Changeset.opIterator(unpacked.ops); - var bankIter = Changeset.stringIterator(unpacked.charBank); - var mut = Changeset.textLinesMutator(lines); - while (csIter.hasNext()) - { - var op = csIter.next(); - switch (op.opcode) - { - case '+': - mut.insert(bankIter.take(op.chars), op.lines); - break; - case '-': - mut.remove(op.chars, op.lines); - break; - case '=': - mut.skip(op.chars, op.lines, ( !! op.attribs)); - break; - } - } - mut.close(); -}; - -Changeset.composeAttributes = function(att1, att2, resultIsMutation, pool) -{ - // att1 and att2 are strings like "*3*f*1c", asMutation is a boolean. - // Sometimes attribute (key,value) pairs are treated as attribute presence - // information, while other times they are treated as operations that - // mutate a set of attributes, and this affects whether an empty value - // is a deletion or a change. - // Examples, of the form (att1Items, att2Items, resultIsMutation) -> result - // ([], [(bold, )], true) -> [(bold, )] - // ([], [(bold, )], false) -> [] - // ([], [(bold, true)], true) -> [(bold, true)] - // ([], [(bold, true)], false) -> [(bold, true)] - // ([(bold, true)], [(bold, )], true) -> [(bold, )] - // ([(bold, true)], [(bold, )], false) -> [] - // pool can be null if att2 has no attributes. - if ((!att1) && resultIsMutation) - { - // In the case of a mutation (i.e. composing two changesets), - // an att2 composed with an empy att1 is just att2. If att1 - // is part of an attribution string, then att2 may remove - // attributes that are already gone, so don't do this optimization. - return att2; - } - if (!att2) return att1; - var atts = []; - att1.replace(/\*([0-9a-z]+)/g, function(_, a) - { - atts.push(pool.getAttrib(Changeset.parseNum(a))); - return ''; - }); - att2.replace(/\*([0-9a-z]+)/g, function(_, a) - { - var pair = pool.getAttrib(Changeset.parseNum(a)); - var found = false; - for (var i = 0; i < atts.length; i++) - { - var oldPair = atts[i]; - if (oldPair[0] == pair[0]) - { - if (pair[1] || resultIsMutation) - { - oldPair[1] = pair[1]; - } - else - { - atts.splice(i, 1); - } - found = true; - break; - } - } - if ((!found) && (pair[1] || resultIsMutation)) - { - atts.push(pair); - } - return ''; - }); - atts.sort(); - var buf = Changeset.stringAssembler(); - for (var i = 0; i < atts.length; i++) - { - buf.append('*'); - buf.append(Changeset.numToString(pool.putAttrib(atts[i]))); - } - //print(att1+" / "+att2+" / "+buf.toString()); - return buf.toString(); -}; - -Changeset._slicerZipperFunc = function(attOp, csOp, opOut, pool) -{ - // attOp is the op from the sequence that is being operated on, either an - // attribution string or the earlier of two changesets being composed. - // pool can be null if definitely not needed. - //print(csOp.toSource()+" "+attOp.toSource()+" "+opOut.toSource()); - if (attOp.opcode == '-') - { - Changeset.copyOp(attOp, opOut); - attOp.opcode = ''; - } - else if (!attOp.opcode) - { - Changeset.copyOp(csOp, opOut); - csOp.opcode = ''; - } - else - { - switch (csOp.opcode) - { - case '-': - { - if (csOp.chars <= attOp.chars) - { - // delete or delete part - if (attOp.opcode == '=') - { - opOut.opcode = '-'; - opOut.chars = csOp.chars; - opOut.lines = csOp.lines; - opOut.attribs = ''; - } - attOp.chars -= csOp.chars; - attOp.lines -= csOp.lines; - csOp.opcode = ''; - if (!attOp.chars) - { - attOp.opcode = ''; - } - } - else - { - // delete and keep going - if (attOp.opcode == '=') - { - opOut.opcode = '-'; - opOut.chars = attOp.chars; - opOut.lines = attOp.lines; - opOut.attribs = ''; - } - csOp.chars -= attOp.chars; - csOp.lines -= attOp.lines; - attOp.opcode = ''; - } - break; - } - case '+': - { - // insert - Changeset.copyOp(csOp, opOut); - csOp.opcode = ''; - break; - } - case '=': - { - if (csOp.chars <= attOp.chars) - { - // keep or keep part - opOut.opcode = attOp.opcode; - opOut.chars = csOp.chars; - opOut.lines = csOp.lines; - opOut.attribs = Changeset.composeAttributes(attOp.attribs, csOp.attribs, attOp.opcode == '=', pool); - csOp.opcode = ''; - attOp.chars -= csOp.chars; - attOp.lines -= csOp.lines; - if (!attOp.chars) - { - attOp.opcode = ''; - } - } - else - { - // keep and keep going - opOut.opcode = attOp.opcode; - opOut.chars = attOp.chars; - opOut.lines = attOp.lines; - opOut.attribs = Changeset.composeAttributes(attOp.attribs, csOp.attribs, attOp.opcode == '=', pool); - attOp.opcode = ''; - csOp.chars -= attOp.chars; - csOp.lines -= attOp.lines; - } - break; - } - case '': - { - Changeset.copyOp(attOp, opOut); - attOp.opcode = ''; - break; - } - } - } -}; - -Changeset.applyToAttribution = function(cs, astr, pool) -{ - var unpacked = Changeset.unpack(cs); - - return Changeset.applyZip(astr, 0, unpacked.ops, 0, function(op1, op2, opOut) - { - return Changeset._slicerZipperFunc(op1, op2, opOut, pool); - }); -}; - -/*Changeset.oneInsertedLineAtATimeOpIterator = function(opsStr, optStartIndex, charBank) { - var iter = Changeset.opIterator(opsStr, optStartIndex); - var bankIndex = 0; - -};*/ - -Changeset.mutateAttributionLines = function(cs, lines, pool) -{ - //dmesg(cs); - //dmesg(lines.toSource()+" ->"); - var unpacked = Changeset.unpack(cs); - var csIter = Changeset.opIterator(unpacked.ops); - var csBank = unpacked.charBank; - var csBankIndex = 0; - // treat the attribution lines as text lines, mutating a line at a time - var mut = Changeset.textLinesMutator(lines); - - var lineIter = null; - - function isNextMutOp() - { - return (lineIter && lineIter.hasNext()) || mut.hasMore(); - } - - function nextMutOp(destOp) - { - if ((!(lineIter && lineIter.hasNext())) && mut.hasMore()) - { - var line = mut.removeLines(1); - lineIter = Changeset.opIterator(line); - } - if (lineIter && lineIter.hasNext()) - { - lineIter.next(destOp); - } - else - { - destOp.opcode = ''; - } - } - var lineAssem = null; - - function outputMutOp(op) - { - //print("outputMutOp: "+op.toSource()); - if (!lineAssem) - { - lineAssem = Changeset.mergingOpAssembler(); - } - lineAssem.append(op); - if (op.lines > 0) - { - Changeset.assert(op.lines == 1, "Can't have op.lines of ", op.lines, " in attribution lines"); - // ship it to the mut - mut.insert(lineAssem.toString(), 1); - lineAssem = null; - } - } - - var csOp = Changeset.newOp(); - var attOp = Changeset.newOp(); - var opOut = Changeset.newOp(); - while (csOp.opcode || csIter.hasNext() || attOp.opcode || isNextMutOp()) - { - if ((!csOp.opcode) && csIter.hasNext()) - { - csIter.next(csOp); - } - //print(csOp.toSource()+" "+attOp.toSource()+" "+opOut.toSource()); - //print(csOp.opcode+"/"+csOp.lines+"/"+csOp.attribs+"/"+lineAssem+"/"+lineIter+"/"+(lineIter?lineIter.hasNext():null)); - //print("csOp: "+csOp.toSource()); - if ((!csOp.opcode) && (!attOp.opcode) && (!lineAssem) && (!(lineIter && lineIter.hasNext()))) - { - break; // done - } - else if (csOp.opcode == '=' && csOp.lines > 0 && (!csOp.attribs) && (!attOp.opcode) && (!lineAssem) && (!(lineIter && lineIter.hasNext()))) - { - // skip multiple lines; this is what makes small changes not order of the document size - mut.skipLines(csOp.lines); - //print("skipped: "+csOp.lines); - csOp.opcode = ''; - } - else if (csOp.opcode == '+') - { - if (csOp.lines > 1) - { - var firstLineLen = csBank.indexOf('\n', csBankIndex) + 1 - csBankIndex; - Changeset.copyOp(csOp, opOut); - csOp.chars -= firstLineLen; - csOp.lines--; - opOut.lines = 1; - opOut.chars = firstLineLen; - } - else - { - Changeset.copyOp(csOp, opOut); - csOp.opcode = ''; - } - outputMutOp(opOut); - csBankIndex += opOut.chars; - opOut.opcode = ''; - } - else - { - if ((!attOp.opcode) && isNextMutOp()) - { - nextMutOp(attOp); - } - //print("attOp: "+attOp.toSource()); - Changeset._slicerZipperFunc(attOp, csOp, opOut, pool); - if (opOut.opcode) - { - outputMutOp(opOut); - opOut.opcode = ''; - } - } - } - - Changeset.assert(!lineAssem, "line assembler not finished"); - mut.close(); - - //dmesg("-> "+lines.toSource()); -}; - -Changeset.joinAttributionLines = function(theAlines) -{ - var assem = Changeset.mergingOpAssembler(); - for (var i = 0; i < theAlines.length; i++) - { - var aline = theAlines[i]; - var iter = Changeset.opIterator(aline); - while (iter.hasNext()) - { - assem.append(iter.next()); - } - } - return assem.toString(); -}; - -Changeset.splitAttributionLines = function(attrOps, text) -{ - var iter = Changeset.opIterator(attrOps); - var assem = Changeset.mergingOpAssembler(); - var lines = []; - var pos = 0; - - function appendOp(op) - { - assem.append(op); - if (op.lines > 0) - { - lines.push(assem.toString()); - assem.clear(); - } - pos += op.chars; - } - - while (iter.hasNext()) - { - var op = iter.next(); - var numChars = op.chars; - var numLines = op.lines; - while (numLines > 1) - { - var newlineEnd = text.indexOf('\n', pos) + 1; - Changeset.assert(newlineEnd > 0, "newlineEnd <= 0 in splitAttributionLines"); - op.chars = newlineEnd - pos; - op.lines = 1; - appendOp(op); - numChars -= op.chars; - numLines -= op.lines; - } - if (numLines == 1) - { - op.chars = numChars; - op.lines = 1; - } - appendOp(op); - } - - return lines; -}; - -Changeset.splitTextLines = function(text) -{ - return text.match(/[^\n]*(?:\n|[^\n]$)/g); -}; - -Changeset.compose = function(cs1, cs2, pool) -{ - var unpacked1 = Changeset.unpack(cs1); - var unpacked2 = Changeset.unpack(cs2); - var len1 = unpacked1.oldLen; - var len2 = unpacked1.newLen; - Changeset.assert(len2 == unpacked2.oldLen, "mismatched composition"); - var len3 = unpacked2.newLen; - var bankIter1 = Changeset.stringIterator(unpacked1.charBank); - var bankIter2 = Changeset.stringIterator(unpacked2.charBank); - var bankAssem = Changeset.stringAssembler(); - - var newOps = Changeset.applyZip(unpacked1.ops, 0, unpacked2.ops, 0, function(op1, op2, opOut) - { - //var debugBuilder = Changeset.stringAssembler(); - //debugBuilder.append(Changeset.opString(op1)); - //debugBuilder.append(','); - //debugBuilder.append(Changeset.opString(op2)); - //debugBuilder.append(' / '); - var op1code = op1.opcode; - var op2code = op2.opcode; - if (op1code == '+' && op2code == '-') - { - bankIter1.skip(Math.min(op1.chars, op2.chars)); - } - Changeset._slicerZipperFunc(op1, op2, opOut, pool); - if (opOut.opcode == '+') - { - if (op2code == '+') - { - bankAssem.append(bankIter2.take(opOut.chars)); - } - else - { - bankAssem.append(bankIter1.take(opOut.chars)); - } - } - - //debugBuilder.append(Changeset.opString(op1)); - //debugBuilder.append(','); - //debugBuilder.append(Changeset.opString(op2)); - //debugBuilder.append(' -> '); - //debugBuilder.append(Changeset.opString(opOut)); - //print(debugBuilder.toString()); - }); - - return Changeset.pack(len1, len3, newOps, bankAssem.toString()); -}; - -Changeset.attributeTester = function(attribPair, pool) -{ - // returns a function that tests if a string of attributes - // (e.g. *3*4) contains a given attribute key,value that - // is already present in the pool. - if (!pool) - { - return never; - } - var attribNum = pool.putAttrib(attribPair, true); - if (attribNum < 0) - { - return never; - } - else - { - var re = new RegExp('\\*' + Changeset.numToString(attribNum) + '(?!\\w)'); - return function(attribs) - { - return re.test(attribs); - }; - } - - function never(attribs) - { - return false; - } -}; - -Changeset.identity = function(N) -{ - return Changeset.pack(N, N, "", ""); -}; - -Changeset.makeSplice = function(oldFullText, spliceStart, numRemoved, newText, optNewTextAPairs, pool) -{ - var oldLen = oldFullText.length; - - if (spliceStart >= oldLen) - { - spliceStart = oldLen - 1; - } - if (numRemoved > oldFullText.length - spliceStart - 1) - { - numRemoved = oldFullText.length - spliceStart - 1; - } - var oldText = oldFullText.substring(spliceStart, spliceStart + numRemoved); - var newLen = oldLen + newText.length - oldText.length; - - var assem = Changeset.smartOpAssembler(); - assem.appendOpWithText('=', oldFullText.substring(0, spliceStart)); - assem.appendOpWithText('-', oldText); - assem.appendOpWithText('+', newText, optNewTextAPairs, pool); - assem.endDocument(); - return Changeset.pack(oldLen, newLen, assem.toString(), newText); -}; - -Changeset.toSplices = function(cs) -{ - // get a list of splices, [startChar, endChar, newText] - var unpacked = Changeset.unpack(cs); - var splices = []; - - var oldPos = 0; - var iter = Changeset.opIterator(unpacked.ops); - var charIter = Changeset.stringIterator(unpacked.charBank); - var inSplice = false; - while (iter.hasNext()) - { - var op = iter.next(); - if (op.opcode == '=') - { - oldPos += op.chars; - inSplice = false; - } - else - { - if (!inSplice) - { - splices.push([oldPos, oldPos, ""]); - inSplice = true; - } - if (op.opcode == '-') - { - oldPos += op.chars; - splices[splices.length - 1][1] += op.chars; - } - else if (op.opcode == '+') - { - splices[splices.length - 1][2] += charIter.take(op.chars); - } - } - } - - return splices; -}; - -Changeset.characterRangeFollow = function(cs, startChar, endChar, insertionsAfter) -{ - var newStartChar = startChar; - var newEndChar = endChar; - var splices = Changeset.toSplices(cs); - var lengthChangeSoFar = 0; - for (var i = 0; i < splices.length; i++) - { - var splice = splices[i]; - var spliceStart = splice[0] + lengthChangeSoFar; - var spliceEnd = splice[1] + lengthChangeSoFar; - var newTextLength = splice[2].length; - var thisLengthChange = newTextLength - (spliceEnd - spliceStart); - - if (spliceStart <= newStartChar && spliceEnd >= newEndChar) - { - // splice fully replaces/deletes range - // (also case that handles insertion at a collapsed selection) - if (insertionsAfter) - { - newStartChar = newEndChar = spliceStart; - } - else - { - newStartChar = newEndChar = spliceStart + newTextLength; - } - } - else if (spliceEnd <= newStartChar) - { - // splice is before range - newStartChar += thisLengthChange; - newEndChar += thisLengthChange; - } - else if (spliceStart >= newEndChar) - { - // splice is after range - } - else if (spliceStart >= newStartChar && spliceEnd <= newEndChar) - { - // splice is inside range - newEndChar += thisLengthChange; - } - else if (spliceEnd < newEndChar) - { - // splice overlaps beginning of range - newStartChar = spliceStart + newTextLength; - newEndChar += thisLengthChange; - } - else - { - // splice overlaps end of range - newEndChar = spliceStart; - } - - lengthChangeSoFar += thisLengthChange; - } - - return [newStartChar, newEndChar]; -}; - -Changeset.moveOpsToNewPool = function(cs, oldPool, newPool) -{ - // works on changeset or attribution string - var dollarPos = cs.indexOf('$'); - if (dollarPos < 0) - { - dollarPos = cs.length; - } - var upToDollar = cs.substring(0, dollarPos); - var fromDollar = cs.substring(dollarPos); - // order of attribs stays the same - return upToDollar.replace(/\*([0-9a-z]+)/g, function(_, a) - { - var oldNum = Changeset.parseNum(a); - var pair = oldPool.getAttrib(oldNum); - var newNum = newPool.putAttrib(pair); - return '*' + Changeset.numToString(newNum); - }) + fromDollar; -}; - -Changeset.makeAttribution = function(text) -{ - var assem = Changeset.smartOpAssembler(); - assem.appendOpWithText('+', text); - return assem.toString(); -}; - -// callable on a changeset, attribution string, or attribs property of an op -Changeset.eachAttribNumber = function(cs, func) -{ - var dollarPos = cs.indexOf('$'); - if (dollarPos < 0) - { - dollarPos = cs.length; - } - var upToDollar = cs.substring(0, dollarPos); - - upToDollar.replace(/\*([0-9a-z]+)/g, function(_, a) - { - func(Changeset.parseNum(a)); - return ''; - }); -}; - -// callable on a changeset, attribution string, or attribs property of an op, -// though it may easily create adjacent ops that can be merged. -Changeset.filterAttribNumbers = function(cs, filter) -{ - return Changeset.mapAttribNumbers(cs, filter); -}; - -Changeset.mapAttribNumbers = function(cs, func) -{ - var dollarPos = cs.indexOf('$'); - if (dollarPos < 0) - { - dollarPos = cs.length; - } - var upToDollar = cs.substring(0, dollarPos); - - var newUpToDollar = upToDollar.replace(/\*([0-9a-z]+)/g, function(s, a) - { - var n = func(Changeset.parseNum(a)); - if (n === true) - { - return s; - } - else if ((typeof n) === "number") - { - return '*' + Changeset.numToString(n); - } - else - { - return ''; - } - }); - - return newUpToDollar + cs.substring(dollarPos); -}; - -Changeset.makeAText = function(text, attribs) -{ - return { - text: text, - attribs: (attribs || Changeset.makeAttribution(text)) - }; -}; - -Changeset.applyToAText = function(cs, atext, pool) -{ - return { - text: Changeset.applyToText(cs, atext.text), - attribs: Changeset.applyToAttribution(cs, atext.attribs, pool) - }; -}; - -Changeset.cloneAText = function(atext) -{ - return { - text: atext.text, - attribs: atext.attribs - }; -}; - -Changeset.copyAText = function(atext1, atext2) -{ - atext2.text = atext1.text; - atext2.attribs = atext1.attribs; -}; - -Changeset.appendATextToAssembler = function(atext, assem) -{ - // intentionally skips last newline char of atext - var iter = Changeset.opIterator(atext.attribs); - var op = Changeset.newOp(); - while (iter.hasNext()) - { - iter.next(op); - if (!iter.hasNext()) - { - // last op, exclude final newline - if (op.lines <= 1) - { - op.lines = 0; - op.chars--; - if (op.chars) - { - assem.append(op); - } - } - else - { - var nextToLastNewlineEnd = atext.text.lastIndexOf('\n', atext.text.length - 2) + 1; - var lastLineLength = atext.text.length - nextToLastNewlineEnd - 1; - op.lines--; - op.chars -= (lastLineLength + 1); - assem.append(op); - op.lines = 0; - op.chars = lastLineLength; - if (op.chars) - { - assem.append(op); - } - } - } - else - { - assem.append(op); - } - } -}; - -Changeset.prepareForWire = function(cs, pool) -{ - var newPool = new AttribPool(); - var newCs = Changeset.moveOpsToNewPool(cs, pool, newPool); - return { - translated: newCs, - pool: newPool - }; -}; - -Changeset.isIdentity = function(cs) -{ - var unpacked = Changeset.unpack(cs); - return unpacked.ops == "" && unpacked.oldLen == unpacked.newLen; -}; - -Changeset.opAttributeValue = function(op, key, pool) -{ - return Changeset.attribsAttributeValue(op.attribs, key, pool); -}; - -Changeset.attribsAttributeValue = function(attribs, key, pool) -{ - var value = ''; - if (attribs) - { - Changeset.eachAttribNumber(attribs, function(n) - { - if (pool.getAttribKey(n) == key) - { - value = pool.getAttribValue(n); - } - }); - } - return value; -}; - -Changeset.builder = function(oldLen) -{ - var assem = Changeset.smartOpAssembler(); - var o = Changeset.newOp(); - var charBank = Changeset.stringAssembler(); - - var self = { - // attribs are [[key1,value1],[key2,value2],...] or '*0*1...' (no pool needed in latter case) - keep: function(N, L, attribs, pool) - { - o.opcode = '='; - o.attribs = (attribs && Changeset.makeAttribsString('=', attribs, pool)) || ''; - o.chars = N; - o.lines = (L || 0); - assem.append(o); - return self; - }, - keepText: function(text, attribs, pool) - { - assem.appendOpWithText('=', text, attribs, pool); - return self; - }, - insert: function(text, attribs, pool) - { - assem.appendOpWithText('+', text, attribs, pool); - charBank.append(text); - return self; - }, - remove: function(N, L) - { - o.opcode = '-'; - o.attribs = ''; - o.chars = N; - o.lines = (L || 0); - assem.append(o); - return self; - }, - toString: function() - { - assem.endDocument(); - var newLen = oldLen + assem.getLengthChange(); - return Changeset.pack(oldLen, newLen, assem.toString(), charBank.toString()); - } - }; - - return self; -}; - -Changeset.makeAttribsString = function(opcode, attribs, pool) -{ - // makeAttribsString(opcode, '*3') or makeAttribsString(opcode, [['foo','bar']], myPool) work - if (!attribs) - { - return ''; - } - else if ((typeof attribs) == "string") - { - return attribs; - } - else if (pool && attribs && attribs.length) - { - if (attribs.length > 1) - { - attribs = attribs.slice(); - attribs.sort(); - } - var result = []; - for (var i = 0; i < attribs.length; i++) - { - var pair = attribs[i]; - if (opcode == '=' || (opcode == '+' && pair[1])) - { - result.push('*' + Changeset.numToString(pool.putAttrib(pair))); - } - } - return result.join(''); - } -}; - -// like "substring" but on a single-line attribution string -Changeset.subattribution = function(astr, start, optEnd) -{ - var iter = Changeset.opIterator(astr, 0); - var assem = Changeset.smartOpAssembler(); - var attOp = Changeset.newOp(); - var csOp = Changeset.newOp(); - var opOut = Changeset.newOp(); - - function doCsOp() - { - if (csOp.chars) - { - while (csOp.opcode && (attOp.opcode || iter.hasNext())) - { - if (!attOp.opcode) iter.next(attOp); - - if (csOp.opcode && attOp.opcode && csOp.chars >= attOp.chars && attOp.lines > 0 && csOp.lines <= 0) - { - csOp.lines++; - } - - Changeset._slicerZipperFunc(attOp, csOp, opOut, null); - if (opOut.opcode) - { - assem.append(opOut); - opOut.opcode = ''; - } - } - } - } - - csOp.opcode = '-'; - csOp.chars = start; - - doCsOp(); - - if (optEnd === undefined) - { - if (attOp.opcode) - { - assem.append(attOp); - } - while (iter.hasNext()) - { - iter.next(attOp); - assem.append(attOp); - } - } - else - { - csOp.opcode = '='; - csOp.chars = optEnd - start; - doCsOp(); - } - - return assem.toString(); -}; - -Changeset.inverse = function(cs, lines, alines, pool) -{ - // lines and alines are what the changeset is meant to apply to. - // They may be arrays or objects with .get(i) and .length methods. - // They include final newlines on lines. - - function lines_get(idx) - { - if (lines.get) - { - return lines.get(idx); - } - else - { - return lines[idx]; - } - } - - function lines_length() - { - if ((typeof lines.length) == "number") - { - return lines.length; - } - else - { - return lines.length(); - } - } - - function alines_get(idx) - { - if (alines.get) - { - return alines.get(idx); - } - else - { - return alines[idx]; - } - } - - function alines_length() - { - if ((typeof alines.length) == "number") - { - return alines.length; - } - else - { - return alines.length(); - } - } - - var curLine = 0; - var curChar = 0; - var curLineOpIter = null; - var curLineOpIterLine; - var curLineNextOp = Changeset.newOp('+'); - - var unpacked = Changeset.unpack(cs); - var csIter = Changeset.opIterator(unpacked.ops); - var builder = Changeset.builder(unpacked.newLen); - - function consumeAttribRuns(numChars, func /*(len, attribs, endsLine)*/ ) - { - - if ((!curLineOpIter) || (curLineOpIterLine != curLine)) - { - // create curLineOpIter and advance it to curChar - curLineOpIter = Changeset.opIterator(alines_get(curLine)); - curLineOpIterLine = curLine; - var indexIntoLine = 0; - var done = false; - while (!done) - { - curLineOpIter.next(curLineNextOp); - if (indexIntoLine + curLineNextOp.chars >= curChar) - { - curLineNextOp.chars -= (curChar - indexIntoLine); - done = true; - } - else - { - indexIntoLine += curLineNextOp.chars; - } - } - } - - while (numChars > 0) - { - if ((!curLineNextOp.chars) && (!curLineOpIter.hasNext())) - { - curLine++; - curChar = 0; - curLineOpIterLine = curLine; - curLineNextOp.chars = 0; - curLineOpIter = Changeset.opIterator(alines_get(curLine)); - } - if (!curLineNextOp.chars) - { - curLineOpIter.next(curLineNextOp); - } - var charsToUse = Math.min(numChars, curLineNextOp.chars); - func(charsToUse, curLineNextOp.attribs, charsToUse == curLineNextOp.chars && curLineNextOp.lines > 0); - numChars -= charsToUse; - curLineNextOp.chars -= charsToUse; - curChar += charsToUse; - } - - if ((!curLineNextOp.chars) && (!curLineOpIter.hasNext())) - { - curLine++; - curChar = 0; - } - } - - function skip(N, L) - { - if (L) - { - curLine += L; - curChar = 0; - } - else - { - if (curLineOpIter && curLineOpIterLine == curLine) - { - consumeAttribRuns(N, function() - {}); - } - else - { - curChar += N; - } - } - } - - function nextText(numChars) - { - var len = 0; - var assem = Changeset.stringAssembler(); - var firstString = lines_get(curLine).substring(curChar); - len += firstString.length; - assem.append(firstString); - - var lineNum = curLine + 1; - while (len < numChars) - { - var nextString = lines_get(lineNum); - len += nextString.length; - assem.append(nextString); - lineNum++; - } - - return assem.toString().substring(0, numChars); - } - - function cachedStrFunc(func) - { - var cache = {}; - return function(s) - { - if (!cache[s]) - { - cache[s] = func(s); - } - return cache[s]; - }; - } - - var attribKeys = []; - var attribValues = []; - while (csIter.hasNext()) - { - var csOp = csIter.next(); - if (csOp.opcode == '=') - { - if (csOp.attribs) - { - attribKeys.length = 0; - attribValues.length = 0; - Changeset.eachAttribNumber(csOp.attribs, function(n) - { - attribKeys.push(pool.getAttribKey(n)); - attribValues.push(pool.getAttribValue(n)); - }); - var undoBackToAttribs = cachedStrFunc(function(attribs) - { - var backAttribs = []; - for (var i = 0; i < attribKeys.length; i++) - { - var appliedKey = attribKeys[i]; - var appliedValue = attribValues[i]; - var oldValue = Changeset.attribsAttributeValue(attribs, appliedKey, pool); - if (appliedValue != oldValue) - { - backAttribs.push([appliedKey, oldValue]); - } - } - return Changeset.makeAttribsString('=', backAttribs, pool); - }); - consumeAttribRuns(csOp.chars, function(len, attribs, endsLine) - { - builder.keep(len, endsLine ? 1 : 0, undoBackToAttribs(attribs)); - }); - } - else - { - skip(csOp.chars, csOp.lines); - builder.keep(csOp.chars, csOp.lines); - } - } - else if (csOp.opcode == '+') - { - builder.remove(csOp.chars, csOp.lines); - } - else if (csOp.opcode == '-') - { - var textBank = nextText(csOp.chars); - var textBankIndex = 0; - consumeAttribRuns(csOp.chars, function(len, attribs, endsLine) - { - builder.insert(textBank.substr(textBankIndex, len), attribs); - textBankIndex += len; - }); - } - } - - return Changeset.checkRep(builder.toString()); -}; - -exports.Changeset = Changeset; -exports.AttribPool = AttribPool; diff --git a/static/js/linestylefilter.js b/static/js/linestylefilter.js index d0b5bc6e..4b455724 100644 --- a/static/js/linestylefilter.js +++ b/static/js/linestylefilter.js @@ -28,7 +28,7 @@ // requires: plugins // requires: undefined -var Changeset = require('/easysync2').Changeset +var Changeset = require('/Changeset'); var plugins = require('/plugins').plugins; var map = require('/ace2_common').map; diff --git a/static/js/linestylefilter_client.js b/static/js/linestylefilter_client.js deleted file mode 100644 index f057e21a..00000000 --- a/static/js/linestylefilter_client.js +++ /dev/null @@ -1,343 +0,0 @@ -/** - * This code is mostly from the old Etherpad. Please help us to comment this code. - * This helps other people to understand this code better and helps them to improve it. - * TL;DR COMMENTS ON THIS FILE ARE HIGHLY APPRECIATED - */ - -// DO NOT EDIT THIS FILE, edit infrastructure/ace/www/linestylefilter.js -// THIS FILE IS ALSO AN APPJET MODULE: etherpad.collab.ace.linestylefilter -/** - * Copyright 2009 Google Inc. - * - * 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. - */ -// requires: easysync2.Changeset -// requires: top -// requires: plugins -// requires: undefined - -var Changeset = require('/easysync2_client').Changeset -var plugins = require('/plugins').plugins; -var map = require('/ace2_common').map; - -var linestylefilter = {}; - -linestylefilter.ATTRIB_CLASSES = { - 'bold': 'tag:b', - 'italic': 'tag:i', - 'underline': 'tag:u', - 'strikethrough': 'tag:s' -}; - -linestylefilter.getAuthorClassName = function(author) -{ - return "author-" + author.replace(/[^a-y0-9]/g, function(c) - { - if (c == ".") return "-"; - return 'z' + c.charCodeAt(0) + 'z'; - }); -}; - -// lineLength is without newline; aline includes newline, -// but may be falsy if lineLength == 0 -linestylefilter.getLineStyleFilter = function(lineLength, aline, textAndClassFunc, apool) -{ - - var plugins_ = plugins; - - if (lineLength == 0) return textAndClassFunc; - - var nextAfterAuthorColors = textAndClassFunc; - - var authorColorFunc = (function() - { - var lineEnd = lineLength; - var curIndex = 0; - var extraClasses; - var leftInAuthor; - - function attribsToClasses(attribs) - { - var classes = ''; - Changeset.eachAttribNumber(attribs, function(n) - { - var key = apool.getAttribKey(n); - if (key) - { - var value = apool.getAttribValue(n); - if (value) - { - if (key == 'author') - { - classes += ' ' + linestylefilter.getAuthorClassName(value); - } - else if (key == 'list') - { - classes += ' list:' + value; - } - else if (key == 'start') - { - classes += ' start:' + value; - } - else if (linestylefilter.ATTRIB_CLASSES[key]) - { - classes += ' ' + linestylefilter.ATTRIB_CLASSES[key]; - } - else - { - classes += plugins_.callHookStr("aceAttribsToClasses", { - linestylefilter: linestylefilter, - key: key, - value: value - }, " ", " ", ""); - } - } - } - }); - return classes.substring(1); - } - - var attributionIter = Changeset.opIterator(aline); - var nextOp, nextOpClasses; - - function goNextOp() - { - nextOp = attributionIter.next(); - nextOpClasses = (nextOp.opcode && attribsToClasses(nextOp.attribs)); - } - goNextOp(); - - function nextClasses() - { - if (curIndex < lineEnd) - { - extraClasses = nextOpClasses; - leftInAuthor = nextOp.chars; - goNextOp(); - while (nextOp.opcode && nextOpClasses == extraClasses) - { - leftInAuthor += nextOp.chars; - goNextOp(); - } - } - } - nextClasses(); - - return function(txt, cls) - { - while (txt.length > 0) - { - if (leftInAuthor <= 0) - { - // prevent infinite loop if something funny's going on - return nextAfterAuthorColors(txt, cls); - } - var spanSize = txt.length; - if (spanSize > leftInAuthor) - { - spanSize = leftInAuthor; - } - var curTxt = txt.substring(0, spanSize); - txt = txt.substring(spanSize); - nextAfterAuthorColors(curTxt, (cls && cls + " ") + extraClasses); - curIndex += spanSize; - leftInAuthor -= spanSize; - if (leftInAuthor == 0) - { - nextClasses(); - } - } - }; - })(); - return authorColorFunc; -}; - -linestylefilter.getAtSignSplitterFilter = function(lineText, textAndClassFunc) -{ - var at = /@/g; - at.lastIndex = 0; - var splitPoints = null; - var execResult; - while ((execResult = at.exec(lineText))) - { - if (!splitPoints) - { - splitPoints = []; - } - splitPoints.push(execResult.index); - } - - if (!splitPoints) return textAndClassFunc; - - return linestylefilter.textAndClassFuncSplitter(textAndClassFunc, splitPoints); -}; - -linestylefilter.getRegexpFilter = function(regExp, tag) -{ - return function(lineText, textAndClassFunc) - { - regExp.lastIndex = 0; - var regExpMatchs = null; - var splitPoints = null; - var execResult; - while ((execResult = regExp.exec(lineText))) - { - if (!regExpMatchs) - { - regExpMatchs = []; - splitPoints = []; - } - var startIndex = execResult.index; - var regExpMatch = execResult[0]; - regExpMatchs.push([startIndex, regExpMatch]); - splitPoints.push(startIndex, startIndex + regExpMatch.length); - } - - if (!regExpMatchs) return textAndClassFunc; - - function regExpMatchForIndex(idx) - { - for (var k = 0; k < regExpMatchs.length; k++) - { - var u = regExpMatchs[k]; - if (idx >= u[0] && idx < u[0] + u[1].length) - { - return u[1]; - } - } - return false; - } - - var handleRegExpMatchsAfterSplit = (function() - { - var curIndex = 0; - return function(txt, cls) - { - var txtlen = txt.length; - var newCls = cls; - var regExpMatch = regExpMatchForIndex(curIndex); - if (regExpMatch) - { - newCls += " " + tag + ":" + regExpMatch; - } - textAndClassFunc(txt, newCls); - curIndex += txtlen; - }; - })(); - - return linestylefilter.textAndClassFuncSplitter(handleRegExpMatchsAfterSplit, splitPoints); - }; -}; - - -linestylefilter.REGEX_WORDCHAR = /[\u0030-\u0039\u0041-\u005A\u0061-\u007A\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u00FF\u0100-\u1FFF\u3040-\u9FFF\uF900-\uFDFF\uFE70-\uFEFE\uFF10-\uFF19\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFDC]/; -linestylefilter.REGEX_URLCHAR = new RegExp('(' + /[-:@a-zA-Z0-9_.,~%+\/\\?=&#;()$]/.source + '|' + linestylefilter.REGEX_WORDCHAR.source + ')'); -linestylefilter.REGEX_URL = new RegExp(/(?:(?:https?|s?ftp|ftps|file|smb|afp|nfs|(x-)?man|gopher|txmt):\/\/|mailto:|www\.)/.source + linestylefilter.REGEX_URLCHAR.source + '*(?![:.,;])' + linestylefilter.REGEX_URLCHAR.source, 'g'); -linestylefilter.getURLFilter = linestylefilter.getRegexpFilter( -linestylefilter.REGEX_URL, 'url'); - -linestylefilter.textAndClassFuncSplitter = function(func, splitPointsOpt) -{ - var nextPointIndex = 0; - var idx = 0; - - // don't split at 0 - while (splitPointsOpt && nextPointIndex < splitPointsOpt.length && splitPointsOpt[nextPointIndex] == 0) - { - nextPointIndex++; - } - - function spanHandler(txt, cls) - { - if ((!splitPointsOpt) || nextPointIndex >= splitPointsOpt.length) - { - func(txt, cls); - idx += txt.length; - } - else - { - var splitPoints = splitPointsOpt; - var pointLocInSpan = splitPoints[nextPointIndex] - idx; - var txtlen = txt.length; - if (pointLocInSpan >= txtlen) - { - func(txt, cls); - idx += txt.length; - if (pointLocInSpan == txtlen) - { - nextPointIndex++; - } - } - else - { - if (pointLocInSpan > 0) - { - func(txt.substring(0, pointLocInSpan), cls); - idx += pointLocInSpan; - } - nextPointIndex++; - // recurse - spanHandler(txt.substring(pointLocInSpan), cls); - } - } - } - return spanHandler; -}; - -linestylefilter.getFilterStack = function(lineText, textAndClassFunc, browser) -{ - var func = linestylefilter.getURLFilter(lineText, textAndClassFunc); - - var plugins_ = plugins; - - var hookFilters = plugins_.callHook("aceGetFilterStack", { - linestylefilter: linestylefilter, - browser: browser - }); - map(hookFilters, function(hookFilter) - { - func = hookFilter(lineText, func); - }); - - if (browser !== undefined && browser.msie) - { - // IE7+ will take an e-mail address like <foo@bar.com> and linkify it to foo@bar.com. - // We then normalize it back to text with no angle brackets. It's weird. So always - // break spans at an "at" sign. - func = linestylefilter.getAtSignSplitterFilter( - lineText, func); - } - return func; -}; - -// domLineObj is like that returned by domline.createDomLine -linestylefilter.populateDomLine = function(textLine, aline, apool, domLineObj) -{ - // remove final newline from text if any - var text = textLine; - if (text.slice(-1) == '\n') - { - text = text.substring(0, text.length - 1); - } - - function textAndClassFunc(tokenText, tokenClass) - { - domLineObj.appendSpan(tokenText, tokenClass); - } - - var func = linestylefilter.getFilterStack(text, textAndClassFunc); - func = linestylefilter.getLineStyleFilter(text.length, aline, func, apool); - func(text, ''); -}; - -exports.linestylefilter = linestylefilter; diff --git a/static/js/pad.js b/static/js/pad.js index 9dab9c61..eb480080 100644 --- a/static/js/pad.js +++ b/static/js/pad.js @@ -46,47 +46,9 @@ var padsavedrevs = require('/pad_savedrevs').padsavedrevs; var paduserlist = require('/pad_userlist').paduserlist; var padutils = require('/pad_utils').padutils; -function createCookie(name, value, days, path) -{ - if (days) - { - var date = new Date(); - date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); - var expires = "; expires=" + date.toGMTString(); - } - else var expires = ""; - - if(!path) - path = "/"; - - document.cookie = name + "=" + value + expires + "; path=" + path; -} - -function readCookie(name) -{ - var nameEQ = name + "="; - var ca = document.cookie.split(';'); - for (var i = 0; i < ca.length; i++) - { - var c = ca[i]; - while (c.charAt(0) == ' ') c = c.substring(1, c.length); - if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); - } - return null; -} - -function randomString() -{ - var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - var string_length = 20; - var randomstring = ''; - for (var i = 0; i < string_length; i++) - { - var rnum = Math.floor(Math.random() * chars.length); - randomstring += chars.substring(rnum, rnum + 1); - } - return "t." + randomstring; -} +var createCookie = require('/pad_utils').createCookie; +var readCookie = require('/pad_utils').readCookie; +var randomString = require('/pad_utils').randomString; function getParams() { @@ -210,7 +172,7 @@ function handshake() var token = readCookie("token"); if (token == null) { - token = randomString(); + token = "t." + randomString(); createCookie("token", token, 60); } diff --git a/static/js/pad_utils.js b/static/js/pad_utils.js index 071185a8..fb538211 100644 --- a/static/js/pad_utils.js +++ b/static/js/pad_utils.js @@ -20,17 +20,58 @@ * limitations under the License. */ +var Security = require('/security'); + +/** + * Generates a random String with the given length. Is needed to generate the Author, Group, readonly, session Ids + */ + +function randomString(len) +{ + var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; + var randomstring = ''; + len = len || 20 + for (var i = 0; i < len; i++) + { + var rnum = Math.floor(Math.random() * chars.length); + randomstring += chars.substring(rnum, rnum + 1); + } + return randomstring; +} + +function createCookie(name, value, days, path) +{ + if (days) + { + var date = new Date(); + date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000)); + var expires = "; expires=" + date.toGMTString(); + } + else var expires = ""; + + if(!path) + path = "/"; + + document.cookie = name + "=" + value + expires + "; path=" + path; +} + +function readCookie(name) +{ + var nameEQ = name + "="; + var ca = document.cookie.split(';'); + for (var i = 0; i < ca.length; i++) + { + var c = ca[i]; + while (c.charAt(0) == ' ') c = c.substring(1, c.length); + if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length, c.length); + } + return null; +} + var padutils = { escapeHtml: function(x) { - return String(x).replace(/[&"<>]/g, function (c) { - return { - '&': '&', - '"': '"', - '<': '<', - '>': '>' - }[c] || c; - }); + return Security.escapeHTML(String(x)); }, uniqueId: function() { @@ -159,7 +200,7 @@ var padutils = { { if (i > idx) { - pieces.push(padutils.escapeHtml(text.substring(idx, i))); + pieces.push(Security.escapeHTML(text.substring(idx, i))); idx = i; } } @@ -170,7 +211,7 @@ var padutils = { var startIndex = urls[j][0]; var href = urls[j][1]; advanceTo(startIndex); - pieces.push('<a ', (target ? 'target="' + target + '" ' : ''), 'href="', padutils.escapeHtml(href), '">'); + pieces.push('<a ', (target ? 'target="' + Security.escapeHTMLAttribute(target) + '" ' : ''), 'href="', Security.escapeHTMLAttribute(href), '">'); advanceTo(startIndex + href.length); pieces.push('</a>'); } @@ -481,4 +522,7 @@ padutils.setupGlobalExceptionHandler = setupGlobalExceptionHandler; padutils.binarySearch = require('/ace2_common').binarySearch; +exports.randomString = randomString; +exports.createCookie = createCookie; +exports.readCookie = readCookie; exports.padutils = padutils; diff --git a/static/js/security.js b/static/js/security.js new file mode 100644 index 00000000..6f42d051 --- /dev/null +++ b/static/js/security.js @@ -0,0 +1,54 @@ +/** + * Copyright 2009 Google Inc. + * + * 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. + */ + +var HTML_ENTITY_MAP = { + '&': '&' +, '<': '<' +, '>': '>' +, '"': '"' +, "'": ''' +, '/': '/' +}; + +// OSWASP Guidlines: &, <, >, ", ' plus forward slash. +var HTML_CHARACTERS_EXPRESSION = /[&"'<>\/]/g; +function escapeHTML(text) { + return text && text.replace(HTML_CHARACTERS_EXPRESSION, function (c) { + return HTML_ENTITY_MAP[c] || c; + }); +} + +// OSWASP Guidlines: escape all non alphanumeric characters in ASCII space. +var HTML_ATTRIBUTE_CHARACTERS_EXPRESSION = + /[\x00-\x2F\x3A-\x40\5B-\x60\x7B-\xFF]/g; +function escapeHTMLAttribute(text) { + return text && text.replace(HTML_ATTRIBUTE_CHARACTERS_EXPRESSION, function (c) { + return "&#x" + ('00' + c.charCodeAt(0).toString(16)).slice(-2) + ";"; + }); +}; + +// OSWASP Guidlines: escape all non alphanumeric characters in ASCII space. +var JAVASCRIPT_CHARACTERS_EXPRESSION = + /[\x00-\x2F\x3A-\x40\5B-\x60\x7B-\xFF]/g; +function escapeJavaScriptData(text) { + return text && text.replace(JAVASCRIPT_CHARACTERS_EXPRESSION, function (c) { + return "\\x" + ('00' + c.charCodeAt(0).toString(16)).slice(-2); + }); +} + +exports.escapeHTML = escapeHTML; +exports.escapeHTMLAttribute = escapeHTMLAttribute; +exports.escapeJavaScriptData = escapeJavaScriptData; diff --git a/static/js/timeslider.js b/static/js/timeslider.js index d2fce8fd..143ef328 100644 --- a/static/js/timeslider.js +++ b/static/js/timeslider.js @@ -26,39 +26,9 @@ require('/jquery'); JSON = require('/json2'); require('/undo-xpopup'); -function createCookie(name,value,days) -{ - if (days) { - var date = new Date(); - date.setTime(date.getTime()+(days*24*60*60*1000)); - var expires = "; expires="+date.toGMTString(); - } - else var expires = ""; - document.cookie = name+"="+value+expires+"; path=/"; -} - -function readCookie(name) -{ - var nameEQ = name + "="; - var ca = document.cookie.split(';'); - for(var i=0;i < ca.length;i++) { - var c = ca[i]; - while (c.charAt(0)==' ') c = c.substring(1,c.length); - if (c.indexOf(nameEQ) == 0) return c.substring(nameEQ.length,c.length); - } - return null; -} - -function randomString() { - var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; - var string_length = 20; - var randomstring = ''; - for (var i=0; i<string_length; i++) { - var rnum = Math.floor(Math.random() * chars.length); - randomstring += chars.substring(rnum,rnum+1); - } - return "t." + randomstring; -} +var createCookie = require('/pad_utils').createCookie; +var readCookie = require('/pad_utils').readCookie; +var randomString = require('/pad_utils').randomString; var socket, token, padId, export_links; @@ -79,7 +49,7 @@ function init() { token = readCookie("token"); if(token == null) { - token = randomString(); + token = "t." + randomString(); createCookie("token", token, 60); } diff --git a/static/js/undomodule.js b/static/js/undomodule.js index aff41a70..886cbdf0 100644 --- a/static/js/undomodule.js +++ b/static/js/undomodule.js @@ -20,7 +20,7 @@ * limitations under the License. */ -var Changeset = require('/easysync2').Changeset; +var Changeset = require('/Changeset'); var extend = require('/ace2_common').extend; var undoModule = (function() |