sharejs.js 55 KB

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