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