Editor.js 39 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178
  1. /**
  2. * Copyright (c) 2006-2012, JGraph Ltd
  3. */
  4. /**
  5. * Editor constructor executed on page load.
  6. */
  7. Editor = function(chromeless, themes, model, graph)
  8. {
  9. mxEventSource.call(this);
  10. this.chromeless = (chromeless != null) ? chromeless : this.chromeless;
  11. this.initStencilRegistry();
  12. this.graph = graph || this.createGraph(themes, model);
  13. this.undoManager = this.createUndoManager();
  14. this.status = '';
  15. this.getOrCreateFilename = function()
  16. {
  17. return this.filename || mxResources.get('drawing', [Editor.pageCounter]) + '.xml';
  18. };
  19. this.getFilename = function()
  20. {
  21. return this.filename;
  22. };
  23. // Sets the status and fires a statusChanged event
  24. this.setStatus = function(value)
  25. {
  26. this.status = value;
  27. this.fireEvent(new mxEventObject('statusChanged'));
  28. };
  29. // Returns the current status
  30. this.getStatus = function()
  31. {
  32. return this.status;
  33. };
  34. // Updates modified state if graph changes
  35. this.graphChangeListener = function()
  36. {
  37. this.setModified(true);
  38. };
  39. this.graph.getModel().addListener(mxEvent.CHANGE, mxUtils.bind(this, function()
  40. {
  41. this.graphChangeListener.apply(this, arguments);
  42. }));
  43. // Sets persistent graph state defaults
  44. this.graph.resetViewOnRootChange = false;
  45. this.init();
  46. };
  47. /**
  48. * Counts open editor tabs (must be global for cross-window access)
  49. */
  50. Editor.pageCounter = 0;
  51. // Cross-domain window access is not allowed in FF, so if we
  52. // were opened from another domain then this will fail.
  53. (function()
  54. {
  55. try
  56. {
  57. var op = window;
  58. while (op.opener != null && typeof op.opener.Editor !== 'undefined' &&
  59. !isNaN(op.opener.Editor.pageCounter))
  60. {
  61. op = op.opener;
  62. }
  63. // Increments the counter in the first opener in the chain
  64. if (op != null)
  65. {
  66. op.Editor.pageCounter++;
  67. Editor.pageCounter = op.Editor.pageCounter;
  68. }
  69. }
  70. catch (e)
  71. {
  72. // ignore
  73. }
  74. })();
  75. /**
  76. * Specifies if local storage should be used (eg. on the iPad which has no filesystem)
  77. */
  78. Editor.useLocalStorage = typeof(Storage) != 'undefined' && mxClient.IS_IOS;
  79. /**
  80. * Images below are for lightbox and embedding toolbars.
  81. */
  82. Editor.helpImage = (mxClient.IS_SVG) ? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAXVBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC5BxTwAAAAH3RSTlMAlUF8boNQIE0LBgOgkGlHNSwqFIx/dGVUOjApmV9ezNACSAAAAIVJREFUGNNtjNsOgzAMQ5NeoVcKDAZs+//PXLKI8YKlWvaRU7jXuFpb9qsbdK05XILUiE8JHQox1Pv3OgFUzf1AGqWqUg+QBwLF0YAeegBlCNgRWOpB5vUfTCmeoHQ/wNdy0jLH/cM+b+wLTw4n/7ACEmHVVy8h6qy8V7MNcGowWpsNbvUFcGUEdSi1s/oAAAAASUVORK5CYII=' :
  83. IMAGE_PATH + '/help.png';
  84. /**
  85. * Sets the default font size.
  86. */
  87. Editor.checkmarkImage = (mxClient.IS_SVG) ? 'data:image/gif;base64,R0lGODlhFQAVAMQfAGxsbHx8fIqKioaGhvb29nJycvr6+sDAwJqamltbW5OTk+np6YGBgeTk5Ly8vJiYmP39/fLy8qWlpa6ursjIyOLi4vj4+N/f3+3t7fT09LCwsHZ2dubm5r6+vmZmZv///yH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IFdpbmRvd3MiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OEY4NTZERTQ5QUFBMTFFMUE5MTVDOTM5MUZGMTE3M0QiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OEY4NTZERTU5QUFBMTFFMUE5MTVDOTM5MUZGMTE3M0QiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4Rjg1NkRFMjlBQUExMUUxQTkxNUM5MzkxRkYxMTczRCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4Rjg1NkRFMzlBQUExMUUxQTkxNUM5MzkxRkYxMTczRCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgH//v38+/r5+Pf29fTz8vHw7+7t7Ovq6ejn5uXk4+Lh4N/e3dzb2tnY19bV1NPS0dDPzs3My8rJyMfGxcTDwsHAv769vLu6ubi3trW0s7KxsK+urayrqqmop6alpKOioaCfnp2cm5qZmJeWlZSTkpGQj46NjIuKiYiHhoWEg4KBgH9+fXx7enl4d3Z1dHNycXBvbm1sa2ppaGdmZWRjYmFgX15dXFtaWVhXVlVUU1JRUE9OTUxLSklIR0ZFRENCQUA/Pj08Ozo5ODc2NTQzMjEwLy4tLCsqKSgnJiUkIyIhIB8eHRwbGhkYFxYVFBMSERAPDg0MCwoJCAcGBQQDAgEAACH5BAEAAB8ALAAAAAAVABUAAAVI4CeOZGmeaKqubKtylktSgCOLRyLd3+QJEJnh4VHcMoOfYQXQLBcBD4PA6ngGlIInEHEhPOANRkaIFhq8SuHCE1Hb8Lh8LgsBADs=' :
  88. IMAGE_PATH + '/checkmark.gif';
  89. /**
  90. * Images below are for lightbox and embedding toolbars.
  91. */
  92. Editor.maximizeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVBAMAAABbObilAAAAElBMVEUAAAAAAAAAAAAAAAAAAAAAAADgKxmiAAAABXRSTlMA758vX1Pw3BoAAABJSURBVAjXY8AJQkODGBhUQ0MhbAUGBiYY24CBgRnGFmZgMISwgwwDGRhEhVVBbAVmEQYGRwMmBjIAQi/CTIRd6G5AuA3dzYQBAHj0EFdHkvV4AAAAAElFTkSuQmCC';
  93. /**
  94. * Specifies the image URL to be used for the transparent background.
  95. */
  96. Editor.zoomOutImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVBAMAAABbObilAAAAElBMVEUAAAAAAAAsLCxxcXEhISFgYGChjTUxAAAAAXRSTlMAQObYZgAAAEdJREFUCNdjIAMwCQrB2YKCggJQJqMwA7MglK1owMBgqABVApITgLJZXFxgbIQ4Qj3CHIT5ggoIe5kgNkM1KSDYKBKqxPkDAPo5BAZBE54hAAAAAElFTkSuQmCC';
  97. /**
  98. * Specifies the image URL to be used for the transparent background.
  99. */
  100. Editor.zoomInImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVBAMAAABbObilAAAAElBMVEUAAAAAAAAsLCwhISFxcXFgYGBavKaoAAAAAXRSTlMAQObYZgAAAElJREFUCNdjIAMwCQrB2YKCggJQJqMIA4sglK3owMzgqABVwsDMwCgAZTMbG8PYCHGEeoQ5CPMFFRD2MkFshmpSQLBRJFSJ8wcAEqcEM2uhl2MAAAAASUVORK5CYII=';
  101. /**
  102. * Specifies the image URL to be used for the transparent background.
  103. */
  104. Editor.zoomFitImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVBAMAAABbObilAAAAD1BMVEUAAAAAAAAwMDBwcHBgYGC1xl09AAAAAXRSTlMAQObYZgAAAEFJREFUCNdjIAMwCQrB2YKCggJQJqMwA7MglK1owMBgqABVApITwMdGqEeYgzBfUAFhLxPEZqgmBQQbRUKFOH8AAK5OA3lA+FFOAAAAAElFTkSuQmCC';
  105. /**
  106. * Specifies the image URL to be used for the transparent background.
  107. */
  108. Editor.layersImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAMAAACeyVWkAAAAaVBMVEUAAAAgICAICAgdHR0PDw8WFhYICAgLCwsXFxcvLy8ODg4uLi4iIiIqKiokJCQYGBgKCgonJycFBQUCAgIqKiocHBwcHBwODg4eHh4cHBwnJycJCQkUFBQqKiojIyMuLi4ZGRkgICAEBATOWYXAAAAAGnRSTlMAD7+fnz8/H7/ff18/77+vr5+fn39/b28fH2xSoKsAAACQSURBVBjTrYxJEsMgDARZZMAY73sgCcn/HxnhKtnk7j6oRq0psfuoyndZ/SuODkHPLzfVT6KeyPePnJ7KrnkRjWMXTn4SMnN8mXe2SSM3ts8L/ZUxxrbAULSYJJULE0Iw9pjpenoICcgcX61mGgTgtCv9Be99pzCoDhNQWQnchD1mup5++CYGcoQexajZbfwAj/0MD8ZOaUgAAAAASUVORK5CYII=';
  109. /**
  110. * Specifies the image URL to be used for the transparent background.
  111. */
  112. Editor.previousImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAAh0lEQVQ4je3UsQnCUBCA4U8hpa1NsoEjpHQJS0dxADdwEMuMIJkgA1hYChbGQgMi+JC8q4L/AB/vDu7x74cWWEZhJU44RmA1zujR5GIbXF9YNrjD/Q0bDRY4fEBZ4P4LlgTnCbAf84pUM8/9hY08tMUtEoQ1LpEgrNBFglChFXR6Q6GfwwR6AGKJMF74Vtt3AAAAAElFTkSuQmCC';
  113. /**
  114. * Specifies the image URL to be used for the transparent background.
  115. */
  116. Editor.nextImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAAi0lEQVQ4jeXUIQ7CUAwA0MeGxWI2yylwnALJUdBcgYvM7QYLmjOQIAkIPmJZghiIvypoUtX0tfnJL38X5ZfaEgUeUcManFBHgS0SLlhHggk3bCPBhCf2keCQR8wjwYTDp6YiZxJmOU1jGw7vGALescuBxsArNlOwd/CM1VSM/ut1qCIw+uOwiMJ+OF4CQzBCXm3hyAAAAABJRU5ErkJggg==';
  117. /**
  118. * Specifies the image URL to be used for the transparent background.
  119. */
  120. Editor.zoomOutLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAilBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////2N2iNAAAALXRSTlMA+vTcKMM96GRBHwXxi0YaX1HLrKWhiHpWEOnOr52Vb2xKSDcT19PKv5l/Ngdk8+viAAABJklEQVQ4y4WT2XaDMAxEvWD2nSSUNEnTJN3r//+9Sj7ILAY6L0ijC4ONYVZRpo6cByrz2YKSUGorGTpz71lPVHvT+avoB5wIkU/mxk8veceSuNoLg44IzziXjvpih72wKQnm8yc2UoiP/LAd8jQfe2Xf4Pq+2EyYIvv9wbzHHCgwxDdlBtWZOdqDfTCVgqpygQpsZaojVAVc9UjQxnAJDIBhiQv84tq3gMQCAVTxVoSibXJf8tMuc7e1TB/DCmejBNg/w1Y3c+AM5vv4w7xM59/oXamrHaLVqPQ+OTCnmMZxgz0SdL5zji0/ld6j88qGa5KIiBB6WeJGKfUKwSMKLuXgvl1TW0tm5R9UQL/efSDYsnzxD8CinhBsTTdugJatKpJwf8v+ADb8QmvW7AeAAAAAAElFTkSuQmCC';
  121. /**
  122. * Specifies the image URL to be used for the transparent background.
  123. */
  124. Editor.zoomInLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAilBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////2N2iNAAAALXRSTlMA+vTcKMM96GRBHwXxi0YaX1HLrKWhiHpWEOnOr52Vb2xKSDcT19PKv5l/Ngdk8+viAAABKElEQVQ4y4WT6WKCMBCENwkBwn2oFKvWqr3L+79es4EkQIDOH2d3Pxk2ABiJlB8JCXjqw4LikHVGLHTm3nM3UeVN5690GBBN0GwyV/3kkrUQR+WeKnREeKpzaXWd77CmJiXGfPIEI4V4yQ9TIW/ntlcMBe731Vts9w5TWG8F5j3mQI4hvrKpdGeYA7CX9qAcl650gVJartxRuhyHVghF8idQAIbFLvCLu28BsQEC6aKtCK6Pyb3JT7PmbmtNH8Ny56CotD/2qOs5cJbuffxgXmCib+xddVU5RNOhkvvkhTlFehzVWCOh3++MYElOhfdovaImnRYVmqDdsuhNp1QrBBE6uGC2+3ZNjGdg5B94oD+9uyVgWT79BwAxEBTWdOu3bWBVgsn/N/AHUD9IC01Oe40AAAAASUVORK5CYII=';
  125. /**
  126. * Specifies the image URL to be used for the transparent background.
  127. */
  128. Editor.actualSizeLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAilBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////2N2iNAAAALXRSTlMA+vTcKMM96GRBHwXxi0YaX1HLrKWhiHpWEOnOr52Vb2xKSDcT19PKv5l/Ngdk8+viAAABIUlEQVQ4y4WT2XqDIBCFBxDc9yTWNEnTJN3r+79eGT4BEbXnaubMr8dBBaM450dCQp4LWFAascGIRd48eB4cNYE7f6XjgGiCFs5c+dml6CFN6j1V6IQIlHPpdV/usKcmJcV88gQTRXjLD9Mhb+fWq8YG9/uCmTCFjeeDeY85UGKIUGUuqzN42kv7oCouq9oHamlzVR1lVfpAIu1QVRiW+sAv7r4FpAYIZZVsRXB9TP5Dfpo1d1trCgzz1iiptH/sUbdz4CzN9+mLeXHn3+hdddd4RDegsrvzwZwSs2GLPRJidAqCLTlVwaMPqpYMWjTWBB2WRW86pVkhSKyDK2bdt2tmagZG4sBD/evdLQHLEvQfAOKRoLCmG1FAB6uKmby+gz+REDn7O5+EwQAAAABJRU5ErkJggg==';
  129. /**
  130. * Specifies the image URL to be used for the transparent background.
  131. */
  132. Editor.layersLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAmVBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+/v7///+bnZkkAAAAMnRSTlMABPr8ByiD88KsTi/rvJb272mjeUA1CuPe1M/KjVxYHxMP6KZ0S9nYzGRGGRaznpGIbzaGUf0AAAHESURBVDjLbZLZYoIwEEVDgLCjbKIgAlqXqt3m/z+uNwu1rcyDhjl3ktnYL7OY254C0VX3yWFZfzDrOClbbgKxi0YDHjwl4jbnRkXxJS/C1YP3DbBhD1n7Ex4uaAqdVDb3yJ/4J/3nJD2to/ngQz/DfUvzMp4JJ5sSCaF5oXmemgQDfDxzbi+Kq4sU+vNcuAmx94JtyOP2DD4Epz2asWSCz4Z/4fECxyNj9zC9xNLHcdPEO+awDKeSaUu0W4twZQiO2hYVisTR3RCtK/c1X6t4xMEpiGqXqVntEBLolkZZsKY4QtwH6jzq67dEHlJysB1aNOD3XT7n1UkasQN59L4yC2RELMDSeCRtz3yV22Ub3ozIUTknYx8JWqDdQxbUes98cR2kZtUSveF/bAhcedwEWmlxIkpZUy4XOCb6VBjjxHvbwo/1lBAHHi2JCr0NI570QhyHq/DhJoE2lLgyA4RVe6KmZ47O/3b86MCP0HWa73A8/C3SUc5Qc1ajt6fgpXJ+RGpMvDSchepZDOOQRcZVIKcK90x2D7etqtI+56+u6n3sPriO6nfphitR4+O2m3EbM7lh3me1FM1o+LMI887rN+s3/wZdTFlpNVJiOAAAAABJRU5ErkJggg==';
  133. /**
  134. * Specifies the image URL to be used for the transparent background.
  135. */
  136. Editor.closeLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAUVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////8IN+deAAAAGnRSTlMAuvAIg/dDM/QlOeuFhj0S5s4vKgzjxJRQNiLSey0AAADNSURBVDjLfZLbEoMgDEQjRRRs1XqX///QNmOHJSnjPkHOGR7IEmeoGtJZstnwjqbRfIsmgEdtPCqe9Ynz7ZSc07rE2QiSc+qv8TvjRXA2PDUm3dpe82iJhOEUfxJJo3aCv+jKmRmH4lcCjCjeh9GWOdL/GZZkXH3PYYDrHBnfc4D/RVZf5sjoC1was+Y6HQxwaUxFvq/a0Pv343VCTxfBSRiB+ab3M3eiQZXmMNBJ3Y8pGRZtYQ7DgHMXJEdPLTaN/qBjzJOBc3nmNcbsA16bMR0oLqf+AAAAAElFTkSuQmCC';
  137. /**
  138. * Specifies the image URL to be used for the transparent background.
  139. */
  140. Editor.editLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAgMAAAAOFJJnAAAACVBMVEUAAAD///////9zeKVjAAAAAnRSTlMAgJsrThgAAABcSURBVBjThc6xDcAgDATAd8MQTEPW8TRUmYCGnzLRYyOlIV+dZFtvkICTFGqiJEzAG0/Uje9oL+e5Vu4F5yUYJxxqGKhQZ0eBvmgwYQLQaARKD1hbiPyDR0QOeAC31EyNe5X/kAAAAABJRU5ErkJggg==';
  141. /**
  142. * Specifies the image URL to be used for the transparent background.
  143. */
  144. Editor.previousLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAPFBMVEUAAAD////////////////////////////////////////////////////////////////////////////YSWgTAAAAE3RSTlMA7fci493c0MW8uJ6CZks4MxQHEZL6ewAAAFZJREFUOMvdkskRgDAMA4lDwg2B7b9XOlge/KKvdsa25KFb5XlRvxXC/DNBEv8IFNjBgGdDgXtFgTyhwDXiQAUHCvwa4Uv6mR6UR+1led2mVonvl+tML45qCQNQLIx7AAAAAElFTkSuQmCC';
  145. /**
  146. * Specifies the image URL to be used for the transparent background.
  147. */
  148. Editor.nextLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAPFBMVEUAAAD////////////////////////////////////////////////////////////////////////////YSWgTAAAAE3RSTlMA7fci493c0MW8uJ6CZks4MxQHEZL6ewAAAFRJREFUOMvd0skRgCAQBVEFwQ0V7fxzNQP6wI05v6pZ/kyj1b7FNgik2gQzzLcAwiUAigHOTwDHK4A1CmB5BJANJG1hQ9qafYcqFlZP3IFc9eVGrR+iIgkDQRUXIAAAAABJRU5ErkJggg==';
  149. // Editor inherits from mxEventSource
  150. mxUtils.extend(Editor, mxEventSource);
  151. /**
  152. * Stores initial state of mxClient.NO_FO.
  153. */
  154. Editor.prototype.originalNoForeignObject = mxClient.NO_FO;
  155. /**
  156. * Specifies the image URL to be used for the transparent background.
  157. */
  158. Editor.prototype.transparentImage = (mxClient.IS_SVG) ? 'data:image/gif;base64,R0lGODlhMAAwAIAAAP///wAAACH5BAEAAAAALAAAAAAwADAAAAIxhI+py+0Po5y02ouz3rz7D4biSJbmiabqyrbuC8fyTNf2jef6zvf+DwwKh8Si8egpAAA7' :
  159. IMAGE_PATH + '/transparent.gif';
  160. /**
  161. * Specifies if the canvas should be extended in all directions. Default is true.
  162. */
  163. Editor.prototype.extendCanvas = true;
  164. /**
  165. * Specifies if the app should run in chromeless mode. Default is false.
  166. * This default is only used if the contructor argument is null.
  167. */
  168. Editor.prototype.chromeless = false;
  169. /**
  170. * Specifies the order of OK/Cancel buttons in dialogs. Default is true.
  171. * Cancel first is used on Macs, Windows/Confluence uses cancel last.
  172. */
  173. Editor.prototype.cancelFirst = true;
  174. /**
  175. * Specifies if the editor is enabled. Default is true.
  176. */
  177. Editor.prototype.enabled = true;
  178. /**
  179. * Contains the name which was used for the last save. Default value is null.
  180. */
  181. Editor.prototype.filename = null;
  182. /**
  183. * Contains the current modified state of the diagram. This is false for
  184. * new diagrams and after the diagram was saved.
  185. */
  186. Editor.prototype.modified = false;
  187. /**
  188. * Specifies if the diagram should be saved automatically if possible. Default
  189. * is true.
  190. */
  191. Editor.prototype.autosave = true;
  192. /**
  193. * Specifies the top spacing for the initial page view. Default is 0.
  194. */
  195. Editor.prototype.initialTopSpacing = 0;
  196. /**
  197. * Specifies the app name. Default is document.title.
  198. */
  199. Editor.prototype.appName = document.title;
  200. /**
  201. *
  202. */
  203. Editor.prototype.editBlankUrl = window.location.protocol + '//' + window.location.host + '/?client=1';
  204. /**
  205. *
  206. */
  207. Editor.prototype.editBlankFallbackUrl = window.location.protocol + '//' + window.location.host + '/?create=drawdata&splash=0';
  208. /**
  209. * Initializes the environment.
  210. */
  211. Editor.prototype.init = function() { };
  212. /**
  213. * Sets the XML node for the current diagram.
  214. */
  215. Editor.prototype.setAutosave = function(value)
  216. {
  217. this.autosave = value;
  218. this.fireEvent(new mxEventObject('autosaveChanged'));
  219. };
  220. /**
  221. *
  222. */
  223. Editor.prototype.getEditBlankUrl = function(params, fallback)
  224. {
  225. return ((fallback) ? this.editBlankFallbackUrl : this.editBlankUrl) + params;
  226. }
  227. /**
  228. *
  229. */
  230. Editor.prototype.editAsNew = function(xml, title)
  231. {
  232. var p = (title != null) ? '&title=' + encodeURIComponent(title) : '';
  233. if (typeof window.postMessage !== 'undefined' && (document.documentMode == null || document.documentMode >= 10))
  234. {
  235. var wnd = null;
  236. var receive = mxUtils.bind(this, function(evt)
  237. {
  238. if (evt.data == 'ready' && evt.source == wnd)
  239. {
  240. wnd.postMessage(xml, '*');
  241. mxEvent.removeListener(window, 'message', receive);
  242. }
  243. });
  244. mxEvent.addListener(window, 'message', receive);
  245. wnd = window.open(this.getEditBlankUrl(p, false));
  246. }
  247. else
  248. {
  249. // Data is pulled from global variable after tab loads
  250. window.drawdata = xml;
  251. window.open(this.getEditBlankUrl(p, true));
  252. }
  253. };
  254. /**
  255. * Sets the XML node for the current diagram.
  256. */
  257. Editor.prototype.createGraph = function(themes, model)
  258. {
  259. var graph = new Graph(null, model, null, null, themes);
  260. graph.transparentBackground = false;
  261. return graph;
  262. };
  263. /**
  264. * Sets the XML node for the current diagram.
  265. */
  266. Editor.prototype.resetGraph = function()
  267. {
  268. this.graph.gridEnabled = !this.chromeless || urlParams['grid'] == '1';
  269. this.graph.graphHandler.guidesEnabled = true;
  270. this.graph.setTooltips(true);
  271. this.graph.setConnectable(true);
  272. this.graph.foldingEnabled = true;
  273. this.graph.scrollbars = this.graph.defaultScrollbars;
  274. this.graph.pageVisible = this.graph.defaultPageVisible;
  275. this.graph.pageBreaksVisible = this.graph.pageVisible;
  276. this.graph.preferPageSize = this.graph.pageBreaksVisible;
  277. this.graph.background = this.graph.defaultGraphBackground;
  278. this.graph.pageScale = mxGraph.prototype.pageScale;
  279. this.graph.pageFormat = mxGraph.prototype.pageFormat;
  280. this.updateGraphComponents();
  281. this.graph.view.setScale(1);
  282. };
  283. /**
  284. * Sets the XML node for the current diagram.
  285. */
  286. Editor.prototype.readGraphState = function(node)
  287. {
  288. this.graph.gridEnabled = node.getAttribute('grid') != '0' && (!this.chromeless || urlParams['grid'] == '1');
  289. this.graph.gridSize = parseFloat(node.getAttribute('gridSize')) || mxGraph.prototype.gridSize;
  290. this.graph.graphHandler.guidesEnabled = node.getAttribute('guides') != '0';
  291. this.graph.setTooltips(node.getAttribute('tooltips') != '0');
  292. this.graph.setConnectable(node.getAttribute('connect') != '0');
  293. this.graph.connectionArrowsEnabled = node.getAttribute('arrows') != '0';
  294. this.graph.foldingEnabled = node.getAttribute('fold') != '0';
  295. if (this.chromeless && this.graph.foldingEnabled)
  296. {
  297. this.graph.foldingEnabled = urlParams['nav'] == '1';
  298. this.graph.cellRenderer.forceControlClickHandler = this.graph.foldingEnabled;
  299. }
  300. var ps = node.getAttribute('pageScale');
  301. if (ps != null)
  302. {
  303. this.graph.pageScale = ps;
  304. }
  305. else
  306. {
  307. this.graph.pageScale = mxGraph.prototype.pageScale;
  308. }
  309. if (!this.graph.lightbox)
  310. {
  311. var pv = node.getAttribute('page');
  312. if (pv != null)
  313. {
  314. this.graph.pageVisible = (pv != '0');
  315. }
  316. else
  317. {
  318. this.graph.pageVisible = this.graph.defaultPageVisible;
  319. }
  320. }
  321. else
  322. {
  323. this.graph.pageVisible = false;
  324. }
  325. this.graph.pageBreaksVisible = this.graph.pageVisible;
  326. this.graph.preferPageSize = this.graph.pageBreaksVisible;
  327. var pw = node.getAttribute('pageWidth');
  328. var ph = node.getAttribute('pageHeight');
  329. if (pw != null && ph != null)
  330. {
  331. this.graph.pageFormat = new mxRectangle(0, 0, parseFloat(pw), parseFloat(ph));
  332. }
  333. // Loads the persistent state settings
  334. var bg = node.getAttribute('background');
  335. if (bg != null && bg.length > 0)
  336. {
  337. this.graph.background = bg;
  338. }
  339. else
  340. {
  341. this.graph.background = this.graph.defaultGraphBackground;
  342. }
  343. };
  344. /**
  345. * Sets the XML node for the current diagram.
  346. */
  347. Editor.prototype.setGraphXml = function(node)
  348. {
  349. if (node != null)
  350. {
  351. var dec = new mxCodec(node.ownerDocument);
  352. if (node.nodeName == 'mxGraphModel')
  353. {
  354. this.graph.model.beginUpdate();
  355. try
  356. {
  357. this.graph.model.clear();
  358. this.graph.view.scale = 1;
  359. this.readGraphState(node);
  360. this.updateGraphComponents();
  361. dec.decode(node, this.graph.getModel());
  362. }
  363. finally
  364. {
  365. this.graph.model.endUpdate();
  366. }
  367. this.fireEvent(new mxEventObject('resetGraphView'));
  368. }
  369. else if (node.nodeName == 'root')
  370. {
  371. this.resetGraph();
  372. // Workaround for invalid XML output in Firefox 20 due to bug in mxUtils.getXml
  373. var wrapper = dec.document.createElement('mxGraphModel');
  374. wrapper.appendChild(node);
  375. dec.decode(wrapper, this.graph.getModel());
  376. this.updateGraphComponents();
  377. this.fireEvent(new mxEventObject('resetGraphView'));
  378. }
  379. else
  380. {
  381. throw {
  382. message: mxResources.get('cannotOpenFile'),
  383. node: node,
  384. toString: function() { return this.message; }
  385. };
  386. }
  387. }
  388. else
  389. {
  390. this.resetGraph();
  391. this.graph.model.clear();
  392. this.fireEvent(new mxEventObject('resetGraphView'));
  393. }
  394. };
  395. /**
  396. * Returns the XML node that represents the current diagram.
  397. */
  398. Editor.prototype.getGraphXml = function(ignoreSelection)
  399. {
  400. ignoreSelection = (ignoreSelection != null) ? ignoreSelection : true;
  401. var node = null;
  402. if (ignoreSelection)
  403. {
  404. var enc = new mxCodec(mxUtils.createXmlDocument());
  405. node = enc.encode(this.graph.getModel());
  406. }
  407. else
  408. {
  409. node = this.graph.encodeCells(this.graph.getSelectionCells());
  410. }
  411. if (this.graph.view.translate.x != 0 || this.graph.view.translate.y != 0)
  412. {
  413. node.setAttribute('dx', Math.round(this.graph.view.translate.x * 100) / 100);
  414. node.setAttribute('dy', Math.round(this.graph.view.translate.y * 100) / 100);
  415. }
  416. node.setAttribute('grid', (this.graph.isGridEnabled()) ? '1' : '0');
  417. node.setAttribute('gridSize', this.graph.gridSize);
  418. node.setAttribute('guides', (this.graph.graphHandler.guidesEnabled) ? '1' : '0');
  419. node.setAttribute('tooltips', (this.graph.tooltipHandler.isEnabled()) ? '1' : '0');
  420. node.setAttribute('connect', (this.graph.connectionHandler.isEnabled()) ? '1' : '0');
  421. node.setAttribute('arrows', (this.graph.connectionArrowsEnabled) ? '1' : '0');
  422. node.setAttribute('fold', (this.graph.foldingEnabled) ? '1' : '0');
  423. node.setAttribute('page', (this.graph.pageVisible) ? '1' : '0');
  424. node.setAttribute('pageScale', this.graph.pageScale);
  425. node.setAttribute('pageWidth', this.graph.pageFormat.width);
  426. node.setAttribute('pageHeight', this.graph.pageFormat.height);
  427. if (this.graph.background != null)
  428. {
  429. node.setAttribute('background', this.graph.background);
  430. }
  431. return node;
  432. };
  433. /**
  434. * Keeps the graph container in sync with the persistent graph state
  435. */
  436. Editor.prototype.updateGraphComponents = function()
  437. {
  438. var graph = this.graph;
  439. if (graph.container != null)
  440. {
  441. graph.view.validateBackground();
  442. graph.container.style.overflow = (graph.scrollbars) ? 'auto' : 'hidden';
  443. this.fireEvent(new mxEventObject('updateGraphComponents'));
  444. }
  445. };
  446. /**
  447. * Sets the modified flag.
  448. */
  449. Editor.prototype.setModified = function(value)
  450. {
  451. this.modified = value;
  452. };
  453. /**
  454. * Sets the filename.
  455. */
  456. Editor.prototype.setFilename = function(value)
  457. {
  458. this.filename = value;
  459. };
  460. /**
  461. * Creates and returns a new undo manager.
  462. */
  463. Editor.prototype.createUndoManager = function()
  464. {
  465. var graph = this.graph;
  466. var undoMgr = new mxUndoManager();
  467. this.undoListener = function(sender, evt)
  468. {
  469. undoMgr.undoableEditHappened(evt.getProperty('edit'));
  470. };
  471. // Installs the command history
  472. var listener = mxUtils.bind(this, function(sender, evt)
  473. {
  474. this.undoListener.apply(this, arguments);
  475. });
  476. graph.getModel().addListener(mxEvent.UNDO, listener);
  477. graph.getView().addListener(mxEvent.UNDO, listener);
  478. // Keeps the selection in sync with the history
  479. var undoHandler = function(sender, evt)
  480. {
  481. var cand = graph.getSelectionCellsForChanges(evt.getProperty('edit').changes);
  482. var model = graph.getModel();
  483. var cells = [];
  484. for (var i = 0; i < cand.length; i++)
  485. {
  486. if ((model.isVertex(cand[i]) || model.isEdge(cand[i])) && graph.view.getState(cand[i]) != null)
  487. {
  488. cells.push(cand[i]);
  489. }
  490. }
  491. graph.setSelectionCells(cells);
  492. };
  493. undoMgr.addListener(mxEvent.UNDO, undoHandler);
  494. undoMgr.addListener(mxEvent.REDO, undoHandler);
  495. return undoMgr;
  496. };
  497. /**
  498. * Adds basic stencil set (no namespace).
  499. */
  500. Editor.prototype.initStencilRegistry = function() { };
  501. /**
  502. * Creates and returns a new undo manager.
  503. */
  504. Editor.prototype.destroy = function()
  505. {
  506. if (this.graph != null)
  507. {
  508. this.graph.destroy();
  509. this.graph = null;
  510. }
  511. };
  512. /**
  513. * Class for asynchronously opening a new window and loading a file at the same
  514. * time. This acts as a bridge between the open dialog and the new editor.
  515. */
  516. OpenFile = function(done)
  517. {
  518. this.producer = null;
  519. this.consumer = null;
  520. this.done = done;
  521. };
  522. /**
  523. * Registers the editor from the new window.
  524. */
  525. OpenFile.prototype.setConsumer = function(value)
  526. {
  527. this.consumer = value;
  528. this.execute();
  529. };
  530. /**
  531. * Sets the data from the loaded file.
  532. */
  533. OpenFile.prototype.setData = function(value, filename)
  534. {
  535. this.data = value;
  536. this.filename = filename;
  537. this.execute();
  538. };
  539. /**
  540. * Displays an error message.
  541. */
  542. OpenFile.prototype.error = function(msg)
  543. {
  544. this.cancel(true);
  545. mxUtils.alert(msg);
  546. };
  547. /**
  548. * Consumes the data.
  549. */
  550. OpenFile.prototype.execute = function()
  551. {
  552. if (this.consumer != null && this.data != null)
  553. {
  554. this.cancel(false);
  555. this.consumer(this.data, this.filename);
  556. }
  557. };
  558. /**
  559. * Cancels the operation.
  560. */
  561. OpenFile.prototype.cancel = function(cancel)
  562. {
  563. if (this.done != null)
  564. {
  565. this.done((cancel != null) ? cancel : true);
  566. }
  567. };
  568. /**
  569. * Static overrides
  570. */
  571. (function()
  572. {
  573. // Uses HTML for background pages (to support grid background image)
  574. mxGraphView.prototype.validateBackgroundPage = function()
  575. {
  576. var graph = this.graph;
  577. if (graph.container != null && !graph.transparentBackground)
  578. {
  579. if (graph.pageVisible)
  580. {
  581. var bounds = this.getBackgroundPageBounds();
  582. if (this.backgroundPageShape == null)
  583. {
  584. // Finds first element in graph container
  585. var firstChild = graph.container.firstChild;
  586. while (firstChild != null && firstChild.nodeType != mxConstants.NODETYPE_ELEMENT)
  587. {
  588. firstChild = firstChild.nextSibling;
  589. }
  590. if (firstChild != null)
  591. {
  592. this.backgroundPageShape = this.createBackgroundPageShape(bounds);
  593. this.backgroundPageShape.scale = 1;
  594. // Shadow filter causes problems in outline window in quirks mode. IE8 standards
  595. // also has known rendering issues inside mxWindow but not using shadow is worse.
  596. this.backgroundPageShape.isShadow = !mxClient.IS_QUIRKS;
  597. this.backgroundPageShape.dialect = mxConstants.DIALECT_STRICTHTML;
  598. this.backgroundPageShape.init(graph.container);
  599. // Required for the browser to render the background page in correct order
  600. firstChild.style.position = 'absolute';
  601. graph.container.insertBefore(this.backgroundPageShape.node, firstChild);
  602. this.backgroundPageShape.redraw();
  603. this.backgroundPageShape.node.className = 'geBackgroundPage';
  604. // Adds listener for double click handling on background
  605. mxEvent.addListener(this.backgroundPageShape.node, 'dblclick',
  606. mxUtils.bind(this, function(evt)
  607. {
  608. graph.dblClick(evt);
  609. })
  610. );
  611. // Adds basic listeners for graph event dispatching outside of the
  612. // container and finishing the handling of a single gesture
  613. mxEvent.addGestureListeners(this.backgroundPageShape.node,
  614. mxUtils.bind(this, function(evt)
  615. {
  616. graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt));
  617. }),
  618. mxUtils.bind(this, function(evt)
  619. {
  620. // Hides the tooltip if mouse is outside container
  621. if (graph.tooltipHandler != null && graph.tooltipHandler.isHideOnHover())
  622. {
  623. graph.tooltipHandler.hide();
  624. }
  625. if (graph.isMouseDown && !mxEvent.isConsumed(evt))
  626. {
  627. graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt));
  628. }
  629. }),
  630. mxUtils.bind(this, function(evt)
  631. {
  632. graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt));
  633. })
  634. );
  635. }
  636. }
  637. else
  638. {
  639. this.backgroundPageShape.scale = 1;
  640. this.backgroundPageShape.bounds = bounds;
  641. this.backgroundPageShape.redraw();
  642. }
  643. }
  644. else if (this.backgroundPageShape != null)
  645. {
  646. this.backgroundPageShape.destroy();
  647. this.backgroundPageShape = null;
  648. }
  649. this.validateBackgroundStyles();
  650. }
  651. };
  652. // Updates the CSS of the background to draw the grid
  653. mxGraphView.prototype.validateBackgroundStyles = function()
  654. {
  655. var graph = this.graph;
  656. var color = (graph.background == null || graph.background == mxConstants.NONE) ? '#ffffff' : graph.background;
  657. var gridColor = (this.gridColor != color.toLowerCase()) ? this.gridColor : '#ffffff';
  658. var image = 'none';
  659. var position = '';
  660. if (graph.isGridEnabled())
  661. {
  662. var phase = 10;
  663. if (mxClient.IS_SVG)
  664. {
  665. // Generates the SVG required for drawing the dynamic grid
  666. image = unescape(encodeURIComponent(this.createSvgGrid(gridColor)));
  667. image = (window.btoa) ? btoa(image) : Base64.encode(image, true);
  668. image = 'url(' + 'data:image/svg+xml;base64,' + image + ')'
  669. phase = graph.gridSize * this.scale * this.gridSteps;
  670. }
  671. else
  672. {
  673. // Fallback to grid wallpaper with fixed size
  674. image = 'url(' + this.gridImage + ')';
  675. }
  676. var x0 = 0;
  677. var y0 = 0;
  678. if (graph.view.backgroundPageShape != null)
  679. {
  680. var bds = this.getBackgroundPageBounds();
  681. x0 = 1 + bds.x;
  682. y0 = 1 + bds.y;
  683. }
  684. // Computes the offset to maintain origin for grid
  685. position = -Math.round(phase - mxUtils.mod(this.translate.x * this.scale - x0, phase)) + 'px ' +
  686. -Math.round(phase - mxUtils.mod(this.translate.y * this.scale - y0, phase)) + 'px';
  687. }
  688. var canvas = graph.view.canvas;
  689. if (canvas.ownerSVGElement != null)
  690. {
  691. canvas = canvas.ownerSVGElement;
  692. }
  693. if (graph.view.backgroundPageShape != null)
  694. {
  695. graph.view.backgroundPageShape.node.style.backgroundPosition = position;
  696. graph.view.backgroundPageShape.node.style.backgroundImage = image;
  697. graph.view.backgroundPageShape.node.style.backgroundColor = color;
  698. graph.container.className = 'geDiagramContainer geDiagramBackdrop';
  699. canvas.style.backgroundImage = 'none';
  700. canvas.style.backgroundColor = '';
  701. }
  702. else
  703. {
  704. graph.container.className = 'geDiagramContainer';
  705. canvas.style.backgroundPosition = position;
  706. canvas.style.backgroundColor = color;
  707. canvas.style.backgroundImage = image;
  708. }
  709. };
  710. // Returns the SVG required for painting the background grid.
  711. mxGraphView.prototype.createSvgGrid = function(color)
  712. {
  713. var tmp = this.graph.gridSize * this.scale;
  714. while (tmp < this.minGridSize)
  715. {
  716. tmp *= 2;
  717. }
  718. var tmp2 = this.gridSteps * tmp;
  719. // Small grid lines
  720. var d = [];
  721. for (var i = 1; i < this.gridSteps; i++)
  722. {
  723. var tmp3 = i * tmp;
  724. d.push('M 0 ' + tmp3 + ' L ' + tmp2 + ' ' + tmp3 + ' M ' + tmp3 + ' 0 L ' + tmp3 + ' ' + tmp2);
  725. }
  726. // KNOWN: Rounding errors for certain scales (eg. 144%, 121% in Chrome, FF and Safari). Workaround
  727. // in Chrome is to use 100% for the svg size, but this results in blurred grid for large diagrams.
  728. var size = tmp2;
  729. var svg = '<svg width="' + size + '" height="' + size + '" xmlns="' + mxConstants.NS_SVG + '">' +
  730. '<defs><pattern id="grid" width="' + tmp2 + '" height="' + tmp2 + '" patternUnits="userSpaceOnUse">' +
  731. '<path d="' + d.join(' ') + '" fill="none" stroke="' + color + '" opacity="0.2" stroke-width="1"/>' +
  732. '<path d="M ' + tmp2 + ' 0 L 0 0 0 ' + tmp2 + '" fill="none" stroke="' + color + '" stroke-width="1"/>' +
  733. '</pattern></defs><rect width="100%" height="100%" fill="url(#grid)"/></svg>';
  734. return svg;
  735. };
  736. // Adds panning for the grid with no page view and disabled scrollbars
  737. var mxGraphPanGraph = mxGraph.prototype.panGraph;
  738. mxGraph.prototype.panGraph = function(dx, dy)
  739. {
  740. mxGraphPanGraph.apply(this, arguments);
  741. if (this.shiftPreview1 != null)
  742. {
  743. var canvas = this.view.canvas;
  744. if (canvas.ownerSVGElement != null)
  745. {
  746. canvas = canvas.ownerSVGElement;
  747. }
  748. var phase = this.gridSize * this.view.scale * this.view.gridSteps;
  749. var position = -Math.round(phase - mxUtils.mod(this.view.translate.x * this.view.scale + dx, phase)) + 'px ' +
  750. -Math.round(phase - mxUtils.mod(this.view.translate.y * this.view.scale + dy, phase)) + 'px';
  751. canvas.style.backgroundPosition = position;
  752. }
  753. };
  754. // Draws page breaks only within the page
  755. mxGraph.prototype.updatePageBreaks = function(visible, width, height)
  756. {
  757. var scale = this.view.scale;
  758. var tr = this.view.translate;
  759. var fmt = this.pageFormat;
  760. var ps = scale * this.pageScale;
  761. var bounds2 = this.view.getBackgroundPageBounds();
  762. width = bounds2.width;
  763. height = bounds2.height;
  764. var bounds = new mxRectangle(scale * tr.x, scale * tr.y, fmt.width * ps, fmt.height * ps);
  765. // Does not show page breaks if the scale is too small
  766. visible = visible && Math.min(bounds.width, bounds.height) > this.minPageBreakDist;
  767. var horizontalCount = (visible) ? Math.ceil(height / bounds.height) - 1 : 0;
  768. var verticalCount = (visible) ? Math.ceil(width / bounds.width) - 1 : 0;
  769. var right = bounds2.x + width;
  770. var bottom = bounds2.y + height;
  771. if (this.horizontalPageBreaks == null && horizontalCount > 0)
  772. {
  773. this.horizontalPageBreaks = [];
  774. }
  775. if (this.verticalPageBreaks == null && verticalCount > 0)
  776. {
  777. this.verticalPageBreaks = [];
  778. }
  779. var drawPageBreaks = mxUtils.bind(this, function(breaks)
  780. {
  781. if (breaks != null)
  782. {
  783. var count = (breaks == this.horizontalPageBreaks) ? horizontalCount : verticalCount;
  784. for (var i = 0; i <= count; i++)
  785. {
  786. var pts = (breaks == this.horizontalPageBreaks) ?
  787. [new mxPoint(Math.round(bounds2.x), Math.round(bounds2.y + (i + 1) * bounds.height)),
  788. new mxPoint(Math.round(right), Math.round(bounds2.y + (i + 1) * bounds.height))] :
  789. [new mxPoint(Math.round(bounds2.x + (i + 1) * bounds.width), Math.round(bounds2.y)),
  790. new mxPoint(Math.round(bounds2.x + (i + 1) * bounds.width), Math.round(bottom))];
  791. if (breaks[i] != null)
  792. {
  793. breaks[i].points = pts;
  794. breaks[i].redraw();
  795. }
  796. else
  797. {
  798. var pageBreak = new mxPolyline(pts, this.pageBreakColor);
  799. pageBreak.dialect = this.dialect;
  800. pageBreak.isDashed = this.pageBreakDashed;
  801. pageBreak.pointerEvents = false;
  802. pageBreak.init(this.view.backgroundPane);
  803. pageBreak.redraw();
  804. breaks[i] = pageBreak;
  805. }
  806. }
  807. for (var i = count; i < breaks.length; i++)
  808. {
  809. breaks[i].destroy();
  810. }
  811. breaks.splice(count, breaks.length - count);
  812. }
  813. });
  814. drawPageBreaks(this.horizontalPageBreaks);
  815. drawPageBreaks(this.verticalPageBreaks);
  816. };
  817. // Disables removing relative children from parents
  818. var mxGraphHandlerShouldRemoveCellsFromParent = mxGraphHandler.prototype.shouldRemoveCellsFromParent;
  819. mxGraphHandler.prototype.shouldRemoveCellsFromParent = function(parent, cells, evt)
  820. {
  821. for (var i = 0; i < cells.length; i++)
  822. {
  823. if (this.graph.getModel().isVertex(cells[i]))
  824. {
  825. var geo = this.graph.getCellGeometry(cells[i]);
  826. if (geo != null && geo.relative)
  827. {
  828. return false;
  829. }
  830. }
  831. }
  832. return mxGraphHandlerShouldRemoveCellsFromParent.apply(this, arguments);
  833. };
  834. // Overrides to ignore hotspot only for target terminal
  835. var mxConnectionHandlerCreateMarker = mxConnectionHandler.prototype.createMarker;
  836. mxConnectionHandler.prototype.createMarker = function()
  837. {
  838. var marker = mxConnectionHandlerCreateMarker.apply(this, arguments);
  839. marker.intersects = mxUtils.bind(this, function(state, evt)
  840. {
  841. if (this.isConnecting())
  842. {
  843. return true;
  844. }
  845. return mxCellMarker.prototype.intersects.apply(marker, arguments);
  846. });
  847. return marker;
  848. };
  849. // Creates background page shape
  850. mxGraphView.prototype.createBackgroundPageShape = function(bounds)
  851. {
  852. return new mxRectangleShape(bounds, '#ffffff', '#cacaca');
  853. };
  854. // Fits the number of background pages to the graph
  855. mxGraphView.prototype.getBackgroundPageBounds = function()
  856. {
  857. var gb = this.getGraphBounds();
  858. // Computes unscaled, untranslated graph bounds
  859. var x = (gb.width > 0) ? gb.x / this.scale - this.translate.x : 0;
  860. var y = (gb.height > 0) ? gb.y / this.scale - this.translate.y : 0;
  861. var w = gb.width / this.scale;
  862. var h = gb.height / this.scale;
  863. var fmt = this.graph.pageFormat;
  864. var ps = this.graph.pageScale;
  865. var pw = fmt.width * ps;
  866. var ph = fmt.height * ps;
  867. var x0 = Math.floor(Math.min(0, x) / pw);
  868. var y0 = Math.floor(Math.min(0, y) / ph);
  869. var xe = Math.ceil(Math.max(1, x + w) / pw);
  870. var ye = Math.ceil(Math.max(1, y + h) / ph);
  871. var rows = xe - x0;
  872. var cols = ye - y0;
  873. var bounds = new mxRectangle(this.scale * (this.translate.x + x0 * pw), this.scale *
  874. (this.translate.y + y0 * ph), this.scale * rows * pw, this.scale * cols * ph);
  875. return bounds;
  876. };
  877. // Add panning for background page in VML
  878. var graphPanGraph = mxGraph.prototype.panGraph;
  879. mxGraph.prototype.panGraph = function(dx, dy)
  880. {
  881. graphPanGraph.apply(this, arguments);
  882. if ((this.dialect != mxConstants.DIALECT_SVG && this.view.backgroundPageShape != null) &&
  883. (!this.useScrollbarsForPanning || !mxUtils.hasScrollbars(this.container)))
  884. {
  885. this.view.backgroundPageShape.node.style.marginLeft = dx + 'px';
  886. this.view.backgroundPageShape.node.style.marginTop = dy + 'px';
  887. }
  888. };
  889. /**
  890. * Consumes click events for disabled menu items.
  891. */
  892. var mxPopupMenuAddItem = mxPopupMenu.prototype.addItem;
  893. mxPopupMenu.prototype.addItem = function(title, image, funct, parent, iconCls, enabled)
  894. {
  895. var result = mxPopupMenuAddItem.apply(this, arguments);
  896. if (enabled != null && !enabled)
  897. {
  898. mxEvent.addListener(result, 'mousedown', function(evt)
  899. {
  900. mxEvent.consume(evt);
  901. });
  902. }
  903. return result;
  904. };
  905. // Selects ancestors before descendants
  906. var graphHandlerGetInitialCellForEvent = mxGraphHandler.prototype.getInitialCellForEvent;
  907. mxGraphHandler.prototype.getInitialCellForEvent = function(me)
  908. {
  909. var model = this.graph.getModel();
  910. var psel = model.getParent(this.graph.getSelectionCell());
  911. var cell = graphHandlerGetInitialCellForEvent.apply(this, arguments);
  912. var parent = model.getParent(cell);
  913. if (psel == null || (psel != cell && psel != parent))
  914. {
  915. while (!this.graph.isCellSelected(cell) && !this.graph.isCellSelected(parent) &&
  916. model.isVertex(parent) && !this.graph.isContainer(parent))
  917. {
  918. cell = parent;
  919. parent = this.graph.getModel().getParent(cell);
  920. }
  921. }
  922. return cell;
  923. };
  924. // Selection is delayed to mouseup if ancestor is selected
  925. var graphHandlerIsDelayedSelection = mxGraphHandler.prototype.isDelayedSelection;
  926. mxGraphHandler.prototype.isDelayedSelection = function(cell, me)
  927. {
  928. var result = graphHandlerIsDelayedSelection.apply(this, arguments);
  929. if (!result)
  930. {
  931. var model = this.graph.getModel();
  932. var parent = model.getParent(cell);
  933. while (parent != null)
  934. {
  935. // Inconsistency for unselected parent swimlane is intended for easier moving
  936. // of stack layouts where the container title section is too far away
  937. if (this.graph.isCellSelected(parent) && model.isVertex(parent))
  938. {
  939. result = true;
  940. break;
  941. }
  942. parent = model.getParent(parent);
  943. }
  944. }
  945. return result;
  946. };
  947. // Delayed selection of parent group
  948. mxGraphHandler.prototype.selectDelayed = function(me)
  949. {
  950. if (!this.graph.popupMenuHandler.isPopupTrigger(me))
  951. {
  952. var cell = me.getCell();
  953. if (cell == null)
  954. {
  955. cell = this.cell;
  956. }
  957. // Selects folded cell for hit on folding icon
  958. var state = this.graph.view.getState(cell)
  959. if (state != null && me.isSource(state.control))
  960. {
  961. this.graph.selectCellForEvent(cell, me.getEvent());
  962. }
  963. else
  964. {
  965. var model = this.graph.getModel();
  966. var parent = model.getParent(cell);
  967. while (!this.graph.isCellSelected(parent) && model.isVertex(parent))
  968. {
  969. cell = parent;
  970. parent = model.getParent(cell);
  971. }
  972. this.graph.selectCellForEvent(cell, me.getEvent());
  973. }
  974. }
  975. };
  976. // Returns last selected ancestor
  977. mxPopupMenuHandler.prototype.getCellForPopupEvent = function(me)
  978. {
  979. var cell = me.getCell();
  980. var model = this.graph.getModel();
  981. var parent = model.getParent(cell);
  982. while (model.isVertex(parent) && !this.graph.isContainer(parent))
  983. {
  984. if (this.graph.isCellSelected(parent))
  985. {
  986. cell = parent;
  987. }
  988. parent = model.getParent(parent);
  989. }
  990. return cell;
  991. };
  992. })();