| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451 |
- import { generateSHA1Hash } from '../../shared/utils/sha1'
- import { debugging, debugConsole } from '@/utils/debugging'
- import getMeta from '@/utils/meta'
- import { postJSON } from '@/infrastructure/fetch-json'
- var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
- var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
- var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
- function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
- /* eslint-disable
- camelcase,
- max-len,
- no-class-assign,
- no-return-assign,
- no-undef,
- no-unused-vars,
- no-use-before-define,
- standard/object-curly-even-spacing,
- */
- // TODO: This file was created by bulk-decaffeinate.
- // Fix any style issues and re-enable lint.
- /*
- * decaffeinate suggestions:
- * DS101: Remove unnecessary use of Array.from
- * DS102: Remove unnecessary code created because of implicit returns
- * DS205: Consider reworking code to avoid use of IIFEs
- * DS207: Consider shorter variations of null checks
- * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
- */
- export const { Doc } = (() => {
- var append = void 0,
- bootstrapTransform = void 0,
- exports = void 0,
- transformComponent = void 0;
- var WEB = true;
- window.sharejs = exports = {};
- var types = exports.types = {};
- // These methods let you build a transform function from a transformComponent function
- // for OT types like text and JSON in which operations are lists of components
- // and transforming them requires N^2 work.
- // Add transform and transformX functions for an OT type which has transformComponent defined.
- // transformComponent(destination array, component, other component, side)
- exports['_bt'] = bootstrapTransform = function bootstrapTransform(type, transformComponent, checkValidOp, append) {
- var _transformX = void 0;
- var transformComponentX = function transformComponentX(left, right, destLeft, destRight) {
- transformComponent(destLeft, left, right, 'left');
- return transformComponent(destRight, right, left, 'right');
- };
- // Transforms rightOp by leftOp. Returns ['rightOp', clientOp']
- type.transformX = type['transformX'] = _transformX = function transformX(leftOp, rightOp) {
- checkValidOp(leftOp);
- checkValidOp(rightOp);
- var newRightOp = [];
- var _iteratorNormalCompletion = true;
- var _didIteratorError = false;
- var _iteratorError = undefined;
- try {
- for (var _iterator = Array.from(rightOp)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
- var rightComponent = _step.value;
- // Generate newLeftOp by composing leftOp by rightComponent
- var newLeftOp = [];
- var k = 0;
- while (k < leftOp.length) {
- var l;
- var nextC = [];
- transformComponentX(leftOp[k], rightComponent, newLeftOp, nextC);
- k++;
- if (nextC.length === 1) {
- rightComponent = nextC[0];
- } else if (nextC.length === 0) {
- var _iteratorNormalCompletion2 = true;
- var _didIteratorError2 = false;
- var _iteratorError2 = undefined;
- try {
- for (var _iterator2 = Array.from(leftOp.slice(k))[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
- l = _step2.value;
- append(newLeftOp, l);
- }
- } catch (err) {
- _didIteratorError2 = true;
- _iteratorError2 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion2 && _iterator2.return) {
- _iterator2.return();
- }
- } finally {
- if (_didIteratorError2) {
- throw _iteratorError2;
- }
- }
- }
- rightComponent = null;
- break;
- } else {
- // Recurse.
- var _Array$from = Array.from(_transformX(leftOp.slice(k), nextC)),
- _Array$from2 = _slicedToArray(_Array$from, 2),
- l_ = _Array$from2[0],
- r_ = _Array$from2[1];
- var _iteratorNormalCompletion3 = true;
- var _didIteratorError3 = false;
- var _iteratorError3 = undefined;
- try {
- for (var _iterator3 = Array.from(l_)[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
- l = _step3.value;
- append(newLeftOp, l);
- }
- } catch (err) {
- _didIteratorError3 = true;
- _iteratorError3 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion3 && _iterator3.return) {
- _iterator3.return();
- }
- } finally {
- if (_didIteratorError3) {
- throw _iteratorError3;
- }
- }
- }
- var _iteratorNormalCompletion4 = true;
- var _didIteratorError4 = false;
- var _iteratorError4 = undefined;
- try {
- for (var _iterator4 = Array.from(r_)[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
- var r = _step4.value;
- append(newRightOp, r);
- }
- } catch (err) {
- _didIteratorError4 = true;
- _iteratorError4 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion4 && _iterator4.return) {
- _iterator4.return();
- }
- } finally {
- if (_didIteratorError4) {
- throw _iteratorError4;
- }
- }
- }
- rightComponent = null;
- break;
- }
- }
- if (rightComponent != null) {
- append(newRightOp, rightComponent);
- }
- leftOp = newLeftOp;
- }
- } catch (err) {
- _didIteratorError = true;
- _iteratorError = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion && _iterator.return) {
- _iterator.return();
- }
- } finally {
- if (_didIteratorError) {
- throw _iteratorError;
- }
- }
- }
- return [leftOp, newRightOp];
- };
- // Transforms op with specified type ('left' or 'right') by otherOp.
- return type.transform = type['transform'] = function (op, otherOp, type) {
- var _ = void 0;
- if (type !== 'left' && type !== 'right') {
- throw new Error("type must be 'left' or 'right'");
- }
- if (otherOp.length === 0) {
- return op;
- }
- // TODO: Benchmark with and without this line. I _think_ it'll make a big difference...?
- if (op.length === 1 && otherOp.length === 1) {
- return transformComponent([], op[0], otherOp[0], type);
- }
- if (type === 'left') {
- var left = void 0;
- var _Array$from3 = Array.from(_transformX(op, otherOp));
- var _Array$from4 = _slicedToArray(_Array$from3, 2);
- left = _Array$from4[0];
- _ = _Array$from4[1];
- return left;
- } else {
- var right = void 0;
- var _Array$from5 = Array.from(_transformX(otherOp, op));
- var _Array$from6 = _slicedToArray(_Array$from5, 2);
- _ = _Array$from6[0];
- right = _Array$from6[1];
- return right;
- }
- };
- };
- // A simple text implementation
- //
- // Operations are lists of components.
- // Each component either inserts or deletes at a specified position in the document.
- //
- // Components are either:
- // {i:'str', p:100}: Insert 'str' at position 100 in the document
- // {d:'str', p:100}: Delete 'str' at position 100 in the document
- //
- // Components in an operation are executed sequentially, so the position of components
- // assumes previous components have already executed.
- //
- // Eg: This op:
- // [{i:'abc', p:0}]
- // is equivalent to this op:
- // [{i:'a', p:0}, {i:'b', p:1}, {i:'c', p:2}]
- // NOTE: The global scope here is shared with other sharejs files when built with closure.
- // Be careful what ends up in your namespace.
- var text = {};
- text.name = 'text';
- text.create = function () {
- return '';
- };
- var strInject = function strInject(s1, pos, s2) {
- return s1.slice(0, pos) + s2 + s1.slice(pos);
- };
- var checkValidComponent = function checkValidComponent(c) {
- if (typeof c.p !== 'number') {
- throw new Error('component missing position field');
- }
- var i_type = _typeof(c.i);
- var d_type = _typeof(c.d);
- var c_type = _typeof(c.c);
- if (!(i_type === 'string' ^ d_type === 'string' ^ c_type === 'string')) {
- throw new Error('component needs an i, d or c field');
- }
- if (!(c.p >= 0)) {
- throw new Error('position cannot be negative');
- }
- };
- var checkValidOp = function checkValidOp(op) {
- var _iteratorNormalCompletion5 = true;
- var _didIteratorError5 = false;
- var _iteratorError5 = undefined;
- try {
- for (var _iterator5 = Array.from(op)[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
- var c = _step5.value;
- checkValidComponent(c);
- }
- } catch (err) {
- _didIteratorError5 = true;
- _iteratorError5 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion5 && _iterator5.return) {
- _iterator5.return();
- }
- } finally {
- if (_didIteratorError5) {
- throw _iteratorError5;
- }
- }
- }
- return true;
- };
- text.apply = function (snapshot, op) {
- checkValidOp(op);
- var _iteratorNormalCompletion6 = true;
- var _didIteratorError6 = false;
- var _iteratorError6 = undefined;
- try {
- for (var _iterator6 = Array.from(op)[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
- var component = _step6.value;
- if (component.i != null) {
- snapshot = strInject(snapshot, component.p, component.i);
- } else if (component.d != null) {
- var deleted = snapshot.slice(component.p, component.p + component.d.length);
- if (component.d !== deleted) {
- throw new Error('Delete component \'' + component.d + '\' does not match deleted text \'' + deleted + '\'');
- }
- snapshot = snapshot.slice(0, component.p) + snapshot.slice(component.p + component.d.length);
- } else if (component.c != null) {
- var comment = snapshot.slice(component.p, component.p + component.c.length);
- if (component.c !== comment) {
- throw new Error('Comment component \'' + component.c + '\' does not match commented text \'' + comment + '\'');
- }
- } else {
- throw new Error('Unknown op type');
- }
- }
- } catch (err) {
- _didIteratorError6 = true;
- _iteratorError6 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion6 && _iterator6.return) {
- _iterator6.return();
- }
- } finally {
- if (_didIteratorError6) {
- throw _iteratorError6;
- }
- }
- }
- return snapshot;
- };
- var cloneAndModify = function cloneAndModify(op, modifications) {
- var v = void 0;
- var newOp = {};
- for (var k in op) {
- v = op[k];
- newOp[k] = v;
- }
- for (k in modifications) {
- v = modifications[k];
- newOp[k] = v;
- }
- return newOp;
- };
- // Exported for use by the random op generator.
- //
- // For simplicity, this version of append does not compress adjacent inserts and deletes of
- // the same text. It would be nice to change that at some stage.
- text._append = append = function append(newOp, c) {
- if (c.i === '' || c.d === '') {
- return;
- }
- if (newOp.length === 0) {
- return newOp.push(c);
- } else {
- var last = newOp[newOp.length - 1];
- // Compose the insert into the previous insert if possible
- if (last.i != null && c.i != null && last.p <= c.p && c.p <= last.p + last.i.length && last.u === c.u) {
- return newOp[newOp.length - 1] = cloneAndModify(last, { i: strInject(last.i, c.p - last.p, c.i) });
- } else if (last.d != null && c.d != null && c.p <= last.p && last.p <= c.p + c.d.length && last.u === c.u) {
- return newOp[newOp.length - 1] = cloneAndModify(last, { d: strInject(c.d, last.p - c.p, last.d), p: c.p });
- } else {
- return newOp.push(c);
- }
- }
- };
- text.compose = function (op1, op2) {
- checkValidOp(op1);
- checkValidOp(op2);
- var newOp = op1.slice();
- var _iteratorNormalCompletion7 = true;
- var _didIteratorError7 = false;
- var _iteratorError7 = undefined;
- try {
- for (var _iterator7 = Array.from(op2)[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
- var c = _step7.value;
- append(newOp, c);
- }
- } catch (err) {
- _didIteratorError7 = true;
- _iteratorError7 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion7 && _iterator7.return) {
- _iterator7.return();
- }
- } finally {
- if (_didIteratorError7) {
- throw _iteratorError7;
- }
- }
- }
- return newOp;
- };
- // Attempt to compress the op components together 'as much as possible'.
- // This implementation preserves order and preserves create/delete pairs.
- text.compress = function (op) {
- return text.compose([], op);
- };
- text.normalize = function (op) {
- var newOp = [];
- // Normalize should allow ops which are a single (unwrapped) component:
- // {i:'asdf', p:23}.
- // There's no good way to test if something is an array:
- // http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/
- // so this is probably the least bad solution.
- if (op.i != null || op.p != null) {
- op = [op];
- }
- var _iteratorNormalCompletion8 = true;
- var _didIteratorError8 = false;
- var _iteratorError8 = undefined;
- try {
- for (var _iterator8 = Array.from(op)[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) {
- var c = _step8.value;
- if (c.p == null) {
- c.p = 0;
- }
- append(newOp, c);
- }
- } catch (err) {
- _didIteratorError8 = true;
- _iteratorError8 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion8 && _iterator8.return) {
- _iterator8.return();
- }
- } finally {
- if (_didIteratorError8) {
- throw _iteratorError8;
- }
- }
- }
- return newOp;
- };
- // This helper method transforms a position by an op component.
- //
- // If c is an insert, insertAfter specifies whether the transform
- // is pushed after the insert (true) or before it (false).
- //
- // insertAfter is optional for deletes.
- var transformPosition = function transformPosition(pos, c, insertAfter) {
- if (c.i != null) {
- if (c.p < pos || c.p === pos && insertAfter) {
- return pos + c.i.length;
- } else {
- return pos;
- }
- } else if (c.d != null) {
- // I think this could also be written as: Math.min(c.p, Math.min(c.p - otherC.p, otherC.d.length))
- // but I think its harder to read that way, and it compiles using ternary operators anyway
- // so its no slower written like this.
- if (pos <= c.p) {
- return pos;
- } else if (pos <= c.p + c.d.length) {
- return c.p;
- } else {
- return pos - c.d.length;
- }
- } else if (c.c != null) {
- return pos;
- } else {
- throw new Error('unknown op type');
- }
- };
- // Helper method to transform a cursor position as a result of an op.
- //
- // Like transformPosition above, if c is an insert, insertAfter specifies whether the cursor position
- // is pushed after an insert (true) or before it (false).
- text.transformCursor = function (position, op, side) {
- var insertAfter = side === 'right';
- var _iteratorNormalCompletion9 = true;
- var _didIteratorError9 = false;
- var _iteratorError9 = undefined;
- try {
- for (var _iterator9 = Array.from(op)[Symbol.iterator](), _step9; !(_iteratorNormalCompletion9 = (_step9 = _iterator9.next()).done); _iteratorNormalCompletion9 = true) {
- var c = _step9.value;
- position = transformPosition(position, c, insertAfter);
- }
- } catch (err) {
- _didIteratorError9 = true;
- _iteratorError9 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion9 && _iterator9.return) {
- _iterator9.return();
- }
- } finally {
- if (_didIteratorError9) {
- throw _iteratorError9;
- }
- }
- }
- return position;
- };
- // Transform an op component by another op component. Asymmetric.
- // The result will be appended to destination.
- //
- // exported for use in JSON type
- text._tc = transformComponent = function transformComponent(dest, c, otherC, side) {
- var cIntersect = void 0,
- intersectEnd = void 0,
- intersectStart = void 0,
- newC = void 0,
- otherIntersect = void 0;
- checkValidOp([c]);
- checkValidOp([otherC]);
- if (c.i != null) {
- append(dest, cloneAndModify(c, { p: transformPosition(c.p, otherC, side === 'right') }));
- } else if (c.d != null) {
- // Delete
- if (otherC.i != null) {
- // delete vs insert
- var s = c.d;
- if (c.p < otherC.p) {
- append(dest, cloneAndModify(c, { d: s.slice(0, otherC.p - c.p) }));
- s = s.slice(otherC.p - c.p);
- }
- if (s !== '') {
- append(dest, cloneAndModify(c, { d: s, p: c.p + otherC.i.length }));
- }
- } else if (otherC.d != null) {
- // Delete vs delete
- if (c.p >= otherC.p + otherC.d.length) {
- append(dest, cloneAndModify(c, { p: c.p - otherC.d.length }));
- } else if (c.p + c.d.length <= otherC.p) {
- append(dest, c);
- } else {
- // They overlap somewhere.
- newC = cloneAndModify(c, { d: '' });
- if (c.p < otherC.p) {
- newC.d = c.d.slice(0, otherC.p - c.p);
- }
- if (c.p + c.d.length > otherC.p + otherC.d.length) {
- newC.d += c.d.slice(otherC.p + otherC.d.length - c.p);
- }
- // This is entirely optional - just for a check that the deleted
- // text in the two ops matches
- intersectStart = Math.max(c.p, otherC.p);
- intersectEnd = Math.min(c.p + c.d.length, otherC.p + otherC.d.length);
- cIntersect = c.d.slice(intersectStart - c.p, intersectEnd - c.p);
- otherIntersect = otherC.d.slice(intersectStart - otherC.p, intersectEnd - otherC.p);
- if (cIntersect !== otherIntersect) {
- throw new Error('Delete ops delete different text in the same region of the document');
- }
- if (newC.d !== '') {
- // This could be rewritten similarly to insert v delete, above.
- newC.p = transformPosition(newC.p, otherC);
- append(dest, newC);
- }
- }
- } else if (otherC.c != null) {
- append(dest, c);
- } else {
- throw new Error('unknown op type');
- }
- } else if (c.c != null) {
- // Comment
- if (otherC.i != null) {
- if (c.p < otherC.p && otherC.p < c.p + c.c.length) {
- var offset = otherC.p - c.p;
- var new_c = c.c.slice(0, +(offset - 1) + 1 || undefined) + otherC.i + c.c.slice(offset);
- append(dest, cloneAndModify(c, { c: new_c }));
- } else {
- append(dest, cloneAndModify(c, { p: transformPosition(c.p, otherC, true) }));
- }
- } else if (otherC.d != null) {
- if (c.p >= otherC.p + otherC.d.length) {
- append(dest, cloneAndModify(c, { p: c.p - otherC.d.length }));
- } else if (c.p + c.c.length <= otherC.p) {
- append(dest, c);
- } else {
- // Delete overlaps comment
- // They overlap somewhere.
- newC = cloneAndModify(c, { c: '' });
- if (c.p < otherC.p) {
- newC.c = c.c.slice(0, otherC.p - c.p);
- }
- if (c.p + c.c.length > otherC.p + otherC.d.length) {
- newC.c += c.c.slice(otherC.p + otherC.d.length - c.p);
- }
- // This is entirely optional - just for a check that the deleted
- // text in the two ops matches
- intersectStart = Math.max(c.p, otherC.p);
- intersectEnd = Math.min(c.p + c.c.length, otherC.p + otherC.d.length);
- cIntersect = c.c.slice(intersectStart - c.p, intersectEnd - c.p);
- otherIntersect = otherC.d.slice(intersectStart - otherC.p, intersectEnd - otherC.p);
- if (cIntersect !== otherIntersect) {
- throw new Error('Delete ops delete different text in the same region of the document');
- }
- newC.p = transformPosition(newC.p, otherC);
- append(dest, newC);
- }
- } else if (otherC.c != null) {
- append(dest, c);
- } else {
- throw new Error('unknown op type');
- }
- }
- return dest;
- };
- var invertComponent = function invertComponent(c) {
- if (c.i != null) {
- return { d: c.i, p: c.p };
- } else {
- return { i: c.d, p: c.p };
- }
- };
- // No need to use append for invert, because the components won't be able to
- // cancel with one another.
- text.invert = function (op) {
- return Array.from(op.slice().reverse()).map(function (c) {
- return invertComponent(c);
- });
- };
- if (WEB != null) {
- if (!exports.types) {
- exports.types = {};
- }
- // This is kind of awful - come up with a better way to hook this helper code up.
- bootstrapTransform(text, transformComponent, checkValidOp, append);
- // [] is used to prevent closure from renaming types.text
- exports.types.text = text;
- }
- // Text document API for text
- text.api = {
- otType: "sharejs-text-ot",
- provides: { text: true },
- // The number of characters in the string
- getLength: function getLength() {
- return this.snapshot.length;
- },
- // Get the text contents of a document
- getText: function getText() {
- return this.snapshot;
- },
- insert: function insert(pos, text, fromUndo, callback) {
- var op = { p: pos, i: text };
- if (fromUndo) {
- op.u = true;
- }
- op = [op];
- this.submitOp(op, callback);
- return op;
- },
- del: function del(pos, length, fromUndo, callback) {
- var op = { p: pos, d: this.snapshot.slice(pos, pos + length) };
- if (fromUndo) {
- op.u = true;
- }
- op = [op];
- this.submitOp(op, callback);
- return op;
- },
- _register: function _register() {
- return this.on('remoteop', function (op) {
- var _this = this;
- return function () {
- var result = [];
- var _iteratorNormalCompletion10 = true;
- var _didIteratorError10 = false;
- var _iteratorError10 = undefined;
- try {
- for (var _iterator10 = Array.from(op)[Symbol.iterator](), _step10; !(_iteratorNormalCompletion10 = (_step10 = _iterator10.next()).done); _iteratorNormalCompletion10 = true) {
- var component = _step10.value;
- if (component.i !== undefined) {
- result.push(_this.emit('insert', component.p, component.i));
- } else if (component.d !== undefined) {
- result.push(_this.emit('delete', component.p, component.d));
- } else {
- result.push(undefined);
- }
- }
- } catch (err) {
- _didIteratorError10 = true;
- _iteratorError10 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion10 && _iterator10.return) {
- _iterator10.return();
- }
- } finally {
- if (_didIteratorError10) {
- throw _iteratorError10;
- }
- }
- }
- return result;
- }();
- });
- }
- };
- // This is a simple port of microevent.js to Coffeescript. I've changed the
- // function names to be consistent with node.js EventEmitter.
- //
- // microevent.js is copyright Jerome Etienne, and licensed under the MIT license:
- // https://github.com/jeromeetienne/microevent.js
- var nextTick = WEB != null ? function (fn) {
- return setTimeout(fn, 0);
- } : process['nextTick'];
- var MicroEvent = function () {
- function MicroEvent() {
- _classCallCheck(this, MicroEvent);
- }
- _createClass(MicroEvent, [{
- key: 'on',
- value: function on(event, fct) {
- if (!this._events) {
- this._events = {};
- }
- if (!this._events[event]) {
- this._events[event] = [];
- }
- this._events[event].push(fct);
- return this;
- }
- }, {
- key: 'removeListener',
- value: function removeListener(event, fct) {
- var _this2 = this;
- if (!this._events) {
- this._events = {};
- }
- var listeners = this._events[event] || (this._events[event] = []);
- // Sadly, there's no IE8- support for indexOf.
- var i = 0;
- while (i < listeners.length) {
- if (listeners[i] === fct) {
- listeners[i] = undefined;
- }
- i++;
- }
- nextTick(function () {
- return _this2._events[event] = Array.from(_this2._events[event]).filter(function (x) {
- return x;
- });
- });
- return this;
- }
- }, {
- key: 'emit',
- value: function emit(event) {
- if (!(this._events != null ? this._events[event] : undefined)) {
- return this;
- }
- for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
- args[_key - 1] = arguments[_key];
- }
- var _iteratorNormalCompletion11 = true;
- var _didIteratorError11 = false;
- var _iteratorError11 = undefined;
- try {
- for (var _iterator11 = Array.from(this._events[event])[Symbol.iterator](), _step11; !(_iteratorNormalCompletion11 = (_step11 = _iterator11.next()).done); _iteratorNormalCompletion11 = true) {
- var fn = _step11.value;
- if (fn) {
- fn.apply(this, args);
- }
- }
- } catch (err) {
- _didIteratorError11 = true;
- _iteratorError11 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion11 && _iterator11.return) {
- _iterator11.return();
- }
- } finally {
- if (_didIteratorError11) {
- throw _iteratorError11;
- }
- }
- }
- return this;
- }
- }]);
- return MicroEvent;
- }();
- // mixin will delegate all MicroEvent.js function in the destination object
- MicroEvent.mixin = function (obj) {
- var proto = obj.prototype || obj;
- // Damn closure compiler :/
- proto.on = MicroEvent.prototype.on;
- proto.removeListener = MicroEvent.prototype.removeListener;
- proto.emit = MicroEvent.prototype.emit;
- return obj;
- };
- if (WEB == null) {
- module.exports = MicroEvent;
- }
- if (WEB != null) {
- exports.extendDoc = function (name, fn) {
- return Doc.prototype[name] = fn;
- };
- }
- // A Doc is a client's view on a sharejs document.
- //
- // Documents are created by calling Connection.open().
- //
- // Documents are event emitters - use doc.on(eventname, fn) to subscribe.
- //
- // Documents get mixed in with their type's API methods. So, you can .insert('foo', 0) into
- // a text document and stuff like that.
- //
- // Events:
- // - remoteop (op)
- // - changed (op)
- // - acknowledge (op)
- // - error
- // - open, closing, closed. 'closing' is not guaranteed to fire before closed.
- var Doc = function () {
- // connection is a Connection object.
- // name is the documents' docName.
- // data can optionally contain known document data, and initial open() call arguments:
- // {v[erson], snapshot={...}, type, create=true/false/undefined}
- // callback will be called once the document is first opened.
- function Doc(connection, name, openData) {
- _classCallCheck(this, Doc);
- // Any of these can be null / undefined at this stage.
- this.flush = this.flush.bind(this);
- this.setFlushDelay = this.setFlushDelay.bind(this);
- this.shout = this.shout.bind(this);
- this.connection = connection;
- this.name = name;
- if (!openData) {
- openData = {};
- }
- this.version = openData.v;
- this.lastServerActivity = performance.now()
- this.snapshot = openData.snaphot;
- if (openData.type) {
- this._setType(openData.type);
- }
- this.state = 'closed';
- this.autoOpen = false;
- // Has the document already been created?
- this._create = openData.create;
- // The op that is currently roundtripping to the server, or null.
- //
- // When the connection reconnects, the inflight op is resubmitted.
- this.inflightOp = null;
- this.inflightCallbacks = [];
- // The auth ids which the client has previously used to attempt to send inflightOp. This is
- // usually empty.
- this.inflightSubmittedIds = [];
- // All ops that are waiting for the server to acknowledge @inflightOp
- this.pendingOp = null;
- this.pendingCallbacks = [];
- }
- // Transform a server op by a client op, and vice versa.
- _createClass(Doc, [{
- key: '_xf',
- value: function _xf(client, server) {
- if (this.type.transformX) {
- return this.type.transformX(client, server);
- } else {
- var client_ = this.type.transform(client, server, 'left');
- var server_ = this.type.transform(server, client, 'right');
- return [client_, server_];
- }
- }
- }, {
- key: '_otApply',
- value: function _otApply(docOp, isRemote, msg) {
- var oldSnapshot = this.snapshot;
- this.snapshot = this.type.apply(this.snapshot, docOp);
- // Its important that these event handlers are called with oldSnapshot.
- // The reason is that the OT type APIs might need to access the snapshots to
- // determine information about the received op.
- this.emit('change', docOp, oldSnapshot, msg, isRemote);
- if (isRemote) {
- return this.emit('remoteop', docOp, oldSnapshot, msg);
- }
- }
- }, {
- key: '_connectionStateChanged',
- value: function _connectionStateChanged(state, data) {
- switch (state) {
- case 'disconnected':
- this.state = 'closed';
- // This is used by the server to make sure that when an op is resubmitted it
- // doesn't end up getting applied twice.
- if (this.inflightOp) {
- this.inflightSubmittedIds.push(this.connection.id);
- }
- this.emit('closed');
- break;
- case 'ok':
- // Might be able to do this when we're connecting... that would save a roundtrip.
- if (this.autoOpen) {
- this.open();
- }
- break;
- case 'stopped':
- if (typeof this._openCallback === 'function') {
- this._openCallback(data);
- }
- break;
- }
- return this.emit(state, data);
- }
- }, {
- key: '_setType',
- value: function _setType(type) {
- if (typeof type === 'string') {
- type = types[type];
- }
- if (!type || !type.compose) {
- throw new Error('Support for types without compose() is not implemented');
- }
- this.type = type;
- if (type.api) {
- for (var k in type.api) {
- var v = type.api[k];this[k] = v;
- }
- return typeof this._register === 'function' ? this._register() : undefined;
- } else {
- return this.provides = {};
- }
- }
- }, {
- key: '_onMessage',
- value: function _onMessage(msg) {
- // debugConsole.warn('s->c', msg)
- if (msg.open === true) {
- // The document has been successfully opened.
- this.state = 'open';
- this._create = false; // Don't try and create the document again next time open() is called.
- if (this.created == null) {
- this.created = !!msg.create;
- }
- if (msg.type) {
- this._setType(msg.type);
- }
- if (msg.create) {
- this.created = true;
- this.snapshot = this.type.create();
- } else {
- if (this.created !== true) {
- this.created = false;
- }
- if (msg.snapshot !== undefined) {
- this.snapshot = msg.snapshot;
- }
- }
- if (msg.v != null) {
- this.version = msg.v;
- }
- // Resend any previously queued operation.
- if (this.inflightOp) {
- var response = {
- doc: this.name,
- op: this.inflightOp,
- v: this.version
- };
- if (this.inflightSubmittedIds.length) {
- response.dupIfSource = this.inflightSubmittedIds;
- }
- this.connection.send(response);
- } else {
- this.flush();
- }
- this.emit('open');
- return typeof this._openCallback === 'function' ? this._openCallback(null) : undefined;
- } else if (msg.open === false) {
- // The document has either been closed, or an open request has failed.
- if (msg.error) {
- // An error occurred opening the document.
- debugConsole.error('Could not open document: ' + msg.error);
- this.emit('error', msg.error);
- if (typeof this._openCallback === 'function') {
- this._openCallback(msg.error);
- }
- }
- this.state = 'closed';
- this.emit('closed');
- if (typeof this._closeCallback === 'function') {
- this._closeCallback();
- }
- return this._closeCallback = null;
- } else if (msg.op === null && error === 'Op already submitted') {
- // Overleaf: note that this branch is never reached, as `error` is always undefined
- // We've tried to resend an op to the server, which has already been received successfully. Do nothing.
- // The op will be confirmed normally when we get the op itself was echoed back from the server
- // (handled below).
- } else if (msg.op === undefined && msg.v !== undefined || msg.op && Array.from(this.inflightSubmittedIds).includes(msg.meta.source)) {
- // Overleaf: avoid clearing inflightOp on repeated acknowledgement of operations on the same version
- if (!msg.error) {
- if (msg.op === undefined && msg.v !== undefined) {
- if (msg.v < this.version) {
- debugConsole.warn('Received an ack for an op with an outdated version.')
- return
- }
- } else {
- if (msg.v < this.version) {
- postJSON('/error/client', {
- body: {
- error: {
- message: 'out-of-order-self-op-ignored'
- },
- meta: { msg: { v: msg.v }, version: this.version }
- }
- })
- // return // TODO: enable this?
- }
- }
- }
- // Our inflight op has been acknowledged.
- var callback = void 0;
- var oldInflightOp = this.inflightOp;
- this.inflightOp = null;
- this.inflightSubmittedIds.length = 0;
- if (this.pendingOp === null) {
- // All ops are acked
- this.emit('saved');
- }
- var error = msg.error;
- if (error) {
- // The server has rejected an op from the client for some reason.
- // We'll send the error message to the user and roll back the change.
- //
- // If the server isn't going to allow edits anyway, we should probably
- // figure out some way to flag that (readonly:true in the open request?)
- if (this.type.invert) {
- var undo = this.type.invert(oldInflightOp);
- // Now we have to transform the undo operation by any server ops & pending ops
- if (this.pendingOp) {
- var _Array$from7 = Array.from(this._xf(this.pendingOp, undo));
- var _Array$from8 = _slicedToArray(_Array$from7, 2);
- this.pendingOp = _Array$from8[0];
- undo = _Array$from8[1];
- }
- // ... and apply it locally, reverting the changes.
- //
- // This call will also call @emit 'remoteop'. I'm still not 100% sure about this
- // functionality, because its really a local op. Basically, the problem is that
- // if the client's op is rejected by the server, the editor window should update
- // to reflect the undo.
- this._otApply(undo, true, msg);
- } else {
- this.emit('error', 'Op apply failed (' + error + ') and the op could not be reverted');
- }
- var _iteratorNormalCompletion12 = true;
- var _didIteratorError12 = false;
- var _iteratorError12 = undefined;
- try {
- for (var _iterator12 = Array.from(this.inflightCallbacks)[Symbol.iterator](), _step12; !(_iteratorNormalCompletion12 = (_step12 = _iterator12.next()).done); _iteratorNormalCompletion12 = true) {
- callback = _step12.value;
- callback(error);
- }
- } catch (err) {
- _didIteratorError12 = true;
- _iteratorError12 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion12 && _iterator12.return) {
- _iterator12.return();
- }
- } finally {
- if (_didIteratorError12) {
- throw _iteratorError12;
- }
- }
- }
- } else {
- // The op applied successfully.
- // We may get multiple acks of the same message if we retried it,
- // so its ok if we receive an ack for a version that we've already gone past.
- // If so, just ignore it
- if (msg.v < this.version) {
- return;
- }
- if (msg.v !== this.version) {
- throw new Error('Invalid version from server');
- }
- this.version++;
- this.lastServerActivity = performance.now()
- this.emit('acknowledge', oldInflightOp);
- var _iteratorNormalCompletion13 = true;
- var _didIteratorError13 = false;
- var _iteratorError13 = undefined;
- try {
- for (var _iterator13 = Array.from(this.inflightCallbacks)[Symbol.iterator](), _step13; !(_iteratorNormalCompletion13 = (_step13 = _iterator13.next()).done); _iteratorNormalCompletion13 = true) {
- callback = _step13.value;
- callback(null, oldInflightOp);
- }
- } catch (err) {
- _didIteratorError13 = true;
- _iteratorError13 = err;
- } finally {
- try {
- if (!_iteratorNormalCompletion13 && _iterator13.return) {
- _iterator13.return();
- }
- } finally {
- if (_didIteratorError13) {
- throw _iteratorError13;
- }
- }
- }
- }
- // Send the next op.
- return this.delayedFlush();
- } else if (msg.op) {
- // We got a new op from the server.
- // msg is {doc:, op:, v:}
- // There is a bug in socket.io (produced on firefox 3.6) which causes messages
- // to be duplicated sometimes.
- // We'll just silently drop subsequent messages.
- if (msg.v < this.version) {
- return;
- }
- if (msg.doc !== this.name) {
- return this.emit('error', 'Expected docName \'' + this.name + '\' but got ' + msg.doc);
- }
- if (msg.v !== this.version) {
- return this.emit('error', 'Expected version ' + this.version + ' but got ' + msg.v);
- }
- // p "if: #{i @inflightOp} pending: #{i @pendingOp} doc '#{@snapshot}' op: #{i msg.op}"
- var docOp = msg.op;
- if (this.inflightOp !== null) {
- var _Array$from9 = Array.from(this._xf(this.inflightOp, docOp));
- var _Array$from10 = _slicedToArray(_Array$from9, 2);
- this.inflightOp = _Array$from10[0];
- docOp = _Array$from10[1];
- }
- if (this.pendingOp !== null) {
- var _Array$from11 = Array.from(this._xf(this.pendingOp, docOp));
- var _Array$from12 = _slicedToArray(_Array$from11, 2);
- this.pendingOp = _Array$from12[0];
- docOp = _Array$from12[1];
- }
- this.version++;
- this.lastServerActivity = performance.now()
- // Finally, apply the op to @snapshot and trigger any event listeners
- return this._otApply(docOp, true, msg);
- } else if (msg.meta) {
- var _msg$meta = msg.meta,
- path = _msg$meta.path,
- value = _msg$meta.value;
- switch (path != null ? path[0] : undefined) {
- case 'shout':
- return this.emit('shout', value);
- default:
- return debugConsole.warn('Unhandled meta op:', msg);
- }
- } else {
- return debugConsole.warn('Unhandled document message:', msg);
- }
- }
- // Send ops to the server, if appropriate.
- //
- // Only one op can be in-flight at a time, so if an op is already on its way then
- // this method does nothing.
- }, {
- key: 'flush',
- value: function flush() {
- this.flushTimeout = null;
- // console.log "CALLED FLUSH"
- if (this.connection.state !== 'ok' || this.inflightOp !== null || this.pendingOp === null) {
- return;
- }
- // Rotate null -> pending -> inflight
- this.inflightOp = this.pendingOp;
- this.inflightCallbacks = this.pendingCallbacks;
- this.pendingOp = null;
- this.pendingCallbacks = [];
- this.emit('flipped_pending_to_inflight');
- if (getMeta('ol-useShareJsHash') || debugging) {
- var now = Date.now()
- var age = this.__lastSubmitTimestamp && (now - this.__lastSubmitTimestamp)
- var RECOMPUTE_HASH_INTERVAL = 5000
- // check the document hash regularly (but not if we have checked in the last 5 seconds)
- var needToRecomputeHash = !this.__lastSubmitTimestamp || (age > RECOMPUTE_HASH_INTERVAL) || (age < 0)
- if (needToRecomputeHash || debugging) {
- // send git hash of current snapshot
- const str = this.getText()
- var sha1 = generateSHA1Hash("blob " + str.length + "\x00" + str)
- this.__lastSubmitTimestamp = now;
- }
- }
- // console.log "SENDING OP TO SERVER", @inflightOp, @version
- var lastVersion = this.__lastVersion;
- this.__lastVersion = this.version;
- return this.connection.send({ doc: this.name, op: this.inflightOp, v: this.version, lastV: lastVersion, hash: sha1});
- }
- // Submit an op to the server. The op maybe held for a little while before being sent, as only one
- // op can be inflight at any time.
- }, {
- key: 'submitOp',
- value: function submitOp(op, callback) {
- if (this.type.normalize != null) {
- op = this.type.normalize(op);
- }
- var oldSnapshot = this.snapshot;
- // If this throws an exception, no changes should have been made to the doc
- this.snapshot = this.type.apply(this.snapshot, op);
- if (this.pendingOp !== null) {
- this.pendingOp = this.type.compose(this.pendingOp, op);
- } else {
- this.pendingOp = op;
- }
- if (callback) {
- this.pendingCallbacks.push(callback);
- }
- this.emit('change', op, oldSnapshot);
- return this.delayedFlush();
- }
- }, {
- key: 'delayedFlush',
- value: function delayedFlush() {
- if (this.flushTimeout == null) {
- return this.flushTimeout = setTimeout(this.flush, this._flushDelay || 0);
- }
- }
- }, {
- key: 'setFlushDelay',
- value: function setFlushDelay(delay) {
- return this._flushDelay = delay;
- }
- }, {
- key: 'shout',
- value: function shout(msg) {
- // Meta ops don't have to queue, they can go direct. Good/bad idea?
- return this.connection.send({ doc: this.name, meta: { path: ['shout'], value: msg } });
- }
- // Open a document. The document starts closed.
- }, {
- key: 'open',
- value: function open(callback) {
- var _this3 = this;
- this.autoOpen = true;
- if (this.state !== 'closed') {
- return;
- }
- var message = {
- doc: this.name,
- open: true
- };
- if (this.snapshot === undefined) {
- message.snapshot = null;
- }
- if (this.type) {
- message.type = this.type.name;
- }
- if (this.version != null) {
- message.v = this.version;
- }
- if (this._create) {
- message.create = true;
- }
- this.connection.send(message);
- this.state = 'opening';
- return this._openCallback = function (error) {
- _this3._openCallback = null;
- return typeof callback === 'function' ? callback(error) : undefined;
- };
- }
- // Close a document.
- }, {
- key: 'close',
- value: function close(callback) {
- this.autoOpen = false;
- if (this.state === 'closed') {
- return typeof callback === 'function' ? callback() : undefined;
- }
- this.connection.send({ doc: this.name, open: false });
- // Should this happen immediately or when we get open:false back from the server?
- this.state = 'closed';
- this.emit('closing');
- return this._closeCallback = callback;
- }
- }]);
- return Doc;
- }();
- // Make documents event emitters
- MicroEvent.mixin(Doc);
- exports.Doc = Doc;
- return exports;
- })()
- export default window.sharejs
|