sharejs.js 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710
  1. 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; }; }();
  2. 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; };
  3. 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"); } }; }();
  4. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  5. /* eslint-disable
  6. camelcase,
  7. max-len,
  8. no-class-assign,
  9. no-return-assign,
  10. no-undef,
  11. no-unused-vars,
  12. no-use-before-define,
  13. standard/object-curly-even-spacing,
  14. */
  15. // TODO: This file was created by bulk-decaffeinate.
  16. // Fix any style issues and re-enable lint.
  17. /*
  18. * decaffeinate suggestions:
  19. * DS101: Remove unnecessary use of Array.from
  20. * DS102: Remove unnecessary code created because of implicit returns
  21. * DS205: Consider reworking code to avoid use of IIFEs
  22. * DS207: Consider shorter variations of null checks
  23. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  24. */
  25. define(['ace/ace','crypto-js/sha1'], function (_ignore, CryptoJSSHA1) {
  26. var append = void 0,
  27. bootstrapTransform = void 0,
  28. exports = void 0,
  29. transformComponent = void 0;
  30. var WEB = true;
  31. window.sharejs = exports = {};
  32. var types = exports.types = {};
  33. // These methods let you build a transform function from a transformComponent function
  34. // for OT types like text and JSON in which operations are lists of components
  35. // and transforming them requires N^2 work.
  36. // Add transform and transformX functions for an OT type which has transformComponent defined.
  37. // transformComponent(destination array, component, other component, side)
  38. exports['_bt'] = bootstrapTransform = function bootstrapTransform(type, transformComponent, checkValidOp, append) {
  39. var _transformX = void 0;
  40. var transformComponentX = function transformComponentX(left, right, destLeft, destRight) {
  41. transformComponent(destLeft, left, right, 'left');
  42. return transformComponent(destRight, right, left, 'right');
  43. };
  44. // Transforms rightOp by leftOp. Returns ['rightOp', clientOp']
  45. type.transformX = type['transformX'] = _transformX = function transformX(leftOp, rightOp) {
  46. checkValidOp(leftOp);
  47. checkValidOp(rightOp);
  48. var newRightOp = [];
  49. var _iteratorNormalCompletion = true;
  50. var _didIteratorError = false;
  51. var _iteratorError = undefined;
  52. try {
  53. for (var _iterator = Array.from(rightOp)[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
  54. var rightComponent = _step.value;
  55. // Generate newLeftOp by composing leftOp by rightComponent
  56. var newLeftOp = [];
  57. var k = 0;
  58. while (k < leftOp.length) {
  59. var l;
  60. var nextC = [];
  61. transformComponentX(leftOp[k], rightComponent, newLeftOp, nextC);
  62. k++;
  63. if (nextC.length === 1) {
  64. rightComponent = nextC[0];
  65. } else if (nextC.length === 0) {
  66. var _iteratorNormalCompletion2 = true;
  67. var _didIteratorError2 = false;
  68. var _iteratorError2 = undefined;
  69. try {
  70. for (var _iterator2 = Array.from(leftOp.slice(k))[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
  71. l = _step2.value;
  72. append(newLeftOp, l);
  73. }
  74. } catch (err) {
  75. _didIteratorError2 = true;
  76. _iteratorError2 = err;
  77. } finally {
  78. try {
  79. if (!_iteratorNormalCompletion2 && _iterator2.return) {
  80. _iterator2.return();
  81. }
  82. } finally {
  83. if (_didIteratorError2) {
  84. throw _iteratorError2;
  85. }
  86. }
  87. }
  88. rightComponent = null;
  89. break;
  90. } else {
  91. // Recurse.
  92. var _Array$from = Array.from(_transformX(leftOp.slice(k), nextC)),
  93. _Array$from2 = _slicedToArray(_Array$from, 2),
  94. l_ = _Array$from2[0],
  95. r_ = _Array$from2[1];
  96. var _iteratorNormalCompletion3 = true;
  97. var _didIteratorError3 = false;
  98. var _iteratorError3 = undefined;
  99. try {
  100. for (var _iterator3 = Array.from(l_)[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
  101. l = _step3.value;
  102. append(newLeftOp, l);
  103. }
  104. } catch (err) {
  105. _didIteratorError3 = true;
  106. _iteratorError3 = err;
  107. } finally {
  108. try {
  109. if (!_iteratorNormalCompletion3 && _iterator3.return) {
  110. _iterator3.return();
  111. }
  112. } finally {
  113. if (_didIteratorError3) {
  114. throw _iteratorError3;
  115. }
  116. }
  117. }
  118. var _iteratorNormalCompletion4 = true;
  119. var _didIteratorError4 = false;
  120. var _iteratorError4 = undefined;
  121. try {
  122. for (var _iterator4 = Array.from(r_)[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) {
  123. var r = _step4.value;
  124. append(newRightOp, r);
  125. }
  126. } catch (err) {
  127. _didIteratorError4 = true;
  128. _iteratorError4 = err;
  129. } finally {
  130. try {
  131. if (!_iteratorNormalCompletion4 && _iterator4.return) {
  132. _iterator4.return();
  133. }
  134. } finally {
  135. if (_didIteratorError4) {
  136. throw _iteratorError4;
  137. }
  138. }
  139. }
  140. rightComponent = null;
  141. break;
  142. }
  143. }
  144. if (rightComponent != null) {
  145. append(newRightOp, rightComponent);
  146. }
  147. leftOp = newLeftOp;
  148. }
  149. } catch (err) {
  150. _didIteratorError = true;
  151. _iteratorError = err;
  152. } finally {
  153. try {
  154. if (!_iteratorNormalCompletion && _iterator.return) {
  155. _iterator.return();
  156. }
  157. } finally {
  158. if (_didIteratorError) {
  159. throw _iteratorError;
  160. }
  161. }
  162. }
  163. return [leftOp, newRightOp];
  164. };
  165. // Transforms op with specified type ('left' or 'right') by otherOp.
  166. return type.transform = type['transform'] = function (op, otherOp, type) {
  167. var _ = void 0;
  168. if (type !== 'left' && type !== 'right') {
  169. throw new Error("type must be 'left' or 'right'");
  170. }
  171. if (otherOp.length === 0) {
  172. return op;
  173. }
  174. // TODO: Benchmark with and without this line. I _think_ it'll make a big difference...?
  175. if (op.length === 1 && otherOp.length === 1) {
  176. return transformComponent([], op[0], otherOp[0], type);
  177. }
  178. if (type === 'left') {
  179. var left = void 0;
  180. var _Array$from3 = Array.from(_transformX(op, otherOp));
  181. var _Array$from4 = _slicedToArray(_Array$from3, 2);
  182. left = _Array$from4[0];
  183. _ = _Array$from4[1];
  184. return left;
  185. } else {
  186. var right = void 0;
  187. var _Array$from5 = Array.from(_transformX(otherOp, op));
  188. var _Array$from6 = _slicedToArray(_Array$from5, 2);
  189. _ = _Array$from6[0];
  190. right = _Array$from6[1];
  191. return right;
  192. }
  193. };
  194. };
  195. // A simple text implementation
  196. //
  197. // Operations are lists of components.
  198. // Each component either inserts or deletes at a specified position in the document.
  199. //
  200. // Components are either:
  201. // {i:'str', p:100}: Insert 'str' at position 100 in the document
  202. // {d:'str', p:100}: Delete 'str' at position 100 in the document
  203. //
  204. // Components in an operation are executed sequentially, so the position of components
  205. // assumes previous components have already executed.
  206. //
  207. // Eg: This op:
  208. // [{i:'abc', p:0}]
  209. // is equivalent to this op:
  210. // [{i:'a', p:0}, {i:'b', p:1}, {i:'c', p:2}]
  211. // NOTE: The global scope here is shared with other sharejs files when built with closure.
  212. // Be careful what ends up in your namespace.
  213. var text = {};
  214. text.name = 'text';
  215. text.create = function () {
  216. return '';
  217. };
  218. var strInject = function strInject(s1, pos, s2) {
  219. return s1.slice(0, pos) + s2 + s1.slice(pos);
  220. };
  221. var checkValidComponent = function checkValidComponent(c) {
  222. if (typeof c.p !== 'number') {
  223. throw new Error('component missing position field');
  224. }
  225. var i_type = _typeof(c.i);
  226. var d_type = _typeof(c.d);
  227. var c_type = _typeof(c.c);
  228. if (!(i_type === 'string' ^ d_type === 'string' ^ c_type === 'string')) {
  229. throw new Error('component needs an i, d or c field');
  230. }
  231. if (!(c.p >= 0)) {
  232. throw new Error('position cannot be negative');
  233. }
  234. };
  235. var checkValidOp = function checkValidOp(op) {
  236. var _iteratorNormalCompletion5 = true;
  237. var _didIteratorError5 = false;
  238. var _iteratorError5 = undefined;
  239. try {
  240. for (var _iterator5 = Array.from(op)[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) {
  241. var c = _step5.value;
  242. checkValidComponent(c);
  243. }
  244. } catch (err) {
  245. _didIteratorError5 = true;
  246. _iteratorError5 = err;
  247. } finally {
  248. try {
  249. if (!_iteratorNormalCompletion5 && _iterator5.return) {
  250. _iterator5.return();
  251. }
  252. } finally {
  253. if (_didIteratorError5) {
  254. throw _iteratorError5;
  255. }
  256. }
  257. }
  258. return true;
  259. };
  260. text.apply = function (snapshot, op) {
  261. checkValidOp(op);
  262. var _iteratorNormalCompletion6 = true;
  263. var _didIteratorError6 = false;
  264. var _iteratorError6 = undefined;
  265. try {
  266. for (var _iterator6 = Array.from(op)[Symbol.iterator](), _step6; !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done); _iteratorNormalCompletion6 = true) {
  267. var component = _step6.value;
  268. if (component.i != null) {
  269. snapshot = strInject(snapshot, component.p, component.i);
  270. } else if (component.d != null) {
  271. var deleted = snapshot.slice(component.p, component.p + component.d.length);
  272. if (component.d !== deleted) {
  273. throw new Error('Delete component \'' + component.d + '\' does not match deleted text \'' + deleted + '\'');
  274. }
  275. snapshot = snapshot.slice(0, component.p) + snapshot.slice(component.p + component.d.length);
  276. } else if (component.c != null) {
  277. var comment = snapshot.slice(component.p, component.p + component.c.length);
  278. if (component.c !== comment) {
  279. throw new Error('Comment component \'' + component.c + '\' does not match commented text \'' + comment + '\'');
  280. }
  281. } else {
  282. throw new Error('Unknown op type');
  283. }
  284. }
  285. } catch (err) {
  286. _didIteratorError6 = true;
  287. _iteratorError6 = err;
  288. } finally {
  289. try {
  290. if (!_iteratorNormalCompletion6 && _iterator6.return) {
  291. _iterator6.return();
  292. }
  293. } finally {
  294. if (_didIteratorError6) {
  295. throw _iteratorError6;
  296. }
  297. }
  298. }
  299. return snapshot;
  300. };
  301. var cloneAndModify = function cloneAndModify(op, modifications) {
  302. var v = void 0;
  303. var newOp = {};
  304. for (var k in op) {
  305. v = op[k];
  306. newOp[k] = v;
  307. }
  308. for (k in modifications) {
  309. v = modifications[k];
  310. newOp[k] = v;
  311. }
  312. return newOp;
  313. };
  314. // Exported for use by the random op generator.
  315. //
  316. // For simplicity, this version of append does not compress adjacent inserts and deletes of
  317. // the same text. It would be nice to change that at some stage.
  318. text._append = append = function append(newOp, c) {
  319. if (c.i === '' || c.d === '') {
  320. return;
  321. }
  322. if (newOp.length === 0) {
  323. return newOp.push(c);
  324. } else {
  325. var last = newOp[newOp.length - 1];
  326. // Compose the insert into the previous insert if possible
  327. if (last.i != null && c.i != null && last.p <= c.p && c.p <= last.p + last.i.length && last.u === c.u) {
  328. return newOp[newOp.length - 1] = cloneAndModify(last, { i: strInject(last.i, c.p - last.p, c.i) });
  329. } else if (last.d != null && c.d != null && c.p <= last.p && last.p <= c.p + c.d.length && last.u === c.u) {
  330. return newOp[newOp.length - 1] = cloneAndModify(last, { d: strInject(c.d, last.p - c.p, last.d), p: c.p });
  331. } else {
  332. return newOp.push(c);
  333. }
  334. }
  335. };
  336. text.compose = function (op1, op2) {
  337. checkValidOp(op1);
  338. checkValidOp(op2);
  339. var newOp = op1.slice();
  340. var _iteratorNormalCompletion7 = true;
  341. var _didIteratorError7 = false;
  342. var _iteratorError7 = undefined;
  343. try {
  344. for (var _iterator7 = Array.from(op2)[Symbol.iterator](), _step7; !(_iteratorNormalCompletion7 = (_step7 = _iterator7.next()).done); _iteratorNormalCompletion7 = true) {
  345. var c = _step7.value;
  346. append(newOp, c);
  347. }
  348. } catch (err) {
  349. _didIteratorError7 = true;
  350. _iteratorError7 = err;
  351. } finally {
  352. try {
  353. if (!_iteratorNormalCompletion7 && _iterator7.return) {
  354. _iterator7.return();
  355. }
  356. } finally {
  357. if (_didIteratorError7) {
  358. throw _iteratorError7;
  359. }
  360. }
  361. }
  362. return newOp;
  363. };
  364. // Attempt to compress the op components together 'as much as possible'.
  365. // This implementation preserves order and preserves create/delete pairs.
  366. text.compress = function (op) {
  367. return text.compose([], op);
  368. };
  369. text.normalize = function (op) {
  370. var newOp = [];
  371. // Normalize should allow ops which are a single (unwrapped) component:
  372. // {i:'asdf', p:23}.
  373. // There's no good way to test if something is an array:
  374. // http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/
  375. // so this is probably the least bad solution.
  376. if (op.i != null || op.p != null) {
  377. op = [op];
  378. }
  379. var _iteratorNormalCompletion8 = true;
  380. var _didIteratorError8 = false;
  381. var _iteratorError8 = undefined;
  382. try {
  383. for (var _iterator8 = Array.from(op)[Symbol.iterator](), _step8; !(_iteratorNormalCompletion8 = (_step8 = _iterator8.next()).done); _iteratorNormalCompletion8 = true) {
  384. var c = _step8.value;
  385. if (c.p == null) {
  386. c.p = 0;
  387. }
  388. append(newOp, c);
  389. }
  390. } catch (err) {
  391. _didIteratorError8 = true;
  392. _iteratorError8 = err;
  393. } finally {
  394. try {
  395. if (!_iteratorNormalCompletion8 && _iterator8.return) {
  396. _iterator8.return();
  397. }
  398. } finally {
  399. if (_didIteratorError8) {
  400. throw _iteratorError8;
  401. }
  402. }
  403. }
  404. return newOp;
  405. };
  406. // This helper method transforms a position by an op component.
  407. //
  408. // If c is an insert, insertAfter specifies whether the transform
  409. // is pushed after the insert (true) or before it (false).
  410. //
  411. // insertAfter is optional for deletes.
  412. var transformPosition = function transformPosition(pos, c, insertAfter) {
  413. if (c.i != null) {
  414. if (c.p < pos || c.p === pos && insertAfter) {
  415. return pos + c.i.length;
  416. } else {
  417. return pos;
  418. }
  419. } else if (c.d != null) {
  420. // I think this could also be written as: Math.min(c.p, Math.min(c.p - otherC.p, otherC.d.length))
  421. // but I think its harder to read that way, and it compiles using ternary operators anyway
  422. // so its no slower written like this.
  423. if (pos <= c.p) {
  424. return pos;
  425. } else if (pos <= c.p + c.d.length) {
  426. return c.p;
  427. } else {
  428. return pos - c.d.length;
  429. }
  430. } else if (c.c != null) {
  431. return pos;
  432. } else {
  433. throw new Error('unknown op type');
  434. }
  435. };
  436. // Helper method to transform a cursor position as a result of an op.
  437. //
  438. // Like transformPosition above, if c is an insert, insertAfter specifies whether the cursor position
  439. // is pushed after an insert (true) or before it (false).
  440. text.transformCursor = function (position, op, side) {
  441. var insertAfter = side === 'right';
  442. var _iteratorNormalCompletion9 = true;
  443. var _didIteratorError9 = false;
  444. var _iteratorError9 = undefined;
  445. try {
  446. for (var _iterator9 = Array.from(op)[Symbol.iterator](), _step9; !(_iteratorNormalCompletion9 = (_step9 = _iterator9.next()).done); _iteratorNormalCompletion9 = true) {
  447. var c = _step9.value;
  448. position = transformPosition(position, c, insertAfter);
  449. }
  450. } catch (err) {
  451. _didIteratorError9 = true;
  452. _iteratorError9 = err;
  453. } finally {
  454. try {
  455. if (!_iteratorNormalCompletion9 && _iterator9.return) {
  456. _iterator9.return();
  457. }
  458. } finally {
  459. if (_didIteratorError9) {
  460. throw _iteratorError9;
  461. }
  462. }
  463. }
  464. return position;
  465. };
  466. // Transform an op component by another op component. Asymmetric.
  467. // The result will be appended to destination.
  468. //
  469. // exported for use in JSON type
  470. text._tc = transformComponent = function transformComponent(dest, c, otherC, side) {
  471. var cIntersect = void 0,
  472. intersectEnd = void 0,
  473. intersectStart = void 0,
  474. newC = void 0,
  475. otherIntersect = void 0;
  476. checkValidOp([c]);
  477. checkValidOp([otherC]);
  478. if (c.i != null) {
  479. append(dest, cloneAndModify(c, { p: transformPosition(c.p, otherC, side === 'right') }));
  480. } else if (c.d != null) {
  481. // Delete
  482. if (otherC.i != null) {
  483. // delete vs insert
  484. var s = c.d;
  485. if (c.p < otherC.p) {
  486. append(dest, cloneAndModify(c, { d: s.slice(0, otherC.p - c.p) }));
  487. s = s.slice(otherC.p - c.p);
  488. }
  489. if (s !== '') {
  490. append(dest, cloneAndModify(c, { d: s, p: c.p + otherC.i.length }));
  491. }
  492. } else if (otherC.d != null) {
  493. // Delete vs delete
  494. if (c.p >= otherC.p + otherC.d.length) {
  495. append(dest, cloneAndModify(c, { p: c.p - otherC.d.length }));
  496. } else if (c.p + c.d.length <= otherC.p) {
  497. append(dest, c);
  498. } else {
  499. // They overlap somewhere.
  500. newC = cloneAndModify(c, { d: '' });
  501. if (c.p < otherC.p) {
  502. newC.d = c.d.slice(0, otherC.p - c.p);
  503. }
  504. if (c.p + c.d.length > otherC.p + otherC.d.length) {
  505. newC.d += c.d.slice(otherC.p + otherC.d.length - c.p);
  506. }
  507. // This is entirely optional - just for a check that the deleted
  508. // text in the two ops matches
  509. intersectStart = Math.max(c.p, otherC.p);
  510. intersectEnd = Math.min(c.p + c.d.length, otherC.p + otherC.d.length);
  511. cIntersect = c.d.slice(intersectStart - c.p, intersectEnd - c.p);
  512. otherIntersect = otherC.d.slice(intersectStart - otherC.p, intersectEnd - otherC.p);
  513. if (cIntersect !== otherIntersect) {
  514. throw new Error('Delete ops delete different text in the same region of the document');
  515. }
  516. if (newC.d !== '') {
  517. // This could be rewritten similarly to insert v delete, above.
  518. newC.p = transformPosition(newC.p, otherC);
  519. append(dest, newC);
  520. }
  521. }
  522. } else if (otherC.c != null) {
  523. append(dest, c);
  524. } else {
  525. throw new Error('unknown op type');
  526. }
  527. } else if (c.c != null) {
  528. // Comment
  529. if (otherC.i != null) {
  530. if (c.p < otherC.p && otherC.p < c.p + c.c.length) {
  531. var offset = otherC.p - c.p;
  532. var new_c = c.c.slice(0, +(offset - 1) + 1 || undefined) + otherC.i + c.c.slice(offset);
  533. append(dest, cloneAndModify(c, { c: new_c }));
  534. } else {
  535. append(dest, cloneAndModify(c, { p: transformPosition(c.p, otherC, true) }));
  536. }
  537. } else if (otherC.d != null) {
  538. if (c.p >= otherC.p + otherC.d.length) {
  539. append(dest, cloneAndModify(c, { p: c.p - otherC.d.length }));
  540. } else if (c.p + c.c.length <= otherC.p) {
  541. append(dest, c);
  542. } else {
  543. // Delete overlaps comment
  544. // They overlap somewhere.
  545. newC = cloneAndModify(c, { c: '' });
  546. if (c.p < otherC.p) {
  547. newC.c = c.c.slice(0, otherC.p - c.p);
  548. }
  549. if (c.p + c.c.length > otherC.p + otherC.d.length) {
  550. newC.c += c.c.slice(otherC.p + otherC.d.length - c.p);
  551. }
  552. // This is entirely optional - just for a check that the deleted
  553. // text in the two ops matches
  554. intersectStart = Math.max(c.p, otherC.p);
  555. intersectEnd = Math.min(c.p + c.c.length, otherC.p + otherC.d.length);
  556. cIntersect = c.c.slice(intersectStart - c.p, intersectEnd - c.p);
  557. otherIntersect = otherC.d.slice(intersectStart - otherC.p, intersectEnd - otherC.p);
  558. if (cIntersect !== otherIntersect) {
  559. throw new Error('Delete ops delete different text in the same region of the document');
  560. }
  561. newC.p = transformPosition(newC.p, otherC);
  562. append(dest, newC);
  563. }
  564. } else if (otherC.c != null) {
  565. append(dest, c);
  566. } else {
  567. throw new Error('unknown op type');
  568. }
  569. }
  570. return dest;
  571. };
  572. var invertComponent = function invertComponent(c) {
  573. if (c.i != null) {
  574. return { d: c.i, p: c.p };
  575. } else {
  576. return { i: c.d, p: c.p };
  577. }
  578. };
  579. // No need to use append for invert, because the components won't be able to
  580. // cancel with one another.
  581. text.invert = function (op) {
  582. return Array.from(op.slice().reverse()).map(function (c) {
  583. return invertComponent(c);
  584. });
  585. };
  586. if (WEB != null) {
  587. if (!exports.types) {
  588. exports.types = {};
  589. }
  590. // This is kind of awful - come up with a better way to hook this helper code up.
  591. bootstrapTransform(text, transformComponent, checkValidOp, append);
  592. // [] is used to prevent closure from renaming types.text
  593. exports.types.text = text;
  594. }
  595. // Text document API for text
  596. text.api = {
  597. provides: { text: true },
  598. // The number of characters in the string
  599. getLength: function getLength() {
  600. return this.snapshot.length;
  601. },
  602. // Get the text contents of a document
  603. getText: function getText() {
  604. return this.snapshot;
  605. },
  606. insert: function insert(pos, text, fromUndo, callback) {
  607. var op = { p: pos, i: text };
  608. if (fromUndo) {
  609. op.u = true;
  610. }
  611. op = [op];
  612. this.submitOp(op, callback);
  613. return op;
  614. },
  615. del: function del(pos, length, fromUndo, callback) {
  616. var op = { p: pos, d: this.snapshot.slice(pos, pos + length) };
  617. if (fromUndo) {
  618. op.u = true;
  619. }
  620. op = [op];
  621. this.submitOp(op, callback);
  622. return op;
  623. },
  624. _register: function _register() {
  625. return this.on('remoteop', function (op) {
  626. var _this = this;
  627. return function () {
  628. var result = [];
  629. var _iteratorNormalCompletion10 = true;
  630. var _didIteratorError10 = false;
  631. var _iteratorError10 = undefined;
  632. try {
  633. for (var _iterator10 = Array.from(op)[Symbol.iterator](), _step10; !(_iteratorNormalCompletion10 = (_step10 = _iterator10.next()).done); _iteratorNormalCompletion10 = true) {
  634. var component = _step10.value;
  635. if (component.i !== undefined) {
  636. result.push(_this.emit('insert', component.p, component.i));
  637. } else if (component.d !== undefined) {
  638. result.push(_this.emit('delete', component.p, component.d));
  639. } else {
  640. result.push(undefined);
  641. }
  642. }
  643. } catch (err) {
  644. _didIteratorError10 = true;
  645. _iteratorError10 = err;
  646. } finally {
  647. try {
  648. if (!_iteratorNormalCompletion10 && _iterator10.return) {
  649. _iterator10.return();
  650. }
  651. } finally {
  652. if (_didIteratorError10) {
  653. throw _iteratorError10;
  654. }
  655. }
  656. }
  657. return result;
  658. }();
  659. });
  660. }
  661. };
  662. // This is a simple port of microevent.js to Coffeescript. I've changed the
  663. // function names to be consistent with node.js EventEmitter.
  664. //
  665. // microevent.js is copyright Jerome Etienne, and licensed under the MIT license:
  666. // https://github.com/jeromeetienne/microevent.js
  667. var nextTick = WEB != null ? function (fn) {
  668. return setTimeout(fn, 0);
  669. } : process['nextTick'];
  670. var MicroEvent = function () {
  671. function MicroEvent() {
  672. _classCallCheck(this, MicroEvent);
  673. }
  674. _createClass(MicroEvent, [{
  675. key: 'on',
  676. value: function on(event, fct) {
  677. if (!this._events) {
  678. this._events = {};
  679. }
  680. if (!this._events[event]) {
  681. this._events[event] = [];
  682. }
  683. this._events[event].push(fct);
  684. return this;
  685. }
  686. }, {
  687. key: 'removeListener',
  688. value: function removeListener(event, fct) {
  689. var _this2 = this;
  690. if (!this._events) {
  691. this._events = {};
  692. }
  693. var listeners = this._events[event] || (this._events[event] = []);
  694. // Sadly, there's no IE8- support for indexOf.
  695. var i = 0;
  696. while (i < listeners.length) {
  697. if (listeners[i] === fct) {
  698. listeners[i] = undefined;
  699. }
  700. i++;
  701. }
  702. nextTick(function () {
  703. return _this2._events[event] = Array.from(_this2._events[event]).filter(function (x) {
  704. return x;
  705. });
  706. });
  707. return this;
  708. }
  709. }, {
  710. key: 'emit',
  711. value: function emit(event) {
  712. if (!(this._events != null ? this._events[event] : undefined)) {
  713. return this;
  714. }
  715. for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
  716. args[_key - 1] = arguments[_key];
  717. }
  718. var _iteratorNormalCompletion11 = true;
  719. var _didIteratorError11 = false;
  720. var _iteratorError11 = undefined;
  721. try {
  722. for (var _iterator11 = Array.from(this._events[event])[Symbol.iterator](), _step11; !(_iteratorNormalCompletion11 = (_step11 = _iterator11.next()).done); _iteratorNormalCompletion11 = true) {
  723. var fn = _step11.value;
  724. if (fn) {
  725. fn.apply(this, args);
  726. }
  727. }
  728. } catch (err) {
  729. _didIteratorError11 = true;
  730. _iteratorError11 = err;
  731. } finally {
  732. try {
  733. if (!_iteratorNormalCompletion11 && _iterator11.return) {
  734. _iterator11.return();
  735. }
  736. } finally {
  737. if (_didIteratorError11) {
  738. throw _iteratorError11;
  739. }
  740. }
  741. }
  742. return this;
  743. }
  744. }]);
  745. return MicroEvent;
  746. }();
  747. // mixin will delegate all MicroEvent.js function in the destination object
  748. MicroEvent.mixin = function (obj) {
  749. var proto = obj.prototype || obj;
  750. // Damn closure compiler :/
  751. proto.on = MicroEvent.prototype.on;
  752. proto.removeListener = MicroEvent.prototype.removeListener;
  753. proto.emit = MicroEvent.prototype.emit;
  754. return obj;
  755. };
  756. if (WEB == null) {
  757. module.exports = MicroEvent;
  758. }
  759. if (WEB != null) {
  760. exports.extendDoc = function (name, fn) {
  761. return Doc.prototype[name] = fn;
  762. };
  763. }
  764. // A Doc is a client's view on a sharejs document.
  765. //
  766. // Documents are created by calling Connection.open().
  767. //
  768. // Documents are event emitters - use doc.on(eventname, fn) to subscribe.
  769. //
  770. // Documents get mixed in with their type's API methods. So, you can .insert('foo', 0) into
  771. // a text document and stuff like that.
  772. //
  773. // Events:
  774. // - remoteop (op)
  775. // - changed (op)
  776. // - acknowledge (op)
  777. // - error
  778. // - open, closing, closed. 'closing' is not guaranteed to fire before closed.
  779. var Doc = function () {
  780. // connection is a Connection object.
  781. // name is the documents' docName.
  782. // data can optionally contain known document data, and initial open() call arguments:
  783. // {v[erson], snapshot={...}, type, create=true/false/undefined}
  784. // callback will be called once the document is first opened.
  785. function Doc(connection, name, openData) {
  786. _classCallCheck(this, Doc);
  787. // Any of these can be null / undefined at this stage.
  788. this.flush = this.flush.bind(this);
  789. this.setFlushDelay = this.setFlushDelay.bind(this);
  790. this.shout = this.shout.bind(this);
  791. this.connection = connection;
  792. this.name = name;
  793. if (!openData) {
  794. openData = {};
  795. }
  796. this.version = openData.v;
  797. this.snapshot = openData.snaphot;
  798. if (openData.type) {
  799. this._setType(openData.type);
  800. }
  801. this.state = 'closed';
  802. this.autoOpen = false;
  803. // Has the document already been created?
  804. this._create = openData.create;
  805. // The op that is currently roundtripping to the server, or null.
  806. //
  807. // When the connection reconnects, the inflight op is resubmitted.
  808. this.inflightOp = null;
  809. this.inflightCallbacks = [];
  810. // The auth ids which the client has previously used to attempt to send inflightOp. This is
  811. // usually empty.
  812. this.inflightSubmittedIds = [];
  813. // All ops that are waiting for the server to acknowledge @inflightOp
  814. this.pendingOp = null;
  815. this.pendingCallbacks = [];
  816. // Some recent ops, incase submitOp is called with an old op version number.
  817. this.serverOps = {};
  818. }
  819. // Transform a server op by a client op, and vice versa.
  820. _createClass(Doc, [{
  821. key: '_xf',
  822. value: function _xf(client, server) {
  823. if (this.type.transformX) {
  824. return this.type.transformX(client, server);
  825. } else {
  826. var client_ = this.type.transform(client, server, 'left');
  827. var server_ = this.type.transform(server, client, 'right');
  828. return [client_, server_];
  829. }
  830. }
  831. }, {
  832. key: '_otApply',
  833. value: function _otApply(docOp, isRemote, msg) {
  834. var oldSnapshot = this.snapshot;
  835. this.snapshot = this.type.apply(this.snapshot, docOp);
  836. // Its important that these event handlers are called with oldSnapshot.
  837. // The reason is that the OT type APIs might need to access the snapshots to
  838. // determine information about the received op.
  839. this.emit('change', docOp, oldSnapshot, msg);
  840. if (isRemote) {
  841. return this.emit('remoteop', docOp, oldSnapshot, msg);
  842. }
  843. }
  844. }, {
  845. key: '_connectionStateChanged',
  846. value: function _connectionStateChanged(state, data) {
  847. switch (state) {
  848. case 'disconnected':
  849. this.state = 'closed';
  850. // This is used by the server to make sure that when an op is resubmitted it
  851. // doesn't end up getting applied twice.
  852. if (this.inflightOp) {
  853. this.inflightSubmittedIds.push(this.connection.id);
  854. }
  855. this.emit('closed');
  856. break;
  857. case 'ok':
  858. // Might be able to do this when we're connecting... that would save a roundtrip.
  859. if (this.autoOpen) {
  860. this.open();
  861. }
  862. break;
  863. case 'stopped':
  864. if (typeof this._openCallback === 'function') {
  865. this._openCallback(data);
  866. }
  867. break;
  868. }
  869. return this.emit(state, data);
  870. }
  871. }, {
  872. key: '_setType',
  873. value: function _setType(type) {
  874. if (typeof type === 'string') {
  875. type = types[type];
  876. }
  877. if (!type || !type.compose) {
  878. throw new Error('Support for types without compose() is not implemented');
  879. }
  880. this.type = type;
  881. if (type.api) {
  882. for (var k in type.api) {
  883. var v = type.api[k];this[k] = v;
  884. }
  885. return typeof this._register === 'function' ? this._register() : undefined;
  886. } else {
  887. return this.provides = {};
  888. }
  889. }
  890. }, {
  891. key: '_onMessage',
  892. value: function _onMessage(msg) {
  893. // console.warn 's->c', msg
  894. if (msg.open === true) {
  895. // The document has been successfully opened.
  896. this.state = 'open';
  897. this._create = false; // Don't try and create the document again next time open() is called.
  898. if (this.created == null) {
  899. this.created = !!msg.create;
  900. }
  901. if (msg.type) {
  902. this._setType(msg.type);
  903. }
  904. if (msg.create) {
  905. this.created = true;
  906. this.snapshot = this.type.create();
  907. } else {
  908. if (this.created !== true) {
  909. this.created = false;
  910. }
  911. if (msg.snapshot !== undefined) {
  912. this.snapshot = msg.snapshot;
  913. }
  914. }
  915. if (msg.v != null) {
  916. this.version = msg.v;
  917. }
  918. // Resend any previously queued operation.
  919. if (this.inflightOp) {
  920. var response = {
  921. doc: this.name,
  922. op: this.inflightOp,
  923. v: this.version
  924. };
  925. if (this.inflightSubmittedIds.length) {
  926. response.dupIfSource = this.inflightSubmittedIds;
  927. }
  928. this.connection.send(response);
  929. } else {
  930. this.flush();
  931. }
  932. this.emit('open');
  933. return typeof this._openCallback === 'function' ? this._openCallback(null) : undefined;
  934. } else if (msg.open === false) {
  935. // The document has either been closed, or an open request has failed.
  936. if (msg.error) {
  937. // An error occurred opening the document.
  938. if (typeof console !== 'undefined' && console !== null) {
  939. console.error('Could not open document: ' + msg.error);
  940. }
  941. this.emit('error', msg.error);
  942. if (typeof this._openCallback === 'function') {
  943. this._openCallback(msg.error);
  944. }
  945. }
  946. this.state = 'closed';
  947. this.emit('closed');
  948. if (typeof this._closeCallback === 'function') {
  949. this._closeCallback();
  950. }
  951. return this._closeCallback = null;
  952. } else if (msg.op === null && error === 'Op already submitted') {
  953. // We've tried to resend an op to the server, which has already been received successfully. Do nothing.
  954. // The op will be confirmed normally when we get the op itself was echoed back from the server
  955. // (handled below).
  956. } else if (msg.op === undefined && msg.v !== undefined || msg.op && Array.from(this.inflightSubmittedIds).includes(msg.meta.source)) {
  957. // Our inflight op has been acknowledged.
  958. var callback = void 0;
  959. var oldInflightOp = this.inflightOp;
  960. this.inflightOp = null;
  961. this.inflightSubmittedIds.length = 0;
  962. if (this.pendingOp === null) {
  963. // All ops are acked
  964. this.emit('saved');
  965. }
  966. var error = msg.error;
  967. if (error) {
  968. // The server has rejected an op from the client for some reason.
  969. // We'll send the error message to the user and roll back the change.
  970. //
  971. // If the server isn't going to allow edits anyway, we should probably
  972. // figure out some way to flag that (readonly:true in the open request?)
  973. if (this.type.invert) {
  974. var undo = this.type.invert(oldInflightOp);
  975. // Now we have to transform the undo operation by any server ops & pending ops
  976. if (this.pendingOp) {
  977. var _Array$from7 = Array.from(this._xf(this.pendingOp, undo));
  978. var _Array$from8 = _slicedToArray(_Array$from7, 2);
  979. this.pendingOp = _Array$from8[0];
  980. undo = _Array$from8[1];
  981. }
  982. // ... and apply it locally, reverting the changes.
  983. //
  984. // This call will also call @emit 'remoteop'. I'm still not 100% sure about this
  985. // functionality, because its really a local op. Basically, the problem is that
  986. // if the client's op is rejected by the server, the editor window should update
  987. // to reflect the undo.
  988. this._otApply(undo, true, msg);
  989. } else {
  990. this.emit('error', 'Op apply failed (' + error + ') and the op could not be reverted');
  991. }
  992. var _iteratorNormalCompletion12 = true;
  993. var _didIteratorError12 = false;
  994. var _iteratorError12 = undefined;
  995. try {
  996. for (var _iterator12 = Array.from(this.inflightCallbacks)[Symbol.iterator](), _step12; !(_iteratorNormalCompletion12 = (_step12 = _iterator12.next()).done); _iteratorNormalCompletion12 = true) {
  997. callback = _step12.value;
  998. callback(error);
  999. }
  1000. } catch (err) {
  1001. _didIteratorError12 = true;
  1002. _iteratorError12 = err;
  1003. } finally {
  1004. try {
  1005. if (!_iteratorNormalCompletion12 && _iterator12.return) {
  1006. _iterator12.return();
  1007. }
  1008. } finally {
  1009. if (_didIteratorError12) {
  1010. throw _iteratorError12;
  1011. }
  1012. }
  1013. }
  1014. } else {
  1015. // The op applied successfully.
  1016. // We may get multiple acks of the same message if we retried it,
  1017. // so its ok if we receive an ack for a version that we've already gone past.
  1018. // If so, just ignore it
  1019. if (msg.v < this.version) {
  1020. return;
  1021. }
  1022. if (msg.v !== this.version) {
  1023. throw new Error('Invalid version from server');
  1024. }
  1025. this.serverOps[this.version] = oldInflightOp;
  1026. this.version++;
  1027. this.emit('acknowledge', oldInflightOp);
  1028. var _iteratorNormalCompletion13 = true;
  1029. var _didIteratorError13 = false;
  1030. var _iteratorError13 = undefined;
  1031. try {
  1032. for (var _iterator13 = Array.from(this.inflightCallbacks)[Symbol.iterator](), _step13; !(_iteratorNormalCompletion13 = (_step13 = _iterator13.next()).done); _iteratorNormalCompletion13 = true) {
  1033. callback = _step13.value;
  1034. callback(null, oldInflightOp);
  1035. }
  1036. } catch (err) {
  1037. _didIteratorError13 = true;
  1038. _iteratorError13 = err;
  1039. } finally {
  1040. try {
  1041. if (!_iteratorNormalCompletion13 && _iterator13.return) {
  1042. _iterator13.return();
  1043. }
  1044. } finally {
  1045. if (_didIteratorError13) {
  1046. throw _iteratorError13;
  1047. }
  1048. }
  1049. }
  1050. }
  1051. // Send the next op.
  1052. return this.delayedFlush();
  1053. } else if (msg.op) {
  1054. // We got a new op from the server.
  1055. // msg is {doc:, op:, v:}
  1056. // There is a bug in socket.io (produced on firefox 3.6) which causes messages
  1057. // to be duplicated sometimes.
  1058. // We'll just silently drop subsequent messages.
  1059. if (msg.v < this.version) {
  1060. return;
  1061. }
  1062. if (msg.doc !== this.name) {
  1063. return this.emit('error', 'Expected docName \'' + this.name + '\' but got ' + msg.doc);
  1064. }
  1065. if (msg.v !== this.version) {
  1066. return this.emit('error', 'Expected version ' + this.version + ' but got ' + msg.v);
  1067. }
  1068. // p "if: #{i @inflightOp} pending: #{i @pendingOp} doc '#{@snapshot}' op: #{i msg.op}"
  1069. var op = msg.op;
  1070. this.serverOps[this.version] = op;
  1071. var docOp = op;
  1072. if (this.inflightOp !== null) {
  1073. var _Array$from9 = Array.from(this._xf(this.inflightOp, docOp));
  1074. var _Array$from10 = _slicedToArray(_Array$from9, 2);
  1075. this.inflightOp = _Array$from10[0];
  1076. docOp = _Array$from10[1];
  1077. }
  1078. if (this.pendingOp !== null) {
  1079. var _Array$from11 = Array.from(this._xf(this.pendingOp, docOp));
  1080. var _Array$from12 = _slicedToArray(_Array$from11, 2);
  1081. this.pendingOp = _Array$from12[0];
  1082. docOp = _Array$from12[1];
  1083. }
  1084. this.version++;
  1085. // Finally, apply the op to @snapshot and trigger any event listeners
  1086. return this._otApply(docOp, true, msg);
  1087. } else if (msg.meta) {
  1088. var _msg$meta = msg.meta,
  1089. path = _msg$meta.path,
  1090. value = _msg$meta.value;
  1091. switch (path != null ? path[0] : undefined) {
  1092. case 'shout':
  1093. return this.emit('shout', value);
  1094. default:
  1095. return typeof console !== 'undefined' && console !== null ? console.warn('Unhandled meta op:', msg) : undefined;
  1096. }
  1097. } else {
  1098. return typeof console !== 'undefined' && console !== null ? console.warn('Unhandled document message:', msg) : undefined;
  1099. }
  1100. }
  1101. // Send ops to the server, if appropriate.
  1102. //
  1103. // Only one op can be in-flight at a time, so if an op is already on its way then
  1104. // this method does nothing.
  1105. }, {
  1106. key: 'flush',
  1107. value: function flush() {
  1108. this.flushTimeout = null;
  1109. // console.log "CALLED FLUSH"
  1110. if (this.connection.state !== 'ok' || this.inflightOp !== null || this.pendingOp === null) {
  1111. return;
  1112. }
  1113. // Rotate null -> pending -> inflight
  1114. this.inflightOp = this.pendingOp;
  1115. this.inflightCallbacks = this.pendingCallbacks;
  1116. this.pendingOp = null;
  1117. this.pendingCallbacks = [];
  1118. this.emit('flipped_pending_to_inflight');
  1119. if (window.useShareJsHash || window.sl_debugging) {
  1120. var now = Date.now()
  1121. var age = this.__lastSubmitTimestamp && (now - this.__lastSubmitTimestamp)
  1122. var RECOMPUTE_HASH_INTERVAL = 5000
  1123. // check the document hash regularly (but not if we have checked in the last 5 seconds)
  1124. var needToRecomputeHash = !this.__lastSubmitTimestamp || (age > RECOMPUTE_HASH_INTERVAL) || (age < 0)
  1125. if (needToRecomputeHash || window.sl_debugging) {
  1126. // send git hash of current snapshot
  1127. var sha1 = CryptoJSSHA1("blob " + this.snapshot.length + "\x00" + this.snapshot).toString()
  1128. this.__lastSubmitTimestamp = now;
  1129. }
  1130. }
  1131. // console.log "SENDING OP TO SERVER", @inflightOp, @version
  1132. var lastVersion = this.__lastVersion;
  1133. this.__lastVersion = this.version;
  1134. return this.connection.send({ doc: this.name, op: this.inflightOp, v: this.version, lastV: lastVersion, hash: sha1});
  1135. }
  1136. // Submit an op to the server. The op maybe held for a little while before being sent, as only one
  1137. // op can be inflight at any time.
  1138. }, {
  1139. key: 'submitOp',
  1140. value: function submitOp(op, callback) {
  1141. if (this.type.normalize != null) {
  1142. op = this.type.normalize(op);
  1143. }
  1144. var oldSnapshot = this.snapshot;
  1145. // If this throws an exception, no changes should have been made to the doc
  1146. this.snapshot = this.type.apply(this.snapshot, op);
  1147. if (this.pendingOp !== null) {
  1148. this.pendingOp = this.type.compose(this.pendingOp, op);
  1149. } else {
  1150. this.pendingOp = op;
  1151. }
  1152. if (callback) {
  1153. this.pendingCallbacks.push(callback);
  1154. }
  1155. this.emit('change', op, oldSnapshot);
  1156. return this.delayedFlush();
  1157. }
  1158. }, {
  1159. key: 'delayedFlush',
  1160. value: function delayedFlush() {
  1161. if (this.flushTimeout == null) {
  1162. return this.flushTimeout = setTimeout(this.flush, this._flushDelay || 0);
  1163. }
  1164. }
  1165. }, {
  1166. key: 'setFlushDelay',
  1167. value: function setFlushDelay(delay) {
  1168. return this._flushDelay = delay;
  1169. }
  1170. }, {
  1171. key: 'shout',
  1172. value: function shout(msg) {
  1173. // Meta ops don't have to queue, they can go direct. Good/bad idea?
  1174. return this.connection.send({ doc: this.name, meta: { path: ['shout'], value: msg } });
  1175. }
  1176. // Open a document. The document starts closed.
  1177. }, {
  1178. key: 'open',
  1179. value: function open(callback) {
  1180. var _this3 = this;
  1181. this.autoOpen = true;
  1182. if (this.state !== 'closed') {
  1183. return;
  1184. }
  1185. var message = {
  1186. doc: this.name,
  1187. open: true
  1188. };
  1189. if (this.snapshot === undefined) {
  1190. message.snapshot = null;
  1191. }
  1192. if (this.type) {
  1193. message.type = this.type.name;
  1194. }
  1195. if (this.version != null) {
  1196. message.v = this.version;
  1197. }
  1198. if (this._create) {
  1199. message.create = true;
  1200. }
  1201. this.connection.send(message);
  1202. this.state = 'opening';
  1203. return this._openCallback = function (error) {
  1204. _this3._openCallback = null;
  1205. return typeof callback === 'function' ? callback(error) : undefined;
  1206. };
  1207. }
  1208. // Close a document.
  1209. }, {
  1210. key: 'close',
  1211. value: function close(callback) {
  1212. this.autoOpen = false;
  1213. if (this.state === 'closed') {
  1214. return typeof callback === 'function' ? callback() : undefined;
  1215. }
  1216. this.connection.send({ doc: this.name, open: false });
  1217. // Should this happen immediately or when we get open:false back from the server?
  1218. this.state = 'closed';
  1219. this.emit('closing');
  1220. return this._closeCallback = callback;
  1221. }
  1222. }]);
  1223. return Doc;
  1224. }();
  1225. // Make documents event emitters
  1226. MicroEvent.mixin(Doc);
  1227. exports.Doc = Doc;
  1228. // This is some utility code to connect an ace editor to a sharejs document.
  1229. var _ace$require = ace.require('ace/range'),
  1230. Range = _ace$require.Range;
  1231. // Convert an ace delta into an op understood by share.js
  1232. var applyAceToShareJS = function applyAceToShareJS(editorDoc, delta, doc, fromUndo) {
  1233. // Get the start position of the range, in no. of characters
  1234. var getStartOffsetPosition = function getStartOffsetPosition(start) {
  1235. // This is quite inefficient - getLines makes a copy of the entire
  1236. // lines array in the document. It would be nice if we could just
  1237. // access them directly.
  1238. var lines = editorDoc.getLines(0, start.row);
  1239. var offset = 0;
  1240. for (var i = 0; i < lines.length; i++) {
  1241. var line = lines[i];
  1242. offset += i < start.row ? line.length : start.column;
  1243. }
  1244. // Add the row number to include newlines.
  1245. return offset + start.row;
  1246. };
  1247. var pos = getStartOffsetPosition(delta.start);
  1248. // NOTE: Keep in sync with EditorWatchdogManager.
  1249. switch (delta.action) {
  1250. case 'insert':
  1251. text = delta.lines.join('\n');
  1252. doc.insert(pos, text, fromUndo);
  1253. break;
  1254. case 'remove':
  1255. text = delta.lines.join('\n');
  1256. doc.del(pos, text.length, fromUndo);
  1257. break;
  1258. default:
  1259. throw new Error('unknown action: ' + delta.action);
  1260. }
  1261. };
  1262. // Attach an ace editor to the document. The editor's contents are replaced
  1263. // with the document's contents.
  1264. window.sharejs.extendDoc('attach_ace', function (editor, maxDocLength) {
  1265. if (!this.provides['text']) {
  1266. throw new Error('Only text documents can be attached to ace');
  1267. }
  1268. var doc = this;
  1269. var editorDoc = editor.getSession().getDocument();
  1270. editorDoc.setNewLineMode('unix');
  1271. function check() {
  1272. return window.setTimeout(function () {
  1273. var editorText = editorDoc.getValue();
  1274. var otText = doc.getText();
  1275. if (editorText !== otText) {
  1276. doc.emit('error','Text does not match in ace')
  1277. console.error('Text does not match!');
  1278. console.error('editor: ' + editorText);
  1279. return console.error('ot: ' + otText);
  1280. }
  1281. }
  1282. // Should probably also replace the editor text with the doc snapshot.
  1283. , 0);
  1284. };
  1285. onDelete(0, editorDoc.getValue());
  1286. onInsert(0, doc.getText());
  1287. check();
  1288. // Listen for edits in ace
  1289. function editorListener(change) {
  1290. if (change.origin === 'remote') {
  1291. // this change has been injected via sharejs
  1292. return;
  1293. }
  1294. if (maxDocLength != null && editorDoc.getValue().length >= maxDocLength) {
  1295. doc.emit('error', new Error('document length is greater than maxDocLength'));
  1296. return;
  1297. }
  1298. var fromUndo = !!(editor.getSession().$fromUndo || editor.getSession().$fromReject);
  1299. applyAceToShareJS(editorDoc, change, doc, fromUndo);
  1300. return check();
  1301. };
  1302. editorDoc.on('change', editorListener);
  1303. // Horribly inefficient.
  1304. function offsetToPos(offset) {
  1305. // Again, very inefficient.
  1306. var lines = editorDoc.getAllLines();
  1307. var row = 0;
  1308. for (row = 0; row < lines.length; row++) {
  1309. var line = lines[row];
  1310. if (offset <= line.length) {
  1311. break;
  1312. }
  1313. // +1 for the newline.
  1314. offset -= lines[row].length + 1;
  1315. }
  1316. return { row: row, column: offset };
  1317. };
  1318. // We want to insert the flag `origin: 'remote'` into the delta if the op
  1319. // is the initial document write or comes from the underlying sharejs doc
  1320. // (which means it is from a remote op), so we have to do the work of
  1321. // editorDoc.insert and editorDoc.remove manually.
  1322. // These methods are copied from ace.js doc#insert and #remove, and then
  1323. // inject the `origin: 'remote'` flag into the delta.
  1324. function onInsert(pos, text) {
  1325. if (editorDoc.getLength() <= 1) {
  1326. editorDoc.$detectNewLine(text);
  1327. }
  1328. var lines = editorDoc.$split(text);
  1329. var position = offsetToPos(pos);
  1330. var start = editorDoc.clippedPos(position.row, position.column);
  1331. var end = {
  1332. row: start.row + lines.length - 1,
  1333. column: (lines.length === 1 ? start.column : 0) + lines[lines.length - 1].length
  1334. };
  1335. editorDoc.applyDelta({
  1336. start: start,
  1337. end: end,
  1338. action: 'insert',
  1339. lines: lines,
  1340. origin: 'remote'
  1341. });
  1342. return check();
  1343. };
  1344. function onDelete(pos, text) {
  1345. var range = Range.fromPoints(offsetToPos(pos), offsetToPos(pos + text.length));
  1346. var start = editorDoc.clippedPos(range.start.row, range.start.column);
  1347. var end = editorDoc.clippedPos(range.end.row, range.end.column);
  1348. editorDoc.applyDelta({
  1349. start: start,
  1350. end: end,
  1351. action: 'remove',
  1352. lines: editorDoc.getLinesForRange({ start: start, end: end }),
  1353. origin: 'remote'
  1354. });
  1355. return check();
  1356. };
  1357. doc.on('insert', onInsert);
  1358. doc.on('delete', onDelete);
  1359. doc.detach_ace = function () {
  1360. doc.removeListener('insert', onInsert);
  1361. doc.removeListener('delete', onDelete);
  1362. editorDoc.removeListener('change', editorListener);
  1363. return delete doc.detach_ace;
  1364. };
  1365. });
  1366. // This is some utility code to connect a CodeMirror editor
  1367. // to a sharejs document.
  1368. // It is heavily inspired from the Ace editor hook.
  1369. // Convert a CodeMirror delta into an op understood by share.js
  1370. var applyCMToShareJS = function applyCMToShareJS(editorDoc, delta, doc, fromUndo) {
  1371. // CodeMirror deltas give a text replacement.
  1372. // I tuned this operation a little bit, for speed.
  1373. var startPos = 0; // Get character position from # of chars in each line.
  1374. var i = 0; // i goes through all lines.
  1375. // Compute the position from the shareJS snapshot because we are in the CodeMirror
  1376. // change event, where the change has already been applied to the editorDoc
  1377. var docLines = doc.snapshot.split('\n', delta.from.line) // only split the document as far as we need to
  1378. while (i < delta.from.line) {
  1379. startPos += docLines[i].length + 1; // Add 1 for '\n'
  1380. i++;
  1381. }
  1382. startPos += delta.from.ch;
  1383. // NOTE: Keep in sync with EditorWatchdogManager.
  1384. if (delta.removed) {
  1385. doc.del(startPos, delta.removed.join('\n').length, fromUndo);
  1386. }
  1387. if (delta.text) {
  1388. return doc.insert(startPos, delta.text.join('\n'), fromUndo);
  1389. }
  1390. };
  1391. // Attach a CodeMirror editor to the document. The editor's contents are replaced
  1392. // with the document's contents.
  1393. // NOTE: When upgrading CM, make sure to check for new special cases of
  1394. // origin prefixes as documented for `doc.setSelection`. We are using
  1395. // a custom `origin: 'remote'` which may conflict.
  1396. // Perma link of the docs at the time of writing this note:
  1397. // https://web.archive.org/web/20201029163528/https://codemirror.net/doc/manual.html#selection_origin
  1398. window.sharejs.extendDoc('attach_cm', function (editor, maxDocLength) {
  1399. if (!this.provides.text) {
  1400. throw new Error('Only text documents can be attached to CodeMirror2');
  1401. }
  1402. var sharedoc = this;
  1403. var editorDoc = editor.getDoc();
  1404. function check() {
  1405. return window.setTimeout(function () {
  1406. var editorText = editor.getValue();
  1407. var otText = sharedoc.getText();
  1408. if (editorText !== otText) {
  1409. sharedoc.emit('error','Text does not match in CodeMirror')
  1410. console.error('Text does not match!');
  1411. console.error('editor: ' + editorText);
  1412. return console.error('ot: ' + otText);
  1413. }
  1414. }
  1415. // Removed editor.setValue here as it would cause recursive loops if
  1416. // consistency check failed - because setting the value would trigger
  1417. // the change event
  1418. , 0);
  1419. };
  1420. onDelete(0, editor.getValue());
  1421. onInsert(0, sharedoc.getText());
  1422. check();
  1423. // Listen for edits in CodeMirror.
  1424. function editorListener(ed, change) {
  1425. if (change.origin === 'remote') {
  1426. // this change has been injected via sharejs
  1427. return;
  1428. }
  1429. if (maxDocLength != null && editorDoc.getValue().length >= maxDocLength) {
  1430. sharedoc.emit('error', new Error('document length is greater than maxDocLength'));
  1431. return;
  1432. }
  1433. var fromUndo = (change.origin === 'undo')
  1434. applyCMToShareJS(editorDoc, change, sharedoc, fromUndo);
  1435. return check();
  1436. };
  1437. editorDoc.on('change', editorListener);
  1438. function onInsert(pos, text) {
  1439. // All the primitives we need are already in CM's API.
  1440. // call signature: editor.replaceRange(text, from, to, origin)
  1441. editor.replaceRange(text, editor.posFromIndex(pos), undefined, 'remote');
  1442. // Clear CM's undo/redo history on remote edit. This prevents issues where
  1443. // a user can accidentally remove another user's edits
  1444. editor.clearHistory();
  1445. return check();
  1446. };
  1447. function onDelete(pos, text) {
  1448. var from = editor.posFromIndex(pos);
  1449. var to = editor.posFromIndex(pos + text.length);
  1450. editor.replaceRange('', from, to, 'remote');
  1451. // Clear CM's undo/redo history on remote edit. This prevents issues where
  1452. // a user can accidentally remove another user's edits
  1453. editor.clearHistory()
  1454. return check();
  1455. };
  1456. this.on('insert', onInsert);
  1457. this.on('delete', onDelete);
  1458. this.detach_cm = function () {
  1459. this.removeListener('insert', onInsert);
  1460. this.removeListener('delete', onDelete);
  1461. editorDoc.off('change', editorListener);
  1462. return delete this.detach_cm;
  1463. };
  1464. });
  1465. return window.sharejs;
  1466. });