DiffSync.js 35 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577
  1. /**
  2. * Copyright (c) 2006-2017, JGraph Ltd
  3. * Copyright (c) 2006-2017, Gaudenz Alder
  4. */
  5. /**
  6. * Removes all labels, user objects and styles from the given node in-place.
  7. */
  8. EditorUi.DIFF_INSERT = 'i';
  9. /**
  10. * Removes all labels, user objects and styles from the given node in-place.
  11. */
  12. EditorUi.DIFF_REMOVE = 'r';
  13. /**
  14. * Removes all labels, user objects and styles from the given node in-place.
  15. */
  16. EditorUi.DIFF_UPDATE = 'u';
  17. /**
  18. * Shared codec.
  19. */
  20. EditorUi.transientViewStateProperties = ['defaultParent', 'currentRoot', 'scrollLeft',
  21. 'scrollTop', 'scale', 'translate', 'lastPasteXml', 'pasteCounter'];
  22. /**
  23. * Contains all view state properties that should not be ignored in diff sync.
  24. */
  25. EditorUi.prototype.viewStateProperties = {background: true, backgroundImage: true, shadowVisible: true,
  26. foldingEnabled: true, pageScale: true, mathEnabled: true, pageFormat: true, extFonts: true};
  27. /**
  28. * Contains all known cell properties that should be ignored for a generic cell diff.
  29. */
  30. EditorUi.prototype.cellProperties = {id: true, value: true, xmlValue: true, vertex: true, edge: true,
  31. visible: true, collapsed: true, connectable: true, parent: true, children: true, previous: true,
  32. source: true, target: true, edges: true, geometry: true, style: true, overlays: true,
  33. mxObjectId: true, mxTransient: true};
  34. /**
  35. * Shared codec.
  36. */
  37. EditorUi.prototype.codec = new mxCodec();
  38. /**
  39. * Applies the given patches to the given pages.
  40. */
  41. EditorUi.prototype.applyPatches = function(pages, patches, markPages, resolver, updateEdgeParents)
  42. {
  43. if (patches != null)
  44. {
  45. for (var i = 0; i < patches.length; i++)
  46. {
  47. if (patches[i] != null)
  48. {
  49. pages = this.patchPages(pages, patches[i],
  50. markPages, resolver, updateEdgeParents);
  51. }
  52. }
  53. }
  54. return pages;
  55. };
  56. /**
  57. * Removes all labels, user objects and styles from the given node in-place.
  58. */
  59. EditorUi.prototype.patchPages = function(pages, diff, markPages, resolver, updateEdgeParents)
  60. {
  61. var resolverLookup = {};
  62. var newPages = [];
  63. var inserted = {};
  64. var removed = {};
  65. var lookup = {};
  66. var moved = {};
  67. if (resolver != null && resolver[EditorUi.DIFF_UPDATE] != null)
  68. {
  69. for (var id in resolver[EditorUi.DIFF_UPDATE])
  70. {
  71. resolverLookup[id] = resolver[EditorUi.DIFF_UPDATE][id];
  72. }
  73. }
  74. if (diff[EditorUi.DIFF_REMOVE] != null)
  75. {
  76. for (var i = 0; i < diff[EditorUi.DIFF_REMOVE].length; i++)
  77. {
  78. removed[diff[EditorUi.DIFF_REMOVE][i]] = true;
  79. }
  80. }
  81. if (diff[EditorUi.DIFF_INSERT] != null)
  82. {
  83. for (var i = 0; i < diff[EditorUi.DIFF_INSERT].length; i++)
  84. {
  85. inserted[diff[EditorUi.DIFF_INSERT][i].previous] = diff[EditorUi.DIFF_INSERT][i];
  86. }
  87. }
  88. if (diff[EditorUi.DIFF_UPDATE] != null)
  89. {
  90. for (var id in diff[EditorUi.DIFF_UPDATE])
  91. {
  92. var pageDiff = diff[EditorUi.DIFF_UPDATE][id];
  93. if (pageDiff.previous != null)
  94. {
  95. moved[pageDiff.previous] = id;
  96. }
  97. }
  98. }
  99. // Restores existing order and creates lookup
  100. if (pages != null)
  101. {
  102. var prev = '';
  103. for (var i = 0; i < pages.length; i++)
  104. {
  105. var pageId = pages[i].getId();
  106. lookup[pageId] = pages[i];
  107. if (moved[prev] == null && !removed[pageId] &&
  108. (diff[EditorUi.DIFF_UPDATE] == null ||
  109. diff[EditorUi.DIFF_UPDATE][pageId] == null ||
  110. diff[EditorUi.DIFF_UPDATE][pageId].previous == null))
  111. {
  112. moved[prev] = pageId;
  113. }
  114. prev = pageId;
  115. }
  116. }
  117. // FIXME: Workaround for possible duplicate pages
  118. var added = {};
  119. var addPage = mxUtils.bind(this, function(page)
  120. {
  121. var id = (page != null) ? page.getId() : '';
  122. if (page != null && !added[id])
  123. {
  124. added[id] = true;
  125. newPages.push(page);
  126. var pageDiff = (diff[EditorUi.DIFF_UPDATE] != null) ?
  127. diff[EditorUi.DIFF_UPDATE][id] : null;
  128. if (pageDiff != null)
  129. {
  130. this.updatePageRoot(page);
  131. if (pageDiff.name != null)
  132. {
  133. page.setName(pageDiff.name);
  134. }
  135. if (pageDiff.view != null)
  136. {
  137. this.patchViewState(page, pageDiff.view);
  138. }
  139. if (pageDiff.cells != null)
  140. {
  141. this.patchPage(page, pageDiff.cells,
  142. resolverLookup[page.getId()],
  143. updateEdgeParents);
  144. }
  145. if (markPages && (pageDiff.cells != null ||
  146. pageDiff.view != null))
  147. {
  148. page.needsUpdate = true;
  149. }
  150. }
  151. }
  152. var mov = moved[id];
  153. if (mov != null)
  154. {
  155. delete moved[id];
  156. addPage(lookup[mov]);
  157. }
  158. var ins = inserted[id];
  159. if (ins != null)
  160. {
  161. delete inserted[id];
  162. insertPage(ins);
  163. }
  164. });
  165. var insertPage = mxUtils.bind(this, function(ins)
  166. {
  167. var diagram = mxUtils.parseXml(ins.data).documentElement;
  168. var newPage = new DiagramPage(diagram);
  169. this.updatePageRoot(newPage);
  170. var page = lookup[newPage.getId()];
  171. if (page == null)
  172. {
  173. addPage(newPage);
  174. }
  175. else
  176. {
  177. this.patchPage(page, this.diffPages([page], [newPage]),
  178. resolverLookup[page.getId()], updateEdgeParents);
  179. if (markPages)
  180. {
  181. page.needsUpdate = true;
  182. }
  183. }
  184. });
  185. addPage();
  186. // Handles orphaned moved pages
  187. for (var id in moved)
  188. {
  189. addPage(lookup[moved[id]]);
  190. delete moved[id];
  191. }
  192. // Handles orphaned inserted pages
  193. for (var id in inserted)
  194. {
  195. insertPage(inserted[id]);
  196. delete inserted[id];
  197. }
  198. return newPages;
  199. };
  200. /**
  201. * Removes all labels, user objects and styles from the given node in-place.
  202. */
  203. EditorUi.prototype.patchViewState = function(page, diff)
  204. {
  205. if (page.viewState != null && diff != null)
  206. {
  207. if (page == this.currentPage)
  208. {
  209. page.viewState = this.editor.graph.getViewState();
  210. }
  211. for (var key in diff)
  212. {
  213. try
  214. {
  215. this.patchViewStateProperty(page, diff, key);
  216. }
  217. catch(e) {} //Ignore TODO Is this correct, we encountered an undefined value for a key (extFonts)
  218. }
  219. if (page == this.currentPage)
  220. {
  221. this.editor.graph.setViewState(page.viewState, true);
  222. }
  223. }
  224. };
  225. /**
  226. * Removes all labels, user objects and styles from the given node in-place.
  227. */
  228. EditorUi.prototype.patchViewStateProperty = function(page, diff, key)
  229. {
  230. page.viewState[key] = JSON.parse(diff[key]);
  231. };
  232. /**
  233. * Removes all labels, user objects and styles from the given node in-place.
  234. */
  235. EditorUi.prototype.createParentLookup = function(model, diff)
  236. {
  237. var parentLookup = {};
  238. function getLookup(id)
  239. {
  240. var result = parentLookup[id];
  241. if (result == null)
  242. {
  243. result = {inserted: [], moved: {}};
  244. parentLookup[id] = result;
  245. }
  246. return result;
  247. };
  248. if (diff[EditorUi.DIFF_INSERT] != null)
  249. {
  250. for (var i = 0; i < diff[EditorUi.DIFF_INSERT].length; i++)
  251. {
  252. var temp = diff[EditorUi.DIFF_INSERT][i];
  253. var par = (temp.parent != null) ? temp.parent : '';
  254. var prev = (temp.previous != null) ? temp.previous : '';
  255. getLookup(par).inserted[prev] = temp;
  256. }
  257. }
  258. if (diff[EditorUi.DIFF_UPDATE] != null)
  259. {
  260. for (var id in diff[EditorUi.DIFF_UPDATE])
  261. {
  262. var temp = diff[EditorUi.DIFF_UPDATE][id];
  263. if (temp.previous != null)
  264. {
  265. var par = temp.parent;
  266. if (par == null)
  267. {
  268. var cell = model.getCell(id);
  269. if (cell != null)
  270. {
  271. var parent = model.getParent(cell);
  272. if (parent != null)
  273. {
  274. par = parent.getId();
  275. }
  276. }
  277. }
  278. if (par != null)
  279. {
  280. getLookup(par).moved[temp.previous] = id;
  281. }
  282. }
  283. }
  284. }
  285. return parentLookup;
  286. };
  287. /**
  288. * Removes all labels, user objects and styles from the given node in-place.
  289. */
  290. EditorUi.prototype.patchPage = function(page, diff, resolver, updateEdgeParents)
  291. {
  292. var model = (page == this.currentPage) ? this.editor.graph.model : new mxGraphModel(page.root);
  293. var parentLookup = this.createParentLookup(model, diff);
  294. model.beginUpdate();
  295. try
  296. {
  297. // Disables or delays update of edge parents to after patch
  298. var prev = model.updateEdgeParent;
  299. var dict = new mxDictionary();
  300. var pendingUpdates = [];
  301. model.updateEdgeParent = function(edge, root)
  302. {
  303. if (!dict.get(edge) && updateEdgeParents)
  304. {
  305. dict.put(edge, true);
  306. pendingUpdates.push(edge);
  307. }
  308. };
  309. // Handles new root cells
  310. var temp = parentLookup[''];
  311. var cellDiff = (temp != null && temp.inserted != null) ? temp.inserted[''] : null;
  312. var root = null;
  313. if (cellDiff != null)
  314. {
  315. root = this.getCellForJson(cellDiff);
  316. }
  317. // Handles cells becoming root
  318. if (root == null)
  319. {
  320. var id = (temp != null && temp.moved != null) ? temp.moved[''] : null;
  321. if (id != null)
  322. {
  323. root = model.getCell(id);
  324. }
  325. }
  326. if (root != null)
  327. {
  328. model.setRoot(root);
  329. page.root = root;
  330. EditorUi.debug('EditorUi.patchPage: Root changed', root.id);
  331. }
  332. // Inserts and updates previous and parent (hierarchy update)
  333. this.patchCellRecursive(page, model, model.root, parentLookup, diff);
  334. // Removes cells after parents have been updated above
  335. if (diff[EditorUi.DIFF_REMOVE] != null)
  336. {
  337. for (var i = 0; i < diff[EditorUi.DIFF_REMOVE].length; i++)
  338. {
  339. var id = diff[EditorUi.DIFF_REMOVE][i];
  340. var cell = model.getCell(id);
  341. if (cell != null)
  342. {
  343. model.remove(cell);
  344. }
  345. }
  346. }
  347. // Updates cell states and terminals
  348. if (diff[EditorUi.DIFF_UPDATE] != null)
  349. {
  350. var res = (resolver != null && resolver.cells != null) ?
  351. resolver.cells[EditorUi.DIFF_UPDATE] : null;
  352. for (var id in diff[EditorUi.DIFF_UPDATE])
  353. {
  354. var cell = model.getCell(id);
  355. if (cell != null)
  356. {
  357. this.patchCell(model, cell,
  358. diff[EditorUi.DIFF_UPDATE][id],
  359. (res != null) ? res[id] : null);
  360. }
  361. else
  362. {
  363. EditorUi.debug('EditorUi.patchPage: Updated cell not found',
  364. id, 'diff', [diff[EditorUi.DIFF_UPDATE][id]]);
  365. }
  366. }
  367. }
  368. // Updates terminals for inserted cells
  369. if (diff[EditorUi.DIFF_INSERT] != null)
  370. {
  371. for (var i = 0; i < diff[EditorUi.DIFF_INSERT].length; i++)
  372. {
  373. var cellDiff = diff[EditorUi.DIFF_INSERT][i];
  374. var cell = model.getCell(cellDiff.id);
  375. if (cell != null)
  376. {
  377. model.setTerminal(cell, model.getCell(cellDiff.source), true);
  378. model.setTerminal(cell, model.getCell(cellDiff.target), false);
  379. }
  380. }
  381. }
  382. // Delayed update of edge parents
  383. model.updateEdgeParent = prev;
  384. if (updateEdgeParents && pendingUpdates.length > 0)
  385. {
  386. for (var i = 0; i < pendingUpdates.length; i++)
  387. {
  388. if (model.contains(pendingUpdates[i]))
  389. {
  390. model.updateEdgeParent(pendingUpdates[i]);
  391. }
  392. }
  393. }
  394. }
  395. finally
  396. {
  397. model.endUpdate();
  398. }
  399. };
  400. /**
  401. * Removes all labels, user objects and styles from the given node in-place.
  402. */
  403. EditorUi.prototype.patchCellRecursive = function(page, model, cell, parentLookup, diff)
  404. {
  405. if (cell != null)
  406. {
  407. var temp = parentLookup[cell.getId()];
  408. var inserted = (temp != null && temp.inserted != null) ? temp.inserted : {};
  409. var moved = (temp != null && temp.moved != null) ? temp.moved : {};
  410. var index = 0;
  411. // Restores existing order
  412. var childCount = model.getChildCount(cell);
  413. var prev = '';
  414. for (var i = 0; i < childCount; i++)
  415. {
  416. var cellId = model.getChildAt(cell, i).getId();
  417. if (moved[prev] == null &&
  418. (diff[EditorUi.DIFF_UPDATE] == null ||
  419. diff[EditorUi.DIFF_UPDATE][cellId] == null ||
  420. (diff[EditorUi.DIFF_UPDATE][cellId].previous == null &&
  421. diff[EditorUi.DIFF_UPDATE][cellId].parent == null)))
  422. {
  423. moved[prev] = cellId;
  424. }
  425. prev = cellId;
  426. }
  427. var addCell = mxUtils.bind(this, function(child, insert)
  428. {
  429. var id = (child != null) ? child.getId() : '';
  430. if (id == null)
  431. {
  432. EditorUi.debug('EditorUi.patchCellRecursive: Inserting cell with null id',
  433. 'cell', child);
  434. }
  435. // Ignores the insert if the cell is already in the model
  436. if (child != null && insert)
  437. {
  438. var ex = model.getCell(id);
  439. if (ex != null && ex != child)
  440. {
  441. child = null;
  442. }
  443. }
  444. if (child != null)
  445. {
  446. if (model.getChildAt(cell, index) != child)
  447. {
  448. model.add(cell, child, index);
  449. }
  450. this.patchCellRecursive(page, model,
  451. child, parentLookup, diff);
  452. index++;
  453. }
  454. return id;
  455. });
  456. // Uses stack to avoid recursion for children
  457. var children = [null];
  458. while (children.length > 0)
  459. {
  460. var entry = children.shift();
  461. var child = (entry != null) ? entry.child : null;
  462. var insert = (entry != null) ? entry.insert : false;
  463. var id = addCell(child, insert);
  464. // Move and insert are mutually exclusive per predecessor
  465. // since an insert changes the predecessor of existing cells
  466. // and is therefore ignored in the loop above where the order
  467. // for existing cells is added to the moved object
  468. var mov = moved[id];
  469. if (mov != null)
  470. {
  471. delete moved[id];
  472. children.push({child: model.getCell(mov)});
  473. }
  474. var ins = inserted[id];
  475. if (ins != null)
  476. {
  477. delete inserted[id];
  478. children.push({child: this.getCellForJson(ins), insert: true});
  479. }
  480. // Orphaned moves and inserts are operations where the previous cell vanished
  481. // in the local model so their position in the child array cannot be determined.
  482. // In this case those cells are appended. Dependencies between orphans are
  483. // maintained because for-in loops enumerate the IDs in order of insertion.
  484. if (children.length == 0)
  485. {
  486. // Handles orphaned moved pages
  487. for (var id in moved)
  488. {
  489. children.push({child: model.getCell(moved[id])});
  490. delete moved[id];
  491. }
  492. // Handles orphaned inserted pages
  493. for (var id in inserted)
  494. {
  495. children.push({child: this.getCellForJson(inserted[id]), insert: true});
  496. delete inserted[id];
  497. }
  498. }
  499. }
  500. }
  501. };
  502. /**
  503. * Removes all labels, user objects and styles from the given node in-place.
  504. */
  505. EditorUi.prototype.patchCell = function(model, cell, diff, resolve)
  506. {
  507. if (cell != null && diff != null)
  508. {
  509. // Last write wins for value except if label is empty
  510. if (resolve == null || (resolve.xmlValue == null &&
  511. (resolve.value == null || resolve.value == '')))
  512. {
  513. if ('value' in diff)
  514. {
  515. model.setValue(cell, diff.value);
  516. }
  517. else if (diff.xmlValue != null)
  518. {
  519. model.setValue(cell, mxUtils.parseXml(diff.xmlValue).documentElement);
  520. }
  521. }
  522. // Last write wins for style
  523. if ((resolve == null || resolve.style == null) && diff.style != null)
  524. {
  525. model.setStyle(cell, diff.style);
  526. }
  527. if (diff.visible != null)
  528. {
  529. model.setVisible(cell, diff.visible == 1);
  530. }
  531. if (diff.collapsed != null)
  532. {
  533. model.setCollapsed(cell, diff.collapsed == 1);
  534. }
  535. if (diff.vertex != null)
  536. {
  537. // Changes vertex state in-place
  538. cell.vertex = diff.vertex == 1;
  539. }
  540. if (diff.edge != null)
  541. {
  542. // Changes edge state in-place
  543. cell.edge = diff.edge == 1;
  544. }
  545. if (diff.connectable != null)
  546. {
  547. // Changes connectable state in-place
  548. cell.connectable = diff.connectable == 1;
  549. }
  550. if (diff.geometry != null)
  551. {
  552. model.setGeometry(cell, this.codec.decode(mxUtils.parseXml(
  553. diff.geometry).documentElement));
  554. }
  555. if (diff.source != null)
  556. {
  557. model.setTerminal(cell, model.getCell(diff.source), true);
  558. }
  559. if (diff.target != null)
  560. {
  561. model.setTerminal(cell, model.getCell(diff.target), false);
  562. }
  563. for (var key in diff)
  564. {
  565. if (!this.cellProperties[key])
  566. {
  567. cell[key] = diff[key];
  568. }
  569. }
  570. }
  571. };
  572. /**
  573. * Returns the pages for the given XML string.
  574. */
  575. EditorUi.prototype.getXmlForPages = function(pages)
  576. {
  577. var node = this.getNodeForPages(pages);
  578. var result = null;
  579. if (node != null)
  580. {
  581. result = mxUtils.getXml(node);
  582. }
  583. return result;
  584. };
  585. /**
  586. * Returns the pages for the given XML string.
  587. */
  588. EditorUi.prototype.getNodeForPages = function(pages)
  589. {
  590. var result = null;
  591. if (this.fileNode != null && pages != null)
  592. {
  593. result = this.fileNode.cloneNode(false);
  594. for (var i = 0; i < pages.length; i++)
  595. {
  596. var enc = new mxCodec(mxUtils.createXmlDocument());
  597. var temp = enc.encode(new mxGraphModel(pages[i].root));
  598. this.editor.graph.saveViewState(pages[i].viewState, temp);
  599. var node = pages[i].node.cloneNode(false);
  600. node.appendChild(temp);
  601. result.appendChild(node);
  602. }
  603. }
  604. return result;
  605. };
  606. /**
  607. * Returns the pages for the given XML string.
  608. */
  609. EditorUi.prototype.getPagesForXml = function(data)
  610. {
  611. var doc = mxUtils.parseXml(data);
  612. return this.getPagesForNode(doc.documentElement);
  613. };
  614. /**
  615. * Returns the pages for the given node.
  616. */
  617. EditorUi.prototype.getPagesForNode = function(node, nodeName)
  618. {
  619. var tmp = this.editor.extractGraphModel(node, true, true);
  620. if (tmp != null)
  621. {
  622. node = tmp;
  623. }
  624. var diagrams = node.getElementsByTagName(nodeName || 'diagram');
  625. var pages = [];
  626. if (diagrams.length > 0)
  627. {
  628. for (var i = 0; i < diagrams.length; i++)
  629. {
  630. var page = new DiagramPage(diagrams[i]);
  631. this.updatePageRoot(page, true);
  632. pages.push(page);
  633. }
  634. }
  635. else if (node.nodeName == 'mxGraphModel')
  636. {
  637. var page = new DiagramPage(node.ownerDocument.createElement('diagram'));
  638. page.setName(mxResources.get('pageWithNumber', [1]));
  639. page.node.appendChild(node.cloneNode(true));
  640. pages.push(page);
  641. }
  642. return pages;
  643. };
  644. /**
  645. * Removes all labels, user objects and styles from the given node in-place.
  646. */
  647. EditorUi.prototype.diffPages = function(oldPages, newPages)
  648. {
  649. var inserted = [];
  650. var removed = [];
  651. var result = {};
  652. var lookup = {};
  653. var diff = {};
  654. var prev = null;
  655. if (oldPages != null && newPages != null)
  656. {
  657. for (var i = 0; i < newPages.length; i++)
  658. {
  659. lookup[newPages[i].getId()] = {page: newPages[i], prev: prev};
  660. prev = newPages[i];
  661. }
  662. prev = null;
  663. for (var i = 0; i < oldPages.length; i++)
  664. {
  665. var id = oldPages[i].getId();
  666. var newPage = lookup[id];
  667. if (newPage == null)
  668. {
  669. removed.push(id);
  670. }
  671. else
  672. {
  673. this.updatePageRoot(oldPages[i]);
  674. this.updatePageRoot(newPage.page);
  675. var temp = this.diffCells(oldPages[i].root, newPage.page.root);
  676. var pageDiff = {};
  677. if (!mxUtils.isEmptyObject(temp))
  678. {
  679. pageDiff.cells = temp;
  680. }
  681. var view = this.diffViewState(oldPages[i], newPage.page);
  682. if (!mxUtils.isEmptyObject(view))
  683. {
  684. pageDiff.view = view;
  685. }
  686. if (((newPage.prev != null) ? prev == null : prev != null) ||
  687. (prev != null && newPage.prev != null &&
  688. prev.getId() != newPage.prev.getId()))
  689. {
  690. pageDiff.previous = (newPage.prev != null) ? newPage.prev.getId() : '';
  691. }
  692. // FIXME: Check why names can be null in newer files
  693. // ignore in hash and do not diff null names for now
  694. if (newPage.page.getName() != null &&
  695. oldPages[i].getName() != newPage.page.getName())
  696. {
  697. pageDiff.name = newPage.page.getName();
  698. }
  699. if (!mxUtils.isEmptyObject(pageDiff))
  700. {
  701. diff[id] = pageDiff;
  702. }
  703. }
  704. delete lookup[oldPages[i].getId()];
  705. prev = oldPages[i];
  706. }
  707. for (var id in lookup)
  708. {
  709. var newPage = lookup[id];
  710. inserted.push({id: newPage.page.getId(),
  711. data: mxUtils.getXml(newPage.page.node),
  712. previous: (newPage.prev != null) ?
  713. newPage.prev.getId() : ''});
  714. }
  715. if (!mxUtils.isEmptyObject(diff))
  716. {
  717. result[EditorUi.DIFF_UPDATE] = diff;
  718. }
  719. if (removed.length > 0)
  720. {
  721. result[EditorUi.DIFF_REMOVE] = removed;
  722. }
  723. if (inserted.length > 0)
  724. {
  725. result[EditorUi.DIFF_INSERT] = inserted;
  726. }
  727. }
  728. return result;
  729. };
  730. /**
  731. * Removes all labels, user objects and styles from the given node in-place.
  732. */
  733. EditorUi.prototype.createCellLookup = function(cell, prev, lookup)
  734. {
  735. lookup = (lookup != null) ? lookup : {};
  736. if (cell.getId() == null)
  737. {
  738. EditorUi.debug('EditorUi.createCellLookup: Ignored inserted cell with no id', cell);
  739. }
  740. else
  741. {
  742. lookup[cell.getId()] = {cell: cell, prev: prev};
  743. }
  744. var childCount = cell.getChildCount();
  745. prev = null;
  746. for (var i = 0; i < childCount; i++)
  747. {
  748. var child = cell.getChildAt(i);
  749. this.createCellLookup(child, prev, lookup);
  750. prev = child;
  751. }
  752. return lookup;
  753. };
  754. /**
  755. * Removes all labels, user objects and styles from the given node in-place.
  756. */
  757. EditorUi.prototype.diffCellRecursive = function(cell, prev, lookup, diff, removed)
  758. {
  759. diff = (diff != null) ? diff : {};
  760. var newCell = lookup[cell.getId()];
  761. delete lookup[cell.getId()];
  762. if (newCell == null)
  763. {
  764. if (cell.getId() == null)
  765. {
  766. EditorUi.debug('EditorUi.diffCellRecursive: Ignored removed cell with no id', cell);
  767. }
  768. else
  769. {
  770. removed.push(cell.getId());
  771. }
  772. }
  773. else
  774. {
  775. var temp = this.diffCell(cell, newCell.cell);
  776. if (temp.parent != null ||
  777. (((newCell.prev != null) ? prev == null : prev != null) ||
  778. (prev != null && newCell.prev != null &&
  779. prev.getId() != newCell.prev.getId())))
  780. {
  781. temp.previous = (newCell.prev != null) ? newCell.prev.getId() : '';
  782. }
  783. if (!mxUtils.isEmptyObject(temp))
  784. {
  785. if (cell.getId() == null)
  786. {
  787. EditorUi.debug('EditorUi.diffCellRecursive: Ignored changed cell with no id', cell, 'diff', temp);
  788. }
  789. else
  790. {
  791. diff[cell.getId()] = temp;
  792. }
  793. }
  794. }
  795. var childCount = cell.getChildCount();
  796. prev = null;
  797. for (var i = 0; i < childCount; i++)
  798. {
  799. var child = cell.getChildAt(i);
  800. this.diffCellRecursive(child, prev, lookup, diff, removed);
  801. prev = child;
  802. }
  803. return diff;
  804. };
  805. /**
  806. * Removes all labels, user objects and styles from the given node in-place.
  807. */
  808. EditorUi.prototype.diffCells = function(oldRoot, newRoot)
  809. {
  810. var result = {};
  811. var inserted = [];
  812. var lookup = this.createCellLookup(newRoot);
  813. // Marks all cells as inserted if root is different
  814. if (newRoot.id == oldRoot.id)
  815. {
  816. var removed = [];
  817. var diff = this.diffCellRecursive(oldRoot, null, lookup, null, removed);
  818. if (!mxUtils.isEmptyObject(diff))
  819. {
  820. result[EditorUi.DIFF_UPDATE] = diff;
  821. }
  822. if (removed.length > 0)
  823. {
  824. result[EditorUi.DIFF_REMOVE] = removed;
  825. }
  826. }
  827. else
  828. {
  829. EditorUi.debug('EditorUi.diffCells: Root changed', newRoot.id);
  830. }
  831. for (var id in lookup)
  832. {
  833. var newCell = lookup[id];
  834. inserted.push(this.getJsonForCell(newCell.cell, newCell.prev));
  835. }
  836. if (inserted.length > 0)
  837. {
  838. result[EditorUi.DIFF_INSERT] = inserted;
  839. }
  840. return result;
  841. };
  842. /**
  843. * Removes all labels, user objects and styles from the given node in-place.
  844. */
  845. EditorUi.prototype.diffViewState = function(oldPage, newPage)
  846. {
  847. var source = oldPage.viewState;
  848. var target = newPage.viewState;
  849. var result = {};
  850. if (oldPage == this.currentPage)
  851. {
  852. source = this.editor.graph.getViewState();
  853. }
  854. if (newPage == this.currentPage)
  855. {
  856. target = this.editor.graph.getViewState();
  857. }
  858. if (source != null && target != null)
  859. {
  860. for (var key in this.viewStateProperties)
  861. {
  862. this.diffViewStateProperty(source, target, key, result);
  863. }
  864. }
  865. return result;
  866. };
  867. /**
  868. * Removes all labels, user objects and styles from the given node in-place.
  869. */
  870. EditorUi.prototype.diffViewStateProperty = function(source, target, key, result)
  871. {
  872. // LATER: Check if normalization is needed for
  873. // object attribute order to compare JSON
  874. var old = JSON.stringify(this.getViewStateProperty(source, key));
  875. var now = JSON.stringify(this.getViewStateProperty(target, key));
  876. if (old != now)
  877. {
  878. result[key] = now;
  879. }
  880. };
  881. /**
  882. * Ignores image data for background pages and normalizes extFonts.
  883. */
  884. EditorUi.prototype.getViewStateProperty = function(viewState, key)
  885. {
  886. var result = viewState[key];
  887. if (key == 'backgroundImage' && result != null &&
  888. result.originalSrc != null)
  889. {
  890. result = {originalSrc: result.originalSrc};
  891. }
  892. else if (key == 'extFonts' && result == null)
  893. {
  894. result = [];
  895. }
  896. return result;
  897. };
  898. /**
  899. * Removes all labels, user objects and styles from the given node in-place.
  900. */
  901. EditorUi.prototype.getCellForJson = function(json)
  902. {
  903. var geometry = (json.geometry != null) ? this.codec.decode(
  904. mxUtils.parseXml(json.geometry).documentElement) : null;
  905. var value = json.value;
  906. if (json.xmlValue != null)
  907. {
  908. value = mxUtils.parseXml(json.xmlValue).documentElement;
  909. }
  910. var cell = new mxCell(value, geometry, json.style);
  911. cell.connectable = json.connectable != 0;
  912. cell.collapsed = json.collapsed == 1;
  913. cell.visible = json.visible != 0;
  914. cell.vertex = json.vertex == 1;
  915. cell.edge = json.edge == 1;
  916. cell.id = json.id;
  917. for (var key in json)
  918. {
  919. if (!this.cellProperties[key])
  920. {
  921. cell[key] = json[key];
  922. }
  923. }
  924. return cell;
  925. };
  926. /**
  927. * Removes all labels, user objects and styles from the given node in-place.
  928. */
  929. EditorUi.prototype.getJsonForCell = function(cell, previous)
  930. {
  931. var result = {id: cell.getId()};
  932. if (cell.vertex)
  933. {
  934. result.vertex = 1;
  935. }
  936. if (cell.edge)
  937. {
  938. result.edge = 1;
  939. }
  940. if (!cell.connectable)
  941. {
  942. result.connectable = 0;
  943. }
  944. if (cell.parent != null)
  945. {
  946. result.parent = cell.parent.getId();
  947. }
  948. if (previous != null)
  949. {
  950. result.previous = previous.getId();
  951. }
  952. if (cell.source != null)
  953. {
  954. result.source = cell.source.getId();
  955. }
  956. if (cell.target != null)
  957. {
  958. result.target = cell.target.getId();
  959. }
  960. if (cell.style != null)
  961. {
  962. result.style = cell.style;
  963. }
  964. if (cell.geometry != null)
  965. {
  966. result.geometry = mxUtils.getXml(this.codec.encode(cell.geometry));
  967. }
  968. if (cell.collapsed)
  969. {
  970. result.collapsed = 1;
  971. }
  972. if (!cell.visible)
  973. {
  974. result.visible = 0;
  975. }
  976. if (cell.value != null)
  977. {
  978. if (typeof cell.value === 'object' && typeof cell.value.nodeType === 'number' &&
  979. typeof cell.value.nodeName === 'string' && typeof cell.value.getAttribute === 'function')
  980. {
  981. result.xmlValue = mxUtils.getXml(cell.value);
  982. }
  983. else
  984. {
  985. result.value = cell.value;
  986. }
  987. }
  988. for (var key in cell)
  989. {
  990. if (!this.cellProperties[key] &&
  991. typeof cell[key] !== 'function')
  992. {
  993. result[key] = cell[key];
  994. }
  995. }
  996. return result;
  997. };
  998. /**
  999. * Removes all labels, user objects and styles from the given node in-place.
  1000. */
  1001. EditorUi.prototype.diffCell = function(oldCell, newCell)
  1002. {
  1003. var diff = {};
  1004. if (oldCell.vertex != newCell.vertex)
  1005. {
  1006. diff.vertex = (newCell.vertex) ? 1 : 0;
  1007. }
  1008. if (oldCell.edge != newCell.edge)
  1009. {
  1010. diff.edge = (newCell.edge) ? 1 : 0;
  1011. }
  1012. if (oldCell.connectable != newCell.connectable)
  1013. {
  1014. diff.connectable = (newCell.connectable) ? 1 : 0;
  1015. }
  1016. if (((oldCell.parent != null) ? newCell.parent == null : newCell.parent != null) ||
  1017. (oldCell.parent != null && newCell.parent != null &&
  1018. oldCell.parent.getId() != newCell.parent.getId()))
  1019. {
  1020. diff.parent = (newCell.parent != null) ? newCell.parent.getId() : '';
  1021. }
  1022. if (((oldCell.source != null) ? newCell.source == null : newCell.source != null) ||
  1023. (oldCell.source != null && newCell.source != null &&
  1024. oldCell.source.getId() != newCell.source.getId()))
  1025. {
  1026. diff.source = (newCell.source != null) ? newCell.source.getId() : '';
  1027. }
  1028. if (((oldCell.target != null) ? newCell.target == null : newCell.target != null) ||
  1029. (oldCell.target != null && newCell.target != null &&
  1030. oldCell.target.getId() != newCell.target.getId()))
  1031. {
  1032. diff.target = (newCell.target != null) ? newCell.target.getId() : '';
  1033. }
  1034. function isNode(value)
  1035. {
  1036. return value != null && typeof value === 'object' && typeof value.nodeType === 'number' &&
  1037. typeof value.nodeName === 'string' && typeof value.getAttribute === 'function';
  1038. };
  1039. if (isNode(oldCell.value) && isNode(newCell.value))
  1040. {
  1041. if (!oldCell.value.isEqualNode(newCell.value))
  1042. {
  1043. diff.xmlValue = mxUtils.getXml(newCell.value);
  1044. }
  1045. }
  1046. else if (oldCell.value != newCell.value)
  1047. {
  1048. if (isNode(newCell.value))
  1049. {
  1050. diff.xmlValue = mxUtils.getXml(newCell.value);
  1051. }
  1052. else
  1053. {
  1054. diff.value = (newCell.value != null) ? newCell.value : null;
  1055. }
  1056. }
  1057. if (oldCell.style != newCell.style)
  1058. {
  1059. // LATER: Split into keys and do fine-grained diff
  1060. diff.style = newCell.style;
  1061. }
  1062. if (oldCell.visible != newCell.visible)
  1063. {
  1064. diff.visible = (newCell.visible) ? 1 : 0;
  1065. }
  1066. if (oldCell.collapsed != newCell.collapsed)
  1067. {
  1068. diff.collapsed = (newCell.collapsed) ? 1 : 0;
  1069. }
  1070. // FIXME: Proto only needed because source.geometry has no constructor (wrong type?)
  1071. if (!this.isObjectEqual(oldCell.geometry, newCell.geometry, new mxGeometry()))
  1072. {
  1073. var node = this.codec.encode(newCell.geometry);
  1074. if (node != null)
  1075. {
  1076. diff.geometry = mxUtils.getXml(node);
  1077. }
  1078. }
  1079. // Compares all keys from oldCell to newCell and uses null in the diff
  1080. // to force the attribute to be removed in the receiving client
  1081. for (var key in oldCell)
  1082. {
  1083. if (!this.cellProperties[key] && typeof oldCell[key] !== 'function' &&
  1084. typeof newCell[key] !== 'function' && oldCell[key] != newCell[key])
  1085. {
  1086. diff[key] = (newCell[key] === undefined) ? null : newCell[key];
  1087. }
  1088. }
  1089. // Compares the remaining keys in newCell with oldCell
  1090. for (var key in newCell)
  1091. {
  1092. if (!(key in oldCell) &&
  1093. !this.cellProperties[key] && typeof oldCell[key] !== 'function' &&
  1094. typeof newCell[key] !== 'function' && oldCell[key] != newCell[key])
  1095. {
  1096. diff[key] = (newCell[key] === undefined) ? null : newCell[key];
  1097. }
  1098. }
  1099. return diff;
  1100. };
  1101. /**
  1102. * Creates a patch that inserts pages and cells that are modified or referenced
  1103. * as parents or terminals in the given diff but have not been saved yet by the
  1104. * remote collaborator. These pages and cells are "adopted" by the local user.
  1105. */
  1106. EditorUi.prototype.resolveCrossReferences = function(ownDiff, theirDiff)
  1107. {
  1108. var resolve = {};
  1109. if (!mxUtils.isEmptyObject(theirDiff))
  1110. {
  1111. this.adoptTheirPages(ownDiff, theirDiff, resolve);
  1112. this.adoptTheirCells(ownDiff, theirDiff, resolve);
  1113. }
  1114. EditorUi.debug('EditorUi.resolveCrossReferences', [this],
  1115. 'ownDiff', ownDiff, 'theirDiff', theirDiff,
  1116. 'resolve', resolve);
  1117. return resolve;
  1118. };
  1119. /**
  1120. * Computes and sends the local changes if the file was changed.
  1121. */
  1122. EditorUi.prototype.adoptTheirPages = function(ownDiff, theirDiff, resolve)
  1123. {
  1124. var theirInsertedPages = {};
  1125. if (theirDiff[EditorUi.DIFF_INSERT] != null)
  1126. {
  1127. for (var i = 0; i < theirDiff[EditorUi.DIFF_INSERT].length; i++)
  1128. {
  1129. theirInsertedPages[theirDiff[EditorUi.DIFF_INSERT][i].id] =
  1130. theirDiff[EditorUi.DIFF_INSERT][i];
  1131. }
  1132. }
  1133. for (var id in ownDiff[EditorUi.DIFF_UPDATE])
  1134. {
  1135. if (theirInsertedPages[id] != null)
  1136. {
  1137. if (resolve[EditorUi.DIFF_INSERT] == null)
  1138. {
  1139. resolve[EditorUi.DIFF_INSERT] = [];
  1140. }
  1141. if (resolve[EditorUi.DIFF_UPDATE] == null)
  1142. {
  1143. resolve[EditorUi.DIFF_UPDATE] = {};
  1144. }
  1145. // Adds changed page to own pages
  1146. resolve[EditorUi.DIFF_INSERT].push(
  1147. theirInsertedPages[id]);
  1148. resolve[EditorUi.DIFF_UPDATE][id] =
  1149. ownDiff[EditorUi.DIFF_UPDATE][id];
  1150. delete ownDiff[EditorUi.DIFF_UPDATE][id];
  1151. }
  1152. }
  1153. };
  1154. /**
  1155. * Computes and sends the local changes if the file was changed.
  1156. */
  1157. EditorUi.prototype.adoptTheirCells = function(ownDiff, theirDiff, resolve)
  1158. {
  1159. for (var id in ownDiff[EditorUi.DIFF_UPDATE])
  1160. {
  1161. var ownPageUpdate = ownDiff[EditorUi.DIFF_UPDATE][id];
  1162. if (ownPageUpdate.cells != null)
  1163. {
  1164. this.adoptTheirCellsFromPage(ownPageUpdate, theirDiff, id, resolve);
  1165. }
  1166. }
  1167. };
  1168. /**
  1169. * Computes and sends the local changes if the file was changed.
  1170. */
  1171. EditorUi.prototype.adoptTheirCellsFromPage = function(ownPageUpdate, theirDiff, pageId, resolve)
  1172. {
  1173. var theirPageUpdate = theirDiff[EditorUi.DIFF_UPDATE] != null ?
  1174. theirDiff[EditorUi.DIFF_UPDATE][pageId] : null;
  1175. if (theirPageUpdate != null && theirPageUpdate.cells != null &&
  1176. theirPageUpdate.cells[EditorUi.DIFF_INSERT] != null)
  1177. {
  1178. var theirUpdatedCells = theirPageUpdate.cells[EditorUi.DIFF_UPDATE];
  1179. var theirInsertedCells = {};
  1180. for (var i = 0; i < theirPageUpdate.cells[EditorUi.DIFF_INSERT].length; i++)
  1181. {
  1182. var entry = theirPageUpdate.cells[EditorUi.DIFF_INSERT][i];
  1183. theirInsertedCells[entry.id] = entry;
  1184. }
  1185. var pageDiff = {};
  1186. pageDiff.cells = {};
  1187. pageDiff.cells[EditorUi.DIFF_INSERT] = [];
  1188. pageDiff.cells[EditorUi.DIFF_UPDATE] = {};
  1189. // Blocks duplicate inserts, deleted below for result
  1190. pageDiff.inserted = {};
  1191. this.resolveOwnInsertedCells(
  1192. ownPageUpdate.cells[EditorUi.DIFF_INSERT],
  1193. theirInsertedCells, pageDiff);
  1194. this.resolveOwnUpdatedCells(
  1195. ownPageUpdate.cells[EditorUi.DIFF_UPDATE],
  1196. theirInsertedCells, theirUpdatedCells,
  1197. pageDiff);
  1198. if (resolve[EditorUi.DIFF_UPDATE] == null)
  1199. {
  1200. resolve[EditorUi.DIFF_UPDATE] = {};
  1201. }
  1202. delete pageDiff.inserted;
  1203. resolve[EditorUi.DIFF_UPDATE][pageId] = pageDiff;
  1204. }
  1205. };
  1206. /**
  1207. * Computes and sends the local changes if the file was changed.
  1208. */
  1209. EditorUi.prototype.resolveOwnInsertedCells = function(ownInsertedCells, theirInsertedCells, pageDiff)
  1210. {
  1211. if (ownInsertedCells != null)
  1212. {
  1213. for (var i = 0; i < ownInsertedCells.length; i++)
  1214. {
  1215. var cell = ownInsertedCells[i];
  1216. if (cell != null)
  1217. {
  1218. this.adoptParentCell(cell.id, null,
  1219. theirInsertedCells, pageDiff);
  1220. this.adoptTerminalCell(cell.id, cell,
  1221. theirInsertedCells, true, pageDiff);
  1222. this.adoptTerminalCell(cell.id, cell,
  1223. theirInsertedCells, false, pageDiff);
  1224. }
  1225. }
  1226. }
  1227. };
  1228. /**
  1229. * Computes and sends the local changes if the file was changed.
  1230. */
  1231. EditorUi.prototype.resolveOwnUpdatedCells = function(ownUpdatedCells, theirInsertedCells, theirUpdatedCells, pageDiff)
  1232. {
  1233. if (ownUpdatedCells != null)
  1234. {
  1235. for (var id in ownUpdatedCells)
  1236. {
  1237. // Adds changed cell to own cells
  1238. var cell = theirInsertedCells[id];
  1239. if (cell != null)
  1240. {
  1241. if (!pageDiff.inserted[id])
  1242. {
  1243. pageDiff.cells[EditorUi.DIFF_INSERT].push(cell);
  1244. pageDiff.inserted[id] = true;
  1245. }
  1246. pageDiff.cells[EditorUi.DIFF_UPDATE][id] =
  1247. ownUpdatedCells[id];
  1248. }
  1249. else if (theirUpdatedCells != null)
  1250. {
  1251. // Adds their referenced terminals
  1252. // and parents to own cells
  1253. var theirCell = theirUpdatedCells[id];
  1254. if (theirCell != null)
  1255. {
  1256. this.adoptParentCell(id, theirCell,
  1257. theirInsertedCells, pageDiff);
  1258. this.adoptTerminalCell(id, theirCell,
  1259. theirInsertedCells, true, pageDiff, id);
  1260. this.adoptTerminalCell(id, theirCell,
  1261. theirInsertedCells, false, pageDiff, id);
  1262. }
  1263. }
  1264. }
  1265. }
  1266. };
  1267. /**
  1268. * Adds unsaved remote parents to the patch.
  1269. */
  1270. EditorUi.prototype.adoptParentCell = function(cellId, cellDiff, theirInsertedCells, pageDiff)
  1271. {
  1272. var cell = theirInsertedCells[cellId];
  1273. var parentId = (cellDiff != null) ? cellDiff.parent :
  1274. ((cell != null) ? cell.parent : null);
  1275. if (parentId != null)
  1276. {
  1277. this.adoptParentCell(parentId, null, theirInsertedCells, pageDiff);
  1278. }
  1279. if (cell != null)
  1280. {
  1281. if (!pageDiff.inserted[cellId])
  1282. {
  1283. pageDiff.cells[EditorUi.DIFF_INSERT].push(cell);
  1284. pageDiff.inserted[cellId] = true;
  1285. }
  1286. }
  1287. else if (cellDiff != null)
  1288. {
  1289. if (pageDiff.cells[EditorUi.DIFF_UPDATE][cellId] == null)
  1290. {
  1291. pageDiff.cells[EditorUi.DIFF_UPDATE][cellId] = {};
  1292. }
  1293. pageDiff.cells[EditorUi.DIFF_UPDATE][cellId] = cellDiff;
  1294. }
  1295. };
  1296. /**
  1297. * Computes and sends the local changes if the file was changed.
  1298. */
  1299. EditorUi.prototype.adoptTerminalCell = function(cellId, cell, theirInsertedCells, source, pageDiff)
  1300. {
  1301. var terminalId = (source) ? cell.source : cell.target;
  1302. var terminal = theirInsertedCells[terminalId];
  1303. if (terminal != null)
  1304. {
  1305. if (!pageDiff.inserted[terminalId])
  1306. {
  1307. pageDiff.cells[EditorUi.DIFF_INSERT].push(terminal);
  1308. pageDiff.inserted[terminalId] = true;
  1309. }
  1310. if (pageDiff.cells[EditorUi.DIFF_UPDATE][cellId] == null)
  1311. {
  1312. pageDiff.cells[EditorUi.DIFF_UPDATE][cellId] = {};
  1313. }
  1314. pageDiff.cells[EditorUi.DIFF_UPDATE][cellId]
  1315. [(source) ? 'source' : 'target'] = terminalId;
  1316. }
  1317. };
  1318. /**
  1319. *
  1320. */
  1321. EditorUi.prototype.isObjectEqual = function(source, target, proto)
  1322. {
  1323. if (source == null && target == null)
  1324. {
  1325. return true;
  1326. }
  1327. else if ((source != null) ? target == null : target != null)
  1328. {
  1329. return false;
  1330. }
  1331. else
  1332. {
  1333. var replacer = function(key, value)
  1334. {
  1335. return (proto == null || proto[key] != value) ? ((value === true) ? 1 : value) : undefined;
  1336. };
  1337. //console.log('eq', JSON.stringify(source, replacer), JSON.stringify(target, replacer));
  1338. return JSON.stringify(source, replacer) == JSON.stringify(target, replacer);
  1339. }
  1340. };