1/** 2 * Copyright (c) 2006-2017, JGraph Ltd 3 * Copyright (c) 2006-2017, Gaudenz Alder 4 */ 5(function() 6{ 7 if (typeof html4 !== 'undefined') 8 { 9 /** 10 * Enables paste from Lucidchart 11 */ 12 html4.ATTRIBS['span::data-lucid-content'] = 0; 13 html4.ATTRIBS['span::data-lucid-type'] = 0; 14 15 /** 16 * Enables custom fonts in labels. 17 */ 18 html4.ATTRIBS['font::data-font-src'] = 0; 19 } 20 21 /** 22 * Specifies the app name. Default is document.title. 23 */ 24 Editor.prototype.appName = 'diagrams.net'; 25 26 /** 27 * Known file types. 28 */ 29 Editor.prototype.diagramFileTypes = [ 30 {description: 'diagramXmlDesc', extension: 'drawio', mimeType: 'text/xml'}, 31 {description: 'diagramPngDesc', extension: 'png', mimeType: 'image/png'}, 32 {description: 'diagramSvgDesc', extension: 'svg', mimeType: 'image/svg'}, 33 {description: 'diagramHtmlDesc', extension: 'html', mimeType: 'text/html'}, 34 {description: 'diagramXmlDesc', extension: 'xml', mimeType: 'text/xml'}]; 35 36 /** 37 * Known file types. 38 */ 39 Editor.prototype.libraryFileTypes = [{description: 'Library (.drawiolib, .xml)', extensions: ['drawiolib', 'xml']}]; 40 41 /** 42 * Additional help text for special file extensions. 43 */ 44 Editor.prototype.fileExtensions = [ 45 {ext: 'html', title: 'filetypeHtml'}, 46 {ext: 'png', title: 'filetypePng'}, 47 {ext: 'svg', title: 'filetypeSvg'}]; 48 49 /** 50 * 51 */ 52 Editor.styles = [{}, 53 {commonStyle: {fontColor: '#5C5C5C', strokeColor: '#006658', fillColor: '#21C0A5'}}, 54 {commonStyle: {fontColor: '#095C86', strokeColor: '#AF45ED', fillColor: '#F694C1'}, 55 edgeStyle: {strokeColor: '#60E696'}}, 56 {commonStyle: {fontColor: '#46495D', strokeColor: '#788AA3', fillColor: '#B2C9AB'}}, 57 {commonStyle: {fontColor: '#5AA9E6', strokeColor: '#FF6392', fillColor: '#FFE45E'}}, 58 {commonStyle: {fontColor: '#1D3557', strokeColor: '#457B9D', fillColor: '#A8DADC'}, 59 graph: {background: '#F1FAEE'}}, 60 {commonStyle: {fontColor: '#393C56', strokeColor: '#E07A5F', fillColor: '#F2CC8F'}, 61 graph: {background: '#F4F1DE', gridColor: '#D4D0C0'}}, 62 {commonStyle: {fontColor: '#143642', strokeColor: '#0F8B8D', fillColor: '#FAE5C7'}, 63 edgeStyle: {strokeColor: '#A8201A'}, 64 graph: {background: '#DAD2D8', gridColor: '#ABA4A9'}}, 65 {commonStyle: {fontColor: '#FEFAE0', strokeColor: '#DDA15E', fillColor: '#BC6C25'}, 66 graph: {background: '#283618', gridColor: '#48632C'}}, 67 {commonStyle: {fontColor: '#E4FDE1', strokeColor: '#028090', fillColor: '#F45B69'}, 68 graph: {background: '#114B5F', gridColor: '#0B3240'}}, 69 {}, 70 {vertexStyle: {strokeColor: '#D0CEE2', fillColor: '#FAD9D5'}, 71 edgeStyle: {strokeColor: '#09555B'}, 72 commonStyle: {fontColor: '#1A1A1A'}}, 73 {vertexStyle: {strokeColor: '#BAC8D3', fillColor: '#09555B', fontColor: '#EEEEEE'}, 74 edgeStyle: {strokeColor: '#0B4D6A'}}, 75 {vertexStyle: {strokeColor: '#D0CEE2', fillColor: '#5D7F99'}, 76 edgeStyle: {strokeColor: '#736CA8'}, 77 commonStyle: {fontColor: '#1A1A1A'}}, 78 {vertexStyle: {strokeColor: '#FFFFFF', fillColor: '#182E3E', fontColor: '#FFFFFF'}, 79 edgeStyle: {strokeColor: '#23445D'}, 80 graph: {background: '#FCE7CD', gridColor: '#CFBDA8'}}, 81 {vertexStyle: {strokeColor: '#FFFFFF', fillColor: '#F08E81'}, 82 edgeStyle: {strokeColor: '#182E3E'}, 83 commonStyle: {fontColor: '#1A1A1A'}, 84 graph: {background: '#B0E3E6', gridColor: '#87AEB0'}}, 85 {vertexStyle: {strokeColor: '#909090', fillColor: '#F5AB50'}, 86 edgeStyle: {strokeColor: '#182E3E'}, 87 commonStyle: {fontColor: '#1A1A1A'}, 88 graph: {background: '#EEEEEE'}}, 89 {vertexStyle: {strokeColor: '#EEEEEE', fillColor: '#56517E', fontColor: '#FFFFFF'}, 90 edgeStyle: {strokeColor: '#182E3E'}, 91 graph: {background: '#FAD9D5', gridColor: '#BFA6A3'}}, 92 {vertexStyle: {strokeColor: '#BAC8D3', fillColor: '#B1DDF0', fontColor: '#182E3E'}, 93 edgeStyle: {strokeColor: '#EEEEEE', fontColor: '#FFFFFF'}, 94 graph: {background: '#09555B', gridColor: '#13B4C2'}}, 95 {vertexStyle: {fillColor: '#EEEEEE', fontColor: '#1A1A1A'}, 96 edgeStyle: {fontColor: '#FFFFFF'}, 97 commonStyle: {strokeColor: '#FFFFFF'}, 98 graph: {background: '#182E3E', gridColor: '#4D94C7'}} 99 ]; 100 101 /** 102 * 103 */ 104 Editor.saveImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0iYmxhY2siIHdpZHRoPSIxOHB4IiBoZWlnaHQ9IjE4cHgiPjxwYXRoIGQ9Ik0wIDBoMjR2MjRIMHoiIGZpbGw9Im5vbmUiLz48cGF0aCBkPSJNMTkgMTJ2N0g1di03SDN2N2MwIDEuMS45IDIgMiAyaDE0YzEuMSAwIDItLjkgMi0ydi03aC0yem0tNiAuNjdsMi41OS0yLjU4TDE3IDExLjVsLTUgNS01LTUgMS40MS0xLjQxTDExIDEyLjY3VjNoMnoiLz48L3N2Zz4='; 105 106 /** 107 * 108 */ 109 Editor.smallPlusImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/plus.png' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MDdCMTdENjVCOEM4MTFFNDlCRjVBNDdCODU5NjNBNUMiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MDdCMTdENjZCOEM4MTFFNDlCRjVBNDdCODU5NjNBNUMiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDowN0IxN0Q2M0I4QzgxMUU0OUJGNUE0N0I4NTk2M0E1QyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDowN0IxN0Q2NEI4QzgxMUU0OUJGNUE0N0I4NTk2M0E1QyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PtjrjmgAAAAtSURBVHjaYvz//z8DMigvLwcLdHZ2MiKLMzEQCaivkLGsrOw/dU0cAr4GCDAARQsQbTFrv10AAAAASUVORK5CYII='; 110 111 /** 112 * 113 */ 114 Editor.spinImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/spin.gif' : 'data:image/gif;base64,R0lGODlhDAAMAPUxAEVriVp7lmCAmmGBm2OCnGmHn3OPpneSqYKbr4OcsIScsI2kto6kt46lt5KnuZmtvpquvpuvv56ywaCzwqK1xKu7yay9yq+/zLHAzbfF0bjG0bzJ1LzK1MDN18jT28nT3M3X3tHa4dTc49Xd5Njf5dng5t3k6d/l6uDm6uru8e7x8/Dz9fT29/b4+Pj5+fj5+vr6+v///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkKADEAIf8LTkVUU0NBUEUyLjADAQAAACwAAAAADAAMAAAGR8CYcEgsOgYAIax4CCQuQldrCBEsiK8VS2hoFGOrlJDA+cZQwkLnqyoJFZKviSS0ICrE0ec0jDAwIiUeGyBFGhMPFBkhZo1BACH5BAkKAC4ALAAAAAAMAAwAhVB0kFR3k1V4k2CAmmWEnW6Lo3KOpXeSqH2XrIOcsISdsImhtIqhtJCmuJGnuZuwv52wwJ+ywZ+ywqm6yLHBzbLCzrXEz7fF0LnH0rrI0r7L1b/M1sXR2cfT28rV3czW3s/Z4Nfe5Nvi6ODm6uLn6+Ln7OLo7OXq7efs7+zw8u/y9PDy9PX3+Pr7+////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAZDQJdwSCxGDAIAoVFkFBwYSyIwGE4OkCJxIdG6WkJEx8sSKj7elfBB0a5SQg1EQ0SVVMPKhDM6iUIkRR4ZFxsgJl6JQQAh+QQJCgAxACwAAAAADAAMAIVGa4lcfZdjgpxkg51nhp5ui6N3kqh5lKqFnbGHn7KIoLOQp7iRp7mSqLmTqbqarr6br7+fssGitcOitcSuvsuuv8uwwMyzw861xNC5x9K6x9K/zNbDztjE0NnG0drJ1NzQ2eDS2+LT2+LV3ePZ4Oba4ebb4ufc4+jm6+7t8PLt8PPt8fPx8/Xx9PX09vf19/j3+Pn///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQ8CYcEgsUhQFggFSjCQmnE1jcBhqGBXiIuAQSi7FGEIgfIzCFoCXFCZiPO0hKBMiwl7ET6eUYqlWLkUnISImKC1xbUEAIfkECQoAMgAsAAAAAAwADACFTnKPT3KPVHaTYoKcb4yjcY6leZSpf5mtgZuvh5+yiqG0i6K1jqW3kae5nrHBnrLBn7LCoLPCobTDqbrIqrvIs8LOtMPPtcPPtcTPuMbRucfSvcrUvsvVwMzWxdHaydTcytXdzNbezdff0drh2ODl2+Ln3eTp4Obq4ujs5Ont5uvu6O3w6u7w6u7x7/L09vj5+vr7+vv7////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkdAmXBILHIcicOCUqxELKKPxKAYgiYd4oMAEWo8RVmjIMScwhmBcJMKXwLCECmMGAhPI1QRwBiaSixCMDFhLSorLi8wYYxCQQAh+QQJCgAxACwAAAAADAAMAIVZepVggJphgZtnhp5vjKN2kah3kqmBmq+KobSLorWNpLaRp7mWq7ybr7+gs8KitcSktsWnuManucexwM2ywc63xtG6yNO9ytS+ytW/zNbDz9jH0tvL1d3N197S2+LU3OPU3ePV3eTX3+Xa4efb4ufd5Onl6u7r7vHs7/Lt8PLw8/Xy9Pby9fb09ff2+Pn3+Pn6+vr///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGSMCYcEgseiwSR+RS7GA4JFGF8RiWNiEiJTERgkjFGAQh/KTCGoJwpApnBkITKrwoCFWnFlEhaAxXLC9CBwAGRS4wQgELYY1CQQAh+QQJCgAzACwAAAAADAAMAIVMcI5SdZFhgZtti6JwjaR4k6mAma6Cm6+KobSLorWLo7WNo7aPpredsMCescGitMOitcSmuMaqu8ixwc2zws63xdC4xtG5x9K9ytXAzdfCztjF0NnF0drK1d3M1t7P2N/P2eDT2+LX3+Xe5Onh5+vi5+vj6Ozk6e3n7O/o7O/q7vHs7/Lt8PPu8fPx8/X3+Pn6+vv7+/v8/Pz///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRcCZcEgsmkIbTOZTLIlGqZNnchm2SCgiJ6IRqljFmQUiXIVnoITQde4chC9Y+LEQxmTFRkFSNFAqDAMIRQoCAAEEDmeLQQAh+QQJCgAwACwAAAAADAAMAIVXeZRefplff5lhgZtph59yjqV2kaeAmq6FnbGFnrGLorWNpLaQp7mRqLmYrb2essGgs8Klt8apusitvcquv8u2xNC7yNO8ydS8ytTAzdfBzdfM1t7N197Q2eDU3OPX3+XZ4ObZ4ebc4+jf5erg5erg5uvp7fDu8fPv8vTz9fb09vf19/j3+Pn4+fn5+vr6+/v///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGRUCYcEgspkwjEKhUVJ1QsBNp0xm2VixiSOMRvlxFGAcTJook5eEHIhQcwpWIkAFQECkNy9AQWFwyEAkPRQ4FAwQIE2llQQAh+QQJCgAvACwAAAAADAAMAIVNcY5SdZFigptph6BvjKN0kKd8lquAmq+EnbGGn7KHn7ONpLaOpbearr+csMCdscCescGhtMOnuMauvsuzws60w862xdC9ytW/y9a/zNbCztjG0drH0tvK1N3M1t7N19/U3ePb4uff5urj6Ozk6e3l6u7m6u7o7PDq7vDt8PPv8vTw8vTw8/X19vf6+vv///8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGQ8CXcEgsvlytVUplJLJIpSEDUESFTELBwSgCCQEV42kjDFiMo4uQsDB2MkLHoEHUTD7DRAHC8VAiZ0QSCgYIDxhNiUEAOw=='; 115 116 /** 117 * 118 */ 119 Editor.globeImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTEuOTkgMkM2LjQ3IDIgMiA2LjQ4IDIgMTJzNC40NyAxMCA5Ljk5IDEwQzE3LjUyIDIyIDIyIDE3LjUyIDIyIDEyUzE3LjUyIDIgMTEuOTkgMnptNi45MyA2aC0yLjk1Yy0uMzItMS4yNS0uNzgtMi40NS0xLjM4LTMuNTYgMS44NC42MyAzLjM3IDEuOTEgNC4zMyAzLjU2ek0xMiA0LjA0Yy44MyAxLjIgMS40OCAyLjUzIDEuOTEgMy45NmgtMy44MmMuNDMtMS40MyAxLjA4LTIuNzYgMS45MS0zLjk2ek00LjI2IDE0QzQuMSAxMy4zNiA0IDEyLjY5IDQgMTJzLjEtMS4zNi4yNi0yaDMuMzhjLS4wOC42Ni0uMTQgMS4zMi0uMTQgMiAwIC42OC4wNiAxLjM0LjE0IDJINC4yNnptLjgyIDJoMi45NWMuMzIgMS4yNS43OCAyLjQ1IDEuMzggMy41Ni0xLjg0LS42My0zLjM3LTEuOS00LjMzLTMuNTZ6bTIuOTUtOEg1LjA4Yy45Ni0xLjY2IDIuNDktMi45MyA0LjMzLTMuNTZDOC44MSA1LjU1IDguMzUgNi43NSA4LjAzIDh6TTEyIDE5Ljk2Yy0uODMtMS4yLTEuNDgtMi41My0xLjkxLTMuOTZoMy44MmMtLjQzIDEuNDMtMS4wOCAyLjc2LTEuOTEgMy45NnpNMTQuMzQgMTRIOS42NmMtLjA5LS42Ni0uMTYtMS4zMi0uMTYtMiAwLS42OC4wNy0xLjM1LjE2LTJoNC42OGMuMDkuNjUuMTYgMS4zMi4xNiAyIDAgLjY4LS4wNyAxLjM0LS4xNiAyem0uMjUgNS41NmMuNi0xLjExIDEuMDYtMi4zMSAxLjM4LTMuNTZoMi45NWMtLjk2IDEuNjUtMi40OSAyLjkzLTQuMzMgMy41NnpNMTYuMzYgMTRjLjA4LS42Ni4xNC0xLjMyLjE0LTIgMC0uNjgtLjA2LTEuMzQtLjE0LTJoMy4zOGMuMTYuNjQuMjYgMS4zMS4yNiAycy0uMSAxLjM2LS4yNiAyaC0zLjM4eiIvPjwvc3ZnPg=='; 120 121 /** 122 * 123 */ 124 Editor.commentImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMjEuOTkgNGMwLTEuMS0uODktMi0xLjk5LTJINGMtMS4xIDAtMiAuOS0yIDJ2MTJjMCAxLjEuOSAyIDIgMmgxNGw0IDQtLjAxLTE4ek0xOCAxNEg2di0yaDEydjJ6bTAtM0g2VjloMTJ2MnptMC0zSDZWNmgxMnYyeiIvPjxwYXRoIGQ9Ik0wIDBoMjR2MjRIMHoiIGZpbGw9Im5vbmUiLz48L3N2Zz4='; 125 126 /** 127 * 128 */ 129 Editor.userImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTIgMTJjMi4yMSAwIDQtMS43OSA0LTRzLTEuNzktNC00LTQtNCAxLjc5LTQgNCAxLjc5IDQgNCA0em0wIDJjLTIuNjcgMC04IDEuMzQtOCA0djJoMTZ2LTJjMC0yLjY2LTUuMzMtNC04LTR6Ii8+PC9zdmc+'; 130 131 /** 132 * 133 */ 134 Editor.shareImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTggMTYuMDhjLS43NiAwLTEuNDQuMy0xLjk2Ljc3TDguOTEgMTIuN2MuMDUtLjIzLjA5LS40Ni4wOS0uN3MtLjA0LS40Ny0uMDktLjdsNy4wNS00LjExYy41NC41IDEuMjUuODEgMi4wNC44MSAxLjY2IDAgMy0xLjM0IDMtM3MtMS4zNC0zLTMtMy0zIDEuMzQtMyAzYzAgLjI0LjA0LjQ3LjA5LjdMOC4wNCA5LjgxQzcuNSA5LjMxIDYuNzkgOSA2IDljLTEuNjYgMC0zIDEuMzQtMyAzczEuMzQgMyAzIDNjLjc5IDAgMS41LS4zMSAyLjA0LS44MWw3LjEyIDQuMTZjLS4wNS4yMS0uMDguNDMtLjA4LjY1IDAgMS42MSAxLjMxIDIuOTIgMi45MiAyLjkyIDEuNjEgMCAyLjkyLTEuMzEgMi45Mi0yLjkycy0xLjMxLTIuOTItMi45Mi0yLjkyeiIvPjwvc3ZnPg=='; 135 136 /** 137 * 138 */ 139 Editor.syncImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTIgNFYxTDggNWw0IDRWNmMzLjMxIDAgNiAyLjY5IDYgNiAwIDEuMDEtLjI1IDEuOTctLjcgMi44bDEuNDYgMS40NkMxOS41NCAxNS4wMyAyMCAxMy41NyAyMCAxMmMwLTQuNDItMy41OC04LTgtOHptMCAxNGMtMy4zMSAwLTYtMi42OS02LTYgMC0xLjAxLjI1LTEuOTcuNy0yLjhMNS4yNCA3Ljc0QzQuNDYgOC45NyA0IDEwLjQzIDQgMTJjMCA0LjQyIDMuNTggOCA4IDh2M2w0LTQtNC00djN6Ii8+PC9zdmc+'; 140 141 /** 142 * 143 */ 144 Editor.syncDisabledImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMTAgNi4zNVY0LjI2Yy0uOC4yMS0xLjU1LjU0LTIuMjMuOTZsMS40NiAxLjQ2Yy4yNS0uMTIuNS0uMjQuNzctLjMzem0tNy4xNC0uOTRsMi4zNiAyLjM2QzQuNDUgOC45OSA0IDEwLjQ0IDQgMTJjMCAyLjIxLjkxIDQuMiAyLjM2IDUuNjRMNCAyMGg2di02bC0yLjI0IDIuMjRDNi42OCAxNS4xNSA2IDEzLjY2IDYgMTJjMC0xIC4yNS0xLjk0LjY4LTIuNzdsOC4wOCA4LjA4Yy0uMjUuMTMtLjUuMjUtLjc3LjM0djIuMDljLjgtLjIxIDEuNTUtLjU0IDIuMjMtLjk2bDIuMzYgMi4zNiAxLjI3LTEuMjdMNC4xNCA0LjE0IDIuODYgNS40MXpNMjAgNGgtNnY2bDIuMjQtMi4yNEMxNy4zMiA4Ljg1IDE4IDEwLjM0IDE4IDEyYzAgMS0uMjUgMS45NC0uNjggMi43N2wxLjQ2IDEuNDZDMTkuNTUgMTUuMDEgMjAgMTMuNTYgMjAgMTJjMC0yLjIxLS45MS00LjItMi4zNi01LjY0TDIwIDR6Ii8+PC9zdmc+'; 145 146 /** 147 * 148 */ 149 Editor.syncProblemImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0Ij48cGF0aCBkPSJNMyAxMmMwIDIuMjEuOTEgNC4yIDIuMzYgNS42NEwzIDIwaDZ2LTZsLTIuMjQgMi4yNEM1LjY4IDE1LjE1IDUgMTMuNjYgNSAxMmMwLTIuNjEgMS42Ny00LjgzIDQtNS42NVY0LjI2QzUuNTUgNS4xNSAzIDguMjcgMyAxMnptOCA1aDJ2LTJoLTJ2MnpNMjEgNGgtNnY2bDIuMjQtMi4yNEMxOC4zMiA4Ljg1IDE5IDEwLjM0IDE5IDEyYzAgMi42MS0xLjY3IDQuODMtNCA1LjY1djIuMDljMy40NS0uODkgNi00LjAxIDYtNy43NCAwLTIuMjEtLjkxLTQuMi0yLjM2LTUuNjRMMjEgNHptLTEwIDloMlY3aC0ydjZ6Ii8+PC9zdmc+'; 150 151 /** 152 * 153 */ 154 Editor.drawLogoImage = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPHN2ZyB2ZXJzaW9uPSIxLjEiIGlkPSJFYmVuZV8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCIKCSB2aWV3Qm94PSIwIDAgMjUwIDI1MCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMjUwIDI1MDsiIHhtbDpzcGFjZT0icHJlc2VydmUiPgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgoJLnN0MntmaWxsOiMwMDA7fQo8L3N0eWxlPgo8cGF0aCBjbGFzcz0ic3QyIiB0cmFuc2Zvcm09InRyYW5zbGF0ZSgwLDE1KSIgZD0iTTE5Ny4xLDEzOC4zaC0yMy43bC0yNS00Mi43YzUuNy0xLjIsOS44LTYuMiw5LjctMTJWNTEuNWMwLTYuOC01LjQtMTIuMy0xMi4yLTEyLjNjMCwwLTAuMSwwLTAuMSwwaC00MS43CgljLTYuOCwwLTEyLjMsNS40LTEyLjMsMTIuMmMwLDAsMCwwLjEsMCwwLjF2MzIuMWMwLDUuOCw0LDEwLjgsOS43LDEybC0yNSw0Mi43SDUyLjljLTYuOCwwLTEyLjMsNS40LTEyLjMsMTIuMmMwLDAsMCwwLjEsMCwwLjEKCXYzMi4xYzAsNi44LDUuNCwxMi4zLDEyLjIsMTIuM2MwLDAsMC4xLDAsMC4xLDBoNDEuN2M2LjgsMCwxMi4zLTUuNCwxMi4zLTEyLjJjMCwwLDAtMC4xLDAtMC4xdi0zMi4xYzAtNi44LTUuNC0xMi4zLTEyLjItMTIuMwoJYzAsMC0wLjEsMC0wLjEsMGgtNGwyNC44LTQyLjRoMTkuM2wyNC45LDQyLjRoLTQuMWMtNi44LDAtMTIuMyw1LjQtMTIuMywxMi4yYzAsMCwwLDAuMSwwLDAuMXYzMi4xYzAsNi44LDUuNCwxMi4zLDEyLjIsMTIuMwoJYzAsMCwwLjEsMCwwLjEsMGg0MS43YzYuOCwwLDEyLjMtNS40LDEyLjMtMTIuMmMwLDAsMC0wLjEsMC0wLjF2LTMyLjFjMC02LjgtNS40LTEyLjMtMTIuMi0xMi4zCglDMTk3LjIsMTM4LjMsMTk3LjIsMTM4LjMsMTk3LjEsMTM4LjN6Ii8+Cjwvc3ZnPgo='; 155 156 /** 157 * 158 */ 159 Editor.tailSpin = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTYiIGhlaWdodD0iMTYiIHZpZXdCb3g9Ii0yIC0yIDQ0IDQ0IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgogICAgPGRlZnM+CiAgICAgICAgPGxpbmVhckdyYWRpZW50IHgxPSI4LjA0MiUiIHkxPSIwJSIgeDI9IjY1LjY4MiUiIHkyPSIyMy44NjUlIiBpZD0iYSI+CiAgICAgICAgICAgIDxzdG9wIHN0b3AtY29sb3I9IiM4MDgwODAiIHN0b3Atb3BhY2l0eT0iMCIgb2Zmc2V0PSIwJSIvPgogICAgICAgICAgICA8c3RvcCBzdG9wLWNvbG9yPSIjODA4MDgwIiBzdG9wLW9wYWNpdHk9Ii42MzEiIG9mZnNldD0iNjMuMTQ2JSIvPgogICAgICAgICAgICA8c3RvcCBzdG9wLWNvbG9yPSIjODA4MDgwIiBvZmZzZXQ9IjEwMCUiLz4KICAgICAgICA8L2xpbmVhckdyYWRpZW50PgogICAgPC9kZWZzPgogICAgPGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4KICAgICAgICA8ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgxIDEpIj4KICAgICAgICAgICAgPHBhdGggZD0iTTM2IDE4YzAtOS45NC04LjA2LTE4LTE4LTE4IiBzdHJva2U9InVybCgjYSkiIHN0cm9rZS13aWR0aD0iNiI+CiAgICAgICAgICAgICAgICA8YW5pbWF0ZVRyYW5zZm9ybQogICAgICAgICAgICAgICAgICAgIGF0dHJpYnV0ZU5hbWU9InRyYW5zZm9ybSIKICAgICAgICAgICAgICAgICAgICB0eXBlPSJyb3RhdGUiCiAgICAgICAgICAgICAgICAgICAgZnJvbT0iMCAxOCAxOCIKICAgICAgICAgICAgICAgICAgICB0bz0iMzYwIDE4IDE4IgogICAgICAgICAgICAgICAgICAgIGR1cj0iMC45cyIKICAgICAgICAgICAgICAgICAgICByZXBlYXRDb3VudD0iaW5kZWZpbml0ZSIgLz4KICAgICAgICAgICAgPC9wYXRoPgogICAgICAgICAgICA8Y2lyY2xlIGZpbGw9IiM4MDgwODAiIGN4PSIzNiIgY3k9IjE4IiByPSIxIj4KICAgICAgICAgICAgICAgIDxhbmltYXRlVHJhbnNmb3JtCiAgICAgICAgICAgICAgICAgICAgYXR0cmlidXRlTmFtZT0idHJhbnNmb3JtIgogICAgICAgICAgICAgICAgICAgIHR5cGU9InJvdGF0ZSIKICAgICAgICAgICAgICAgICAgICBmcm9tPSIwIDE4IDE4IgogICAgICAgICAgICAgICAgICAgIHRvPSIzNjAgMTggMTgiCiAgICAgICAgICAgICAgICAgICAgZHVyPSIwLjlzIgogICAgICAgICAgICAgICAgICAgIHJlcGVhdENvdW50PSJpbmRlZmluaXRlIiAvPgogICAgICAgICAgICA8L2NpcmNsZT4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPgo='; 160 161 /** 162 * Used in the GraphViewer lightbox. 163 */ 164 Editor.tweetImage = IMAGE_PATH + '/tweet.png'; 165 166 /** 167 * Used in the GraphViewer lightbox. 168 */ 169 Editor.facebookImage = IMAGE_PATH + '/facebook.png'; 170 171 /** 172 * 173 */ 174 Editor.blankImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg=='; 175 176 /** 177 * 178 */ 179 Editor.hiResImage = (mxClient.IS_SVG) ? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAA+CAMAAACLMWy1AAAAh1BMVEUAAABMTExERERBQUFBQUFFRUVAQEBCQkJAQEA6OjpDQ0NKSkpBQUFBQUFERERERERBQUFCQkJCQkJCQkJJSUlBQUFCQkJDQ0NDQ0NCQkJDQ0NBQUFBQUFCQkJBQUFCQkJCQkJDQ0NCQkJHR0dBQUFCQkJCQkJAQEBCQkJDQ0NAQEBERERCQkIk1hS2AAAAKnRSTlMAAjj96BL7PgQFRwfu3TYazKuVjRXl1V1DPCn1uLGjnWNVIgy9hU40eGqPkM38AAACG0lEQVRYw+2X63KbMBCFzwZblgGDceN74muatpLe//m6MHV3gHGFAv2RjM94MAbxzdnVsQbBDKwH8AH8MDAyafzjqYeyOG04XE7RS8nIRDXg6BlT+rA0nmtAPh+NQRDxIASIMG44rAMrGunBgHwy3uUldxggIStGKp2f+DQc2O4h4eQsX3O2IFB/oEbsjOKbStnjAEA+zJ0ylZTbgvoDn8xNyn6Dbj5Kd4GsNpABa6duQPfSdEj88TgMAhKuCWjAkgmFXPLnsD0pWd3OFGdrMugQII/eOMPEiGOzqPMIeWrcSoMCg71W1pXBPvCP+gS/OdXqQ3uW23+93XGWLl/OaBb805bNcBPoEIcVJsnHzcxpZH86u5KZ9gDby5dQCcnKqdbke4ItI4Tzd7IW9hZQt4EO6GG9b9sYuuK9Wwn8TIr2xKbF2+3Nhr+qxChJ/AI6pIfCu4z4Zowp4ZUNihz79vewzctnHDwTvQO/hCdFBzrUGDOPn2Y/F8YKT4oOATLvlhOznzmBSdFBJWtc58y7r+UVFOCQczy3wpN6pegDqHtsCPTGvH9JuTO0Dyg8icldYPk+RB6g8Aofj4m2EKBvtTmUPD9xDd1pPcSReV2U5iD/ik2yrngtvvqBfPzOvKiDTKTsCdoHZJ7pLLffgTwlJ5vJdtJV2/jiAYaLvLGhMAEDO5QcDg2M/jOw/8Zn+K3ZwJvHT7ZffgC/NvA3zcybTeIfE4EAAAAASUVORK5CYII=' : IMAGE_PATH + '/img-hi-res.png'; 180 181 /** 182 * 183 */ 184 Editor.loResImage = (mxClient.IS_SVG) ? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAFAAAAA+CAMAAACLMWy1AAAAS1BMVEVAQEAAAAA1NTVBQUFDQ0NDQ0NFRUVERERBQUFBQUFBQUFAQEBBQUFBQUFCQkJCQkJCQkJBQUFCQkJDQ0NDQ0NCQkJCQkJCQkJGRkb5/XqTAAAAGXRSTlP+AAWODlASCsesX+Lc2LyWe3pwa1tCPjohjSJfoAAAAI1JREFUWMPt1MkKhTAMRuG0anvneXr/J71nUypKcdqI/N8yhLMKMZE1CahnClDQzMPB44ED3EgeCubgDWnWQMHpwTtKwTe+UHD4sJ94wbUEHHFGhILlYDeSnsQeabeCgsPBgB0MOZZ9oGA5GJFiJSfUULAfjLjARrhCwX7wh2YCDwVbwZkUBKqFFJRN+wOcwSgR2sREcgAAAABJRU5ErkJggg==' : IMAGE_PATH + '/img-lo-res.png'; 185 186 /** 187 * 188 */ 189 Editor.cameraImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGhlaWdodD0iMThweCIgdmlld0JveD0iMCAwIDI0IDI0IiB3aWR0aD0iMThweCIgZmlsbD0iIzAwMDAwMCI+PHBhdGggZD0iTTAgMGgyNHYyNEgwVjB6IiBmaWxsPSJub25lIi8+PHBhdGggZD0iTTE0LjEyIDRsMS44MyAySDIwdjEySDRWNmg0LjA1bDEuODMtMmg0LjI0TTE1IDJIOUw3LjE3IDRINGMtMS4xIDAtMiAuOS0yIDJ2MTJjMCAxLjEuOSAyIDIgMmgxNmMxLjEgMCAyLS45IDItMlY2YzAtMS4xLS45LTItMi0yaC0zLjE3TDE1IDJ6bS0zIDdjMS42NSAwIDMgMS4zNSAzIDNzLTEuMzUgMy0zIDMtMy0xLjM1LTMtMyAxLjM1LTMgMy0zbTAtMmMtMi43NiAwLTUgMi4yNC01IDVzMi4yNCA1IDUgNSA1LTIuMjQgNS01LTIuMjQtNS01LTV6Ii8+PC9zdmc+'; 190 191 /** 192 * 193 */ 194 Editor.tagsImage = 'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGVuYWJsZS1iYWNrZ3JvdW5kPSJuZXcgMCAwIDI0IDI0IiBoZWlnaHQ9IjE4cHgiIHZpZXdCb3g9IjAgMCAyNCAyNCIgd2lkdGg9IjE4cHgiIGZpbGw9IiMwMDAwMDAiPjxnPjxwYXRoIGQ9Ik0wLDBoMjR2MjRIMFYweiIgZmlsbD0ibm9uZSIvPjwvZz48Zz48Zz48cGF0aCBkPSJNMjEuNDEsMTEuNDFsLTguODMtOC44M0MxMi4yMSwyLjIxLDExLjcsMiwxMS4xNywySDRDMi45LDIsMiwyLjksMiw0djcuMTdjMCwwLjUzLDAuMjEsMS4wNCwwLjU5LDEuNDFsOC44Myw4LjgzIGMwLjc4LDAuNzgsMi4wNSwwLjc4LDIuODMsMGw3LjE3LTcuMTdDMjIuMiwxMy40NiwyMi4yLDEyLjIsMjEuNDEsMTEuNDF6IE0xMi44MywyMEw0LDExLjE3VjRoNy4xN0wyMCwxMi44M0wxMi44MywyMHoiLz48Y2lyY2xlIGN4PSI2LjUiIGN5PSI2LjUiIHI9IjEuNSIvPjwvZz48L2c+PC9zdmc+'; 195 196 /** 197 * Broken image symbol for offline SVG. 198 */ 199 Editor.svgBrokenImage = Graph.createSvgImage(10, 10, '<rect x="0" y="0" width="10" height="10" stroke="#000" fill="transparent"/><path d="m 0 0 L 10 10 L 0 10 L 10 0" stroke="#000" fill="transparent"/>'); 200 201 /** 202 * Error image for not found images 203 */ 204 Editor.errorImage = 'data:image/gif;base64,R0lGODlhEAAQAPcAAADGAIQAAISEhP8AAP///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////yH5BAEAAAAALAAAAAAQABAAAAhoAAEIFBigYMGBCAkGGMCQ4cGECxtKHBAAYUQCEzFSHLiQgMeGHjEGEAAg4oCQJz86LCkxpEqHAkwyRClxpEyXGmGaREmTIsmOL1GO/DkzI0yOE2sKIMlRJsWhCQHENDiUaVSpS5cmDAgAOw=='; 205 206 /** 207 * Error image for not found images 208 */ 209 Editor.configurationKey = '.configuration'; 210 211 /** 212 * Error image for not found images 213 */ 214 Editor.settingsKey = '.drawio-config'; 215 216 /** 217 * Default value for custom libraries in mxSettings. 218 */ 219 Editor.defaultCustomLibraries = []; 220 221 /** 222 * Default value for custom libraries in mxSettings. 223 */ 224 Editor.enableCustomLibraries = true; 225 226 /** 227 * Specifies if custom properties should be enabled. 228 */ 229 Editor.enableCustomProperties = true; 230 231 /** 232 * Sets the default value for including a copy of the diagram. 233 * Default is true. 234 */ 235 Editor.defaultIncludeDiagram = true; 236 237 /** 238 * Specifies if custom properties should be enabled. 239 */ 240 Editor.enableServiceWorker = urlParams['pwa'] != '0' && 241 'serviceWorker' in navigator && (urlParams['offline'] == '1' || 242 /.*\.diagrams\.net$/.test(window.location.hostname) || 243 /.*\.draw\.io$/.test(window.location.hostname)); 244 245 /** 246 * Specifies if web fonts are enabled. 247 */ 248 Editor.enableWebFonts = urlParams['safe-style-src'] != '1'; 249 250 /** 251 * Disables the shadow option in the format panel. 252 */ 253 Editor.enableShadowOption = !mxClient.IS_SF; 254 255 /** 256 * Disables the export URL function. 257 */ 258 Editor.enableExportUrl = true; 259 260 /** 261 * Specifies if XML files should be compressed. Default is true. 262 */ 263 Editor.compressXml = true; 264 265 /** 266 * Specifies if XML files should be compressed. Default is true. 267 */ 268 Editor.oneDriveInlinePicker = (window.urlParams != null && window.urlParams['inlinePicker'] == '0') ? false : true; 269 270 /** 271 * Specifies global variables. 272 */ 273 Editor.globalVars = null; 274 275 /** 276 * Reference to the config object passed to <configure>. 277 */ 278 Editor.config = null; 279 280 /** 281 * Reference to the version of the last config object in 282 * <configure>. If this is different to the last version in 283 * mxSettings.parse, then the settings are reset. 284 */ 285 Editor.configVersion = null; 286 287 /** 288 * Default border for image export (to allow for sketch style). 289 */ 290 Editor.defaultBorder = 5; 291 292 /** 293 * Common properties for all edges. 294 */ 295 Editor.commonProperties = [ 296 {name: 'comic', dispName: 'Comic', type: 'bool', defVal: false, isVisible: function(state, format) 297 { 298 return mxUtils.getValue(state.style, 'sketch', '0') != '1'; 299 }}, 300 {name: 'jiggle', dispName: 'Jiggle', type: 'float', min: 0, defVal: 1, isVisible: function(state, format) 301 { 302 return mxUtils.getValue(state.style, 'comic', '0') == '1' || 303 mxUtils.getValue(state.style, 'sketch', (urlParams['rough'] == '1') ? '1' : '0') == '1'; 304 }}, 305 {name: 'fillWeight', dispName: 'Fill Weight', type: 'int', defVal: -1, isVisible: function(state, format) 306 { 307 return mxUtils.getValue(state.style, 'sketch', (urlParams['rough'] == '1') ? '1' : '0') == '1'; 308 }}, 309 {name: 'hachureGap', dispName: 'Hachure Gap', type: 'int', defVal: -1, isVisible: function(state, format) 310 { 311 return mxUtils.getValue(state.style, 'sketch', (urlParams['rough'] == '1') ? '1' : '0') == '1'; 312 }}, 313 {name: 'hachureAngle', dispName: 'Hachure Angle', type: 'int', defVal: -41, isVisible: function(state, format) 314 { 315 return mxUtils.getValue(state.style, 'sketch', (urlParams['rough'] == '1') ? '1' : '0') == '1'; 316 }}, 317 {name: 'curveFitting', dispName: 'Curve Fitting', type: 'float', defVal: 0.95, isVisible: function(state, format) 318 { 319 return mxUtils.getValue(state.style, 'sketch', (urlParams['rough'] == '1') ? '1' : '0') == '1'; 320 }}, 321 {name: 'simplification', dispName: 'Simplification', type: 'float', defVal: 0, min: 0, max: 1, isVisible: function(state, format) 322 { 323 return mxUtils.getValue(state.style, 'sketch', (urlParams['rough'] == '1') ? '1' : '0') == '1'; 324 }}, 325 {name: 'disableMultiStroke', dispName: 'Disable Multi Stroke', type: 'bool', defVal: false, isVisible: function(state, format) 326 { 327 return mxUtils.getValue(state.style, 'sketch', (urlParams['rough'] == '1') ? '1' : '0') == '1'; 328 }}, 329 {name: 'disableMultiStrokeFill', dispName: 'Disable Multi Stroke Fill', type: 'bool', defVal: false, isVisible: function(state, format) 330 { 331 return mxUtils.getValue(state.style, 'sketch', (urlParams['rough'] == '1') ? '1' : '0') == '1'; 332 }}, 333 {name: 'dashOffset', dispName: 'Dash Offset', type: 'int', defVal: -1, isVisible: function(state, format) 334 { 335 return mxUtils.getValue(state.style, 'sketch', (urlParams['rough'] == '1') ? '1' : '0') == '1'; 336 }}, 337 {name: 'dashGap', dispName: 'Dash Gap', type: 'int', defVal: -1, isVisible: function(state, format) 338 { 339 return mxUtils.getValue(state.style, 'sketch', (urlParams['rough'] == '1') ? '1' : '0') == '1'; 340 }}, 341 {name: 'zigzagOffset', dispName: 'ZigZag Offset', type: 'int', defVal: -1, isVisible: function(state, format) 342 { 343 return mxUtils.getValue(state.style, 'sketch', (urlParams['rough'] == '1') ? '1' : '0') == '1'; 344 }}, 345 {name: 'jiggle', dispName: 'Jiggle', type: 'float', min: 0, defVal: 1, isVisible: function(state, format) 346 { 347 return mxUtils.getValue(state.style, 'comic', '0') == '1' || 348 mxUtils.getValue(state.style, 'sketch', (urlParams['rough'] == '1') ? '1' : '0') == '1'; 349 }}, 350 {name: 'sketchStyle', dispName: 'Sketch Style', type: 'enum', defVal: 'rough', 351 enumList: [{val: 'rough', dispName: 'Rough'}, {val: 'comic', dispName: 'Comic'}], 352 isVisible: function(state, format) 353 { 354 return mxUtils.getValue(state.style, 'sketch', (urlParams['rough'] == '1') ? '1' : '0') == '1'; 355 }} 356 ]; 357 358 /** 359 * Common properties for all edges. 360 */ 361 Editor.commonEdgeProperties = [ 362 {type: 'separator'}, 363 {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE}, 364 {name: 'sourcePortConstraint', dispName: 'Source Constraint', type: 'enum', defVal: 'none', 365 enumList: [{val: 'none', dispName: 'None'}, {val: 'north', dispName: 'North'}, {val: 'east', dispName: 'East'}, {val: 'south', dispName: 'South'}, {val: 'west', dispName: 'West'}] 366 }, 367 {name: 'targetPortConstraint', dispName: 'Target Constraint', type: 'enum', defVal: 'none', 368 enumList: [{val: 'none', dispName: 'None'}, {val: 'north', dispName: 'North'}, {val: 'east', dispName: 'East'}, {val: 'south', dispName: 'South'}, {val: 'west', dispName: 'West'}] 369 }, 370 {name: 'jettySize', dispName: 'Jetty Size', type: 'int', min: 0, defVal: 'auto', allowAuto: true, isVisible: function(state) 371 { 372 return mxUtils.getValue(state.style, mxConstants.STYLE_EDGE, null) == 'orthogonalEdgeStyle'; 373 }}, 374 {name: 'fillOpacity', dispName: 'Fill Opacity', type: 'int', min: 0, max: 100, defVal: 100}, 375 {name: 'strokeOpacity', dispName: 'Stroke Opacity', type: 'int', min: 0, max: 100, defVal: 100}, 376 {name: 'startFill', dispName: 'Start Fill', type: 'bool', defVal: true}, 377 {name: 'endFill', dispName: 'End Fill', type: 'bool', defVal: true}, 378 {name: 'perimeterSpacing', dispName: 'Terminal Spacing', type: 'float', defVal: 0}, 379 {name: 'anchorPointDirection', dispName: 'Anchor Direction', type: 'bool', defVal: true}, 380 {name: 'snapToPoint', dispName: 'Snap to Point', type: 'bool', defVal: false}, 381 {name: 'fixDash', dispName: 'Fixed Dash', type: 'bool', defVal: false}, 382 {name: 'editable', dispName: 'Editable', type: 'bool', defVal: true}, 383 {name: 'metaEdit', dispName: 'Edit Dialog', type: 'bool', defVal: false}, 384 {name: 'backgroundOutline', dispName: 'Background Outline', type: 'bool', defVal: false}, 385 {name: 'bendable', dispName: 'Bendable', type: 'bool', defVal: true}, 386 {name: 'movable', dispName: 'Movable', type: 'bool', defVal: true}, 387 {name: 'cloneable', dispName: 'Cloneable', type: 'bool', defVal: true}, 388 {name: 'deletable', dispName: 'Deletable', type: 'bool', defVal: true}, 389 {name: 'noJump', dispName: 'No Jumps', type: 'bool', defVal: false}, 390 {name: 'flowAnimation', dispName: 'Flow Animation', type: 'bool', defVal: false}, 391 {name: 'ignoreEdge', dispName: 'Ignore Edge', type: 'bool', defVal: false}, 392 {name: 'orthogonalLoop', dispName: 'Loop Routing', type: 'bool', defVal: false}, 393 {name: 'orthogonal', dispName: 'Orthogonal', type: 'bool', defVal: false} 394 ].concat(Editor.commonProperties); 395 396 /** 397 * Common properties for all vertices. 398 */ 399 Editor.commonVertexProperties = [ 400 {name: 'colspan', dispName: 'Colspan', type: 'int', min: 1, defVal: 1, isVisible: function(state, format) 401 { 402 var graph = format.editorUi.editor.graph; 403 404 return urlParams['test'] == '1' && state.vertices.length == 1 && 405 state.edges.length == 0 && graph.isTableCell(state.vertices[0]); 406 }}, 407 {name: 'rowspan', dispName: 'Rowspan', type: 'int', min: 1, defVal: 1, isVisible: function(state, format) 408 { 409 var graph = format.editorUi.editor.graph; 410 411 return urlParams['test'] == '1' && state.vertices.length == 1 && 412 state.edges.length == 0 && graph.isTableCell(state.vertices[0]); 413 }}, 414 {type: 'separator'}, 415 {name: 'resizeLastRow', dispName: 'Resize Last Row', type: 'bool', getDefaultValue: function(state, format) 416 { 417 var cell = (state.vertices.length == 1 && state.edges.length == 0) ? state.vertices[0] : null; 418 var graph = format.editorUi.editor.graph; 419 var style = graph.getCellStyle(cell); 420 421 return mxUtils.getValue(style, 'resizeLastRow', '0') == '1'; 422 }, isVisible: function(state, format) 423 { 424 var graph = format.editorUi.editor.graph; 425 426 return state.vertices.length == 1 && state.edges.length == 0 && 427 graph.isTable(state.vertices[0]); 428 }}, 429 {name: 'resizeLast', dispName: 'Resize Last Column', type: 'bool', getDefaultValue: function(state, format) 430 { 431 var cell = (state.vertices.length == 1 && state.edges.length == 0) ? state.vertices[0] : null; 432 var graph = format.editorUi.editor.graph; 433 var style = graph.getCellStyle(cell); 434 435 return mxUtils.getValue(style, 'resizeLast', '0') == '1'; 436 }, isVisible: function(state, format) 437 { 438 var graph = format.editorUi.editor.graph; 439 440 return state.vertices.length == 1 && state.edges.length == 0 && 441 graph.isTable(state.vertices[0]); 442 }}, 443 {name: 'fillOpacity', dispName: 'Fill Opacity', type: 'int', min: 0, max: 100, defVal: 100}, 444 {name: 'strokeOpacity', dispName: 'Stroke Opacity', type: 'int', min: 0, max: 100, defVal: 100}, 445 {name: 'overflow', dispName: 'Text Overflow', defVal: 'visible', type: 'enum', 446 enumList: [{val: 'visible', dispName: 'Visible'}, {val: 'hidden', dispName: 'Hidden'}, {val: 'block', dispName: 'Block'}, 447 {val: 'fill', dispName: 'Fill'}, {val: 'width', dispName: 'Width'}] 448 }, 449 {name: 'noLabel', dispName: 'Hide Label', type: 'bool', defVal: false}, 450 {name: 'labelPadding', dispName: 'Label Padding', type: 'float', defVal: 0}, 451 {name: 'direction', dispName: 'Direction', type: 'enum', defVal: 'east', 452 enumList: [{val: 'north', dispName: 'North'}, {val: 'east', dispName: 'East'}, {val: 'south', dispName: 'South'}, {val: 'west', dispName: 'West'}] 453 }, 454 {name: 'portConstraint', dispName: 'Constraint', type: 'enum', defVal: 'none', 455 enumList: [{val: 'none', dispName: 'None'}, {val: 'north', dispName: 'North'}, {val: 'east', dispName: 'East'}, {val: 'south', dispName: 'South'}, {val: 'west', dispName: 'West'}] 456 }, 457 {name: 'portConstraintRotation', dispName: 'Rotate Constraint', type: 'bool', defVal: false}, 458 {name: 'connectable', dispName: 'Connectable', type: 'bool', getDefaultValue: function(state, format) 459 { 460 var cell = (state.vertices.length == 1 && state.edges.length == 0) ? state.vertices[0] : null; 461 var graph = format.editorUi.editor.graph; 462 463 return graph.isCellConnectable(cell); 464 }, isVisible: function(state, format) 465 { 466 return state.vertices.length == 1 && state.edges.length == 0; 467 }}, 468 {name: 'allowArrows', dispName: 'Allow Arrows', type: 'bool', defVal: true}, 469 {name: 'snapToPoint', dispName: 'Snap to Point', type: 'bool', defVal: false}, 470 {name: 'perimeter', dispName: 'Perimeter', defVal: 'none', type: 'enum', 471 enumList: [{val: 'none', dispName: 'None'}, 472 {val: 'rectanglePerimeter', dispName: 'Rectangle'}, {val: 'ellipsePerimeter', dispName: 'Ellipse'}, 473 {val: 'rhombusPerimeter', dispName: 'Rhombus'}, {val: 'trianglePerimeter', dispName: 'Triangle'}, 474 {val: 'hexagonPerimeter2', dispName: 'Hexagon'}, {val: 'lifelinePerimeter', dispName: 'Lifeline'}, 475 {val: 'orthogonalPerimeter', dispName: 'Orthogonal'}, {val: 'backbonePerimeter', dispName: 'Backbone'}, 476 {val: 'calloutPerimeter', dispName: 'Callout'}, {val: 'parallelogramPerimeter', dispName: 'Parallelogram'}, 477 {val: 'trapezoidPerimeter', dispName: 'Trapezoid'}, {val: 'stepPerimeter', dispName: 'Step'}, 478 {val: 'centerPerimeter', dispName: 'Center'}] 479 }, 480 {name: 'fixDash', dispName: 'Fixed Dash', type: 'bool', defVal: false}, 481 {name: 'autosize', dispName: 'Autosize', type: 'bool', defVal: false}, 482 {name: 'container', dispName: 'Container', type: 'bool', defVal: false, isVisible: function(state, format) 483 { 484 return state.vertices.length == 1 && state.edges.length == 0; 485 }}, 486 {name: 'dropTarget', dispName: 'Drop Target', type: 'bool', getDefaultValue: function(state, format) 487 { 488 var cell = (state.vertices.length == 1 && state.edges.length == 0) ? state.vertices[0] : null; 489 var graph = format.editorUi.editor.graph; 490 491 return cell != null && (graph.isSwimlane(cell) || graph.model.getChildCount(cell) > 0); 492 }, isVisible: function(state, format) 493 { 494 return state.vertices.length == 1 && state.edges.length == 0; 495 }}, 496 {name: 'collapsible', dispName: 'Collapsible', type: 'bool', getDefaultValue: function(state, format) 497 { 498 var cell = (state.vertices.length == 1 && state.edges.length == 0) ? state.vertices[0] : null; 499 var graph = format.editorUi.editor.graph; 500 501 return cell != null && ((graph.isContainer(cell) && state.style['collapsible'] != '0') || 502 (!graph.isContainer(cell) && state.style['collapsible'] == '1')); 503 }, isVisible: function(state, format) 504 { 505 return state.vertices.length == 1 && state.edges.length == 0; 506 }}, 507 {name: 'recursiveResize', dispName: 'Resize Children', type: 'bool', defVal: true, isVisible: function(state, format) 508 { 509 return state.vertices.length == 1 && state.edges.length == 0 && 510 !format.editorUi.editor.graph.isSwimlane(state.vertices[0]) && 511 mxUtils.getValue(state.style, 'childLayout', null) == null; 512 }}, 513 {name: 'expand', dispName: 'Expand', type: 'bool', defVal: true}, 514 {name: 'part', dispName: 'Part', type: 'bool', defVal: false, isVisible: function(state, format) 515 { 516 var model = format.editorUi.editor.graph.model; 517 518 return (state.vertices.length > 0) ? model.isVertex(model.getParent(state.vertices[0])) : false; 519 }}, 520 {name: 'editable', dispName: 'Editable', type: 'bool', defVal: true}, 521 {name: 'metaEdit', dispName: 'Edit Dialog', type: 'bool', defVal: false}, 522 {name: 'backgroundOutline', dispName: 'Background Outline', type: 'bool', defVal: false}, 523 {name: 'movable', dispName: 'Movable', type: 'bool', defVal: true}, 524 {name: 'movableLabel', dispName: 'Movable Label', type: 'bool', defVal: false, isVisible: function(state, format) 525 { 526 var geo = (state.vertices.length > 0) ? format.editorUi.editor.graph.getCellGeometry(state.vertices[0]) : null; 527 528 return geo != null && !geo.relative; 529 }}, 530 {name: 'resizable', dispName: 'Resizable', type: 'bool', defVal: true}, 531 {name: 'resizeWidth', dispName: 'Resize Width', type: 'bool', defVal: false}, 532 {name: 'resizeHeight', dispName: 'Resize Height', type: 'bool', defVal: false}, 533 {name: 'rotatable', dispName: 'Rotatable', type: 'bool', defVal: true}, 534 {name: 'cloneable', dispName: 'Cloneable', type: 'bool', defVal: true}, 535 {name: 'deletable', dispName: 'Deletable', type: 'bool', defVal: true}, 536 {name: 'treeFolding', dispName: 'Tree Folding', type: 'bool', defVal: false}, 537 {name: 'treeMoving', dispName: 'Tree Moving', type: 'bool', defVal: false}, 538 {name: 'pointerEvents', dispName: 'Pointer Events', type: 'bool', defVal: true, isVisible: function(state, format) 539 { 540 var fillColor = mxUtils.getValue(state.style, mxConstants.STYLE_FILLCOLOR, null); 541 542 return format.editorUi.editor.graph.isSwimlane(state.vertices[0]) || 543 fillColor == null || fillColor == mxConstants.NONE || 544 state.style['pointerEvents'] != null; 545 }}, 546 {name: 'moveCells', dispName: 'Move Cells on Fold', type: 'bool', defVal: false, isVisible: function(state, format) 547 { 548 return state.vertices.length > 0 && format.editorUi.editor.graph.isContainer(state.vertices[0]); 549 }} 550 ].concat(Editor.commonProperties); 551 552 /** 553 * Default value for the CSV import dialog. 554 */ 555 Editor.defaultCsvValue = '##\n' + 556 '## Example CSV import. Use ## for comments and # for configuration. Paste CSV below.\n' + 557 '## The following names are reserved and should not be used (or ignored):\n' + 558 '## id, tooltip, placeholder(s), link and label (see below)\n' + 559 '##\n' + 560 '#\n' + 561 '## Node label with placeholders and HTML.\n' + 562 '## Default is \'%name_of_first_column%\'.\n' + 563 '#\n' + 564 '# label: %name%<br><i style="color:gray;">%position%</i><br><a href="mailto:%email%">Email</a>\n' + 565 '#\n' + 566 '## Node style (placeholders are replaced once).\n' + 567 '## Default is the current style for nodes.\n' + 568 '#\n' + 569 '# style: label;image=%image%;whiteSpace=wrap;html=1;rounded=1;fillColor=%fill%;strokeColor=%stroke%;\n' + 570 '#\n' + 571 '## Parent style for nodes with child nodes (placeholders are replaced once).\n' + 572 '#\n' + 573 '# parentstyle: swimlane;whiteSpace=wrap;html=1;childLayout=stackLayout;horizontal=1;horizontalStack=0;resizeParent=1;resizeLast=0;collapsible=1;\n' + 574 '#\n' + 575 '## Optional column name that contains a reference to a named style in styles.\n' + 576 '## Default is the current style for nodes.\n' + 577 '#\n' + 578 '# stylename: -\n' + 579 '#\n' + 580 '## JSON for named styles of the form {"name": "style", "name": "style"} where style is a cell style with\n' + 581 '## placeholders that are replaced once.\n' + 582 '#\n' + 583 '# styles: -\n' + 584 '#\n' + 585 '## JSON for variables in styles of the form {"name": "value", "name": "value"} where name is a string\n' + 586 '## that will replace a placeholder in a style.\n' + 587 '#\n' + 588 '# vars: -\n' + 589 '#\n' + 590 '## Optional column name that contains a reference to a named label in labels.\n' + 591 '## Default is the current label.\n' + 592 '#\n' + 593 '# labelname: -\n' + 594 '#\n' + 595 '## JSON for named labels of the form {"name": "label", "name": "label"} where label is a cell label with\n' + 596 '## placeholders.\n' + 597 '#\n' + 598 '# labels: -\n' + 599 '#\n' + 600 '## Uses the given column name as the identity for cells (updates existing cells).\n' + 601 '## Default is no identity (empty value or -).\n' + 602 '#\n' + 603 '# identity: -\n' + 604 '#\n' + 605 '## Uses the given column name as the parent reference for cells. Default is no parent (empty or -).\n' + 606 '## The identity above is used for resolving the reference so it must be specified.\n' + 607 '#\n' + 608 '# parent: -\n' + 609 '#\n' + 610 '## Adds a prefix to the identity of cells to make sure they do not collide with existing cells (whose\n' + 611 '## IDs are numbers from 0..n, sometimes with a GUID prefix in the context of realtime collaboration).\n' + 612 '## Default is csvimport-.\n' + 613 '#\n' + 614 '# namespace: csvimport-\n' + 615 '#\n' + 616 '## Connections between rows ("from": source colum, "to": target column).\n' + 617 '## Label, style and invert are optional. Defaults are \'\', current style and false.\n' + 618 '## If placeholders are used in the style, they are replaced with data from the source.\n' + 619 '## An optional placeholders can be set to target to use data from the target instead.\n' + 620 '## In addition to label, an optional fromlabel and tolabel can be used to name the column\n' + 621 '## that contains the text for the label in the edges source or target (invert ignored).\n' + 622 '## In addition to those, an optional source and targetlabel can be used to specify a label\n' + 623 '## that contains placeholders referencing the respective columns in the source or target row.\n' + 624 '## The label is created in the form fromlabel + sourcelabel + label + tolabel + targetlabel.\n' + 625 '## Additional labels can be added by using an optional labels array with entries of the\n' + 626 '## form {"label": string, "x": number, "y": number, "dx": number, "dy": number} where\n' + 627 '## x is from -1 to 1 along the edge, y is orthogonal, and dx/dy are offsets in pixels.\n' + 628 '## An optional placeholders with the string value "source" or "target" can be specified\n' + 629 '## to replace placeholders in the additional label with data from the source or target.\n' + 630 '## The target column may contain a comma-separated list of values.\n' + 631 '## Multiple connect entries are allowed.\n' + 632 '#\n' + 633 '# connect: {"from": "manager", "to": "name", "invert": true, "label": "manages", \\\n' + 634 '# "style": "curved=1;endArrow=blockThin;endFill=1;fontSize=11;"}\n' + 635 '# connect: {"from": "refs", "to": "id", "style": "curved=1;fontSize=11;"}\n' + 636 '#\n' + 637 '## Node x-coordinate. Possible value is a column name. Default is empty. Layouts will\n' + 638 '## override this value.\n' + 639 '#\n' + 640 '# left: \n' + 641 '#\n' + 642 '## Node y-coordinate. Possible value is a column name. Default is empty. Layouts will\n' + 643 '## override this value.\n' + 644 '#\n' + 645 '# top: \n' + 646 '#\n' + 647 '## Node width. Possible value is a number (in px), auto or an @ sign followed by a column\n' + 648 '## name that contains the value for the width. Default is auto.\n' + 649 '#\n' + 650 '# width: auto\n' + 651 '#\n' + 652 '## Node height. Possible value is a number (in px), auto or an @ sign followed by a column\n' + 653 '## name that contains the value for the height. Default is auto.\n' + 654 '#\n' + 655 '# height: auto\n' + 656 '#\n' + 657 '## Padding for autosize. Default is 0.\n' + 658 '#\n' + 659 '# padding: -12\n' + 660 '#\n' + 661 '## Comma-separated list of ignored columns for metadata. (These can be\n' + 662 '## used for connections and styles but will not be added as metadata.)\n' + 663 '#\n' + 664 '# ignore: id,image,fill,stroke,refs,manager\n' + 665 '#\n' + 666 '## Column to be renamed to link attribute (used as link).\n' + 667 '#\n' + 668 '# link: url\n' + 669 '#\n' + 670 '## Spacing between nodes. Default is 40.\n' + 671 '#\n' + 672 '# nodespacing: 40\n' + 673 '#\n' + 674 '## Spacing between levels of hierarchical layouts. Default is 100.\n' + 675 '#\n' + 676 '# levelspacing: 100\n' + 677 '#\n' + 678 '## Spacing between parallel edges. Default is 40. Use 0 to disable.\n' + 679 '#\n' + 680 '# edgespacing: 40\n' + 681 '#\n' + 682 '## Name or JSON of layout. Possible values are auto, none, verticaltree, horizontaltree,\n' + 683 '## verticalflow, horizontalflow, organic, circle or a JSON string as used in Layout, Apply.\n' + 684 '## Default is auto.\n' + 685 '#\n' + 686 '# layout: auto\n' + 687 '#\n' + 688 '## ---- CSV below this line. First line are column names. ----\n' + 689 'name,position,id,location,manager,email,fill,stroke,refs,url,image\n' + 690 'Tessa Miller,CFO,emi,Office 1,,me@example.com,#dae8fc,#6c8ebf,,https://www.draw.io,https://cdn3.iconfinder.com/data/icons/user-avatars-1/512/users-3-128.png\n' + 691 'Edward Morrison,Brand Manager,emo,Office 2,Tessa Miller,me@example.com,#d5e8d4,#82b366,,https://www.draw.io,https://cdn3.iconfinder.com/data/icons/user-avatars-1/512/users-10-3-128.png\n' + 692 'Alison Donovan,System Admin,rdo,Office 3,Tessa Miller,me@example.com,#d5e8d4,#82b366,"emo,tva",https://www.draw.io,https://cdn3.iconfinder.com/data/icons/user-avatars-1/512/users-2-128.png\n' + 693 'Evan Valet,HR Director,tva,Office 4,Tessa Miller,me@example.com,#d5e8d4,#82b366,,https://www.draw.io,https://cdn3.iconfinder.com/data/icons/user-avatars-1/512/users-9-2-128.png\n'; 694 695 /** 696 * Compresses the given string. 697 */ 698 Editor.createRoughCanvas = function(c) 699 { 700 var rc = rough.canvas( 701 { 702 // Provides expected function but return value is not used 703 getContext: function() 704 { 705 return c; 706 } 707 }); 708 709 rc.draw = function(drawable) 710 { 711 var sets = drawable.sets || []; 712 var o = drawable.options || this.getDefaultOptions(); 713 714 for (var i = 0; i < sets.length; i++) 715 { 716 var drawing = sets[i]; 717 718 switch (drawing.type) 719 { 720 case 'path': 721 if (o.stroke != null) 722 { 723 this._drawToContext(c, drawing, o); 724 } 725 break; 726 case 'fillPath': 727 this._drawToContext(c, drawing, o); 728 break; 729 case 'fillSketch': 730 this.fillSketch(c, drawing, o); 731 break; 732 } 733 } 734 }; 735 736 rc.fillSketch = function(ctx, drawing, o) 737 { 738 var strokeColor = c.state.strokeColor; 739 var strokeWidth = c.state.strokeWidth; 740 var strokeAlpha = c.state.strokeAlpha; 741 var dashed = c.state.dashed; 742 743 var fweight = o.fillWeight; 744 if (fweight < 0) 745 { 746 fweight = o.strokeWidth / 2; 747 } 748 749 c.setStrokeAlpha(c.state.fillAlpha); 750 c.setStrokeColor(o.fill || ''); 751 c.setStrokeWidth(fweight); 752 c.setDashed(false); 753 754 this._drawToContext(ctx, drawing, o); 755 756 c.setDashed(dashed); 757 c.setStrokeWidth(strokeWidth); 758 c.setStrokeColor(strokeColor); 759 c.setStrokeAlpha(strokeAlpha); 760 }; 761 762 rc._drawToContext = function(ctx, drawing, o) 763 { 764 ctx.begin(); 765 766 for (var i = 0; i < drawing.ops.length; i++) 767 { 768 var item = drawing.ops[i]; 769 var data = item.data; 770 771 switch (item.op) 772 { 773 case 'move': 774 ctx.moveTo(data[0], data[1]); 775 break; 776 case 'bcurveTo': 777 ctx.curveTo(data[0], data[1], data[2], data[3], data[4], data[5]); 778 break; 779 case 'lineTo': 780 ctx.lineTo(data[0], data[1]); 781 break; 782 } 783 }; 784 785 ctx.end(); 786 787 if (drawing.type === 'fillPath' && o.filled) 788 { 789 ctx.fill(); 790 } 791 else 792 { 793 ctx.stroke(); 794 } 795 }; 796 797 return rc; 798 }; 799 800 /** 801 * Uses RoughJs for drawing comic shapes. 802 */ 803 (function() 804 { 805 /** 806 * Adds handJiggle style (jiggle=n sets jiggle) 807 */ 808 function RoughCanvas(canvas, rc, shape) 809 { 810 this.canvas = canvas; 811 this.rc = rc; 812 this.shape = shape; 813 814 // Avoids "spikes" in the output 815 this.canvas.setLineJoin('round'); 816 this.canvas.setLineCap('round'); 817 818 this.originalBegin = this.canvas.begin; 819 this.canvas.begin = mxUtils.bind(this, RoughCanvas.prototype.begin); 820 821 this.originalEnd = this.canvas.end; 822 this.canvas.end = mxUtils.bind(this, RoughCanvas.prototype.end); 823 824 this.originalRect = this.canvas.rect; 825 this.canvas.rect = mxUtils.bind(this, RoughCanvas.prototype.rect); 826 827 this.originalRoundrect = this.canvas.roundrect; 828 this.canvas.roundrect = mxUtils.bind(this, RoughCanvas.prototype.roundrect); 829 830 this.originalEllipse = this.canvas.ellipse; 831 this.canvas.ellipse = mxUtils.bind(this, RoughCanvas.prototype.ellipse); 832 833 this.originalLineTo = this.canvas.lineTo; 834 this.canvas.lineTo = mxUtils.bind(this, RoughCanvas.prototype.lineTo); 835 836 this.originalMoveTo = this.canvas.moveTo; 837 this.canvas.moveTo = mxUtils.bind(this, RoughCanvas.prototype.moveTo); 838 839 this.originalQuadTo = this.canvas.quadTo; 840 this.canvas.quadTo = mxUtils.bind(this, RoughCanvas.prototype.quadTo); 841 842 this.originalCurveTo = this.canvas.curveTo; 843 this.canvas.curveTo = mxUtils.bind(this, RoughCanvas.prototype.curveTo); 844 845 this.originalArcTo = this.canvas.arcTo; 846 this.canvas.arcTo = mxUtils.bind(this, RoughCanvas.prototype.arcTo); 847 848 this.originalClose = this.canvas.close; 849 this.canvas.close = mxUtils.bind(this, RoughCanvas.prototype.close); 850 851 this.originalFill = this.canvas.fill; 852 this.canvas.fill = mxUtils.bind(this, RoughCanvas.prototype.fill); 853 854 this.originalStroke = this.canvas.stroke; 855 this.canvas.stroke = mxUtils.bind(this, RoughCanvas.prototype.stroke); 856 857 this.originalFillAndStroke = this.canvas.fillAndStroke; 858 this.canvas.fillAndStroke = mxUtils.bind(this, RoughCanvas.prototype.fillAndStroke); 859 860 this.path = []; 861 this.passThrough = false; 862 }; 863 864 RoughCanvas.prototype.moveOp = 'M'; 865 RoughCanvas.prototype.lineOp = 'L'; 866 RoughCanvas.prototype.quadOp = 'Q'; 867 RoughCanvas.prototype.curveOp = 'C'; 868 RoughCanvas.prototype.closeOp = 'Z'; 869 870 RoughCanvas.prototype.getStyle = function(stroke, fill) 871 { 872 // Random seed created from cell ID 873 var seed = 1; 874 875 if (this.shape.state != null) 876 { 877 var str = this.shape.state.cell.id; 878 879 if (str != null) 880 { 881 for (var i = 0; i < str.length; i++) 882 { 883 seed = ((seed << 5) - seed + str.charCodeAt(i)) << 0; 884 } 885 } 886 } 887 888 var style = {strokeWidth: this.canvas.state.strokeWidth, seed: seed, preserveVertices: true}; 889 var defs = this.rc.getDefaultOptions(); 890 891 if (stroke) 892 { 893 style.stroke = this.canvas.state.strokeColor === 'none' ? 'transparent' : this.canvas.state.strokeColor; 894 } 895 else 896 { 897 delete style.stroke; 898 } 899 900 var gradient = null; 901 style.filled = fill; 902 903 if (fill) 904 { 905 style.fill = this.canvas.state.fillColor === 'none' ? '' : this.canvas.state.fillColor; 906 gradient = this.canvas.state.gradientColor === 'none' ? null : this.canvas.state.gradientColor; 907 } 908 else 909 { 910 style.fill = ''; 911 } 912 913 // Applies cell style 914 style['bowing'] = mxUtils.getValue(this.shape.style, 'bowing', defs['bowing']); 915 style['hachureAngle'] = mxUtils.getValue(this.shape.style, 'hachureAngle', defs['hachureAngle']); 916 style['curveFitting'] = mxUtils.getValue(this.shape.style, 'curveFitting', defs['curveFitting']); 917 style['roughness'] = mxUtils.getValue(this.shape.style, 'jiggle', defs['roughness']); 918 style['simplification'] = mxUtils.getValue(this.shape.style, 'simplification', defs['simplification']); 919 style['disableMultiStroke'] = mxUtils.getValue(this.shape.style, 'disableMultiStroke', defs['disableMultiStroke']); 920 style['disableMultiStrokeFill'] = mxUtils.getValue(this.shape.style, 'disableMultiStrokeFill', defs['disableMultiStrokeFill']); 921 922 var hachureGap = mxUtils.getValue(this.shape.style, 'hachureGap', -1); 923 style['hachureGap'] = (hachureGap == 'auto') ? -1 : hachureGap; 924 style['dashGap'] = mxUtils.getValue(this.shape.style, 'dashGap', hachureGap); 925 style['dashOffset'] = mxUtils.getValue(this.shape.style, 'dashOffset', hachureGap); 926 style['zigzagOffset'] = mxUtils.getValue(this.shape.style, 'zigzagOffset', hachureGap); 927 928 var fillWeight = mxUtils.getValue(this.shape.style, 'fillWeight', -1); 929 style['fillWeight'] = (fillWeight == 'auto') ? -1 : fillWeight; 930 931 var fillStyle = mxUtils.getValue(this.shape.style, 'fillStyle', 'auto'); 932 933 if (fillStyle == 'auto') 934 { 935 var bg = mxUtils.hex2rgba((this.shape.state != null) ? 936 this.shape.state.view.graph.shapeBackgroundColor : 937 (Editor.isDarkMode() ? Editor.darkColor : '#ffffff')); 938 fillStyle = (style.fill != null && (gradient != null || (bg != null && 939 style.fill == bg))) ? 'solid' : defs['fillStyle']; 940 } 941 942 style['fillStyle'] = fillStyle; 943 944 return style; 945 }; 946 947 RoughCanvas.prototype.begin = function() 948 { 949 if (this.passThrough) 950 { 951 this.originalBegin.apply(this.canvas, arguments); 952 } 953 else 954 { 955 this.path = []; 956 } 957 }; 958 959 RoughCanvas.prototype.end = function() 960 { 961 if (this.passThrough) 962 { 963 this.originalEnd.apply(this.canvas, arguments); 964 } 965 else 966 { 967 // do nothing 968 } 969 }; 970 971 RoughCanvas.prototype.addOp = function() 972 { 973 if (this.path != null) 974 { 975 this.path.push(arguments[0]); 976 977 if (arguments.length > 2) 978 { 979 var s = this.canvas.state; 980 981 for (var i = 2; i < arguments.length; i += 2) 982 { 983 this.lastX = arguments[i - 1]; 984 this.lastY = arguments[i]; 985 986 this.path.push(this.canvas.format((this.lastX))); 987 this.path.push(this.canvas.format((this.lastY))); 988 } 989 } 990 } 991 }; 992 993 RoughCanvas.prototype.lineTo = function(endX, endY) 994 { 995 if (this.passThrough) 996 { 997 this.originalLineTo.apply(this.canvas, arguments); 998 } 999 else 1000 { 1001 this.addOp(this.lineOp, endX, endY); 1002 this.lastX = endX; 1003 this.lastY = endY; 1004 } 1005 }; 1006 1007 RoughCanvas.prototype.moveTo = function(endX, endY) 1008 { 1009 if (this.passThrough) 1010 { 1011 this.originalMoveTo.apply(this.canvas, arguments); 1012 } 1013 else 1014 { 1015 this.addOp(this.moveOp, endX, endY); 1016 this.lastX = endX; 1017 this.lastY = endY; 1018 this.firstX = endX; 1019 this.firstY = endY; 1020 } 1021 }; 1022 1023 RoughCanvas.prototype.close = function() 1024 { 1025 if (this.passThrough) 1026 { 1027 this.originalClose.apply(this.canvas, arguments); 1028 } 1029 else 1030 { 1031 this.addOp(this.closeOp); 1032 } 1033 }; 1034 1035 RoughCanvas.prototype.quadTo = function(x1, y1, x2, y2) 1036 { 1037 if (this.passThrough) 1038 { 1039 this.originalQuadTo.apply(this.canvas, arguments); 1040 } 1041 else 1042 { 1043 this.addOp(this.quadOp, x1, y1, x2, y2); 1044 this.lastX = x2; 1045 this.lastY = y2; 1046 } 1047 }; 1048 1049 RoughCanvas.prototype.curveTo = function(x1, y1, x2, y2, x3, y3) 1050 { 1051 if (this.passThrough) 1052 { 1053 this.originalCurveTo.apply(this.canvas, arguments); 1054 } 1055 else 1056 { 1057 this.addOp(this.curveOp, x1, y1, x2, y2, x3, y3); 1058 this.lastX = x3; 1059 this.lastY = y3; 1060 } 1061 }; 1062 1063 RoughCanvas.prototype.arcTo = function(rx, ry, angle, largeArcFlag, sweepFlag, x, y) 1064 { 1065 if (this.passThrough) 1066 { 1067 this.originalArcTo.apply(this.canvas, arguments); 1068 } 1069 else 1070 { 1071 var curves = mxUtils.arcToCurves(this.lastX, this.lastY, rx, ry, angle, largeArcFlag, sweepFlag, x, y); 1072 1073 if (curves != null) 1074 { 1075 for (var i = 0; i < curves.length; i += 6) 1076 { 1077 this.curveTo(curves[i], curves[i + 1], curves[i + 2], 1078 curves[i + 3], curves[i + 4], curves[i + 5]); 1079 } 1080 } 1081 1082 this.lastX = x; 1083 this.lastY = y; 1084 } 1085 }; 1086 1087 RoughCanvas.prototype.rect = function(x, y, w, h) 1088 { 1089 if (this.passThrough) 1090 { 1091 this.originalRect.apply(this.canvas, arguments); 1092 } 1093 else 1094 { 1095 this.path = []; 1096 this.nextShape = this.rc.generator.rectangle(x, y, w, h, this.getStyle(true, true)); 1097 } 1098 }; 1099 1100 RoughCanvas.prototype.ellipse = function(x, y, w, h) 1101 { 1102 if (this.passThrough) 1103 { 1104 this.originalEllipse.apply(this.canvas, arguments); 1105 } 1106 else 1107 { 1108 this.path = []; 1109 this.nextShape = this.rc.generator.ellipse(x + w / 2, y + h / 2, w, h, this.getStyle(true, true)); 1110 } 1111 }; 1112 1113 RoughCanvas.prototype.roundrect = function(x, y, w, h, dx, dy) 1114 { 1115 if (this.passThrough) 1116 { 1117 this.originalRoundrect.apply(this.canvas, arguments); 1118 } 1119 else 1120 { 1121 this.begin(); 1122 this.moveTo(x + dx, y); 1123 this.lineTo(x + w - dx, y); 1124 this.quadTo(x + w, y, x + w, y + dy); 1125 this.lineTo(x + w, y + h - dy); 1126 this.quadTo(x + w, y + h, x + w - dx, y + h); 1127 this.lineTo(x + dx, y + h); 1128 this.quadTo(x, y + h, x, y + h - dy); 1129 this.lineTo(x, y + dy); 1130 this.quadTo(x, y, x + dx, y); 1131 } 1132 }; 1133 1134 RoughCanvas.prototype.drawPath = function(style) 1135 { 1136 if (this.path.length > 0) 1137 { 1138 this.passThrough = true; 1139 try 1140 { 1141 this.rc.path(this.path.join(' '), style); 1142 } 1143 catch (e) 1144 { 1145 // ignore 1146 } 1147 this.passThrough = false; 1148 } 1149 else if (this.nextShape != null) 1150 { 1151 for (var key in style) 1152 { 1153 this.nextShape.options[key] = style[key]; 1154 } 1155 1156 if (style['stroke'] == null) 1157 { 1158 delete this.nextShape.options['stroke']; 1159 } 1160 1161 if (!style.filled) 1162 { 1163 delete this.nextShape.options['fill']; 1164 } 1165 1166 this.passThrough = true; 1167 this.rc.draw(this.nextShape); 1168 this.passThrough = false; 1169 } 1170 }; 1171 1172 RoughCanvas.prototype.stroke = function() 1173 { 1174 if (this.passThrough) 1175 { 1176 this.originalStroke.apply(this.canvas, arguments); 1177 } 1178 else 1179 { 1180 this.drawPath(this.getStyle(true, false)); 1181 } 1182 }; 1183 1184 RoughCanvas.prototype.fill = function() 1185 { 1186 if (this.passThrough) 1187 { 1188 this.originalFill.apply(this.canvas, arguments); 1189 } 1190 else 1191 { 1192 this.drawPath(this.getStyle(false, true)); 1193 } 1194 }; 1195 1196 RoughCanvas.prototype.fillAndStroke = function() 1197 { 1198 if (this.passThrough) 1199 { 1200 this.originalFillAndStroke.apply(this.canvas, arguments); 1201 } 1202 else 1203 { 1204 this.drawPath(this.getStyle(true, true)); 1205 } 1206 }; 1207 1208 RoughCanvas.prototype.destroy = function() 1209 { 1210 this.canvas.lineTo = this.originalLineTo; 1211 this.canvas.moveTo = this.originalMoveTo; 1212 this.canvas.close = this.originalClose; 1213 this.canvas.quadTo = this.originalQuadTo; 1214 this.canvas.curveTo = this.originalCurveTo; 1215 this.canvas.arcTo = this.originalArcTo; 1216 this.canvas.close = this.originalClose; 1217 this.canvas.fill = this.originalFill; 1218 this.canvas.stroke = this.originalStroke; 1219 this.canvas.fillAndStroke = this.originalFillAndStroke; 1220 this.canvas.begin = this.originalBegin; 1221 this.canvas.end = this.originalEnd; 1222 this.canvas.rect = this.originalRect; 1223 this.canvas.ellipse = this.originalEllipse; 1224 this.canvas.roundrect = this.originalRoundrect; 1225 }; 1226 1227 // Returns a new HandJiggle canvas 1228 mxShape.prototype.createRoughCanvas = function(c) 1229 { 1230 return new RoughCanvas(c, Editor.createRoughCanvas(c), this); 1231 }; 1232 1233 // Overrides to include sketch style 1234 var shapeCreateHandJiggle = mxShape.prototype.createHandJiggle; 1235 mxShape.prototype.createHandJiggle = function(c) 1236 { 1237 if (!this.outline && this.style != null && mxUtils.getValue(this.style, 1238 'sketch', /*(urlParams['sketch'] != '1' && urlParams['rough'] == '1') ? 1239 '1' : */'0') != '0') 1240 { 1241 if (mxUtils.getValue(this.style, 'sketchStyle', 'rough') == 'comic') 1242 { 1243 return this.createComicCanvas(c); 1244 } 1245 else 1246 { 1247 return this.createRoughCanvas(c); 1248 } 1249 } 1250 else 1251 { 1252 return shapeCreateHandJiggle.apply(this, arguments); 1253 } 1254 }; 1255 1256 // Overrides for event handling on transparent background for sketch style 1257 var shapePaint = mxShape.prototype.paint; 1258 mxShape.prototype.paint = function(c) 1259 { 1260 var addTolerance = c.addTolerance; 1261 var events = true; 1262 1263 if (this.style != null) 1264 { 1265 events = mxUtils.getValue(this.style, mxConstants.STYLE_POINTER_EVENTS, '1') == '1'; 1266 } 1267 1268 if (c.handJiggle != null && c.handJiggle.constructor == RoughCanvas && !this.outline) 1269 { 1270 // Save needed for possible transforms applied during paint 1271 c.save(); 1272 var fill = this.fill; 1273 var stroke = this.stroke; 1274 this.fill = null; 1275 this.stroke = null; 1276 1277 var configurePointerEvents = this.configurePointerEvents; 1278 1279 // Ignores color changes during paint 1280 var setStrokeColor = c.setStrokeColor; 1281 1282 c.setStrokeColor = function() 1283 { 1284 // ignore 1285 }; 1286 1287 var setFillColor = c.setFillColor; 1288 1289 c.setFillColor = function() 1290 { 1291 // ignore 1292 }; 1293 1294 // Adds stroke tolerance for plain rendering if filled 1295 if (!events && fill != null) 1296 { 1297 this.configurePointerEvents = function() 1298 { 1299 // ignore 1300 }; 1301 } 1302 1303 c.handJiggle.passThrough = true; 1304 1305 shapePaint.apply(this, arguments); 1306 1307 c.handJiggle.passThrough = false; 1308 c.setFillColor = setFillColor; 1309 c.setStrokeColor = setStrokeColor; 1310 this.configurePointerEvents = configurePointerEvents; 1311 this.stroke = stroke; 1312 this.fill = fill; 1313 c.restore(); 1314 1315 // Bypasses stroke tolerance for sketched rendering if filled 1316 if (events && fill != null) 1317 { 1318 c.addTolerance = function() 1319 { 1320 // ignore 1321 }; 1322 } 1323 } 1324 1325 shapePaint.apply(this, arguments); 1326 c.addTolerance = addTolerance; 1327 }; 1328 1329 // Overrides glass effect to disable sketch style 1330 var shapePaintGlassEffect = mxShape.prototype.paintGlassEffect; 1331 mxShape.prototype.paintGlassEffect = function(c, x, y, w, h, arc) 1332 { 1333 if (c.handJiggle != null && c.handJiggle.constructor == RoughCanvas) 1334 { 1335 c.handJiggle.passThrough = true; 1336 shapePaintGlassEffect.apply(this, arguments); 1337 c.handJiggle.passThrough = false; 1338 } 1339 else 1340 { 1341 shapePaintGlassEffect.apply(this, arguments); 1342 } 1343 }; 1344 })(); 1345 1346 /** 1347 * Compresses the given string. 1348 */ 1349 Editor.fastCompress = function(data) 1350 { 1351 if (data == null || data.length == 0 || typeof(pako) === 'undefined') 1352 { 1353 return data; 1354 } 1355 else 1356 { 1357 return Graph.arrayBufferToString(pako.deflateRaw(data)); 1358 } 1359 }; 1360 1361 /** 1362 * Decompresses the given string. 1363 */ 1364 Editor.fastDecompress = function(data) 1365 { 1366 if (data == null || data.length == 0 || typeof(pako) === 'undefined') 1367 { 1368 return data; 1369 } 1370 else 1371 { 1372 return pako.inflateRaw(Graph.stringToArrayBuffer(atob(data)), {to: 'string'}); 1373 } 1374 }; 1375 1376 /** 1377 * Helper function to extract the graph model XML node. 1378 */ 1379 Editor.extractGraphModel = function(node, allowMxFile, checked) 1380 { 1381 if (node != null && typeof(pako) !== 'undefined') 1382 { 1383 var tmp = node.ownerDocument.getElementsByTagName('div'); 1384 var divs = []; 1385 1386 if (tmp != null && tmp.length > 0) 1387 { 1388 for (var i = 0; i < tmp.length; i++) 1389 { 1390 if (tmp[i].getAttribute('class') == 'mxgraph') 1391 { 1392 divs.push(tmp[i]); 1393 break; 1394 } 1395 } 1396 } 1397 1398 if (divs.length > 0) 1399 { 1400 var data = divs[0].getAttribute('data-mxgraph'); 1401 1402 if (data != null) 1403 { 1404 var config = JSON.parse(data); 1405 1406 if (config != null && config.xml != null) 1407 { 1408 var doc2 = mxUtils.parseXml(config.xml); 1409 node = doc2.documentElement; 1410 } 1411 } 1412 else 1413 { 1414 var divs2 = divs[0].getElementsByTagName('div'); 1415 1416 if (divs2.length > 0) 1417 { 1418 var data = mxUtils.getTextContent(divs2[0]); 1419 data = Graph.decompress(data, null, checked); 1420 1421 if (data.length > 0) 1422 { 1423 var doc2 = mxUtils.parseXml(data); 1424 node = doc2.documentElement; 1425 } 1426 } 1427 } 1428 } 1429 } 1430 1431 if (node != null && node.nodeName == 'svg') 1432 { 1433 var tmp = node.getAttribute('content'); 1434 1435 if (tmp != null && tmp.charAt(0) != '<' && tmp.charAt(0) != '%') 1436 { 1437 tmp = unescape((window.atob) ? atob(tmp) : Base64.decode(cont, tmp)); 1438 } 1439 1440 if (tmp != null && tmp.charAt(0) == '%') 1441 { 1442 tmp = decodeURIComponent(tmp); 1443 } 1444 1445 if (tmp != null && tmp.length > 0) 1446 { 1447 node = mxUtils.parseXml(tmp).documentElement; 1448 } 1449 else 1450 { 1451 throw {message: mxResources.get('notADiagramFile')}; 1452 } 1453 } 1454 1455 if (node != null && !allowMxFile) 1456 { 1457 var diagramNode = null; 1458 1459 if (node.nodeName == 'diagram') 1460 { 1461 diagramNode = node; 1462 } 1463 else if (node.nodeName == 'mxfile') 1464 { 1465 var diagrams = node.getElementsByTagName('diagram'); 1466 1467 if (diagrams.length > 0) 1468 { 1469 diagramNode = diagrams[Math.max(0, Math.min(diagrams.length - 1, urlParams['page'] || 0))]; 1470 } 1471 } 1472 1473 if (diagramNode != null) 1474 { 1475 node = Editor.parseDiagramNode(diagramNode, checked); 1476 } 1477 } 1478 1479 if (node != null && node.nodeName != 'mxGraphModel' && (!allowMxFile || node.nodeName != 'mxfile')) 1480 { 1481 node = null; 1482 } 1483 1484 return node; 1485 }; 1486 1487 /** 1488 * Extracts the XML from the compressed or non-compressed text chunk. 1489 */ 1490 Editor.parseDiagramNode = function(diagramNode, checked) 1491 { 1492 var text = mxUtils.trim(mxUtils.getTextContent(diagramNode)); 1493 var node = null; 1494 1495 if (text.length > 0) 1496 { 1497 var tmp = Graph.decompress(text, null, checked); 1498 1499 if (tmp != null && tmp.length > 0) 1500 { 1501 node = mxUtils.parseXml(tmp).documentElement; 1502 } 1503 } 1504 else 1505 { 1506 var temp = mxUtils.getChildNodes(diagramNode); 1507 1508 if (temp.length > 0) 1509 { 1510 // Creates new document for unique IDs within mxGraphModel 1511 var doc = mxUtils.createXmlDocument(); 1512 doc.appendChild(doc.importNode(temp[0], true)); 1513 node = doc.documentElement; 1514 } 1515 } 1516 1517 return node; 1518 }; 1519 1520 /** 1521 * Extracts the XML from the compressed or non-compressed text chunk. 1522 */ 1523 Editor.getDiagramNodeXml = function(diagramNode) 1524 { 1525 var text = mxUtils.getTextContent(diagramNode); 1526 var xml = null; 1527 1528 if (text.length > 0) 1529 { 1530 xml = Graph.decompress(text); 1531 } 1532 else if (diagramNode.firstChild != null) 1533 { 1534 xml = mxUtils.getXml(diagramNode.firstChild); 1535 } 1536 1537 return xml; 1538 }; 1539 1540 /** 1541 * Static method for parsing PDF files. 1542 */ 1543 Editor.extractGraphModelFromPdf = function(base64) 1544 { 1545 base64 = base64.substring(base64.indexOf(',') + 1); 1546 1547 // Workaround for invalid character error in Safari 1548 var f = (window.atob && !mxClient.IS_SF) ? atob(base64) : Base64.decode(base64, true); 1549 1550 //The new format of embedding diagram XML as embedded file (attachment) is in PDF 1.7 1551 if (f.substring(0, 8) == '%PDF-1.7') 1552 { 1553 var blockStart = f.indexOf('EmbeddedFile'); 1554 1555 if (blockStart > -1) 1556 { 1557 var streamStart = f.indexOf('stream', blockStart) + 9; //the start of the stream [skipping header check] 1558 var fileInfo = f.substring(blockStart, streamStart); 1559 1560 if (fileInfo.indexOf('application#2Fvnd.jgraph.mxfile') > 0) 1561 { 1562 var streamEnd = f.indexOf('endstream', streamStart - 1); 1563 1564 return pako.inflateRaw(Graph.stringToArrayBuffer(f.substring(streamStart, streamEnd)), {to: 'string'}); 1565 } 1566 } 1567 1568 //Not found 1569 return null; 1570 } 1571 1572 var check = '/Subject (%3Cmxfile'; 1573 var result = null; 1574 var curline = ''; 1575 var checked = 0; 1576 var pos = 0; 1577 var obj = []; 1578 var buf = null; 1579 var nr = null; 1580 1581 while (pos < f.length) 1582 { 1583 var b = f.charCodeAt(pos); 1584 pos += 1; 1585 1586 if (b != 10) 1587 { 1588 curline += String.fromCharCode(b); 1589 } 1590 1591 if (b == check.charCodeAt(checked)) 1592 { 1593 checked++; 1594 } 1595 else 1596 { 1597 checked = 0; 1598 } 1599 1600 if (checked == check.length) 1601 { 1602 var end = f.indexOf('%3C%2Fmxfile%3E)', pos) + 15; //15 is the length of encoded </mxfile> 1603 pos -= 9; //9 is the length of encoded <mxfile 1604 1605 // Default case is XML inlined in Subject metadata 1606 if (end > pos) 1607 { 1608 result = f.substring(pos, end); 1609 1610 break; 1611 } 1612 } 1613 1614 // Creates table for lookup if no inline data is found 1615 if (b == 10) 1616 { 1617 if (curline == 'endobj') 1618 { 1619 buf = null; 1620 } 1621 else if (curline.substring(curline.length - 3, curline.length) == 'obj' || 1622 curline == 'xref' || curline == 'trailer') 1623 { 1624 buf = []; 1625 obj[curline.split(' ')[0]] = buf; 1626 } 1627 else if (buf != null) 1628 { 1629 buf.push(curline); 1630 } 1631 1632 curline = ''; 1633 } 1634 } 1635 1636 // Extract XML via references 1637 if (result == null) 1638 { 1639 result = Editor.extractGraphModelFromXref(obj); 1640 } 1641 1642 if (result != null) 1643 { 1644 result = decodeURIComponent(result. 1645 replace(/\\\(/g, "("). 1646 replace(/\\\)/g, ")")); 1647 } 1648 1649 return result; 1650 }; 1651 1652 /** 1653 * Static method for extracting Subject via references of the form 1654 * 1655 * << /Size 33 /Root 20 0 R /Info 1 0 R and 1 0 obj << /Subject 22 0 R 1656 * 1657 * Where Info is the metadata block and Subject is the data block. 1658 */ 1659 Editor.extractGraphModelFromXref = function(obj) 1660 { 1661 var trailer = obj['trailer']; 1662 var result = null; 1663 1664 // Gets Info object 1665 if (trailer != null) 1666 { 1667 var arr = /.* \/Info (\d+) (\d+) R/g.exec(trailer.join('\n')); 1668 1669 if (arr != null && arr.length > 0) 1670 { 1671 var info = obj[arr[1]]; 1672 1673 if (info != null) 1674 { 1675 arr = /.* \/Subject (\d+) (\d+) R/g.exec(info.join('\n')); 1676 1677 if (arr != null && arr.length > 0) 1678 { 1679 var subj = obj[arr[1]]; 1680 1681 if (subj != null) 1682 { 1683 subj = subj.join('\n'); 1684 result = subj.substring(1, subj.length - 1); 1685 } 1686 } 1687 } 1688 } 1689 } 1690 1691 return result; 1692 }; 1693 1694 /** 1695 * Extracts the XML from the compressed or non-compressed text chunk. 1696 */ 1697 Editor.extractGraphModelFromPng = function(data) 1698 { 1699 var result = null; 1700 1701 try 1702 { 1703 var base64 = data.substring(data.indexOf(',') + 1); 1704 1705 // Workaround for invalid character error in Safari 1706 var binary = (window.atob && !mxClient.IS_SF) ? atob(base64) : Base64.decode(base64, true); 1707 1708 EditorUi.parsePng(binary, mxUtils.bind(this, function(pos, type, length) 1709 { 1710 var value = binary.substring(pos + 8, pos + 8 + length); 1711 1712 if (type == 'zTXt') 1713 { 1714 var idx = value.indexOf(String.fromCharCode(0)); 1715 1716 if (value.substring(0, idx) == 'mxGraphModel') 1717 { 1718 // Workaround for Java URL Encoder using + for spaces, which isn't compatible with JS 1719 var xmlData = pako.inflateRaw(Graph.stringToArrayBuffer( 1720 value.substring(idx + 2)), {to: 'string'}).replace(/\+/g,' '); 1721 1722 if (xmlData != null && xmlData.length > 0) 1723 { 1724 result = xmlData; 1725 } 1726 } 1727 } 1728 // Uncompressed section is normally not used 1729 else if (type == 'tEXt') 1730 { 1731 var vals = value.split(String.fromCharCode(0)); 1732 1733 if (vals.length > 1 && (vals[0] == 'mxGraphModel' || 1734 vals[0] == 'mxfile')) 1735 { 1736 result = vals[1]; 1737 } 1738 } 1739 1740 if (result != null || type == 'IDAT') 1741 { 1742 // Stops processing the file as our text chunks 1743 // are always placed before the data section 1744 return true; 1745 } 1746 })); 1747 } 1748 catch (e) 1749 { 1750 // ignores decoding errors 1751 } 1752 1753 if (result != null && result.charAt(0) == '%') 1754 { 1755 result = decodeURIComponent(result); 1756 } 1757 1758 // Workaround for double encoded content 1759 if (result != null && result.charAt(0) == '%') 1760 { 1761 result = decodeURIComponent(result); 1762 } 1763 1764 return result; 1765 }; 1766 1767 /** 1768 * Extracts any parsers errors in the given XML. 1769 */ 1770 Editor.extractParserError = function(node, defaultCause) 1771 { 1772 var cause = null; 1773 var errors = (node != null) ? node.getElementsByTagName('parsererror') : null; 1774 1775 if (errors != null && errors.length > 0) 1776 { 1777 cause = defaultCause || mxResources.get('invalidChars'); 1778 var divs = errors[0].getElementsByTagName('div'); 1779 1780 if (divs.length > 0) 1781 { 1782 cause = mxUtils.getTextContent(divs[0]); 1783 } 1784 } 1785 1786 return (cause != null) ? mxUtils.trim(cause) : cause; 1787 }; 1788 1789 /** 1790 * Adds the given retry function to the given error. 1791 */ 1792 Editor.addRetryToError = function(err, retry) 1793 { 1794 if (err != null) 1795 { 1796 var e = (err.error != null) ? err.error : err; 1797 1798 if (e.retry == null) 1799 { 1800 e.retry = retry; 1801 } 1802 } 1803 }; 1804 1805 /** 1806 * Global configuration of the Editor 1807 * see https://www.diagrams.net/doc/faq/configure-diagram-editor 1808 * 1809 * For defaultVertexStyle, defaultEdgeStyle and defaultLibraries, this must be called before 1810 * mxSettings.load via global config variable window.mxLoadSettings = false. 1811 */ 1812 Editor.configure = function(config, untrusted) 1813 { 1814 if (config != null) 1815 { 1816 Editor.config = config; 1817 Editor.configVersion = config.version; 1818 Menus.prototype.defaultFonts = config.defaultFonts || Menus.prototype.defaultFonts; 1819 ColorDialog.prototype.presetColors = config.presetColors || ColorDialog.prototype.presetColors; 1820 ColorDialog.prototype.defaultColors = config.defaultColors || ColorDialog.prototype.defaultColors; 1821 ColorDialog.prototype.colorNames = config.colorNames || ColorDialog.prototype.colorNames; 1822 StyleFormatPanel.prototype.defaultColorSchemes = config.defaultColorSchemes || StyleFormatPanel.prototype.defaultColorSchemes; 1823 Graph.prototype.defaultEdgeLength = config.defaultEdgeLength || Graph.prototype.defaultEdgeLength; 1824 DrawioFile.prototype.autosaveDelay = config.autosaveDelay || DrawioFile.prototype.autosaveDelay; 1825 1826 if (config.templateFile != null) 1827 { 1828 EditorUi.templateFile = config.templateFile; 1829 } 1830 1831 if (config.styles != null) 1832 { 1833 Editor.styles = config.styles; 1834 } 1835 1836 if (config.globalVars != null) 1837 { 1838 Editor.globalVars = config.globalVars; 1839 } 1840 1841 if (config.compressXml != null) 1842 { 1843 Editor.compressXml = config.compressXml; 1844 } 1845 1846 if (config.includeDiagram != null) 1847 { 1848 Editor.defaultIncludeDiagram = config.includeDiagram; 1849 } 1850 1851 if (config.simpleLabels != null) 1852 { 1853 Editor.simpleLabels = config.simpleLabels; 1854 } 1855 1856 if (config.oneDriveInlinePicker != null) 1857 { 1858 Editor.oneDriveInlinePicker = config.oneDriveInlinePicker; 1859 } 1860 1861 if (config.darkColor != null) 1862 { 1863 Editor.darkColor = config.darkColor; 1864 } 1865 1866 if (config.lightColor != null) 1867 { 1868 Editor.lightColor = config.lightColor; 1869 } 1870 1871 if (config.settingsName != null) 1872 { 1873 Editor.configurationKey = '.' + config.settingsName + '-configuration'; 1874 Editor.settingsKey = '.' + config.settingsName + '-config'; 1875 mxSettings.key = Editor.settingsKey; 1876 } 1877 1878 if (config.customFonts) 1879 { 1880 Menus.prototype.defaultFonts = config.customFonts. 1881 concat(Menus.prototype.defaultFonts); 1882 } 1883 1884 if (config.customPresetColors) 1885 { 1886 ColorDialog.prototype.presetColors = config.customPresetColors. 1887 concat(ColorDialog.prototype.presetColors); 1888 } 1889 1890 if (config.customColorSchemes != null) 1891 { 1892 StyleFormatPanel.prototype.defaultColorSchemes = config.customColorSchemes. 1893 concat(StyleFormatPanel.prototype.defaultColorSchemes); 1894 } 1895 1896 // Custom CSS injected directly into the page 1897 if (config.css != null) 1898 { 1899 var s = document.createElement('style'); 1900 s.setAttribute('type', 'text/css'); 1901 s.appendChild(document.createTextNode(config.css)); 1902 1903 var t = document.getElementsByTagName('script')[0]; 1904 t.parentNode.insertBefore(s, t); 1905 } 1906 1907 // Configures the custom libraries 1908 if (config.libraries != null) 1909 { 1910 Sidebar.prototype.customEntries = config.libraries; 1911 } 1912 1913 // Defines the enabled built-in libraries. 1914 if (config.enabledLibraries != null) 1915 { 1916 Sidebar.prototype.enabledLibraries = config.enabledLibraries; 1917 } 1918 1919 // Overrides default libraries 1920 if (config.defaultLibraries != null) 1921 { 1922 Sidebar.prototype.defaultEntries = config.defaultLibraries; 1923 } 1924 1925 // Overrides default custom libraries 1926 if (config.defaultCustomLibraries != null) 1927 { 1928 Editor.defaultCustomLibraries = config.defaultCustomLibraries; 1929 } 1930 1931 // Disables custom libraries 1932 if (config.enableCustomLibraries != null) 1933 { 1934 Editor.enableCustomLibraries = config.enableCustomLibraries; 1935 } 1936 1937 // Overrides default vertex style 1938 if (config.defaultVertexStyle != null) 1939 { 1940 Graph.prototype.defaultVertexStyle = config.defaultVertexStyle; 1941 } 1942 1943 // Overrides default edge style 1944 if (config.defaultEdgeStyle != null) 1945 { 1946 Graph.prototype.defaultEdgeStyle = config.defaultEdgeStyle; 1947 } 1948 1949 // Overrides default page visible 1950 if (config.defaultPageVisible != null) 1951 { 1952 Graph.prototype.defaultPageVisible = config.defaultPageVisible; 1953 } 1954 1955 // Overrides default grid enabled 1956 if (config.defaultGridEnabled != null) 1957 { 1958 Graph.prototype.defaultGridEnabled = config.defaultGridEnabled; 1959 } 1960 1961 // Overrides mouse wheel function 1962 if (config.zoomWheel != null) 1963 { 1964 Graph.zoomWheel = config.zoomWheel; 1965 } 1966 1967 // Overrides zoom factor 1968 if (config.zoomFactor != null) 1969 { 1970 var val = parseFloat(config.zoomFactor); 1971 1972 if (!isNaN(val) && val > 1) 1973 { 1974 Graph.prototype.zoomFactor = val; 1975 } 1976 else 1977 { 1978 EditorUi.debug('Invalid zoomFactor: value must be float > 1'); 1979 } 1980 } 1981 1982 // Overrides grid steps 1983 if (config.gridSteps != null) 1984 { 1985 var val = parseInt(config.gridSteps); 1986 1987 if (!isNaN(val) && val > 0) 1988 { 1989 mxGraphView.prototype.gridSteps = val; 1990 } 1991 else 1992 { 1993 EditorUi.debug('Invalid gridSteps: value must be int > 0'); 1994 } 1995 } 1996 1997 if (config.pageFormat != null) 1998 { 1999 var w = parseInt(config.pageFormat.width); 2000 var h = parseInt(config.pageFormat.height); 2001 2002 if (!isNaN(w) && w > 0 && !isNaN(h) && h > 0) 2003 { 2004 mxGraph.prototype.defaultPageFormat = new mxRectangle(0, 0, w, h); 2005 mxGraph.prototype.pageFormat = mxGraph.prototype.defaultPageFormat; 2006 } 2007 else 2008 { 2009 EditorUi.debug('Invalid pageFormat: value must be {width: int, height: int}'); 2010 } 2011 } 2012 2013 if (config.thumbWidth) 2014 { 2015 Sidebar.prototype.thumbWidth = config.thumbWidth; 2016 } 2017 2018 if (config.thumbHeight) 2019 { 2020 Sidebar.prototype.thumbHeight = config.thumbHeight; 2021 } 2022 2023 if (config.emptyLibraryXml) 2024 { 2025 EditorUi.prototype.emptyLibraryXml = config.emptyLibraryXml; 2026 } 2027 2028 if (config.emptyDiagramXml) 2029 { 2030 EditorUi.prototype.emptyDiagramXml = config.emptyDiagramXml; 2031 } 2032 2033 if (config.sidebarWidth) 2034 { 2035 EditorUi.prototype.hsplitPosition = config.sidebarWidth; 2036 } 2037 2038 if (config.sidebarTitles) 2039 { 2040 Sidebar.prototype.sidebarTitles = config.sidebarTitles; 2041 } 2042 2043 if (config.sidebarTitleSize) 2044 { 2045 var val = parseInt(config.sidebarTitleSize); 2046 2047 if (!isNaN(val) && val > 0) 2048 { 2049 Sidebar.prototype.sidebarTitleSize = val; 2050 } 2051 else 2052 { 2053 EditorUi.debug('Invalid sidebarTitleSize: value must be int > 0'); 2054 } 2055 } 2056 2057 if (config.fontCss) 2058 { 2059 if (typeof config.fontCss === 'string') 2060 { 2061 Editor.configureFontCss(config.fontCss); 2062 } 2063 else 2064 { 2065 EditorUi.debug('Invalid fontCss: value must be string'); 2066 } 2067 } 2068 2069 if (config.autosaveDelay != null) 2070 { 2071 var val = parseInt(config.autosaveDelay); 2072 2073 if (!isNaN(val) && val > 0) 2074 { 2075 DrawioFile.prototype.autosaveDelay = val; 2076 } 2077 else 2078 { 2079 EditorUi.debug('Invalid autosaveDelay: value must be int > 0'); 2080 } 2081 } 2082 2083 if (config.plugins != null && !untrusted) 2084 { 2085 // Required for callback 2086 App.initPluginCallback(); 2087 2088 for (var i = 0; i < config.plugins.length; i++) 2089 { 2090 mxscript(config.plugins[i]); 2091 } 2092 } 2093 2094 if(config.maxImageBytes != null) 2095 { 2096 EditorUi.prototype.maxImageBytes = config.maxImageBytes; 2097 } 2098 2099 if(config.maxImageSize != null) 2100 { 2101 EditorUi.prototype.maxImageSize = config.maxImageSize; 2102 } 2103 } 2104 }; 2105 2106 /** 2107 * Adds the global fontCss configuration. 2108 */ 2109 Editor.configureFontCss = function(fontCss) 2110 { 2111 if (fontCss != null) 2112 { 2113 Editor.prototype.fontCss = fontCss; 2114 var t = document.getElementsByTagName('script')[0]; 2115 2116 if (t != null && t.parentNode != null) 2117 { 2118 var s = document.createElement('style'); 2119 s.setAttribute('type', 'text/css'); 2120 s.appendChild(document.createTextNode(fontCss)); 2121 t.parentNode.insertBefore(s, t); 2122 2123 // Preloads fonts where supported 2124 var parts = fontCss.split('url('); 2125 2126 for (var i = 1; i < parts.length; i++) 2127 { 2128 var idx = parts[i].indexOf(')'); 2129 var url = Editor.trimCssUrl(parts[i].substring(0, idx)); 2130 2131 var l = document.createElement('link'); 2132 l.setAttribute('rel', 'preload'); 2133 l.setAttribute('href', url); 2134 l.setAttribute('as', 'font'); 2135 l.setAttribute('crossorigin', ''); 2136 2137 t.parentNode.insertBefore(l, t); 2138 } 2139 } 2140 } 2141 }; 2142 2143 /** 2144 * Strips leading and trailing quotes and spaces 2145 */ 2146 Editor.trimCssUrl = function(str) 2147 { 2148 return str.replace(new RegExp("^[\\s\"']+", "g"), "").replace(new RegExp("[\\s\"']+$", "g"), ""); 2149 } 2150 2151 /** 2152 * Prefix for URLs that reference Google fonts. 2153 */ 2154 Editor.GOOGLE_FONTS = 'https://fonts.googleapis.com/css?family='; 2155 2156 /** 2157 * Alphabet for global unique IDs. 2158 */ 2159 Editor.GUID_ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_'; 2160 2161 /** 2162 * Default length for global unique IDs. 2163 */ 2164 Editor.GUID_LENGTH = 20; 2165 2166 /** 2167 * Default length for global unique IDs. 2168 */ 2169 Editor.guid = function(length) 2170 { 2171 var len = (length != null) ? length : Editor.GUID_LENGTH; 2172 var rtn = []; 2173 2174 for (var i = 0; i < len; i++) 2175 { 2176 rtn.push(Editor.GUID_ALPHABET.charAt(Math.floor(Math.random() * Editor.GUID_ALPHABET.length))); 2177 } 2178 2179 return rtn.join(''); 2180 }; 2181 2182 /** 2183 * General timeout is 25 seconds. 2184 */ 2185 Editor.prototype.timeout = 25000; 2186 2187 /** 2188 * Mathjax output ignores CSS transforms in Safari (lightbox and normal mode). 2189 * Check the following test case on page 2 before enabling this in production: 2190 * https://devhost.jgraph.com/git/drawio/etc/embed/sf-math-fo-clipping.html?dev=1 2191 * UPDATE: Fixed via position:static CSS override in initMath. 2192 */ 2193 Editor.prototype.useForeignObjectForMath = true; 2194 2195 /** 2196 * Executes the first step for connecting to Google Drive. 2197 */ 2198 Editor.prototype.editButtonLink = (urlParams['edit'] != null) ? decodeURIComponent(urlParams['edit']) : null; 2199 2200 /** 2201 * Specifies if img.crossOrigin is supported. This is true for all browsers except IE10 and earlier. 2202 */ 2203 Editor.prototype.crossOriginImages = !mxClient.IS_IE; 2204 2205 /** 2206 * Adds support for old stylesheets and compressed files 2207 */ 2208 var editorSetGraphXml = Editor.prototype.setGraphXml; 2209 Editor.prototype.setGraphXml = function(node) 2210 { 2211 node = (node != null && node.nodeName != 'mxlibrary') ? this.extractGraphModel(node) : null; 2212 2213 if (node != null) 2214 { 2215 // Checks input for parser errors 2216 var errs = node.getElementsByTagName('parsererror'); 2217 2218 if (errs != null && errs.length > 0) 2219 { 2220 var elt = errs[0]; 2221 var divs = elt.getElementsByTagName('div'); 2222 2223 if (divs != null && divs.length > 0) 2224 { 2225 elt = divs[0]; 2226 } 2227 2228 throw {message: mxUtils.getTextContent(elt)}; 2229 } 2230 else if (node.nodeName == 'mxGraphModel') 2231 { 2232 var style = node.getAttribute('style') || 'default-style2'; 2233 2234 // Decodes the style if required 2235 if (urlParams['embed'] != '1' && (style == null || style == '')) 2236 { 2237 var node2 = (this.graph.themes != null) ? 2238 this.graph.themes['default-old'] : 2239 mxUtils.load(STYLE_PATH + '/default-old.xml').getDocumentElement(); 2240 2241 if (node2 != null) 2242 { 2243 var dec2 = new mxCodec(node2.ownerDocument); 2244 dec2.decode(node2, this.graph.getStylesheet()); 2245 } 2246 } 2247 else if (style != this.graph.currentStyle) 2248 { 2249 var node2 = (this.graph.themes != null) ? 2250 this.graph.themes[style] : 2251 mxUtils.load(STYLE_PATH + '/' + style + '.xml').getDocumentElement() 2252 2253 if (node2 != null) 2254 { 2255 var dec2 = new mxCodec(node2.ownerDocument); 2256 dec2.decode(node2, this.graph.getStylesheet()); 2257 } 2258 } 2259 2260 this.graph.currentStyle = style; 2261 this.graph.mathEnabled = (urlParams['math'] == '1' || node.getAttribute('math') == '1'); 2262 2263 var bgImg = node.getAttribute('backgroundImage'); 2264 2265 if (bgImg != null) 2266 { 2267 this.graph.setBackgroundImage(this.graph.parseBackgroundImage(bgImg)); 2268 } 2269 else 2270 { 2271 this.graph.setBackgroundImage(null); 2272 } 2273 2274 mxClient.NO_FO = ((this.graph.mathEnabled && !this.useForeignObjectForMath)) ? 2275 true : this.originalNoForeignObject; 2276 2277 this.graph.useCssTransforms = !mxClient.NO_FO && 2278 this.isChromelessView() && 2279 this.graph.isCssTransformsSupported(); 2280 this.graph.updateCssTransform(); 2281 2282 this.graph.setShadowVisible(node.getAttribute('shadow') == '1', false); 2283 2284 var extFonts = node.getAttribute('extFonts'); 2285 2286 if (extFonts) 2287 { 2288 try 2289 { 2290 extFonts = extFonts.split('|').map(function(ef) 2291 { 2292 var parts = ef.split('^'); 2293 return {name: parts[0], url: parts[1]}; 2294 }); 2295 2296 for (var i = 0; i < extFonts.length; i++) 2297 { 2298 this.graph.addExtFont(extFonts[i].name, extFonts[i].url); 2299 } 2300 } 2301 catch(e) 2302 { 2303 console.log('ExtFonts format error: ' + e.message); 2304 } 2305 } 2306 else if (this.graph.extFonts != null && this.graph.extFonts.length > 0) 2307 { 2308 this.graph.extFonts = []; 2309 } 2310 } 2311 2312 // Calls updateGraphComponents 2313 editorSetGraphXml.apply(this, arguments); 2314 } 2315 else 2316 { 2317 throw { 2318 message: mxResources.get('notADiagramFile') || 'Invalid data', 2319 toString: function() { return this.message; } 2320 }; 2321 } 2322 }; 2323 2324 /** 2325 * Adds persistent style to file 2326 */ 2327 var editorGetGraphXml = Editor.prototype.getGraphXml; 2328 Editor.prototype.getGraphXml = function(ignoreSelection, resolveReferences) 2329 { 2330 ignoreSelection = (ignoreSelection != null) ? ignoreSelection : true; 2331 var node = editorGetGraphXml.apply(this, arguments); 2332 2333 // Adds the current style 2334 if (this.graph.currentStyle != null && this.graph.currentStyle != 'default-style2') 2335 { 2336 node.setAttribute('style', this.graph.currentStyle); 2337 } 2338 2339 var bgImg = this.graph.getBackgroundImageObject( 2340 this.graph.backgroundImage, 2341 resolveReferences); 2342 2343 // Adds the background image 2344 if (bgImg != null) 2345 { 2346 node.setAttribute('backgroundImage', JSON.stringify(bgImg)); 2347 } 2348 2349 node.setAttribute('math', (this.graph.mathEnabled) ? '1' : '0'); 2350 node.setAttribute('shadow', (this.graph.shadowVisible) ? '1' : '0'); 2351 2352 if (this.graph.extFonts != null && this.graph.extFonts.length > 0) 2353 { 2354 var strExtFonts = this.graph.extFonts.map(function(ef) 2355 { 2356 return ef.name + '^' + ef.url; 2357 }); 2358 2359 node.setAttribute('extFonts', strExtFonts.join('|')); 2360 } 2361 2362 return node; 2363 }; 2364 2365 /** 2366 * Helper function to extract the graph model XML node. 2367 */ 2368 Editor.prototype.isDataSvg = function(svg) 2369 { 2370 try 2371 { 2372 var svgRoot = mxUtils.parseXml(svg).documentElement; 2373 var tmp = svgRoot.getAttribute('content'); 2374 2375 if (tmp != null) 2376 { 2377 if (tmp != null && tmp.charAt(0) != '<' && tmp.charAt(0) != '%') 2378 { 2379 tmp = unescape((window.atob) ? atob(tmp) : Base64.decode(cont, tmp)); 2380 } 2381 2382 if (tmp != null && tmp.charAt(0) == '%') 2383 { 2384 tmp = decodeURIComponent(tmp); 2385 } 2386 2387 if (tmp != null && tmp.length > 0) 2388 { 2389 var node = mxUtils.parseXml(tmp).documentElement; 2390 2391 2392 return node.nodeName == 'mxfile' || node.nodeName == 'mxGraphModel'; 2393 } 2394 } 2395 } 2396 catch (e) 2397 { 2398 // ignore 2399 } 2400 2401 return false; 2402 }; 2403 2404 /** 2405 * Helper function to extract the graph model XML node. 2406 */ 2407 Editor.prototype.extractGraphModel = function(node, allowMxFile, checked) 2408 { 2409 return Editor.extractGraphModel.apply(this, arguments); 2410 }; 2411 2412 /** 2413 * Overrides reset graph. 2414 */ 2415 var editorResetGraph = Editor.prototype.resetGraph; 2416 Editor.prototype.resetGraph = function() 2417 { 2418 this.graph.mathEnabled = (urlParams['math'] == '1'); 2419 this.graph.view.x0 = null; 2420 this.graph.view.y0 = null; 2421 mxClient.NO_FO = ((this.graph.mathEnabled && !this.useForeignObjectForMath)) ? 2422 true : this.originalNoForeignObject; 2423 2424 this.graph.useCssTransforms = !mxClient.NO_FO && 2425 this.isChromelessView() && 2426 this.graph.isCssTransformsSupported(); 2427 this.graph.updateCssTransform(); 2428 2429 editorResetGraph.apply(this, arguments); 2430 }; 2431 2432 /** 2433 * Math support. 2434 */ 2435 var editorUpdateGraphComponents = Editor.prototype.updateGraphComponents; 2436 Editor.prototype.updateGraphComponents = function() 2437 { 2438 editorUpdateGraphComponents.apply(this, arguments); 2439 mxClient.NO_FO = ((this.graph.mathEnabled && !this.useForeignObjectForMath) && 2440 Editor.MathJaxRender != null) ? true : this.originalNoForeignObject; 2441 2442 this.graph.useCssTransforms = !mxClient.NO_FO && 2443 this.isChromelessView() && 2444 this.graph.isCssTransformsSupported(); 2445 this.graph.updateCssTransform(); 2446 }; 2447 2448 /** 2449 * Overrides relative position to fix clipping bug in Webkit. 2450 */ 2451 Editor.mathJaxWebkitCss = 'div.MathJax_SVG_Display { position: static; }\n' + 2452 'span.MathJax_SVG { position: static !important; }'; 2453 2454 /** 2455 * Initializes math typesetting and loads respective code. 2456 */ 2457 Editor.initMath = function(src, config) 2458 { 2459 if (typeof(window.MathJax) === 'undefined') 2460 { 2461 src = ((src != null) ? src : DRAW_MATH_URL + '/MathJax.js') + '?config=TeX-MML-AM_' + 2462 ((urlParams['math-output'] == 'html') ? 'HTMLorMML' : 'SVG') + '-full'; 2463 Editor.mathJaxQueue = []; 2464 2465 Editor.doMathJaxRender = function(container) 2466 { 2467 window.setTimeout(function() 2468 { 2469 if (container.style.visibility != 'hidden') 2470 { 2471 MathJax.Hub.Queue(['Typeset', MathJax.Hub, container]); 2472 } 2473 }, 0); 2474 }; 2475 2476 var font = (urlParams['math-font'] != null) ? 2477 decodeURIComponent(urlParams['math-font']) : 'TeX'; 2478 2479 config = (config != null) ? config : 2480 { 2481 'HTML-CSS': { 2482 availableFonts: [font], 2483 imageFont: null 2484 }, 2485 SVG: { 2486 font: font, 2487 // Needed for client-side export to work 2488 useFontCache: false 2489 }, 2490 // Ignores math in in-place editor 2491 tex2jax: { 2492 ignoreClass: 'mxCellEditor' 2493 }, 2494 asciimath2jax: { 2495 ignoreClass: 'mxCellEditor' 2496 } 2497 }; 2498 2499 // Disables global typesetting and messages on startup, adds queue for 2500 // asynchronous rendering while MathJax is loading 2501 window.MathJax = 2502 { 2503 skipStartupTypeset: true, 2504 showMathMenu: false, 2505 messageStyle: 'none', 2506 AuthorInit: function () 2507 { 2508 MathJax.Hub.Config(config); 2509 2510 MathJax.Hub.Register.StartupHook('Begin', function() 2511 { 2512 for (var i = 0; i < Editor.mathJaxQueue.length; i++) 2513 { 2514 Editor.doMathJaxRender(Editor.mathJaxQueue[i]); 2515 } 2516 }); 2517 } 2518 }; 2519 2520 // Adds global enqueue method for async rendering 2521 Editor.MathJaxRender = function(container) 2522 { 2523 // Initial rendering when MathJax finished loading 2524 if (typeof(MathJax) !== 'undefined' && typeof(MathJax.Hub) !== 'undefined') 2525 { 2526 Editor.doMathJaxRender(container); 2527 } 2528 else 2529 { 2530 Editor.mathJaxQueue.push(container); 2531 } 2532 }; 2533 2534 // Adds global clear queue method 2535 Editor.MathJaxClear = function() 2536 { 2537 Editor.mathJaxQueue = []; 2538 }; 2539 2540 // Updates math typesetting after changes 2541 var editorInit = Editor.prototype.init; 2542 2543 Editor.prototype.init = function() 2544 { 2545 editorInit.apply(this, arguments); 2546 2547 this.graph.addListener(mxEvent.SIZE, mxUtils.bind(this, function(sender, evt) 2548 { 2549 if (this.graph.container != null && this.graph.mathEnabled && !this.graph.blockMathRender) 2550 { 2551 Editor.MathJaxRender(this.graph.container); 2552 } 2553 })); 2554 }; 2555 2556 var tags = document.getElementsByTagName('script'); 2557 2558 if (tags != null && tags.length > 0) 2559 { 2560 var s = document.createElement('script'); 2561 s.setAttribute('type', 'text/javascript'); 2562 s.setAttribute('src', src); 2563 tags[0].parentNode.appendChild(s); 2564 } 2565 2566 // Workaround for zoomed math clipping in Webkit 2567 try 2568 { 2569 if (mxClient.IS_GC || mxClient.IS_SF) 2570 { 2571 var style = document.createElement('style') 2572 style.type = 'text/css'; 2573 style.innerHTML = Editor.mathJaxWebkitCss; 2574 document.getElementsByTagName('head')[0].appendChild(style); 2575 } 2576 } 2577 catch (e) 2578 { 2579 // ignore 2580 } 2581 } 2582 }; 2583 2584 /** 2585 * Return array of string values, or NULL if CSV string not well formed. 2586 */ 2587 Editor.prototype.csvToArray = function(text) 2588 { 2589 var re_valid = /^\s*(?:'[^'\\]*(?:\\[\S\s][^'\\]*)*'|"[^"\\]*(?:\\[\S\s][^"\\]*)*"|[^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)\s*(?:,\s*(?:'[^'\\]*(?:\\[\S\s][^'\\]*)*'|"[^"\\]*(?:\\[\S\s][^"\\]*)*"|[^,'"\s\\]*(?:\s+[^,'"\s\\]+)*)\s*)*$/; 2590 var re_value = /(?!\s*$)\s*(?:'([^'\\]*(?:\\[\S\s][^'\\]*)*)'|"([^"\\]*(?:\\[\S\s][^"\\]*)*)"|([^,'"\s\\]*(?:\s+[^,'"\s\\]+)*))\s*(?:,|$)/g; 2591 // Return NULL if input string is not well formed CSV string. 2592 if (!re_valid.test(text)) return null; 2593 var a = []; // Initialize array to receive values. 2594 text.replace(re_value, // "Walk" the string using replace with callback. 2595 function(m0, m1, m2, m3) { 2596 // Remove backslash from \' in single quoted values. 2597 if (m1 !== undefined) a.push(m1.replace(/\\'/g, "'")); 2598 // Remove backslash from \" in double quoted values. 2599 else if (m2 !== undefined) a.push(m2.replace(/\\"/g, '"')); 2600 else if (m3 !== undefined) a.push(m3); 2601 return ''; // Return empty string. 2602 }); 2603 // Handle special case of empty last value. 2604 if (/,\s*$/.test(text)) a.push(''); 2605 return a; 2606 }; 2607 2608 /** 2609 * Returns true if the given URL is known to have CORS headers and is 2610 * allowed by CSP. 2611 */ 2612 Editor.prototype.isCorsEnabledForUrl = function(url) 2613 { 2614 // Disables proxy for desktop and chrome app as it is served locally 2615 if (mxClient.IS_CHROMEAPP || EditorUi.isElectronApp) 2616 { 2617 return true; 2618 } 2619 2620 // Does not use proxy for same domain 2621 if (url.substring(0, window.location.origin.length) == window.location.origin) 2622 { 2623 return true; 2624 } 2625 2626 // Blocked by CSP in production but allowed for hosted deployment 2627 if (urlParams['cors'] != null && this.corsRegExp == null) 2628 { 2629 this.corsRegExp = new RegExp(decodeURIComponent(urlParams['cors'])); 2630 } 2631 2632 // No access-control-allow-origin for some Iconfinder images, add this when fixed: 2633 // /^https?:\/\/[^\/]*\.iconfinder.com\//.test(url) || 2634 return (this.corsRegExp != null && this.corsRegExp.test(url)) || 2635 url.substring(0, 34) === 'https://raw.githubusercontent.com/'; 2636 }; 2637 2638 2639 /** 2640 * Converts all images in the SVG output to data URIs for immediate rendering 2641 */ 2642 Editor.prototype.createImageUrlConverter = function() 2643 { 2644 var converter = new mxUrlConverter(); 2645 converter.updateBaseUrl(); 2646 2647 // Extends convert to avoid CORS using an image proxy server where needed 2648 var convert = converter.convert; 2649 var self = this; 2650 2651 converter.convert = function(src) 2652 { 2653 if (src != null) 2654 { 2655 var remote = src.substring(0, 7) == 'http://' || src.substring(0, 8) == 'https://'; 2656 2657 if (remote && !navigator.onLine) 2658 { 2659 src = Editor.svgBrokenImage.src; 2660 } 2661 else if (remote && src.substring(0, converter.baseUrl.length) != converter.baseUrl && 2662 (!self.crossOriginImages || !self.isCorsEnabledForUrl(src))) 2663 { 2664 src = PROXY_URL + '?url=' + encodeURIComponent(src); 2665 } 2666 else if (src.substring(0, 19) != 'chrome-extension://' && !mxClient.IS_CHROMEAPP) 2667 { 2668 src = convert.apply(this, arguments); 2669 } 2670 } 2671 2672 return src; 2673 }; 2674 2675 return converter; 2676 }; 2677 2678 /** 2679 * 2680 */ 2681 Editor.createSvgDataUri = function(svg) 2682 { 2683 return 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svg))); 2684 }; 2685 2686 /** 2687 * 2688 */ 2689 Editor.prototype.convertImageToDataUri = function(url, callback) 2690 { 2691 try 2692 { 2693 var acceptResponse = true; 2694 2695 var timeoutThread = window.setTimeout(mxUtils.bind(this, function() 2696 { 2697 acceptResponse = false; 2698 callback(Editor.svgBrokenImage.src); 2699 }), this.timeout); 2700 2701 if (/(\.svg)$/i.test(url)) 2702 { 2703 mxUtils.get(url, mxUtils.bind(this, function(req) 2704 { 2705 window.clearTimeout(timeoutThread); 2706 2707 if (acceptResponse) 2708 { 2709 callback(Editor.createSvgDataUri(req.getText())); 2710 } 2711 }), 2712 function() 2713 { 2714 window.clearTimeout(timeoutThread); 2715 2716 if (acceptResponse) 2717 { 2718 callback(Editor.svgBrokenImage.src); 2719 } 2720 }); 2721 } 2722 else 2723 { 2724 var img = new Image(); 2725 2726 if (this.crossOriginImages) 2727 { 2728 img.crossOrigin = 'anonymous'; 2729 } 2730 2731 img.onload = function() 2732 { 2733 window.clearTimeout(timeoutThread); 2734 2735 if (acceptResponse) 2736 { 2737 try 2738 { 2739 var canvas = document.createElement('canvas'); 2740 var ctx = canvas.getContext('2d'); 2741 canvas.height = img.height; 2742 canvas.width = img.width; 2743 ctx.drawImage(img, 0, 0); 2744 2745 callback(canvas.toDataURL()); 2746 } 2747 catch (e) 2748 { 2749 callback(Editor.svgBrokenImage.src); 2750 } 2751 } 2752 }; 2753 2754 img.onerror = function() 2755 { 2756 window.clearTimeout(timeoutThread); 2757 2758 if (acceptResponse) 2759 { 2760 callback(Editor.svgBrokenImage.src); 2761 } 2762 }; 2763 2764 img.src = url; 2765 } 2766 } 2767 catch (e) 2768 { 2769 callback(Editor.svgBrokenImage.src); 2770 } 2771 }; 2772 2773 2774 /** 2775 * Converts all images in the SVG output to data URIs for immediate rendering 2776 */ 2777 Editor.prototype.convertImages = function(svgRoot, callback, imageCache, converter) 2778 { 2779 // Converts images to data URLs for immediate painting 2780 if (converter == null) 2781 { 2782 converter = this.createImageUrlConverter(); 2783 } 2784 2785 // Barrier for asynchronous image loading 2786 var counter = 0; 2787 2788 function inc() 2789 { 2790 counter++; 2791 }; 2792 2793 function dec() 2794 { 2795 counter--; 2796 2797 if (counter == 0) 2798 { 2799 callback(svgRoot); 2800 } 2801 }; 2802 2803 var cache = imageCache || new Object(); 2804 2805 var convertImages = mxUtils.bind(this, function(tagName, srcAttr) 2806 { 2807 var images = svgRoot.getElementsByTagName(tagName); 2808 2809 for (var i = 0; i < images.length; i++) 2810 { 2811 (mxUtils.bind(this, function(img) 2812 { 2813 try 2814 { 2815 if (img != null) 2816 { 2817 var src = converter.convert(img.getAttribute(srcAttr)); 2818 2819 // Data URIs are pass-through 2820 if (src != null && src.substring(0, 5) != 'data:') 2821 { 2822 var tmp = cache[src]; 2823 2824 if (tmp == null) 2825 { 2826 inc(); 2827 2828 this.convertImageToDataUri(src, function(uri) 2829 { 2830 if (uri != null) 2831 { 2832 cache[src] = uri; 2833 img.setAttribute(srcAttr, uri); 2834 } 2835 2836 dec(); 2837 }); 2838 } 2839 else 2840 { 2841 img.setAttribute(srcAttr, tmp); 2842 } 2843 } 2844 else if (src != null) 2845 { 2846 img.setAttribute(srcAttr, src); 2847 } 2848 } 2849 } 2850 catch (e) 2851 { 2852 // ignore 2853 } 2854 }))(images[i]); 2855 } 2856 }); 2857 2858 // Converts all known image tags in output 2859 // LATER: Add support for images in CSS 2860 convertImages('image', 'xlink:href'); 2861 convertImages('img', 'src'); 2862 2863 // All from cache or no images 2864 if (counter == 0) 2865 { 2866 callback(svgRoot); 2867 } 2868 }; 2869 2870 /** 2871 * Base64 encodes the given string. This method seems to be more 2872 * robust for encoding PNG from binary AJAX responses. 2873 */ 2874 Editor.base64Encode = function(str) 2875 { 2876 var CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; 2877 var out = "", i = 0, len = str.length, c1, c2, c3; 2878 2879 while (i < len) 2880 { 2881 c1 = str.charCodeAt(i++) & 0xff; 2882 2883 if (i == len) 2884 { 2885 out += CHARS.charAt(c1 >> 2); 2886 out += CHARS.charAt((c1 & 0x3) << 4); 2887 out += "=="; 2888 break; 2889 } 2890 2891 c2 = str.charCodeAt(i++); 2892 2893 if (i == len) 2894 { 2895 out += CHARS.charAt(c1 >> 2); 2896 out += CHARS.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)); 2897 out += CHARS.charAt((c2 & 0xF) << 2); 2898 out += "="; 2899 break; 2900 } 2901 2902 c3 = str.charCodeAt(i++); 2903 out += CHARS.charAt(c1 >> 2); 2904 out += CHARS.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4)); 2905 out += CHARS.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6)); 2906 out += CHARS.charAt(c3 & 0x3F); 2907 } 2908 2909 return out; 2910 }; 2911 2912 /** 2913 * Checks if the client is authorized and calls the next step. 2914 */ 2915 Editor.prototype.loadUrl = function(url, success, error, forceBinary, retry, dataUriPrefix, noBinary, headers) 2916 { 2917 try 2918 { 2919 var binary = !noBinary && (forceBinary || /(\.png)($|\?)/i.test(url) || 2920 /(\.jpe?g)($|\?)/i.test(url) || /(\.gif)($|\?)/i.test(url) || 2921 /(\.pdf)($|\?)/i.test(url)); 2922 retry = (retry != null) ? retry : true; 2923 2924 var fn = mxUtils.bind(this, function() 2925 { 2926 mxUtils.get(url, mxUtils.bind(this, function(req) 2927 { 2928 if (req.getStatus() >= 200 && req.getStatus() <= 299) 2929 { 2930 if (success != null) 2931 { 2932 var data = req.getText(); 2933 2934 // Returns PNG as base64 encoded data URI 2935 if (binary) 2936 { 2937 // NOTE: This requires BinaryToArray VB script in the page 2938 if ((document.documentMode == 9 || document.documentMode == 10) && 2939 typeof window.mxUtilsBinaryToArray !== 'undefined') 2940 { 2941 var bin = mxUtilsBinaryToArray(req.request.responseBody).toArray(); 2942 var tmp = new Array(bin.length); 2943 2944 for (var i = 0; i < bin.length; i++) 2945 { 2946 tmp[i] = String.fromCharCode(bin[i]); 2947 } 2948 2949 data = tmp.join(''); 2950 } 2951 2952 // LATER: Could be JPG but modern browsers 2953 // ignore the mime type in the data URI 2954 dataUriPrefix = (dataUriPrefix != null) ? dataUriPrefix : 'data:image/png;base64,'; 2955 data = dataUriPrefix + Editor.base64Encode(data); 2956 } 2957 2958 success(data); 2959 } 2960 } 2961 else if (error != null) 2962 { 2963 if (req.getStatus() == 0) 2964 { 2965 // Handles CORS errors 2966 error({message: mxResources.get('accessDenied')}, req); 2967 } 2968 else 2969 { 2970 error({message: mxResources.get('error') + ' ' + req.getStatus()}, req); 2971 } 2972 } 2973 }), function(req) 2974 { 2975 if (error != null) 2976 { 2977 error({message: mxResources.get('error') + ' ' + req.getStatus()}); 2978 } 2979 }, binary, this.timeout, function() 2980 { 2981 if (retry && error != null) 2982 { 2983 error({code: App.ERROR_TIMEOUT, retry: fn}); 2984 } 2985 }, headers); 2986 }); 2987 2988 fn(); 2989 } 2990 catch (e) 2991 { 2992 if (error != null) 2993 { 2994 error(e); 2995 } 2996 } 2997 }; 2998 2999 /** 3000 * Makes all relative font URLs absolute in the given font CSS. 3001 */ 3002 Editor.prototype.absoluteCssFonts = function(fontCss) 3003 { 3004 var result = null; 3005 3006 if (fontCss != null) 3007 { 3008 var parts = fontCss.split('url('); 3009 3010 if (parts.length > 0) 3011 { 3012 result = [parts[0]]; 3013 3014 // Gets path for URL 3015 var path = window.location.pathname; 3016 var idx = (path != null) ? path.lastIndexOf('/') : -1; 3017 3018 if (idx >= 0) 3019 { 3020 path = path.substring(0, idx + 1); 3021 } 3022 3023 // Gets base tag from head 3024 var temp = document.getElementsByTagName('base'); 3025 var base = null; 3026 3027 if (temp != null && temp.length > 0) 3028 { 3029 base = temp[0].getAttribute('href'); 3030 } 3031 3032 for (var i = 1; i < parts.length; i++) 3033 { 3034 var idx = parts[i].indexOf(')'); 3035 3036 if (idx > 0) 3037 { 3038 var url = Editor.trimCssUrl(parts[i].substring(0, idx)); 3039 3040 if (this.graph.isRelativeUrl(url)) 3041 { 3042 url = (base != null) ? base + url : (window.location.protocol + '//' + window.location.hostname + 3043 ((url.charAt(0) == '/') ? '' : path) + url); 3044 } 3045 3046 result.push('url("' + url + '"' + parts[i].substring(idx)); 3047 } 3048 else 3049 { 3050 result.push(parts[i]); 3051 } 3052 } 3053 } 3054 else 3055 { 3056 result = [fontCss] 3057 } 3058 } 3059 3060 return (result != null) ? result.join('') : null; 3061 }; 3062 3063 /** 3064 * For the fonts in CSS to be applied when rendering images on canvas, the actual 3065 * font data must be made available via a data URI encoding of the file. 3066 */ 3067 Editor.prototype.embedCssFonts = function(fontCss, then) 3068 { 3069 var parts = fontCss.split('url('); 3070 var waiting = 0; 3071 3072 if (this.cachedFonts == null) 3073 { 3074 this.cachedFonts = {}; 3075 } 3076 3077 var finish = mxUtils.bind(this, function() 3078 { 3079 if (waiting == 0) 3080 { 3081 // Constructs string 3082 var result = [parts[0]]; 3083 3084 for (var j = 1; j < parts.length; j++) 3085 { 3086 var idx = parts[j].indexOf(')'); 3087 result.push('url("'); 3088 result.push(this.cachedFonts[Editor.trimCssUrl(parts[j].substring(0, idx))]); 3089 result.push('"' + parts[j].substring(idx)); 3090 } 3091 3092 then(result.join('')); 3093 } 3094 }); 3095 3096 if (parts.length > 0) 3097 { 3098 for (var i = 1; i < parts.length; i++) 3099 { 3100 var idx = parts[i].indexOf(')'); 3101 var format = null; 3102 3103 // Checks if there is a format directive 3104 var fmtIdx = parts[i].indexOf('format(', idx); 3105 3106 if (fmtIdx > 0) 3107 { 3108 format = Editor.trimCssUrl(parts[i].substring(fmtIdx + 7, parts[i].indexOf(')', fmtIdx))); 3109 } 3110 3111 (mxUtils.bind(this, function(url) 3112 { 3113 if (this.cachedFonts[url] == null) 3114 { 3115 // Mark font as being fetched and fetch it 3116 this.cachedFonts[url] = url; 3117 waiting++; 3118 3119 var mime = 'application/x-font-ttf'; 3120 3121 // See https://stackoverflow.com/questions/2871655/proper-mime-type-for-fonts 3122 if (format == 'svg' || /(\.svg)($|\?)/i.test(url)) 3123 { 3124 mime = 'image/svg+xml'; 3125 } 3126 else if (format == 'otf' || format == 'embedded-opentype' || /(\.otf)($|\?)/i.test(url)) 3127 { 3128 mime = 'application/x-font-opentype'; 3129 } 3130 else if (format == 'woff' || /(\.woff)($|\?)/i.test(url)) 3131 { 3132 mime = 'application/font-woff'; 3133 } 3134 else if (format == 'woff2' || /(\.woff2)($|\?)/i.test(url)) 3135 { 3136 mime = 'application/font-woff2'; 3137 } 3138 else if (format == 'eot' || /(\.eot)($|\?)/i.test(url)) 3139 { 3140 mime = 'application/vnd.ms-fontobject'; 3141 } 3142 else if (format == 'sfnt' || /(\.sfnt)($|\?)/i.test(url)) 3143 { 3144 mime = 'application/font-sfnt'; 3145 } 3146 3147 var realUrl = url; 3148 3149 if ((/^https?:\/\//.test(realUrl)) && !this.isCorsEnabledForUrl(realUrl)) 3150 { 3151 realUrl = PROXY_URL + '?url=' + encodeURIComponent(url); 3152 } 3153 3154 // LATER: Remove cache-control header 3155 this.loadUrl(realUrl, mxUtils.bind(this, function(uri) 3156 { 3157 this.cachedFonts[url] = uri; 3158 waiting--; 3159 finish(); 3160 }), mxUtils.bind(this, function(err) 3161 { 3162 // LATER: handle error 3163 waiting--; 3164 finish(); 3165 }), true, null, 'data:' + mime + ';charset=utf-8;base64,'); 3166 } 3167 }))(Editor.trimCssUrl(parts[i].substring(0, idx)), format); 3168 } 3169 3170 //In case all fonts are cached 3171 finish(); 3172 } 3173 else 3174 { 3175 //No font urls found 3176 then(fontCss); 3177 } 3178 }; 3179 3180 /** 3181 * For the fontCSS to be applied when rendering images on canvas, the actual 3182 * font data must be made available via a data URI encoding of the file. 3183 */ 3184 Editor.prototype.loadFonts = function(then) 3185 { 3186 if (this.fontCss != null && this.resolvedFontCss == null) 3187 { 3188 this.embedCssFonts(this.fontCss, mxUtils.bind(this, function(resolvedFontCss) 3189 { 3190 this.resolvedFontCss = resolvedFontCss; 3191 3192 if (then != null) 3193 { 3194 then(); 3195 } 3196 })); 3197 } 3198 else if (then != null) 3199 { 3200 then(); 3201 } 3202 }; 3203 3204 /** 3205 * Embeds external fonts 3206 */ 3207 Editor.prototype.embedExtFonts = function(callback) 3208 { 3209 var extFonts = this.graph.getCustomFonts(); 3210 3211 if (extFonts.length > 0) 3212 { 3213 var styleCnt = '', waiting = 0; 3214 3215 if (this.cachedGoogleFonts == null) 3216 { 3217 this.cachedGoogleFonts = {}; 3218 } 3219 3220 var googleCssDone = mxUtils.bind(this, function() 3221 { 3222 if (waiting == 0) 3223 { 3224 this.embedCssFonts(styleCnt, callback); 3225 } 3226 }); 3227 3228 for (var i = 0; i < extFonts.length; i++) 3229 { 3230 (mxUtils.bind(this, function(fontName, fontUrl) 3231 { 3232 if (Graph.isCssFontUrl(fontUrl)) 3233 { 3234 if (this.cachedGoogleFonts[fontUrl] == null) 3235 { 3236 waiting++; 3237 3238 this.loadUrl(fontUrl, mxUtils.bind(this, function(css) 3239 { 3240 this.cachedGoogleFonts[fontUrl] = css; 3241 styleCnt += css; 3242 waiting--; 3243 googleCssDone(); 3244 }), mxUtils.bind(this, function(err) 3245 { 3246 // LATER: handle error 3247 waiting--; 3248 styleCnt += '@import url(' + fontUrl + ');'; 3249 googleCssDone(); 3250 })); 3251 } 3252 else 3253 { 3254 styleCnt += this.cachedGoogleFonts[fontUrl]; 3255 } 3256 } 3257 else 3258 { 3259 styleCnt += '@font-face {' + 3260 'font-family: "' + fontName + '";' + 3261 'src: url("' + fontUrl + '")}'; 3262 } 3263 }))(extFonts[i].name, extFonts[i].url); 3264 } 3265 3266 googleCssDone(); 3267 } 3268 else 3269 { 3270 callback(); 3271 } 3272 }; 3273 3274 /** 3275 * Copies MathJax CSS into the SVG output. 3276 */ 3277 Editor.prototype.addMathCss = function(svgRoot) 3278 { 3279 var defs = svgRoot.getElementsByTagName('defs'); 3280 3281 if (defs != null && defs.length > 0) 3282 { 3283 var styles = document.getElementsByTagName('style'); 3284 3285 for (var i = 0; i < styles.length; i++) 3286 { 3287 // Ignores style elements with no MathJax CSS 3288 if (mxUtils.getTextContent(styles[i]).indexOf('MathJax') > 0) 3289 { 3290 defs[0].appendChild(styles[i].cloneNode(true)); 3291 } 3292 } 3293 } 3294 }; 3295 3296 /** 3297 * Adds the global fontCss configuration. 3298 */ 3299 Editor.prototype.addFontCss = function(svgRoot, fontCss) 3300 { 3301 fontCss = (fontCss != null) ? fontCss : this.absoluteCssFonts(this.fontCss); 3302 3303 // Creates defs element if not available 3304 if (fontCss != null) 3305 { 3306 var defs = svgRoot.getElementsByTagName('defs'); 3307 var svgDoc = svgRoot.ownerDocument; 3308 var defsElt = null; 3309 3310 if (defs.length == 0) 3311 { 3312 defsElt = (svgDoc.createElementNS != null) ? 3313 svgDoc.createElementNS(mxConstants.NS_SVG, 'defs') : 3314 svgDoc.createElement('defs'); 3315 3316 if (svgRoot.firstChild != null) 3317 { 3318 svgRoot.insertBefore(defsElt, svgRoot.firstChild); 3319 } 3320 else 3321 { 3322 svgRoot.appendChild(defsElt); 3323 } 3324 } 3325 else 3326 { 3327 defsElt = defs[0]; 3328 } 3329 3330 var style = (svgDoc.createElementNS != null) ? 3331 svgDoc.createElementNS(mxConstants.NS_SVG, 'style') : 3332 svgDoc.createElement('style'); 3333 style.setAttribute('type', 'text/css'); 3334 mxUtils.setTextContent(style, fontCss); 3335 defsElt.appendChild(style); 3336 } 3337 }; 3338 3339 /** 3340 * Disables client-side image export if math is enabled. 3341 */ 3342 Editor.prototype.isExportToCanvas = function() 3343 { 3344 return mxClient.IS_CHROMEAPP || this.useCanvasForExport; 3345 }; 3346 3347 /** 3348 * Returns the maximum possible scale for the given canvas dimension and scale. 3349 * This will return the given scale or the maximum scale that can be used to 3350 * generate a valid image in the current browser. 3351 * 3352 * See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas 3353 */ 3354 Editor.prototype.getMaxCanvasScale = function(w, h, scale) 3355 { 3356 var max = (mxClient.IS_FF) ? 8192 : 16384; 3357 3358 return Math.min(scale, Math.min(max / w, max / h)); 3359 }; 3360 3361 /** 3362 * 3363 */ 3364 Editor.prototype.exportToCanvas = function(callback, width, imageCache, background, error, limitHeight, 3365 ignoreSelection, scale, transparentBackground, addShadow, converter, graph, border, noCrop, grid, 3366 keepTheme, exportType, cells) 3367 { 3368 try 3369 { 3370 limitHeight = (limitHeight != null) ? limitHeight : true; 3371 ignoreSelection = (ignoreSelection != null) ? ignoreSelection : true; 3372 graph = (graph != null) ? graph : this.graph; 3373 border = (border != null) ? border : 0; 3374 3375 var bg = (transparentBackground) ? null : graph.background; 3376 3377 if (bg == mxConstants.NONE) 3378 { 3379 bg = null; 3380 } 3381 3382 if (bg == null) 3383 { 3384 bg = background; 3385 } 3386 3387 // Handles special case where background is null but transparent is false 3388 if (bg == null && transparentBackground == false) 3389 { 3390 bg = (keepTheme) ? this.graph.defaultPageBackgroundColor : '#ffffff'; 3391 } 3392 3393 this.convertImages(graph.getSvg(null, null, border, noCrop, null, ignoreSelection, 3394 null, null, null, addShadow, null, keepTheme, exportType, cells), 3395 mxUtils.bind(this, function(svgRoot) 3396 { 3397 try 3398 { 3399 var img = new Image(); 3400 3401 img.onload = mxUtils.bind(this, function() 3402 { 3403 try 3404 { 3405 var canvas = document.createElement('canvas'); 3406 var w = parseInt(svgRoot.getAttribute('width')); 3407 var h = parseInt(svgRoot.getAttribute('height')); 3408 scale = (scale != null) ? scale : 1; 3409 3410 if (width != null) 3411 { 3412 scale = (!limitHeight) ? width / w : Math.min(1, Math.min((width * 3) / (h * 4), width / w)); 3413 } 3414 3415 scale = this.getMaxCanvasScale(w, h, scale); 3416 w = Math.ceil(scale * w); 3417 h = Math.ceil(scale * h); 3418 3419 canvas.setAttribute('width', w); 3420 canvas.setAttribute('height', h); 3421 var ctx = canvas.getContext('2d'); 3422 3423 if (bg != null) 3424 { 3425 ctx.beginPath(); 3426 ctx.rect(0, 0, w, h); 3427 ctx.fillStyle = bg; 3428 ctx.fill(); 3429 } 3430 3431 if (scale != 1) 3432 { 3433 ctx.scale(scale, scale); 3434 } 3435 3436 function drawImage() 3437 { 3438 // Workaround for broken data URI images in Safari on first export 3439 if (mxClient.IS_SF) 3440 { 3441 window.setTimeout(function() 3442 { 3443 ctx.drawImage(img, 0, 0); 3444 callback(canvas, svgRoot); 3445 }, 0); 3446 } 3447 else 3448 { 3449 ctx.drawImage(img, 0, 0); 3450 callback(canvas, svgRoot); 3451 } 3452 }; 3453 3454 if (grid) 3455 { 3456 var view = graph.view; 3457 var curViewScale = view.scale; 3458 view.scale = 1; //Reset the scale temporary to generate unscaled grid image which is then scaled 3459 var gridImage = btoa(unescape(encodeURIComponent(view.createSvgGrid(view.gridColor)))); 3460 view.scale = curViewScale; 3461 gridImage = 'data:image/svg+xml;base64,' + gridImage; 3462 var phase = graph.gridSize * view.gridSteps * scale; 3463 3464 var b = graph.getGraphBounds(); 3465 var tx = view.translate.x * curViewScale; 3466 var ty = view.translate.y * curViewScale; 3467 var x0 = tx + (b.x - tx) / curViewScale - border; 3468 var y0 = ty + (b.y - ty) / curViewScale - border; 3469 3470 var background = new Image(); 3471 3472 background.onload = function() 3473 { 3474 try 3475 { 3476 var x = -Math.round(phase - mxUtils.mod((tx - x0) * scale, phase)); 3477 var y = -Math.round(phase - mxUtils.mod((ty - y0) * scale, phase)); 3478 3479 for (var i = x; i < w; i += phase) 3480 { 3481 for (var j = y; j < h; j += phase) 3482 { 3483 ctx.drawImage(background, i / scale, j / scale); 3484 } 3485 } 3486 3487 drawImage(); 3488 } 3489 catch (e) 3490 { 3491 if (error != null) 3492 { 3493 error(e); 3494 } 3495 } 3496 }; 3497 3498 background.onerror = function(e) 3499 { 3500 if (error != null) 3501 { 3502 error(e); 3503 } 3504 }; 3505 3506 background.src = gridImage; 3507 } 3508 else 3509 { 3510 drawImage(); 3511 } 3512 } 3513 catch (e) 3514 { 3515 if (error != null) 3516 { 3517 error(e); 3518 } 3519 } 3520 }); 3521 3522 img.onerror = function(e) 3523 { 3524 //console.log('img', e, img.src); 3525 3526 if (error != null) 3527 { 3528 error(e); 3529 } 3530 }; 3531 3532 if (addShadow) 3533 { 3534 this.graph.addSvgShadow(svgRoot); 3535 } 3536 3537 if (this.graph.mathEnabled) 3538 { 3539 this.addMathCss(svgRoot); 3540 } 3541 3542 var done = mxUtils.bind(this, function() 3543 { 3544 try 3545 { 3546 if (this.resolvedFontCss != null) 3547 { 3548 this.addFontCss(svgRoot, this.resolvedFontCss); 3549 } 3550 3551 img.src = Editor.createSvgDataUri(mxUtils.getXml(svgRoot)); 3552 } 3553 catch (e) 3554 { 3555 if (error != null) 3556 { 3557 error(e); 3558 } 3559 } 3560 }); 3561 3562 this.embedExtFonts(mxUtils.bind(this, function(extFontsEmbeddedCss) 3563 { 3564 try 3565 { 3566 if (extFontsEmbeddedCss != null) 3567 { 3568 this.addFontCss(svgRoot, extFontsEmbeddedCss); 3569 } 3570 3571 this.loadFonts(done); 3572 } 3573 catch (e) 3574 { 3575 if (error != null) 3576 { 3577 error(e); 3578 } 3579 } 3580 })); 3581 } 3582 catch (e) 3583 { 3584 //console.log('src', e, img.src); 3585 3586 if (error != null) 3587 { 3588 error(e); 3589 } 3590 } 3591 }), imageCache, converter); 3592 } 3593 catch (e) 3594 { 3595 if (error != null) 3596 { 3597 error(e); 3598 } 3599 } 3600 }; 3601 3602 Editor.crcTable = []; 3603 3604 for (var n = 0; n < 256; n++) 3605 { 3606 var c = n; 3607 3608 for (var k = 0; k < 8; k++) 3609 { 3610 if ((c & 1) == 1) 3611 { 3612 c = 0xedb88320 ^ (c >>> 1); 3613 } 3614 else 3615 { 3616 c >>>= 1; 3617 } 3618 3619 Editor.crcTable[n] = c; 3620 } 3621 } 3622 3623 Editor.updateCRC = function(crc, data, off, len) 3624 { 3625 var c = crc; 3626 3627 for (var n = 0; n < len; n++) 3628 { 3629 c = Editor.crcTable[(c ^ data.charCodeAt(off + n)) & 0xff] ^ (c >>> 8); 3630 } 3631 3632 return c; 3633 }; 3634 3635 Editor.crc32 = function(str) 3636 { 3637 var crc = 0 ^ (-1); 3638 3639 for (var i = 0; i < str.length; i++ ) 3640 { 3641 crc = (crc >>> 8) ^ Editor.crcTable[(crc ^ str.charCodeAt(i)) & 0xFF]; 3642 } 3643 3644 return (crc ^ (-1)) >>> 0; 3645 }; 3646 3647 /** 3648 * Adds the given text to the compressed or non-compressed text chunk. 3649 */ 3650 Editor.writeGraphModelToPng = function(data, type, key, value, error) 3651 { 3652 var base64 = data.substring(data.indexOf(',') + 1); 3653 var f = (window.atob) ? atob(base64) : Base64.decode(base64, true); 3654 var pos = 0; 3655 3656 function fread(d, count) 3657 { 3658 var start = pos; 3659 pos += count; 3660 3661 return d.substring(start, pos); 3662 }; 3663 3664 // Reads unsigned long 32 bit big endian 3665 function _freadint(d) 3666 { 3667 var bytes = fread(d, 4); 3668 3669 return bytes.charCodeAt(3) + (bytes.charCodeAt(2) << 8) + 3670 (bytes.charCodeAt(1) << 16) + (bytes.charCodeAt(0) << 24); 3671 }; 3672 3673 function writeInt(num) 3674 { 3675 return String.fromCharCode((num >> 24) & 0x000000ff, (num >> 16) & 0x000000ff, 3676 (num >> 8) & 0x000000ff, num & 0x000000ff); 3677 }; 3678 3679 // Checks signature 3680 if (fread(f,8) != String.fromCharCode(137) + 'PNG' + String.fromCharCode(13, 10, 26, 10)) 3681 { 3682 if (error != null) 3683 { 3684 error(); 3685 } 3686 3687 return; 3688 } 3689 3690 // Reads header chunk 3691 fread(f,4); 3692 3693 if (fread(f,4) != 'IHDR') 3694 { 3695 if (error != null) 3696 { 3697 error(); 3698 } 3699 3700 return; 3701 } 3702 3703 fread(f, 17); 3704 var result = f.substring(0, pos); 3705 3706 do 3707 { 3708 var n = _freadint(f); 3709 var chunk = fread(f,4); 3710 3711 if (chunk == 'IDAT') 3712 { 3713 result = f.substring(0, pos - 8); 3714 3715 if (type == 'pHYs' && key == 'dpi') 3716 { 3717 var dpm = Math.round(value / 0.0254); //One inch is equal to exactly 0.0254 meters. 3718 var chunkData = writeInt(dpm) + writeInt(dpm) + String.fromCharCode(1); 3719 } 3720 else 3721 { 3722 var chunkData = key + String.fromCharCode(0) + 3723 ((type == 'zTXt') ? String.fromCharCode(0) : '') + 3724 value; 3725 } 3726 3727 var crc = 0xffffffff; 3728 crc = Editor.updateCRC(crc, type, 0, 4); 3729 crc = Editor.updateCRC(crc, chunkData, 0, chunkData.length); 3730 3731 result += writeInt(chunkData.length) + type + chunkData + writeInt(crc ^ 0xffffffff); 3732 result += f.substring(pos - 8, f.length); 3733 3734 break; 3735 } 3736 3737 result += f.substring(pos - 8, pos - 4 + n); 3738 fread(f,n); 3739 fread(f,4); 3740 } 3741 while (n); 3742 3743 return 'data:image/png;base64,' + ((window.btoa) ? btoa(result) : Base64.encode(result, true)); 3744 }; 3745 3746 /** 3747 * Adds persistence for recent colors 3748 */ 3749 if (window.ColorDialog) 3750 { 3751 FilenameDialog.filenameHelpLink = 'https://www.diagrams.net/doc/faq/save-file-formats'; 3752 3753 var colorDialogAddRecentColor = ColorDialog.addRecentColor; 3754 3755 ColorDialog.addRecentColor = function(color, max) 3756 { 3757 colorDialogAddRecentColor.apply(this, arguments); 3758 3759 mxSettings.setRecentColors(ColorDialog.recentColors); 3760 mxSettings.save(); 3761 }; 3762 3763 var colorDialogResetRecentColors = ColorDialog.resetRecentColors; 3764 3765 ColorDialog.resetRecentColors = function() 3766 { 3767 colorDialogResetRecentColors.apply(this, arguments); 3768 3769 mxSettings.setRecentColors(ColorDialog.recentColors); 3770 mxSettings.save(); 3771 }; 3772 } 3773 3774 // Overrides ID for pages 3775 if (window.EditDataDialog) 3776 { 3777 EditDataDialog.getDisplayIdForCell = function(ui, cell) 3778 { 3779 var id = null; 3780 3781 if (ui.editor.graph.getModel().getParent(cell) != null) 3782 { 3783 id = cell.getId(); 3784 } 3785 else if (ui.currentPage != null) 3786 { 3787 id = ui.currentPage.getId(); 3788 } 3789 3790 return id; 3791 }; 3792 } 3793 3794 var AddCustomPropertyDialog = function(editorUi, callback) 3795 { 3796 var row, td; 3797 3798 var table = document.createElement('table'); 3799 var tbody = document.createElement('tbody'); 3800 table.setAttribute('cellpadding', (mxClient.IS_SF) ? '0' : '2'); 3801 3802 row = document.createElement('tr'); 3803 3804 td = document.createElement('td'); 3805 td.style.fontSize = '10pt'; 3806 td.style.width = '100px'; 3807 mxUtils.write(td, mxResources.get('name', null, 'Name') + ':'); 3808 3809 row.appendChild(td); 3810 3811 var nameInput = document.createElement('input'); 3812 nameInput.style.width = '180px'; 3813 3814 td = document.createElement('td'); 3815 td.appendChild(nameInput); 3816 row.appendChild(td); 3817 3818 tbody.appendChild(row); 3819 3820 row = document.createElement('tr'); 3821 3822 td = document.createElement('td'); 3823 td.style.fontSize = '10pt'; 3824 mxUtils.write(td, mxResources.get('type', null, 'Type') + ':'); 3825 3826 row.appendChild(td); 3827 3828 var typeSelect = document.createElement('select'); 3829 typeSelect.style.width = '180px'; 3830 3831 var boolOption = document.createElement('option'); 3832 boolOption.setAttribute('value', 'bool'); 3833 mxUtils.write(boolOption, mxResources.get('bool', null, 'Boolean')); 3834 typeSelect.appendChild(boolOption); 3835 3836 var clrOption = document.createElement('option'); 3837 clrOption.setAttribute('value', 'color'); 3838 mxUtils.write(clrOption, mxResources.get('color', null, 'Color')); 3839 typeSelect.appendChild(clrOption); 3840 3841 var enumOption = document.createElement('option'); 3842 enumOption.setAttribute('value', 'enum'); 3843 mxUtils.write(enumOption, mxResources.get('enum', null, 'Enumeration')); 3844 typeSelect.appendChild(enumOption); 3845 3846 var floatOption = document.createElement('option'); 3847 floatOption.setAttribute('value', 'float'); 3848 mxUtils.write(floatOption, mxResources.get('float', null, 'Float')); 3849 typeSelect.appendChild(floatOption); 3850 3851 var intOption = document.createElement('option'); 3852 intOption.setAttribute('value', 'int'); 3853 mxUtils.write(intOption, mxResources.get('int', null, 'Int')); 3854 typeSelect.appendChild(intOption); 3855 3856 var strOption = document.createElement('option'); 3857 strOption.setAttribute('value', 'string'); 3858 mxUtils.write(strOption, mxResources.get('string', null, 'String')); 3859 typeSelect.appendChild(strOption); 3860 3861 td = document.createElement('td'); 3862 td.appendChild(typeSelect); 3863 row.appendChild(td); 3864 3865 tbody.appendChild(row); 3866 3867 row = document.createElement('tr'); 3868 3869 td = document.createElement('td'); 3870 td.style.fontSize = '10pt'; 3871 mxUtils.write(td, mxResources.get('dispName', null, 'Display Name') + ':'); 3872 3873 row.appendChild(td); 3874 3875 var dispNameInput = document.createElement('input'); 3876 dispNameInput.style.width = '180px'; 3877 3878 td = document.createElement('td'); 3879 td.appendChild(dispNameInput); 3880 row.appendChild(td); 3881 3882 tbody.appendChild(row); 3883 3884 var listRow = document.createElement('tr'); 3885 3886 td = document.createElement('td'); 3887 td.style.fontSize = '10pt'; 3888 mxUtils.write(td, mxResources.get('enumList', null, 'Enum List') + ' (csv):'); 3889 3890 listRow.appendChild(td); 3891 3892 var enumListInput = document.createElement('input'); 3893 enumListInput.style.width = '180px'; 3894 3895 td = document.createElement('td'); 3896 td.appendChild(enumListInput); 3897 listRow.appendChild(td); 3898 3899 listRow.style.display = 'none'; 3900 tbody.appendChild(listRow); 3901 3902 table.appendChild(tbody); 3903 3904 function typeChanged() 3905 { 3906 if (typeSelect.value === 'enum') 3907 { 3908 listRow.style.display = ''; 3909 this.container.parentNode.style.height = "150px"; 3910 3911 } 3912 else 3913 { 3914 listRow.style.display = 'none'; 3915 this.container.parentNode.style.height = "130px"; 3916 } 3917 }; 3918 3919 mxEvent.addListener(typeSelect, 'change', mxUtils.bind(this, typeChanged)); 3920 3921 row = document.createElement('tr'); 3922 td = document.createElement('td'); 3923 td.setAttribute('align', 'right'); 3924 td.style.paddingTop = '22px'; 3925 td.colSpan = 2; 3926 3927 var addBtn = mxUtils.button(mxResources.get('add', null, 'Add'), mxUtils.bind(this, function() 3928 { 3929 var name = nameInput.value; 3930 3931 if (name == "") 3932 { 3933 nameInput.style.border = "1px solid red"; 3934 return; 3935 } 3936 3937 var type = typeSelect.value; 3938 var dispName = dispNameInput.value; 3939 3940 if (dispName == "") 3941 { 3942 dispNameInput.style.border = "1px solid red"; 3943 return; 3944 } 3945 3946 var enumList = enumListInput.value; 3947 3948 if (enumList == "" && type == "enum") 3949 { 3950 enumListInput.style.border = "1px solid red"; 3951 return; 3952 3953 } 3954 3955 if (enumList != null) 3956 { 3957 enumList = enumList.split(','); 3958 3959 for (var i = 0; i < enumList.length; i++) 3960 { 3961 enumList[i] = enumList[i].trim(); 3962 } 3963 } 3964 3965 if (callback) 3966 { 3967 callback(editorUi, name, type, dispName, enumList); 3968 editorUi.hideDialog(); 3969 } 3970 })); 3971 addBtn.className = 'geBtn gePrimaryBtn'; 3972 3973 var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() 3974 { 3975 editorUi.hideDialog(); 3976 }); 3977 cancelBtn.className = 'geBtn'; 3978 3979 if (editorUi.editor.cancelFirst) 3980 { 3981 td.appendChild(cancelBtn); 3982 td.appendChild(addBtn); 3983 } 3984 else 3985 { 3986 td.appendChild(addBtn); 3987 td.appendChild(cancelBtn); 3988 } 3989 3990 row.appendChild(td); 3991 tbody.appendChild(row); 3992 table.appendChild(tbody); 3993 this.container = table; 3994 }; 3995 3996 if (window.StyleFormatPanel != null) 3997 { 3998 var formatInit = Format.prototype.init; 3999 4000 Format.prototype.init = function() 4001 { 4002 formatInit.apply(this, arguments); 4003 4004 this.editorUi.editor.addListener('fileLoaded', this.update); 4005 }; 4006 4007 var formatRefresh = Format.prototype.refresh; 4008 4009 Format.prototype.refresh = function() 4010 { 4011 if (this.editorUi.getCurrentFile() != null || urlParams['embed'] == '1' || 4012 this.editorUi.editor.chromeless) 4013 { 4014 formatRefresh.apply(this, arguments); 4015 } 4016 else 4017 { 4018 this.clear(); 4019 } 4020 }; 4021 4022 /** 4023 * Hook for subclassers. 4024 */ 4025 DiagramFormatPanel.prototype.isShadowOptionVisible = function() 4026 { 4027 var file = this.editorUi.getCurrentFile(); 4028 4029 return urlParams['embed'] == '1' || (file != null && file.isEditable()); 4030 }; 4031 4032 /** 4033 * Option is not visible in default theme. 4034 */ 4035 DiagramFormatPanel.prototype.isMathOptionVisible = function(div) 4036 { 4037 return false; 4038 }; 4039 4040 /** 4041 * Add global shadow option. 4042 */ 4043 var diagramFormatPanelAddView = DiagramFormatPanel.prototype.addView; 4044 4045 DiagramFormatPanel.prototype.addView = function(div) 4046 { 4047 var div = diagramFormatPanelAddView.apply(this, arguments); 4048 var file = this.editorUi.getCurrentFile(); 4049 4050 if (mxClient.IS_SVG && this.isShadowOptionVisible()) 4051 { 4052 var ui = this.editorUi; 4053 var editor = ui.editor; 4054 var graph = editor.graph; 4055 4056 var option = this.createOption(mxResources.get('shadow'), function() 4057 { 4058 return graph.shadowVisible; 4059 }, function(checked) 4060 { 4061 var change = new ChangePageSetup(ui); 4062 change.ignoreColor = true; 4063 change.ignoreImage = true; 4064 change.shadowVisible = checked; 4065 4066 graph.model.execute(change); 4067 }, 4068 { 4069 install: function(apply) 4070 { 4071 this.listener = function() 4072 { 4073 apply(graph.shadowVisible); 4074 }; 4075 4076 ui.addListener('shadowVisibleChanged', this.listener); 4077 }, 4078 destroy: function() 4079 { 4080 ui.removeListener(this.listener); 4081 } 4082 }); 4083 4084 if (!Editor.enableShadowOption) 4085 { 4086 option.getElementsByTagName('input')[0].setAttribute('disabled', 'disabled'); 4087 mxUtils.setOpacity(option, 60); 4088 } 4089 4090 div.appendChild(option); 4091 } 4092 4093 return div; 4094 }; 4095 4096 /** 4097 * Adds autosave and math typesetting options. 4098 */ 4099 var diagramFormatPanelAddOptions = DiagramFormatPanel.prototype.addOptions; 4100 DiagramFormatPanel.prototype.addOptions = function(div) 4101 { 4102 div = diagramFormatPanelAddOptions.apply(this, arguments); 4103 4104 var ui = this.editorUi; 4105 var editor = ui.editor; 4106 var graph = editor.graph; 4107 4108 if (graph.isEnabled()) 4109 { 4110 var file = ui.getCurrentFile(); 4111 4112 if (file != null && file.isAutosaveOptional()) 4113 { 4114 var opt = this.createOption(mxResources.get('autosave'), function() 4115 { 4116 return ui.editor.autosave; 4117 }, function(checked) 4118 { 4119 ui.editor.setAutosave(checked); 4120 4121 if (ui.editor.autosave && file.isModified()) 4122 { 4123 file.fileChanged(); 4124 } 4125 }, 4126 { 4127 install: function(apply) 4128 { 4129 this.listener = function() 4130 { 4131 apply(ui.editor.autosave); 4132 }; 4133 4134 ui.editor.addListener('autosaveChanged', this.listener); 4135 }, 4136 destroy: function() 4137 { 4138 ui.editor.removeListener(this.listener); 4139 } 4140 }); 4141 4142 div.appendChild(opt); 4143 } 4144 } 4145 4146 if (this.isMathOptionVisible() && graph.isEnabled() && typeof(MathJax) !== 'undefined') 4147 { 4148 // Math 4149 var option = this.createOption(mxResources.get('mathematicalTypesetting'), function() 4150 { 4151 return graph.mathEnabled; 4152 }, function(checked) 4153 { 4154 ui.actions.get('mathematicalTypesetting').funct(); 4155 }, 4156 { 4157 install: function(apply) 4158 { 4159 this.listener = function() 4160 { 4161 apply(graph.mathEnabled); 4162 }; 4163 4164 ui.addListener('mathEnabledChanged', this.listener); 4165 }, 4166 destroy: function() 4167 { 4168 ui.removeListener(this.listener); 4169 } 4170 }); 4171 4172 option.style.paddingTop = '5px'; 4173 div.appendChild(option); 4174 4175 var help = ui.menus.createHelpLink('https://www.diagrams.net/doc/faq/math-typesetting'); 4176 help.style.position = 'relative'; 4177 help.style.marginLeft = '6px'; 4178 help.style.top = '2px'; 4179 option.appendChild(help); 4180 } 4181 4182 return div; 4183 }; 4184 4185 mxCellRenderer.prototype.defaultVertexShape.prototype.customProperties = [ 4186 {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE}, 4187 {name: 'absoluteArcSize', dispName: 'Abs. Arc Size', type: 'bool', defVal: false} 4188 ]; 4189 4190 mxCellRenderer.defaultShapes['link'].prototype.customProperties = [ 4191 {name: 'width', dispName: 'Width', type: 'float', min:0, defVal: 4} 4192 ]; 4193 4194 mxCellRenderer.defaultShapes['flexArrow'].prototype.customProperties = [ 4195 {name: 'width', dispName: 'Width', type: 'float', min:0, defVal: 10}, 4196 {name: 'startWidth', dispName: 'Start Width', type: 'float', min:0, defVal: 20}, 4197 {name: 'endWidth', dispName: 'End Width', type: 'float', min:0, defVal: 20} 4198 ]; 4199 4200 mxCellRenderer.defaultShapes['process'].prototype.customProperties = [ 4201 {name: 'size', dispName: 'Indent', type: 'float', min: 0, max: 0.5, defVal: 0.1} 4202 ]; 4203 4204 mxCellRenderer.defaultShapes['rhombus'].prototype.customProperties = [ 4205 {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, max: 50, defVal: mxConstants.LINE_ARCSIZE}, 4206 {name: 'double', dispName: 'Double', type: 'bool', defVal: false} 4207 ]; 4208 4209 mxCellRenderer.defaultShapes['partialRectangle'].prototype.customProperties = [ 4210 {name: 'top', dispName: 'Top Line', type: 'bool', defVal: true}, 4211 {name: 'bottom', dispName: 'Bottom Line', type: 'bool', defVal: true}, 4212 {name: 'left', dispName: 'Left Line', type: 'bool', defVal: true}, 4213 {name: 'right', dispName: 'Right Line', type: 'bool', defVal: true} 4214 ]; 4215 4216 mxCellRenderer.defaultShapes['parallelogram'].prototype.customProperties = [ 4217 {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE}, 4218 {name: 'size', dispName: 'Slope Angle', type: 'float', min:0, max: 1, defVal: 0.2} 4219 ]; 4220 4221 mxCellRenderer.defaultShapes['hexagon'].prototype.customProperties = [ 4222 {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE}, 4223 {name: 'size', dispName: 'Slope Angle', type: 'float', min:0, max: 1, defVal: 0.25} 4224 ]; 4225 4226 mxCellRenderer.defaultShapes['triangle'].prototype.customProperties = [ 4227 {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE} 4228 ]; 4229 4230 mxCellRenderer.defaultShapes['document'].prototype.customProperties = [ 4231 {name: 'size', dispName: 'Size', type: 'float', defVal: 0.3, min:0, max:1} 4232 ]; 4233 4234 mxCellRenderer.defaultShapes['internalStorage'].prototype.customProperties = [ 4235 {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE}, 4236 {name: 'dx', dispName: 'Left Line', type: 'float', min:0, defVal: 20}, 4237 {name: 'dy', dispName: 'Top Line', type: 'float', min:0, defVal: 20} 4238 ]; 4239 4240 mxCellRenderer.defaultShapes['cube'].prototype.customProperties = [ 4241 {name: 'size', dispName: 'Size', type: 'float', min:0, defVal:20 }, 4242 {name: 'darkOpacity', dispName: 'Dark Opacity', type: 'float', min:-1, max:1, defVal:0 }, 4243 {name: 'darkOpacity2', dispName: 'Dark Opacity 2', type: 'float', min:-1, max:1, defVal:0 } 4244 ]; 4245 4246 mxCellRenderer.defaultShapes['step'].prototype.customProperties = [ 4247 {name: 'size', dispName: 'Notch Size', type: 'float', min:0, defVal:20}, 4248 {name: 'fixedSize', dispName: 'Fixed Size', type: 'bool', defVal:true} 4249 ]; 4250 4251 mxCellRenderer.defaultShapes['trapezoid'].prototype.customProperties = [ 4252 {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE}, 4253 {name: 'size', dispName: 'Slope Angle', type: 'float', min:0, max: 1, defVal: 0.2} 4254 ]; 4255 4256 mxCellRenderer.defaultShapes['tape'].prototype.customProperties = [ 4257 {name: 'size', dispName: 'Size', type: 'float', min:0, max:1, defVal:0.4 } 4258 ]; 4259 4260 mxCellRenderer.defaultShapes['note'].prototype.customProperties = [ 4261 {name: 'size', dispName: 'Fold Size', type: 'float', min:0, defVal: 30}, 4262 {name: 'darkOpacity', dispName: 'Dark Opacity', type: 'float', min:-1, max:1, defVal:0 }, 4263 ]; 4264 4265 mxCellRenderer.defaultShapes['card'].prototype.customProperties = [ 4266 {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE}, 4267 {name: 'size', dispName: 'Cutoff Size', type: 'float', min:0, defVal: 30} 4268 ]; 4269 4270 mxCellRenderer.defaultShapes['callout'].prototype.customProperties = [ 4271 {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE}, 4272 {name: 'base', dispName: 'Callout Width', type: 'float', min:0, defVal: 20}, 4273 {name: 'size', dispName: 'Callout Length', type: 'float', min:0, defVal: 30}, 4274 {name: 'position', dispName: 'Callout Position', type: 'float', min:0, max:1, defVal: 0.5}, 4275 {name: 'position2', dispName: 'Callout Tip Position', type: 'float', min:0, max:1, defVal: 0.5} 4276 ]; 4277 4278 mxCellRenderer.defaultShapes['folder'].prototype.customProperties = [ 4279 {name: 'tabWidth', dispName: 'Tab Width', type: 'float'}, 4280 {name: 'tabHeight', dispName: 'Tab Height', type: 'float'}, 4281 {name: 'tabPosition', dispName: 'Tap Position', type: 'enum', 4282 enumList: [{val: 'left', dispName: 'Left'}, {val: 'right', dispName: 'Right'}] 4283 } 4284 ]; 4285 4286 mxCellRenderer.defaultShapes['swimlane'].prototype.customProperties = [ 4287 {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: 15}, 4288 {name: 'startSize', dispName: 'Header Size', type: 'float'}, 4289 {name: 'horizontal', dispName: 'Horizontal', type: 'bool', defVal: true}, 4290 {name: 'separatorColor', dispName: 'Separator Color', type: 'color', defVal: null}, 4291 ]; 4292 4293 mxCellRenderer.defaultShapes['table'].prototype.customProperties = [ 4294 {name: 'rowLines', dispName: 'Row Lines', type: 'bool', defVal: true}, 4295 {name: 'columnLines', dispName: 'Column Lines', type: 'bool', defVal: true}, 4296 {name: 'fixedRows', dispName: 'Fixed Rows', type: 'bool', defVal: false}, 4297 {name: 'resizeLast', dispName: 'Resize Last Column', type: 'bool', defVal: false}, 4298 {name: 'resizeLastRow', dispName: 'Resize Last Row', type: 'bool', defVal: false}]. 4299 concat(mxCellRenderer.defaultShapes['swimlane'].prototype.customProperties); 4300 4301 mxCellRenderer.defaultShapes['doubleEllipse'].prototype.customProperties = [ 4302 {name: 'margin', dispName: 'Indent', type: 'float', min:0, defVal:4} 4303 ]; 4304 4305 mxCellRenderer.defaultShapes['ext'].prototype.customProperties = [ 4306 {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: 15}, 4307 {name: 'double', dispName: 'Double', type: 'bool', defVal: false}, 4308 {name: 'margin', dispName: 'Indent', type: 'float', min: 0, defVal:0} 4309 ]; 4310 4311 mxCellRenderer.defaultShapes['curlyBracket'].prototype.customProperties = [ 4312 {name: 'rounded', dispName: 'Rounded', type: 'bool', defVal: true}, 4313 {name: 'size', dispName: 'Size', type: 'float', min:0, max: 1, defVal: 0.5} 4314 ]; 4315 4316 mxCellRenderer.defaultShapes['image'].prototype.customProperties = [ 4317 {name: 'imageAspect', dispName: 'Fixed Image Aspect', type: 'bool', defVal:true} 4318 ]; 4319 4320 mxCellRenderer.defaultShapes['label'].prototype.customProperties = [ 4321 {name: 'imageAspect', dispName: 'Fixed Image Aspect', type: 'bool', defVal:true}, 4322 {name: 'imageAlign', dispName: 'Image Align', type: 'enum', 4323 enumList: [{val: 'left', dispName: 'Left'}, 4324 {val: 'center', dispName: 'Center'}, 4325 {val: 'right', dispName: 'Right'}], defVal: 'left'}, 4326 {name: 'imageVerticalAlign', dispName: 'Image Vertical Align', type: 'enum', 4327 enumList: [{val: 'top', dispName: 'Top'}, 4328 {val: 'middle', dispName: 'Middle'}, 4329 {val: 'bottom', dispName: 'Bottom'}], defVal: 'middle'}, 4330 {name: 'imageWidth', dispName: 'Image Width', type: 'float', min:0, defVal: 24}, 4331 {name: 'imageHeight', dispName: 'Image Height', type: 'float', min:0, defVal: 24}, 4332 {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: 12}, 4333 {name: 'absoluteArcSize', dispName: 'Abs. Arc Size', type: 'bool', defVal: false} 4334 ]; 4335 4336 mxCellRenderer.defaultShapes['dataStorage'].prototype.customProperties = [ 4337 {name: 'size', dispName: 'Size', type: 'float', min:0, max:1, defVal:0.1 } 4338 ]; 4339 4340 mxCellRenderer.defaultShapes['manualInput'].prototype.customProperties = [ 4341 {name: 'size', dispName: 'Size', type: 'float', min:0, defVal:30 }, 4342 {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: 20} 4343 ]; 4344 4345 mxCellRenderer.defaultShapes['loopLimit'].prototype.customProperties = [ 4346 {name: 'size', dispName: 'Size', type: 'float', min:0, defVal:20 }, 4347 {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: 20} 4348 ]; 4349 4350 mxCellRenderer.defaultShapes['offPageConnector'].prototype.customProperties = [ 4351 {name: 'size', dispName: 'Size', type: 'float', min:0, defVal:38 }, 4352 {name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: 20} 4353 ]; 4354 4355 mxCellRenderer.defaultShapes['display'].prototype.customProperties = [ 4356 {name: 'size', dispName: 'Size', type: 'float', min: 0, max: 1, defVal: 0.25 } 4357 ]; 4358 4359 mxCellRenderer.defaultShapes['singleArrow'].prototype.customProperties = [ 4360 {name: 'arrowWidth', dispName: 'Arrow Width', type: 'float', min: 0, max: 1, defVal: 0.3 }, 4361 {name: 'arrowSize', dispName: 'Arrowhead Length', type: 'float', min: 0, max: 1, defVal: 0.2 } 4362 ]; 4363 4364 mxCellRenderer.defaultShapes['doubleArrow'].prototype.customProperties = [ 4365 {name: 'arrowWidth', dispName: 'Arrow Width', type: 'float', min: 0, max: 1, defVal: 0.3 }, 4366 {name: 'arrowSize', dispName: 'Arrowhead Length', type: 'float', min: 0, max: 1, defVal: 0.2 } 4367 ]; 4368 4369 mxCellRenderer.defaultShapes['cross'].prototype.customProperties = [ 4370 {name: 'size', dispName: 'Size', type: 'float', min: 0, max: 1, defVal: 0.2 } 4371 ]; 4372 4373 mxCellRenderer.defaultShapes['corner'].prototype.customProperties = [ 4374 {name: 'dx', dispName: 'Width1', type: 'float', min: 0, defVal: 20 }, 4375 {name: 'dy', dispName: 'Width2', type: 'float', min: 0, defVal: 20 } 4376 ]; 4377 4378 mxCellRenderer.defaultShapes['tee'].prototype.customProperties = [ 4379 {name: 'dx', dispName: 'Width1', type: 'float', min: 0, defVal: 20 }, 4380 {name: 'dy', dispName: 'Width2', type: 'float', min: 0, defVal: 20 } 4381 ]; 4382 4383 mxCellRenderer.defaultShapes['umlLifeline'].prototype.customProperties = [ 4384 {name: 'participant', dispName:'Participant', type:'enum', defVal:'none', enumList:[ 4385 {val:'none', dispName: 'Default'}, 4386 {val:'umlActor', dispName: 'Actor'}, 4387 {val:'umlBoundary', dispName: 'Boundary'}, 4388 {val:'umlEntity', dispName: 'Entity'}, 4389 {val:'umlControl', dispName: 'Control'}, 4390 ]}, 4391 {name: 'size', dispName:'Height', type:'float', defVal:40, min:0} 4392 ]; 4393 4394 mxCellRenderer.defaultShapes['umlFrame'].prototype.customProperties = [ 4395 {name: 'width', dispName:'Title Width', type:'float', defVal:60, min:0}, 4396 {name: 'height', dispName:'Title Height', type:'float', defVal:30, min:0} 4397 ]; 4398 4399 /** 4400 * Configures global color schemes. 4401 */ 4402 StyleFormatPanel.prototype.defaultColorSchemes = [[{fill: '', stroke: ''}, {fill: '#f5f5f5', stroke: '#666666', font: '#333333'}, 4403 {fill: '#dae8fc', stroke: '#6c8ebf'}, {fill: '#d5e8d4', stroke: '#82b366'}, 4404 {fill: '#ffe6cc', stroke: '#d79b00'}, {fill: '#fff2cc', stroke: '#d6b656'}, 4405 {fill: '#f8cecc', stroke: '#b85450'}, {fill: '#e1d5e7', stroke: '#9673a6'}], 4406 [{fill: '', stroke: ''}, {fill: '#60a917', stroke: '#2D7600', font: '#ffffff'}, 4407 {fill: '#008a00', stroke: '#005700', font: '#ffffff'}, {fill: '#1ba1e2', stroke: '#006EAF', font: '#ffffff'}, 4408 {fill: '#0050ef', stroke: '#001DBC', font: '#ffffff'}, {fill: '#6a00ff', stroke: '#3700CC', font: '#ffffff'}, 4409 //{fill: '#aa00ff', stroke: '#7700CC', font: '#ffffff'}, 4410 {fill: '#d80073', stroke: '#A50040', font: '#ffffff'}, {fill: '#a20025', stroke: '#6F0000', font: '#ffffff'}], 4411 [{fill: '#e51400', stroke: '#B20000', font: '#ffffff'}, {fill: '#fa6800', stroke: '#C73500', font: '#000000'}, 4412 {fill: '#f0a30a', stroke: '#BD7000', font: '#000000'}, {fill: '#e3c800', stroke: '#B09500', font: '#000000'}, 4413 {fill: '#6d8764', stroke: '#3A5431', font: '#ffffff'}, {fill: '#647687', stroke: '#314354', font: '#ffffff'}, 4414 {fill: '#76608a', stroke: '#432D57', font: '#ffffff'}, {fill: '#a0522d', stroke: '#6D1F00', font: '#ffffff'}], 4415 [{fill: '', stroke: ''}, {fill: mxConstants.NONE, stroke: ''}, 4416 {fill: '#fad7ac', stroke: '#b46504'}, {fill: '#fad9d5', stroke: '#ae4132'}, 4417 {fill: '#b0e3e6', stroke: '#0e8088'}, {fill: '#b1ddf0', stroke: '#10739e'}, 4418 {fill: '#d0cee2', stroke: '#56517e'}, {fill: '#bac8d3', stroke: '#23445d'}], 4419 [{fill: '', stroke: ''}, 4420 {fill: '#f5f5f5', stroke: '#666666', gradient: '#b3b3b3'}, 4421 {fill: '#dae8fc', stroke: '#6c8ebf', gradient: '#7ea6e0'}, 4422 {fill: '#d5e8d4', stroke: '#82b366', gradient: '#97d077'}, 4423 {fill: '#ffcd28', stroke: '#d79b00', gradient: '#ffa500'}, 4424 {fill: '#fff2cc', stroke: '#d6b656', gradient: '#ffd966'}, 4425 {fill: '#f8cecc', stroke: '#b85450', gradient: '#ea6b66'}, 4426 {fill: '#e6d0de', stroke: '#996185', gradient: '#d5739d'}], 4427 [{fill: '', stroke: ''}, {fill: '#eeeeee', stroke: '#36393d'}, 4428 {fill: '#f9f7ed', stroke: '#36393d'}, {fill: '#ffcc99', stroke: '#36393d'}, 4429 {fill: '#cce5ff', stroke: '#36393d'}, {fill: '#ffff88', stroke: '#36393d'}, 4430 {fill: '#cdeb8b', stroke: '#36393d'}, {fill: '#ffcccc', stroke: '#36393d'}]]; 4431 4432 /** 4433 * Configures custom color schemes. 4434 */ 4435 StyleFormatPanel.prototype.customColorSchemes = null; 4436 4437 StyleFormatPanel.prototype.findCommonProperties = function(cell, properties, addAll) 4438 { 4439 if (properties == null) return; 4440 4441 var handleCustomProp = function(custProperties) 4442 { 4443 if (custProperties != null) 4444 { 4445 if (addAll) 4446 { 4447 for (var i = 0; i < custProperties.length; i++) 4448 { 4449 properties[custProperties[i].name] = custProperties[i]; 4450 } 4451 } 4452 else 4453 { 4454 for (var key in properties) 4455 { 4456 var found = false; 4457 4458 for (var i = 0; i < custProperties.length; i++) 4459 { 4460 if (custProperties[i].name == key && custProperties[i].type == properties[key].type) 4461 { 4462 found = true; 4463 break; 4464 } 4465 } 4466 4467 if (!found) 4468 { 4469 delete properties[key]; 4470 } 4471 } 4472 } 4473 } 4474 }; 4475 4476 var view = this.editorUi.editor.graph.view; 4477 var state = view.getState(cell); 4478 4479 if (state != null && state.shape != null) 4480 { 4481 //Add common properties to all shapes 4482 if (!state.shape.commonCustomPropAdded) 4483 { 4484 state.shape.commonCustomPropAdded = true; 4485 state.shape.customProperties = state.shape.customProperties || []; 4486 4487 if (state.cell.vertex) 4488 { 4489 Array.prototype.push.apply(state.shape.customProperties, Editor.commonVertexProperties); 4490 } 4491 else 4492 { 4493 Array.prototype.push.apply(state.shape.customProperties, Editor.commonEdgeProperties); 4494 } 4495 } 4496 4497 handleCustomProp(state.shape.customProperties); 4498 } 4499 4500 //This currently is not needed but let's keep it in case we needed in the future 4501 var userCustomProp = cell.getAttribute('customProperties'); 4502 4503 if (userCustomProp != null) 4504 { 4505 try 4506 { 4507 handleCustomProp(JSON.parse(userCustomProp)); 4508 } 4509 catch(e){} 4510 } 4511 }; 4512 4513 /** 4514 * Adds predefiend styles. 4515 */ 4516 var styleFormatPanelInit = StyleFormatPanel.prototype.init; 4517 4518 StyleFormatPanel.prototype.init = function() 4519 { 4520 // TODO: Update sstate in Format 4521 var sstate = this.format.getSelectionState(); 4522 4523 if (sstate.style.shape != 'image' && !sstate.containsLabel && sstate.cells.length > 0) 4524 { 4525 this.container.appendChild(this.addStyles(this.createPanel())); 4526 } 4527 4528 styleFormatPanelInit.apply(this, arguments); 4529 4530 if (Editor.enableCustomProperties) 4531 { 4532 var properties = {}; 4533 var vertices = sstate.vertices; 4534 var edges = sstate.edges; 4535 4536 for (var i = 0; i < vertices.length; i++) 4537 { 4538 this.findCommonProperties(vertices[i], properties, i == 0); 4539 } 4540 4541 for (var i = 0; i < edges.length; i++) 4542 { 4543 this.findCommonProperties(edges[i], properties, vertices.length == 0 && i == 0); 4544 } 4545 4546 if (Object.getOwnPropertyNames != null && Object.getOwnPropertyNames(properties).length > 0) 4547 { 4548 this.container.appendChild(this.addProperties(this.createPanel(), properties, sstate)); 4549 } 4550 } 4551 }; 4552 4553 /** 4554 * Overridden to add copy and paste style. 4555 */ 4556 var styleFormatPanelAddStyleOps = StyleFormatPanel.prototype.addStyleOps; 4557 4558 StyleFormatPanel.prototype.addStyleOps = function(div) 4559 { 4560 var ss = this.format.getSelectionState(); 4561 var graph = this.editorUi.editor.graph; 4562 4563 var btn = mxUtils.button(mxResources.get('copyStyle'), mxUtils.bind(this, function(evt) 4564 { 4565 this.editorUi.actions.get('copyStyle').funct(); 4566 })); 4567 4568 btn.setAttribute('title', mxResources.get('copyStyle') + ' (' + this.editorUi.actions.get('copyStyle').shortcut + ')'); 4569 btn.style.marginBottom = '2px'; 4570 btn.style.width = '104px'; 4571 btn.style.marginRight = '2px'; 4572 4573 div.appendChild(btn); 4574 4575 if (ss.cells.length > 0) 4576 { 4577 var btn = mxUtils.button(mxResources.get('pasteStyle'), mxUtils.bind(this, function(evt) 4578 { 4579 this.editorUi.actions.get('pasteStyle').funct(); 4580 })); 4581 4582 btn.setAttribute('title', mxResources.get('pasteStyle') + ' (' + this.editorUi.actions.get('pasteStyle').shortcut + ')'); 4583 btn.style.marginBottom = '2px'; 4584 btn.style.width = '104px'; 4585 4586 div.appendChild(btn); 4587 } 4588 else 4589 { 4590 btn.style.width = '210px'; 4591 } 4592 4593 mxUtils.br(div); 4594 4595 return styleFormatPanelAddStyleOps.apply(this, arguments); 4596 }; 4597 4598 /** 4599 * Initial collapsed state of the properties panel. 4600 */ 4601 EditorUi.prototype.propertiesCollapsed = true; 4602 4603 /** 4604 * Create Properties Panel 4605 */ 4606 StyleFormatPanel.prototype.addProperties = function(div, properties, state) 4607 { 4608 var that = this; 4609 var graph = this.editorUi.editor.graph; 4610 var secondLevel = []; 4611 4612 function insertAfter(newElem, curElem) 4613 { 4614 curElem.parentNode.insertBefore(newElem, curElem.nextSibling); 4615 }; 4616 4617 function applyStyleVal(pName, newVal, prop, delIndex) 4618 { 4619 graph.getModel().beginUpdate(); 4620 try 4621 { 4622 var changedProps = []; 4623 var changedVals = []; 4624 4625 if (prop.index != null) 4626 { 4627 var allVals = []; 4628 var curVal = prop.parentRow.nextSibling; 4629 4630 while(curVal && curVal.getAttribute('data-pName') == pName) 4631 { 4632 allVals.push(curVal.getAttribute('data-pValue')); 4633 curVal = curVal.nextSibling; 4634 } 4635 4636 if (prop.index < allVals.length) 4637 { 4638 if (delIndex != null) 4639 { 4640 allVals.splice(delIndex, 1); 4641 } 4642 else 4643 { 4644 allVals[prop.index] = newVal; 4645 } 4646 } 4647 else 4648 { 4649 allVals.push(newVal); 4650 } 4651 4652 if (prop.size != null && allVals.length > prop.size) //trim the array to the specifies size 4653 { 4654 allVals = allVals.slice(0, prop.size); 4655 } 4656 4657 newVal = allVals.join(','); 4658 4659 if (prop.countProperty != null) 4660 { 4661 graph.setCellStyles(prop.countProperty, allVals.length, graph.getSelectionCells()); 4662 4663 changedProps.push(prop.countProperty); 4664 changedVals.push(allVals.length); 4665 } 4666 } 4667 4668 graph.setCellStyles(pName, newVal, graph.getSelectionCells()); 4669 changedProps.push(pName); 4670 changedVals.push(newVal); 4671 4672 if (prop.dependentProps != null) 4673 { 4674 for (var i = 0; i < prop.dependentProps.length; i++) 4675 { 4676 var defVal = prop.dependentPropsDefVal[i]; 4677 var vals = prop.dependentPropsVals[i]; 4678 4679 if (vals.length > newVal) 4680 { 4681 vals = vals.slice(0, newVal); 4682 } 4683 else 4684 { 4685 for (var j = vals.length; j < newVal; j++) 4686 { 4687 vals.push(defVal); 4688 } 4689 } 4690 4691 vals = vals.join(','); 4692 graph.setCellStyles(prop.dependentProps[i], vals, graph.getSelectionCells()); 4693 changedProps.push(prop.dependentProps[i]); 4694 changedVals.push(vals); 4695 } 4696 } 4697 4698 if (typeof(prop.onChange) == 'function') 4699 { 4700 prop.onChange(graph, newVal); 4701 } 4702 4703 that.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', changedProps, 4704 'values', changedVals, 'cells', graph.getSelectionCells())); 4705 } 4706 finally 4707 { 4708 graph.getModel().endUpdate(); 4709 } 4710 } 4711 4712 function setElementPos(td, elem, adjustHeight) 4713 { 4714 var divPos = mxUtils.getOffset(div, true); 4715 var pos = mxUtils.getOffset(td, true); 4716 elem.style.position = 'absolute'; 4717 elem.style.left = (pos.x - divPos.x) + 'px'; 4718 elem.style.top = (pos.y - divPos.y) + 'px'; 4719 elem.style.width = td.offsetWidth + 'px'; 4720 elem.style.height = (td.offsetHeight - (adjustHeight? 4 : 0)) + 'px'; 4721 elem.style.zIndex = 5; 4722 }; 4723 4724 function createColorBtn(pName, pValue, prop) 4725 { 4726 var clrDiv = document.createElement('div'); 4727 clrDiv.style.width = '32px'; 4728 clrDiv.style.height = '4px'; 4729 clrDiv.style.margin = '2px'; 4730 clrDiv.style.border = '1px solid black'; 4731 clrDiv.style.background = !pValue || pValue == 'none'? 'url(\'' + Dialog.prototype.noColorImage + '\')' : pValue; 4732 4733 btn = mxUtils.button('', mxUtils.bind(that, function(evt) 4734 { 4735 this.editorUi.pickColor(pValue, function(color) 4736 { 4737 clrDiv.style.background = color == 'none'? 'url(\'' + Dialog.prototype.noColorImage + '\')' : color; 4738 applyStyleVal(pName, color, prop); 4739 }); 4740 mxEvent.consume(evt); 4741 })); 4742 4743 btn.style.height = '12px'; 4744 btn.style.width = '40px'; 4745 btn.className = 'geColorBtn'; 4746 4747 btn.appendChild(clrDiv); 4748 return btn; 4749 }; 4750 4751 function createDynArrList(pName, pValue, subType, defVal, countProperty, myRow, flipBkg) 4752 { 4753 if (pValue != null) 4754 { 4755 var vals = pValue.split(','); 4756 secondLevel.push({name: pName, values: vals, type: subType, defVal: defVal, countProperty: countProperty, parentRow: myRow, isDeletable: true, flipBkg: flipBkg}); 4757 } 4758 4759 btn = mxUtils.button('+', mxUtils.bind(that, function(evt) 4760 { 4761 var beforeElem = myRow; 4762 var index = 0; 4763 4764 while (beforeElem.nextSibling != null) 4765 { 4766 var cur = beforeElem.nextSibling; 4767 var elemPName = cur.getAttribute('data-pName'); 4768 4769 if (elemPName == pName) 4770 { 4771 beforeElem = beforeElem.nextSibling; 4772 index++; 4773 } 4774 else 4775 { 4776 break; 4777 } 4778 } 4779 4780 var newProp = {type: subType, parentRow: myRow, index: index, isDeletable: true, defVal: defVal, countProperty: countProperty}; 4781 var arrItem = createPropertyRow(pName, '', newProp, index % 2 == 0, flipBkg); 4782 applyStyleVal(pName, defVal, newProp); 4783 insertAfter(arrItem, beforeElem); 4784 4785 mxEvent.consume(evt); 4786 })); 4787 4788 btn.style.height = '16px'; 4789 btn.style.width = '25px'; 4790 btn.className = 'geColorBtn'; 4791 4792 return btn; 4793 }; 4794 4795 function createStaticArrList(pName, pValue, subType, defVal, size, myRow, flipBkg) 4796 { 4797 if (size > 0) 4798 { 4799 var vals = new Array(size); 4800 4801 var curVals = pValue != null? pValue.split(',') : []; 4802 4803 for (var i = 0; i < size; i++) 4804 { 4805 vals[i] = curVals[i] != null? curVals[i] : (defVal != null? defVal : ''); 4806 } 4807 4808 secondLevel.push({name: pName, values: vals, type: subType, defVal: defVal, parentRow: myRow, flipBkg: flipBkg, size: size}); 4809 } 4810 4811 return document.createElement('div'); //empty cell 4812 }; 4813 4814 function createCheckbox(pName, pValue, prop) 4815 { 4816 var input = document.createElement('input'); 4817 input.type = 'checkbox'; 4818 input.checked = pValue == '1'; 4819 4820 mxEvent.addListener(input, 'change', function() 4821 { 4822 applyStyleVal(pName, input.checked? '1' : '0', prop); 4823 }); 4824 return input; 4825 }; 4826 4827 function createPropertyRow(pName, pValue, prop, isOdd, flipBkg) 4828 { 4829 var pDiplayName = prop.dispName; 4830 var pType = prop.type; 4831 var row = document.createElement('tr'); 4832 row.className = 'gePropRow' + (flipBkg? 'Dark' : '') + (isOdd? 'Alt' : '') + ' gePropNonHeaderRow'; 4833 row.setAttribute('data-pName', pName); 4834 row.setAttribute('data-pValue', pValue); 4835 var rightAlig = false; 4836 4837 if (prop.index != null) 4838 { 4839 row.setAttribute('data-index', prop.index); 4840 pDiplayName = (pDiplayName != null? pDiplayName : '') + '[' + prop.index + ']'; 4841 rightAlig = true; 4842 } 4843 4844 var td = document.createElement('td'); 4845 td.className = 'gePropRowCell'; 4846 var label = mxResources.get(pDiplayName, null, pDiplayName); 4847 mxUtils.write(td, label); 4848 td.setAttribute('title', label); 4849 4850 if (rightAlig) 4851 { 4852 td.style.textAlign = 'right'; 4853 } 4854 4855 row.appendChild(td); 4856 td = document.createElement('td'); 4857 td.className = 'gePropRowCell'; 4858 4859 if (pType == 'color') 4860 { 4861 td.appendChild(createColorBtn(pName, pValue, prop)); 4862 } 4863 else if (pType == 'bool' || pType == 'boolean') 4864 { 4865 td.appendChild(createCheckbox(pName, pValue, prop)); 4866 } 4867 else if (pType == 'enum') 4868 { 4869 var pEnumList = prop.enumList; 4870 4871 for (var i = 0; i < pEnumList.length; i++) 4872 { 4873 var op = pEnumList[i]; 4874 4875 if (op.val == pValue) 4876 { 4877 mxUtils.write(td, mxResources.get(op.dispName, null, op.dispName)); 4878 break; 4879 } 4880 } 4881 4882 mxEvent.addListener(td, 'click', mxUtils.bind(that, function() 4883 { 4884 var select = document.createElement('select'); 4885 setElementPos(td, select); 4886 4887 for (var i = 0; i < pEnumList.length; i++) 4888 { 4889 var op = pEnumList[i]; 4890 var opElem = document.createElement('option'); 4891 opElem.value = mxUtils.htmlEntities(op.val); 4892 mxUtils.write(opElem, mxResources.get(op.dispName, null, op.dispName)); 4893 select.appendChild(opElem); 4894 } 4895 4896 select.value = pValue; 4897 4898 div.appendChild(select); 4899 4900 mxEvent.addListener(select, 'change', function() 4901 { 4902 var newVal = mxUtils.htmlEntities(select.value); 4903 applyStyleVal(pName, newVal, prop); 4904 //set value triggers a redraw of the panel which removes the select and updates the row 4905 }); 4906 4907 select.focus(); 4908 4909 //FF calls blur on focus! so set the event after focusing (not with selects but to be safe) 4910 mxEvent.addListener(select, 'blur', function() 4911 { 4912 div.removeChild(select); 4913 }); 4914 })); 4915 } 4916 else if (pType == 'dynamicArr') 4917 { 4918 td.appendChild(createDynArrList(pName, pValue, prop.subType, prop.subDefVal, prop.countProperty, row, flipBkg)); 4919 } 4920 else if (pType == 'staticArr') 4921 { 4922 td.appendChild(createStaticArrList(pName, pValue, prop.subType, prop.subDefVal, prop.size, row, flipBkg)); 4923 } 4924 else if (pType == 'readOnly') 4925 { 4926 var inp = document.createElement('input'); 4927 inp.setAttribute('readonly', ''); 4928 inp.value = pValue; 4929 inp.style.width = '96px'; 4930 inp.style.borderWidth = '0px'; 4931 td.appendChild(inp); 4932 } 4933 else 4934 { 4935 td.innerHTML = pValue; 4936 4937 mxEvent.addListener(td, 'click', mxUtils.bind(that, function() 4938 { 4939 var input = document.createElement('input'); 4940 setElementPos(td, input, true); 4941 input.value = pValue; 4942 input.className = 'gePropEditor'; 4943 4944 if ((pType == 'int' || pType == 'float') && !prop.allowAuto) 4945 { 4946 input.type = 'number'; 4947 input.step = pType == 'int'? '1' : 'any'; 4948 4949 if (prop.min != null) 4950 { 4951 input.min = parseFloat(prop.min); 4952 } 4953 4954 if (prop.max != null) 4955 { 4956 input.max = parseFloat(prop.max); 4957 } 4958 } 4959 4960 div.appendChild(input); 4961 4962 function setInputVal() 4963 { 4964 var inputVal = input.value; 4965 inputVal = inputVal.length == 0 && pType != 'string'? 0 : inputVal; 4966 4967 if (prop.allowAuto) 4968 { 4969 if (inputVal.trim != null && inputVal.trim().toLowerCase() == 'auto') 4970 { 4971 inputVal = 'auto'; 4972 pType = 'string'; 4973 } 4974 else 4975 { 4976 inputVal = parseFloat(inputVal); 4977 inputVal = isNaN(inputVal)? 0 : inputVal; 4978 } 4979 } 4980 4981 if (prop.min != null && inputVal < prop.min) 4982 { 4983 inputVal = prop.min; 4984 } 4985 else if (prop.max != null && inputVal > prop.max) 4986 { 4987 inputVal = prop.max; 4988 } 4989 4990 var newVal = mxUtils.htmlEntities((pType == 'int'? parseInt(inputVal) : inputVal) + ''); 4991 4992 applyStyleVal(pName, newVal, prop); 4993 } 4994 4995 mxEvent.addListener(input, 'keypress', function(e) 4996 { 4997 if (e.keyCode == 13) 4998 { 4999 setInputVal(); 5000 //set value triggers a redraw of the panel which removes the input 5001 } 5002 }); 5003 5004 input.focus(); 5005 5006 //FF calls blur on focus! so set the event after focusing 5007 mxEvent.addListener(input, 'blur', function() 5008 { 5009 setInputVal(); 5010 }); 5011 })); 5012 } 5013 5014 if (prop.isDeletable) 5015 { 5016 var delBtn = mxUtils.button('-', mxUtils.bind(that, function(evt) 5017 { 5018 //delete the node by refreshing the properties 5019 applyStyleVal(pName, '', prop, prop.index); 5020 5021 mxEvent.consume(evt); 5022 })); 5023 5024 delBtn.style.height = '16px'; 5025 delBtn.style.width = '25px'; 5026 delBtn.style.float = 'right'; 5027 delBtn.className = 'geColorBtn'; 5028 td.appendChild(delBtn); 5029 } 5030 5031 row.appendChild(td); 5032 return row; 5033 }; 5034 5035 div.style.position = 'relative'; 5036 div.style.padding = '0'; 5037 var grid = document.createElement('table'); 5038 grid.className = 'geProperties'; 5039 grid.style.whiteSpace = 'nowrap'; 5040 grid.style.width = '100%'; 5041 //create header row 5042 var hrow = document.createElement('tr'); 5043 hrow.className = 'gePropHeader'; 5044 var th = document.createElement('th'); 5045 th.className = 'gePropHeaderCell'; 5046 var collapseImg = document.createElement('img'); 5047 collapseImg.src = Sidebar.prototype.expandedImage; 5048 collapseImg.style.verticalAlign = 'middle'; 5049 th.appendChild(collapseImg); 5050 mxUtils.write(th, mxResources.get('property')); 5051 hrow.style.cursor = 'pointer'; 5052 5053 var onFold = function() 5054 { 5055 var rows = grid.querySelectorAll('.gePropNonHeaderRow'); 5056 var display; 5057 5058 if (!that.editorUi.propertiesCollapsed) 5059 { 5060 collapseImg.src = Sidebar.prototype.expandedImage; 5061 display = ''; 5062 } 5063 else 5064 { 5065 collapseImg.src = Sidebar.prototype.collapsedImage; 5066 display = 'none'; 5067 5068 for (var e = div.childNodes.length - 1; e >= 0 ; e--) 5069 { 5070 //Blur can be executed concurrently with this method and the element is removed before removing it here 5071 try 5072 { 5073 var child = div.childNodes[e]; 5074 var nodeName = child.nodeName.toUpperCase(); 5075 5076 if (nodeName == 'INPUT' || nodeName == 'SELECT') 5077 { 5078 div.removeChild(child); 5079 } 5080 } 5081 catch(ex){} 5082 } 5083 } 5084 5085 for (var r = 0; r < rows.length; r++) 5086 { 5087 rows[r].style.display = display; 5088 } 5089 }; 5090 5091 mxEvent.addListener(hrow, 'click', function() 5092 { 5093 that.editorUi.propertiesCollapsed = !that.editorUi.propertiesCollapsed; 5094 onFold(); 5095 }); 5096 hrow.appendChild(th); 5097 th = document.createElement('th'); 5098 th.className = 'gePropHeaderCell'; 5099 th.innerHTML = mxResources.get('value'); 5100 hrow.appendChild(th); 5101 grid.appendChild(hrow); 5102 5103 var isOdd = false; 5104 var flipBkg = false; 5105 5106 var cellId = null; 5107 5108 if (state.vertices.length == 1 && state.edges.length == 0) 5109 { 5110 cellId = state.vertices[0].id; 5111 } 5112 else if (state.vertices.length == 0 && state.edges.length == 1) 5113 { 5114 cellId = state.edges[0].id; 5115 } 5116 5117 //Add it to top (always) 5118 if (cellId != null) 5119 { 5120 grid.appendChild(createPropertyRow('id', mxUtils.htmlEntities(cellId), {dispName: 'ID', type: 'readOnly'}, true, false)); 5121 } 5122 5123 for (var key in properties) 5124 { 5125 var prop = properties[key]; 5126 5127 if (typeof(prop.isVisible) == 'function') 5128 { 5129 if (!prop.isVisible(state, this)) continue; 5130 } 5131 5132 var pValue = state.style[key] != null? mxUtils.htmlEntities(state.style[key] + '') : 5133 ((prop.getDefaultValue != null) ? prop.getDefaultValue(state, this) : prop.defVal); //or undefined if defVal is undefined 5134 5135 if (prop.type == 'separator') 5136 { 5137 flipBkg = !flipBkg; 5138 continue; 5139 } 5140 else if (prop.type == 'staticArr') //if dynamic values are needed, a more elegant technique is needed to replace such values 5141 { 5142 prop.size = parseInt(state.style[prop.sizeProperty] || properties[prop.sizeProperty].defVal) || 0; 5143 } 5144 else if (prop.dependentProps != null) 5145 { 5146 var dependentProps = prop.dependentProps; 5147 var dependentPropsVals = []; 5148 var dependentPropsDefVal = []; 5149 5150 for (var i = 0; i < dependentProps.length; i++) 5151 { 5152 var propVal = state.style[dependentProps[i]]; 5153 dependentPropsDefVal.push(properties[dependentProps[i]].subDefVal); 5154 dependentPropsVals.push(propVal != null? propVal.split(',') : []); 5155 } 5156 5157 prop.dependentPropsDefVal = dependentPropsDefVal; 5158 prop.dependentPropsVals = dependentPropsVals; 5159 } 5160 5161 grid.appendChild(createPropertyRow(key, pValue, prop, isOdd, flipBkg)); 5162 5163 isOdd = !isOdd; 5164 } 5165 5166 for (var i = 0; i < secondLevel.length; i++) 5167 { 5168 var prop = secondLevel[i]; 5169 var insertElem = prop.parentRow; 5170 5171 for (var j = 0; j < prop.values.length; j++) 5172 { 5173 //mxUtils.clone failed because of the HTM element, so manual cloning is used 5174 var iProp = {type: prop.type, parentRow: prop.parentRow, isDeletable: prop.isDeletable, index: j, defVal: prop.defVal, countProperty: prop.countProperty, size: prop.size}; 5175 var arrItem = createPropertyRow(prop.name, prop.values[j], iProp, j % 2 == 0, prop.flipBkg); 5176 insertAfter(arrItem, insertElem); 5177 insertElem = arrItem; 5178 } 5179 } 5180 5181 div.appendChild(grid); 5182 onFold(); 5183 5184 return div; 5185 }; 5186 5187 /** 5188 * Creates the buttons for the predefined styles. 5189 */ 5190 StyleFormatPanel.prototype.addStyles = function(div) 5191 { 5192 var ui = this.editorUi; 5193 var graph = ui.editor.graph; 5194 var picker = document.createElement('div'); 5195 picker.style.whiteSpace = 'nowrap'; 5196 picker.style.paddingLeft = '24px'; 5197 picker.style.paddingRight = '20px'; 5198 div.style.paddingLeft = '16px'; 5199 div.style.paddingBottom = '6px'; 5200 div.style.position = 'relative'; 5201 div.appendChild(picker); 5202 5203 var stylenames = ['plain-gray', 'plain-blue', 'plain-green', 'plain-turquoise', 5204 'plain-orange', 'plain-yellow', 'plain-red', 'plain-pink', 'plain-purple', 'gray', 5205 'blue', 'green', 'turquoise', 'orange', 'yellow', 'red', 'pink', 'purple']; 5206 5207 // Maximum palettes to switch the switcher 5208 var maxEntries = 10; 5209 5210 // Selector 5211 var switcher = document.createElement('div'); 5212 switcher.style.whiteSpace = 'nowrap'; 5213 switcher.style.position = 'relative'; 5214 switcher.style.textAlign = 'center'; 5215 switcher.style.width = '210px'; 5216 5217 var dots = []; 5218 5219 for (var i = 0; i < this.defaultColorSchemes.length; i++) 5220 { 5221 var dot = document.createElement('div'); 5222 dot.style.display = 'inline-block'; 5223 dot.style.width = '6px'; 5224 dot.style.height = '6px'; 5225 dot.style.marginLeft = '4px'; 5226 dot.style.marginRight = '3px'; 5227 dot.style.borderRadius = '3px'; 5228 dot.style.cursor = 'pointer'; 5229 dot.style.background = 'transparent'; 5230 dot.style.border = '1px solid #b5b6b7'; 5231 5232 (mxUtils.bind(this, function(index) 5233 { 5234 mxEvent.addListener(dot, 'click', mxUtils.bind(this, function() 5235 { 5236 setScheme(index); 5237 })); 5238 }))(i); 5239 5240 dots.push(dot); 5241 switcher.appendChild(dot); 5242 } 5243 5244 var setScheme = mxUtils.bind(this, function(index) 5245 { 5246 if (dots[index] != null) 5247 { 5248 if (this.format.currentScheme != null && dots[this.format.currentScheme] != null) 5249 { 5250 dots[this.format.currentScheme].style.background = 'transparent'; 5251 } 5252 5253 this.format.currentScheme = index; 5254 updateScheme(this.defaultColorSchemes[this.format.currentScheme]); 5255 dots[this.format.currentScheme].style.background = '#84d7ff'; 5256 } 5257 }); 5258 5259 var updateScheme = mxUtils.bind(this, function(colorsets) 5260 { 5261 var addButton = mxUtils.bind(this, function(colorset) 5262 { 5263 var btn = mxUtils.button('', mxUtils.bind(this, function(evt) 5264 { 5265 graph.getModel().beginUpdate(); 5266 try 5267 { 5268 var cells = this.format.getSelectionState().cells; 5269 5270 for (var i = 0; i < cells.length; i++) 5271 { 5272 var style = graph.getModel().getStyle(cells[i]); 5273 5274 for (var j = 0; j < stylenames.length; j++) 5275 { 5276 style = mxUtils.removeStylename(style, stylenames[j]); 5277 } 5278 5279 var defaults = (graph.getModel().isVertex(cells[i])) ? graph.defaultVertexStyle : graph.defaultEdgeStyle; 5280 5281 if (colorset != null) 5282 { 5283 if (!mxEvent.isShiftDown(evt)) 5284 { 5285 if (colorset['fill'] == '') 5286 { 5287 style = mxUtils.setStyle(style, mxConstants.STYLE_FILLCOLOR, null); 5288 } 5289 else 5290 { 5291 style = mxUtils.setStyle(style, mxConstants.STYLE_FILLCOLOR, colorset['fill'] || 5292 mxUtils.getValue(defaults, mxConstants.STYLE_FILLCOLOR, null)); 5293 } 5294 5295 style = mxUtils.setStyle(style, mxConstants.STYLE_GRADIENTCOLOR, colorset['gradient'] || 5296 mxUtils.getValue(defaults, mxConstants.STYLE_GRADIENTCOLOR, null)); 5297 5298 if (!mxEvent.isControlDown(evt) && (!mxClient.IS_MAC || !mxEvent.isMetaDown(evt)) && 5299 graph.getModel().isVertex(cells[i])) 5300 { 5301 style = mxUtils.setStyle(style, mxConstants.STYLE_FONTCOLOR, colorset['font'] || 5302 mxUtils.getValue(defaults, mxConstants.STYLE_FONTCOLOR, null)); 5303 } 5304 } 5305 5306 if (!mxEvent.isAltDown(evt)) 5307 { 5308 if (colorset['stroke'] == '') 5309 { 5310 style = mxUtils.setStyle(style, mxConstants.STYLE_STROKECOLOR, null); 5311 } 5312 else 5313 { 5314 style = mxUtils.setStyle(style, mxConstants.STYLE_STROKECOLOR, colorset['stroke'] || 5315 mxUtils.getValue(defaults, mxConstants.STYLE_STROKECOLOR, null)); 5316 } 5317 } 5318 } 5319 else 5320 { 5321 style = mxUtils.setStyle(style, mxConstants.STYLE_FILLCOLOR, 5322 mxUtils.getValue(defaults, mxConstants.STYLE_FILLCOLOR, '#ffffff')); 5323 style = mxUtils.setStyle(style, mxConstants.STYLE_STROKECOLOR, 5324 mxUtils.getValue(defaults, mxConstants.STYLE_STROKECOLOR, '#000000')); 5325 style = mxUtils.setStyle(style, mxConstants.STYLE_GRADIENTCOLOR, 5326 mxUtils.getValue(defaults, mxConstants.STYLE_GRADIENTCOLOR, null)); 5327 5328 if (graph.getModel().isVertex(cells[i])) 5329 { 5330 style = mxUtils.setStyle(style, mxConstants.STYLE_FONTCOLOR, 5331 mxUtils.getValue(defaults, mxConstants.STYLE_FONTCOLOR, null)); 5332 } 5333 } 5334 5335 graph.getModel().setStyle(cells[i], style); 5336 } 5337 } 5338 finally 5339 { 5340 graph.getModel().endUpdate(); 5341 } 5342 })); 5343 5344 btn.className = 'geStyleButton'; 5345 btn.style.width = '36px'; 5346 btn.style.height = (this.defaultColorSchemes.length <= maxEntries) ? '24px' : '30px'; 5347 btn.style.margin = '0px 6px 6px 0px'; 5348 5349 if (colorset != null) 5350 { 5351 var b = (urlParams['sketch'] == '1') ? '2px solid' : '1px solid'; 5352 5353 if (colorset['gradient'] != null) 5354 { 5355 if (mxClient.IS_IE && (document.documentMode < 10)) 5356 { 5357 btn.style.filter = 'progid:DXImageTransform.Microsoft.Gradient('+ 5358 'StartColorStr=\'' + colorset['fill'] + 5359 '\', EndColorStr=\'' + colorset['gradient'] + '\', GradientType=0)'; 5360 } 5361 else 5362 { 5363 btn.style.backgroundImage = 'linear-gradient(' + colorset['fill'] + ' 0px,' + 5364 colorset['gradient'] + ' 100%)'; 5365 } 5366 } 5367 else if (colorset['fill'] == mxConstants.NONE) 5368 { 5369 btn.style.background = 'url(\'' + Dialog.prototype.noColorImage + '\')'; 5370 } 5371 else if (colorset['fill'] == '') 5372 { 5373 btn.style.backgroundColor = mxUtils.getValue(graph.defaultVertexStyle, 5374 mxConstants.STYLE_FILLCOLOR, (Editor.isDarkMode()) ? Editor.darkColor : '#ffffff'); 5375 } 5376 else 5377 { 5378 btn.style.backgroundColor = colorset['fill'] || mxUtils.getValue(graph.defaultVertexStyle, 5379 mxConstants.STYLE_FILLCOLOR, (Editor.isDarkMode()) ? Editor.darkColor : '#ffffff'); 5380 } 5381 5382 if (colorset['stroke'] == mxConstants.NONE) 5383 { 5384 btn.style.border = b + ' transparent'; 5385 } 5386 else if (colorset['stroke'] == '') 5387 { 5388 btn.style.border = b + ' ' + mxUtils.getValue(graph.defaultVertexStyle, 5389 mxConstants.STYLE_STROKECOLOR, (!Editor.isDarkMode()) ? Editor.darkColor : '#ffffff'); 5390 } 5391 else 5392 { 5393 btn.style.border = b + ' ' + (colorset['stroke'] || mxUtils.getValue(graph.defaultVertexStyle, 5394 mxConstants.STYLE_STROKECOLOR, (!Editor.isDarkMode()) ? Editor.darkColor : '#ffffff')); 5395 } 5396 5397 if (colorset['title'] != null) 5398 { 5399 btn.setAttribute('title', colorset['title']); 5400 } 5401 } 5402 else 5403 { 5404 var bg = mxUtils.getValue(graph.defaultVertexStyle, mxConstants.STYLE_FILLCOLOR, '#ffffff'); 5405 var bd = mxUtils.getValue(graph.defaultVertexStyle, mxConstants.STYLE_STROKECOLOR, '#000000'); 5406 5407 btn.style.backgroundColor = bg; 5408 btn.style.border = '1px solid ' + bd; 5409 } 5410 5411 btn.style.borderRadius = '0'; 5412 5413 picker.appendChild(btn); 5414 }); 5415 5416 picker.innerHTML = ''; 5417 5418 for (var i = 0; i < colorsets.length; i++) 5419 { 5420 if (i > 0 && mxUtils.mod(i, 4) == 0) 5421 { 5422 mxUtils.br(picker); 5423 } 5424 5425 addButton(colorsets[i]); 5426 } 5427 }); 5428 5429 if (this.format.currentScheme == null) 5430 { 5431 setScheme(Editor.isDarkMode() ? 1 : (urlParams['sketch'] == '1' ? 5 : 0)); 5432 } 5433 else 5434 { 5435 setScheme(this.format.currentScheme); 5436 } 5437 5438 var bottom = (this.defaultColorSchemes.length <= maxEntries) ? 28 : 8; 5439 5440 var left = document.createElement('div'); 5441 left.style.cssText = 'position:absolute;left:10px;top:8px;bottom:' + bottom + 'px;width:20px;margin:4px;opacity:0.5;' + 5442 'background-repeat:no-repeat;background-position:center center;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAQBAMAAADQT4M0AAAAIVBMVEUAAAB2dnZ4eHh3d3d1dXVxcXF2dnZ2dnZ2dnZxcXF2dnYmb3w1AAAACnRSTlMAfCTkhhvb7cQSPH2JPgAAADRJREFUCNdjwACMAmBKaiGYs2oJmLPKAZ3DabU8AMRTXpUKopislqFyVzCAuUZgikkBZjoAcMYLnp53P/UAAAAASUVORK5CYII=);'; 5443 5444 mxEvent.addListener(left, 'click', mxUtils.bind(this, function() 5445 { 5446 setScheme(mxUtils.mod(this.format.currentScheme - 1, this.defaultColorSchemes.length)); 5447 })); 5448 5449 var right = document.createElement('div'); 5450 right.style.cssText = 'position:absolute;left:202px;top:8px;bottom:' + bottom + 'px;width:20px;margin:4px;opacity:0.5;' + 5451 'background-repeat:no-repeat;background-position:center center;background-image:url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAQBAMAAADQT4M0AAAAIVBMVEUAAAB2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnZ2dnYBuwCcAAAACnRSTlMAfCTkhhvb7cQSPH2JPgAAADZJREFUCNdjQAOMAmBKaiGY8loF5rKswsZlrVo8AUiFrTICcbIWK8A5DF1gDoMymMPApIAwHwCS0Qx/U7qCBQAAAABJRU5ErkJggg==);'; 5452 5453 if (this.defaultColorSchemes.length > 1) 5454 { 5455 div.appendChild(left); 5456 div.appendChild(right); 5457 } 5458 5459 mxEvent.addListener(right, 'click', mxUtils.bind(this, function() 5460 { 5461 setScheme(mxUtils.mod(this.format.currentScheme + 1, this.defaultColorSchemes.length)); 5462 })); 5463 5464 // Hover state 5465 function addHoverState(elt) 5466 { 5467 mxEvent.addListener(elt, 'mouseenter', function() 5468 { 5469 elt.style.opacity = '1'; 5470 }); 5471 mxEvent.addListener(elt, 'mouseleave', function() 5472 { 5473 elt.style.opacity = '0.5'; 5474 }); 5475 }; 5476 5477 addHoverState(left); 5478 addHoverState(right); 5479 5480 updateScheme(this.defaultColorSchemes[this.format.currentScheme]); 5481 5482 if (this.defaultColorSchemes.length <= maxEntries) 5483 { 5484 div.appendChild(switcher); 5485 } 5486 5487 return div; 5488 }; 5489 5490 StyleFormatPanel.prototype.addEditOps = function(div) 5491 { 5492 var ss = this.format.getSelectionState(); 5493 var btn = null; 5494 5495 if (ss.cells.length == 1) 5496 { 5497 btn = mxUtils.button(mxResources.get('editStyle'), mxUtils.bind(this, function(evt) 5498 { 5499 this.editorUi.actions.get('editStyle').funct(); 5500 })); 5501 5502 btn.setAttribute('title', mxResources.get('editStyle') + ' (' + this.editorUi.actions.get('editStyle').shortcut + ')'); 5503 btn.style.width = '210px'; 5504 btn.style.marginBottom = '2px'; 5505 5506 div.appendChild(btn); 5507 } 5508 5509 var graph = this.editorUi.editor.graph; 5510 var state = (ss.cells.length == 1) ? graph.view.getState(ss.cells[0]) : null; 5511 5512 if (state != null && state.shape != null && state.shape.stencil != null) 5513 { 5514 var btn2 = mxUtils.button(mxResources.get('editShape'), mxUtils.bind(this, function(evt) 5515 { 5516 this.editorUi.actions.get('editShape').funct(); 5517 })); 5518 5519 btn2.setAttribute('title', mxResources.get('editShape')); 5520 btn2.style.marginBottom = '2px'; 5521 5522 if (btn == null) 5523 { 5524 btn2.style.width = '210px'; 5525 } 5526 else 5527 { 5528 btn.style.width = '104px'; 5529 btn2.style.width = '104px'; 5530 btn2.style.marginLeft = '2px'; 5531 } 5532 5533 div.appendChild(btn2); 5534 } 5535 else if (ss.image && ss.cells.length > 0) 5536 { 5537 var btn2 = mxUtils.button(mxResources.get('editImage'), mxUtils.bind(this, function(evt) 5538 { 5539 this.editorUi.actions.get('image').funct(); 5540 })); 5541 5542 btn2.setAttribute('title', mxResources.get('editImage')); 5543 btn2.style.marginBottom = '2px'; 5544 5545 if (btn == null) 5546 { 5547 btn2.style.width = '210px'; 5548 } 5549 else 5550 { 5551 btn.style.width = '104px'; 5552 btn2.style.width = '104px'; 5553 btn2.style.marginLeft = '2px'; 5554 } 5555 5556 div.appendChild(btn2); 5557 } 5558 5559 return div; 5560 }; 5561 } 5562 5563 /** 5564 * Lookup table for mapping from font URL and name to elements in the DOM. 5565 */ 5566 Graph.customFontElements = {}; 5567 5568 /** 5569 * Lookup table for recent custom fonts. 5570 */ 5571 Graph.recentCustomFonts = {}; 5572 5573 /** 5574 * Returns true if the given font URL references a Google font. 5575 */ 5576 Graph.isGoogleFontUrl = function(url) 5577 { 5578 return url.substring(0, Editor.GOOGLE_FONTS.length) == Editor.GOOGLE_FONTS; 5579 }; 5580 5581 /** 5582 * Returns true if the given font URL is a CSS file. 5583 */ 5584 Graph.isCssFontUrl = function(url) 5585 { 5586 return Graph.isGoogleFontUrl(url); 5587 }; 5588 5589 /** 5590 * Creates the DOM node for the custom font. 5591 */ 5592 Graph.createFontElement = function(name, url) 5593 { 5594 var elt = null; 5595 5596 if (Graph.isCssFontUrl(url)) 5597 { 5598 elt = document.createElement('link'); 5599 elt.setAttribute('rel', 'stylesheet'); 5600 elt.setAttribute('type', 'text/css'); 5601 elt.setAttribute('charset', 'UTF-8'); 5602 elt.setAttribute('href', url); 5603 } 5604 else 5605 { 5606 elt = document.createElement('style'); 5607 mxUtils.write(elt, '@font-face {\n' + 5608 'font-family: "' + name + '";\n' + 5609 'src: url("' + url + '");\n}'); 5610 } 5611 5612 return elt; 5613 }; 5614 5615 /** 5616 * Adds a font to the document. 5617 */ 5618 Graph.addFont = function(name, url, callback) 5619 { 5620 if (name != null && name.length > 0 && url != null && url.length > 0) 5621 { 5622 var key = name.toLowerCase(); 5623 5624 // Blocks UI font from being overwritten 5625 if (key != 'helvetica' && name != 'arial' && key != 'sans-serif') 5626 { 5627 var entry = Graph.customFontElements[key]; 5628 5629 // Replaces element if URL has changed 5630 if (entry != null && entry.url != url) 5631 { 5632 entry.elt.parentNode.removeChild(entry.elt); 5633 entry = null; 5634 } 5635 5636 if (entry == null) 5637 { 5638 var realUrl = url; 5639 5640 // Fixes possible mixed content by using proxy 5641 if (url.substring(0, 5) == 'http:') 5642 { 5643 realUrl = PROXY_URL + '?url=' + encodeURIComponent(url); 5644 } 5645 5646 entry = {name: name, url: url, elt: Graph.createFontElement(name, realUrl)}; 5647 Graph.customFontElements[key] = entry; 5648 Graph.recentCustomFonts[key] = entry; 5649 var head = document.getElementsByTagName('head')[0]; 5650 5651 if (callback != null) 5652 { 5653 if (entry.elt.nodeName.toLowerCase() == 'link') 5654 { 5655 entry.elt.onload = callback; 5656 entry.elt.onerror = callback; 5657 } 5658 else 5659 { 5660 callback(); 5661 } 5662 } 5663 5664 if (head != null) 5665 { 5666 head.appendChild(entry.elt); 5667 } 5668 } 5669 else if (callback != null) 5670 { 5671 callback(); 5672 } 5673 } 5674 else if (callback != null) 5675 { 5676 callback(); 5677 } 5678 } 5679 else if (callback != null) 5680 { 5681 callback(); 5682 } 5683 5684 return name; 5685 }; 5686 5687 /** 5688 * Returns the URL for the given font name if it exists in the document. 5689 * Otherwise it returns the given URL. 5690 */ 5691 Graph.getFontUrl = function(name, url) 5692 { 5693 var font = Graph.customFontElements[name.toLowerCase()]; 5694 5695 if (font != null) 5696 { 5697 url = font.url; 5698 } 5699 5700 return url; 5701 }; 5702 5703 /** 5704 * Processes the fonts in the given element and its descendants. 5705 */ 5706 Graph.processFontAttributes = function(elt) 5707 { 5708 var elts = elt.getElementsByTagName('*'); 5709 5710 for (var i = 0; i < elts.length; i++) 5711 { 5712 var url = elts[i].getAttribute('data-font-src'); 5713 5714 if (url != null) 5715 { 5716 var name = (elts[i].nodeName == 'FONT') ? 5717 elts[i].getAttribute('face') : 5718 elts[i].style.fontFamily; 5719 5720 if (name != null) 5721 { 5722 Graph.addFont(name, url); 5723 } 5724 } 5725 } 5726 }; 5727 5728 /** 5729 * Processes the font in the given cell style. 5730 */ 5731 Graph.processFontStyle = function(style) 5732 { 5733 if (style != null) 5734 { 5735 var url = mxUtils.getValue(style, 'fontSource', null); 5736 5737 if (url != null) 5738 { 5739 var name = mxUtils.getValue(style, mxConstants.STYLE_FONTFAMILY, null); 5740 5741 if (name != null) 5742 { 5743 Graph.addFont(name, decodeURIComponent(url)); 5744 } 5745 } 5746 } 5747 5748 return style; 5749 }; 5750 5751 /** 5752 * Changes the default stylename so that it matches the old named style 5753 * if one was specified in the XML. 5754 */ 5755 Graph.prototype.defaultThemeName = 'default-style2'; 5756 5757 /** 5758 * Contains the last XML that was pasted. 5759 */ 5760 Graph.prototype.lastPasteXml = null; 5761 5762 /** 5763 * Contains the number of times the last XML was pasted. 5764 */ 5765 Graph.prototype.pasteCounter = 0; 5766 5767 /** 5768 * Graph Overrides 5769 */ 5770 Graph.prototype.defaultScrollbars = urlParams['sb'] != '0'; 5771 5772 /** 5773 * Specifies if the page should be visible for new files. Default is true. 5774 */ 5775 Graph.prototype.defaultPageVisible = urlParams['pv'] != '0'; 5776 5777 /** 5778 * Specifies if the page should be visible for new files. Default is true. 5779 */ 5780 Graph.prototype.shadowId = 'dropShadow'; 5781 5782 /** 5783 * Properties for the SVG shadow effect. 5784 */ 5785 Graph.prototype.svgShadowColor = '#3D4574'; 5786 5787 /** 5788 * Properties for the SVG shadow effect. 5789 */ 5790 Graph.prototype.svgShadowOpacity = '0.4'; 5791 5792 /** 5793 * Properties for the SVG shadow effect. 5794 */ 5795 Graph.prototype.svgShadowBlur = '1.7'; 5796 5797 /** 5798 * Properties for the SVG shadow effect. 5799 */ 5800 Graph.prototype.svgShadowSize = '3'; 5801 5802 /** 5803 * Enables move of bends/segments without selecting. 5804 */ 5805 Graph.prototype.edgeMode = urlParams['edge'] != 'move'; 5806 5807 /** 5808 * Enables move of bends/segments without selecting. 5809 */ 5810 Graph.prototype.hiddenTags = null; 5811 5812 /** 5813 * Enables move of bends/segments without selecting. 5814 */ 5815 Graph.prototype.defaultMathEnabled = false; 5816 5817 /** 5818 * Adds rack child layout style. 5819 */ 5820 var graphInit = Graph.prototype.init; 5821 5822 Graph.prototype.init = function() 5823 { 5824 graphInit.apply(this, arguments); 5825 5826 // Array of hidden tags used in isCellVisible override 5827 this.hiddenTags = []; 5828 5829 //TODO initialize Freehand in the correct location! 5830 if (window.mxFreehand) 5831 { 5832 this.freehand = new mxFreehand(this); 5833 } 5834 5835 // Override insert location for current mouse point 5836 var mouseEvent = null; 5837 5838 function setMouseEvent(evt) 5839 { 5840 mouseEvent = evt; 5841 }; 5842 5843 mxEvent.addListener(this.container, 'mouseenter', setMouseEvent); 5844 mxEvent.addListener(this.container, 'mousemove', setMouseEvent); 5845 mxEvent.addListener(this.container, 'mouseleave', function(evt) 5846 { 5847 mouseEvent = null; 5848 }); 5849 5850 // Extends getInsertPoint to use the current mouse location 5851 this.isMouseInsertPoint = function() 5852 { 5853 return mouseEvent != null; 5854 }; 5855 5856 var getInsertPoint = this.getInsertPoint; 5857 5858 this.getInsertPoint = function() 5859 { 5860 if (mouseEvent != null) 5861 { 5862 return this.getPointForEvent(mouseEvent); 5863 } 5864 5865 return getInsertPoint.apply(this, arguments); 5866 }; 5867 5868 var layoutManagerGetLayout = this.layoutManager.getLayout; 5869 5870 this.layoutManager.getLayout = function(cell) 5871 { 5872 // Workaround for possible invalid style after change and before view validation 5873 var style = this.graph.getCellStyle(cell); 5874 5875 // mxRackContainer may be undefined as it is dynamically loaded at render time 5876 if (style != null) 5877 { 5878 if (style['childLayout'] == 'rack') 5879 { 5880 var rackLayout = new mxStackLayout(this.graph, false); 5881 var unitSize = 20; 5882 5883 if (style['rackUnitSize'] != null) 5884 { 5885 rackLayout.gridSize = parseFloat(style['rackUnitSize']); 5886 } 5887 else 5888 { 5889 rackLayout.gridSize = (typeof mxRackContainer !== 'undefined') ? mxRackContainer.unitSize : unitSize; 5890 } 5891 5892 rackLayout.marginLeft = style['marginLeft'] || 0; 5893 rackLayout.marginRight = style['marginRight'] || 0; 5894 rackLayout.marginTop = style['marginTop'] || 0; 5895 rackLayout.marginBottom = style['marginBottom'] || 0; 5896 rackLayout.allowGaps = style['allowGaps'] || 0; 5897 rackLayout.horizontal = mxUtils.getValue(style, 'horizontalRack', '0') == '1'; 5898 rackLayout.resizeParent = false; 5899 rackLayout.fill = true; 5900 5901 return rackLayout; 5902 } 5903 } 5904 5905 return layoutManagerGetLayout.apply(this, arguments); 5906 } 5907 5908 this.updateGlobalUrlVariables(); 5909 }; 5910 5911 /** 5912 * Adds support for custom fonts in cell styles. 5913 */ 5914 var graphPostProcessCellStyle = Graph.prototype.postProcessCellStyle; 5915 Graph.prototype.postProcessCellStyle = function(style) 5916 { 5917 this.replaceDefaultColors(style); 5918 5919 return Graph.processFontStyle(graphPostProcessCellStyle.apply(this, arguments)); 5920 }; 5921 5922 /** 5923 * Replaces default colors. 5924 */ 5925 Graph.prototype.replaceDefaultColors = function(style) 5926 { 5927 if (style != null) 5928 { 5929 var bg = mxUtils.hex2rgba(this.shapeBackgroundColor); 5930 var fg = mxUtils.hex2rgba(this.shapeForegroundColor); 5931 5932 this.replaceDefaultColor(style, mxConstants.STYLE_FONTCOLOR, fg); 5933 this.replaceDefaultColor(style, mxConstants.STYLE_FILLCOLOR, bg); 5934 this.replaceDefaultColor(style, mxConstants.STYLE_STROKECOLOR, fg); 5935 this.replaceDefaultColor(style, mxConstants.STYLE_IMAGE_BORDER, fg); 5936 this.replaceDefaultColor(style, mxConstants.STYLE_IMAGE_BACKGROUND, bg); 5937 this.replaceDefaultColor(style, mxConstants.STYLE_LABEL_BORDERCOLOR, fg); 5938 this.replaceDefaultColor(style, mxConstants.STYLE_SWIMLANE_FILLCOLOR, bg); 5939 this.replaceDefaultColor(style, mxConstants.STYLE_LABEL_BACKGROUNDCOLOR, bg); 5940 } 5941 }; 5942 5943 /** 5944 * Replaces the colors for the given key. 5945 */ 5946 Graph.prototype.replaceDefaultColor = function(style, key, value) 5947 { 5948 if (style != null && style[key] == 'default' && value != null) 5949 { 5950 style[key] = value; 5951 } 5952 }; 5953 5954 /** 5955 * Handles custom fonts in labels. 5956 */ 5957 var mxSvgCanvas2DUpdateTextNodes = mxSvgCanvas2D.prototype.updateTextNodes; 5958 mxSvgCanvas2D.prototype.updateTextNodes = function(x, y, w, h, align, valign, wrap, overflow, clip, rotation, g) 5959 { 5960 mxSvgCanvas2DUpdateTextNodes.apply(this, arguments); 5961 Graph.processFontAttributes(g); 5962 }; 5963 5964 /** 5965 * Handles custom fonts in labels. 5966 */ 5967 var mxTextRedraw = mxText.prototype.redraw; 5968 mxText.prototype.redraw = function() 5969 { 5970 mxTextRedraw.apply(this, arguments); 5971 5972 // Handles label rendered without foreign object 5973 if (this.node != null && this.node.nodeName == 'DIV') 5974 { 5975 Graph.processFontAttributes(this.node); 5976 } 5977 }; 5978 5979 Graph.prototype.createTagsDialog = function(isEnabled, invert, addFn) 5980 { 5981 var graph = this; 5982 var allTags = graph.hiddenTags.slice(); 5983 5984 var div = document.createElement('div'); 5985 div.style.userSelect = 'none'; 5986 div.style.overflow = 'hidden'; 5987 div.style.padding = '10px'; 5988 div.style.height = '100%'; 5989 5990 var tagCloud = document.createElement('div'); 5991 tagCloud.style.boxSizing = 'border-box'; 5992 tagCloud.style.borderRadius = '4px'; 5993 tagCloud.style.userSelect = 'none'; 5994 tagCloud.style.overflow = 'auto'; 5995 tagCloud.style.position = 'absolute'; 5996 tagCloud.style.left = '10px'; 5997 tagCloud.style.right = '10px'; 5998 tagCloud.style.top = '10px'; 5999 tagCloud.style.border = (graph.isEnabled()) ? '1px solid #808080' : 'none'; 6000 tagCloud.style.bottom = (graph.isEnabled()) ? '48px' : '10px'; 6001 6002 div.appendChild(tagCloud); 6003 6004 function removeInvisibleSelectionCells() 6005 { 6006 var cells = graph.getSelectionCells(); 6007 var visible = []; 6008 6009 for (var i = 0; i < cells.length; i++) 6010 { 6011 if (graph.isCellVisible(cells[i])) 6012 { 6013 visible.push(cells[i]); 6014 } 6015 } 6016 6017 graph.setSelectionCells(visible); 6018 }; 6019 6020 function setAllVisible(visible) 6021 { 6022 if (visible) 6023 { 6024 graph.hiddenTags = []; 6025 } 6026 else 6027 { 6028 graph.hiddenTags = allTags.slice(); 6029 } 6030 6031 removeInvisibleSelectionCells(); 6032 graph.refresh(); 6033 }; 6034 6035 var resetBtn = mxUtils.button(mxResources.get('reset'), function(evt) 6036 { 6037 graph.hiddenTags = []; 6038 6039 if (!mxEvent.isShiftDown(evt)) 6040 { 6041 allTags = graph.hiddenTags.slice(); 6042 } 6043 6044 removeInvisibleSelectionCells(); 6045 graph.refresh(); 6046 }); 6047 6048 resetBtn.setAttribute('title', mxResources.get('reset')); 6049 resetBtn.className = 'geBtn'; 6050 resetBtn.style.margin = '0 4px 0 0'; 6051 6052 var addBtn = mxUtils.button(mxResources.get('add'), function() 6053 { 6054 if (addFn != null) 6055 { 6056 // Takes all tags and callback to update all tags 6057 addFn(allTags, function(newAllTags) 6058 { 6059 allTags = newAllTags; 6060 refreshUi(); 6061 }); 6062 } 6063 }); 6064 addBtn.setAttribute('title', mxResources.get('add')); 6065 addBtn.className = 'geBtn'; 6066 addBtn.style.margin = '0'; 6067 6068 graph.addListener(mxEvent.ROOT, function() 6069 { 6070 allTags = graph.hiddenTags.slice(); 6071 }); 6072 6073 function refreshTags(tags, selected) 6074 { 6075 tagCloud.innerHTML = ''; 6076 6077 if (tags.length > 0) 6078 { 6079 var table = document.createElement('table'); 6080 table.setAttribute('cellpadding', '2'); 6081 table.style.boxSizing = 'border-box'; 6082 table.style.tableLayout = 'fixed'; 6083 table.style.width = '100%'; 6084 6085 var tbody = document.createElement('tbody'); 6086 6087 if (tags != null && tags.length > 0) 6088 { 6089 for (var i = 0; i < tags.length; i++) 6090 { 6091 (function(tag) 6092 { 6093 function setTagVisible() 6094 { 6095 var temp = allTags.slice(); 6096 var index = mxUtils.indexOf(temp, tag); 6097 temp.splice(index, 1); 6098 graph.hiddenTags = temp; 6099 removeInvisibleSelectionCells(); 6100 graph.refresh(); 6101 }; 6102 6103 function selectCells() 6104 { 6105 var cells = graph.getCellsForTags( 6106 [tag], null, null, true); 6107 6108 if (graph.isEnabled()) 6109 { 6110 graph.setSelectionCells(cells); 6111 } 6112 else 6113 { 6114 graph.highlightCells(cells); 6115 } 6116 }; 6117 6118 var visible = mxUtils.indexOf(graph.hiddenTags, tag) < 0; 6119 var row = document.createElement('tr'); 6120 var td = document.createElement('td'); 6121 td.style.align = 'center'; 6122 td.style.width = '16px'; 6123 6124 var img = document.createElement('img'); 6125 img.setAttribute('src', visible ? Editor.visibleImage : Editor.hiddenImage); 6126 img.setAttribute('title', mxResources.get(visible ? 'hideIt' : 'show', [tag])); 6127 mxUtils.setOpacity(img, visible ? 75 : 25); 6128 img.style.verticalAlign = 'middle'; 6129 img.style.cursor = 'pointer'; 6130 img.style.width = '16px'; 6131 6132 if (invert || Editor.isDarkMode()) 6133 { 6134 img.style.filter = 'invert(100%)'; 6135 } 6136 6137 td.appendChild(img); 6138 6139 mxEvent.addListener(img, 'click', function(evt) 6140 { 6141 var idx = mxUtils.indexOf(graph.hiddenTags, tag); 6142 6143 if (mxEvent.isShiftDown(evt)) 6144 { 6145 setAllVisible(mxUtils.indexOf(graph.hiddenTags, tag) >= 0); 6146 } 6147 else 6148 { 6149 if (idx < 0) 6150 { 6151 graph.hiddenTags.push(tag); 6152 } 6153 else if (idx >= 0) 6154 { 6155 graph.hiddenTags.splice(idx, 1); 6156 } 6157 6158 removeInvisibleSelectionCells(); 6159 graph.refresh(); 6160 } 6161 6162 mxEvent.consume(evt); 6163 }); 6164 6165 row.appendChild(td); 6166 6167 td = document.createElement('td'); 6168 td.style.overflow = 'hidden'; 6169 td.style.whiteSpace = 'nowrap'; 6170 td.style.textOverflow = 'ellipsis'; 6171 td.style.verticalAlign = 'middle'; 6172 td.style.cursor = 'pointer'; 6173 td.setAttribute('title', tag); 6174 6175 a = document.createElement('a'); 6176 mxUtils.write(a, tag); 6177 a.style.textOverflow = 'ellipsis'; 6178 a.style.position = 'relative'; 6179 mxUtils.setOpacity(a, visible ? 100 : 40); 6180 td.appendChild(a); 6181 6182 mxEvent.addListener(td, 'click', (function(evt) 6183 { 6184 if (mxEvent.isShiftDown(evt)) 6185 { 6186 setAllVisible(true); 6187 selectCells(); 6188 } 6189 else 6190 { 6191 if (visible && graph.hiddenTags.length > 0) 6192 { 6193 setAllVisible(true); 6194 } 6195 else 6196 { 6197 setTagVisible(); 6198 } 6199 } 6200 6201 mxEvent.consume(evt); 6202 })); 6203 6204 row.appendChild(td); 6205 6206 if (graph.isEnabled()) 6207 { 6208 td = document.createElement('td'); 6209 td.style.verticalAlign = 'middle'; 6210 td.style.textAlign = 'center'; 6211 td.style.width = '18px'; 6212 6213 if (selected == null) 6214 { 6215 td.style.align = 'center'; 6216 td.style.width = '16px'; 6217 6218 var img = document.createElement('img'); 6219 img.setAttribute('src', Editor.crossImage); 6220 img.setAttribute('title', mxResources.get('removeIt', [tag])); 6221 mxUtils.setOpacity(img, visible ? 75 : 25); 6222 img.style.verticalAlign = 'middle'; 6223 img.style.cursor = 'pointer'; 6224 img.style.width = '16px'; 6225 6226 if (invert || Editor.isDarkMode()) 6227 { 6228 img.style.filter = 'invert(100%)'; 6229 } 6230 6231 mxEvent.addListener(img, 'click', function(evt) 6232 { 6233 var idx = mxUtils.indexOf(allTags, tag); 6234 6235 if (idx >= 0) 6236 { 6237 allTags.splice(idx, 1); 6238 } 6239 6240 graph.removeTagsForCells( 6241 graph.model.getDescendants( 6242 graph.model.getRoot()), [tag]); 6243 graph.refresh(); 6244 6245 mxEvent.consume(evt); 6246 }); 6247 6248 td.appendChild(img); 6249 } 6250 else 6251 { 6252 var cb2 = document.createElement('input'); 6253 cb2.setAttribute('type', 'checkbox'); 6254 cb2.style.margin = '0px'; 6255 6256 cb2.defaultChecked = (selected != null && 6257 mxUtils.indexOf(selected, tag) >= 0); 6258 cb2.checked = cb2.defaultChecked; 6259 cb2.style.background = 'transparent'; 6260 cb2.setAttribute('title', mxResources.get( 6261 cb2.defaultChecked ? 6262 'removeIt' : 'add', [tag])); 6263 6264 mxEvent.addListener(cb2, 'change', function(evt) 6265 { 6266 if (cb2.checked) 6267 { 6268 graph.addTagsForCells(graph.getSelectionCells(), [tag]); 6269 } 6270 else 6271 { 6272 graph.removeTagsForCells(graph.getSelectionCells(), [tag]); 6273 } 6274 6275 mxEvent.consume(evt); 6276 }); 6277 6278 td.appendChild(cb2); 6279 } 6280 6281 row.appendChild(td); 6282 } 6283 6284 tbody.appendChild(row); 6285 })(tags[i]); 6286 } 6287 } 6288 6289 table.appendChild(tbody); 6290 tagCloud.appendChild(table); 6291 } 6292 }; 6293 6294 var refreshUi = mxUtils.bind(this, function(sender, evt) 6295 { 6296 if (isEnabled()) 6297 { 6298 var tags = graph.getAllTags(); 6299 6300 for (var i = 0; i < tags.length; i++) 6301 { 6302 if (mxUtils.indexOf(allTags, tags[i]) < 0) 6303 { 6304 allTags.push(tags[i]); 6305 } 6306 } 6307 6308 allTags.sort(); 6309 6310 if (graph.isSelectionEmpty()) 6311 { 6312 refreshTags(allTags); 6313 } 6314 else 6315 { 6316 refreshTags(allTags, graph.getCommonTagsForCells( 6317 graph.getSelectionCells())); 6318 } 6319 } 6320 }); 6321 6322 graph.selectionModel.addListener(mxEvent.CHANGE, refreshUi); 6323 graph.model.addListener(mxEvent.CHANGE, refreshUi); 6324 graph.addListener(mxEvent.REFRESH, refreshUi); 6325 6326 var footer = document.createElement('div'); 6327 footer.style.boxSizing = 'border-box'; 6328 footer.style.whiteSpace = 'nowrap'; 6329 footer.style.position = 'absolute'; 6330 footer.style.overflow = 'hidden'; 6331 footer.style.bottom = '0px'; 6332 footer.style.height = '42px'; 6333 footer.style.right = '10px'; 6334 footer.style.left = '10px'; 6335 6336 if (graph.isEnabled()) 6337 { 6338 footer.appendChild(resetBtn); 6339 footer.appendChild(addBtn); 6340 div.appendChild(footer); 6341 } 6342 6343 return {div: div, refresh: refreshUi}; 6344 }; 6345 6346 /** 6347 * Returns all custom fonts (old and new). 6348 */ 6349 Graph.prototype.getCustomFonts = function() 6350 { 6351 var fonts = this.extFonts; 6352 6353 if (fonts != null) 6354 { 6355 fonts = fonts.slice(); 6356 } 6357 else 6358 { 6359 fonts = []; 6360 } 6361 6362 for (var key in Graph.customFontElements) 6363 { 6364 var font = Graph.customFontElements[key]; 6365 fonts.push({name: font.name, url: font.url}); 6366 } 6367 6368 return fonts; 6369 }; 6370 6371 /** 6372 * Assigns the given custom font to the selected text. 6373 */ 6374 Graph.prototype.setFont = function(name, url) 6375 { 6376 // Adds the font element to the document 6377 Graph.addFont(name, url); 6378 6379 // Only valid known fonts are allowed as parameters so we set 6380 // the real font name and the data-source-face in the element 6381 // which is used as the face attribute when editing stops 6382 // KNOWN: Undo for the DOM change is not working 6383 document.execCommand('fontname', false, name); 6384 6385 // Finds element with new font name and checks its data-font-src attribute 6386 if (url != null) 6387 { 6388 var fonts = this.cellEditor.textarea.getElementsByTagName('font'); 6389 6390 // Enforces consistent font naming 6391 url = Graph.getFontUrl(name, url); 6392 6393 for (var i = 0; i < fonts.length; i++) 6394 { 6395 if (fonts[i].getAttribute('face') == name) 6396 { 6397 if (fonts[i].getAttribute('data-font-src') != url) 6398 { 6399 fonts[i].setAttribute('data-font-src', url); 6400 } 6401 } 6402 } 6403 } 6404 }; 6405 6406 /** 6407 * Disables fast zoom with shadow in lightbox for Safari 6408 * to work around blank output on retina screen. 6409 */ 6410 var graphIsFastZoomEnabled = Graph.prototype.isFastZoomEnabled; 6411 6412 Graph.prototype.isFastZoomEnabled = function() 6413 { 6414 return graphIsFastZoomEnabled.apply(this, arguments) && (!this.shadowVisible || !mxClient.IS_SF); 6415 }; 6416 6417 /** 6418 * Updates the global variables from the vars URL parameter. 6419 */ 6420 Graph.prototype.updateGlobalUrlVariables = function() 6421 { 6422 this.globalVars = Editor.globalVars; 6423 6424 if (urlParams['vars'] != null) 6425 { 6426 try 6427 { 6428 this.globalVars = (this.globalVars != null) ? mxUtils.clone(this.globalVars) : {}; 6429 var vars = JSON.parse(decodeURIComponent(urlParams['vars'])); 6430 6431 if (vars != null) 6432 { 6433 for (var key in vars) 6434 { 6435 this.globalVars[key] = vars[key]; 6436 } 6437 } 6438 } 6439 catch (e) 6440 { 6441 if (window.console != null) 6442 { 6443 console.log('Error in vars URL parameter: ' + e); 6444 } 6445 } 6446 } 6447 }; 6448 6449 /** 6450 * Returns all global variables used for export. This function never returns null. 6451 * This can be overridden by plugins to return global variables for export. 6452 */ 6453 Graph.prototype.getExportVariables = function() 6454 { 6455 return (this.globalVars != null) ? mxUtils.clone(this.globalVars) : {}; 6456 }; 6457 6458 /** 6459 * Adds support for vars URL parameter. 6460 */ 6461 var graphGetGlobalVariable = Graph.prototype.getGlobalVariable; 6462 6463 Graph.prototype.getGlobalVariable = function(name) 6464 { 6465 var val = graphGetGlobalVariable.apply(this, arguments); 6466 6467 if (val == null && this.globalVars != null) 6468 { 6469 val = this.globalVars[name]; 6470 } 6471 6472 return val; 6473 }; 6474 6475 /** 6476 * Cached default stylesheet for image export in dark mode. 6477 */ 6478 Graph.prototype.getDefaultStylesheet = function() 6479 { 6480 if (this.defaultStylesheet == null) 6481 { 6482 var node = this.themes['default-style2']; 6483 var dec = new mxCodec(node.ownerDocument); 6484 this.defaultStylesheet = dec.decode(node); 6485 } 6486 6487 return this.defaultStylesheet; 6488 }; 6489 6490 /** 6491 * Overiddes function to use url parameter 6492 */ 6493 Graph.prototype.isViewer = function() 6494 { 6495 return urlParams['viewer']; 6496 }; 6497 6498 /** 6499 * Temporarily overrides stylesheet during image export in dark mode. 6500 */ 6501 var graphGetSvg = Graph.prototype.getSvg; 6502 6503 Graph.prototype.getSvg = function(background, scale, border, nocrop, crisp, 6504 ignoreSelection, showText, imgExport, linkTarget, hasShadow, 6505 incExtFonts, keepTheme, exportType, cells) 6506 { 6507 var temp = null; 6508 var tempFg = null; 6509 var tempBg = null; 6510 6511 if (!keepTheme && this.themes != null && this.defaultThemeName == 'darkTheme') 6512 { 6513 temp = this.stylesheet; 6514 tempFg = this.shapeForegroundColor; 6515 tempBg = this.shapeBackgroundColor; 6516 this.shapeForegroundColor = (this.defaultThemeName == 'darkTheme') ? 6517 '#000000' : Editor.lightColor; 6518 this.shapeBackgroundColor = (this.defaultThemeName == 'darkTheme') ? 6519 '#ffffff' : Editor.darkColor; 6520 this.stylesheet = this.getDefaultStylesheet(); 6521 // LATER: Fix math export in dark mode by fetching text nodes before 6522 // calling refresh and changing the font color in-place 6523 this.refresh(); 6524 } 6525 6526 var result = graphGetSvg.apply(this, arguments); 6527 var extFonts = this.getCustomFonts(); 6528 6529 // Adds external fonts 6530 if (incExtFonts && extFonts.length > 0) 6531 { 6532 var svgDoc = result.ownerDocument; 6533 var style = (svgDoc.createElementNS != null) ? 6534 svgDoc.createElementNS(mxConstants.NS_SVG, 'style') : svgDoc.createElement('style'); 6535 svgDoc.setAttributeNS != null? style.setAttributeNS('type', 'text/css') : style.setAttribute('type', 'text/css'); 6536 6537 var prefix = ''; 6538 var postfix = ''; 6539 6540 for (var i = 0; i < extFonts.length; i++) 6541 { 6542 var fontName = extFonts[i].name, fontUrl = extFonts[i].url; 6543 6544 if (Graph.isCssFontUrl(fontUrl)) 6545 { 6546 prefix += '@import url(' + fontUrl + ');\n'; 6547 } 6548 else 6549 { 6550 postfix += '@font-face {\n' + 6551 'font-family: "' + fontName + '";\n' + 6552 'src: url("' + fontUrl + '");\n}\n'; 6553 } 6554 } 6555 6556 style.appendChild(svgDoc.createTextNode(prefix + postfix)); 6557 result.getElementsByTagName('defs')[0].appendChild(style); 6558 } 6559 6560 if (temp != null) 6561 { 6562 this.shapeBackgroundColor = tempBg; 6563 this.shapeForegroundColor = tempFg; 6564 this.stylesheet = temp; 6565 this.refresh(); 6566 } 6567 6568 return result; 6569 }; 6570 6571 /** 6572 * Overridden to support client-side math typesetting. 6573 */ 6574 var graphCreateSvgImageExport = Graph.prototype.createSvgImageExport; 6575 6576 Graph.prototype.createSvgImageExport = function() 6577 { 6578 var imgExport = graphCreateSvgImageExport.apply(this, arguments); 6579 6580 if (this.mathEnabled) 6581 { 6582 var drawText = imgExport.drawText; 6583 6584 // Replaces input with rendered markup 6585 imgExport.drawText = function(state, c) 6586 { 6587 if (state.text != null && state.text.value != null && state.text.checkBounds() && 6588 (mxUtils.isNode(state.text.value) || state.text.dialect == mxConstants.DIALECT_STRICTHTML)) 6589 { 6590 var clone = state.text.getContentNode(); 6591 6592 if (clone != null) 6593 { 6594 clone = clone.cloneNode(true); 6595 6596 // Removes duplicate math output 6597 if (clone.getElementsByTagNameNS) 6598 { 6599 var ele = clone.getElementsByTagNameNS('http://www.w3.org/1998/Math/MathML', 'math'); 6600 6601 while (ele.length > 0) 6602 { 6603 ele[0].parentNode.removeChild(ele[0]); 6604 } 6605 } 6606 6607 if (clone.innerHTML != null) 6608 { 6609 var prev = state.text.value; 6610 state.text.value = clone.innerHTML; 6611 drawText.apply(this, arguments); 6612 state.text.value = prev; 6613 } 6614 } 6615 } 6616 else 6617 { 6618 drawText.apply(this, arguments); 6619 } 6620 }; 6621 } 6622 6623 return imgExport; 6624 }; 6625 6626 /** 6627 * Adds workaround for math rendering in Chrome. 6628 * 6629 * Workaround for https://bugs.webkit.org/show_bug.cgi?id=93358 in WebKit 6630 * 6631 * Adding an absolute position DIV before the SVG seems to mitigate the problem. 6632 */ 6633 var graphViewValidateBackgroundPage = mxGraphView.prototype.validateBackgroundPage; 6634 6635 mxGraphView.prototype.validateBackgroundPage = function() 6636 { 6637 graphViewValidateBackgroundPage.apply(this, arguments); 6638 6639 if (mxClient.IS_GC && this.getDrawPane() != null) 6640 { 6641 var g = this.getDrawPane().parentNode; 6642 6643 if (this.graph.mathEnabled && !mxClient.NO_FO && 6644 (this.webKitForceRepaintNode == null || 6645 this.webKitForceRepaintNode.parentNode == null) && 6646 this.graph.container.firstChild.nodeName == 'svg') 6647 { 6648 this.webKitForceRepaintNode = document.createElement('div'); 6649 this.webKitForceRepaintNode.style.cssText = 'position:absolute;'; 6650 g.ownerSVGElement.parentNode.insertBefore(this.webKitForceRepaintNode, g.ownerSVGElement); 6651 } 6652 else if (this.webKitForceRepaintNode != null && (!this.graph.mathEnabled || 6653 (this.graph.container.firstChild.nodeName != 'svg' && 6654 this.graph.container.firstChild != this.webKitForceRepaintNode))) 6655 { 6656 if (this.webKitForceRepaintNode.parentNode != null) 6657 { 6658 this.webKitForceRepaintNode.parentNode.removeChild(this.webKitForceRepaintNode); 6659 } 6660 6661 this.webKitForceRepaintNode = null; 6662 } 6663 } 6664 }; 6665 6666 /** 6667 * Updates the SVG for the background image if it references another page. 6668 */ 6669 var graphRefresh = Graph.prototype.refresh; 6670 Graph.prototype.refresh = function() 6671 { 6672 graphRefresh.apply(this, arguments); 6673 this.refreshBackgroundImage(); 6674 }; 6675 6676 /** 6677 * Updates the SVG for the background image if it references another page. 6678 */ 6679 Graph.prototype.refreshBackgroundImage = function() 6680 { 6681 if (this.backgroundImage != null && this.backgroundImage.originalSrc != null) 6682 { 6683 this.setBackgroundImage(this.backgroundImage); 6684 this.view.validateBackgroundImage(); 6685 } 6686 }; 6687 6688 /** 6689 * Sets default style (used in editor.get/setGraphXml below) 6690 */ 6691 var graphLoadStylesheet = Graph.prototype.loadStylesheet; 6692 6693 Graph.prototype.loadStylesheet = function() 6694 { 6695 graphLoadStylesheet.apply(this, arguments); 6696 this.currentStyle = 'default-style2'; 6697 }; 6698 6699 /** 6700 * Adds support for data:action/json,{"actions":[actions]} where actions is 6701 * a comma-separated list of JSON objects. 6702 * 6703 * An example action is: 6704 * 6705 * data:action/json,{"actions":[{"toggle": {"cells": ["3", "4"]}}]} 6706 * 6707 * This toggles the visible state of the cells with ID 3 and 4. 6708 */ 6709 Graph.prototype.handleCustomLink = function(href) 6710 { 6711 if (href.substring(0, 17) == 'data:action/json,') 6712 { 6713 var link = JSON.parse(href.substring(17)); 6714 6715 if (link.actions != null) 6716 { 6717 this.executeCustomActions(link.actions); 6718 } 6719 } 6720 }; 6721 6722 /** 6723 * Runs the given actions and invokes done when all actions have been executed. 6724 * When adding new actions that reference cell IDs support for updating 6725 * those cell IDs must be handled in Graph.updateCustomLinkActions 6726 */ 6727 Graph.prototype.executeCustomActions = function(actions, done) 6728 { 6729 if (!this.executingCustomActions) 6730 { 6731 this.executingCustomActions = true; 6732 var updatingModel = false; 6733 var waitCounter = 0; 6734 var index = 0; 6735 6736 var beginUpdate = mxUtils.bind(this, function() 6737 { 6738 if (!updatingModel) 6739 { 6740 updatingModel = true; 6741 this.model.beginUpdate(); 6742 } 6743 }); 6744 6745 var endUpdate = mxUtils.bind(this, function() 6746 { 6747 if (updatingModel) 6748 { 6749 updatingModel = false; 6750 this.model.endUpdate(); 6751 } 6752 }); 6753 6754 var waitAndExecute = mxUtils.bind(this, function() 6755 { 6756 if (waitCounter > 0) 6757 { 6758 waitCounter--; 6759 } 6760 6761 if (waitCounter == 0) 6762 { 6763 executeNextAction() 6764 } 6765 }); 6766 6767 var executeNextAction = mxUtils.bind(this, function() 6768 { 6769 if (index < actions.length) 6770 { 6771 var stop = this.stoppingCustomActions; 6772 var action = actions[index++]; 6773 var animations = []; 6774 6775 // Executes open actions before starting transaction 6776 if (action.open != null) 6777 { 6778 endUpdate(); 6779 6780 if (this.isCustomLink(action.open)) 6781 { 6782 if (!this.customLinkClicked(action.open)) 6783 { 6784 return; 6785 } 6786 } 6787 else 6788 { 6789 this.openLink(action.open); 6790 } 6791 } 6792 6793 if (action.wait != null && !stop) 6794 { 6795 this.pendingExecuteNextAction = mxUtils.bind(this, function() 6796 { 6797 this.pendingExecuteNextAction = null; 6798 this.pendingWaitThread = null; 6799 waitAndExecute(); 6800 }); 6801 6802 waitCounter++; 6803 this.pendingWaitThread = window.setTimeout(this.pendingExecuteNextAction, 6804 (action.wait != '') ? parseInt(action.wait) : 1000); 6805 endUpdate(); 6806 } 6807 6808 if (action.opacity != null && action.opacity.value != null) 6809 { 6810 Graph.setOpacityForNodes(this.getNodesForCells( 6811 this.getCellsForAction(action.opacity, true)), 6812 action.opacity.value); 6813 } 6814 6815 if (action.fadeIn != null) 6816 { 6817 waitCounter++; 6818 Graph.fadeNodes(this.getNodesForCells( 6819 this.getCellsForAction(action.fadeIn, true)), 6820 0, 1, waitAndExecute, (stop) ? 6821 0 : action.fadeIn.delay); 6822 } 6823 6824 if (action.fadeOut != null) 6825 { 6826 waitCounter++; 6827 Graph.fadeNodes(this.getNodesForCells( 6828 this.getCellsForAction(action.fadeOut, true)), 6829 1, 0, waitAndExecute, (stop) ? 6830 0 : action.fadeOut.delay); 6831 } 6832 6833 if (action.wipeIn != null) 6834 { 6835 animations = animations.concat(this.createWipeAnimations( 6836 this.getCellsForAction(action.wipeIn, true), true)); 6837 } 6838 6839 if (action.wipeOut != null) 6840 { 6841 animations = animations.concat(this.createWipeAnimations( 6842 this.getCellsForAction(action.wipeOut, true), false)); 6843 } 6844 6845 // Executes all actions that change cell states 6846 if (action.toggle != null) 6847 { 6848 beginUpdate(); 6849 this.toggleCells(this.getCellsForAction(action.toggle, true)); 6850 } 6851 6852 if (action.show != null) 6853 { 6854 beginUpdate(); 6855 var temp = this.getCellsForAction(action.show, true); 6856 Graph.setOpacityForNodes(this.getNodesForCells(temp), 1); 6857 this.setCellsVisible(temp, true); 6858 } 6859 6860 if (action.hide != null) 6861 { 6862 beginUpdate(); 6863 var temp = this.getCellsForAction(action.hide, true); 6864 Graph.setOpacityForNodes(this.getNodesForCells(temp), 0); 6865 this.setCellsVisible(temp, false); 6866 } 6867 6868 if (action.toggleStyle != null && action.toggleStyle.key != null) 6869 { 6870 beginUpdate(); 6871 this.toggleCellStyles(action.toggleStyle.key, (action.toggleStyle.defaultValue != null) ? 6872 action.toggleStyle.defaultValue : '0', this.getCellsForAction(action.toggleStyle, true)); 6873 } 6874 6875 if (action.style != null && action.style.key != null) 6876 { 6877 beginUpdate(); 6878 this.setCellStyles(action.style.key, action.style.value, 6879 this.getCellsForAction(action.style, true)); 6880 } 6881 6882 // Executes stateless actions on cells 6883 var cells = []; 6884 6885 if (action.select != null && this.isEnabled()) 6886 { 6887 cells = this.getCellsForAction(action.select); 6888 this.setSelectionCells(cells); 6889 } 6890 6891 if (action.highlight != null) 6892 { 6893 cells = this.getCellsForAction(action.highlight); 6894 this.highlightCells(cells, action.highlight.color, 6895 action.highlight.duration, 6896 action.highlight.opacity); 6897 } 6898 6899 if (action.scroll != null) 6900 { 6901 cells = this.getCellsForAction(action.scroll); 6902 } 6903 6904 if (action.viewbox != null) 6905 { 6906 this.fitWindow(action.viewbox, action.viewbox.border); 6907 } 6908 6909 if (cells.length > 0) 6910 { 6911 this.scrollCellToVisible(cells[0]); 6912 } 6913 6914 if (action.tags != null) 6915 { 6916 var hidden = []; 6917 6918 if (action.tags.hidden != null) 6919 { 6920 hidden = hidden.concat(action.tags.hidden); 6921 } 6922 6923 if (action.tags.visible != null) 6924 { 6925 var all = this.getAllTags(); 6926 6927 for (var i = 0; i < all.length; i++) 6928 { 6929 if (mxUtils.indexOf(action.tags.visible, all[i]) < 0 && 6930 mxUtils.indexOf(hidden, all[i]) < 0) 6931 { 6932 hidden.push(all[i]); 6933 } 6934 } 6935 } 6936 6937 this.hiddenTags = hidden; 6938 this.refresh(); 6939 } 6940 6941 if (animations.length > 0) 6942 { 6943 waitCounter++; 6944 this.executeAnimations(animations, waitAndExecute, 6945 (stop) ? 1 : action.steps, 6946 (stop) ? 0 : action.delay); 6947 } 6948 6949 if (waitCounter == 0) 6950 { 6951 executeNextAction(); 6952 } 6953 else 6954 { 6955 endUpdate(); 6956 } 6957 } 6958 else 6959 { 6960 this.executingCustomActions = false; 6961 this.stoppingCustomActions = false; 6962 endUpdate(); 6963 6964 if (done != null) 6965 { 6966 done(); 6967 } 6968 } 6969 }); 6970 6971 executeNextAction(); 6972 } 6973 else 6974 { 6975 this.stoppingCustomActions = true; 6976 6977 if (this.pendingWaitThread != null) 6978 { 6979 window.clearTimeout(this.pendingWaitThread); 6980 } 6981 6982 if (this.pendingExecuteNextAction != null) 6983 { 6984 this.pendingExecuteNextAction(); 6985 } 6986 6987 this.fireEvent(new mxEventObject('stopExecutingCustomActions')); 6988 } 6989 }; 6990 6991 /** 6992 * Updates cell IDs in custom links on the given cell and its label. 6993 */ 6994 Graph.prototype.doUpdateCustomLinksForCell = function(mapping, cell) 6995 { 6996 var href = this.getLinkForCell(cell); 6997 6998 if (href != null && href.substring(0, 17) == 'data:action/json,') 6999 { 7000 this.setLinkForCell(cell, this.updateCustomLink(mapping, href)); 7001 } 7002 7003 if (this.isHtmlLabel(cell)) 7004 { 7005 var temp = document.createElement('div'); 7006 temp.innerHTML = this.sanitizeHtml(this.getLabel(cell)); 7007 var links = temp.getElementsByTagName('a'); 7008 var changed = false; 7009 7010 for (var i = 0; i < links.length; i++) 7011 { 7012 href = links[i].getAttribute('href'); 7013 7014 if (href != null && href.substring(0, 17) == 'data:action/json,') 7015 { 7016 links[i].setAttribute('href', this.updateCustomLink(mapping, href)); 7017 changed = true; 7018 } 7019 } 7020 7021 if (changed) 7022 { 7023 this.labelChanged(cell, temp.innerHTML); 7024 } 7025 } 7026 }; 7027 7028 /** 7029 * Updates cell IDs in the given custom link and returns the updated link. 7030 */ 7031 Graph.prototype.updateCustomLink = function(mapping, href) 7032 { 7033 if (href.substring(0, 17) == 'data:action/json,') 7034 { 7035 try 7036 { 7037 var link = JSON.parse(href.substring(17)); 7038 7039 if (link.actions != null) 7040 { 7041 this.updateCustomLinkActions(mapping, link.actions); 7042 href = 'data:action/json,' + JSON.stringify(link); 7043 } 7044 } 7045 catch (e) 7046 { 7047 // Ignore 7048 } 7049 } 7050 7051 return href; 7052 }; 7053 7054 /** 7055 * Updates cell IDs in the given custom link actions. 7056 */ 7057 Graph.prototype.updateCustomLinkActions = function(mapping, actions) 7058 { 7059 for (var i = 0; i < actions.length; i++) 7060 { 7061 var action = actions[i]; 7062 7063 for (var name in action) 7064 { 7065 this.updateCustomLinkAction(mapping, action[name], 'cells'); 7066 this.updateCustomLinkAction(mapping, action[name], 'excludeCells'); 7067 } 7068 } 7069 }; 7070 7071 /** 7072 * Updates cell IDs in the given custom link action. 7073 */ 7074 Graph.prototype.updateCustomLinkAction = function(mapping, action, name) 7075 { 7076 if (action != null && action[name] != null) 7077 { 7078 var result = []; 7079 7080 for (var i = 0; i < action[name].length; i++) 7081 { 7082 if (action[name][i] == '*') 7083 { 7084 result.push(action[name][i]); 7085 } 7086 else 7087 { 7088 var temp = mapping[action[name][i]]; 7089 7090 if (temp != null) 7091 { 7092 if (temp != '') 7093 { 7094 result.push(temp); 7095 } 7096 } 7097 else 7098 { 7099 result.push(action[name][i]); 7100 } 7101 } 7102 } 7103 7104 action[name] = result; 7105 } 7106 }; 7107 7108 /** 7109 * Handles each action in the action array of a custom link. This code 7110 * handles toggle actions for cell IDs. 7111 */ 7112 Graph.prototype.getCellsForAction = function(action, layers) 7113 { 7114 var result = this.getCellsById(action.cells).concat( 7115 this.getCellsForTags(action.tags, null, layers)); 7116 7117 // Removes excluded cells 7118 if (action.excludeCells != null) 7119 { 7120 var temp = []; 7121 7122 for (var i = 0; i < result.length; i++) 7123 { 7124 if (action.excludeCells.indexOf(result[i].id) < 0) 7125 { 7126 temp.push(result[i]); 7127 } 7128 } 7129 7130 result = temp; 7131 } 7132 7133 return result; 7134 }; 7135 7136 /** 7137 * Returns the cells in the model (or given array) that have all of the 7138 * given tags in their tags property. 7139 */ 7140 Graph.prototype.getCellsById = function(ids) 7141 { 7142 var result = []; 7143 7144 if (ids != null) 7145 { 7146 for (var i = 0; i < ids.length; i++) 7147 { 7148 if (ids[i] == '*') 7149 { 7150 var parent = this.model.getRoot(); 7151 7152 result = result.concat(this.model.filterDescendants(function(cell) 7153 { 7154 return cell != parent; 7155 }, parent)); 7156 } 7157 else 7158 { 7159 var cell = this.model.getCell(ids[i]); 7160 7161 if (cell != null) 7162 { 7163 result.push(cell); 7164 } 7165 } 7166 } 7167 } 7168 7169 return result; 7170 }; 7171 7172 /** 7173 * Adds support for custom fonts in cell styles. 7174 */ 7175 var graphIsCellVisible = Graph.prototype.isCellVisible; 7176 Graph.prototype.isCellVisible = function(cell) 7177 { 7178 return graphIsCellVisible.apply(this, arguments) && 7179 !this.isAllTagsHidden(this.getTagsForCell(cell)); 7180 }; 7181 7182 /** 7183 * Returns the cells in the model (or given array) that have all of the 7184 * given tags in their tags property. 7185 */ 7186 Graph.prototype.isAllTagsHidden = function(tags) 7187 { 7188 if (tags == null || tags.length == 0 || 7189 this.hiddenTags.length == 0) 7190 { 7191 return false; 7192 } 7193 else 7194 { 7195 var tmp = tags.split(' '); 7196 7197 if (tmp.length > this.hiddenTags.length) 7198 { 7199 return false; 7200 } 7201 else 7202 { 7203 for (var i = 0; i < tmp.length; i++) 7204 { 7205 if (mxUtils.indexOf(this.hiddenTags, tmp[i]) < 0) 7206 { 7207 return false; 7208 } 7209 } 7210 7211 return true; 7212 } 7213 } 7214 }; 7215 7216 /** 7217 * Returns the cells in the model (or given array) that have all of the 7218 * given tags in their tags property. 7219 */ 7220 Graph.prototype.getCellsForTags = function(tagList, cells, includeLayers, checkVisible) 7221 { 7222 var result = []; 7223 7224 if (tagList != null) 7225 { 7226 cells = (cells != null) ? cells : this.model.getDescendants(this.model.getRoot()); 7227 7228 var tagCount = 0; 7229 var lookup = {}; 7230 7231 for (var i = 0; i < tagList.length; i++) 7232 { 7233 if (tagList[i].length > 0) 7234 { 7235 lookup[tagList[i]] = true; 7236 tagCount++; 7237 } 7238 } 7239 7240 for (var i = 0; i < cells.length; i++) 7241 { 7242 if ((includeLayers && this.model.getParent(cells[i]) == this.model.root) || 7243 this.model.isVertex(cells[i]) || this.model.isEdge(cells[i])) 7244 { 7245 var tags = this.getTagsForCell(cells[i]); 7246 var match = false; 7247 7248 if (tags.length > 0) 7249 { 7250 var tmp = tags.split(' '); 7251 7252 if (tmp.length >= tagList.length) 7253 { 7254 var matchCount = 0; 7255 7256 for (var j = 0; j < tmp.length && (matchCount < tagCount); j++) 7257 { 7258 if (lookup[tmp[j]] != null) 7259 { 7260 matchCount++; 7261 } 7262 } 7263 7264 match = matchCount == tagCount; 7265 } 7266 } 7267 7268 if (match && ((checkVisible != true) || this.isCellVisible(cells[i]))) 7269 { 7270 result.push(cells[i]); 7271 } 7272 } 7273 } 7274 } 7275 7276 return result; 7277 }; 7278 /** 7279 * Returns all tags in the diagram. 7280 */ 7281 Graph.prototype.getAllTags = function() 7282 { 7283 return this.getTagsForCells( 7284 this.model.getDescendants( 7285 this.model.getRoot())); 7286 7287 }; 7288 7289 /** 7290 * Returns the common tags for the given cells as a array. 7291 */ 7292 Graph.prototype.getCommonTagsForCells = function(cells) 7293 { 7294 var commonTokens = null; 7295 var validTags = []; 7296 7297 for (var i = 0; i < cells.length; i++) 7298 { 7299 var tags = this.getTagsForCell(cells[i]); 7300 validTags = []; 7301 7302 if (tags.length > 0) 7303 { 7304 var tokens = tags.split(' '); 7305 var temp = {}; 7306 7307 for (var j = 0; j < tokens.length; j++) 7308 { 7309 if (commonTokens == null || commonTokens[tokens[j]] != null) 7310 { 7311 temp[tokens[j]] = true; 7312 validTags.push(tokens[j]); 7313 } 7314 } 7315 7316 commonTokens = temp; 7317 } 7318 else 7319 { 7320 return []; 7321 } 7322 } 7323 7324 return validTags; 7325 }; 7326 7327 /** 7328 * Returns all tags for the given cells as an array. 7329 */ 7330 Graph.prototype.getTagsForCells = function(cells) 7331 { 7332 var tokens = []; 7333 var temp = {}; 7334 7335 for (var i = 0; i < cells.length; i++) 7336 { 7337 var tags = this.getTagsForCell(cells[i]); 7338 7339 if (tags.length > 0) 7340 { 7341 var t = tags.split(' '); 7342 7343 for (var j = 0; j < t.length; j++) 7344 { 7345 if (temp[t[j]] == null) 7346 { 7347 temp[t[j]] = true; 7348 tokens.push(t[j]); 7349 } 7350 } 7351 } 7352 } 7353 7354 return tokens; 7355 }; 7356 7357 /** 7358 * Returns the tags for the given cell as a string. 7359 */ 7360 Graph.prototype.getTagsForCell = function(cell) 7361 { 7362 return this.getAttributeForCell(cell, 'tags', ''); 7363 }; 7364 7365 /** 7366 * Adds the given array of tags to the given array cells. 7367 */ 7368 Graph.prototype.addTagsForCells = function(cells, tagList) 7369 { 7370 if (cells.length > 0 && tagList.length > 0) 7371 { 7372 this.model.beginUpdate(); 7373 7374 try 7375 { 7376 for (var i = 0; i < cells.length; i++) 7377 { 7378 var temp = this.getTagsForCell(cells[i]); 7379 var tags = temp.split(' '); 7380 var changed = false; 7381 7382 for (var j = 0; j < tagList.length; j++) 7383 { 7384 var tag = mxUtils.trim(tagList[j]); 7385 7386 if (tag != '' && mxUtils.indexOf(tags, tag) < 0) 7387 { 7388 temp = (temp.length > 0) ? temp + ' ' + tag : tag; 7389 changed = true; 7390 } 7391 } 7392 7393 if (changed) 7394 { 7395 this.setAttributeForCell(cells[i], 'tags', temp); 7396 } 7397 } 7398 } 7399 finally 7400 { 7401 this.model.endUpdate(); 7402 } 7403 } 7404 }; 7405 7406 /** 7407 * Removes the given array of tags from the given array cells. 7408 */ 7409 Graph.prototype.removeTagsForCells = function(cells, tagList) 7410 { 7411 if (cells.length > 0 && tagList.length > 0) 7412 { 7413 this.model.beginUpdate(); 7414 7415 try 7416 { 7417 for (var i = 0; i < cells.length; i++) 7418 { 7419 var tags = this.getTagsForCell(cells[i]); 7420 7421 if (tags.length > 0) 7422 { 7423 var tokens = tags.split(' '); 7424 var changed = false; 7425 7426 for (var j = 0; j < tagList.length; j++) 7427 { 7428 var idx = mxUtils.indexOf(tokens, tagList[j]); 7429 7430 if (idx >= 0) 7431 { 7432 tokens.splice(idx, 1); 7433 changed = true; 7434 } 7435 } 7436 7437 if (changed) 7438 { 7439 this.setAttributeForCell(cells[i], 'tags', tokens.join(' ')); 7440 } 7441 } 7442 } 7443 } 7444 finally 7445 { 7446 this.model.endUpdate(); 7447 } 7448 } 7449 }; 7450 7451 /** 7452 * Shows or hides the given cells. 7453 */ 7454 Graph.prototype.toggleCells = function(cells) 7455 { 7456 this.model.beginUpdate(); 7457 try 7458 { 7459 for (var i = 0; i < cells.length; i++) 7460 { 7461 this.model.setVisible(cells[i], !this.model.isVisible(cells[i])) 7462 } 7463 } 7464 finally 7465 { 7466 this.model.endUpdate(); 7467 } 7468 }; 7469 7470 /** 7471 * Shows or hides the given cells. 7472 */ 7473 Graph.prototype.setCellsVisible = function(cells, visible) 7474 { 7475 this.model.beginUpdate(); 7476 try 7477 { 7478 for (var i = 0; i < cells.length; i++) 7479 { 7480 this.model.setVisible(cells[i], visible); 7481 } 7482 } 7483 finally 7484 { 7485 this.model.endUpdate(); 7486 } 7487 }; 7488 7489 /** 7490 * Highlights the given cell. 7491 */ 7492 Graph.prototype.highlightCells = function(cells, color, duration, opacity) 7493 { 7494 for (var i = 0; i < cells.length; i++) 7495 { 7496 this.highlightCell(cells[i], color, duration, opacity); 7497 } 7498 }; 7499 7500 /** 7501 * Highlights the given cell. 7502 */ 7503 Graph.prototype.highlightCell = function(cell, color, duration, opacity) 7504 { 7505 color = (color != null) ? color : mxConstants.DEFAULT_VALID_COLOR; 7506 duration = (duration != null) ? duration : 1000; 7507 var state = this.view.getState(cell); 7508 7509 if (state != null) 7510 { 7511 var sw = Math.max(5, mxUtils.getValue(state.style, mxConstants.STYLE_STROKEWIDTH, 1) + 4); 7512 var hl = new mxCellHighlight(this, color, sw, false); 7513 7514 if (opacity != null) 7515 { 7516 hl.opacity = opacity; 7517 } 7518 7519 hl.highlight(state); 7520 7521 // Fades out the highlight after a duration 7522 window.setTimeout(function() 7523 { 7524 if (hl.shape != null) 7525 { 7526 mxUtils.setPrefixedStyle(hl.shape.node.style, 'transition', 'all 1200ms ease-in-out'); 7527 hl.shape.node.style.opacity = 0; 7528 } 7529 7530 // Destroys the highlight after the fade 7531 window.setTimeout(function() 7532 { 7533 hl.destroy(); 7534 }, 1200); 7535 }, duration); 7536 } 7537 }; 7538 7539 /** 7540 * Adds a shadow filter to the given svg root. 7541 */ 7542 Graph.prototype.addSvgShadow = function(svgRoot, group, createOnly, extend) 7543 { 7544 createOnly = (createOnly != null) ? createOnly : false; 7545 extend = (extend != null) ? extend : true; 7546 7547 var svgDoc = svgRoot.ownerDocument; 7548 7549 var filter = (svgDoc.createElementNS != null) ? 7550 svgDoc.createElementNS(mxConstants.NS_SVG, 'filter') : svgDoc.createElement('filter'); 7551 filter.setAttribute('id', this.shadowId); 7552 7553 var blur = (svgDoc.createElementNS != null) ? 7554 svgDoc.createElementNS(mxConstants.NS_SVG, 'feGaussianBlur') : svgDoc.createElement('feGaussianBlur'); 7555 blur.setAttribute('in', 'SourceAlpha'); 7556 blur.setAttribute('stdDeviation', this.svgShadowBlur); 7557 blur.setAttribute('result', 'blur'); 7558 filter.appendChild(blur); 7559 7560 var offset = (svgDoc.createElementNS != null) ? 7561 svgDoc.createElementNS(mxConstants.NS_SVG, 'feOffset') : svgDoc.createElement('feOffset'); 7562 offset.setAttribute('in', 'blur'); 7563 offset.setAttribute('dx', this.svgShadowSize); 7564 offset.setAttribute('dy', this.svgShadowSize); 7565 offset.setAttribute('result', 'offsetBlur'); 7566 filter.appendChild(offset); 7567 7568 var flood = (svgDoc.createElementNS != null) ? 7569 svgDoc.createElementNS(mxConstants.NS_SVG, 'feFlood') : svgDoc.createElement('feFlood'); 7570 flood.setAttribute('flood-color', this.svgShadowColor); 7571 flood.setAttribute('flood-opacity', this.svgShadowOpacity); 7572 flood.setAttribute('result', 'offsetColor'); 7573 filter.appendChild(flood); 7574 7575 var composite = (svgDoc.createElementNS != null) ? 7576 svgDoc.createElementNS(mxConstants.NS_SVG, 'feComposite') : svgDoc.createElement('feComposite'); 7577 composite.setAttribute('in', 'offsetColor'); 7578 composite.setAttribute('in2', 'offsetBlur'); 7579 composite.setAttribute('operator', 'in'); 7580 composite.setAttribute('result', 'offsetBlur'); 7581 filter.appendChild(composite); 7582 7583 var feBlend = (svgDoc.createElementNS != null) ? 7584 svgDoc.createElementNS(mxConstants.NS_SVG, 'feBlend') : svgDoc.createElement('feBlend'); 7585 feBlend.setAttribute('in', 'SourceGraphic'); 7586 feBlend.setAttribute('in2', 'offsetBlur'); 7587 filter.appendChild(feBlend); 7588 7589 // Creates defs element if not available 7590 var defs = svgRoot.getElementsByTagName('defs'); 7591 var defsElt = null; 7592 7593 if (defs.length == 0) 7594 { 7595 defsElt = (svgDoc.createElementNS != null) ? 7596 svgDoc.createElementNS(mxConstants.NS_SVG, 'defs') : svgDoc.createElement('defs'); 7597 7598 if (svgRoot.firstChild != null) 7599 { 7600 svgRoot.insertBefore(defsElt, svgRoot.firstChild); 7601 } 7602 else 7603 { 7604 svgRoot.appendChild(defsElt); 7605 } 7606 } 7607 else 7608 { 7609 defsElt = defs[0]; 7610 } 7611 7612 defsElt.appendChild(filter); 7613 7614 if (!createOnly) 7615 { 7616 group = (group != null) ? group : svgRoot.getElementsByTagName('g')[0]; 7617 7618 if (group != null) 7619 { 7620 group.setAttribute('filter', 'url(#' + this.shadowId + ')'); 7621 7622 if (!isNaN(parseInt(svgRoot.getAttribute('width'))) && extend) 7623 { 7624 svgRoot.setAttribute('width', parseInt(svgRoot.getAttribute('width')) + 6); 7625 svgRoot.setAttribute('height', parseInt(svgRoot.getAttribute('height')) + 6); 7626 7627 // Updates viewbox if one exists 7628 var vb = svgRoot.getAttribute('viewBox'); 7629 7630 if (vb != null && vb.length > 0) 7631 { 7632 var tokens = vb.split(' '); 7633 7634 if (tokens.length > 3) 7635 { 7636 w = parseFloat(tokens[2]) + 6; 7637 h = parseFloat(tokens[3]) + 6; 7638 7639 svgRoot.setAttribute('viewBox', tokens[0] + ' ' + tokens[1] + ' ' + w + ' ' + h); 7640 } 7641 } 7642 } 7643 } 7644 } 7645 7646 return filter; 7647 }; 7648 7649 /** 7650 * Loads the stylesheet for this graph. 7651 */ 7652 Graph.prototype.setShadowVisible = function(value, fireEvent) 7653 { 7654 if (mxClient.IS_SVG && !mxClient.IS_SF) 7655 { 7656 fireEvent = (fireEvent != null) ? fireEvent : true; 7657 this.shadowVisible = value; 7658 7659 if (this.shadowVisible) 7660 { 7661 this.view.getDrawPane().setAttribute('filter', 'url(#' + this.shadowId + ')'); 7662 } 7663 else 7664 { 7665 this.view.getDrawPane().removeAttribute('filter'); 7666 } 7667 7668 if (fireEvent) 7669 { 7670 this.fireEvent(new mxEventObject('shadowVisibleChanged')); 7671 } 7672 } 7673 }; 7674 7675 /** 7676 * Selects first unlocked layer if one exists 7677 */ 7678 Graph.prototype.selectUnlockedLayer = function() 7679 { 7680 if (this.defaultParent == null) 7681 { 7682 var childCount = this.model.getChildCount(this.model.root); 7683 var cell = null; 7684 var index = 0; 7685 7686 do 7687 { 7688 cell = this.model.getChildAt(this.model.root, index); 7689 } while (index++ < childCount && mxUtils.getValue(this.getCellStyle(cell), 'locked', '0') == '1') 7690 7691 if (cell != null) 7692 { 7693 this.setDefaultParent(cell); 7694 } 7695 } 7696 }; 7697 7698 /** 7699 * Specifies special libraries that are loaded via dynamic JS. Add cases 7700 * where the filename cannot be worked out from the package name. The 7701 * standard scheme for this mapping is stencils/packagename.xml. If there 7702 * are multiple XML files, any JS files or any anomalies in the filename or 7703 * directory that contains the file, then an entry must be added here and 7704 * in EmbedServlet2 for the loading of the shapes to work. 7705 */ 7706 // Required to avoid 404 for mockup.xml since naming of mxgraph.mockup.anchor does not contain 7707 // buttons even though it is defined in the mxMockupButtons.js file. This could only be fixed 7708 // with aliases for existing shapes or aliases for basenames, but this is essentially the same. 7709 mxStencilRegistry.libraries['mockup'] = [SHAPES_PATH + '/mockup/mxMockupButtons.js']; 7710 7711 mxStencilRegistry.libraries['arrows2'] = [SHAPES_PATH + '/mxArrows.js']; 7712 mxStencilRegistry.libraries['atlassian'] = [STENCIL_PATH + '/atlassian.xml', SHAPES_PATH + '/mxAtlassian.js']; 7713 mxStencilRegistry.libraries['bpmn'] = [SHAPES_PATH + '/mxBasic.js', STENCIL_PATH + '/bpmn.xml', SHAPES_PATH + '/bpmn/mxBpmnShape2.js']; 7714 mxStencilRegistry.libraries['bpmn2'] = [SHAPES_PATH + '/mxBasic.js', STENCIL_PATH + '/bpmn.xml', SHAPES_PATH + '/bpmn/mxBpmnShape2.js']; 7715 mxStencilRegistry.libraries['c4'] = [SHAPES_PATH + '/mxC4.js']; 7716 mxStencilRegistry.libraries['cisco19'] = [SHAPES_PATH + '/mxCisco19.js', STENCIL_PATH + '/cisco19.xml']; 7717 mxStencilRegistry.libraries['cisco_safe'] = [SHAPES_PATH + '/mxCiscoSafe.js', STENCIL_PATH + '/cisco_safe/architecture.xml', STENCIL_PATH + '/cisco_safe/business_icons.xml', STENCIL_PATH + '/cisco_safe/capability.xml', STENCIL_PATH + '/cisco_safe/design.xml', STENCIL_PATH + '/cisco_safe/iot_things_icons.xml', STENCIL_PATH + '/cisco_safe/people_places_things_icons.xml', STENCIL_PATH + '/cisco_safe/security_icons.xml', STENCIL_PATH + '/cisco_safe/technology_icons.xml', STENCIL_PATH + '/cisco_safe/threat.xml']; 7718 mxStencilRegistry.libraries['dfd'] = [SHAPES_PATH + '/mxDFD.js']; 7719 mxStencilRegistry.libraries['er'] = [SHAPES_PATH + '/er/mxER.js']; 7720 mxStencilRegistry.libraries['kubernetes'] = [SHAPES_PATH + '/mxKubernetes.js', STENCIL_PATH + '/kubernetes.xml']; 7721 mxStencilRegistry.libraries['flowchart'] = [SHAPES_PATH + '/mxFlowchart.js', STENCIL_PATH + '/flowchart.xml']; 7722 mxStencilRegistry.libraries['ios'] = [SHAPES_PATH + '/mockup/mxMockupiOS.js']; 7723 mxStencilRegistry.libraries['rackGeneral'] = [SHAPES_PATH + '/rack/mxRack.js', STENCIL_PATH + '/rack/general.xml']; 7724 mxStencilRegistry.libraries['rackF5'] = [STENCIL_PATH + '/rack/f5.xml']; 7725 mxStencilRegistry.libraries['lean_mapping'] = [SHAPES_PATH + '/mxLeanMap.js', STENCIL_PATH + '/lean_mapping.xml']; 7726 mxStencilRegistry.libraries['basic'] = [SHAPES_PATH + '/mxBasic.js', STENCIL_PATH + '/basic.xml']; 7727 mxStencilRegistry.libraries['ios7icons'] = [STENCIL_PATH + '/ios7/icons.xml']; 7728 mxStencilRegistry.libraries['ios7ui'] = [SHAPES_PATH + '/ios7/mxIOS7Ui.js', STENCIL_PATH + '/ios7/misc.xml']; 7729 mxStencilRegistry.libraries['android'] = [SHAPES_PATH + '/mxAndroid.js', STENCIL_PATH + '/android/android.xml']; 7730 mxStencilRegistry.libraries['electrical/abstract'] = [SHAPES_PATH + '/mxElectrical.js', STENCIL_PATH + '/electrical/abstract.xml']; 7731 mxStencilRegistry.libraries['electrical/logic_gates'] = [SHAPES_PATH + '/mxElectrical.js', STENCIL_PATH + '/electrical/logic_gates.xml']; 7732 mxStencilRegistry.libraries['electrical/miscellaneous'] = [SHAPES_PATH + '/mxElectrical.js', STENCIL_PATH + '/electrical/miscellaneous.xml']; 7733 mxStencilRegistry.libraries['electrical/signal_sources'] = [SHAPES_PATH + '/mxElectrical.js', STENCIL_PATH + '/electrical/signal_sources.xml']; 7734 mxStencilRegistry.libraries['electrical/transmission'] = [SHAPES_PATH + '/mxElectrical.js', STENCIL_PATH + '/electrical/transmission.xml']; 7735 mxStencilRegistry.libraries['infographic'] = [SHAPES_PATH + '/mxInfographic.js']; 7736 mxStencilRegistry.libraries['mockup/buttons'] = [SHAPES_PATH + '/mockup/mxMockupButtons.js']; 7737 mxStencilRegistry.libraries['mockup/containers'] = [SHAPES_PATH + '/mockup/mxMockupContainers.js']; 7738 mxStencilRegistry.libraries['mockup/forms'] = [SHAPES_PATH + '/mockup/mxMockupForms.js']; 7739 mxStencilRegistry.libraries['mockup/graphics'] = [SHAPES_PATH + '/mockup/mxMockupGraphics.js', STENCIL_PATH + '/mockup/misc.xml']; 7740 mxStencilRegistry.libraries['mockup/markup'] = [SHAPES_PATH + '/mockup/mxMockupMarkup.js']; 7741 mxStencilRegistry.libraries['mockup/misc'] = [SHAPES_PATH + '/mockup/mxMockupMisc.js', STENCIL_PATH + '/mockup/misc.xml']; 7742 mxStencilRegistry.libraries['mockup/navigation'] = [SHAPES_PATH + '/mockup/mxMockupNavigation.js', STENCIL_PATH + '/mockup/misc.xml']; 7743 mxStencilRegistry.libraries['mockup/text'] = [SHAPES_PATH + '/mockup/mxMockupText.js']; 7744 mxStencilRegistry.libraries['floorplan'] = [SHAPES_PATH + '/mxFloorplan.js', STENCIL_PATH + '/floorplan.xml']; 7745 mxStencilRegistry.libraries['bootstrap'] = [SHAPES_PATH + '/mxBootstrap.js', SHAPES_PATH + '/mxBasic.js', STENCIL_PATH + '/bootstrap.xml']; 7746 mxStencilRegistry.libraries['gmdl'] = [SHAPES_PATH + '/mxGmdl.js', STENCIL_PATH + '/gmdl.xml']; 7747 mxStencilRegistry.libraries['gcp2'] = [SHAPES_PATH + '/mxGCP2.js', STENCIL_PATH + '/gcp2.xml']; 7748 mxStencilRegistry.libraries['ibm'] = [SHAPES_PATH + '/mxIBM.js', STENCIL_PATH + '/ibm.xml']; 7749 mxStencilRegistry.libraries['cabinets'] = [SHAPES_PATH + '/mxCabinets.js', STENCIL_PATH + '/cabinets.xml']; 7750 mxStencilRegistry.libraries['archimate'] = [SHAPES_PATH + '/mxArchiMate.js']; 7751 mxStencilRegistry.libraries['archimate3'] = [SHAPES_PATH + '/mxArchiMate3.js']; 7752 mxStencilRegistry.libraries['sysml'] = [SHAPES_PATH + '/mxSysML.js']; 7753 mxStencilRegistry.libraries['eip'] = [SHAPES_PATH + '/mxEip.js', STENCIL_PATH + '/eip.xml']; 7754 mxStencilRegistry.libraries['networks'] = [SHAPES_PATH + '/mxNetworks.js', STENCIL_PATH + '/networks.xml']; 7755 mxStencilRegistry.libraries['aws3d'] = [SHAPES_PATH + '/mxAWS3D.js', STENCIL_PATH + '/aws3d.xml']; 7756 mxStencilRegistry.libraries['aws4'] = [SHAPES_PATH + '/mxAWS4.js', STENCIL_PATH + '/aws4.xml']; 7757 mxStencilRegistry.libraries['aws4b'] = [SHAPES_PATH + '/mxAWS4.js', STENCIL_PATH + '/aws4.xml']; 7758 mxStencilRegistry.libraries['uml25'] = [SHAPES_PATH + '/mxUML25.js']; 7759 mxStencilRegistry.libraries['veeam'] = [STENCIL_PATH + '/veeam/2d.xml', STENCIL_PATH + '/veeam/3d.xml', STENCIL_PATH + '/veeam/veeam.xml']; 7760 mxStencilRegistry.libraries['veeam2'] = [STENCIL_PATH + '/veeam/2d.xml', STENCIL_PATH + '/veeam/3d.xml', STENCIL_PATH + '/veeam/veeam2.xml']; 7761 mxStencilRegistry.libraries['pid2inst'] = [SHAPES_PATH + '/pid2/mxPidInstruments.js']; 7762 mxStencilRegistry.libraries['pid2misc'] = [SHAPES_PATH + '/pid2/mxPidMisc.js', STENCIL_PATH + '/pid/misc.xml']; 7763 mxStencilRegistry.libraries['pid2valves'] = [SHAPES_PATH + '/pid2/mxPidValves.js']; 7764 mxStencilRegistry.libraries['pidFlowSensors'] = [STENCIL_PATH + '/pid/flow_sensors.xml']; 7765 7766 // Triggers dynamic loading for markers 7767 mxMarker.getPackageForType = function(type) 7768 { 7769 var name = null; 7770 7771 if (type != null && type.length > 0) 7772 { 7773 if (type.substring(0, 2) == 'ER') 7774 { 7775 name = 'mxgraph.er'; 7776 } 7777 else if (type.substring(0, 5) == 'sysML') 7778 { 7779 name = 'mxgraph.sysml'; 7780 } 7781 } 7782 7783 return name; 7784 }; 7785 7786 var mxMarkerCreateMarker = mxMarker.createMarker; 7787 7788 mxMarker.createMarker = function(canvas, shape, type, pe, unitX, unitY, size, source, sw, filled) 7789 { 7790 if (type != null) 7791 { 7792 var f = mxMarker.markers[type]; 7793 7794 if (f == null) 7795 { 7796 var name = this.getPackageForType(type); 7797 7798 if (name != null) 7799 { 7800 mxStencilRegistry.getStencil(name); 7801 } 7802 } 7803 } 7804 7805 return mxMarkerCreateMarker.apply(this, arguments); 7806 }; 7807 7808 /** 7809 * Constructs a new print dialog. 7810 */ 7811 PrintDialog.prototype.create = function(editorUi, titleText) 7812 { 7813 var graph = editorUi.editor.graph; 7814 var div = document.createElement('div'); 7815 7816 var title = document.createElement('h3'); 7817 title.style.width = '100%'; 7818 title.style.textAlign = 'center'; 7819 title.style.marginTop = '0px'; 7820 mxUtils.write(title, titleText || mxResources.get('print')); 7821 div.appendChild(title); 7822 7823 var pageCount = 1; 7824 var currentPage = 1; 7825 7826 // Pages 7827 var pagesSection = document.createElement('div'); 7828 pagesSection.style.cssText = 'border-bottom:1px solid lightGray;padding-bottom:12px;margin-bottom:12px;'; 7829 7830 var allPagesRadio = document.createElement('input'); 7831 allPagesRadio.style.cssText = 'margin-right:8px;margin-bottom:8px;'; 7832 allPagesRadio.setAttribute('value', 'all'); 7833 allPagesRadio.setAttribute('type', 'radio'); 7834 allPagesRadio.setAttribute('name', 'pages-printdialog'); 7835 7836 pagesSection.appendChild(allPagesRadio); 7837 7838 var span = document.createElement('span'); 7839 mxUtils.write(span, mxResources.get('printAllPages')); 7840 pagesSection.appendChild(span); 7841 7842 mxUtils.br(pagesSection); 7843 7844 // Pages ... to ... 7845 var pagesRadio = allPagesRadio.cloneNode(true); 7846 allPagesRadio.setAttribute('checked', 'checked'); 7847 pagesRadio.setAttribute('value', 'range'); 7848 pagesSection.appendChild(pagesRadio); 7849 7850 var span = document.createElement('span'); 7851 mxUtils.write(span, mxResources.get('pages') + ':'); 7852 pagesSection.appendChild(span); 7853 7854 var pagesFromInput = document.createElement('input'); 7855 pagesFromInput.style.cssText = 'margin:0 8px 0 8px;' 7856 pagesFromInput.setAttribute('value', '1'); 7857 pagesFromInput.setAttribute('type', 'number'); 7858 pagesFromInput.setAttribute('min', '1'); 7859 pagesFromInput.style.width = '50px'; 7860 pagesSection.appendChild(pagesFromInput); 7861 7862 var span = document.createElement('span'); 7863 mxUtils.write(span, mxResources.get('to')); 7864 pagesSection.appendChild(span); 7865 7866 var pagesToInput = pagesFromInput.cloneNode(true); 7867 pagesSection.appendChild(pagesToInput); 7868 7869 mxEvent.addListener(pagesFromInput, 'focus', function() 7870 { 7871 pagesRadio.checked = true; 7872 }); 7873 7874 mxEvent.addListener(pagesToInput, 'focus', function() 7875 { 7876 pagesRadio.checked = true; 7877 }); 7878 7879 function validatePageRange() 7880 { 7881 pagesToInput.value = Math.max(1, Math.min(pageCount, Math.max(parseInt(pagesToInput.value), parseInt(pagesFromInput.value)))); 7882 pagesFromInput.value = Math.max(1, Math.min(pageCount, Math.min(parseInt(pagesToInput.value), parseInt(pagesFromInput.value)))); 7883 }; 7884 7885 mxEvent.addListener(pagesFromInput, 'change', validatePageRange); 7886 mxEvent.addListener(pagesToInput, 'change', validatePageRange); 7887 7888 if (editorUi.pages != null) 7889 { 7890 pageCount = editorUi.pages.length; 7891 7892 if (editorUi.currentPage != null) 7893 { 7894 for (var i = 0; i < editorUi.pages.length; i++) 7895 { 7896 if (editorUi.currentPage == editorUi.pages[i]) 7897 { 7898 currentPage = i + 1; 7899 pagesFromInput.value = currentPage; 7900 pagesToInput.value = currentPage; 7901 break; 7902 } 7903 } 7904 } 7905 } 7906 7907 pagesFromInput.setAttribute('max', pageCount); 7908 pagesToInput.setAttribute('max', pageCount); 7909 7910 if (!editorUi.isPagesEnabled()) 7911 { 7912 pagesRadio.checked = true; 7913 } 7914 else if (pageCount > 1) 7915 { 7916 div.appendChild(pagesSection); 7917 pagesRadio.checked = true; 7918 } 7919 7920 // Adjust to ... 7921 var adjustSection = document.createElement('div'); 7922 adjustSection.style.marginBottom = '10px'; 7923 7924 var adjustRadio = document.createElement('input'); 7925 adjustRadio.style.marginRight = '8px'; 7926 7927 adjustRadio.setAttribute('value', 'adjust'); 7928 adjustRadio.setAttribute('type', 'radio'); 7929 adjustRadio.setAttribute('name', 'printZoom'); 7930 adjustSection.appendChild(adjustRadio); 7931 7932 var span = document.createElement('span'); 7933 mxUtils.write(span, mxResources.get('adjustTo')); 7934 adjustSection.appendChild(span); 7935 7936 var zoomInput = document.createElement('input'); 7937 zoomInput.style.cssText = 'margin:0 8px 0 8px;'; 7938 zoomInput.setAttribute('value', '100 %'); 7939 zoomInput.style.width = '50px'; 7940 adjustSection.appendChild(zoomInput); 7941 7942 mxEvent.addListener(zoomInput, 'focus', function() 7943 { 7944 adjustRadio.checked = true; 7945 }); 7946 7947 div.appendChild(adjustSection); 7948 7949 // Fit to ... 7950 var fitSection = pagesSection.cloneNode(false); 7951 7952 var fitRadio = adjustRadio.cloneNode(true); 7953 fitRadio.setAttribute('value', 'fit'); 7954 adjustRadio.setAttribute('checked', 'checked'); 7955 7956 var spanFitRadio = document.createElement('div'); 7957 spanFitRadio.style.cssText = 'display:inline-block;height:100%;vertical-align:top;padding-top:2px;'; 7958 spanFitRadio.appendChild(fitRadio); 7959 fitSection.appendChild(spanFitRadio); 7960 7961 var table = document.createElement('table'); 7962 table.style.display = 'inline-block'; 7963 var tbody = document.createElement('tbody'); 7964 7965 var row1 = document.createElement('tr'); 7966 var row2 = row1.cloneNode(true); 7967 7968 var td1 = document.createElement('td'); 7969 var td2 = td1.cloneNode(true); 7970 var td3 = td1.cloneNode(true); 7971 7972 var td4 = td1.cloneNode(true); 7973 var td5 = td1.cloneNode(true); 7974 var td6 = td1.cloneNode(true); 7975 7976 td1.style.textAlign = 'right'; 7977 td4.style.textAlign = 'right'; 7978 7979 mxUtils.write(td1, mxResources.get('fitTo')); 7980 7981 var sheetsAcrossInput = document.createElement('input'); 7982 sheetsAcrossInput.style.cssText = 'margin:0 8px 0 8px;'; 7983 sheetsAcrossInput.setAttribute('value', '1'); 7984 sheetsAcrossInput.setAttribute('min', '1'); 7985 sheetsAcrossInput.setAttribute('type', 'number'); 7986 sheetsAcrossInput.style.width = '40px'; 7987 td2.appendChild(sheetsAcrossInput); 7988 7989 var span = document.createElement('span'); 7990 mxUtils.write(span, mxResources.get('fitToSheetsAcross')); 7991 td3.appendChild(span); 7992 7993 mxUtils.write(td4, mxResources.get('fitToBy')); 7994 7995 var sheetsDownInput = sheetsAcrossInput.cloneNode(true); 7996 td5.appendChild(sheetsDownInput); 7997 7998 mxEvent.addListener(sheetsAcrossInput, 'focus', function() 7999 { 8000 fitRadio.checked = true; 8001 }); 8002 8003 mxEvent.addListener(sheetsDownInput, 'focus', function() 8004 { 8005 fitRadio.checked = true; 8006 }); 8007 8008 var span = document.createElement('span'); 8009 mxUtils.write(span, mxResources.get('fitToSheetsDown')); 8010 td6.appendChild(span); 8011 8012 row1.appendChild(td1); 8013 row1.appendChild(td2); 8014 row1.appendChild(td3); 8015 8016 row2.appendChild(td4); 8017 row2.appendChild(td5); 8018 row2.appendChild(td6); 8019 8020 tbody.appendChild(row1); 8021 tbody.appendChild(row2); 8022 table.appendChild(tbody); 8023 fitSection.appendChild(table); 8024 8025 div.appendChild(fitSection); 8026 8027 // Page scale ... 8028 var pageScaleSection = document.createElement('div'); 8029 8030 var span = document.createElement('div'); 8031 span.style.fontWeight = 'bold'; 8032 span.style.marginBottom = '12px'; 8033 mxUtils.write(span, mxResources.get('paperSize')); 8034 pageScaleSection.appendChild(span); 8035 8036 var span = document.createElement('div'); 8037 span.style.marginBottom = '12px'; 8038 8039 var accessor = PageSetupDialog.addPageFormatPanel(span, 'printdialog', 8040 editorUi.editor.graph.pageFormat || mxConstants.PAGE_FORMAT_A4_PORTRAIT); 8041 pageScaleSection.appendChild(span); 8042 8043 var span = document.createElement('span'); 8044 mxUtils.write(span, mxResources.get('pageScale')); 8045 pageScaleSection.appendChild(span); 8046 8047 var pageScaleInput = document.createElement('input'); 8048 pageScaleInput.style.cssText = 'margin:0 8px 0 8px;'; 8049 pageScaleInput.setAttribute('value', '100 %'); 8050 pageScaleInput.style.width = '60px'; 8051 pageScaleSection.appendChild(pageScaleInput); 8052 8053 div.appendChild(pageScaleSection); 8054 8055 // Buttons 8056 var buttons = document.createElement('div'); 8057 buttons.style.cssText = 'text-align:right;margin:48px 0 0 0;'; 8058 8059 // Overall scale for print-out to account for print borders in dialogs etc 8060 function preview(print) 8061 { 8062 var printScale = parseInt(pageScaleInput.value) / 100; 8063 8064 if (isNaN(printScale)) 8065 { 8066 printScale = 1; 8067 pageScaleInput.value = '100 %'; 8068 } 8069 8070 // Workaround to match available paper size in actual print output 8071 printScale *= 0.75; 8072 8073 // Disables dark mode while printing 8074 var darkStylesheet = null; 8075 8076 if (graph.themes != null && graph.defaultThemeName == 'darkTheme') 8077 { 8078 darkStylesheet = graph.stylesheet; 8079 graph.stylesheet = graph.getDefaultStylesheet() 8080 graph.refresh(); 8081 } 8082 8083 function printGraph(thisGraph, pv, forcePageBreaks) 8084 { 8085 // Workaround for CSS transforms affecting the print output 8086 // is to disable during print output and restore after 8087 var prev = thisGraph.useCssTransforms; 8088 var prevTranslate = thisGraph.currentTranslate; 8089 var prevScale = thisGraph.currentScale; 8090 var prevViewTranslate = thisGraph.view.translate; 8091 var prevViewScale = thisGraph.view.scale; 8092 8093 if (thisGraph.useCssTransforms) 8094 { 8095 thisGraph.useCssTransforms = false; 8096 thisGraph.currentTranslate = new mxPoint(0,0); 8097 thisGraph.currentScale = 1; 8098 thisGraph.view.translate = new mxPoint(0,0); 8099 thisGraph.view.scale = 1; 8100 } 8101 8102 // Negative coordinates are cropped or shifted if page visible 8103 var gb = thisGraph.getGraphBounds(); 8104 var border = 0; 8105 var x0 = 0; 8106 var y0 = 0; 8107 8108 var pf = accessor.get(); 8109 var scale = 1 / thisGraph.pageScale; 8110 var autoOrigin = fitRadio.checked; 8111 8112 if (autoOrigin) 8113 { 8114 var h = parseInt(sheetsAcrossInput.value); 8115 var v = parseInt(sheetsDownInput.value); 8116 8117 scale = Math.min((pf.height * v) / (gb.height / thisGraph.view.scale), 8118 (pf.width * h) / (gb.width / thisGraph.view.scale)); 8119 } 8120 else 8121 { 8122 scale = parseInt(zoomInput.value) / (100 * thisGraph.pageScale); 8123 8124 if (isNaN(scale)) 8125 { 8126 printScale = 1 / thisGraph.pageScale; 8127 zoomInput.value = '100 %'; 8128 } 8129 } 8130 8131 // Applies print scale 8132 pf = mxRectangle.fromRectangle(pf); 8133 pf.width = Math.ceil(pf.width * printScale); 8134 pf.height = Math.ceil(pf.height * printScale); 8135 scale *= printScale; 8136 8137 // Starts at first visible page 8138 if (!autoOrigin && thisGraph.pageVisible) 8139 { 8140 var layout = thisGraph.getPageLayout(); 8141 x0 -= layout.x * pf.width; 8142 y0 -= layout.y * pf.height; 8143 } 8144 else 8145 { 8146 autoOrigin = true; 8147 } 8148 8149 if (pv == null) 8150 { 8151 pv = PrintDialog.createPrintPreview(thisGraph, scale, pf, border, x0, y0, autoOrigin); 8152 pv.pageSelector = false; 8153 pv.mathEnabled = false; 8154 8155 var file = editorUi.getCurrentFile(); 8156 8157 if (file != null) 8158 { 8159 pv.title = file.getTitle(); 8160 } 8161 8162 var writeHead = pv.writeHead; 8163 8164 // Overridden to add custom fonts 8165 pv.writeHead = function(doc) 8166 { 8167 writeHead.apply(this, arguments); 8168 8169 // Workaround for zoomed math clipping in Webkit 8170 if (mxClient.IS_GC || mxClient.IS_SF) 8171 { 8172 doc.writeln('<style type="text/css">'); 8173 doc.writeln(Editor.mathJaxWebkitCss); 8174 doc.writeln('</style>'); 8175 } 8176 8177 // Fixes font weight for PDF export in Chrome 8178 if (mxClient.IS_GC) 8179 { 8180 doc.writeln('<style type="text/css">'); 8181 doc.writeln('@media print {'); 8182 doc.writeln('span.MathJax_SVG svg { shape-rendering: crispEdges; }'); 8183 doc.writeln('}'); 8184 doc.writeln('</style>'); 8185 } 8186 8187 if (editorUi.editor.fontCss != null) 8188 { 8189 doc.writeln('<style type="text/css">'); 8190 doc.writeln(editorUi.editor.fontCss); 8191 doc.writeln('</style>'); 8192 } 8193 8194 var extFonts = thisGraph.getCustomFonts(); 8195 8196 for (var i = 0; i < extFonts.length; i++) 8197 { 8198 var fontName = extFonts[i].name; 8199 var fontUrl = extFonts[i].url; 8200 8201 if (Graph.isCssFontUrl(fontUrl)) 8202 { 8203 doc.writeln('<link rel="stylesheet" href="' + 8204 mxUtils.htmlEntities(fontUrl) + 8205 '" charset="UTF-8" type="text/css">'); 8206 } 8207 else 8208 { 8209 doc.writeln('<style type="text/css">'); 8210 doc.writeln('@font-face {\n' + 8211 'font-family: "' + mxUtils.htmlEntities(fontName) + '";\n' + 8212 'src: url("' + mxUtils.htmlEntities(fontUrl) + '");\n}'); 8213 doc.writeln('</style>'); 8214 } 8215 } 8216 }; 8217 8218 if (typeof(MathJax) !== 'undefined') 8219 { 8220 // Adds class to ignore if math is disabled 8221 var printPreviewRenderPage = pv.renderPage; 8222 8223 pv.renderPage = function(w, h, dx, dy, content, pageNumber) 8224 { 8225 var prev = mxClient.NO_FO; 8226 mxClient.NO_FO = (this.graph.mathEnabled && !editorUi.editor.useForeignObjectForMath) ? 8227 true : editorUi.editor.originalNoForeignObject; 8228 var result = printPreviewRenderPage.apply(this, arguments); 8229 mxClient.NO_FO = prev; 8230 8231 if (this.graph.mathEnabled) 8232 { 8233 this.mathEnabled = this.mathEnabled || true; 8234 } 8235 else 8236 { 8237 result.className = 'geDisableMathJax'; 8238 } 8239 8240 return result; 8241 }; 8242 } 8243 8244 // Switches stylesheet for print output in dark mode 8245 var temp = null; 8246 8247 // Disables dashed printing of flowAnimation 8248 var enableFlowAnimation = graph.enableFlowAnimation; 8249 graph.enableFlowAnimation = false; 8250 8251 if (graph.themes != null && graph.defaultThemeName == 'darkTheme') 8252 { 8253 temp = graph.stylesheet; 8254 graph.stylesheet = graph.getDefaultStylesheet() 8255 graph.refresh(); 8256 } 8257 8258 // Generates the print output 8259 pv.open(null, null, forcePageBreaks, true); 8260 8261 // Restores flowAnimation 8262 graph.enableFlowAnimation = enableFlowAnimation; 8263 8264 // Restores the stylesheet 8265 if (temp != null) 8266 { 8267 graph.stylesheet = temp; 8268 graph.refresh(); 8269 } 8270 } 8271 else 8272 { 8273 var bg = thisGraph.background; 8274 8275 if (bg == null || bg == '' || bg == mxConstants.NONE) 8276 { 8277 bg = '#ffffff'; 8278 } 8279 8280 pv.backgroundColor = bg; 8281 pv.autoOrigin = autoOrigin; 8282 pv.appendGraph(thisGraph, scale, x0, y0, forcePageBreaks, true); 8283 8284 var extFonts = thisGraph.getCustomFonts(); 8285 8286 if (pv.wnd != null) 8287 { 8288 for (var i = 0; i < extFonts.length; i++) 8289 { 8290 var fontName = extFonts[i].name; 8291 var fontUrl = extFonts[i].url; 8292 8293 if (Graph.isCssFontUrl(fontUrl)) 8294 { 8295 pv.wnd.document.writeln('<link rel="stylesheet" href="' + 8296 mxUtils.htmlEntities(fontUrl) + 8297 '" charset="UTF-8" type="text/css">'); 8298 } 8299 else 8300 { 8301 pv.wnd.document.writeln('<style type="text/css">'); 8302 pv.wnd.document.writeln('@font-face {\n' + 8303 'font-family: "' + mxUtils.htmlEntities(fontName) + '";\n' + 8304 'src: url("' + mxUtils.htmlEntities(fontUrl) + '");\n}'); 8305 pv.wnd.document.writeln('</style>'); 8306 } 8307 } 8308 } 8309 } 8310 8311 // Restores state if css transforms are used 8312 if (prev) 8313 { 8314 thisGraph.useCssTransforms = prev; 8315 thisGraph.currentTranslate = prevTranslate; 8316 thisGraph.currentScale = prevScale; 8317 thisGraph.view.translate = prevViewTranslate; 8318 thisGraph.view.scale = prevViewScale; 8319 } 8320 8321 return pv; 8322 }; 8323 8324 var pagesFrom = pagesFromInput.value; 8325 var pagesTo = pagesToInput.value; 8326 var ignorePages = !allPagesRadio.checked; 8327 var pv = null; 8328 8329 if (EditorUi.isElectronApp) 8330 { 8331 PrintDialog.electronPrint(editorUi, allPagesRadio.checked, pagesFrom, pagesTo, fitRadio.checked, 8332 sheetsAcrossInput.value, sheetsDownInput.value, parseInt(zoomInput.value) / 100, 8333 parseInt(pageScaleInput.value) / 100, accessor.get()); 8334 8335 return; 8336 } 8337 8338 if (ignorePages) 8339 { 8340 ignorePages = pagesFrom == currentPage && pagesTo == currentPage; 8341 } 8342 8343 if (!ignorePages && editorUi.pages != null && editorUi.pages.length) 8344 { 8345 var i0 = 0; 8346 var imax = editorUi.pages.length - 1; 8347 8348 if (!allPagesRadio.checked) 8349 { 8350 i0 = parseInt(pagesFrom) - 1; 8351 imax = parseInt(pagesTo) - 1; 8352 } 8353 8354 for (var i = i0; i <= imax; i++) 8355 { 8356 var page = editorUi.pages[i]; 8357 var tempGraph = (page == editorUi.currentPage) ? graph : null; 8358 8359 if (tempGraph == null) 8360 { 8361 tempGraph = editorUi.createTemporaryGraph(graph.stylesheet); 8362 8363 // Restores graph settings that are relevant for printing 8364 var pageVisible = true; 8365 var mathEnabled = false; 8366 var bg = null; 8367 var bgImage = null; 8368 8369 if (page.viewState == null) 8370 { 8371 // Workaround to extract view state from XML node 8372 // This changes the state of the page and parses 8373 // the XML for the graph model even if not needed. 8374 if (page.root == null) 8375 { 8376 editorUi.updatePageRoot(page); 8377 } 8378 } 8379 8380 if (page.viewState != null) 8381 { 8382 pageVisible = page.viewState.pageVisible; 8383 mathEnabled = page.viewState.mathEnabled; 8384 bg = page.viewState.background; 8385 bgImage = page.viewState.backgroundImage; 8386 tempGraph.extFonts = page.viewState.extFonts; 8387 } 8388 8389 tempGraph.background = bg; 8390 tempGraph.backgroundImage = (bgImage != null) ? new mxImage(bgImage.src, bgImage.width, bgImage.height) : null; 8391 tempGraph.pageVisible = pageVisible; 8392 tempGraph.mathEnabled = mathEnabled; 8393 8394 // Redirects placeholders to current page 8395 var graphGetGlobalVariable = tempGraph.getGlobalVariable; 8396 8397 tempGraph.getGlobalVariable = function(name) 8398 { 8399 if (name == 'page') 8400 { 8401 return page.getName(); 8402 } 8403 else if (name == 'pagenumber') 8404 { 8405 return i + 1; 8406 } 8407 else if (name == 'pagecount') 8408 { 8409 return (editorUi.pages != null) ? editorUi.pages.length : 1; 8410 } 8411 8412 return graphGetGlobalVariable.apply(this, arguments); 8413 }; 8414 8415 document.body.appendChild(tempGraph.container); 8416 editorUi.updatePageRoot(page); 8417 tempGraph.model.setRoot(page.root); 8418 } 8419 8420 pv = printGraph(tempGraph, pv, i != imax); 8421 8422 if (tempGraph != graph) 8423 { 8424 tempGraph.container.parentNode.removeChild(tempGraph.container); 8425 } 8426 } 8427 } 8428 else 8429 { 8430 pv = printGraph(graph); 8431 } 8432 8433 if (pv == null) 8434 { 8435 editorUi.handleError({message: mxResources.get('errorUpdatingPreview')}); 8436 } 8437 else 8438 { 8439 if (pv.mathEnabled) 8440 { 8441 var doc = pv.wnd.document; 8442 8443 // Adds asynchronous printing when MathJax finishes rendering 8444 // via global variable that is checked in math-print.js to 8445 // avoid generating unsafe-inline script or adding SHA to CSP 8446 if (print) 8447 { 8448 pv.wnd.IMMEDIATE_PRINT = true; 8449 } 8450 8451 doc.writeln('<script type="text/javascript" src="' + DRAWIO_BASE_URL + '/js/math-print.js"></script>'); 8452 } 8453 8454 pv.closeDocument(); 8455 8456 if (!pv.mathEnabled && print) 8457 { 8458 PrintDialog.printPreview(pv); 8459 } 8460 } 8461 8462 // Restores dark mode 8463 if (darkStylesheet != null) 8464 { 8465 graph.stylesheet = darkStylesheet; 8466 graph.refresh(); 8467 } 8468 }; 8469 8470 var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() 8471 { 8472 editorUi.hideDialog(); 8473 }); 8474 cancelBtn.className = 'geBtn'; 8475 8476 if (editorUi.editor.cancelFirst) 8477 { 8478 buttons.appendChild(cancelBtn); 8479 } 8480 8481 if (!editorUi.isOffline()) 8482 { 8483 var helpBtn = mxUtils.button(mxResources.get('help'), function() 8484 { 8485 graph.openLink('https://www.diagrams.net/doc/faq/print-diagram'); 8486 }); 8487 8488 helpBtn.className = 'geBtn'; 8489 buttons.appendChild(helpBtn); 8490 } 8491 8492 if (PrintDialog.previewEnabled) 8493 { 8494 var previewBtn = mxUtils.button(mxResources.get('preview'), function() 8495 { 8496 editorUi.hideDialog(); 8497 preview(false); 8498 }); 8499 previewBtn.className = 'geBtn'; 8500 buttons.appendChild(previewBtn); 8501 } 8502 8503 var printBtn = mxUtils.button(mxResources.get((!PrintDialog.previewEnabled) ? 'ok' : 'print'), function() 8504 { 8505 editorUi.hideDialog(); 8506 preview(true); 8507 }); 8508 printBtn.className = 'geBtn gePrimaryBtn'; 8509 buttons.appendChild(printBtn); 8510 8511 if (!editorUi.editor.cancelFirst) 8512 { 8513 buttons.appendChild(cancelBtn); 8514 } 8515 8516 div.appendChild(buttons); 8517 8518 this.container = div; 8519 }; 8520 8521 // Execute fit page on page setup changes 8522 var changePageSetupExecute = ChangePageSetup.prototype.execute; 8523 8524 ChangePageSetup.prototype.execute = function() 8525 { 8526 if (this.page == null) 8527 { 8528 this.page = this.ui.currentPage; 8529 } 8530 8531 // Workaround for redo existing change with different current page 8532 if (this.page != this.ui.currentPage) 8533 { 8534 if (this.page.viewState != null) 8535 { 8536 if (!this.ignoreColor) 8537 { 8538 this.page.viewState.background = this.color; 8539 } 8540 8541 if (!this.ignoreImage) 8542 { 8543 var img = this.image; 8544 8545 if (img != null && img.src != null && Graph.isPageLink(img.src)) 8546 { 8547 img = {originalSrc: img.src}; 8548 } 8549 8550 this.page.viewState.backgroundImage = img; 8551 } 8552 8553 if (this.format != null) 8554 { 8555 this.page.viewState.pageFormat = this.format; 8556 } 8557 8558 if (this.mathEnabled != null) 8559 { 8560 this.page.viewState.mathEnabled = this.mathEnabled; 8561 } 8562 8563 if (this.shadowVisible != null) 8564 { 8565 this.page.viewState.shadowVisible = this.shadowVisible; 8566 } 8567 } 8568 } 8569 else 8570 { 8571 changePageSetupExecute.apply(this, arguments); 8572 8573 if (this.mathEnabled != null && this.mathEnabled != this.ui.isMathEnabled()) 8574 { 8575 this.ui.setMathEnabled(this.mathEnabled); 8576 this.mathEnabled = !this.mathEnabled; 8577 } 8578 8579 if (this.shadowVisible != null && this.shadowVisible != this.ui.editor.graph.shadowVisible) 8580 { 8581 this.ui.editor.graph.setShadowVisible(this.shadowVisible); 8582 this.shadowVisible = !this.shadowVisible; 8583 } 8584 } 8585 }; 8586 8587 /** 8588 * Capability check for canvas export 8589 */ 8590 Editor.prototype.useCanvasForExport = false; 8591 8592 try 8593 { 8594 var canvas = document.createElement('canvas'); 8595 var img = new Image(); 8596 8597 // LATER: Capability check should not be async 8598 img.onload = function() 8599 { 8600 try 8601 { 8602 var ctx = canvas.getContext('2d'); 8603 ctx.drawImage(img, 0, 0); 8604 8605 // Works in Chrome, Firefox, Edge, Safari and Opera 8606 var result = canvas.toDataURL('image/png'); 8607 Editor.prototype.useCanvasForExport = result != null && result.length > 6; 8608 } 8609 catch (e) 8610 { 8611 // ignore 8612 } 8613 }; 8614 8615 // Checks if SVG with foreignObject can be exported 8616 var svg = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="1px" height="1px" version="1.1"><foreignObject pointer-events="all" width="1" height="1"><div xmlns="http://www.w3.org/1999/xhtml"></div></foreignObject></svg>'; 8617 img.src = 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svg))); 8618 } 8619 catch (e) 8620 { 8621 // ignore 8622 } 8623 8624})(); 8625 8626// Extends codec for ChangePageSetup 8627(function() 8628{ 8629 var codec = new mxObjectCodec(new ChangePageSetup(), ['ui', 'previousColor', 'previousImage', 'previousFormat']); 8630 8631 codec.beforeDecode = function(dec, node, obj) 8632 { 8633 obj.ui = dec.ui; 8634 8635 return node; 8636 }; 8637 8638 codec.afterDecode = function(dec, node, obj) 8639 { 8640 obj.previousColor = obj.color; 8641 obj.previousImage = obj.image; 8642 obj.previousFormat = obj.format; 8643 8644 if (obj.foldingEnabled != null) 8645 { 8646 obj.foldingEnabled = !obj.foldingEnabled; 8647 } 8648 8649 if (obj.mathEnabled != null) 8650 { 8651 obj.mathEnabled = !obj.mathEnabled; 8652 } 8653 8654 if (obj.shadowVisible != null) 8655 { 8656 obj.shadowVisible = !obj.shadowVisible; 8657 } 8658 8659 return obj; 8660 }; 8661 8662 mxCodecRegistry.register(codec); 8663})(); 8664 8665// Extends codec for ChangeGridColor 8666(function() 8667{ 8668 var codec = new mxObjectCodec(new ChangeGridColor(), ['ui']); 8669 8670 codec.beforeDecode = function(dec, node, obj) 8671 { 8672 obj.ui = dec.ui; 8673 8674 return node; 8675 }; 8676 8677 mxCodecRegistry.register(codec); 8678})(); 8679 8680