Editor.js 108 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202
  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, fn)
  26. {
  27. this.status = value;
  28. this.statusFunction = fn;
  29. this.fireEvent(new mxEventObject('statusChanged'));
  30. };
  31. // Returns the current status
  32. this.getStatus = function()
  33. {
  34. return this.status;
  35. };
  36. // Updates modified state if graph changes
  37. this.graphChangeListener = function(sender, eventObject)
  38. {
  39. var edit = (eventObject != null) ? eventObject.getProperty('edit') : null;
  40. if (edit == null || !edit.ignoreEdit)
  41. {
  42. this.setModified(true);
  43. }
  44. };
  45. this.graph.getModel().addListener(mxEvent.CHANGE, mxUtils.bind(this, function()
  46. {
  47. this.graphChangeListener.apply(this, arguments);
  48. }));
  49. // Sets persistent graph state defaults
  50. this.graph.resetViewOnRootChange = false;
  51. this.init();
  52. };
  53. /**
  54. * Measurements Units
  55. */
  56. mxConstants.POINTS = 1;
  57. mxConstants.MILLIMETERS = 2;
  58. mxConstants.INCHES = 3;
  59. mxConstants.METERS = 4;
  60. /**
  61. * This ratio is with page scale 1
  62. */
  63. mxConstants.PIXELS_PER_MM = 3.937;
  64. mxConstants.PIXELS_PER_INCH = 100;
  65. /**
  66. * Counts open editor tabs (must be global for cross-window access)
  67. */
  68. Editor.pageCounter = 0;
  69. // Cross-domain window access is not allowed in FF, so if we
  70. // were opened from another domain then this will fail.
  71. (function()
  72. {
  73. try
  74. {
  75. var op = window;
  76. while (op.opener != null && typeof op.opener.Editor !== 'undefined' &&
  77. !isNaN(op.opener.Editor.pageCounter) &&
  78. // Workaround for possible infinite loop in FF https://drawio.atlassian.net/browse/DS-795
  79. op.opener != op)
  80. {
  81. op = op.opener;
  82. }
  83. // Increments the counter in the first opener in the chain
  84. if (op != null)
  85. {
  86. op.Editor.pageCounter++;
  87. Editor.pageCounter = op.Editor.pageCounter;
  88. }
  89. }
  90. catch (e)
  91. {
  92. // ignore
  93. }
  94. })();
  95. /**
  96. *
  97. */
  98. Editor.defaultHtmlFont = '-apple-system, BlinkMacSystemFont, "Segoe UI Variable", "Segoe UI", system-ui, ui-sans-serif, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"';
  99. /**
  100. * Specifies if local storage should be used (eg. on the iPad which has no filesystem)
  101. */
  102. Editor.useLocalStorage = typeof(Storage) != 'undefined' && mxClient.IS_IOS;
  103. /**
  104. * Window width for simple mode to collapse panels.
  105. */
  106. Editor.smallScreenWidth = 1024;
  107. /**
  108. *
  109. */
  110. Editor.lightCheckmarkImage = '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=';
  111. Editor.darkCheckmarkImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAMAAACeyVWkAAAARVBMVEUAAACZmZkICAgEBASNjY2Dg4MYGBiTk5N5eXl1dXVmZmZQUFBCQkI3NzceHh4MDAykpKSJiYl+fn5sbGxaWlo/Pz8SEhK96uPlAAAAAXRSTlMAQObYZgAAAE5JREFUGNPFzTcSgDAQQ1HJGUfy/Y9K7V1qeOUfzQifCQZai1XHaz11LFysbDbzgDSSWMZiETz3+b8yNUc/MMsktxuC8XQBSncdLwz+8gCCggGXzBcozAAAAABJRU5ErkJggg==';
  112. Editor.helpImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBmaWxsPSJub25lIiBkPSJNMCAwaDI0djI0SDB6Ii8+PHBhdGggZD0iTTExIDE4aDJ2LTJoLTJ2MnptMS0xNkM2LjQ4IDIgMiA2LjQ4IDIgMTJzNC40OCAxMCAxMCAxMCAxMC00LjQ4IDEwLTEwUzE3LjUyIDIgMTIgMnptMCAxOGMtNC40MSAwLTgtMy41OS04LThzMy41OS04IDgtOCA4IDMuNTkgOCA4LTMuNTkgOC04IDh6bTAtMTRjLTIuMjEgMC00IDEuNzktNCA0aDJjMC0xLjEuOS0yIDItMnMyIC45IDIgMmMwIDItMyAxLjc1LTMgNWgyYzAtMi4yNSAzLTIuNSAzLTUgMC0yLjIxLTEuNzktNC00LTR6Ii8+PC9zdmc+';
  113. Editor.menuImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMjRweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTMgMThoMTh2LTJIM3Yyem0wLTVoMTh2LTJIM3Yyem0wLTd2MmgxOFY2SDN6Ii8+PC9zdmc+';
  114. Editor.moveImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjI4cHgiIGhlaWdodD0iMjhweCI+PGc+PC9nPjxnPjxnPjxnPjxwYXRoIHRyYW5zZm9ybT0idHJhbnNsYXRlKDIuNCwyLjQpc2NhbGUoMC44KXJvdGF0ZSg0NSwxMiwxMikiIHN0cm9rZT0iIzI5YjZmMiIgZmlsbD0iIzI5YjZmMiIgZD0iTTE1LDNsMi4zLDIuM2wtMi44OSwyLjg3bDEuNDIsMS40MkwxOC43LDYuN0wyMSw5VjNIMTV6IE0zLDlsMi4zLTIuM2wyLjg3LDIuODlsMS40Mi0xLjQyTDYuNyw1LjNMOSwzSDNWOXogTTksMjEgbC0yLjMtMi4zbDIuODktMi44N2wtMS40Mi0xLjQyTDUuMywxNy4zTDMsMTV2Nkg5eiBNMjEsMTVsLTIuMywyLjNsLTIuODctMi44OWwtMS40MiwxLjQybDIuODksMi44N0wxNSwyMWg2VjE1eiIvPjwvZz48L2c+PC9nPjwvc3ZnPgo=';
  115. Editor.zoomInImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTUuNSAxNGgtLjc5bC0uMjgtLjI3QzE1LjQxIDEyLjU5IDE2IDExLjExIDE2IDkuNSAxNiA1LjkxIDEzLjA5IDMgOS41IDNTMyA1LjkxIDMgOS41IDUuOTEgMTYgOS41IDE2YzEuNjEgMCAzLjA5LS41OSA0LjIzLTEuNTdsLjI3LjI4di43OWw1IDQuOTlMMjAuNDkgMTlsLTQuOTktNXptLTYgMEM3LjAxIDE0IDUgMTEuOTkgNSA5LjVTNy4wMSA1IDkuNSA1IDE0IDcuMDEgMTQgOS41IDExLjk5IDE0IDkuNSAxNHptMi41LTRoLTJ2Mkg5di0ySDdWOWgyVjdoMXYyaDJ2MXoiLz48L3N2Zz4=';
  116. Editor.zoomOutImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTUuNSAxNGgtLjc5bC0uMjgtLjI3QzE1LjQxIDEyLjU5IDE2IDExLjExIDE2IDkuNSAxNiA1LjkxIDEzLjA5IDMgOS41IDNTMyA1LjkxIDMgOS41IDUuOTEgMTYgOS41IDE2YzEuNjEgMCAzLjA5LS41OSA0LjIzLTEuNTdsLjI3LjI4di43OWw1IDQuOTlMMjAuNDkgMTlsLTQuOTktNXptLTYgMEM3LjAxIDE0IDUgMTEuOTkgNSA5LjVTNy4wMSA1IDkuNSA1IDE0IDcuMDEgMTQgOS41IDExLjk5IDE0IDkuNSAxNHpNNyA5aDV2MUg3eiIvPjwvc3ZnPg==';
  117. Editor.fullscreenImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMyA1djRoMlY1aDRWM0g1Yy0xLjEgMC0yIC45LTIgMnptMiAxMEgzdjRjMCAxLjEuOSAyIDIgMmg0di0ySDV2LTR6bTE0IDRoLTR2Mmg0YzEuMSAwIDItLjkgMi0ydi00aC0ydjR6bTAtMTZoLTR2Mmg0djRoMlY1YzAtMS4xLS45LTItMi0yeiIvPjwvc3ZnPg==';
  118. Editor.fullscreenExitImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMjRweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTUgMTZoM3YzaDJ2LTVINXYyem0zLThINXYyaDVWNUg4djN6bTYgMTFoMnYtM2gzdi0yaC01djV6bTItMTFWNWgtMnY1aDVWOGgtM3oiLz48L3N2Zz4=';
  119. Editor.zoomFitImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTUgMTVIM3Y0YzAgMS4xLjkgMiAyIDJoNHYtMkg1di00ek01IDVoNFYzSDVjLTEuMSAwLTIgLjktMiAydjRoMlY1em03IDNjLTIuMjEgMC00IDEuNzktNCA0czEuNzkgNCA0IDQgNC0xLjc5IDQtNC0xLjc5LTQtNC00em0wIDZjLTEuMSAwLTItLjktMi0ycy45LTIgMi0yIDIgLjkgMiAyLS45IDItMiAyem03LTExaC00djJoNHY0aDJWNWMwLTEuMS0uOS0yLTItMnptMCAxNmgtNHYyaDRjMS4xIDAgMi0uOSAyLTJ2LTRoLTJ2NHoiLz48L3N2Zz4=';
  120. Editor.layersImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTExLjk5IDE4LjU0bC03LjM3LTUuNzNMMyAxNC4wN2w5IDcgOS03LTEuNjMtMS4yN3pNMTIgMTZsNy4zNi01LjczTDIxIDlsLTktNy05IDcgMS42MyAxLjI3TDEyIDE2em0wLTExLjQ3TDE3Ljc0IDkgMTIgMTMuNDcgNi4yNiA5IDEyIDQuNTN6Ii8+PC9zdmc+';
  121. Editor.previousImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTE1LjQxIDcuNDFMMTQgNmwtNiA2IDYgNiAxLjQxLTEuNDFMMTAuODMgMTJsNC41OC00LjU5eiIvPjwvc3ZnPg==';
  122. Editor.nextImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTEwIDZMOC41OSA3LjQxIDEzLjE3IDEybC00LjU4IDQuNTlMMTAgMThsNi02LTYtNnoiLz48L3N2Zz4=';
  123. Editor.editImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTE0LjA2IDkuMDJsLjkyLjkyTDUuOTIgMTlINXYtLjkybDkuMDYtOS4wNk0xNy42NiAzYy0uMjUgMC0uNTEuMS0uNy4yOWwtMS44MyAxLjgzIDMuNzUgMy43NSAxLjgzLTEuODNjLjM5LS4zOS4zOS0xLjAyIDAtMS40MWwtMi4zNC0yLjM0Yy0uMi0uMi0uNDUtLjI5LS43MS0uMjl6bS0zLjYgMy4xOUwzIDE3LjI1VjIxaDMuNzVMMTcuODEgOS45NGwtMy43NS0zLjc1eiIvPjwvc3ZnPg==';
  124. Editor.duplicateImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTE2IDFINGMtMS4xIDAtMiAuOS0yIDJ2MTRoMlYzaDEyVjF6bTMgNEg4Yy0xLjEgMC0yIC45LTIgMnYxNGMwIDEuMS45IDIgMiAyaDExYzEuMSAwIDItLjkgMi0yVjdjMC0xLjEtLjktMi0yLTJ6bTAgMTZIOFY3aDExdjE0eiIvPjwvc3ZnPg==';
  125. Editor.addImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTE5IDEzaC02djZoLTJ2LTZINXYtMmg2VjVoMnY2aDZ2MnoiLz48L3N2Zz4=';
  126. Editor.crossImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTE5IDYuNDFMMTcuNTkgNSAxMiAxMC41OSA2LjQxIDUgNSA2LjQxIDEwLjU5IDEyIDUgMTcuNTkgNi40MSAxOSAxMiAxMy40MSAxNy41OSAxOSAxOSAxNy41OSAxMy40MSAxMiAxOSA2LjQxeiIvPjwvc3ZnPg==';
  127. Editor.verticalDotsImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTEyIDhjMS4xIDAgMi0uOSAyLTJzLS45LTItMi0yLTIgLjktMiAyIC45IDIgMiAyem0wIDJjLTEuMSAwLTIgLjktMiAycy45IDIgMiAyIDItLjkgMi0yLS45LTItMi0yem0wIDZjLTEuMSAwLTIgLjktMiAycy45IDIgMiAyIDItLjkgMi0yLS45LTItMi0yeiIvPjwvc3ZnPg==';
  128. Editor.trashImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTE2IDl2MTBIOFY5aDhtLTEuNS02aC01bC0xIDFINXYyaDE0VjRoLTMuNWwtMS0xek0xOCA3SDZ2MTJjMCAxLjEuOSAyIDIgMmg4YzEuMSAwIDItLjkgMi0yVjd6Ii8+PC9zdmc+';
  129. Editor.hiddenImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6bTAgMGgyNHYyNEgwVjB6bTAgMGgyNHYyNEgwVjB6bTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTEyIDZjMy43OSAwIDcuMTcgMi4xMyA4LjgyIDUuNS0uNTkgMS4yMi0xLjQyIDIuMjctMi40MSAzLjEybDEuNDEgMS40MWMxLjM5LTEuMjMgMi40OS0yLjc3IDMuMTgtNC41M0MyMS4yNyA3LjExIDE3IDQgMTIgNGMtMS4yNyAwLTIuNDkuMi0zLjY0LjU3bDEuNjUgMS42NUMxMC42NiA2LjA5IDExLjMyIDYgMTIgNnptLTEuMDcgMS4xNEwxMyA5LjIxYy41Ny4yNSAxLjAzLjcxIDEuMjggMS4yOGwyLjA3IDIuMDdjLjA4LS4zNC4xNC0uNy4xNC0xLjA3QzE2LjUgOS4wMSAxNC40OCA3IDEyIDdjLS4zNyAwLS43Mi4wNS0xLjA3LjE0ek0yLjAxIDMuODdsMi42OCAyLjY4QzMuMDYgNy44MyAxLjc3IDkuNTMgMSAxMS41IDIuNzMgMTUuODkgNyAxOSAxMiAxOWMxLjUyIDAgMi45OC0uMjkgNC4zMi0uODJsMy40MiAzLjQyIDEuNDEtMS40MUwzLjQyIDIuNDUgMi4wMSAzLjg3em03LjUgNy41bDIuNjEgMi42MWMtLjA0LjAxLS4wOC4wMi0uMTIuMDItMS4zOCAwLTIuNS0xLjEyLTIuNS0yLjUgMC0uMDUuMDEtLjA4LjAxLS4xM3ptLTMuNC0zLjRsMS43NSAxLjc1Yy0uMjMuNTUtLjM2IDEuMTUtLjM2IDEuNzggMCAyLjQ4IDIuMDIgNC41IDQuNSA0LjUuNjMgMCAxLjIzLS4xMyAxLjc3LS4zNmwuOTguOThjLS44OC4yNC0xLjguMzgtMi43NS4zOC0zLjc5IDAtNy4xNy0yLjEzLTguODItNS41LjctMS40MyAxLjcyLTIuNjEgMi45My0zLjUzeiIvPjwvc3ZnPg==';
  130. Editor.visibleImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTEyIDZjMy43OSAwIDcuMTcgMi4xMyA4LjgyIDUuNUMxOS4xNyAxNC44NyAxNS43OSAxNyAxMiAxN3MtNy4xNy0yLjEzLTguODItNS41QzQuODMgOC4xMyA4LjIxIDYgMTIgNm0wLTJDNyA0IDIuNzMgNy4xMSAxIDExLjUgMi43MyAxNS44OSA3IDE5IDEyIDE5czkuMjctMy4xMSAxMS03LjVDMjEuMjcgNy4xMSAxNyA0IDEyIDR6bTAgNWMxLjM4IDAgMi41IDEuMTIgMi41IDIuNVMxMy4zOCAxNCAxMiAxNHMtMi41LTEuMTItMi41LTIuNVMxMC42MiA5IDEyIDltMC0yYy0yLjQ4IDAtNC41IDIuMDItNC41IDQuNVM5LjUyIDE2IDEyIDE2czQuNS0yLjAyIDQuNS00LjVTMTQuNDggNyAxMiA3eiIvPjwvc3ZnPg==';
  131. Editor.lockedImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PGcgZmlsbD0ibm9uZSI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6Ii8+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBvcGFjaXR5PSIuODciLz48L2c+PHBhdGggZD0iTTE4IDhoLTFWNmMwLTIuNzYtMi4yNC01LTUtNVM3IDMuMjQgNyA2djJINmMtMS4xIDAtMiAuOS0yIDJ2MTBjMCAxLjEuOSAyIDIgMmgxMmMxLjEgMCAyLS45IDItMlYxMGMwLTEuMS0uOS0yLTItMnpNOSA2YzAtMS42NiAxLjM0LTMgMy0zczMgMS4zNCAzIDN2Mkg5VjZ6bTkgMTRINlYxMGgxMnYxMHptLTYtM2MxLjEgMCAyLS45IDItMnMtLjktMi0yLTItMiAuOS0yIDIgLjkgMiAyIDJ6Ii8+PC9zdmc+';
  132. Editor.unlockedImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMjRweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTE4IDhoLTFWNmMwLTIuNzYtMi4yNC01LTUtNVM3IDMuMjQgNyA2aDJjMC0xLjY2IDEuMzQtMyAzLTNzMyAxLjM0IDMgM3YySDZjLTEuMSAwLTIgLjktMiAydjEwYzAgMS4xLjkgMiAyIDJoMTJjMS4xIDAgMi0uOSAyLTJWMTBjMC0xLjEtLjktMi0yLTJ6bTAgMTJINlYxMGgxMnYxMHptLTYtM2MxLjEgMCAyLS45IDItMnMtLjktMi0yLTItMiAuOS0yIDIgLjkgMiAyIDJ6Ii8+PC9zdmc+';
  133. Editor.printImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTE5IDhoLTFWM0g2djVINWMtMS42NiAwLTMgMS4zNC0zIDN2Nmg0djRoMTJ2LTRoNHYtNmMwLTEuNjYtMS4zNC0zLTMtM3pNOCA1aDh2M0g4VjV6bTggMTJ2Mkg4di00aDh2MnptMi0ydi0ySDZ2Mkg0di00YzAtLjU1LjQ1LTEgMS0xaDE0Yy41NSAwIDEgLjQ1IDEgMXY0aC0yeiIvPjxjaXJjbGUgY3g9IjE4IiBjeT0iMTEuNSIgcj0iMSIvPjwvc3ZnPg==';
  134. Editor.refreshImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTE3LjY1IDYuMzVDMTYuMiA0LjkgMTQuMjEgNCAxMiA0Yy00LjQyIDAtNy45OSAzLjU4LTcuOTkgOHMzLjU3IDggNy45OSA4YzMuNzMgMCA2Ljg0LTIuNTUgNy43My02aC0yLjA4Yy0uODIgMi4zMy0zLjA0IDQtNS42NSA0LTMuMzEgMC02LTIuNjktNi02czIuNjktNiA2LTZjMS42NiAwIDMuMTQuNjkgNC4yMiAxLjc4TDEzIDExaDdWNGwtMi4zNSAyLjM1eiIvPjwvc3ZnPg==';
  135. Editor.backImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIiBvcGFjaXR5PSIuODciLz48cGF0aCBkPSJNMTcuNTEgMy44N0wxNS43MyAyLjEgNS44NCAxMmw5LjkgOS45IDEuNzctMS43N0w5LjM4IDEybDguMTMtOC4xM3oiLz48L3N2Zz4=';
  136. Editor.closeImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIiBvcGFjaXR5PSIuODciLz48cGF0aCBkPSJNMTIgMkM2LjQ3IDIgMiA2LjQ3IDIgMTJzNC40NyAxMCAxMCAxMCAxMC00LjQ3IDEwLTEwUzE3LjUzIDIgMTIgMnptMCAxOGMtNC40MSAwLTgtMy41OS04LThzMy41OS04IDgtOCA4IDMuNTkgOCA4LTMuNTkgOC04IDh6bTMuNTktMTNMMTIgMTAuNTkgOC40MSA3IDcgOC40MSAxMC41OSAxMiA3IDE1LjU5IDguNDEgMTcgMTIgMTMuNDEgMTUuNTkgMTcgMTcgMTUuNTkgMTMuNDEgMTIgMTcgOC40MXoiLz48L3N2Zz4='
  137. Editor.closeBlackImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjZweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMjZweCI+PGVsbGlwc2UgY3g9IjEyIiBjeT0iMTIiIHJ4PSI5IiByeT0iOSIgc3Ryb2tlPSJub25lIiBmaWxsPSIjMDAwIi8+PHBhdGggZmlsbD0iI2ZmZiIgZD0iTTE0LjU5IDhMMTIgMTAuNTkgOS40MSA4IDggOS40MSAxMC41OSAxMiA4IDE0LjU5IDkuNDEgMTYgMTIgMTMuNDEgMTQuNTkgMTYgMTYgMTQuNTkgMTMuNDEgMTIgMTYgOS40MSAxNC41OSA4ek0xMiAyQzYuNDcgMiAyIDYuNDcgMiAxMnM0LjQ3IDEwIDEwIDEwIDEwLTQuNDcgMTAtMTBTMTcuNTMgMiAxMiAyem0wIDE4Yy00LjQxIDAtOC0zLjU5LTgtOHMzLjU5LTggOC04IDggMy41OSA4IDgtMy41OSA4LTggOHoiLz48L3N2Zz4=';
  138. Editor.minusImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iNDgiIHdpZHRoPSI0OCI+PHBhdGggZD0iTTEwIDI1LjV2LTNoMjh2M1oiLz48L3N2Zz4=';
  139. Editor.plusImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTkgMTNoLTZ2NmgtMnYtNkg1di0yaDZWNWgydjZoNnYyeiIvPjwvc3ZnPg==';
  140. Editor.addBoxImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHdpZHRoPSIyNCI+PHBhdGggZD0iTTExIDE3aDJ2LTRoNHYtMmgtNFY3aC0ydjRIN3YyaDRabS02IDRxLS44MjUgMC0xLjQxMy0uNTg3UTMgMTkuODI1IDMgMTlWNXEwLS44MjUuNTg3LTEuNDEzUTQuMTc1IDMgNSAzaDE0cS44MjUgMCAxLjQxMy41ODdRMjEgNC4xNzUgMjEgNXYxNHEwIC44MjUtLjU4NyAxLjQxM1ExOS44MjUgMjEgMTkgMjFabTAtMmgxNFY1SDV2MTRaTTUgNXYxNFY1WiIvPjwvc3ZnPg==';
  141. Editor.shapesImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHdpZHRoPSIyNCI+PHBhdGggZD0iTTE1IDE1Wm0tNyAyLjk1cS4yNS4wMjUuNDg4LjAzOFE4LjcyNSAxOCA5IDE4dC41MTItLjAxMnEuMjM4LS4wMTMuNDg4LS4wMzhWMjBoMTBWMTBoLTIuMDVxLjAyNS0uMjUuMDM4LS40ODhRMTggOS4yNzUgMTggOXQtLjAxMi0uNTEyUTE3Ljk3NSA4LjI1IDE3Ljk1IDhIMjBxLjgyNSAwIDEuNDEzLjU4N1EyMiA5LjE3NSAyMiAxMHYxMHEwIC44MjUtLjU4NyAxLjQxM1EyMC44MjUgMjIgMjAgMjJIMTBxLS44MjUgMC0xLjQxMi0uNTg3UTggMjAuODI1IDggMjBaTTkgMTZxLTIuOTI1IDAtNC45NjMtMi4wMzhRMiAxMS45MjUgMiA5dDIuMDM3LTQuOTYzUTYuMDc1IDIgOSAycTIuOTI1IDAgNC45NjMgMi4wMzdRMTYgNi4wNzUgMTYgOXEwIDIuOTI1LTIuMDM3IDQuOTYyUTExLjkyNSAxNiA5IDE2Wm0wLTJxMi4wNzUgMCAzLjUzOC0xLjQ2M1ExNCAxMS4wNzUgMTQgOXQtMS40NjItMy41MzdRMTEuMDc1IDQgOSA0IDYuOTI1IDQgNS40NjMgNS40NjMgNCA2LjkyNSA0IDl0MS40NjMgMy41MzdRNi45MjUgMTQgOSAxNFptMC01WiIvPjwvc3ZnPg==';
  142. Editor.formatImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHdpZHRoPSIyNCI+PHBhdGggZD0iTTExIDIycS0uODI1IDAtMS40MTItLjU4N1E5IDIwLjgyNSA5IDIwdi00SDZxLS44MjUgMC0xLjQxMi0uNTg4UTQgMTQuODI1IDQgMTRWN3EwLTEuNjUgMS4xNzUtMi44MjVRNi4zNSAzIDggM2gxMnYxMXEwIC44MjUtLjU4NyAxLjQxMlExOC44MjUgMTYgMTggMTZoLTN2NHEwIC44MjUtLjU4NyAxLjQxM1ExMy44MjUgMjIgMTMgMjJaTTYgMTBoMTJWNWgtMXY0aC0yVjVoLTF2MmgtMlY1SDhxLS44MjUgMC0xLjQxMi41ODhRNiA2LjE3NSA2IDdabTAgNGgxMnYtMkg2djJabTAtMnYyWiIvPjwvc3ZnPg==';
  143. Editor.freehandImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjRweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMjRweCIgZmlsbD0iIzAwMDAwMCI+PHJlY3QgZmlsbD0ibm9uZSIgaGVpZ2h0PSIyNCIgd2lkdGg9IjI0Ii8+PHBhdGggZD0iTTQuNSw4YzEuMDQsMCwyLjM0LTEuNSw0LjI1LTEuNWMxLjUyLDAsMi43NSwxLjIzLDIuNzUsMi43NWMwLDIuMDQtMS45OSwzLjE1LTMuOTEsNC4yMkM1LjQyLDE0LjY3LDQsMTUuNTcsNCwxNyBjMCwxLjEsMC45LDIsMiwydjJjLTIuMjEsMC00LTEuNzktNC00YzAtMi43MSwyLjU2LTQuMTQsNC42Mi01LjI4YzEuNDItMC43OSwyLjg4LTEuNiwyLjg4LTIuNDdjMC0wLjQxLTAuMzQtMC43NS0wLjc1LTAuNzUgQzcuNSw4LjUsNi4yNSwxMCw0LjUsMTBDMy4xMiwxMCwyLDguODgsMiw3LjVDMiw1LjQ1LDQuMTcsMi44Myw1LDJsMS40MSwxLjQxQzUuNDEsNC40Miw0LDYuNDMsNCw3LjVDNCw3Ljc4LDQuMjIsOCw0LjUsOHogTTgsMjEgbDMuNzUsMGw4LjA2LTguMDZsLTMuNzUtMy43NUw4LDE3LjI1TDgsMjF6IE0xMCwxOC4wOGw2LjA2LTYuMDZsMC45MiwwLjkyTDEwLjkyLDE5TDEwLDE5TDEwLDE4LjA4eiBNMjAuMzcsNi4yOSBjLTAuMzktMC4zOS0xLjAyLTAuMzktMS40MSwwbC0xLjgzLDEuODNsMy43NSwzLjc1bDEuODMtMS44M2MwLjM5LTAuMzksMC4zOS0xLjAyLDAtMS40MUwyMC4zNyw2LjI5eiIvPjwvc3ZnPg==';
  144. Editor.undoImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTIuNSA4Yy0yLjY1IDAtNS4wNS45OS02LjkgMi42TDIgN3Y5aDlsLTMuNjItMy42MmMxLjM5LTEuMTYgMy4xNi0xLjg4IDUuMTItMS44OCAzLjU0IDAgNi41NSAyLjMxIDcuNiA1LjVsMi4zNy0uNzhDMjEuMDggMTEuMDMgMTcuMTUgOCAxMi41IDh6Ii8+PC9zdmc+';
  145. Editor.redoImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTguNCAxMC42QzE2LjU1IDguOTkgMTQuMTUgOCAxMS41IDhjLTQuNjUgMC04LjU4IDMuMDMtOS45NiA3LjIyTDMuOSAxNmMxLjA1LTMuMTkgNC4wNS01LjUgNy42LTUuNSAxLjk1IDAgMy43My43MiA1LjEyIDEuODhMMTMgMTZoOVY3bC0zLjYgMy42eiIvPjwvc3ZnPg==';
  146. Editor.outlineImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHdpZHRoPSIyNCI+PHBhdGggZD0ibTE1IDIxLTYtMi4xLTQuNjUgMS44cS0uNS4yLS45MjUtLjExM1EzIDIwLjI3NSAzIDE5Ljc1di0xNHEwLS4zMjUuMTg4LS41NzUuMTg3LS4yNS41MTItLjM3NUw5IDNsNiAyLjEgNC42NS0xLjhxLjUtLjIuOTI1LjExMi40MjUuMzEzLjQyNS44Mzh2MTRxMCAuMzI1LS4xODguNTc1LS4xODcuMjUtLjUxMi4zNzVabS0xLTIuNDVWNi44NWwtNC0xLjR2MTEuN1ptMiAwIDMtMVY1LjdsLTMgMS4xNVpNNSAxOC4zbDMtMS4xNVY1LjQ1bC0zIDFaTTE2IDYuODV2MTEuN1ptLTgtMS40djExLjdaIi8+PC9zdmc+';
  147. Editor.saveImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTE5IDEydjdINXYtN0gzdjdjMCAxLjEuOSAyIDIgMmgxNGMxLjEgMCAyLS45IDItMnYtN2gtMnptLTYgLjY3bDIuNTktMi41OEwxNyAxMS41bC01IDUtNS01IDEuNDEtMS40MUwxMSAxMi42N1YzaDJ2OS42N3oiLz48L3N2Zz4=';
  148. Editor.compareImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iNDgiIHdpZHRoPSI0OCI+PHBhdGggZD0ibTE1Ljg1IDQwLTIuMS0yLjEgNi4wNS02LjA1SDR2LTNoMTUuOGwtNi4wNS02LjA1IDIuMS0yLjEgOS42NSA5LjY1Wm0xNi4zLTEyLjctOS42NS05LjY1TDMyLjE1IDhsMi4xIDIuMS02LjA1IDYuMDVINDR2M0gyOC4ybDYuMDUgNi4wNVoiLz48L3N2Zz4=';
  149. Editor.expandMoreImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHdpZHRoPSIyNCI+PHBhdGggZD0ibTEyIDE1LjM3NS02LTYgMS40LTEuNCA0LjYgNC42IDQuNi00LjYgMS40IDEuNFoiLz48L3N2Zz4=';
  150. Editor.expandLessImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHdpZHRoPSIyNCI+PHBhdGggZD0ibTcuNCAxNS4zNzUtMS40LTEuNCA2LTYgNiA2LTEuNCAxLjQtNC42LTQuNloiLz48L3N2Zz4=';
  151. Editor.gearImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHdpZHRoPSIyNCI+PHBhdGggZD0ibTkuMjUgMjItLjQtMy4ycS0uMzI1LS4xMjUtLjYxMi0uMy0uMjg4LS4xNzUtLjU2My0uMzc1TDQuNyAxOS4zNzVsLTIuNzUtNC43NSAyLjU3NS0xLjk1UTQuNSAxMi41IDQuNSAxMi4zMzd2LS42NzVxMC0uMTYyLjAyNS0uMzM3TDEuOTUgOS4zNzVsMi43NS00Ljc1IDIuOTc1IDEuMjVxLjI3NS0uMi41NzUtLjM3NS4zLS4xNzUuNi0uM2wuNC0zLjJoNS41bC40IDMuMnEuMzI1LjEyNS42MTMuMy4yODcuMTc1LjU2Mi4zNzVsMi45NzUtMS4yNSAyLjc1IDQuNzUtMi41NzUgMS45NXEuMDI1LjE3NS4wMjUuMzM3di42NzVxMCAuMTYzLS4wNS4zMzhsMi41NzUgMS45NS0yLjc1IDQuNzUtMi45NS0xLjI1cS0uMjc1LjItLjU3NS4zNzUtLjMuMTc1LS42LjNsLS40IDMuMlptMi44LTYuNXExLjQ1IDAgMi40NzUtMS4wMjVRMTUuNTUgMTMuNDUgMTUuNTUgMTJxMC0xLjQ1LTEuMDI1LTIuNDc1UTEzLjUgOC41IDEyLjA1IDguNXEtMS40NzUgMC0yLjQ4OCAxLjAyNVE4LjU1IDEwLjU1IDguNTUgMTJxMCAxLjQ1IDEuMDEyIDIuNDc1UTEwLjU3NSAxNS41IDEyLjA1IDE1LjVabTAtMnEtLjYyNSAwLTEuMDYyLS40MzgtLjQzOC0uNDM3LS40MzgtMS4wNjJ0LjQzOC0xLjA2MnEuNDM3LS40MzggMS4wNjItLjQzOHQxLjA2My40MzhxLjQzNy40MzcuNDM3IDEuMDYydC0uNDM3IDEuMDYycS0uNDM4LjQzOC0xLjA2My40MzhaTTEyIDEyWm0tMSA4aDEuOTc1bC4zNS0yLjY1cS43NzUtLjIgMS40MzgtLjU4OC42NjItLjM4NyAxLjIxMi0uOTM3bDIuNDc1IDEuMDI1Ljk3NS0xLjctMi4xNS0xLjYyNXEuMTI1LS4zNS4xNzUtLjczOC4wNS0uMzg3LjA1LS43ODd0LS4wNS0uNzg4cS0uMDUtLjM4Ny0uMTc1LS43MzdsMi4xNS0xLjYyNS0uOTc1LTEuNy0yLjQ3NSAxLjA1cS0uNTUtLjU3NS0xLjIxMi0uOTYzLS42NjMtLjM4Ny0xLjQzOC0uNTg3TDEzIDRoLTEuOTc1bC0uMzUgMi42NXEtLjc3NS4yLTEuNDM3LjU4Ny0uNjYzLjM4OC0xLjIxMy45MzhMNS41NSA3LjE1bC0uOTc1IDEuNyAyLjE1IDEuNnEtLjEyNS4zNzUtLjE3NS43NS0uMDUuMzc1LS4wNS44IDAgLjQuMDUuNzc1dC4xNzUuNzVsLTIuMTUgMS42MjUuOTc1IDEuNyAyLjQ3NS0xLjA1cS41NS41NzUgMS4yMTMuOTYyLjY2Mi4zODggMS40MzcuNTg4WiIvPjwvc3ZnPg==';
  152. Editor.extensionImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHdpZHRoPSIyNCI+PHBhdGggZD0iTTguOCAyMUg1cS0uODI1IDAtMS40MTMtLjU4N1EzIDE5LjgyNSAzIDE5di0zLjhxMS4yIDAgMi4xLS43NjIuOS0uNzYzLjktMS45MzggMC0xLjE3NS0uOS0xLjkzOFE0LjIgOS44IDMgOS44VjZxMC0uODI1LjU4Ny0xLjQxMlE0LjE3NSA0IDUgNGg0cTAtMS4wNS43MjUtMS43NzVRMTAuNDUgMS41IDExLjUgMS41cTEuMDUgMCAxLjc3NS43MjVRMTQgMi45NSAxNCA0aDRxLjgyNSAwIDEuNDEzLjU4OFEyMCA1LjE3NSAyMCA2djRxMS4wNSAwIDEuNzc1LjcyNS43MjUuNzI1LjcyNSAxLjc3NSAwIDEuMDUtLjcyNSAxLjc3NVEyMS4wNSAxNSAyMCAxNXY0cTAgLjgyNS0uNTg3IDEuNDEzUTE4LjgyNSAyMSAxOCAyMWgtMy44cTAtMS4yNS0uNzg3LTIuMTI1UTEyLjYyNSAxOCAxMS41IDE4dC0xLjkxMi44NzVROC44IDE5Ljc1IDguOCAyMVpNNSAxOWgyLjEyNXEuNi0xLjY1IDEuOTI1LTIuMzI1UTEwLjM3NSAxNiAxMS41IDE2cTEuMTI1IDAgMi40NS42NzUgMS4zMjUuNjc1IDEuOTI1IDIuMzI1SDE4di02aDJxLjIgMCAuMzUtLjE1LjE1LS4xNS4xNS0uMzUgMC0uMi0uMTUtLjM1UTIwLjIgMTIgMjAgMTJoLTJWNmgtNlY0cTAtLjItLjE1LS4zNS0uMTUtLjE1LS4zNS0uMTUtLjIgMC0uMzUuMTVRMTEgMy44IDExIDR2Mkg1djIuMnExLjM1LjUgMi4xNzUgMS42NzVROCAxMS4wNSA4IDEyLjVxMCAxLjQyNS0uODI1IDIuNlQ1IDE2LjhabTcuNzUtNy43NVoiLz48L3N2Zz4=';
  153. Editor.colorDropperImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iNDgiIHdpZHRoPSI0OCI+PHBhdGggZD0iTTYgNDJ2LTguNGwxOC44NS0xOC44NS0zLjYtMy42TDIzLjMgOS4xbDQuNiA0LjZMMzUgNi42cS41NS0uNTUgMS4xNzUtLjU1dDEuMTc1LjU1bDQuMDUgNC4wNXEuNTUuNTUuNTUgMS4xNzVUNDEuNCAxM2wtNy4xIDcuMSA0LjYgNC42LTIuMDUgMi4wNS0zLjYtMy42TDE0LjQgNDJabTMtM2g0LjM1TDMxLjEgMjEuMjVsLTQuMzUtNC4zNUw5IDM0LjY1Wm0yMy4xNS0yMSA2LjItNi4yLTIuMTUtMi4xNS02LjIgNi4yWm0wIDBMMzAgMTUuODUgMzIuMTUgMThaIi8+PC9zdmc+';
  154. Editor.magnifyImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgLTk2MCA5NjAgOTYwIiB3aWR0aD0iMjQiPjxwYXRoIGQ9Ik03ODQtMTIwIDUzMi0zNzJxLTMwIDI0LTY5IDM4dC04MyAxNHEtMTA5IDAtMTg0LjUtNzUuNVQxMjAtNTgwcTAtMTA5IDc1LjUtMTg0LjVUMzgwLTg0MHExMDkgMCAxODQuNSA3NS41VDY0MC01ODBxMCA0NC0xNCA4M3QtMzggNjlsMjUyIDI1Mi01NiA1NlpNMzgwLTQwMHE3NSAwIDEyNy41LTUyLjVUNTYwLTU4MHEwLTc1LTUyLjUtMTI3LjVUMzgwLTc2MHEtNzUgMC0xMjcuNSA1Mi41VDIwMC01ODBxMCA3NSA1Mi41IDEyNy41VDM4MC00MDBaIi8+PC9zdmc+';
  155. Editor.sendImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgLTk2MCA5NjAgOTYwIiB3aWR0aD0iMjQiPjxwYXRoIGQ9Ik0xMjAtMTYwdi02NDBsNzYwIDMyMC03NjAgMzIwWm04MC0xMjAgNDc0LTIwMC00NzQtMjAwdjE0MGwyNDAgNjAtMjQwIDYwdjE0MFptMCAwdi00MDAgNDAwWiIvPjwvc3ZnPg==';
  156. Editor.sparklesImage = 'data:image/svg+xml;base64,PCFET0NUWVBFIHN2ZyBQVUJMSUMgIi0vL1czQy8vRFREIFNWRyAxLjEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvR3JhcGhpY3MvU1ZHLzEuMS9EVEQvc3ZnMTEuZHRkIj4KDTwhLS0gVXBsb2FkZWQgdG86IFNWRyBSZXBvLCB3d3cuc3ZncmVwby5jb20sIFRyYW5zZm9ybWVkIGJ5OiBTVkcgUmVwbyBNaXhlciBUb29scyAtLT4KPHN2ZyB3aWR0aD0iODAwcHgiIGhlaWdodD0iODAwcHgiIHZpZXdCb3g9IjAgMCA1MTIgNTEyIiBpZD0iaWNvbnMiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgZmlsbD0iI2ZjZmY0NyIgZGF0YS1kYXJrcmVhZGVyLWlubGluZS1maWxsPSIiIHN0eWxlPSItLWRhcmtyZWFkZXItaW5saW5lLWZpbGw6ICNkNmQ4NTA7IC0tZGFya3JlYWRlci1pbmxpbmUtc3Ryb2tlOiAjZDZkODUwOyIgc3Ryb2tlPSIjZmNmZjQ3IiBkYXRhLWRhcmtyZWFkZXItaW5saW5lLXN0cm9rZT0iIj4KDTxnIGlkPSJTVkdSZXBvX2JnQ2FycmllciIgc3Ryb2tlLXdpZHRoPSIwIi8+Cg08ZyBpZD0iU1ZHUmVwb190cmFjZXJDYXJyaWVyIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiLz4KDTxnIGlkPSJTVkdSZXBvX2ljb25DYXJyaWVyIj4KDTxwYXRoIGQ9Ik0yNTkuOTIsMjYyLjkxLDIxNi40LDE0OS43N2E5LDksMCwwLDAtMTYuOCwwTDE1Ni4wOCwyNjIuOTFhOSw5LDAsMCwxLTUuMTcsNS4xN0wzNy43NywzMTEuNmE5LDksMCwwLDAsMCwxNi44bDExMy4xNCw0My41MmE5LDksMCwwLDEsNS4xNyw1LjE3TDE5OS42LDQ5MC4yM2E5LDksMCwwLDAsMTYuOCwwbDQzLjUyLTExMy4xNGE5LDksMCwwLDEsNS4xNy01LjE3TDM3OC4yMywzMjguNGE5LDksMCwwLDAsMC0xNi44TDI2NS4wOSwyNjguMDhBOSw5LDAsMCwxLDI1OS45MiwyNjIuOTFaIiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIxOS45NjgiIGRhdGEtZGFya3JlYWRlci1pbmxpbmUtc3Ryb2tlPSIiIHN0eWxlPSItLWRhcmtyZWFkZXItaW5saW5lLXN0cm9rZTogIzE4MWExYjsiLz4KDTxwb2x5Z29uIHBvaW50cz0iMTA4IDY4IDg4IDE2IDY4IDY4IDE2IDg4IDY4IDEwOCA4OCAxNjAgMTA4IDEwOCAxNjAgODggMTA4IDY4IiBmaWxsPSJub25lIiBzdHJva2U9IiMwMDAwMDAiIHN0cm9rZS1saW5lY2FwPSJyb3VuZCIgc3Ryb2tlLWxpbmVqb2luPSJyb3VuZCIgc3Ryb2tlLXdpZHRoPSIxOS45NjgiIGRhdGEtZGFya3JlYWRlci1pbmxpbmUtc3Ryb2tlPSIiIHN0eWxlPSItLWRhcmtyZWFkZXItaW5saW5lLXN0cm9rZTogIzE4MWExYjsiLz4KDTxwb2x5Z29uIHBvaW50cz0iNDI2LjY3IDExNy4zMyA0MDAgNDggMzczLjMzIDExNy4zMyAzMDQgMTQ0IDM3My4zMyAxNzAuNjcgNDAwIDI0MCA0MjYuNjcgMTcwLjY3IDQ5NiAxNDQgNDI2LjY3IDExNy4zMyIgZmlsbD0ibm9uZSIgc3Ryb2tlPSIjMDAwMDAwIiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIHN0cm9rZS13aWR0aD0iMTkuOTY4IiBkYXRhLWRhcmtyZWFkZXItaW5saW5lLXN0cm9rZT0iIiBzdHlsZT0iLS1kYXJrcmVhZGVyLWlubGluZS1zdHJva2U6ICMxODFhMWI7Ii8+Cg08L2c+Cg08L3N2Zz4=';
  157. Editor.checkmarkImage = Editor.lightCheckmarkImage;
  158. /**
  159. * All fill styles supported by rough.js.
  160. */
  161. Editor.roughFillStyles = [{val: 'auto', dispName: 'Auto'}, {val: 'hachure', dispName: 'Hachure'},
  162. {val: 'solid', dispName: 'Solid'}, {val: 'zigzag', dispName: 'ZigZag'},
  163. {val: 'cross-hatch', dispName: 'Cross Hatch'}, {val: 'dashed', dispName: 'Dashed'},
  164. {val: 'zigzag-line', dispName: 'ZigZag Line'}];
  165. /**
  166. * Fill styles for normal mode.
  167. */
  168. Editor.fillStyles = [{val: 'auto', dispName: 'Auto'}, {val: 'hatch', dispName: 'Hatch'},
  169. {val: 'solid', dispName: 'Solid'}, {val: 'dots', dispName: 'Dots'},
  170. {val: 'cross-hatch', dispName: 'Cross Hatch'}, {val: 'dashed', dispName: 'Dashed'},
  171. {val: 'zigzag-line', dispName: 'ZigZag Line'}];
  172. /**
  173. * List of supported custom themes.
  174. */
  175. Editor.themes = [];
  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 the image URL to be used for the transparent background.
  182. */
  183. Editor.hintOffset = 20;
  184. /**
  185. * Delay in ms to show shape picker on hover over blue arrows.
  186. */
  187. Editor.shapePickerHoverDelay = 300;
  188. /**
  189. * Specifies the image URL to be used for the transparent background.
  190. */
  191. Editor.fitWindowBorders = null;
  192. /**
  193. * Specifies if the diagram should be saved automatically if possible. Default
  194. * is true.
  195. */
  196. Editor.popupsAllowed = window.urlParams != null? urlParams['noDevice'] != '1' : true;
  197. /**
  198. * Specifies if the html and whiteSpace styles should be removed on inserted cells.
  199. */
  200. Editor.simpleLabels = false;
  201. /**
  202. * Specifies if the native clipboard is enabled. Blocked in iframes for possible sandbox attribute.
  203. * LATER: Check if actually blocked.
  204. */
  205. Editor.enableNativeCipboard = window == window.top && !mxClient.IS_FF && navigator.clipboard != null;
  206. /**
  207. * Dynamic change of dark mode for minimal and sketch theme.
  208. */
  209. Editor.sketchMode = false;
  210. /**
  211. * Dynamic change of dark mode for minimal and sketch theme.
  212. */
  213. Editor.enableCssDarkMode = window.mxIsElectron? false : true; // Desktop app defaults to classic darkmode
  214. /**
  215. * Dynamic change of dark mode for minimal and sketch theme.
  216. */
  217. Editor.cssDarkMode = false;
  218. /**
  219. * Dynamic change of dark mode for minimal and sketch theme.
  220. */
  221. Editor.darkMode = false;
  222. /**
  223. * Dynamic change of dark mode for minimal and sketch theme.
  224. */
  225. Editor.currentTheme = uiTheme;
  226. /**
  227. * Dynamic change of dark mode for minimal and sketch theme.
  228. */
  229. Editor.darkColor = (Editor.enableCssDarkMode) ? '#121212' : '#18141D';
  230. /**
  231. * Dynamic change of dark mode for minimal and sketch theme.
  232. */
  233. Editor.lightColor = '#f0f0f0';
  234. /**
  235. * Label for the font size unit. Default is 'px'.
  236. */
  237. Editor.fontSizeUnit = 'px';
  238. /**
  239. * Default unit for page sizes. Default is inches.
  240. */
  241. Editor.pageSizeUnit = mxConstants.INCHES;
  242. /**
  243. * Returns the current state of the dark mode.
  244. */
  245. Editor.isDarkMode = function(value)
  246. {
  247. return Editor.darkMode;
  248. };
  249. /**
  250. * Returns true if the given URL is a PNG data URL.
  251. */
  252. Editor.isPngDataUrl = function(url)
  253. {
  254. return url != null && url.substring(0, 15) == 'data:image/png;'
  255. };
  256. /**
  257. * Returns true if the given binary data is a PNG file.
  258. */
  259. Editor.isPngData = function(data)
  260. {
  261. return data.length > 8 && data.charCodeAt(0) == 137 && data.charCodeAt(1) == 80 &&
  262. data.charCodeAt(2) == 78 && data.charCodeAt(3) == 71 && data.charCodeAt(4) == 13 &&
  263. data.charCodeAt(5) == 10 && data.charCodeAt(6) == 26 && data.charCodeAt(7) == 10;
  264. };
  265. /**
  266. * Converts HTML to plain text.
  267. */
  268. Editor.convertHtmlToText = function(label)
  269. {
  270. if (label != null)
  271. {
  272. var temp = document.createElement('div');
  273. temp.innerHTML = Graph.sanitizeHtml(label);
  274. return mxUtils.extractTextWithWhitespace(temp.childNodes)
  275. }
  276. else
  277. {
  278. return null;
  279. }
  280. };
  281. /**
  282. * Extracts the XML from the compressed or non-compressed text chunk.
  283. */
  284. Editor.extractGraphModelFromPng = function(data)
  285. {
  286. var result = null;
  287. try
  288. {
  289. var base64 = data.substring(data.indexOf(',') + 1);
  290. // Workaround for invalid character error in Safari
  291. var binary = (window.atob && !mxClient.IS_SF) ? atob(base64) : Base64.decode(base64, true);
  292. EditorUi.parsePng(binary, mxUtils.bind(this, function(pos, type, length)
  293. {
  294. var value = binary.substring(pos + 8, pos + 8 + length);
  295. if (type == 'zTXt')
  296. {
  297. var idx = value.indexOf(String.fromCharCode(0));
  298. if (value.substring(0, idx) == 'mxGraphModel')
  299. {
  300. // Workaround for Java URL Encoder using + for spaces, which isn't compatible with JS
  301. var xmlData = pako.inflateRaw(Graph.stringToArrayBuffer(
  302. value.substring(idx + 2)), {to: 'string'}).replace(/\+/g,' ');
  303. if (xmlData != null && xmlData.length > 0)
  304. {
  305. result = xmlData;
  306. }
  307. }
  308. }
  309. // Uncompressed section is normally not used
  310. else if (type == 'tEXt')
  311. {
  312. var vals = value.split(String.fromCharCode(0));
  313. if (vals.length > 1 && (vals[0] == 'mxGraphModel' ||
  314. vals[0] == 'mxfile'))
  315. {
  316. result = vals[1];
  317. }
  318. }
  319. if (result != null || type == 'IDAT')
  320. {
  321. // Stops processing the file as our text chunks
  322. // are always placed before the data section
  323. return true;
  324. }
  325. }));
  326. }
  327. catch (e)
  328. {
  329. // ignores decoding errors
  330. }
  331. if (result != null && result.charAt(0) == '%')
  332. {
  333. result = decodeURIComponent(result);
  334. }
  335. // Workaround for double encoded content
  336. if (result != null && result.charAt(0) == '%')
  337. {
  338. result = decodeURIComponent(result);
  339. }
  340. return result;
  341. };
  342. /**
  343. * Soundex algorithm for strings.
  344. * See https://www.codedrome.com/the-soundex-algorithm-in-javascript/
  345. */
  346. Editor.soundex = function(name)
  347. {
  348. if (name == null || name.length == 0)
  349. {
  350. return '';
  351. }
  352. else
  353. {
  354. var s = [];
  355. var si = 1;
  356. var c;
  357. // Changed: s maps to 0 not 2 to ignore plurals
  358. // ABCDEFGHIJKLMNOPQRSTUVWXYZ
  359. var mappings = '01230120022455012603010202';
  360. s[0] = name[0].toUpperCase();
  361. for(var i = 1, l = name.length; i < l; i++)
  362. {
  363. c = (name[i].toUpperCase()).charCodeAt(0) - 65;
  364. if(c >= 0 && c <= 25)
  365. {
  366. if(mappings[c] != '0')
  367. {
  368. if(mappings[c] != s[si-1])
  369. {
  370. s[si] = mappings[c];
  371. si++;
  372. }
  373. if(si > 3)
  374. {
  375. break;
  376. }
  377. }
  378. }
  379. }
  380. if(si <= 3)
  381. {
  382. while(si <= 3)
  383. {
  384. s[si] = '0';
  385. si++;
  386. }
  387. }
  388. return s.join('');
  389. }
  390. };
  391. /**
  392. * Selects the given part of the input element.
  393. */
  394. Editor.selectFilename = function(input)
  395. {
  396. var end = input.value.lastIndexOf('.');
  397. if (end > 0)
  398. {
  399. var ext = input.value.substring(end + 1);
  400. if (ext != 'drawio')
  401. {
  402. if (mxUtils.indexOf(['png', 'svg', 'html', 'xml', 'pdf'], ext) >= 0)
  403. {
  404. var temp = input.value.lastIndexOf('.drawio.', end);
  405. if (temp > 0)
  406. {
  407. end = temp;
  408. }
  409. }
  410. }
  411. }
  412. end = (end > 0) ? end : input.value.length;
  413. Editor.selectSubstring(input, 0, end);
  414. };
  415. /**
  416. * Selects the given part of the input element.
  417. */
  418. Editor.selectSubstring = function(input, startPos, endPos)
  419. {
  420. input.focus();
  421. if (typeof input.selectionStart != 'undefined')
  422. {
  423. input.selectionStart = startPos;
  424. input.selectionEnd = endPos;
  425. }
  426. else if (document.selection && document.selection.createRange)
  427. {
  428. // IE branch
  429. input.select();
  430. var range = document.selection.createRange();
  431. range.collapse(true);
  432. range.moveEnd('character', endPos);
  433. range.moveStart('character', startPos);
  434. range.select();
  435. }
  436. };
  437. /**
  438. *
  439. */
  440. Editor.toUnit = function(value, unit)
  441. {
  442. if (unit == mxConstants.INCHES)
  443. {
  444. return Math.round(value * 100 / mxConstants.PIXELS_PER_INCH) / 100;
  445. }
  446. else if (unit == mxConstants.MILLIMETERS)
  447. {
  448. return Math.round(value * 100 / mxConstants.PIXELS_PER_MM) / 100;
  449. }
  450. else if (unit == mxConstants.METERS)
  451. {
  452. return Math.round(value * 1000 / (mxConstants.PIXELS_PER_MM * 1000)) / 1000;
  453. }
  454. else
  455. {
  456. return Math.round(value);
  457. }
  458. };
  459. /**
  460. *
  461. */
  462. Editor.fromUnit = function(value, unit)
  463. {
  464. if (unit == mxConstants.INCHES)
  465. {
  466. return Math.round(value * mxConstants.PIXELS_PER_INCH);
  467. }
  468. else if (unit == mxConstants.MILLIMETERS)
  469. {
  470. return Math.round(value * mxConstants.PIXELS_PER_MM);
  471. }
  472. else if (unit == mxConstants.METERS)
  473. {
  474. return Math.round(value * mxConstants.PIXELS_PER_MM * 1000);
  475. }
  476. else
  477. {
  478. return Math.round(value);
  479. }
  480. };
  481. /**
  482. * Editor inherits from mxEventSource
  483. */
  484. mxUtils.extend(Editor, mxEventSource);
  485. /**
  486. * Stores initial state of mxClient.NO_FO.
  487. */
  488. Editor.prototype.originalNoForeignObject = mxClient.NO_FO;
  489. /**
  490. * Specifies the image URL to be used for the transparent background.
  491. */
  492. Editor.prototype.transparentImage = (mxClient.IS_SVG) ? 'data:image/gif;base64,R0lGODlhMAAwAIAAAP///wAAACH5BAEAAAAALAAAAAAwADAAAAIxhI+py+0Po5y02ouz3rz7D4biSJbmiabqyrbuC8fyTNf2jef6zvf+DwwKh8Si8egpAAA7' :
  493. IMAGE_PATH + '/transparent.gif';
  494. /**
  495. * Specifies if the canvas should be extended in all directions. Default is true.
  496. */
  497. Editor.prototype.extendCanvas = true;
  498. /**
  499. * Specifies if the app should run in chromeless mode. Default is false.
  500. * This default is only used if the contructor argument is null.
  501. */
  502. Editor.prototype.chromeless = false;
  503. /**
  504. * Specifies the order of OK/Cancel buttons in dialogs. Default is true.
  505. * Cancel first is used on Macs, Windows/Confluence uses cancel last.
  506. */
  507. Editor.prototype.cancelFirst = true;
  508. /**
  509. * Specifies if the editor is enabled. Default is true.
  510. */
  511. Editor.prototype.enabled = true;
  512. /**
  513. * Contains the name which was used for the last save. Default value is null.
  514. */
  515. Editor.prototype.filename = null;
  516. /**
  517. * Contains the current modified state of the diagram. This is false for
  518. * new diagrams and after the diagram was saved.
  519. */
  520. Editor.prototype.modified = false;
  521. /**
  522. * Specifies if the diagram should be saved automatically if possible. Default
  523. * is true.
  524. */
  525. Editor.prototype.autosave = true;
  526. /**
  527. * Specifies the top spacing for the initial page view. Default is 0.
  528. */
  529. Editor.prototype.initialTopSpacing = 0;
  530. /**
  531. * Specifies the app name. Default is document.title.
  532. */
  533. Editor.prototype.appName = document.title;
  534. /**
  535. *
  536. */
  537. Editor.prototype.editBlankUrl = window.location.origin + window.location.pathname;
  538. /**
  539. * Default value for the graph container overflow style.
  540. */
  541. Editor.prototype.defaultGraphOverflow = 'hidden';
  542. /**
  543. * Initializes the environment.
  544. */
  545. Editor.prototype.init = function() { };
  546. /**
  547. * Sets the XML node for the current diagram.
  548. */
  549. Editor.prototype.isChromelessView = function()
  550. {
  551. return this.chromeless;
  552. };
  553. /**
  554. * Sets the XML node for the current diagram.
  555. */
  556. Editor.prototype.setAutosave = function(value)
  557. {
  558. this.autosave = value;
  559. this.fireEvent(new mxEventObject('autosaveChanged'));
  560. };
  561. /**
  562. *
  563. */
  564. Editor.prototype.getEditBlankUrl = function(params)
  565. {
  566. return this.editBlankUrl + params;
  567. }
  568. /**
  569. *
  570. */
  571. Editor.prototype.editAsNew = function(xml, title)
  572. {
  573. var p = (title != null) ? '?title=' + encodeURIComponent(title) : '';
  574. if (urlParams['ui'] != null)
  575. {
  576. p += ((p.length > 0) ? '&' : '?') + 'ui=' + urlParams['ui'];
  577. }
  578. if (typeof window.postMessage !== 'undefined' &&
  579. (document.documentMode == null ||
  580. document.documentMode >= 10))
  581. {
  582. var wnd = null;
  583. var l = mxUtils.bind(this, function(evt)
  584. {
  585. if (evt.data == 'ready' && evt.source == wnd)
  586. {
  587. mxEvent.removeListener(window, 'message', l);
  588. wnd.postMessage(xml, '*');
  589. }
  590. });
  591. mxEvent.addListener(window, 'message', l);
  592. wnd = this.graph.openLink(this.getEditBlankUrl(
  593. p + ((p.length > 0) ? '&' : '?') +
  594. 'client=1'), null, true);
  595. }
  596. else
  597. {
  598. this.graph.openLink(this.getEditBlankUrl(p) +
  599. '#R' + encodeURIComponent(xml));
  600. }
  601. };
  602. /**
  603. * Sets the XML node for the current diagram.
  604. */
  605. Editor.prototype.createGraph = function(themes, model)
  606. {
  607. var graph = new Graph(null, model, null, null, themes);
  608. graph.transparentBackground = false;
  609. // Disables CSS transforms in Safari in chromeless mode
  610. var graphIsCssTransformsSupported = graph.isCssTransformsSupported;
  611. var self = this;
  612. graph.isCssTransformsSupported = function()
  613. {
  614. return graphIsCssTransformsSupported.apply(this, arguments) &&
  615. (!self.chromeless || !mxClient.IS_SF);
  616. };
  617. // Opens all links in a new window while editing
  618. if (!this.chromeless)
  619. {
  620. graph.isBlankLink = function(href)
  621. {
  622. return !this.isExternalProtocol(href);
  623. };
  624. }
  625. return graph;
  626. };
  627. /**
  628. * Sets the XML node for the current diagram.
  629. */
  630. Editor.prototype.resetGraph = function()
  631. {
  632. this.graph.gridEnabled = this.graph.defaultGridEnabled && (!this.isChromelessView() || urlParams['grid'] == '1');
  633. this.graph.graphHandler.guidesEnabled = true;
  634. this.graph.setTooltips(true);
  635. this.graph.setConnectable(true);
  636. this.graph.foldingEnabled = true;
  637. this.graph.scrollbars = this.graph.defaultScrollbars;
  638. this.graph.pageVisible = this.graph.defaultPageVisible;
  639. this.graph.pageBreaksVisible = this.graph.pageVisible;
  640. this.graph.preferPageSize = this.graph.pageBreaksVisible;
  641. this.graph.background = null;
  642. this.graph.pageScale = mxGraph.prototype.pageScale;
  643. this.graph.pageFormat = mxGraph.prototype.pageFormat;
  644. this.graph.currentScale = 1;
  645. this.graph.currentTranslate.x = 0;
  646. this.graph.currentTranslate.y = 0;
  647. this.updateGraphComponents();
  648. this.graph.view.setScale(1);
  649. };
  650. /**
  651. * Sets the XML node for the current diagram.
  652. */
  653. Editor.prototype.readGraphState = function(node)
  654. {
  655. var grid = node.getAttribute('grid');
  656. if (grid == null || grid == '')
  657. {
  658. grid = this.graph.defaultGridEnabled ? '1' : '0';
  659. }
  660. this.graph.gridEnabled = grid != '0' && (!this.isChromelessView() || urlParams['grid'] == '1');
  661. this.graph.gridSize = parseFloat(node.getAttribute('gridSize')) || mxGraph.prototype.gridSize;
  662. this.graph.graphHandler.guidesEnabled = node.getAttribute('guides') != '0';
  663. this.graph.setTooltips(node.getAttribute('tooltips') != '0');
  664. this.graph.setConnectable(node.getAttribute('connect') != '0');
  665. this.graph.connectionArrowsEnabled = node.getAttribute('arrows') != '0';
  666. this.graph.foldingEnabled = node.getAttribute('fold') != '0';
  667. if (this.isChromelessView() && this.graph.foldingEnabled)
  668. {
  669. this.graph.foldingEnabled = urlParams['nav'] == '1';
  670. this.graph.cellRenderer.forceControlClickHandler = this.graph.foldingEnabled;
  671. }
  672. var ps = parseFloat(node.getAttribute('pageScale'));
  673. if (!isNaN(ps) && ps > 0)
  674. {
  675. this.graph.pageScale = ps;
  676. }
  677. else
  678. {
  679. this.graph.pageScale = mxGraph.prototype.pageScale;
  680. }
  681. if (!this.graph.isLightboxView() && !this.graph.isViewer())
  682. {
  683. var pv = node.getAttribute('page');
  684. if (pv != null)
  685. {
  686. this.graph.pageVisible = (pv != '0');
  687. }
  688. else
  689. {
  690. this.graph.pageVisible = this.graph.defaultPageVisible;
  691. }
  692. }
  693. else
  694. {
  695. this.graph.pageVisible = false;
  696. }
  697. this.graph.pageBreaksVisible = this.graph.pageVisible;
  698. this.graph.preferPageSize = this.graph.pageBreaksVisible;
  699. var pw = parseFloat(node.getAttribute('pageWidth'));
  700. var ph = parseFloat(node.getAttribute('pageHeight'));
  701. if (!isNaN(pw) && !isNaN(ph))
  702. {
  703. this.graph.pageFormat = new mxRectangle(0, 0, pw, ph);
  704. }
  705. // Loads the persistent state settings
  706. var bg = node.getAttribute('background');
  707. if (bg != null && bg.length > 0)
  708. {
  709. this.graph.background = bg;
  710. }
  711. else
  712. {
  713. this.graph.background = null;
  714. }
  715. };
  716. /**
  717. * Sets the XML node for the current diagram.
  718. */
  719. Editor.prototype.setGraphXml = function(node)
  720. {
  721. if (node != null)
  722. {
  723. var dec = new mxCodec(node.ownerDocument);
  724. if (node.nodeName == 'mxGraphModel')
  725. {
  726. this.graph.model.beginUpdate();
  727. try
  728. {
  729. this.graph.model.clear();
  730. this.graph.view.scale = 1;
  731. this.readGraphState(node);
  732. this.updateGraphComponents();
  733. dec.decode(node, this.graph.getModel());
  734. }
  735. finally
  736. {
  737. this.graph.model.endUpdate();
  738. }
  739. this.fireEvent(new mxEventObject('resetGraphView'));
  740. }
  741. else if (node.nodeName == 'root')
  742. {
  743. this.resetGraph();
  744. // Workaround for invalid XML output in Firefox 20 due to bug in mxUtils.getXml
  745. var wrapper = dec.document.createElement('mxGraphModel');
  746. wrapper.appendChild(node);
  747. dec.decode(wrapper, this.graph.getModel());
  748. this.updateGraphComponents();
  749. this.fireEvent(new mxEventObject('resetGraphView'));
  750. }
  751. else
  752. {
  753. throw {
  754. message: mxResources.get('cannotOpenFile'),
  755. node: node,
  756. toString: function() { return this.message; }
  757. };
  758. }
  759. }
  760. else
  761. {
  762. this.resetGraph();
  763. this.graph.model.clear();
  764. this.fireEvent(new mxEventObject('resetGraphView'));
  765. }
  766. };
  767. /**
  768. * Returns the XML node that represents the current diagram.
  769. */
  770. Editor.prototype.getGraphXml = function(ignoreSelection)
  771. {
  772. ignoreSelection = (ignoreSelection != null) ? ignoreSelection : true;
  773. var node = null;
  774. if (ignoreSelection)
  775. {
  776. var enc = new mxCodec(mxUtils.createXmlDocument());
  777. node = enc.encode(this.graph.getModel());
  778. enc.document.appendChild(node);
  779. }
  780. else
  781. {
  782. node = this.graph.encodeCells(mxUtils.sortCells(
  783. this.graph.model.getTopmostCells(
  784. this.graph.getSelectionCells())));
  785. }
  786. if (this.graph.view.translate.x != 0 || this.graph.view.translate.y != 0)
  787. {
  788. node.setAttribute('dx', Math.round(this.graph.view.translate.x * 100) / 100);
  789. node.setAttribute('dy', Math.round(this.graph.view.translate.y * 100) / 100);
  790. }
  791. node.setAttribute('grid', (this.graph.isGridEnabled()) ? '1' : '0');
  792. node.setAttribute('gridSize', this.graph.gridSize);
  793. node.setAttribute('guides', (this.graph.graphHandler.guidesEnabled) ? '1' : '0');
  794. node.setAttribute('tooltips', (this.graph.tooltipHandler.isEnabled()) ? '1' : '0');
  795. node.setAttribute('connect', (this.graph.connectionHandler.isEnabled()) ? '1' : '0');
  796. node.setAttribute('arrows', (this.graph.connectionArrowsEnabled) ? '1' : '0');
  797. node.setAttribute('fold', (this.graph.foldingEnabled) ? '1' : '0');
  798. node.setAttribute('page', (this.graph.pageVisible) ? '1' : '0');
  799. node.setAttribute('pageScale', this.graph.pageScale);
  800. node.setAttribute('pageWidth', this.graph.pageFormat.width);
  801. node.setAttribute('pageHeight', this.graph.pageFormat.height);
  802. if (this.graph.background != null)
  803. {
  804. node.setAttribute('background', this.graph.background);
  805. }
  806. return node;
  807. };
  808. /**
  809. * Keeps the graph container in sync with the persistent graph state
  810. */
  811. Editor.prototype.updateGraphComponents = function()
  812. {
  813. var graph = this.graph;
  814. if (graph.container != null)
  815. {
  816. graph.view.validateBackground();
  817. graph.container.style.overflow = (graph.scrollbars) ?
  818. 'auto' : this.defaultGraphOverflow;
  819. this.fireEvent(new mxEventObject('updateGraphComponents'));
  820. }
  821. };
  822. /**
  823. * Sets the modified flag.
  824. */
  825. Editor.prototype.setModified = function(value)
  826. {
  827. this.modified = value;
  828. };
  829. /**
  830. * Sets the filename.
  831. */
  832. Editor.prototype.setFilename = function(value)
  833. {
  834. this.filename = value;
  835. };
  836. /**
  837. * Creates and returns a new undo manager.
  838. */
  839. Editor.prototype.createUndoManager = function()
  840. {
  841. var graph = this.graph;
  842. var undoMgr = new mxUndoManager();
  843. this.undoListener = function(sender, evt)
  844. {
  845. undoMgr.undoableEditHappened(evt.getProperty('edit'));
  846. };
  847. // Installs the command history
  848. var listener = mxUtils.bind(this, function(sender, evt)
  849. {
  850. this.undoListener.apply(this, arguments);
  851. });
  852. graph.getModel().addListener(mxEvent.UNDO, listener);
  853. graph.getView().addListener(mxEvent.UNDO, listener);
  854. // Keeps the selection in sync with the history
  855. var undoHandler = function(sender, evt)
  856. {
  857. var changes = evt.getProperty('edit').changes;
  858. var cand = graph.getSelectionCellsForChanges(changes);
  859. var scrolled = false;
  860. if (cand.length > 0)
  861. {
  862. var cells = [];
  863. for (var i = 0; i < cand.length; i++)
  864. {
  865. if (graph.view.getState(cand[i]) != null)
  866. {
  867. cells.push(cand[i]);
  868. if (!scrolled)
  869. {
  870. graph.scrollCellToVisible(cand[i]);
  871. scrolled = true;
  872. }
  873. }
  874. }
  875. graph.setSelectionCells(cells);
  876. }
  877. };
  878. undoMgr.addListener(mxEvent.UNDO, undoHandler);
  879. undoMgr.addListener(mxEvent.REDO, undoHandler);
  880. return undoMgr;
  881. };
  882. /**
  883. * Adds basic stencil set (no namespace).
  884. */
  885. Editor.prototype.initStencilRegistry = function() { };
  886. /**
  887. * Creates and returns a new undo manager.
  888. */
  889. Editor.prototype.destroy = function()
  890. {
  891. if (this.graph != null)
  892. {
  893. this.graph.destroy();
  894. this.graph = null;
  895. }
  896. };
  897. /**
  898. * Class for asynchronously opening a new window and loading a file at the same
  899. * time. This acts as a bridge between the open dialog and the new editor.
  900. */
  901. OpenFile = function(done)
  902. {
  903. this.producer = null;
  904. this.consumer = null;
  905. this.done = done;
  906. this.args = null;
  907. };
  908. /**
  909. * Registers the editor from the new window.
  910. */
  911. OpenFile.prototype.setConsumer = function(value)
  912. {
  913. this.consumer = value;
  914. this.execute();
  915. };
  916. /**
  917. * Sets the data from the loaded file.
  918. */
  919. OpenFile.prototype.setData = function()
  920. {
  921. this.args = arguments;
  922. this.execute();
  923. };
  924. /**
  925. * Displays an error message.
  926. */
  927. OpenFile.prototype.error = function(msg)
  928. {
  929. this.cancel(true);
  930. mxUtils.alert(msg);
  931. };
  932. /**
  933. * Consumes the data.
  934. */
  935. OpenFile.prototype.execute = function()
  936. {
  937. if (this.consumer != null && this.args != null)
  938. {
  939. this.cancel(false);
  940. this.consumer.apply(this, this.args);
  941. }
  942. };
  943. /**
  944. * Cancels the operation.
  945. */
  946. OpenFile.prototype.cancel = function(cancel)
  947. {
  948. if (this.done != null)
  949. {
  950. this.done((cancel != null) ? cancel : true);
  951. }
  952. };
  953. /**
  954. * Basic dialogs that are available in the viewer (print dialog).
  955. */
  956. function Dialog(editorUi, elt, w, h, modal, closable, onClose, noScroll, transparent, onResize, ignoreBgClick)
  957. {
  958. this.editorUi = editorUi;
  959. var dx = transparent? 57 : 0;
  960. var w0 = w;
  961. var h0 = h;
  962. var padding = transparent? 0 : 64; //No padding needed for transparent dialogs
  963. var ds = (!Editor.inlineFullscreen && editorUi.embedViewport != null) ?
  964. mxUtils.clone(editorUi.embedViewport) : this.getDocumentSize();
  965. // Workaround for print dialog offset in viewer lightbox
  966. if (editorUi.embedViewport == null && window.innerHeight != null)
  967. {
  968. ds.height = window.innerHeight;
  969. }
  970. var dh = ds.height;
  971. var left = Math.max(1, Math.round((ds.width - w - padding) / 2));
  972. var top = Math.max(1, Math.round((dh - h - editorUi.footerHeight) / 3));
  973. // Keeps window size inside available space
  974. elt.style.maxHeight = '100%';
  975. w = (document.body != null) ? Math.min(w, document.body.scrollWidth - padding) : w;
  976. h = Math.min(h, dh - padding);
  977. // Increments zIndex to put subdialogs and background over existing dialogs and background
  978. if (editorUi.dialogs.length > 0)
  979. {
  980. this.zIndex += editorUi.dialogs.length * 2;
  981. }
  982. if (this.bg == null)
  983. {
  984. this.bg = editorUi.createDiv('geBackground');
  985. this.bg.style.position = 'absolute';
  986. this.bg.style.height = dh + 'px';
  987. this.bg.style.right = '0px';
  988. this.bg.style.zIndex = this.zIndex - 2;
  989. mxUtils.setOpacity(this.bg, this.bgOpacity);
  990. }
  991. var origin = mxUtils.getDocumentScrollOrigin(document);
  992. this.bg.style.left = origin.x + 'px';
  993. this.bg.style.top = origin.y + 'px';
  994. left += origin.x;
  995. top += origin.y;
  996. if (!Editor.inlineFullscreen && editorUi.embedViewport != null)
  997. {
  998. this.bg.style.height = this.getDocumentSize().height + 'px';
  999. top += editorUi.embedViewport.y;
  1000. left += editorUi.embedViewport.x;
  1001. }
  1002. if (modal)
  1003. {
  1004. document.body.appendChild(this.bg);
  1005. }
  1006. var div = editorUi.createDiv(transparent? 'geTransDialog' : 'geDialog');
  1007. var pos = this.getPosition(left, top, w, h);
  1008. left = pos.x;
  1009. top = pos.y;
  1010. div.style.width = w + 'px';
  1011. div.style.height = h + 'px';
  1012. div.style.left = left + 'px';
  1013. div.style.top = top + 'px';
  1014. div.style.zIndex = this.zIndex;
  1015. div.appendChild(elt);
  1016. document.body.appendChild(div);
  1017. // Adds vertical scrollbars if needed
  1018. if (!noScroll && elt.clientHeight > div.clientHeight - padding)
  1019. {
  1020. elt.style.overflowY = 'auto';
  1021. }
  1022. //Prevent horizontal scrollbar
  1023. elt.style.overflowX = 'hidden';
  1024. if (closable)
  1025. {
  1026. var img = document.createElement('img');
  1027. img.setAttribute('src', Dialog.prototype.closeImage);
  1028. img.setAttribute('title', mxResources.get('close'));
  1029. img.className = 'geDialogClose';
  1030. img.style.top = (top + 14) + 'px';
  1031. img.style.left = (left + w + 38 - dx) + 'px';
  1032. img.style.zIndex = this.zIndex;
  1033. mxEvent.addListener(img, 'click', mxUtils.bind(this, function()
  1034. {
  1035. editorUi.hideDialog(true);
  1036. }));
  1037. document.body.appendChild(img);
  1038. this.dialogImg = img;
  1039. if (!ignoreBgClick)
  1040. {
  1041. var mouseDownSeen = false;
  1042. mxEvent.addGestureListeners(this.bg, mxUtils.bind(this, function(evt)
  1043. {
  1044. mouseDownSeen = true;
  1045. }), null, mxUtils.bind(this, function(evt)
  1046. {
  1047. if (mouseDownSeen)
  1048. {
  1049. editorUi.hideDialog(true);
  1050. mouseDownSeen = false;
  1051. }
  1052. }));
  1053. }
  1054. }
  1055. this.resizeListener = mxUtils.bind(this, function()
  1056. {
  1057. if (onResize != null)
  1058. {
  1059. var newWH = onResize();
  1060. if (newWH != null)
  1061. {
  1062. w0 = w = newWH.w;
  1063. h0 = h = newWH.h;
  1064. }
  1065. }
  1066. var ds = (!Editor.inlineFullscreen && editorUi.embedViewport != null) ?
  1067. mxUtils.clone(editorUi.embedViewport) : this.getDocumentSize();
  1068. dh = ds.height;
  1069. this.bg.style.height = dh + 'px';
  1070. if (!Editor.inlineFullscreen && editorUi.embedViewport != null)
  1071. {
  1072. this.bg.style.height = this.getDocumentSize().height + 'px';
  1073. }
  1074. left = Math.max(1, Math.round((ds.width - w - padding) / 2));
  1075. top = Math.max(1, Math.round((dh - h - editorUi.footerHeight) / 3));
  1076. w = (document.body != null) ? Math.min(w0, document.body.scrollWidth - padding) : w0;
  1077. h = Math.min(h0, dh - padding);
  1078. // var dh = ds.height;
  1079. var left = Math.max(1, Math.round((ds.width - w - padding) / 2));
  1080. var top = Math.max(1, Math.round((dh - h - editorUi.footerHeight) / 3));
  1081. var pos = this.getPosition(left, top, w, h);
  1082. left = pos.x;
  1083. top = pos.y;
  1084. var origin = mxUtils.getDocumentScrollOrigin(document);
  1085. left += origin.x;
  1086. top += origin.y;
  1087. if (!Editor.inlineFullscreen && editorUi.embedViewport != null)
  1088. {
  1089. top += editorUi.embedViewport.y;
  1090. left += editorUi.embedViewport.x;
  1091. }
  1092. div.style.left = left + 'px';
  1093. div.style.top = top + 'px';
  1094. div.style.width = w + 'px';
  1095. div.style.height = h + 'px';
  1096. // Adds vertical scrollbars if needed
  1097. if (!noScroll && elt.clientHeight > div.clientHeight - padding)
  1098. {
  1099. elt.style.overflowY = 'auto';
  1100. }
  1101. if (this.dialogImg != null)
  1102. {
  1103. this.dialogImg.style.top = (top + 14) + 'px';
  1104. this.dialogImg.style.left = (left + w + 38 - dx) + 'px';
  1105. }
  1106. });
  1107. if (editorUi.embedViewport != null)
  1108. {
  1109. editorUi.addListener('embedViewportChanged', this.resizeListener);
  1110. }
  1111. else
  1112. {
  1113. mxEvent.addListener(window, 'resize', this.resizeListener);
  1114. }
  1115. this.onDialogClose = onClose;
  1116. this.container = div;
  1117. editorUi.editor.fireEvent(new mxEventObject('showDialog'));
  1118. };
  1119. /**
  1120. *
  1121. */
  1122. Dialog.prototype.zIndex = mxPopupMenu.prototype.zIndex - 2;
  1123. /**
  1124. *
  1125. */
  1126. Dialog.prototype.noColorImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/nocolor.png' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkEzRDlBMUUwODYxMTExRTFCMzA4RDdDMjJBMEMxRDM3IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkEzRDlBMUUxODYxMTExRTFCMzA4RDdDMjJBMEMxRDM3Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QTNEOUExREU4NjExMTFFMUIzMDhEN0MyMkEwQzFEMzciIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QTNEOUExREY4NjExMTFFMUIzMDhEN0MyMkEwQzFEMzciLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5xh3fmAAAABlBMVEX////MzMw46qqDAAAAGElEQVR42mJggAJGKGAYIIGBth8KAAIMAEUQAIElnLuQAAAAAElFTkSuQmCC';
  1127. /**
  1128. *
  1129. */
  1130. Dialog.prototype.defaultColorImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/defaultcolor.png' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUAAAD///+l2Z/dAAAAKUlEQVQI1wXBMREAIAwEsHAMjJVQKZVW6U8CDpdH0QxLnLjxoqJjYvMBewMJ51TWcscAAAAASUVORK5CYII=';
  1131. /**
  1132. *
  1133. */
  1134. Dialog.prototype.closeImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/close.png' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJAQMAAADaX5RTAAAABlBMVEV7mr3///+wksspAAAAAnRSTlP/AOW3MEoAAAAdSURBVAgdY9jXwCDDwNDRwHCwgeExmASygSL7GgB12QiqNHZZIwAAAABJRU5ErkJggg==';
  1135. /**
  1136. *
  1137. */
  1138. 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==';
  1139. /**
  1140. * Removes the dialog from the DOM.
  1141. */
  1142. Dialog.prototype.bgOpacity = 80;
  1143. /**
  1144. * Removes the dialog from the DOM.
  1145. */
  1146. Dialog.prototype.getDocumentSize = function()
  1147. {
  1148. return mxUtils.getDocumentSize();
  1149. };
  1150. /**
  1151. * Removes the dialog from the DOM.
  1152. */
  1153. Dialog.prototype.getPosition = function(left, top)
  1154. {
  1155. return new mxPoint(left, top);
  1156. };
  1157. /**
  1158. * Removes the dialog from the DOM.
  1159. */
  1160. Dialog.prototype.close = function(cancel, isEsc)
  1161. {
  1162. if (this.onDialogClose != null)
  1163. {
  1164. if (this.onDialogClose(cancel, isEsc) == false)
  1165. {
  1166. return false;
  1167. }
  1168. this.onDialogClose = null;
  1169. }
  1170. if (this.dialogImg != null && this.dialogImg.parentNode != null)
  1171. {
  1172. this.dialogImg.parentNode.removeChild(this.dialogImg);
  1173. this.dialogImg = null;
  1174. }
  1175. if (this.bg != null && this.bg.parentNode != null)
  1176. {
  1177. this.bg.parentNode.removeChild(this.bg);
  1178. }
  1179. if (this.editorUi.embedViewport != null)
  1180. {
  1181. this.editorUi.removeListener(this.resizeListener);
  1182. }
  1183. else
  1184. {
  1185. mxEvent.removeListener(window, 'resize', this.resizeListener);
  1186. }
  1187. if (this.container.parentNode != null)
  1188. {
  1189. this.container.parentNode.removeChild(this.container);
  1190. }
  1191. };
  1192. /**
  1193. *
  1194. */
  1195. var ErrorDialog = function(editorUi, title, message, buttonText, fn, retry, buttonText2, fn2, hide, buttonText3, fn3)
  1196. {
  1197. hide = (hide != null) ? hide : true;
  1198. var div = document.createElement('div');
  1199. div.style.textAlign = 'center';
  1200. if (title != null)
  1201. {
  1202. var hd = document.createElement('div');
  1203. hd.style.padding = '0px';
  1204. hd.style.margin = '0px';
  1205. hd.style.fontSize = '18px';
  1206. hd.style.paddingBottom = '16px';
  1207. hd.style.marginBottom = '10px';
  1208. hd.style.borderBottom = '1px solid #c0c0c0';
  1209. hd.style.color = 'gray';
  1210. hd.style.whiteSpace = 'nowrap';
  1211. hd.style.textOverflow = 'ellipsis';
  1212. hd.style.overflow = 'hidden';
  1213. mxUtils.write(hd, title);
  1214. hd.setAttribute('title', title);
  1215. div.appendChild(hd);
  1216. }
  1217. var p2 = document.createElement('div');
  1218. p2.style.lineHeight = '1.2em';
  1219. p2.style.padding = '6px';
  1220. if (typeof message === 'string')
  1221. {
  1222. message = message.replace(/\n/g, '<br/>');
  1223. }
  1224. p2.innerHTML = Graph.sanitizeHtml(message);
  1225. div.appendChild(p2);
  1226. var btns = document.createElement('div');
  1227. btns.style.marginTop = '12px';
  1228. btns.style.textAlign = 'center';
  1229. if (retry != null)
  1230. {
  1231. var retryBtn = mxUtils.button(mxResources.get('tryAgain'), function()
  1232. {
  1233. editorUi.hideDialog();
  1234. retry();
  1235. });
  1236. retryBtn.className = 'geBtn';
  1237. btns.appendChild(retryBtn);
  1238. btns.style.textAlign = 'center';
  1239. }
  1240. if (buttonText3 != null)
  1241. {
  1242. var btn3 = mxUtils.button(buttonText3, function()
  1243. {
  1244. if (fn3 != null)
  1245. {
  1246. fn3();
  1247. }
  1248. });
  1249. btn3.className = 'geBtn';
  1250. btns.appendChild(btn3);
  1251. }
  1252. var btn = mxUtils.button(buttonText, function()
  1253. {
  1254. if (hide)
  1255. {
  1256. editorUi.hideDialog();
  1257. }
  1258. if (fn != null)
  1259. {
  1260. fn();
  1261. }
  1262. });
  1263. btn.className = 'geBtn';
  1264. btns.appendChild(btn);
  1265. if (buttonText2 != null)
  1266. {
  1267. var mainBtn = mxUtils.button(buttonText2, function()
  1268. {
  1269. if (hide)
  1270. {
  1271. editorUi.hideDialog();
  1272. }
  1273. if (fn2 != null)
  1274. {
  1275. fn2();
  1276. }
  1277. });
  1278. mainBtn.className = 'geBtn gePrimaryBtn';
  1279. btns.appendChild(mainBtn);
  1280. }
  1281. this.init = function()
  1282. {
  1283. btn.focus();
  1284. };
  1285. div.appendChild(btns);
  1286. this.container = div;
  1287. };
  1288. /**
  1289. * Constructs a new print dialog.
  1290. */
  1291. var PrintDialog = function(editorUi, title, fn)
  1292. {
  1293. this.create(editorUi, title, fn);
  1294. };
  1295. /**
  1296. * Constructs a new print dialog.
  1297. */
  1298. PrintDialog.prototype.create = function(editorUi)
  1299. {
  1300. var graph = editorUi.editor.graph;
  1301. var row, td;
  1302. var table = document.createElement('table');
  1303. table.style.width = '100%';
  1304. table.style.height = '100%';
  1305. var tbody = document.createElement('tbody');
  1306. row = document.createElement('tr');
  1307. var onePageCheckBox = document.createElement('input');
  1308. onePageCheckBox.setAttribute('type', 'checkbox');
  1309. td = document.createElement('td');
  1310. td.setAttribute('colspan', '2');
  1311. td.style.fontSize = '10pt';
  1312. td.appendChild(onePageCheckBox);
  1313. var span = document.createElement('span');
  1314. mxUtils.write(span, ' ' + mxResources.get('fitPage'));
  1315. td.appendChild(span);
  1316. mxEvent.addListener(span, 'click', function(evt)
  1317. {
  1318. onePageCheckBox.checked = !onePageCheckBox.checked;
  1319. pageCountCheckBox.checked = !onePageCheckBox.checked;
  1320. mxEvent.consume(evt);
  1321. });
  1322. mxEvent.addListener(onePageCheckBox, 'change', function()
  1323. {
  1324. pageCountCheckBox.checked = !onePageCheckBox.checked;
  1325. });
  1326. row.appendChild(td);
  1327. tbody.appendChild(row);
  1328. row = row.cloneNode(false);
  1329. var pageCountCheckBox = document.createElement('input');
  1330. pageCountCheckBox.setAttribute('type', 'checkbox');
  1331. td = document.createElement('td');
  1332. td.style.fontSize = '10pt';
  1333. td.appendChild(pageCountCheckBox);
  1334. var span = document.createElement('span');
  1335. mxUtils.write(span, ' ' + mxResources.get('posterPrint') + ':');
  1336. td.appendChild(span);
  1337. mxEvent.addListener(span, 'click', function(evt)
  1338. {
  1339. pageCountCheckBox.checked = !pageCountCheckBox.checked;
  1340. onePageCheckBox.checked = !pageCountCheckBox.checked;
  1341. mxEvent.consume(evt);
  1342. });
  1343. row.appendChild(td);
  1344. var pageCountInput = document.createElement('input');
  1345. pageCountInput.setAttribute('value', '1');
  1346. pageCountInput.setAttribute('type', 'number');
  1347. pageCountInput.setAttribute('min', '1');
  1348. pageCountInput.setAttribute('size', '4');
  1349. pageCountInput.setAttribute('disabled', 'disabled');
  1350. pageCountInput.style.width = '50px';
  1351. td = document.createElement('td');
  1352. td.style.fontSize = '10pt';
  1353. td.appendChild(pageCountInput);
  1354. mxUtils.write(td, ' ' + mxResources.get('pages') + ' (max)');
  1355. row.appendChild(td);
  1356. tbody.appendChild(row);
  1357. mxEvent.addListener(pageCountCheckBox, 'change', function()
  1358. {
  1359. if (pageCountCheckBox.checked)
  1360. {
  1361. pageCountInput.removeAttribute('disabled');
  1362. }
  1363. else
  1364. {
  1365. pageCountInput.setAttribute('disabled', 'disabled');
  1366. }
  1367. onePageCheckBox.checked = !pageCountCheckBox.checked;
  1368. });
  1369. row = row.cloneNode(false);
  1370. td = document.createElement('td');
  1371. mxUtils.write(td, mxResources.get('pageScale') + ':');
  1372. row.appendChild(td);
  1373. td = document.createElement('td');
  1374. var pageScaleInput = document.createElement('input');
  1375. pageScaleInput.setAttribute('value', '100 %');
  1376. pageScaleInput.setAttribute('size', '5');
  1377. pageScaleInput.style.width = '50px';
  1378. td.appendChild(pageScaleInput);
  1379. row.appendChild(td);
  1380. tbody.appendChild(row);
  1381. row = document.createElement('tr');
  1382. td = document.createElement('td');
  1383. td.colSpan = 2;
  1384. td.style.paddingTop = '20px';
  1385. td.setAttribute('align', 'right');
  1386. // Overall scale for print-out to account for print borders in dialogs etc
  1387. function preview(print)
  1388. {
  1389. var autoOrigin = onePageCheckBox.checked || pageCountCheckBox.checked;
  1390. var printScale = parseInt(pageScaleInput.value) / 100;
  1391. if (isNaN(printScale))
  1392. {
  1393. printScale = 1;
  1394. pageScaleInput.value = '100%';
  1395. }
  1396. // Workaround to match available paper size in actual print output
  1397. if (mxClient.IS_SF)
  1398. {
  1399. printScale *= 0.75;
  1400. }
  1401. var pf = graph.pageFormat || mxConstants.PAGE_FORMAT_A4_PORTRAIT;
  1402. var scale = 1 / graph.pageScale;
  1403. if (autoOrigin)
  1404. {
  1405. var pageCount = (onePageCheckBox.checked) ? 1 : parseInt(pageCountInput.value);
  1406. if (!isNaN(pageCount))
  1407. {
  1408. scale = mxUtils.getScaleForPageCount(pageCount, graph, pf);
  1409. }
  1410. }
  1411. // Negative coordinates are cropped or shifted if page visible
  1412. var border = 0;
  1413. var x0 = 0;
  1414. var y0 = 0;
  1415. // Applies print scale
  1416. pf = mxRectangle.fromRectangle(pf);
  1417. pf.width = Math.ceil(pf.width * printScale);
  1418. pf.height = Math.ceil(pf.height * printScale);
  1419. scale *= printScale;
  1420. // Starts at first visible page
  1421. if (!autoOrigin && graph.pageVisible)
  1422. {
  1423. var layout = graph.getPageLayout();
  1424. x0 -= layout.x * pf.width;
  1425. y0 -= layout.y * pf.height;
  1426. }
  1427. else
  1428. {
  1429. autoOrigin = true;
  1430. }
  1431. var preview = PrintDialog.createPrintPreview(graph, scale, pf, border, x0, y0, autoOrigin);
  1432. preview.open();
  1433. if (print)
  1434. {
  1435. PrintDialog.printPreview(preview);
  1436. }
  1437. };
  1438. var cancelBtn = mxUtils.button(mxResources.get('cancel'), function()
  1439. {
  1440. editorUi.hideDialog();
  1441. });
  1442. cancelBtn.className = 'geBtn';
  1443. if (editorUi.editor.cancelFirst)
  1444. {
  1445. td.appendChild(cancelBtn);
  1446. }
  1447. if (PrintDialog.previewEnabled)
  1448. {
  1449. var previewBtn = mxUtils.button(mxResources.get('preview'), function()
  1450. {
  1451. editorUi.hideDialog();
  1452. preview(false);
  1453. });
  1454. previewBtn.className = 'geBtn';
  1455. td.appendChild(previewBtn);
  1456. }
  1457. var printBtn = mxUtils.button(mxResources.get((!PrintDialog.previewEnabled) ? 'ok' : 'print'), function()
  1458. {
  1459. editorUi.hideDialog();
  1460. preview(true);
  1461. });
  1462. printBtn.className = 'geBtn gePrimaryBtn';
  1463. td.appendChild(printBtn);
  1464. if (!editorUi.editor.cancelFirst)
  1465. {
  1466. td.appendChild(cancelBtn);
  1467. }
  1468. row.appendChild(td);
  1469. tbody.appendChild(row);
  1470. table.appendChild(tbody);
  1471. this.container = table;
  1472. };
  1473. /**
  1474. * Constructs a new print dialog.
  1475. */
  1476. PrintDialog.printPreview = function(preview)
  1477. {
  1478. try
  1479. {
  1480. if (preview.wnd != null)
  1481. {
  1482. var printFn = function()
  1483. {
  1484. preview.wnd.focus();
  1485. preview.wnd.print();
  1486. preview.wnd.close();
  1487. };
  1488. // Workaround for rendering SVG output and
  1489. // make window available for printing
  1490. window.setTimeout(printFn, 500);
  1491. }
  1492. }
  1493. catch (e)
  1494. {
  1495. // ignores possible Access Denied
  1496. }
  1497. };
  1498. /**
  1499. * Constructs a new print dialog.
  1500. */
  1501. PrintDialog.createPrintPreview = function(graph, scale, pf, border, x0, y0, autoOrigin)
  1502. {
  1503. var preview = new mxPrintPreview(graph, scale, pf, border, x0, y0);
  1504. preview.title = mxResources.get('preview');
  1505. preview.addPageCss = !mxClient.IS_SF;
  1506. preview.printBackgroundImage = true;
  1507. preview.autoOrigin = autoOrigin;
  1508. var bg = graph.background;
  1509. if (bg == null || bg == '' || bg == mxConstants.NONE)
  1510. {
  1511. bg = '#ffffff';
  1512. }
  1513. preview.backgroundColor = bg;
  1514. // Excludes hints from being printed as text labels
  1515. var isTextLabel = preview.isTextLabel;
  1516. preview.isTextLabel = function(node)
  1517. {
  1518. return !node.className == 'geHint' && isTextLabel.apply(this, arguments);
  1519. };
  1520. // Makes relative links absolute
  1521. var getLinkForCellState = preview.getLinkForCellState;
  1522. preview.getLinkForCellState = function(state)
  1523. {
  1524. return graph.getAbsoluteUrl(getLinkForCellState.apply(this, arguments));
  1525. };
  1526. return preview;
  1527. };
  1528. /**
  1529. * Specifies if the preview button should be enabled. Default is true.
  1530. */
  1531. PrintDialog.previewEnabled = true;
  1532. /**
  1533. * Constructs a new page setup dialog.
  1534. */
  1535. var PageSetupDialog = function(editorUi)
  1536. {
  1537. var graph = editorUi.editor.graph;
  1538. var row, td;
  1539. var table = document.createElement('table');
  1540. table.style.width = '100%';
  1541. table.style.height = '100%';
  1542. var tbody = document.createElement('tbody');
  1543. row = document.createElement('tr');
  1544. td = document.createElement('td');
  1545. td.style.verticalAlign = 'top';
  1546. td.style.fontSize = '10pt';
  1547. mxUtils.write(td, mxResources.get('paperSize') + ':');
  1548. row.appendChild(td);
  1549. td = document.createElement('td');
  1550. td.style.verticalAlign = 'top';
  1551. td.style.fontSize = '10pt';
  1552. var accessor = PageSetupDialog.addPageFormatPanel(td, 'pagesetupdialog', graph.pageFormat);
  1553. row.appendChild(td);
  1554. tbody.appendChild(row);
  1555. row = document.createElement('tr');
  1556. td = document.createElement('td');
  1557. mxUtils.write(td, mxResources.get('gridSize') + ':');
  1558. row.appendChild(td);
  1559. td = document.createElement('td');
  1560. td.style.whiteSpace = 'nowrap';
  1561. var gridSizeInput = document.createElement('input');
  1562. gridSizeInput.setAttribute('type', 'number');
  1563. gridSizeInput.setAttribute('min', '0');
  1564. gridSizeInput.style.width = '40px';
  1565. gridSizeInput.style.marginLeft = '6px';
  1566. gridSizeInput.value = graph.getGridSize();
  1567. td.appendChild(gridSizeInput);
  1568. mxEvent.addListener(gridSizeInput, 'change', function()
  1569. {
  1570. var value = parseInt(gridSizeInput.value);
  1571. gridSizeInput.value = Math.max(1, (isNaN(value)) ? graph.getGridSize() : value);
  1572. });
  1573. row.appendChild(td);
  1574. tbody.appendChild(row);
  1575. row = document.createElement('tr');
  1576. td = document.createElement('td');
  1577. mxUtils.write(td, mxResources.get('background') + ':');
  1578. row.appendChild(td);
  1579. td = document.createElement('td');
  1580. var changeImageLink = document.createElement('button');
  1581. changeImageLink.className = 'geBtn';
  1582. changeImageLink.style.margin = '0px';
  1583. mxUtils.write(changeImageLink, mxResources.get('change') + '...');
  1584. var imgPreview = document.createElement('div');
  1585. imgPreview.style.display = 'inline-block';
  1586. imgPreview.style.verticalAlign = 'middle';
  1587. imgPreview.style.backgroundPosition = 'center center';
  1588. imgPreview.style.backgroundRepeat = 'no-repeat';
  1589. imgPreview.style.backgroundSize = 'contain';
  1590. imgPreview.style.border = '1px solid lightGray';
  1591. imgPreview.style.borderRadius = '4px';
  1592. imgPreview.style.marginRight = '14px';
  1593. imgPreview.style.height = '32px';
  1594. imgPreview.style.width = '64px';
  1595. imgPreview.style.cursor = 'pointer';
  1596. imgPreview.style.padding = '4px';
  1597. var newBackgroundImage = graph.backgroundImage;
  1598. var newBackgroundColor = graph.background;
  1599. var newShadowVisible = graph.shadowVisible;
  1600. function updateBackgroundImage()
  1601. {
  1602. var img = newBackgroundImage;
  1603. if (img != null && img.originalSrc != null)
  1604. {
  1605. img = editorUi.createImageForPageLink(img.originalSrc, null);
  1606. }
  1607. if (img != null && img.src != null)
  1608. {
  1609. imgPreview.style.backgroundImage = 'url(' + img.src + ')';
  1610. imgPreview.style.display = 'inline-block';
  1611. }
  1612. else
  1613. {
  1614. imgPreview.style.backgroundImage = '';
  1615. imgPreview.style.display = 'none';
  1616. }
  1617. imgPreview.style.backgroundColor = '';
  1618. if (newBackgroundColor != null && newBackgroundColor != mxConstants.NONE)
  1619. {
  1620. imgPreview.style.backgroundColor = newBackgroundColor;
  1621. imgPreview.style.display = 'inline-block';
  1622. }
  1623. };
  1624. var changeImage = function(evt)
  1625. {
  1626. editorUi.showBackgroundImageDialog(function(image, failed, color, shadowVisible)
  1627. {
  1628. if (!failed)
  1629. {
  1630. if (image != null && image.src != null && Graph.isPageLink(image.src))
  1631. {
  1632. image = {originalSrc: image.src};
  1633. }
  1634. newBackgroundImage = image;
  1635. newShadowVisible = shadowVisible;
  1636. }
  1637. newBackgroundColor = color;
  1638. updateBackgroundImage();
  1639. }, newBackgroundImage, newBackgroundColor, true);
  1640. mxEvent.consume(evt);
  1641. };
  1642. mxEvent.addListener(changeImageLink, 'click', changeImage);
  1643. mxEvent.addListener(imgPreview, 'click', changeImage);
  1644. updateBackgroundImage();
  1645. td.appendChild(imgPreview);
  1646. td.appendChild(changeImageLink);
  1647. row.appendChild(td);
  1648. tbody.appendChild(row);
  1649. row = document.createElement('tr');
  1650. td = document.createElement('td');
  1651. td.colSpan = 2;
  1652. td.style.paddingTop = '16px';
  1653. td.setAttribute('align', 'right');
  1654. var cancelBtn = mxUtils.button(mxResources.get('cancel'), function()
  1655. {
  1656. editorUi.hideDialog();
  1657. });
  1658. cancelBtn.className = 'geBtn';
  1659. if (editorUi.editor.cancelFirst)
  1660. {
  1661. td.appendChild(cancelBtn);
  1662. }
  1663. var applyBtn = mxUtils.button(mxResources.get('apply'), function()
  1664. {
  1665. editorUi.hideDialog();
  1666. var gridSize = parseInt(gridSizeInput.value);
  1667. if (!isNaN(gridSize) && graph.gridSize !== gridSize)
  1668. {
  1669. graph.setGridSize(gridSize);
  1670. }
  1671. var change = new ChangePageSetup(editorUi, newBackgroundColor,
  1672. newBackgroundImage, accessor.get());
  1673. change.ignoreColor = graph.background == newBackgroundColor;
  1674. var oldSrc = (graph.backgroundImage != null) ? graph.backgroundImage.src : null;
  1675. var newSrc = (newBackgroundImage != null) ? newBackgroundImage.src : null;
  1676. change.ignoreImage = oldSrc === newSrc;
  1677. if (newShadowVisible != null)
  1678. {
  1679. change.shadowVisible = newShadowVisible;
  1680. }
  1681. if (graph.pageFormat.width != change.previousFormat.width ||
  1682. graph.pageFormat.height != change.previousFormat.height ||
  1683. !change.ignoreColor || !change.ignoreImage||
  1684. change.shadowVisible != graph.shadowVisible)
  1685. {
  1686. graph.model.execute(change);
  1687. }
  1688. });
  1689. applyBtn.className = 'geBtn gePrimaryBtn';
  1690. td.appendChild(applyBtn);
  1691. if (!editorUi.editor.cancelFirst)
  1692. {
  1693. td.appendChild(cancelBtn);
  1694. }
  1695. row.appendChild(td);
  1696. tbody.appendChild(row);
  1697. table.appendChild(tbody);
  1698. this.container = table;
  1699. };
  1700. /**
  1701. *
  1702. */
  1703. PageSetupDialog.addPageFormatPanel = function(div, namePostfix, pageFormat, pageFormatListener)
  1704. {
  1705. var formatName = 'format-' + namePostfix;
  1706. var portraitCheckBox = document.createElement('input');
  1707. portraitCheckBox.setAttribute('name', formatName);
  1708. portraitCheckBox.setAttribute('type', 'radio');
  1709. portraitCheckBox.setAttribute('value', 'portrait');
  1710. var landscapeCheckBox = document.createElement('input');
  1711. landscapeCheckBox.setAttribute('name', formatName);
  1712. landscapeCheckBox.setAttribute('type', 'radio');
  1713. landscapeCheckBox.setAttribute('value', 'landscape');
  1714. var paperSizeSelect = document.createElement('select');
  1715. paperSizeSelect.style.marginBottom = '4px';
  1716. paperSizeSelect.style.borderRadius = '4px';
  1717. paperSizeSelect.style.borderWidth = '1px';
  1718. paperSizeSelect.style.borderStyle = 'solid';
  1719. paperSizeSelect.style.boxSizing = 'border-box';
  1720. paperSizeSelect.style.padding = '2px';
  1721. paperSizeSelect.style.width = '206px';
  1722. var formatDiv = document.createElement('div');
  1723. formatDiv.style.whiteSpace = 'nowrap';
  1724. formatDiv.style.marginLeft = '4px';
  1725. formatDiv.style.width = '210px';
  1726. formatDiv.style.height = '24px';
  1727. portraitCheckBox.style.marginRight = '6px';
  1728. formatDiv.appendChild(portraitCheckBox);
  1729. var portraitSpan = document.createElement('span');
  1730. portraitSpan.style.maxWidth = '100px';
  1731. mxUtils.write(portraitSpan, mxResources.get('portrait'));
  1732. formatDiv.appendChild(portraitSpan);
  1733. landscapeCheckBox.style.marginLeft = '10px';
  1734. landscapeCheckBox.style.marginRight = '6px';
  1735. formatDiv.appendChild(landscapeCheckBox);
  1736. var landscapeSpan = document.createElement('span');
  1737. landscapeSpan.style.width = '100px';
  1738. mxUtils.write(landscapeSpan, mxResources.get('landscape'));
  1739. formatDiv.appendChild(landscapeSpan)
  1740. var customDiv = document.createElement('div');
  1741. customDiv.style.whiteSpace = 'nowrap';
  1742. customDiv.style.marginLeft = '4px';
  1743. customDiv.style.fontSize = '12px';
  1744. customDiv.style.width = '210px';
  1745. customDiv.style.height = '24px';
  1746. var widthInput = document.createElement('input');
  1747. widthInput.setAttribute('size', '7');
  1748. widthInput.setAttribute('title', mxResources.get('width'));
  1749. widthInput.style.textAlign = 'right';
  1750. customDiv.appendChild(widthInput);
  1751. mxUtils.write(customDiv, ' x ');
  1752. var heightInput = document.createElement('input');
  1753. heightInput.setAttribute('size', '7');
  1754. heightInput.setAttribute('title', mxResources.get('height'));
  1755. heightInput.style.textAlign = 'right';
  1756. customDiv.appendChild(heightInput);
  1757. var unitSelect = document.createElement('select');
  1758. unitSelect.style.marginLeft = '4px';
  1759. unitSelect.style.maxWidth = '78px';
  1760. unitSelect.style.width = '78px';
  1761. var units = [{label: mxResources.get('points'), unit: mxConstants.POINTS},
  1762. {label: mxResources.get('inches'), unit: mxConstants.INCHES},
  1763. {label: mxResources.get('millimeters'), unit: mxConstants.MILLIMETERS}];
  1764. for (var i = 0; i < units.length; i++)
  1765. {
  1766. var unitOption = document.createElement('option');
  1767. unitOption.setAttribute('value', units[i].unit);
  1768. mxUtils.write(unitOption, units[i].label);
  1769. unitSelect.appendChild(unitOption);
  1770. }
  1771. unitSelect.value = Editor.pageSizeUnit;
  1772. customDiv.appendChild(unitSelect);
  1773. formatDiv.style.display = 'none';
  1774. customDiv.style.display = 'none';
  1775. var pf = new Object();
  1776. var formats = PageSetupDialog.getFormats();
  1777. for (var i = 0; i < formats.length; i++)
  1778. {
  1779. var f = formats[i];
  1780. pf[f.key] = f;
  1781. var paperSizeOption = document.createElement('option');
  1782. paperSizeOption.setAttribute('value', f.key);
  1783. mxUtils.write(paperSizeOption, f.title);
  1784. paperSizeSelect.appendChild(paperSizeOption);
  1785. }
  1786. var listener = function()
  1787. {
  1788. paperSizeSelect.value = 'custom';
  1789. for (var i = 0; i < formats.length; i++)
  1790. {
  1791. var f = formats[i];
  1792. if (f.format != null)
  1793. {
  1794. // Fixes wrong values for previous A4 and A5 page sizes
  1795. if (f.key == 'a4')
  1796. {
  1797. if (pageFormat.width == 826)
  1798. {
  1799. pageFormat = mxRectangle.fromRectangle(pageFormat);
  1800. pageFormat.width = 827;
  1801. }
  1802. else if (pageFormat.height == 826)
  1803. {
  1804. pageFormat = mxRectangle.fromRectangle(pageFormat);
  1805. pageFormat.height = 827;
  1806. }
  1807. }
  1808. else if (f.key == 'a5')
  1809. {
  1810. if (pageFormat.width == 584)
  1811. {
  1812. pageFormat = mxRectangle.fromRectangle(pageFormat);
  1813. pageFormat.width = 583;
  1814. }
  1815. else if (pageFormat.height == 584)
  1816. {
  1817. pageFormat = mxRectangle.fromRectangle(pageFormat);
  1818. pageFormat.height = 583;
  1819. }
  1820. }
  1821. if (pageFormat.width == f.format.width && pageFormat.height == f.format.height)
  1822. {
  1823. paperSizeSelect.value = f.key;
  1824. portraitCheckBox.setAttribute('checked', 'checked');
  1825. portraitCheckBox.defaultChecked = true;
  1826. portraitCheckBox.checked = true;
  1827. landscapeCheckBox.removeAttribute('checked');
  1828. landscapeCheckBox.defaultChecked = false;
  1829. landscapeCheckBox.checked = false;
  1830. detected = true;
  1831. }
  1832. else if (pageFormat.width == f.format.height && pageFormat.height == f.format.width)
  1833. {
  1834. paperSizeSelect.value = f.key;
  1835. portraitCheckBox.removeAttribute('checked');
  1836. portraitCheckBox.defaultChecked = false;
  1837. portraitCheckBox.checked = false;
  1838. landscapeCheckBox.setAttribute('checked', 'checked');
  1839. landscapeCheckBox.defaultChecked = true;
  1840. landscapeCheckBox.checked = true;
  1841. detected = true;
  1842. }
  1843. }
  1844. }
  1845. // Selects custom format which is last in list
  1846. if (paperSizeSelect.value == 'custom')
  1847. {
  1848. widthInput.value = Editor.toUnit(pageFormat.width, unitSelect.value);
  1849. heightInput.value = Editor.toUnit(pageFormat.height, unitSelect.value);
  1850. portraitCheckBox.setAttribute('checked', 'checked');
  1851. paperSizeSelect.value = 'custom';
  1852. formatDiv.style.display = 'none';
  1853. customDiv.style.display = '';
  1854. }
  1855. else
  1856. {
  1857. formatDiv.style.display = '';
  1858. customDiv.style.display = 'none';
  1859. }
  1860. };
  1861. listener();
  1862. div.appendChild(paperSizeSelect);
  1863. mxUtils.br(div);
  1864. div.appendChild(formatDiv);
  1865. div.appendChild(customDiv);
  1866. var update = function(evt, quiet)
  1867. {
  1868. var f = pf[paperSizeSelect.value];
  1869. if (f.format != null)
  1870. {
  1871. widthInput.value = Editor.toUnit((!landscapeCheckBox.checked) ?
  1872. f.format.width : f.format.height, unitSelect.value);
  1873. heightInput.value = Editor.toUnit((!landscapeCheckBox.checked) ?
  1874. f.format.height : f.format.width, unitSelect.value);
  1875. customDiv.style.display = 'none';
  1876. formatDiv.style.display = '';
  1877. }
  1878. else
  1879. {
  1880. formatDiv.style.display = 'none';
  1881. customDiv.style.display = '';
  1882. }
  1883. var wi = parseFloat(widthInput.value);
  1884. if (isNaN(wi) || wi <= 0)
  1885. {
  1886. widthInput.value = Editor.toUnit(pageFormat.width, unitSelect.value);
  1887. }
  1888. var hi = parseFloat(heightInput.value);
  1889. if (isNaN(hi) || hi <= 0)
  1890. {
  1891. heightInput.value = Editor.toUnit(pageFormat.height, unitSelect.value);
  1892. }
  1893. var newPageFormat = new mxRectangle(0, 0,
  1894. Math.floor(Editor.fromUnit(parseFloat(widthInput.value), unitSelect.value)),
  1895. Math.floor(Editor.fromUnit(parseFloat(heightInput.value), unitSelect.value)));
  1896. // Initial select of custom should not update page format to avoid update of combo
  1897. if (!quiet && (newPageFormat.width != pageFormat.width ||
  1898. newPageFormat.height != pageFormat.height))
  1899. {
  1900. pageFormat = newPageFormat;
  1901. // Updates page format and reloads format panel
  1902. if (pageFormatListener != null)
  1903. {
  1904. pageFormatListener(pageFormat);
  1905. }
  1906. }
  1907. };
  1908. mxEvent.addListener(portraitSpan, 'click', function(evt)
  1909. {
  1910. portraitCheckBox.checked = true;
  1911. update(evt);
  1912. mxEvent.consume(evt);
  1913. });
  1914. mxEvent.addListener(landscapeSpan, 'click', function(evt)
  1915. {
  1916. landscapeCheckBox.checked = true;
  1917. update(evt);
  1918. mxEvent.consume(evt);
  1919. });
  1920. mxEvent.addListener(widthInput, 'blur', update);
  1921. mxEvent.addListener(widthInput, 'click', update);
  1922. mxEvent.addListener(heightInput, 'blur', update);
  1923. mxEvent.addListener(heightInput, 'click', update);
  1924. mxEvent.addListener(landscapeCheckBox, 'change', update);
  1925. mxEvent.addListener(portraitCheckBox, 'change', update);
  1926. mxEvent.addListener(paperSizeSelect, 'change', function(evt)
  1927. {
  1928. update(evt, paperSizeSelect.value == 'custom');
  1929. mxEvent.consume(evt);
  1930. });
  1931. mxEvent.addListener(unitSelect, 'change', function(evt)
  1932. {
  1933. widthInput.value = Editor.toUnit(Editor.fromUnit(widthInput.value, Editor.pageSizeUnit), unitSelect.value);
  1934. heightInput.value = Editor.toUnit(Editor.fromUnit(heightInput.value, Editor.pageSizeUnit), unitSelect.value);
  1935. Editor.pageSizeUnit = unitSelect.value;
  1936. update(evt, true);
  1937. mxEvent.consume(evt);
  1938. });
  1939. update(null, true);
  1940. return {set: function(value)
  1941. {
  1942. pageFormat = value;
  1943. listener(null, null, true);
  1944. },get: function()
  1945. {
  1946. return pageFormat;
  1947. }, widthInput: widthInput,
  1948. heightInput: heightInput};
  1949. };
  1950. /**
  1951. *
  1952. */
  1953. PageSetupDialog.getFormats = function()
  1954. {
  1955. return [{key: 'letter', title: 'US-Letter (8,5" x 11")', format: mxConstants.PAGE_FORMAT_LETTER_PORTRAIT},
  1956. {key: 'legal', title: 'US-Legal (8,5" x 14")', format: new mxRectangle(0, 0, 850, 1400)},
  1957. {key: 'tabloid', title: 'US-Tabloid (11" x 17")', format: new mxRectangle(0, 0, 1100, 1700)},
  1958. {key: 'executive', title: 'US-Executive (7" x 10")', format: new mxRectangle(0, 0, 700, 1000)},
  1959. {key: 'a0', title: 'A0 (841 mm x 1189 mm)', format: new mxRectangle(0, 0, 3300, 4681)},
  1960. {key: 'a1', title: 'A1 (594 mm x 841 mm)', format: new mxRectangle(0, 0, 2339, 3300)},
  1961. {key: 'a2', title: 'A2 (420 mm x 594 mm)', format: new mxRectangle(0, 0, 1654, 2336)},
  1962. {key: 'a3', title: 'A3 (297 mm x 420 mm)', format: new mxRectangle(0, 0, 1169, 1654)},
  1963. {key: 'a4', title: 'A4 (210 mm x 297 mm)', format: mxConstants.PAGE_FORMAT_A4_PORTRAIT},
  1964. {key: 'a5', title: 'A5 (148 mm x 210 mm)', format: new mxRectangle(0, 0, 583, 827)},
  1965. {key: 'a6', title: 'A6 (105 mm x 148 mm)', format: new mxRectangle(0, 0, 413, 583)},
  1966. {key: 'a7', title: 'A7 (74 mm x 105 mm)', format: new mxRectangle(0, 0, 291, 413)},
  1967. {key: 'b4', title: 'B4 (250 mm x 353 mm)', format: new mxRectangle(0, 0, 980, 1390)},
  1968. {key: 'b5', title: 'B5 (176 mm x 250 mm)', format: new mxRectangle(0, 0, 690, 980)},
  1969. {key: '16-9', title: '16:9 (1600 x 900)', format: new mxRectangle(0, 0, 900, 1600)},
  1970. {key: '16-10', title: '16:10 (1920 x 1200)', format: new mxRectangle(0, 0, 1200, 1920)},
  1971. {key: '4-3', title: '4:3 (1600 x 1200)', format: new mxRectangle(0, 0, 1200, 1600)},
  1972. {key: 'custom', title: mxResources.get('custom'), format: null}];
  1973. };
  1974. /**
  1975. * Constructs a new filename dialog.
  1976. */
  1977. var FilenameDialog = function(editorUi, filename, buttonText, fn, label,
  1978. validateFn, content, helpLink, closeOnBtn, cancelFn, hints)
  1979. {
  1980. closeOnBtn = (closeOnBtn != null) ? closeOnBtn : true;
  1981. var container = document.createElement('div');
  1982. var table = document.createElement('div');
  1983. table.style.width = '100%';
  1984. table.style.display = 'grid';
  1985. table.style.gap = '5px 8px';
  1986. table.style.gridAutoColumns = 'auto 1fr';
  1987. // Workaround for clipped focus rectangle in FF
  1988. table.style.boxSizing = 'border-box';
  1989. table.style.padding = '3px';
  1990. var td = document.createElement('div');
  1991. td.style.display = 'inline-flex';
  1992. td.style.alignItems = 'center';
  1993. td.style.justifyContent = 'flex-end';
  1994. td.style.minWidth = '0';
  1995. // Wrapper for text ellipsis
  1996. var wrapper = document.createElement('div');
  1997. wrapper.style.display = 'inline-block';
  1998. wrapper.style.textOverflow = 'ellipsis';
  1999. wrapper.style.whiteSpace = 'nowrap';
  2000. wrapper.style.overflow = 'hidden';
  2001. wrapper.style.fontSize = '10pt';
  2002. // Workaround for vertical clipping
  2003. wrapper.style.padding = '2px 0';
  2004. wrapper.setAttribute('title', label || mxResources.get('filename'));
  2005. mxUtils.write(wrapper, (label || mxResources.get('filename')) + ':');
  2006. td.appendChild(wrapper);
  2007. table.appendChild(td);
  2008. var nameInput = document.createElement('input');
  2009. nameInput.setAttribute('value', filename || '');
  2010. nameInput.style.flexGrow = '1';
  2011. var genericBtn = mxUtils.button(buttonText, function()
  2012. {
  2013. if (validateFn == null || validateFn(nameInput.value))
  2014. {
  2015. if (closeOnBtn)
  2016. {
  2017. editorUi.hideDialog();
  2018. }
  2019. fn(nameInput.value);
  2020. }
  2021. });
  2022. genericBtn.className = 'geBtn gePrimaryBtn';
  2023. this.init = function()
  2024. {
  2025. if (label == null && content != null)
  2026. {
  2027. return;
  2028. }
  2029. if (hints != null)
  2030. {
  2031. Editor.selectFilename(nameInput);
  2032. }
  2033. else
  2034. {
  2035. nameInput.focus();
  2036. if (mxClient.IS_GC || mxClient.IS_FF || document.documentMode >= 5)
  2037. {
  2038. nameInput.select();
  2039. }
  2040. else
  2041. {
  2042. document.execCommand('selectAll', false, null);
  2043. }
  2044. }
  2045. // Installs drag and drop handler for links
  2046. if (Graph.fileSupport)
  2047. {
  2048. // Setup the dnd listeners
  2049. var dlg = table.parentNode;
  2050. if (dlg != null)
  2051. {
  2052. var dropElt = null;
  2053. mxEvent.addListener(dlg, 'dragleave', function(evt)
  2054. {
  2055. if (dropElt != null)
  2056. {
  2057. dropElt.style.backgroundColor = '';
  2058. dropElt = null;
  2059. }
  2060. evt.stopPropagation();
  2061. evt.preventDefault();
  2062. });
  2063. mxEvent.addListener(dlg, 'dragover', mxUtils.bind(this, function(evt)
  2064. {
  2065. // IE 10 does not implement pointer-events so it can't have a drop highlight
  2066. if (dropElt == null && (!mxClient.IS_IE || document.documentMode > 10))
  2067. {
  2068. dropElt = nameInput;
  2069. dropElt.style.backgroundColor = '#ebf2f9';
  2070. }
  2071. evt.stopPropagation();
  2072. evt.preventDefault();
  2073. }));
  2074. mxEvent.addListener(dlg, 'drop', mxUtils.bind(this, function(evt)
  2075. {
  2076. if (dropElt != null)
  2077. {
  2078. dropElt.style.backgroundColor = '';
  2079. dropElt = null;
  2080. }
  2081. if (mxUtils.indexOf(evt.dataTransfer.types, 'text/uri-list') >= 0)
  2082. {
  2083. nameInput.value = decodeURIComponent(evt.dataTransfer.getData('text/uri-list'));
  2084. genericBtn.click();
  2085. }
  2086. evt.stopPropagation();
  2087. evt.preventDefault();
  2088. }));
  2089. }
  2090. }
  2091. };
  2092. td = document.createElement('div');
  2093. td.style.display = 'inline-flex';
  2094. td.style.alignItems = 'center';
  2095. td.style.gap = '4px';
  2096. td.style.whiteSpace = 'nowrap';
  2097. td.appendChild(nameInput);
  2098. table.appendChild(td);
  2099. if (label != null || content == null)
  2100. {
  2101. if (hints != null && editorUi.editor.diagramFileTypes != null)
  2102. {
  2103. td = document.createElement('div');
  2104. td.style.display = 'inline-flex';
  2105. td.style.alignItems = 'center';
  2106. td.style.textOverflow = 'ellipsis';
  2107. td.style.justifyContent = 'flex-end';
  2108. td.style.fontSize = '10pt';
  2109. mxUtils.write(td, mxResources.get('type') + ':');
  2110. table.appendChild(td);
  2111. td = document.createElement('div');
  2112. td.style.display = 'inline-flex';
  2113. td.style.alignItems = 'center';
  2114. td.style.whiteSpace = 'nowrap';
  2115. table.appendChild(td);
  2116. var typeSelect = FilenameDialog.createFileTypes(editorUi,
  2117. nameInput, editorUi.editor.diagramFileTypes);
  2118. typeSelect.style.width = '100%';
  2119. td.appendChild(typeSelect);
  2120. }
  2121. }
  2122. if (content != null)
  2123. {
  2124. table.appendChild(content);
  2125. }
  2126. row = document.createElement('div');
  2127. row.style.gridColumn = '1 / span 2';
  2128. row.style.paddingTop = (hints != null) ? '6px' : '14px';
  2129. row.style.whiteSpace = 'nowrap';
  2130. row.style.display = 'inline-flex';
  2131. row.style.alignItems = 'center';
  2132. row.style.justifyContent = 'flex-end';
  2133. row.setAttribute('align', 'right');
  2134. var cancelBtn = mxUtils.button(mxResources.get('cancel'), function()
  2135. {
  2136. editorUi.hideDialog();
  2137. if (cancelFn != null)
  2138. {
  2139. cancelFn();
  2140. }
  2141. });
  2142. cancelBtn.className = 'geBtn';
  2143. if (helpLink != null && !editorUi.isOffline())
  2144. {
  2145. row.appendChild(editorUi.createHelpIcon(helpLink));
  2146. }
  2147. if (editorUi.editor.cancelFirst)
  2148. {
  2149. row.appendChild(cancelBtn);
  2150. }
  2151. mxEvent.addListener(nameInput, 'keypress', function(e)
  2152. {
  2153. if (e.keyCode == 13)
  2154. {
  2155. genericBtn.click();
  2156. }
  2157. });
  2158. row.appendChild(genericBtn);
  2159. if (!editorUi.editor.cancelFirst)
  2160. {
  2161. row.appendChild(cancelBtn);
  2162. }
  2163. table.appendChild(row);
  2164. container.appendChild(table);
  2165. this.container = container;
  2166. };
  2167. /**
  2168. *
  2169. */
  2170. FilenameDialog.filenameHelpLink = null;
  2171. /**
  2172. *
  2173. */
  2174. FilenameDialog.createFileTypes = function(editorUi, nameInput, types)
  2175. {
  2176. var typeSelect = document.createElement('select');
  2177. for (var i = 0; i < types.length; i++)
  2178. {
  2179. var typeOption = document.createElement('option');
  2180. typeOption.setAttribute('value', i);
  2181. mxUtils.write(typeOption, mxResources.get(types[i].description) +
  2182. ' (.' + types[i].extension + ')');
  2183. typeSelect.appendChild(typeOption);
  2184. }
  2185. mxEvent.addListener(typeSelect, 'change', function(evt)
  2186. {
  2187. var ext = types[typeSelect.value].extension;
  2188. var idx2 = nameInput.value.lastIndexOf('.drawio.');
  2189. var idx = (idx2 > 0) ? idx2 : nameInput.value.lastIndexOf('.');
  2190. if (ext != 'drawio')
  2191. {
  2192. ext = 'drawio.' + ext;
  2193. }
  2194. if (idx > 0)
  2195. {
  2196. nameInput.value = nameInput.value.substring(0, idx + 1) + ext;
  2197. }
  2198. else
  2199. {
  2200. nameInput.value = nameInput.value + '.' + ext;
  2201. }
  2202. if ('createEvent' in document)
  2203. {
  2204. var changeEvent = document.createEvent('HTMLEvents');
  2205. changeEvent.initEvent('change', false, true);
  2206. nameInput.dispatchEvent(changeEvent);
  2207. }
  2208. else
  2209. {
  2210. nameInput.fireEvent('onchange');
  2211. }
  2212. });
  2213. var nameInputChanged = function(evt)
  2214. {
  2215. var name = nameInput.value.toLowerCase();
  2216. var active = 0;
  2217. // Finds current extension
  2218. for (var i = 0; i < types.length; i++)
  2219. {
  2220. var ext = types[i].extension;
  2221. var subExt = null;
  2222. if (ext != 'drawio')
  2223. {
  2224. subExt = ext;
  2225. ext = '.drawio.' + ext;
  2226. }
  2227. if (name.substring(name.length - ext.length - 1) == '.' + ext ||
  2228. (subExt != null && name.substring(name.length - subExt.length - 1) == '.' + subExt))
  2229. {
  2230. active = i;
  2231. break;
  2232. }
  2233. }
  2234. typeSelect.value = active;
  2235. };
  2236. mxEvent.addListener(nameInput, 'change', nameInputChanged);
  2237. mxEvent.addListener(nameInput, 'keyup', nameInputChanged);
  2238. nameInputChanged();
  2239. return typeSelect;
  2240. };
  2241. /**
  2242. *
  2243. */
  2244. var WrapperWindow = function(editorUi, title, x, y, w, h, fn)
  2245. {
  2246. var div = editorUi.createSidebarContainer();
  2247. fn(div);
  2248. this.window = new mxWindow(title, div, x, y, w, h, true, true);
  2249. this.window.destroyOnClose = false;
  2250. this.window.setMaximizable(false);
  2251. this.window.setResizable(true);
  2252. this.window.setClosable(true);
  2253. this.window.setVisible(true);
  2254. editorUi.installResizeHandler(this, true);
  2255. // Workaround for text selection starting in Safari
  2256. // when dragging shapes outside of window
  2257. if (mxClient.IS_SF)
  2258. {
  2259. this.window.div.onselectstart = mxUtils.bind(this, function(evt)
  2260. {
  2261. if (evt == null)
  2262. {
  2263. evt = window.event;
  2264. }
  2265. return (evt != null && editorUi.isSelectionAllowed(evt));
  2266. });
  2267. }
  2268. };
  2269. /**
  2270. * Static overrides
  2271. */
  2272. (function()
  2273. {
  2274. // Uses HTML for background pages (to support grid background image)
  2275. mxGraphView.prototype.validateBackgroundPage = function()
  2276. {
  2277. var graph = this.graph;
  2278. if (graph.container != null && !graph.transparentBackground)
  2279. {
  2280. if (graph.pageVisible)
  2281. {
  2282. var bounds = this.getBackgroundPageBounds();
  2283. if (this.backgroundPageShape == null)
  2284. {
  2285. // Finds first element in graph container
  2286. var firstChild = graph.container.firstChild;
  2287. while (firstChild != null && firstChild.nodeType != mxConstants.NODETYPE_ELEMENT)
  2288. {
  2289. firstChild = firstChild.nextSibling;
  2290. }
  2291. if (firstChild != null)
  2292. {
  2293. this.backgroundPageShape = this.createBackgroundPageShape(bounds);
  2294. this.backgroundPageShape.scale = 1;
  2295. // IE8 standards has known rendering issues inside mxWindow but not using shadow is worse.
  2296. this.backgroundPageShape.isShadow = true;
  2297. this.backgroundPageShape.dialect = mxConstants.DIALECT_STRICTHTML;
  2298. this.backgroundPageShape.init(graph.container);
  2299. // Required for the browser to render the background page in correct order
  2300. firstChild.style.position = 'absolute';
  2301. graph.container.insertBefore(this.backgroundPageShape.node, firstChild);
  2302. this.backgroundPageShape.redraw();
  2303. this.backgroundPageShape.node.className = 'geBackgroundPage';
  2304. // Adds listener for double click handling on background
  2305. mxEvent.addListener(this.backgroundPageShape.node, 'dblclick',
  2306. mxUtils.bind(this, function(evt)
  2307. {
  2308. graph.dblClick(evt);
  2309. })
  2310. );
  2311. // Adds basic listeners for graph event dispatching outside of the
  2312. // container and finishing the handling of a single gesture
  2313. mxEvent.addGestureListeners(this.backgroundPageShape.node,
  2314. mxUtils.bind(this, function(evt)
  2315. {
  2316. graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt));
  2317. }),
  2318. mxUtils.bind(this, function(evt)
  2319. {
  2320. // Hides the tooltip if mouse is outside container
  2321. if (graph.tooltipHandler != null && graph.tooltipHandler.isHideOnHover())
  2322. {
  2323. graph.tooltipHandler.hide();
  2324. }
  2325. if (graph.isMouseDown && !mxEvent.isConsumed(evt))
  2326. {
  2327. graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt));
  2328. }
  2329. }),
  2330. mxUtils.bind(this, function(evt)
  2331. {
  2332. graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt));
  2333. })
  2334. );
  2335. }
  2336. }
  2337. else
  2338. {
  2339. this.backgroundPageShape.scale = 1;
  2340. this.backgroundPageShape.bounds = bounds;
  2341. this.backgroundPageShape.redraw();
  2342. }
  2343. }
  2344. else if (this.backgroundPageShape != null)
  2345. {
  2346. this.backgroundPageShape.destroy();
  2347. this.backgroundPageShape = null;
  2348. }
  2349. this.validateBackgroundStyles();
  2350. }
  2351. };
  2352. // Updates the CSS of the background to draw the grid
  2353. mxGraphView.prototype.validateBackgroundStyles = function(factor, cx, cy)
  2354. {
  2355. var graph = this.graph;
  2356. factor = (factor != null) ? factor : 1;
  2357. var color = (graph.background == null || graph.background == mxConstants.NONE) ?
  2358. graph.defaultPageBackgroundColor : graph.background;
  2359. var gridColor = (color != null && this.gridColor != color.toLowerCase()) ? this.gridColor : '#ffffff';
  2360. var image = 'none';
  2361. var position = '';
  2362. if (graph.isGridEnabled() || graph.gridVisible)
  2363. {
  2364. var phase = 10;
  2365. if (mxClient.IS_SVG)
  2366. {
  2367. // Generates the SVG required for drawing the dynamic grid
  2368. image = unescape(encodeURIComponent(this.createSvgGrid(gridColor, factor)));
  2369. image = (window.btoa) ? btoa(image) : Base64.encode(image, true);
  2370. image = 'url(' + 'data:image/svg+xml;base64,' + image + ')'
  2371. phase = graph.gridSize * this.scale * this.gridSteps * factor;
  2372. }
  2373. else
  2374. {
  2375. // Fallback to grid wallpaper with fixed size
  2376. image = 'url(' + this.gridImage + ')';
  2377. }
  2378. var x0 = 0;
  2379. var y0 = 0;
  2380. var dx = (cx != null) ? cx - this.translate.x * this.scale : 0;
  2381. var dy = (cy != null) ? cy - this.translate.y * this.scale : 0;
  2382. var p = graph.gridSize * this.scale * this.gridSteps;
  2383. var ddx = dx % p;
  2384. var ddy = dy % p;
  2385. if (graph.view.backgroundPageShape != null)
  2386. {
  2387. var bds = this.getBackgroundPageBounds();
  2388. x0 = 1 + bds.x;
  2389. y0 = 1 + bds.y;
  2390. }
  2391. // Computes the offset to maintain origin for grid
  2392. position = -Math.round(phase - mxUtils.mod(this.translate.x * this.scale - x0 + dx, phase) + ddx * factor) + 'px ' +
  2393. -Math.round(phase - mxUtils.mod(this.translate.y * this.scale - y0 + dy, phase) + ddy * factor) + 'px';
  2394. }
  2395. var canvas = graph.view.canvas;
  2396. if (canvas.ownerSVGElement != null)
  2397. {
  2398. canvas = canvas.ownerSVGElement;
  2399. }
  2400. var useDiagramBackground = !Editor.isDarkMode() && graph.enableDiagramBackground;
  2401. if (graph.view.backgroundPageShape != null)
  2402. {
  2403. graph.view.backgroundPageShape.node.style.backgroundPosition = position;
  2404. graph.view.backgroundPageShape.node.style.backgroundImage = image;
  2405. graph.view.backgroundPageShape.node.style.backgroundColor = color;
  2406. graph.view.backgroundPageShape.node.style.borderColor = graph.defaultPageBorderColor;
  2407. graph.container.classList.add('geDiagramBackdrop');
  2408. canvas.style.backgroundImage = 'none';
  2409. canvas.style.backgroundColor = '';
  2410. if (useDiagramBackground)
  2411. {
  2412. graph.container.style.backgroundColor = graph.diagramBackgroundColor;
  2413. }
  2414. else
  2415. {
  2416. graph.container.style.backgroundColor = '';
  2417. }
  2418. }
  2419. else
  2420. {
  2421. graph.container.classList.remove('geDiagramBackdrop');
  2422. canvas.style.backgroundPosition = position;
  2423. canvas.style.backgroundImage = image;
  2424. if (useDiagramBackground && (graph.background == null ||
  2425. graph.background == mxConstants.NONE))
  2426. {
  2427. canvas.style.backgroundColor = graph.diagramBackgroundColor;
  2428. graph.container.style.backgroundColor = '';
  2429. }
  2430. else
  2431. {
  2432. canvas.style.backgroundColor = color;
  2433. }
  2434. }
  2435. };
  2436. // Returns the SVG required for painting the background grid.
  2437. mxGraphView.prototype.createSvgGrid = function(color, factor)
  2438. {
  2439. factor = (factor != null) ? factor : 1;
  2440. var tmp = this.graph.gridSize * this.scale * factor;
  2441. while (tmp < this.minGridSize)
  2442. {
  2443. tmp *= 2;
  2444. }
  2445. var tmp2 = this.gridSteps * tmp;
  2446. // Small grid lines
  2447. var d = [];
  2448. for (var i = 1; i < this.gridSteps; i++)
  2449. {
  2450. var tmp3 = i * tmp;
  2451. d.push('M 0 ' + tmp3 + ' L ' + tmp2 + ' ' + tmp3 + ' M ' + tmp3 + ' 0 L ' + tmp3 + ' ' + tmp2);
  2452. }
  2453. // KNOWN: Rounding errors for certain scales (eg. 144%, 121% in Chrome, FF and Safari). Workaround
  2454. // in Chrome is to use 100% for the svg size, but this results in blurred grid for large diagrams.
  2455. var size = tmp2;
  2456. var svg = '<svg width="' + size + '" height="' + size + '" xmlns="' + mxConstants.NS_SVG + '">' +
  2457. '<defs><pattern id="grid" width="' + tmp2 + '" height="' + tmp2 + '" patternUnits="userSpaceOnUse">' +
  2458. '<path d="' + d.join(' ') + '" fill="none" stroke="' + color + '" opacity="0.2" stroke-width="1"/>' +
  2459. '<path d="M ' + tmp2 + ' 0 L 0 0 0 ' + tmp2 + '" fill="none" stroke="' + color + '" stroke-width="1"/>' +
  2460. '</pattern></defs><rect width="100%" height="100%" fill="url(#grid)"/></svg>';
  2461. return svg;
  2462. };
  2463. // Adds panning for the grid with no page view and disabled scrollbars
  2464. var mxGraphPanGraph = mxGraph.prototype.panGraph;
  2465. mxGraph.prototype.panGraph = function(dx, dy)
  2466. {
  2467. mxGraphPanGraph.apply(this, arguments);
  2468. if (this.shiftPreview1 != null)
  2469. {
  2470. var canvas = this.view.canvas;
  2471. if (canvas.ownerSVGElement != null)
  2472. {
  2473. canvas = canvas.ownerSVGElement;
  2474. }
  2475. var phase = this.gridSize * this.view.scale * this.view.gridSteps;
  2476. var position = -Math.round(phase - mxUtils.mod(this.view.translate.x * this.view.scale + dx, phase)) + 'px ' +
  2477. -Math.round(phase - mxUtils.mod(this.view.translate.y * this.view.scale + dy, phase)) + 'px';
  2478. canvas.style.backgroundPosition = position;
  2479. }
  2480. };
  2481. // Draws page breaks only within the page
  2482. mxGraph.prototype.updatePageBreaks = function(visible, width, height)
  2483. {
  2484. var scale = this.view.scale;
  2485. var tr = this.view.translate;
  2486. var fmt = this.pageFormat;
  2487. var ps = scale * this.pageScale;
  2488. var bounds2 = this.view.getBackgroundPageBounds();
  2489. width = bounds2.width;
  2490. height = bounds2.height;
  2491. var bounds = new mxRectangle(scale * tr.x, scale * tr.y, fmt.width * ps, fmt.height * ps);
  2492. // Does not show page breaks if the scale is too small
  2493. visible = visible && Math.min(bounds.width, bounds.height) > this.minPageBreakDist;
  2494. var horizontalCount = (visible) ? Math.ceil(height / bounds.height) - 1 : 0;
  2495. var verticalCount = (visible) ? Math.ceil(width / bounds.width) - 1 : 0;
  2496. var right = bounds2.x + width;
  2497. var bottom = bounds2.y + height;
  2498. if (this.horizontalPageBreaks == null && horizontalCount > 0)
  2499. {
  2500. this.horizontalPageBreaks = [];
  2501. }
  2502. if (this.verticalPageBreaks == null && verticalCount > 0)
  2503. {
  2504. this.verticalPageBreaks = [];
  2505. }
  2506. var drawPageBreaks = mxUtils.bind(this, function(breaks)
  2507. {
  2508. if (breaks != null)
  2509. {
  2510. var count = (breaks == this.horizontalPageBreaks) ? horizontalCount : verticalCount;
  2511. for (var i = 0; i <= count; i++)
  2512. {
  2513. var pts = (breaks == this.horizontalPageBreaks) ?
  2514. [new mxPoint(Math.round(bounds2.x), Math.round(bounds2.y + (i + 1) * bounds.height)),
  2515. new mxPoint(Math.round(right), Math.round(bounds2.y + (i + 1) * bounds.height))] :
  2516. [new mxPoint(Math.round(bounds2.x + (i + 1) * bounds.width), Math.round(bounds2.y)),
  2517. new mxPoint(Math.round(bounds2.x + (i + 1) * bounds.width), Math.round(bottom))];
  2518. if (breaks[i] != null)
  2519. {
  2520. breaks[i].points = pts;
  2521. breaks[i].redraw();
  2522. }
  2523. else
  2524. {
  2525. var pageBreak = new mxPolyline(pts, this.pageBreakColor);
  2526. pageBreak.dialect = this.dialect;
  2527. pageBreak.isDashed = this.pageBreakDashed;
  2528. pageBreak.pointerEvents = false;
  2529. pageBreak.init(this.view.backgroundPane);
  2530. pageBreak.redraw();
  2531. breaks[i] = pageBreak;
  2532. }
  2533. }
  2534. for (var i = count; i < breaks.length; i++)
  2535. {
  2536. if (breaks[i] != null)
  2537. {
  2538. breaks[i].destroy();
  2539. }
  2540. }
  2541. breaks.splice(count, breaks.length - count);
  2542. }
  2543. });
  2544. drawPageBreaks(this.horizontalPageBreaks);
  2545. drawPageBreaks(this.verticalPageBreaks);
  2546. };
  2547. // Disables removing relative children and table rows and cells from parents
  2548. var mxGraphHandlerShouldRemoveCellsFromParent = mxGraphHandler.prototype.shouldRemoveCellsFromParent;
  2549. mxGraphHandler.prototype.shouldRemoveCellsFromParent = function(parent, cells, evt)
  2550. {
  2551. for (var i = 0; i < cells.length; i++)
  2552. {
  2553. if (this.graph.isTableCell(cells[i]) || this.graph.isTableRow(cells[i]))
  2554. {
  2555. return false;
  2556. }
  2557. else if (this.graph.getModel().isVertex(cells[i]))
  2558. {
  2559. var geo = this.graph.getCellGeometry(cells[i]);
  2560. if (geo != null && geo.relative)
  2561. {
  2562. return false;
  2563. }
  2564. }
  2565. }
  2566. return mxGraphHandlerShouldRemoveCellsFromParent.apply(this, arguments);
  2567. };
  2568. // Overrides to ignore hotspot only for target terminal
  2569. var mxConnectionHandlerCreateMarker = mxConnectionHandler.prototype.createMarker;
  2570. mxConnectionHandler.prototype.createMarker = function()
  2571. {
  2572. var marker = mxConnectionHandlerCreateMarker.apply(this, arguments);
  2573. marker.intersects = mxUtils.bind(this, function(state, evt)
  2574. {
  2575. if (this.isConnecting())
  2576. {
  2577. return true;
  2578. }
  2579. return mxCellMarker.prototype.intersects.apply(marker, arguments);
  2580. });
  2581. return marker;
  2582. };
  2583. // Creates background page shape
  2584. mxGraphView.prototype.createBackgroundPageShape = function(bounds)
  2585. {
  2586. return new mxRectangleShape(bounds, '#ffffff', this.graph.defaultPageBorderColor);
  2587. };
  2588. // Fits the number of background pages to the graph
  2589. mxGraphView.prototype.getBackgroundPageBounds = function()
  2590. {
  2591. var gb = this.getGraphBounds();
  2592. // Computes unscaled, untranslated graph bounds
  2593. var x = (gb.width > 0) ? gb.x / this.scale - this.translate.x : 0;
  2594. var y = (gb.height > 0) ? gb.y / this.scale - this.translate.y : 0;
  2595. var w = gb.width / this.scale;
  2596. var h = gb.height / this.scale;
  2597. var fmt = this.graph.pageFormat;
  2598. var ps = this.graph.pageScale;
  2599. var pw = fmt.width * ps;
  2600. var ph = fmt.height * ps;
  2601. var x0 = Math.floor(Math.min(0, x) / pw);
  2602. var y0 = Math.floor(Math.min(0, y) / ph);
  2603. var xe = Math.ceil(Math.max(1, x + w) / pw);
  2604. var ye = Math.ceil(Math.max(1, y + h) / ph);
  2605. var rows = xe - x0;
  2606. var cols = ye - y0;
  2607. var bounds = new mxRectangle(this.scale * (this.translate.x + x0 * pw), this.scale *
  2608. (this.translate.y + y0 * ph), this.scale * rows * pw, this.scale * cols * ph);
  2609. return bounds;
  2610. };
  2611. // Add panning for background page in VML
  2612. var graphPanGraph = mxGraph.prototype.panGraph;
  2613. mxGraph.prototype.panGraph = function(dx, dy)
  2614. {
  2615. graphPanGraph.apply(this, arguments);
  2616. if ((this.dialect != mxConstants.DIALECT_SVG && this.view.backgroundPageShape != null) &&
  2617. (!this.useScrollbarsForPanning || !mxUtils.hasScrollbars(this.container)))
  2618. {
  2619. this.view.backgroundPageShape.node.style.marginLeft = dx + 'px';
  2620. this.view.backgroundPageShape.node.style.marginTop = dy + 'px';
  2621. }
  2622. };
  2623. /**
  2624. * Consumes click events for disabled menu items.
  2625. */
  2626. var mxPopupMenuAddItem = mxPopupMenu.prototype.addItem;
  2627. mxPopupMenu.prototype.addItem = function(title, image, funct, parent, iconCls, enabled)
  2628. {
  2629. var result = mxPopupMenuAddItem.apply(this, arguments);
  2630. if (enabled != null && !enabled)
  2631. {
  2632. mxEvent.addListener(result, 'mousedown', function(evt)
  2633. {
  2634. mxEvent.consume(evt);
  2635. });
  2636. }
  2637. return result;
  2638. };
  2639. /**
  2640. * Selects tables before cells and rows.
  2641. */
  2642. var mxGraphHandlerIsPropagateSelectionCell = mxGraphHandler.prototype.isPropagateSelectionCell;
  2643. mxGraphHandler.prototype.isPropagateSelectionCell = function(cell, immediate, me)
  2644. {
  2645. var result = false;
  2646. var parent = this.graph.model.getParent(cell)
  2647. if (immediate)
  2648. {
  2649. var geo = (this.graph.model.isEdge(cell)) ? null :
  2650. this.graph.getCellGeometry(cell);
  2651. result = !this.graph.model.isEdge(parent) &&
  2652. !this.graph.isSiblingSelected(cell) &&
  2653. ((geo != null && geo.relative) ||
  2654. !this.graph.isContainer(parent) ||
  2655. this.graph.isPart(cell));
  2656. }
  2657. else
  2658. {
  2659. result = mxGraphHandlerIsPropagateSelectionCell.apply(this, arguments);
  2660. if (this.graph.isTableCell(cell) || this.graph.isTableRow(cell))
  2661. {
  2662. var table = parent;
  2663. if (!this.graph.isTable(table))
  2664. {
  2665. table = this.graph.model.getParent(table);
  2666. }
  2667. result = !this.graph.selectionCellsHandler.isHandled(table) ||
  2668. (this.graph.isCellSelected(table) && this.graph.isToggleEvent(me.getEvent())) ||
  2669. (this.graph.isCellSelected(cell) && !this.graph.isToggleEvent(me.getEvent())) ||
  2670. (this.graph.isTableCell(cell) && this.graph.isCellSelected(parent));
  2671. }
  2672. }
  2673. return result;
  2674. };
  2675. /**
  2676. * Returns last selected ancestor
  2677. */
  2678. mxPopupMenuHandler.prototype.getCellForPopupEvent = function(me)
  2679. {
  2680. var cell = me.getCell();
  2681. var model = this.graph.getModel();
  2682. var parent = model.getParent(cell);
  2683. var state = this.graph.view.getState(parent);
  2684. var selected = this.graph.isCellSelected(cell);
  2685. while (state != null && (model.isVertex(parent) || model.isEdge(parent)))
  2686. {
  2687. var temp = this.graph.isCellSelected(parent);
  2688. selected = selected || temp;
  2689. if (temp || (!selected && (this.graph.isTableCell(cell) ||
  2690. this.graph.isTableRow(cell))))
  2691. {
  2692. cell = parent;
  2693. }
  2694. parent = model.getParent(parent);
  2695. }
  2696. return cell;
  2697. };
  2698. })();