Editor.js 37 KB

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