Editor.js 76 KB

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