Editor.js 70 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149
  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(sender, eventObject)
  36. {
  37. var edit = (eventObject != null) ? eventObject.getProperty('edit') : null;
  38. if (edit == null || !edit.ignoreEdit)
  39. {
  40. this.setModified(true);
  41. }
  42. };
  43. this.graph.getModel().addListener(mxEvent.CHANGE, mxUtils.bind(this, function()
  44. {
  45. this.graphChangeListener.apply(this, arguments);
  46. }));
  47. // Sets persistent graph state defaults
  48. this.graph.resetViewOnRootChange = false;
  49. this.init();
  50. };
  51. /**
  52. * Counts open editor tabs (must be global for cross-window access)
  53. */
  54. Editor.pageCounter = 0;
  55. // Cross-domain window access is not allowed in FF, so if we
  56. // were opened from another domain then this will fail.
  57. (function()
  58. {
  59. try
  60. {
  61. var op = window;
  62. while (op.opener != null && typeof op.opener.Editor !== 'undefined' &&
  63. !isNaN(op.opener.Editor.pageCounter) &&
  64. // Workaround for possible infinite loop in FF https://drawio.atlassian.net/browse/DS-795
  65. op.opener != op)
  66. {
  67. op = op.opener;
  68. }
  69. // Increments the counter in the first opener in the chain
  70. if (op != null)
  71. {
  72. op.Editor.pageCounter++;
  73. Editor.pageCounter = op.Editor.pageCounter;
  74. }
  75. }
  76. catch (e)
  77. {
  78. // ignore
  79. }
  80. })();
  81. /**
  82. * Specifies if local storage should be used (eg. on the iPad which has no filesystem)
  83. */
  84. Editor.useLocalStorage = typeof(Storage) != 'undefined' && mxClient.IS_IOS;
  85. /**
  86. * Images below are for lightbox and embedding toolbars.
  87. */
  88. Editor.helpImage = (mxClient.IS_SVG) ? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAXVBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC5BxTwAAAAH3RSTlMAlUF8boNQIE0LBgOgkGlHNSwqFIx/dGVUOjApmV9ezNACSAAAAIVJREFUGNNtjNsOgzAMQ5NeoVcKDAZs+//PXLKI8YKlWvaRU7jXuFpb9qsbdK05XILUiE8JHQox1Pv3OgFUzf1AGqWqUg+QBwLF0YAeegBlCNgRWOpB5vUfTCmeoHQ/wNdy0jLH/cM+b+wLTw4n/7ACEmHVVy8h6qy8V7MNcGowWpsNbvUFcGUEdSi1s/oAAAAASUVORK5CYII=' :
  89. IMAGE_PATH + '/help.png';
  90. /**
  91. * Sets the default font size.
  92. */
  93. 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=' :
  94. IMAGE_PATH + '/checkmark.gif';
  95. /**
  96. * Images below are for lightbox and embedding toolbars.
  97. */
  98. Editor.maximizeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVBAMAAABbObilAAAAElBMVEUAAAAAAAAAAAAAAAAAAAAAAADgKxmiAAAABXRSTlMA758vX1Pw3BoAAABJSURBVAjXY8AJQkODGBhUQ0MhbAUGBiYY24CBgRnGFmZgMISwgwwDGRhEhVVBbAVmEQYGRwMmBjIAQi/CTIRd6G5AuA3dzYQBAHj0EFdHkvV4AAAAAElFTkSuQmCC';
  99. /**
  100. * Specifies the image URL to be used for the transparent background.
  101. */
  102. Editor.zoomOutImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVBAMAAABbObilAAAAElBMVEUAAAAAAAAsLCxxcXEhISFgYGChjTUxAAAAAXRSTlMAQObYZgAAAEdJREFUCNdjIAMwCQrB2YKCggJQJqMwA7MglK1owMBgqABVApITgLJZXFxgbIQ4Qj3CHIT5ggoIe5kgNkM1KSDYKBKqxPkDAPo5BAZBE54hAAAAAElFTkSuQmCC';
  103. /**
  104. * Specifies the image URL to be used for the transparent background.
  105. */
  106. Editor.zoomInImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVBAMAAABbObilAAAAElBMVEUAAAAAAAAsLCwhISFxcXFgYGBavKaoAAAAAXRSTlMAQObYZgAAAElJREFUCNdjIAMwCQrB2YKCggJQJqMIA4sglK3owMzgqABVwsDMwCgAZTMbG8PYCHGEeoQ5CPMFFRD2MkFshmpSQLBRJFSJ8wcAEqcEM2uhl2MAAAAASUVORK5CYII=';
  107. /**
  108. * Specifies the image URL to be used for the transparent background.
  109. */
  110. Editor.zoomFitImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVBAMAAABbObilAAAAD1BMVEUAAAAAAAAwMDBwcHBgYGC1xl09AAAAAXRSTlMAQObYZgAAAEFJREFUCNdjIAMwCQrB2YKCggJQJqMwA7MglK1owMBgqABVApITwMdGqEeYgzBfUAFhLxPEZqgmBQQbRUKFOH8AAK5OA3lA+FFOAAAAAElFTkSuQmCC';
  111. /**
  112. * Specifies the image URL to be used for the transparent background.
  113. */
  114. Editor.layersImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAMAAACeyVWkAAAAaVBMVEUAAAAgICAICAgdHR0PDw8WFhYICAgLCwsXFxcvLy8ODg4uLi4iIiIqKiokJCQYGBgKCgonJycFBQUCAgIqKiocHBwcHBwODg4eHh4cHBwnJycJCQkUFBQqKiojIyMuLi4ZGRkgICAEBATOWYXAAAAAGnRSTlMAD7+fnz8/H7/ff18/77+vr5+fn39/b28fH2xSoKsAAACQSURBVBjTrYxJEsMgDARZZMAY73sgCcn/HxnhKtnk7j6oRq0psfuoyndZ/SuODkHPLzfVT6KeyPePnJ7KrnkRjWMXTn4SMnN8mXe2SSM3ts8L/ZUxxrbAULSYJJULE0Iw9pjpenoICcgcX61mGgTgtCv9Be99pzCoDhNQWQnchD1mup5++CYGcoQexajZbfwAj/0MD8ZOaUgAAAAASUVORK5CYII=';
  115. /**
  116. * Specifies the image URL to be used for the transparent background.
  117. */
  118. Editor.previousImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAAh0lEQVQ4je3UsQnCUBCA4U8hpa1NsoEjpHQJS0dxADdwEMuMIJkgA1hYChbGQgMi+JC8q4L/AB/vDu7x74cWWEZhJU44RmA1zujR5GIbXF9YNrjD/Q0bDRY4fEBZ4P4LlgTnCbAf84pUM8/9hY08tMUtEoQ1LpEgrNBFglChFXR6Q6GfwwR6AGKJMF74Vtt3AAAAAElFTkSuQmCC';
  119. /**
  120. * Specifies the image URL to be used for the transparent background.
  121. */
  122. Editor.nextImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAAi0lEQVQ4jeXUIQ7CUAwA0MeGxWI2yylwnALJUdBcgYvM7QYLmjOQIAkIPmJZghiIvypoUtX0tfnJL38X5ZfaEgUeUcManFBHgS0SLlhHggk3bCPBhCf2keCQR8wjwYTDp6YiZxJmOU1jGw7vGALescuBxsArNlOwd/CM1VSM/ut1qCIw+uOwiMJ+OF4CQzBCXm3hyAAAAABJRU5ErkJggg==';
  123. /**
  124. * Specifies the image URL to be used for the transparent background.
  125. */
  126. Editor.zoomOutLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAilBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////2N2iNAAAALXRSTlMA+vTcKMM96GRBHwXxi0YaX1HLrKWhiHpWEOnOr52Vb2xKSDcT19PKv5l/Ngdk8+viAAABJklEQVQ4y4WT2XaDMAxEvWD2nSSUNEnTJN3r//+9Sj7ILAY6L0ijC4ONYVZRpo6cByrz2YKSUGorGTpz71lPVHvT+avoB5wIkU/mxk8veceSuNoLg44IzziXjvpih72wKQnm8yc2UoiP/LAd8jQfe2Xf4Pq+2EyYIvv9wbzHHCgwxDdlBtWZOdqDfTCVgqpygQpsZaojVAVc9UjQxnAJDIBhiQv84tq3gMQCAVTxVoSibXJf8tMuc7e1TB/DCmejBNg/w1Y3c+AM5vv4w7xM59/oXamrHaLVqPQ+OTCnmMZxgz0SdL5zji0/ld6j88qGa5KIiBB6WeJGKfUKwSMKLuXgvl1TW0tm5R9UQL/efSDYsnzxD8CinhBsTTdugJatKpJwf8v+ADb8QmvW7AeAAAAAAElFTkSuQmCC';
  127. /**
  128. * Specifies the image URL to be used for the transparent background.
  129. */
  130. Editor.zoomInLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAilBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////2N2iNAAAALXRSTlMA+vTcKMM96GRBHwXxi0YaX1HLrKWhiHpWEOnOr52Vb2xKSDcT19PKv5l/Ngdk8+viAAABKElEQVQ4y4WT6WKCMBCENwkBwn2oFKvWqr3L+79es4EkQIDOH2d3Pxk2ABiJlB8JCXjqw4LikHVGLHTm3nM3UeVN5690GBBN0GwyV/3kkrUQR+WeKnREeKpzaXWd77CmJiXGfPIEI4V4yQ9TIW/ntlcMBe731Vts9w5TWG8F5j3mQI4hvrKpdGeYA7CX9qAcl650gVJartxRuhyHVghF8idQAIbFLvCLu28BsQEC6aKtCK6Pyb3JT7PmbmtNH8Ny56CotD/2qOs5cJbuffxgXmCib+xddVU5RNOhkvvkhTlFehzVWCOh3++MYElOhfdovaImnRYVmqDdsuhNp1QrBBE6uGC2+3ZNjGdg5B94oD+9uyVgWT79BwAxEBTWdOu3bWBVgsn/N/AHUD9IC01Oe40AAAAASUVORK5CYII=';
  131. /**
  132. * Specifies the image URL to be used for the transparent background.
  133. */
  134. Editor.actualSizeLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAilBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////2N2iNAAAALXRSTlMA+vTcKMM96GRBHwXxi0YaX1HLrKWhiHpWEOnOr52Vb2xKSDcT19PKv5l/Ngdk8+viAAABIUlEQVQ4y4WT2XqDIBCFBxDc9yTWNEnTJN3r+79eGT4BEbXnaubMr8dBBaM450dCQp4LWFAascGIRd48eB4cNYE7f6XjgGiCFs5c+dml6CFN6j1V6IQIlHPpdV/usKcmJcV88gQTRXjLD9Mhb+fWq8YG9/uCmTCFjeeDeY85UGKIUGUuqzN42kv7oCouq9oHamlzVR1lVfpAIu1QVRiW+sAv7r4FpAYIZZVsRXB9TP5Dfpo1d1trCgzz1iiptH/sUbdz4CzN9+mLeXHn3+hdddd4RDegsrvzwZwSs2GLPRJidAqCLTlVwaMPqpYMWjTWBB2WRW86pVkhSKyDK2bdt2tmagZG4sBD/evdLQHLEvQfAOKRoLCmG1FAB6uKmby+gz+REDn7O5+EwQAAAABJRU5ErkJggg==';
  135. /**
  136. * Specifies the image URL to be used for the transparent background.
  137. */
  138. Editor.printLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAXVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9RKvvlAAAAHnRSTlMAydnl77qbMLT093H7K4Nd4Ktn082+lYt5bkklEgP44nQSAAAApUlEQVQ4y73P2Q6DIBRF0cOgbRHHzhP//5m9mBAQKjG1cT0Yc7ITAMu1LNQgUZiQ2DYoNQ0sCQb6qgHAfRx48opq3J9AZ6xuF7uOew8Ik1OsCZRS2UAC9V+D9a+QZYxNA45YFQftPtSkATOhw7dAc0vPBwKWiIOjP0JZ0yMuQJ27g36DipOUsqRAM0dR8KD1/ILHaHSE/w8DIx09E3g/BTce6rHUB5sAPKvfF+JdAAAAAElFTkSuQmCC';
  139. /**
  140. * Specifies the image URL to be used for the transparent background.
  141. */
  142. 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==';
  143. /**
  144. * Specifies the image URL to be used for the transparent background.
  145. */
  146. Editor.closeLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAUVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////8IN+deAAAAGnRSTlMAuvAIg/dDM/QlOeuFhj0S5s4vKgzjxJRQNiLSey0AAADNSURBVDjLfZLbEoMgDEQjRRRs1XqX///QNmOHJSnjPkHOGR7IEmeoGtJZstnwjqbRfIsmgEdtPCqe9Ynz7ZSc07rE2QiSc+qv8TvjRXA2PDUm3dpe82iJhOEUfxJJo3aCv+jKmRmH4lcCjCjeh9GWOdL/GZZkXH3PYYDrHBnfc4D/RVZf5sjoC1was+Y6HQxwaUxFvq/a0Pv343VCTxfBSRiB+ab3M3eiQZXmMNBJ3Y8pGRZtYQ7DgHMXJEdPLTaN/qBjzJOBc3nmNcbsA16bMR0oLqf+AAAAAElFTkSuQmCC';
  147. /**
  148. * Specifies the image URL to be used for the transparent background.
  149. */
  150. Editor.editLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAgMAAAAOFJJnAAAACVBMVEUAAAD///////9zeKVjAAAAAnRSTlMAgJsrThgAAABcSURBVBjThc6xDcAgDATAd8MQTEPW8TRUmYCGnzLRYyOlIV+dZFtvkICTFGqiJEzAG0/Uje9oL+e5Vu4F5yUYJxxqGKhQZ0eBvmgwYQLQaARKD1hbiPyDR0QOeAC31EyNe5X/kAAAAABJRU5ErkJggg==';
  151. /**
  152. * Specifies the image URL to be used for the transparent background.
  153. */
  154. Editor.previousLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAPFBMVEUAAAD////////////////////////////////////////////////////////////////////////////YSWgTAAAAE3RSTlMA7fci493c0MW8uJ6CZks4MxQHEZL6ewAAAFZJREFUOMvdkskRgDAMA4lDwg2B7b9XOlge/KKvdsa25KFb5XlRvxXC/DNBEv8IFNjBgGdDgXtFgTyhwDXiQAUHCvwa4Uv6mR6UR+1led2mVonvl+tML45qCQNQLIx7AAAAAElFTkSuQmCC';
  155. /**
  156. * Specifies the image URL to be used for the transparent background.
  157. */
  158. Editor.nextLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAPFBMVEUAAAD////////////////////////////////////////////////////////////////////////////YSWgTAAAAE3RSTlMA7fci493c0MW8uJ6CZks4MxQHEZL6ewAAAFRJREFUOMvd0skRgCAQBVEFwQ0V7fxzNQP6wI05v6pZ/kyj1b7FNgik2gQzzLcAwiUAigHOTwDHK4A1CmB5BJANJG1hQ9qafYcqFlZP3IFc9eVGrR+iIgkDQRUXIAAAAABJRU5ErkJggg==';
  159. // Editor inherits from mxEventSource
  160. mxUtils.extend(Editor, mxEventSource);
  161. /**
  162. * Stores initial state of mxClient.NO_FO.
  163. */
  164. Editor.prototype.originalNoForeignObject = mxClient.NO_FO;
  165. /**
  166. * Specifies the image URL to be used for the transparent background.
  167. */
  168. Editor.prototype.transparentImage = (mxClient.IS_SVG) ? 'data:image/gif;base64,R0lGODlhMAAwAIAAAP///wAAACH5BAEAAAAALAAAAAAwADAAAAIxhI+py+0Po5y02ouz3rz7D4biSJbmiabqyrbuC8fyTNf2jef6zvf+DwwKh8Si8egpAAA7' :
  169. IMAGE_PATH + '/transparent.gif';
  170. /**
  171. * Specifies if the canvas should be extended in all directions. Default is true.
  172. */
  173. Editor.prototype.extendCanvas = true;
  174. /**
  175. * Specifies if the app should run in chromeless mode. Default is false.
  176. * This default is only used if the contructor argument is null.
  177. */
  178. Editor.prototype.chromeless = false;
  179. /**
  180. * Specifies the order of OK/Cancel buttons in dialogs. Default is true.
  181. * Cancel first is used on Macs, Windows/Confluence uses cancel last.
  182. */
  183. Editor.prototype.cancelFirst = true;
  184. /**
  185. * Specifies if the editor is enabled. Default is true.
  186. */
  187. Editor.prototype.enabled = true;
  188. /**
  189. * Contains the name which was used for the last save. Default value is null.
  190. */
  191. Editor.prototype.filename = null;
  192. /**
  193. * Contains the current modified state of the diagram. This is false for
  194. * new diagrams and after the diagram was saved.
  195. */
  196. Editor.prototype.modified = false;
  197. /**
  198. * Specifies if the diagram should be saved automatically if possible. Default
  199. * is true.
  200. */
  201. Editor.prototype.autosave = true;
  202. /**
  203. * Specifies the top spacing for the initial page view. Default is 0.
  204. */
  205. Editor.prototype.initialTopSpacing = 0;
  206. /**
  207. * Specifies the app name. Default is document.title.
  208. */
  209. Editor.prototype.appName = document.title;
  210. /**
  211. *
  212. */
  213. Editor.prototype.editBlankUrl = window.location.protocol + '//' + window.location.host + '/?client=1';
  214. /**
  215. *
  216. */
  217. Editor.prototype.editBlankFallbackUrl = window.location.protocol + '//' + window.location.host + '/?create=drawdata&splash=0';
  218. /**
  219. * Initializes the environment.
  220. */
  221. Editor.prototype.init = function() { };
  222. /**
  223. * Sets the XML node for the current diagram.
  224. */
  225. Editor.prototype.setAutosave = function(value)
  226. {
  227. this.autosave = value;
  228. this.fireEvent(new mxEventObject('autosaveChanged'));
  229. };
  230. /**
  231. *
  232. */
  233. Editor.prototype.getEditBlankUrl = function(params, fallback)
  234. {
  235. return ((fallback) ? this.editBlankFallbackUrl : this.editBlankUrl) + params;
  236. }
  237. /**
  238. *
  239. */
  240. Editor.prototype.editAsNew = function(xml, title)
  241. {
  242. var p = (title != null) ? '&title=' + encodeURIComponent(title) : '';
  243. if (typeof window.postMessage !== 'undefined' && (document.documentMode == null || document.documentMode >= 10))
  244. {
  245. var wnd = null;
  246. var receive = mxUtils.bind(this, function(evt)
  247. {
  248. if (evt.data == 'ready' && evt.source == wnd)
  249. {
  250. wnd.postMessage(xml, '*');
  251. mxEvent.removeListener(window, 'message', receive);
  252. }
  253. });
  254. mxEvent.addListener(window, 'message', receive);
  255. wnd = window.open(this.getEditBlankUrl(p, false));
  256. }
  257. else
  258. {
  259. // Data is pulled from global variable after tab loads
  260. window.drawdata = xml;
  261. window.open(this.getEditBlankUrl(p, true));
  262. }
  263. };
  264. /**
  265. * Sets the XML node for the current diagram.
  266. */
  267. Editor.prototype.createGraph = function(themes, model)
  268. {
  269. var graph = new Graph(null, model, null, null, themes);
  270. graph.transparentBackground = false;
  271. // Opens all links in a new window while editing
  272. if (!this.chromeless)
  273. {
  274. graph.isBlankLink = function(href)
  275. {
  276. return !this.isExternalProtocol(href);
  277. };
  278. }
  279. return graph;
  280. };
  281. /**
  282. * Sets the XML node for the current diagram.
  283. */
  284. Editor.prototype.resetGraph = function()
  285. {
  286. this.graph.gridEnabled = !this.chromeless || urlParams['grid'] == '1';
  287. this.graph.graphHandler.guidesEnabled = true;
  288. this.graph.setTooltips(true);
  289. this.graph.setConnectable(true);
  290. this.graph.foldingEnabled = true;
  291. this.graph.scrollbars = this.graph.defaultScrollbars;
  292. this.graph.pageVisible = this.graph.defaultPageVisible;
  293. this.graph.pageBreaksVisible = this.graph.pageVisible;
  294. this.graph.preferPageSize = this.graph.pageBreaksVisible;
  295. this.graph.background = this.graph.defaultGraphBackground;
  296. this.graph.pageScale = mxGraph.prototype.pageScale;
  297. this.graph.pageFormat = mxGraph.prototype.pageFormat;
  298. this.updateGraphComponents();
  299. this.graph.view.setScale(1);
  300. };
  301. /**
  302. * Sets the XML node for the current diagram.
  303. */
  304. Editor.prototype.readGraphState = function(node)
  305. {
  306. this.graph.gridEnabled = node.getAttribute('grid') != '0' && (!this.chromeless || urlParams['grid'] == '1');
  307. this.graph.gridSize = parseFloat(node.getAttribute('gridSize')) || mxGraph.prototype.gridSize;
  308. this.graph.graphHandler.guidesEnabled = node.getAttribute('guides') != '0';
  309. this.graph.setTooltips(node.getAttribute('tooltips') != '0');
  310. this.graph.setConnectable(node.getAttribute('connect') != '0');
  311. this.graph.connectionArrowsEnabled = node.getAttribute('arrows') != '0';
  312. this.graph.foldingEnabled = node.getAttribute('fold') != '0';
  313. if (this.chromeless && this.graph.foldingEnabled)
  314. {
  315. this.graph.foldingEnabled = urlParams['nav'] == '1';
  316. this.graph.cellRenderer.forceControlClickHandler = this.graph.foldingEnabled;
  317. }
  318. var ps = node.getAttribute('pageScale');
  319. if (ps != null)
  320. {
  321. this.graph.pageScale = ps;
  322. }
  323. else
  324. {
  325. this.graph.pageScale = mxGraph.prototype.pageScale;
  326. }
  327. if (!this.graph.lightbox)
  328. {
  329. var pv = node.getAttribute('page');
  330. if (pv != null)
  331. {
  332. this.graph.pageVisible = (pv != '0');
  333. }
  334. else
  335. {
  336. this.graph.pageVisible = this.graph.defaultPageVisible;
  337. }
  338. }
  339. else
  340. {
  341. this.graph.pageVisible = false;
  342. }
  343. this.graph.pageBreaksVisible = this.graph.pageVisible;
  344. this.graph.preferPageSize = this.graph.pageBreaksVisible;
  345. var pw = node.getAttribute('pageWidth');
  346. var ph = node.getAttribute('pageHeight');
  347. if (pw != null && ph != null)
  348. {
  349. this.graph.pageFormat = new mxRectangle(0, 0, parseFloat(pw), parseFloat(ph));
  350. }
  351. // Loads the persistent state settings
  352. var bg = node.getAttribute('background');
  353. if (bg != null && bg.length > 0)
  354. {
  355. this.graph.background = bg;
  356. }
  357. else
  358. {
  359. this.graph.background = this.graph.defaultGraphBackground;
  360. }
  361. };
  362. /**
  363. * Sets the XML node for the current diagram.
  364. */
  365. Editor.prototype.setGraphXml = function(node)
  366. {
  367. if (node != null)
  368. {
  369. var dec = new mxCodec(node.ownerDocument);
  370. if (node.nodeName == 'mxGraphModel')
  371. {
  372. this.graph.model.beginUpdate();
  373. try
  374. {
  375. this.graph.model.clear();
  376. this.graph.view.scale = 1;
  377. this.readGraphState(node);
  378. this.updateGraphComponents();
  379. dec.decode(node, this.graph.getModel());
  380. }
  381. finally
  382. {
  383. this.graph.model.endUpdate();
  384. }
  385. this.fireEvent(new mxEventObject('resetGraphView'));
  386. }
  387. else if (node.nodeName == 'root')
  388. {
  389. this.resetGraph();
  390. // Workaround for invalid XML output in Firefox 20 due to bug in mxUtils.getXml
  391. var wrapper = dec.document.createElement('mxGraphModel');
  392. wrapper.appendChild(node);
  393. dec.decode(wrapper, this.graph.getModel());
  394. this.updateGraphComponents();
  395. this.fireEvent(new mxEventObject('resetGraphView'));
  396. }
  397. else
  398. {
  399. throw {
  400. message: mxResources.get('cannotOpenFile'),
  401. node: node,
  402. toString: function() { return this.message; }
  403. };
  404. }
  405. }
  406. else
  407. {
  408. this.resetGraph();
  409. this.graph.model.clear();
  410. this.fireEvent(new mxEventObject('resetGraphView'));
  411. }
  412. };
  413. /**
  414. * Returns the XML node that represents the current diagram.
  415. */
  416. Editor.prototype.getGraphXml = function(ignoreSelection)
  417. {
  418. ignoreSelection = (ignoreSelection != null) ? ignoreSelection : true;
  419. var node = null;
  420. if (ignoreSelection)
  421. {
  422. var enc = new mxCodec(mxUtils.createXmlDocument());
  423. node = enc.encode(this.graph.getModel());
  424. }
  425. else
  426. {
  427. node = this.graph.encodeCells(mxUtils.sortCells(this.graph.model.getTopmostCells(
  428. this.graph.getSelectionCells())));
  429. }
  430. if (this.graph.view.translate.x != 0 || this.graph.view.translate.y != 0)
  431. {
  432. node.setAttribute('dx', Math.round(this.graph.view.translate.x * 100) / 100);
  433. node.setAttribute('dy', Math.round(this.graph.view.translate.y * 100) / 100);
  434. }
  435. node.setAttribute('grid', (this.graph.isGridEnabled()) ? '1' : '0');
  436. node.setAttribute('gridSize', this.graph.gridSize);
  437. node.setAttribute('guides', (this.graph.graphHandler.guidesEnabled) ? '1' : '0');
  438. node.setAttribute('tooltips', (this.graph.tooltipHandler.isEnabled()) ? '1' : '0');
  439. node.setAttribute('connect', (this.graph.connectionHandler.isEnabled()) ? '1' : '0');
  440. node.setAttribute('arrows', (this.graph.connectionArrowsEnabled) ? '1' : '0');
  441. node.setAttribute('fold', (this.graph.foldingEnabled) ? '1' : '0');
  442. node.setAttribute('page', (this.graph.pageVisible) ? '1' : '0');
  443. node.setAttribute('pageScale', this.graph.pageScale);
  444. node.setAttribute('pageWidth', this.graph.pageFormat.width);
  445. node.setAttribute('pageHeight', this.graph.pageFormat.height);
  446. if (this.graph.background != null)
  447. {
  448. node.setAttribute('background', this.graph.background);
  449. }
  450. return node;
  451. };
  452. /**
  453. * Keeps the graph container in sync with the persistent graph state
  454. */
  455. Editor.prototype.updateGraphComponents = function()
  456. {
  457. var graph = this.graph;
  458. if (graph.container != null)
  459. {
  460. graph.view.validateBackground();
  461. graph.container.style.overflow = (graph.scrollbars) ? 'auto' : 'hidden';
  462. this.fireEvent(new mxEventObject('updateGraphComponents'));
  463. }
  464. };
  465. /**
  466. * Sets the modified flag.
  467. */
  468. Editor.prototype.setModified = function(value)
  469. {
  470. this.modified = value;
  471. };
  472. /**
  473. * Sets the filename.
  474. */
  475. Editor.prototype.setFilename = function(value)
  476. {
  477. this.filename = value;
  478. };
  479. /**
  480. * Creates and returns a new undo manager.
  481. */
  482. Editor.prototype.createUndoManager = function()
  483. {
  484. var graph = this.graph;
  485. var undoMgr = new mxUndoManager();
  486. this.undoListener = function(sender, evt)
  487. {
  488. undoMgr.undoableEditHappened(evt.getProperty('edit'));
  489. };
  490. // Installs the command history
  491. var listener = mxUtils.bind(this, function(sender, evt)
  492. {
  493. this.undoListener.apply(this, arguments);
  494. });
  495. graph.getModel().addListener(mxEvent.UNDO, listener);
  496. graph.getView().addListener(mxEvent.UNDO, listener);
  497. // Keeps the selection in sync with the history
  498. var undoHandler = function(sender, evt)
  499. {
  500. var cand = graph.getSelectionCellsForChanges(evt.getProperty('edit').changes);
  501. var model = graph.getModel();
  502. var cells = [];
  503. for (var i = 0; i < cand.length; i++)
  504. {
  505. if ((model.isVertex(cand[i]) || model.isEdge(cand[i])) && graph.view.getState(cand[i]) != null)
  506. {
  507. cells.push(cand[i]);
  508. }
  509. }
  510. graph.setSelectionCells(cells);
  511. };
  512. undoMgr.addListener(mxEvent.UNDO, undoHandler);
  513. undoMgr.addListener(mxEvent.REDO, undoHandler);
  514. return undoMgr;
  515. };
  516. /**
  517. * Adds basic stencil set (no namespace).
  518. */
  519. Editor.prototype.initStencilRegistry = function() { };
  520. /**
  521. * Creates and returns a new undo manager.
  522. */
  523. Editor.prototype.destroy = function()
  524. {
  525. if (this.graph != null)
  526. {
  527. this.graph.destroy();
  528. this.graph = null;
  529. }
  530. };
  531. /**
  532. * Class for asynchronously opening a new window and loading a file at the same
  533. * time. This acts as a bridge between the open dialog and the new editor.
  534. */
  535. OpenFile = function(done)
  536. {
  537. this.producer = null;
  538. this.consumer = null;
  539. this.done = done;
  540. this.args = null;
  541. };
  542. /**
  543. * Registers the editor from the new window.
  544. */
  545. OpenFile.prototype.setConsumer = function(value)
  546. {
  547. this.consumer = value;
  548. this.execute();
  549. };
  550. /**
  551. * Sets the data from the loaded file.
  552. */
  553. OpenFile.prototype.setData = function()
  554. {
  555. this.args = arguments;
  556. this.execute();
  557. };
  558. /**
  559. * Displays an error message.
  560. */
  561. OpenFile.prototype.error = function(msg)
  562. {
  563. this.cancel(true);
  564. mxUtils.alert(msg);
  565. };
  566. /**
  567. * Consumes the data.
  568. */
  569. OpenFile.prototype.execute = function()
  570. {
  571. if (this.consumer != null && this.args != null)
  572. {
  573. this.cancel(false);
  574. this.consumer.apply(this, this.args);
  575. }
  576. };
  577. /**
  578. * Cancels the operation.
  579. */
  580. OpenFile.prototype.cancel = function(cancel)
  581. {
  582. if (this.done != null)
  583. {
  584. this.done((cancel != null) ? cancel : true);
  585. }
  586. };
  587. /**
  588. * Basic dialogs that are available in the viewer (print dialog).
  589. */
  590. function Dialog(editorUi, elt, w, h, modal, closable, onClose)
  591. {
  592. var dx = 0;
  593. if (mxClient.IS_VML && (document.documentMode == null || document.documentMode < 8))
  594. {
  595. // Adds padding as a workaround for box model in older IE versions
  596. // This needs to match the total padding of geDialog in CSS
  597. dx = 80;
  598. }
  599. w += dx;
  600. h += dx;
  601. var dh = Math.max(document.body.clientHeight, document.documentElement.clientHeight);
  602. var left = Math.max(1, Math.round((document.body.clientWidth - w - 64) / 2));
  603. var top = Math.max(1, Math.round((dh - h - editorUi.footerHeight) / 3));
  604. // Keeps window size inside available space
  605. if (!mxClient.IS_QUIRKS)
  606. {
  607. elt.style.maxHeight = '100%';
  608. }
  609. w = Math.min(w, document.body.scrollWidth - 64);
  610. if (h > dh - 64)
  611. {
  612. elt.style.overflowY = 'auto';
  613. }
  614. h = Math.min(h, dh - 64);
  615. // Increments zIndex to put subdialogs and background over existing dialogs and background
  616. if (editorUi.dialogs.length > 0)
  617. {
  618. this.zIndex += editorUi.dialogs.length * 2;
  619. }
  620. if (this.bg == null)
  621. {
  622. this.bg = editorUi.createDiv('background');
  623. this.bg.style.position = 'absolute';
  624. this.bg.style.background = 'white';
  625. this.bg.style.height = dh + 'px';
  626. this.bg.style.right = '0px';
  627. this.bg.style.zIndex = this.zIndex - 2;
  628. mxUtils.setOpacity(this.bg, this.bgOpacity);
  629. if (mxClient.IS_QUIRKS)
  630. {
  631. new mxDivResizer(this.bg);
  632. }
  633. }
  634. var origin = mxUtils.getDocumentScrollOrigin(document);
  635. this.bg.style.left = origin.x + 'px';
  636. this.bg.style.top = origin.y + 'px';
  637. left += origin.x;
  638. top += origin.y;
  639. if (modal)
  640. {
  641. document.body.appendChild(this.bg);
  642. }
  643. var div = editorUi.createDiv('geDialog');
  644. div.style.width = w + 'px';
  645. div.style.height = h + 'px';
  646. div.style.left = left + 'px';
  647. div.style.top = top + 'px';
  648. div.style.zIndex = this.zIndex;
  649. div.appendChild(elt);
  650. document.body.appendChild(div);
  651. if (closable)
  652. {
  653. var img = document.createElement('img');
  654. img.setAttribute('src', Dialog.prototype.closeImage);
  655. img.setAttribute('title', mxResources.get('close'));
  656. img.className = 'geDialogClose';
  657. img.style.top = (top + 14) + 'px';
  658. img.style.left = (left + w + 38 - dx) + 'px';
  659. img.style.zIndex = this.zIndex;
  660. mxEvent.addListener(img, 'click', mxUtils.bind(this, function()
  661. {
  662. editorUi.hideDialog(true);
  663. }));
  664. document.body.appendChild(img);
  665. this.dialogImg = img;
  666. mxEvent.addGestureListeners(this.bg, null, null, mxUtils.bind(this, function(evt)
  667. {
  668. editorUi.hideDialog(true);
  669. }));
  670. }
  671. this.onDialogClose = onClose;
  672. this.container = div;
  673. editorUi.editor.fireEvent(new mxEventObject('showDialog'));
  674. };
  675. /**
  676. *
  677. */
  678. Dialog.prototype.zIndex = mxPopupMenu.prototype.zIndex - 1;
  679. /**
  680. *
  681. */
  682. Dialog.prototype.noColorImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/nocolor.png' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkEzRDlBMUUwODYxMTExRTFCMzA4RDdDMjJBMEMxRDM3IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkEzRDlBMUUxODYxMTExRTFCMzA4RDdDMjJBMEMxRDM3Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QTNEOUExREU4NjExMTFFMUIzMDhEN0MyMkEwQzFEMzciIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QTNEOUExREY4NjExMTFFMUIzMDhEN0MyMkEwQzFEMzciLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5xh3fmAAAABlBMVEX////MzMw46qqDAAAAGElEQVR42mJggAJGKGAYIIGBth8KAAIMAEUQAIElnLuQAAAAAElFTkSuQmCC';
  683. /**
  684. *
  685. */
  686. Dialog.prototype.closeImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/close.png' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJAQMAAADaX5RTAAAABlBMVEV7mr3///+wksspAAAAAnRSTlP/AOW3MEoAAAAdSURBVAgdY9jXwCDDwNDRwHCwgeExmASygSL7GgB12QiqNHZZIwAAAABJRU5ErkJggg==';
  687. /**
  688. *
  689. */
  690. Dialog.prototype.clearImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/clear.gif' : 'data:image/gif;base64,R0lGODlhDQAKAIABAMDAwP///yH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IFdpbmRvd3MiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OUIzOEM1NzI4NjEyMTFFMUEzMkNDMUE3NjZERDE2QjIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OUIzOEM1NzM4NjEyMTFFMUEzMkNDMUE3NjZERDE2QjIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo5QjM4QzU3MDg2MTIxMUUxQTMyQ0MxQTc2NkREMTZCMiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo5QjM4QzU3MTg2MTIxMUUxQTMyQ0MxQTc2NkREMTZCMiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgH//v38+/r5+Pf29fTz8vHw7+7t7Ovq6ejn5uXk4+Lh4N/e3dzb2tnY19bV1NPS0dDPzs3My8rJyMfGxcTDwsHAv769vLu6ubi3trW0s7KxsK+urayrqqmop6alpKOioaCfnp2cm5qZmJeWlZSTkpGQj46NjIuKiYiHhoWEg4KBgH9+fXx7enl4d3Z1dHNycXBvbm1sa2ppaGdmZWRjYmFgX15dXFtaWVhXVlVUU1JRUE9OTUxLSklIR0ZFRENCQUA/Pj08Ozo5ODc2NTQzMjEwLy4tLCsqKSgnJiUkIyIhIB8eHRwbGhkYFxYVFBMSERAPDg0MCwoJCAcGBQQDAgEAACH5BAEAAAEALAAAAAANAAoAAAIXTGCJebD9jEOTqRlttXdrB32PJ2ncyRQAOw==';
  691. /**
  692. *
  693. */
  694. Dialog.prototype.lockedImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/locked.png' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAMAAABhq6zVAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MzdDMDZCODExNzIxMTFFNUI0RTk5NTg4OTcyMUUyODEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MzdDMDZCODIxNzIxMTFFNUI0RTk5NTg4OTcyMUUyODEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDozN0MwNkI3RjE3MjExMUU1QjRFOTk1ODg5NzIxRTI4MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDozN0MwNkI4MDE3MjExMUU1QjRFOTk1ODg5NzIxRTI4MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PvqMCFYAAAAVUExURZmZmb+/v7KysqysrMzMzLGxsf///4g8N1cAAAAHdFJOU////////wAaSwNGAAAAPElEQVR42lTMQQ4AIQgEwUa0//9kTQirOweYOgDqAMbZUr10AGlAwx4/BJ2QJ4U0L5brYjovvpv32xZgAHZaATFtMbu4AAAAAElFTkSuQmCC';
  695. /**
  696. *
  697. */
  698. Dialog.prototype.unlockedImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/unlocked.png' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAMAAABhq6zVAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MzdDMDZCN0QxNzIxMTFFNUI0RTk5NTg4OTcyMUUyODEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MzdDMDZCN0UxNzIxMTFFNUI0RTk5NTg4OTcyMUUyODEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDozN0MwNkI3QjE3MjExMUU1QjRFOTk1ODg5NzIxRTI4MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDozN0MwNkI3QzE3MjExMUU1QjRFOTk1ODg5NzIxRTI4MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PkKMpVwAAAAYUExURZmZmbKysr+/v6ysrOXl5czMzLGxsf///zHN5lwAAAAIdFJOU/////////8A3oO9WQAAADxJREFUeNpUzFESACAEBNBVsfe/cZJU+8Mzs8CIABCidtfGOndnYsT40HDSiCcbPdoJo10o9aI677cpwACRoAF3dFNlswAAAABJRU5ErkJggg==';
  699. /**
  700. * Removes the dialog from the DOM.
  701. */
  702. Dialog.prototype.bgOpacity = 80;
  703. /**
  704. * Removes the dialog from the DOM.
  705. */
  706. Dialog.prototype.close = function(cancel)
  707. {
  708. if (this.onDialogClose != null)
  709. {
  710. this.onDialogClose(cancel);
  711. this.onDialogClose = null;
  712. }
  713. if (this.dialogImg != null)
  714. {
  715. this.dialogImg.parentNode.removeChild(this.dialogImg);
  716. this.dialogImg = null;
  717. }
  718. if (this.bg != null && this.bg.parentNode != null)
  719. {
  720. this.bg.parentNode.removeChild(this.bg);
  721. }
  722. this.container.parentNode.removeChild(this.container);
  723. };
  724. /**
  725. * Constructs a new print dialog.
  726. */
  727. var PrintDialog = function(editorUi, title)
  728. {
  729. this.create(editorUi, title);
  730. };
  731. /**
  732. * Constructs a new print dialog.
  733. */
  734. PrintDialog.prototype.create = function(editorUi)
  735. {
  736. var graph = editorUi.editor.graph;
  737. var row, td;
  738. var table = document.createElement('table');
  739. table.style.width = '100%';
  740. table.style.height = '100%';
  741. var tbody = document.createElement('tbody');
  742. row = document.createElement('tr');
  743. var onePageCheckBox = document.createElement('input');
  744. onePageCheckBox.setAttribute('type', 'checkbox');
  745. td = document.createElement('td');
  746. td.setAttribute('colspan', '2');
  747. td.style.fontSize = '10pt';
  748. td.appendChild(onePageCheckBox);
  749. var span = document.createElement('span');
  750. mxUtils.write(span, ' ' + mxResources.get('fitPage'));
  751. td.appendChild(span);
  752. mxEvent.addListener(span, 'click', function(evt)
  753. {
  754. onePageCheckBox.checked = !onePageCheckBox.checked;
  755. pageCountCheckBox.checked = !onePageCheckBox.checked;
  756. mxEvent.consume(evt);
  757. });
  758. mxEvent.addListener(onePageCheckBox, 'change', function()
  759. {
  760. pageCountCheckBox.checked = !onePageCheckBox.checked;
  761. });
  762. row.appendChild(td);
  763. tbody.appendChild(row);
  764. row = row.cloneNode(false);
  765. var pageCountCheckBox = document.createElement('input');
  766. pageCountCheckBox.setAttribute('type', 'checkbox');
  767. td = document.createElement('td');
  768. td.style.fontSize = '10pt';
  769. td.appendChild(pageCountCheckBox);
  770. var span = document.createElement('span');
  771. mxUtils.write(span, ' ' + mxResources.get('posterPrint') + ':');
  772. td.appendChild(span);
  773. mxEvent.addListener(span, 'click', function(evt)
  774. {
  775. pageCountCheckBox.checked = !pageCountCheckBox.checked;
  776. onePageCheckBox.checked = !pageCountCheckBox.checked;
  777. mxEvent.consume(evt);
  778. });
  779. row.appendChild(td);
  780. var pageCountInput = document.createElement('input');
  781. pageCountInput.setAttribute('value', '1');
  782. pageCountInput.setAttribute('type', 'number');
  783. pageCountInput.setAttribute('min', '1');
  784. pageCountInput.setAttribute('size', '4');
  785. pageCountInput.setAttribute('disabled', 'disabled');
  786. pageCountInput.style.width = '50px';
  787. td = document.createElement('td');
  788. td.style.fontSize = '10pt';
  789. td.appendChild(pageCountInput);
  790. mxUtils.write(td, ' ' + mxResources.get('pages') + ' (max)');
  791. row.appendChild(td);
  792. tbody.appendChild(row);
  793. mxEvent.addListener(pageCountCheckBox, 'change', function()
  794. {
  795. if (pageCountCheckBox.checked)
  796. {
  797. pageCountInput.removeAttribute('disabled');
  798. }
  799. else
  800. {
  801. pageCountInput.setAttribute('disabled', 'disabled');
  802. }
  803. onePageCheckBox.checked = !pageCountCheckBox.checked;
  804. });
  805. row = row.cloneNode(false);
  806. td = document.createElement('td');
  807. mxUtils.write(td, mxResources.get('pageScale') + ':');
  808. row.appendChild(td);
  809. td = document.createElement('td');
  810. var pageScaleInput = document.createElement('input');
  811. pageScaleInput.setAttribute('value', '100 %');
  812. pageScaleInput.setAttribute('size', '5');
  813. pageScaleInput.style.width = '50px';
  814. td.appendChild(pageScaleInput);
  815. row.appendChild(td);
  816. tbody.appendChild(row);
  817. row = document.createElement('tr');
  818. td = document.createElement('td');
  819. td.colSpan = 2;
  820. td.style.paddingTop = '20px';
  821. td.setAttribute('align', 'right');
  822. // Overall scale for print-out to account for print borders in dialogs etc
  823. function preview(print)
  824. {
  825. var autoOrigin = onePageCheckBox.checked || pageCountCheckBox.checked;
  826. var printScale = parseInt(pageScaleInput.value) / 100;
  827. if (isNaN(printScale))
  828. {
  829. printScale = 1;
  830. pageScaleInput.value = '100%';
  831. }
  832. // Workaround to match available paper size in actual print output
  833. printScale *= 0.75;
  834. var pf = graph.pageFormat || mxConstants.PAGE_FORMAT_A4_PORTRAIT;
  835. var scale = 1 / graph.pageScale;
  836. if (autoOrigin)
  837. {
  838. var pageCount = (onePageCheckBox.checked) ? 1 : parseInt(pageCountInput.value);
  839. if (!isNaN(pageCount))
  840. {
  841. scale = mxUtils.getScaleForPageCount(pageCount, graph, pf);
  842. }
  843. }
  844. // Negative coordinates are cropped or shifted if page visible
  845. var gb = graph.getGraphBounds();
  846. var border = 0;
  847. var x0 = 0;
  848. var y0 = 0;
  849. // Applies print scale
  850. pf = mxRectangle.fromRectangle(pf);
  851. pf.width = Math.ceil(pf.width * printScale);
  852. pf.height = Math.ceil(pf.height * printScale);
  853. scale *= printScale;
  854. // Starts at first visible page
  855. if (!autoOrigin && graph.pageVisible)
  856. {
  857. var layout = graph.getPageLayout();
  858. x0 -= layout.x * pf.width;
  859. y0 -= layout.y * pf.height;
  860. }
  861. else
  862. {
  863. autoOrigin = true;
  864. }
  865. var preview = PrintDialog.createPrintPreview(graph, scale, pf, border, x0, y0, autoOrigin);
  866. preview.open();
  867. if (print)
  868. {
  869. PrintDialog.printPreview(preview);
  870. }
  871. };
  872. var cancelBtn = mxUtils.button(mxResources.get('cancel'), function()
  873. {
  874. editorUi.hideDialog();
  875. });
  876. cancelBtn.className = 'geBtn';
  877. if (editorUi.editor.cancelFirst)
  878. {
  879. td.appendChild(cancelBtn);
  880. }
  881. if (PrintDialog.previewEnabled)
  882. {
  883. var previewBtn = mxUtils.button(mxResources.get('preview'), function()
  884. {
  885. editorUi.hideDialog();
  886. preview(false);
  887. });
  888. previewBtn.className = 'geBtn';
  889. td.appendChild(previewBtn);
  890. }
  891. var printBtn = mxUtils.button(mxResources.get((!PrintDialog.previewEnabled) ? 'ok' : 'print'), function()
  892. {
  893. editorUi.hideDialog();
  894. preview(true);
  895. });
  896. printBtn.className = 'geBtn gePrimaryBtn';
  897. td.appendChild(printBtn);
  898. if (!editorUi.editor.cancelFirst)
  899. {
  900. td.appendChild(cancelBtn);
  901. }
  902. row.appendChild(td);
  903. tbody.appendChild(row);
  904. table.appendChild(tbody);
  905. this.container = table;
  906. };
  907. /**
  908. * Constructs a new print dialog.
  909. */
  910. PrintDialog.printPreview = function(preview)
  911. {
  912. if (preview.wnd != null)
  913. {
  914. var printFn = function()
  915. {
  916. preview.wnd.focus();
  917. preview.wnd.print();
  918. preview.wnd.close();
  919. };
  920. // Workaround for Google Chrome which needs a bit of a
  921. // delay in order to render the SVG contents
  922. // Needs testing in production
  923. if (mxClient.IS_GC)
  924. {
  925. window.setTimeout(printFn, 500);
  926. }
  927. else
  928. {
  929. printFn();
  930. }
  931. }
  932. };
  933. /**
  934. * Constructs a new print dialog.
  935. */
  936. PrintDialog.createPrintPreview = function(graph, scale, pf, border, x0, y0, autoOrigin)
  937. {
  938. var preview = new mxPrintPreview(graph, scale, pf, border, x0, y0);
  939. preview.title = mxResources.get('preview');
  940. preview.printBackgroundImage = true;
  941. preview.autoOrigin = autoOrigin;
  942. var bg = graph.background;
  943. if (bg == null || bg == '' || bg == mxConstants.NONE)
  944. {
  945. bg = '#ffffff';
  946. }
  947. preview.backgroundColor = bg;
  948. var writeHead = preview.writeHead;
  949. // Adds a border in the preview
  950. preview.writeHead = function(doc)
  951. {
  952. writeHead.apply(this, arguments);
  953. doc.writeln('<style type="text/css">');
  954. doc.writeln('@media screen {');
  955. doc.writeln(' body > div { padding:30px;box-sizing:content-box; }');
  956. doc.writeln('}');
  957. doc.writeln('</style>');
  958. };
  959. return preview;
  960. };
  961. /**
  962. * Specifies if the preview button should be enabled. Default is true.
  963. */
  964. PrintDialog.previewEnabled = true;
  965. /**
  966. * Constructs a new page setup dialog.
  967. */
  968. var PageSetupDialog = function(editorUi)
  969. {
  970. var graph = editorUi.editor.graph;
  971. var row, td;
  972. var table = document.createElement('table');
  973. table.style.width = '100%';
  974. table.style.height = '100%';
  975. var tbody = document.createElement('tbody');
  976. row = document.createElement('tr');
  977. td = document.createElement('td');
  978. td.style.verticalAlign = 'top';
  979. td.style.fontSize = '10pt';
  980. mxUtils.write(td, mxResources.get('paperSize') + ':');
  981. row.appendChild(td);
  982. td = document.createElement('td');
  983. td.style.verticalAlign = 'top';
  984. td.style.fontSize = '10pt';
  985. var accessor = PageSetupDialog.addPageFormatPanel(td, 'pagesetupdialog', graph.pageFormat);
  986. row.appendChild(td);
  987. tbody.appendChild(row);
  988. row = document.createElement('tr');
  989. td = document.createElement('td');
  990. mxUtils.write(td, mxResources.get('background') + ':');
  991. row.appendChild(td);
  992. td = document.createElement('td');
  993. td.style.whiteSpace = 'nowrap';
  994. var backgroundInput = document.createElement('input');
  995. backgroundInput.setAttribute('type', 'text');
  996. var backgroundButton = document.createElement('button');
  997. backgroundButton.style.width = '18px';
  998. backgroundButton.style.height = '18px';
  999. backgroundButton.style.marginRight = '20px';
  1000. backgroundButton.style.backgroundPosition = 'center center';
  1001. backgroundButton.style.backgroundRepeat = 'no-repeat';
  1002. var newBackgroundColor = graph.background;
  1003. function updateBackgroundColor()
  1004. {
  1005. if (newBackgroundColor == null || newBackgroundColor == mxConstants.NONE)
  1006. {
  1007. backgroundButton.style.backgroundColor = '';
  1008. backgroundButton.style.backgroundImage = 'url(\'' + Dialog.prototype.noColorImage + '\')';
  1009. }
  1010. else
  1011. {
  1012. backgroundButton.style.backgroundColor = newBackgroundColor;
  1013. backgroundButton.style.backgroundImage = '';
  1014. }
  1015. };
  1016. updateBackgroundColor();
  1017. mxEvent.addListener(backgroundButton, 'click', function(evt)
  1018. {
  1019. editorUi.pickColor(newBackgroundColor || 'none', function(color)
  1020. {
  1021. newBackgroundColor = color;
  1022. updateBackgroundColor();
  1023. });
  1024. mxEvent.consume(evt);
  1025. });
  1026. td.appendChild(backgroundButton);
  1027. mxUtils.write(td, mxResources.get('gridSize') + ':');
  1028. var gridSizeInput = document.createElement('input');
  1029. gridSizeInput.setAttribute('type', 'number');
  1030. gridSizeInput.setAttribute('min', '0');
  1031. gridSizeInput.style.width = '40px';
  1032. gridSizeInput.style.marginLeft = '6px';
  1033. gridSizeInput.value = graph.getGridSize();
  1034. td.appendChild(gridSizeInput);
  1035. mxEvent.addListener(gridSizeInput, 'change', function()
  1036. {
  1037. var value = parseInt(gridSizeInput.value);
  1038. gridSizeInput.value = Math.max(1, (isNaN(value)) ? graph.getGridSize() : value);
  1039. });
  1040. row.appendChild(td);
  1041. tbody.appendChild(row);
  1042. row = document.createElement('tr');
  1043. td = document.createElement('td');
  1044. mxUtils.write(td, mxResources.get('image') + ':');
  1045. row.appendChild(td);
  1046. td = document.createElement('td');
  1047. var changeImageLink = document.createElement('a');
  1048. changeImageLink.style.textDecoration = 'underline';
  1049. changeImageLink.style.cursor = 'pointer';
  1050. changeImageLink.style.color = '#a0a0a0';
  1051. var newBackgroundImage = graph.backgroundImage;
  1052. function updateBackgroundImage()
  1053. {
  1054. if (newBackgroundImage == null)
  1055. {
  1056. changeImageLink.removeAttribute('title');
  1057. changeImageLink.style.fontSize = '';
  1058. changeImageLink.innerHTML = mxResources.get('change') + '...';
  1059. }
  1060. else
  1061. {
  1062. changeImageLink.setAttribute('title', newBackgroundImage.src);
  1063. changeImageLink.style.fontSize = '11px';
  1064. changeImageLink.innerHTML = newBackgroundImage.src.substring(0, 42) + '...';
  1065. }
  1066. };
  1067. mxEvent.addListener(changeImageLink, 'click', function(evt)
  1068. {
  1069. editorUi.showBackgroundImageDialog(function(image)
  1070. {
  1071. newBackgroundImage = image;
  1072. updateBackgroundImage();
  1073. });
  1074. mxEvent.consume(evt);
  1075. });
  1076. updateBackgroundImage();
  1077. td.appendChild(changeImageLink);
  1078. row.appendChild(td);
  1079. tbody.appendChild(row);
  1080. row = document.createElement('tr');
  1081. td = document.createElement('td');
  1082. td.colSpan = 2;
  1083. td.style.paddingTop = '16px';
  1084. td.setAttribute('align', 'right');
  1085. var cancelBtn = mxUtils.button(mxResources.get('cancel'), function()
  1086. {
  1087. editorUi.hideDialog();
  1088. });
  1089. cancelBtn.className = 'geBtn';
  1090. if (editorUi.editor.cancelFirst)
  1091. {
  1092. td.appendChild(cancelBtn);
  1093. }
  1094. var applyBtn = mxUtils.button(mxResources.get('apply'), function()
  1095. {
  1096. editorUi.hideDialog();
  1097. if (graph.gridSize !== gridSizeInput.value)
  1098. {
  1099. graph.setGridSize(parseInt(gridSizeInput.value));
  1100. }
  1101. var change = new ChangePageSetup(editorUi, newBackgroundColor,
  1102. newBackgroundImage, accessor.get());
  1103. change.ignoreColor = graph.background == newBackgroundColor;
  1104. var oldSrc = (graph.backgroundImage != null) ? graph.backgroundImage.src : null;
  1105. var newSrc = (newBackgroundImage != null) ? newBackgroundImage.src : null;
  1106. change.ignoreImage = oldSrc === newSrc;
  1107. if (graph.pageFormat.width != change.previousFormat.width ||
  1108. graph.pageFormat.height != change.previousFormat.height ||
  1109. !change.ignoreColor || !change.ignoreImage)
  1110. {
  1111. graph.model.execute(change);
  1112. }
  1113. });
  1114. applyBtn.className = 'geBtn gePrimaryBtn';
  1115. td.appendChild(applyBtn);
  1116. if (!editorUi.editor.cancelFirst)
  1117. {
  1118. td.appendChild(cancelBtn);
  1119. }
  1120. row.appendChild(td);
  1121. tbody.appendChild(row);
  1122. table.appendChild(tbody);
  1123. this.container = table;
  1124. };
  1125. /**
  1126. *
  1127. */
  1128. PageSetupDialog.addPageFormatPanel = function(div, namePostfix, pageFormat, pageFormatListener)
  1129. {
  1130. var formatName = 'format-' + namePostfix;
  1131. var portraitCheckBox = document.createElement('input');
  1132. portraitCheckBox.setAttribute('name', formatName);
  1133. portraitCheckBox.setAttribute('type', 'radio');
  1134. portraitCheckBox.setAttribute('value', 'portrait');
  1135. var landscapeCheckBox = document.createElement('input');
  1136. landscapeCheckBox.setAttribute('name', formatName);
  1137. landscapeCheckBox.setAttribute('type', 'radio');
  1138. landscapeCheckBox.setAttribute('value', 'landscape');
  1139. var paperSizeSelect = document.createElement('select');
  1140. paperSizeSelect.style.marginBottom = '8px';
  1141. paperSizeSelect.style.width = '202px';
  1142. var formatDiv = document.createElement('div');
  1143. formatDiv.style.marginLeft = '4px';
  1144. formatDiv.style.width = '210px';
  1145. formatDiv.style.height = '24px';
  1146. portraitCheckBox.style.marginRight = '6px';
  1147. formatDiv.appendChild(portraitCheckBox);
  1148. var portraitSpan = document.createElement('span');
  1149. portraitSpan.style.maxWidth = '100px';
  1150. mxUtils.write(portraitSpan, mxResources.get('portrait'));
  1151. formatDiv.appendChild(portraitSpan);
  1152. landscapeCheckBox.style.marginLeft = '10px';
  1153. landscapeCheckBox.style.marginRight = '6px';
  1154. formatDiv.appendChild(landscapeCheckBox);
  1155. var landscapeSpan = document.createElement('span');
  1156. landscapeSpan.style.width = '100px';
  1157. mxUtils.write(landscapeSpan, mxResources.get('landscape'));
  1158. formatDiv.appendChild(landscapeSpan)
  1159. var customDiv = document.createElement('div');
  1160. customDiv.style.marginLeft = '4px';
  1161. customDiv.style.width = '210px';
  1162. customDiv.style.height = '24px';
  1163. var widthInput = document.createElement('input');
  1164. widthInput.setAttribute('size', '7');
  1165. widthInput.setAttribute('value', pageFormat.width);
  1166. widthInput.style.textAlign = 'right';
  1167. customDiv.appendChild(widthInput);
  1168. mxUtils.write(customDiv, ' in x ');
  1169. var heightInput = document.createElement('input');
  1170. heightInput.setAttribute('size', '7');
  1171. heightInput.setAttribute('value', pageFormat.height);
  1172. heightInput.style.textAlign = 'right';
  1173. customDiv.appendChild(heightInput);
  1174. mxUtils.write(customDiv, ' in');
  1175. formatDiv.style.display = 'none';
  1176. customDiv.style.display = 'none';
  1177. var pf = new Object();
  1178. var formats = PageSetupDialog.getFormats();
  1179. for (var i = 0; i < formats.length; i++)
  1180. {
  1181. var f = formats[i];
  1182. pf[f.key] = f;
  1183. var paperSizeOption = document.createElement('option');
  1184. paperSizeOption.setAttribute('value', f.key);
  1185. mxUtils.write(paperSizeOption, f.title);
  1186. paperSizeSelect.appendChild(paperSizeOption);
  1187. }
  1188. var customSize = false;
  1189. function listener(sender, evt, force)
  1190. {
  1191. if (force || (widthInput != document.activeElement && heightInput != document.activeElement))
  1192. {
  1193. var detected = false;
  1194. for (var i = 0; i < formats.length; i++)
  1195. {
  1196. var f = formats[i];
  1197. // Special case where custom was chosen
  1198. if (customSize)
  1199. {
  1200. if (f.key == 'custom')
  1201. {
  1202. paperSizeSelect.value = f.key;
  1203. customSize = false;
  1204. }
  1205. }
  1206. else if (f.format != null)
  1207. {
  1208. // Fixes wrong values for previous A4 and A5 page sizes
  1209. if (f.key == 'a4')
  1210. {
  1211. if (pageFormat.width == 826)
  1212. {
  1213. pageFormat = mxRectangle.fromRectangle(pageFormat);
  1214. pageFormat.width = 827;
  1215. }
  1216. else if (pageFormat.height == 826)
  1217. {
  1218. pageFormat = mxRectangle.fromRectangle(pageFormat);
  1219. pageFormat.height = 827;
  1220. }
  1221. }
  1222. else if (f.key == 'a5')
  1223. {
  1224. if (pageFormat.width == 584)
  1225. {
  1226. pageFormat = mxRectangle.fromRectangle(pageFormat);
  1227. pageFormat.width = 583;
  1228. }
  1229. else if (pageFormat.height == 584)
  1230. {
  1231. pageFormat = mxRectangle.fromRectangle(pageFormat);
  1232. pageFormat.height = 583;
  1233. }
  1234. }
  1235. if (pageFormat.width == f.format.width && pageFormat.height == f.format.height)
  1236. {
  1237. paperSizeSelect.value = f.key;
  1238. portraitCheckBox.setAttribute('checked', 'checked');
  1239. portraitCheckBox.defaultChecked = true;
  1240. portraitCheckBox.checked = true;
  1241. landscapeCheckBox.removeAttribute('checked');
  1242. landscapeCheckBox.defaultChecked = false;
  1243. landscapeCheckBox.checked = false;
  1244. detected = true;
  1245. }
  1246. else if (pageFormat.width == f.format.height && pageFormat.height == f.format.width)
  1247. {
  1248. paperSizeSelect.value = f.key;
  1249. portraitCheckBox.removeAttribute('checked');
  1250. portraitCheckBox.defaultChecked = false;
  1251. portraitCheckBox.checked = false;
  1252. landscapeCheckBox.setAttribute('checked', 'checked');
  1253. landscapeCheckBox.defaultChecked = true;
  1254. landscapeCheckBox.checked = true;
  1255. detected = true;
  1256. }
  1257. }
  1258. }
  1259. // Selects custom format which is last in list
  1260. if (!detected)
  1261. {
  1262. widthInput.value = pageFormat.width / 100;
  1263. heightInput.value =pageFormat.height / 100;
  1264. paperSizeOption.setAttribute('selected', 'selected');
  1265. portraitCheckBox.setAttribute('checked', 'checked');
  1266. portraitCheckBox.defaultChecked = true;
  1267. formatDiv.style.display = 'none';
  1268. customDiv.style.display = '';
  1269. }
  1270. else
  1271. {
  1272. formatDiv.style.display = '';
  1273. customDiv.style.display = 'none';
  1274. }
  1275. }
  1276. };
  1277. listener();
  1278. div.appendChild(paperSizeSelect);
  1279. mxUtils.br(div);
  1280. div.appendChild(formatDiv);
  1281. div.appendChild(customDiv);
  1282. var currentPageFormat = pageFormat;
  1283. var update = function()
  1284. {
  1285. var f = pf[paperSizeSelect.value];
  1286. if (f.format != null)
  1287. {
  1288. widthInput.value = f.format.width / 100;
  1289. heightInput.value = f.format.height / 100;
  1290. customDiv.style.display = 'none';
  1291. formatDiv.style.display = '';
  1292. }
  1293. else
  1294. {
  1295. formatDiv.style.display = 'none';
  1296. customDiv.style.display = '';
  1297. }
  1298. var newPageFormat = new mxRectangle(0, 0,
  1299. Math.floor(parseFloat(widthInput.value) * 100),
  1300. Math.floor(parseFloat(heightInput.value) * 100));
  1301. if (paperSizeSelect.value != 'custom' && landscapeCheckBox.checked)
  1302. {
  1303. newPageFormat = new mxRectangle(0, 0, newPageFormat.height, newPageFormat.width);
  1304. }
  1305. if (newPageFormat.width != currentPageFormat.width || newPageFormat.height != currentPageFormat.height)
  1306. {
  1307. currentPageFormat = newPageFormat;
  1308. if (pageFormatListener != null)
  1309. {
  1310. pageFormatListener(currentPageFormat);
  1311. }
  1312. }
  1313. };
  1314. mxEvent.addListener(portraitSpan, 'click', function(evt)
  1315. {
  1316. portraitCheckBox.checked = true;
  1317. update();
  1318. mxEvent.consume(evt);
  1319. });
  1320. mxEvent.addListener(landscapeSpan, 'click', function(evt)
  1321. {
  1322. landscapeCheckBox.checked = true;
  1323. update();
  1324. mxEvent.consume(evt);
  1325. });
  1326. mxEvent.addListener(widthInput, 'blur', update);
  1327. mxEvent.addListener(widthInput, 'click', update);
  1328. mxEvent.addListener(heightInput, 'blur', update);
  1329. mxEvent.addListener(heightInput, 'click', update);
  1330. mxEvent.addListener(landscapeCheckBox, 'change', update);
  1331. mxEvent.addListener(portraitCheckBox, 'change', update);
  1332. mxEvent.addListener(paperSizeSelect, 'change', function()
  1333. {
  1334. // Handles special case where custom was chosen
  1335. customSize = paperSizeSelect.value == 'custom';
  1336. update();
  1337. });
  1338. update();
  1339. return {set: function(value)
  1340. {
  1341. pageFormat = value;
  1342. listener(null, null, true);
  1343. },get: function()
  1344. {
  1345. return currentPageFormat;
  1346. }, widthInput: widthInput,
  1347. heightInput: heightInput};
  1348. };
  1349. /**
  1350. *
  1351. */
  1352. PageSetupDialog.getFormats = function()
  1353. {
  1354. return [{key: 'letter', title: 'US-Letter (8,5" x 11")', format: mxConstants.PAGE_FORMAT_LETTER_PORTRAIT},
  1355. {key: 'legal', title: 'US-Legal (8,5" x 14")', format: new mxRectangle(0, 0, 850, 1400)},
  1356. {key: 'tabloid', title: 'US-Tabloid (279 mm x 432 mm)', format: new mxRectangle(0, 0, 1100, 1700)},
  1357. {key: 'a0', title: 'A0 (841 mm x 1189 mm)', format: new mxRectangle(0, 0, 3300, 4681)},
  1358. {key: 'a1', title: 'A1 (594 mm x 841 mm)', format: new mxRectangle(0, 0, 2339, 3300)},
  1359. {key: 'a2', title: 'A2 (420 mm x 594 mm)', format: new mxRectangle(0, 0, 1654, 2336)},
  1360. {key: 'a3', title: 'A3 (297 mm x 420 mm)', format: new mxRectangle(0, 0, 1169, 1654)},
  1361. {key: 'a4', title: 'A4 (210 mm x 297 mm)', format: mxConstants.PAGE_FORMAT_A4_PORTRAIT},
  1362. {key: 'a5', title: 'A5 (148 mm x 210 mm)', format: new mxRectangle(0, 0, 583, 827)},
  1363. {key: 'a6', title: 'A6 (105 mm x 148 mm)', format: new mxRectangle(0, 0, 413, 583)},
  1364. {key: 'a7', title: 'A7 (74 mm x 105 mm)', format: new mxRectangle(0, 0, 291, 413)},
  1365. {key: 'custom', title: mxResources.get('custom'), format: null}];
  1366. };
  1367. /**
  1368. * Static overrides
  1369. */
  1370. (function()
  1371. {
  1372. // Uses HTML for background pages (to support grid background image)
  1373. mxGraphView.prototype.validateBackgroundPage = function()
  1374. {
  1375. var graph = this.graph;
  1376. if (graph.container != null && !graph.transparentBackground)
  1377. {
  1378. if (graph.pageVisible)
  1379. {
  1380. var bounds = this.getBackgroundPageBounds();
  1381. if (this.backgroundPageShape == null)
  1382. {
  1383. // Finds first element in graph container
  1384. var firstChild = graph.container.firstChild;
  1385. while (firstChild != null && firstChild.nodeType != mxConstants.NODETYPE_ELEMENT)
  1386. {
  1387. firstChild = firstChild.nextSibling;
  1388. }
  1389. if (firstChild != null)
  1390. {
  1391. this.backgroundPageShape = this.createBackgroundPageShape(bounds);
  1392. this.backgroundPageShape.scale = 1;
  1393. // Shadow filter causes problems in outline window in quirks mode. IE8 standards
  1394. // also has known rendering issues inside mxWindow but not using shadow is worse.
  1395. this.backgroundPageShape.isShadow = !mxClient.IS_QUIRKS;
  1396. this.backgroundPageShape.dialect = mxConstants.DIALECT_STRICTHTML;
  1397. this.backgroundPageShape.init(graph.container);
  1398. // Required for the browser to render the background page in correct order
  1399. firstChild.style.position = 'absolute';
  1400. graph.container.insertBefore(this.backgroundPageShape.node, firstChild);
  1401. this.backgroundPageShape.redraw();
  1402. this.backgroundPageShape.node.className = 'geBackgroundPage';
  1403. // Adds listener for double click handling on background
  1404. mxEvent.addListener(this.backgroundPageShape.node, 'dblclick',
  1405. mxUtils.bind(this, function(evt)
  1406. {
  1407. graph.dblClick(evt);
  1408. })
  1409. );
  1410. // Adds basic listeners for graph event dispatching outside of the
  1411. // container and finishing the handling of a single gesture
  1412. mxEvent.addGestureListeners(this.backgroundPageShape.node,
  1413. mxUtils.bind(this, function(evt)
  1414. {
  1415. graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt));
  1416. }),
  1417. mxUtils.bind(this, function(evt)
  1418. {
  1419. // Hides the tooltip if mouse is outside container
  1420. if (graph.tooltipHandler != null && graph.tooltipHandler.isHideOnHover())
  1421. {
  1422. graph.tooltipHandler.hide();
  1423. }
  1424. if (graph.isMouseDown && !mxEvent.isConsumed(evt))
  1425. {
  1426. graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt));
  1427. }
  1428. }),
  1429. mxUtils.bind(this, function(evt)
  1430. {
  1431. graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt));
  1432. })
  1433. );
  1434. }
  1435. }
  1436. else
  1437. {
  1438. this.backgroundPageShape.scale = 1;
  1439. this.backgroundPageShape.bounds = bounds;
  1440. this.backgroundPageShape.redraw();
  1441. }
  1442. }
  1443. else if (this.backgroundPageShape != null)
  1444. {
  1445. this.backgroundPageShape.destroy();
  1446. this.backgroundPageShape = null;
  1447. }
  1448. this.validateBackgroundStyles();
  1449. }
  1450. };
  1451. // Updates the CSS of the background to draw the grid
  1452. mxGraphView.prototype.validateBackgroundStyles = function()
  1453. {
  1454. var graph = this.graph;
  1455. var color = (graph.background == null || graph.background == mxConstants.NONE) ? '#ffffff' : graph.background;
  1456. var gridColor = (this.gridColor != color.toLowerCase()) ? this.gridColor : '#ffffff';
  1457. var image = 'none';
  1458. var position = '';
  1459. if (graph.isGridEnabled())
  1460. {
  1461. var phase = 10;
  1462. if (mxClient.IS_SVG)
  1463. {
  1464. // Generates the SVG required for drawing the dynamic grid
  1465. image = unescape(encodeURIComponent(this.createSvgGrid(gridColor)));
  1466. image = (window.btoa) ? btoa(image) : Base64.encode(image, true);
  1467. image = 'url(' + 'data:image/svg+xml;base64,' + image + ')'
  1468. phase = graph.gridSize * this.scale * this.gridSteps;
  1469. }
  1470. else
  1471. {
  1472. // Fallback to grid wallpaper with fixed size
  1473. image = 'url(' + this.gridImage + ')';
  1474. }
  1475. var x0 = 0;
  1476. var y0 = 0;
  1477. if (graph.view.backgroundPageShape != null)
  1478. {
  1479. var bds = this.getBackgroundPageBounds();
  1480. x0 = 1 + bds.x;
  1481. y0 = 1 + bds.y;
  1482. }
  1483. // Computes the offset to maintain origin for grid
  1484. position = -Math.round(phase - mxUtils.mod(this.translate.x * this.scale - x0, phase)) + 'px ' +
  1485. -Math.round(phase - mxUtils.mod(this.translate.y * this.scale - y0, phase)) + 'px';
  1486. }
  1487. var canvas = graph.view.canvas;
  1488. if (canvas.ownerSVGElement != null)
  1489. {
  1490. canvas = canvas.ownerSVGElement;
  1491. }
  1492. if (graph.view.backgroundPageShape != null)
  1493. {
  1494. graph.view.backgroundPageShape.node.style.backgroundPosition = position;
  1495. graph.view.backgroundPageShape.node.style.backgroundImage = image;
  1496. graph.view.backgroundPageShape.node.style.backgroundColor = color;
  1497. graph.container.className = 'geDiagramContainer geDiagramBackdrop';
  1498. canvas.style.backgroundImage = 'none';
  1499. canvas.style.backgroundColor = '';
  1500. }
  1501. else
  1502. {
  1503. graph.container.className = 'geDiagramContainer';
  1504. canvas.style.backgroundPosition = position;
  1505. canvas.style.backgroundColor = color;
  1506. canvas.style.backgroundImage = image;
  1507. }
  1508. };
  1509. // Returns the SVG required for painting the background grid.
  1510. mxGraphView.prototype.createSvgGrid = function(color)
  1511. {
  1512. var tmp = this.graph.gridSize * this.scale;
  1513. while (tmp < this.minGridSize)
  1514. {
  1515. tmp *= 2;
  1516. }
  1517. var tmp2 = this.gridSteps * tmp;
  1518. // Small grid lines
  1519. var d = [];
  1520. for (var i = 1; i < this.gridSteps; i++)
  1521. {
  1522. var tmp3 = i * tmp;
  1523. d.push('M 0 ' + tmp3 + ' L ' + tmp2 + ' ' + tmp3 + ' M ' + tmp3 + ' 0 L ' + tmp3 + ' ' + tmp2);
  1524. }
  1525. // KNOWN: Rounding errors for certain scales (eg. 144%, 121% in Chrome, FF and Safari). Workaround
  1526. // in Chrome is to use 100% for the svg size, but this results in blurred grid for large diagrams.
  1527. var size = tmp2;
  1528. var svg = '<svg width="' + size + '" height="' + size + '" xmlns="' + mxConstants.NS_SVG + '">' +
  1529. '<defs><pattern id="grid" width="' + tmp2 + '" height="' + tmp2 + '" patternUnits="userSpaceOnUse">' +
  1530. '<path d="' + d.join(' ') + '" fill="none" stroke="' + color + '" opacity="0.2" stroke-width="1"/>' +
  1531. '<path d="M ' + tmp2 + ' 0 L 0 0 0 ' + tmp2 + '" fill="none" stroke="' + color + '" stroke-width="1"/>' +
  1532. '</pattern></defs><rect width="100%" height="100%" fill="url(#grid)"/></svg>';
  1533. return svg;
  1534. };
  1535. // Adds panning for the grid with no page view and disabled scrollbars
  1536. var mxGraphPanGraph = mxGraph.prototype.panGraph;
  1537. mxGraph.prototype.panGraph = function(dx, dy)
  1538. {
  1539. mxGraphPanGraph.apply(this, arguments);
  1540. if (this.shiftPreview1 != null)
  1541. {
  1542. var canvas = this.view.canvas;
  1543. if (canvas.ownerSVGElement != null)
  1544. {
  1545. canvas = canvas.ownerSVGElement;
  1546. }
  1547. var phase = this.gridSize * this.view.scale * this.view.gridSteps;
  1548. var position = -Math.round(phase - mxUtils.mod(this.view.translate.x * this.view.scale + dx, phase)) + 'px ' +
  1549. -Math.round(phase - mxUtils.mod(this.view.translate.y * this.view.scale + dy, phase)) + 'px';
  1550. canvas.style.backgroundPosition = position;
  1551. }
  1552. };
  1553. // Draws page breaks only within the page
  1554. mxGraph.prototype.updatePageBreaks = function(visible, width, height)
  1555. {
  1556. var scale = this.view.scale;
  1557. var tr = this.view.translate;
  1558. var fmt = this.pageFormat;
  1559. var ps = scale * this.pageScale;
  1560. var bounds2 = this.view.getBackgroundPageBounds();
  1561. width = bounds2.width;
  1562. height = bounds2.height;
  1563. var bounds = new mxRectangle(scale * tr.x, scale * tr.y, fmt.width * ps, fmt.height * ps);
  1564. // Does not show page breaks if the scale is too small
  1565. visible = visible && Math.min(bounds.width, bounds.height) > this.minPageBreakDist;
  1566. var horizontalCount = (visible) ? Math.ceil(height / bounds.height) - 1 : 0;
  1567. var verticalCount = (visible) ? Math.ceil(width / bounds.width) - 1 : 0;
  1568. var right = bounds2.x + width;
  1569. var bottom = bounds2.y + height;
  1570. if (this.horizontalPageBreaks == null && horizontalCount > 0)
  1571. {
  1572. this.horizontalPageBreaks = [];
  1573. }
  1574. if (this.verticalPageBreaks == null && verticalCount > 0)
  1575. {
  1576. this.verticalPageBreaks = [];
  1577. }
  1578. var drawPageBreaks = mxUtils.bind(this, function(breaks)
  1579. {
  1580. if (breaks != null)
  1581. {
  1582. var count = (breaks == this.horizontalPageBreaks) ? horizontalCount : verticalCount;
  1583. for (var i = 0; i <= count; i++)
  1584. {
  1585. var pts = (breaks == this.horizontalPageBreaks) ?
  1586. [new mxPoint(Math.round(bounds2.x), Math.round(bounds2.y + (i + 1) * bounds.height)),
  1587. new mxPoint(Math.round(right), Math.round(bounds2.y + (i + 1) * bounds.height))] :
  1588. [new mxPoint(Math.round(bounds2.x + (i + 1) * bounds.width), Math.round(bounds2.y)),
  1589. new mxPoint(Math.round(bounds2.x + (i + 1) * bounds.width), Math.round(bottom))];
  1590. if (breaks[i] != null)
  1591. {
  1592. breaks[i].points = pts;
  1593. breaks[i].redraw();
  1594. }
  1595. else
  1596. {
  1597. var pageBreak = new mxPolyline(pts, this.pageBreakColor);
  1598. pageBreak.dialect = this.dialect;
  1599. pageBreak.isDashed = this.pageBreakDashed;
  1600. pageBreak.pointerEvents = false;
  1601. pageBreak.init(this.view.backgroundPane);
  1602. pageBreak.redraw();
  1603. breaks[i] = pageBreak;
  1604. }
  1605. }
  1606. for (var i = count; i < breaks.length; i++)
  1607. {
  1608. breaks[i].destroy();
  1609. }
  1610. breaks.splice(count, breaks.length - count);
  1611. }
  1612. });
  1613. drawPageBreaks(this.horizontalPageBreaks);
  1614. drawPageBreaks(this.verticalPageBreaks);
  1615. };
  1616. // Disables removing relative children from parents
  1617. var mxGraphHandlerShouldRemoveCellsFromParent = mxGraphHandler.prototype.shouldRemoveCellsFromParent;
  1618. mxGraphHandler.prototype.shouldRemoveCellsFromParent = function(parent, cells, evt)
  1619. {
  1620. for (var i = 0; i < cells.length; i++)
  1621. {
  1622. if (this.graph.getModel().isVertex(cells[i]))
  1623. {
  1624. var geo = this.graph.getCellGeometry(cells[i]);
  1625. if (geo != null && geo.relative)
  1626. {
  1627. return false;
  1628. }
  1629. }
  1630. }
  1631. return mxGraphHandlerShouldRemoveCellsFromParent.apply(this, arguments);
  1632. };
  1633. // Overrides to ignore hotspot only for target terminal
  1634. var mxConnectionHandlerCreateMarker = mxConnectionHandler.prototype.createMarker;
  1635. mxConnectionHandler.prototype.createMarker = function()
  1636. {
  1637. var marker = mxConnectionHandlerCreateMarker.apply(this, arguments);
  1638. marker.intersects = mxUtils.bind(this, function(state, evt)
  1639. {
  1640. if (this.isConnecting())
  1641. {
  1642. return true;
  1643. }
  1644. return mxCellMarker.prototype.intersects.apply(marker, arguments);
  1645. });
  1646. return marker;
  1647. };
  1648. // Creates background page shape
  1649. mxGraphView.prototype.createBackgroundPageShape = function(bounds)
  1650. {
  1651. return new mxRectangleShape(bounds, '#ffffff', '#cacaca');
  1652. };
  1653. // Fits the number of background pages to the graph
  1654. mxGraphView.prototype.getBackgroundPageBounds = function()
  1655. {
  1656. var gb = this.getGraphBounds();
  1657. // Computes unscaled, untranslated graph bounds
  1658. var x = (gb.width > 0) ? gb.x / this.scale - this.translate.x : 0;
  1659. var y = (gb.height > 0) ? gb.y / this.scale - this.translate.y : 0;
  1660. var w = gb.width / this.scale;
  1661. var h = gb.height / this.scale;
  1662. var fmt = this.graph.pageFormat;
  1663. var ps = this.graph.pageScale;
  1664. var pw = fmt.width * ps;
  1665. var ph = fmt.height * ps;
  1666. var x0 = Math.floor(Math.min(0, x) / pw);
  1667. var y0 = Math.floor(Math.min(0, y) / ph);
  1668. var xe = Math.ceil(Math.max(1, x + w) / pw);
  1669. var ye = Math.ceil(Math.max(1, y + h) / ph);
  1670. var rows = xe - x0;
  1671. var cols = ye - y0;
  1672. var bounds = new mxRectangle(this.scale * (this.translate.x + x0 * pw), this.scale *
  1673. (this.translate.y + y0 * ph), this.scale * rows * pw, this.scale * cols * ph);
  1674. return bounds;
  1675. };
  1676. // Add panning for background page in VML
  1677. var graphPanGraph = mxGraph.prototype.panGraph;
  1678. mxGraph.prototype.panGraph = function(dx, dy)
  1679. {
  1680. graphPanGraph.apply(this, arguments);
  1681. if ((this.dialect != mxConstants.DIALECT_SVG && this.view.backgroundPageShape != null) &&
  1682. (!this.useScrollbarsForPanning || !mxUtils.hasScrollbars(this.container)))
  1683. {
  1684. this.view.backgroundPageShape.node.style.marginLeft = dx + 'px';
  1685. this.view.backgroundPageShape.node.style.marginTop = dy + 'px';
  1686. }
  1687. };
  1688. /**
  1689. * Consumes click events for disabled menu items.
  1690. */
  1691. var mxPopupMenuAddItem = mxPopupMenu.prototype.addItem;
  1692. mxPopupMenu.prototype.addItem = function(title, image, funct, parent, iconCls, enabled)
  1693. {
  1694. var result = mxPopupMenuAddItem.apply(this, arguments);
  1695. if (enabled != null && !enabled)
  1696. {
  1697. mxEvent.addListener(result, 'mousedown', function(evt)
  1698. {
  1699. mxEvent.consume(evt);
  1700. });
  1701. }
  1702. return result;
  1703. };
  1704. // Selects ancestors before descendants
  1705. var graphHandlerGetInitialCellForEvent = mxGraphHandler.prototype.getInitialCellForEvent;
  1706. mxGraphHandler.prototype.getInitialCellForEvent = function(me)
  1707. {
  1708. var model = this.graph.getModel();
  1709. var psel = model.getParent(this.graph.getSelectionCell());
  1710. var cell = graphHandlerGetInitialCellForEvent.apply(this, arguments);
  1711. var parent = model.getParent(cell);
  1712. if (psel == null || (psel != cell && psel != parent))
  1713. {
  1714. while (!this.graph.isCellSelected(cell) && !this.graph.isCellSelected(parent) &&
  1715. model.isVertex(parent) && !this.graph.isContainer(parent))
  1716. {
  1717. cell = parent;
  1718. parent = this.graph.getModel().getParent(cell);
  1719. }
  1720. }
  1721. return cell;
  1722. };
  1723. // Selection is delayed to mouseup if ancestor is selected
  1724. var graphHandlerIsDelayedSelection = mxGraphHandler.prototype.isDelayedSelection;
  1725. mxGraphHandler.prototype.isDelayedSelection = function(cell, me)
  1726. {
  1727. var result = graphHandlerIsDelayedSelection.apply(this, arguments);
  1728. if (!result)
  1729. {
  1730. var model = this.graph.getModel();
  1731. var parent = model.getParent(cell);
  1732. while (parent != null)
  1733. {
  1734. // Inconsistency for unselected parent swimlane is intended for easier moving
  1735. // of stack layouts where the container title section is too far away
  1736. if (this.graph.isCellSelected(parent) && model.isVertex(parent))
  1737. {
  1738. result = true;
  1739. break;
  1740. }
  1741. parent = model.getParent(parent);
  1742. }
  1743. }
  1744. return result;
  1745. };
  1746. // Delayed selection of parent group
  1747. mxGraphHandler.prototype.selectDelayed = function(me)
  1748. {
  1749. if (!this.graph.popupMenuHandler.isPopupTrigger(me))
  1750. {
  1751. var cell = me.getCell();
  1752. if (cell == null)
  1753. {
  1754. cell = this.cell;
  1755. }
  1756. // Selects folded cell for hit on folding icon
  1757. var state = this.graph.view.getState(cell)
  1758. if (state != null && me.isSource(state.control))
  1759. {
  1760. this.graph.selectCellForEvent(cell, me.getEvent());
  1761. }
  1762. else
  1763. {
  1764. var model = this.graph.getModel();
  1765. var parent = model.getParent(cell);
  1766. while (!this.graph.isCellSelected(parent) && model.isVertex(parent))
  1767. {
  1768. cell = parent;
  1769. parent = model.getParent(cell);
  1770. }
  1771. this.graph.selectCellForEvent(cell, me.getEvent());
  1772. }
  1773. }
  1774. };
  1775. // Returns last selected ancestor
  1776. mxPopupMenuHandler.prototype.getCellForPopupEvent = function(me)
  1777. {
  1778. var cell = me.getCell();
  1779. var model = this.graph.getModel();
  1780. var parent = model.getParent(cell);
  1781. while (model.isVertex(parent) && !this.graph.isContainer(parent))
  1782. {
  1783. if (this.graph.isCellSelected(parent))
  1784. {
  1785. cell = parent;
  1786. }
  1787. parent = model.getParent(parent);
  1788. }
  1789. return cell;
  1790. };
  1791. })();