diff --git a/frontend/config/webpack.config.dev.js b/frontend/config/webpack.config.dev.js index d08f4f21db..0c426d09b8 100644 --- a/frontend/config/webpack.config.dev.js +++ b/frontend/config/webpack.config.dev.js @@ -89,11 +89,6 @@ module.exports = { require.resolve('react-dev-utils/webpackHotDevClient'), paths.appSrc + "/draft.js", ], - draw: [ - require.resolve('./polyfills'), - require.resolve('react-dev-utils/webpackHotDevClient'), - paths.appSrc + "/draw/draw.js", - ], sharedDirView: [ require.resolve('./polyfills'), require.resolve('react-dev-utils/webpackHotDevClient'), diff --git a/frontend/config/webpack.config.prod.js b/frontend/config/webpack.config.prod.js index ef854a2877..eb220a7562 100644 --- a/frontend/config/webpack.config.prod.js +++ b/frontend/config/webpack.config.prod.js @@ -66,7 +66,6 @@ module.exports = { fileHistoryOld: [require.resolve('./polyfills'), paths.appSrc + "/file-history-old.js"], app: [require.resolve('./polyfills'), paths.appSrc + "/app.js"], draft: [require.resolve('./polyfills'), paths.appSrc + "/draft.js"], - draw: [require.resolve('./polyfills'), paths.appSrc + "/draw/draw.js"], sharedDirView: [require.resolve('./polyfills'), paths.appSrc + "/shared-dir-view.js"], sharedFileViewMarkdown: [require.resolve('./polyfills'), paths.appSrc + "/shared-file-view-markdown.js"], sharedFileViewText: [require.resolve('./polyfills'), paths.appSrc + "/shared-file-view-text.js"], diff --git a/frontend/src/draw/draw-viewer.js b/frontend/src/draw/draw-viewer.js deleted file mode 100644 index 6893b03efd..0000000000 --- a/frontend/src/draw/draw-viewer.js +++ /dev/null @@ -1,152 +0,0 @@ -import { seafileAPI } from '../utils/seafile-api'; -import { Utils } from '../utils/utils'; -import toaster from '../components/toast'; - -var mxRectangle = window.mxRectangle; -var mxGraph = window.mxGraph; -var mxCodec = window.mxCodec; -var mxUtils = window.mxUtils; - -class DrawViewer { - - constructor(graph) { - this.graph = graph; - graph.setEnabled(false); - } - - loadFile() { - seafileAPI.getFileContent(window.app.config.rawPath).then((res) => { - var doc = mxUtils.parseXml(res.data); - /* eslint-disable */ - console.log(doc.documentElement); - /* eslint-enable */ - this.setGraphXml(doc.documentElement); - }).catch(error => { - let errMessage = Utils.getErrorMsg(error); - toaster.danger(errMessage); - }); - } - - readGraphState(node) { - this.graph.gridEnabled = false; - this.graph.gridSize = parseFloat(node.getAttribute('gridSize')) || window.mxGraph.prototype.gridSize; - this.graph.graphHandler.guidesEnabled = node.getAttribute('guides') != '0'; - this.graph.setTooltips(node.getAttribute('tooltips') != '0'); - this.graph.setConnectable(node.getAttribute('connect') != '0'); - this.graph.connectionArrowsEnabled = node.getAttribute('arrows') != '0'; - this.graph.foldingEnabled = node.getAttribute('fold') != '0'; - - if (this.graph.foldingEnabled) - { - this.graph.foldingEnabled = false; - this.graph.cellRenderer.forceControlClickHandler = this.graph.foldingEnabled; - } - - var ps = node.getAttribute('pageScale'); - - if (ps != null) - { - this.graph.pageScale = ps; - } - else - { - this.graph.pageScale = window.mxGraph.prototype.pageScale; - } - - - this.graph.pageVisible = false; - this.graph.pageBreaksVisible = this.graph.pageVisible; - this.graph.preferPageSize = this.graph.pageBreaksVisible; - - var pw = node.getAttribute('pageWidth'); - var ph = node.getAttribute('pageHeight'); - - if (pw != null && ph != null) - { - this.graph.pageFormat = new mxRectangle(0, 0, parseFloat(pw), parseFloat(ph)); - } - - // Loads the persistent state settings - var bg = node.getAttribute('background'); - - if (bg != null && bg.length > 0) - { - this.graph.background = bg; - } - else - { - this.graph.background = this.graph.defaultGraphBackground; - } - } - - /** - * Sets the XML node for the current diagram. - */ - setGraphXml(node) { - if (node != null) - { - var dec = new mxCodec(node.ownerDocument); - - if (node.nodeName == 'mxGraphModel') - { - this.graph.model.beginUpdate(); - - try - { - this.graph.model.clear(); - this.graph.view.scale = 1; - this.readGraphState(node); - this.updateGraphComponents(); - dec.decode(node, this.graph.getModel()); - } - finally - { - this.graph.model.endUpdate(); - } - } - } - else - { - this.resetGraph(); - this.graph.model.clear(); - } - } - - /** - * Keeps the graph container in sync with the persistent graph state - */ - updateGraphComponents() { - var graph = this.graph; - - if (graph.container != null) - { - graph.view.validateBackground(); - graph.container.style.overflow = (graph.scrollbars) ? 'auto' : 'hidden'; - } - } - - /** - * Sets the XML node for the current diagram. - */ - resetGraph() { - this.graph.gridEnabled = false; - this.graph.graphHandler.guidesEnabled = true; - this.graph.setTooltips(true); - this.graph.setConnectable(true); - this.graph.foldingEnabled = true; - this.graph.scrollbars = this.graph.defaultScrollbars; - this.graph.pageVisible = this.graph.defaultPageVisible; - this.graph.pageBreaksVisible = this.graph.pageVisible; - this.graph.preferPageSize = this.graph.pageBreaksVisible; - this.graph.background = this.graph.defaultGraphBackground; - this.graph.pageScale = mxGraph.prototype.pageScale; - this.graph.pageFormat = mxGraph.prototype.pageFormat; - this.graph.currentScale = 1; - this.graph.currentTranslate.x = 0; - this.graph.currentTranslate.y = 0; - this.updateGraphComponents(); - this.graph.view.setScale(1); - } -} - -export default DrawViewer; diff --git a/frontend/src/draw/draw.js b/frontend/src/draw/draw.js deleted file mode 100644 index 454fa2214c..0000000000 --- a/frontend/src/draw/draw.js +++ /dev/null @@ -1,28 +0,0 @@ -import { seafileAPI } from '../utils/seafile-api'; -import DrawViewer from './draw-viewer'; -import { Utils } from '../utils/utils'; -import toaster from '../components/toast'; - -function loadFile(editorUi) { - return seafileAPI.getFileContent(window.app.config.rawPath).then((res) => { - var doc = window.mxUtils.parseXml(res.data); - //console.log(doc.documentElement); - editorUi.editor.setGraphXml(doc.documentElement); - editorUi.editor.setModified(false); - editorUi.editor.undoManager.clear(); - }).catch(error => { - let errMessage = Utils.getErrorMsg(error); - toaster.danger(errMessage); - }); -} - -function saveFile(content) { - return seafileAPI.getUpdateLink(window.app.config.repoID, window.app.config.parentDir).then((res) => { - return seafileAPI.updateFile(res.data, window.app.config.path, window.app.config.filename, content); - }); -} - - -window.loadFile = loadFile; -window.saveFile = saveFile; -window.DrawViewer = DrawViewer; diff --git a/media/grapheditor/common.css b/media/grapheditor/common.css deleted file mode 100644 index 37bfb0eac0..0000000000 --- a/media/grapheditor/common.css +++ /dev/null @@ -1,162 +0,0 @@ -div.mxRubberband { - position: absolute; - overflow: hidden; - border-style: solid; - border-width: 1px; - border-color: #0000FF; - background: #0077FF; -} -.mxCellEditor { - background: url(data:image/gif;base64,R0lGODlhMAAwAIAAAP///wAAACH5BAEAAAAALAAAAAAwADAAAAIxhI+py+0Po5y02ouz3rz7D4biSJbmiabqyrbuC8fyTNf2jef6zvf+DwwKh8Si8egpAAA7); - _background: url('../images/transparent.gif'); - border-color: transparent; - border-style: solid; - display: inline-block; - position: absolute; - overflow: visible; - word-wrap: normal; - border-width: 0; - min-width: 1px; - resize: none; - padding: 0px; - margin: 0px; -} -.mxPlainTextEditor * { - padding: 0px; - margin: 0px; -} -div.mxWindow { - -webkit-box-shadow: 3px 3px 12px #C0C0C0; - -moz-box-shadow: 3px 3px 12px #C0C0C0; - box-shadow: 3px 3px 12px #C0C0C0; - background: url('../images/window.gif'); - border:1px solid #c3c3c3; - position: absolute; - overflow: hidden; - z-index: 1; -} -table.mxWindow { - border-collapse: collapse; - table-layout: fixed; - font-family: Arial; - font-size: 8pt; -} -td.mxWindowTitle { - background: url('../images/window-title.gif') repeat-x; - text-overflow: ellipsis; - white-space: nowrap; - text-align: center; - font-weight: bold; - overflow: hidden; - height: 13px; - padding: 2px; - padding-top: 4px; - padding-bottom: 6px; - color: black; -} -td.mxWindowPane { - vertical-align: top; - padding: 0px; -} -div.mxWindowPane { - overflow: hidden; - position: relative; -} -td.mxWindowPane td { - font-family: Arial; - font-size: 8pt; -} -td.mxWindowPane input, td.mxWindowPane select, td.mxWindowPane textarea, td.mxWindowPane radio { - border-color: #8C8C8C; - border-style: solid; - border-width: 1px; - font-family: Arial; - font-size: 8pt; - padding: 1px; -} -td.mxWindowPane button { - background: url('../images/button.gif') repeat-x; - font-family: Arial; - font-size: 8pt; - padding: 2px; - float: left; -} -img.mxToolbarItem { - margin-right: 6px; - margin-bottom: 6px; - border-width: 1px; -} -select.mxToolbarCombo { - vertical-align: top; - border-style: inset; - border-width: 2px; -} -div.mxToolbarComboContainer { - padding: 2px; -} -img.mxToolbarMode { - margin: 2px; - margin-right: 4px; - margin-bottom: 4px; - border-width: 0px; -} -img.mxToolbarModeSelected { - margin: 0px; - margin-right: 2px; - margin-bottom: 2px; - border-width: 2px; - border-style: inset; -} -div.mxTooltip { - -webkit-box-shadow: 3px 3px 12px #C0C0C0; - -moz-box-shadow: 3px 3px 12px #C0C0C0; - box-shadow: 3px 3px 12px #C0C0C0; - background: #FFFFCC; - border-style: solid; - border-width: 1px; - border-color: black; - font-family: Arial; - font-size: 8pt; - position: absolute; - cursor: default; - padding: 4px; - color: black; -} -div.mxPopupMenu { - -webkit-box-shadow: 3px 3px 12px #C0C0C0; - -moz-box-shadow: 3px 3px 12px #C0C0C0; - box-shadow: 3px 3px 12px #C0C0C0; - background: url('../images/window.gif'); - position: absolute; - border-style: solid; - border-width: 1px; - border-color: black; -} -table.mxPopupMenu { - border-collapse: collapse; - margin-top: 1px; - margin-bottom: 1px; -} -tr.mxPopupMenuItem { - color: black; - cursor: pointer; -} -tr.mxPopupMenuItemHover { - background-color: #000066; - color: #FFFFFF; - cursor: pointer; -} -td.mxPopupMenuItem { - padding: 2px 30px 2px 10px; - white-space: nowrap; - font-family: Arial; - font-size: 8pt; -} -td.mxPopupMenuIcon { - background-color: #D0D0D0; - padding: 2px 4px 2px 4px; -} -.mxDisabled { - opacity: 0.2 !important; - cursor:default !important; -} diff --git a/media/grapheditor/deflate/base64.js b/media/grapheditor/deflate/base64.js deleted file mode 100644 index 1e870ac40d..0000000000 --- a/media/grapheditor/deflate/base64.js +++ /dev/null @@ -1,151 +0,0 @@ - -/** -* -* Base64 encode / decode -* http://www.webtoolkit.info/ -* -**/ - -var Base64 = { - - // private property - _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", - - // public method for encoding - encode : function (input, binary) { - binary = (binary != null) ? binary : false; - var output = ""; - var chr1, chr2, chr3, enc1, enc2, enc3, enc4; - var i = 0; - - if (!binary) - { - input = Base64._utf8_encode(input); - } - - while (i < input.length) { - - chr1 = input.charCodeAt(i++); - chr2 = input.charCodeAt(i++); - chr3 = input.charCodeAt(i++); - - enc1 = chr1 >> 2; - enc2 = ((chr1 & 3) << 4) | (chr2 >> 4); - enc3 = ((chr2 & 15) << 2) | (chr3 >> 6); - enc4 = chr3 & 63; - - if (isNaN(chr2)) { - enc3 = enc4 = 64; - } else if (isNaN(chr3)) { - enc4 = 64; - } - - output = output + - this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + - this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4); - - } - - return output; - }, - - // public method for decoding - decode : function (input, binary) { - binary = (binary != null) ? binary : false; - var output = ""; - var chr1, chr2, chr3; - var enc1, enc2, enc3, enc4; - var i = 0; - - input = input.replace(/[^A-Za-z0-9\+\/\=]/g, ""); - - while (i < input.length) { - - enc1 = this._keyStr.indexOf(input.charAt(i++)); - enc2 = this._keyStr.indexOf(input.charAt(i++)); - enc3 = this._keyStr.indexOf(input.charAt(i++)); - enc4 = this._keyStr.indexOf(input.charAt(i++)); - - chr1 = (enc1 << 2) | (enc2 >> 4); - chr2 = ((enc2 & 15) << 4) | (enc3 >> 2); - chr3 = ((enc3 & 3) << 6) | enc4; - - output = output + String.fromCharCode(chr1); - - if (enc3 != 64) { - output = output + String.fromCharCode(chr2); - } - if (enc4 != 64) { - output = output + String.fromCharCode(chr3); - } - - } - - if (!binary) - { - output = Base64._utf8_decode(output); - } - - return output; - - }, - - // private method for UTF-8 encoding - _utf8_encode : function (string) { - string = string.replace(/\r\n/g,"\n"); - var utftext = ""; - - for (var n = 0; n < string.length; n++) { - - var c = string.charCodeAt(n); - - if (c < 128) { - utftext += String.fromCharCode(c); - } - else if((c > 127) && (c < 2048)) { - utftext += String.fromCharCode((c >> 6) | 192); - utftext += String.fromCharCode((c & 63) | 128); - } - else { - utftext += String.fromCharCode((c >> 12) | 224); - utftext += String.fromCharCode(((c >> 6) & 63) | 128); - utftext += String.fromCharCode((c & 63) | 128); - } - - } - - return utftext; - }, - - // private method for UTF-8 decoding - _utf8_decode : function (utftext) { - var string = ""; - var i = 0; - var c = c1 = c2 = 0; - - while ( i < utftext.length ) { - - c = utftext.charCodeAt(i); - - if (c < 128) { - string += String.fromCharCode(c); - i++; - } - else if((c > 191) && (c < 224)) { - c2 = utftext.charCodeAt(i+1); - string += String.fromCharCode(((c & 31) << 6) | (c2 & 63)); - i += 2; - } - else { - c2 = utftext.charCodeAt(i+1); - c3 = utftext.charCodeAt(i+2); - string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)); - i += 3; - } - - } - - return string; - } - -} diff --git a/media/grapheditor/deflate/pako.min.js b/media/grapheditor/deflate/pako.min.js deleted file mode 100644 index 92405bd2e7..0000000000 --- a/media/grapheditor/deflate/pako.min.js +++ /dev/null @@ -1,3 +0,0 @@ -/* pako 1.0.3 nodeca/pako */ -!function(t){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=t();else if("function"==typeof define&&define.amd)define([],t);else{var e;e="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,e.pako=t()}}(function(){return function t(e,a,i){function n(s,o){if(!a[s]){if(!e[s]){var l="function"==typeof require&&require;if(!o&&l)return l(s,!0);if(r)return r(s,!0);var h=new Error("Cannot find module '"+s+"'");throw h.code="MODULE_NOT_FOUND",h}var d=a[s]={exports:{}};e[s][0].call(d.exports,function(t){var a=e[s][1][t];return n(a?a:t)},d,d.exports,t,e,a,i)}return a[s].exports}for(var r="function"==typeof require&&require,s=0;s0?e.windowBits=-e.windowBits:e.gzip&&e.windowBits>0&&e.windowBits<16&&(e.windowBits+=16),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new f,this.strm.avail_out=0;var a=o.deflateInit2(this.strm,e.level,e.method,e.windowBits,e.memLevel,e.strategy);if(a!==b)throw new Error(d[a]);if(e.header&&o.deflateSetHeader(this.strm,e.header),e.dictionary){var n;if(n="string"==typeof e.dictionary?h.string2buf(e.dictionary):"[object ArrayBuffer]"===_.call(e.dictionary)?new Uint8Array(e.dictionary):e.dictionary,a=o.deflateSetDictionary(this.strm,n),a!==b)throw new Error(d[a]);this._dict_set=!0}}function n(t,e){var a=new i(e);if(a.push(t,!0),a.err)throw a.msg;return a.result}function r(t,e){return e=e||{},e.raw=!0,n(t,e)}function s(t,e){return e=e||{},e.gzip=!0,n(t,e)}var o=t("./zlib/deflate"),l=t("./utils/common"),h=t("./utils/strings"),d=t("./zlib/messages"),f=t("./zlib/zstream"),_=Object.prototype.toString,u=0,c=4,b=0,g=1,m=2,w=-1,p=0,v=8;i.prototype.push=function(t,e){var a,i,n=this.strm,r=this.options.chunkSize;if(this.ended)return!1;i=e===~~e?e:e===!0?c:u,"string"==typeof t?n.input=h.string2buf(t):"[object ArrayBuffer]"===_.call(t)?n.input=new Uint8Array(t):n.input=t,n.next_in=0,n.avail_in=n.input.length;do{if(0===n.avail_out&&(n.output=new l.Buf8(r),n.next_out=0,n.avail_out=r),a=o.deflate(n,i),a!==g&&a!==b)return this.onEnd(a),this.ended=!0,!1;0!==n.avail_out&&(0!==n.avail_in||i!==c&&i!==m)||("string"===this.options.to?this.onData(h.buf2binstring(l.shrinkBuf(n.output,n.next_out))):this.onData(l.shrinkBuf(n.output,n.next_out)))}while((n.avail_in>0||0===n.avail_out)&&a!==g);return i===c?(a=o.deflateEnd(this.strm),this.onEnd(a),this.ended=!0,a===b):i!==m||(this.onEnd(b),n.avail_out=0,!0)},i.prototype.onData=function(t){this.chunks.push(t)},i.prototype.onEnd=function(t){t===b&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=l.flattenChunks(this.chunks)),this.chunks=[],this.err=t,this.msg=this.strm.msg},a.Deflate=i,a.deflate=n,a.deflateRaw=r,a.gzip=s},{"./utils/common":3,"./utils/strings":4,"./zlib/deflate":8,"./zlib/messages":13,"./zlib/zstream":15}],2:[function(t,e,a){"use strict";function i(t){if(!(this instanceof i))return new i(t);this.options=o.assign({chunkSize:16384,windowBits:0,to:""},t||{});var e=this.options;e.raw&&e.windowBits>=0&&e.windowBits<16&&(e.windowBits=-e.windowBits,0===e.windowBits&&(e.windowBits=-15)),!(e.windowBits>=0&&e.windowBits<16)||t&&t.windowBits||(e.windowBits+=32),e.windowBits>15&&e.windowBits<48&&0===(15&e.windowBits)&&(e.windowBits|=15),this.err=0,this.msg="",this.ended=!1,this.chunks=[],this.strm=new f,this.strm.avail_out=0;var a=s.inflateInit2(this.strm,e.windowBits);if(a!==h.Z_OK)throw new Error(d[a]);this.header=new _,s.inflateGetHeader(this.strm,this.header)}function n(t,e){var a=new i(e);if(a.push(t,!0),a.err)throw a.msg;return a.result}function r(t,e){return e=e||{},e.raw=!0,n(t,e)}var s=t("./zlib/inflate"),o=t("./utils/common"),l=t("./utils/strings"),h=t("./zlib/constants"),d=t("./zlib/messages"),f=t("./zlib/zstream"),_=t("./zlib/gzheader"),u=Object.prototype.toString;i.prototype.push=function(t,e){var a,i,n,r,d,f,_=this.strm,c=this.options.chunkSize,b=this.options.dictionary,g=!1;if(this.ended)return!1;i=e===~~e?e:e===!0?h.Z_FINISH:h.Z_NO_FLUSH,"string"==typeof t?_.input=l.binstring2buf(t):"[object ArrayBuffer]"===u.call(t)?_.input=new Uint8Array(t):_.input=t,_.next_in=0,_.avail_in=_.input.length;do{if(0===_.avail_out&&(_.output=new o.Buf8(c),_.next_out=0,_.avail_out=c),a=s.inflate(_,h.Z_NO_FLUSH),a===h.Z_NEED_DICT&&b&&(f="string"==typeof b?l.string2buf(b):"[object ArrayBuffer]"===u.call(b)?new Uint8Array(b):b,a=s.inflateSetDictionary(this.strm,f)),a===h.Z_BUF_ERROR&&g===!0&&(a=h.Z_OK,g=!1),a!==h.Z_STREAM_END&&a!==h.Z_OK)return this.onEnd(a),this.ended=!0,!1;_.next_out&&(0!==_.avail_out&&a!==h.Z_STREAM_END&&(0!==_.avail_in||i!==h.Z_FINISH&&i!==h.Z_SYNC_FLUSH)||("string"===this.options.to?(n=l.utf8border(_.output,_.next_out),r=_.next_out-n,d=l.buf2string(_.output,n),_.next_out=r,_.avail_out=c-r,r&&o.arraySet(_.output,_.output,n,r,0),this.onData(d)):this.onData(o.shrinkBuf(_.output,_.next_out)))),0===_.avail_in&&0===_.avail_out&&(g=!0)}while((_.avail_in>0||0===_.avail_out)&&a!==h.Z_STREAM_END);return a===h.Z_STREAM_END&&(i=h.Z_FINISH),i===h.Z_FINISH?(a=s.inflateEnd(this.strm),this.onEnd(a),this.ended=!0,a===h.Z_OK):i!==h.Z_SYNC_FLUSH||(this.onEnd(h.Z_OK),_.avail_out=0,!0)},i.prototype.onData=function(t){this.chunks.push(t)},i.prototype.onEnd=function(t){t===h.Z_OK&&("string"===this.options.to?this.result=this.chunks.join(""):this.result=o.flattenChunks(this.chunks)),this.chunks=[],this.err=t,this.msg=this.strm.msg},a.Inflate=i,a.inflate=n,a.inflateRaw=r,a.ungzip=n},{"./utils/common":3,"./utils/strings":4,"./zlib/constants":6,"./zlib/gzheader":9,"./zlib/inflate":11,"./zlib/messages":13,"./zlib/zstream":15}],3:[function(t,e,a){"use strict";var i="undefined"!=typeof Uint8Array&&"undefined"!=typeof Uint16Array&&"undefined"!=typeof Int32Array;a.assign=function(t){for(var e=Array.prototype.slice.call(arguments,1);e.length;){var a=e.shift();if(a){if("object"!=typeof a)throw new TypeError(a+"must be non-object");for(var i in a)a.hasOwnProperty(i)&&(t[i]=a[i])}}return t},a.shrinkBuf=function(t,e){return t.length===e?t:t.subarray?t.subarray(0,e):(t.length=e,t)};var n={arraySet:function(t,e,a,i,n){if(e.subarray&&t.subarray)return void t.set(e.subarray(a,a+i),n);for(var r=0;r=252?6:l>=248?5:l>=240?4:l>=224?3:l>=192?2:1;o[254]=o[254]=1,a.string2buf=function(t){var e,a,i,r,s,o=t.length,l=0;for(r=0;r>>6,e[s++]=128|63&a):a<65536?(e[s++]=224|a>>>12,e[s++]=128|a>>>6&63,e[s++]=128|63&a):(e[s++]=240|a>>>18,e[s++]=128|a>>>12&63,e[s++]=128|a>>>6&63,e[s++]=128|63&a);return e},a.buf2binstring=function(t){return i(t,t.length)},a.binstring2buf=function(t){for(var e=new n.Buf8(t.length),a=0,i=e.length;a4)h[n++]=65533,a+=s-1;else{for(r&=2===s?31:3===s?15:7;s>1&&a1?h[n++]=65533:r<65536?h[n++]=r:(r-=65536,h[n++]=55296|r>>10&1023,h[n++]=56320|1023&r)}return i(h,n)},a.utf8border=function(t,e){var a;for(e=e||t.length,e>t.length&&(e=t.length),a=e-1;a>=0&&128===(192&t[a]);)a--;return a<0?e:0===a?e:a+o[t[a]]>e?a:e}},{"./common":3}],5:[function(t,e,a){"use strict";function i(t,e,a,i){for(var n=65535&t|0,r=t>>>16&65535|0,s=0;0!==a;){s=a>2e3?2e3:a,a-=s;do n=n+e[i++]|0,r=r+n|0;while(--s);n%=65521,r%=65521}return n|r<<16|0}e.exports=i},{}],6:[function(t,e,a){"use strict";e.exports={Z_NO_FLUSH:0,Z_PARTIAL_FLUSH:1,Z_SYNC_FLUSH:2,Z_FULL_FLUSH:3,Z_FINISH:4,Z_BLOCK:5,Z_TREES:6,Z_OK:0,Z_STREAM_END:1,Z_NEED_DICT:2,Z_ERRNO:-1,Z_STREAM_ERROR:-2,Z_DATA_ERROR:-3,Z_BUF_ERROR:-5,Z_NO_COMPRESSION:0,Z_BEST_SPEED:1,Z_BEST_COMPRESSION:9,Z_DEFAULT_COMPRESSION:-1,Z_FILTERED:1,Z_HUFFMAN_ONLY:2,Z_RLE:3,Z_FIXED:4,Z_DEFAULT_STRATEGY:0,Z_BINARY:0,Z_TEXT:1,Z_UNKNOWN:2,Z_DEFLATED:8}},{}],7:[function(t,e,a){"use strict";function i(){for(var t,e=[],a=0;a<256;a++){t=a;for(var i=0;i<8;i++)t=1&t?3988292384^t>>>1:t>>>1;e[a]=t}return e}function n(t,e,a,i){var n=r,s=i+a;t^=-1;for(var o=i;o>>8^n[255&(t^e[o])];return t^-1}var r=i();e.exports=n},{}],8:[function(t,e,a){"use strict";function i(t,e){return t.msg=D[e],e}function n(t){return(t<<1)-(t>4?9:0)}function r(t){for(var e=t.length;--e>=0;)t[e]=0}function s(t){var e=t.state,a=e.pending;a>t.avail_out&&(a=t.avail_out),0!==a&&(R.arraySet(t.output,e.pending_buf,e.pending_out,a,t.next_out),t.next_out+=a,e.pending_out+=a,t.total_out+=a,t.avail_out-=a,e.pending-=a,0===e.pending&&(e.pending_out=0))}function o(t,e){C._tr_flush_block(t,t.block_start>=0?t.block_start:-1,t.strstart-t.block_start,e),t.block_start=t.strstart,s(t.strm)}function l(t,e){t.pending_buf[t.pending++]=e}function h(t,e){t.pending_buf[t.pending++]=e>>>8&255,t.pending_buf[t.pending++]=255&e}function d(t,e,a,i){var n=t.avail_in;return n>i&&(n=i),0===n?0:(t.avail_in-=n,R.arraySet(e,t.input,t.next_in,n,a),1===t.state.wrap?t.adler=N(t.adler,e,n,a):2===t.state.wrap&&(t.adler=O(t.adler,e,n,a)),t.next_in+=n,t.total_in+=n,n)}function f(t,e){var a,i,n=t.max_chain_length,r=t.strstart,s=t.prev_length,o=t.nice_match,l=t.strstart>t.w_size-ft?t.strstart-(t.w_size-ft):0,h=t.window,d=t.w_mask,f=t.prev,_=t.strstart+dt,u=h[r+s-1],c=h[r+s];t.prev_length>=t.good_match&&(n>>=2),o>t.lookahead&&(o=t.lookahead);do if(a=e,h[a+s]===c&&h[a+s-1]===u&&h[a]===h[r]&&h[++a]===h[r+1]){r+=2,a++;do;while(h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&h[++r]===h[++a]&&r<_);if(i=dt-(_-r),r=_-dt,i>s){if(t.match_start=e,s=i,i>=o)break;u=h[r+s-1],c=h[r+s]}}while((e=f[e&d])>l&&0!==--n);return s<=t.lookahead?s:t.lookahead}function _(t){var e,a,i,n,r,s=t.w_size;do{if(n=t.window_size-t.lookahead-t.strstart,t.strstart>=s+(s-ft)){R.arraySet(t.window,t.window,s,s,0),t.match_start-=s,t.strstart-=s,t.block_start-=s,a=t.hash_size,e=a;do i=t.head[--e],t.head[e]=i>=s?i-s:0;while(--a);a=s,e=a;do i=t.prev[--e],t.prev[e]=i>=s?i-s:0;while(--a);n+=s}if(0===t.strm.avail_in)break;if(a=d(t.strm,t.window,t.strstart+t.lookahead,n),t.lookahead+=a,t.lookahead+t.insert>=ht)for(r=t.strstart-t.insert,t.ins_h=t.window[r],t.ins_h=(t.ins_h<t.pending_buf_size-5&&(a=t.pending_buf_size-5);;){if(t.lookahead<=1){if(_(t),0===t.lookahead&&e===I)return vt;if(0===t.lookahead)break}t.strstart+=t.lookahead,t.lookahead=0;var i=t.block_start+a;if((0===t.strstart||t.strstart>=i)&&(t.lookahead=t.strstart-i,t.strstart=i,o(t,!1),0===t.strm.avail_out))return vt;if(t.strstart-t.block_start>=t.w_size-ft&&(o(t,!1),0===t.strm.avail_out))return vt}return t.insert=0,e===F?(o(t,!0),0===t.strm.avail_out?yt:xt):t.strstart>t.block_start&&(o(t,!1),0===t.strm.avail_out)?vt:vt}function c(t,e){for(var a,i;;){if(t.lookahead=ht&&(t.ins_h=(t.ins_h<=ht)if(i=C._tr_tally(t,t.strstart-t.match_start,t.match_length-ht),t.lookahead-=t.match_length,t.match_length<=t.max_lazy_match&&t.lookahead>=ht){t.match_length--;do t.strstart++,t.ins_h=(t.ins_h<=ht&&(t.ins_h=(t.ins_h<4096)&&(t.match_length=ht-1)),t.prev_length>=ht&&t.match_length<=t.prev_length){n=t.strstart+t.lookahead-ht,i=C._tr_tally(t,t.strstart-1-t.prev_match,t.prev_length-ht),t.lookahead-=t.prev_length-1,t.prev_length-=2;do++t.strstart<=n&&(t.ins_h=(t.ins_h<=ht&&t.strstart>0&&(n=t.strstart-1,i=s[n],i===s[++n]&&i===s[++n]&&i===s[++n])){r=t.strstart+dt;do;while(i===s[++n]&&i===s[++n]&&i===s[++n]&&i===s[++n]&&i===s[++n]&&i===s[++n]&&i===s[++n]&&i===s[++n]&&nt.lookahead&&(t.match_length=t.lookahead)}if(t.match_length>=ht?(a=C._tr_tally(t,1,t.match_length-ht),t.lookahead-=t.match_length,t.strstart+=t.match_length,t.match_length=0):(a=C._tr_tally(t,0,t.window[t.strstart]),t.lookahead--,t.strstart++),a&&(o(t,!1),0===t.strm.avail_out))return vt}return t.insert=0,e===F?(o(t,!0),0===t.strm.avail_out?yt:xt):t.last_lit&&(o(t,!1),0===t.strm.avail_out)?vt:kt}function m(t,e){for(var a;;){if(0===t.lookahead&&(_(t),0===t.lookahead)){if(e===I)return vt;break}if(t.match_length=0,a=C._tr_tally(t,0,t.window[t.strstart]),t.lookahead--,t.strstart++,a&&(o(t,!1),0===t.strm.avail_out))return vt}return t.insert=0,e===F?(o(t,!0),0===t.strm.avail_out?yt:xt):t.last_lit&&(o(t,!1),0===t.strm.avail_out)?vt:kt}function w(t,e,a,i,n){this.good_length=t,this.max_lazy=e,this.nice_length=a,this.max_chain=i,this.func=n}function p(t){t.window_size=2*t.w_size,r(t.head),t.max_lazy_match=Z[t.level].max_lazy,t.good_match=Z[t.level].good_length,t.nice_match=Z[t.level].nice_length,t.max_chain_length=Z[t.level].max_chain,t.strstart=0,t.block_start=0,t.lookahead=0,t.insert=0,t.match_length=t.prev_length=ht-1,t.match_available=0,t.ins_h=0}function v(){this.strm=null,this.status=0,this.pending_buf=null,this.pending_buf_size=0,this.pending_out=0,this.pending=0,this.wrap=0,this.gzhead=null,this.gzindex=0,this.method=V,this.last_flush=-1,this.w_size=0,this.w_bits=0,this.w_mask=0,this.window=null,this.window_size=0,this.prev=null,this.head=null,this.ins_h=0,this.hash_size=0,this.hash_bits=0,this.hash_mask=0,this.hash_shift=0,this.block_start=0,this.match_length=0,this.prev_match=0,this.match_available=0,this.strstart=0,this.match_start=0,this.lookahead=0,this.prev_length=0,this.max_chain_length=0,this.max_lazy_match=0,this.level=0,this.strategy=0,this.good_match=0,this.nice_match=0,this.dyn_ltree=new R.Buf16(2*ot),this.dyn_dtree=new R.Buf16(2*(2*rt+1)),this.bl_tree=new R.Buf16(2*(2*st+1)),r(this.dyn_ltree),r(this.dyn_dtree),r(this.bl_tree),this.l_desc=null,this.d_desc=null,this.bl_desc=null,this.bl_count=new R.Buf16(lt+1),this.heap=new R.Buf16(2*nt+1),r(this.heap),this.heap_len=0,this.heap_max=0,this.depth=new R.Buf16(2*nt+1),r(this.depth),this.l_buf=0,this.lit_bufsize=0,this.last_lit=0,this.d_buf=0,this.opt_len=0,this.static_len=0,this.matches=0,this.insert=0,this.bi_buf=0,this.bi_valid=0}function k(t){var e;return t&&t.state?(t.total_in=t.total_out=0,t.data_type=Q,e=t.state,e.pending=0,e.pending_out=0,e.wrap<0&&(e.wrap=-e.wrap),e.status=e.wrap?ut:wt,t.adler=2===e.wrap?0:1,e.last_flush=I,C._tr_init(e),H):i(t,K)}function y(t){var e=k(t);return e===H&&p(t.state),e}function x(t,e){return t&&t.state?2!==t.state.wrap?K:(t.state.gzhead=e,H):K}function z(t,e,a,n,r,s){if(!t)return K;var o=1;if(e===Y&&(e=6),n<0?(o=0,n=-n):n>15&&(o=2,n-=16),r<1||r>$||a!==V||n<8||n>15||e<0||e>9||s<0||s>W)return i(t,K);8===n&&(n=9);var l=new v;return t.state=l,l.strm=t,l.wrap=o,l.gzhead=null,l.w_bits=n,l.w_size=1<L||e<0)return t?i(t,K):K;if(o=t.state,!t.output||!t.input&&0!==t.avail_in||o.status===pt&&e!==F)return i(t,0===t.avail_out?P:K);if(o.strm=t,a=o.last_flush,o.last_flush=e,o.status===ut)if(2===o.wrap)t.adler=0,l(o,31),l(o,139),l(o,8),o.gzhead?(l(o,(o.gzhead.text?1:0)+(o.gzhead.hcrc?2:0)+(o.gzhead.extra?4:0)+(o.gzhead.name?8:0)+(o.gzhead.comment?16:0)),l(o,255&o.gzhead.time),l(o,o.gzhead.time>>8&255),l(o,o.gzhead.time>>16&255),l(o,o.gzhead.time>>24&255),l(o,9===o.level?2:o.strategy>=G||o.level<2?4:0),l(o,255&o.gzhead.os),o.gzhead.extra&&o.gzhead.extra.length&&(l(o,255&o.gzhead.extra.length),l(o,o.gzhead.extra.length>>8&255)),o.gzhead.hcrc&&(t.adler=O(t.adler,o.pending_buf,o.pending,0)),o.gzindex=0,o.status=ct):(l(o,0),l(o,0),l(o,0),l(o,0),l(o,0),l(o,9===o.level?2:o.strategy>=G||o.level<2?4:0),l(o,zt),o.status=wt);else{var _=V+(o.w_bits-8<<4)<<8,u=-1;u=o.strategy>=G||o.level<2?0:o.level<6?1:6===o.level?2:3,_|=u<<6,0!==o.strstart&&(_|=_t),_+=31-_%31,o.status=wt,h(o,_),0!==o.strstart&&(h(o,t.adler>>>16),h(o,65535&t.adler)),t.adler=1}if(o.status===ct)if(o.gzhead.extra){for(d=o.pending;o.gzindex<(65535&o.gzhead.extra.length)&&(o.pending!==o.pending_buf_size||(o.gzhead.hcrc&&o.pending>d&&(t.adler=O(t.adler,o.pending_buf,o.pending-d,d)),s(t),d=o.pending,o.pending!==o.pending_buf_size));)l(o,255&o.gzhead.extra[o.gzindex]),o.gzindex++;o.gzhead.hcrc&&o.pending>d&&(t.adler=O(t.adler,o.pending_buf,o.pending-d,d)),o.gzindex===o.gzhead.extra.length&&(o.gzindex=0,o.status=bt)}else o.status=bt;if(o.status===bt)if(o.gzhead.name){d=o.pending;do{if(o.pending===o.pending_buf_size&&(o.gzhead.hcrc&&o.pending>d&&(t.adler=O(t.adler,o.pending_buf,o.pending-d,d)),s(t),d=o.pending,o.pending===o.pending_buf_size)){f=1;break}f=o.gzindexd&&(t.adler=O(t.adler,o.pending_buf,o.pending-d,d)),0===f&&(o.gzindex=0,o.status=gt)}else o.status=gt;if(o.status===gt)if(o.gzhead.comment){d=o.pending;do{if(o.pending===o.pending_buf_size&&(o.gzhead.hcrc&&o.pending>d&&(t.adler=O(t.adler,o.pending_buf,o.pending-d,d)),s(t),d=o.pending,o.pending===o.pending_buf_size)){f=1;break}f=o.gzindexd&&(t.adler=O(t.adler,o.pending_buf,o.pending-d,d)),0===f&&(o.status=mt)}else o.status=mt;if(o.status===mt&&(o.gzhead.hcrc?(o.pending+2>o.pending_buf_size&&s(t),o.pending+2<=o.pending_buf_size&&(l(o,255&t.adler),l(o,t.adler>>8&255),t.adler=0,o.status=wt)):o.status=wt),0!==o.pending){if(s(t),0===t.avail_out)return o.last_flush=-1,H}else if(0===t.avail_in&&n(e)<=n(a)&&e!==F)return i(t,P);if(o.status===pt&&0!==t.avail_in)return i(t,P);if(0!==t.avail_in||0!==o.lookahead||e!==I&&o.status!==pt){var c=o.strategy===G?m(o,e):o.strategy===X?g(o,e):Z[o.level].func(o,e);if(c!==yt&&c!==xt||(o.status=pt),c===vt||c===yt)return 0===t.avail_out&&(o.last_flush=-1),H;if(c===kt&&(e===U?C._tr_align(o):e!==L&&(C._tr_stored_block(o,0,0,!1),e===T&&(r(o.head),0===o.lookahead&&(o.strstart=0,o.block_start=0,o.insert=0))),s(t),0===t.avail_out))return o.last_flush=-1,H}return e!==F?H:o.wrap<=0?j:(2===o.wrap?(l(o,255&t.adler),l(o,t.adler>>8&255),l(o,t.adler>>16&255),l(o,t.adler>>24&255),l(o,255&t.total_in),l(o,t.total_in>>8&255),l(o,t.total_in>>16&255),l(o,t.total_in>>24&255)):(h(o,t.adler>>>16),h(o,65535&t.adler)),s(t),o.wrap>0&&(o.wrap=-o.wrap),0!==o.pending?H:j)}function E(t){var e;return t&&t.state?(e=t.state.status,e!==ut&&e!==ct&&e!==bt&&e!==gt&&e!==mt&&e!==wt&&e!==pt?i(t,K):(t.state=null,e===wt?i(t,M):H)):K}function A(t,e){var a,i,n,s,o,l,h,d,f=e.length;if(!t||!t.state)return K;if(a=t.state,s=a.wrap,2===s||1===s&&a.status!==ut||a.lookahead)return K;for(1===s&&(t.adler=N(t.adler,e,f,0)),a.wrap=0,f>=a.w_size&&(0===s&&(r(a.head),a.strstart=0,a.block_start=0,a.insert=0),d=new R.Buf8(a.w_size),R.arraySet(d,e,f-a.w_size,a.w_size,0),e=d,f=a.w_size),o=t.avail_in,l=t.next_in,h=t.input,t.avail_in=f,t.next_in=0,t.input=e,_(a);a.lookahead>=ht;){i=a.strstart,n=a.lookahead-(ht-1);do a.ins_h=(a.ins_h<>>24,b>>>=y,g-=y,y=k>>>16&255,0===y)A[o++]=65535&k;else{if(!(16&y)){if(0===(64&y)){k=m[(65535&k)+(b&(1<>>=y,g-=y),g<15&&(b+=E[r++]<>>24,b>>>=y,g-=y,y=k>>>16&255,!(16&y)){if(0===(64&y)){k=w[(65535&k)+(b&(1<d){t.msg="invalid distance too far back",a.mode=i;break t}if(b>>>=y,g-=y,y=o-l,z>y){if(y=z-y,y>_&&a.sane){t.msg="invalid distance too far back",a.mode=i;break t}if(B=0,S=c,0===u){if(B+=f-y,y2;)A[o++]=S[B++],A[o++]=S[B++],A[o++]=S[B++],x-=3;x&&(A[o++]=S[B++],x>1&&(A[o++]=S[B++]))}else{B=o-z;do A[o++]=A[B++],A[o++]=A[B++],A[o++]=A[B++],x-=3;while(x>2);x&&(A[o++]=A[B++],x>1&&(A[o++]=A[B++]))}break}}break}}while(r>3,r-=x,g-=x<<3,b&=(1<>>24&255)+(t>>>8&65280)+((65280&t)<<8)+((255&t)<<24)}function n(){this.mode=0,this.last=!1,this.wrap=0,this.havedict=!1,this.flags=0,this.dmax=0,this.check=0,this.total=0,this.head=null,this.wbits=0,this.wsize=0,this.whave=0,this.wnext=0,this.window=null,this.hold=0,this.bits=0,this.length=0,this.offset=0,this.extra=0,this.lencode=null,this.distcode=null,this.lenbits=0,this.distbits=0,this.ncode=0,this.nlen=0,this.ndist=0,this.have=0,this.next=null,this.lens=new w.Buf16(320),this.work=new w.Buf16(288),this.lendyn=null,this.distdyn=null,this.sane=0,this.back=0,this.was=0}function r(t){var e;return t&&t.state?(e=t.state,t.total_in=t.total_out=e.total=0,t.msg="",e.wrap&&(t.adler=1&e.wrap),e.mode=T,e.last=0,e.havedict=0,e.dmax=32768,e.head=null,e.hold=0,e.bits=0,e.lencode=e.lendyn=new w.Buf32(bt),e.distcode=e.distdyn=new w.Buf32(gt),e.sane=1,e.back=-1,Z):N}function s(t){var e;return t&&t.state?(e=t.state,e.wsize=0,e.whave=0,e.wnext=0,r(t)):N}function o(t,e){var a,i;return t&&t.state?(i=t.state,e<0?(a=0,e=-e):(a=(e>>4)+1,e<48&&(e&=15)),e&&(e<8||e>15)?N:(null!==i.window&&i.wbits!==e&&(i.window=null),i.wrap=a,i.wbits=e,s(t))):N}function l(t,e){var a,i;return t?(i=new n,t.state=i,i.window=null,a=o(t,e),a!==Z&&(t.state=null),a):N}function h(t){return l(t,wt)}function d(t){if(pt){var e;for(g=new w.Buf32(512),m=new w.Buf32(32),e=0;e<144;)t.lens[e++]=8;for(;e<256;)t.lens[e++]=9;for(;e<280;)t.lens[e++]=7;for(;e<288;)t.lens[e++]=8;for(y(z,t.lens,0,288,g,0,t.work,{bits:9}),e=0;e<32;)t.lens[e++]=5;y(B,t.lens,0,32,m,0,t.work,{bits:5}),pt=!1}t.lencode=g,t.lenbits=9,t.distcode=m,t.distbits=5}function f(t,e,a,i){var n,r=t.state;return null===r.window&&(r.wsize=1<=r.wsize?(w.arraySet(r.window,e,a-r.wsize,r.wsize,0),r.wnext=0,r.whave=r.wsize):(n=r.wsize-r.wnext,n>i&&(n=i),w.arraySet(r.window,e,a-i,n,r.wnext),i-=n,i?(w.arraySet(r.window,e,a-i,i,0),r.wnext=i,r.whave=r.wsize):(r.wnext+=n,r.wnext===r.wsize&&(r.wnext=0),r.whave>>8&255,a.check=v(a.check,Et,2,0),_=0,u=0,a.mode=F;break}if(a.flags=0,a.head&&(a.head.done=!1),!(1&a.wrap)||(((255&_)<<8)+(_>>8))%31){t.msg="incorrect header check",a.mode=_t;break}if((15&_)!==U){t.msg="unknown compression method",a.mode=_t;break}if(_>>>=4,u-=4,yt=(15&_)+8,0===a.wbits)a.wbits=yt;else if(yt>a.wbits){t.msg="invalid window size",a.mode=_t;break}a.dmax=1<>8&1),512&a.flags&&(Et[0]=255&_,Et[1]=_>>>8&255,a.check=v(a.check,Et,2,0)),_=0,u=0,a.mode=L;case L:for(;u<32;){if(0===l)break t;l--,_+=n[s++]<>>8&255,Et[2]=_>>>16&255,Et[3]=_>>>24&255,a.check=v(a.check,Et,4,0)),_=0,u=0,a.mode=H;case H:for(;u<16;){if(0===l)break t;l--,_+=n[s++]<>8),512&a.flags&&(Et[0]=255&_,Et[1]=_>>>8&255,a.check=v(a.check,Et,2,0)),_=0,u=0,a.mode=j;case j:if(1024&a.flags){for(;u<16;){if(0===l)break t;l--,_+=n[s++]<>>8&255,a.check=v(a.check,Et,2,0)),_=0,u=0}else a.head&&(a.head.extra=null);a.mode=K;case K:if(1024&a.flags&&(g=a.length,g>l&&(g=l),g&&(a.head&&(yt=a.head.extra_len-a.length,a.head.extra||(a.head.extra=new Array(a.head.extra_len)),w.arraySet(a.head.extra,n,s,g,yt)),512&a.flags&&(a.check=v(a.check,n,g,s)),l-=g,s+=g,a.length-=g),a.length))break t;a.length=0,a.mode=M;case M:if(2048&a.flags){if(0===l)break t;g=0;do yt=n[s+g++],a.head&&yt&&a.length<65536&&(a.head.name+=String.fromCharCode(yt));while(yt&&g>9&1,a.head.done=!0),t.adler=a.check=0,a.mode=X;break;case q:for(;u<32;){if(0===l)break t;l--,_+=n[s++]<>>=7&u,u-=7&u,a.mode=ht;break}for(;u<3;){if(0===l)break t;l--,_+=n[s++]<>>=1,u-=1,3&_){case 0:a.mode=J;break;case 1:if(d(a),a.mode=at,e===A){_>>>=2,u-=2;break t}break;case 2:a.mode=$;break;case 3:t.msg="invalid block type",a.mode=_t}_>>>=2,u-=2;break;case J:for(_>>>=7&u,u-=7&u;u<32;){if(0===l)break t;l--,_+=n[s++]<>>16^65535)){t.msg="invalid stored block lengths",a.mode=_t;break}if(a.length=65535&_,_=0,u=0,a.mode=Q,e===A)break t;case Q:a.mode=V;case V:if(g=a.length){if(g>l&&(g=l),g>h&&(g=h),0===g)break t;w.arraySet(r,n,s,g,o),l-=g,s+=g,h-=g,o+=g,a.length-=g;break}a.mode=X;break;case $:for(;u<14;){if(0===l)break t; -l--,_+=n[s++]<>>=5,u-=5,a.ndist=(31&_)+1,_>>>=5,u-=5,a.ncode=(15&_)+4,_>>>=4,u-=4,a.nlen>286||a.ndist>30){t.msg="too many length or distance symbols",a.mode=_t;break}a.have=0,a.mode=tt;case tt:for(;a.have>>=3,u-=3}for(;a.have<19;)a.lens[At[a.have++]]=0;if(a.lencode=a.lendyn,a.lenbits=7,zt={bits:a.lenbits},xt=y(x,a.lens,0,19,a.lencode,0,a.work,zt),a.lenbits=zt.bits,xt){t.msg="invalid code lengths set",a.mode=_t;break}a.have=0,a.mode=et;case et:for(;a.have>>24,mt=St>>>16&255,wt=65535&St,!(gt<=u);){if(0===l)break t;l--,_+=n[s++]<>>=gt,u-=gt,a.lens[a.have++]=wt;else{if(16===wt){for(Bt=gt+2;u>>=gt,u-=gt,0===a.have){t.msg="invalid bit length repeat",a.mode=_t;break}yt=a.lens[a.have-1],g=3+(3&_),_>>>=2,u-=2}else if(17===wt){for(Bt=gt+3;u>>=gt,u-=gt,yt=0,g=3+(7&_),_>>>=3,u-=3}else{for(Bt=gt+7;u>>=gt,u-=gt,yt=0,g=11+(127&_),_>>>=7,u-=7}if(a.have+g>a.nlen+a.ndist){t.msg="invalid bit length repeat",a.mode=_t;break}for(;g--;)a.lens[a.have++]=yt}}if(a.mode===_t)break;if(0===a.lens[256]){t.msg="invalid code -- missing end-of-block",a.mode=_t;break}if(a.lenbits=9,zt={bits:a.lenbits},xt=y(z,a.lens,0,a.nlen,a.lencode,0,a.work,zt),a.lenbits=zt.bits,xt){t.msg="invalid literal/lengths set",a.mode=_t;break}if(a.distbits=6,a.distcode=a.distdyn,zt={bits:a.distbits},xt=y(B,a.lens,a.nlen,a.ndist,a.distcode,0,a.work,zt),a.distbits=zt.bits,xt){t.msg="invalid distances set",a.mode=_t;break}if(a.mode=at,e===A)break t;case at:a.mode=it;case it:if(l>=6&&h>=258){t.next_out=o,t.avail_out=h,t.next_in=s,t.avail_in=l,a.hold=_,a.bits=u,k(t,b),o=t.next_out,r=t.output,h=t.avail_out,s=t.next_in,n=t.input,l=t.avail_in,_=a.hold,u=a.bits,a.mode===X&&(a.back=-1);break}for(a.back=0;St=a.lencode[_&(1<>>24,mt=St>>>16&255,wt=65535&St,!(gt<=u);){if(0===l)break t;l--,_+=n[s++]<>pt)],gt=St>>>24,mt=St>>>16&255,wt=65535&St,!(pt+gt<=u);){if(0===l)break t;l--,_+=n[s++]<>>=pt,u-=pt,a.back+=pt}if(_>>>=gt,u-=gt,a.back+=gt,a.length=wt,0===mt){a.mode=lt;break}if(32&mt){a.back=-1,a.mode=X;break}if(64&mt){t.msg="invalid literal/length code",a.mode=_t;break}a.extra=15&mt,a.mode=nt;case nt:if(a.extra){for(Bt=a.extra;u>>=a.extra,u-=a.extra,a.back+=a.extra}a.was=a.length,a.mode=rt;case rt:for(;St=a.distcode[_&(1<>>24,mt=St>>>16&255,wt=65535&St,!(gt<=u);){if(0===l)break t;l--,_+=n[s++]<>pt)],gt=St>>>24,mt=St>>>16&255,wt=65535&St,!(pt+gt<=u);){if(0===l)break t;l--,_+=n[s++]<>>=pt,u-=pt,a.back+=pt}if(_>>>=gt,u-=gt,a.back+=gt,64&mt){t.msg="invalid distance code",a.mode=_t;break}a.offset=wt,a.extra=15&mt,a.mode=st;case st:if(a.extra){for(Bt=a.extra;u>>=a.extra,u-=a.extra,a.back+=a.extra}if(a.offset>a.dmax){t.msg="invalid distance too far back",a.mode=_t;break}a.mode=ot;case ot:if(0===h)break t;if(g=b-h,a.offset>g){if(g=a.offset-g,g>a.whave&&a.sane){t.msg="invalid distance too far back",a.mode=_t;break}g>a.wnext?(g-=a.wnext,m=a.wsize-g):m=a.wnext-g,g>a.length&&(g=a.length),bt=a.window}else bt=r,m=o-a.offset,g=a.length;g>h&&(g=h),h-=g,a.length-=g;do r[o++]=bt[m++];while(--g);0===a.length&&(a.mode=it);break;case lt:if(0===h)break t;r[o++]=a.length,h--,a.mode=it;break;case ht:if(a.wrap){for(;u<32;){if(0===l)break t;l--,_|=n[s++]<=1&&0===j[N];N--);if(O>N&&(O=N),0===N)return b[g++]=20971520,b[g++]=20971520,w.bits=1,0;for(C=1;C0&&(t===o||1!==N))return-1;for(K[1]=0,Z=1;Zr||t===h&&T>s)return 1;for(var Y=0;;){Y++,B=Z-I,m[R]z?(S=M[P+m[R]],E=L[H+m[R]]):(S=96,E=0),p=1<>I)+v]=B<<24|S<<16|E|0;while(0!==v);for(p=1<>=1;if(0!==p?(F&=p-1,F+=p):F=0,R++,0===--j[Z]){if(Z===N)break;Z=e[a+m[R]]}if(Z>O&&(F&y)!==k){for(0===I&&(I=O),x+=C,D=Z-I,U=1<r||t===h&&T>s)return 1;k=F&y,b[k]=O<<24|D<<16|x-g|0}}return 0!==F&&(b[x+F]=Z-I<<24|64<<16|0),w.bits=O,0}},{"../utils/common":3}],13:[function(t,e,a){"use strict";e.exports={2:"need dictionary",1:"stream end",0:"","-1":"file error","-2":"stream error","-3":"data error","-4":"insufficient memory","-5":"buffer error","-6":"incompatible version"}},{}],14:[function(t,e,a){"use strict";function i(t){for(var e=t.length;--e>=0;)t[e]=0}function n(t,e,a,i,n){this.static_tree=t,this.extra_bits=e,this.extra_base=a,this.elems=i,this.max_length=n,this.has_stree=t&&t.length}function r(t,e){this.dyn_tree=t,this.max_code=0,this.stat_desc=e}function s(t){return t<256?lt[t]:lt[256+(t>>>7)]}function o(t,e){t.pending_buf[t.pending++]=255&e,t.pending_buf[t.pending++]=e>>>8&255}function l(t,e,a){t.bi_valid>W-a?(t.bi_buf|=e<>W-t.bi_valid,t.bi_valid+=a-W):(t.bi_buf|=e<>>=1,a<<=1;while(--e>0);return a>>>1}function f(t){16===t.bi_valid?(o(t,t.bi_buf),t.bi_buf=0,t.bi_valid=0):t.bi_valid>=8&&(t.pending_buf[t.pending++]=255&t.bi_buf,t.bi_buf>>=8,t.bi_valid-=8)}function _(t,e){var a,i,n,r,s,o,l=e.dyn_tree,h=e.max_code,d=e.stat_desc.static_tree,f=e.stat_desc.has_stree,_=e.stat_desc.extra_bits,u=e.stat_desc.extra_base,c=e.stat_desc.max_length,b=0;for(r=0;r<=X;r++)t.bl_count[r]=0;for(l[2*t.heap[t.heap_max]+1]=0,a=t.heap_max+1;ac&&(r=c,b++),l[2*i+1]=r,i>h||(t.bl_count[r]++,s=0,i>=u&&(s=_[i-u]),o=l[2*i],t.opt_len+=o*(r+s),f&&(t.static_len+=o*(d[2*i+1]+s)));if(0!==b){do{for(r=c-1;0===t.bl_count[r];)r--;t.bl_count[r]--,t.bl_count[r+1]+=2,t.bl_count[c]--,b-=2}while(b>0);for(r=c;0!==r;r--)for(i=t.bl_count[r];0!==i;)n=t.heap[--a],n>h||(l[2*n+1]!==r&&(t.opt_len+=(r-l[2*n+1])*l[2*n],l[2*n+1]=r),i--)}}function u(t,e,a){var i,n,r=new Array(X+1),s=0;for(i=1;i<=X;i++)r[i]=s=s+a[i-1]<<1;for(n=0;n<=e;n++){var o=t[2*n+1];0!==o&&(t[2*n]=d(r[o]++,o))}}function c(){var t,e,a,i,r,s=new Array(X+1);for(a=0,i=0;i>=7;i8?o(t,t.bi_buf):t.bi_valid>0&&(t.pending_buf[t.pending++]=t.bi_buf),t.bi_buf=0,t.bi_valid=0}function m(t,e,a,i){g(t),i&&(o(t,a),o(t,~a)),N.arraySet(t.pending_buf,t.window,e,a,t.pending),t.pending+=a}function w(t,e,a,i){var n=2*e,r=2*a;return t[n]>1;a>=1;a--)p(t,r,a);n=l;do a=t.heap[1],t.heap[1]=t.heap[t.heap_len--],p(t,r,1),i=t.heap[1],t.heap[--t.heap_max]=a,t.heap[--t.heap_max]=i,r[2*n]=r[2*a]+r[2*i],t.depth[n]=(t.depth[a]>=t.depth[i]?t.depth[a]:t.depth[i])+1,r[2*a+1]=r[2*i+1]=n,t.heap[1]=n++,p(t,r,1);while(t.heap_len>=2);t.heap[--t.heap_max]=t.heap[1],_(t,e),u(r,h,t.bl_count)}function y(t,e,a){var i,n,r=-1,s=e[1],o=0,l=7,h=4;for(0===s&&(l=138,h=3),e[2*(a+1)+1]=65535,i=0;i<=a;i++)n=s,s=e[2*(i+1)+1],++o=3&&0===t.bl_tree[2*nt[e]+1];e--);return t.opt_len+=3*(e+1)+5+5+4,e}function B(t,e,a,i){var n;for(l(t,e-257,5),l(t,a-1,5),l(t,i-4,4),n=0;n>>=1)if(1&a&&0!==t.dyn_ltree[2*e])return D;if(0!==t.dyn_ltree[18]||0!==t.dyn_ltree[20]||0!==t.dyn_ltree[26])return I;for(e=32;e0?(t.strm.data_type===U&&(t.strm.data_type=S(t)),k(t,t.l_desc),k(t,t.d_desc),s=z(t),n=t.opt_len+3+7>>>3,r=t.static_len+3+7>>>3,r<=n&&(n=r)):n=r=a+5,a+4<=n&&e!==-1?A(t,e,a,i):t.strategy===O||r===n?(l(t,(F<<1)+(i?1:0),3),v(t,st,ot)):(l(t,(L<<1)+(i?1:0),3),B(t,t.l_desc.max_code+1,t.d_desc.max_code+1,s+1),v(t,t.dyn_ltree,t.dyn_dtree)),b(t),i&&g(t)}function C(t,e,a){return t.pending_buf[t.d_buf+2*t.last_lit]=e>>>8&255,t.pending_buf[t.d_buf+2*t.last_lit+1]=255&e,t.pending_buf[t.l_buf+t.last_lit]=255&a,t.last_lit++,0===e?t.dyn_ltree[2*a]++:(t.matches++,e--,t.dyn_ltree[2*(ht[a]+M+1)]++,t.dyn_dtree[2*s(e)]++),t.last_lit===t.lit_bufsize-1}var N=t("../utils/common"),O=4,D=0,I=1,U=2,T=0,F=1,L=2,H=3,j=258,K=29,M=256,P=M+1+K,Y=30,q=19,G=2*P+1,X=15,W=16,J=7,Q=256,V=16,$=17,tt=18,et=[0,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,0],at=[0,0,0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13],it=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,3,7],nt=[16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15],rt=512,st=new Array(2*(P+2));i(st);var ot=new Array(2*Y);i(ot);var lt=new Array(rt);i(lt);var ht=new Array(j-H+1);i(ht);var dt=new Array(K);i(dt);var ft=new Array(Y);i(ft);var _t,ut,ct,bt=!1;a._tr_init=E,a._tr_stored_block=A,a._tr_flush_block=R,a._tr_tally=C,a._tr_align=Z},{"../utils/common":3}],15:[function(t,e,a){"use strict";function i(){this.input=null,this.next_in=0,this.avail_in=0,this.total_in=0,this.output=null,this.next_out=0,this.avail_out=0,this.total_out=0,this.msg="",this.state=null,this.data_type=2,this.adler=0}e.exports=i},{}],"/":[function(t,e,a){"use strict";var i=t("./lib/utils/common").assign,n=t("./lib/deflate"),r=t("./lib/inflate"),s=t("./lib/zlib/constants"),o={};i(o,n,r,s),e.exports=o},{"./lib/deflate":1,"./lib/inflate":2,"./lib/utils/common":3,"./lib/zlib/constants":6}]},{},[])("/")}); diff --git a/media/grapheditor/images/checkmark.gif b/media/grapheditor/images/checkmark.gif deleted file mode 100644 index d79444daa9..0000000000 Binary files a/media/grapheditor/images/checkmark.gif and /dev/null differ diff --git a/media/grapheditor/images/clear.gif b/media/grapheditor/images/clear.gif deleted file mode 100644 index c6acf0a434..0000000000 Binary files a/media/grapheditor/images/clear.gif and /dev/null differ diff --git a/media/grapheditor/images/close.png b/media/grapheditor/images/close.png deleted file mode 100644 index d319efb6e1..0000000000 Binary files a/media/grapheditor/images/close.png and /dev/null differ diff --git a/media/grapheditor/images/collapsed.gif b/media/grapheditor/images/collapsed.gif deleted file mode 100644 index ce977743d5..0000000000 Binary files a/media/grapheditor/images/collapsed.gif and /dev/null differ diff --git a/media/grapheditor/images/dropdown.gif b/media/grapheditor/images/dropdown.gif deleted file mode 100644 index 9c94ea5af2..0000000000 Binary files a/media/grapheditor/images/dropdown.gif and /dev/null differ diff --git a/media/grapheditor/images/dropdown.png b/media/grapheditor/images/dropdown.png deleted file mode 100644 index 0aeac2db68..0000000000 Binary files a/media/grapheditor/images/dropdown.png and /dev/null differ diff --git a/media/grapheditor/images/edit.gif b/media/grapheditor/images/edit.gif deleted file mode 100644 index 3c07607352..0000000000 Binary files a/media/grapheditor/images/edit.gif and /dev/null differ diff --git a/media/grapheditor/images/expanded.gif b/media/grapheditor/images/expanded.gif deleted file mode 100644 index 461297fc80..0000000000 Binary files a/media/grapheditor/images/expanded.gif and /dev/null differ diff --git a/media/grapheditor/images/grid.gif b/media/grapheditor/images/grid.gif deleted file mode 100644 index f4e7063e87..0000000000 Binary files a/media/grapheditor/images/grid.gif and /dev/null differ diff --git a/media/grapheditor/images/handle-fixed.png b/media/grapheditor/images/handle-fixed.png deleted file mode 100644 index b4b600b1a5..0000000000 Binary files a/media/grapheditor/images/handle-fixed.png and /dev/null differ diff --git a/media/grapheditor/images/handle-main.png b/media/grapheditor/images/handle-main.png deleted file mode 100644 index ee067ff811..0000000000 Binary files a/media/grapheditor/images/handle-main.png and /dev/null differ diff --git a/media/grapheditor/images/handle-rotate.png b/media/grapheditor/images/handle-rotate.png deleted file mode 100644 index f3dd46e2d1..0000000000 Binary files a/media/grapheditor/images/handle-rotate.png and /dev/null differ diff --git a/media/grapheditor/images/handle-secondary.png b/media/grapheditor/images/handle-secondary.png deleted file mode 100644 index b4a30900a9..0000000000 Binary files a/media/grapheditor/images/handle-secondary.png and /dev/null differ diff --git a/media/grapheditor/images/handle-terminal.png b/media/grapheditor/images/handle-terminal.png deleted file mode 100644 index ec03b316c1..0000000000 Binary files a/media/grapheditor/images/handle-terminal.png and /dev/null differ diff --git a/media/grapheditor/images/help.png b/media/grapheditor/images/help.png deleted file mode 100644 index 17ee2eb85e..0000000000 Binary files a/media/grapheditor/images/help.png and /dev/null differ diff --git a/media/grapheditor/images/locked.png b/media/grapheditor/images/locked.png deleted file mode 100644 index 8dbac82415..0000000000 Binary files a/media/grapheditor/images/locked.png and /dev/null differ diff --git a/media/grapheditor/images/logo.png b/media/grapheditor/images/logo.png deleted file mode 100644 index 053a1eb2b1..0000000000 Binary files a/media/grapheditor/images/logo.png and /dev/null differ diff --git a/media/grapheditor/images/nocolor.png b/media/grapheditor/images/nocolor.png deleted file mode 100644 index aec4534d6a..0000000000 Binary files a/media/grapheditor/images/nocolor.png and /dev/null differ diff --git a/media/grapheditor/images/refresh.png b/media/grapheditor/images/refresh.png deleted file mode 100644 index b131204409..0000000000 Binary files a/media/grapheditor/images/refresh.png and /dev/null differ diff --git a/media/grapheditor/images/round-drop.png b/media/grapheditor/images/round-drop.png deleted file mode 100644 index b5e2fb603c..0000000000 Binary files a/media/grapheditor/images/round-drop.png and /dev/null differ diff --git a/media/grapheditor/images/search.png b/media/grapheditor/images/search.png deleted file mode 100644 index d763a72d3b..0000000000 Binary files a/media/grapheditor/images/search.png and /dev/null differ diff --git a/media/grapheditor/images/tooltip.png b/media/grapheditor/images/tooltip.png deleted file mode 100644 index ad20c04e87..0000000000 Binary files a/media/grapheditor/images/tooltip.png and /dev/null differ diff --git a/media/grapheditor/images/transparent.gif b/media/grapheditor/images/transparent.gif deleted file mode 100644 index 76040f2b09..0000000000 Binary files a/media/grapheditor/images/transparent.gif and /dev/null differ diff --git a/media/grapheditor/images/triangle-down.png b/media/grapheditor/images/triangle-down.png deleted file mode 100644 index d03b98fc5f..0000000000 Binary files a/media/grapheditor/images/triangle-down.png and /dev/null differ diff --git a/media/grapheditor/images/triangle-left.png b/media/grapheditor/images/triangle-left.png deleted file mode 100644 index 44b8425511..0000000000 Binary files a/media/grapheditor/images/triangle-left.png and /dev/null differ diff --git a/media/grapheditor/images/triangle-right.png b/media/grapheditor/images/triangle-right.png deleted file mode 100644 index 98656281fc..0000000000 Binary files a/media/grapheditor/images/triangle-right.png and /dev/null differ diff --git a/media/grapheditor/images/triangle-up.png b/media/grapheditor/images/triangle-up.png deleted file mode 100644 index 6d93676735..0000000000 Binary files a/media/grapheditor/images/triangle-up.png and /dev/null differ diff --git a/media/grapheditor/images/unlocked.png b/media/grapheditor/images/unlocked.png deleted file mode 100644 index 5ea44c1d35..0000000000 Binary files a/media/grapheditor/images/unlocked.png and /dev/null differ diff --git a/media/grapheditor/index.html b/media/grapheditor/index.html deleted file mode 100644 index 1b270ca296..0000000000 --- a/media/grapheditor/index.html +++ /dev/null @@ -1,110 +0,0 @@ - - - - - Grapheditor - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/media/grapheditor/js/Actions.js b/media/grapheditor/js/Actions.js deleted file mode 100644 index 0389311cd7..0000000000 --- a/media/grapheditor/js/Actions.js +++ /dev/null @@ -1,1412 +0,0 @@ -/** - * Copyright (c) 2006-2012, JGraph Ltd - */ -/** - * Constructs the actions object for the given UI. - */ -function Actions(editorUi) -{ - this.editorUi = editorUi; - this.actions = new Object(); - this.init(); -}; - -/** - * Adds the default actions. - */ -Actions.prototype.init = function() -{ - var ui = this.editorUi; - var editor = ui.editor; - var graph = editor.graph; - var isGraphEnabled = function() - { - return Action.prototype.isEnabled.apply(this, arguments) && graph.isEnabled(); - }; - - // File actions - this.addAction('new...', function() { graph.openLink(ui.getUrl()); }); - this.addAction('open...', function() - { - window.openNew = true; - window.openKey = 'open'; - - ui.openFile(); - }); - this.addAction('import...', function() - { - window.openNew = false; - window.openKey = 'import'; - - // Closes dialog after open - window.openFile = new OpenFile(mxUtils.bind(this, function() - { - ui.hideDialog(); - })); - - window.openFile.setConsumer(mxUtils.bind(this, function(xml, filename) - { - try - { - var doc = mxUtils.parseXml(xml); - editor.graph.setSelectionCells(editor.graph.importGraphModel(doc.documentElement)); - } - catch (e) - { - mxUtils.alert(mxResources.get('invalidOrMissingFile') + ': ' + e.message); - } - })); - - // Removes openFile if dialog is closed - ui.showDialog(new OpenDialog(this).container, 320, 220, true, true, function() - { - window.openFile = null; - }); - }).isEnabled = isGraphEnabled; - this.addAction('save', function() { ui.saveFile(false); }, null, null, Editor.ctrlKey + '+S').isEnabled = isGraphEnabled; - this.addAction('saveAs...', function() { ui.saveFile(true); }, null, null, Editor.ctrlKey + '+Shift+S').isEnabled = isGraphEnabled; - this.addAction('export...', function() { ui.showDialog(new ExportDialog(ui).container, 300, 230, true, true); }); - this.addAction('editDiagram...', function() - { - var dlg = new EditDiagramDialog(ui); - ui.showDialog(dlg.container, 620, 420, true, false); - dlg.init(); - }); - this.addAction('pageSetup...', function() { ui.showDialog(new PageSetupDialog(ui).container, 320, 220, true, true); }).isEnabled = isGraphEnabled; - this.addAction('print...', function() { ui.showDialog(new PrintDialog(ui).container, 300, 180, true, true); }, null, 'sprite-print', Editor.ctrlKey + '+P'); - this.addAction('preview', function() { mxUtils.show(graph, null, 10, 10); }); - - // Edit actions - this.addAction('undo', function() { ui.undo(); }, null, 'sprite-undo', Editor.ctrlKey + '+Z'); - this.addAction('redo', function() { ui.redo(); }, null, 'sprite-redo', (!mxClient.IS_WIN) ? Editor.ctrlKey + '+Shift+Z' : Editor.ctrlKey + '+Y'); - this.addAction('cut', function() { mxClipboard.cut(graph); }, null, 'sprite-cut', Editor.ctrlKey + '+X'); - this.addAction('copy', function() { mxClipboard.copy(graph); }, null, 'sprite-copy', Editor.ctrlKey + '+C'); - this.addAction('paste', function() - { - if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) - { - mxClipboard.paste(graph); - } - }, false, 'sprite-paste', Editor.ctrlKey + '+V'); - this.addAction('pasteHere', function(evt) - { - if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) - { - graph.getModel().beginUpdate(); - try - { - var cells = mxClipboard.paste(graph); - - if (cells != null) - { - var includeEdges = true; - - for (var i = 0; i < cells.length && includeEdges; i++) - { - includeEdges = includeEdges && graph.model.isEdge(cells[i]); - } - - var t = graph.view.translate; - var s = graph.view.scale; - var dx = t.x; - var dy = t.y; - var bb = null; - - if (cells.length == 1 && includeEdges) - { - var geo = graph.getCellGeometry(cells[0]); - - if (geo != null) - { - bb = geo.getTerminalPoint(true); - } - } - - bb = (bb != null) ? bb : graph.getBoundingBoxFromGeometry(cells, includeEdges); - - if (bb != null) - { - var x = Math.round(graph.snap(graph.popupMenuHandler.triggerX / s - dx)); - var y = Math.round(graph.snap(graph.popupMenuHandler.triggerY / s - dy)); - - graph.cellsMoved(cells, x - bb.x, y - bb.y); - } - } - } - finally - { - graph.getModel().endUpdate(); - } - } - }); - - this.addAction('copySize', function(evt) - { - var cell = graph.getSelectionCell(); - - if (graph.isEnabled() && cell != null && graph.getModel().isVertex(cell)) - { - var geo = graph.getCellGeometry(cell); - - if (geo != null) - { - ui.copiedSize = new mxRectangle(geo.x, geo.y, geo.width, geo.height); - } - } - }, null, null, 'Alt+Shit+X'); - - this.addAction('pasteSize', function(evt) - { - if (graph.isEnabled() && !graph.isSelectionEmpty() && ui.copiedSize != null) - { - graph.getModel().beginUpdate(); - - try - { - var cells = graph.getSelectionCells(); - - for (var i = 0; i < cells.length; i++) - { - if (graph.getModel().isVertex(cells[i])) - { - var geo = graph.getCellGeometry(cells[i]); - - if (geo != null) - { - geo = geo.clone(); - geo.width = ui.copiedSize.width; - geo.height = ui.copiedSize.height; - - graph.getModel().setGeometry(cells[i], geo); - } - } - } - } - finally - { - graph.getModel().endUpdate(); - } - } - }, null, null, 'Alt+Shit+V'); - - function deleteCells(includeEdges) - { - // Cancels interactive operations - graph.escape(); - var cells = graph.getDeletableCells(graph.getSelectionCells()); - - if (cells != null && cells.length > 0) - { - var parents = graph.model.getParents(cells); - graph.removeCells(cells, includeEdges); - - // Selects parents for easier editing of groups - if (parents != null) - { - var select = []; - - for (var i = 0; i < parents.length; i++) - { - if (graph.model.contains(parents[i]) && - (graph.model.isVertex(parents[i]) || - graph.model.isEdge(parents[i]))) - { - select.push(parents[i]); - } - } - - graph.setSelectionCells(select); - } - } - }; - - this.addAction('delete', function(evt) - { - deleteCells(evt != null && mxEvent.isShiftDown(evt)); - }, null, null, 'Delete'); - this.addAction('deleteAll', function() - { - deleteCells(true); - }, null, null, Editor.ctrlKey + '+Delete'); - this.addAction('duplicate', function() - { - graph.setSelectionCells(graph.duplicateCells()); - }, null, null, Editor.ctrlKey + '+D'); - this.put('turn', new Action(mxResources.get('turn') + ' / ' + mxResources.get('reverse'), function() - { - graph.turnShapes(graph.getSelectionCells()); - }, null, null, Editor.ctrlKey + '+R')); - this.addAction('selectVertices', function() { graph.selectVertices(); }, null, null, Editor.ctrlKey + '+Shift+I'); - this.addAction('selectEdges', function() { graph.selectEdges(); }, null, null, Editor.ctrlKey + '+Shift+E'); - this.addAction('selectAll', function() { graph.selectAll(null, true); }, null, null, Editor.ctrlKey + '+A'); - this.addAction('selectNone', function() { graph.clearSelection(); }, null, null, Editor.ctrlKey + '+Shift+A'); - this.addAction('lockUnlock', function() - { - if (!graph.isSelectionEmpty()) - { - graph.getModel().beginUpdate(); - try - { - var defaultValue = graph.isCellMovable(graph.getSelectionCell()) ? 1 : 0; - graph.toggleCellStyles(mxConstants.STYLE_MOVABLE, defaultValue); - graph.toggleCellStyles(mxConstants.STYLE_RESIZABLE, defaultValue); - graph.toggleCellStyles(mxConstants.STYLE_ROTATABLE, defaultValue); - graph.toggleCellStyles(mxConstants.STYLE_DELETABLE, defaultValue); - graph.toggleCellStyles(mxConstants.STYLE_EDITABLE, defaultValue); - graph.toggleCellStyles('connectable', defaultValue); - } - finally - { - graph.getModel().endUpdate(); - } - } - }, null, null, Editor.ctrlKey + '+L'); - - // Navigation actions - this.addAction('home', function() { graph.home(); }, null, null, 'Home'); - this.addAction('exitGroup', function() { graph.exitGroup(); }, null, null, Editor.ctrlKey + '+Shift+Home'); - this.addAction('enterGroup', function() { graph.enterGroup(); }, null, null, Editor.ctrlKey + '+Shift+End'); - this.addAction('collapse', function() { graph.foldCells(true); }, null, null, Editor.ctrlKey + '+Home'); - this.addAction('expand', function() { graph.foldCells(false); }, null, null, Editor.ctrlKey + '+End'); - - // Arrange actions - this.addAction('toFront', function() { graph.orderCells(false); }, null, null, Editor.ctrlKey + '+Shift+F'); - this.addAction('toBack', function() { graph.orderCells(true); }, null, null, Editor.ctrlKey + '+Shift+B'); - this.addAction('group', function() - { - if (graph.getSelectionCount() == 1) - { - graph.setCellStyles('container', '1'); - } - else - { - graph.setSelectionCell(graph.groupCells(null, 0)); - } - }, null, null, Editor.ctrlKey + '+G'); - this.addAction('ungroup', function() - { - if (graph.getSelectionCount() == 1 && graph.getModel().getChildCount(graph.getSelectionCell()) == 0) - { - graph.setCellStyles('container', '0'); - } - else - { - graph.setSelectionCells(graph.ungroupCells()); - } - }, null, null, Editor.ctrlKey + '+Shift+U'); - this.addAction('removeFromGroup', function() { graph.removeCellsFromParent(); }); - // Adds action - this.addAction('edit', function() - { - if (graph.isEnabled()) - { - graph.startEditingAtCell(); - } - }, null, null, 'F2/Enter'); - this.addAction('editData...', function() - { - var cell = graph.getSelectionCell() || graph.getModel().getRoot(); - ui.showDataDialog(cell); - }, null, null, Editor.ctrlKey + '+M'); - this.addAction('editTooltip...', function() - { - var graph = ui.editor.graph; - - if (graph.isEnabled() && !graph.isSelectionEmpty()) - { - var cell = graph.getSelectionCell(); - var tooltip = ''; - - if (mxUtils.isNode(cell.value)) - { - var tmp = cell.value.getAttribute('tooltip'); - - if (tmp != null) - { - tooltip = tmp; - } - } - - var dlg = new TextareaDialog(ui, mxResources.get('editTooltip') + ':', tooltip, function(newValue) - { - graph.setTooltipForCell(cell, newValue); - }); - ui.showDialog(dlg.container, 320, 200, true, true); - dlg.init(); - } - }, null, null, 'Alt+Shift+T'); - this.addAction('openLink', function() - { - var link = graph.getLinkForCell(graph.getSelectionCell()); - - if (link != null) - { - graph.openLink(link); - } - }); - this.addAction('editLink...', function() - { - var graph = ui.editor.graph; - - if (graph.isEnabled() && !graph.isSelectionEmpty()) - { - var cell = graph.getSelectionCell(); - var value = graph.getLinkForCell(cell) || ''; - - ui.showLinkDialog(value, mxResources.get('apply'), function(link) - { - link = mxUtils.trim(link); - graph.setLinkForCell(cell, (link.length > 0) ? link : null); - }); - } - }, null, null, 'Alt+Shift+L'); - this.addAction('insertLink...', function() - { - if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) - { - ui.showLinkDialog('', mxResources.get('insert'), function(link, docs) - { - link = mxUtils.trim(link); - - if (link.length > 0) - { - var icon = null; - var title = graph.getLinkTitle(link); - - if (docs != null && docs.length > 0) - { - icon = docs[0].iconUrl; - title = docs[0].name || docs[0].type; - title = title.charAt(0).toUpperCase() + title.substring(1); - - if (title.length > 30) - { - title = title.substring(0, 30) + '...'; - } - } - - var pt = graph.getFreeInsertPoint(); - var linkCell = new mxCell(title, new mxGeometry(pt.x, pt.y, 100, 40), - 'fontColor=#0000EE;fontStyle=4;rounded=1;overflow=hidden;' + ((icon != null) ? - 'shape=label;imageWidth=16;imageHeight=16;spacingLeft=26;align=left;image=' + icon : - 'spacing=10;')); - linkCell.vertex = true; - - graph.setLinkForCell(linkCell, link); - graph.cellSizeUpdated(linkCell, true); - - graph.getModel().beginUpdate(); - try - { - linkCell = graph.addCell(linkCell); - graph.fireEvent(new mxEventObject('cellsInserted', 'cells', [linkCell])); - } - finally - { - graph.getModel().endUpdate(); - } - - graph.setSelectionCell(linkCell); - graph.scrollCellToVisible(graph.getSelectionCell()); - } - }); - } - }).isEnabled = isGraphEnabled; - this.addAction('link...', mxUtils.bind(this, function() - { - var graph = ui.editor.graph; - - if (graph.isEnabled()) - { - if (graph.cellEditor.isContentEditing()) - { - var elt = graph.getSelectedElement(); - var link = graph.getParentByName(elt, 'A', graph.cellEditor.textarea); - var oldValue = ''; - - // Workaround for FF returning the outermost selected element after double - // click on a DOM hierarchy with a link inside (but not as topmost element) - if (link == null && elt != null && elt.getElementsByTagName != null) - { - // Finds all links in the selected DOM and uses the link - // where the selection text matches its text content - var links = elt.getElementsByTagName('a'); - - for (var i = 0; i < links.length && link == null; i++) - { - if (links[i].textContent == elt.textContent) - { - graph.selectNode(links[i]); - link = links[i]; - } - } - } - - if (link != null && link.nodeName == 'A') - { - oldValue = link.getAttribute('href') || ''; - } - - var selState = graph.cellEditor.saveSelection(); - - ui.showLinkDialog(oldValue, mxResources.get('apply'), mxUtils.bind(this, function(value) - { - graph.cellEditor.restoreSelection(selState); - - if (value != null) - { - graph.insertLink(value); - } - })); - } - else if (graph.isSelectionEmpty()) - { - this.get('insertLink').funct(); - } - else - { - this.get('editLink').funct(); - } - } - })).isEnabled = isGraphEnabled; - this.addAction('autosize', function() - { - var cells = graph.getSelectionCells(); - - if (cells != null) - { - graph.getModel().beginUpdate(); - try - { - for (var i = 0; i < cells.length; i++) - { - var cell = cells[i]; - - if (graph.getModel().getChildCount(cell)) - { - graph.updateGroupBounds([cell], 20); - } - else - { - var state = graph.view.getState(cell); - var geo = graph.getCellGeometry(cell); - - if (graph.getModel().isVertex(cell) && state != null && state.text != null && - geo != null && graph.isWrapping(cell)) - { - geo = geo.clone(); - geo.height = state.text.boundingBox.height / graph.view.scale; - graph.getModel().setGeometry(cell, geo); - } - else - { - graph.updateCellSize(cell); - } - } - } - } - finally - { - graph.getModel().endUpdate(); - } - } - }, null, null, Editor.ctrlKey + '+Shift+Y'); - this.addAction('formattedText', function() - { - var state = graph.getView().getState(graph.getSelectionCell()); - - if (state != null) - { - var value = '1'; - graph.stopEditing(); - - graph.getModel().beginUpdate(); - try - { - if (state.style['html'] == '1') - { - value = null; - var label = graph.convertValueToString(state.cell); - - if (mxUtils.getValue(state.style, 'nl2Br', '1') != '0') - { - // Removes newlines from HTML and converts breaks to newlines - // to match the HTML output in plain text - label = label.replace(/\n/g, '').replace(//g, '\n'); - } - - // Removes HTML tags - var temp = document.createElement('div'); - temp.innerHTML = label; - label = mxUtils.extractTextWithWhitespace(temp.childNodes); - - graph.cellLabelChanged(state.cell, label); - } - else - { - // Converts HTML tags to text - var label = mxUtils.htmlEntities(graph.convertValueToString(state.cell), false); - - if (mxUtils.getValue(state.style, 'nl2Br', '1') != '0') - { - // Converts newlines in plain text to breaks in HTML - // to match the plain text output - label = label.replace(/\n/g, '
'); - } - - graph.cellLabelChanged(state.cell, graph.sanitizeHtml(label)); - } - - graph.setCellStyles('html', value); - ui.fireEvent(new mxEventObject('styleChanged', 'keys', ['html'], - 'values', [(value != null) ? value : '0'], 'cells', - graph.getSelectionCells())); - } - finally - { - graph.getModel().endUpdate(); - } - } - }); - this.addAction('wordWrap', function() - { - var state = graph.getView().getState(graph.getSelectionCell()); - var value = 'wrap'; - - graph.stopEditing(); - - if (state != null && state.style[mxConstants.STYLE_WHITE_SPACE] == 'wrap') - { - value = null; - } - - graph.setCellStyles(mxConstants.STYLE_WHITE_SPACE, value); - }); - this.addAction('rotation', function() - { - var value = '0'; - var state = graph.getView().getState(graph.getSelectionCell()); - - if (state != null) - { - value = state.style[mxConstants.STYLE_ROTATION] || value; - } - - var dlg = new FilenameDialog(ui, value, mxResources.get('apply'), function(newValue) - { - if (newValue != null && newValue.length > 0) - { - graph.setCellStyles(mxConstants.STYLE_ROTATION, newValue); - } - }, mxResources.get('enterValue') + ' (' + mxResources.get('rotation') + ' 0-360)'); - - ui.showDialog(dlg.container, 375, 80, true, true); - dlg.init(); - }); - // View actions - this.addAction('resetView', function() - { - graph.zoomTo(1); - ui.resetScrollbars(); - }, null, null, Editor.ctrlKey + '+H'); - this.addAction('zoomIn', function(evt) { graph.zoomIn(); }, null, null, Editor.ctrlKey + ' + (Numpad) / Alt+Mousewheel'); - this.addAction('zoomOut', function(evt) { graph.zoomOut(); }, null, null, Editor.ctrlKey + ' - (Numpad) / Alt+Mousewheel'); - this.addAction('fitWindow', function() { graph.fit(); }, null, null, Editor.ctrlKey + '+Shift+H'); - this.addAction('fitPage', mxUtils.bind(this, function() - { - if (!graph.pageVisible) - { - this.get('pageView').funct(); - } - - var fmt = graph.pageFormat; - var ps = graph.pageScale; - var cw = graph.container.clientWidth - 10; - var ch = graph.container.clientHeight - 10; - var scale = Math.floor(20 * Math.min(cw / fmt.width / ps, ch / fmt.height / ps)) / 20; - graph.zoomTo(scale); - - if (mxUtils.hasScrollbars(graph.container)) - { - var pad = graph.getPagePadding(); - graph.container.scrollTop = pad.y * graph.view.scale - 1; - graph.container.scrollLeft = Math.min(pad.x * graph.view.scale, (graph.container.scrollWidth - graph.container.clientWidth) / 2) - 1; - } - }), null, null, Editor.ctrlKey + '+J'); - this.addAction('fitTwoPages', mxUtils.bind(this, function() - { - if (!graph.pageVisible) - { - this.get('pageView').funct(); - } - - var fmt = graph.pageFormat; - var ps = graph.pageScale; - var cw = graph.container.clientWidth - 10; - var ch = graph.container.clientHeight - 10; - - var scale = Math.floor(20 * Math.min(cw / (2 * fmt.width) / ps, ch / fmt.height / ps)) / 20; - graph.zoomTo(scale); - - if (mxUtils.hasScrollbars(graph.container)) - { - var pad = graph.getPagePadding(); - graph.container.scrollTop = Math.min(pad.y, (graph.container.scrollHeight - graph.container.clientHeight) / 2); - graph.container.scrollLeft = Math.min(pad.x, (graph.container.scrollWidth - graph.container.clientWidth) / 2); - } - }), null, null, Editor.ctrlKey + '+Shift+J'); - this.addAction('fitPageWidth', mxUtils.bind(this, function() - { - if (!graph.pageVisible) - { - this.get('pageView').funct(); - } - - var fmt = graph.pageFormat; - var ps = graph.pageScale; - var cw = graph.container.clientWidth - 10; - - var scale = Math.floor(20 * cw / fmt.width / ps) / 20; - graph.zoomTo(scale); - - if (mxUtils.hasScrollbars(graph.container)) - { - var pad = graph.getPagePadding(); - graph.container.scrollLeft = Math.min(pad.x * graph.view.scale, - (graph.container.scrollWidth - graph.container.clientWidth) / 2); - } - })); - this.put('customZoom', new Action(mxResources.get('custom') + '...', mxUtils.bind(this, function() - { - var dlg = new FilenameDialog(this.editorUi, parseInt(graph.getView().getScale() * 100), mxResources.get('apply'), mxUtils.bind(this, function(newValue) - { - var val = parseInt(newValue); - - if (!isNaN(val) && val > 0) - { - graph.zoomTo(val / 100); - } - }), mxResources.get('zoom') + ' (%)'); - this.editorUi.showDialog(dlg.container, 300, 80, true, true); - dlg.init(); - }), null, null, Editor.ctrlKey + '+0')); - this.addAction('pageScale...', mxUtils.bind(this, function() - { - var dlg = new FilenameDialog(this.editorUi, parseInt(graph.pageScale * 100), mxResources.get('apply'), mxUtils.bind(this, function(newValue) - { - var val = parseInt(newValue); - - if (!isNaN(val) && val > 0) - { - ui.setPageScale(val / 100); - } - }), mxResources.get('pageScale') + ' (%)'); - this.editorUi.showDialog(dlg.container, 300, 80, true, true); - dlg.init(); - })); - - // Option actions - var action = null; - action = this.addAction('grid', function() - { - graph.setGridEnabled(!graph.isGridEnabled()); - ui.fireEvent(new mxEventObject('gridEnabledChanged')); - }, null, null, Editor.ctrlKey + '+Shift+G'); - action.setToggleAction(true); - action.setSelectedCallback(function() { return graph.isGridEnabled(); }); - action.setEnabled(false); - - action = this.addAction('guides', function() - { - graph.graphHandler.guidesEnabled = !graph.graphHandler.guidesEnabled; - ui.fireEvent(new mxEventObject('guidesEnabledChanged')); - }); - action.setToggleAction(true); - action.setSelectedCallback(function() { return graph.graphHandler.guidesEnabled; }); - action.setEnabled(false); - - action = this.addAction('tooltips', function() - { - graph.tooltipHandler.setEnabled(!graph.tooltipHandler.isEnabled()); - }); - action.setToggleAction(true); - action.setSelectedCallback(function() { return graph.tooltipHandler.isEnabled(); }); - - action = this.addAction('collapseExpand', function() - { - var change = new ChangePageSetup(ui); - change.ignoreColor = true; - change.ignoreImage = true; - change.foldingEnabled = !graph.foldingEnabled; - - graph.model.execute(change); - }); - action.setToggleAction(true); - action.setSelectedCallback(function() { return graph.foldingEnabled; }); - action.isEnabled = isGraphEnabled; - action = this.addAction('scrollbars', function() - { - ui.setScrollbars(!ui.hasScrollbars()); - }); - action.setToggleAction(true); - action.setSelectedCallback(function() { return graph.scrollbars; }); - action = this.addAction('pageView', mxUtils.bind(this, function() - { - ui.setPageVisible(!graph.pageVisible); - })); - action.setToggleAction(true); - action.setSelectedCallback(function() { return graph.pageVisible; }); - action = this.addAction('connectionArrows', function() - { - graph.connectionArrowsEnabled = !graph.connectionArrowsEnabled; - ui.fireEvent(new mxEventObject('connectionArrowsChanged')); - }, null, null, 'Alt+Shift+A'); - action.setToggleAction(true); - action.setSelectedCallback(function() { return graph.connectionArrowsEnabled; }); - action = this.addAction('connectionPoints', function() - { - graph.setConnectable(!graph.connectionHandler.isEnabled()); - ui.fireEvent(new mxEventObject('connectionPointsChanged')); - }, null, null, 'Alt+Shift+P'); - action.setToggleAction(true); - action.setSelectedCallback(function() { return graph.connectionHandler.isEnabled(); }); - action = this.addAction('copyConnect', function() - { - graph.connectionHandler.setCreateTarget(!graph.connectionHandler.isCreateTarget()); - ui.fireEvent(new mxEventObject('copyConnectChanged')); - }); - action.setToggleAction(true); - action.setSelectedCallback(function() { return graph.connectionHandler.isCreateTarget(); }); - action.isEnabled = isGraphEnabled; - action = this.addAction('autosave', function() - { - ui.editor.setAutosave(!ui.editor.autosave); - }); - action.setToggleAction(true); - action.setSelectedCallback(function() { return ui.editor.autosave; }); - action.isEnabled = isGraphEnabled; - action.visible = false; - - // Help actions - this.addAction('help', function() - { - var ext = ''; - - if (mxResources.isLanguageSupported(mxClient.language)) - { - ext = '_' + mxClient.language; - } - - graph.openLink(RESOURCES_PATH + '/help' + ext + '.html'); - }); - - var showingAbout = false; - - this.put('about', new Action(mxResources.get('about') + ' Graph Editor...', function() - { - if (!showingAbout) - { - ui.showDialog(new AboutDialog(ui).container, 320, 280, true, true, function() - { - showingAbout = false; - }); - - showingAbout = true; - } - }, null, null, 'F1')); - - // Font style actions - var toggleFontStyle = mxUtils.bind(this, function(key, style, fn, shortcut) - { - return this.addAction(key, function() - { - if (fn != null && graph.cellEditor.isContentEditing()) - { - fn(); - } - else - { - graph.stopEditing(false); - - graph.getModel().beginUpdate(); - try - { - graph.toggleCellStyleFlags(mxConstants.STYLE_FONTSTYLE, style); - - // Removes bold and italic tags and CSS styles inside labels - if ((style & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD) - { - graph.updateLabelElements(graph.getSelectionCells(), function(elt) - { - elt.style.fontWeight = null; - - if (elt.nodeName == 'B') - { - graph.replaceElement(elt); - } - }); - } - else if ((style & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC) - { - graph.updateLabelElements(graph.getSelectionCells(), function(elt) - { - elt.style.fontStyle = null; - - if (elt.nodeName == 'I') - { - graph.replaceElement(elt); - } - }); - } - else if ((style & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE) - { - graph.updateLabelElements(graph.getSelectionCells(), function(elt) - { - elt.style.textDecoration = null; - - if (elt.nodeName == 'U') - { - graph.replaceElement(elt); - } - }); - } - } - finally - { - graph.getModel().endUpdate(); - } - } - }, null, null, shortcut); - }); - - toggleFontStyle('bold', mxConstants.FONT_BOLD, function() { document.execCommand('bold', false, null); }, Editor.ctrlKey + '+B'); - toggleFontStyle('italic', mxConstants.FONT_ITALIC, function() { document.execCommand('italic', false, null); }, Editor.ctrlKey + '+I'); - toggleFontStyle('underline', mxConstants.FONT_UNDERLINE, function() { document.execCommand('underline', false, null); }, Editor.ctrlKey + '+U'); - - // Color actions - this.addAction('fontColor...', function() { ui.menus.pickColor(mxConstants.STYLE_FONTCOLOR, 'forecolor', '000000'); }); - this.addAction('strokeColor...', function() { ui.menus.pickColor(mxConstants.STYLE_STROKECOLOR); }); - this.addAction('fillColor...', function() { ui.menus.pickColor(mxConstants.STYLE_FILLCOLOR); }); - this.addAction('gradientColor...', function() { ui.menus.pickColor(mxConstants.STYLE_GRADIENTCOLOR); }); - this.addAction('backgroundColor...', function() { ui.menus.pickColor(mxConstants.STYLE_LABEL_BACKGROUNDCOLOR, 'backcolor'); }); - this.addAction('borderColor...', function() { ui.menus.pickColor(mxConstants.STYLE_LABEL_BORDERCOLOR); }); - - // Format actions - this.addAction('vertical', function() { ui.menus.toggleStyle(mxConstants.STYLE_HORIZONTAL, true); }); - this.addAction('shadow', function() { ui.menus.toggleStyle(mxConstants.STYLE_SHADOW); }); - this.addAction('solid', function() - { - graph.getModel().beginUpdate(); - try - { - graph.setCellStyles(mxConstants.STYLE_DASHED, null); - graph.setCellStyles(mxConstants.STYLE_DASH_PATTERN, null); - ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], - 'values', [null, null], 'cells', graph.getSelectionCells())); - } - finally - { - graph.getModel().endUpdate(); - } - }); - this.addAction('dashed', function() - { - graph.getModel().beginUpdate(); - try - { - graph.setCellStyles(mxConstants.STYLE_DASHED, '1'); - graph.setCellStyles(mxConstants.STYLE_DASH_PATTERN, null); - ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], - 'values', ['1', null], 'cells', graph.getSelectionCells())); - } - finally - { - graph.getModel().endUpdate(); - } - }); - this.addAction('dotted', function() - { - graph.getModel().beginUpdate(); - try - { - graph.setCellStyles(mxConstants.STYLE_DASHED, '1'); - graph.setCellStyles(mxConstants.STYLE_DASH_PATTERN, '1 4'); - ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], - 'values', ['1', '1 4'], 'cells', graph.getSelectionCells())); - } - finally - { - graph.getModel().endUpdate(); - } - }); - this.addAction('sharp', function() - { - graph.getModel().beginUpdate(); - try - { - graph.setCellStyles(mxConstants.STYLE_ROUNDED, '0'); - graph.setCellStyles(mxConstants.STYLE_CURVED, '0'); - ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED], - 'values', ['0', '0'], 'cells', graph.getSelectionCells())); - } - finally - { - graph.getModel().endUpdate(); - } - }); - this.addAction('rounded', function() - { - graph.getModel().beginUpdate(); - try - { - graph.setCellStyles(mxConstants.STYLE_ROUNDED, '1'); - graph.setCellStyles(mxConstants.STYLE_CURVED, '0'); - ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED], - 'values', ['1', '0'], 'cells', graph.getSelectionCells())); - } - finally - { - graph.getModel().endUpdate(); - } - }); - this.addAction('toggleRounded', function() - { - if (!graph.isSelectionEmpty() && graph.isEnabled()) - { - graph.getModel().beginUpdate(); - try - { - var cells = graph.getSelectionCells(); - var state = graph.view.getState(cells[0]); - var style = (state != null) ? state.style : graph.getCellStyle(cells[0]); - var value = (mxUtils.getValue(style, mxConstants.STYLE_ROUNDED, '0') == '1') ? '0' : '1'; - - graph.setCellStyles(mxConstants.STYLE_ROUNDED, value); - graph.setCellStyles(mxConstants.STYLE_CURVED, null); - ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED], - 'values', [value, '0'], 'cells', graph.getSelectionCells())); - } - finally - { - graph.getModel().endUpdate(); - } - } - }); - this.addAction('curved', function() - { - graph.getModel().beginUpdate(); - try - { - graph.setCellStyles(mxConstants.STYLE_ROUNDED, '0'); - graph.setCellStyles(mxConstants.STYLE_CURVED, '1'); - ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED], - 'values', ['0', '1'], 'cells', graph.getSelectionCells())); - } - finally - { - graph.getModel().endUpdate(); - } - }); - this.addAction('collapsible', function() - { - var state = graph.view.getState(graph.getSelectionCell()); - var value = '1'; - - if (state != null && graph.getFoldingImage(state) != null) - { - value = '0'; - } - - graph.setCellStyles('collapsible', value); - ui.fireEvent(new mxEventObject('styleChanged', 'keys', ['collapsible'], - 'values', [value], 'cells', graph.getSelectionCells())); - }); - this.addAction('editStyle...', mxUtils.bind(this, function() - { - var cells = graph.getSelectionCells(); - - if (cells != null && cells.length > 0) - { - var model = graph.getModel(); - - var dlg = new TextareaDialog(this.editorUi, mxResources.get('editStyle') + ':', - model.getStyle(cells[0]) || '', function(newValue) - { - if (newValue != null) - { - graph.setCellStyle(mxUtils.trim(newValue), cells); - } - }, null, null, 400, 220); - this.editorUi.showDialog(dlg.container, 420, 300, true, true); - dlg.init(); - } - }), null, null, Editor.ctrlKey + '+E'); - this.addAction('setAsDefaultStyle', function() - { - if (graph.isEnabled() && !graph.isSelectionEmpty()) - { - ui.setDefaultStyle(graph.getSelectionCell()); - } - }, null, null, Editor.ctrlKey + '+Shift+D'); - this.addAction('clearDefaultStyle', function() - { - if (graph.isEnabled()) - { - ui.clearDefaultStyle(); - } - }, null, null, Editor.ctrlKey + '+Shift+R'); - this.addAction('addWaypoint', function() - { - var cell = graph.getSelectionCell(); - - if (cell != null && graph.getModel().isEdge(cell)) - { - var handler = editor.graph.selectionCellsHandler.getHandler(cell); - - if (handler instanceof mxEdgeHandler) - { - var t = graph.view.translate; - var s = graph.view.scale; - var dx = t.x; - var dy = t.y; - - var parent = graph.getModel().getParent(cell); - var pgeo = graph.getCellGeometry(parent); - - while (graph.getModel().isVertex(parent) && pgeo != null) - { - dx += pgeo.x; - dy += pgeo.y; - - parent = graph.getModel().getParent(parent); - pgeo = graph.getCellGeometry(parent); - } - - var x = Math.round(graph.snap(graph.popupMenuHandler.triggerX / s - dx)); - var y = Math.round(graph.snap(graph.popupMenuHandler.triggerY / s - dy)); - - handler.addPointAt(handler.state, x, y); - } - } - }); - this.addAction('removeWaypoint', function() - { - // TODO: Action should run with "this" set to action - var rmWaypointAction = ui.actions.get('removeWaypoint'); - - if (rmWaypointAction.handler != null) - { - // NOTE: Popupevent handled and action updated in Menus.createPopupMenu - rmWaypointAction.handler.removePoint(rmWaypointAction.handler.state, rmWaypointAction.index); - } - }); - this.addAction('clearWaypoints', function() - { - var cells = graph.getSelectionCells(); - - if (cells != null) - { - cells = graph.addAllEdges(cells); - - graph.getModel().beginUpdate(); - try - { - for (var i = 0; i < cells.length; i++) - { - var cell = cells[i]; - - if (graph.getModel().isEdge(cell)) - { - var geo = graph.getCellGeometry(cell); - - if (geo != null) - { - geo = geo.clone(); - geo.points = null; - graph.getModel().setGeometry(cell, geo); - } - } - } - } - finally - { - graph.getModel().endUpdate(); - } - } - }, null, null, 'Alt+Shift+C'); - action = this.addAction('subscript', mxUtils.bind(this, function() - { - if (graph.cellEditor.isContentEditing()) - { - document.execCommand('subscript', false, null); - } - }), null, null, Editor.ctrlKey + '+,'); - action = this.addAction('superscript', mxUtils.bind(this, function() - { - if (graph.cellEditor.isContentEditing()) - { - document.execCommand('superscript', false, null); - } - }), null, null, Editor.ctrlKey + '+.'); - this.addAction('image...', function() - { - if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) - { - var title = mxResources.get('image') + ' (' + mxResources.get('url') + '):'; - var state = graph.getView().getState(graph.getSelectionCell()); - var value = ''; - - if (state != null) - { - value = state.style[mxConstants.STYLE_IMAGE] || value; - } - - var selectionState = graph.cellEditor.saveSelection(); - - ui.showImageDialog(title, value, function(newValue, w, h) - { - // Inserts image into HTML text - if (graph.cellEditor.isContentEditing()) - { - graph.cellEditor.restoreSelection(selectionState); - graph.insertImage(newValue, w, h); - } - else - { - var cells = graph.getSelectionCells(); - - if (newValue != null && (newValue.length > 0 || cells.length > 0)) - { - var select = null; - - graph.getModel().beginUpdate(); - try - { - // Inserts new cell if no cell is selected - if (cells.length == 0) - { - var pt = graph.getFreeInsertPoint(); - cells = [graph.insertVertex(graph.getDefaultParent(), null, '', pt.x, pt.y, w, h, - 'shape=image;imageAspect=0;aspect=fixed;verticalLabelPosition=bottom;verticalAlign=top;')]; - select = cells; - graph.fireEvent(new mxEventObject('cellsInserted', 'cells', select)); - } - - graph.setCellStyles(mxConstants.STYLE_IMAGE, (newValue.length > 0) ? newValue : null, cells); - - // Sets shape only if not already shape with image (label or image) - var state = graph.view.getState(cells[0]); - var style = (state != null) ? state.style : graph.getCellStyle(cells[0]); - - if (style[mxConstants.STYLE_SHAPE] != 'image' && style[mxConstants.STYLE_SHAPE] != 'label') - { - graph.setCellStyles(mxConstants.STYLE_SHAPE, 'image', cells); - } - else if (newValue.length == 0) - { - graph.setCellStyles(mxConstants.STYLE_SHAPE, null, cells); - } - - if (graph.getSelectionCount() == 1) - { - if (w != null && h != null) - { - var cell = cells[0]; - var geo = graph.getModel().getGeometry(cell); - - if (geo != null) - { - geo = geo.clone(); - geo.width = w; - geo.height = h; - graph.getModel().setGeometry(cell, geo); - } - } - } - } - finally - { - graph.getModel().endUpdate(); - } - - if (select != null) - { - graph.setSelectionCells(select); - graph.scrollCellToVisible(select[0]); - } - } - } - }, graph.cellEditor.isContentEditing(), !graph.cellEditor.isContentEditing()); - } - }).isEnabled = isGraphEnabled; - this.addAction('insertImage...', function() - { - if (graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent())) - { - graph.clearSelection(); - ui.actions.get('image').funct(); - } - }).isEnabled = isGraphEnabled; - action = this.addAction('layers', mxUtils.bind(this, function() - { - if (this.layersWindow == null) - { - // LATER: Check outline window for initial placement - this.layersWindow = new LayersWindow(ui, document.body.offsetWidth - 280, 120, 220, 180); - this.layersWindow.window.addListener('show', function() - { - ui.fireEvent(new mxEventObject('layers')); - }); - this.layersWindow.window.addListener('hide', function() - { - ui.fireEvent(new mxEventObject('layers')); - }); - this.layersWindow.window.setVisible(true); - ui.fireEvent(new mxEventObject('layers')); - } - else - { - this.layersWindow.window.setVisible(!this.layersWindow.window.isVisible()); - } - }), null, null, Editor.ctrlKey + '+Shift+L'); - action.setToggleAction(true); - action.setSelectedCallback(mxUtils.bind(this, function() { return this.layersWindow != null && this.layersWindow.window.isVisible(); })); - action = this.addAction('formatPanel', mxUtils.bind(this, function() - { - ui.toggleFormatPanel(); - }), null, null, Editor.ctrlKey + '+Shift+P'); - action.setToggleAction(true); - action.setSelectedCallback(mxUtils.bind(this, function() { return ui.formatWidth > 0; })); - action = this.addAction('outline', mxUtils.bind(this, function() - { - if (this.outlineWindow == null) - { - // LATER: Check layers window for initial placement - this.outlineWindow = new OutlineWindow(ui, document.body.offsetWidth - 260, 100, 180, 180); - this.outlineWindow.window.addListener('show', function() - { - ui.fireEvent(new mxEventObject('outline')); - }); - this.outlineWindow.window.addListener('hide', function() - { - ui.fireEvent(new mxEventObject('outline')); - }); - this.outlineWindow.window.setVisible(true); - ui.fireEvent(new mxEventObject('outline')); - } - else - { - this.outlineWindow.window.setVisible(!this.outlineWindow.window.isVisible()); - } - }), null, null, Editor.ctrlKey + '+Shift+O'); - - action.setToggleAction(true); - action.setSelectedCallback(mxUtils.bind(this, function() { return this.outlineWindow != null && this.outlineWindow.window.isVisible(); })); -}; - -/** - * Registers the given action under the given name. - */ -Actions.prototype.addAction = function(key, funct, enabled, iconCls, shortcut) -{ - var title; - - if (key.substring(key.length - 3) == '...') - { - key = key.substring(0, key.length - 3); - title = mxResources.get(key) + '...'; - } - else - { - title = mxResources.get(key); - } - - return this.put(key, new Action(title, funct, enabled, iconCls, shortcut)); -}; - -/** - * Registers the given action under the given name. - */ -Actions.prototype.put = function(name, action) -{ - this.actions[name] = action; - - return action; -}; - -/** - * Returns the action for the given name or null if no such action exists. - */ -Actions.prototype.get = function(name) -{ - return this.actions[name]; -}; - -/** - * Constructs a new action for the given parameters. - */ -function Action(label, funct, enabled, iconCls, shortcut) -{ - mxEventSource.call(this); - this.label = label; - this.funct = this.createFunction(funct); - this.enabled = (enabled != null) ? enabled : true; - this.iconCls = iconCls; - this.shortcut = shortcut; - this.visible = true; -}; - -// Action inherits from mxEventSource -mxUtils.extend(Action, mxEventSource); - -/** - * Sets the enabled state of the action and fires a stateChanged event. - */ -Action.prototype.createFunction = function(funct) -{ - return funct; -}; - -/** - * Sets the enabled state of the action and fires a stateChanged event. - */ -Action.prototype.setEnabled = function(value) -{ - if (this.enabled != value) - { - this.enabled = value; - this.fireEvent(new mxEventObject('stateChanged')); - } -}; - -/** - * Sets the enabled state of the action and fires a stateChanged event. - */ -Action.prototype.isEnabled = function() -{ - return this.enabled; -}; - -/** - * Sets the enabled state of the action and fires a stateChanged event. - */ -Action.prototype.setToggleAction = function(value) -{ - this.toggleAction = value; -}; - -/** - * Sets the enabled state of the action and fires a stateChanged event. - */ -Action.prototype.setSelectedCallback = function(funct) -{ - this.selectedCallback = funct; -}; - -/** - * Sets the enabled state of the action and fires a stateChanged event. - */ -Action.prototype.isSelected = function() -{ - return this.selectedCallback(); -}; diff --git a/media/grapheditor/js/Dialogs.js b/media/grapheditor/js/Dialogs.js deleted file mode 100644 index c7dbf6c089..0000000000 --- a/media/grapheditor/js/Dialogs.js +++ /dev/null @@ -1,2542 +0,0 @@ -/** - * Copyright (c) 2006-2012, JGraph Ltd - */ -/** - * Constructs a new open dialog. - */ -var OpenDialog = function() -{ - var iframe = document.createElement('iframe'); - iframe.style.backgroundColor = 'transparent'; - iframe.allowTransparency = 'true'; - iframe.style.borderStyle = 'none'; - iframe.style.borderWidth = '0px'; - iframe.style.overflow = 'hidden'; - iframe.frameBorder = '0'; - - // Adds padding as a workaround for box model in older IE versions - var dx = (mxClient.IS_VML && (document.documentMode == null || document.documentMode < 8)) ? 20 : 0; - - iframe.setAttribute('width', (((Editor.useLocalStorage) ? 640 : 320) + dx) + 'px'); - iframe.setAttribute('height', (((Editor.useLocalStorage) ? 480 : 220) + dx) + 'px'); - iframe.setAttribute('src', OPEN_FORM); - - this.container = iframe; -}; - -/** - * Constructs a new color dialog. - */ -var ColorDialog = function(editorUi, color, apply, cancelFn) -{ - this.editorUi = editorUi; - - var input = document.createElement('input'); - input.style.marginBottom = '10px'; - input.style.width = '216px'; - - // Required for picker to render in IE - if (mxClient.IS_IE) - { - input.style.marginTop = '10px'; - document.body.appendChild(input); - } - - this.init = function() - { - if (!mxClient.IS_TOUCH) - { - input.focus(); - } - }; - - var picker = new jscolor.color(input); - picker.pickerOnfocus = false; - picker.showPicker(); - - var div = document.createElement('div'); - jscolor.picker.box.style.position = 'relative'; - jscolor.picker.box.style.width = '230px'; - jscolor.picker.box.style.height = '100px'; - jscolor.picker.box.style.paddingBottom = '10px'; - div.appendChild(jscolor.picker.box); - - var center = document.createElement('center'); - - function createRecentColorTable() - { - var table = addPresets((ColorDialog.recentColors.length == 0) ? ['FFFFFF'] : - ColorDialog.recentColors, 11, 'FFFFFF', true); - table.style.marginBottom = '8px'; - - return table; - }; - - function addPresets(presets, rowLength, defaultColor, addResetOption) - { - rowLength = (rowLength != null) ? rowLength : 12; - var table = document.createElement('table'); - table.style.borderCollapse = 'collapse'; - table.setAttribute('cellspacing', '0'); - table.style.marginBottom = '20px'; - table.style.cellSpacing = '0px'; - var tbody = document.createElement('tbody'); - table.appendChild(tbody); - - var rows = presets.length / rowLength; - - for (var row = 0; row < rows; row++) - { - var tr = document.createElement('tr'); - - for (var i = 0; i < rowLength; i++) - { - (function(clr) - { - var td = document.createElement('td'); - td.style.border = '1px solid black'; - td.style.padding = '0px'; - td.style.width = '16px'; - td.style.height = '16px'; - - if (clr == null) - { - clr = defaultColor; - } - - if (clr == 'none') - { - td.style.background = 'url(\'' + Dialog.prototype.noColorImage + '\')'; - } - else - { - td.style.backgroundColor = '#' + clr; - } - - tr.appendChild(td); - - if (clr != null) - { - td.style.cursor = 'pointer'; - - mxEvent.addListener(td, 'click', function() - { - if (clr == 'none') - { - picker.fromString('ffffff'); - input.value = 'none'; - } - else - { - picker.fromString(clr); - } - }); - } - })(presets[row * rowLength + i]); - } - - tbody.appendChild(tr); - } - - if (addResetOption) - { - var td = document.createElement('td'); - td.setAttribute('title', mxResources.get('reset')); - td.style.border = '1px solid black'; - td.style.padding = '0px'; - td.style.width = '16px'; - td.style.height = '16px'; - td.style.backgroundImage = 'url(\'' + Dialog.prototype.closeImage + '\')'; - td.style.backgroundPosition = 'center center'; - td.style.backgroundRepeat = 'no-repeat'; - td.style.cursor = 'pointer'; - - tr.appendChild(td); - - mxEvent.addListener(td, 'click', function() - { - ColorDialog.resetRecentColors(); - table.parentNode.replaceChild(createRecentColorTable(), table); - }); - } - - center.appendChild(table); - - return table; - }; - - div.appendChild(input); - mxUtils.br(div); - - // Adds recent colors - createRecentColorTable(); - - // Adds presets - var table = addPresets(this.presetColors); - table.style.marginBottom = '8px'; - table = addPresets(this.defaultColors); - table.style.marginBottom = '16px'; - - div.appendChild(center); - - var buttons = document.createElement('div'); - buttons.style.textAlign = 'right'; - buttons.style.whiteSpace = 'nowrap'; - - var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() - { - editorUi.hideDialog(); - - if (cancelFn != null) - { - cancelFn(); - } - }); - cancelBtn.className = 'geBtn'; - - if (editorUi.editor.cancelFirst) - { - buttons.appendChild(cancelBtn); - } - - var applyFunction = (apply != null) ? apply : this.createApplyFunction(); - - var applyBtn = mxUtils.button(mxResources.get('apply'), function() - { - var color = input.value; - ColorDialog.addRecentColor(color, 12); - - if (color != 'none' && color.charAt(0) != '#') - { - color = '#' + color; - } - - applyFunction(color); - editorUi.hideDialog(); - }); - applyBtn.className = 'geBtn gePrimaryBtn'; - buttons.appendChild(applyBtn); - - if (!editorUi.editor.cancelFirst) - { - buttons.appendChild(cancelBtn); - } - - if (color != null) - { - if (color == 'none') - { - picker.fromString('ffffff'); - input.value = 'none'; - } - else - { - picker.fromString(color); - } - } - - div.appendChild(buttons); - this.picker = picker; - this.colorInput = input; - - // LATER: Only fires if input if focused, should always - // fire if this dialog is showing. - mxEvent.addListener(div, 'keydown', function(e) - { - if (e.keyCode == 27) - { - editorUi.hideDialog(); - - if (cancelFn != null) - { - cancelFn(); - } - - mxEvent.consume(e); - } - }); - - this.container = div; -}; - -/** - * Creates function to apply value - */ -ColorDialog.prototype.presetColors = ['E6D0DE', 'CDA2BE', 'B5739D', 'E1D5E7', 'C3ABD0', 'A680B8', 'D4E1F5', 'A9C4EB', '7EA6E0', 'D5E8D4', '9AC7BF', '67AB9F', 'D5E8D4', 'B9E0A5', '97D077', 'FFF2CC', 'FFE599', 'FFD966', 'FFF4C3', 'FFCE9F', 'FFB570', 'F8CECC', 'F19C99', 'EA6B66']; - -/** - * Creates function to apply value - */ -ColorDialog.prototype.defaultColors = ['none', 'FFFFFF', 'E6E6E6', 'CCCCCC', 'B3B3B3', '999999', '808080', '666666', '4D4D4D', '333333', '1A1A1A', '000000', 'FFCCCC', 'FFE6CC', 'FFFFCC', 'E6FFCC', 'CCFFCC', 'CCFFE6', 'CCFFFF', 'CCE5FF', 'CCCCFF', 'E5CCFF', 'FFCCFF', 'FFCCE6', - 'FF9999', 'FFCC99', 'FFFF99', 'CCFF99', '99FF99', '99FFCC', '99FFFF', '99CCFF', '9999FF', 'CC99FF', 'FF99FF', 'FF99CC', 'FF6666', 'FFB366', 'FFFF66', 'B3FF66', '66FF66', '66FFB3', '66FFFF', '66B2FF', '6666FF', 'B266FF', 'FF66FF', 'FF66B3', 'FF3333', 'FF9933', 'FFFF33', - '99FF33', '33FF33', '33FF99', '33FFFF', '3399FF', '3333FF', '9933FF', 'FF33FF', 'FF3399', 'FF0000', 'FF8000', 'FFFF00', '80FF00', '00FF00', '00FF80', '00FFFF', '007FFF', '0000FF', '7F00FF', 'FF00FF', 'FF0080', 'CC0000', 'CC6600', 'CCCC00', '66CC00', '00CC00', '00CC66', - '00CCCC', '0066CC', '0000CC', '6600CC', 'CC00CC', 'CC0066', '990000', '994C00', '999900', '4D9900', '009900', '00994D', '009999', '004C99', '000099', '4C0099', '990099', '99004D', '660000', '663300', '666600', '336600', '006600', '006633', '006666', '003366', '000066', - '330066', '660066', '660033', '330000', '331A00', '333300', '1A3300', '003300', '00331A', '003333', '001933', '000033', '190033', '330033', '33001A']; - -/** - * Creates function to apply value - */ -ColorDialog.prototype.createApplyFunction = function() -{ - return mxUtils.bind(this, function(color) - { - var graph = this.editorUi.editor.graph; - - graph.getModel().beginUpdate(); - try - { - graph.setCellStyles(this.currentColorKey, color); - this.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', [this.currentColorKey], - 'values', [color], 'cells', graph.getSelectionCells())); - } - finally - { - graph.getModel().endUpdate(); - } - }); -}; - -/** - * - */ -ColorDialog.recentColors = []; - -/** - * Adds recent color for later use. - */ -ColorDialog.addRecentColor = function(color, max) -{ - if (color != null) - { - mxUtils.remove(color, ColorDialog.recentColors); - ColorDialog.recentColors.splice(0, 0, color); - - if (ColorDialog.recentColors.length >= max) - { - ColorDialog.recentColors.pop(); - } - } -}; - -/** - * Adds recent color for later use. - */ -ColorDialog.resetRecentColors = function() -{ - ColorDialog.recentColors = []; -}; - -/** - * Constructs a new about dialog. - */ -var AboutDialog = function(editorUi) -{ - var div = document.createElement('div'); - div.setAttribute('align', 'center'); - var h3 = document.createElement('h3'); - mxUtils.write(h3, mxResources.get('about') + ' GraphEditor'); - div.appendChild(h3); - var img = document.createElement('img'); - img.style.border = '0px'; - img.setAttribute('width', '176'); - img.setAttribute('width', '151'); - img.setAttribute('src', IMAGE_PATH + '/logo.png'); - div.appendChild(img); - mxUtils.br(div); - mxUtils.write(div, 'Powered by mxGraph ' + mxClient.VERSION); - mxUtils.br(div); - var link = document.createElement('a'); - link.setAttribute('href', 'http://www.jgraph.com/'); - link.setAttribute('target', '_blank'); - mxUtils.write(link, 'www.jgraph.com'); - div.appendChild(link); - mxUtils.br(div); - mxUtils.br(div); - var closeBtn = mxUtils.button(mxResources.get('close'), function() - { - editorUi.hideDialog(); - }); - closeBtn.className = 'geBtn gePrimaryBtn'; - div.appendChild(closeBtn); - - this.container = div; -}; - -/** - * Constructs a new filename dialog. - */ -var FilenameDialog = function(editorUi, filename, buttonText, fn, label, validateFn, content, helpLink, closeOnBtn, cancelFn) -{ - closeOnBtn = (closeOnBtn != null) ? closeOnBtn : true; - var row, td; - - var table = document.createElement('table'); - var tbody = document.createElement('tbody'); - table.style.marginTop = '8px'; - - row = document.createElement('tr'); - - td = document.createElement('td'); - td.style.whiteSpace = 'nowrap'; - td.style.fontSize = '10pt'; - td.style.width = '120px'; - mxUtils.write(td, (label || mxResources.get('filename')) + ':'); - - row.appendChild(td); - - var nameInput = document.createElement('input'); - nameInput.setAttribute('value', filename || ''); - nameInput.style.marginLeft = '4px'; - nameInput.style.width = '180px'; - - var genericBtn = mxUtils.button(buttonText, function() - { - if (validateFn == null || validateFn(nameInput.value)) - { - if (closeOnBtn) - { - editorUi.hideDialog(); - } - - fn(nameInput.value); - } - }); - genericBtn.className = 'geBtn gePrimaryBtn'; - - this.init = function() - { - if (label == null && content != null) - { - return; - } - - nameInput.focus(); - - if (mxClient.IS_GC || mxClient.IS_FF || document.documentMode >= 5 || mxClient.IS_QUIRKS) - { - nameInput.select(); - } - else - { - document.execCommand('selectAll', false, null); - } - - // Installs drag and drop handler for links - if (Graph.fileSupport) - { - // Setup the dnd listeners - var dlg = table.parentNode; - var graph = editorUi.editor.graph; - var dropElt = null; - - mxEvent.addListener(dlg, 'dragleave', function(evt) - { - if (dropElt != null) - { - dropElt.style.backgroundColor = ''; - dropElt = null; - } - - evt.stopPropagation(); - evt.preventDefault(); - }); - - mxEvent.addListener(dlg, 'dragover', mxUtils.bind(this, function(evt) - { - // IE 10 does not implement pointer-events so it can't have a drop highlight - if (dropElt == null && (!mxClient.IS_IE || document.documentMode > 10)) - { - dropElt = nameInput; - dropElt.style.backgroundColor = '#ebf2f9'; - } - - evt.stopPropagation(); - evt.preventDefault(); - })); - - mxEvent.addListener(dlg, 'drop', mxUtils.bind(this, function(evt) - { - if (dropElt != null) - { - dropElt.style.backgroundColor = ''; - dropElt = null; - } - - if (mxUtils.indexOf(evt.dataTransfer.types, 'text/uri-list') >= 0) - { - nameInput.value = decodeURIComponent(evt.dataTransfer.getData('text/uri-list')); - genericBtn.click(); - } - - evt.stopPropagation(); - evt.preventDefault(); - })); - } - }; - - td = document.createElement('td'); - td.appendChild(nameInput); - row.appendChild(td); - - if (label != null || content == null) - { - tbody.appendChild(row); - } - - if (content != null) - { - row = document.createElement('tr'); - td = document.createElement('td'); - td.colSpan = 2; - td.appendChild(content); - row.appendChild(td); - tbody.appendChild(row); - } - - row = document.createElement('tr'); - td = document.createElement('td'); - td.colSpan = 2; - td.style.paddingTop = '20px'; - td.style.whiteSpace = 'nowrap'; - td.setAttribute('align', 'right'); - - var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() - { - editorUi.hideDialog(); - - if (cancelFn != null) - { - cancelFn(); - } - }); - cancelBtn.className = 'geBtn'; - - if (editorUi.editor.cancelFirst) - { - td.appendChild(cancelBtn); - } - - if (helpLink != null) - { - var helpBtn = mxUtils.button(mxResources.get('help'), function() - { - editorUi.editor.graph.openLink(helpLink); - }); - - helpBtn.className = 'geBtn'; - td.appendChild(helpBtn); - } - - mxEvent.addListener(nameInput, 'keypress', function(e) - { - if (e.keyCode == 13) - { - genericBtn.click(); - } - }); - - td.appendChild(genericBtn); - - if (!editorUi.editor.cancelFirst) - { - td.appendChild(cancelBtn); - } - - row.appendChild(td); - tbody.appendChild(row); - table.appendChild(tbody); - - this.container = table; -}; - -/** - * Constructs a new textarea dialog. - */ -var TextareaDialog = function(editorUi, title, url, fn, cancelFn, cancelTitle, w, h, addButtons, noHide, noWrap, applyTitle) -{ - w = (w != null) ? w : 300; - h = (h != null) ? h : 120; - noHide = (noHide != null) ? noHide : false; - var row, td; - - var table = document.createElement('table'); - var tbody = document.createElement('tbody'); - - row = document.createElement('tr'); - - td = document.createElement('td'); - td.style.fontSize = '10pt'; - td.style.width = '100px'; - mxUtils.write(td, title); - - row.appendChild(td); - tbody.appendChild(row); - - row = document.createElement('tr'); - td = document.createElement('td'); - - var nameInput = document.createElement('textarea'); - - if (noWrap) - { - nameInput.setAttribute('wrap', 'off'); - } - - nameInput.setAttribute('spellcheck', 'false'); - nameInput.setAttribute('autocorrect', 'off'); - nameInput.setAttribute('autocomplete', 'off'); - nameInput.setAttribute('autocapitalize', 'off'); - - mxUtils.write(nameInput, url || ''); - nameInput.style.resize = 'none'; - nameInput.style.width = w + 'px'; - nameInput.style.height = h + 'px'; - - this.textarea = nameInput; - - this.init = function() - { - nameInput.focus(); - nameInput.scrollTop = 0; - }; - - td.appendChild(nameInput); - row.appendChild(td); - - tbody.appendChild(row); - - row = document.createElement('tr'); - td = document.createElement('td'); - td.style.paddingTop = '14px'; - td.style.whiteSpace = 'nowrap'; - td.setAttribute('align', 'right'); - - var cancelBtn = mxUtils.button(cancelTitle || mxResources.get('cancel'), function() - { - editorUi.hideDialog(); - - if (cancelFn != null) - { - cancelFn(); - } - }); - cancelBtn.className = 'geBtn'; - - if (editorUi.editor.cancelFirst) - { - td.appendChild(cancelBtn); - } - - if (addButtons != null) - { - addButtons(td, nameInput); - } - - if (fn != null) - { - var genericBtn = mxUtils.button(applyTitle || mxResources.get('apply'), function() - { - if (!noHide) - { - editorUi.hideDialog(); - } - - fn(nameInput.value); - }); - - genericBtn.className = 'geBtn gePrimaryBtn'; - td.appendChild(genericBtn); - } - - if (!editorUi.editor.cancelFirst) - { - td.appendChild(cancelBtn); - } - - row.appendChild(td); - tbody.appendChild(row); - table.appendChild(tbody); - this.container = table; -}; - -/** - * Constructs a new edit file dialog. - */ -var EditDiagramDialog = function(editorUi) -{ - var div = document.createElement('div'); - div.style.textAlign = 'right'; - var textarea = document.createElement('textarea'); - textarea.setAttribute('wrap', 'off'); - textarea.setAttribute('spellcheck', 'false'); - textarea.setAttribute('autocorrect', 'off'); - textarea.setAttribute('autocomplete', 'off'); - textarea.setAttribute('autocapitalize', 'off'); - textarea.style.overflow = 'auto'; - textarea.style.resize = 'none'; - textarea.style.width = '600px'; - textarea.style.height = '360px'; - textarea.style.marginBottom = '16px'; - - textarea.value = mxUtils.getPrettyXml(editorUi.editor.getGraphXml()); - div.appendChild(textarea); - - this.init = function() - { - textarea.focus(); - }; - - // Enables dropping files - if (Graph.fileSupport) - { - function handleDrop(evt) - { - evt.stopPropagation(); - evt.preventDefault(); - - if (evt.dataTransfer.files.length > 0) - { - var file = evt.dataTransfer.files[0]; - var reader = new FileReader(); - - reader.onload = function(e) - { - textarea.value = e.target.result; - }; - - reader.readAsText(file); - } - else - { - textarea.value = editorUi.extractGraphModelFromEvent(evt); - } - }; - - function handleDragOver(evt) - { - evt.stopPropagation(); - evt.preventDefault(); - }; - - // Setup the dnd listeners. - textarea.addEventListener('dragover', handleDragOver, false); - textarea.addEventListener('drop', handleDrop, false); - } - - var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() - { - editorUi.hideDialog(); - }); - cancelBtn.className = 'geBtn'; - - if (editorUi.editor.cancelFirst) - { - div.appendChild(cancelBtn); - } - - var select = document.createElement('select'); - select.style.width = '180px'; - select.className = 'geBtn'; - - if (editorUi.editor.graph.isEnabled()) - { - var replaceOption = document.createElement('option'); - replaceOption.setAttribute('value', 'replace'); - mxUtils.write(replaceOption, mxResources.get('replaceExistingDrawing')); - select.appendChild(replaceOption); - } - - var newOption = document.createElement('option'); - newOption.setAttribute('value', 'new'); - mxUtils.write(newOption, mxResources.get('openInNewWindow')); - - if (EditDiagramDialog.showNewWindowOption) - { - select.appendChild(newOption); - } - - if (editorUi.editor.graph.isEnabled()) - { - var importOption = document.createElement('option'); - importOption.setAttribute('value', 'import'); - mxUtils.write(importOption, mxResources.get('addToExistingDrawing')); - select.appendChild(importOption); - } - - div.appendChild(select); - - var okBtn = mxUtils.button(mxResources.get('ok'), function() - { - // Removes all illegal control characters before parsing - var data = editorUi.editor.graph.zapGremlins(mxUtils.trim(textarea.value)); - var error = null; - - if (select.value == 'new') - { - window.openFile = new OpenFile(function() - { - editorUi.hideDialog(); - window.openFile = null; - }); - - window.openFile.setData(data, null); - editorUi.editor.graph.openLink(editorUi.getUrl()); - } - else if (select.value == 'replace') - { - editorUi.editor.graph.model.beginUpdate(); - try - { - editorUi.editor.setGraphXml(mxUtils.parseXml(data).documentElement); - // LATER: Why is hideDialog between begin-/endUpdate faster? - editorUi.hideDialog(); - } - catch (e) - { - error = e; - } - finally - { - editorUi.editor.graph.model.endUpdate(); - } - } - else if (select.value == 'import') - { - editorUi.editor.graph.model.beginUpdate(); - try - { - var doc = mxUtils.parseXml(data); - var model = new mxGraphModel(); - var codec = new mxCodec(doc); - codec.decode(doc.documentElement, model); - - var children = model.getChildren(model.getChildAt(model.getRoot(), 0)); - editorUi.editor.graph.setSelectionCells(editorUi.editor.graph.importCells(children)); - - // LATER: Why is hideDialog between begin-/endUpdate faster? - editorUi.hideDialog(); - } - catch (e) - { - error = e; - } - finally - { - editorUi.editor.graph.model.endUpdate(); - } - } - - if (error != null) - { - mxUtils.alert(error.message); - } - }); - okBtn.className = 'geBtn gePrimaryBtn'; - div.appendChild(okBtn); - - if (!editorUi.editor.cancelFirst) - { - div.appendChild(cancelBtn); - } - - this.container = div; -}; - -/** - * - */ -EditDiagramDialog.showNewWindowOption = true; - -/** - * Constructs a new export dialog. - */ -var ExportDialog = function(editorUi) -{ - var graph = editorUi.editor.graph; - var bounds = graph.getGraphBounds(); - var scale = graph.view.scale; - - var width = Math.ceil(bounds.width / scale); - var height = Math.ceil(bounds.height / scale); - - var row, td; - - var table = document.createElement('table'); - var tbody = document.createElement('tbody'); - table.setAttribute('cellpadding', (mxClient.IS_SF) ? '0' : '2'); - - row = document.createElement('tr'); - - td = document.createElement('td'); - td.style.fontSize = '10pt'; - td.style.width = '100px'; - mxUtils.write(td, mxResources.get('filename') + ':'); - - row.appendChild(td); - - var nameInput = document.createElement('input'); - nameInput.setAttribute('value', editorUi.editor.getOrCreateFilename()); - nameInput.style.width = '180px'; - - td = document.createElement('td'); - td.appendChild(nameInput); - row.appendChild(td); - - tbody.appendChild(row); - - row = document.createElement('tr'); - - td = document.createElement('td'); - td.style.fontSize = '10pt'; - mxUtils.write(td, mxResources.get('format') + ':'); - - row.appendChild(td); - - var imageFormatSelect = document.createElement('select'); - imageFormatSelect.style.width = '180px'; - - var pngOption = document.createElement('option'); - pngOption.setAttribute('value', 'png'); - mxUtils.write(pngOption, mxResources.get('formatPng')); - imageFormatSelect.appendChild(pngOption); - - var gifOption = document.createElement('option'); - - if (ExportDialog.showGifOption) - { - gifOption.setAttribute('value', 'gif'); - mxUtils.write(gifOption, mxResources.get('formatGif')); - imageFormatSelect.appendChild(gifOption); - } - - var jpgOption = document.createElement('option'); - jpgOption.setAttribute('value', 'jpg'); - mxUtils.write(jpgOption, mxResources.get('formatJpg')); - imageFormatSelect.appendChild(jpgOption); - - var pdfOption = document.createElement('option'); - pdfOption.setAttribute('value', 'pdf'); - mxUtils.write(pdfOption, mxResources.get('formatPdf')); - imageFormatSelect.appendChild(pdfOption); - - var svgOption = document.createElement('option'); - svgOption.setAttribute('value', 'svg'); - mxUtils.write(svgOption, mxResources.get('formatSvg')); - imageFormatSelect.appendChild(svgOption); - - if (ExportDialog.showXmlOption) - { - var xmlOption = document.createElement('option'); - xmlOption.setAttribute('value', 'xml'); - mxUtils.write(xmlOption, mxResources.get('formatXml')); - imageFormatSelect.appendChild(xmlOption); - } - - td = document.createElement('td'); - td.appendChild(imageFormatSelect); - row.appendChild(td); - - tbody.appendChild(row); - - row = document.createElement('tr'); - - td = document.createElement('td'); - td.style.fontSize = '10pt'; - mxUtils.write(td, mxResources.get('zoom') + ' (%):'); - - row.appendChild(td); - - var zoomInput = document.createElement('input'); - zoomInput.setAttribute('type', 'number'); - zoomInput.setAttribute('value', '100'); - zoomInput.style.width = '180px'; - - td = document.createElement('td'); - td.appendChild(zoomInput); - row.appendChild(td); - - tbody.appendChild(row); - - row = document.createElement('tr'); - - td = document.createElement('td'); - td.style.fontSize = '10pt'; - mxUtils.write(td, mxResources.get('width') + ':'); - - row.appendChild(td); - - var widthInput = document.createElement('input'); - widthInput.setAttribute('value', width); - widthInput.style.width = '180px'; - - td = document.createElement('td'); - td.appendChild(widthInput); - row.appendChild(td); - - tbody.appendChild(row); - - row = document.createElement('tr'); - - td = document.createElement('td'); - td.style.fontSize = '10pt'; - mxUtils.write(td, mxResources.get('height') + ':'); - - row.appendChild(td); - - var heightInput = document.createElement('input'); - heightInput.setAttribute('value', height); - heightInput.style.width = '180px'; - - td = document.createElement('td'); - td.appendChild(heightInput); - row.appendChild(td); - - tbody.appendChild(row); - - row = document.createElement('tr'); - - td = document.createElement('td'); - td.style.fontSize = '10pt'; - mxUtils.write(td, mxResources.get('background') + ':'); - - row.appendChild(td); - - var transparentCheckbox = document.createElement('input'); - transparentCheckbox.setAttribute('type', 'checkbox'); - transparentCheckbox.checked = graph.background == null || graph.background == mxConstants.NONE; - - td = document.createElement('td'); - td.appendChild(transparentCheckbox); - mxUtils.write(td, mxResources.get('transparent')); - - row.appendChild(td); - - tbody.appendChild(row); - - row = document.createElement('tr'); - - td = document.createElement('td'); - td.style.fontSize = '10pt'; - mxUtils.write(td, mxResources.get('borderWidth') + ':'); - - row.appendChild(td); - - var borderInput = document.createElement('input'); - borderInput.setAttribute('type', 'number'); - borderInput.setAttribute('value', ExportDialog.lastBorderValue); - borderInput.style.width = '180px'; - - td = document.createElement('td'); - td.appendChild(borderInput); - row.appendChild(td); - - tbody.appendChild(row); - table.appendChild(tbody); - - // Handles changes in the export format - function formatChanged() - { - var name = nameInput.value; - var dot = name.lastIndexOf('.'); - - if (dot > 0) - { - nameInput.value = name.substring(0, dot + 1) + imageFormatSelect.value; - } - else - { - nameInput.value = name + '.' + imageFormatSelect.value; - } - - if (imageFormatSelect.value === 'xml') - { - zoomInput.setAttribute('disabled', 'true'); - widthInput.setAttribute('disabled', 'true'); - heightInput.setAttribute('disabled', 'true'); - borderInput.setAttribute('disabled', 'true'); - } - else - { - zoomInput.removeAttribute('disabled'); - widthInput.removeAttribute('disabled'); - heightInput.removeAttribute('disabled'); - borderInput.removeAttribute('disabled'); - } - - if (imageFormatSelect.value === 'png' || imageFormatSelect.value === 'svg') - { - transparentCheckbox.removeAttribute('disabled'); - } - else - { - transparentCheckbox.setAttribute('disabled', 'disabled'); - } - }; - - mxEvent.addListener(imageFormatSelect, 'change', formatChanged); - formatChanged(); - - function checkValues() - { - if (widthInput.value * heightInput.value > MAX_AREA || widthInput.value <= 0) - { - widthInput.style.backgroundColor = 'red'; - } - else - { - widthInput.style.backgroundColor = ''; - } - - if (widthInput.value * heightInput.value > MAX_AREA || heightInput.value <= 0) - { - heightInput.style.backgroundColor = 'red'; - } - else - { - heightInput.style.backgroundColor = ''; - } - }; - - mxEvent.addListener(zoomInput, 'change', function() - { - var s = Math.max(0, parseFloat(zoomInput.value) || 100) / 100; - zoomInput.value = parseFloat((s * 100).toFixed(2)); - - if (width > 0) - { - widthInput.value = Math.floor(width * s); - heightInput.value = Math.floor(height * s); - } - else - { - zoomInput.value = '100'; - widthInput.value = width; - heightInput.value = height; - } - - checkValues(); - }); - - mxEvent.addListener(widthInput, 'change', function() - { - var s = parseInt(widthInput.value) / width; - - if (s > 0) - { - zoomInput.value = parseFloat((s * 100).toFixed(2)); - heightInput.value = Math.floor(height * s); - } - else - { - zoomInput.value = '100'; - widthInput.value = width; - heightInput.value = height; - } - - checkValues(); - }); - - mxEvent.addListener(heightInput, 'change', function() - { - var s = parseInt(heightInput.value) / height; - - if (s > 0) - { - zoomInput.value = parseFloat((s * 100).toFixed(2)); - widthInput.value = Math.floor(width * s); - } - else - { - zoomInput.value = '100'; - widthInput.value = width; - heightInput.value = height; - } - - checkValues(); - }); - - row = document.createElement('tr'); - td = document.createElement('td'); - td.setAttribute('align', 'right'); - td.style.paddingTop = '22px'; - td.colSpan = 2; - - var saveBtn = mxUtils.button(mxResources.get('export'), mxUtils.bind(this, function() - { - if (parseInt(zoomInput.value) <= 0) - { - mxUtils.alert(mxResources.get('drawingEmpty')); - } - else - { - var name = nameInput.value; - var format = imageFormatSelect.value; - var s = Math.max(0, parseFloat(zoomInput.value) || 100) / 100; - var b = Math.max(0, parseInt(borderInput.value)); - var bg = graph.background; - - if ((format == 'svg' || format == 'png') && transparentCheckbox.checked) - { - bg = null; - } - else if (bg == null || bg == mxConstants.NONE) - { - bg = '#ffffff'; - } - - ExportDialog.lastBorderValue = b; - ExportDialog.exportFile(editorUi, name, format, bg, s, b); - } - })); - saveBtn.className = 'geBtn gePrimaryBtn'; - - var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() - { - editorUi.hideDialog(); - }); - cancelBtn.className = 'geBtn'; - - if (editorUi.editor.cancelFirst) - { - td.appendChild(cancelBtn); - td.appendChild(saveBtn); - } - else - { - td.appendChild(saveBtn); - td.appendChild(cancelBtn); - } - - row.appendChild(td); - tbody.appendChild(row); - table.appendChild(tbody); - this.container = table; -}; - -/** - * Remembers last value for border. - */ -ExportDialog.lastBorderValue = 0; - -/** - * Global switches for the export dialog. - */ -ExportDialog.showGifOption = true; - -/** - * Global switches for the export dialog. - */ -ExportDialog.showXmlOption = true; - -/** - * Hook for getting the export format. Returns null for the default - * intermediate XML export format or a function that returns the - * parameter and value to be used in the request in the form - * key=value, where value should be URL encoded. - */ -ExportDialog.exportFile = function(editorUi, name, format, bg, s, b) -{ - var graph = editorUi.editor.graph; - - if (format == 'xml') - { - ExportDialog.saveLocalFile(editorUi, mxUtils.getXml(editorUi.editor.getGraphXml()), name, format); - } - else if (format == 'svg') - { - ExportDialog.saveLocalFile(editorUi, mxUtils.getXml(graph.getSvg(bg, s, b)), name, format); - } - else - { - var bounds = graph.getGraphBounds(); - - // New image export - var xmlDoc = mxUtils.createXmlDocument(); - var root = xmlDoc.createElement('output'); - xmlDoc.appendChild(root); - - // Renders graph. Offset will be multiplied with state's scale when painting state. - var xmlCanvas = new mxXmlCanvas2D(root); - xmlCanvas.translate(Math.floor((b / s - bounds.x) / graph.view.scale), - Math.floor((b / s - bounds.y) / graph.view.scale)); - xmlCanvas.scale(s / graph.view.scale); - - var imgExport = new mxImageExport() - imgExport.drawState(graph.getView().getState(graph.model.root), xmlCanvas); - - // Puts request data together - var param = 'xml=' + encodeURIComponent(mxUtils.getXml(root)); - var w = Math.ceil(bounds.width * s / graph.view.scale + 2 * b); - var h = Math.ceil(bounds.height * s / graph.view.scale + 2 * b); - - // Requests image if request is valid - if (param.length <= MAX_REQUEST_SIZE && w * h < MAX_AREA) - { - editorUi.hideDialog(); - var req = new mxXmlRequest(EXPORT_URL, 'format=' + format + - '&filename=' + encodeURIComponent(name) + - '&bg=' + ((bg != null) ? bg : 'none') + - '&w=' + w + '&h=' + h + '&' + param); - req.simulate(document, '_blank'); - } - else - { - mxUtils.alert(mxResources.get('drawingTooLarge')); - } - } -}; - -/** - * Hook for getting the export format. Returns null for the default - * intermediate XML export format or a function that returns the - * parameter and value to be used in the request in the form - * key=value, where value should be URL encoded. - */ -ExportDialog.saveLocalFile = function(editorUi, data, filename, format) -{ - if (data.length < MAX_REQUEST_SIZE) - { - editorUi.hideDialog(); - var req = new mxXmlRequest(SAVE_URL, 'xml=' + encodeURIComponent(data) + '&filename=' + - encodeURIComponent(filename) + '&format=' + format); - req.simulate(document, '_blank'); - } - else - { - mxUtils.alert(mxResources.get('drawingTooLarge')); - mxUtils.popup(xml); - } -}; - -/** - * Constructs a new metadata dialog. - */ -var EditDataDialog = function(ui, cell) -{ - var div = document.createElement('div'); - var graph = ui.editor.graph; - - var value = graph.getModel().getValue(cell); - - // Converts the value to an XML node - if (!mxUtils.isNode(value)) - { - var doc = mxUtils.createXmlDocument(); - var obj = doc.createElement('object'); - obj.setAttribute('label', value || ''); - value = obj; - } - - // Creates the dialog contents - var form = new mxForm('properties'); - form.table.style.width = '100%'; - - var attrs = value.attributes; - var names = []; - var texts = []; - var count = 0; - - var id = EditDataDialog.getDisplayIdForCell(ui, cell); - - // FIXME: Fix remove button for quirks mode - var addRemoveButton = function(text, name) - { - var wrapper = document.createElement('div'); - wrapper.style.position = 'relative'; - wrapper.style.paddingRight = '20px'; - wrapper.style.boxSizing = 'border-box'; - wrapper.style.width = '100%'; - - var removeAttr = document.createElement('a'); - var img = mxUtils.createImage(Dialog.prototype.closeImage); - img.style.height = '9px'; - img.style.fontSize = '9px'; - img.style.marginBottom = (mxClient.IS_IE11) ? '-1px' : '5px'; - - removeAttr.className = 'geButton'; - removeAttr.setAttribute('title', mxResources.get('delete')); - removeAttr.style.position = 'absolute'; - removeAttr.style.top = '4px'; - removeAttr.style.right = '0px'; - removeAttr.style.margin = '0px'; - removeAttr.style.width = '9px'; - removeAttr.style.height = '9px'; - removeAttr.style.cursor = 'pointer'; - removeAttr.appendChild(img); - - var removeAttrFn = (function(name) - { - return function() - { - var count = 0; - - for (var j = 0; j < names.length; j++) - { - if (names[j] == name) - { - texts[j] = null; - form.table.deleteRow(count + ((id != null) ? 1 : 0)); - - break; - } - - if (texts[j] != null) - { - count++; - } - } - }; - })(name); - - mxEvent.addListener(removeAttr, 'click', removeAttrFn); - - var parent = text.parentNode; - wrapper.appendChild(text); - wrapper.appendChild(removeAttr); - parent.appendChild(wrapper); - }; - - var addTextArea = function(index, name, value) - { - names[index] = name; - texts[index] = form.addTextarea(names[count] + ':', value, 2); - texts[index].style.width = '100%'; - - addRemoveButton(texts[index], name); - }; - - var temp = []; - var isLayer = graph.getModel().getParent(cell) == graph.getModel().getRoot(); - - for (var i = 0; i < attrs.length; i++) - { - if ((isLayer || attrs[i].nodeName != 'label') && attrs[i].nodeName != 'placeholders') - { - temp.push({name: attrs[i].nodeName, value: attrs[i].nodeValue}); - } - } - - // Sorts by name - temp.sort(function(a, b) - { - if (a.name < b.name) - { - return -1; - } - else if (a.name > b.name) - { - return 1; - } - else - { - return 0; - } - }); - - if (id != null) - { - var text = document.createElement('input'); - text.style.width = '280px'; - text.style.textAlign = 'center'; - text.setAttribute('type', 'text'); - text.setAttribute('readOnly', 'true'); - text.setAttribute('value', id); - - form.addField(mxResources.get('id') + ':', text); - } - - for (var i = 0; i < temp.length; i++) - { - addTextArea(count, temp[i].name, temp[i].value); - count++; - } - - var top = document.createElement('div'); - top.style.cssText = 'position:absolute;left:30px;right:30px;overflow-y:auto;top:30px;bottom:80px;'; - top.appendChild(form.table); - - var newProp = document.createElement('div'); - newProp.style.whiteSpace = 'nowrap'; - newProp.style.marginTop = '6px'; - - var nameInput = document.createElement('input'); - nameInput.setAttribute('placeholder', mxResources.get('enterPropertyName')); - nameInput.setAttribute('type', 'text'); - nameInput.setAttribute('size', (mxClient.IS_IE || mxClient.IS_IE11) ? '18' : '22'); - nameInput.style.marginLeft = '2px'; - - newProp.appendChild(nameInput); - top.appendChild(newProp); - div.appendChild(top); - - var addBtn = mxUtils.button(mxResources.get('addProperty'), function() - { - var name = nameInput.value; - - // Avoid ':' in attribute names which seems to be valid in Chrome - if (name.length > 0 && name != 'label' && name != 'placeholders' && name.indexOf(':') < 0) - { - try - { - var idx = mxUtils.indexOf(names, name); - - if (idx >= 0 && texts[idx] != null) - { - texts[idx].focus(); - } - else - { - // Checks if the name is valid - var clone = value.cloneNode(false); - clone.setAttribute(name, ''); - - if (idx >= 0) - { - names.splice(idx, 1); - texts.splice(idx, 1); - } - - names.push(name); - var text = form.addTextarea(name + ':', '', 2); - text.style.width = '100%'; - texts.push(text); - addRemoveButton(text, name); - - text.focus(); - } - - nameInput.value = ''; - } - catch (e) - { - mxUtils.alert(e); - } - } - else - { - mxUtils.alert(mxResources.get('invalidName')); - } - }); - - this.init = function() - { - if (texts.length > 0) - { - texts[0].focus(); - } - else - { - nameInput.focus(); - } - }; - - addBtn.setAttribute('disabled', 'disabled'); - addBtn.style.marginLeft = '10px'; - addBtn.style.width = '144px'; - newProp.appendChild(addBtn); - - var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() - { - ui.hideDialog.apply(ui, arguments); - }); - - cancelBtn.className = 'geBtn'; - - var applyBtn = mxUtils.button(mxResources.get('apply'), function() - { - try - { - ui.hideDialog.apply(ui, arguments); - - // Clones and updates the value - value = value.cloneNode(true); - var removeLabel = false; - - for (var i = 0; i < names.length; i++) - { - if (texts[i] == null) - { - value.removeAttribute(names[i]); - } - else - { - value.setAttribute(names[i], texts[i].value); - removeLabel = removeLabel || (names[i] == 'placeholder' && - value.getAttribute('placeholders') == '1'); - } - } - - // Removes label if placeholder is assigned - if (removeLabel) - { - value.removeAttribute('label'); - } - - // Updates the value of the cell (undoable) - graph.getModel().setValue(cell, value); - } - catch (e) - { - mxUtils.alert(e); - } - }); - applyBtn.className = 'geBtn gePrimaryBtn'; - - function updateAddBtn() - { - if (nameInput.value.length > 0) - { - addBtn.removeAttribute('disabled'); - } - else - { - addBtn.setAttribute('disabled', 'disabled'); - } - }; - - mxEvent.addListener(nameInput, 'keyup', updateAddBtn); - - // Catches all changes that don't fire a keyup (such as paste via mouse) - mxEvent.addListener(nameInput, 'change', updateAddBtn); - - var buttons = document.createElement('div'); - buttons.style.cssText = 'position:absolute;left:30px;right:30px;text-align:right;bottom:30px;height:40px;' - - if (ui.editor.graph.getModel().isVertex(cell) || ui.editor.graph.getModel().isEdge(cell)) - { - var replace = document.createElement('span'); - replace.style.marginRight = '10px'; - var input = document.createElement('input'); - input.setAttribute('type', 'checkbox'); - input.style.marginRight = '6px'; - - if (value.getAttribute('placeholders') == '1') - { - input.setAttribute('checked', 'checked'); - input.defaultChecked = true; - } - - mxEvent.addListener(input, 'click', function() - { - if (value.getAttribute('placeholders') == '1') - { - value.removeAttribute('placeholders'); - } - else - { - value.setAttribute('placeholders', '1'); - } - }); - - replace.appendChild(input); - mxUtils.write(replace, mxResources.get('placeholders')); - - if (EditDataDialog.placeholderHelpLink != null) - { - var link = document.createElement('a'); - link.setAttribute('href', EditDataDialog.placeholderHelpLink); - link.setAttribute('title', mxResources.get('help')); - link.setAttribute('target', '_blank'); - link.style.marginLeft = '10px'; - link.style.cursor = 'help'; - - var icon = document.createElement('img'); - icon.setAttribute('border', '0'); - icon.setAttribute('valign', 'middle'); - icon.style.marginTop = (mxClient.IS_IE11) ? '0px' : '-4px'; - icon.setAttribute('src', Editor.helpImage); - link.appendChild(icon); - - replace.appendChild(link); - } - - buttons.appendChild(replace); - } - - if (ui.editor.cancelFirst) - { - buttons.appendChild(cancelBtn); - buttons.appendChild(applyBtn); - } - else - { - buttons.appendChild(applyBtn); - buttons.appendChild(cancelBtn); - } - - div.appendChild(buttons); - this.container = div; -}; - -/** - * Optional help link. - */ -EditDataDialog.getDisplayIdForCell = function(ui, cell) -{ - var id = null; - - if (ui.editor.graph.getModel().getParent(cell) != null) - { - id = cell.getId(); - } - - return id; -}; - -/** - * Optional help link. - */ -EditDataDialog.placeholderHelpLink = null; - -/** - * Constructs a new link dialog. - */ -var LinkDialog = function(editorUi, initialValue, btnLabel, fn) -{ - var div = document.createElement('div'); - mxUtils.write(div, mxResources.get('editLink') + ':'); - - var inner = document.createElement('div'); - inner.className = 'geTitle'; - inner.style.backgroundColor = 'transparent'; - inner.style.borderColor = 'transparent'; - inner.style.whiteSpace = 'nowrap'; - inner.style.textOverflow = 'clip'; - inner.style.cursor = 'default'; - - if (!mxClient.IS_VML) - { - inner.style.paddingRight = '20px'; - } - - var linkInput = document.createElement('input'); - linkInput.setAttribute('value', initialValue); - linkInput.setAttribute('placeholder', 'http://www.example.com/'); - linkInput.setAttribute('type', 'text'); - linkInput.style.marginTop = '6px'; - linkInput.style.width = '400px'; - linkInput.style.backgroundImage = 'url(\'' + Dialog.prototype.clearImage + '\')'; - linkInput.style.backgroundRepeat = 'no-repeat'; - linkInput.style.backgroundPosition = '100% 50%'; - linkInput.style.paddingRight = '14px'; - - var cross = document.createElement('div'); - cross.setAttribute('title', mxResources.get('reset')); - cross.style.position = 'relative'; - cross.style.left = '-16px'; - cross.style.width = '12px'; - cross.style.height = '14px'; - cross.style.cursor = 'pointer'; - - // Workaround for inline-block not supported in IE - cross.style.display = (mxClient.IS_VML) ? 'inline' : 'inline-block'; - cross.style.top = ((mxClient.IS_VML) ? 0 : 3) + 'px'; - - // Needed to block event transparency in IE - cross.style.background = 'url(' + IMAGE_PATH + '/transparent.gif)'; - - mxEvent.addListener(cross, 'click', function() - { - linkInput.value = ''; - linkInput.focus(); - }); - - inner.appendChild(linkInput); - inner.appendChild(cross); - div.appendChild(inner); - - this.init = function() - { - linkInput.focus(); - - if (mxClient.IS_GC || mxClient.IS_FF || document.documentMode >= 5 || mxClient.IS_QUIRKS) - { - linkInput.select(); - } - else - { - document.execCommand('selectAll', false, null); - } - }; - - var btns = document.createElement('div'); - btns.style.marginTop = '18px'; - btns.style.textAlign = 'right'; - - mxEvent.addListener(linkInput, 'keypress', function(e) - { - if (e.keyCode == 13) - { - editorUi.hideDialog(); - fn(linkInput.value); - } - }); - - var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() - { - editorUi.hideDialog(); - }); - cancelBtn.className = 'geBtn'; - - if (editorUi.editor.cancelFirst) - { - btns.appendChild(cancelBtn); - } - - var mainBtn = mxUtils.button(btnLabel, function() - { - editorUi.hideDialog(); - fn(linkInput.value); - }); - mainBtn.className = 'geBtn gePrimaryBtn'; - btns.appendChild(mainBtn); - - if (!editorUi.editor.cancelFirst) - { - btns.appendChild(cancelBtn); - } - - div.appendChild(btns); - - this.container = div; -}; - -/** - * - */ -var OutlineWindow = function(editorUi, x, y, w, h) -{ - var graph = editorUi.editor.graph; - - var div = document.createElement('div'); - div.style.position = 'absolute'; - div.style.width = '100%'; - div.style.height = '100%'; - div.style.border = '1px solid whiteSmoke'; - div.style.overflow = 'hidden'; - - this.window = new mxWindow(mxResources.get('outline'), div, x, y, w, h, true, true); - this.window.minimumSize = new mxRectangle(0, 0, 80, 80); - this.window.destroyOnClose = false; - this.window.setMaximizable(false); - this.window.setResizable(true); - this.window.setClosable(true); - this.window.setVisible(true); - - this.window.setLocation = function(x, y) - { - var iw = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; - var ih = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; - - x = Math.max(0, Math.min(x, iw - this.table.clientWidth)); - y = Math.max(0, Math.min(y, ih - this.table.clientHeight - 48)); - - if (this.getX() != x || this.getY() != y) - { - mxWindow.prototype.setLocation.apply(this, arguments); - } - }; - - var resizeListener = mxUtils.bind(this, function() - { - var x = this.window.getX(); - var y = this.window.getY(); - - this.window.setLocation(x, y); - }); - - mxEvent.addListener(window, 'resize', resizeListener); - - var outline = editorUi.createOutline(this.window); - - this.destroy = function() - { - mxEvent.removeListener(window, 'resize', resizeListener); - this.window.destroy(); - outline.destroy(); - } - - this.window.addListener(mxEvent.RESIZE, mxUtils.bind(this, function() - { - outline.update(false); - outline.outline.sizeDidChange(); - })); - - this.window.addListener(mxEvent.SHOW, mxUtils.bind(this, function() - { - outline.suspended = false; - outline.outline.refresh(); - outline.update(); - })); - - this.window.addListener(mxEvent.HIDE, mxUtils.bind(this, function() - { - outline.suspended = true; - })); - - this.window.addListener(mxEvent.NORMALIZE, mxUtils.bind(this, function() - { - outline.suspended = false; - outline.update(); - })); - - this.window.addListener(mxEvent.MINIMIZE, mxUtils.bind(this, function() - { - outline.suspended = true; - })); - - var outlineCreateGraph = outline.createGraph; - outline.createGraph = function(container) - { - var g = outlineCreateGraph.apply(this, arguments); - g.gridEnabled = false; - g.pageScale = graph.pageScale; - g.pageFormat = graph.pageFormat; - g.background = (graph.background == null || graph.background == mxConstants.NONE) ? graph.defaultPageBackgroundColor : graph.background; - g.pageVisible = graph.pageVisible; - - var current = mxUtils.getCurrentStyle(graph.container); - div.style.backgroundColor = current.backgroundColor; - - return g; - }; - - function update() - { - outline.outline.pageScale = graph.pageScale; - outline.outline.pageFormat = graph.pageFormat; - outline.outline.pageVisible = graph.pageVisible; - outline.outline.background = (graph.background == null || graph.background == mxConstants.NONE) ? graph.defaultPageBackgroundColor : graph.background;; - - var current = mxUtils.getCurrentStyle(graph.container); - div.style.backgroundColor = current.backgroundColor; - - if (graph.view.backgroundPageShape != null && outline.outline.view.backgroundPageShape != null) - { - outline.outline.view.backgroundPageShape.fill = graph.view.backgroundPageShape.fill; - } - - outline.outline.refresh(); - }; - - outline.init(div); - - editorUi.editor.addListener('resetGraphView', update); - editorUi.addListener('pageFormatChanged', update); - editorUi.addListener('backgroundColorChanged', update); - editorUi.addListener('backgroundImageChanged', update); - editorUi.addListener('pageViewChanged', function() - { - update(); - outline.update(true); - }); - - if (outline.outline.dialect == mxConstants.DIALECT_SVG) - { - var zoomInAction = editorUi.actions.get('zoomIn'); - var zoomOutAction = editorUi.actions.get('zoomOut'); - - mxEvent.addMouseWheelListener(function(evt, up) - { - var outlineWheel = false; - var source = mxEvent.getSource(evt); - - while (source != null) - { - if (source == outline.outline.view.canvas.ownerSVGElement) - { - outlineWheel = true; - break; - } - - source = source.parentNode; - } - - if (outlineWheel) - { - if (up) - { - zoomInAction.funct(); - } - else - { - zoomOutAction.funct(); - } - - mxEvent.consume(evt); - } - }); - } -}; - -/** - * - */ -var LayersWindow = function(editorUi, x, y, w, h) -{ - var graph = editorUi.editor.graph; - - var div = document.createElement('div'); - div.style.userSelect = 'none'; - div.style.background = (Dialog.backdropColor == 'white') ? 'whiteSmoke' : Dialog.backdropColor; - div.style.border = '1px solid whiteSmoke'; - div.style.height = '100%'; - div.style.marginBottom = '10px'; - div.style.overflow = 'auto'; - - var tbarHeight = (!EditorUi.compactUi) ? '30px' : '26px'; - - var listDiv = document.createElement('div') - listDiv.style.backgroundColor = (Dialog.backdropColor == 'white') ? '#dcdcdc' : '#e5e5e5'; - listDiv.style.position = 'absolute'; - listDiv.style.overflow = 'auto'; - listDiv.style.left = '0px'; - listDiv.style.right = '0px'; - listDiv.style.top = '0px'; - listDiv.style.bottom = (parseInt(tbarHeight) + 7) + 'px'; - div.appendChild(listDiv); - - var dragSource = null; - var dropIndex = null; - - mxEvent.addListener(div, 'dragover', function(evt) - { - evt.dataTransfer.dropEffect = 'move'; - dropIndex = 0; - evt.stopPropagation(); - evt.preventDefault(); - }); - - // Workaround for "no element found" error in FF - mxEvent.addListener(div, 'drop', function(evt) - { - evt.stopPropagation(); - evt.preventDefault(); - }); - - var layerCount = null; - var selectionLayer = null; - - var ldiv = document.createElement('div'); - - ldiv.className = 'geToolbarContainer'; - ldiv.style.position = 'absolute'; - ldiv.style.bottom = '0px'; - ldiv.style.left = '0px'; - ldiv.style.right = '0px'; - ldiv.style.height = tbarHeight; - ldiv.style.overflow = 'hidden'; - ldiv.style.padding = (!EditorUi.compactUi) ? '1px' : '4px 0px 3px 0px'; - ldiv.style.backgroundColor = (Dialog.backdropColor == 'white') ? 'whiteSmoke' : Dialog.backdropColor; - ldiv.style.borderWidth = '1px 0px 0px 0px'; - ldiv.style.borderColor = '#c3c3c3'; - ldiv.style.borderStyle = 'solid'; - ldiv.style.display = 'block'; - ldiv.style.whiteSpace = 'nowrap'; - - if (mxClient.IS_QUIRKS) - { - ldiv.style.filter = 'none'; - } - - var link = document.createElement('a'); - link.className = 'geButton'; - - if (mxClient.IS_QUIRKS) - { - link.style.filter = 'none'; - } - - var removeLink = link.cloneNode(); - removeLink.innerHTML = '
'; - - mxEvent.addListener(removeLink, 'click', function(evt) - { - if (graph.isEnabled()) - { - graph.model.beginUpdate(); - try - { - var index = graph.model.root.getIndex(selectionLayer); - graph.removeCells([selectionLayer], false); - - // Creates default layer if no layer exists - if (graph.model.getChildCount(graph.model.root) == 0) - { - graph.model.add(graph.model.root, new mxCell()); - graph.setDefaultParent(null); - } - else if (index > 0 && index <= graph.model.getChildCount(graph.model.root)) - { - graph.setDefaultParent(graph.model.getChildAt(graph.model.root, index - 1)); - } - else - { - graph.setDefaultParent(null); - } - } - finally - { - graph.model.endUpdate(); - } - } - - mxEvent.consume(evt); - }); - - if (!graph.isEnabled()) - { - removeLink.className = 'geButton mxDisabled'; - } - - ldiv.appendChild(removeLink); - - var insertLink = link.cloneNode(); - insertLink.innerHTML = '
'; - - mxEvent.addListener(insertLink, 'click', function(evt) - { - if (graph.isEnabled() && !graph.isSelectionEmpty()) - { - graph.moveCells(graph.getSelectionCells(), 0, 0, false, selectionLayer); - } - }); - - ldiv.appendChild(insertLink); - - var dataLink = link.cloneNode(); - dataLink.innerHTML = '
'; - dataLink.setAttribute('title', mxResources.get('rename')); - - mxEvent.addListener(dataLink, 'click', function(evt) - { - if (graph.isEnabled()) - { - editorUi.showDataDialog(selectionLayer); - } - - mxEvent.consume(evt); - }); - - if (!graph.isEnabled()) - { - dataLink.className = 'geButton mxDisabled'; - } - - ldiv.appendChild(dataLink); - - function renameLayer(layer) - { - if (graph.isEnabled() && layer != null) - { - var label = graph.convertValueToString(layer); - var dlg = new FilenameDialog(editorUi, label || mxResources.get('background'), mxResources.get('rename'), mxUtils.bind(this, function(newValue) - { - if (newValue != null) - { - graph.cellLabelChanged(layer, newValue); - } - }), mxResources.get('enterName')); - editorUi.showDialog(dlg.container, 300, 100, true, true); - dlg.init(); - } - }; - - var duplicateLink = link.cloneNode(); - duplicateLink.innerHTML = '
'; - - mxEvent.addListener(duplicateLink, 'click', function(evt) - { - if (graph.isEnabled()) - { - var newCell = null; - graph.model.beginUpdate(); - try - { - newCell = graph.cloneCells([selectionLayer])[0]; - graph.cellLabelChanged(newCell, mxResources.get('untitledLayer')); - newCell.setVisible(true); - newCell = graph.addCell(newCell, graph.model.root); - graph.setDefaultParent(newCell); - } - finally - { - graph.model.endUpdate(); - } - - if (newCell != null && !graph.isCellLocked(newCell)) - { - graph.selectAll(newCell); - } - } - }); - - if (!graph.isEnabled()) - { - duplicateLink.className = 'geButton mxDisabled'; - } - - ldiv.appendChild(duplicateLink); - - var addLink = link.cloneNode(); - addLink.innerHTML = '
'; - addLink.setAttribute('title', mxResources.get('addLayer')); - - mxEvent.addListener(addLink, 'click', function(evt) - { - if (graph.isEnabled()) - { - graph.model.beginUpdate(); - - try - { - var cell = graph.addCell(new mxCell(mxResources.get('untitledLayer')), graph.model.root); - graph.setDefaultParent(cell); - } - finally - { - graph.model.endUpdate(); - } - } - - mxEvent.consume(evt); - }); - - if (!graph.isEnabled()) - { - addLink.className = 'geButton mxDisabled'; - } - - ldiv.appendChild(addLink); - - div.appendChild(ldiv); - - function refresh() - { - layerCount = graph.model.getChildCount(graph.model.root) - listDiv.innerHTML = ''; - - function addLayer(index, label, child, defaultParent) - { - var ldiv = document.createElement('div'); - ldiv.className = 'geToolbarContainer'; - - ldiv.style.overflow = 'hidden'; - ldiv.style.position = 'relative'; - ldiv.style.padding = '4px'; - ldiv.style.height = '22px'; - ldiv.style.display = 'block'; - ldiv.style.backgroundColor = 'whiteSmoke'; - ldiv.style.borderWidth = '0px 0px 1px 0px'; - ldiv.style.borderColor = '#c3c3c3'; - ldiv.style.borderStyle = 'solid'; - ldiv.style.whiteSpace = 'nowrap'; - ldiv.setAttribute('title', label); - - var left = document.createElement('div'); - left.style.display = 'inline-block'; - left.style.width = '100%'; - left.style.textOverflow = 'ellipsis'; - left.style.overflow = 'hidden'; - - mxEvent.addListener(ldiv, 'dragover', function(evt) - { - evt.dataTransfer.dropEffect = 'move'; - dropIndex = index; - evt.stopPropagation(); - evt.preventDefault(); - }); - - mxEvent.addListener(ldiv, 'dragstart', function(evt) - { - dragSource = ldiv; - - // Workaround for no DnD on DIV in FF - if (mxClient.IS_FF) - { - // LATER: Check what triggers a parse as XML on this in FF after drop - evt.dataTransfer.setData('Text', ''); - } - }); - - mxEvent.addListener(ldiv, 'dragend', function(evt) - { - if (dragSource != null && dropIndex != null) - { - graph.addCell(child, graph.model.root, dropIndex); - } - - dragSource = null; - dropIndex = null; - evt.stopPropagation(); - evt.preventDefault(); - }); - - var btn = document.createElement('img'); - btn.setAttribute('draggable', 'false'); - btn.setAttribute('align', 'top'); - btn.setAttribute('border', '0'); - btn.style.padding = '4px'; - btn.setAttribute('title', mxResources.get('lockUnlock')); - - var state = graph.view.getState(child); - var style = (state != null) ? state.style : graph.getCellStyle(child); - - if (mxUtils.getValue(style, 'locked', '0') == '1') - { - btn.setAttribute('src', Dialog.prototype.lockedImage); - } - else - { - btn.setAttribute('src', Dialog.prototype.unlockedImage); - } - - if (graph.isEnabled()) - { - btn.style.cursor = 'pointer'; - } - - mxEvent.addListener(btn, 'click', function(evt) - { - if (graph.isEnabled()) - { - var value = null; - - graph.getModel().beginUpdate(); - try - { - value = (mxUtils.getValue(style, 'locked', '0') == '1') ? null : '1'; - graph.setCellStyles('locked', value, [child]); - } - finally - { - graph.getModel().endUpdate(); - } - - if (value == '1') - { - graph.removeSelectionCells(graph.getModel().getDescendants(child)); - } - - mxEvent.consume(evt); - } - }); - - left.appendChild(btn); - - var inp = document.createElement('input'); - inp.setAttribute('type', 'checkbox'); - inp.setAttribute('title', mxResources.get('hideIt', [child.value || mxResources.get('background')])); - inp.style.marginLeft = '4px'; - inp.style.marginRight = '6px'; - inp.style.marginTop = '4px'; - left.appendChild(inp); - - if (graph.model.isVisible(child)) - { - inp.setAttribute('checked', 'checked'); - inp.defaultChecked = true; - } - - mxEvent.addListener(inp, 'click', function(evt) - { - graph.model.setVisible(child, !graph.model.isVisible(child)); - mxEvent.consume(evt); - }); - - mxUtils.write(left, label); - ldiv.appendChild(left); - - if (graph.isEnabled()) - { - // Fallback if no drag and drop is available - if (mxClient.IS_TOUCH || mxClient.IS_POINTER || mxClient.IS_VML || - (mxClient.IS_IE && document.documentMode < 10)) - { - var right = document.createElement('div'); - right.style.display = 'block'; - right.style.textAlign = 'right'; - right.style.whiteSpace = 'nowrap'; - right.style.position = 'absolute'; - right.style.right = '6px'; - right.style.top = '6px'; - - // Poor man's change layer order - if (index > 0) - { - var img2 = document.createElement('a'); - - img2.setAttribute('title', mxResources.get('toBack')); - - img2.className = 'geButton'; - img2.style.cssFloat = 'none'; - img2.innerHTML = '▼'; - img2.style.width = '14px'; - img2.style.height = '14px'; - img2.style.fontSize = '14px'; - img2.style.margin = '0px'; - img2.style.marginTop = '-1px'; - right.appendChild(img2); - - mxEvent.addListener(img2, 'click', function(evt) - { - if (graph.isEnabled()) - { - graph.addCell(child, graph.model.root, index - 1); - } - - mxEvent.consume(evt); - }); - } - - if (index >= 0 && index < layerCount - 1) - { - var img1 = document.createElement('a'); - - img1.setAttribute('title', mxResources.get('toFront')); - - img1.className = 'geButton'; - img1.style.cssFloat = 'none'; - img1.innerHTML = '▲'; - img1.style.width = '14px'; - img1.style.height = '14px'; - img1.style.fontSize = '14px'; - img1.style.margin = '0px'; - img1.style.marginTop = '-1px'; - right.appendChild(img1); - - mxEvent.addListener(img1, 'click', function(evt) - { - if (graph.isEnabled()) - { - graph.addCell(child, graph.model.root, index + 1); - } - - mxEvent.consume(evt); - }); - } - - ldiv.appendChild(right); - } - - if (mxClient.IS_SVG && (!mxClient.IS_IE || document.documentMode >= 10)) - { - ldiv.setAttribute('draggable', 'true'); - ldiv.style.cursor = 'move'; - } - } - - mxEvent.addListener(ldiv, 'dblclick', function(evt) - { - var nodeName = mxEvent.getSource(evt).nodeName; - - if (nodeName != 'INPUT' && nodeName != 'IMG') - { - renameLayer(child); - mxEvent.consume(evt); - } - }); - - if (graph.getDefaultParent() == child) - { - ldiv.style.background = '#e6eff8'; - ldiv.style.fontWeight = (graph.isEnabled()) ? 'bold' : ''; - selectionLayer = child; - } - else - { - mxEvent.addListener(ldiv, 'click', function(evt) - { - if (graph.isEnabled()) - { - graph.setDefaultParent(defaultParent); - graph.view.setCurrentRoot(null); - refresh(); - } - }); - } - - listDiv.appendChild(ldiv); - }; - - // Cannot be moved or deleted - for (var i = layerCount - 1; i >= 0; i--) - { - (mxUtils.bind(this, function(child) - { - addLayer(i, graph.convertValueToString(child) || - mxResources.get('background'), child, child); - }))(graph.model.getChildAt(graph.model.root, i)); - } - - var label = graph.convertValueToString(selectionLayer) || mxResources.get('background'); - removeLink.setAttribute('title', mxResources.get('removeIt', [label])); - insertLink.setAttribute('title', mxResources.get('moveSelectionTo', [label])); - duplicateLink.setAttribute('title', mxResources.get('duplicateIt', [label])); - dataLink.setAttribute('title', mxResources.get('editData')); - - if (graph.isSelectionEmpty()) - { - insertLink.className = 'geButton mxDisabled'; - } - }; - - refresh(); - graph.model.addListener(mxEvent.CHANGE, function() - { - refresh(); - }); - - graph.selectionModel.addListener(mxEvent.CHANGE, function() - { - if (graph.isSelectionEmpty()) - { - insertLink.className = 'geButton mxDisabled'; - } - else - { - insertLink.className = 'geButton'; - } - }); - - this.window = new mxWindow(mxResources.get('layers'), div, x, y, w, h, true, true); - this.window.minimumSize = new mxRectangle(0, 0, 120, 120); - this.window.destroyOnClose = false; - this.window.setMaximizable(false); - this.window.setResizable(true); - this.window.setClosable(true); - this.window.setVisible(true); - - // Make refresh available via instance - this.refreshLayers = refresh; - - this.window.setLocation = function(x, y) - { - var iw = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; - var ih = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight; - - x = Math.max(0, Math.min(x, iw - this.table.clientWidth)); - y = Math.max(0, Math.min(y, ih - this.table.clientHeight - 48)); - - if (this.getX() != x || this.getY() != y) - { - mxWindow.prototype.setLocation.apply(this, arguments); - } - }; - - var resizeListener = mxUtils.bind(this, function() - { - var x = this.window.getX(); - var y = this.window.getY(); - - this.window.setLocation(x, y); - }); - - mxEvent.addListener(window, 'resize', resizeListener); - - this.destroy = function() - { - mxEvent.removeListener(window, 'resize', resizeListener); - this.window.destroy(); - } -}; diff --git a/media/grapheditor/js/Editor.js b/media/grapheditor/js/Editor.js deleted file mode 100644 index 3205f94346..0000000000 --- a/media/grapheditor/js/Editor.js +++ /dev/null @@ -1,2252 +0,0 @@ -/** - * Copyright (c) 2006-2012, JGraph Ltd - */ -/** - * Editor constructor executed on page load. - */ -Editor = function(chromeless, themes, model, graph, editable) -{ - mxEventSource.call(this); - this.chromeless = (chromeless != null) ? chromeless : this.chromeless; - this.initStencilRegistry(); - this.graph = graph || this.createGraph(themes, model); - this.editable = (editable != null) ? editable : !chromeless; - this.undoManager = this.createUndoManager(); - this.status = ''; - - this.getOrCreateFilename = function() - { - return this.filename || mxResources.get('drawing', [Editor.pageCounter]) + '.xml'; - }; - - this.getFilename = function() - { - return this.filename; - }; - - // Sets the status and fires a statusChanged event - this.setStatus = function(value) - { - this.status = value; - this.fireEvent(new mxEventObject('statusChanged')); - }; - - // Returns the current status - this.getStatus = function() - { - return this.status; - }; - - // Updates modified state if graph changes - this.graphChangeListener = function(sender, eventObject) - { - var edit = (eventObject != null) ? eventObject.getProperty('edit') : null; - - if (edit == null || !edit.ignoreEdit) - { - this.setModified(true); - } - }; - - this.graph.getModel().addListener(mxEvent.CHANGE, mxUtils.bind(this, function() - { - this.graphChangeListener.apply(this, arguments); - })); - - // Sets persistent graph state defaults - this.graph.resetViewOnRootChange = false; - this.init(); -}; - -/** - * Counts open editor tabs (must be global for cross-window access) - */ -Editor.pageCounter = 0; - -// Cross-domain window access is not allowed in FF, so if we -// were opened from another domain then this will fail. -(function() -{ - try - { - var op = window; - - while (op.opener != null && typeof op.opener.Editor !== 'undefined' && - !isNaN(op.opener.Editor.pageCounter) && - // Workaround for possible infinite loop in FF https://drawio.atlassian.net/browse/DS-795 - op.opener != op) - { - op = op.opener; - } - - // Increments the counter in the first opener in the chain - if (op != null) - { - op.Editor.pageCounter++; - Editor.pageCounter = op.Editor.pageCounter; - } - } - catch (e) - { - // ignore - } -})(); - -/** - * Specifies if local storage should be used (eg. on the iPad which has no filesystem) - */ -Editor.useLocalStorage = typeof(Storage) != 'undefined' && mxClient.IS_IOS; - -/** - * Images below are for lightbox and embedding toolbars. - */ -Editor.helpImage = (mxClient.IS_SVG) ? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAXVBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAC5BxTwAAAAH3RSTlMAlUF8boNQIE0LBgOgkGlHNSwqFIx/dGVUOjApmV9ezNACSAAAAIVJREFUGNNtjNsOgzAMQ5NeoVcKDAZs+//PXLKI8YKlWvaRU7jXuFpb9qsbdK05XILUiE8JHQox1Pv3OgFUzf1AGqWqUg+QBwLF0YAeegBlCNgRWOpB5vUfTCmeoHQ/wNdy0jLH/cM+b+wLTw4n/7ACEmHVVy8h6qy8V7MNcGowWpsNbvUFcGUEdSi1s/oAAAAASUVORK5CYII=' : - IMAGE_PATH + '/help.png'; - -/** - * Sets the default font size. - */ -Editor.checkmarkImage = (mxClient.IS_SVG) ? 'data:image/gif;base64,R0lGODlhFQAVAMQfAGxsbHx8fIqKioaGhvb29nJycvr6+sDAwJqamltbW5OTk+np6YGBgeTk5Ly8vJiYmP39/fLy8qWlpa6ursjIyOLi4vj4+N/f3+3t7fT09LCwsHZ2dubm5r6+vmZmZv///yH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IFdpbmRvd3MiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OEY4NTZERTQ5QUFBMTFFMUE5MTVDOTM5MUZGMTE3M0QiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OEY4NTZERTU5QUFBMTFFMUE5MTVDOTM5MUZGMTE3M0QiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4Rjg1NkRFMjlBQUExMUUxQTkxNUM5MzkxRkYxMTczRCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4Rjg1NkRFMzlBQUExMUUxQTkxNUM5MzkxRkYxMTczRCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgH//v38+/r5+Pf29fTz8vHw7+7t7Ovq6ejn5uXk4+Lh4N/e3dzb2tnY19bV1NPS0dDPzs3My8rJyMfGxcTDwsHAv769vLu6ubi3trW0s7KxsK+urayrqqmop6alpKOioaCfnp2cm5qZmJeWlZSTkpGQj46NjIuKiYiHhoWEg4KBgH9+fXx7enl4d3Z1dHNycXBvbm1sa2ppaGdmZWRjYmFgX15dXFtaWVhXVlVUU1JRUE9OTUxLSklIR0ZFRENCQUA/Pj08Ozo5ODc2NTQzMjEwLy4tLCsqKSgnJiUkIyIhIB8eHRwbGhkYFxYVFBMSERAPDg0MCwoJCAcGBQQDAgEAACH5BAEAAB8ALAAAAAAVABUAAAVI4CeOZGmeaKqubKtylktSgCOLRyLd3+QJEJnh4VHcMoOfYQXQLBcBD4PA6ngGlIInEHEhPOANRkaIFhq8SuHCE1Hb8Lh8LgsBADs=' : - IMAGE_PATH + '/checkmark.gif'; - -/** - * Images below are for lightbox and embedding toolbars. - */ -Editor.maximizeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVBAMAAABbObilAAAAElBMVEUAAAAAAAAAAAAAAAAAAAAAAADgKxmiAAAABXRSTlMA758vX1Pw3BoAAABJSURBVAjXY8AJQkODGBhUQ0MhbAUGBiYY24CBgRnGFmZgMISwgwwDGRhEhVVBbAVmEQYGRwMmBjIAQi/CTIRd6G5AuA3dzYQBAHj0EFdHkvV4AAAAAElFTkSuQmCC'; - -/** - * Specifies the image URL to be used for the transparent background. - */ -Editor.zoomOutImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVBAMAAABbObilAAAAElBMVEUAAAAAAAAsLCxxcXEhISFgYGChjTUxAAAAAXRSTlMAQObYZgAAAEdJREFUCNdjIAMwCQrB2YKCggJQJqMwA7MglK1owMBgqABVApITgLJZXFxgbIQ4Qj3CHIT5ggoIe5kgNkM1KSDYKBKqxPkDAPo5BAZBE54hAAAAAElFTkSuQmCC'; - -/** - * Specifies the image URL to be used for the transparent background. - */ -Editor.zoomInImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVBAMAAABbObilAAAAElBMVEUAAAAAAAAsLCwhISFxcXFgYGBavKaoAAAAAXRSTlMAQObYZgAAAElJREFUCNdjIAMwCQrB2YKCggJQJqMIA4sglK3owMzgqABVwsDMwCgAZTMbG8PYCHGEeoQ5CPMFFRD2MkFshmpSQLBRJFSJ8wcAEqcEM2uhl2MAAAAASUVORK5CYII='; - -/** - * Specifies the image URL to be used for the transparent background. - */ -Editor.zoomFitImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVBAMAAABbObilAAAAD1BMVEUAAAAAAAAwMDBwcHBgYGC1xl09AAAAAXRSTlMAQObYZgAAAEFJREFUCNdjIAMwCQrB2YKCggJQJqMwA7MglK1owMBgqABVApITwMdGqEeYgzBfUAFhLxPEZqgmBQQbRUKFOH8AAK5OA3lA+FFOAAAAAElFTkSuQmCC'; - -/** - * Specifies the image URL to be used for the transparent background. - */ -Editor.layersImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAMAAACeyVWkAAAAaVBMVEUAAAAgICAICAgdHR0PDw8WFhYICAgLCwsXFxcvLy8ODg4uLi4iIiIqKiokJCQYGBgKCgonJycFBQUCAgIqKiocHBwcHBwODg4eHh4cHBwnJycJCQkUFBQqKiojIyMuLi4ZGRkgICAEBATOWYXAAAAAGnRSTlMAD7+fnz8/H7/ff18/77+vr5+fn39/b28fH2xSoKsAAACQSURBVBjTrYxJEsMgDARZZMAY73sgCcn/HxnhKtnk7j6oRq0psfuoyndZ/SuODkHPLzfVT6KeyPePnJ7KrnkRjWMXTn4SMnN8mXe2SSM3ts8L/ZUxxrbAULSYJJULE0Iw9pjpenoICcgcX61mGgTgtCv9Be99pzCoDhNQWQnchD1mup5++CYGcoQexajZbfwAj/0MD8ZOaUgAAAAASUVORK5CYII='; - -/** - * Specifies the image URL to be used for the transparent background. - */ -Editor.previousImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAAh0lEQVQ4je3UsQnCUBCA4U8hpa1NsoEjpHQJS0dxADdwEMuMIJkgA1hYChbGQgMi+JC8q4L/AB/vDu7x74cWWEZhJU44RmA1zujR5GIbXF9YNrjD/Q0bDRY4fEBZ4P4LlgTnCbAf84pUM8/9hY08tMUtEoQ1LpEgrNBFglChFXR6Q6GfwwR6AGKJMF74Vtt3AAAAAElFTkSuQmCC'; - -/** - * Specifies the image URL to be used for the transparent background. - */ -Editor.nextImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAAABmJLR0QA/wD/AP+gvaeTAAAAi0lEQVQ4jeXUIQ7CUAwA0MeGxWI2yylwnALJUdBcgYvM7QYLmjOQIAkIPmJZghiIvypoUtX0tfnJL38X5ZfaEgUeUcManFBHgS0SLlhHggk3bCPBhCf2keCQR8wjwYTDp6YiZxJmOU1jGw7vGALescuBxsArNlOwd/CM1VSM/ut1qCIw+uOwiMJ+OF4CQzBCXm3hyAAAAABJRU5ErkJggg=='; - -/** - * Specifies the image URL to be used for the transparent background. - */ -Editor.editImage = (mxClient.IS_SVG) ? 'data:image/gif;base64,R0lGODlhCwALAIABAFdXV////yH5BAEAAAEALAAAAAALAAsAAAIZjB8AiKuc4jvLOGqzrjX6zmkWyChXaUJBAQA7' : IMAGE_PATH + '/edit.gif'; - -/** - * Specifies the image URL to be used for the transparent background. - */ -Editor.zoomOutLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAilBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////2N2iNAAAALXRSTlMA+vTcKMM96GRBHwXxi0YaX1HLrKWhiHpWEOnOr52Vb2xKSDcT19PKv5l/Ngdk8+viAAABJklEQVQ4y4WT2XaDMAxEvWD2nSSUNEnTJN3r//+9Sj7ILAY6L0ijC4ONYVZRpo6cByrz2YKSUGorGTpz71lPVHvT+avoB5wIkU/mxk8veceSuNoLg44IzziXjvpih72wKQnm8yc2UoiP/LAd8jQfe2Xf4Pq+2EyYIvv9wbzHHCgwxDdlBtWZOdqDfTCVgqpygQpsZaojVAVc9UjQxnAJDIBhiQv84tq3gMQCAVTxVoSibXJf8tMuc7e1TB/DCmejBNg/w1Y3c+AM5vv4w7xM59/oXamrHaLVqPQ+OTCnmMZxgz0SdL5zji0/ld6j88qGa5KIiBB6WeJGKfUKwSMKLuXgvl1TW0tm5R9UQL/efSDYsnzxD8CinhBsTTdugJatKpJwf8v+ADb8QmvW7AeAAAAAAElFTkSuQmCC'; - -/** - * Specifies the image URL to be used for the transparent background. - */ -Editor.zoomInLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAilBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////2N2iNAAAALXRSTlMA+vTcKMM96GRBHwXxi0YaX1HLrKWhiHpWEOnOr52Vb2xKSDcT19PKv5l/Ngdk8+viAAABKElEQVQ4y4WT6WKCMBCENwkBwn2oFKvWqr3L+79es4EkQIDOH2d3Pxk2ABiJlB8JCXjqw4LikHVGLHTm3nM3UeVN5690GBBN0GwyV/3kkrUQR+WeKnREeKpzaXWd77CmJiXGfPIEI4V4yQ9TIW/ntlcMBe731Vts9w5TWG8F5j3mQI4hvrKpdGeYA7CX9qAcl650gVJartxRuhyHVghF8idQAIbFLvCLu28BsQEC6aKtCK6Pyb3JT7PmbmtNH8Ny56CotD/2qOs5cJbuffxgXmCib+xddVU5RNOhkvvkhTlFehzVWCOh3++MYElOhfdovaImnRYVmqDdsuhNp1QrBBE6uGC2+3ZNjGdg5B94oD+9uyVgWT79BwAxEBTWdOu3bWBVgsn/N/AHUD9IC01Oe40AAAAASUVORK5CYII='; - -/** - * Specifies the image URL to be used for the transparent background. - */ -Editor.actualSizeLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAilBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////2N2iNAAAALXRSTlMA+vTcKMM96GRBHwXxi0YaX1HLrKWhiHpWEOnOr52Vb2xKSDcT19PKv5l/Ngdk8+viAAABIUlEQVQ4y4WT2XqDIBCFBxDc9yTWNEnTJN3r+79eGT4BEbXnaubMr8dBBaM450dCQp4LWFAascGIRd48eB4cNYE7f6XjgGiCFs5c+dml6CFN6j1V6IQIlHPpdV/usKcmJcV88gQTRXjLD9Mhb+fWq8YG9/uCmTCFjeeDeY85UGKIUGUuqzN42kv7oCouq9oHamlzVR1lVfpAIu1QVRiW+sAv7r4FpAYIZZVsRXB9TP5Dfpo1d1trCgzz1iiptH/sUbdz4CzN9+mLeXHn3+hdddd4RDegsrvzwZwSs2GLPRJidAqCLTlVwaMPqpYMWjTWBB2WRW86pVkhSKyDK2bdt2tmagZG4sBD/evdLQHLEvQfAOKRoLCmG1FAB6uKmby+gz+REDn7O5+EwQAAAABJRU5ErkJggg=='; - -/** - * Specifies the image URL to be used for the transparent background. - */ -Editor.printLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAXVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9RKvvlAAAAHnRSTlMAydnl77qbMLT093H7K4Nd4Ktn082+lYt5bkklEgP44nQSAAAApUlEQVQ4y73P2Q6DIBRF0cOgbRHHzhP//5m9mBAQKjG1cT0Yc7ITAMu1LNQgUZiQ2DYoNQ0sCQb6qgHAfRx48opq3J9AZ6xuF7uOew8Ik1OsCZRS2UAC9V+D9a+QZYxNA45YFQftPtSkATOhw7dAc0vPBwKWiIOjP0JZ0yMuQJ27g36DipOUsqRAM0dR8KD1/ILHaHSE/w8DIx09E3g/BTce6rHUB5sAPKvfF+JdAAAAAElFTkSuQmCC'; - -/** - * Specifies the image URL to be used for the transparent background. - */ -Editor.layersLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAmVBMVEUAAAD////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+/v7///+bnZkkAAAAMnRSTlMABPr8ByiD88KsTi/rvJb272mjeUA1CuPe1M/KjVxYHxMP6KZ0S9nYzGRGGRaznpGIbzaGUf0AAAHESURBVDjLbZLZYoIwEEVDgLCjbKIgAlqXqt3m/z+uNwu1rcyDhjl3ktnYL7OY254C0VX3yWFZfzDrOClbbgKxi0YDHjwl4jbnRkXxJS/C1YP3DbBhD1n7Ex4uaAqdVDb3yJ/4J/3nJD2to/ngQz/DfUvzMp4JJ5sSCaF5oXmemgQDfDxzbi+Kq4sU+vNcuAmx94JtyOP2DD4Epz2asWSCz4Z/4fECxyNj9zC9xNLHcdPEO+awDKeSaUu0W4twZQiO2hYVisTR3RCtK/c1X6t4xMEpiGqXqVntEBLolkZZsKY4QtwH6jzq67dEHlJysB1aNOD3XT7n1UkasQN59L4yC2RELMDSeCRtz3yV22Ub3ozIUTknYx8JWqDdQxbUes98cR2kZtUSveF/bAhcedwEWmlxIkpZUy4XOCb6VBjjxHvbwo/1lBAHHi2JCr0NI570QhyHq/DhJoE2lLgyA4RVe6KmZ47O/3b86MCP0HWa73A8/C3SUc5Qc1ajt6fgpXJ+RGpMvDSchepZDOOQRcZVIKcK90x2D7etqtI+56+u6n3sPriO6nfphitR4+O2m3EbM7lh3me1FM1o+LMI887rN+s3/wZdTFlpNVJiOAAAAABJRU5ErkJggg=='; - -/** - * Specifies the image URL to be used for the transparent background. - */ -Editor.closeLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAUVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////8IN+deAAAAGnRSTlMAuvAIg/dDM/QlOeuFhj0S5s4vKgzjxJRQNiLSey0AAADNSURBVDjLfZLbEoMgDEQjRRRs1XqX///QNmOHJSnjPkHOGR7IEmeoGtJZstnwjqbRfIsmgEdtPCqe9Ynz7ZSc07rE2QiSc+qv8TvjRXA2PDUm3dpe82iJhOEUfxJJo3aCv+jKmRmH4lcCjCjeh9GWOdL/GZZkXH3PYYDrHBnfc4D/RVZf5sjoC1was+Y6HQxwaUxFvq/a0Pv343VCTxfBSRiB+ab3M3eiQZXmMNBJ3Y8pGRZtYQ7DgHMXJEdPLTaN/qBjzJOBc3nmNcbsA16bMR0oLqf+AAAAAElFTkSuQmCC'; - -/** - * Specifies the image URL to be used for the transparent background. - */ -Editor.editLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAgVBMVEUAAAD///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////9d3yJTAAAAKnRSTlMA+hzi3nRQWyXzkm0h2j3u54gzEgSXjlYoTBgJxL2loGpAOS3Jt7Wxm35Ga7gRAAAA6UlEQVQ4y63Q2XaCMBSF4Q0JBasoQ5DJqbXjfv8HbCK2BZNwo/8FXHx7rcMC7lQu0iX8qU/qtvAWCpoqH8dYzS0SwaV5eK/UAf8X9pd2CWKzuF5Jrftp1owXwnIGLUaL3PYndOHf4kNNXWrXK/m7CHunk7K8LE6YtBpcknwG9GKxnroY+ylBXcx4xKyx/u/EuXi509cP9V7OO1oyHnzrdFTcqLG/4ibBA5pIMr/4xvKzuQDkVy9wW8SgBFD6HDvuzMvrZcC9QlkfMzI7w64m+b4PqBMNHB05lH21PVxJo2/fBXxV4hB38PcD+5AkI4FuETsAAAAASUVORK5CYII='; - -/** - * Specifies the image URL to be used for the transparent background. - */ -Editor.previousLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAPFBMVEUAAAD////////////////////////////////////////////////////////////////////////////YSWgTAAAAE3RSTlMA7fci493c0MW8uJ6CZks4MxQHEZL6ewAAAFZJREFUOMvdkskRgDAMA4lDwg2B7b9XOlge/KKvdsa25KFb5XlRvxXC/DNBEv8IFNjBgGdDgXtFgTyhwDXiQAUHCvwa4Uv6mR6UR+1led2mVonvl+tML45qCQNQLIx7AAAAAElFTkSuQmCC'; - -/** - * Specifies the image URL to be used for the transparent background. - */ -Editor.nextLargeImage = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAPFBMVEUAAAD////////////////////////////////////////////////////////////////////////////YSWgTAAAAE3RSTlMA7fci493c0MW8uJ6CZks4MxQHEZL6ewAAAFRJREFUOMvd0skRgCAQBVEFwQ0V7fxzNQP6wI05v6pZ/kyj1b7FNgik2gQzzLcAwiUAigHOTwDHK4A1CmB5BJANJG1hQ9qafYcqFlZP3IFc9eVGrR+iIgkDQRUXIAAAAABJRU5ErkJggg=='; - -/** - * Specifies the image URL to be used for the transparent background. - */ -Editor.ctrlKey = (mxClient.IS_MAC) ? 'Cmd' : 'Ctrl'; - -/** - * Specifies if the diagram should be saved automatically if possible. Default - * is true. - */ -Editor.popupsAllowed = true; - -/** - * Editor inherits from mxEventSource - */ -mxUtils.extend(Editor, mxEventSource); - -/** - * Stores initial state of mxClient.NO_FO. - */ -Editor.prototype.originalNoForeignObject = mxClient.NO_FO; - -/** - * Specifies the image URL to be used for the transparent background. - */ -Editor.prototype.transparentImage = (mxClient.IS_SVG) ? 'data:image/gif;base64,R0lGODlhMAAwAIAAAP///wAAACH5BAEAAAAALAAAAAAwADAAAAIxhI+py+0Po5y02ouz3rz7D4biSJbmiabqyrbuC8fyTNf2jef6zvf+DwwKh8Si8egpAAA7' : - IMAGE_PATH + '/transparent.gif'; - -/** - * Specifies if the canvas should be extended in all directions. Default is true. - */ -Editor.prototype.extendCanvas = true; - -/** - * Specifies if the app should run in chromeless mode. Default is false. - * This default is only used if the contructor argument is null. - */ -Editor.prototype.chromeless = false; - -/** - * Specifies the order of OK/Cancel buttons in dialogs. Default is true. - * Cancel first is used on Macs, Windows/Confluence uses cancel last. - */ -Editor.prototype.cancelFirst = true; - -/** - * Specifies if the editor is enabled. Default is true. - */ -Editor.prototype.enabled = true; - -/** - * Contains the name which was used for the last save. Default value is null. - */ -Editor.prototype.filename = null; - -/** - * Contains the current modified state of the diagram. This is false for - * new diagrams and after the diagram was saved. - */ -Editor.prototype.modified = false; - -/** - * Specifies if the diagram should be saved automatically if possible. Default - * is true. - */ -Editor.prototype.autosave = true; - -/** - * Specifies the top spacing for the initial page view. Default is 0. - */ -Editor.prototype.initialTopSpacing = 0; - -/** - * Specifies the app name. Default is document.title. - */ -Editor.prototype.appName = document.title; - -/** - * - */ -Editor.prototype.editBlankUrl = window.location.protocol + '//' + window.location.host + '/'; - -/** - * Initializes the environment. - */ -Editor.prototype.init = function() { }; - -/** - * Sets the XML node for the current diagram. - */ -Editor.prototype.isChromelessView = function() -{ - return this.chromeless; -}; - -/** - * Sets the XML node for the current diagram. - */ -Editor.prototype.setAutosave = function(value) -{ - this.autosave = value; - this.fireEvent(new mxEventObject('autosaveChanged')); -}; - -/** - * - */ -Editor.prototype.getEditBlankUrl = function(params) -{ - return this.editBlankUrl + params; -} - -/** - * - */ -Editor.prototype.editAsNew = function(xml, title) -{ - var p = (title != null) ? '?title=' + encodeURIComponent(title) : ''; - - if (urlParams['ui'] != null) - { - p += ((p.length > 0) ? '&' : '?') + 'ui=' + urlParams['ui']; - } - - if (this.editorWindow != null && !this.editorWindow.closed) - { - this.editorWindow.focus(); - } - else - { - if (typeof window.postMessage !== 'undefined' && (document.documentMode == null || document.documentMode >= 10)) - { - if (this.editorWindow == null) - { - mxEvent.addListener(window, 'message', mxUtils.bind(this, function(evt) - { - if (evt.data == 'ready' && evt.source == this.editorWindow) - { - this.editorWindow.postMessage(xml, '*'); - } - })); - } - - this.editorWindow = this.graph.openLink(this.getEditBlankUrl(p + - ((p.length > 0) ? '&' : '?') + 'client=1'), null, true); - } - else - { - this.editorWindow = this.graph.openLink(this.getEditBlankUrl(p) + - '#R' + encodeURIComponent(xml)); - } - } -}; - -/** - * Sets the XML node for the current diagram. - */ -Editor.prototype.createGraph = function(themes, model) -{ - var graph = new Graph(null, model, null, null, themes); - graph.transparentBackground = false; - - // Opens all links in a new window while editing - if (!this.chromeless) - { - graph.isBlankLink = function(href) - { - return !this.isExternalProtocol(href); - }; - } - - return graph; -}; - -/** - * Sets the XML node for the current diagram. - */ -Editor.prototype.resetGraph = function() -{ - this.graph.gridEnabled = !this.isChromelessView() || urlParams['grid'] == '1'; - this.graph.graphHandler.guidesEnabled = true; - this.graph.setTooltips(true); - this.graph.setConnectable(true); - this.graph.foldingEnabled = true; - this.graph.scrollbars = this.graph.defaultScrollbars; - this.graph.pageVisible = this.graph.defaultPageVisible; - this.graph.pageBreaksVisible = this.graph.pageVisible; - this.graph.preferPageSize = this.graph.pageBreaksVisible; - this.graph.background = this.graph.defaultGraphBackground; - this.graph.pageScale = mxGraph.prototype.pageScale; - this.graph.pageFormat = mxGraph.prototype.pageFormat; - this.graph.currentScale = 1; - this.graph.currentTranslate.x = 0; - this.graph.currentTranslate.y = 0; - this.updateGraphComponents(); - this.graph.view.setScale(1); -}; - -/** - * Sets the XML node for the current diagram. - */ -Editor.prototype.readGraphState = function(node) -{ - this.graph.gridEnabled = node.getAttribute('grid') != '0' && (!this.isChromelessView() || urlParams['grid'] == '1'); - this.graph.gridSize = parseFloat(node.getAttribute('gridSize')) || mxGraph.prototype.gridSize; - this.graph.graphHandler.guidesEnabled = node.getAttribute('guides') != '0'; - this.graph.setTooltips(node.getAttribute('tooltips') != '0'); - this.graph.setConnectable(node.getAttribute('connect') != '0'); - this.graph.connectionArrowsEnabled = node.getAttribute('arrows') != '0'; - this.graph.foldingEnabled = node.getAttribute('fold') != '0'; - - if (this.isChromelessView() && this.graph.foldingEnabled) - { - this.graph.foldingEnabled = urlParams['nav'] == '1'; - this.graph.cellRenderer.forceControlClickHandler = this.graph.foldingEnabled; - } - - var ps = node.getAttribute('pageScale'); - - if (ps != null) - { - this.graph.pageScale = ps; - } - else - { - this.graph.pageScale = mxGraph.prototype.pageScale; - } - - if (!this.graph.isLightboxView()) - { - var pv = node.getAttribute('page'); - - if (pv != null) - { - this.graph.pageVisible = (pv != '0'); - } - else - { - this.graph.pageVisible = this.graph.defaultPageVisible; - } - } - else - { - this.graph.pageVisible = false; - } - - this.graph.pageBreaksVisible = this.graph.pageVisible; - this.graph.preferPageSize = this.graph.pageBreaksVisible; - - var pw = node.getAttribute('pageWidth'); - var ph = node.getAttribute('pageHeight'); - - if (pw != null && ph != null) - { - this.graph.pageFormat = new mxRectangle(0, 0, parseFloat(pw), parseFloat(ph)); - } - - // Loads the persistent state settings - var bg = node.getAttribute('background'); - - if (bg != null && bg.length > 0) - { - this.graph.background = bg; - } - else - { - this.graph.background = this.graph.defaultGraphBackground; - } -}; - -/** - * Sets the XML node for the current diagram. - */ -Editor.prototype.setGraphXml = function(node) -{ - if (node != null) - { - var dec = new mxCodec(node.ownerDocument); - - if (node.nodeName == 'mxGraphModel') - { - this.graph.model.beginUpdate(); - - try - { - this.graph.model.clear(); - this.graph.view.scale = 1; - this.readGraphState(node); - this.updateGraphComponents(); - dec.decode(node, this.graph.getModel()); - } - finally - { - this.graph.model.endUpdate(); - } - - this.fireEvent(new mxEventObject('resetGraphView')); - } - else if (node.nodeName == 'root') - { - this.resetGraph(); - - // Workaround for invalid XML output in Firefox 20 due to bug in mxUtils.getXml - var wrapper = dec.document.createElement('mxGraphModel'); - wrapper.appendChild(node); - - dec.decode(wrapper, this.graph.getModel()); - this.updateGraphComponents(); - this.fireEvent(new mxEventObject('resetGraphView')); - } - else - { - throw { - message: mxResources.get('cannotOpenFile'), - node: node, - toString: function() { return this.message; } - }; - } - } - else - { - this.resetGraph(); - this.graph.model.clear(); - this.fireEvent(new mxEventObject('resetGraphView')); - } -}; - -/** - * Returns the XML node that represents the current diagram. - */ -Editor.prototype.getGraphXml = function(ignoreSelection) -{ - ignoreSelection = (ignoreSelection != null) ? ignoreSelection : true; - var node = null; - - if (ignoreSelection) - { - var enc = new mxCodec(mxUtils.createXmlDocument()); - node = enc.encode(this.graph.getModel()); - } - else - { - node = this.graph.encodeCells(mxUtils.sortCells(this.graph.model.getTopmostCells( - this.graph.getSelectionCells()))); - } - - if (this.graph.view.translate.x != 0 || this.graph.view.translate.y != 0) - { - node.setAttribute('dx', Math.round(this.graph.view.translate.x * 100) / 100); - node.setAttribute('dy', Math.round(this.graph.view.translate.y * 100) / 100); - } - - node.setAttribute('grid', (this.graph.isGridEnabled()) ? '1' : '0'); - node.setAttribute('gridSize', this.graph.gridSize); - node.setAttribute('guides', (this.graph.graphHandler.guidesEnabled) ? '1' : '0'); - node.setAttribute('tooltips', (this.graph.tooltipHandler.isEnabled()) ? '1' : '0'); - node.setAttribute('connect', (this.graph.connectionHandler.isEnabled()) ? '1' : '0'); - node.setAttribute('arrows', (this.graph.connectionArrowsEnabled) ? '1' : '0'); - node.setAttribute('fold', (this.graph.foldingEnabled) ? '1' : '0'); - node.setAttribute('page', (this.graph.pageVisible) ? '1' : '0'); - node.setAttribute('pageScale', this.graph.pageScale); - node.setAttribute('pageWidth', this.graph.pageFormat.width); - node.setAttribute('pageHeight', this.graph.pageFormat.height); - - if (this.graph.background != null) - { - node.setAttribute('background', this.graph.background); - } - - return node; -}; - -/** - * Keeps the graph container in sync with the persistent graph state - */ -Editor.prototype.updateGraphComponents = function() -{ - var graph = this.graph; - - if (graph.container != null) - { - graph.view.validateBackground(); - graph.container.style.overflow = (graph.scrollbars) ? 'auto' : 'hidden'; - - this.fireEvent(new mxEventObject('updateGraphComponents')); - } -}; - -/** - * Sets the modified flag. - */ -Editor.prototype.setModified = function(value) -{ - this.modified = value; -}; - -/** - * Sets the filename. - */ -Editor.prototype.setFilename = function(value) -{ - this.filename = value; -}; - -/** - * Creates and returns a new undo manager. - */ -Editor.prototype.createUndoManager = function() -{ - var graph = this.graph; - var undoMgr = new mxUndoManager(); - - this.undoListener = function(sender, evt) - { - undoMgr.undoableEditHappened(evt.getProperty('edit')); - }; - - // Installs the command history - var listener = mxUtils.bind(this, function(sender, evt) - { - this.undoListener.apply(this, arguments); - }); - - graph.getModel().addListener(mxEvent.UNDO, listener); - graph.getView().addListener(mxEvent.UNDO, listener); - - // Keeps the selection in sync with the history - var undoHandler = function(sender, evt) - { - var cand = graph.getSelectionCellsForChanges(evt.getProperty('edit').changes); - var model = graph.getModel(); - var cells = []; - - for (var i = 0; i < cand.length; i++) - { - if ((model.isVertex(cand[i]) || model.isEdge(cand[i])) && graph.view.getState(cand[i]) != null) - { - cells.push(cand[i]); - } - } - - graph.setSelectionCells(cells); - }; - - undoMgr.addListener(mxEvent.UNDO, undoHandler); - undoMgr.addListener(mxEvent.REDO, undoHandler); - - return undoMgr; -}; - -/** - * Adds basic stencil set (no namespace). - */ -Editor.prototype.initStencilRegistry = function() { }; - -/** - * Creates and returns a new undo manager. - */ -Editor.prototype.destroy = function() -{ - if (this.graph != null) - { - this.graph.destroy(); - this.graph = null; - } -}; - -/** - * Class for asynchronously opening a new window and loading a file at the same - * time. This acts as a bridge between the open dialog and the new editor. - */ -OpenFile = function(done) -{ - this.producer = null; - this.consumer = null; - this.done = done; - this.args = null; -}; - -/** - * Registers the editor from the new window. - */ -OpenFile.prototype.setConsumer = function(value) -{ - this.consumer = value; - this.execute(); -}; - -/** - * Sets the data from the loaded file. - */ -OpenFile.prototype.setData = function() -{ - this.args = arguments; - this.execute(); -}; - -/** - * Displays an error message. - */ -OpenFile.prototype.error = function(msg) -{ - this.cancel(true); - mxUtils.alert(msg); -}; - -/** - * Consumes the data. - */ -OpenFile.prototype.execute = function() -{ - if (this.consumer != null && this.args != null) - { - this.cancel(false); - this.consumer.apply(this, this.args); - } -}; - -/** - * Cancels the operation. - */ -OpenFile.prototype.cancel = function(cancel) -{ - if (this.done != null) - { - this.done((cancel != null) ? cancel : true); - } -}; - -/** - * Basic dialogs that are available in the viewer (print dialog). - */ -function Dialog(editorUi, elt, w, h, modal, closable, onClose, noScroll, transparent) -{ - var dx = 0; - - if (mxClient.IS_VML && (document.documentMode == null || document.documentMode < 8)) - { - // Adds padding as a workaround for box model in older IE versions - // This needs to match the total padding of geDialog in CSS - dx = 80; - } - - w += dx; - h += dx; - - var w0 = w; - var h0 = h; - - // clientHeight check is attempted fix for print dialog offset in viewer lightbox - var dh = (document.documentElement.clientHeight > 0) ? document.documentElement.clientHeight : - Math.max(document.body.clientHeight || 0, document.documentElement.clientHeight); - var left = Math.max(1, Math.round((document.body.clientWidth - w - 64) / 2)); - var top = Math.max(1, Math.round((dh - h - editorUi.footerHeight) / 3)); - - // Keeps window size inside available space - if (!mxClient.IS_QUIRKS) - { - elt.style.maxHeight = '100%'; - } - - w = Math.min(w, document.body.scrollWidth - 64); - h = Math.min(h, dh - 64); - - // Increments zIndex to put subdialogs and background over existing dialogs and background - if (editorUi.dialogs.length > 0) - { - this.zIndex += editorUi.dialogs.length * 2; - } - - if (this.bg == null) - { - this.bg = editorUi.createDiv('background'); - this.bg.style.position = 'absolute'; - this.bg.style.background = Dialog.backdropColor; - this.bg.style.height = dh + 'px'; - this.bg.style.right = '0px'; - this.bg.style.zIndex = this.zIndex - 2; - - mxUtils.setOpacity(this.bg, this.bgOpacity); - - if (mxClient.IS_QUIRKS) - { - new mxDivResizer(this.bg); - } - } - - var origin = mxUtils.getDocumentScrollOrigin(document); - this.bg.style.left = origin.x + 'px'; - this.bg.style.top = origin.y + 'px'; - left += origin.x; - top += origin.y; - - if (modal) - { - document.body.appendChild(this.bg); - } - - var div = editorUi.createDiv(transparent? 'geTransDialog' : 'geDialog'); - var pos = this.getPosition(left, top, w, h); - left = pos.x; - top = pos.y; - - div.style.width = w + 'px'; - div.style.height = h + 'px'; - div.style.left = left + 'px'; - div.style.top = top + 'px'; - div.style.zIndex = this.zIndex; - - div.appendChild(elt); - document.body.appendChild(div); - - // Adds vertical scrollbars if needed - if (!noScroll && elt.clientHeight > div.clientHeight - 64) - { - elt.style.overflowY = 'auto'; - } - - if (closable) - { - var img = document.createElement('img'); - - img.setAttribute('src', Dialog.prototype.closeImage); - img.setAttribute('title', mxResources.get('close')); - img.className = 'geDialogClose'; - img.style.top = (top + 14) + 'px'; - img.style.left = (left + w + 38 - dx) + 'px'; - img.style.zIndex = this.zIndex; - - mxEvent.addListener(img, 'click', mxUtils.bind(this, function() - { - editorUi.hideDialog(true); - })); - - document.body.appendChild(img); - this.dialogImg = img; - - mxEvent.addGestureListeners(this.bg, null, null, mxUtils.bind(this, function(evt) - { - editorUi.hideDialog(true); - })); - } - - this.resizeListener = mxUtils.bind(this, function() - { - dh = Math.max(document.body.clientHeight, document.documentElement.clientHeight); - this.bg.style.height = dh + 'px'; - - left = Math.max(1, Math.round((document.body.clientWidth - w - 64) / 2)); - top = Math.max(1, Math.round((dh - h - editorUi.footerHeight) / 3)); - w = Math.min(w0, document.body.scrollWidth - 64); - h = Math.min(h0, dh - 64); - - var pos = this.getPosition(left, top, w, h); - left = pos.x; - top = pos.y; - - div.style.left = left + 'px'; - div.style.top = top + 'px'; - div.style.width = w + 'px'; - div.style.height = h + 'px'; - - // Adds vertical scrollbars if needed - if (!noScroll && elt.clientHeight > div.clientHeight - 64) - { - elt.style.overflowY = 'auto'; - } - - if (this.dialogImg != null) - { - this.dialogImg.style.top = (top + 14) + 'px'; - this.dialogImg.style.left = (left + w + 38 - dx) + 'px'; - } - }); - - mxEvent.addListener(window, 'resize', this.resizeListener); - - this.onDialogClose = onClose; - this.container = div; - - editorUi.editor.fireEvent(new mxEventObject('showDialog')); -}; - -/** - * - */ -Dialog.backdropColor = 'white'; - -/** - * - */ -Dialog.prototype.zIndex = mxPopupMenu.prototype.zIndex - 1; - -/** - * - */ -Dialog.prototype.noColorImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/nocolor.png' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkEzRDlBMUUwODYxMTExRTFCMzA4RDdDMjJBMEMxRDM3IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkEzRDlBMUUxODYxMTExRTFCMzA4RDdDMjJBMEMxRDM3Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QTNEOUExREU4NjExMTFFMUIzMDhEN0MyMkEwQzFEMzciIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QTNEOUExREY4NjExMTFFMUIzMDhEN0MyMkEwQzFEMzciLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz5xh3fmAAAABlBMVEX////MzMw46qqDAAAAGElEQVR42mJggAJGKGAYIIGBth8KAAIMAEUQAIElnLuQAAAAAElFTkSuQmCC'; - -/** - * - */ -Dialog.prototype.closeImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/close.png' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAkAAAAJAQMAAADaX5RTAAAABlBMVEV7mr3///+wksspAAAAAnRSTlP/AOW3MEoAAAAdSURBVAgdY9jXwCDDwNDRwHCwgeExmASygSL7GgB12QiqNHZZIwAAAABJRU5ErkJggg=='; - -/** - * - */ -Dialog.prototype.clearImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/clear.gif' : 'data:image/gif;base64,R0lGODlhDQAKAIABAMDAwP///yH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IFdpbmRvd3MiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OUIzOEM1NzI4NjEyMTFFMUEzMkNDMUE3NjZERDE2QjIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OUIzOEM1NzM4NjEyMTFFMUEzMkNDMUE3NjZERDE2QjIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo5QjM4QzU3MDg2MTIxMUUxQTMyQ0MxQTc2NkREMTZCMiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo5QjM4QzU3MTg2MTIxMUUxQTMyQ0MxQTc2NkREMTZCMiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PgH//v38+/r5+Pf29fTz8vHw7+7t7Ovq6ejn5uXk4+Lh4N/e3dzb2tnY19bV1NPS0dDPzs3My8rJyMfGxcTDwsHAv769vLu6ubi3trW0s7KxsK+urayrqqmop6alpKOioaCfnp2cm5qZmJeWlZSTkpGQj46NjIuKiYiHhoWEg4KBgH9+fXx7enl4d3Z1dHNycXBvbm1sa2ppaGdmZWRjYmFgX15dXFtaWVhXVlVUU1JRUE9OTUxLSklIR0ZFRENCQUA/Pj08Ozo5ODc2NTQzMjEwLy4tLCsqKSgnJiUkIyIhIB8eHRwbGhkYFxYVFBMSERAPDg0MCwoJCAcGBQQDAgEAACH5BAEAAAEALAAAAAANAAoAAAIXTGCJebD9jEOTqRlttXdrB32PJ2ncyRQAOw=='; - -/** - * - */ -Dialog.prototype.lockedImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/locked.png' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAMAAABhq6zVAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MzdDMDZCODExNzIxMTFFNUI0RTk5NTg4OTcyMUUyODEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MzdDMDZCODIxNzIxMTFFNUI0RTk5NTg4OTcyMUUyODEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDozN0MwNkI3RjE3MjExMUU1QjRFOTk1ODg5NzIxRTI4MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDozN0MwNkI4MDE3MjExMUU1QjRFOTk1ODg5NzIxRTI4MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PvqMCFYAAAAVUExURZmZmb+/v7KysqysrMzMzLGxsf///4g8N1cAAAAHdFJOU////////wAaSwNGAAAAPElEQVR42lTMQQ4AIQgEwUa0//9kTQirOweYOgDqAMbZUr10AGlAwx4/BJ2QJ4U0L5brYjovvpv32xZgAHZaATFtMbu4AAAAAElFTkSuQmCC'; - -/** - * - */ -Dialog.prototype.unlockedImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/unlocked.png' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAMAAABhq6zVAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6MzdDMDZCN0QxNzIxMTFFNUI0RTk5NTg4OTcyMUUyODEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6MzdDMDZCN0UxNzIxMTFFNUI0RTk5NTg4OTcyMUUyODEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDozN0MwNkI3QjE3MjExMUU1QjRFOTk1ODg5NzIxRTI4MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDozN0MwNkI3QzE3MjExMUU1QjRFOTk1ODg5NzIxRTI4MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PkKMpVwAAAAYUExURZmZmbKysr+/v6ysrOXl5czMzLGxsf///zHN5lwAAAAIdFJOU/////////8A3oO9WQAAADxJREFUeNpUzFESACAEBNBVsfe/cZJU+8Mzs8CIABCidtfGOndnYsT40HDSiCcbPdoJo10o9aI677cpwACRoAF3dFNlswAAAABJRU5ErkJggg=='; - -/** - * Removes the dialog from the DOM. - */ -Dialog.prototype.bgOpacity = 80; - -/** - * Removes the dialog from the DOM. - */ -Dialog.prototype.getPosition = function(left, top) -{ - return new mxPoint(left, top); -}; - -/** - * Removes the dialog from the DOM. - */ -Dialog.prototype.close = function(cancel) -{ - if (this.onDialogClose != null) - { - this.onDialogClose(cancel); - this.onDialogClose = null; - } - - if (this.dialogImg != null) - { - this.dialogImg.parentNode.removeChild(this.dialogImg); - this.dialogImg = null; - } - - if (this.bg != null && this.bg.parentNode != null) - { - this.bg.parentNode.removeChild(this.bg); - } - - mxEvent.removeListener(window, 'resize', this.resizeListener); - this.container.parentNode.removeChild(this.container); -}; - -/** - * Constructs a new print dialog. - */ -var PrintDialog = function(editorUi, title) -{ - this.create(editorUi, title); -}; - -/** - * Constructs a new print dialog. - */ -PrintDialog.prototype.create = function(editorUi) -{ - var graph = editorUi.editor.graph; - var row, td; - - var table = document.createElement('table'); - table.style.width = '100%'; - table.style.height = '100%'; - var tbody = document.createElement('tbody'); - - row = document.createElement('tr'); - - var onePageCheckBox = document.createElement('input'); - onePageCheckBox.setAttribute('type', 'checkbox'); - td = document.createElement('td'); - td.setAttribute('colspan', '2'); - td.style.fontSize = '10pt'; - td.appendChild(onePageCheckBox); - - var span = document.createElement('span'); - mxUtils.write(span, ' ' + mxResources.get('fitPage')); - td.appendChild(span); - - mxEvent.addListener(span, 'click', function(evt) - { - onePageCheckBox.checked = !onePageCheckBox.checked; - pageCountCheckBox.checked = !onePageCheckBox.checked; - mxEvent.consume(evt); - }); - - mxEvent.addListener(onePageCheckBox, 'change', function() - { - pageCountCheckBox.checked = !onePageCheckBox.checked; - }); - - row.appendChild(td); - tbody.appendChild(row); - - row = row.cloneNode(false); - - var pageCountCheckBox = document.createElement('input'); - pageCountCheckBox.setAttribute('type', 'checkbox'); - td = document.createElement('td'); - td.style.fontSize = '10pt'; - td.appendChild(pageCountCheckBox); - - var span = document.createElement('span'); - mxUtils.write(span, ' ' + mxResources.get('posterPrint') + ':'); - td.appendChild(span); - - mxEvent.addListener(span, 'click', function(evt) - { - pageCountCheckBox.checked = !pageCountCheckBox.checked; - onePageCheckBox.checked = !pageCountCheckBox.checked; - mxEvent.consume(evt); - }); - - row.appendChild(td); - - var pageCountInput = document.createElement('input'); - pageCountInput.setAttribute('value', '1'); - pageCountInput.setAttribute('type', 'number'); - pageCountInput.setAttribute('min', '1'); - pageCountInput.setAttribute('size', '4'); - pageCountInput.setAttribute('disabled', 'disabled'); - pageCountInput.style.width = '50px'; - - td = document.createElement('td'); - td.style.fontSize = '10pt'; - td.appendChild(pageCountInput); - mxUtils.write(td, ' ' + mxResources.get('pages') + ' (max)'); - row.appendChild(td); - tbody.appendChild(row); - - mxEvent.addListener(pageCountCheckBox, 'change', function() - { - if (pageCountCheckBox.checked) - { - pageCountInput.removeAttribute('disabled'); - } - else - { - pageCountInput.setAttribute('disabled', 'disabled'); - } - - onePageCheckBox.checked = !pageCountCheckBox.checked; - }); - - row = row.cloneNode(false); - - td = document.createElement('td'); - mxUtils.write(td, mxResources.get('pageScale') + ':'); - row.appendChild(td); - - td = document.createElement('td'); - var pageScaleInput = document.createElement('input'); - pageScaleInput.setAttribute('value', '100 %'); - pageScaleInput.setAttribute('size', '5'); - pageScaleInput.style.width = '50px'; - - td.appendChild(pageScaleInput); - row.appendChild(td); - tbody.appendChild(row); - - row = document.createElement('tr'); - td = document.createElement('td'); - td.colSpan = 2; - td.style.paddingTop = '20px'; - td.setAttribute('align', 'right'); - - // Overall scale for print-out to account for print borders in dialogs etc - function preview(print) - { - var autoOrigin = onePageCheckBox.checked || pageCountCheckBox.checked; - var printScale = parseInt(pageScaleInput.value) / 100; - - if (isNaN(printScale)) - { - printScale = 1; - pageScaleInput.value = '100%'; - } - - // Workaround to match available paper size in actual print output - printScale *= 0.75; - - var pf = graph.pageFormat || mxConstants.PAGE_FORMAT_A4_PORTRAIT; - var scale = 1 / graph.pageScale; - - if (autoOrigin) - { - var pageCount = (onePageCheckBox.checked) ? 1 : parseInt(pageCountInput.value); - - if (!isNaN(pageCount)) - { - scale = mxUtils.getScaleForPageCount(pageCount, graph, pf); - } - } - - // Negative coordinates are cropped or shifted if page visible - var gb = graph.getGraphBounds(); - var border = 0; - var x0 = 0; - var y0 = 0; - - // Applies print scale - pf = mxRectangle.fromRectangle(pf); - pf.width = Math.ceil(pf.width * printScale); - pf.height = Math.ceil(pf.height * printScale); - scale *= printScale; - - // Starts at first visible page - if (!autoOrigin && graph.pageVisible) - { - var layout = graph.getPageLayout(); - x0 -= layout.x * pf.width; - y0 -= layout.y * pf.height; - } - else - { - autoOrigin = true; - } - - var preview = PrintDialog.createPrintPreview(graph, scale, pf, border, x0, y0, autoOrigin); - preview.open(); - - if (print) - { - PrintDialog.printPreview(preview); - } - }; - - var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() - { - editorUi.hideDialog(); - }); - cancelBtn.className = 'geBtn'; - - if (editorUi.editor.cancelFirst) - { - td.appendChild(cancelBtn); - } - - if (PrintDialog.previewEnabled) - { - var previewBtn = mxUtils.button(mxResources.get('preview'), function() - { - editorUi.hideDialog(); - preview(false); - }); - previewBtn.className = 'geBtn'; - td.appendChild(previewBtn); - } - - var printBtn = mxUtils.button(mxResources.get((!PrintDialog.previewEnabled) ? 'ok' : 'print'), function() - { - editorUi.hideDialog(); - preview(true); - }); - printBtn.className = 'geBtn gePrimaryBtn'; - td.appendChild(printBtn); - - if (!editorUi.editor.cancelFirst) - { - td.appendChild(cancelBtn); - } - - row.appendChild(td); - tbody.appendChild(row); - - table.appendChild(tbody); - this.container = table; -}; - -/** - * Constructs a new print dialog. - */ -PrintDialog.printPreview = function(preview) -{ - if (preview.wnd != null) - { - var printFn = function() - { - preview.wnd.focus(); - preview.wnd.print(); - preview.wnd.close(); - }; - - // Workaround for Google Chrome which needs a bit of a - // delay in order to render the SVG contents - // Needs testing in production - if (mxClient.IS_GC) - { - window.setTimeout(printFn, 500); - } - else - { - printFn(); - } - } -}; - -/** - * Constructs a new print dialog. - */ -PrintDialog.createPrintPreview = function(graph, scale, pf, border, x0, y0, autoOrigin) -{ - var preview = new mxPrintPreview(graph, scale, pf, border, x0, y0); - preview.title = mxResources.get('preview'); - preview.printBackgroundImage = true; - preview.autoOrigin = autoOrigin; - var bg = graph.background; - - if (bg == null || bg == '' || bg == mxConstants.NONE) - { - bg = '#ffffff'; - } - - preview.backgroundColor = bg; - - var writeHead = preview.writeHead; - - // Adds a border in the preview - preview.writeHead = function(doc) - { - writeHead.apply(this, arguments); - - doc.writeln(''); - }; - - return preview; -}; - -/** - * Specifies if the preview button should be enabled. Default is true. - */ -PrintDialog.previewEnabled = true; - -/** - * Constructs a new page setup dialog. - */ -var PageSetupDialog = function(editorUi) -{ - var graph = editorUi.editor.graph; - var row, td; - - var table = document.createElement('table'); - table.style.width = '100%'; - table.style.height = '100%'; - var tbody = document.createElement('tbody'); - - row = document.createElement('tr'); - - td = document.createElement('td'); - td.style.verticalAlign = 'top'; - td.style.fontSize = '10pt'; - mxUtils.write(td, mxResources.get('paperSize') + ':'); - - row.appendChild(td); - - td = document.createElement('td'); - td.style.verticalAlign = 'top'; - td.style.fontSize = '10pt'; - - var accessor = PageSetupDialog.addPageFormatPanel(td, 'pagesetupdialog', graph.pageFormat); - - row.appendChild(td); - tbody.appendChild(row); - - row = document.createElement('tr'); - - td = document.createElement('td'); - mxUtils.write(td, mxResources.get('background') + ':'); - - row.appendChild(td); - - td = document.createElement('td'); - td.style.whiteSpace = 'nowrap'; - - var backgroundInput = document.createElement('input'); - backgroundInput.setAttribute('type', 'text'); - var backgroundButton = document.createElement('button'); - - backgroundButton.style.width = '18px'; - backgroundButton.style.height = '18px'; - backgroundButton.style.marginRight = '20px'; - backgroundButton.style.backgroundPosition = 'center center'; - backgroundButton.style.backgroundRepeat = 'no-repeat'; - - var newBackgroundColor = graph.background; - - function updateBackgroundColor() - { - if (newBackgroundColor == null || newBackgroundColor == mxConstants.NONE) - { - backgroundButton.style.backgroundColor = ''; - backgroundButton.style.backgroundImage = 'url(\'' + Dialog.prototype.noColorImage + '\')'; - } - else - { - backgroundButton.style.backgroundColor = newBackgroundColor; - backgroundButton.style.backgroundImage = ''; - } - }; - - updateBackgroundColor(); - - mxEvent.addListener(backgroundButton, 'click', function(evt) - { - editorUi.pickColor(newBackgroundColor || 'none', function(color) - { - newBackgroundColor = color; - updateBackgroundColor(); - }); - mxEvent.consume(evt); - }); - - td.appendChild(backgroundButton); - - mxUtils.write(td, mxResources.get('gridSize') + ':'); - - var gridSizeInput = document.createElement('input'); - gridSizeInput.setAttribute('type', 'number'); - gridSizeInput.setAttribute('min', '0'); - gridSizeInput.style.width = '40px'; - gridSizeInput.style.marginLeft = '6px'; - - gridSizeInput.value = graph.getGridSize(); - td.appendChild(gridSizeInput); - - mxEvent.addListener(gridSizeInput, 'change', function() - { - var value = parseInt(gridSizeInput.value); - gridSizeInput.value = Math.max(1, (isNaN(value)) ? graph.getGridSize() : value); - }); - - row.appendChild(td); - tbody.appendChild(row); - - row = document.createElement('tr'); - td = document.createElement('td'); - - mxUtils.write(td, mxResources.get('image') + ':'); - - row.appendChild(td); - td = document.createElement('td'); - - var changeImageLink = document.createElement('a'); - changeImageLink.style.textDecoration = 'underline'; - changeImageLink.style.cursor = 'pointer'; - changeImageLink.style.color = '#a0a0a0'; - - var newBackgroundImage = graph.backgroundImage; - - function updateBackgroundImage() - { - if (newBackgroundImage == null) - { - changeImageLink.removeAttribute('title'); - changeImageLink.style.fontSize = ''; - changeImageLink.innerHTML = mxResources.get('change') + '...'; - } - else - { - changeImageLink.setAttribute('title', newBackgroundImage.src); - changeImageLink.style.fontSize = '11px'; - changeImageLink.innerHTML = newBackgroundImage.src.substring(0, 42) + '...'; - } - }; - - mxEvent.addListener(changeImageLink, 'click', function(evt) - { - editorUi.showBackgroundImageDialog(function(image) - { - newBackgroundImage = image; - updateBackgroundImage(); - }); - - mxEvent.consume(evt); - }); - - updateBackgroundImage(); - - td.appendChild(changeImageLink); - - row.appendChild(td); - tbody.appendChild(row); - - row = document.createElement('tr'); - td = document.createElement('td'); - td.colSpan = 2; - td.style.paddingTop = '16px'; - td.setAttribute('align', 'right'); - - var cancelBtn = mxUtils.button(mxResources.get('cancel'), function() - { - editorUi.hideDialog(); - }); - cancelBtn.className = 'geBtn'; - - if (editorUi.editor.cancelFirst) - { - td.appendChild(cancelBtn); - } - - var applyBtn = mxUtils.button(mxResources.get('apply'), function() - { - editorUi.hideDialog(); - - if (graph.gridSize !== gridSizeInput.value) - { - graph.setGridSize(parseInt(gridSizeInput.value)); - } - - var change = new ChangePageSetup(editorUi, newBackgroundColor, - newBackgroundImage, accessor.get()); - change.ignoreColor = graph.background == newBackgroundColor; - - var oldSrc = (graph.backgroundImage != null) ? graph.backgroundImage.src : null; - var newSrc = (newBackgroundImage != null) ? newBackgroundImage.src : null; - - change.ignoreImage = oldSrc === newSrc; - - if (graph.pageFormat.width != change.previousFormat.width || - graph.pageFormat.height != change.previousFormat.height || - !change.ignoreColor || !change.ignoreImage) - { - graph.model.execute(change); - } - }); - applyBtn.className = 'geBtn gePrimaryBtn'; - td.appendChild(applyBtn); - - if (!editorUi.editor.cancelFirst) - { - td.appendChild(cancelBtn); - } - - row.appendChild(td); - tbody.appendChild(row); - - table.appendChild(tbody); - this.container = table; -}; - -/** - * - */ -PageSetupDialog.addPageFormatPanel = function(div, namePostfix, pageFormat, pageFormatListener) -{ - var formatName = 'format-' + namePostfix; - - var portraitCheckBox = document.createElement('input'); - portraitCheckBox.setAttribute('name', formatName); - portraitCheckBox.setAttribute('type', 'radio'); - portraitCheckBox.setAttribute('value', 'portrait'); - - var landscapeCheckBox = document.createElement('input'); - landscapeCheckBox.setAttribute('name', formatName); - landscapeCheckBox.setAttribute('type', 'radio'); - landscapeCheckBox.setAttribute('value', 'landscape'); - - var paperSizeSelect = document.createElement('select'); - paperSizeSelect.style.marginBottom = '8px'; - paperSizeSelect.style.width = '202px'; - - var formatDiv = document.createElement('div'); - formatDiv.style.marginLeft = '4px'; - formatDiv.style.width = '210px'; - formatDiv.style.height = '24px'; - - portraitCheckBox.style.marginRight = '6px'; - formatDiv.appendChild(portraitCheckBox); - - var portraitSpan = document.createElement('span'); - portraitSpan.style.maxWidth = '100px'; - mxUtils.write(portraitSpan, mxResources.get('portrait')); - formatDiv.appendChild(portraitSpan); - - landscapeCheckBox.style.marginLeft = '10px'; - landscapeCheckBox.style.marginRight = '6px'; - formatDiv.appendChild(landscapeCheckBox); - - var landscapeSpan = document.createElement('span'); - landscapeSpan.style.width = '100px'; - mxUtils.write(landscapeSpan, mxResources.get('landscape')); - formatDiv.appendChild(landscapeSpan) - - var customDiv = document.createElement('div'); - customDiv.style.marginLeft = '4px'; - customDiv.style.width = '210px'; - customDiv.style.height = '24px'; - - var widthInput = document.createElement('input'); - widthInput.setAttribute('size', '7'); - widthInput.style.textAlign = 'right'; - customDiv.appendChild(widthInput); - mxUtils.write(customDiv, ' in x '); - - var heightInput = document.createElement('input'); - heightInput.setAttribute('size', '7'); - heightInput.style.textAlign = 'right'; - customDiv.appendChild(heightInput); - mxUtils.write(customDiv, ' in'); - - formatDiv.style.display = 'none'; - customDiv.style.display = 'none'; - - var pf = new Object(); - var formats = PageSetupDialog.getFormats(); - - for (var i = 0; i < formats.length; i++) - { - var f = formats[i]; - pf[f.key] = f; - - var paperSizeOption = document.createElement('option'); - paperSizeOption.setAttribute('value', f.key); - mxUtils.write(paperSizeOption, f.title); - paperSizeSelect.appendChild(paperSizeOption); - } - - var customSize = false; - - function listener(sender, evt, force) - { - if (force || (widthInput != document.activeElement && heightInput != document.activeElement)) - { - var detected = false; - - for (var i = 0; i < formats.length; i++) - { - var f = formats[i]; - - // Special case where custom was chosen - if (customSize) - { - if (f.key == 'custom') - { - paperSizeSelect.value = f.key; - customSize = false; - } - } - else if (f.format != null) - { - // Fixes wrong values for previous A4 and A5 page sizes - if (f.key == 'a4') - { - if (pageFormat.width == 826) - { - pageFormat = mxRectangle.fromRectangle(pageFormat); - pageFormat.width = 827; - } - else if (pageFormat.height == 826) - { - pageFormat = mxRectangle.fromRectangle(pageFormat); - pageFormat.height = 827; - } - } - else if (f.key == 'a5') - { - if (pageFormat.width == 584) - { - pageFormat = mxRectangle.fromRectangle(pageFormat); - pageFormat.width = 583; - } - else if (pageFormat.height == 584) - { - pageFormat = mxRectangle.fromRectangle(pageFormat); - pageFormat.height = 583; - } - } - - if (pageFormat.width == f.format.width && pageFormat.height == f.format.height) - { - paperSizeSelect.value = f.key; - portraitCheckBox.setAttribute('checked', 'checked'); - portraitCheckBox.defaultChecked = true; - portraitCheckBox.checked = true; - landscapeCheckBox.removeAttribute('checked'); - landscapeCheckBox.defaultChecked = false; - landscapeCheckBox.checked = false; - detected = true; - } - else if (pageFormat.width == f.format.height && pageFormat.height == f.format.width) - { - paperSizeSelect.value = f.key; - portraitCheckBox.removeAttribute('checked'); - portraitCheckBox.defaultChecked = false; - portraitCheckBox.checked = false; - landscapeCheckBox.setAttribute('checked', 'checked'); - landscapeCheckBox.defaultChecked = true; - landscapeCheckBox.checked = true; - detected = true; - } - } - } - - // Selects custom format which is last in list - if (!detected) - { - widthInput.value = pageFormat.width / 100; - heightInput.value = pageFormat.height / 100; - portraitCheckBox.setAttribute('checked', 'checked'); - paperSizeSelect.value = 'custom'; - formatDiv.style.display = 'none'; - customDiv.style.display = ''; - } - else - { - formatDiv.style.display = ''; - customDiv.style.display = 'none'; - } - } - }; - - listener(); - - div.appendChild(paperSizeSelect); - mxUtils.br(div); - - div.appendChild(formatDiv); - div.appendChild(customDiv); - - var currentPageFormat = pageFormat; - - var update = function(evt, selectChanged) - { - var f = pf[paperSizeSelect.value]; - - if (f.format != null) - { - widthInput.value = f.format.width / 100; - heightInput.value = f.format.height / 100; - customDiv.style.display = 'none'; - formatDiv.style.display = ''; - } - else - { - formatDiv.style.display = 'none'; - customDiv.style.display = ''; - } - - if (isNaN(parseFloat(widthInput.value))) - { - widthInput.value = pageFormat.width / 100; - } - - if (isNaN(parseFloat(heightInput.value))) - { - heightInput.value = pageFormat.height / 100; - } - - var newPageFormat = new mxRectangle(0, 0, - Math.floor(parseFloat(widthInput.value) * 100), - Math.floor(parseFloat(heightInput.value) * 100)); - - if (paperSizeSelect.value != 'custom' && landscapeCheckBox.checked) - { - newPageFormat = new mxRectangle(0, 0, newPageFormat.height, newPageFormat.width); - } - - // Initial select of custom should not update page format to avoid update of combo - if ((!selectChanged || !customSize) && (newPageFormat.width != currentPageFormat.width || - newPageFormat.height != currentPageFormat.height)) - { - currentPageFormat = newPageFormat; - - // Updates page format and reloads format panel - if (pageFormatListener != null) - { - pageFormatListener(currentPageFormat); - } - } - }; - - mxEvent.addListener(portraitSpan, 'click', function(evt) - { - portraitCheckBox.checked = true; - update(evt); - mxEvent.consume(evt); - }); - - mxEvent.addListener(landscapeSpan, 'click', function(evt) - { - landscapeCheckBox.checked = true; - update(evt); - mxEvent.consume(evt); - }); - - mxEvent.addListener(widthInput, 'blur', update); - mxEvent.addListener(widthInput, 'click', update); - mxEvent.addListener(heightInput, 'blur', update); - mxEvent.addListener(heightInput, 'click', update); - mxEvent.addListener(landscapeCheckBox, 'change', update); - mxEvent.addListener(portraitCheckBox, 'change', update); - mxEvent.addListener(paperSizeSelect, 'change', function(evt) - { - // Handles special case where custom was chosen - customSize = paperSizeSelect.value == 'custom'; - update(evt, true); - }); - - update(); - - return {set: function(value) - { - pageFormat = value; - listener(null, null, true); - },get: function() - { - return currentPageFormat; - }, widthInput: widthInput, - heightInput: heightInput}; -}; - -/** - * - */ -PageSetupDialog.getFormats = function() -{ - return [{key: 'letter', title: 'US-Letter (8,5" x 11")', format: mxConstants.PAGE_FORMAT_LETTER_PORTRAIT}, - {key: 'legal', title: 'US-Legal (8,5" x 14")', format: new mxRectangle(0, 0, 850, 1400)}, - {key: 'tabloid', title: 'US-Tabloid (279 mm x 432 mm)', format: new mxRectangle(0, 0, 1100, 1700)}, - {key: 'a0', title: 'A0 (841 mm x 1189 mm)', format: new mxRectangle(0, 0, 3300, 4681)}, - {key: 'a1', title: 'A1 (594 mm x 841 mm)', format: new mxRectangle(0, 0, 2339, 3300)}, - {key: 'a2', title: 'A2 (420 mm x 594 mm)', format: new mxRectangle(0, 0, 1654, 2336)}, - {key: 'a3', title: 'A3 (297 mm x 420 mm)', format: new mxRectangle(0, 0, 1169, 1654)}, - {key: 'a4', title: 'A4 (210 mm x 297 mm)', format: mxConstants.PAGE_FORMAT_A4_PORTRAIT}, - {key: 'a5', title: 'A5 (148 mm x 210 mm)', format: new mxRectangle(0, 0, 583, 827)}, - {key: 'a6', title: 'A6 (105 mm x 148 mm)', format: new mxRectangle(0, 0, 413, 583)}, - {key: 'a7', title: 'A7 (74 mm x 105 mm)', format: new mxRectangle(0, 0, 291, 413)}, - {key: 'custom', title: mxResources.get('custom'), format: null}]; -}; - -/** - * Static overrides - */ -(function() -{ - // Uses HTML for background pages (to support grid background image) - mxGraphView.prototype.validateBackgroundPage = function() - { - var graph = this.graph; - - if (graph.container != null && !graph.transparentBackground) - { - if (graph.pageVisible) - { - var bounds = this.getBackgroundPageBounds(); - - if (this.backgroundPageShape == null) - { - // Finds first element in graph container - var firstChild = graph.container.firstChild; - - while (firstChild != null && firstChild.nodeType != mxConstants.NODETYPE_ELEMENT) - { - firstChild = firstChild.nextSibling; - } - - if (firstChild != null) - { - this.backgroundPageShape = this.createBackgroundPageShape(bounds); - this.backgroundPageShape.scale = 1; - - // Shadow filter causes problems in outline window in quirks mode. IE8 standards - // also has known rendering issues inside mxWindow but not using shadow is worse. - this.backgroundPageShape.isShadow = !mxClient.IS_QUIRKS; - this.backgroundPageShape.dialect = mxConstants.DIALECT_STRICTHTML; - this.backgroundPageShape.init(graph.container); - - // Required for the browser to render the background page in correct order - firstChild.style.position = 'absolute'; - graph.container.insertBefore(this.backgroundPageShape.node, firstChild); - this.backgroundPageShape.redraw(); - - this.backgroundPageShape.node.className = 'geBackgroundPage'; - - // Adds listener for double click handling on background - mxEvent.addListener(this.backgroundPageShape.node, 'dblclick', - mxUtils.bind(this, function(evt) - { - graph.dblClick(evt); - }) - ); - - // Adds basic listeners for graph event dispatching outside of the - // container and finishing the handling of a single gesture - mxEvent.addGestureListeners(this.backgroundPageShape.node, - mxUtils.bind(this, function(evt) - { - graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt)); - }), - mxUtils.bind(this, function(evt) - { - // Hides the tooltip if mouse is outside container - if (graph.tooltipHandler != null && graph.tooltipHandler.isHideOnHover()) - { - graph.tooltipHandler.hide(); - } - - if (graph.isMouseDown && !mxEvent.isConsumed(evt)) - { - graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt)); - } - }), - mxUtils.bind(this, function(evt) - { - graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt)); - }) - ); - } - } - else - { - this.backgroundPageShape.scale = 1; - this.backgroundPageShape.bounds = bounds; - this.backgroundPageShape.redraw(); - } - } - else if (this.backgroundPageShape != null) - { - this.backgroundPageShape.destroy(); - this.backgroundPageShape = null; - } - - this.validateBackgroundStyles(); - } - }; - - // Updates the CSS of the background to draw the grid - mxGraphView.prototype.validateBackgroundStyles = function() - { - var graph = this.graph; - var color = (graph.background == null || graph.background == mxConstants.NONE) ? graph.defaultPageBackgroundColor : graph.background; - var gridColor = (color != null && this.gridColor != color.toLowerCase()) ? this.gridColor : '#ffffff'; - var image = 'none'; - var position = ''; - - if (graph.isGridEnabled()) - { - var phase = 10; - - if (mxClient.IS_SVG) - { - // Generates the SVG required for drawing the dynamic grid - image = unescape(encodeURIComponent(this.createSvgGrid(gridColor))); - image = (window.btoa) ? btoa(image) : Base64.encode(image, true); - image = 'url(' + 'data:image/svg+xml;base64,' + image + ')' - phase = graph.gridSize * this.scale * this.gridSteps; - } - else - { - // Fallback to grid wallpaper with fixed size - image = 'url(' + this.gridImage + ')'; - } - - var x0 = 0; - var y0 = 0; - - if (graph.view.backgroundPageShape != null) - { - var bds = this.getBackgroundPageBounds(); - - x0 = 1 + bds.x; - y0 = 1 + bds.y; - } - - // Computes the offset to maintain origin for grid - position = -Math.round(phase - mxUtils.mod(this.translate.x * this.scale - x0, phase)) + 'px ' + - -Math.round(phase - mxUtils.mod(this.translate.y * this.scale - y0, phase)) + 'px'; - } - - var canvas = graph.view.canvas; - - if (canvas.ownerSVGElement != null) - { - canvas = canvas.ownerSVGElement; - } - - if (graph.view.backgroundPageShape != null) - { - graph.view.backgroundPageShape.node.style.backgroundPosition = position; - graph.view.backgroundPageShape.node.style.backgroundImage = image; - graph.view.backgroundPageShape.node.style.backgroundColor = color; - graph.container.className = 'geDiagramContainer geDiagramBackdrop'; - canvas.style.backgroundImage = 'none'; - canvas.style.backgroundColor = ''; - } - else - { - graph.container.className = 'geDiagramContainer'; - canvas.style.backgroundPosition = position; - canvas.style.backgroundColor = color; - canvas.style.backgroundImage = image; - } - }; - - // Returns the SVG required for painting the background grid. - mxGraphView.prototype.createSvgGrid = function(color) - { - var tmp = this.graph.gridSize * this.scale; - - while (tmp < this.minGridSize) - { - tmp *= 2; - } - - var tmp2 = this.gridSteps * tmp; - - // Small grid lines - var d = []; - - for (var i = 1; i < this.gridSteps; i++) - { - var tmp3 = i * tmp; - d.push('M 0 ' + tmp3 + ' L ' + tmp2 + ' ' + tmp3 + ' M ' + tmp3 + ' 0 L ' + tmp3 + ' ' + tmp2); - } - - // KNOWN: Rounding errors for certain scales (eg. 144%, 121% in Chrome, FF and Safari). Workaround - // in Chrome is to use 100% for the svg size, but this results in blurred grid for large diagrams. - var size = tmp2; - var svg = '' + - '' + - '' + - '' + - ''; - - return svg; - }; - - // Adds panning for the grid with no page view and disabled scrollbars - var mxGraphPanGraph = mxGraph.prototype.panGraph; - mxGraph.prototype.panGraph = function(dx, dy) - { - mxGraphPanGraph.apply(this, arguments); - - if (this.shiftPreview1 != null) - { - var canvas = this.view.canvas; - - if (canvas.ownerSVGElement != null) - { - canvas = canvas.ownerSVGElement; - } - - var phase = this.gridSize * this.view.scale * this.view.gridSteps; - var position = -Math.round(phase - mxUtils.mod(this.view.translate.x * this.view.scale + dx, phase)) + 'px ' + - -Math.round(phase - mxUtils.mod(this.view.translate.y * this.view.scale + dy, phase)) + 'px'; - canvas.style.backgroundPosition = position; - } - }; - - // Draws page breaks only within the page - mxGraph.prototype.updatePageBreaks = function(visible, width, height) - { - var scale = this.view.scale; - var tr = this.view.translate; - var fmt = this.pageFormat; - var ps = scale * this.pageScale; - - var bounds2 = this.view.getBackgroundPageBounds(); - - width = bounds2.width; - height = bounds2.height; - var bounds = new mxRectangle(scale * tr.x, scale * tr.y, fmt.width * ps, fmt.height * ps); - - // Does not show page breaks if the scale is too small - visible = visible && Math.min(bounds.width, bounds.height) > this.minPageBreakDist; - - var horizontalCount = (visible) ? Math.ceil(height / bounds.height) - 1 : 0; - var verticalCount = (visible) ? Math.ceil(width / bounds.width) - 1 : 0; - var right = bounds2.x + width; - var bottom = bounds2.y + height; - - if (this.horizontalPageBreaks == null && horizontalCount > 0) - { - this.horizontalPageBreaks = []; - } - - if (this.verticalPageBreaks == null && verticalCount > 0) - { - this.verticalPageBreaks = []; - } - - var drawPageBreaks = mxUtils.bind(this, function(breaks) - { - if (breaks != null) - { - var count = (breaks == this.horizontalPageBreaks) ? horizontalCount : verticalCount; - - for (var i = 0; i <= count; i++) - { - var pts = (breaks == this.horizontalPageBreaks) ? - [new mxPoint(Math.round(bounds2.x), Math.round(bounds2.y + (i + 1) * bounds.height)), - new mxPoint(Math.round(right), Math.round(bounds2.y + (i + 1) * bounds.height))] : - [new mxPoint(Math.round(bounds2.x + (i + 1) * bounds.width), Math.round(bounds2.y)), - new mxPoint(Math.round(bounds2.x + (i + 1) * bounds.width), Math.round(bottom))]; - - if (breaks[i] != null) - { - breaks[i].points = pts; - breaks[i].redraw(); - } - else - { - var pageBreak = new mxPolyline(pts, this.pageBreakColor); - pageBreak.dialect = this.dialect; - pageBreak.isDashed = this.pageBreakDashed; - pageBreak.pointerEvents = false; - pageBreak.init(this.view.backgroundPane); - pageBreak.redraw(); - - breaks[i] = pageBreak; - } - } - - for (var i = count; i < breaks.length; i++) - { - breaks[i].destroy(); - } - - breaks.splice(count, breaks.length - count); - } - }); - - drawPageBreaks(this.horizontalPageBreaks); - drawPageBreaks(this.verticalPageBreaks); - }; - - // Disables removing relative children from parents - var mxGraphHandlerShouldRemoveCellsFromParent = mxGraphHandler.prototype.shouldRemoveCellsFromParent; - mxGraphHandler.prototype.shouldRemoveCellsFromParent = function(parent, cells, evt) - { - for (var i = 0; i < cells.length; i++) - { - if (this.graph.getModel().isVertex(cells[i])) - { - var geo = this.graph.getCellGeometry(cells[i]); - - if (geo != null && geo.relative) - { - return false; - } - } - } - - return mxGraphHandlerShouldRemoveCellsFromParent.apply(this, arguments); - }; - - // Overrides to ignore hotspot only for target terminal - var mxConnectionHandlerCreateMarker = mxConnectionHandler.prototype.createMarker; - mxConnectionHandler.prototype.createMarker = function() - { - var marker = mxConnectionHandlerCreateMarker.apply(this, arguments); - - marker.intersects = mxUtils.bind(this, function(state, evt) - { - if (this.isConnecting()) - { - return true; - } - - return mxCellMarker.prototype.intersects.apply(marker, arguments); - }); - - return marker; - }; - - // Creates background page shape - mxGraphView.prototype.createBackgroundPageShape = function(bounds) - { - return new mxRectangleShape(bounds, '#ffffff', this.graph.defaultPageBorderColor); - }; - - // Fits the number of background pages to the graph - mxGraphView.prototype.getBackgroundPageBounds = function() - { - var gb = this.getGraphBounds(); - - // Computes unscaled, untranslated graph bounds - var x = (gb.width > 0) ? gb.x / this.scale - this.translate.x : 0; - var y = (gb.height > 0) ? gb.y / this.scale - this.translate.y : 0; - var w = gb.width / this.scale; - var h = gb.height / this.scale; - - var fmt = this.graph.pageFormat; - var ps = this.graph.pageScale; - - var pw = fmt.width * ps; - var ph = fmt.height * ps; - - var x0 = Math.floor(Math.min(0, x) / pw); - var y0 = Math.floor(Math.min(0, y) / ph); - var xe = Math.ceil(Math.max(1, x + w) / pw); - var ye = Math.ceil(Math.max(1, y + h) / ph); - - var rows = xe - x0; - var cols = ye - y0; - - var bounds = new mxRectangle(this.scale * (this.translate.x + x0 * pw), this.scale * - (this.translate.y + y0 * ph), this.scale * rows * pw, this.scale * cols * ph); - - return bounds; - }; - - // Add panning for background page in VML - var graphPanGraph = mxGraph.prototype.panGraph; - mxGraph.prototype.panGraph = function(dx, dy) - { - graphPanGraph.apply(this, arguments); - - if ((this.dialect != mxConstants.DIALECT_SVG && this.view.backgroundPageShape != null) && - (!this.useScrollbarsForPanning || !mxUtils.hasScrollbars(this.container))) - { - this.view.backgroundPageShape.node.style.marginLeft = dx + 'px'; - this.view.backgroundPageShape.node.style.marginTop = dy + 'px'; - } - }; - - /** - * Consumes click events for disabled menu items. - */ - var mxPopupMenuAddItem = mxPopupMenu.prototype.addItem; - mxPopupMenu.prototype.addItem = function(title, image, funct, parent, iconCls, enabled) - { - var result = mxPopupMenuAddItem.apply(this, arguments); - - if (enabled != null && !enabled) - { - mxEvent.addListener(result, 'mousedown', function(evt) - { - mxEvent.consume(evt); - }); - } - - return result; - }; - - // Selects ancestors before descendants - var graphHandlerGetInitialCellForEvent = mxGraphHandler.prototype.getInitialCellForEvent; - mxGraphHandler.prototype.getInitialCellForEvent = function(me) - { - var model = this.graph.getModel(); - var psel = model.getParent(this.graph.getSelectionCell()); - var cell = graphHandlerGetInitialCellForEvent.apply(this, arguments); - var parent = model.getParent(cell); - - if (psel == null || (psel != cell && psel != parent)) - { - while (!this.graph.isCellSelected(cell) && !this.graph.isCellSelected(parent) && - model.isVertex(parent) && !this.graph.isContainer(parent)) - { - cell = parent; - parent = this.graph.getModel().getParent(cell); - } - } - - return cell; - }; - - // Selection is delayed to mouseup if ancestor is selected - var graphHandlerIsDelayedSelection = mxGraphHandler.prototype.isDelayedSelection; - mxGraphHandler.prototype.isDelayedSelection = function(cell, me) - { - var result = graphHandlerIsDelayedSelection.apply(this, arguments); - - if (!result) - { - var model = this.graph.getModel(); - var parent = model.getParent(cell); - - while (parent != null) - { - // Inconsistency for unselected parent swimlane is intended for easier moving - // of stack layouts where the container title section is too far away - if (this.graph.isCellSelected(parent) && model.isVertex(parent)) - { - result = true; - break; - } - - parent = model.getParent(parent); - } - } - - return result; - }; - - // Delayed selection of parent group - mxGraphHandler.prototype.selectDelayed = function(me) - { - if (!this.graph.popupMenuHandler.isPopupTrigger(me)) - { - var cell = me.getCell(); - - if (cell == null) - { - cell = this.cell; - } - - // Selects folded cell for hit on folding icon - var state = this.graph.view.getState(cell) - - if (state != null && me.isSource(state.control)) - { - this.graph.selectCellForEvent(cell, me.getEvent()); - } - else - { - var model = this.graph.getModel(); - var parent = model.getParent(cell); - - while (!this.graph.isCellSelected(parent) && model.isVertex(parent)) - { - cell = parent; - parent = model.getParent(cell); - } - - this.graph.selectCellForEvent(cell, me.getEvent()); - } - } - }; - - // Returns last selected ancestor - mxPopupMenuHandler.prototype.getCellForPopupEvent = function(me) - { - var cell = me.getCell(); - var model = this.graph.getModel(); - var parent = model.getParent(cell); - - while (model.isVertex(parent) && !this.graph.isContainer(parent)) - { - if (this.graph.isCellSelected(parent)) - { - cell = parent; - } - - parent = model.getParent(parent); - } - - return cell; - }; - -})(); diff --git a/media/grapheditor/js/EditorUi.js b/media/grapheditor/js/EditorUi.js deleted file mode 100644 index 9e3eb711f3..0000000000 --- a/media/grapheditor/js/EditorUi.js +++ /dev/null @@ -1,4216 +0,0 @@ -/** - * Copyright (c) 2006-2012, JGraph Ltd - */ -/** - * Constructs a new graph editor - */ -EditorUi = function(editor, container, lightbox) -{ - mxEventSource.call(this); - - this.destroyFunctions = []; - this.editor = editor || new Editor(); - this.container = container || document.body; - - var graph = this.editor.graph; - graph.lightbox = lightbox; - - // Faster scrollwheel zoom is possible with CSS transforms - if (graph.useCssTransforms) - { - this.lazyZoomDelay = 0; - } - - // Pre-fetches submenu image or replaces with embedded image if supported - if (mxClient.IS_SVG) - { - mxPopupMenu.prototype.submenuImage = 'data:image/gif;base64,R0lGODlhCQAJAIAAAP///zMzMyH5BAEAAAAALAAAAAAJAAkAAAIPhI8WebHsHopSOVgb26AAADs='; - } - else - { - new Image().src = mxPopupMenu.prototype.submenuImage; - } - - // Pre-fetches connect image - if (!mxClient.IS_SVG && mxConnectionHandler.prototype.connectImage != null) - { - new Image().src = mxConnectionHandler.prototype.connectImage.src; - } - - // Disables graph and forced panning in chromeless mode - if (this.editor.chromeless && !this.editor.editable) - { - this.footerHeight = 0; - graph.isEnabled = function() { return false; }; - graph.panningHandler.isForcePanningEvent = function(me) - { - return !mxEvent.isPopupTrigger(me.getEvent()); - }; - } - - // Creates the user interface - this.actions = new Actions(this); - this.menus = this.createMenus(); - this.createDivs(); - this.createUi(); - this.refresh(); - - // Disables HTML and text selection - var textEditing = mxUtils.bind(this, function(evt) - { - if (evt == null) - { - evt = window.event; - } - - return (this.isSelectionAllowed(evt) || graph.isEditing()); - }); - - // Disables text selection while not editing and no dialog visible - if (this.container == document.body) - { - this.menubarContainer.onselectstart = textEditing; - this.menubarContainer.onmousedown = textEditing; - this.toolbarContainer.onselectstart = textEditing; - this.toolbarContainer.onmousedown = textEditing; - this.diagramContainer.onselectstart = textEditing; - this.diagramContainer.onmousedown = textEditing; - this.sidebarContainer.onselectstart = textEditing; - this.sidebarContainer.onmousedown = textEditing; - this.formatContainer.onselectstart = textEditing; - this.formatContainer.onmousedown = textEditing; - this.footerContainer.onselectstart = textEditing; - this.footerContainer.onmousedown = textEditing; - - if (this.tabContainer != null) - { - // Mouse down is needed for drag and drop - this.tabContainer.onselectstart = textEditing; - } - } - - // And uses built-in context menu while editing - if (!this.editor.chromeless || this.editor.editable) - { - // Allows context menu for links in hints - var linkHandler = function(evt) - { - var source = mxEvent.getSource(evt); - - if (source.nodeName == 'A') - { - while (source != null) - { - if (source.className == 'geHint') - { - return true; - } - - source = source.parentNode; - } - } - - return textEditing(evt); - }; - - if (mxClient.IS_IE && (typeof(document.documentMode) === 'undefined' || document.documentMode < 9)) - { - mxEvent.addListener(this.diagramContainer, 'contextmenu', linkHandler); - } - else - { - // Allows browser context menu outside of diagram and sidebar - this.diagramContainer.oncontextmenu = linkHandler; - } - } - else - { - graph.panningHandler.usePopupTrigger = false; - } - - // Contains the main graph instance inside the given panel - graph.init(this.diagramContainer); - - // Improves line wrapping for in-place editor - if (mxClient.IS_SVG && graph.view.getDrawPane() != null) - { - var root = graph.view.getDrawPane().ownerSVGElement; - - if (root != null) - { - root.style.position = 'absolute'; - } - } - - // Creates hover icons - this.hoverIcons = this.createHoverIcons(); - - // Adds tooltip when mouse is over scrollbars to show space-drag panning option - mxEvent.addListener(this.diagramContainer, 'mousemove', mxUtils.bind(this, function(evt) - { - var off = mxUtils.getOffset(this.diagramContainer); - - if (mxEvent.getClientX(evt) - off.x - this.diagramContainer.clientWidth > 0 || - mxEvent.getClientY(evt) - off.y - this.diagramContainer.clientHeight > 0) - { - this.diagramContainer.setAttribute('title', mxResources.get('panTooltip')); - } - else - { - this.diagramContainer.removeAttribute('title'); - } - })); - - // Escape key hides dialogs, adds space+drag panning - var spaceKeyPressed = false; - - // Overrides hovericons to disable while space key is pressed - var hoverIconsIsResetEvent = this.hoverIcons.isResetEvent; - - this.hoverIcons.isResetEvent = function(evt, allowShift) - { - return spaceKeyPressed || hoverIconsIsResetEvent.apply(this, arguments); - }; - - this.keydownHandler = mxUtils.bind(this, function(evt) - { - if (evt.which == 32 /* Space */) - { - spaceKeyPressed = true; - this.hoverIcons.reset(); - graph.container.style.cursor = 'move'; - - // Disables scroll after space keystroke with scrollbars - if (!graph.isEditing() && mxEvent.getSource(evt) == graph.container) - { - mxEvent.consume(evt); - } - } - else if (!mxEvent.isConsumed(evt) && evt.keyCode == 27 /* Escape */) - { - this.hideDialog(); - } - }); - - mxEvent.addListener(document, 'keydown', this.keydownHandler); - - this.keyupHandler = mxUtils.bind(this, function(evt) - { - graph.container.style.cursor = ''; - spaceKeyPressed = false; - }); - - mxEvent.addListener(document, 'keyup', this.keyupHandler); - - // Forces panning for middle and right mouse buttons - var panningHandlerIsForcePanningEvent = graph.panningHandler.isForcePanningEvent; - graph.panningHandler.isForcePanningEvent = function(me) - { - // Ctrl+left button is reported as right button in FF on Mac - return panningHandlerIsForcePanningEvent.apply(this, arguments) || - spaceKeyPressed || (mxEvent.isMouseEvent(me.getEvent()) && - (this.usePopupTrigger || !mxEvent.isPopupTrigger(me.getEvent())) && - ((!mxEvent.isControlDown(me.getEvent()) && - mxEvent.isRightMouseButton(me.getEvent())) || - mxEvent.isMiddleMouseButton(me.getEvent()))); - }; - - // Ctrl/Cmd+Enter applies editing value except in Safari where Ctrl+Enter creates - // a new line (while Enter creates a new paragraph and Shift+Enter stops) - var cellEditorIsStopEditingEvent = graph.cellEditor.isStopEditingEvent; - graph.cellEditor.isStopEditingEvent = function(evt) - { - return cellEditorIsStopEditingEvent.apply(this, arguments) || - (evt.keyCode == 13 && ((!mxClient.IS_SF && mxEvent.isControlDown(evt)) || - (mxClient.IS_MAC && mxEvent.isMetaDown(evt)) || - (mxClient.IS_SF && mxEvent.isShiftDown(evt)))); - }; - - // Switches toolbar for text editing - var textMode = false; - var fontMenu = null; - var sizeMenu = null; - var nodes = null; - - var updateToolbar = mxUtils.bind(this, function() - { - if (this.toolbar != null && textMode != graph.cellEditor.isContentEditing()) - { - var node = this.toolbar.container.firstChild; - var newNodes = []; - - while (node != null) - { - var tmp = node.nextSibling; - - if (mxUtils.indexOf(this.toolbar.staticElements, node) < 0) - { - node.parentNode.removeChild(node); - newNodes.push(node); - } - - node = tmp; - } - - // Saves references to special items - var tmp1 = this.toolbar.fontMenu; - var tmp2 = this.toolbar.sizeMenu; - - if (nodes == null) - { - this.toolbar.createTextToolbar(); - } - else - { - for (var i = 0; i < nodes.length; i++) - { - this.toolbar.container.appendChild(nodes[i]); - } - - // Restores references to special items - this.toolbar.fontMenu = fontMenu; - this.toolbar.sizeMenu = sizeMenu; - } - - textMode = graph.cellEditor.isContentEditing(); - fontMenu = tmp1; - sizeMenu = tmp2; - nodes = newNodes; - } - }); - - var ui = this; - - // Overrides cell editor to update toolbar - var cellEditorStartEditing = graph.cellEditor.startEditing; - graph.cellEditor.startEditing = function() - { - cellEditorStartEditing.apply(this, arguments); - updateToolbar(); - - if (graph.cellEditor.isContentEditing()) - { - var updating = false; - - var updateCssHandler = function() - { - if (!updating) - { - updating = true; - - window.setTimeout(function() - { - var selectedElement = graph.getSelectedElement(); - var node = selectedElement; - - while (node != null && node.nodeType != mxConstants.NODETYPE_ELEMENT) - { - node = node.parentNode; - } - - if (node != null) - { - var css = mxUtils.getCurrentStyle(node); - - if (css != null && ui.toolbar != null) - { - // Strips leading and trailing quotes - var ff = css.fontFamily; - - if (ff.charAt(0) == '\'') - { - ff = ff.substring(1); - } - - if (ff.charAt(ff.length - 1) == '\'') - { - ff = ff.substring(0, ff.length - 1); - } - - ui.toolbar.setFontName(ff); - ui.toolbar.setFontSize(parseInt(css.fontSize)); - } - } - - updating = false; - }, 0); - } - }; - - mxEvent.addListener(graph.cellEditor.textarea, 'input', updateCssHandler) - mxEvent.addListener(graph.cellEditor.textarea, 'touchend', updateCssHandler); - mxEvent.addListener(graph.cellEditor.textarea, 'mouseup', updateCssHandler); - mxEvent.addListener(graph.cellEditor.textarea, 'keyup', updateCssHandler); - updateCssHandler(); - } - }; - - var cellEditorStopEditing = graph.cellEditor.stopEditing; - graph.cellEditor.stopEditing = function(cell, trigger) - { - cellEditorStopEditing.apply(this, arguments); - updateToolbar(); - }; - - // Enables scrollbars and sets cursor style for the container - graph.container.setAttribute('tabindex', '0'); - graph.container.style.cursor = 'default'; - - // Workaround for page scroll if embedded via iframe - if (window.self === window.top && graph.container.parentNode != null) - { - try - { - graph.container.focus(); - } - catch (e) - { - // ignores error in old versions of IE - } - } - - // Keeps graph container focused on mouse down - var graphFireMouseEvent = graph.fireMouseEvent; - graph.fireMouseEvent = function(evtName, me, sender) - { - if (evtName == mxEvent.MOUSE_DOWN) - { - this.container.focus(); - } - - graphFireMouseEvent.apply(this, arguments); - }; - - // Configures automatic expand on mouseover - graph.popupMenuHandler.autoExpand = true; - - // Installs context menu - if (this.menus != null) - { - graph.popupMenuHandler.factoryMethod = mxUtils.bind(this, function(menu, cell, evt) - { - this.menus.createPopupMenu(menu, cell, evt); - }); - } - - // Hides context menu - mxEvent.addGestureListeners(document, mxUtils.bind(this, function(evt) - { - graph.popupMenuHandler.hideMenu(); - })); - - // Create handler for key events - this.keyHandler = this.createKeyHandler(editor); - - // Getter for key handler - this.getKeyHandler = function() - { - return keyHandler; - }; - - // Stores the current style and assigns it to new cells - var styles = ['rounded', 'shadow', 'glass', 'dashed', 'dashPattern', 'comic', 'labelBackgroundColor']; - var connectStyles = ['shape', 'edgeStyle', 'curved', 'rounded', 'elbow', 'comic', 'jumpStyle', 'jumpSize']; - - // Note: Everything that is not in styles is ignored (styles is augmented below) - this.setDefaultStyle = function(cell) - { - var state = graph.view.getState(cell); - - if (state != null) - { - // Ignores default styles - var clone = cell.clone(); - clone.style = '' - var defaultStyle = graph.getCellStyle(clone); - var values = []; - var keys = []; - - for (var key in state.style) - { - if (defaultStyle[key] != state.style[key]) - { - values.push(state.style[key]); - keys.push(key); - } - } - - // Handles special case for value "none" - var cellStyle = graph.getModel().getStyle(state.cell); - var tokens = (cellStyle != null) ? cellStyle.split(';') : []; - - for (var i = 0; i < tokens.length; i++) - { - var tmp = tokens[i]; - var pos = tmp.indexOf('='); - - if (pos >= 0) - { - var key = tmp.substring(0, pos); - var value = tmp.substring(pos + 1); - - if (defaultStyle[key] != null && value == 'none') - { - values.push(value); - keys.push(key); - } - } - } - - // Resets current style - if (graph.getModel().isEdge(state.cell)) - { - graph.currentEdgeStyle = {}; - } - else - { - graph.currentVertexStyle = {} - } - - this.fireEvent(new mxEventObject('styleChanged', 'keys', keys, 'values', values, 'cells', [state.cell])); - } - }; - - this.clearDefaultStyle = function() - { - graph.currentEdgeStyle = mxUtils.clone(graph.defaultEdgeStyle); - graph.currentVertexStyle = mxUtils.clone(graph.defaultVertexStyle); - - // Updates UI - this.fireEvent(new mxEventObject('styleChanged', 'keys', [], 'values', [], 'cells', [])); - }; - - // Keys that should be ignored if the cell has a value (known: new default for all cells is html=1 so - // for the html key this effecticely only works for edges inserted via the connection handler) - var valueStyles = ['fontFamily', 'fontSize', 'fontColor']; - - // Keys that always update the current edge style regardless of selection - var alwaysEdgeStyles = ['edgeStyle', 'startArrow', 'startFill', 'startSize', 'endArrow', - 'endFill', 'endSize', 'jettySize', 'orthogonalLoop']; - - // Keys that are ignored together (if one appears all are ignored) - var keyGroups = [['startArrow', 'startFill', 'startSize', 'endArrow', 'endFill', 'endSize', 'jettySize', 'orthogonalLoop'], - ['strokeColor', 'strokeWidth'], - ['fillColor', 'gradientColor'], - valueStyles, - ['opacity'], - ['align'], - ['html']]; - - // Adds all keys used above to the styles array - for (var i = 0; i < keyGroups.length; i++) - { - for (var j = 0; j < keyGroups[i].length; j++) - { - styles.push(keyGroups[i][j]); - } - } - - for (var i = 0; i < connectStyles.length; i++) - { - if (mxUtils.indexOf(styles, connectStyles[i]) < 0) - { - styles.push(connectStyles[i]); - } - } - - // Implements a global current style for edges and vertices that is applied to new cells - var insertHandler = function(cells, asText) - { - var model = graph.getModel(); - - model.beginUpdate(); - try - { - // Applies only basic text styles - if (asText) - { - var edge = model.isEdge(cell); - var current = (edge) ? graph.currentEdgeStyle : graph.currentVertexStyle; - var textStyles = ['fontSize', 'fontFamily', 'fontColor']; - - for (var j = 0; j < textStyles.length; j++) - { - var value = current[textStyles[j]]; - - if (value != null) - { - graph.setCellStyles(textStyles[j], value, cells); - } - } - } - else - { - for (var i = 0; i < cells.length; i++) - { - var cell = cells[i]; - - // Removes styles defined in the cell style from the styles to be applied - var cellStyle = model.getStyle(cell); - var tokens = (cellStyle != null) ? cellStyle.split(';') : []; - var appliedStyles = styles.slice(); - - for (var j = 0; j < tokens.length; j++) - { - var tmp = tokens[j]; - var pos = tmp.indexOf('='); - - if (pos >= 0) - { - var key = tmp.substring(0, pos); - var index = mxUtils.indexOf(appliedStyles, key); - - if (index >= 0) - { - appliedStyles.splice(index, 1); - } - - // Handles special cases where one defined style ignores other styles - for (var k = 0; k < keyGroups.length; k++) - { - var group = keyGroups[k]; - - if (mxUtils.indexOf(group, key) >= 0) - { - for (var l = 0; l < group.length; l++) - { - var index2 = mxUtils.indexOf(appliedStyles, group[l]); - - if (index2 >= 0) - { - appliedStyles.splice(index2, 1); - } - } - } - } - } - } - - // Applies the current style to the cell - var edge = model.isEdge(cell); - var current = (edge) ? graph.currentEdgeStyle : graph.currentVertexStyle; - var newStyle = model.getStyle(cell); - - for (var j = 0; j < appliedStyles.length; j++) - { - var key = appliedStyles[j]; - var styleValue = current[key]; - - if (styleValue != null && (key != 'shape' || edge)) - { - // Special case: Connect styles are not applied here but in the connection handler - if (!edge || mxUtils.indexOf(connectStyles, key) < 0) - { - newStyle = mxUtils.setStyle(newStyle, key, styleValue); - } - } - } - - model.setStyle(cell, newStyle); - } - } - } - finally - { - model.endUpdate(); - } - }; - - graph.addListener('cellsInserted', function(sender, evt) - { - insertHandler(evt.getProperty('cells')); - }); - - graph.addListener('textInserted', function(sender, evt) - { - insertHandler(evt.getProperty('cells'), true); - }); - - graph.connectionHandler.addListener(mxEvent.CONNECT, function(sender, evt) - { - var cells = [evt.getProperty('cell')]; - - if (evt.getProperty('terminalInserted')) - { - cells.push(evt.getProperty('terminal')); - } - - insertHandler(cells); - }); - - this.addListener('styleChanged', mxUtils.bind(this, function(sender, evt) - { - // Checks if edges and/or vertices were modified - var cells = evt.getProperty('cells'); - var vertex = false; - var edge = false; - - if (cells.length > 0) - { - for (var i = 0; i < cells.length; i++) - { - vertex = graph.getModel().isVertex(cells[i]) || vertex; - edge = graph.getModel().isEdge(cells[i]) || edge; - - if (edge && vertex) - { - break; - } - } - } - else - { - vertex = true; - edge = true; - } - - var keys = evt.getProperty('keys'); - var values = evt.getProperty('values'); - - for (var i = 0; i < keys.length; i++) - { - var common = mxUtils.indexOf(valueStyles, keys[i]) >= 0; - - // Ignores transparent stroke colors - if (keys[i] != 'strokeColor' || (values[i] != null && values[i] != 'none')) - { - // Special case: Edge style and shape - if (mxUtils.indexOf(connectStyles, keys[i]) >= 0) - { - if (edge || mxUtils.indexOf(alwaysEdgeStyles, keys[i]) >= 0) - { - if (values[i] == null) - { - delete graph.currentEdgeStyle[keys[i]]; - } - else - { - graph.currentEdgeStyle[keys[i]] = values[i]; - } - } - // Uses style for vertex if defined in styles - else if (vertex && mxUtils.indexOf(styles, keys[i]) >= 0) - { - if (values[i] == null) - { - delete graph.currentVertexStyle[keys[i]]; - } - else - { - graph.currentVertexStyle[keys[i]] = values[i]; - } - } - } - else if (mxUtils.indexOf(styles, keys[i]) >= 0) - { - if (vertex || common) - { - if (values[i] == null) - { - delete graph.currentVertexStyle[keys[i]]; - } - else - { - graph.currentVertexStyle[keys[i]] = values[i]; - } - } - - if (edge || common || mxUtils.indexOf(alwaysEdgeStyles, keys[i]) >= 0) - { - if (values[i] == null) - { - delete graph.currentEdgeStyle[keys[i]]; - } - else - { - graph.currentEdgeStyle[keys[i]] = values[i]; - } - } - } - } - } - - if (this.toolbar != null) - { - this.toolbar.setFontName(graph.currentVertexStyle['fontFamily'] || Menus.prototype.defaultFont); - this.toolbar.setFontSize(graph.currentVertexStyle['fontSize'] || Menus.prototype.defaultFontSize); - - if (this.toolbar.edgeStyleMenu != null) - { - // Updates toolbar icon for edge style - var edgeStyleDiv = this.toolbar.edgeStyleMenu.getElementsByTagName('div')[0]; - - if (graph.currentEdgeStyle['edgeStyle'] == 'orthogonalEdgeStyle' && graph.currentEdgeStyle['curved'] == '1') - { - edgeStyleDiv.className = 'geSprite geSprite-curved'; - } - else if (graph.currentEdgeStyle['edgeStyle'] == 'straight' || graph.currentEdgeStyle['edgeStyle'] == 'none' || - graph.currentEdgeStyle['edgeStyle'] == null) - { - edgeStyleDiv.className = 'geSprite geSprite-straight'; - } - else if (graph.currentEdgeStyle['edgeStyle'] == 'entityRelationEdgeStyle') - { - edgeStyleDiv.className = 'geSprite geSprite-entity'; - } - else if (graph.currentEdgeStyle['edgeStyle'] == 'elbowEdgeStyle') - { - edgeStyleDiv.className = 'geSprite geSprite-' + ((graph.currentEdgeStyle['elbow'] == 'vertical') ? - 'verticalelbow' : 'horizontalelbow'); - } - else if (graph.currentEdgeStyle['edgeStyle'] == 'isometricEdgeStyle') - { - edgeStyleDiv.className = 'geSprite geSprite-' + ((graph.currentEdgeStyle['elbow'] == 'vertical') ? - 'verticalisometric' : 'horizontalisometric'); - } - else - { - edgeStyleDiv.className = 'geSprite geSprite-orthogonal'; - } - } - - if (this.toolbar.edgeShapeMenu != null) - { - // Updates icon for edge shape - var edgeShapeDiv = this.toolbar.edgeShapeMenu.getElementsByTagName('div')[0]; - - if (graph.currentEdgeStyle['shape'] == 'link') - { - edgeShapeDiv.className = 'geSprite geSprite-linkedge'; - } - else if (graph.currentEdgeStyle['shape'] == 'flexArrow') - { - edgeShapeDiv.className = 'geSprite geSprite-arrow'; - } - else if (graph.currentEdgeStyle['shape'] == 'arrow') - { - edgeShapeDiv.className = 'geSprite geSprite-simplearrow'; - } - else - { - edgeShapeDiv.className = 'geSprite geSprite-connection'; - } - } - - // Updates icon for optinal line start shape - if (this.toolbar.lineStartMenu != null) - { - var lineStartDiv = this.toolbar.lineStartMenu.getElementsByTagName('div')[0]; - - lineStartDiv.className = this.getCssClassForMarker('start', - graph.currentEdgeStyle['shape'], graph.currentEdgeStyle[mxConstants.STYLE_STARTARROW], - mxUtils.getValue(graph.currentEdgeStyle, 'startFill', '1')); - } - - // Updates icon for optinal line end shape - if (this.toolbar.lineEndMenu != null) - { - var lineEndDiv = this.toolbar.lineEndMenu.getElementsByTagName('div')[0]; - - lineEndDiv.className = this.getCssClassForMarker('end', - graph.currentEdgeStyle['shape'], graph.currentEdgeStyle[mxConstants.STYLE_ENDARROW], - mxUtils.getValue(graph.currentEdgeStyle, 'endFill', '1')); - } - } - })); - - // Update font size and font family labels - if (this.toolbar != null) - { - var update = mxUtils.bind(this, function() - { - var ff = graph.currentVertexStyle['fontFamily'] || 'Helvetica'; - var fs = String(graph.currentVertexStyle['fontSize'] || '12'); - var state = graph.getView().getState(graph.getSelectionCell()); - - if (state != null) - { - ff = state.style[mxConstants.STYLE_FONTFAMILY] || ff; - fs = state.style[mxConstants.STYLE_FONTSIZE] || fs; - - if (ff.length > 10) - { - ff = ff.substring(0, 8) + '...'; - } - } - - this.toolbar.setFontName(ff); - this.toolbar.setFontSize(fs); - }); - - graph.getSelectionModel().addListener(mxEvent.CHANGE, update); - graph.getModel().addListener(mxEvent.CHANGE, update); - } - - // Makes sure the current layer is visible when cells are added - graph.addListener(mxEvent.CELLS_ADDED, function(sender, evt) - { - var cells = evt.getProperty('cells'); - var parent = evt.getProperty('parent'); - - if (graph.getModel().isLayer(parent) && !graph.isCellVisible(parent) && cells != null && cells.length > 0) - { - graph.getModel().setVisible(parent, true); - } - }); - - // Global handler to hide the current menu - this.gestureHandler = mxUtils.bind(this, function(evt) - { - if (this.currentMenu != null && mxEvent.getSource(evt) != this.currentMenu.div) - { - this.hideCurrentMenu(); - } - }); - - mxEvent.addGestureListeners(document, this.gestureHandler); - - // Updates the editor UI after the window has been resized or the orientation changes - // Timeout is workaround for old IE versions which have a delay for DOM client sizes. - // Should not use delay > 0 to avoid handle multiple repaints during window resize - this.resizeHandler = mxUtils.bind(this, function() - { - window.setTimeout(mxUtils.bind(this, function() - { - if (this.editor.graph != null) - { - this.refresh(); - } - }), 0); - }); - - mxEvent.addListener(window, 'resize', this.resizeHandler); - - this.orientationChangeHandler = mxUtils.bind(this, function() - { - this.refresh(); - }); - - mxEvent.addListener(window, 'orientationchange', this.orientationChangeHandler); - - // Workaround for bug on iOS see - // http://stackoverflow.com/questions/19012135/ios-7-ipad-safari-landscape-innerheight-outerheight-layout-issue - if (mxClient.IS_IOS && !window.navigator.standalone) - { - this.scrollHandler = mxUtils.bind(this, function() - { - window.scrollTo(0, 0); - }); - - mxEvent.addListener(window, 'scroll', this.scrollHandler); - } - - /** - * Sets the initial scrollbar locations after a file was loaded. - */ - this.editor.addListener('resetGraphView', mxUtils.bind(this, function() - { - this.resetScrollbars(); - })); - - /** - * Repaints the grid. - */ - this.addListener('gridEnabledChanged', mxUtils.bind(this, function() - { - graph.view.validateBackground(); - })); - - this.addListener('backgroundColorChanged', mxUtils.bind(this, function() - { - graph.view.validateBackground(); - })); - - /** - * Repaints the grid. - */ - graph.addListener('gridSizeChanged', mxUtils.bind(this, function() - { - if (graph.isGridEnabled()) - { - graph.view.validateBackground(); - } - })); - - // Resets UI, updates action and menu states - this.editor.resetGraph(); - this.init(); - this.open(); -}; - -// Extends mxEventSource -mxUtils.extend(EditorUi, mxEventSource); - -/** - * Global config that specifies if the compact UI elements should be used. - */ -EditorUi.compactUi = true; - -/** - * Specifies the size of the split bar. - */ -EditorUi.prototype.splitSize = (mxClient.IS_TOUCH || mxClient.IS_POINTER) ? 12 : 8; - -/** - * Specifies the height of the menubar. Default is 34. - */ -EditorUi.prototype.menubarHeight = 30; - -/** - * Specifies the width of the format panel should be enabled. Default is true. - */ -EditorUi.prototype.formatEnabled = true; - -/** - * Specifies the width of the format panel. Default is 240. - */ -EditorUi.prototype.formatWidth = 240; - -/** - * Specifies the height of the toolbar. Default is 36. - */ -EditorUi.prototype.toolbarHeight = 34; - -/** - * Specifies the height of the footer. Default is 28. - */ -EditorUi.prototype.footerHeight = 28; - -/** - * Specifies the height of the optional sidebarFooterContainer. Default is 34. - */ -EditorUi.prototype.sidebarFooterHeight = 34; - -/** - * Specifies the position of the horizontal split bar. Default is 208 or 118 for - * screen widths <= 640px. - */ -EditorUi.prototype.hsplitPosition = (screen.width <= 640) ? 118 : 208; - -/** - * Specifies if animations are allowed in . Default is true. - */ -EditorUi.prototype.allowAnimation = true; - -/** - * Specifies if animations are allowed in . Default is true. - */ -EditorUi.prototype.lightboxMaxFitScale = 2; - -/** - * Specifies if animations are allowed in . Default is true. - */ -EditorUi.prototype.lightboxVerticalDivider = 4; - -/** - * Specifies if single click on horizontal split should collapse sidebar. Default is false. - */ -EditorUi.prototype.hsplitClickEnabled = false; - -/** - * Installs the listeners to update the action states. - */ -EditorUi.prototype.init = function() -{ - /** - * Keypress starts immediate editing on selection cell - */ - var graph = this.editor.graph; - - mxEvent.addListener(graph.container, 'keydown', mxUtils.bind(this, function(evt) - { - this.onKeyDown(evt); - })); - mxEvent.addListener(graph.container, 'keypress', mxUtils.bind(this, function(evt) - { - this.onKeyPress(evt); - })); - - // Updates action states - this.addUndoListener(); - this.addBeforeUnloadListener(); - - graph.getSelectionModel().addListener(mxEvent.CHANGE, mxUtils.bind(this, function() - { - this.updateActionStates(); - })); - - graph.getModel().addListener(mxEvent.CHANGE, mxUtils.bind(this, function() - { - this.updateActionStates(); - })); - - // Changes action states after change of default parent - var graphSetDefaultParent = graph.setDefaultParent; - var ui = this; - - this.editor.graph.setDefaultParent = function() - { - graphSetDefaultParent.apply(this, arguments); - ui.updateActionStates(); - }; - - // Hack to make editLink available in vertex handler - graph.editLink = ui.actions.get('editLink').funct; - - this.updateActionStates(); - this.initClipboard(); - this.initCanvas(); - - if (this.format != null) - { - this.format.init(); - } -}; - -/** - * Returns true if the given event should start editing. This implementation returns true. - */ -EditorUi.prototype.onKeyDown = function(evt) -{ - var graph = this.editor.graph; - - // Tab selects next cell - if (evt.which == 9 && graph.isEnabled() && !mxEvent.isAltDown(evt)) - { - if (graph.isEditing()) - { - graph.stopEditing(false); - } - else - { - graph.selectCell(!mxEvent.isShiftDown(evt)); - } - - mxEvent.consume(evt); - } -}; - -/** - * Returns true if the given event should start editing. This implementation returns true. - */ -EditorUi.prototype.onKeyPress = function(evt) -{ - var graph = this.editor.graph; - - // KNOWN: Focus does not work if label is empty in quirks mode - if (this.isImmediateEditingEvent(evt) && !graph.isEditing() && !graph.isSelectionEmpty() && evt.which !== 0 && - !mxEvent.isAltDown(evt) && !mxEvent.isControlDown(evt) && !mxEvent.isMetaDown(evt)) - { - graph.escape(); - graph.startEditing(); - - // Workaround for FF where char is lost if cursor is placed before char - if (mxClient.IS_FF) - { - var ce = graph.cellEditor; - ce.textarea.innerHTML = String.fromCharCode(evt.which); - - // Moves cursor to end of textarea - var range = document.createRange(); - range.selectNodeContents(ce.textarea); - range.collapse(false); - var sel = window.getSelection(); - sel.removeAllRanges(); - sel.addRange(range); - } - } -}; - -/** - * Returns true if the given event should start editing. This implementation returns true. - */ -EditorUi.prototype.isImmediateEditingEvent = function(evt) -{ - return true; -}; - -/** - * Private helper method. - */ -EditorUi.prototype.getCssClassForMarker = function(prefix, shape, marker, fill) -{ - var result = ''; - - if (shape == 'flexArrow') - { - result = (marker != null && marker != mxConstants.NONE) ? - 'geSprite geSprite-' + prefix + 'blocktrans' : 'geSprite geSprite-noarrow'; - } - else - { - if (marker == mxConstants.ARROW_CLASSIC) - { - result = (fill == '1') ? 'geSprite geSprite-' + prefix + 'classic' : 'geSprite geSprite-' + prefix + 'classictrans'; - } - else if (marker == mxConstants.ARROW_CLASSIC_THIN) - { - result = (fill == '1') ? 'geSprite geSprite-' + prefix + 'classicthin' : 'geSprite geSprite-' + prefix + 'classicthintrans'; - } - else if (marker == mxConstants.ARROW_OPEN) - { - result = 'geSprite geSprite-' + prefix + 'open'; - } - else if (marker == mxConstants.ARROW_OPEN_THIN) - { - result = 'geSprite geSprite-' + prefix + 'openthin'; - } - else if (marker == mxConstants.ARROW_BLOCK) - { - result = (fill == '1') ? 'geSprite geSprite-' + prefix + 'block' : 'geSprite geSprite-' + prefix + 'blocktrans'; - } - else if (marker == mxConstants.ARROW_BLOCK_THIN) - { - result = (fill == '1') ? 'geSprite geSprite-' + prefix + 'blockthin' : 'geSprite geSprite-' + prefix + 'blockthintrans'; - } - else if (marker == mxConstants.ARROW_OVAL) - { - result = (fill == '1') ? 'geSprite geSprite-' + prefix + 'oval' : 'geSprite geSprite-' + prefix + 'ovaltrans'; - } - else if (marker == mxConstants.ARROW_DIAMOND) - { - result = (fill == '1') ? 'geSprite geSprite-' + prefix + 'diamond' : 'geSprite geSprite-' + prefix + 'diamondtrans'; - } - else if (marker == mxConstants.ARROW_DIAMOND_THIN) - { - result = (fill == '1') ? 'geSprite geSprite-' + prefix + 'thindiamond' : 'geSprite geSprite-' + prefix + 'thindiamondtrans'; - } - else if (marker == 'openAsync') - { - result = 'geSprite geSprite-' + prefix + 'openasync'; - } - else if (marker == 'dash') - { - result = 'geSprite geSprite-' + prefix + 'dash'; - } - else if (marker == 'cross') - { - result = 'geSprite geSprite-' + prefix + 'cross'; - } - else if (marker == 'async') - { - result = (fill == '1') ? 'geSprite geSprite-' + prefix + 'async' : 'geSprite geSprite-' + prefix + 'asynctrans'; - } - else if (marker == 'circle' || marker == 'circlePlus') - { - result = (fill == '1' || marker == 'circle') ? 'geSprite geSprite-' + prefix + 'circle' : 'geSprite geSprite-' + prefix + 'circleplus'; - } - else if (marker == 'ERone') - { - result = 'geSprite geSprite-' + prefix + 'erone'; - } - else if (marker == 'ERmandOne') - { - result = 'geSprite geSprite-' + prefix + 'eronetoone'; - } - else if (marker == 'ERmany') - { - result = 'geSprite geSprite-' + prefix + 'ermany'; - } - else if (marker == 'ERoneToMany') - { - result = 'geSprite geSprite-' + prefix + 'eronetomany'; - } - else if (marker == 'ERzeroToOne') - { - result = 'geSprite geSprite-' + prefix + 'eroneopt'; - } - else if (marker == 'ERzeroToMany') - { - result = 'geSprite geSprite-' + prefix + 'ermanyopt'; - } - else - { - result = 'geSprite geSprite-noarrow'; - } - } - - return result; -}; - -/** - * Overridden in Menus.js - */ -EditorUi.prototype.createMenus = function() -{ - return null; -}; - -/** - * Hook for allowing selection and context menu for certain events. - */ -EditorUi.prototype.updatePasteActionStates = function() -{ - var graph = this.editor.graph; - var paste = this.actions.get('paste'); - var pasteHere = this.actions.get('pasteHere'); - - paste.setEnabled(this.editor.graph.cellEditor.isContentEditing() || (!mxClipboard.isEmpty() && - graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent()))); - pasteHere.setEnabled(paste.isEnabled()); -}; - -/** - * Hook for allowing selection and context menu for certain events. - */ -EditorUi.prototype.initClipboard = function() -{ - var ui = this; - - var mxClipboardCut = mxClipboard.cut; - mxClipboard.cut = function(graph) - { - if (graph.cellEditor.isContentEditing()) - { - document.execCommand('cut', false, null); - } - else - { - mxClipboardCut.apply(this, arguments); - } - - ui.updatePasteActionStates(); - }; - - var mxClipboardCopy = mxClipboard.copy; - mxClipboard.copy = function(graph) - { - if (graph.cellEditor.isContentEditing()) - { - document.execCommand('copy', false, null); - } - else - { - mxClipboardCopy.apply(this, arguments); - } - - ui.updatePasteActionStates(); - }; - - var mxClipboardPaste = mxClipboard.paste; - mxClipboard.paste = function(graph) - { - var result = null; - - if (graph.cellEditor.isContentEditing()) - { - document.execCommand('paste', false, null); - } - else - { - result = mxClipboardPaste.apply(this, arguments); - } - - ui.updatePasteActionStates(); - - return result; - }; - - // Overrides cell editor to update paste action state - var cellEditorStartEditing = this.editor.graph.cellEditor.startEditing; - - this.editor.graph.cellEditor.startEditing = function() - { - cellEditorStartEditing.apply(this, arguments); - ui.updatePasteActionStates(); - }; - - var cellEditorStopEditing = this.editor.graph.cellEditor.stopEditing; - - this.editor.graph.cellEditor.stopEditing = function(cell, trigger) - { - cellEditorStopEditing.apply(this, arguments); - ui.updatePasteActionStates(); - }; - - this.updatePasteActionStates(); -}; - -/** - * Initializes the infinite canvas. - */ -EditorUi.prototype.lazyZoomDelay = 20; - -/** - * Initializes the infinite canvas. - */ -EditorUi.prototype.initCanvas = function() -{ - // Initial page layout view, scrollBuffer and timer-based scrolling - var graph = this.editor.graph; - graph.timerAutoScroll = true; - - /** - * Returns the padding for pages in page view with scrollbars. - */ - graph.getPagePadding = function() - { - return new mxPoint(Math.max(0, Math.round((graph.container.offsetWidth - 34) / graph.view.scale)), - Math.max(0, Math.round((graph.container.offsetHeight - 34) / graph.view.scale))); - }; - - // Fits the number of background pages to the graph - graph.view.getBackgroundPageBounds = function() - { - var layout = this.graph.getPageLayout(); - var page = this.graph.getPageSize(); - - return new mxRectangle(this.scale * (this.translate.x + layout.x * page.width), - this.scale * (this.translate.y + layout.y * page.height), - this.scale * layout.width * page.width, - this.scale * layout.height * page.height); - }; - - graph.getPreferredPageSize = function(bounds, width, height) - { - var pages = this.getPageLayout(); - var size = this.getPageSize(); - - return new mxRectangle(0, 0, pages.width * size.width, pages.height * size.height); - }; - - // Scales pages/graph to fit available size - var resize = null; - var ui = this; - - if (this.editor.isChromelessView()) - { - resize = mxUtils.bind(this, function(autoscale, maxScale, cx, cy) - { - if (graph.container != null) - { - cx = (cx != null) ? cx : 0; - cy = (cy != null) ? cy : 0; - - var bds = (graph.pageVisible) ? graph.view.getBackgroundPageBounds() : graph.getGraphBounds(); - var scroll = mxUtils.hasScrollbars(graph.container); - var tr = graph.view.translate; - var s = graph.view.scale; - - // Normalizes the bounds - var b = mxRectangle.fromRectangle(bds); - b.x = b.x / s - tr.x; - b.y = b.y / s - tr.y; - b.width /= s; - b.height /= s; - - var st = graph.container.scrollTop; - var sl = graph.container.scrollLeft; - var sb = (mxClient.IS_QUIRKS || document.documentMode >= 8) ? 20 : 14; - - if (document.documentMode == 8 || document.documentMode == 9) - { - sb += 3; - } - - var cw = graph.container.offsetWidth - sb; - var ch = graph.container.offsetHeight - sb; - - var ns = (autoscale) ? Math.max(0.3, Math.min(maxScale || 1, cw / b.width)) : s; - var dx = ((cw - ns * b.width) / 2) / ns; - var dy = (this.lightboxVerticalDivider == 0) ? 0 : ((ch - ns * b.height) / this.lightboxVerticalDivider) / ns; - - if (scroll) - { - dx = Math.max(dx, 0); - dy = Math.max(dy, 0); - } - - if (scroll || bds.width < cw || bds.height < ch) - { - graph.view.scaleAndTranslate(ns, Math.floor(dx - b.x), Math.floor(dy - b.y)); - graph.container.scrollTop = st * ns / s; - graph.container.scrollLeft = sl * ns / s; - } - else if (cx != 0 || cy != 0) - { - var t = graph.view.translate; - graph.view.setTranslate(Math.floor(t.x + cx / s), Math.floor(t.y + cy / s)); - } - } - }); - - // Hack to make function available to subclassers - this.chromelessResize = resize; - - // Hook for subclassers for override - this.chromelessWindowResize = mxUtils.bind(this, function() - { - this.chromelessResize(false); - }); - - // Removable resize listener - var autoscaleResize = mxUtils.bind(this, function() - { - this.chromelessWindowResize(false); - }); - - mxEvent.addListener(window, 'resize', autoscaleResize); - - this.destroyFunctions.push(function() - { - mxEvent.removeListener(window, 'resize', autoscaleResize); - }); - - this.editor.addListener('resetGraphView', mxUtils.bind(this, function() - { - this.chromelessResize(true); - })); - - this.actions.get('zoomIn').funct = mxUtils.bind(this, function(evt) - { - graph.zoomIn(); - this.chromelessResize(false); - }); - this.actions.get('zoomOut').funct = mxUtils.bind(this, function(evt) - { - graph.zoomOut(); - this.chromelessResize(false); - }); - - // Creates toolbar for viewer - do not use CSS here - // as this may be used in a viewer that has no CSS - if (urlParams['toolbar'] != '0') - { - this.chromelessToolbar = document.createElement('div'); - this.chromelessToolbar.style.position = 'fixed'; - this.chromelessToolbar.style.overflow = 'hidden'; - this.chromelessToolbar.style.boxSizing = 'border-box'; - this.chromelessToolbar.style.whiteSpace = 'nowrap'; - this.chromelessToolbar.style.backgroundColor = '#000000'; - this.chromelessToolbar.style.padding = '10px 10px 8px 10px'; - this.chromelessToolbar.style.left = '50%'; - - if (!mxClient.IS_VML) - { - mxUtils.setPrefixedStyle(this.chromelessToolbar.style, 'borderRadius', '20px'); - mxUtils.setPrefixedStyle(this.chromelessToolbar.style, 'transition', 'opacity 600ms ease-in-out'); - } - - var updateChromelessToolbarPosition = mxUtils.bind(this, function() - { - var css = mxUtils.getCurrentStyle(graph.container); - this.chromelessToolbar.style.bottom = ((css != null) ? parseInt(css['margin-bottom'] || 0) : 0) + - ((this.tabContainer != null) ? (20 + parseInt(this.tabContainer.style.height)) : 20) + 'px'; - }); - - this.editor.addListener('resetGraphView', updateChromelessToolbarPosition); - updateChromelessToolbarPosition(); - - var btnCount = 0; - - var addButton = mxUtils.bind(this, function(fn, imgSrc, tip) - { - btnCount++; - - var a = document.createElement('span'); - a.style.paddingLeft = '8px'; - a.style.paddingRight = '8px'; - a.style.cursor = 'pointer'; - mxEvent.addListener(a, 'click', fn); - - if (tip != null) - { - a.setAttribute('title', tip); - } - - var img = document.createElement('img'); - img.setAttribute('border', '0'); - img.setAttribute('src', imgSrc); - - a.appendChild(img); - this.chromelessToolbar.appendChild(a); - - return a; - }); - - var prevButton = addButton(mxUtils.bind(this, function(evt) - { - this.actions.get('previousPage').funct(); - mxEvent.consume(evt); - }), Editor.previousLargeImage, mxResources.get('previousPage')); - - var pageInfo = document.createElement('div'); - pageInfo.style.display = 'inline-block'; - pageInfo.style.verticalAlign = 'top'; - pageInfo.style.fontFamily = 'Helvetica,Arial'; - pageInfo.style.marginTop = '8px'; - pageInfo.style.fontSize = '14px'; - pageInfo.style.color = '#ffffff'; - this.chromelessToolbar.appendChild(pageInfo); - - var nextButton = addButton(mxUtils.bind(this, function(evt) - { - this.actions.get('nextPage').funct(); - mxEvent.consume(evt); - }), Editor.nextLargeImage, mxResources.get('nextPage')); - - var updatePageInfo = mxUtils.bind(this, function() - { - if (this.pages != null && this.pages.length > 1 && this.currentPage != null) - { - pageInfo.innerHTML = ''; - mxUtils.write(pageInfo, (mxUtils.indexOf(this.pages, this.currentPage) + 1) + ' / ' + this.pages.length); - } - }); - - prevButton.style.paddingLeft = '0px'; - prevButton.style.paddingRight = '4px'; - nextButton.style.paddingLeft = '4px'; - nextButton.style.paddingRight = '0px'; - - var updatePageButtons = mxUtils.bind(this, function() - { - if (this.pages != null && this.pages.length > 1 && this.currentPage != null) - { - nextButton.style.display = ''; - prevButton.style.display = ''; - pageInfo.style.display = 'inline-block'; - } - else - { - nextButton.style.display = 'none'; - prevButton.style.display = 'none'; - pageInfo.style.display = 'none'; - } - - updatePageInfo(); - }); - - this.editor.addListener('resetGraphView', updatePageButtons); - this.editor.addListener('pageSelected', updatePageInfo); - - addButton(mxUtils.bind(this, function(evt) - { - this.actions.get('zoomOut').funct(); - mxEvent.consume(evt); - }), Editor.zoomOutLargeImage, mxResources.get('zoomOut') + ' (Alt+Mousewheel)'); - - addButton(mxUtils.bind(this, function(evt) - { - this.actions.get('zoomIn').funct(); - mxEvent.consume(evt); - }), Editor.zoomInLargeImage, mxResources.get('zoomIn') + ' (Alt+Mousewheel)'); - - addButton(mxUtils.bind(this, function(evt) - { - if (graph.isLightboxView()) - { - if (graph.view.scale == 1) - { - this.lightboxFit(); - } - else - { - graph.zoomTo(1); - } - - this.chromelessResize(false); - } - else - { - this.chromelessResize(true); - } - - mxEvent.consume(evt); - }), Editor.actualSizeLargeImage, mxResources.get('fit')); - - // Changes toolbar opacity on hover - var fadeThread = null; - var fadeThread2 = null; - - var fadeOut = mxUtils.bind(this, function(delay) - { - if (fadeThread != null) - { - window.clearTimeout(fadeThread); - fadeThead = null; - } - - if (fadeThread2 != null) - { - window.clearTimeout(fadeThread2); - fadeThead2 = null; - } - - fadeThread = window.setTimeout(mxUtils.bind(this, function() - { - mxUtils.setOpacity(this.chromelessToolbar, 0); - fadeThread = null; - - fadeThread2 = window.setTimeout(mxUtils.bind(this, function() - { - this.chromelessToolbar.style.display = 'none'; - fadeThread2 = null; - }), 600); - }), delay || 200); - }); - - var fadeIn = mxUtils.bind(this, function(opacity) - { - if (fadeThread != null) - { - window.clearTimeout(fadeThread); - fadeThead = null; - } - - if (fadeThread2 != null) - { - window.clearTimeout(fadeThread2); - fadeThead2 = null; - } - - this.chromelessToolbar.style.display = ''; - mxUtils.setOpacity(this.chromelessToolbar, opacity || 30); - }); - - if (urlParams['layers'] == '1') - { - this.layersDialog = null; - - var layersButton = addButton(mxUtils.bind(this, function(evt) - { - if (this.layersDialog != null) - { - this.layersDialog.parentNode.removeChild(this.layersDialog); - this.layersDialog = null; - } - else - { - this.layersDialog = graph.createLayersDialog(); - - mxEvent.addListener(this.layersDialog, 'mouseleave', mxUtils.bind(this, function() - { - this.layersDialog.parentNode.removeChild(this.layersDialog); - this.layersDialog = null; - })); - - var r = layersButton.getBoundingClientRect(); - - mxUtils.setPrefixedStyle(this.layersDialog.style, 'borderRadius', '5px'); - this.layersDialog.style.position = 'fixed'; - this.layersDialog.style.fontFamily = 'Helvetica,Arial'; - this.layersDialog.style.backgroundColor = '#000000'; - this.layersDialog.style.width = '160px'; - this.layersDialog.style.padding = '4px 2px 4px 2px'; - this.layersDialog.style.color = '#ffffff'; - mxUtils.setOpacity(this.layersDialog, 70); - this.layersDialog.style.left = r.left + 'px'; - this.layersDialog.style.bottom = parseInt(this.chromelessToolbar.style.bottom) + - this.chromelessToolbar.offsetHeight + 4 + 'px'; - - // Puts the dialog on top of the container z-index - var style = mxUtils.getCurrentStyle(this.editor.graph.container); - this.layersDialog.style.zIndex = style.zIndex; - - document.body.appendChild(this.layersDialog); - } - - mxEvent.consume(evt); - }), Editor.layersLargeImage, mxResources.get('layers')); - - // Shows/hides layers button depending on content - var model = graph.getModel(); - - model.addListener(mxEvent.CHANGE, function() - { - layersButton.style.display = (model.getChildCount(model.root) > 1) ? '' : 'none'; - }); - } - - this.addChromelessToolbarItems(addButton); - - if (this.editor.editButtonLink != null || this.editor.editButtonFunc != null) - { - addButton(mxUtils.bind(this, function(evt) - { - if (this.editor.editButtonFunc != null) - { - this.editor.editButtonFunc(); - } - else if (this.editor.editButtonLink == '_blank') - { - this.editor.editAsNew(this.getEditBlankXml()); - } - else - { - graph.openLink(this.editor.editButtonLink, 'editWindow'); - } - - mxEvent.consume(evt); - }), Editor.editLargeImage, mxResources.get('edit')); - } - - if (graph.lightbox && (urlParams['close'] == '1' || this.container != document.body)) - { - addButton(mxUtils.bind(this, function(evt) - { - if (urlParams['close'] == '1') - { - window.close(); - } - else - { - this.destroy(); - mxEvent.consume(evt); - } - }), Editor.closeLargeImage, mxResources.get('close') + ' (Escape)'); - } - - // Initial state invisible - this.chromelessToolbar.style.display = 'none'; - mxUtils.setPrefixedStyle(this.chromelessToolbar.style, 'transform', 'translate(-50%,0)'); - graph.container.appendChild(this.chromelessToolbar); - - mxEvent.addListener(graph.container, (mxClient.IS_POINTER) ? 'pointermove' : 'mousemove', mxUtils.bind(this, function(evt) - { - if (!mxEvent.isTouchEvent(evt)) - { - if (!mxEvent.isShiftDown(evt)) - { - fadeIn(30); - } - - fadeOut(); - } - })); - - mxEvent.addListener(this.chromelessToolbar, (mxClient.IS_POINTER) ? 'pointermove' : 'mousemove', function(evt) - { - mxEvent.consume(evt); - }); - - mxEvent.addListener(this.chromelessToolbar, 'mouseenter', mxUtils.bind(this, function(evt) - { - if (!mxEvent.isShiftDown(evt)) - { - fadeIn(100); - } - else - { - fadeOut(); - } - })); - - mxEvent.addListener(this.chromelessToolbar, 'mousemove', mxUtils.bind(this, function(evt) - { - if (!mxEvent.isShiftDown(evt)) - { - fadeIn(100); - } - else - { - fadeOut(); - } - - mxEvent.consume(evt); - })); - - mxEvent.addListener(this.chromelessToolbar, 'mouseleave', mxUtils.bind(this, function(evt) - { - if (!mxEvent.isTouchEvent(evt)) - { - fadeIn(30); - } - })); - - // Shows/hides toolbar for touch devices - var tol = graph.getTolerance(); - - graph.addMouseListener( - { - startX: 0, - startY: 0, - scrollLeft: 0, - scrollTop: 0, - mouseDown: function(sender, me) - { - this.startX = me.getGraphX(); - this.startY = me.getGraphY(); - this.scrollLeft = graph.container.scrollLeft; - this.scrollTop = graph.container.scrollTop; - }, - mouseMove: function(sender, me) {}, - mouseUp: function(sender, me) - { - if (mxEvent.isTouchEvent(me.getEvent())) - { - if ((Math.abs(this.scrollLeft - graph.container.scrollLeft) < tol && - Math.abs(this.scrollTop - graph.container.scrollTop) < tol) && - (Math.abs(this.startX - me.getGraphX()) < tol && - Math.abs(this.startY - me.getGraphY()) < tol)) - { - if (parseFloat(ui.chromelessToolbar.style.opacity || 0) > 0) - { - fadeOut(); - } - else - { - fadeIn(30); - } - } - } - } - }); - } // end if toolbar - - // Installs handling of highlight and handling links to relative links and anchors - if (!this.editor.editable) - { - this.addChromelessClickHandler(); - } - } - else if (this.editor.extendCanvas) - { - /** - * Guesses autoTranslate to avoid another repaint (see below). - * Works if only the scale of the graph changes or if pages - * are visible and the visible pages do not change. - */ - var graphViewValidate = graph.view.validate; - graph.view.validate = function() - { - if (this.graph.container != null && mxUtils.hasScrollbars(this.graph.container)) - { - var pad = this.graph.getPagePadding(); - var size = this.graph.getPageSize(); - - // Updating scrollbars here causes flickering in quirks and is not needed - // if zoom method is always used to set the current scale on the graph. - var tx = this.translate.x; - var ty = this.translate.y; - this.translate.x = pad.x - (this.x0 || 0) * size.width; - this.translate.y = pad.y - (this.y0 || 0) * size.height; - } - - graphViewValidate.apply(this, arguments); - }; - - var graphSizeDidChange = graph.sizeDidChange; - graph.sizeDidChange = function() - { - if (this.container != null && mxUtils.hasScrollbars(this.container)) - { - var pages = this.getPageLayout(); - var pad = this.getPagePadding(); - var size = this.getPageSize(); - - // Updates the minimum graph size - var minw = Math.ceil(2 * pad.x + pages.width * size.width); - var minh = Math.ceil(2 * pad.y + pages.height * size.height); - - var min = graph.minimumGraphSize; - - // LATER: Fix flicker of scrollbar size in IE quirks mode - // after delayed call in window.resize event handler - if (min == null || min.width != minw || min.height != minh) - { - graph.minimumGraphSize = new mxRectangle(0, 0, minw, minh); - } - - // Updates auto-translate to include padding and graph size - var dx = pad.x - pages.x * size.width; - var dy = pad.y - pages.y * size.height; - - if (!this.autoTranslate && (this.view.translate.x != dx || this.view.translate.y != dy)) - { - this.autoTranslate = true; - this.view.x0 = pages.x; - this.view.y0 = pages.y; - - // NOTE: THIS INVOKES THIS METHOD AGAIN. UNFORTUNATELY THERE IS NO WAY AROUND THIS SINCE THE - // BOUNDS ARE KNOWN AFTER THE VALIDATION AND SETTING THE TRANSLATE TRIGGERS A REVALIDATION. - // SHOULD MOVE TRANSLATE/SCALE TO VIEW. - var tx = graph.view.translate.x; - var ty = graph.view.translate.y; - graph.view.setTranslate(dx, dy); - - // LATER: Fix rounding errors for small zoom - graph.container.scrollLeft += Math.round((dx - tx) * graph.view.scale); - graph.container.scrollTop += Math.round((dy - ty) * graph.view.scale); - - this.autoTranslate = false; - - return; - } - - graphSizeDidChange.apply(this, arguments); - } - }; - } - - // Accumulates the zoom factor while the rendering is taking place - // so that not the complete sequence of zoom steps must be painted - graph.updateZoomTimeout = null; - graph.cumulativeZoomFactor = 1; - - var cursorPosition = null; - - graph.lazyZoom = function(zoomIn) - { - if (this.updateZoomTimeout != null) - { - window.clearTimeout(this.updateZoomTimeout); - } - - // Switches to 1% zoom steps below 15% - // Lower bound depdends on rounding below - if (zoomIn) - { - if (this.view.scale * this.cumulativeZoomFactor < 0.15) - { - this.cumulativeZoomFactor = (this.view.scale + 0.01) / this.view.scale; - } - else - { - // Uses to 5% zoom steps for better grid rendering in webkit - // and to avoid rounding errors for zoom steps - this.cumulativeZoomFactor *= this.zoomFactor; - this.cumulativeZoomFactor = Math.round(this.view.scale * this.cumulativeZoomFactor * 20) / 20 / this.view.scale; - } - } - else - { - if (this.view.scale * this.cumulativeZoomFactor <= 0.15) - { - this.cumulativeZoomFactor = (this.view.scale - 0.01) / this.view.scale; - } - else - { - // Uses to 5% zoom steps for better grid rendering in webkit - // and to avoid rounding errors for zoom steps - this.cumulativeZoomFactor /= this.zoomFactor; - this.cumulativeZoomFactor = Math.round(this.view.scale * this.cumulativeZoomFactor * 20) / 20 / this.view.scale; - } - } - - this.cumulativeZoomFactor = Math.max(0.01, Math.min(this.view.scale * this.cumulativeZoomFactor, 160) / this.view.scale); - - this.updateZoomTimeout = window.setTimeout(mxUtils.bind(this, function() - { - var offset = mxUtils.getOffset(graph.container); - var dx = 0; - var dy = 0; - - if (cursorPosition != null) - { - dx = graph.container.offsetWidth / 2 - cursorPosition.x + offset.x; - dy = graph.container.offsetHeight / 2 - cursorPosition.y + offset.y; - } - - var prev = this.view.scale; - this.zoom(this.cumulativeZoomFactor); - var s = this.view.scale; - - if (s != prev) - { - if (resize != null) - { - ui.chromelessResize(false, null, dx * (this.cumulativeZoomFactor - 1), - dy * (this.cumulativeZoomFactor - 1)); - } - - if (mxUtils.hasScrollbars(graph.container) && (dx != 0 || dy != 0)) - { - graph.container.scrollLeft -= dx * (this.cumulativeZoomFactor - 1); - graph.container.scrollTop -= dy * (this.cumulativeZoomFactor - 1); - } - } - - this.cumulativeZoomFactor = 1; - this.updateZoomTimeout = null; - }), this.lazyZoomDelay); - }; - - mxEvent.addMouseWheelListener(mxUtils.bind(this, function(evt, up) - { - // Ctrl+wheel (or pinch on touchpad) is a native browser zoom event is OS X - // LATER: Add support for zoom via pinch on trackpad for Chrome in OS X - if ((this.dialogs == null || this.dialogs.length == 0) && graph.isZoomWheelEvent(evt)) - { - var source = mxEvent.getSource(evt); - - while (source != null) - { - if (source == graph.container) - { - cursorPosition = new mxPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt)); - graph.lazyZoom(up); - mxEvent.consume(evt); - - return; - } - - source = source.parentNode; - } - } - })); -}; - -/** - * Creates a temporary graph instance for rendering off-screen content. - */ -EditorUi.prototype.addChromelessToolbarItems = function(addButton) -{ - addButton(mxUtils.bind(this, function(evt) - { - this.actions.get('print').funct(); - mxEvent.consume(evt); - }), Editor.printLargeImage, mxResources.get('print')); -}; - -/** - * Creates a temporary graph instance for rendering off-screen content. - */ -EditorUi.prototype.createTemporaryGraph = function(stylesheet) -{ - var graph = new Graph(document.createElement('div'), null, null, stylesheet); - graph.resetViewOnRootChange = false; - graph.setConnectable(false); - graph.gridEnabled = false; - graph.autoScroll = false; - graph.setTooltips(false); - graph.setEnabled(false); - - // Container must be in the DOM for correct HTML rendering - graph.container.style.visibility = 'hidden'; - graph.container.style.position = 'absolute'; - graph.container.style.overflow = 'hidden'; - graph.container.style.height = '1px'; - graph.container.style.width = '1px'; - - return graph; -}; - -/** - * - */ -EditorUi.prototype.addChromelessClickHandler = function() -{ - var hl = urlParams['highlight']; - - // Adds leading # for highlight color code - if (hl != null && hl.length > 0) - { - hl = '#' + hl; - } - - this.editor.graph.addClickHandler(hl); -}; - -/** - * - */ -EditorUi.prototype.toggleFormatPanel = function(forceHide) -{ - this.formatWidth = (forceHide || this.formatWidth > 0) ? 0 : 240; - this.formatContainer.style.display = (forceHide || this.formatWidth > 0) ? '' : 'none'; - this.refresh(); - this.format.refresh(); - this.fireEvent(new mxEventObject('formatWidthChanged')); -}; - -/** - * Adds support for placeholders in labels. - */ -EditorUi.prototype.lightboxFit = function(maxHeight) -{ - if (this.isDiagramEmpty()) - { - this.editor.graph.view.setScale(1); - } - else - { - var p = urlParams['border']; - var border = 60; - - if (p != null) - { - border = parseInt(p); - } - - // LATER: Use initial graph bounds to avoid rounding errors - this.editor.graph.maxFitScale = this.lightboxMaxFitScale; - this.editor.graph.fit(border, null, null, null, null, null, maxHeight); - this.editor.graph.maxFitScale = null; - } -}; - -/** - * Translates this point by the given vector. - * - * @param {number} dx X-coordinate of the translation. - * @param {number} dy Y-coordinate of the translation. - */ -EditorUi.prototype.isDiagramEmpty = function() -{ - var model = this.editor.graph.getModel(); - - return model.getChildCount(model.root) == 1 && model.getChildCount(model.getChildAt(model.root, 0)) == 0; -}; - -/** - * Hook for allowing selection and context menu for certain events. - */ -EditorUi.prototype.isSelectionAllowed = function(evt) -{ - return mxEvent.getSource(evt).nodeName == 'SELECT' || (mxEvent.getSource(evt).nodeName == 'INPUT' && - mxUtils.isAncestorNode(this.formatContainer, mxEvent.getSource(evt))); -}; - -/** - * Installs dialog if browser window is closed without saving - * This must be disabled during save and image export. - */ -EditorUi.prototype.addBeforeUnloadListener = function() -{ - // Installs dialog if browser window is closed without saving - // This must be disabled during save and image export - window.onbeforeunload = mxUtils.bind(this, function() - { - if (!this.editor.isChromelessView()) - { - return this.onBeforeUnload(); - } - }); -}; - -/** - * Sets the onbeforeunload for the application - */ -EditorUi.prototype.onBeforeUnload = function() -{ - if (this.editor.modified) - { - return mxResources.get('allChangesLost'); - } -}; - -/** - * Opens the current diagram via the window.opener if one exists. - */ -EditorUi.prototype.open = function() -{ - // Cross-domain window access is not allowed in FF, so if we - // were opened from another domain then this will fail. - try - { - if (window.opener != null && window.opener.openFile != null) - { - window.opener.openFile.setConsumer(mxUtils.bind(this, function(xml, filename) - { - try - { - var doc = mxUtils.parseXml(xml); - this.editor.setGraphXml(doc.documentElement); - this.editor.setModified(false); - this.editor.undoManager.clear(); - - if (filename != null) - { - this.editor.setFilename(filename); - this.updateDocumentTitle(); - } - - return; - } - catch (e) - { - mxUtils.alert(mxResources.get('invalidOrMissingFile') + ': ' + e.message); - } - })); - } - } - catch(e) - { - // ignore - } - - // Fires as the last step if no file was loaded - this.editor.graph.view.validate(); - - // Required only in special cases where an initial file is opened - // and the minimumGraphSize changes and CSS must be updated. - this.editor.graph.sizeDidChange(); - this.editor.fireEvent(new mxEventObject('resetGraphView')); -}; - -/** - * Sets the current menu and element. - */ -EditorUi.prototype.setCurrentMenu = function(menu, elt) -{ - this.currentMenuElt = elt; - this.currentMenu = menu; -}; - -/** - * Resets the current menu and element. - */ -EditorUi.prototype.resetCurrentMenu = function() -{ - this.currentMenuElt = null; - this.currentMenu = null; -}; - -/** - * Hides and destroys the current menu. - */ -EditorUi.prototype.hideCurrentMenu = function() -{ - if (this.currentMenu != null) - { - this.currentMenu.hideMenu(); - this.resetCurrentMenu(); - } -}; - -/** - * Updates the document title. - */ -EditorUi.prototype.updateDocumentTitle = function() -{ - var title = this.editor.getOrCreateFilename(); - - if (this.editor.appName != null) - { - title += ' - ' + this.editor.appName; - } - - document.title = title; -}; - -/** - * Updates the document title. - */ -EditorUi.prototype.createHoverIcons = function() -{ - return new HoverIcons(this.editor.graph); -}; - -/** - * Returns the URL for a copy of this editor with no state. - */ -EditorUi.prototype.redo = function() -{ - try - { - var graph = this.editor.graph; - - if (graph.isEditing()) - { - document.execCommand('redo', false, null); - } - else - { - this.editor.undoManager.redo(); - } - } - catch (e) - { - // ignore all errors - } -}; - -/** - * Returns the URL for a copy of this editor with no state. - */ -EditorUi.prototype.undo = function() -{ - try - { - var graph = this.editor.graph; - - if (graph.isEditing()) - { - // Stops editing and executes undo on graph if native undo - // does not affect current editing value - var value = graph.cellEditor.textarea.innerHTML; - document.execCommand('undo', false, null); - - if (value == graph.cellEditor.textarea.innerHTML) - { - graph.stopEditing(true); - this.editor.undoManager.undo(); - } - } - else - { - this.editor.undoManager.undo(); - } - } - catch (e) - { - // ignore all errors - } -}; - -/** - * Returns the URL for a copy of this editor with no state. - */ -EditorUi.prototype.canRedo = function() -{ - return this.editor.graph.isEditing() || this.editor.undoManager.canRedo(); -}; - -/** - * Returns the URL for a copy of this editor with no state. - */ -EditorUi.prototype.canUndo = function() -{ - return this.editor.graph.isEditing() || this.editor.undoManager.canUndo(); -}; - -/** - * - */ -EditorUi.prototype.getEditBlankXml = function() -{ - return mxUtils.getXml(this.editor.getGraphXml()); -}; - -/** - * Returns the URL for a copy of this editor with no state. - */ -EditorUi.prototype.getUrl = function(pathname) -{ - var href = (pathname != null) ? pathname : window.location.pathname; - var parms = (href.indexOf('?') > 0) ? 1 : 0; - - // Removes template URL parameter for new blank diagram - for (var key in urlParams) - { - if (parms == 0) - { - href += '?'; - } - else - { - href += '&'; - } - - href += key + '=' + urlParams[key]; - parms++; - } - - return href; -}; - -/** - * Specifies if the graph has scrollbars. - */ -EditorUi.prototype.setScrollbars = function(value) -{ - var graph = this.editor.graph; - var prev = graph.container.style.overflow; - graph.scrollbars = value; - this.editor.updateGraphComponents(); - - if (prev != graph.container.style.overflow) - { - if (graph.container.style.overflow == 'hidden') - { - var t = graph.view.translate; - graph.view.setTranslate(t.x - graph.container.scrollLeft / graph.view.scale, t.y - graph.container.scrollTop / graph.view.scale); - graph.container.scrollLeft = 0; - graph.container.scrollTop = 0; - graph.minimumGraphSize = null; - graph.sizeDidChange(); - } - else - { - var dx = graph.view.translate.x; - var dy = graph.view.translate.y; - - graph.view.translate.x = 0; - graph.view.translate.y = 0; - graph.sizeDidChange(); - graph.container.scrollLeft -= Math.round(dx * graph.view.scale); - graph.container.scrollTop -= Math.round(dy * graph.view.scale); - } - } - - this.fireEvent(new mxEventObject('scrollbarsChanged')); -}; - -/** - * Returns true if the graph has scrollbars. - */ -EditorUi.prototype.hasScrollbars = function() -{ - return this.editor.graph.scrollbars; -}; - -/** - * Resets the state of the scrollbars. - */ -EditorUi.prototype.resetScrollbars = function() -{ - var graph = this.editor.graph; - - if (!this.editor.extendCanvas) - { - graph.container.scrollTop = 0; - graph.container.scrollLeft = 0; - - if (!mxUtils.hasScrollbars(graph.container)) - { - graph.view.setTranslate(0, 0); - } - } - else if (!this.editor.isChromelessView()) - { - if (mxUtils.hasScrollbars(graph.container)) - { - if (graph.pageVisible) - { - var pad = graph.getPagePadding(); - graph.container.scrollTop = Math.floor(pad.y - this.editor.initialTopSpacing) - 1; - graph.container.scrollLeft = Math.floor(Math.min(pad.x, - (graph.container.scrollWidth - graph.container.clientWidth) / 2)) - 1; - - // Scrolls graph to visible area - var bounds = graph.getGraphBounds(); - - if (bounds.width > 0 && bounds.height > 0) - { - if (bounds.x > graph.container.scrollLeft + graph.container.clientWidth * 0.9) - { - graph.container.scrollLeft = Math.min(bounds.x + bounds.width - graph.container.clientWidth, bounds.x - 10); - } - - if (bounds.y > graph.container.scrollTop + graph.container.clientHeight * 0.9) - { - graph.container.scrollTop = Math.min(bounds.y + bounds.height - graph.container.clientHeight, bounds.y - 10); - } - } - } - else - { - var bounds = graph.getGraphBounds(); - var width = Math.max(bounds.width, graph.scrollTileSize.width * graph.view.scale); - var height = Math.max(bounds.height, graph.scrollTileSize.height * graph.view.scale); - graph.container.scrollTop = Math.floor(Math.max(0, bounds.y - Math.max(20, (graph.container.clientHeight - height) / 4))); - graph.container.scrollLeft = Math.floor(Math.max(0, bounds.x - Math.max(0, (graph.container.clientWidth - width) / 2))); - } - } - else - { - // This code is not actively used since the default for scrollbars is always true - if (graph.pageVisible) - { - var b = graph.view.getBackgroundPageBounds(); - graph.view.setTranslate(Math.floor(Math.max(0, (graph.container.clientWidth - b.width) / 2) - b.x), - Math.floor(Math.max(0, (graph.container.clientHeight - b.height) / 2) - b.y)); - } - else - { - var bounds = graph.getGraphBounds(); - graph.view.setTranslate(Math.floor(Math.max(0, Math.max(0, (graph.container.clientWidth - bounds.width) / 2) - bounds.x)), - Math.floor(Math.max(0, Math.max(20, (graph.container.clientHeight - bounds.height) / 4)) - bounds.y)); - } - } - } -}; - -/** - * Loads the stylesheet for this graph. - */ -EditorUi.prototype.setPageVisible = function(value) -{ - var graph = this.editor.graph; - var hasScrollbars = mxUtils.hasScrollbars(graph.container); - var tx = 0; - var ty = 0; - - if (hasScrollbars) - { - tx = graph.view.translate.x * graph.view.scale - graph.container.scrollLeft; - ty = graph.view.translate.y * graph.view.scale - graph.container.scrollTop; - } - - graph.pageVisible = value; - graph.pageBreaksVisible = value; - graph.preferPageSize = value; - graph.view.validateBackground(); - - // Workaround for possible handle offset - if (hasScrollbars) - { - var cells = graph.getSelectionCells(); - graph.clearSelection(); - graph.setSelectionCells(cells); - } - - // Calls updatePageBreaks - graph.sizeDidChange(); - - if (hasScrollbars) - { - graph.container.scrollLeft = graph.view.translate.x * graph.view.scale - tx; - graph.container.scrollTop = graph.view.translate.y * graph.view.scale - ty; - } - - this.fireEvent(new mxEventObject('pageViewChanged')); -}; - -/** - * Change types - */ -function ChangePageSetup(ui, color, image, format) -{ - this.ui = ui; - this.color = color; - this.previousColor = color; - this.image = image; - this.previousImage = image; - this.format = format; - this.previousFormat = format; - - // Needed since null are valid values for color and image - this.ignoreColor = false; - this.ignoreImage = false; -} - -/** - * Implementation of the undoable page rename. - */ -ChangePageSetup.prototype.execute = function() -{ - var graph = this.ui.editor.graph; - - if (!this.ignoreColor) - { - this.color = this.previousColor; - var tmp = graph.background; - this.ui.setBackgroundColor(this.previousColor); - this.previousColor = tmp; - } - - if (!this.ignoreImage) - { - this.image = this.previousImage; - var tmp = graph.backgroundImage; - this.ui.setBackgroundImage(this.previousImage); - this.previousImage = tmp; - } - - if (this.previousFormat != null) - { - this.format = this.previousFormat; - var tmp = graph.pageFormat; - - if (this.previousFormat.width != tmp.width || - this.previousFormat.height != tmp.height) - { - this.ui.setPageFormat(this.previousFormat); - this.previousFormat = tmp; - } - } - - if (this.foldingEnabled != null && this.foldingEnabled != this.ui.editor.graph.foldingEnabled) - { - this.ui.setFoldingEnabled(this.foldingEnabled); - this.foldingEnabled = !this.foldingEnabled; - } -}; - -// Registers codec for ChangePageSetup -(function() -{ - var codec = new mxObjectCodec(new ChangePageSetup(), ['ui', 'previousColor', 'previousImage', 'previousFormat']); - - codec.afterDecode = function(dec, node, obj) - { - obj.previousColor = obj.color; - obj.previousImage = obj.image; - obj.previousFormat = obj.format; - - if (obj.foldingEnabled != null) - { - obj.foldingEnabled = !obj.foldingEnabled; - } - - return obj; - }; - - mxCodecRegistry.register(codec); -})(); - -/** - * Loads the stylesheet for this graph. - */ -EditorUi.prototype.setBackgroundColor = function(value) -{ - this.editor.graph.background = value; - this.editor.graph.view.validateBackground(); - - this.fireEvent(new mxEventObject('backgroundColorChanged')); -}; - -/** - * Loads the stylesheet for this graph. - */ -EditorUi.prototype.setFoldingEnabled = function(value) -{ - this.editor.graph.foldingEnabled = value; - this.editor.graph.view.revalidate(); - - this.fireEvent(new mxEventObject('foldingEnabledChanged')); -}; - -/** - * Loads the stylesheet for this graph. - */ -EditorUi.prototype.setPageFormat = function(value) -{ - this.editor.graph.pageFormat = value; - - if (!this.editor.graph.pageVisible) - { - this.actions.get('pageView').funct(); - } - else - { - this.editor.graph.view.validateBackground(); - this.editor.graph.sizeDidChange(); - } - - this.fireEvent(new mxEventObject('pageFormatChanged')); -}; - -/** - * Loads the stylesheet for this graph. - */ -EditorUi.prototype.setPageScale = function(value) -{ - this.editor.graph.pageScale = value; - - if (!this.editor.graph.pageVisible) - { - this.actions.get('pageView').funct(); - } - else - { - this.editor.graph.view.validateBackground(); - this.editor.graph.sizeDidChange(); - } - - this.fireEvent(new mxEventObject('pageScaleChanged')); -}; - -/** - * Loads the stylesheet for this graph. - */ -EditorUi.prototype.setGridColor = function(value) -{ - this.editor.graph.view.gridColor = value; - this.editor.graph.view.validateBackground(); - this.fireEvent(new mxEventObject('gridColorChanged')); -}; - -/** - * Updates the states of the given undo/redo items. - */ -EditorUi.prototype.addUndoListener = function() -{ - var undo = this.actions.get('undo'); - var redo = this.actions.get('redo'); - - var undoMgr = this.editor.undoManager; - - var undoListener = mxUtils.bind(this, function() - { - undo.setEnabled(this.canUndo()); - redo.setEnabled(this.canRedo()); - }); - - undoMgr.addListener(mxEvent.ADD, undoListener); - undoMgr.addListener(mxEvent.UNDO, undoListener); - undoMgr.addListener(mxEvent.REDO, undoListener); - undoMgr.addListener(mxEvent.CLEAR, undoListener); - - // Overrides cell editor to update action states - var cellEditorStartEditing = this.editor.graph.cellEditor.startEditing; - - this.editor.graph.cellEditor.startEditing = function() - { - cellEditorStartEditing.apply(this, arguments); - undoListener(); - }; - - var cellEditorStopEditing = this.editor.graph.cellEditor.stopEditing; - - this.editor.graph.cellEditor.stopEditing = function(cell, trigger) - { - cellEditorStopEditing.apply(this, arguments); - undoListener(); - }; - - // Updates the button states once - undoListener(); -}; - -/** -* Updates the states of the given toolbar items based on the selection. -*/ -EditorUi.prototype.updateActionStates = function() -{ - var graph = this.editor.graph; - var selected = !graph.isSelectionEmpty(); - var vertexSelected = false; - var edgeSelected = false; - - var cells = graph.getSelectionCells(); - - if (cells != null) - { - for (var i = 0; i < cells.length; i++) - { - var cell = cells[i]; - - if (graph.getModel().isEdge(cell)) - { - edgeSelected = true; - } - - if (graph.getModel().isVertex(cell)) - { - vertexSelected = true; - } - - if (edgeSelected && vertexSelected) - { - break; - } - } - } - - // Updates action states - var actions = ['cut', 'copy', 'bold', 'italic', 'underline', 'delete', 'duplicate', - 'editStyle', 'editTooltip', 'editLink', 'backgroundColor', 'borderColor', - 'edit', 'toFront', 'toBack', 'lockUnlock', 'solid', 'dashed', 'pasteSize', - 'dotted', 'fillColor', 'gradientColor', 'shadow', 'fontColor', - 'formattedText', 'rounded', 'toggleRounded', 'sharp', 'strokeColor']; - - for (var i = 0; i < actions.length; i++) - { - this.actions.get(actions[i]).setEnabled(selected); - } - - this.actions.get('setAsDefaultStyle').setEnabled(graph.getSelectionCount() == 1); - this.actions.get('clearWaypoints').setEnabled(!graph.isSelectionEmpty()); - this.actions.get('copySize').setEnabled(graph.getSelectionCount() == 1); - this.actions.get('turn').setEnabled(!graph.isSelectionEmpty()); - this.actions.get('curved').setEnabled(edgeSelected); - this.actions.get('rotation').setEnabled(vertexSelected); - this.actions.get('wordWrap').setEnabled(vertexSelected); - this.actions.get('autosize').setEnabled(vertexSelected); - var oneVertexSelected = vertexSelected && graph.getSelectionCount() == 1; - this.actions.get('group').setEnabled(graph.getSelectionCount() > 1 || - (oneVertexSelected && !graph.isContainer(graph.getSelectionCell()))); - this.actions.get('ungroup').setEnabled(graph.getSelectionCount() == 1 && - (graph.getModel().getChildCount(graph.getSelectionCell()) > 0 || - (oneVertexSelected && graph.isContainer(graph.getSelectionCell())))); - this.actions.get('removeFromGroup').setEnabled(oneVertexSelected && - graph.getModel().isVertex(graph.getModel().getParent(graph.getSelectionCell()))); - - // Updates menu states - var state = graph.view.getState(graph.getSelectionCell()); - this.menus.get('navigation').setEnabled(selected || graph.view.currentRoot != null); - this.actions.get('collapsible').setEnabled(vertexSelected && - (graph.isContainer(graph.getSelectionCell()) || graph.model.getChildCount(graph.getSelectionCell()) > 0)); - this.actions.get('home').setEnabled(graph.view.currentRoot != null); - this.actions.get('exitGroup').setEnabled(graph.view.currentRoot != null); - this.actions.get('enterGroup').setEnabled(graph.getSelectionCount() == 1 && graph.isValidRoot(graph.getSelectionCell())); - var foldable = graph.getSelectionCount() == 1 && graph.isCellFoldable(graph.getSelectionCell()); - this.actions.get('expand').setEnabled(foldable); - this.actions.get('collapse').setEnabled(foldable); - this.actions.get('editLink').setEnabled(graph.getSelectionCount() == 1); - this.actions.get('openLink').setEnabled(graph.getSelectionCount() == 1 && - graph.getLinkForCell(graph.getSelectionCell()) != null); - this.actions.get('guides').setEnabled(graph.isEnabled()); - this.actions.get('grid').setEnabled(!this.editor.chromeless || this.editor.editable); - - var unlocked = graph.isEnabled() && !graph.isCellLocked(graph.getDefaultParent()); - this.menus.get('layout').setEnabled(unlocked); - this.menus.get('insert').setEnabled(unlocked); - this.menus.get('direction').setEnabled(unlocked && vertexSelected); - this.menus.get('align').setEnabled(unlocked && vertexSelected && graph.getSelectionCount() > 1); - this.menus.get('distribute').setEnabled(unlocked && vertexSelected && graph.getSelectionCount() > 1); - this.actions.get('selectVertices').setEnabled(unlocked); - this.actions.get('selectEdges').setEnabled(unlocked); - this.actions.get('selectAll').setEnabled(unlocked); - this.actions.get('selectNone').setEnabled(unlocked); - - this.updatePasteActionStates(); -}; - -/** - * Refreshes the viewport. - */ -EditorUi.prototype.refresh = function(sizeDidChange) -{ - sizeDidChange = (sizeDidChange != null) ? sizeDidChange : true; - - var quirks = mxClient.IS_IE && (document.documentMode == null || document.documentMode == 5); - var w = this.container.clientWidth; - var h = this.container.clientHeight; - - if (this.container == document.body) - { - w = document.body.clientWidth || document.documentElement.clientWidth; - h = (quirks) ? document.body.clientHeight || document.documentElement.clientHeight : document.documentElement.clientHeight; - } - - // Workaround for bug on iOS see - // http://stackoverflow.com/questions/19012135/ios-7-ipad-safari-landscape-innerheight-outerheight-layout-issue - // FIXME: Fix if footer visible - var off = 0; - - if (mxClient.IS_IOS && !window.navigator.standalone) - { - if (window.innerHeight != document.documentElement.clientHeight) - { - off = document.documentElement.clientHeight - window.innerHeight; - window.scrollTo(0, 0); - } - } - - var effHsplitPosition = Math.max(0, Math.min(this.hsplitPosition, w - this.splitSize - 20)); - - var tmp = 0; - - if (this.menubar != null) - { - this.menubarContainer.style.height = this.menubarHeight + 'px'; - tmp += this.menubarHeight; - } - - if (this.toolbar != null) - { - this.toolbarContainer.style.top = this.menubarHeight + 'px'; - this.toolbarContainer.style.height = this.toolbarHeight + 'px'; - tmp += this.toolbarHeight; - } - - if (tmp > 0 && !mxClient.IS_QUIRKS) - { - tmp += 1; - } - - var sidebarFooterHeight = 0; - - if (this.sidebarFooterContainer != null) - { - var bottom = this.footerHeight + off; - sidebarFooterHeight = Math.max(0, Math.min(h - tmp - bottom, this.sidebarFooterHeight)); - this.sidebarFooterContainer.style.width = effHsplitPosition + 'px'; - this.sidebarFooterContainer.style.height = sidebarFooterHeight + 'px'; - this.sidebarFooterContainer.style.bottom = bottom + 'px'; - } - - var fw = (this.format != null) ? this.formatWidth : 0; - this.sidebarContainer.style.top = tmp + 'px'; - this.sidebarContainer.style.width = effHsplitPosition + 'px'; - this.formatContainer.style.top = tmp + 'px'; - this.formatContainer.style.width = fw + 'px'; - this.formatContainer.style.display = (this.format != null) ? '' : 'none'; - - this.diagramContainer.style.left = (this.hsplit.parentNode != null) ? (effHsplitPosition + this.splitSize) + 'px' : '0px'; - this.diagramContainer.style.top = this.sidebarContainer.style.top; - this.footerContainer.style.height = this.footerHeight + 'px'; - this.hsplit.style.top = this.sidebarContainer.style.top; - this.hsplit.style.bottom = (this.footerHeight + off) + 'px'; - this.hsplit.style.left = effHsplitPosition + 'px'; - - if (this.tabContainer != null) - { - this.tabContainer.style.left = this.diagramContainer.style.left; - } - - if (quirks) - { - this.menubarContainer.style.width = w + 'px'; - this.toolbarContainer.style.width = this.menubarContainer.style.width; - var sidebarHeight = Math.max(0, h - this.footerHeight - this.menubarHeight - this.toolbarHeight); - this.sidebarContainer.style.height = (sidebarHeight - sidebarFooterHeight) + 'px'; - this.formatContainer.style.height = sidebarHeight + 'px'; - this.diagramContainer.style.width = (this.hsplit.parentNode != null) ? Math.max(0, w - effHsplitPosition - this.splitSize - fw) + 'px' : w + 'px'; - this.footerContainer.style.width = this.menubarContainer.style.width; - var diagramHeight = Math.max(0, h - this.footerHeight - this.menubarHeight - this.toolbarHeight); - - if (this.tabContainer != null) - { - this.tabContainer.style.width = this.diagramContainer.style.width; - this.tabContainer.style.bottom = (this.footerHeight + off) + 'px'; - diagramHeight -= this.tabContainer.clientHeight; - } - - this.diagramContainer.style.height = diagramHeight + 'px'; - this.hsplit.style.height = diagramHeight + 'px'; - } - else - { - if (this.footerHeight > 0) - { - this.footerContainer.style.bottom = off + 'px'; - } - - this.diagramContainer.style.right = fw + 'px'; - var th = 0; - - if (this.tabContainer != null) - { - this.tabContainer.style.bottom = (this.footerHeight + off) + 'px'; - this.tabContainer.style.right = this.diagramContainer.style.right; - th = this.tabContainer.clientHeight; - } - - this.sidebarContainer.style.bottom = (this.footerHeight + sidebarFooterHeight + off) + 'px'; - this.formatContainer.style.bottom = (this.footerHeight + off) + 'px'; - this.diagramContainer.style.bottom = (this.footerHeight + off + th) + 'px'; - } - - if (sizeDidChange) - { - this.editor.graph.sizeDidChange(); - } -}; - -/** - * Creates the required containers. - */ -EditorUi.prototype.createTabContainer = function() -{ - return null; -}; - -/** - * Creates the required containers. - */ -EditorUi.prototype.createDivs = function() -{ - this.menubarContainer = this.createDiv('geMenubarContainer'); - this.toolbarContainer = this.createDiv('geToolbarContainer'); - this.sidebarContainer = this.createDiv('geSidebarContainer'); - this.formatContainer = this.createDiv('geSidebarContainer geFormatContainer'); - this.diagramContainer = this.createDiv('geDiagramContainer'); - this.footerContainer = this.createDiv('geFooterContainer'); - this.hsplit = this.createDiv('geHsplit'); - this.hsplit.setAttribute('title', mxResources.get('collapseExpand')); - - // Sets static style for containers - this.menubarContainer.style.top = '0px'; - this.menubarContainer.style.left = '0px'; - this.menubarContainer.style.right = '0px'; - this.toolbarContainer.style.left = '0px'; - this.toolbarContainer.style.right = '0px'; - this.sidebarContainer.style.left = '0px'; - this.formatContainer.style.right = '0px'; - this.formatContainer.style.zIndex = '1'; - this.diagramContainer.style.right = ((this.format != null) ? this.formatWidth : 0) + 'px'; - this.footerContainer.style.left = '0px'; - this.footerContainer.style.right = '0px'; - this.footerContainer.style.bottom = '0px'; - this.footerContainer.style.zIndex = mxPopupMenu.prototype.zIndex - 2; - this.hsplit.style.width = this.splitSize + 'px'; - this.sidebarFooterContainer = this.createSidebarFooterContainer(); - - if (this.sidebarFooterContainer) - { - this.sidebarFooterContainer.style.left = '0px'; - } - - if (!this.editor.chromeless) - { - this.tabContainer = this.createTabContainer(); - } - else - { - this.diagramContainer.style.border = 'none'; - } -}; - -/** - * Hook for sidebar footer container. This implementation returns null. - */ -EditorUi.prototype.createSidebarFooterContainer = function() -{ - return null; -}; - -/** - * Creates the required containers. - */ -EditorUi.prototype.createUi = function() -{ - // Creates menubar - this.menubar = (this.editor.chromeless) ? null : this.menus.createMenubar(this.createDiv('geMenubar')); - - if (this.menubar != null) - { - this.menubarContainer.appendChild(this.menubar.container); - } - - // Adds status bar in menubar - if (this.menubar != null) - { - this.statusContainer = this.createStatusContainer(); - - // Connects the status bar to the editor status - this.editor.addListener('statusChanged', mxUtils.bind(this, function() - { - this.setStatusText(this.editor.getStatus()); - })); - - this.setStatusText(this.editor.getStatus()); - this.menubar.container.appendChild(this.statusContainer); - - // Inserts into DOM - this.container.appendChild(this.menubarContainer); - } - - // Creates the sidebar - this.sidebar = (this.editor.chromeless) ? null : this.createSidebar(this.sidebarContainer); - - if (this.sidebar != null) - { - this.container.appendChild(this.sidebarContainer); - } - - // Creates the format sidebar - this.format = (this.editor.chromeless || !this.formatEnabled) ? null : this.createFormat(this.formatContainer); - - if (this.format != null) - { - this.container.appendChild(this.formatContainer); - } - - // Creates the footer - var footer = (this.editor.chromeless) ? null : this.createFooter(); - - if (footer != null) - { - this.footerContainer.appendChild(footer); - this.container.appendChild(this.footerContainer); - } - - if (this.sidebar != null && this.sidebarFooterContainer) - { - this.container.appendChild(this.sidebarFooterContainer); - } - - this.container.appendChild(this.diagramContainer); - - if (this.container != null && this.tabContainer != null) - { - this.container.appendChild(this.tabContainer); - } - - // Creates toolbar - this.toolbar = (this.editor.chromeless) ? null : this.createToolbar(this.createDiv('geToolbar')); - - if (this.toolbar != null) - { - this.toolbarContainer.appendChild(this.toolbar.container); - this.container.appendChild(this.toolbarContainer); - } - - // HSplit - if (this.sidebar != null) - { - this.container.appendChild(this.hsplit); - - this.addSplitHandler(this.hsplit, true, 0, mxUtils.bind(this, function(value) - { - this.hsplitPosition = value; - this.refresh(); - })); - } -}; - -/** - * Creates a new toolbar for the given container. - */ -EditorUi.prototype.createStatusContainer = function() -{ - var container = document.createElement('a'); - container.className = 'geItem geStatus'; - - if (screen.width < 420) - { - container.style.maxWidth = Math.max(20, screen.width - 320) + 'px'; - container.style.overflow = 'hidden'; - } - - return container; -}; - -/** - * Creates a new toolbar for the given container. - */ -EditorUi.prototype.setStatusText = function(value) -{ - this.statusContainer.innerHTML = value; -}; - -/** - * Creates a new toolbar for the given container. - */ -EditorUi.prototype.createToolbar = function(container) -{ - return new Toolbar(this, container); -}; - -/** - * Creates a new sidebar for the given container. - */ -EditorUi.prototype.createSidebar = function(container) -{ - return new Sidebar(this, container); -}; - -/** - * Creates a new sidebar for the given container. - */ -EditorUi.prototype.createFormat = function(container) -{ - return new Format(this, container); -}; - -/** - * Creates and returns a new footer. - */ -EditorUi.prototype.createFooter = function() -{ - return this.createDiv('geFooter'); -}; - -/** - * Creates the actual toolbar for the toolbar container. - */ -EditorUi.prototype.createDiv = function(classname) -{ - var elt = document.createElement('div'); - elt.className = classname; - - return elt; -}; - -/** - * Updates the states of the given undo/redo items. - */ -EditorUi.prototype.addSplitHandler = function(elt, horizontal, dx, onChange) -{ - var start = null; - var initial = null; - var ignoreClick = true; - var last = null; - - // Disables built-in pan and zoom in IE10 and later - if (mxClient.IS_POINTER) - { - elt.style.touchAction = 'none'; - } - - var getValue = mxUtils.bind(this, function() - { - var result = parseInt(((horizontal) ? elt.style.left : elt.style.bottom)); - - // Takes into account hidden footer - if (!horizontal) - { - result = result + dx - this.footerHeight; - } - - return result; - }); - - function moveHandler(evt) - { - if (start != null) - { - var pt = new mxPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt)); - onChange(Math.max(0, initial + ((horizontal) ? (pt.x - start.x) : (start.y - pt.y)) - dx)); - mxEvent.consume(evt); - - if (initial != getValue()) - { - ignoreClick = true; - last = null; - } - } - }; - - function dropHandler(evt) - { - moveHandler(evt); - initial = null; - start = null; - }; - - mxEvent.addGestureListeners(elt, function(evt) - { - start = new mxPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt)); - initial = getValue(); - ignoreClick = false; - mxEvent.consume(evt); - }); - - mxEvent.addListener(elt, 'click', mxUtils.bind(this, function(evt) - { - if (!ignoreClick && this.hsplitClickEnabled) - { - var next = (last != null) ? last - dx : 0; - last = getValue(); - onChange(next); - mxEvent.consume(evt); - } - })); - - mxEvent.addGestureListeners(document, null, moveHandler, dropHandler); - - this.destroyFunctions.push(function() - { - mxEvent.removeGestureListeners(document, null, moveHandler, dropHandler); - }); -}; - -/** - * Displays a print dialog. - */ -EditorUi.prototype.showDialog = function(elt, w, h, modal, closable, onClose, noScroll, trasparent) -{ - this.editor.graph.tooltipHandler.hideTooltip(); - - if (this.dialogs == null) - { - this.dialogs = []; - } - - this.dialog = new Dialog(this, elt, w, h, modal, closable, onClose, noScroll, trasparent); - this.dialogs.push(this.dialog); -}; - -/** - * Displays a print dialog. - */ -EditorUi.prototype.hideDialog = function(cancel) -{ - if (this.dialogs != null && this.dialogs.length > 0) - { - var dlg = this.dialogs.pop(); - dlg.close(cancel); - - this.dialog = (this.dialogs.length > 0) ? this.dialogs[this.dialogs.length - 1] : null; - - if (this.dialog == null && this.editor.graph.container.style.visibility != 'hidden') - { - this.editor.graph.container.focus(); - } - - mxUtils.clearSelection(); - this.editor.fireEvent(new mxEventObject('hideDialog')); - } -}; - -/** - * Display a color dialog. - */ -EditorUi.prototype.pickColor = function(color, apply) -{ - var graph = this.editor.graph; - var selState = graph.cellEditor.saveSelection(); - - var dlg = new ColorDialog(this, color || 'none', function(color) - { - graph.cellEditor.restoreSelection(selState); - apply(color); - }, function() - { - graph.cellEditor.restoreSelection(selState); - }); - this.showDialog(dlg.container, 230, 430, true, false); - dlg.init(); -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -EditorUi.prototype.openFile = function() -{ - // Closes dialog after open - window.openFile = new OpenFile(mxUtils.bind(this, function(cancel) - { - this.hideDialog(cancel); - })); - - // Removes openFile if dialog is closed - this.showDialog(new OpenDialog(this).container, (Editor.useLocalStorage) ? 640 : 320, - (Editor.useLocalStorage) ? 480 : 220, true, true, function() - { - window.openFile = null; - }); -}; - -/** - * Extracs the graph model from the given HTML data from a data transfer event. - */ -EditorUi.prototype.extractGraphModelFromHtml = function(data) -{ - var result = null; - - try - { - var idx = data.indexOf('<mxGraphModel '); - - if (idx >= 0) - { - var idx2 = data.lastIndexOf('</mxGraphModel>'); - - if (idx2 > idx) - { - result = data.substring(idx, idx2 + 21).replace(/>/g, '>'). - replace(/</g, '<').replace(/\\"/g, '"').replace(/\n/g, ''); - } - } - } - catch (e) - { - // ignore - } - - return result; -}; - -/** - * Opens the given files in the editor. - */ -EditorUi.prototype.extractGraphModelFromEvent = function(evt) -{ - var result = null; - var data = null; - - if (evt != null) - { - var provider = (evt.dataTransfer != null) ? evt.dataTransfer : evt.clipboardData; - - if (provider != null) - { - if (document.documentMode == 10 || document.documentMode == 11) - { - data = provider.getData('Text'); - } - else - { - data = (mxUtils.indexOf(provider.types, 'text/html') >= 0) ? provider.getData('text/html') : null; - - if (mxUtils.indexOf(provider.types, 'text/plain' && (data == null || data.length == 0))) - { - data = provider.getData('text/plain'); - } - } - - if (data != null) - { - data = this.editor.graph.zapGremlins(mxUtils.trim(data)); - - // Tries parsing as HTML document with embedded XML - var xml = this.extractGraphModelFromHtml(data); - - if (xml != null) - { - data = xml; - } - } - } - } - - if (data != null && this.isCompatibleString(data)) - { - result = data; - } - - return result; -}; - -/** - * Hook for subclassers to return true if event data is a supported format. - * This implementation always returns false. - */ -EditorUi.prototype.isCompatibleString = function(data) -{ - return false; -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -EditorUi.prototype.saveFile = function(forceDialog) -{ - if (!forceDialog && this.editor.filename != null) - { - this.save(this.editor.getOrCreateFilename()); - } - else - { - var dlg = new FilenameDialog(this, this.editor.getOrCreateFilename(), mxResources.get('save'), mxUtils.bind(this, function(name) - { - this.save(name); - }), null, mxUtils.bind(this, function(name) - { - if (name != null && name.length > 0) - { - return true; - } - - mxUtils.confirm(mxResources.get('invalidName')); - - return false; - })); - this.showDialog(dlg.container, 300, 100, true, true); - dlg.init(); - } -}; - -/** - * Saves the current graph under the given filename. - */ -EditorUi.prototype.save = function(name) -{ - if (name != null) - { - if (this.editor.graph.isEditing()) - { - this.editor.graph.stopEditing(); - } - - var xml = mxUtils.getXml(this.editor.getGraphXml()); - - try - { - if (Editor.useLocalStorage) - { - if (localStorage.getItem(name) != null && - !mxUtils.confirm(mxResources.get('replaceIt', [name]))) - { - return; - } - - localStorage.setItem(name, xml); - this.editor.setStatus(mxUtils.htmlEntities(mxResources.get('saved')) + ' ' + new Date()); - } - else - { - if (xml.length < MAX_REQUEST_SIZE) - { - new mxXmlRequest(SAVE_URL, 'filename=' + encodeURIComponent(name) + - '&xml=' + encodeURIComponent(xml)).simulate(document, '_blank'); - } - else - { - mxUtils.alert(mxResources.get('drawingTooLarge')); - mxUtils.popup(xml); - - return; - } - } - - this.editor.setModified(false); - this.editor.setFilename(name); - this.updateDocumentTitle(); - } - catch (e) - { - this.editor.setStatus(mxUtils.htmlEntities(mxResources.get('errorSavingFile'))); - } - } -}; - -/** - * Executes the given layout. - */ -EditorUi.prototype.executeLayout = function(exec, animate, post) -{ - var graph = this.editor.graph; - - if (graph.isEnabled()) - { - graph.getModel().beginUpdate(); - try - { - exec(); - } - catch (e) - { - throw e; - } - finally - { - // Animates the changes in the graph model except - // for Camino, where animation is too slow - if (this.allowAnimation && animate && navigator.userAgent.indexOf('Camino') < 0) - { - // New API for animating graph layout results asynchronously - var morph = new mxMorphing(graph); - morph.addListener(mxEvent.DONE, mxUtils.bind(this, function() - { - graph.getModel().endUpdate(); - - if (post != null) - { - post(); - } - })); - - morph.startAnimation(); - } - else - { - graph.getModel().endUpdate(); - - if (post != null) - { - post(); - } - } - } - } -}; - -/** - * Hides the current menu. - */ -EditorUi.prototype.showImageDialog = function(title, value, fn, ignoreExisting) -{ - var cellEditor = this.editor.graph.cellEditor; - var selState = cellEditor.saveSelection(); - var newValue = mxUtils.prompt(title, value); - cellEditor.restoreSelection(selState); - - if (newValue != null && newValue.length > 0) - { - var img = new Image(); - - img.onload = function() - { - fn(newValue, img.width, img.height); - }; - img.onerror = function() - { - fn(null); - mxUtils.alert(mxResources.get('fileNotFound')); - }; - - img.src = newValue; - } - else - { - fn(null); - } -}; - -/** - * Hides the current menu. - */ -EditorUi.prototype.showLinkDialog = function(value, btnLabel, fn) -{ - var dlg = new LinkDialog(this, value, btnLabel, fn); - this.showDialog(dlg.container, 420, 90, true, true); - dlg.init(); -}; - -/** - * Hides the current menu. - */ -EditorUi.prototype.showDataDialog = function(cell) -{ - if (cell != null) - { - var dlg = new EditDataDialog(this, cell); - this.showDialog(dlg.container, 340, 340, true, false, null, false); - dlg.init(); - } -}; - -/** - * Hides the current menu. - */ -EditorUi.prototype.showBackgroundImageDialog = function(apply) -{ - apply = (apply != null) ? apply : mxUtils.bind(this, function(image) - { - var change = new ChangePageSetup(this, null, image); - change.ignoreColor = true; - - this.editor.graph.model.execute(change); - }); - - var newValue = mxUtils.prompt(mxResources.get('backgroundImage'), ''); - - if (newValue != null && newValue.length > 0) - { - var img = new Image(); - - img.onload = function() - { - apply(new mxImage(newValue, img.width, img.height)); - }; - img.onerror = function() - { - apply(null); - mxUtils.alert(mxResources.get('fileNotFound')); - }; - - img.src = newValue; - } - else - { - apply(null); - } -}; - -/** - * Loads the stylesheet for this graph. - */ -EditorUi.prototype.setBackgroundImage = function(image) -{ - this.editor.graph.setBackgroundImage(image); - this.editor.graph.view.validateBackgroundImage(); - - this.fireEvent(new mxEventObject('backgroundImageChanged')); -}; - -/** - * Creates the keyboard event handler for the current graph and history. - */ -EditorUi.prototype.confirm = function(msg, okFn, cancelFn) -{ - if (mxUtils.confirm(msg)) - { - if (okFn != null) - { - okFn(); - } - } - else if (cancelFn != null) - { - cancelFn(); - } -}; - -/** - * Creates the keyboard event handler for the current graph and history. - */ -EditorUi.prototype.createOutline = function(wnd) -{ - var outline = new mxOutline(this.editor.graph); - outline.border = 20; - - mxEvent.addListener(window, 'resize', function() - { - outline.update(); - }); - - this.addListener('pageFormatChanged', function() - { - outline.update(); - }); - - return outline; -}; - -/** - * Creates the keyboard event handler for the current graph and history. - */ -EditorUi.prototype.createKeyHandler = function(editor) -{ - var editorUi = this; - var graph = this.editor.graph; - var keyHandler = new mxKeyHandler(graph); - - var isEventIgnored = keyHandler.isEventIgnored; - keyHandler.isEventIgnored = function(evt) - { - // Handles undo/redo/ctrl+./,/u via action and allows ctrl+b/i only if editing value is HTML (except for FF and Safari) - return (!this.isControlDown(evt) || mxEvent.isShiftDown(evt) || (evt.keyCode != 90 && evt.keyCode != 89 && - evt.keyCode != 188 && evt.keyCode != 190 && evt.keyCode != 85)) && ((evt.keyCode != 66 && evt.keyCode != 73) || - !this.isControlDown(evt) || (this.graph.cellEditor.isContentEditing() && !mxClient.IS_FF && !mxClient.IS_SF)) && - isEventIgnored.apply(this, arguments); - }; - - // Ignores graph enabled state but not chromeless state - keyHandler.isEnabledForEvent = function(evt) - { - return (!mxEvent.isConsumed(evt) && this.isGraphEvent(evt) && this.isEnabled() && - (editorUi.dialogs == null || editorUi.dialogs.length == 0)); - }; - - // Routes command-key to control-key on Mac - keyHandler.isControlDown = function(evt) - { - return mxEvent.isControlDown(evt) || (mxClient.IS_MAC && evt.metaKey); - }; - - var queue = []; - var thread = null; - - // Helper function to move cells with the cursor keys - function nudge(keyCode, stepSize, resize) - { - queue.push(function() - { - if (!graph.isSelectionEmpty() && graph.isEnabled()) - { - stepSize = (stepSize != null) ? stepSize : 1; - - if (resize) - { - // Resizes all selected vertices - graph.getModel().beginUpdate(); - try - { - var cells = graph.getSelectionCells(); - - for (var i = 0; i < cells.length; i++) - { - if (graph.getModel().isVertex(cells[i]) && graph.isCellResizable(cells[i])) - { - var geo = graph.getCellGeometry(cells[i]); - - if (geo != null) - { - geo = geo.clone(); - - if (keyCode == 37) - { - geo.width = Math.max(0, geo.width - stepSize); - } - else if (keyCode == 38) - { - geo.height = Math.max(0, geo.height - stepSize); - } - else if (keyCode == 39) - { - geo.width += stepSize; - } - else if (keyCode == 40) - { - geo.height += stepSize; - } - - graph.getModel().setGeometry(cells[i], geo); - } - } - } - } - finally - { - graph.getModel().endUpdate(); - } - } - else - { - // Moves vertices up/down in a stack layout - var cell = graph.getSelectionCell(); - var parent = graph.model.getParent(cell); - var layout = null; - - if (graph.getSelectionCount() == 1 && graph.model.isVertex(cell) && - graph.layoutManager != null && !graph.isCellLocked(cell)) - { - layout = graph.layoutManager.getLayout(parent); - } - - if (layout != null && layout.constructor == mxStackLayout) - { - var index = parent.getIndex(cell); - - if (keyCode == 37 || keyCode == 38) - { - graph.model.add(parent, cell, Math.max(0, index - 1)); - } - else if (keyCode == 39 ||keyCode == 40) - { - graph.model.add(parent, cell, Math.min(graph.model.getChildCount(parent), index + 1)); - } - } - else - { - var dx = 0; - var dy = 0; - - if (keyCode == 37) - { - dx = -stepSize; - } - else if (keyCode == 38) - { - dy = -stepSize; - } - else if (keyCode == 39) - { - dx = stepSize; - } - else if (keyCode == 40) - { - dy = stepSize; - } - - graph.moveCells(graph.getMovableCells(graph.getSelectionCells()), dx, dy); - } - } - } - }); - - if (thread != null) - { - window.clearTimeout(thread); - } - - thread = window.setTimeout(function() - { - if (queue.length > 0) - { - graph.getModel().beginUpdate(); - try - { - for (var i = 0; i < queue.length; i++) - { - queue[i](); - } - - queue = []; - } - finally - { - graph.getModel().endUpdate(); - } - graph.scrollCellToVisible(graph.getSelectionCell()); - } - }, 200); - }; - - // Overridden to handle special alt+shift+cursor keyboard shortcuts - var directions = {37: mxConstants.DIRECTION_WEST, 38: mxConstants.DIRECTION_NORTH, - 39: mxConstants.DIRECTION_EAST, 40: mxConstants.DIRECTION_SOUTH}; - - var keyHandlerGetFunction = keyHandler.getFunction; - - // Alt+Shift+Keycode mapping to action - var altShiftActions = {67: this.actions.get('clearWaypoints'), // Alt+Shift+C - 65: this.actions.get('connectionArrows'), // Alt+Shift+A - 76: this.actions.get('editLink'), // Alt+Shift+L - 80: this.actions.get('connectionPoints'), // Alt+Shift+P - 84: this.actions.get('editTooltip'), // Alt+Shift+T - 86: this.actions.get('pasteSize'), // Alt+Shift+V - 88: this.actions.get('copySize') // Alt+Shift+X - }; - - mxKeyHandler.prototype.getFunction = function(evt) - { - if (graph.isEnabled()) - { - // TODO: Add alt modified state in core API, here are some specific cases - if (mxEvent.isShiftDown(evt) && mxEvent.isAltDown(evt)) - { - var action = altShiftActions[evt.keyCode]; - - if (action != null) - { - return action.funct; - } - } - - if (evt.keyCode == 9 && mxEvent.isAltDown(evt)) - { - if (mxEvent.isShiftDown(evt)) - { - // Alt+Shift+Tab - return function() - { - graph.selectParentCell(); - }; - } - else - { - // Alt+Tab - return function() - { - graph.selectChildCell(); - }; - } - } - else if (directions[evt.keyCode] != null && !graph.isSelectionEmpty()) - { - if (mxEvent.isShiftDown(evt) && mxEvent.isAltDown(evt)) - { - if (graph.model.isVertex(graph.getSelectionCell())) - { - return function() - { - var cells = graph.connectVertex(graph.getSelectionCell(), directions[evt.keyCode], - graph.defaultEdgeLength, evt, true); - - if (cells != null && cells.length > 0) - { - if (cells.length == 1 && graph.model.isEdge(cells[0])) - { - graph.setSelectionCell(graph.model.getTerminal(cells[0], false)); - } - else - { - graph.setSelectionCell(cells[cells.length - 1]); - } - - graph.scrollCellToVisible(graph.getSelectionCell()); - - if (editorUi.hoverIcons != null) - { - editorUi.hoverIcons.update(graph.view.getState(graph.getSelectionCell())); - } - } - }; - } - } - else - { - // Avoids consuming event if no vertex is selected by returning null below - // Cursor keys move and resize (ctrl) cells - if (this.isControlDown(evt)) - { - return function() - { - nudge(evt.keyCode, (mxEvent.isShiftDown(evt)) ? graph.gridSize : null, true); - }; - } - else - { - return function() - { - nudge(evt.keyCode, (mxEvent.isShiftDown(evt)) ? graph.gridSize : null); - }; - } - } - } - } - - return keyHandlerGetFunction.apply(this, arguments); - }; - - // Binds keystrokes to actions - keyHandler.bindAction = mxUtils.bind(this, function(code, control, key, shift) - { - var action = this.actions.get(key); - - if (action != null) - { - var f = function() - { - if (action.isEnabled()) - { - action.funct(); - } - }; - - if (control) - { - if (shift) - { - keyHandler.bindControlShiftKey(code, f); - } - else - { - keyHandler.bindControlKey(code, f); - } - } - else - { - if (shift) - { - keyHandler.bindShiftKey(code, f); - } - else - { - keyHandler.bindKey(code, f); - } - } - } - }); - - var ui = this; - var keyHandlerEscape = keyHandler.escape; - keyHandler.escape = function(evt) - { - keyHandlerEscape.apply(this, arguments); - }; - - // Ignores enter keystroke. Remove this line if you want the - // enter keystroke to stop editing. N, W, T are reserved. - keyHandler.enter = function() {}; - - keyHandler.bindControlShiftKey(36, function() { graph.exitGroup(); }); // Ctrl+Shift+Home - keyHandler.bindControlShiftKey(35, function() { graph.enterGroup(); }); // Ctrl+Shift+End - keyHandler.bindKey(36, function() { graph.home(); }); // Home - keyHandler.bindKey(35, function() { graph.refresh(); }); // End - keyHandler.bindAction(107, true, 'zoomIn'); // Ctrl+Plus - keyHandler.bindAction(109, true, 'zoomOut'); // Ctrl+Minus - keyHandler.bindAction(80, true, 'print'); // Ctrl+P - keyHandler.bindAction(79, true, 'outline', true); // Ctrl+Shift+O - keyHandler.bindAction(112, false, 'about'); // F1 - - if (!this.editor.chromeless || this.editor.editable) - { - keyHandler.bindControlKey(36, function() { if (graph.isEnabled()) { graph.foldCells(true); }}); // Ctrl+Home - keyHandler.bindControlKey(35, function() { if (graph.isEnabled()) { graph.foldCells(false); }}); // Ctrl+End - keyHandler.bindControlKey(13, function() { if (graph.isEnabled()) { graph.setSelectionCells(graph.duplicateCells(graph.getSelectionCells(), false)); }}); // Ctrl+Enter - keyHandler.bindAction(8, false, 'delete'); // Backspace - keyHandler.bindAction(8, true, 'deleteAll'); // Backspace - keyHandler.bindAction(46, false, 'delete'); // Delete - keyHandler.bindAction(46, true, 'deleteAll'); // Ctrl+Delete - keyHandler.bindAction(72, true, 'resetView'); // Ctrl+H - keyHandler.bindAction(72, true, 'fitWindow', true); // Ctrl+Shift+H - keyHandler.bindAction(74, true, 'fitPage'); // Ctrl+J - keyHandler.bindAction(74, true, 'fitTwoPages', true); // Ctrl+Shift+J - keyHandler.bindAction(48, true, 'customZoom'); // Ctrl+0 - keyHandler.bindAction(82, true, 'turn'); // Ctrl+R - keyHandler.bindAction(82, true, 'clearDefaultStyle', true); // Ctrl+Shift+R - keyHandler.bindAction(83, true, 'save'); // Ctrl+S - keyHandler.bindAction(83, true, 'saveAs', true); // Ctrl+Shift+S - keyHandler.bindAction(65, true, 'selectAll'); // Ctrl+A - keyHandler.bindAction(65, true, 'selectNone', true); // Ctrl+A - keyHandler.bindAction(73, true, 'selectVertices', true); // Ctrl+Shift+I - keyHandler.bindAction(69, true, 'selectEdges', true); // Ctrl+Shift+E - keyHandler.bindAction(69, true, 'editStyle'); // Ctrl+E - keyHandler.bindAction(66, true, 'bold'); // Ctrl+B - keyHandler.bindAction(66, true, 'toBack', true); // Ctrl+Shift+B - keyHandler.bindAction(70, true, 'toFront', true); // Ctrl+Shift+F - keyHandler.bindAction(68, true, 'duplicate'); // Ctrl+D - keyHandler.bindAction(68, true, 'setAsDefaultStyle', true); // Ctrl+Shift+D - keyHandler.bindAction(90, true, 'undo'); // Ctrl+Z - keyHandler.bindAction(89, true, 'autosize', true); // Ctrl+Shift+Y - keyHandler.bindAction(88, true, 'cut'); // Ctrl+X - keyHandler.bindAction(67, true, 'copy'); // Ctrl+C - keyHandler.bindAction(86, true, 'paste'); // Ctrl+V - keyHandler.bindAction(71, true, 'group'); // Ctrl+G - keyHandler.bindAction(77, true, 'editData'); // Ctrl+M - keyHandler.bindAction(71, true, 'grid', true); // Ctrl+Shift+G - keyHandler.bindAction(73, true, 'italic'); // Ctrl+I - keyHandler.bindAction(76, true, 'lockUnlock'); // Ctrl+L - keyHandler.bindAction(76, true, 'layers', true); // Ctrl+Shift+L - keyHandler.bindAction(80, true, 'formatPanel', true); // Ctrl+Shift+P - keyHandler.bindAction(85, true, 'underline'); // Ctrl+U - keyHandler.bindAction(85, true, 'ungroup', true); // Ctrl+Shift+U - keyHandler.bindAction(190, true, 'superscript'); // Ctrl+. - keyHandler.bindAction(188, true, 'subscript'); // Ctrl+, - keyHandler.bindKey(13, function() { if (graph.isEnabled()) { graph.startEditingAtCell(); }}); // Enter - keyHandler.bindKey(113, function() { if (graph.isEnabled()) { graph.startEditingAtCell(); }}); // F2 - } - - if (!mxClient.IS_WIN) - { - keyHandler.bindAction(90, true, 'redo', true); // Ctrl+Shift+Z - } - else - { - keyHandler.bindAction(89, true, 'redo'); // Ctrl+Y - } - - return keyHandler; -}; - -/** - * Creates the keyboard event handler for the current graph and history. - */ -EditorUi.prototype.destroy = function() -{ - if (this.editor != null) - { - this.editor.destroy(); - this.editor = null; - } - - if (this.menubar != null) - { - this.menubar.destroy(); - this.menubar = null; - } - - if (this.toolbar != null) - { - this.toolbar.destroy(); - this.toolbar = null; - } - - if (this.sidebar != null) - { - this.sidebar.destroy(); - this.sidebar = null; - } - - if (this.keyHandler != null) - { - this.keyHandler.destroy(); - this.keyHandler = null; - } - - if (this.keydownHandler != null) - { - mxEvent.removeListener(document, 'keydown', this.keydownHandler); - this.keydownHandler = null; - } - - if (this.keyupHandler != null) - { - mxEvent.removeListener(document, 'keyup', this.keyupHandler); - this.keyupHandler = null; - } - - if (this.resizeHandler != null) - { - mxEvent.removeListener(window, 'resize', this.resizeHandler); - this.resizeHandler = null; - } - - if (this.gestureHandler != null) - { - mxEvent.removeGestureListeners(document, this.gestureHandler); - this.gestureHandler = null; - } - - if (this.orientationChangeHandler != null) - { - mxEvent.removeListener(window, 'orientationchange', this.orientationChangeHandler); - this.orientationChangeHandler = null; - } - - if (this.scrollHandler != null) - { - mxEvent.removeListener(window, 'scroll', this.scrollHandler); - this.scrollHandler = null; - } - - if (this.destroyFunctions != null) - { - for (var i = 0; i < this.destroyFunctions.length; i++) - { - this.destroyFunctions[i](); - } - - this.destroyFunctions = null; - } - - var c = [this.menubarContainer, this.toolbarContainer, this.sidebarContainer, - this.formatContainer, this.diagramContainer, this.footerContainer, - this.chromelessToolbar, this.hsplit, this.sidebarFooterContainer, - this.layersDialog]; - - for (var i = 0; i < c.length; i++) - { - if (c[i] != null && c[i].parentNode != null) - { - c[i].parentNode.removeChild(c[i]); - } - } -}; diff --git a/media/grapheditor/js/Format.js b/media/grapheditor/js/Format.js deleted file mode 100644 index 87eac59531..0000000000 --- a/media/grapheditor/js/Format.js +++ /dev/null @@ -1,5497 +0,0 @@ -/** - * Copyright (c) 2006-2012, JGraph Ltd - */ -Format = function(editorUi, container) -{ - this.editorUi = editorUi; - this.container = container; -}; - -/** - * Returns information about the current selection. - */ -Format.prototype.labelIndex = 0; - -/** - * Returns information about the current selection. - */ -Format.prototype.currentIndex = 0; - -/** - * Returns information about the current selection. - */ -Format.prototype.showCloseButton = true; - -/** - * Background color for inactive tabs. - */ -Format.prototype.inactiveTabBackgroundColor = '#d7d7d7'; - -/** - * Background color for inactive tabs. - */ -Format.prototype.roundableShapes = ['label', 'rectangle', 'internalStorage', 'corner', - 'parallelogram', 'swimlane', 'triangle', 'trapezoid', - 'ext', 'step', 'tee', 'process', 'link', - 'rhombus', 'offPageConnector', 'loopLimit', 'hexagon', - 'manualInput', 'curlyBracket', 'singleArrow', 'callout', - 'doubleArrow', 'flexArrow', 'card', 'umlLifeline']; - -/** - * Adds the label menu items to the given menu and parent. - */ -Format.prototype.init = function() -{ - var ui = this.editorUi; - var editor = ui.editor; - var graph = editor.graph; - - this.update = mxUtils.bind(this, function(sender, evt) - { - this.clearSelectionState(); - this.refresh(); - }); - - graph.getSelectionModel().addListener(mxEvent.CHANGE, this.update); - graph.addListener(mxEvent.EDITING_STARTED, this.update); - graph.addListener(mxEvent.EDITING_STOPPED, this.update); - graph.getModel().addListener(mxEvent.CHANGE, this.update); - graph.addListener(mxEvent.ROOT, mxUtils.bind(this, function() - { - this.refresh(); - })); - - editor.addListener('autosaveChanged', mxUtils.bind(this, function() - { - this.refresh(); - })); - - this.refresh(); -}; - -/** - * Returns information about the current selection. - */ -Format.prototype.clearSelectionState = function() -{ - this.selectionState = null; -}; - -/** - * Returns information about the current selection. - */ -Format.prototype.getSelectionState = function() -{ - if (this.selectionState == null) - { - this.selectionState = this.createSelectionState(); - } - - return this.selectionState; -}; - -/** - * Returns information about the current selection. - */ -Format.prototype.createSelectionState = function() -{ - var cells = this.editorUi.editor.graph.getSelectionCells(); - var result = this.initSelectionState(); - - for (var i = 0; i < cells.length; i++) - { - this.updateSelectionStateForCell(result, cells[i], cells); - } - - return result; -}; - -/** - * Returns information about the current selection. - */ -Format.prototype.initSelectionState = function() -{ - return {vertices: [], edges: [], x: null, y: null, width: null, height: null, style: {}, - containsImage: false, containsLabel: false, fill: true, glass: true, rounded: true, - comic: true, autoSize: false, image: true, shadow: true, lineJumps: true}; -}; - -/** - * Returns information about the current selection. - */ -Format.prototype.updateSelectionStateForCell = function(result, cell, cells) -{ - var graph = this.editorUi.editor.graph; - - if (graph.getModel().isVertex(cell)) - { - result.vertices.push(cell); - var geo = graph.getCellGeometry(cell); - - if (geo != null) - { - if (geo.width > 0) - { - if (result.width == null) - { - result.width = geo.width; - } - else if (result.width != geo.width) - { - result.width = ''; - } - } - else - { - result.containsLabel = true; - } - - if (geo.height > 0) - { - if (result.height == null) - { - result.height = geo.height; - } - else if (result.height != geo.height) - { - result.height = ''; - } - } - else - { - result.containsLabel = true; - } - - if (!geo.relative || geo.offset != null) - { - var x = (geo.relative) ? geo.offset.x : geo.x; - var y = (geo.relative) ? geo.offset.y : geo.y; - - if (result.x == null) - { - result.x = x; - } - else if (result.x != x) - { - result.x = ''; - } - - if (result.y == null) - { - result.y = y; - } - else if (result.y != y) - { - result.y = ''; - } - } - } - } - else if (graph.getModel().isEdge(cell)) - { - result.edges.push(cell); - } - - var state = graph.view.getState(cell); - - if (state != null) - { - result.autoSize = result.autoSize || this.isAutoSizeState(state); - result.glass = result.glass && this.isGlassState(state); - result.rounded = result.rounded && this.isRoundedState(state); - result.lineJumps = result.lineJumps && this.isLineJumpState(state); - result.comic = result.comic && this.isComicState(state); - result.image = result.image && this.isImageState(state); - result.shadow = result.shadow && this.isShadowState(state); - result.fill = result.fill && this.isFillState(state); - - var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); - result.containsImage = result.containsImage || shape == 'image'; - - for (var key in state.style) - { - var value = state.style[key]; - - if (value != null) - { - if (result.style[key] == null) - { - result.style[key] = value; - } - else if (result.style[key] != value) - { - result.style[key] = ''; - } - } - } - } -}; - -/** - * Returns information about the current selection. - */ -Format.prototype.isFillState = function(state) -{ - return state.view.graph.model.isVertex(state.cell) || - mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null) == 'arrow' || - mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null) == 'filledEdge' || - mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null) == 'flexArrow'; -}; - -/** - * Returns information about the current selection. - */ -Format.prototype.isGlassState = function(state) -{ - var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); - - return (shape == 'label' || shape == 'rectangle' || shape == 'internalStorage' || - shape == 'ext' || shape == 'umlLifeline' || shape == 'swimlane' || - shape == 'process'); -}; - -/** - * Returns information about the current selection. - */ -Format.prototype.isRoundedState = function(state) -{ - return (state.shape != null) ? state.shape.isRoundable() : - mxUtils.indexOf(this.roundableShapes, mxUtils.getValue(state.style, - mxConstants.STYLE_SHAPE, null)) >= 0; -}; - -/** - * Returns information about the current selection. - */ -Format.prototype.isLineJumpState = function(state) -{ - var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); - var curved = mxUtils.getValue(state.style, mxConstants.STYLE_CURVED, false); - - return !curved && (shape == 'connector' || shape == 'filledEdge'); -}; - -/** - * Returns information about the current selection. - */ -Format.prototype.isComicState = function(state) -{ - var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); - - return mxUtils.indexOf(['label', 'rectangle', 'internalStorage', 'corner', 'parallelogram', 'note', 'collate', - 'swimlane', 'triangle', 'trapezoid', 'ext', 'step', 'tee', 'process', 'link', 'rhombus', - 'offPageConnector', 'loopLimit', 'hexagon', 'manualInput', 'singleArrow', 'doubleArrow', - 'flexArrow', 'filledEdge', 'card', 'umlLifeline', 'connector', 'folder', 'component', 'sortShape', - 'cross', 'umlFrame', 'cube', 'isoCube', 'isoRectangle', 'partialRectangle'], shape) >= 0; -}; - -/** - * Returns information about the current selection. - */ -Format.prototype.isAutoSizeState = function(state) -{ - return mxUtils.getValue(state.style, mxConstants.STYLE_AUTOSIZE, null) == '1'; -}; - -/** - * Returns information about the current selection. - */ -Format.prototype.isImageState = function(state) -{ - var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); - - return (shape == 'label' || shape == 'image'); -}; - -/** - * Returns information about the current selection. - */ -Format.prototype.isShadowState = function(state) -{ - var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); - - return (shape != 'image'); -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -Format.prototype.clear = function() -{ - this.container.innerHTML = ''; - - // Destroy existing panels - if (this.panels != null) - { - for (var i = 0; i < this.panels.length; i++) - { - this.panels[i].destroy(); - } - } - - this.panels = []; -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -Format.prototype.refresh = function() -{ - // Performance tweak: No refresh needed if not visible - if (this.container.style.width == '0px') - { - return; - } - - this.clear(); - var ui = this.editorUi; - var graph = ui.editor.graph; - - var div = document.createElement('div'); - div.style.whiteSpace = 'nowrap'; - div.style.color = 'rgb(112, 112, 112)'; - div.style.textAlign = 'left'; - div.style.cursor = 'default'; - - var label = document.createElement('div'); - label.style.border = '1px solid #c0c0c0'; - label.style.borderWidth = '0px 0px 1px 0px'; - label.style.textAlign = 'center'; - label.style.fontWeight = 'bold'; - label.style.overflow = 'hidden'; - label.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; - label.style.paddingTop = '8px'; - label.style.height = (mxClient.IS_QUIRKS) ? '34px' : '25px'; - label.style.width = '100%'; - this.container.appendChild(div); - - if (graph.isSelectionEmpty()) - { - mxUtils.write(label, mxResources.get('diagram')); - - // Adds button to hide the format panel since - // people don't seem to find the toolbar button - // and the menu item in the format menu - if (this.showCloseButton) - { - var img = document.createElement('img'); - img.setAttribute('border', '0'); - img.setAttribute('src', Dialog.prototype.closeImage); - img.setAttribute('title', mxResources.get('hide')); - img.style.position = 'absolute'; - img.style.display = 'block'; - img.style.right = '0px'; - img.style.top = '8px'; - img.style.cursor = 'pointer'; - img.style.marginTop = '1px'; - img.style.marginRight = '17px'; - img.style.border = '1px solid transparent'; - img.style.padding = '1px'; - img.style.opacity = 0.5; - label.appendChild(img) - - mxEvent.addListener(img, 'click', function() - { - ui.actions.get('formatPanel').funct(); - }); - } - - div.appendChild(label); - this.panels.push(new DiagramFormatPanel(this, ui, div)); - } - else if (graph.isEditing()) - { - mxUtils.write(label, mxResources.get('text')); - div.appendChild(label); - this.panels.push(new TextFormatPanel(this, ui, div)); - } - else - { - var containsLabel = this.getSelectionState().containsLabel; - var currentLabel = null; - var currentPanel = null; - - var addClickHandler = mxUtils.bind(this, function(elt, panel, index) - { - var clickHandler = mxUtils.bind(this, function(evt) - { - if (currentLabel != elt) - { - if (containsLabel) - { - this.labelIndex = index; - } - else - { - this.currentIndex = index; - } - - if (currentLabel != null) - { - currentLabel.style.backgroundColor = this.inactiveTabBackgroundColor; - currentLabel.style.borderBottomWidth = '1px'; - } - - currentLabel = elt; - currentLabel.style.backgroundColor = ''; - currentLabel.style.borderBottomWidth = '0px'; - - if (currentPanel != panel) - { - if (currentPanel != null) - { - currentPanel.style.display = 'none'; - } - - currentPanel = panel; - currentPanel.style.display = ''; - } - } - }); - - mxEvent.addListener(elt, 'click', clickHandler); - - if (index == ((containsLabel) ? this.labelIndex : this.currentIndex)) - { - // Invokes handler directly as a workaround for no click on DIV in KHTML. - clickHandler(); - } - }); - - var idx = 0; - - label.style.backgroundColor = this.inactiveTabBackgroundColor; - label.style.borderLeftWidth = '1px'; - label.style.width = (containsLabel) ? '50%' : '33.3%'; - label.style.width = (containsLabel) ? '50%' : '33.3%'; - var label2 = label.cloneNode(false); - var label3 = label2.cloneNode(false); - - // Workaround for ignored background in IE - label2.style.backgroundColor = this.inactiveTabBackgroundColor; - label3.style.backgroundColor = this.inactiveTabBackgroundColor; - - // Style - if (containsLabel) - { - label2.style.borderLeftWidth = '0px'; - } - else - { - label.style.borderLeftWidth = '0px'; - mxUtils.write(label, mxResources.get('style')); - div.appendChild(label); - - var stylePanel = div.cloneNode(false); - stylePanel.style.display = 'none'; - this.panels.push(new StyleFormatPanel(this, ui, stylePanel)); - this.container.appendChild(stylePanel); - - addClickHandler(label, stylePanel, idx++); - } - - // Text - mxUtils.write(label2, mxResources.get('text')); - div.appendChild(label2); - - var textPanel = div.cloneNode(false); - textPanel.style.display = 'none'; - this.panels.push(new TextFormatPanel(this, ui, textPanel)); - this.container.appendChild(textPanel); - - // Arrange - mxUtils.write(label3, mxResources.get('arrange')); - div.appendChild(label3); - - var arrangePanel = div.cloneNode(false); - arrangePanel.style.display = 'none'; - this.panels.push(new ArrangePanel(this, ui, arrangePanel)); - this.container.appendChild(arrangePanel); - - addClickHandler(label2, textPanel, idx++); - addClickHandler(label3, arrangePanel, idx++); - } -}; - -/** - * Base class for format panels. - */ -BaseFormatPanel = function(format, editorUi, container) -{ - this.format = format; - this.editorUi = editorUi; - this.container = container; - this.listeners = []; -}; - -/** - * - */ -BaseFormatPanel.prototype.buttonBackgroundColor = 'white'; - -/** - * Adds the given color option. - */ -BaseFormatPanel.prototype.getSelectionState = function() -{ - var graph = this.editorUi.editor.graph; - var cells = graph.getSelectionCells(); - var shape = null; - - for (var i = 0; i < cells.length; i++) - { - var state = graph.view.getState(cells[i]); - - if (state != null) - { - var tmp = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); - - if (tmp != null) - { - if (shape == null) - { - shape = tmp; - } - else if (shape != tmp) - { - return null; - } - } - - } - } - - return shape; -}; - -/** - * Install input handler. - */ -BaseFormatPanel.prototype.installInputHandler = function(input, key, defaultValue, min, max, unit, textEditFallback, isFloat) -{ - unit = (unit != null) ? unit : ''; - isFloat = (isFloat != null) ? isFloat : false; - - var ui = this.editorUi; - var graph = ui.editor.graph; - - min = (min != null) ? min : 1; - max = (max != null) ? max : 999; - - var selState = null; - var updating = false; - - var update = mxUtils.bind(this, function(evt) - { - var value = (isFloat) ? parseFloat(input.value) : parseInt(input.value); - - // Special case: angle mod 360 - if (!isNaN(value) && key == mxConstants.STYLE_ROTATION) - { - // Workaround for decimal rounding errors in floats is to - // use integer and round all numbers to two decimal point - value = mxUtils.mod(Math.round(value * 100), 36000) / 100; - } - - value = Math.min(max, Math.max(min, (isNaN(value)) ? defaultValue : value)); - - if (graph.cellEditor.isContentEditing() && textEditFallback) - { - if (!updating) - { - updating = true; - - if (selState != null) - { - graph.cellEditor.restoreSelection(selState); - selState = null; - } - - textEditFallback(value); - input.value = value + unit; - - // Restore focus and selection in input - updating = false; - } - } - else if (value != mxUtils.getValue(this.format.getSelectionState().style, key, defaultValue)) - { - if (graph.isEditing()) - { - graph.stopEditing(true); - } - - graph.getModel().beginUpdate(); - try - { - graph.setCellStyles(key, value, graph.getSelectionCells()); - - // Handles special case for fontSize where HTML labels are parsed and updated - if (key == mxConstants.STYLE_FONTSIZE) - { - graph.updateLabelElements(graph.getSelectionCells(), function(elt) - { - elt.style.fontSize = value + 'px'; - elt.removeAttribute('size'); - }); - } - - ui.fireEvent(new mxEventObject('styleChanged', 'keys', [key], - 'values', [value], 'cells', graph.getSelectionCells())); - } - finally - { - graph.getModel().endUpdate(); - } - } - - input.value = value + unit; - mxEvent.consume(evt); - }); - - if (textEditFallback && graph.cellEditor.isContentEditing()) - { - // KNOWN: Arrow up/down clear selection text in quirks/IE 8 - // Text size via arrow button limits to 16 in IE11. Why? - mxEvent.addListener(input, 'mousedown', function() - { - if (document.activeElement == graph.cellEditor.textarea) - { - selState = graph.cellEditor.saveSelection(); - } - }); - - mxEvent.addListener(input, 'touchstart', function() - { - if (document.activeElement == graph.cellEditor.textarea) - { - selState = graph.cellEditor.saveSelection(); - } - }); - } - - mxEvent.addListener(input, 'change', update); - mxEvent.addListener(input, 'blur', update); - - return update; -}; - -/** - * Adds the given option. - */ -BaseFormatPanel.prototype.createPanel = function() -{ - var div = document.createElement('div'); - div.style.padding = '12px 0px 12px 18px'; - div.style.borderBottom = '1px solid #c0c0c0'; - - return div; -}; - -/** - * Adds the given option. - */ -BaseFormatPanel.prototype.createTitle = function(title) -{ - var div = document.createElement('div'); - div.style.padding = '0px 0px 6px 0px'; - div.style.whiteSpace = 'nowrap'; - div.style.overflow = 'hidden'; - div.style.width = '200px'; - div.style.fontWeight = 'bold'; - mxUtils.write(div, title); - - return div; -}; - -/** - * - */ -BaseFormatPanel.prototype.createStepper = function(input, update, step, height, disableFocus, defaultValue) -{ - step = (step != null) ? step : 1; - height = (height != null) ? height : 8; - - if (mxClient.IS_QUIRKS) - { - height = height - 2; - } - else if (mxClient.IS_MT || document.documentMode >= 8) - { - height = height + 1; - } - - var stepper = document.createElement('div'); - mxUtils.setPrefixedStyle(stepper.style, 'borderRadius', '3px'); - stepper.style.border = '1px solid rgb(192, 192, 192)'; - stepper.style.position = 'absolute'; - - var up = document.createElement('div'); - up.style.borderBottom = '1px solid rgb(192, 192, 192)'; - up.style.position = 'relative'; - up.style.height = height + 'px'; - up.style.width = '10px'; - up.className = 'geBtnUp'; - stepper.appendChild(up); - - var down = up.cloneNode(false); - down.style.border = 'none'; - down.style.height = height + 'px'; - down.className = 'geBtnDown'; - stepper.appendChild(down); - - mxEvent.addListener(down, 'click', function(evt) - { - if (input.value == '') - { - input.value = defaultValue || '2'; - } - - var val = parseInt(input.value); - - if (!isNaN(val)) - { - input.value = val - step; - - if (update != null) - { - update(evt); - } - } - - mxEvent.consume(evt); - }); - - mxEvent.addListener(up, 'click', function(evt) - { - if (input.value == '') - { - input.value = defaultValue || '0'; - } - - var val = parseInt(input.value); - - if (!isNaN(val)) - { - input.value = val + step; - - if (update != null) - { - update(evt); - } - } - - mxEvent.consume(evt); - }); - - // Disables transfer of focus to DIV but also :active CSS - // so it's only used for fontSize where the focus should - // stay on the selected text, but not for any other input. - if (disableFocus) - { - var currentSelection = null; - - mxEvent.addGestureListeners(stepper, - function(evt) - { - // Workaround for lost current selection in page because of focus in IE - if (mxClient.IS_QUIRKS || document.documentMode == 8) - { - currentSelection = document.selection.createRange(); - } - - mxEvent.consume(evt); - }, - null, - function(evt) - { - // Workaround for lost current selection in page because of focus in IE - if (currentSelection != null) - { - try - { - currentSelection.select(); - } - catch (e) - { - // ignore - } - - currentSelection = null; - mxEvent.consume(evt); - } - } - ); - } - - return stepper; -}; - -/** - * Adds the given option. - */ -BaseFormatPanel.prototype.createOption = function(label, isCheckedFn, setCheckedFn, listener) -{ - var div = document.createElement('div'); - div.style.padding = '6px 0px 1px 0px'; - div.style.whiteSpace = 'nowrap'; - div.style.overflow = 'hidden'; - div.style.width = '200px'; - div.style.height = (mxClient.IS_QUIRKS) ? '27px' : '18px'; - - var cb = document.createElement('input'); - cb.setAttribute('type', 'checkbox'); - cb.style.margin = '0px 6px 0px 0px'; - div.appendChild(cb); - - var span = document.createElement('span'); - mxUtils.write(span, label); - div.appendChild(span); - - var applying = false; - var value = isCheckedFn(); - - var apply = function(newValue) - { - if (!applying) - { - applying = true; - - if (newValue) - { - cb.setAttribute('checked', 'checked'); - cb.defaultChecked = true; - cb.checked = true; - } - else - { - cb.removeAttribute('checked'); - cb.defaultChecked = false; - cb.checked = false; - } - - if (value != newValue) - { - value = newValue; - - // Checks if the color value needs to be updated in the model - if (isCheckedFn() != value) - { - setCheckedFn(value); - } - } - - applying = false; - } - }; - - mxEvent.addListener(div, 'click', function(evt) - { - if (cb.getAttribute('disabled') != 'disabled') - { - // Toggles checkbox state for click on label - var source = mxEvent.getSource(evt); - - if (source == div || source == span) - { - cb.checked = !cb.checked; - } - - apply(cb.checked); - } - }); - - apply(value); - - if (listener != null) - { - listener.install(apply); - this.listeners.push(listener); - } - - return div; -}; - -/** - * The string 'null' means use null in values. - */ -BaseFormatPanel.prototype.createCellOption = function(label, key, defaultValue, enabledValue, disabledValue, fn, action, stopEditing) -{ - enabledValue = (enabledValue != null) ? ((enabledValue == 'null') ? null : enabledValue) : '1'; - disabledValue = (disabledValue != null) ? ((disabledValue == 'null') ? null : disabledValue) : '0'; - - var ui = this.editorUi; - var editor = ui.editor; - var graph = editor.graph; - - return this.createOption(label, function() - { - // Seems to be null sometimes, not sure why... - var state = graph.view.getState(graph.getSelectionCell()); - - if (state != null) - { - return mxUtils.getValue(state.style, key, defaultValue) != disabledValue; - } - - return null; - }, function(checked) - { - if (stopEditing) - { - graph.stopEditing(); - } - - if (action != null) - { - action.funct(); - } - else - { - graph.getModel().beginUpdate(); - try - { - var value = (checked) ? enabledValue : disabledValue; - graph.setCellStyles(key, value, graph.getSelectionCells()); - - if (fn != null) - { - fn(graph.getSelectionCells(), value); - } - - ui.fireEvent(new mxEventObject('styleChanged', 'keys', [key], - 'values', [value], 'cells', graph.getSelectionCells())); - } - finally - { - graph.getModel().endUpdate(); - } - } - }, - { - install: function(apply) - { - this.listener = function() - { - // Seems to be null sometimes, not sure why... - var state = graph.view.getState(graph.getSelectionCell()); - - if (state != null) - { - apply(mxUtils.getValue(state.style, key, defaultValue) != disabledValue); - } - }; - - graph.getModel().addListener(mxEvent.CHANGE, this.listener); - }, - destroy: function() - { - graph.getModel().removeListener(this.listener); - } - }); -}; - -/** - * Adds the given color option. - */ -BaseFormatPanel.prototype.createColorOption = function(label, getColorFn, setColorFn, defaultColor, listener, callbackFn, hideCheckbox) -{ - var div = document.createElement('div'); - div.style.padding = '6px 0px 1px 0px'; - div.style.whiteSpace = 'nowrap'; - div.style.overflow = 'hidden'; - div.style.width = '200px'; - div.style.height = (mxClient.IS_QUIRKS) ? '27px' : '18px'; - - var cb = document.createElement('input'); - cb.setAttribute('type', 'checkbox'); - cb.style.margin = '0px 6px 0px 0px'; - - if (!hideCheckbox) - { - div.appendChild(cb); - } - - var span = document.createElement('span'); - mxUtils.write(span, label); - div.appendChild(span); - - var applying = false; - var value = getColorFn(); - - var btn = null; - - var apply = function(color, disableUpdate, forceUpdate) - { - if (!applying) - { - applying = true; - btn.innerHTML = '
'; - - // Fine-tuning in Firefox, quirks mode and IE8 standards - if (mxClient.IS_QUIRKS || document.documentMode == 8) - { - btn.firstChild.style.margin = '0px'; - } - - if (color != null && color != mxConstants.NONE) - { - cb.setAttribute('checked', 'checked'); - cb.defaultChecked = true; - cb.checked = true; - } - else - { - cb.removeAttribute('checked'); - cb.defaultChecked = false; - cb.checked = false; - } - - btn.style.display = (cb.checked || hideCheckbox) ? '' : 'none'; - - if (callbackFn != null) - { - callbackFn(color); - } - - if (!disableUpdate) - { - value = color; - - // Checks if the color value needs to be updated in the model - if (forceUpdate || hideCheckbox || getColorFn() != value) - { - setColorFn(value); - } - } - - applying = false; - } - }; - - btn = mxUtils.button('', mxUtils.bind(this, function(evt) - { - this.editorUi.pickColor(value, function(color) - { - apply(color, null, true); - }); - mxEvent.consume(evt); - })); - - btn.style.position = 'absolute'; - btn.style.marginTop = '-4px'; - btn.style.right = (mxClient.IS_QUIRKS) ? '0px' : '20px'; - btn.style.height = '22px'; - btn.className = 'geColorBtn'; - btn.style.display = (cb.checked || hideCheckbox) ? '' : 'none'; - div.appendChild(btn); - - mxEvent.addListener(div, 'click', function(evt) - { - var source = mxEvent.getSource(evt); - - if (source == cb || source.nodeName != 'INPUT') - { - // Toggles checkbox state for click on label - if (source != cb) - { - cb.checked = !cb.checked; - } - - // Overrides default value with current value to make it easier - // to restore previous value if the checkbox is clicked twice - if (!cb.checked && value != null && value != mxConstants.NONE && - defaultColor != mxConstants.NONE) - { - defaultColor = value; - } - - apply((cb.checked) ? defaultColor : mxConstants.NONE); - } - }); - - apply(value, true); - - if (listener != null) - { - listener.install(apply); - this.listeners.push(listener); - } - - return div; -}; - -/** - * - */ -BaseFormatPanel.prototype.createCellColorOption = function(label, colorKey, defaultColor, callbackFn, setStyleFn) -{ - var ui = this.editorUi; - var editor = ui.editor; - var graph = editor.graph; - - return this.createColorOption(label, function() - { - // Seems to be null sometimes, not sure why... - var state = graph.view.getState(graph.getSelectionCell()); - - if (state != null) - { - return mxUtils.getValue(state.style, colorKey, null); - } - - return null; - }, function(color) - { - graph.getModel().beginUpdate(); - try - { - if (setStyleFn != null) - { - setStyleFn(color); - } - - graph.setCellStyles(colorKey, color, graph.getSelectionCells()); - ui.fireEvent(new mxEventObject('styleChanged', 'keys', [colorKey], - 'values', [color], 'cells', graph.getSelectionCells())); - } - finally - { - graph.getModel().endUpdate(); - } - }, defaultColor || mxConstants.NONE, - { - install: function(apply) - { - this.listener = function() - { - // Seems to be null sometimes, not sure why... - var state = graph.view.getState(graph.getSelectionCell()); - - if (state != null) - { - apply(mxUtils.getValue(state.style, colorKey, null)); - } - }; - - graph.getModel().addListener(mxEvent.CHANGE, this.listener); - }, - destroy: function() - { - graph.getModel().removeListener(this.listener); - } - }, callbackFn); -}; - -/** - * - */ -BaseFormatPanel.prototype.addArrow = function(elt, height) -{ - height = (height != null) ? height : 10; - - var arrow = document.createElement('div'); - arrow.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; - arrow.style.padding = '6px'; - arrow.style.paddingRight = '4px'; - - var m = (10 - height); - - if (m == 2) - { - arrow.style.paddingTop = 6 + 'px'; - } - else if (m > 0) - { - arrow.style.paddingTop = (6 - m) + 'px'; - } - else - { - arrow.style.marginTop = '-2px'; - } - - arrow.style.height = height + 'px'; - arrow.style.borderLeft = '1px solid #a0a0a0'; - arrow.innerHTML = ''; - mxUtils.setOpacity(arrow, 70); - - var symbol = elt.getElementsByTagName('div')[0]; - - if (symbol != null) - { - symbol.style.paddingRight = '6px'; - symbol.style.marginLeft = '4px'; - symbol.style.marginTop = '-1px'; - symbol.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; - mxUtils.setOpacity(symbol, 60); - } - - mxUtils.setOpacity(elt, 100); - elt.style.border = '1px solid #a0a0a0'; - elt.style.backgroundColor = this.buttonBackgroundColor; - elt.style.backgroundImage = 'none'; - elt.style.width = 'auto'; - elt.className += ' geColorBtn'; - mxUtils.setPrefixedStyle(elt.style, 'borderRadius', '3px'); - - elt.appendChild(arrow); - - return symbol; -}; - -/** - * - */ -BaseFormatPanel.prototype.addUnitInput = function(container, unit, right, width, update, step, marginTop, disableFocus) -{ - marginTop = (marginTop != null) ? marginTop : 0; - - var input = document.createElement('input'); - input.style.position = 'absolute'; - input.style.textAlign = 'right'; - input.style.marginTop = '-2px'; - input.style.right = (right + 12) + 'px'; - input.style.width = width + 'px'; - container.appendChild(input); - - var stepper = this.createStepper(input, update, step, null, disableFocus); - stepper.style.marginTop = (marginTop - 2) + 'px'; - stepper.style.right = right + 'px'; - container.appendChild(stepper); - - return input; -}; - -/** - * - */ -BaseFormatPanel.prototype.createRelativeOption = function(label, key, width, handler, init) -{ - width = (width != null) ? width : 44; - - var graph = this.editorUi.editor.graph; - var div = this.createPanel(); - div.style.paddingTop = '10px'; - div.style.paddingBottom = '10px'; - mxUtils.write(div, label); - div.style.fontWeight = 'bold'; - - var update = mxUtils.bind(this, function(evt) - { - if (handler != null) - { - handler(input); - } - else - { - var value = parseInt(input.value); - value = Math.min(100, Math.max(0, (isNaN(value)) ? 100 : value)); - var state = graph.view.getState(graph.getSelectionCell()); - - if (state != null && value != mxUtils.getValue(state.style, key, 100)) - { - // Removes entry in style (assumes 100 is default for relative values) - if (value == 100) - { - value = null; - } - - graph.setCellStyles(key, value, graph.getSelectionCells()); - this.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', [key], - 'values', [value], 'cells', graph.getSelectionCells())); - } - - input.value = ((value != null) ? value : '100') + ' %'; - } - - mxEvent.consume(evt); - }); - - var input = this.addUnitInput(div, '%', 20, width, update, 10, -15, handler != null); - - if (key != null) - { - var listener = mxUtils.bind(this, function(sender, evt, force) - { - if (force || input != document.activeElement) - { - var ss = this.format.getSelectionState(); - var tmp = parseInt(mxUtils.getValue(ss.style, key, 100)); - input.value = (isNaN(tmp)) ? '' : tmp + ' %'; - } - }); - - mxEvent.addListener(input, 'keydown', function(e) - { - if (e.keyCode == 13) - { - graph.container.focus(); - mxEvent.consume(e); - } - else if (e.keyCode == 27) - { - listener(null, null, true); - graph.container.focus(); - mxEvent.consume(e); - } - }); - - graph.getModel().addListener(mxEvent.CHANGE, listener); - this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); - listener(); - } - - mxEvent.addListener(input, 'blur', update); - mxEvent.addListener(input, 'change', update); - - if (init != null) - { - init(input); - } - - return div; -}; - -/** - * - */ -BaseFormatPanel.prototype.addLabel = function(div, title, right, width) -{ - width = (width != null) ? width : 61; - - var label = document.createElement('div'); - mxUtils.write(label, title); - label.style.position = 'absolute'; - label.style.right = right + 'px'; - label.style.width = width + 'px'; - label.style.marginTop = '6px'; - label.style.textAlign = 'center'; - div.appendChild(label); -}; - -/** - * - */ -BaseFormatPanel.prototype.addKeyHandler = function(input, listener) -{ - mxEvent.addListener(input, 'keydown', mxUtils.bind(this, function(e) - { - if (e.keyCode == 13) - { - this.editorUi.editor.graph.container.focus(); - mxEvent.consume(e); - } - else if (e.keyCode == 27) - { - if (listener != null) - { - listener(null, null, true); - } - - this.editorUi.editor.graph.container.focus(); - mxEvent.consume(e); - } - })); -}; - -/** - * - */ -BaseFormatPanel.prototype.styleButtons = function(elts) -{ - for (var i = 0; i < elts.length; i++) - { - mxUtils.setPrefixedStyle(elts[i].style, 'borderRadius', '3px'); - mxUtils.setOpacity(elts[i], 100); - elts[i].style.border = '1px solid #a0a0a0'; - elts[i].style.padding = '4px'; - elts[i].style.paddingTop = '3px'; - elts[i].style.paddingRight = '1px'; - elts[i].style.margin = '1px'; - elts[i].style.width = '24px'; - elts[i].style.height = '20px'; - elts[i].className += ' geColorBtn'; - } -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -BaseFormatPanel.prototype.destroy = function() -{ - if (this.listeners != null) - { - for (var i = 0; i < this.listeners.length; i++) - { - this.listeners[i].destroy(); - } - - this.listeners = null; - } -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -ArrangePanel = function(format, editorUi, container) -{ - BaseFormatPanel.call(this, format, editorUi, container); - this.init(); -}; - -mxUtils.extend(ArrangePanel, BaseFormatPanel); - -/** - * Adds the label menu items to the given menu and parent. - */ -ArrangePanel.prototype.init = function() -{ - var graph = this.editorUi.editor.graph; - var ss = this.format.getSelectionState(); - - this.container.appendChild(this.addLayerOps(this.createPanel())); - // Special case that adds two panels - this.addGeometry(this.container); - this.addEdgeGeometry(this.container); - - if (!ss.containsLabel || ss.edges.length == 0) - { - this.container.appendChild(this.addAngle(this.createPanel())); - } - - if (!ss.containsLabel && ss.edges.length == 0) - { - this.container.appendChild(this.addFlip(this.createPanel())); - } - - if (ss.vertices.length > 1) - { - this.container.appendChild(this.addAlign(this.createPanel())); - this.container.appendChild(this.addDistribute(this.createPanel())); - } - - this.container.appendChild(this.addGroupOps(this.createPanel())); -}; - -/** - * - */ -ArrangePanel.prototype.addLayerOps = function(div) -{ - var ui = this.editorUi; - - var btn = mxUtils.button(mxResources.get('toFront'), function(evt) - { - ui.actions.get('toFront').funct(); - }) - - btn.setAttribute('title', mxResources.get('toFront') + ' (' + this.editorUi.actions.get('toFront').shortcut + ')'); - btn.style.width = '100px'; - btn.style.marginRight = '2px'; - div.appendChild(btn); - - var btn = mxUtils.button(mxResources.get('toBack'), function(evt) - { - ui.actions.get('toBack').funct(); - }) - - btn.setAttribute('title', mxResources.get('toBack') + ' (' + this.editorUi.actions.get('toBack').shortcut + ')'); - btn.style.width = '100px'; - div.appendChild(btn); - - return div; -}; - -/** - * - */ -ArrangePanel.prototype.addGroupOps = function(div) -{ - var ui = this.editorUi; - var graph = ui.editor.graph; - var cell = graph.getSelectionCell(); - var ss = this.format.getSelectionState(); - var count = 0; - var btn = null; - - div.style.paddingTop = '8px'; - div.style.paddingBottom = '6px'; - - if (graph.getSelectionCount() > 1) - { - btn = mxUtils.button(mxResources.get('group'), function(evt) - { - ui.actions.get('group').funct(); - }) - - btn.setAttribute('title', mxResources.get('group') + ' (' + this.editorUi.actions.get('group').shortcut + ')'); - btn.style.width = '202px'; - btn.style.marginBottom = '2px'; - div.appendChild(btn); - count++; - } - else if (graph.getSelectionCount() == 1 && !graph.getModel().isEdge(cell) && !graph.isSwimlane(cell) && - graph.getModel().getChildCount(cell) > 0) - { - btn = mxUtils.button(mxResources.get('ungroup'), function(evt) - { - ui.actions.get('ungroup').funct(); - }) - - btn.setAttribute('title', mxResources.get('ungroup') + ' (' + - this.editorUi.actions.get('ungroup').shortcut + ')'); - btn.style.width = '202px'; - btn.style.marginBottom = '2px'; - div.appendChild(btn); - count++; - } - - if (ss.vertices.length > 0) - { - if (count > 0) - { - mxUtils.br(div); - count = 0; - } - - var btn = mxUtils.button(mxResources.get('copySize'), function(evt) - { - ui.actions.get('copySize').funct(); - }); - - btn.setAttribute('title', mxResources.get('copySize') + ' (' + - this.editorUi.actions.get('copySize').shortcut + ')'); - btn.style.width = '202px'; - btn.style.marginBottom = '2px'; - - div.appendChild(btn); - count++; - - if (ui.copiedSize != null) - { - var btn2 = mxUtils.button(mxResources.get('pasteSize'), function(evt) - { - ui.actions.get('pasteSize').funct(); - }); - - btn2.setAttribute('title', mxResources.get('pasteSize') + ' (' + - this.editorUi.actions.get('pasteSize').shortcut + ')'); - - div.appendChild(btn2); - count++; - - btn.style.width = '100px'; - btn.style.marginBottom = '2px'; - btn2.style.width = '100px'; - btn2.style.marginBottom = '2px'; - } - } - - if (graph.getSelectionCount() == 1 && graph.getModel().isVertex(cell) && - graph.getModel().isVertex(graph.getModel().getParent(cell))) - { - if (count > 0) - { - mxUtils.br(div); - } - - btn = mxUtils.button(mxResources.get('removeFromGroup'), function(evt) - { - ui.actions.get('removeFromGroup').funct(); - }) - - btn.setAttribute('title', mxResources.get('removeFromGroup')); - btn.style.width = '202px'; - btn.style.marginBottom = '2px'; - div.appendChild(btn); - count++; - } - else if (graph.getSelectionCount() > 0) - { - if (count > 0) - { - mxUtils.br(div); - } - - btn = mxUtils.button(mxResources.get('clearWaypoints'), mxUtils.bind(this, function(evt) - { - this.editorUi.actions.get('clearWaypoints').funct(); - })); - - btn.setAttribute('title', mxResources.get('clearWaypoints') + ' (' + this.editorUi.actions.get('clearWaypoints').shortcut + ')'); - btn.style.width = '202px'; - btn.style.marginBottom = '2px'; - div.appendChild(btn); - - count++; - } - - if (graph.getSelectionCount() == 1) - { - if (count > 0) - { - mxUtils.br(div); - } - - btn = mxUtils.button(mxResources.get('editData'), mxUtils.bind(this, function(evt) - { - this.editorUi.actions.get('editData').funct(); - })); - - btn.setAttribute('title', mxResources.get('editData') + ' (' + this.editorUi.actions.get('editData').shortcut + ')'); - btn.style.width = '100px'; - btn.style.marginBottom = '2px'; - div.appendChild(btn); - count++; - - btn = mxUtils.button(mxResources.get('editLink'), mxUtils.bind(this, function(evt) - { - this.editorUi.actions.get('editLink').funct(); - })); - - btn.setAttribute('title', mxResources.get('editLink')); - btn.style.width = '100px'; - btn.style.marginLeft = '2px'; - btn.style.marginBottom = '2px'; - div.appendChild(btn); - count++; - } - - if (count == 0) - { - div.style.display = 'none'; - } - - return div; -}; - -/** - * - */ -ArrangePanel.prototype.addAlign = function(div) -{ - var graph = this.editorUi.editor.graph; - div.style.paddingTop = '6px'; - div.style.paddingBottom = '12px'; - div.appendChild(this.createTitle(mxResources.get('align'))); - - var stylePanel = document.createElement('div'); - stylePanel.style.position = 'relative'; - stylePanel.style.paddingLeft = '0px'; - stylePanel.style.borderWidth = '0px'; - stylePanel.className = 'geToolbarContainer'; - - if (mxClient.IS_QUIRKS) - { - div.style.height = '60px'; - } - - var left = this.editorUi.toolbar.addButton('geSprite-alignleft', mxResources.get('left'), - function() { graph.alignCells(mxConstants.ALIGN_LEFT); }, stylePanel); - var center = this.editorUi.toolbar.addButton('geSprite-aligncenter', mxResources.get('center'), - function() { graph.alignCells(mxConstants.ALIGN_CENTER); }, stylePanel); - var right = this.editorUi.toolbar.addButton('geSprite-alignright', mxResources.get('right'), - function() { graph.alignCells(mxConstants.ALIGN_RIGHT); }, stylePanel); - - var top = this.editorUi.toolbar.addButton('geSprite-aligntop', mxResources.get('top'), - function() { graph.alignCells(mxConstants.ALIGN_TOP); }, stylePanel); - var middle = this.editorUi.toolbar.addButton('geSprite-alignmiddle', mxResources.get('middle'), - function() { graph.alignCells(mxConstants.ALIGN_MIDDLE); }, stylePanel); - var bottom = this.editorUi.toolbar.addButton('geSprite-alignbottom', mxResources.get('bottom'), - function() { graph.alignCells(mxConstants.ALIGN_BOTTOM); }, stylePanel); - - this.styleButtons([left, center, right, top, middle, bottom]); - right.style.marginRight = '6px'; - div.appendChild(stylePanel); - - return div; -}; - -/** - * - */ -ArrangePanel.prototype.addFlip = function(div) -{ - var ui = this.editorUi; - var editor = ui.editor; - var graph = editor.graph; - div.style.paddingTop = '6px'; - div.style.paddingBottom = '10px'; - - var span = document.createElement('div'); - span.style.marginTop = '2px'; - span.style.marginBottom = '8px'; - span.style.fontWeight = 'bold'; - mxUtils.write(span, mxResources.get('flip')); - div.appendChild(span); - - var btn = mxUtils.button(mxResources.get('horizontal'), function(evt) - { - graph.toggleCellStyles(mxConstants.STYLE_FLIPH, false); - }) - - btn.setAttribute('title', mxResources.get('horizontal')); - btn.style.width = '100px'; - btn.style.marginRight = '2px'; - div.appendChild(btn); - - var btn = mxUtils.button(mxResources.get('vertical'), function(evt) - { - graph.toggleCellStyles(mxConstants.STYLE_FLIPV, false); - }) - - btn.setAttribute('title', mxResources.get('vertical')); - btn.style.width = '100px'; - div.appendChild(btn); - - return div; -}; - -/** - * - */ -ArrangePanel.prototype.addDistribute = function(div) -{ - var ui = this.editorUi; - var editor = ui.editor; - var graph = editor.graph; - div.style.paddingTop = '6px'; - div.style.paddingBottom = '12px'; - - div.appendChild(this.createTitle(mxResources.get('distribute'))); - - var btn = mxUtils.button(mxResources.get('horizontal'), function(evt) - { - graph.distributeCells(true); - }) - - btn.setAttribute('title', mxResources.get('horizontal')); - btn.style.width = '100px'; - btn.style.marginRight = '2px'; - div.appendChild(btn); - - var btn = mxUtils.button(mxResources.get('vertical'), function(evt) - { - graph.distributeCells(false); - }) - - btn.setAttribute('title', mxResources.get('vertical')); - btn.style.width = '100px'; - div.appendChild(btn); - - return div; -}; - -/** - * - */ -ArrangePanel.prototype.addAngle = function(div) -{ - var ui = this.editorUi; - var editor = ui.editor; - var graph = editor.graph; - var ss = this.format.getSelectionState(); - - div.style.paddingBottom = '8px'; - - var span = document.createElement('div'); - span.style.position = 'absolute'; - span.style.width = '70px'; - span.style.marginTop = '0px'; - span.style.fontWeight = 'bold'; - - var input = null; - var update = null; - var btn = null; - - if (ss.edges.length == 0) - { - mxUtils.write(span, mxResources.get('angle')); - div.appendChild(span); - - input = this.addUnitInput(div, '°', 20, 44, function() - { - update.apply(this, arguments); - }); - - mxUtils.br(div); - div.style.paddingTop = '10px'; - } - else - { - div.style.paddingTop = '8px'; - } - - if (!ss.containsLabel) - { - var label = mxResources.get('reverse'); - - if (ss.vertices.length > 0 && ss.edges.length > 0) - { - label = mxResources.get('turn') + ' / ' + label; - } - else if (ss.vertices.length > 0) - { - label = mxResources.get('turn'); - } - - btn = mxUtils.button(label, function(evt) - { - ui.actions.get('turn').funct(); - }) - - btn.setAttribute('title', label + ' (' + this.editorUi.actions.get('turn').shortcut + ')'); - btn.style.width = '202px'; - div.appendChild(btn); - - if (input != null) - { - btn.style.marginTop = '8px'; - } - } - - if (input != null) - { - var listener = mxUtils.bind(this, function(sender, evt, force) - { - if (force || document.activeElement != input) - { - ss = this.format.getSelectionState(); - var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_ROTATION, 0)); - input.value = (isNaN(tmp)) ? '' : tmp + '°'; - } - }); - - update = this.installInputHandler(input, mxConstants.STYLE_ROTATION, 0, 0, 360, '°', null, true); - this.addKeyHandler(input, listener); - - graph.getModel().addListener(mxEvent.CHANGE, listener); - this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); - listener(); - } - - return div; -}; - -/** - * - */ -ArrangePanel.prototype.addGeometry = function(container) -{ - var ui = this.editorUi; - var graph = ui.editor.graph; - var rect = this.format.getSelectionState(); - - var div = this.createPanel(); - div.style.paddingBottom = '8px'; - - var span = document.createElement('div'); - span.style.position = 'absolute'; - span.style.width = '50px'; - span.style.marginTop = '0px'; - span.style.fontWeight = 'bold'; - mxUtils.write(span, mxResources.get('size')); - div.appendChild(span); - - var widthUpdate, heightUpdate, leftUpdate, topUpdate; - var width = this.addUnitInput(div, 'pt', 84, 44, function() - { - widthUpdate.apply(this, arguments); - }); - var height = this.addUnitInput(div, 'pt', 20, 44, function() - { - heightUpdate.apply(this, arguments); - }); - - var autosizeBtn = document.createElement('div'); - autosizeBtn.className = 'geSprite geSprite-fit'; - autosizeBtn.setAttribute('title', mxResources.get('autosize') + ' (' + this.editorUi.actions.get('autosize').shortcut + ')'); - autosizeBtn.style.position = 'relative'; - autosizeBtn.style.cursor = 'pointer'; - autosizeBtn.style.marginTop = '-3px'; - autosizeBtn.style.border = '0px'; - autosizeBtn.style.left = '52px'; - mxUtils.setOpacity(autosizeBtn, 50); - - mxEvent.addListener(autosizeBtn, 'mouseenter', function() - { - mxUtils.setOpacity(autosizeBtn, 100); - }); - - mxEvent.addListener(autosizeBtn, 'mouseleave', function() - { - mxUtils.setOpacity(autosizeBtn, 50); - }); - - mxEvent.addListener(autosizeBtn, 'click', function() - { - ui.actions.get('autosize').funct(); - }); - - div.appendChild(autosizeBtn); - this.addLabel(div, mxResources.get('width'), 84); - this.addLabel(div, mxResources.get('height'), 20); - mxUtils.br(div); - - var wrapper = document.createElement('div'); - wrapper.style.paddingTop = '8px'; - wrapper.style.paddingRight = '20px'; - wrapper.style.whiteSpace = 'nowrap'; - wrapper.style.textAlign = 'right'; - var opt = this.createCellOption(mxResources.get('constrainProportions'), - mxConstants.STYLE_ASPECT, null, 'fixed', 'null'); - opt.style.width = '100%'; - wrapper.appendChild(opt); - div.appendChild(wrapper); - - var constrainCheckbox = opt.getElementsByTagName('input')[0]; - this.addKeyHandler(width, listener); - this.addKeyHandler(height, listener); - - widthUpdate = this.addGeometryHandler(width, function(geo, value) - { - if (geo.width > 0) - { - var value = Math.max(1, value); - - if (constrainCheckbox.checked) - { - geo.height = Math.round((geo.height * value * 100) / geo.width) / 100; - } - - geo.width = value; - } - }); - heightUpdate = this.addGeometryHandler(height, function(geo, value) - { - if (geo.height > 0) - { - var value = Math.max(1, value); - - if (constrainCheckbox.checked) - { - geo.width = Math.round((geo.width * value * 100) / geo.height) / 100; - } - - geo.height = value; - } - }); - - container.appendChild(div); - - var div2 = this.createPanel(); - div2.style.paddingBottom = '30px'; - - var span = document.createElement('div'); - span.style.position = 'absolute'; - span.style.width = '70px'; - span.style.marginTop = '0px'; - span.style.fontWeight = 'bold'; - mxUtils.write(span, mxResources.get('position')); - div2.appendChild(span); - - var left = this.addUnitInput(div2, 'pt', 84, 44, function() - { - leftUpdate.apply(this, arguments); - }); - var top = this.addUnitInput(div2, 'pt', 20, 44, function() - { - topUpdate.apply(this, arguments); - }); - - mxUtils.br(div2); - this.addLabel(div2, mxResources.get('left'), 84); - this.addLabel(div2, mxResources.get('top'), 20); - - var listener = mxUtils.bind(this, function(sender, evt, force) - { - rect = this.format.getSelectionState(); - - if (!rect.containsLabel && rect.vertices.length == graph.getSelectionCount() && - rect.width != null && rect.height != null) - { - div.style.display = ''; - - if (force || document.activeElement != width) - { - width.value = rect.width + ((rect.width == '') ? '' : ' pt'); - } - - if (force || document.activeElement != height) - { - height.value = rect.height + ((rect.height == '') ? '' : ' pt'); - } - } - else - { - div.style.display = 'none'; - } - - if (rect.vertices.length == graph.getSelectionCount() && - rect.x != null && rect.y != null) - { - div2.style.display = ''; - - if (force || document.activeElement != left) - { - left.value = rect.x + ((rect.x == '') ? '' : ' pt'); - } - - if (force || document.activeElement != top) - { - top.value = rect.y + ((rect.y == '') ? '' : ' pt'); - } - } - else - { - div2.style.display = 'none'; - } - }); - - this.addKeyHandler(left, listener); - this.addKeyHandler(top, listener); - - graph.getModel().addListener(mxEvent.CHANGE, listener); - this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); - listener(); - - leftUpdate = this.addGeometryHandler(left, function(geo, value) - { - if (geo.relative) - { - geo.offset.x = value; - } - else - { - geo.x = value; - } - }); - topUpdate = this.addGeometryHandler(top, function(geo, value) - { - if (geo.relative) - { - geo.offset.y = value; - } - else - { - geo.y = value; - } - }); - - container.appendChild(div2); -}; - -/** - * - */ -ArrangePanel.prototype.addGeometryHandler = function(input, fn) -{ - var ui = this.editorUi; - var graph = ui.editor.graph; - var initialValue = null; - - function update(evt) - { - if (input.value != '') - { - var value = parseFloat(input.value); - - if (value != initialValue) - { - graph.getModel().beginUpdate(); - try - { - var cells = graph.getSelectionCells(); - - for (var i = 0; i < cells.length; i++) - { - if (graph.getModel().isVertex(cells[i])) - { - var geo = graph.getCellGeometry(cells[i]); - - if (geo != null) - { - geo = geo.clone(); - fn(geo, value); - - graph.getModel().setGeometry(cells[i], geo); - } - } - } - } - finally - { - graph.getModel().endUpdate(); - } - - initialValue = value; - input.value = value + ' pt'; - } - else if (isNaN(value)) - { - input.value = initialValue + ' pt'; - } - } - - mxEvent.consume(evt); - }; - - mxEvent.addListener(input, 'blur', update); - mxEvent.addListener(input, 'change', update); - mxEvent.addListener(input, 'focus', function() - { - initialValue = input.value; - }); - - return update; -}; - -ArrangePanel.prototype.addEdgeGeometryHandler = function(input, fn) -{ - var ui = this.editorUi; - var graph = ui.editor.graph; - var initialValue = null; - - function update(evt) - { - if (input.value != '') - { - var value = parseFloat(input.value); - - if (isNaN(value)) - { - input.value = initialValue + ' pt'; - } - else if (value != initialValue) - { - graph.getModel().beginUpdate(); - try - { - var cells = graph.getSelectionCells(); - - for (var i = 0; i < cells.length; i++) - { - if (graph.getModel().isEdge(cells[i])) - { - var geo = graph.getCellGeometry(cells[i]); - - if (geo != null) - { - geo = geo.clone(); - fn(geo, value); - - graph.getModel().setGeometry(cells[i], geo); - } - } - } - } - finally - { - graph.getModel().endUpdate(); - } - - initialValue = value; - input.value = value + ' pt'; - } - } - - mxEvent.consume(evt); - }; - - mxEvent.addListener(input, 'blur', update); - mxEvent.addListener(input, 'change', update); - mxEvent.addListener(input, 'focus', function() - { - initialValue = input.value; - }); - - return update; -}; - -/** - * - */ -ArrangePanel.prototype.addEdgeGeometry = function(container) -{ - var ui = this.editorUi; - var graph = ui.editor.graph; - var rect = this.format.getSelectionState(); - - var div = this.createPanel(); - - var span = document.createElement('div'); - span.style.position = 'absolute'; - span.style.width = '70px'; - span.style.marginTop = '0px'; - span.style.fontWeight = 'bold'; - mxUtils.write(span, mxResources.get('width')); - div.appendChild(span); - - var widthUpdate, xtUpdate, ytUpdate, xsUpdate, ysUpdate; - var width = this.addUnitInput(div, 'pt', 20, 44, function() - { - widthUpdate.apply(this, arguments); - }); - - mxUtils.br(div); - this.addKeyHandler(width, listener); - - function widthUpdate(evt) - { - // Maximum stroke width is 999 - var value = parseInt(width.value); - value = Math.min(999, Math.max(1, (isNaN(value)) ? 1 : value)); - - if (value != mxUtils.getValue(rect.style, 'width', mxCellRenderer.defaultShapes['flexArrow'].prototype.defaultWidth)) - { - graph.setCellStyles('width', value, graph.getSelectionCells()); - ui.fireEvent(new mxEventObject('styleChanged', 'keys', ['width'], - 'values', [value], 'cells', graph.getSelectionCells())); - } - - width.value = value + ' pt'; - mxEvent.consume(evt); - }; - - mxEvent.addListener(width, 'blur', widthUpdate); - mxEvent.addListener(width, 'change', widthUpdate); - - container.appendChild(div); - - var divs = this.createPanel(); - divs.style.paddingBottom = '30px'; - - var span = document.createElement('div'); - span.style.position = 'absolute'; - span.style.width = '70px'; - span.style.marginTop = '0px'; - span.style.fontWeight = 'bold'; - mxUtils.write(span, 'Start'); - divs.appendChild(span); - - var xs = this.addUnitInput(divs, 'pt', 84, 44, function() - { - xsUpdate.apply(this, arguments); - }); - var ys = this.addUnitInput(divs, 'pt', 20, 44, function() - { - ysUpdate.apply(this, arguments); - }); - - mxUtils.br(divs); - this.addLabel(divs, mxResources.get('left'), 84); - this.addLabel(divs, mxResources.get('top'), 20); - container.appendChild(divs); - this.addKeyHandler(xs, listener); - this.addKeyHandler(ys, listener); - - var divt = this.createPanel(); - divt.style.paddingBottom = '30px'; - - var span = document.createElement('div'); - span.style.position = 'absolute'; - span.style.width = '70px'; - span.style.marginTop = '0px'; - span.style.fontWeight = 'bold'; - mxUtils.write(span, 'End'); - divt.appendChild(span); - - var xt = this.addUnitInput(divt, 'pt', 84, 44, function() - { - xtUpdate.apply(this, arguments); - }); - var yt = this.addUnitInput(divt, 'pt', 20, 44, function() - { - ytUpdate.apply(this, arguments); - }); - - mxUtils.br(divt); - this.addLabel(divt, mxResources.get('left'), 84); - this.addLabel(divt, mxResources.get('top'), 20); - container.appendChild(divt); - this.addKeyHandler(xt, listener); - this.addKeyHandler(yt, listener); - - var listener = mxUtils.bind(this, function(sender, evt, force) - { - rect = this.format.getSelectionState(); - var cell = graph.getSelectionCell(); - - if (rect.style.shape == 'link' || rect.style.shape == 'flexArrow') - { - div.style.display = ''; - - if (force || document.activeElement != width) - { - var value = mxUtils.getValue(rect.style, 'width', - mxCellRenderer.defaultShapes['flexArrow'].prototype.defaultWidth); - width.value = value + ' pt'; - } - } - else - { - div.style.display = 'none'; - } - - if (graph.getSelectionCount() == 1 && graph.model.isEdge(cell)) - { - var geo = graph.model.getGeometry(cell); - - if (geo.sourcePoint != null && graph.model.getTerminal(cell, true) == null) - { - xs.value = geo.sourcePoint.x; - ys.value = geo.sourcePoint.y; - } - else - { - divs.style.display = 'none'; - } - - if (geo.targetPoint != null && graph.model.getTerminal(cell, false) == null) - { - xt.value = geo.targetPoint.x; - yt.value = geo.targetPoint.y; - } - else - { - divt.style.display = 'none'; - } - } - else - { - divs.style.display = 'none'; - divt.style.display = 'none'; - } - }); - - xsUpdate = this.addEdgeGeometryHandler(xs, function(geo, value) - { - geo.sourcePoint.x = value; - }); - - ysUpdate = this.addEdgeGeometryHandler(ys, function(geo, value) - { - geo.sourcePoint.y = value; - }); - - xtUpdate = this.addEdgeGeometryHandler(xt, function(geo, value) - { - geo.targetPoint.x = value; - }); - - ytUpdate = this.addEdgeGeometryHandler(yt, function(geo, value) - { - geo.targetPoint.y = value; - }); - - graph.getModel().addListener(mxEvent.CHANGE, listener); - this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); - listener(); -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -TextFormatPanel = function(format, editorUi, container) -{ - BaseFormatPanel.call(this, format, editorUi, container); - this.init(); -}; - -mxUtils.extend(TextFormatPanel, BaseFormatPanel); - -/** - * Adds the label menu items to the given menu and parent. - */ -TextFormatPanel.prototype.init = function() -{ - this.container.style.borderBottom = 'none'; - this.addFont(this.container); -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -TextFormatPanel.prototype.addFont = function(container) -{ - var ui = this.editorUi; - var editor = ui.editor; - var graph = editor.graph; - var ss = this.format.getSelectionState(); - - var title = this.createTitle(mxResources.get('font')); - title.style.paddingLeft = '18px'; - title.style.paddingTop = '10px'; - title.style.paddingBottom = '6px'; - container.appendChild(title); - - var stylePanel = this.createPanel(); - stylePanel.style.paddingTop = '2px'; - stylePanel.style.paddingBottom = '2px'; - stylePanel.style.position = 'relative'; - stylePanel.style.marginLeft = '-2px'; - stylePanel.style.borderWidth = '0px'; - stylePanel.className = 'geToolbarContainer'; - - if (mxClient.IS_QUIRKS) - { - stylePanel.style.display = 'block'; - } - - if (graph.cellEditor.isContentEditing()) - { - var cssPanel = stylePanel.cloneNode(); - - var cssMenu = this.editorUi.toolbar.addMenu(mxResources.get('style'), - mxResources.get('style'), true, 'formatBlock', cssPanel, null, true); - cssMenu.style.color = 'rgb(112, 112, 112)'; - cssMenu.style.whiteSpace = 'nowrap'; - cssMenu.style.overflow = 'hidden'; - cssMenu.style.margin = '0px'; - this.addArrow(cssMenu); - cssMenu.style.width = '192px'; - cssMenu.style.height = '15px'; - - var arrow = cssMenu.getElementsByTagName('div')[0]; - arrow.style.cssFloat = 'right'; - container.appendChild(cssPanel); - - // Workaround for offset in FF - if (mxClient.IS_FF) - { - cssMenu.getElementsByTagName('div')[0].style.marginTop = '-18px'; - } - } - - container.appendChild(stylePanel); - - var colorPanel = this.createPanel(); - colorPanel.style.marginTop = '8px'; - colorPanel.style.borderTop = '1px solid #c0c0c0'; - colorPanel.style.paddingTop = '6px'; - colorPanel.style.paddingBottom = '6px'; - - var fontMenu = this.editorUi.toolbar.addMenu('Helvetica', mxResources.get('fontFamily'), - true, 'fontFamily', stylePanel, null, true); - fontMenu.style.color = 'rgb(112, 112, 112)'; - fontMenu.style.whiteSpace = 'nowrap'; - fontMenu.style.overflow = 'hidden'; - fontMenu.style.margin = '0px'; - - this.addArrow(fontMenu); - fontMenu.style.width = '192px'; - fontMenu.style.height = '15px'; - - // Workaround for offset in FF - if (mxClient.IS_FF) - { - fontMenu.getElementsByTagName('div')[0].style.marginTop = '-18px'; - } - - var stylePanel2 = stylePanel.cloneNode(false); - stylePanel2.style.marginLeft = '-3px'; - var fontStyleItems = this.editorUi.toolbar.addItems(['bold', 'italic', 'underline'], stylePanel2, true); - fontStyleItems[0].setAttribute('title', mxResources.get('bold') + ' (' + this.editorUi.actions.get('bold').shortcut + ')'); - fontStyleItems[1].setAttribute('title', mxResources.get('italic') + ' (' + this.editorUi.actions.get('italic').shortcut + ')'); - fontStyleItems[2].setAttribute('title', mxResources.get('underline') + ' (' + this.editorUi.actions.get('underline').shortcut + ')'); - - var verticalItem = this.editorUi.toolbar.addItems(['vertical'], stylePanel2, true)[0]; - - if (mxClient.IS_QUIRKS) - { - mxUtils.br(container); - } - - container.appendChild(stylePanel2); - - this.styleButtons(fontStyleItems); - this.styleButtons([verticalItem]); - - var stylePanel3 = stylePanel.cloneNode(false); - stylePanel3.style.marginLeft = '-3px'; - stylePanel3.style.paddingBottom = '0px'; - - // Helper function to return a wrapper function does not pass any arguments - var callFn = function(fn) - { - return function() - { - return fn(); - }; - }; - - var left = this.editorUi.toolbar.addButton('geSprite-left', mxResources.get('left'), - (graph.cellEditor.isContentEditing()) ? - function() - { - document.execCommand('justifyleft', false, null); - } : callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_LEFT])), stylePanel3); - var center = this.editorUi.toolbar.addButton('geSprite-center', mxResources.get('center'), - (graph.cellEditor.isContentEditing()) ? - function() - { - document.execCommand('justifycenter', false, null); - } : callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_CENTER])), stylePanel3); - var right = this.editorUi.toolbar.addButton('geSprite-right', mxResources.get('right'), - (graph.cellEditor.isContentEditing()) ? - function() - { - document.execCommand('justifyright', false, null); - } : callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_ALIGN], [mxConstants.ALIGN_RIGHT])), stylePanel3); - - this.styleButtons([left, center, right]); - - if (graph.cellEditor.isContentEditing()) - { - var clear = this.editorUi.toolbar.addButton('geSprite-removeformat', mxResources.get('removeFormat'), - function() - { - document.execCommand('removeformat', false, null); - }, stylePanel2); - this.styleButtons([clear]); - } - - var top = this.editorUi.toolbar.addButton('geSprite-top', mxResources.get('top'), - callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_VERTICAL_ALIGN], [mxConstants.ALIGN_TOP])), stylePanel3); - var middle = this.editorUi.toolbar.addButton('geSprite-middle', mxResources.get('middle'), - callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_VERTICAL_ALIGN], [mxConstants.ALIGN_MIDDLE])), stylePanel3); - var bottom = this.editorUi.toolbar.addButton('geSprite-bottom', mxResources.get('bottom'), - callFn(this.editorUi.menus.createStyleChangeFunction([mxConstants.STYLE_VERTICAL_ALIGN], [mxConstants.ALIGN_BOTTOM])), stylePanel3); - - this.styleButtons([top, middle, bottom]); - - if (mxClient.IS_QUIRKS) - { - mxUtils.br(container); - } - - container.appendChild(stylePanel3); - - // Hack for updating UI state below based on current text selection - // currentTable is the current selected DOM table updated below - var sub, sup, full, tableWrapper, currentTable, tableCell, tableRow; - - if (graph.cellEditor.isContentEditing()) - { - top.style.display = 'none'; - middle.style.display = 'none'; - bottom.style.display = 'none'; - verticalItem.style.display = 'none'; - - full = this.editorUi.toolbar.addButton('geSprite-justifyfull', null, - function() - { - document.execCommand('justifyfull', false, null); - }, stylePanel3); - this.styleButtons([full, - sub = this.editorUi.toolbar.addButton('geSprite-subscript', - mxResources.get('subscript') + ' (' + Editor.ctrlKey + '+,)', - function() - { - document.execCommand('subscript', false, null); - }, stylePanel3), sup = this.editorUi.toolbar.addButton('geSprite-superscript', - mxResources.get('superscript') + ' (' + Editor.ctrlKey + '+.)', - function() - { - document.execCommand('superscript', false, null); - }, stylePanel3)]); - full.style.marginRight = '9px'; - - var tmp = stylePanel3.cloneNode(false); - tmp.style.paddingTop = '4px'; - var btns = [this.editorUi.toolbar.addButton('geSprite-orderedlist', mxResources.get('numberedList'), - function() - { - document.execCommand('insertorderedlist', false, null); - }, tmp), - this.editorUi.toolbar.addButton('geSprite-unorderedlist', mxResources.get('bulletedList'), - function() - { - document.execCommand('insertunorderedlist', false, null); - }, tmp), - this.editorUi.toolbar.addButton('geSprite-outdent', mxResources.get('decreaseIndent'), - function() - { - document.execCommand('outdent', false, null); - }, tmp), - this.editorUi.toolbar.addButton('geSprite-indent', mxResources.get('increaseIndent'), - function() - { - document.execCommand('indent', false, null); - }, tmp), - this.editorUi.toolbar.addButton('geSprite-code', mxResources.get('html'), - function() - { - graph.cellEditor.toggleViewMode(); - }, tmp)]; - this.styleButtons(btns); - btns[btns.length - 1].style.marginLeft = '9px'; - - if (mxClient.IS_QUIRKS) - { - mxUtils.br(container); - tmp.style.height = '40'; - } - - container.appendChild(tmp); - } - else - { - fontStyleItems[2].style.marginRight = '9px'; - right.style.marginRight = '9px'; - } - - // Label position - var stylePanel4 = stylePanel.cloneNode(false); - stylePanel4.style.marginLeft = '0px'; - stylePanel4.style.paddingTop = '8px'; - stylePanel4.style.paddingBottom = '4px'; - stylePanel4.style.fontWeight = 'normal'; - - mxUtils.write(stylePanel4, mxResources.get('position')); - - // Adds label position options - var positionSelect = document.createElement('select'); - positionSelect.style.position = 'absolute'; - positionSelect.style.right = '20px'; - positionSelect.style.width = '97px'; - positionSelect.style.marginTop = '-2px'; - - var directions = ['topLeft', 'top', 'topRight', 'left', 'center', 'right', 'bottomLeft', 'bottom', 'bottomRight']; - var lset = {'topLeft': [mxConstants.ALIGN_LEFT, mxConstants.ALIGN_TOP, mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_BOTTOM], - 'top': [mxConstants.ALIGN_CENTER, mxConstants.ALIGN_TOP, mxConstants.ALIGN_CENTER, mxConstants.ALIGN_BOTTOM], - 'topRight': [mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_TOP, mxConstants.ALIGN_LEFT, mxConstants.ALIGN_BOTTOM], - 'left': [mxConstants.ALIGN_LEFT, mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_MIDDLE], - 'center': [mxConstants.ALIGN_CENTER, mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_CENTER, mxConstants.ALIGN_MIDDLE], - 'right': [mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_MIDDLE, mxConstants.ALIGN_LEFT, mxConstants.ALIGN_MIDDLE], - 'bottomLeft': [mxConstants.ALIGN_LEFT, mxConstants.ALIGN_BOTTOM, mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_TOP], - 'bottom': [mxConstants.ALIGN_CENTER, mxConstants.ALIGN_BOTTOM, mxConstants.ALIGN_CENTER, mxConstants.ALIGN_TOP], - 'bottomRight': [mxConstants.ALIGN_RIGHT, mxConstants.ALIGN_BOTTOM, mxConstants.ALIGN_LEFT, mxConstants.ALIGN_TOP]}; - - for (var i = 0; i < directions.length; i++) - { - var positionOption = document.createElement('option'); - positionOption.setAttribute('value', directions[i]); - mxUtils.write(positionOption, mxResources.get(directions[i])); - positionSelect.appendChild(positionOption); - } - - stylePanel4.appendChild(positionSelect); - - // Writing direction - var stylePanel5 = stylePanel.cloneNode(false); - stylePanel5.style.marginLeft = '0px'; - stylePanel5.style.paddingTop = '4px'; - stylePanel5.style.paddingBottom = '4px'; - stylePanel5.style.fontWeight = 'normal'; - - mxUtils.write(stylePanel5, mxResources.get('writingDirection')); - - // Adds writing direction options - // LATER: Handle reselect of same option in all selects (change event - // is not fired for same option so have opened state on click) and - // handle multiple different styles for current selection - var dirSelect = document.createElement('select'); - dirSelect.style.position = 'absolute'; - dirSelect.style.right = '20px'; - dirSelect.style.width = '97px'; - dirSelect.style.marginTop = '-2px'; - - // NOTE: For automatic we use the value null since automatic - // requires the text to be non formatted and non-wrapped - var dirs = ['automatic', 'leftToRight', 'rightToLeft']; - var dirSet = {'automatic': null, - 'leftToRight': mxConstants.TEXT_DIRECTION_LTR, - 'rightToLeft': mxConstants.TEXT_DIRECTION_RTL}; - - for (var i = 0; i < dirs.length; i++) - { - var dirOption = document.createElement('option'); - dirOption.setAttribute('value', dirs[i]); - mxUtils.write(dirOption, mxResources.get(dirs[i])); - dirSelect.appendChild(dirOption); - } - - stylePanel5.appendChild(dirSelect); - - if (!graph.isEditing()) - { - container.appendChild(stylePanel4); - - mxEvent.addListener(positionSelect, 'change', function(evt) - { - graph.getModel().beginUpdate(); - try - { - var vals = lset[positionSelect.value]; - - if (vals != null) - { - graph.setCellStyles(mxConstants.STYLE_LABEL_POSITION, vals[0], graph.getSelectionCells()); - graph.setCellStyles(mxConstants.STYLE_VERTICAL_LABEL_POSITION, vals[1], graph.getSelectionCells()); - graph.setCellStyles(mxConstants.STYLE_ALIGN, vals[2], graph.getSelectionCells()); - graph.setCellStyles(mxConstants.STYLE_VERTICAL_ALIGN, vals[3], graph.getSelectionCells()); - } - } - finally - { - graph.getModel().endUpdate(); - } - - mxEvent.consume(evt); - }); - - // LATER: Update dir in text editor while editing and update style with label - // NOTE: The tricky part is handling and passing on the auto value - container.appendChild(stylePanel5); - - mxEvent.addListener(dirSelect, 'change', function(evt) - { - graph.setCellStyles(mxConstants.STYLE_TEXT_DIRECTION, dirSet[dirSelect.value], graph.getSelectionCells()); - mxEvent.consume(evt); - }); - } - - // Font size - var input = document.createElement('input'); - input.style.textAlign = 'right'; - input.style.marginTop = '4px'; - - if (!mxClient.IS_QUIRKS) - { - input.style.position = 'absolute'; - input.style.right = '32px'; - } - - input.style.width = '46px'; - input.style.height = (mxClient.IS_QUIRKS) ? '21px' : '17px'; - stylePanel2.appendChild(input); - - // Workaround for font size 4 if no text is selected is update font size below - // after first character was entered (as the font element is lazy created) - var pendingFontSize = null; - - var inputUpdate = this.installInputHandler(input, mxConstants.STYLE_FONTSIZE, Menus.prototype.defaultFontSize, 1, 999, ' pt', - function(fontSize) - { - // IE does not support containsNode - // KNOWN: Fixes font size issues but bypasses undo - if (window.getSelection && !mxClient.IS_IE && !mxClient.IS_IE11) - { - var selection = window.getSelection(); - var container = (selection.rangeCount > 0) ? selection.getRangeAt(0).commonAncestorContainer : - graph.cellEditor.textarea; - - function updateSize(elt, ignoreContains) - { - if (elt != graph.cellEditor.textarea && graph.cellEditor.textarea.contains(elt) && - (ignoreContains || selection.containsNode(elt, true))) - { - if (elt.nodeName == 'FONT') - { - elt.removeAttribute('size'); - elt.style.fontSize = fontSize + 'px'; - } - else - { - var css = mxUtils.getCurrentStyle(elt); - - if (css.fontSize != fontSize + 'px') - { - if (mxUtils.getCurrentStyle(elt.parentNode).fontSize != fontSize + 'px') - { - elt.style.fontSize = fontSize + 'px'; - } - else - { - elt.style.fontSize = ''; - } - } - } - } - }; - - // Wraps text node or mixed selection with leading text in a font element - if (container == graph.cellEditor.textarea || - container.nodeType != mxConstants.NODETYPE_ELEMENT) - { - document.execCommand('fontSize', false, '1'); - } - - if (container != graph.cellEditor.textarea) - { - container = container.parentNode; - } - - if (container.nodeType == mxConstants.NODETYPE_ELEMENT) - { - var elts = container.getElementsByTagName('*'); - updateSize(container); - - for (var i = 0; i < elts.length; i++) - { - updateSize(elts[i]); - } - } - - input.value = fontSize + ' pt'; - } - else if (window.getSelection || document.selection) - { - // Checks selection - var par = null; - - if (document.selection) - { - par = document.selection.createRange().parentElement(); - } - else - { - var selection = window.getSelection(); - - if (selection.rangeCount > 0) - { - par = selection.getRangeAt(0).commonAncestorContainer; - } - } - - // Node.contains does not work for text nodes in IE11 - function isOrContains(container, node) - { - while (node != null) - { - if (node === container) - { - return true; - } - - node = node.parentNode; - } - - return false; - }; - - if (par != null && isOrContains(graph.cellEditor.textarea, par)) - { - pendingFontSize = fontSize; - - // Workaround for can't set font size in px is to change font size afterwards - document.execCommand('fontSize', false, '4'); - var elts = graph.cellEditor.textarea.getElementsByTagName('font'); - - for (var i = 0; i < elts.length; i++) - { - if (elts[i].getAttribute('size') == '4') - { - elts[i].removeAttribute('size'); - elts[i].style.fontSize = pendingFontSize + 'px'; - - // Overrides fontSize in input with the one just assigned as a workaround - // for potential fontSize values of parent elements that don't match - window.setTimeout(function() - { - input.value = pendingFontSize + ' pt'; - pendingFontSize = null; - }, 0); - - break; - } - } - } - } - }, true); - - var stepper = this.createStepper(input, inputUpdate, 1, 10, true, Menus.prototype.defaultFontSize); - stepper.style.display = input.style.display; - stepper.style.marginTop = '4px'; - - if (!mxClient.IS_QUIRKS) - { - stepper.style.right = '20px'; - } - - stylePanel2.appendChild(stepper); - - var arrow = fontMenu.getElementsByTagName('div')[0]; - arrow.style.cssFloat = 'right'; - - var bgColorApply = null; - var currentBgColor = '#ffffff'; - - var fontColorApply = null; - var currentFontColor = '#000000'; - - var bgPanel = (graph.cellEditor.isContentEditing()) ? this.createColorOption(mxResources.get('backgroundColor'), function() - { - return currentBgColor; - }, function(color) - { - document.execCommand('backcolor', false, (color != mxConstants.NONE) ? color : 'transparent'); - }, '#ffffff', - { - install: function(apply) { bgColorApply = apply; }, - destroy: function() { bgColorApply = null; } - }, null, true) : this.createCellColorOption(mxResources.get('backgroundColor'), mxConstants.STYLE_LABEL_BACKGROUNDCOLOR, '#ffffff', null, function(color) - { - graph.updateLabelElements(graph.getSelectionCells(), function(elt) - { - elt.style.backgroundColor = null; - }); - }); - bgPanel.style.fontWeight = 'bold'; - - var borderPanel = this.createCellColorOption(mxResources.get('borderColor'), mxConstants.STYLE_LABEL_BORDERCOLOR, '#000000'); - borderPanel.style.fontWeight = 'bold'; - - var panel = (graph.cellEditor.isContentEditing()) ? this.createColorOption(mxResources.get('fontColor'), function() - { - return currentFontColor; - }, function(color) - { - document.execCommand('forecolor', false, (color != mxConstants.NONE) ? color : 'transparent'); - }, '#000000', - { - install: function(apply) { fontColorApply = apply; }, - destroy: function() { fontColorApply = null; } - }, null, true) : this.createCellColorOption(mxResources.get('fontColor'), mxConstants.STYLE_FONTCOLOR, '#000000', function(color) - { - if (color == null || color == mxConstants.NONE) - { - bgPanel.style.display = 'none'; - } - else - { - bgPanel.style.display = ''; - } - - borderPanel.style.display = bgPanel.style.display; - }, function(color) - { - if (color == null || color == mxConstants.NONE) - { - graph.setCellStyles(mxConstants.STYLE_NOLABEL, '1', graph.getSelectionCells()); - } - else - { - graph.setCellStyles(mxConstants.STYLE_NOLABEL, null, graph.getSelectionCells()); - } - - graph.updateLabelElements(graph.getSelectionCells(), function(elt) - { - elt.removeAttribute('color'); - elt.style.color = null; - }); - }); - panel.style.fontWeight = 'bold'; - - colorPanel.appendChild(panel); - colorPanel.appendChild(bgPanel); - - if (!graph.cellEditor.isContentEditing()) - { - colorPanel.appendChild(borderPanel); - } - - container.appendChild(colorPanel); - - var extraPanel = this.createPanel(); - extraPanel.style.paddingTop = '2px'; - extraPanel.style.paddingBottom = '4px'; - - // LATER: Fix toggle using '' instead of 'null' - var wwOpt = this.createCellOption(mxResources.get('wordWrap'), mxConstants.STYLE_WHITE_SPACE, null, 'wrap', 'null', null, null, true); - wwOpt.style.fontWeight = 'bold'; - - // Word wrap in edge labels only supported via labelWidth style - if (!ss.containsLabel && !ss.autoSize && ss.edges.length == 0) - { - extraPanel.appendChild(wwOpt); - } - - // Delegates switch of style to formattedText action as it also convertes newlines - var htmlOpt = this.createCellOption(mxResources.get('formattedText'), 'html', '0', - null, null, null, ui.actions.get('formattedText')); - htmlOpt.style.fontWeight = 'bold'; - extraPanel.appendChild(htmlOpt); - - var spacingPanel = this.createPanel(); - spacingPanel.style.paddingTop = '10px'; - spacingPanel.style.paddingBottom = '28px'; - spacingPanel.style.fontWeight = 'normal'; - - var span = document.createElement('div'); - span.style.position = 'absolute'; - span.style.width = '70px'; - span.style.marginTop = '0px'; - span.style.fontWeight = 'bold'; - mxUtils.write(span, mxResources.get('spacing')); - spacingPanel.appendChild(span); - - var topUpdate, globalUpdate, leftUpdate, bottomUpdate, rightUpdate; - var topSpacing = this.addUnitInput(spacingPanel, 'pt', 91, 44, function() - { - topUpdate.apply(this, arguments); - }); - var globalSpacing = this.addUnitInput(spacingPanel, 'pt', 20, 44, function() - { - globalUpdate.apply(this, arguments); - }); - - mxUtils.br(spacingPanel); - this.addLabel(spacingPanel, mxResources.get('top'), 91); - this.addLabel(spacingPanel, mxResources.get('global'), 20); - mxUtils.br(spacingPanel); - mxUtils.br(spacingPanel); - - var leftSpacing = this.addUnitInput(spacingPanel, 'pt', 162, 44, function() - { - leftUpdate.apply(this, arguments); - }); - var bottomSpacing = this.addUnitInput(spacingPanel, 'pt', 91, 44, function() - { - bottomUpdate.apply(this, arguments); - }); - var rightSpacing = this.addUnitInput(spacingPanel, 'pt', 20, 44, function() - { - rightUpdate.apply(this, arguments); - }); - - mxUtils.br(spacingPanel); - this.addLabel(spacingPanel, mxResources.get('left'), 162); - this.addLabel(spacingPanel, mxResources.get('bottom'), 91); - this.addLabel(spacingPanel, mxResources.get('right'), 20); - - if (!graph.cellEditor.isContentEditing()) - { - container.appendChild(extraPanel); - container.appendChild(this.createRelativeOption(mxResources.get('opacity'), mxConstants.STYLE_TEXT_OPACITY)); - container.appendChild(spacingPanel); - } - else - { - var selState = null; - var lineHeightInput = null; - - container.appendChild(this.createRelativeOption(mxResources.get('lineheight'), null, null, function(input) - { - var value = (input.value == '') ? 120 : parseInt(input.value); - value = Math.max(0, (isNaN(value)) ? 120 : value); - - if (selState != null) - { - graph.cellEditor.restoreSelection(selState); - selState = null; - } - - var selectedElement = graph.getSelectedElement(); - var node = selectedElement; - - while (node != null && node.nodeType != mxConstants.NODETYPE_ELEMENT) - { - node = node.parentNode; - } - - if (node != null && node == graph.cellEditor.textarea && graph.cellEditor.textarea.firstChild != null) - { - if (graph.cellEditor.textarea.firstChild.nodeName != 'P') - { - graph.cellEditor.textarea.innerHTML = '

' + graph.cellEditor.textarea.innerHTML + '

'; - } - - node = graph.cellEditor.textarea.firstChild; - } - - if (node != null && node != graph.cellEditor.textarea && graph.cellEditor.textarea.contains(node)) - { - node.style.lineHeight = value + '%'; - } - - input.value = value + ' %'; - }, function(input) - { - // Used in CSS handler to update current value - lineHeightInput = input; - - // KNOWN: Arrow up/down clear selection text in quirks/IE 8 - // Text size via arrow button limits to 16 in IE11. Why? - mxEvent.addListener(input, 'mousedown', function() - { - if (document.activeElement == graph.cellEditor.textarea) - { - selState = graph.cellEditor.saveSelection(); - } - }); - - mxEvent.addListener(input, 'touchstart', function() - { - if (document.activeElement == graph.cellEditor.textarea) - { - selState = graph.cellEditor.saveSelection(); - } - }); - - input.value = '120 %'; - })); - - var insertPanel = stylePanel.cloneNode(false); - insertPanel.style.paddingLeft = '0px'; - var insertBtns = this.editorUi.toolbar.addItems(['link', 'image'], insertPanel, true); - - var btns = [ - this.editorUi.toolbar.addButton('geSprite-horizontalrule', mxResources.get('insertHorizontalRule'), - function() - { - document.execCommand('inserthorizontalrule', false); - }, insertPanel), - this.editorUi.toolbar.addMenuFunctionInContainer(insertPanel, 'geSprite-table', mxResources.get('table'), false, mxUtils.bind(this, function(menu) - { - this.editorUi.menus.addInsertTableItem(menu); - }))]; - this.styleButtons(insertBtns); - this.styleButtons(btns); - - var wrapper2 = this.createPanel(); - wrapper2.style.paddingTop = '10px'; - wrapper2.style.paddingBottom = '10px'; - wrapper2.appendChild(this.createTitle(mxResources.get('insert'))); - wrapper2.appendChild(insertPanel); - container.appendChild(wrapper2); - - if (mxClient.IS_QUIRKS) - { - wrapper2.style.height = '70'; - } - - var tablePanel = stylePanel.cloneNode(false); - tablePanel.style.paddingLeft = '0px'; - - var btns = [ - this.editorUi.toolbar.addButton('geSprite-insertcolumnbefore', mxResources.get('insertColumnBefore'), - mxUtils.bind(this, function() - { - try - { - if (currentTable != null) - { - graph.selectNode(graph.insertColumn(currentTable, (tableCell != null) ? tableCell.cellIndex : 0)); - } - } - catch (e) - { - this.editorUi.handleError(e); - } - }), tablePanel), - this.editorUi.toolbar.addButton('geSprite-insertcolumnafter', mxResources.get('insertColumnAfter'), - mxUtils.bind(this, function() - { - try - { - if (currentTable != null) - { - graph.selectNode(graph.insertColumn(currentTable, (tableCell != null) ? tableCell.cellIndex + 1 : -1)); - } - } - catch (e) - { - this.editorUi.handleError(e); - } - }), tablePanel), - this.editorUi.toolbar.addButton('geSprite-deletecolumn', mxResources.get('deleteColumn'), - mxUtils.bind(this, function() - { - try - { - if (currentTable != null && tableCell != null) - { - graph.deleteColumn(currentTable, tableCell.cellIndex); - } - } - catch (e) - { - this.editorUi.handleError(e); - } - }), tablePanel), - this.editorUi.toolbar.addButton('geSprite-insertrowbefore', mxResources.get('insertRowBefore'), - mxUtils.bind(this, function() - { - try - { - if (currentTable != null && tableRow != null) - { - graph.selectNode(graph.insertRow(currentTable, tableRow.sectionRowIndex)); - } - } - catch (e) - { - this.editorUi.handleError(e); - } - }), tablePanel), - this.editorUi.toolbar.addButton('geSprite-insertrowafter', mxResources.get('insertRowAfter'), - mxUtils.bind(this, function() - { - try - { - if (currentTable != null && tableRow != null) - { - graph.selectNode(graph.insertRow(currentTable, tableRow.sectionRowIndex + 1)); - } - } - catch (e) - { - this.editorUi.handleError(e); - } - }), tablePanel), - this.editorUi.toolbar.addButton('geSprite-deleterow', mxResources.get('deleteRow'), - mxUtils.bind(this, function() - { - try - { - if (currentTable != null && tableRow != null) - { - graph.deleteRow(currentTable, tableRow.sectionRowIndex); - } - } - catch (e) - { - this.editorUi.handleError(e); - } - }), tablePanel)]; - this.styleButtons(btns); - btns[2].style.marginRight = '9px'; - - var wrapper3 = this.createPanel(); - wrapper3.style.paddingTop = '10px'; - wrapper3.style.paddingBottom = '10px'; - wrapper3.appendChild(this.createTitle(mxResources.get('table'))); - wrapper3.appendChild(tablePanel); - - if (mxClient.IS_QUIRKS) - { - mxUtils.br(container); - wrapper3.style.height = '70'; - } - - var tablePanel2 = stylePanel.cloneNode(false); - tablePanel2.style.paddingLeft = '0px'; - - var btns = [ - this.editorUi.toolbar.addButton('geSprite-strokecolor', mxResources.get('borderColor'), - mxUtils.bind(this, function() - { - if (currentTable != null) - { - // Converts rgb(r,g,b) values - var color = currentTable.style.borderColor.replace( - /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g, - function($0, $1, $2, $3) { - return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2); - }); - this.editorUi.pickColor(color, function(newColor) - { - if (newColor == null || newColor == mxConstants.NONE) - { - currentTable.removeAttribute('border'); - currentTable.style.border = ''; - currentTable.style.borderCollapse = ''; - } - else - { - currentTable.setAttribute('border', '1'); - currentTable.style.border = '1px solid ' + newColor; - currentTable.style.borderCollapse = 'collapse'; - } - }); - } - }), tablePanel2), - this.editorUi.toolbar.addButton('geSprite-fillcolor', mxResources.get('backgroundColor'), - mxUtils.bind(this, function() - { - // Converts rgb(r,g,b) values - if (currentTable != null) - { - var color = currentTable.style.backgroundColor.replace( - /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g, - function($0, $1, $2, $3) { - return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2); - }); - this.editorUi.pickColor(color, function(newColor) - { - if (newColor == null || newColor == mxConstants.NONE) - { - currentTable.style.backgroundColor = ''; - } - else - { - currentTable.style.backgroundColor = newColor; - } - }); - } - }), tablePanel2), - this.editorUi.toolbar.addButton('geSprite-fit', mxResources.get('spacing'), - function() - { - if (currentTable != null) - { - var value = currentTable.getAttribute('cellPadding') || 0; - - var dlg = new FilenameDialog(ui, value, mxResources.get('apply'), mxUtils.bind(this, function(newValue) - { - if (newValue != null && newValue.length > 0) - { - currentTable.setAttribute('cellPadding', newValue); - } - else - { - currentTable.removeAttribute('cellPadding'); - } - }), mxResources.get('spacing')); - ui.showDialog(dlg.container, 300, 80, true, true); - dlg.init(); - } - }, tablePanel2), - this.editorUi.toolbar.addButton('geSprite-left', mxResources.get('left'), - function() - { - if (currentTable != null) - { - currentTable.setAttribute('align', 'left'); - } - }, tablePanel2), - this.editorUi.toolbar.addButton('geSprite-center', mxResources.get('center'), - function() - { - if (currentTable != null) - { - currentTable.setAttribute('align', 'center'); - } - }, tablePanel2), - this.editorUi.toolbar.addButton('geSprite-right', mxResources.get('right'), - function() - { - if (currentTable != null) - { - currentTable.setAttribute('align', 'right'); - } - }, tablePanel2)]; - this.styleButtons(btns); - btns[2].style.marginRight = '9px'; - - if (mxClient.IS_QUIRKS) - { - mxUtils.br(wrapper3); - mxUtils.br(wrapper3); - } - - wrapper3.appendChild(tablePanel2); - container.appendChild(wrapper3); - - tableWrapper = wrapper3; - } - - function setSelected(elt, selected) - { - if (mxClient.IS_IE && (mxClient.IS_QUIRKS || document.documentMode < 10)) - { - elt.style.filter = (selected) ? 'progid:DXImageTransform.Microsoft.Gradient('+ - 'StartColorStr=\'#c5ecff\', EndColorStr=\'#87d4fb\', GradientType=0)' : ''; - } - else - { - elt.style.backgroundImage = (selected) ? 'linear-gradient(#c5ecff 0px,#87d4fb 100%)' : ''; - } - }; - - var listener = mxUtils.bind(this, function(sender, evt, force) - { - ss = this.format.getSelectionState(); - var fontStyle = mxUtils.getValue(ss.style, mxConstants.STYLE_FONTSTYLE, 0); - setSelected(fontStyleItems[0], (fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD); - setSelected(fontStyleItems[1], (fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC); - setSelected(fontStyleItems[2], (fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE); - fontMenu.firstChild.nodeValue = mxUtils.htmlEntities(mxUtils.getValue(ss.style, mxConstants.STYLE_FONTFAMILY, Menus.prototype.defaultFont)); - - setSelected(verticalItem, mxUtils.getValue(ss.style, mxConstants.STYLE_HORIZONTAL, '1') == '0'); - - if (force || document.activeElement != input) - { - var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_FONTSIZE, Menus.prototype.defaultFontSize)); - input.value = (isNaN(tmp)) ? '' : tmp + ' pt'; - } - - var align = mxUtils.getValue(ss.style, mxConstants.STYLE_ALIGN, mxConstants.ALIGN_CENTER); - setSelected(left, align == mxConstants.ALIGN_LEFT); - setSelected(center, align == mxConstants.ALIGN_CENTER); - setSelected(right, align == mxConstants.ALIGN_RIGHT); - - var valign = mxUtils.getValue(ss.style, mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE); - setSelected(top, valign == mxConstants.ALIGN_TOP); - setSelected(middle, valign == mxConstants.ALIGN_MIDDLE); - setSelected(bottom, valign == mxConstants.ALIGN_BOTTOM); - - var pos = mxUtils.getValue(ss.style, mxConstants.STYLE_LABEL_POSITION, mxConstants.ALIGN_CENTER); - var vpos = mxUtils.getValue(ss.style, mxConstants.STYLE_VERTICAL_LABEL_POSITION, mxConstants.ALIGN_MIDDLE); - - if (pos == mxConstants.ALIGN_LEFT && vpos == mxConstants.ALIGN_TOP) - { - positionSelect.value = 'topLeft'; - } - else if (pos == mxConstants.ALIGN_CENTER && vpos == mxConstants.ALIGN_TOP) - { - positionSelect.value = 'top'; - } - else if (pos == mxConstants.ALIGN_RIGHT && vpos == mxConstants.ALIGN_TOP) - { - positionSelect.value = 'topRight'; - } - else if (pos == mxConstants.ALIGN_LEFT && vpos == mxConstants.ALIGN_BOTTOM) - { - positionSelect.value = 'bottomLeft'; - } - else if (pos == mxConstants.ALIGN_CENTER && vpos == mxConstants.ALIGN_BOTTOM) - { - positionSelect.value = 'bottom'; - } - else if (pos == mxConstants.ALIGN_RIGHT && vpos == mxConstants.ALIGN_BOTTOM) - { - positionSelect.value = 'bottomRight'; - } - else if (pos == mxConstants.ALIGN_LEFT) - { - positionSelect.value = 'left'; - } - else if (pos == mxConstants.ALIGN_RIGHT) - { - positionSelect.value = 'right'; - } - else - { - positionSelect.value = 'center'; - } - - var dir = mxUtils.getValue(ss.style, mxConstants.STYLE_TEXT_DIRECTION, mxConstants.DEFAULT_TEXT_DIRECTION); - - if (dir == mxConstants.TEXT_DIRECTION_RTL) - { - dirSelect.value = 'rightToLeft'; - } - else if (dir == mxConstants.TEXT_DIRECTION_LTR) - { - dirSelect.value = 'leftToRight'; - } - else if (dir == mxConstants.TEXT_DIRECTION_AUTO) - { - dirSelect.value = 'automatic'; - } - - if (force || document.activeElement != globalSpacing) - { - var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING, 2)); - globalSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; - } - - if (force || document.activeElement != topSpacing) - { - var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING_TOP, 0)); - topSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; - } - - if (force || document.activeElement != rightSpacing) - { - var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING_RIGHT, 0)); - rightSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; - } - - if (force || document.activeElement != bottomSpacing) - { - var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING_BOTTOM, 0)); - bottomSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; - } - - if (force || document.activeElement != leftSpacing) - { - var tmp = parseFloat(mxUtils.getValue(ss.style, mxConstants.STYLE_SPACING_LEFT, 0)); - leftSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; - } - }); - - globalUpdate = this.installInputHandler(globalSpacing, mxConstants.STYLE_SPACING, 2, -999, 999, ' pt'); - topUpdate = this.installInputHandler(topSpacing, mxConstants.STYLE_SPACING_TOP, 0, -999, 999, ' pt'); - rightUpdate = this.installInputHandler(rightSpacing, mxConstants.STYLE_SPACING_RIGHT, 0, -999, 999, ' pt'); - bottomUpdate = this.installInputHandler(bottomSpacing, mxConstants.STYLE_SPACING_BOTTOM, 0, -999, 999, ' pt'); - leftUpdate = this.installInputHandler(leftSpacing, mxConstants.STYLE_SPACING_LEFT, 0, -999, 999, ' pt'); - - this.addKeyHandler(input, listener); - this.addKeyHandler(globalSpacing, listener); - this.addKeyHandler(topSpacing, listener); - this.addKeyHandler(rightSpacing, listener); - this.addKeyHandler(bottomSpacing, listener); - this.addKeyHandler(leftSpacing, listener); - - graph.getModel().addListener(mxEvent.CHANGE, listener); - this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); - listener(); - - if (graph.cellEditor.isContentEditing()) - { - var updating = false; - - var updateCssHandler = function() - { - if (!updating) - { - updating = true; - - window.setTimeout(function() - { - var selectedElement = graph.getSelectedElement(); - var node = selectedElement; - - while (node != null && node.nodeType != mxConstants.NODETYPE_ELEMENT) - { - node = node.parentNode; - } - - if (node != null) - { - // Workaround for commonAncestor on range in IE11 returning parent of common ancestor - if (node == graph.cellEditor.textarea && graph.cellEditor.textarea.children.length == 1 && - graph.cellEditor.textarea.firstChild.nodeType == mxConstants.NODETYPE_ELEMENT) - { - node = graph.cellEditor.textarea.firstChild; - } - - function getRelativeLineHeight(fontSize, lineHeight, elt) - { - if (elt.style.lineHeight.substring(elt.style.lineHeight.length - 1) == '%') - { - return parseInt(elt.style.lineHeight) / 100; - } - else - { - return (lineHeight.substring(lineHeight.length - 2) == 'px') ? - parseFloat(lineHeight) / fontSize : parseInt(lineHeight); - } - }; - - function getAbsoluteFontSize(fontSize) - { - if (fontSize.substring(fontSize.length - 2) == 'px') - { - return parseFloat(fontSize); - } - else - { - return mxConstants.DEFAULT_FONTSIZE; - } - } - - //var realCss = mxUtils.getCurrentStyle(selectedElement); - var css = mxUtils.getCurrentStyle(node); - var fontSize = getAbsoluteFontSize(css.fontSize); - var lineHeight = getRelativeLineHeight(fontSize, css.lineHeight, node); - - // Finds common font size - var elts = node.getElementsByTagName('*'); - - // IE does not support containsNode - if (elts.length > 0 && window.getSelection && !mxClient.IS_IE && !mxClient.IS_IE11) - { - var selection = window.getSelection(); - - for (var i = 0; i < elts.length; i++) - { - if (selection.containsNode(elts[i], true)) - { - temp = mxUtils.getCurrentStyle(elts[i]); - fontSize = Math.max(getAbsoluteFontSize(temp.fontSize), fontSize); - var lh = getRelativeLineHeight(fontSize, temp.lineHeight, elts[i]); - - if (lh != lineHeight || isNaN(lh)) - { - lineHeight = ''; - } - } - } - } - - if (css != null) - { - setSelected(fontStyleItems[0], css.fontWeight == 'bold' || graph.getParentByName(node, 'B', graph.cellEditor.textarea) != null); - setSelected(fontStyleItems[1], css.fontStyle == 'italic' || graph.getParentByName(node, 'I', graph.cellEditor.textarea) != null); - setSelected(fontStyleItems[2], graph.getParentByName(node, 'U', graph.cellEditor.textarea) != null); - setSelected(left, css.textAlign == 'left'); - setSelected(center, css.textAlign == 'center'); - setSelected(right, css.textAlign == 'right'); - setSelected(full, css.textAlign == 'justify'); - setSelected(sup, graph.getParentByName(node, 'SUP', graph.cellEditor.textarea) != null); - setSelected(sub, graph.getParentByName(node, 'SUB', graph.cellEditor.textarea) != null); - - currentTable = graph.getParentByName(node, 'TABLE', graph.cellEditor.textarea); - tableRow = (currentTable == null) ? null : graph.getParentByName(node, 'TR', currentTable); - tableCell = (currentTable == null) ? null : graph.getParentByName(node, 'TD', currentTable); - tableWrapper.style.display = (currentTable != null) ? '' : 'none'; - - if (document.activeElement != input) - { - if (node.nodeName == 'FONT' && node.getAttribute('size') == '4' && - pendingFontSize != null) - { - node.removeAttribute('size'); - node.style.fontSize = pendingFontSize + ' pt'; - pendingFontSize = null; - } - else - { - input.value = (isNaN(fontSize)) ? '' : fontSize + ' pt'; - } - - var lh = parseFloat(lineHeight); - - if (!isNaN(lh)) - { - lineHeightInput.value = Math.round(lh * 100) + ' %'; - } - else - { - lineHeightInput.value = '100 %'; - } - } - - // Converts rgb(r,g,b) values - var color = css.color.replace( - /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g, - function($0, $1, $2, $3) { - return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2); - }); - var color2 = css.backgroundColor.replace( - /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g, - function($0, $1, $2, $3) { - return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2); - }); - - // Updates the color picker for the current font - if (fontColorApply != null) - { - if (color.charAt(0) == '#') - { - currentFontColor = color; - } - else - { - currentFontColor = '#000000'; - } - - fontColorApply(currentFontColor, true); - } - - if (bgColorApply != null) - { - if (color2.charAt(0) == '#') - { - currentBgColor = color2; - } - else - { - currentBgColor = null; - } - - bgColorApply(currentBgColor, true); - } - - // Workaround for firstChild is null or not an object - // in the log which seems to be IE8- only / 29.01.15 - if (fontMenu.firstChild != null) - { - // Strips leading and trailing quotes - var ff = css.fontFamily; - - if (ff.charAt(0) == '\'') - { - ff = ff.substring(1); - } - - if (ff.charAt(ff.length - 1) == '\'') - { - ff = ff.substring(0, ff.length - 1); - } - - if (ff.charAt(0) == '"') - { - ff = ff.substring(1); - } - - if (ff.charAt(ff.length - 1) == '"') - { - ff = ff.substring(0, ff.length - 1); - } - - fontMenu.firstChild.nodeValue = ff; - } - } - } - - updating = false; - }, 0); - } - }; - - mxEvent.addListener(graph.cellEditor.textarea, 'input', updateCssHandler) - mxEvent.addListener(graph.cellEditor.textarea, 'touchend', updateCssHandler); - mxEvent.addListener(graph.cellEditor.textarea, 'mouseup', updateCssHandler); - mxEvent.addListener(graph.cellEditor.textarea, 'keyup', updateCssHandler); - this.listeners.push({destroy: function() - { - // No need to remove listener since textarea is destroyed after edit - }}); - updateCssHandler(); - } - - return container; -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -StyleFormatPanel = function(format, editorUi, container) -{ - BaseFormatPanel.call(this, format, editorUi, container); - this.init(); -}; - -mxUtils.extend(StyleFormatPanel, BaseFormatPanel); - -/** - * - */ -StyleFormatPanel.prototype.defaultStrokeColor = 'black'; - -/** - * Adds the label menu items to the given menu and parent. - */ -StyleFormatPanel.prototype.init = function() -{ - var ui = this.editorUi; - var editor = ui.editor; - var graph = editor.graph; - var ss = this.format.getSelectionState(); - - if (ss.containsImage && ss.vertices.length == 1 && ss.style.shape == 'image' && - ss.style.image != null && ss.style.image.substring(0, 19) == 'data:image/svg+xml;') - { - this.container.appendChild(this.addSvgStyles(this.createPanel())); - } - - if (!ss.containsImage || ss.style.shape == 'image') - { - this.container.appendChild(this.addFill(this.createPanel())); - } - - this.container.appendChild(this.addStroke(this.createPanel())); - this.container.appendChild(this.addLineJumps(this.createPanel())); - var opacityPanel = this.createRelativeOption(mxResources.get('opacity'), mxConstants.STYLE_OPACITY, 41); - opacityPanel.style.paddingTop = '8px'; - opacityPanel.style.paddingBottom = '8px'; - this.container.appendChild(opacityPanel); - this.container.appendChild(this.addEffects(this.createPanel())); - var opsPanel = this.addEditOps(this.createPanel()); - - if (opsPanel.firstChild != null) - { - mxUtils.br(opsPanel); - } - - this.container.appendChild(this.addStyleOps(opsPanel)); -}; - -/** - * Use browser for parsing CSS. - */ -StyleFormatPanel.prototype.getCssRules = function(css) -{ - var doc = document.implementation.createHTMLDocument(''); - var styleElement = document.createElement('style'); - - mxUtils.setTextContent(styleElement, css); - doc.body.appendChild(styleElement); - - return styleElement.sheet.cssRules; -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -StyleFormatPanel.prototype.addSvgStyles = function(container) -{ - var ui = this.editorUi; - var graph = ui.editor.graph; - var ss = this.format.getSelectionState(); - container.style.paddingTop = '6px'; - container.style.paddingBottom = '6px'; - container.style.fontWeight = 'bold'; - container.style.display = 'none'; - - try - { - var exp = ss.style.editableCssRules; - - if (exp != null) - { - var regex = new RegExp(exp); - - var data = ss.style.image.substring(ss.style.image.indexOf(',') + 1); - var xml = (window.atob) ? atob(data) : Base64.decode(data, true); - var svg = mxUtils.parseXml(xml); - - if (svg != null) - { - var styles = svg.getElementsByTagName('style'); - - for (var i = 0; i < styles.length; i++) - { - var rules = this.getCssRules(mxUtils.getTextContent(styles[i])); - - for (var j = 0; j < rules.length; j++) - { - this.addSvgRule(container, rules[j], svg, styles[i], rules, j, regex); - } - } - } - } - } - catch (e) - { - // ignore - } - - return container; -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -StyleFormatPanel.prototype.addSvgRule = function(container, rule, svg, styleElem, rules, ruleIndex, regex) -{ - var ui = this.editorUi; - var graph = ui.editor.graph; - - if (regex.test(rule.selectorText)) - { - function rgb2hex(rgb) - { - rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i); - - return (rgb && rgb.length === 4) ? "#" + - ("0" + parseInt(rgb[1],10).toString(16)).slice(-2) + - ("0" + parseInt(rgb[2],10).toString(16)).slice(-2) + - ("0" + parseInt(rgb[3],10).toString(16)).slice(-2) : ''; - }; - - var addStyleRule = mxUtils.bind(this, function(rule, key, label) - { - if (rule.style[key] != '') - { - var option = this.createColorOption(label + ' ' + rule.selectorText, function() - { - return rgb2hex(rule.style[key]); - }, function(color) - { - rules[ruleIndex].style[key] = color; - var cssTxt = ''; - - for (var i = 0; i < rules.length; i++) - { - cssTxt += rules[i].cssText + ' '; - } - - styleElem.textContent = cssTxt; - var xml = mxUtils.getXml(svg.documentElement); - - graph.setCellStyles(mxConstants.STYLE_IMAGE, 'data:image/svg+xml,' + - ((window.btoa) ? btoa(xml) : Base64.encode(xml, true)), - graph.getSelectionCells()); - }, '#ffffff', - { - install: function(apply) - { - // ignore - }, - destroy: function() - { - // ignore - } - }); - - container.appendChild(option); - - // Shows container if rules are added - container.style.display = ''; - } - }); - - addStyleRule(rule, 'fill', mxResources.get('fill')); - addStyleRule(rule, 'stroke', mxResources.get('line')); - } -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -StyleFormatPanel.prototype.addEditOps = function(div) -{ - var ss = this.format.getSelectionState(); - var btn = null; - - if (this.editorUi.editor.graph.getSelectionCount() == 1) - { - btn = mxUtils.button(mxResources.get('editStyle'), mxUtils.bind(this, function(evt) - { - this.editorUi.actions.get('editStyle').funct(); - })); - - btn.setAttribute('title', mxResources.get('editStyle') + ' (' + this.editorUi.actions.get('editStyle').shortcut + ')'); - btn.style.width = '202px'; - btn.style.marginBottom = '2px'; - - div.appendChild(btn); - } - - if (ss.image) - { - var btn2 = mxUtils.button(mxResources.get('editImage'), mxUtils.bind(this, function(evt) - { - this.editorUi.actions.get('image').funct(); - })); - - btn2.setAttribute('title', mxResources.get('editImage')); - btn2.style.marginBottom = '2px'; - - if (btn == null) - { - btn2.style.width = '202px'; - } - else - { - btn.style.width = '100px'; - btn2.style.width = '100px'; - btn2.style.marginLeft = '2px'; - } - - div.appendChild(btn2); - } - - return div; -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -StyleFormatPanel.prototype.addFill = function(container) -{ - var ui = this.editorUi; - var graph = ui.editor.graph; - var ss = this.format.getSelectionState(); - container.style.paddingTop = '6px'; - container.style.paddingBottom = '6px'; - - // Adds gradient direction option - var gradientSelect = document.createElement('select'); - gradientSelect.style.position = 'absolute'; - gradientSelect.style.marginTop = '-2px'; - gradientSelect.style.right = (mxClient.IS_QUIRKS) ? '52px' : '72px'; - gradientSelect.style.width = '70px'; - - // Stops events from bubbling to color option event handler - mxEvent.addListener(gradientSelect, 'click', function(evt) - { - mxEvent.consume(evt); - }); - - var gradientPanel = this.createCellColorOption(mxResources.get('gradient'), mxConstants.STYLE_GRADIENTCOLOR, '#ffffff', function(color) - { - if (color == null || color == mxConstants.NONE) - { - gradientSelect.style.display = 'none'; - } - else - { - gradientSelect.style.display = ''; - } - }); - - var fillKey = (ss.style.shape == 'image') ? mxConstants.STYLE_IMAGE_BACKGROUND : mxConstants.STYLE_FILLCOLOR; - var label = (ss.style.shape == 'image') ? mxResources.get('background') : mxResources.get('fill'); - - var fillPanel = this.createCellColorOption(label, fillKey, '#ffffff'); - fillPanel.style.fontWeight = 'bold'; - - var tmpColor = mxUtils.getValue(ss.style, fillKey, null); - gradientPanel.style.display = (tmpColor != null && tmpColor != mxConstants.NONE && - ss.fill && ss.style.shape != 'image') ? '' : 'none'; - - var directions = [mxConstants.DIRECTION_NORTH, mxConstants.DIRECTION_EAST, - mxConstants.DIRECTION_SOUTH, mxConstants.DIRECTION_WEST]; - - for (var i = 0; i < directions.length; i++) - { - var gradientOption = document.createElement('option'); - gradientOption.setAttribute('value', directions[i]); - mxUtils.write(gradientOption, mxResources.get(directions[i])); - gradientSelect.appendChild(gradientOption); - } - - gradientPanel.appendChild(gradientSelect); - - var listener = mxUtils.bind(this, function() - { - ss = this.format.getSelectionState(); - var value = mxUtils.getValue(ss.style, mxConstants.STYLE_GRADIENT_DIRECTION, mxConstants.DIRECTION_SOUTH); - - // Handles empty string which is not allowed as a value - if (value == '') - { - value = mxConstants.DIRECTION_SOUTH; - } - - gradientSelect.value = value; - container.style.display = (ss.fill) ? '' : 'none'; - - var fillColor = mxUtils.getValue(ss.style, mxConstants.STYLE_FILLCOLOR, null); - - if (!ss.fill || ss.containsImage || fillColor == null || fillColor == mxConstants.NONE || ss.style.shape == 'filledEdge') - { - gradientPanel.style.display = 'none'; - } - else - { - gradientPanel.style.display = ''; - } - }); - - graph.getModel().addListener(mxEvent.CHANGE, listener); - this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); - listener(); - - mxEvent.addListener(gradientSelect, 'change', function(evt) - { - graph.setCellStyles(mxConstants.STYLE_GRADIENT_DIRECTION, gradientSelect.value, graph.getSelectionCells()); - mxEvent.consume(evt); - }); - - container.appendChild(fillPanel); - container.appendChild(gradientPanel); - - // Adds custom colors - var custom = this.getCustomColors(); - - for (var i = 0; i < custom.length; i++) - { - container.appendChild(this.createCellColorOption(custom[i].title, custom[i].key, custom[i].defaultValue)); - } - - return container; -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -StyleFormatPanel.prototype.getCustomColors = function() -{ - var ss = this.format.getSelectionState(); - var result = []; - - if (ss.style.shape == 'swimlane') - { - result.push({title: mxResources.get('laneColor'), key: 'swimlaneFillColor', defaultValue: '#ffffff'}); - } - - return result; -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -StyleFormatPanel.prototype.addStroke = function(container) -{ - var ui = this.editorUi; - var graph = ui.editor.graph; - var ss = this.format.getSelectionState(); - - container.style.paddingTop = '4px'; - container.style.paddingBottom = '4px'; - container.style.whiteSpace = 'normal'; - - var colorPanel = document.createElement('div'); - colorPanel.style.fontWeight = 'bold'; - - // Adds gradient direction option - var styleSelect = document.createElement('select'); - styleSelect.style.position = 'absolute'; - styleSelect.style.marginTop = '-2px'; - styleSelect.style.right = '72px'; - styleSelect.style.width = '80px'; - - var styles = ['sharp', 'rounded', 'curved']; - - for (var i = 0; i < styles.length; i++) - { - var styleOption = document.createElement('option'); - styleOption.setAttribute('value', styles[i]); - mxUtils.write(styleOption, mxResources.get(styles[i])); - styleSelect.appendChild(styleOption); - } - - mxEvent.addListener(styleSelect, 'change', function(evt) - { - graph.getModel().beginUpdate(); - try - { - var keys = [mxConstants.STYLE_ROUNDED, mxConstants.STYLE_CURVED]; - // Default for rounded is 1 - var values = ['0', null]; - - if (styleSelect.value == 'rounded') - { - values = ['1', null]; - } - else if (styleSelect.value == 'curved') - { - values = [null, '1']; - } - - for (var i = 0; i < keys.length; i++) - { - graph.setCellStyles(keys[i], values[i], graph.getSelectionCells()); - } - - ui.fireEvent(new mxEventObject('styleChanged', 'keys', keys, - 'values', values, 'cells', graph.getSelectionCells())); - } - finally - { - graph.getModel().endUpdate(); - } - - mxEvent.consume(evt); - }); - - // Stops events from bubbling to color option event handler - mxEvent.addListener(styleSelect, 'click', function(evt) - { - mxEvent.consume(evt); - }); - - var strokeKey = (ss.style.shape == 'image') ? mxConstants.STYLE_IMAGE_BORDER : mxConstants.STYLE_STROKECOLOR; - var label = (ss.style.shape == 'image') ? mxResources.get('border') : mxResources.get('line'); - - var lineColor = this.createCellColorOption(label, strokeKey, '#000000'); - lineColor.appendChild(styleSelect); - colorPanel.appendChild(lineColor); - - // Used if only edges selected - var stylePanel = colorPanel.cloneNode(false); - stylePanel.style.fontWeight = 'normal'; - stylePanel.style.whiteSpace = 'nowrap'; - stylePanel.style.position = 'relative'; - stylePanel.style.paddingLeft = '16px' - stylePanel.style.marginBottom = '2px'; - stylePanel.style.marginTop = '2px'; - stylePanel.className = 'geToolbarContainer'; - - var addItem = mxUtils.bind(this, function(menu, width, cssName, keys, values) - { - var item = this.editorUi.menus.styleChange(menu, '', keys, values, 'geIcon', null); - - var pat = document.createElement('div'); - pat.style.width = width + 'px'; - pat.style.height = '1px'; - pat.style.borderBottom = '1px ' + cssName + ' ' + this.defaultStrokeColor; - pat.style.paddingTop = '6px'; - - item.firstChild.firstChild.style.padding = '0px 4px 0px 4px'; - item.firstChild.firstChild.style.width = width + 'px'; - item.firstChild.firstChild.appendChild(pat); - - return item; - }); - - var pattern = this.editorUi.toolbar.addMenuFunctionInContainer(stylePanel, 'geSprite-orthogonal', mxResources.get('pattern'), false, mxUtils.bind(this, function(menu) - { - addItem(menu, 75, 'solid', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], [null, null]).setAttribute('title', mxResources.get('solid')); - addItem(menu, 75, 'dashed', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', null]).setAttribute('title', mxResources.get('dashed')); - addItem(menu, 75, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 1']).setAttribute('title', mxResources.get('dotted') + ' (1)'); - addItem(menu, 75, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 2']).setAttribute('title', mxResources.get('dotted') + ' (2)'); - addItem(menu, 75, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 4']).setAttribute('title', mxResources.get('dotted') + ' (3)'); - })); - - // Used for mixed selection (vertices and edges) - var altStylePanel = stylePanel.cloneNode(false); - - var edgeShape = this.editorUi.toolbar.addMenuFunctionInContainer(altStylePanel, 'geSprite-connection', mxResources.get('connection'), false, mxUtils.bind(this, function(menu) - { - this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_SHAPE, mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, 'width'], [null, null, null, null], 'geIcon geSprite geSprite-connection', null, true).setAttribute('title', mxResources.get('line')); - this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_SHAPE, mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, 'width'], ['link', null, null, null], 'geIcon geSprite geSprite-linkedge', null, true).setAttribute('title', mxResources.get('link')); - this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_SHAPE, mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, 'width'], ['flexArrow', null, null, null], 'geIcon geSprite geSprite-arrow', null, true).setAttribute('title', mxResources.get('arrow')); - this.editorUi.menus.styleChange(menu, '', [mxConstants.STYLE_SHAPE, mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE, 'width'], ['arrow', null, null, null], 'geIcon geSprite geSprite-simplearrow', null, true).setAttribute('title', mxResources.get('simpleArrow')); - })); - - var altPattern = this.editorUi.toolbar.addMenuFunctionInContainer(altStylePanel, 'geSprite-orthogonal', mxResources.get('pattern'), false, mxUtils.bind(this, function(menu) - { - addItem(menu, 33, 'solid', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], [null, null]).setAttribute('title', mxResources.get('solid')); - addItem(menu, 33, 'dashed', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', null]).setAttribute('title', mxResources.get('dashed')); - addItem(menu, 33, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 1']).setAttribute('title', mxResources.get('dotted') + ' (1)'); - addItem(menu, 33, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 2']).setAttribute('title', mxResources.get('dotted') + ' (2)'); - addItem(menu, 33, 'dotted', [mxConstants.STYLE_DASHED, mxConstants.STYLE_DASH_PATTERN], ['1', '1 4']).setAttribute('title', mxResources.get('dotted') + ' (3)'); - })); - - var stylePanel2 = stylePanel.cloneNode(false); - - // Stroke width - var input = document.createElement('input'); - input.style.textAlign = 'right'; - input.style.marginTop = '2px'; - input.style.width = '41px'; - input.setAttribute('title', mxResources.get('linewidth')); - - stylePanel.appendChild(input); - - var altInput = input.cloneNode(true); - altStylePanel.appendChild(altInput); - - function update(evt) - { - // Maximum stroke width is 999 - var value = parseInt(input.value); - value = Math.min(999, Math.max(1, (isNaN(value)) ? 1 : value)); - - if (value != mxUtils.getValue(ss.style, mxConstants.STYLE_STROKEWIDTH, 1)) - { - graph.setCellStyles(mxConstants.STYLE_STROKEWIDTH, value, graph.getSelectionCells()); - ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_STROKEWIDTH], - 'values', [value], 'cells', graph.getSelectionCells())); - } - - input.value = value + ' pt'; - mxEvent.consume(evt); - }; - - function altUpdate(evt) - { - // Maximum stroke width is 999 - var value = parseInt(altInput.value); - value = Math.min(999, Math.max(1, (isNaN(value)) ? 1 : value)); - - if (value != mxUtils.getValue(ss.style, mxConstants.STYLE_STROKEWIDTH, 1)) - { - graph.setCellStyles(mxConstants.STYLE_STROKEWIDTH, value, graph.getSelectionCells()); - ui.fireEvent(new mxEventObject('styleChanged', 'keys', [mxConstants.STYLE_STROKEWIDTH], - 'values', [value], 'cells', graph.getSelectionCells())); - } - - altInput.value = value + ' pt'; - mxEvent.consume(evt); - }; - - var stepper = this.createStepper(input, update, 1, 9); - stepper.style.display = input.style.display; - stepper.style.marginTop = '2px'; - stylePanel.appendChild(stepper); - - var altStepper = this.createStepper(altInput, altUpdate, 1, 9); - altStepper.style.display = altInput.style.display; - altStepper.style.marginTop = '2px'; - altStylePanel.appendChild(altStepper); - - if (!mxClient.IS_QUIRKS) - { - input.style.position = 'absolute'; - input.style.right = '32px'; - input.style.height = '15px'; - stepper.style.right = '20px'; - - altInput.style.position = 'absolute'; - altInput.style.right = '32px'; - altInput.style.height = '15px'; - altStepper.style.right = '20px'; - } - else - { - input.style.height = '17px'; - altInput.style.height = '17px'; - } - - mxEvent.addListener(input, 'blur', update); - mxEvent.addListener(input, 'change', update); - - mxEvent.addListener(altInput, 'blur', altUpdate); - mxEvent.addListener(altInput, 'change', altUpdate); - - if (mxClient.IS_QUIRKS) - { - mxUtils.br(stylePanel2); - mxUtils.br(stylePanel2); - } - - var edgeStyle = this.editorUi.toolbar.addMenuFunctionInContainer(stylePanel2, 'geSprite-orthogonal', mxResources.get('waypoints'), false, mxUtils.bind(this, function(menu) - { - if (ss.style.shape != 'arrow') - { - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], [null, null, null], 'geIcon geSprite geSprite-straight', null, true).setAttribute('title', mxResources.get('straight')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['orthogonalEdgeStyle', null, null], 'geIcon geSprite geSprite-orthogonal', null, true).setAttribute('title', mxResources.get('orthogonal')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['elbowEdgeStyle', null, null, null], 'geIcon geSprite geSprite-horizontalelbow', null, true).setAttribute('title', mxResources.get('simple')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['elbowEdgeStyle', 'vertical', null, null], 'geIcon geSprite geSprite-verticalelbow', null, true).setAttribute('title', mxResources.get('simple')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['isometricEdgeStyle', null, null, null], 'geIcon geSprite geSprite-horizontalisometric', null, true).setAttribute('title', mxResources.get('isometric')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['isometricEdgeStyle', 'vertical', null, null], 'geIcon geSprite geSprite-verticalisometric', null, true).setAttribute('title', mxResources.get('isometric')); - - if (ss.style.shape == 'connector') - { - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['orthogonalEdgeStyle', '1', null], 'geIcon geSprite geSprite-curved', null, true).setAttribute('title', mxResources.get('curved')); - } - - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['entityRelationEdgeStyle', null, null], 'geIcon geSprite geSprite-entity', null, true).setAttribute('title', mxResources.get('entityRelation')); - } - })); - - var lineStart = this.editorUi.toolbar.addMenuFunctionInContainer(stylePanel2, 'geSprite-startclassic', mxResources.get('linestart'), false, mxUtils.bind(this, function(menu) - { - if (ss.style.shape == 'connector' || ss.style.shape == 'flexArrow' || ss.style.shape == 'filledEdge') - { - var item = this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.NONE, 0], 'geIcon', null, false); - item.setAttribute('title', mxResources.get('none')); - item.firstChild.firstChild.innerHTML = '' + mxUtils.htmlEntities(mxResources.get('none')) + ''; - - if (ss.style.shape == 'connector' || ss.style.shape == 'filledEdge') - { - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_CLASSIC, 1], 'geIcon geSprite geSprite-startclassic', null, false).setAttribute('title', mxResources.get('classic')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_CLASSIC_THIN, 1], 'geIcon geSprite geSprite-startclassicthin', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_OPEN, 0], 'geIcon geSprite geSprite-startopen', null, false).setAttribute('title', mxResources.get('openArrow')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_OPEN_THIN, 0], 'geIcon geSprite geSprite-startopenthin', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['openAsync', 0], 'geIcon geSprite geSprite-startopenasync', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_BLOCK, 1], 'geIcon geSprite geSprite-startblock', null, false).setAttribute('title', mxResources.get('block')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_BLOCK_THIN, 1], 'geIcon geSprite geSprite-startblockthin', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['async', 1], 'geIcon geSprite geSprite-startasync', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_OVAL, 1], 'geIcon geSprite geSprite-startoval', null, false).setAttribute('title', mxResources.get('oval')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_DIAMOND, 1], 'geIcon geSprite geSprite-startdiamond', null, false).setAttribute('title', mxResources.get('diamond')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_DIAMOND_THIN, 1], 'geIcon geSprite geSprite-startthindiamond', null, false).setAttribute('title', mxResources.get('diamondThin')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_CLASSIC, 0], 'geIcon geSprite geSprite-startclassictrans', null, false).setAttribute('title', mxResources.get('classic')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_CLASSIC_THIN, 0], 'geIcon geSprite geSprite-startclassicthintrans', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_BLOCK, 0], 'geIcon geSprite geSprite-startblocktrans', null, false).setAttribute('title', mxResources.get('block')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_BLOCK_THIN, 0], 'geIcon geSprite geSprite-startblockthintrans', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['async', 0], 'geIcon geSprite geSprite-startasynctrans', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_OVAL, 0], 'geIcon geSprite geSprite-startovaltrans', null, false).setAttribute('title', mxResources.get('oval')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_DIAMOND, 0], 'geIcon geSprite geSprite-startdiamondtrans', null, false).setAttribute('title', mxResources.get('diamond')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], [mxConstants.ARROW_DIAMOND_THIN, 0], 'geIcon geSprite geSprite-startthindiamondtrans', null, false).setAttribute('title', mxResources.get('diamondThin')); - - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['dash', 0], 'geIcon geSprite geSprite-startdash', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['cross', 0], 'geIcon geSprite geSprite-startcross', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['circlePlus', 0], 'geIcon geSprite geSprite-startcircleplus', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['circle', 1], 'geIcon geSprite geSprite-startcircle', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERone', 0], 'geIcon geSprite geSprite-starterone', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERmandOne', 0], 'geIcon geSprite geSprite-starteronetoone', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERmany', 0], 'geIcon geSprite geSprite-startermany', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERoneToMany', 0], 'geIcon geSprite geSprite-starteronetomany', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERzeroToOne', 1], 'geIcon geSprite geSprite-starteroneopt', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW, 'startFill'], ['ERzeroToMany', 1], 'geIcon geSprite geSprite-startermanyopt', null, false); - } - else - { - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_STARTARROW], [mxConstants.ARROW_BLOCK], 'geIcon geSprite geSprite-startblocktrans', null, false).setAttribute('title', mxResources.get('block')); - } - } - })); - - var lineEnd = this.editorUi.toolbar.addMenuFunctionInContainer(stylePanel2, 'geSprite-endclassic', mxResources.get('lineend'), false, mxUtils.bind(this, function(menu) - { - if (ss.style.shape == 'connector' || ss.style.shape == 'flexArrow' || ss.style.shape == 'filledEdge') - { - var item = this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.NONE, 0], 'geIcon', null, false); - item.setAttribute('title', mxResources.get('none')); - item.firstChild.firstChild.innerHTML = '' + mxUtils.htmlEntities(mxResources.get('none')) + ''; - - if (ss.style.shape == 'connector' || ss.style.shape == 'filledEdge') - { - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_CLASSIC, 1], 'geIcon geSprite geSprite-endclassic', null, false).setAttribute('title', mxResources.get('classic')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_CLASSIC_THIN, 1], 'geIcon geSprite geSprite-endclassicthin', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_OPEN, 0], 'geIcon geSprite geSprite-endopen', null, false).setAttribute('title', mxResources.get('openArrow')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_OPEN_THIN, 0], 'geIcon geSprite geSprite-endopenthin', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['openAsync', 0], 'geIcon geSprite geSprite-endopenasync', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_BLOCK, 1], 'geIcon geSprite geSprite-endblock', null, false).setAttribute('title', mxResources.get('block')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_BLOCK_THIN, 1], 'geIcon geSprite geSprite-endblockthin', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['async', 1], 'geIcon geSprite geSprite-endasync', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_OVAL, 1], 'geIcon geSprite geSprite-endoval', null, false).setAttribute('title', mxResources.get('oval')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_DIAMOND, 1], 'geIcon geSprite geSprite-enddiamond', null, false).setAttribute('title', mxResources.get('diamond')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_DIAMOND_THIN, 1], 'geIcon geSprite geSprite-endthindiamond', null, false).setAttribute('title', mxResources.get('diamondThin')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_CLASSIC, 0], 'geIcon geSprite geSprite-endclassictrans', null, false).setAttribute('title', mxResources.get('classic')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_CLASSIC_THIN, 0], 'geIcon geSprite geSprite-endclassicthintrans', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_BLOCK, 0], 'geIcon geSprite geSprite-endblocktrans', null, false).setAttribute('title', mxResources.get('block')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_BLOCK_THIN, 0], 'geIcon geSprite geSprite-endblockthintrans', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['async', 0], 'geIcon geSprite geSprite-endasynctrans', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_OVAL, 0], 'geIcon geSprite geSprite-endovaltrans', null, false).setAttribute('title', mxResources.get('oval')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_DIAMOND, 0], 'geIcon geSprite geSprite-enddiamondtrans', null, false).setAttribute('title', mxResources.get('diamond')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], [mxConstants.ARROW_DIAMOND_THIN, 0], 'geIcon geSprite geSprite-endthindiamondtrans', null, false).setAttribute('title', mxResources.get('diamondThin')); - - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['dash', 0], 'geIcon geSprite geSprite-enddash', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['cross', 0], 'geIcon geSprite geSprite-endcross', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['circlePlus', 0], 'geIcon geSprite geSprite-endcircleplus', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['circle', 1], 'geIcon geSprite geSprite-endcircle', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERone', 0], 'geIcon geSprite geSprite-enderone', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERmandOne', 0], 'geIcon geSprite geSprite-enderonetoone', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERmany', 0], 'geIcon geSprite geSprite-endermany', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERoneToMany', 0], 'geIcon geSprite geSprite-enderonetomany', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERzeroToOne', 1], 'geIcon geSprite geSprite-enderoneopt', null, false); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW, 'endFill'], ['ERzeroToMany', 1], 'geIcon geSprite geSprite-endermanyopt', null, false); - } - else - { - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_ENDARROW], [mxConstants.ARROW_BLOCK], 'geIcon geSprite geSprite-endblocktrans', null, false).setAttribute('title', mxResources.get('block')); - } - } - })); - - this.addArrow(edgeShape, 8); - this.addArrow(edgeStyle); - this.addArrow(lineStart); - this.addArrow(lineEnd); - - var symbol = this.addArrow(pattern, 9); - symbol.className = 'geIcon'; - symbol.style.width = '84px'; - - var altSymbol = this.addArrow(altPattern, 9); - altSymbol.className = 'geIcon'; - altSymbol.style.width = '22px'; - - var solid = document.createElement('div'); - solid.style.width = '85px'; - solid.style.height = '1px'; - solid.style.borderBottom = '1px solid ' + this.defaultStrokeColor; - solid.style.marginBottom = '9px'; - symbol.appendChild(solid); - - var altSolid = document.createElement('div'); - altSolid.style.width = '23px'; - altSolid.style.height = '1px'; - altSolid.style.borderBottom = '1px solid ' + this.defaultStrokeColor; - altSolid.style.marginBottom = '9px'; - altSymbol.appendChild(altSolid); - - pattern.style.height = '15px'; - altPattern.style.height = '15px'; - edgeShape.style.height = '15px'; - edgeStyle.style.height = '17px'; - lineStart.style.marginLeft = '3px'; - lineStart.style.height = '17px'; - lineEnd.style.marginLeft = '3px'; - lineEnd.style.height = '17px'; - - container.appendChild(colorPanel); - container.appendChild(altStylePanel); - container.appendChild(stylePanel); - - var arrowPanel = stylePanel.cloneNode(false); - arrowPanel.style.paddingBottom = '6px'; - arrowPanel.style.paddingTop = '4px'; - arrowPanel.style.fontWeight = 'normal'; - - var span = document.createElement('div'); - span.style.position = 'absolute'; - span.style.marginLeft = '3px'; - span.style.marginBottom = '12px'; - span.style.marginTop = '2px'; - span.style.fontWeight = 'normal'; - span.style.width = '76px'; - - mxUtils.write(span, mxResources.get('lineend')); - arrowPanel.appendChild(span); - - var endSpacingUpdate, endSizeUpdate; - var endSpacing = this.addUnitInput(arrowPanel, 'pt', 74, 33, function() - { - endSpacingUpdate.apply(this, arguments); - }); - var endSize = this.addUnitInput(arrowPanel, 'pt', 20, 33, function() - { - endSizeUpdate.apply(this, arguments); - }); - - mxUtils.br(arrowPanel); - - var spacer = document.createElement('div'); - spacer.style.height = '8px'; - arrowPanel.appendChild(spacer); - - span = span.cloneNode(false); - mxUtils.write(span, mxResources.get('linestart')); - arrowPanel.appendChild(span); - - var startSpacingUpdate, startSizeUpdate; - var startSpacing = this.addUnitInput(arrowPanel, 'pt', 74, 33, function() - { - startSpacingUpdate.apply(this, arguments); - }); - var startSize = this.addUnitInput(arrowPanel, 'pt', 20, 33, function() - { - startSizeUpdate.apply(this, arguments); - }); - - mxUtils.br(arrowPanel); - this.addLabel(arrowPanel, mxResources.get('spacing'), 74, 50); - this.addLabel(arrowPanel, mxResources.get('size'), 20, 50); - mxUtils.br(arrowPanel); - - var perimeterPanel = colorPanel.cloneNode(false); - perimeterPanel.style.fontWeight = 'normal'; - perimeterPanel.style.position = 'relative'; - perimeterPanel.style.paddingLeft = '16px' - perimeterPanel.style.marginBottom = '2px'; - perimeterPanel.style.marginTop = '6px'; - perimeterPanel.style.borderWidth = '0px'; - perimeterPanel.style.paddingBottom = '18px'; - - var span = document.createElement('div'); - span.style.position = 'absolute'; - span.style.marginLeft = '3px'; - span.style.marginBottom = '12px'; - span.style.marginTop = '1px'; - span.style.fontWeight = 'normal'; - span.style.width = '120px'; - mxUtils.write(span, mxResources.get('perimeter')); - perimeterPanel.appendChild(span); - - var perimeterUpdate; - var perimeterSpacing = this.addUnitInput(perimeterPanel, 'pt', 20, 41, function() - { - perimeterUpdate.apply(this, arguments); - }); - - if (ss.edges.length == graph.getSelectionCount()) - { - container.appendChild(stylePanel2); - - if (mxClient.IS_QUIRKS) - { - mxUtils.br(container); - mxUtils.br(container); - } - - container.appendChild(arrowPanel); - } - else if (ss.vertices.length == graph.getSelectionCount()) - { - if (mxClient.IS_QUIRKS) - { - mxUtils.br(container); - } - - container.appendChild(perimeterPanel); - } - - var listener = mxUtils.bind(this, function(sender, evt, force) - { - ss = this.format.getSelectionState(); - var color = mxUtils.getValue(ss.style, strokeKey, null); - - if (force || document.activeElement != input) - { - var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_STROKEWIDTH, 1)); - input.value = (isNaN(tmp)) ? '' : tmp + ' pt'; - } - - if (force || document.activeElement != altInput) - { - var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_STROKEWIDTH, 1)); - altInput.value = (isNaN(tmp)) ? '' : tmp + ' pt'; - } - - styleSelect.style.visibility = (ss.style.shape == 'connector' || ss.style.shape == 'filledEdge') ? '' : 'hidden'; - - if (mxUtils.getValue(ss.style, mxConstants.STYLE_CURVED, null) == '1') - { - styleSelect.value = 'curved'; - } - else if (mxUtils.getValue(ss.style, mxConstants.STYLE_ROUNDED, null) == '1') - { - styleSelect.value = 'rounded'; - } - - if (mxUtils.getValue(ss.style, mxConstants.STYLE_DASHED, null) == '1') - { - if (mxUtils.getValue(ss.style, mxConstants.STYLE_DASH_PATTERN, null) == null) - { - solid.style.borderBottom = '1px dashed ' + this.defaultStrokeColor; - } - else - { - solid.style.borderBottom = '1px dotted ' + this.defaultStrokeColor; - } - } - else - { - solid.style.borderBottom = '1px solid ' + this.defaultStrokeColor; - } - - altSolid.style.borderBottom = solid.style.borderBottom; - - // Updates toolbar icon for edge style - var edgeStyleDiv = edgeStyle.getElementsByTagName('div')[0]; - var es = mxUtils.getValue(ss.style, mxConstants.STYLE_EDGE, null); - - if (mxUtils.getValue(ss.style, mxConstants.STYLE_NOEDGESTYLE, null) == '1') - { - es = null; - } - - if (es == 'orthogonalEdgeStyle' && mxUtils.getValue(ss.style, mxConstants.STYLE_CURVED, null) == '1') - { - edgeStyleDiv.className = 'geSprite geSprite-curved'; - } - else if (es == 'straight' || es == 'none' || es == null) - { - edgeStyleDiv.className = 'geSprite geSprite-straight'; - } - else if (es == 'entityRelationEdgeStyle') - { - edgeStyleDiv.className = 'geSprite geSprite-entity'; - } - else if (es == 'elbowEdgeStyle') - { - edgeStyleDiv.className = 'geSprite ' + ((mxUtils.getValue(ss.style, - mxConstants.STYLE_ELBOW, null) == 'vertical') ? - 'geSprite-verticalelbow' : 'geSprite-horizontalelbow'); - } - else if (es == 'isometricEdgeStyle') - { - edgeStyleDiv.className = 'geSprite ' + ((mxUtils.getValue(ss.style, - mxConstants.STYLE_ELBOW, null) == 'vertical') ? - 'geSprite-verticalisometric' : 'geSprite-horizontalisometric'); - } - else - { - edgeStyleDiv.className = 'geSprite geSprite-orthogonal'; - } - - // Updates icon for edge shape - var edgeShapeDiv = edgeShape.getElementsByTagName('div')[0]; - - if (ss.style.shape == 'link') - { - edgeShapeDiv.className = 'geSprite geSprite-linkedge'; - } - else if (ss.style.shape == 'flexArrow') - { - edgeShapeDiv.className = 'geSprite geSprite-arrow'; - } - else if (ss.style.shape == 'arrow') - { - edgeShapeDiv.className = 'geSprite geSprite-simplearrow'; - } - else - { - edgeShapeDiv.className = 'geSprite geSprite-connection'; - } - - if (ss.edges.length == graph.getSelectionCount()) - { - altStylePanel.style.display = ''; - stylePanel.style.display = 'none'; - } - else - { - altStylePanel.style.display = 'none'; - stylePanel.style.display = ''; - } - - function updateArrow(marker, fill, elt, prefix) - { - var markerDiv = elt.getElementsByTagName('div')[0]; - - markerDiv.className = ui.getCssClassForMarker(prefix, ss.style.shape, marker, fill); - - if (markerDiv.className == 'geSprite geSprite-noarrow') - { - markerDiv.innerHTML = mxUtils.htmlEntities(mxResources.get('none')); - markerDiv.style.backgroundImage = 'none'; - markerDiv.style.verticalAlign = 'top'; - markerDiv.style.marginTop = '5px'; - markerDiv.style.fontSize = '10px'; - markerDiv.style.filter = 'none'; - markerDiv.style.color = this.defaultStrokeColor; - markerDiv.nextSibling.style.marginTop = '0px'; - } - - return markerDiv; - }; - - var sourceDiv = updateArrow(mxUtils.getValue(ss.style, mxConstants.STYLE_STARTARROW, null), - mxUtils.getValue(ss.style, 'startFill', '1'), lineStart, 'start'); - var targetDiv = updateArrow(mxUtils.getValue(ss.style, mxConstants.STYLE_ENDARROW, null), - mxUtils.getValue(ss.style, 'endFill', '1'), lineEnd, 'end'); - - // Special cases for markers - if (ss.style.shape == 'arrow') - { - sourceDiv.className = 'geSprite geSprite-noarrow'; - targetDiv.className = 'geSprite geSprite-endblocktrans'; - } - else if (ss.style.shape == 'link') - { - sourceDiv.className = 'geSprite geSprite-noarrow'; - targetDiv.className = 'geSprite geSprite-noarrow'; - } - - mxUtils.setOpacity(edgeStyle, (ss.style.shape == 'arrow') ? 30 : 100); - - if (ss.style.shape != 'connector' && ss.style.shape != 'flexArrow' && ss.style.shape != 'filledEdge') - { - mxUtils.setOpacity(lineStart, 30); - mxUtils.setOpacity(lineEnd, 30); - } - else - { - mxUtils.setOpacity(lineStart, 100); - mxUtils.setOpacity(lineEnd, 100); - } - - if (force || document.activeElement != startSize) - { - var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_MARKERSIZE)); - startSize.value = (isNaN(tmp)) ? '' : tmp + ' pt'; - } - - if (force || document.activeElement != startSpacing) - { - var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_SOURCE_PERIMETER_SPACING, 0)); - startSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; - } - - if (force || document.activeElement != endSize) - { - var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_ENDSIZE, mxConstants.DEFAULT_MARKERSIZE)); - endSize.value = (isNaN(tmp)) ? '' : tmp + ' pt'; - } - - if (force || document.activeElement != startSpacing) - { - var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_TARGET_PERIMETER_SPACING, 0)); - endSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; - } - - if (force || document.activeElement != perimeterSpacing) - { - var tmp = parseInt(mxUtils.getValue(ss.style, mxConstants.STYLE_PERIMETER_SPACING, 0)); - perimeterSpacing.value = (isNaN(tmp)) ? '' : tmp + ' pt'; - } - }); - - startSizeUpdate = this.installInputHandler(startSize, mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_MARKERSIZE, 0, 999, ' pt'); - startSpacingUpdate = this.installInputHandler(startSpacing, mxConstants.STYLE_SOURCE_PERIMETER_SPACING, 0, -999, 999, ' pt'); - endSizeUpdate = this.installInputHandler(endSize, mxConstants.STYLE_ENDSIZE, mxConstants.DEFAULT_MARKERSIZE, 0, 999, ' pt'); - endSpacingUpdate = this.installInputHandler(endSpacing, mxConstants.STYLE_TARGET_PERIMETER_SPACING, 0, -999, 999, ' pt'); - perimeterUpdate = this.installInputHandler(perimeterSpacing, mxConstants.STYLE_PERIMETER_SPACING, 0, 0, 999, ' pt'); - - this.addKeyHandler(input, listener); - this.addKeyHandler(startSize, listener); - this.addKeyHandler(startSpacing, listener); - this.addKeyHandler(endSize, listener); - this.addKeyHandler(endSpacing, listener); - this.addKeyHandler(perimeterSpacing, listener); - - graph.getModel().addListener(mxEvent.CHANGE, listener); - this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); - listener(); - - return container; -}; - -/** - * Adds UI for configuring line jumps. - */ -StyleFormatPanel.prototype.addLineJumps = function(container) -{ - var ss = this.format.getSelectionState(); - - if (Graph.lineJumpsEnabled && ss.edges.length > 0 && - ss.vertices.length == 0 && ss.lineJumps) - { - container.style.padding = '8px 0px 24px 18px'; - - var ui = this.editorUi; - var editor = ui.editor; - var graph = editor.graph; - - var span = document.createElement('div'); - span.style.position = 'absolute'; - span.style.fontWeight = 'bold'; - span.style.width = '80px'; - - mxUtils.write(span, mxResources.get('lineJumps')); - container.appendChild(span); - - var styleSelect = document.createElement('select'); - styleSelect.style.position = 'absolute'; - styleSelect.style.marginTop = '-2px'; - styleSelect.style.right = '76px'; - styleSelect.style.width = '62px'; - - var styles = ['none', 'arc', 'gap', 'sharp']; - - for (var i = 0; i < styles.length; i++) - { - var styleOption = document.createElement('option'); - styleOption.setAttribute('value', styles[i]); - mxUtils.write(styleOption, mxResources.get(styles[i])); - styleSelect.appendChild(styleOption); - } - - mxEvent.addListener(styleSelect, 'change', function(evt) - { - graph.getModel().beginUpdate(); - try - { - graph.setCellStyles('jumpStyle', styleSelect.value, graph.getSelectionCells()); - ui.fireEvent(new mxEventObject('styleChanged', 'keys', ['jumpStyle'], - 'values', [styleSelect.value], 'cells', graph.getSelectionCells())); - } - finally - { - graph.getModel().endUpdate(); - } - - mxEvent.consume(evt); - }); - - // Stops events from bubbling to color option event handler - mxEvent.addListener(styleSelect, 'click', function(evt) - { - mxEvent.consume(evt); - }); - - container.appendChild(styleSelect); - - var jumpSizeUpdate; - - var jumpSize = this.addUnitInput(container, 'pt', 22, 33, function() - { - jumpSizeUpdate.apply(this, arguments); - }); - - jumpSizeUpdate = this.installInputHandler(jumpSize, 'jumpSize', - Graph.defaultJumpSize, 0, 999, ' pt'); - - var listener = mxUtils.bind(this, function(sender, evt, force) - { - ss = this.format.getSelectionState(); - styleSelect.value = mxUtils.getValue(ss.style, 'jumpStyle', 'none'); - - if (force || document.activeElement != jumpSize) - { - var tmp = parseInt(mxUtils.getValue(ss.style, 'jumpSize', Graph.defaultJumpSize)); - jumpSize.value = (isNaN(tmp)) ? '' : tmp + ' pt'; - } - }); - - this.addKeyHandler(jumpSize, listener); - - graph.getModel().addListener(mxEvent.CHANGE, listener); - this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); - listener(); - } - else - { - container.style.display = 'none'; - } - - return container; -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -StyleFormatPanel.prototype.addEffects = function(div) -{ - var ui = this.editorUi; - var editor = ui.editor; - var graph = editor.graph; - var ss = this.format.getSelectionState(); - - div.style.paddingTop = '0px'; - div.style.paddingBottom = '2px'; - - var table = document.createElement('table'); - - if (mxClient.IS_QUIRKS) - { - table.style.fontSize = '1em'; - } - - table.style.width = '100%'; - table.style.fontWeight = 'bold'; - table.style.paddingRight = '20px'; - var tbody = document.createElement('tbody'); - var row = document.createElement('tr'); - row.style.padding = '0px'; - var left = document.createElement('td'); - left.style.padding = '0px'; - left.style.width = '50%'; - left.setAttribute('valign', 'top'); - - var right = left.cloneNode(true); - right.style.paddingLeft = '8px'; - row.appendChild(left); - row.appendChild(right); - tbody.appendChild(row); - table.appendChild(tbody); - div.appendChild(table); - - var current = left; - var count = 0; - - var addOption = mxUtils.bind(this, function(label, key, defaultValue) - { - var opt = this.createCellOption(label, key, defaultValue); - opt.style.width = '100%'; - current.appendChild(opt); - current = (current == left) ? right : left; - count++; - }); - - var listener = mxUtils.bind(this, function(sender, evt, force) - { - ss = this.format.getSelectionState(); - - left.innerHTML = ''; - right.innerHTML = ''; - current = left; - - if (ss.rounded) - { - addOption(mxResources.get('rounded'), mxConstants.STYLE_ROUNDED, 0); - } - - if (ss.style.shape == 'swimlane') - { - addOption(mxResources.get('divider'), 'swimlaneLine', 1); - } - - if (!ss.containsImage) - { - addOption(mxResources.get('shadow'), mxConstants.STYLE_SHADOW, 0); - } - - if (ss.glass) - { - addOption(mxResources.get('glass'), mxConstants.STYLE_GLASS, 0); - } - - if (ss.comic) - { - addOption(mxResources.get('comic'), 'comic', 0); - } - - if (count == 0) - { - div.style.display = 'none'; - } - }); - - graph.getModel().addListener(mxEvent.CHANGE, listener); - this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); - listener(); - - return div; -} - -/** - * Adds the label menu items to the given menu and parent. - */ -StyleFormatPanel.prototype.addStyleOps = function(div) -{ - div.style.paddingTop = '10px'; - div.style.paddingBottom = '10px'; - - var btn = mxUtils.button(mxResources.get('setAsDefaultStyle'), mxUtils.bind(this, function(evt) - { - this.editorUi.actions.get('setAsDefaultStyle').funct(); - })); - - btn.setAttribute('title', mxResources.get('setAsDefaultStyle') + ' (' + this.editorUi.actions.get('setAsDefaultStyle').shortcut + ')'); - btn.style.width = '202px'; - div.appendChild(btn); - - return div; -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -DiagramFormatPanel = function(format, editorUi, container) -{ - BaseFormatPanel.call(this, format, editorUi, container); - this.init(); -}; - -mxUtils.extend(DiagramFormatPanel, BaseFormatPanel); - -/** - * Switch to disable page view. - */ -DiagramFormatPanel.showPageView = true; - -/** - * Specifies if the background image option should be shown. Default is true. - */ -DiagramFormatPanel.prototype.showBackgroundImageOption = true; - -/** - * Adds the label menu items to the given menu and parent. - */ -DiagramFormatPanel.prototype.init = function() -{ - var ui = this.editorUi; - var editor = ui.editor; - var graph = editor.graph; - - this.container.appendChild(this.addView(this.createPanel())); - - if (graph.isEnabled()) - { - this.container.appendChild(this.addOptions(this.createPanel())); - this.container.appendChild(this.addPaperSize(this.createPanel())); - this.container.appendChild(this.addStyleOps(this.createPanel())); - } -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -DiagramFormatPanel.prototype.addView = function(div) -{ - var ui = this.editorUi; - var editor = ui.editor; - var graph = editor.graph; - - div.appendChild(this.createTitle(mxResources.get('view'))); - - // Grid - this.addGridOption(div); - - if (graph.isEnabled()) - { - // Page View - if (DiagramFormatPanel.showPageView) - { - div.appendChild(this.createOption(mxResources.get('pageView'), function() - { - return graph.pageVisible; - }, function(checked) - { - ui.actions.get('pageView').funct(); - }, - { - install: function(apply) - { - this.listener = function() - { - apply(graph.pageVisible); - }; - - ui.addListener('pageViewChanged', this.listener); - }, - destroy: function() - { - ui.removeListener(this.listener); - } - })); - } - - // Background - var bg = this.createColorOption(mxResources.get('background'), function() - { - return graph.background; - }, function(color) - { - var change = new ChangePageSetup(ui, color); - change.ignoreImage = true; - - graph.model.execute(change); - }, '#ffffff', - { - install: function(apply) - { - this.listener = function() - { - apply(graph.background); - }; - - ui.addListener('backgroundColorChanged', this.listener); - }, - destroy: function() - { - ui.removeListener(this.listener); - } - }); - - if (this.showBackgroundImageOption) - { - var btn = mxUtils.button(mxResources.get('image'), function(evt) - { - ui.showBackgroundImageDialog(); - mxEvent.consume(evt); - }) - - btn.style.position = 'absolute'; - btn.className = 'geColorBtn'; - btn.style.marginTop = '-4px'; - btn.style.paddingBottom = (document.documentMode == 11 || mxClient.IS_MT) ? '0px' : '2px'; - btn.style.height = '22px'; - btn.style.right = (mxClient.IS_QUIRKS) ? '52px' : '72px'; - btn.style.width = '56px'; - - bg.appendChild(btn); - } - - div.appendChild(bg); - } - - return div; -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -DiagramFormatPanel.prototype.addOptions = function(div) -{ - var ui = this.editorUi; - var editor = ui.editor; - var graph = editor.graph; - - div.appendChild(this.createTitle(mxResources.get('options'))); - - if (graph.isEnabled()) - { - // Connection arrows - div.appendChild(this.createOption(mxResources.get('connectionArrows'), function() - { - return graph.connectionArrowsEnabled; - }, function(checked) - { - ui.actions.get('connectionArrows').funct(); - }, - { - install: function(apply) - { - this.listener = function() - { - apply(graph.connectionArrowsEnabled); - }; - - ui.addListener('connectionArrowsChanged', this.listener); - }, - destroy: function() - { - ui.removeListener(this.listener); - } - })); - - // Connection points - div.appendChild(this.createOption(mxResources.get('connectionPoints'), function() - { - return graph.connectionHandler.isEnabled(); - }, function(checked) - { - ui.actions.get('connectionPoints').funct(); - }, - { - install: function(apply) - { - this.listener = function() - { - apply(graph.connectionHandler.isEnabled()); - }; - - ui.addListener('connectionPointsChanged', this.listener); - }, - destroy: function() - { - ui.removeListener(this.listener); - } - })); - - // Guides - div.appendChild(this.createOption(mxResources.get('guides'), function() - { - return graph.graphHandler.guidesEnabled; - }, function(checked) - { - ui.actions.get('guides').funct(); - }, - { - install: function(apply) - { - this.listener = function() - { - apply(graph.graphHandler.guidesEnabled); - }; - - ui.addListener('guidesEnabledChanged', this.listener); - }, - destroy: function() - { - ui.removeListener(this.listener); - } - })); - } - - return div; -}; - -/** - * - */ -DiagramFormatPanel.prototype.addGridOption = function(container) -{ - var ui = this.editorUi; - var graph = ui.editor.graph; - - var input = document.createElement('input'); - input.style.position = 'absolute'; - input.style.textAlign = 'right'; - input.style.width = '38px'; - input.value = graph.getGridSize() + ' pt'; - - var stepper = this.createStepper(input, update); - input.style.display = (graph.isGridEnabled()) ? '' : 'none'; - stepper.style.display = input.style.display; - - mxEvent.addListener(input, 'keydown', function(e) - { - if (e.keyCode == 13) - { - graph.container.focus(); - mxEvent.consume(e); - } - else if (e.keyCode == 27) - { - input.value = graph.getGridSize(); - graph.container.focus(); - mxEvent.consume(e); - } - }); - - function update(evt) - { - var value = parseInt(input.value); - value = Math.max(1, (isNaN(value)) ? 10 : value); - - if (value != graph.getGridSize()) - { - graph.setGridSize(value) - } - - input.value = value + ' pt'; - mxEvent.consume(evt); - }; - - mxEvent.addListener(input, 'blur', update); - mxEvent.addListener(input, 'change', update); - - if (mxClient.IS_SVG) - { - input.style.marginTop = '-2px'; - input.style.right = '84px'; - stepper.style.marginTop = '-16px'; - stepper.style.right = '72px'; - - var panel = this.createColorOption(mxResources.get('grid'), function() - { - var color = graph.view.gridColor; - - return (graph.isGridEnabled()) ? color : null; - }, function(color) - { - if (color == mxConstants.NONE) - { - graph.setGridEnabled(false); - } - else - { - graph.setGridEnabled(true); - ui.setGridColor(color); - } - - input.style.display = (graph.isGridEnabled()) ? '' : 'none'; - stepper.style.display = input.style.display; - ui.fireEvent(new mxEventObject('gridEnabledChanged')); - }, '#e0e0e0', - { - install: function(apply) - { - this.listener = function() - { - apply((graph.isGridEnabled()) ? graph.view.gridColor : null); - }; - - ui.addListener('gridColorChanged', this.listener); - ui.addListener('gridEnabledChanged', this.listener); - }, - destroy: function() - { - ui.removeListener(this.listener); - } - }); - - panel.appendChild(input); - panel.appendChild(stepper); - container.appendChild(panel); - } - else - { - input.style.marginTop = '2px'; - input.style.right = '32px'; - stepper.style.marginTop = '2px'; - stepper.style.right = '20px'; - - container.appendChild(input); - container.appendChild(stepper); - - container.appendChild(this.createOption(mxResources.get('grid'), function() - { - return graph.isGridEnabled(); - }, function(checked) - { - graph.setGridEnabled(checked); - - if (graph.isGridEnabled()) - { - graph.view.gridColor = '#e0e0e0'; - } - - ui.fireEvent(new mxEventObject('gridEnabledChanged')); - }, - { - install: function(apply) - { - this.listener = function() - { - input.style.display = (graph.isGridEnabled()) ? '' : 'none'; - stepper.style.display = input.style.display; - - apply(graph.isGridEnabled()); - }; - - ui.addListener('gridEnabledChanged', this.listener); - }, - destroy: function() - { - ui.removeListener(this.listener); - } - })); - } -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -DiagramFormatPanel.prototype.addDocumentProperties = function(div) -{ - // Hook for subclassers - var ui = this.editorUi; - var editor = ui.editor; - var graph = editor.graph; - - div.appendChild(this.createTitle(mxResources.get('options'))); - - return div; -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -DiagramFormatPanel.prototype.addPaperSize = function(div) -{ - var ui = this.editorUi; - var editor = ui.editor; - var graph = editor.graph; - - div.appendChild(this.createTitle(mxResources.get('paperSize'))); - - var accessor = PageSetupDialog.addPageFormatPanel(div, 'formatpanel', graph.pageFormat, function(pageFormat) - { - if (graph.pageFormat == null || graph.pageFormat.width != pageFormat.width || - graph.pageFormat.height != pageFormat.height) - { - var change = new ChangePageSetup(ui, null, null, pageFormat); - change.ignoreColor = true; - change.ignoreImage = true; - - graph.model.execute(change); - } - }); - - this.addKeyHandler(accessor.widthInput, function() - { - accessor.set(graph.pageFormat); - }); - this.addKeyHandler(accessor.heightInput, function() - { - accessor.set(graph.pageFormat); - }); - - var listener = function() - { - accessor.set(graph.pageFormat); - }; - - ui.addListener('pageFormatChanged', listener); - this.listeners.push({destroy: function() { ui.removeListener(listener); }}); - - graph.getModel().addListener(mxEvent.CHANGE, listener); - this.listeners.push({destroy: function() { graph.getModel().removeListener(listener); }}); - - return div; -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -DiagramFormatPanel.prototype.addStyleOps = function(div) -{ - var btn = mxUtils.button(mxResources.get('editData'), mxUtils.bind(this, function(evt) - { - this.editorUi.actions.get('editData').funct(); - })); - - btn.setAttribute('title', mxResources.get('editData') + ' (' + this.editorUi.actions.get('editData').shortcut + ')'); - btn.style.width = '202px'; - btn.style.marginBottom = '2px'; - div.appendChild(btn); - - mxUtils.br(div); - - btn = mxUtils.button(mxResources.get('clearDefaultStyle'), mxUtils.bind(this, function(evt) - { - this.editorUi.actions.get('clearDefaultStyle').funct(); - })); - - btn.setAttribute('title', mxResources.get('clearDefaultStyle') + ' (' + this.editorUi.actions.get('clearDefaultStyle').shortcut + ')'); - btn.style.width = '202px'; - div.appendChild(btn); - - return div; -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -DiagramFormatPanel.prototype.destroy = function() -{ - BaseFormatPanel.prototype.destroy.apply(this, arguments); - - if (this.gridEnabledListener) - { - this.editorUi.removeListener(this.gridEnabledListener); - this.gridEnabledListener = null; - } -}; diff --git a/media/grapheditor/js/Graph.js b/media/grapheditor/js/Graph.js deleted file mode 100644 index 1241b9dda5..0000000000 --- a/media/grapheditor/js/Graph.js +++ /dev/null @@ -1,8440 +0,0 @@ -/** - * Copyright (c) 2006-2012, JGraph Ltd - */ -// Workaround for allowing target="_blank" in HTML sanitizer -// see https://code.google.com/p/google-caja/issues/detail?can=2&q=&colspec=ID%20Type%20Status%20Priority%20Owner%20Summary&groupby=&sort=&id=1296 -if (typeof html4 !== 'undefined') -{ - html4.ATTRIBS["a::target"] = 0; - html4.ATTRIBS["source::src"] = 0; - html4.ATTRIBS["video::src"] = 0; - // Would be nice for tooltips but probably a security risk... - //html4.ATTRIBS["video::autoplay"] = 0; - //html4.ATTRIBS["video::autobuffer"] = 0; -} - -/** - * Sets global constants. - */ -// Changes default colors -mxConstants.SHADOW_OPACITY = 0.25; -mxConstants.SHADOWCOLOR = '#000000'; -mxConstants.VML_SHADOWCOLOR = '#d0d0d0'; -mxGraph.prototype.pageBreakColor = '#c0c0c0'; -mxGraph.prototype.pageScale = 1; - -// Letter page format is default in US, Canada and Mexico -(function() -{ - try - { - if (navigator != null && navigator.language != null) - { - var lang = navigator.language.toLowerCase(); - mxGraph.prototype.pageFormat = (lang === 'en-us' || lang === 'en-ca' || lang === 'es-mx') ? - mxConstants.PAGE_FORMAT_LETTER_PORTRAIT : mxConstants.PAGE_FORMAT_A4_PORTRAIT; - } - } - catch (e) - { - // ignore - } -})(); - -// Matches label positions of mxGraph 1.x -mxText.prototype.baseSpacingTop = 5; -mxText.prototype.baseSpacingBottom = 1; - -// Keeps edges between relative child cells inside parent -mxGraphModel.prototype.ignoreRelativeEdgeParent = false; - -// Defines grid properties -mxGraphView.prototype.gridImage = (mxClient.IS_SVG) ? 'data:image/gif;base64,R0lGODlhCgAKAJEAAAAAAP///8zMzP///yH5BAEAAAMALAAAAAAKAAoAAAIJ1I6py+0Po2wFADs=' : - IMAGE_PATH + '/grid.gif'; -mxGraphView.prototype.gridSteps = 4; -mxGraphView.prototype.minGridSize = 4; - -// UrlParams is null in embed mode -mxGraphView.prototype.gridColor = '#e0e0e0'; - -// Alternative text for unsupported foreignObjects -mxSvgCanvas2D.prototype.foAltText = '[Not supported by viewer]'; - -/** - * Constructs a new graph instance. Note that the constructor does not take a - * container because the graph instance is needed for creating the UI, which - * in turn will create the container for the graph. Hence, the container is - * assigned later in EditorUi. - */ -/** - * Defines graph class. - */ -Graph = function(container, model, renderHint, stylesheet, themes) -{ - mxGraph.call(this, container, model, renderHint, stylesheet); - - this.themes = themes || this.defaultThemes; - this.currentEdgeStyle = mxUtils.clone(this.defaultEdgeStyle); - this.currentVertexStyle = mxUtils.clone(this.defaultVertexStyle); - - // Sets the base domain URL and domain path URL for relative links. - var b = this.baseUrl; - var p = b.indexOf('//'); - this.domainUrl = ''; - this.domainPathUrl = ''; - - if (p > 0) - { - var d = b.indexOf('/', p + 2); - - if (d > 0) - { - this.domainUrl = b.substring(0, d); - } - - d = b.lastIndexOf('/'); - - if (d > 0) - { - this.domainPathUrl = b.substring(0, d + 1); - } - } - - // Adds support for HTML labels via style. Note: Currently, only the Java - // backend supports HTML labels but CSS support is limited to the following: - // http://docs.oracle.com/javase/6/docs/api/index.html?javax/swing/text/html/CSS.html - // TODO: Wrap should not affect isHtmlLabel output (should be handled later) - this.isHtmlLabel = function(cell) - { - var state = this.view.getState(cell); - var style = (state != null) ? state.style : this.getCellStyle(cell); - - return style['html'] == '1' || style[mxConstants.STYLE_WHITE_SPACE] == 'wrap'; - }; - - // Implements a listener for hover and click handling on edges - if (this.edgeMode) - { - var start = { - point: null, - event: null, - state: null, - handle: null, - selected: false - }; - - // Uses this event to process mouseDown to check the selection state before it is changed - this.addListener(mxEvent.FIRE_MOUSE_EVENT, mxUtils.bind(this, function(sender, evt) - { - if (evt.getProperty('eventName') == 'mouseDown' && this.isEnabled()) - { - var me = evt.getProperty('event'); - - if (!mxEvent.isControlDown(me.getEvent()) && !mxEvent.isShiftDown(me.getEvent())) - { - var state = me.getState(); - - if (state != null) - { - // Checks if state was removed in call to stopEditing above - if (this.model.isEdge(state.cell)) - { - start.point = new mxPoint(me.getGraphX(), me.getGraphY()); - start.selected = this.isCellSelected(state.cell); - start.state = state; - start.event = me; - - if (state.text != null && state.text.boundingBox != null && - mxUtils.contains(state.text.boundingBox, me.getGraphX(), me.getGraphY())) - { - start.handle = mxEvent.LABEL_HANDLE; - } - else - { - var handler = this.selectionCellsHandler.getHandler(state.cell); - - if (handler != null && handler.bends != null && handler.bends.length > 0) - { - start.handle = handler.getHandleForEvent(me); - } - } - } - } - } - } - })); - - var mouseDown = null; - - this.addMouseListener( - { - mouseDown: function(sender, me) {}, - mouseMove: mxUtils.bind(this, function(sender, me) - { - // Checks if any other handler is active - var handlerMap = this.selectionCellsHandler.handlers.map; - - for (var key in handlerMap) - { - if (handlerMap[key].index != null) - { - return; - } - } - - if (this.isEnabled() && !this.panningHandler.isActive() && !mxEvent.isControlDown(me.getEvent()) && - !mxEvent.isShiftDown(me.getEvent()) && !mxEvent.isAltDown(me.getEvent())) - { - var tol = this.tolerance; - - if (start.point != null && start.state != null && start.event != null) - { - var state = start.state; - - if (Math.abs(start.point.x - me.getGraphX()) > tol || - Math.abs(start.point.y - me.getGraphY()) > tol) - { - // Lazy selection for edges inside groups - if (!this.isCellSelected(state.cell)) - { - this.setSelectionCell(state.cell); - } - - var handler = this.selectionCellsHandler.getHandler(state.cell); - - if (handler != null && handler.bends != null && handler.bends.length > 0) - { - var handle = handler.getHandleForEvent(start.event); - var edgeStyle = this.view.getEdgeStyle(state); - var entity = edgeStyle == mxEdgeStyle.EntityRelation; - - // Handles special case where label was clicked on unselected edge in which - // case the label will be moved regardless of the handle that is returned - if (!start.selected && start.handle == mxEvent.LABEL_HANDLE) - { - handle = start.handle; - } - - if (!entity || handle == 0 || handle == handler.bends.length - 1 || handle == mxEvent.LABEL_HANDLE) - { - // Source or target handle or connected for direct handle access or orthogonal line - // with just two points where the central handle is moved regardless of mouse position - if (handle == mxEvent.LABEL_HANDLE || handle == 0 || state.visibleSourceState != null || - handle == handler.bends.length - 1 || state.visibleTargetState != null) - { - if (!entity && handle != mxEvent.LABEL_HANDLE) - { - var pts = state.absolutePoints; - - // Default case where handles are at corner points handles - // drag of corner as drag of existing point - if (pts != null && ((edgeStyle == null && handle == null) || - edgeStyle == mxEdgeStyle.OrthConnector)) - { - // Does not use handles if they were not initially visible - handle = start.handle; - - if (handle == null) - { - var box = new mxRectangle(start.point.x, start.point.y); - box.grow(mxEdgeHandler.prototype.handleImage.width / 2); - - if (mxUtils.contains(box, pts[0].x, pts[0].y)) - { - // Moves source terminal handle - handle = 0; - } - else if (mxUtils.contains(box, pts[pts.length - 1].x, pts[pts.length - 1].y)) - { - // Moves target terminal handle - handle = handler.bends.length - 1; - } - else - { - // Checks if edge has no bends - var nobends = edgeStyle != null && (pts.length == 2 || (pts.length == 3 && - ((Math.round(pts[0].x - pts[1].x) == 0 && Math.round(pts[1].x - pts[2].x) == 0) || - (Math.round(pts[0].y - pts[1].y) == 0 && Math.round(pts[1].y - pts[2].y) == 0)))); - - if (nobends) - { - // Moves central handle for straight orthogonal edges - handle = 2; - } - else - { - // Finds and moves vertical or horizontal segment - handle = mxUtils.findNearestSegment(state, start.point.x, start.point.y); - - // Converts segment to virtual handle index - if (edgeStyle == null) - { - handle = mxEvent.VIRTUAL_HANDLE - handle; - } - // Maps segment to handle - else - { - handle += 1; - } - } - } - } - } - - // Creates a new waypoint and starts moving it - if (handle == null) - { - handle = mxEvent.VIRTUAL_HANDLE; - } - } - - handler.start(me.getGraphX(), me.getGraphX(), handle); - start.state = null; - start.event = null; - start.point = null; - start.handle = null; - start.selected = false; - me.consume(); - - // Removes preview rectangle in graph handler - this.graphHandler.reset(); - } - } - else if (entity && (state.visibleSourceState != null || state.visibleTargetState != null)) - { - // Disables moves on entity to make it consistent - this.graphHandler.reset(); - me.consume(); - } - } - } - } - else - { - // Updates cursor for unselected edges under the mouse - var state = me.getState(); - - if (state != null) - { - // Checks if state was removed in call to stopEditing above - if (this.model.isEdge(state.cell)) - { - var cursor = null; - var pts = state.absolutePoints; - - if (pts != null) - { - var box = new mxRectangle(me.getGraphX(), me.getGraphY()); - box.grow(mxEdgeHandler.prototype.handleImage.width / 2); - - if (state.text != null && state.text.boundingBox != null && - mxUtils.contains(state.text.boundingBox, me.getGraphX(), me.getGraphY())) - { - cursor = 'move'; - } - else if (mxUtils.contains(box, pts[0].x, pts[0].y) || - mxUtils.contains(box, pts[pts.length - 1].x, pts[pts.length - 1].y)) - { - cursor = 'pointer'; - } - else if (state.visibleSourceState != null || state.visibleTargetState != null) - { - // Moving is not allowed for entity relation but still indicate hover state - var tmp = this.view.getEdgeStyle(state); - cursor = 'crosshair'; - - if (tmp != mxEdgeStyle.EntityRelation && this.isOrthogonal(state)) - { - var idx = mxUtils.findNearestSegment(state, me.getGraphX(), me.getGraphY()); - - if (idx < pts.length - 1 && idx >= 0) - { - cursor = (Math.round(pts[idx].x - pts[idx + 1].x) == 0) ? - 'col-resize' : 'row-resize'; - } - } - } - } - - if (cursor != null) - { - state.setCursor(cursor); - } - } - } - } - } - }), - mouseUp: mxUtils.bind(this, function(sender, me) - { - start.state = null; - start.event = null; - start.point = null; - start.handle = null; - }) - }); - } - - // HTML entities are displayed as plain text in wrapped plain text labels - this.cellRenderer.getLabelValue = function(state) - { - var result = mxCellRenderer.prototype.getLabelValue.apply(this, arguments); - - if (state.view.graph.isHtmlLabel(state.cell)) - { - if (state.style['html'] != 1) - { - result = mxUtils.htmlEntities(result, false); - } - else - { - result = state.view.graph.sanitizeHtml(result); - } - } - - return result; - }; - - // All code below not available and not needed in embed mode - if (typeof mxVertexHandler !== 'undefined') - { - this.setConnectable(true); - this.setDropEnabled(true); - this.setPanning(true); - this.setTooltips(true); - this.setAllowLoops(true); - this.allowAutoPanning = true; - this.resetEdgesOnConnect = false; - this.constrainChildren = false; - this.constrainRelativeChildren = true; - - // Do not scroll after moving cells - this.graphHandler.scrollOnMove = false; - this.graphHandler.scaleGrid = true; - - // Disables cloning of connection sources by default - this.connectionHandler.setCreateTarget(false); - this.connectionHandler.insertBeforeSource = true; - - // Disables built-in connection starts - this.connectionHandler.isValidSource = function(cell, me) - { - return false; - }; - - // Sets the style to be used when an elbow edge is double clicked - this.alternateEdgeStyle = 'vertical'; - - if (stylesheet == null) - { - this.loadStylesheet(); - } - - // Adds page centers to the guides for moving cells - var graphHandlerGetGuideStates = this.graphHandler.getGuideStates; - this.graphHandler.getGuideStates = function() - { - var result = graphHandlerGetGuideStates.apply(this, arguments); - - // Create virtual cell state for page centers - if (this.graph.pageVisible) - { - var guides = []; - - var pf = this.graph.pageFormat; - var ps = this.graph.pageScale; - var pw = pf.width * ps; - var ph = pf.height * ps; - var t = this.graph.view.translate; - var s = this.graph.view.scale; - - var layout = this.graph.getPageLayout(); - - for (var i = 0; i < layout.width; i++) - { - guides.push(new mxRectangle(((layout.x + i) * pw + t.x) * s, - (layout.y * ph + t.y) * s, pw * s, ph * s)); - } - - for (var j = 0; j < layout.height; j++) - { - guides.push(new mxRectangle((layout.x * pw + t.x) * s, - ((layout.y + j) * ph + t.y) * s, pw * s, ph * s)); - } - - // Page center guides have predence over normal guides - result = guides.concat(result); - } - - return result; - }; - - // Overrides zIndex for dragElement - mxDragSource.prototype.dragElementZIndex = mxPopupMenu.prototype.zIndex; - - // Overrides color for virtual guides for page centers - mxGuide.prototype.getGuideColor = function(state, horizontal) - { - return (state.cell == null) ? '#ffa500' /* orange */ : mxConstants.GUIDE_COLOR; - }; - - // Changes color of move preview for black backgrounds - this.graphHandler.createPreviewShape = function(bounds) - { - this.previewColor = (this.graph.background == '#000000') ? '#ffffff' : mxGraphHandler.prototype.previewColor; - - return mxGraphHandler.prototype.createPreviewShape.apply(this, arguments); - }; - - // Handles parts of cells by checking if part=1 is in the style and returning the parent - // if the parent is not already in the list of cells. container style is used to disable - // step into swimlanes and dropTarget style is used to disable acting as a drop target. - // LATER: Handle recursive parts - this.graphHandler.getCells = function(initialCell) - { - var cells = mxGraphHandler.prototype.getCells.apply(this, arguments); - var newCells = []; - - for (var i = 0; i < cells.length; i++) - { - var state = this.graph.view.getState(cells[i]); - var style = (state != null) ? state.style : this.graph.getCellStyle(cells[i]); - - if (mxUtils.getValue(style, 'part', '0') == '1') - { - var parent = this.graph.model.getParent(cells[i]); - - if (this.graph.model.isVertex(parent) && mxUtils.indexOf(cells, parent) < 0) - { - newCells.push(parent); - } - } - else - { - newCells.push(cells[i]); - } - } - - return newCells; - }; - - // Handles parts of cells when cloning the source for new connections - this.connectionHandler.createTargetVertex = function(evt, source) - { - var state = this.graph.view.getState(source); - var style = (state != null) ? state.style : this.graph.getCellStyle(source); - - if (mxUtils.getValue(style, 'part', false)) - { - var parent = this.graph.model.getParent(source); - - if (this.graph.model.isVertex(parent)) - { - source = parent; - } - } - - return mxConnectionHandler.prototype.createTargetVertex.apply(this, arguments); - }; - - var rubberband = new mxRubberband(this); - - this.getRubberband = function() - { - return rubberband; - }; - - // Timer-based activation of outline connect in connection handler - var startTime = new Date().getTime(); - var timeOnTarget = 0; - - var connectionHandlerMouseMove = this.connectionHandler.mouseMove; - - this.connectionHandler.mouseMove = function() - { - var prev = this.currentState; - connectionHandlerMouseMove.apply(this, arguments); - - if (prev != this.currentState) - { - startTime = new Date().getTime(); - timeOnTarget = 0; - } - else - { - timeOnTarget = new Date().getTime() - startTime; - } - }; - - // Activates outline connect after 1500ms with touch event or if alt is pressed inside the shape - // outlineConnect=0 is a custom style that means do not connect to strokes inside the shape, - // or in other words, connect to the shape's perimeter if the highlight is under the mouse - // (the name is because the highlight, including all strokes, is called outline in the code) - var connectionHandleIsOutlineConnectEvent = this.connectionHandler.isOutlineConnectEvent; - - this.connectionHandler.isOutlineConnectEvent = function(me) - { - return (this.currentState != null && me.getState() == this.currentState && timeOnTarget > 2000) || - ((this.currentState == null || mxUtils.getValue(this.currentState.style, 'outlineConnect', '1') != '0') && - connectionHandleIsOutlineConnectEvent.apply(this, arguments)); - }; - - // Adds shift+click to toggle selection state - var isToggleEvent = this.isToggleEvent; - this.isToggleEvent = function(evt) - { - return isToggleEvent.apply(this, arguments) || mxEvent.isShiftDown(evt); - }; - - // Workaround for Firefox where first mouse down is received - // after tap and hold if scrollbars are visible, which means - // start rubberband immediately if no cell is under mouse. - var isForceRubberBandEvent = rubberband.isForceRubberbandEvent; - rubberband.isForceRubberbandEvent = function(me) - { - return isForceRubberBandEvent.apply(this, arguments) || - (mxUtils.hasScrollbars(this.graph.container) && mxClient.IS_FF && - mxClient.IS_WIN && me.getState() == null && mxEvent.isTouchEvent(me.getEvent())); - }; - - // Shows hand cursor while panning - var prevCursor = null; - - this.panningHandler.addListener(mxEvent.PAN_START, mxUtils.bind(this, function() - { - if (this.isEnabled()) - { - prevCursor = this.container.style.cursor; - this.container.style.cursor = 'move'; - } - })); - - this.panningHandler.addListener(mxEvent.PAN_END, mxUtils.bind(this, function() - { - if (this.isEnabled()) - { - this.container.style.cursor = prevCursor; - } - })); - - this.popupMenuHandler.autoExpand = true; - - this.popupMenuHandler.isSelectOnPopup = function(me) - { - return mxEvent.isMouseEvent(me.getEvent()); - }; - - // Handles links if graph is read-only or cell is locked - var click = this.click; - this.click = function(me) - { - var locked = me.state == null && me.sourceState != null && this.isCellLocked(me.sourceState.cell); - - if ((!this.isEnabled() || locked) && !me.isConsumed()) - { - var cell = (locked) ? me.sourceState.cell : me.getCell(); - - if (cell != null) - { - var link = this.getLinkForCell(cell); - - if (link != null) - { - if (this.isCustomLink(link)) - { - this.customLinkClicked(link); - } - else - { - this.openLink(link); - } - } - } - } - else - { - return click.apply(this, arguments); - } - }; - - // Redirects tooltips for locked cells - this.tooltipHandler.getStateForEvent = function(me) - { - return me.sourceState; - }; - - // Redirects cursor for locked cells - var getCursorForMouseEvent = this.getCursorForMouseEvent; - this.getCursorForMouseEvent = function(me) - { - var locked = me.state == null && me.sourceState != null && this.isCellLocked(me.sourceState.cell); - - return this.getCursorForCell((locked) ? me.sourceState.cell : me.getCell()); - }; - - // Shows pointer cursor for clickable cells with links - // ie. if the graph is disabled and cells cannot be selected - var getCursorForCell = this.getCursorForCell; - this.getCursorForCell = function(cell) - { - if (!this.isEnabled() || this.isCellLocked(cell)) - { - var link = this.getLinkForCell(cell); - - if (link != null) - { - return 'pointer'; - } - else if (this.isCellLocked(cell)) - { - return 'default'; - } - } - - return getCursorForCell.apply(this, arguments); - }; - - // Changes rubberband selection to be recursive - this.selectRegion = function(rect, evt) - { - var cells = this.getAllCells(rect.x, rect.y, rect.width, rect.height); - this.selectCellsForEvent(cells, evt); - - return cells; - }; - - // Recursive implementation for rubberband selection - this.getAllCells = function(x, y, width, height, parent, result) - { - result = (result != null) ? result : []; - - if (width > 0 || height > 0) - { - var model = this.getModel(); - var right = x + width; - var bottom = y + height; - - if (parent == null) - { - parent = this.getCurrentRoot(); - - if (parent == null) - { - parent = model.getRoot(); - } - } - - if (parent != null) - { - var childCount = model.getChildCount(parent); - - for (var i = 0; i < childCount; i++) - { - var cell = model.getChildAt(parent, i); - var state = this.view.getState(cell); - - if (state != null && this.isCellVisible(cell) && mxUtils.getValue(state.style, 'locked', '0') != '1') - { - var deg = mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION) || 0; - var box = state; - - if (deg != 0) - { - box = mxUtils.getBoundingBox(box, deg); - } - - if ((model.isEdge(cell) || model.isVertex(cell)) && - box.x >= x && box.y + box.height <= bottom && - box.y >= y && box.x + box.width <= right) - { - result.push(cell); - } - - this.getAllCells(x, y, width, height, cell, result); - } - } - } - } - - return result; - }; - - // Never removes cells from parents that are being moved - var graphHandlerShouldRemoveCellsFromParent = this.graphHandler.shouldRemoveCellsFromParent; - this.graphHandler.shouldRemoveCellsFromParent = function(parent, cells, evt) - { - if (this.graph.isCellSelected(parent)) - { - return false; - } - - return graphHandlerShouldRemoveCellsFromParent.apply(this, arguments); - }; - - // Unlocks all cells - this.isCellLocked = function(cell) - { - var pState = this.view.getState(cell); - - while (pState != null) - { - if (mxUtils.getValue(pState.style, 'locked', '0') == '1') - { - return true; - } - - pState = this.view.getState(this.model.getParent(pState.cell)); - } - - return false; - }; - - var tapAndHoldSelection = null; - - // Uses this event to process mouseDown to check the selection state before it is changed - this.addListener(mxEvent.FIRE_MOUSE_EVENT, mxUtils.bind(this, function(sender, evt) - { - if (evt.getProperty('eventName') == 'mouseDown') - { - var me = evt.getProperty('event'); - var state = me.getState(); - - if (state != null && !this.isSelectionEmpty() && !this.isCellSelected(state.cell)) - { - tapAndHoldSelection = this.getSelectionCells(); - } - else - { - tapAndHoldSelection = null; - } - } - })); - - // Tap and hold on background starts rubberband for multiple selected - // cells the cell associated with the event is deselected - this.addListener(mxEvent.TAP_AND_HOLD, mxUtils.bind(this, function(sender, evt) - { - if (!mxEvent.isMultiTouchEvent(evt)) - { - var me = evt.getProperty('event'); - var cell = evt.getProperty('cell'); - - if (cell == null) - { - var pt = mxUtils.convertPoint(this.container, - mxEvent.getClientX(me), mxEvent.getClientY(me)); - rubberband.start(pt.x, pt.y); - } - else if (tapAndHoldSelection != null) - { - this.addSelectionCells(tapAndHoldSelection); - } - else if (this.getSelectionCount() > 1 && this.isCellSelected(cell)) - { - this.removeSelectionCell(cell); - } - - // Blocks further processing of the event - tapAndHoldSelection = null; - evt.consume(); - } - })); - - // On connect the target is selected and we clone the cell of the preview edge for insert - this.connectionHandler.selectCells = function(edge, target) - { - this.graph.setSelectionCell(target || edge); - }; - - // Shows connection points only if cell not selected - this.connectionHandler.constraintHandler.isStateIgnored = function(state, source) - { - return source && state.view.graph.isCellSelected(state.cell); - }; - - // Updates constraint handler if the selection changes - this.selectionModel.addListener(mxEvent.CHANGE, mxUtils.bind(this, function() - { - var ch = this.connectionHandler.constraintHandler; - - if (ch.currentFocus != null && ch.isStateIgnored(ch.currentFocus, true)) - { - ch.currentFocus = null; - ch.constraints = null; - ch.destroyIcons(); - } - - ch.destroyFocusHighlight(); - })); - - // Initializes touch interface - if (Graph.touchStyle) - { - this.initTouch(); - } - - /** - * Adds locking - */ - var graphUpdateMouseEvent = this.updateMouseEvent; - this.updateMouseEvent = function(me) - { - me = graphUpdateMouseEvent.apply(this, arguments); - - if (me.state != null && this.isCellLocked(me.getCell())) - { - me.state = null; - } - - return me; - }; - } - - //Create a unique offset object for each graph instance. - this.currentTranslate = new mxPoint(0, 0); -}; - -/** - * Specifies if the touch UI should be used (cannot detect touch in FF so always on for Windows/Linux) - */ -Graph.touchStyle = mxClient.IS_TOUCH || (mxClient.IS_FF && mxClient.IS_WIN) || navigator.maxTouchPoints > 0 || - navigator.msMaxTouchPoints > 0 || window.urlParams == null || urlParams['touch'] == '1'; - -/** - * Shortcut for capability check. - */ -Graph.fileSupport = window.File != null && window.FileReader != null && window.FileList != null && - (window.urlParams == null || urlParams['filesupport'] != '0'); - -/** - * Default size for line jumps. - */ -Graph.lineJumpsEnabled = true; - -/** - * Default size for line jumps. - */ -Graph.defaultJumpSize = 6; - -/** - * Helper function (requires atob). - */ -Graph.createSvgImage = function(w, h, data) -{ - var tmp = unescape(encodeURIComponent( - '' + - '' + data + '')); - - return new mxImage('data:image/svg+xml;base64,' + ((window.btoa) ? btoa(tmp) : Base64.encode(tmp, true)), w, h) -}; - -/** - * Graph inherits from mxGraph. - */ -mxUtils.extend(Graph, mxGraph); - -/** - * Allows all values in fit. - */ -Graph.prototype.minFitScale = null; - -/** - * Allows all values in fit. - */ -Graph.prototype.maxFitScale = null; - -/** - * Sets the policy for links. Possible values are "self" to replace any framesets, - * "blank" to load the URL in and "auto" (default). - */ -Graph.prototype.linkPolicy = (urlParams['target'] == 'frame') ? 'blank' : (urlParams['target'] || 'auto'); - -/** - * Target for links that open in a new window. Default is _blank. - */ -Graph.prototype.linkTarget = (urlParams['target'] == 'frame') ? '_self' : '_blank'; - -/** - * Scrollbars are enabled on non-touch devices (not including Firefox because touch events - * cannot be detected in Firefox, see above). - */ -Graph.prototype.defaultScrollbars = !mxClient.IS_IOS; - -/** - * Specifies if the page should be visible for new files. Default is true. - */ -Graph.prototype.defaultPageVisible = true; - -/** - * Specifies if the app should run in chromeless mode. Default is false. - * This default is only used if the contructor argument is null. - */ -Graph.prototype.lightbox = false; - -/** - * - */ -Graph.prototype.defaultPageBackgroundColor = '#ffffff'; - -/** - * - */ -Graph.prototype.defaultPageBorderColor = '#ffffff'; - -/** - * - */ -Graph.prototype.defaultGraphBackground = '#ffffff'; - -/** - * Specifies the size of the size for "tiles" to be used for a graph with - * scrollbars but no visible background page. A good value is large - * enough to reduce the number of repaints that is caused for auto- - * translation, which depends on this value, and small enough to give - * a small empty buffer around the graph. Default is 400x400. - */ -Graph.prototype.scrollTileSize = new mxRectangle(0, 0, 400, 400); - -/** - * Overrides the background color and paints a transparent background. - */ -Graph.prototype.transparentBackground = true; - -/** - * Sets the default target for all links in cells. - */ -Graph.prototype.defaultEdgeLength = 80; - -/** - * Disables move of bends/segments without selecting. - */ -Graph.prototype.edgeMode = false; - -/** - * Allows all values in fit. - */ -Graph.prototype.connectionArrowsEnabled = true; - -/** - * Specifies the regular expression for matching placeholders. - */ -Graph.prototype.placeholderPattern = new RegExp('%(date\{.*\}|[^%^\{^\}]+)%', 'g'); - -/** - * Specifies the regular expression for matching placeholders. - */ -Graph.prototype.absoluteUrlPattern = new RegExp('^(?:[a-z]+:)?//', 'i'); - -/** - * Specifies the default name for the theme. Default is 'default'. - */ -Graph.prototype.defaultThemeName = 'default'; - -/** - * Specifies the default name for the theme. Default is 'default'. - */ -Graph.prototype.defaultThemes = {}; - -/** - * Base URL for relative links. - */ -Graph.prototype.baseUrl = (urlParams['base'] != null) ? - decodeURIComponent(urlParams['base']) : - (((window != window.top) ? document.referrer : - document.location.toString()).split('#')[0]); - -/** - * Specifies if the label should be edited after an insert. - */ -Graph.prototype.editAfterInsert = false; - -/** - * Defines the built-in properties to be ignored in tooltips. - */ -Graph.prototype.builtInProperties = ['label', 'tooltip', 'placeholders', 'placeholder']; - -/** - * Installs child layout styles. - */ -Graph.prototype.init = function(container) -{ - mxGraph.prototype.init.apply(this, arguments); - - // Intercepts links with no target attribute and opens in new window - this.cellRenderer.initializeLabel = function(state, shape) - { - mxCellRenderer.prototype.initializeLabel.apply(this, arguments); - - // Checks tolerance for clicks on links - var tol = state.view.graph.tolerance; - var handleClick = true; - var first = null; - - var down = mxUtils.bind(this, function(evt) - { - handleClick = true; - first = new mxPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt)); - }); - - var move = mxUtils.bind(this, function(evt) - { - handleClick = handleClick && first != null && - Math.abs(first.x - mxEvent.getClientX(evt)) < tol && - Math.abs(first.y - mxEvent.getClientY(evt)) < tol; - }); - - var up = mxUtils.bind(this, function(evt) - { - if (handleClick) - { - var elt = mxEvent.getSource(evt) - - while (elt != null && elt != shape.node) - { - if (elt.nodeName.toLowerCase() == 'a') - { - state.view.graph.labelLinkClicked(state, elt, evt); - break; - } - - elt = elt.parentNode; - } - } - }); - - mxEvent.addGestureListeners(shape.node, down, move, up); - mxEvent.addListener(shape.node, 'click', function(evt) - { - mxEvent.consume(evt); - }); - }; - - this.initLayoutManager(); -}; - -/** - * Implements zoom and offset via CSS transforms. This is currently only used - * in read-only as there are fewer issues with the mxCellState not being scaled - * and translated. - * - * KNOWN ISSUES TO FIX: - * - Apply CSS transforms to HTML labels in IE11 - */ -(function() -{ - /** - * Uses CSS transforms for scale and translate. - */ - Graph.prototype.useCssTransforms = false; - - /** - * Contains the scale. - */ - Graph.prototype.currentScale = 1; - - /** - * Contains the offset. - */ - Graph.prototype.currentTranslate = new mxPoint(0, 0); - - /** - * Only foreignObject supported for now (no IE11). - */ - Graph.prototype.isCssTransformsSupported = function() - { - return this.dialect == mxConstants.DIALECT_SVG && !mxClient.NO_FO; - }; - - /** - * Function: getCellAt - * - * Needs to modify original method for recursive call. - */ - Graph.prototype.getCellAt = function(x, y, parent, vertices, edges, ignoreFn) - { - if (this.useCssTransforms) - { - x = x / this.currentScale - this.currentTranslate.x; - y = y / this.currentScale - this.currentTranslate.y; - } - - return this.getScaledCellAt.apply(this, arguments); - }; - - /** - * Function: getScaledCellAt - * - * Overridden for recursion. - */ - Graph.prototype.getScaledCellAt = function(x, y, parent, vertices, edges, ignoreFn) - { - vertices = (vertices != null) ? vertices : true; - edges = (edges != null) ? edges : true; - - if (parent == null) - { - parent = this.getCurrentRoot(); - - if (parent == null) - { - parent = this.getModel().getRoot(); - } - } - - if (parent != null) - { - var childCount = this.model.getChildCount(parent); - - for (var i = childCount - 1; i >= 0; i--) - { - var cell = this.model.getChildAt(parent, i); - var result = this.getScaledCellAt(x, y, cell, vertices, edges, ignoreFn); - - if (result != null) - { - return result; - } - else if (this.isCellVisible(cell) && (edges && this.model.isEdge(cell) || - vertices && this.model.isVertex(cell))) - { - var state = this.view.getState(cell); - - if (state != null && (ignoreFn == null || !ignoreFn(state, x, y)) && - this.intersects(state, x, y)) - { - return cell; - } - } - } - } - - return null; - }; - - - /** - * Function: repaint - * - * Updates the highlight after a change of the model or view. - */ - mxCellHighlight.prototype.getStrokeWidth = function(state) - { - var s = this.strokeWidth; - - if (this.graph.useCssTransforms) - { - s /= this.graph.currentScale; - } - - return s; - }; - - /** - * Function: getGraphBounds - * - * Overrides getGraphBounds to use bounding box from SVG. - */ - mxGraphView.prototype.getGraphBounds = function() - { - var b = this.graphBounds; - - if (this.graph.useCssTransforms) - { - var t = this.graph.currentTranslate; - var s = this.graph.currentScale; - - b = new mxRectangle( - (b.x + t.x) * s, (b.y + t.y) * s, - b.width * s, b.height * s); - } - - return b; - }; - - /** - * Function: viewStateChanged - * - * Overrides to bypass full cell tree validation. - * TODO: Check if this improves performance - */ - mxGraphView.prototype.viewStateChanged = function() - { - if (this.graph.useCssTransforms) - { - this.validate(); - this.graph.sizeDidChange(); - } - else - { - this.revalidate(); - this.graph.sizeDidChange(); - } - }; - - /** - * Function: validate - * - * Overrides validate to normalize validation view state and pass - * current state to CSS transform. - */ - var graphViewValidate = mxGraphView.prototype.validate; - - mxGraphView.prototype.validate = function(cell) - { - if (this.graph.useCssTransforms) - { - this.graph.currentScale = this.scale; - this.graph.currentTranslate.x = this.translate.x; - this.graph.currentTranslate.y = this.translate.y; - - this.scale = 1; - this.translate.x = 0; - this.translate.y = 0; - } - - graphViewValidate.apply(this, arguments); - - if (this.graph.useCssTransforms) - { - this.graph.updateCssTransform(); - - this.scale = this.graph.currentScale; - this.translate.x = this.graph.currentTranslate.x; - this.translate.y = this.graph.currentTranslate.y; - } - }; - - /** - * Function: updateCssTransform - * - * Zooms out of the graph by . - */ - Graph.prototype.updateCssTransform = function() - { - var temp = this.view.getDrawPane(); - - if (temp != null) - { - var g = temp.parentNode; - - if (!this.useCssTransforms) - { - g.removeAttribute('transformOrigin'); - g.removeAttribute('transform'); - } - else - { - var prev = g.getAttribute('transform'); - g.setAttribute('transformOrigin', '0 0'); - g.setAttribute('transform', 'scale(' + this.currentScale + ',' + this.currentScale + ')' + - 'translate(' + this.currentTranslate.x + ',' + this.currentTranslate.y + ')'); - - // Applies workarounds only if translate has changed - if (prev != g.getAttribute('transform')) - { - try - { - // Applies transform to labels outside of the SVG DOM - // Excluded via isCssTransformsSupported - // if (mxClient.NO_FO) - // { - // var transform = 'scale(' + this.currentScale + ')' + 'translate(' + - // this.currentTranslate.x + 'px,' + this.currentTranslate.y + 'px)'; - // - // this.view.states.visit(mxUtils.bind(this, function(cell, state) - // { - // if (state.text != null && state.text.node != null) - // { - // // Stores initial CSS transform that is used for the label alignment - // if (state.text.originalTransform == null) - // { - // state.text.originalTransform = state.text.node.style.transform; - // } - // - // state.text.node.style.transform = transform + state.text.originalTransform; - // } - // })); - // } - // Workaround for https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/4320441/ - if (mxClient.IS_EDGE) - { - // Recommended workaround is to do this on all - // foreignObjects, but this seems to be faster - var val = g.style.display; - g.style.display = 'none'; - g.getBBox(); - g.style.display = val; - } - } - catch (e) - { - // ignore - } - } - } - } - }; - - var graphViewValidateBackgroundPage = mxGraphView.prototype.validateBackgroundPage; - - mxGraphView.prototype.validateBackgroundPage = function() - { - var useCssTranforms = this.graph.useCssTransforms, scale = this.scale, - translate = this.translate; - - if (useCssTranforms) - { - this.scale = this.graph.currentScale; - this.translate = this.graph.currentTranslate; - } - - graphViewValidateBackgroundPage.apply(this, arguments); - - if (useCssTranforms) - { - this.scale = scale; - this.translate = translate; - } - }; - - var graphUpdatePageBreaks = mxGraph.prototype.updatePageBreaks; - - mxGraph.prototype.updatePageBreaks = function(visible, width, height) - { - var useCssTranforms = this.useCssTransforms, scale = this.view.scale, - translate = this.view.translate; - - if (useCssTranforms) - { - this.view.scale = 1; - this.view.translate = new mxPoint(0, 0); - this.useCssTransforms = false; - } - - graphUpdatePageBreaks.apply(this, arguments); - - if (useCssTranforms) - { - this.view.scale = scale; - this.view.translate = translate; - this.useCssTransforms = true; - } - }; - -})(); - -/** - * Sets the XML node for the current diagram. - */ -Graph.prototype.isLightboxView = function() -{ - return this.lightbox; -}; - -/** - * Installs automatic layout via styles - */ -Graph.prototype.labelLinkClicked = function(state, elt, evt) -{ - var href = elt.getAttribute('href'); - - if (href != null && !this.isCustomLink(href) && (mxEvent.isLeftMouseButton(evt) && - !mxEvent.isPopupTrigger(evt)) || mxEvent.isTouchEvent(evt)) - { - if (!this.isEnabled() || this.isCellLocked(state.cell)) - { - var target = this.isBlankLink(href) ? this.linkTarget : '_top'; - this.openLink(this.getAbsoluteUrl(href), target); - } - - mxEvent.consume(evt); - } -}; - -/** - * Returns the size of the page format scaled with the page size. - */ -Graph.prototype.openLink = function(href, target, allowOpener) -{ - var result = window; - - // Workaround for blocking in same iframe - if (target == '_self' && window != window.top) - { - window.location.href = href; - } - else - { - // Avoids page reload for anchors (workaround for IE but used everywhere) - if (href.substring(0, this.baseUrl.length) == this.baseUrl && - href.charAt(this.baseUrl.length) == '#' && - target == '_top' && window == window.top) - { - var hash = href.split('#')[1]; - - // Forces navigation if on same hash - if (window.location.hash == '#' + hash) - { - window.location.hash = ''; - } - - window.location.hash = hash; - } - else - { - result = window.open(href, target); - - if (result != null && !allowOpener) - { - result.opener = null; - } - } - } - - return result; -}; - -/** - * Adds support for page links. - */ -Graph.prototype.getLinkTitle = function(href) -{ - return href.substring(href.lastIndexOf('/') + 1); -}; - -/** - * Adds support for page links. - */ -Graph.prototype.isCustomLink = function(href) -{ - return href.substring(0, 5) == 'data:'; -}; - -/** - * Adds support for page links. - */ -Graph.prototype.customLinkClicked = function(link) -{ - return false; -}; - -/** - * Returns true if the fiven href references an external protocol that - * should never open in a new window. Default returns true for mailto. - */ -Graph.prototype.isExternalProtocol = function(href) -{ - return href.substring(0, 7) === 'mailto:'; -}; - -/** - * Hook for links to open in same window. Default returns true for anchors, - * links to same domain or if target == 'self' in the config. - */ -Graph.prototype.isBlankLink = function(href) -{ - return !this.isExternalProtocol(href) && - (this.linkPolicy === 'blank' || - (this.linkPolicy !== 'self' && - !this.isRelativeUrl(href) && - href.substring(0, this.domainUrl.length) !== this.domainUrl)); -}; - -/** - * - */ -Graph.prototype.isRelativeUrl = function(url) -{ - return url != null && !this.absoluteUrlPattern.test(url) && - url.substring(0, 5) !== 'data:' && - !this.isExternalProtocol(url); -}; - -/** - * Installs automatic layout via styles - */ -Graph.prototype.initLayoutManager = function() -{ - this.layoutManager = new mxLayoutManager(this); - - this.layoutManager.getLayout = function(cell) - { - var state = this.graph.view.getState(cell); - var style = (state != null) ? state.style : this.graph.getCellStyle(cell); - - if (style['childLayout'] == 'stackLayout') - { - var stackLayout = new mxStackLayout(this.graph, true); - stackLayout.resizeParentMax = mxUtils.getValue(style, 'resizeParentMax', '1') == '1'; - stackLayout.horizontal = mxUtils.getValue(style, 'horizontalStack', '1') == '1'; - stackLayout.resizeParent = mxUtils.getValue(style, 'resizeParent', '1') == '1'; - stackLayout.resizeLast = mxUtils.getValue(style, 'resizeLast', '0') == '1'; - stackLayout.spacing = style['stackSpacing'] || stackLayout.spacing; - stackLayout.border = style['stackBorder'] || stackLayout.border; - stackLayout.marginLeft = style['marginLeft'] || 0; - stackLayout.marginRight = style['marginRight'] || 0; - stackLayout.marginTop = style['marginTop'] || 0; - stackLayout.marginBottom = style['marginBottom'] || 0; - stackLayout.fill = true; - - return stackLayout; - } - else if (style['childLayout'] == 'treeLayout') - { - var treeLayout = new mxCompactTreeLayout(this.graph); - treeLayout.horizontal = mxUtils.getValue(style, 'horizontalTree', '1') == '1'; - treeLayout.resizeParent = mxUtils.getValue(style, 'resizeParent', '1') == '1'; - treeLayout.groupPadding = mxUtils.getValue(style, 'parentPadding', 20); - treeLayout.levelDistance = mxUtils.getValue(style, 'treeLevelDistance', 30); - treeLayout.maintainParentLocation = true; - treeLayout.edgeRouting = false; - treeLayout.resetEdges = false; - - return treeLayout; - } - else if (style['childLayout'] == 'flowLayout') - { - var flowLayout = new mxHierarchicalLayout(this.graph, mxUtils.getValue(style, - 'flowOrientation', mxConstants.DIRECTION_EAST)); - flowLayout.resizeParent = mxUtils.getValue(style, 'resizeParent', '1') == '1'; - flowLayout.parentBorder = mxUtils.getValue(style, 'parentPadding', 20); - flowLayout.maintainParentLocation = true; - - // Special undocumented styles for changing the hierarchical - flowLayout.intraCellSpacing = mxUtils.getValue(style, 'intraCellSpacing', mxHierarchicalLayout.prototype.intraCellSpacing); - flowLayout.interRankCellSpacing = mxUtils.getValue(style, 'interRankCellSpacing', mxHierarchicalLayout.prototype.interRankCellSpacing); - flowLayout.interHierarchySpacing = mxUtils.getValue(style, 'interHierarchySpacing', mxHierarchicalLayout.prototype.interHierarchySpacing); - flowLayout.parallelEdgeSpacing = mxUtils.getValue(style, 'parallelEdgeSpacing', mxHierarchicalLayout.prototype.parallelEdgeSpacing); - - return flowLayout; - } - - return null; - }; -}; - - /** - * Returns the size of the page format scaled with the page size. - */ -Graph.prototype.getPageSize = function() -{ - return (this.pageVisible) ? new mxRectangle(0, 0, this.pageFormat.width * this.pageScale, - this.pageFormat.height * this.pageScale) : this.scrollTileSize; -}; - -/** - * Returns a rectangle describing the position and count of the - * background pages, where x and y are the position of the top, - * left page and width and height are the vertical and horizontal - * page count. - */ -Graph.prototype.getPageLayout = function() -{ - var size = this.getPageSize(); - var bounds = this.getGraphBounds(); - - if (bounds.width == 0 || bounds.height == 0) - { - return new mxRectangle(0, 0, 1, 1); - } - else - { - // Computes untransformed graph bounds - var x = Math.ceil(bounds.x / this.view.scale - this.view.translate.x); - var y = Math.ceil(bounds.y / this.view.scale - this.view.translate.y); - var w = Math.floor(bounds.width / this.view.scale); - var h = Math.floor(bounds.height / this.view.scale); - - var x0 = Math.floor(x / size.width); - var y0 = Math.floor(y / size.height); - var w0 = Math.ceil((x + w) / size.width) - x0; - var h0 = Math.ceil((y + h) / size.height) - y0; - - return new mxRectangle(x0, y0, w0, h0); - } -}; - -/** - * Sanitizes the given HTML markup. - */ -Graph.prototype.sanitizeHtml = function(value, editing) -{ - // Uses https://code.google.com/p/google-caja/wiki/JsHtmlSanitizer - // NOTE: Original minimized sanitizer was modified to support - // data URIs for images, mailto and special data:-links. - // LATER: Add MathML to whitelisted tags - function urlX(link) - { - if (link != null && link.toString().toLowerCase().substring(0, 11) !== 'javascript:') - { - return link; - } - - return null; - }; - function idX(id) { return id }; - - return html_sanitize(value, urlX, idX); -}; - -/** - * Revalidates all cells with placeholders in the current graph model. - */ -Graph.prototype.updatePlaceholders = function() -{ - var model = this.model; - var validate = false; - - for (var key in this.model.cells) - { - var cell = this.model.cells[key]; - - if (this.isReplacePlaceholders(cell)) - { - this.view.invalidate(cell, false, false); - validate = true; - } - } - - if (validate) - { - this.view.validate(); - } -}; - -/** - * Adds support for placeholders in labels. - */ -Graph.prototype.isReplacePlaceholders = function(cell) -{ - return cell.value != null && typeof(cell.value) == 'object' && - cell.value.getAttribute('placeholders') == '1'; -}; - -/** - * Returns true if the given mouse wheel event should be used for zooming. This - * is invoked if no dialogs are showing and returns true with Alt or Control - * (except macOS) is pressed. - */ -Graph.prototype.isZoomWheelEvent = function(evt) -{ - return mxEvent.isAltDown(evt) || (mxEvent.isMetaDown(evt) && mxClient.IS_MAC) || - (mxEvent.isControlDown(evt) && !mxClient.IS_MAC); -}; - -/** - * Adds Alt+click to select cells behind cells. - */ -Graph.prototype.isTransparentClickEvent = function(evt) -{ - return mxEvent.isAltDown(evt); -}; - -/** - * Adds ctrl+shift+connect to disable connections. - */ -Graph.prototype.isIgnoreTerminalEvent = function(evt) -{ - return mxEvent.isShiftDown(evt) && mxEvent.isControlDown(evt); -}; - -/** - * Adds support for placeholders in labels. - */ -Graph.prototype.isSplitTarget = function(target, cells, evt) -{ - return !this.model.isEdge(cells[0]) && - !mxEvent.isAltDown(evt) && !mxEvent.isShiftDown(evt) && - mxGraph.prototype.isSplitTarget.apply(this, arguments); -}; - -/** - * Adds support for placeholders in labels. - */ -Graph.prototype.getLabel = function(cell) -{ - var result = mxGraph.prototype.getLabel.apply(this, arguments); - - if (result != null && this.isReplacePlaceholders(cell) && cell.getAttribute('placeholder') == null) - { - result = this.replacePlaceholders(cell, result); - } - - return result; -}; - -/** - * Adds labelMovable style. - */ -Graph.prototype.isLabelMovable = function(cell) -{ - var state = this.view.getState(cell); - var style = (state != null) ? state.style : this.getCellStyle(cell); - - return !this.isCellLocked(cell) && - ((this.model.isEdge(cell) && this.edgeLabelsMovable) || - (this.model.isVertex(cell) && (this.vertexLabelsMovable || - mxUtils.getValue(style, 'labelMovable', '0') == '1'))); -}; - -/** - * Adds event if grid size is changed. - */ -Graph.prototype.setGridSize = function(value) -{ - this.gridSize = value; - this.fireEvent(new mxEventObject('gridSizeChanged')); -}; - -/** - * Private helper method. - */ -Graph.prototype.getGlobalVariable = function(name) -{ - var val = null; - - if (name == 'date') - { - val = new Date().toLocaleDateString(); - } - else if (name == 'time') - { - val = new Date().toLocaleTimeString(); - } - else if (name == 'timestamp') - { - val = new Date().toLocaleString(); - } - else if (name.substring(0, 5) == 'date{') - { - var fmt = name.substring(5, name.length - 1); - val = this.formatDate(new Date(), fmt); - } - - return val; -}; - -/** - * Formats a date, see http://blog.stevenlevithan.com/archives/date-time-format - */ -Graph.prototype.formatDate = function(date, mask, utc) -{ - // LATER: Cache regexs - if (this.dateFormatCache == null) - { - this.dateFormatCache = { - i18n: { - dayNames: [ - "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", - "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday" - ], - monthNames: [ - "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", - "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" - ] - }, - - masks: { - "default": "ddd mmm dd yyyy HH:MM:ss", - shortDate: "m/d/yy", - mediumDate: "mmm d, yyyy", - longDate: "mmmm d, yyyy", - fullDate: "dddd, mmmm d, yyyy", - shortTime: "h:MM TT", - mediumTime: "h:MM:ss TT", - longTime: "h:MM:ss TT Z", - isoDate: "yyyy-mm-dd", - isoTime: "HH:MM:ss", - isoDateTime: "yyyy-mm-dd'T'HH:MM:ss", - isoUtcDateTime: "UTC:yyyy-mm-dd'T'HH:MM:ss'Z'" - } - }; - } - - var dF = this.dateFormatCache; - var token = /d{1,4}|m{1,4}|yy(?:yy)?|([HhMsTt])\1?|[LloSZ]|"[^"]*"|'[^']*'/g, - timezone = /\b(?:[PMCEA][SDP]T|(?:Pacific|Mountain|Central|Eastern|Atlantic) (?:Standard|Daylight|Prevailing) Time|(?:GMT|UTC)(?:[-+]\d{4})?)\b/g, - timezoneClip = /[^-+\dA-Z]/g, - pad = function (val, len) { - val = String(val); - len = len || 2; - while (val.length < len) val = "0" + val; - return val; - }; - - // You can't provide utc if you skip other args (use the "UTC:" mask prefix) - if (arguments.length == 1 && Object.prototype.toString.call(date) == "[object String]" && !/\d/.test(date)) { - mask = date; - date = undefined; - } - - // Passing date through Date applies Date.parse, if necessary - date = date ? new Date(date) : new Date; - if (isNaN(date)) throw SyntaxError("invalid date"); - - mask = String(dF.masks[mask] || mask || dF.masks["default"]); - - // Allow setting the utc argument via the mask - if (mask.slice(0, 4) == "UTC:") { - mask = mask.slice(4); - utc = true; - } - - var _ = utc ? "getUTC" : "get", - d = date[_ + "Date"](), - D = date[_ + "Day"](), - m = date[_ + "Month"](), - y = date[_ + "FullYear"](), - H = date[_ + "Hours"](), - M = date[_ + "Minutes"](), - s = date[_ + "Seconds"](), - L = date[_ + "Milliseconds"](), - o = utc ? 0 : date.getTimezoneOffset(), - flags = { - d: d, - dd: pad(d), - ddd: dF.i18n.dayNames[D], - dddd: dF.i18n.dayNames[D + 7], - m: m + 1, - mm: pad(m + 1), - mmm: dF.i18n.monthNames[m], - mmmm: dF.i18n.monthNames[m + 12], - yy: String(y).slice(2), - yyyy: y, - h: H % 12 || 12, - hh: pad(H % 12 || 12), - H: H, - HH: pad(H), - M: M, - MM: pad(M), - s: s, - ss: pad(s), - l: pad(L, 3), - L: pad(L > 99 ? Math.round(L / 10) : L), - t: H < 12 ? "a" : "p", - tt: H < 12 ? "am" : "pm", - T: H < 12 ? "A" : "P", - TT: H < 12 ? "AM" : "PM", - Z: utc ? "UTC" : (String(date).match(timezone) || [""]).pop().replace(timezoneClip, ""), - o: (o > 0 ? "-" : "+") + pad(Math.floor(Math.abs(o) / 60) * 100 + Math.abs(o) % 60, 4), - S: ["th", "st", "nd", "rd"][d % 10 > 3 ? 0 : (d % 100 - d % 10 != 10) * d % 10] - }; - - return mask.replace(token, function ($0) - { - return $0 in flags ? flags[$0] : $0.slice(1, $0.length - 1); - }); -}; - -/** - * - */ -Graph.prototype.createLayersDialog = function() -{ - var div = document.createElement('div'); - div.style.position = 'absolute'; - - var model = this.getModel(); - var childCount = model.getChildCount(model.root); - - for (var i = 0; i < childCount; i++) - { - (mxUtils.bind(this, function(layer) - { - var span = document.createElement('div'); - span.style.overflow = 'hidden'; - span.style.textOverflow = 'ellipsis'; - span.style.padding = '2px'; - span.style.whiteSpace = 'nowrap'; - - var cb = document.createElement('input'); - cb.style.display = 'inline-block'; - cb.setAttribute('type', 'checkbox'); - - if (model.isVisible(layer)) - { - cb.setAttribute('checked', 'checked'); - cb.defaultChecked = true; - } - - span.appendChild(cb); - - var title = this.convertValueToString(layer) || (mxResources.get('background') || 'Background'); - span.setAttribute('title', title); - mxUtils.write(span, title); - div.appendChild(span); - - mxEvent.addListener(cb, 'click', function() - { - if (cb.getAttribute('checked') != null) - { - cb.removeAttribute('checked'); - } - else - { - cb.setAttribute('checked', 'checked'); - } - - model.setVisible(layer, cb.checked); - }); - })(model.getChildAt(model.root, i))); - } - - return div; -}; - -/** - * Private helper method. - */ -Graph.prototype.replacePlaceholders = function(cell, str) -{ - var result = []; - - if (str != null) - { - var last = 0; - - while (match = this.placeholderPattern.exec(str)) - { - var val = match[0]; - - if (val.length > 2 && val != '%label%' && val != '%tooltip%') - { - var tmp = null; - - if (match.index > last && str.charAt(match.index - 1) == '%') - { - tmp = val.substring(1); - } - else - { - var name = val.substring(1, val.length - 1); - - // Workaround for invalid char for getting attribute in older versions of IE - if (name.indexOf('{') < 0) - { - var current = cell; - - while (tmp == null && current != null) - { - if (current.value != null && typeof(current.value) == 'object') - { - tmp = (current.hasAttribute(name)) ? ((current.getAttribute(name) != null) ? - current.getAttribute(name) : '') : null; - } - - current = this.model.getParent(current); - } - } - - if (tmp == null) - { - tmp = this.getGlobalVariable(name); - } - } - - result.push(str.substring(last, match.index) + ((tmp != null) ? tmp : val)); - last = match.index + val.length; - } - } - - result.push(str.substring(last)); - } - - return result.join(''); -}; - -/** - * Selects cells for connect vertex return value. - */ -Graph.prototype.selectCellsForConnectVertex = function(cells, evt, hoverIcons) -{ - // Selects only target vertex if one exists - if (cells.length == 2 && this.model.isVertex(cells[1])) - { - this.setSelectionCell(cells[1]); - - if (hoverIcons != null) - { - // Adds hover icons to new target vertex for touch devices - if (mxEvent.isTouchEvent(evt)) - { - hoverIcons.update(hoverIcons.getState(this.view.getState(cells[1]))); - } - else - { - // Hides hover icons after click with mouse - hoverIcons.reset(); - } - } - - this.scrollCellToVisible(cells[1]); - } - else - { - this.setSelectionCells(cells); - } -}; - -/** - * Adds a connection to the given vertex. - */ -Graph.prototype.connectVertex = function(source, direction, length, evt, forceClone, ignoreCellAt) -{ - ignoreCellAt = (ignoreCellAt) ? ignoreCellAt : false; - - var pt = (source.geometry.relative && source.parent.geometry != null) ? - new mxPoint(source.parent.geometry.width * source.geometry.x, source.parent.geometry.height * source.geometry.y) : - new mxPoint(source.geometry.x, source.geometry.y); - - if (direction == mxConstants.DIRECTION_NORTH) - { - pt.x += source.geometry.width / 2; - pt.y -= length ; - } - else if (direction == mxConstants.DIRECTION_SOUTH) - { - pt.x += source.geometry.width / 2; - pt.y += source.geometry.height + length; - } - else if (direction == mxConstants.DIRECTION_WEST) - { - pt.x -= length; - pt.y += source.geometry.height / 2; - } - else - { - pt.x += source.geometry.width + length; - pt.y += source.geometry.height / 2; - } - - var parentState = this.view.getState(this.model.getParent(source)); - var s = this.view.scale; - var t = this.view.translate; - var dx = t.x * s; - var dy = t.y * s; - - if (this.model.isVertex(parentState.cell)) - { - dx = parentState.x; - dy = parentState.y; - } - - // Workaround for relative child cells - if (this.model.isVertex(source.parent) && source.geometry.relative) - { - pt.x += source.parent.geometry.x; - pt.y += source.parent.geometry.y; - } - - // Checks actual end point of edge for target cell - var target = (ignoreCellAt || (mxEvent.isControlDown(evt) && !forceClone)) ? - null : this.getCellAt(dx + pt.x * s, dy + pt.y * s); - - if (this.model.isAncestor(target, source)) - { - target = null; - } - - // Checks if target or ancestor is locked - var temp = target; - - while (temp != null) - { - if (this.isCellLocked(temp)) - { - target = null; - break; - } - - temp = this.model.getParent(temp); - } - - // Checks if source and target intersect - if (target != null) - { - var sourceState = this.view.getState(source); - var targetState = this.view.getState(target); - - if (sourceState != null && targetState != null && mxUtils.intersects(sourceState, targetState)) - { - target = null; - } - } - - var duplicate = !mxEvent.isShiftDown(evt) || forceClone; - - if (duplicate) - { - if (direction == mxConstants.DIRECTION_NORTH) - { - pt.y -= source.geometry.height / 2; - } - else if (direction == mxConstants.DIRECTION_SOUTH) - { - pt.y += source.geometry.height / 2; - } - else if (direction == mxConstants.DIRECTION_WEST) - { - pt.x -= source.geometry.width / 2; - } - else - { - pt.x += source.geometry.width / 2; - } - } - - // Uses connectable parent vertex if one exists - if (target != null && !this.isCellConnectable(target)) - { - var parent = this.getModel().getParent(target); - - if (this.getModel().isVertex(parent) && this.isCellConnectable(parent)) - { - target = parent; - } - } - - if (target == source || this.model.isEdge(target) || !this.isCellConnectable(target)) - { - target = null; - } - - var result = []; - - this.model.beginUpdate(); - try - { - var realTarget = target; - - if (realTarget == null && duplicate) - { - // Handles relative children - var cellToClone = source; - var geo = this.getCellGeometry(source); - - while (geo != null && geo.relative) - { - cellToClone = this.getModel().getParent(cellToClone); - geo = this.getCellGeometry(cellToClone); - } - - // Handle consistuents for cloning - var state = this.view.getState(cellToClone); - var style = (state != null) ? state.style : this.getCellStyle(cellToClone); - - if (mxUtils.getValue(style, 'part', false)) - { - var tmpParent = this.model.getParent(cellToClone); - - if (this.model.isVertex(tmpParent)) - { - cellToClone = tmpParent; - } - } - - realTarget = this.duplicateCells([cellToClone], false)[0]; - - var geo = this.getCellGeometry(realTarget); - - if (geo != null) - { - geo.x = pt.x - geo.width / 2; - geo.y = pt.y - geo.height / 2; - } - } - - // Never connects children in stack layouts - var layout = null; - - if (this.layoutManager != null) - { - layout = this.layoutManager.getLayout(this.model.getParent(source)); - } - - var edge = ((mxEvent.isControlDown(evt) && duplicate) || (target == null && layout != null && layout.constructor == mxStackLayout)) ? null : - this.insertEdge(this.model.getParent(source), null, '', source, realTarget, this.createCurrentEdgeStyle()); - - // Inserts edge before source - if (edge != null && this.connectionHandler.insertBeforeSource) - { - var index = null; - var tmp = source; - - while (tmp.parent != null && tmp.geometry != null && - tmp.geometry.relative && tmp.parent != edge.parent) - { - tmp = this.model.getParent(tmp); - } - - if (tmp != null && tmp.parent != null && tmp.parent == edge.parent) - { - var index = tmp.parent.getIndex(tmp); - this.model.add(tmp.parent, edge, index); - } - } - - // Special case: Click on west icon puts clone before cell - if (target == null && realTarget != null && layout != null && source.parent != null && - layout.constructor == mxStackLayout && direction == mxConstants.DIRECTION_WEST) - { - var index = source.parent.getIndex(source); - this.model.add(source.parent, realTarget, index); - } - - if (edge != null) - { - result.push(edge); - } - - if (target == null && realTarget != null) - { - result.push(realTarget); - } - - if (realTarget == null && edge != null) - { - edge.geometry.setTerminalPoint(pt, false); - } - - if (edge != null) - { - this.fireEvent(new mxEventObject('cellsInserted', 'cells', [edge])); - } - } - finally - { - this.model.endUpdate(); - } - - return result; -}; - -/** - * Returns all labels in the diagram as a string. - */ -Graph.prototype.getIndexableText = function() -{ - var tmp = document.createElement('div'); - var labels = []; - var label = ''; - - for (var key in this.model.cells) - { - var cell = this.model.cells[key]; - - if (this.model.isVertex(cell) || this.model.isEdge(cell)) - { - if (this.isHtmlLabel(cell)) - { - tmp.innerHTML = this.getLabel(cell); - label = mxUtils.extractTextWithWhitespace([tmp]); - } - else - { - label = this.getLabel(cell); - } - - label = mxUtils.trim(label.replace(/[\x00-\x1F\x7F-\x9F]|\s+/g, ' ')); - - if (label.length > 0) - { - labels.push(label); - } - } - } - - return labels.join(' '); -}; - -/** - * Returns the label for the given cell. - */ -Graph.prototype.convertValueToString = function(cell) -{ - if (cell.value != null && typeof(cell.value) == 'object') - { - if (this.isReplacePlaceholders(cell) && cell.getAttribute('placeholder') != null) - { - var name = cell.getAttribute('placeholder'); - var current = cell; - var result = null; - - while (result == null && current != null) - { - if (current.value != null && typeof(current.value) == 'object') - { - result = (current.hasAttribute(name)) ? ((current.getAttribute(name) != null) ? - current.getAttribute(name) : '') : null; - } - - current = this.model.getParent(current); - } - - return result || ''; - } - else - { - return cell.value.getAttribute('label') || ''; - } - } - - return mxGraph.prototype.convertValueToString.apply(this, arguments); -}; - -/** - * Returns the link for the given cell. - */ -Graph.prototype.getLinksForState = function(state) -{ - if (state != null && state.text != null && state.text.node != null) - { - return state.text.node.getElementsByTagName('a'); - } - - return null; -}; - -/** - * Returns the link for the given cell. - */ -Graph.prototype.getLinkForCell = function(cell) -{ - if (cell.value != null && typeof(cell.value) == 'object') - { - var link = cell.value.getAttribute('link'); - - // Removes links with leading javascript: protocol - // TODO: Check more possible attack vectors - if (link != null && link.toLowerCase().substring(0, 11) === 'javascript:') - { - link = link.substring(11); - } - - return link; - } - - return null; -}; - -/** - * Overrides label orientation for collapsed swimlanes inside stack. - */ -Graph.prototype.getCellStyle = function(cell) -{ - var style = mxGraph.prototype.getCellStyle.apply(this, arguments); - - if (cell != null && this.layoutManager != null) - { - var parent = this.model.getParent(cell); - - if (this.model.isVertex(parent) && this.isCellCollapsed(cell)) - { - var layout = this.layoutManager.getLayout(parent); - - if (layout != null && layout.constructor == mxStackLayout) - { - style[mxConstants.STYLE_HORIZONTAL] = !layout.horizontal; - } - } - } - - return style; -}; - -/** - * Disables alternate width persistence for stack layout parents - */ -Graph.prototype.updateAlternateBounds = function(cell, geo, willCollapse) -{ - if (cell != null && geo != null && this.layoutManager != null && geo.alternateBounds != null) - { - var layout = this.layoutManager.getLayout(this.model.getParent(cell)); - - if (layout != null && layout.constructor == mxStackLayout) - { - if (layout.horizontal) - { - geo.alternateBounds.height = 0; - } - else - { - geo.alternateBounds.width = 0; - } - } - } - - mxGraph.prototype.updateAlternateBounds.apply(this, arguments); -}; - -/** - * Adds Shift+collapse/expand and size management for folding inside stack - */ -Graph.prototype.isMoveCellsEvent = function(evt) -{ - return mxEvent.isShiftDown(evt); -}; - -/** - * Adds Shift+collapse/expand and size management for folding inside stack - */ -Graph.prototype.foldCells = function(collapse, recurse, cells, checkFoldable, evt) -{ - recurse = (recurse != null) ? recurse : false; - - if (cells == null) - { - cells = this.getFoldableCells(this.getSelectionCells(), collapse); - } - - if (cells != null) - { - this.model.beginUpdate(); - - try - { - mxGraph.prototype.foldCells.apply(this, arguments); - - // Resizes all parent stacks if alt is not pressed - if (this.layoutManager != null) - { - for (var i = 0; i < cells.length; i++) - { - var state = this.view.getState(cells[i]); - var geo = this.getCellGeometry(cells[i]); - - if (state != null && geo != null) - { - var dx = Math.round(geo.width - state.width / this.view.scale); - var dy = Math.round(geo.height - state.height / this.view.scale); - - if (dy != 0 || dx != 0) - { - var parent = this.model.getParent(cells[i]); - var layout = this.layoutManager.getLayout(parent); - - if (layout == null) - { - // Moves cells to the right and down after collapse/expand - if (evt != null && this.isMoveCellsEvent(evt)) - { - this.moveSiblings(state, parent, dx, dy); - } - } - else if ((evt == null || !mxEvent.isAltDown(evt)) && layout.constructor == mxStackLayout && !layout.resizeLast) - { - this.resizeParentStacks(parent, layout, dx, dy); - } - } - } - } - } - } - finally - { - this.model.endUpdate(); - } - - // Selects cells after folding - if (this.isEnabled()) - { - this.setSelectionCells(cells); - } - } -}; - -/** - * Overrides label orientation for collapsed swimlanes inside stack. - */ -Graph.prototype.moveSiblings = function(state, parent, dx, dy) -{ - this.model.beginUpdate(); - try - { - var cells = this.getCellsBeyond(state.x, state.y, parent, true, true); - - for (var i = 0; i < cells.length; i++) - { - if (cells[i] != state.cell) - { - var tmp = this.view.getState(cells[i]); - var geo = this.getCellGeometry(cells[i]); - - if (tmp != null && geo != null) - { - geo = geo.clone(); - geo.translate(Math.round(dx * Math.max(0, Math.min(1, (tmp.x - state.x) / state.width))), - Math.round(dy * Math.max(0, Math.min(1, (tmp.y - state.y) / state.height)))); - this.model.setGeometry(cells[i], geo); - } - } - } - } - finally - { - this.model.endUpdate(); - } -}; - -/** - * Overrides label orientation for collapsed swimlanes inside stack. - */ -Graph.prototype.resizeParentStacks = function(parent, layout, dx, dy) -{ - if (this.layoutManager != null && layout != null && layout.constructor == mxStackLayout && !layout.resizeLast) - { - this.model.beginUpdate(); - try - { - var dir = layout.horizontal; - - // Bubble resize up for all parent stack layouts with same orientation - while (parent != null && layout != null && layout.constructor == mxStackLayout && - layout.horizontal == dir && !layout.resizeLast) - { - var pgeo = this.getCellGeometry(parent); - var pstate = this.view.getState(parent); - - if (pstate != null && pgeo != null) - { - pgeo = pgeo.clone(); - - if (layout.horizontal) - { - pgeo.width += dx + Math.min(0, pstate.width / this.view.scale - pgeo.width); - } - else - { - pgeo.height += dy + Math.min(0, pstate.height / this.view.scale - pgeo.height); - } - - this.model.setGeometry(parent, pgeo); - } - - parent = this.model.getParent(parent); - layout = this.layoutManager.getLayout(parent); - } - } - finally - { - this.model.endUpdate(); - } - } -}; - -/** - * Disables drill-down for non-swimlanes. - */ -Graph.prototype.isContainer = function(cell) -{ - var state = this.view.getState(cell); - var style = (state != null) ? state.style : this.getCellStyle(cell); - - if (this.isSwimlane(cell)) - { - return style['container'] != '0'; - } - else - { - return style['container'] == '1'; - } -}; - -/** - * Adds a connectable style. - */ -Graph.prototype.isCellConnectable = function(cell) -{ - var state = this.view.getState(cell); - var style = (state != null) ? state.style : this.getCellStyle(cell); - - return (style['connectable'] != null) ? style['connectable'] != '0' : - mxGraph.prototype.isCellConnectable.apply(this, arguments); -}; - -/** - * Function: selectAll - * - * Selects all children of the given parent cell or the children of the - * default parent if no parent is specified. To select leaf vertices and/or - * edges use . - * - * Parameters: - * - * parent - Optional whose children should be selected. - * Default is . - */ -Graph.prototype.selectAll = function(parent) -{ - parent = parent || this.getDefaultParent(); - - if (!this.isCellLocked(parent)) - { - mxGraph.prototype.selectAll.apply(this, arguments); - } -}; - -/** - * Function: selectCells - * - * Selects all vertices and/or edges depending on the given boolean - * arguments recursively, starting at the given parent or the default - * parent if no parent is specified. Use to select all cells. - * For vertices, only cells with no children are selected. - * - * Parameters: - * - * vertices - Boolean indicating if vertices should be selected. - * edges - Boolean indicating if edges should be selected. - * parent - Optional that acts as the root of the recursion. - * Default is . - */ -Graph.prototype.selectCells = function(vertices, edges, parent) -{ - parent = parent || this.getDefaultParent(); - - if (!this.isCellLocked(parent)) - { - mxGraph.prototype.selectCells.apply(this, arguments); - } -}; - -/** - * Function: getSwimlaneAt - * - * Returns the bottom-most swimlane that intersects the given point (x, y) - * in the cell hierarchy that starts at the given parent. - * - * Parameters: - * - * x - X-coordinate of the location to be checked. - * y - Y-coordinate of the location to be checked. - * parent - that should be used as the root of the recursion. - * Default is . - */ -Graph.prototype.getSwimlaneAt = function (x, y, parent) -{ - parent = parent || this.getDefaultParent(); - - if (!this.isCellLocked(parent)) - { - return mxGraph.prototype.getSwimlaneAt.apply(this, arguments); - } - - return null; -}; - -/** - * Disables folding for non-swimlanes. - */ -Graph.prototype.isCellFoldable = function(cell) -{ - var state = this.view.getState(cell); - var style = (state != null) ? state.style : this.getCellStyle(cell); - - return this.foldingEnabled && !this.isCellLocked(cell) && - ((this.isContainer(cell) && style['collapsible'] != '0') || - (!this.isContainer(cell) && style['collapsible'] == '1')); -}; - -/** - * Stops all interactions and clears the selection. - */ -Graph.prototype.reset = function() -{ - if (this.isEditing()) - { - this.stopEditing(true); - } - - this.escape(); - - if (!this.isSelectionEmpty()) - { - this.clearSelection(); - } -}; - -/** - * Overridden to limit zoom to 1% - 16.000%. - */ -Graph.prototype.zoom = function(factor, center) -{ - factor = Math.max(0.01, Math.min(this.view.scale * factor, 160)) / this.view.scale; - - mxGraph.prototype.zoom.apply(this, arguments); -}; - -/** - * Function: zoomIn - * - * Zooms into the graph by . - */ -Graph.prototype.zoomIn = function() -{ - // Switches to 1% zoom steps below 15% - if (this.view.scale < 0.15) - { - this.zoom((this.view.scale + 0.01) / this.view.scale); - } - else - { - // Uses to 5% zoom steps for better grid rendering in webkit - // and to avoid rounding errors for zoom steps - this.zoom((Math.round(this.view.scale * this.zoomFactor * 20) / 20) / this.view.scale); - } -}; - -/** - * Function: zoomOut - * - * Zooms out of the graph by . - */ -Graph.prototype.zoomOut = function() -{ - // Switches to 1% zoom steps below 15% - if (this.view.scale <= 0.15) - { - this.zoom((this.view.scale - 0.01) / this.view.scale); - } - else - { - // Uses to 5% zoom steps for better grid rendering in webkit - // and to avoid rounding errors for zoom steps - this.zoom((Math.round(this.view.scale * (1 / this.zoomFactor) * 20) / 20) / this.view.scale); - } -}; - -/** - * Overrides tooltips to show custom tooltip or metadata. - */ -Graph.prototype.getTooltipForCell = function(cell) -{ - var tip = ''; - - if (mxUtils.isNode(cell.value)) - { - var tmp = cell.value.getAttribute('tooltip'); - - if (tmp != null) - { - if (tmp != null && this.isReplacePlaceholders(cell)) - { - tmp = this.replacePlaceholders(cell, tmp); - } - - tip = this.sanitizeHtml(tmp); - } - else - { - var ignored = this.builtInProperties; - var attrs = cell.value.attributes; - var temp = []; - - // Hides links in edit mode - if (this.isEnabled()) - { - ignored.push('link'); - } - - for (var i = 0; i < attrs.length; i++) - { - if (mxUtils.indexOf(ignored, attrs[i].nodeName) < 0 && attrs[i].nodeValue.length > 0) - { - temp.push({name: attrs[i].nodeName, value: attrs[i].nodeValue}); - } - } - - // Sorts by name - temp.sort(function(a, b) - { - if (a.name < b.name) - { - return -1; - } - else if (a.name > b.name) - { - return 1; - } - else - { - return 0; - } - }); - - for (var i = 0; i < temp.length; i++) - { - if (temp[i].name != 'link' || !this.isCustomLink(temp[i].value)) - { - tip += ((temp[i].name != 'link') ? '' + temp[i].name + ': ' : '') + - mxUtils.htmlEntities(temp[i].value) + '\n'; - } - } - - if (tip.length > 0) - { - tip = tip.substring(0, tip.length - 1); - - if (mxClient.IS_SVG) - { - tip = '
' + tip + '
'; - } - } - } - } - - return tip; -}; - -/** - * Turns the given string into an array. - */ -Graph.prototype.stringToBytes = function(str) -{ - var arr = new Array(str.length); - - for (var i = 0; i < str.length; i++) - { - arr[i] = str.charCodeAt(i); - } - - return arr; -}; - -/** - * Turns the given array into a string. - */ -Graph.prototype.bytesToString = function(arr) -{ - var result = new Array(arr.length); - - for (var i = 0; i < arr.length; i++) - { - result[i] = String.fromCharCode(arr[i]); - } - - return result.join(''); -}; - -/** - * Returns a base64 encoded version of the compressed string. - */ -Graph.prototype.compress = function(data) -{ - if (data == null || data.length == 0 || typeof(pako) === 'undefined') - { - return data; - } - else - { - var tmp = this.bytesToString(pako.deflateRaw(encodeURIComponent(data))); - - return (window.btoa) ? btoa(tmp) : Base64.encode(tmp, true); - } -}; - -/** - * Returns a decompressed version of the base64 encoded string. - */ -Graph.prototype.decompress = function(data) -{ - if (data == null || data.length == 0 || typeof(pako) === 'undefined') - { - return data; - } - else - { - var tmp = (window.atob) ? atob(data) : Base64.decode(data, true); - - return this.zapGremlins(decodeURIComponent( - this.bytesToString(pako.inflateRaw(tmp)))); - } -}; - -/** - * Removes all illegal control characters with ASCII code <32 except TAB, LF - * and CR. - */ -Graph.prototype.zapGremlins = function(text) -{ - var checked = []; - - for (var i = 0; i < text.length; i++) - { - var code = text.charCodeAt(i); - - // Removes all control chars except TAB, LF and CR - if (code >= 32 || code == 9 || code == 10 || code == 13) - { - checked.push(text.charAt(i)); - } - } - - return checked.join(''); -}; - -/** - * Hover icons are used for hover, vertex handler and drag from sidebar. - */ -HoverIcons = function(graph) -{ - this.graph = graph; - this.init(); -}; - -/** - * Up arrow. - */ -HoverIcons.prototype.arrowSpacing = 2; - -/** - * Delay to switch to another state for overlapping bbox. Default is 500ms. - */ -HoverIcons.prototype.updateDelay = 500; - -/** - * Delay to switch between states. Default is 140ms. - */ -HoverIcons.prototype.activationDelay = 140; - -/** - * Up arrow. - */ -HoverIcons.prototype.currentState = null; - -/** - * Up arrow. - */ -HoverIcons.prototype.activeArrow = null; - -/** - * Up arrow. - */ -HoverIcons.prototype.inactiveOpacity = 15; - -/** - * Up arrow. - */ -HoverIcons.prototype.cssCursor = 'copy'; - -/** - * Whether to hide arrows that collide with vertices. - * LATER: Add keyboard override, touch support. - */ -HoverIcons.prototype.checkCollisions = true; - -/** - * Up arrow. - */ -HoverIcons.prototype.arrowFill = '#29b6f2'; - -/** - * Up arrow. - */ -HoverIcons.prototype.triangleUp = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/triangle-up.png', 26, 14) : - Graph.createSvgImage(18, 28, ''); - -/** - * Right arrow. - */ -HoverIcons.prototype.triangleRight = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/triangle-right.png', 14, 26) : - Graph.createSvgImage(26, 18, ''); - -/** - * Down arrow. - */ -HoverIcons.prototype.triangleDown = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/triangle-down.png', 26, 14) : - Graph.createSvgImage(18, 26, ''); - -/** - * Left arrow. - */ -HoverIcons.prototype.triangleLeft = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/triangle-left.png', 14, 26) : - Graph.createSvgImage(28, 18, ''); - -/** - * Round target. - */ -HoverIcons.prototype.roundDrop = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/round-drop.png', 26, 26) : - Graph.createSvgImage(26, 26, ''); - -/** - * Refresh target. - */ -HoverIcons.prototype.refreshTarget = new mxImage((mxClient.IS_SVG) ? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACYAAAAmCAYAAACoPemuAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6NDQxNERDRTU1QjY1MTFFNDkzNTRFQTVEMTdGMTdBQjciIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6NDQxNERDRTY1QjY1MTFFNDkzNTRFQTVEMTdGMTdBQjciPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo0NDE0RENFMzVCNjUxMUU0OTM1NEVBNUQxN0YxN0FCNyIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo0NDE0RENFNDVCNjUxMUU0OTM1NEVBNUQxN0YxN0FCNyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PsvuX50AAANaSURBVHja7FjRZ1tRGD9ZJ1NCyIQSwrivI4Q8hCpjlFDyFEoYfSp9Ko1QWnmo0If+BSXkIfo0QirTMUpeGo2EPfWllFYjZMLKLDJn53d3biU337m5J223bPbxk5t7v+/c3/2+73znO8fDOWezKM/YjMpz68Lj8ejY+QTeCCwLxOS9qPxtyN+6wAeBTwJ31CCO0cJDjXBGBN4LfIepSwykTUT1bgpuib0SONIgo8KRHOtRiCFcvUcgZeGrHPNBxLIyFPyRgTGz0xLbegJCdmzpElue5KlAIMDX19d5uVzm5+fnfDAYmMA17uEZdOx2Yvb/sHlu2S0xwymn5ufneTab5b1ej08S6EAXNrDd2dnhiUTim21MvMtwQ6yiIrWwsMDPzs64rsBmf3/fvM7n89TYlUnEllSkQqEQv7q64g+Vk5MTVXosORErU0Zer5f0FEIlw2N6MxwO82QyaXql2+2SxDqdjopYWUUsqEp45IldqtWq6UWVh/1+P7+8vCTJ4QMUJSRIEXuneoH96w8PDyeWAnhSJfCqwm6NIlaklFdXV0cGhRcQ2mlJQXK5nMq2YPEZbnteU1U2lUqN/D84OGD9fl+5fgnSrFarsUwmw0qlEru4uBjTicViTk3Cr27HSnxR+Doyz0ZE1CAWiUTusbu7y9rttlZv5fP5WDQavYfIMba4uEipfhF8XtqJoZXx/uH+sC/4vPg7OljZZQbsCmLtYzc3N6zRaJhotVrmfx0xDINtbm6athYUeXpHdbBNaqZUKpWxWXV7e2vex+xaWVnhc3NzjrPUXgexyCt0m67LBV7uJMITjqRE4o8tZeg8FPpFitgapYxiOC0poFgsji1jKNo6BZZckrAGUtJsNk1vqAihCBcKhTE7hNWhqw2qFnGy5UFOUYJVIJ1OjzSE+BCEilon0URavRmBqnbbQ00AXbm+vnZc9O1tj72OnQoc2+cwygRkb2+P1et17ZoEm3g87lRmjgWZ00kbXkNuse6/Bu2wlegIxfb2tuvWGroO4bO2c4bbzUh60mxDXm1sbJhhxkQYnhS4h2fUZoRAWnf7lv8N27f8P7Xhnekjgpk+VKGOoQbsiY+hhhtF3YO7twIJ+ULvUGv+GQ2fQEvWxI/THNx5/p/BaspPAQYAqStgiSQwCDoAAAAASUVORK5CYII=' : - IMAGE_PATH + '/refresh.png', 38, 38); - -/** - * Tolerance for hover icon clicks. - */ -HoverIcons.prototype.tolerance = (mxClient.IS_TOUCH) ? 6 : 0; - -/** - * - */ -HoverIcons.prototype.init = function() -{ - this.arrowUp = this.createArrow(this.triangleUp, mxResources.get('plusTooltip')); - this.arrowRight = this.createArrow(this.triangleRight, mxResources.get('plusTooltip')); - this.arrowDown = this.createArrow(this.triangleDown, mxResources.get('plusTooltip')); - this.arrowLeft = this.createArrow(this.triangleLeft, mxResources.get('plusTooltip')); - - this.elts = [this.arrowUp, this.arrowRight, this.arrowDown, this.arrowLeft]; - - this.repaintHandler = mxUtils.bind(this, function() - { - this.repaint(); - }); - - this.graph.selectionModel.addListener(mxEvent.CHANGE, this.repaintHandler); - this.graph.model.addListener(mxEvent.CHANGE, this.repaintHandler); - this.graph.view.addListener(mxEvent.SCALE_AND_TRANSLATE, this.repaintHandler); - this.graph.view.addListener(mxEvent.TRANSLATE, this.repaintHandler); - this.graph.view.addListener(mxEvent.SCALE, this.repaintHandler); - this.graph.view.addListener(mxEvent.DOWN, this.repaintHandler); - this.graph.view.addListener(mxEvent.UP, this.repaintHandler); - this.graph.addListener(mxEvent.ROOT, this.repaintHandler); - - // Resets the mouse point on escape - this.graph.addListener(mxEvent.ESCAPE, mxUtils.bind(this, function() - { - this.mouseDownPoint = null; - })); - - // Removes hover icons if mouse leaves the container - mxEvent.addListener(this.graph.container, 'mouseleave', mxUtils.bind(this, function(evt) - { - // Workaround for IE11 firing mouseleave for touch in diagram - if (evt.relatedTarget != null && mxEvent.getSource(evt) == this.graph.container) - { - this.setDisplay('none'); - } - })); - - // Resets current state when in-place editor starts - this.graph.addListener(mxEvent.START_EDITING, mxUtils.bind(this, function(evt) - { - this.reset(); - })); - - // Resets current state after update of selection state for touch events - var graphClick = this.graph.click; - this.graph.click = mxUtils.bind(this, function(me) - { - graphClick.apply(this.graph, arguments); - - if (this.currentState != null && !this.graph.isCellSelected(this.currentState.cell) && - mxEvent.isTouchEvent(me.getEvent()) && !this.graph.model.isVertex(me.getCell())) - { - this.reset(); - } - }); - - // Checks if connection handler was active in mouse move - // as workaround for possible double connection inserted - var connectionHandlerActive = false; - - // Implements a listener for hover and click handling - this.graph.addMouseListener( - { - mouseDown: mxUtils.bind(this, function(sender, me) - { - connectionHandlerActive = false; - var evt = me.getEvent(); - - if (this.isResetEvent(evt)) - { - this.reset(); - } - else if (!this.isActive()) - { - var state = this.getState(me.getState()); - - if (state != null || !mxEvent.isTouchEvent(evt)) - { - this.update(state); - } - } - - this.setDisplay('none'); - }), - mouseMove: mxUtils.bind(this, function(sender, me) - { - var evt = me.getEvent(); - - if (this.isResetEvent(evt)) - { - this.reset(); - } - else if (!this.graph.isMouseDown && !mxEvent.isTouchEvent(evt)) - { - this.update(this.getState(me.getState()), - me.getGraphX(), me.getGraphY()); - } - - if (this.graph.connectionHandler != null && - this.graph.connectionHandler.shape != null) - { - connectionHandlerActive = true; - } - }), - mouseUp: mxUtils.bind(this, function(sender, me) - { - var evt = me.getEvent(); - var pt = mxUtils.convertPoint(this.graph.container, - mxEvent.getClientX(evt), mxEvent.getClientY(evt)) - - if (this.isResetEvent(evt)) - { - this.reset(); - } - else if (this.isActive() && !connectionHandlerActive && - this.mouseDownPoint != null) - { - this.click(this.currentState, this.getDirection(), me); - } - else if (this.isActive()) - { - // Selects target vertex after drag and clone if not only new edge was inserted - if (this.graph.getSelectionCount() != 1 || !this.graph.model.isEdge( - this.graph.getSelectionCell())) - { - this.update(this.getState(this.graph.view.getState( - this.graph.getCellAt(me.getGraphX(), me.getGraphY())))); - } - else - { - this.reset(); - } - } - else if (mxEvent.isTouchEvent(evt) || (this.bbox != null && - mxUtils.contains(this.bbox, me.getGraphX(), me.getGraphY()))) - { - // Shows existing hover icons if inside bounding box - this.setDisplay(''); - this.repaint(); - } - else if (!mxEvent.isTouchEvent(evt)) - { - this.reset(); - } - - connectionHandlerActive = false; - this.resetActiveArrow(); - }) - }); -}; - -/** - * - */ -HoverIcons.prototype.isResetEvent = function(evt, allowShift) -{ - return mxEvent.isAltDown(evt) || (this.activeArrow == null && mxEvent.isShiftDown(evt)) || - mxEvent.isMetaDown(evt) || (mxEvent.isPopupTrigger(evt) && !mxEvent.isControlDown(evt)); -}; - -/** - * - */ -HoverIcons.prototype.createArrow = function(img, tooltip) -{ - var arrow = null; - - if (mxClient.IS_IE && !mxClient.IS_SVG) - { - // Workaround for PNG images in IE6 - if (mxClient.IS_IE6 && document.compatMode != 'CSS1Compat') - { - arrow = document.createElement(mxClient.VML_PREFIX + ':image'); - arrow.setAttribute('src', img.src); - arrow.style.borderStyle = 'none'; - } - else - { - arrow = document.createElement('div'); - arrow.style.backgroundImage = 'url(' + img.src + ')'; - arrow.style.backgroundPosition = 'center'; - arrow.style.backgroundRepeat = 'no-repeat'; - } - - arrow.style.width = (img.width + 4) + 'px'; - arrow.style.height = (img.height + 4) + 'px'; - arrow.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; - } - else - { - arrow = mxUtils.createImage(img.src); - arrow.style.width = img.width + 'px'; - arrow.style.height = img.height + 'px'; - arrow.style.padding = this.tolerance + 'px'; - } - - if (tooltip != null) - { - arrow.setAttribute('title', tooltip); - } - - arrow.style.position = 'absolute'; - arrow.style.cursor = this.cssCursor; - - mxEvent.addGestureListeners(arrow, mxUtils.bind(this, function(evt) - { - if (this.currentState != null && !this.isResetEvent(evt)) - { - this.mouseDownPoint = mxUtils.convertPoint(this.graph.container, - mxEvent.getClientX(evt), mxEvent.getClientY(evt)); - this.drag(evt, this.mouseDownPoint.x, this.mouseDownPoint.y); - this.activeArrow = arrow; - this.setDisplay('none'); - mxEvent.consume(evt); - } - })); - - // Captures mouse events as events on graph - mxEvent.redirectMouseEvents(arrow, this.graph, this.currentState); - - mxEvent.addListener(arrow, 'mouseenter', mxUtils.bind(this, function(evt) - { - // Workaround for Firefox firing mouseenter on touchend - if (mxEvent.isMouseEvent(evt)) - { - if (this.activeArrow != null && this.activeArrow != arrow) - { - mxUtils.setOpacity(this.activeArrow, this.inactiveOpacity); - } - - this.graph.connectionHandler.constraintHandler.reset(); - mxUtils.setOpacity(arrow, 100); - this.activeArrow = arrow; - } - })); - - mxEvent.addListener(arrow, 'mouseleave', mxUtils.bind(this, function(evt) - { - // Workaround for IE11 firing this event on touch - if (!this.graph.isMouseDown) - { - this.resetActiveArrow(); - } - })); - - return arrow; -}; - -/** - * - */ -HoverIcons.prototype.resetActiveArrow = function() -{ - if (this.activeArrow != null) - { - mxUtils.setOpacity(this.activeArrow, this.inactiveOpacity); - this.activeArrow = null; - } -}; - -/** - * - */ -HoverIcons.prototype.getDirection = function() -{ - var dir = mxConstants.DIRECTION_EAST; - - if (this.activeArrow == this.arrowUp) - { - dir = mxConstants.DIRECTION_NORTH; - } - else if (this.activeArrow == this.arrowDown) - { - dir = mxConstants.DIRECTION_SOUTH; - } - else if (this.activeArrow == this.arrowLeft) - { - dir = mxConstants.DIRECTION_WEST; - } - - return dir; -}; - -/** - * - */ -HoverIcons.prototype.visitNodes = function(visitor) -{ - for (var i = 0; i < this.elts.length; i++) - { - if (this.elts[i] != null) - { - visitor(this.elts[i]); - } - } -}; - -/** - * - */ -HoverIcons.prototype.removeNodes = function() -{ - this.visitNodes(function(elt) - { - if (elt.parentNode != null) - { - elt.parentNode.removeChild(elt); - } - }); -}; - -/** - * - */ -HoverIcons.prototype.setDisplay = function(display) -{ - this.visitNodes(function(elt) - { - elt.style.display = display; - }); -}; - -/** - * - */ -HoverIcons.prototype.isActive = function() -{ - return this.activeArrow != null && this.currentState != null; -}; - -/** - * - */ -HoverIcons.prototype.drag = function(evt, x, y) -{ - this.graph.popupMenuHandler.hideMenu(); - this.graph.stopEditing(false); - - // Checks if state was removed in call to stopEditing above - if (this.currentState != null) - { - this.graph.connectionHandler.start(this.currentState, x, y); - this.graph.isMouseTrigger = mxEvent.isMouseEvent(evt); - this.graph.isMouseDown = true; - - // Hides handles for selection cell - var handler = this.graph.selectionCellsHandler.getHandler(this.currentState.cell); - - if (handler != null) - { - handler.setHandlesVisible(false); - } - - // Ctrl+shift drag sets source constraint - var es = this.graph.connectionHandler.edgeState; - - if (evt != null && mxEvent.isShiftDown(evt) && mxEvent.isControlDown(evt) && es != null && - mxUtils.getValue(es.style, mxConstants.STYLE_EDGE, null) === 'orthogonalEdgeStyle') - { - var direction = this.getDirection(); - es.cell.style = mxUtils.setStyle(es.cell.style, 'sourcePortConstraint', direction); - es.style['sourcePortConstraint'] = direction; - } - } -}; - -/** - * - */ -HoverIcons.prototype.getStateAt = function(state, x, y) -{ - return this.graph.view.getState(this.graph.getCellAt(x, y)); -}; - -/** - * - */ -HoverIcons.prototype.click = function(state, dir, me) -{ - var evt = me.getEvent(); - var x = me.getGraphX(); - var y = me.getGraphY(); - - var tmp = this.getStateAt(state, x, y); - - if (tmp != null && this.graph.model.isEdge(tmp.cell) && !mxEvent.isControlDown(evt) && - (tmp.getVisibleTerminalState(true) == state || tmp.getVisibleTerminalState(false) == state)) - { - this.graph.setSelectionCell(tmp.cell); - this.reset(); - } - else if (state != null) - { - var cells = this.graph.connectVertex(state.cell, dir, this.graph.defaultEdgeLength, evt); - this.graph.selectCellsForConnectVertex(cells, evt, this); - - // Selects only target vertex if one exists - if (cells.length == 2 && this.graph.model.isVertex(cells[1])) - { - this.graph.setSelectionCell(cells[1]); - - // Adds hover icons to new target vertex for touch devices - if (mxEvent.isTouchEvent(evt)) - { - this.update(this.getState(this.graph.view.getState(cells[1]))); - } - else - { - // Hides hover icons after click with mouse - this.reset(); - } - - this.graph.scrollCellToVisible(cells[1]); - } - else - { - this.graph.setSelectionCells(cells); - } - } - - me.consume(); -}; - -/** - * - */ -HoverIcons.prototype.reset = function(clearTimeout) -{ - clearTimeout = (clearTimeout == null) ? true : clearTimeout; - - if (clearTimeout && this.updateThread != null) - { - window.clearTimeout(this.updateThread); - } - - this.mouseDownPoint = null; - this.currentState = null; - this.activeArrow = null; - this.removeNodes(); - this.bbox = null; -}; - -/** - * - */ -HoverIcons.prototype.repaint = function() -{ - this.bbox = null; - - if (this.currentState != null) - { - // Checks if cell was deleted - this.currentState = this.getState(this.currentState); - - // Cell was deleted - if (this.currentState != null && - this.graph.model.isVertex(this.currentState.cell) && - this.graph.isCellConnectable(this.currentState.cell)) - { - var bds = mxRectangle.fromRectangle(this.currentState); - - // Uses outer bounding box to take rotation into account - if (this.currentState.shape != null && this.currentState.shape.boundingBox != null) - { - bds = mxRectangle.fromRectangle(this.currentState.shape.boundingBox); - } - - bds.grow(this.graph.tolerance); - bds.grow(this.arrowSpacing); - - var handler = this.graph.selectionCellsHandler.getHandler(this.currentState.cell); - - if (handler != null) - { - bds.x -= handler.horizontalOffset / 2; - bds.y -= handler.verticalOffset / 2; - bds.width += handler.horizontalOffset; - bds.height += handler.verticalOffset; - - // Adds bounding box of rotation handle to avoid overlap - if (handler.rotationShape != null && handler.rotationShape.node != null && - handler.rotationShape.node.style.visibility != 'hidden' && - handler.rotationShape.node.style.display != 'none' && - handler.rotationShape.boundingBox != null) - { - bds.add(handler.rotationShape.boundingBox); - } - } - - this.arrowUp.style.left = Math.round(this.currentState.getCenterX() - this.triangleUp.width / 2 - this.tolerance) + 'px'; - this.arrowUp.style.top = Math.round(bds.y - this.triangleUp.height - this.tolerance) + 'px'; - mxUtils.setOpacity(this.arrowUp, this.inactiveOpacity); - - this.arrowRight.style.left = Math.round(bds.x + bds.width - this.tolerance) + 'px'; - this.arrowRight.style.top = Math.round(this.currentState.getCenterY() - this.triangleRight.height / 2 - this.tolerance) + 'px'; - mxUtils.setOpacity(this.arrowRight, this.inactiveOpacity); - - this.arrowDown.style.left = this.arrowUp.style.left; - this.arrowDown.style.top = Math.round(bds.y + bds.height - this.tolerance) + 'px'; - mxUtils.setOpacity(this.arrowDown, this.inactiveOpacity); - - this.arrowLeft.style.left = Math.round(bds.x - this.triangleLeft.width - this.tolerance) + 'px'; - this.arrowLeft.style.top = this.arrowRight.style.top; - mxUtils.setOpacity(this.arrowLeft, this.inactiveOpacity); - - if (this.checkCollisions) - { - var right = this.graph.getCellAt(bds.x + bds.width + - this.triangleRight.width / 2, this.currentState.getCenterY()); - var left = this.graph.getCellAt(bds.x - this.triangleLeft.width / 2, this.currentState.getCenterY()); - var top = this.graph.getCellAt(this.currentState.getCenterX(), bds.y - this.triangleUp.height / 2); - var bottom = this.graph.getCellAt(this.currentState.getCenterX(), bds.y + bds.height + this.triangleDown.height / 2); - - // Shows hover icons large cell is behind all directions of current cell - if (right != null && right == left && left == top && top == bottom) - { - right = null; - left = null; - top = null; - bottom = null; - } - - var currentGeo = this.graph.getCellGeometry(this.currentState.cell); - - var checkCollision = mxUtils.bind(this, function(cell, arrow) - { - var geo = this.graph.model.isVertex(cell) && this.graph.getCellGeometry(cell); - - // Ignores collision if vertex is more than 3 times the size of this vertex - if (cell != null && !this.graph.model.isAncestor(cell, this.currentState.cell) && - (geo == null || currentGeo == null || (geo.height < 6 * currentGeo.height && - geo.width < 6 * currentGeo.width))) - { - arrow.style.visibility = 'hidden'; - } - else - { - arrow.style.visibility = 'visible'; - } - }); - - checkCollision(right, this.arrowRight); - checkCollision(left, this.arrowLeft); - checkCollision(top, this.arrowUp); - checkCollision(bottom, this.arrowDown); - } - else - { - this.arrowLeft.style.visibility = 'visible'; - this.arrowRight.style.visibility = 'visible'; - this.arrowUp.style.visibility = 'visible'; - this.arrowDown.style.visibility = 'visible'; - } - - if (this.graph.tooltipHandler.isEnabled()) - { - this.arrowLeft.setAttribute('title', mxResources.get('plusTooltip')); - this.arrowRight.setAttribute('title', mxResources.get('plusTooltip')); - this.arrowUp.setAttribute('title', mxResources.get('plusTooltip')); - this.arrowDown.setAttribute('title', mxResources.get('plusTooltip')); - } - else - { - this.arrowLeft.removeAttribute('title'); - this.arrowRight.removeAttribute('title'); - this.arrowUp.removeAttribute('title'); - this.arrowDown.removeAttribute('title'); - } - } - else - { - this.reset(); - } - - // Updates bounding box - if (this.currentState != null) - { - this.bbox = this.computeBoundingBox(); - - // Adds tolerance for hover - if (this.bbox != null) - { - this.bbox.grow(10); - } - } - } -}; - -/** - * - */ -HoverIcons.prototype.computeBoundingBox = function() -{ - var bbox = (!this.graph.model.isEdge(this.currentState.cell)) ? mxRectangle.fromRectangle(this.currentState) : null; - - this.visitNodes(function(elt) - { - if (elt.parentNode != null) - { - var tmp = new mxRectangle(elt.offsetLeft, elt.offsetTop, elt.offsetWidth, elt.offsetHeight); - - if (bbox == null) - { - bbox = tmp; - } - else - { - bbox.add(tmp); - } - } - }); - - return bbox; -}; - -/** - * - */ -HoverIcons.prototype.getState = function(state) -{ - if (state != null) - { - var cell = state.cell; - - // Uses connectable parent vertex if child is not connectable - if (this.graph.getModel().isVertex(cell) && !this.graph.isCellConnectable(cell)) - { - var parent = this.graph.getModel().getParent(cell); - - if (this.graph.getModel().isVertex(parent) && this.graph.isCellConnectable(parent)) - { - cell = parent; - } - } - - // Ignores locked cells and edges - if (this.graph.isCellLocked(cell) || this.graph.model.isEdge(cell)) - { - cell = null; - } - - state = this.graph.view.getState(cell); - } - - return state; -}; - -/** - * - */ -HoverIcons.prototype.update = function(state, x, y) -{ - if (!this.graph.connectionArrowsEnabled) - { - this.reset(); - } - else - { - var timeOnTarget = null; - - // Time on target - if (this.prev != state || this.isActive()) - { - this.startTime = new Date().getTime(); - this.prev = state; - timeOnTarget = 0; - - if (this.updateThread != null) - { - window.clearTimeout(this.updateThread); - } - - if (state != null) - { - // Starts timer to update current state with no mouse events - this.updateThread = window.setTimeout(mxUtils.bind(this, function() - { - if (!this.isActive() && !this.graph.isMouseDown && - !this.graph.panningHandler.isActive()) - { - this.prev = state; - this.update(state, x, y); - } - }), this.updateDelay + 10); - } - } - else if (this.startTime != null) - { - timeOnTarget = new Date().getTime() - this.startTime; - } - - this.setDisplay(''); - - if (this.currentState != null && this.currentState != state && timeOnTarget < this.activationDelay && - this.bbox != null && !mxUtils.contains(this.bbox, x, y)) - { - this.reset(false); - } - else if (this.currentState != null || timeOnTarget > this.activationDelay) - { - if (this.currentState != state && ((timeOnTarget > this.updateDelay && state != null) || - this.bbox == null || x == null || y == null || !mxUtils.contains(this.bbox, x, y))) - { - if (state != null && this.graph.isEnabled()) - { - this.removeNodes(); - this.setCurrentState(state); - this.repaint(); - - // Resets connection points on other focused cells - if (this.graph.connectionHandler.constraintHandler.currentFocus != state) - { - this.graph.connectionHandler.constraintHandler.reset(); - } - } - else - { - this.reset(); - } - } - } - } -}; - -/** - * - */ -HoverIcons.prototype.setCurrentState = function(state) -{ - if (state.style['portConstraint'] != 'eastwest') - { - this.graph.container.appendChild(this.arrowUp); - this.graph.container.appendChild(this.arrowDown); - } - - this.graph.container.appendChild(this.arrowRight); - this.graph.container.appendChild(this.arrowLeft); - this.currentState = state; -}; - -(function() -{ - - /** - * Reset the list of processed edges. - */ - var mxGraphViewResetValidationState = mxGraphView.prototype.resetValidationState; - - mxGraphView.prototype.resetValidationState = function() - { - mxGraphViewResetValidationState.apply(this, arguments); - - this.validEdges = []; - }; - - /** - * Updates jumps for valid edges and repaints if needed. - */ - var mxGraphViewValidateCellState = mxGraphView.prototype.validateCellState; - - mxGraphView.prototype.validateCellState = function(cell, recurse) - { - var state = this.getState(cell); - - // Forces repaint if jumps change on a valid edge - if (state != null && this.graph.model.isEdge(state.cell) && - state.style != null && state.style[mxConstants.STYLE_CURVED] != 1 && - !state.invalid && this.updateLineJumps(state)) - { - this.graph.cellRenderer.redraw(state, false, this.isRendering()); - } - - state = mxGraphViewValidateCellState.apply(this, arguments); - - // Adds to the list of edges that may intersect with later edges - if (state != null && this.graph.model.isEdge(state.cell) && - state.style[mxConstants.STYLE_CURVED] != 1) - { - // LATER: Reuse jumps for valid edges - this.validEdges.push(state); - } - - return state; - }; - - /** - * Forces repaint if routed points have changed. - */ - var mxCellRendererIsShapeInvalid = mxCellRenderer.prototype.isShapeInvalid; - - mxCellRenderer.prototype.isShapeInvalid = function(state, shape) - { - return mxCellRendererIsShapeInvalid.apply(this, arguments) || - (state.routedPoints != null && shape.routedPoints != null && - !mxUtils.equalPoints(shape.routedPoints, state.routedPoints)) - }; - - - /** - * Updates jumps for invalid edges. - */ - var mxGraphViewUpdateCellState = mxGraphView.prototype.updateCellState; - - mxGraphView.prototype.updateCellState = function(state) - { - mxGraphViewUpdateCellState.apply(this, arguments); - - // Updates jumps on invalid edge before repaint - if (this.graph.model.isEdge(state.cell) && - state.style[mxConstants.STYLE_CURVED] != 1) - { - this.updateLineJumps(state); - } - }; - - /** - * Updates the jumps between given state and processed edges. - */ - mxGraphView.prototype.updateLineJumps = function(state) - { - var pts = state.absolutePoints; - - if (Graph.lineJumpsEnabled) - { - var changed = state.routedPoints != null; - var actual = null; - - if (pts != null && this.validEdges != null && - mxUtils.getValue(state.style, 'jumpStyle', 'none') !== 'none') - { - var thresh = 0.5 * this.scale; - changed = false; - actual = []; - - // Type 0 means normal waypoint, 1 means jump - function addPoint(type, x, y) - { - var rpt = new mxPoint(x, y); - rpt.type = type; - - actual.push(rpt); - var curr = (state.routedPoints != null) ? state.routedPoints[actual.length - 1] : null; - - return curr == null || curr.type != type || curr.x != x || curr.y != y; - }; - - for (var i = 0; i < pts.length - 1; i++) - { - var p1 = pts[i + 1]; - var p0 = pts[i]; - var list = []; - - // Ignores waypoints on straight segments - var pn = pts[i + 2]; - - while (i < pts.length - 2 && - mxUtils.ptSegDistSq(p0.x, p0.y, pn.x, pn.y, - p1.x, p1.y) < 1 * this.scale * this.scale) - { - p1 = pn; - i++; - pn = pts[i + 2]; - } - - changed = addPoint(0, p0.x, p0.y) || changed; - - // Processes all previous edges - for (var e = 0; e < this.validEdges.length; e++) - { - var state2 = this.validEdges[e]; - var pts2 = state2.absolutePoints; - - if (pts2 != null && mxUtils.intersects(state, state2) && state2.style['noJump'] != '1') - { - // Compares each segment of the edge with the current segment - for (var j = 0; j < pts2.length - 1; j++) - { - var p3 = pts2[j + 1]; - var p2 = pts2[j]; - - // Ignores waypoints on straight segments - pn = pts2[j + 2]; - - while (j < pts2.length - 2 && - mxUtils.ptSegDistSq(p2.x, p2.y, pn.x, pn.y, - p3.x, p3.y) < 1 * this.scale * this.scale) - { - p3 = pn; - j++; - pn = pts2[j + 2]; - } - - var pt = mxUtils.intersection(p0.x, p0.y, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y); - - // Handles intersection between two segments - if (pt != null && (Math.abs(pt.x - p2.x) > thresh || - Math.abs(pt.y - p2.y) > thresh) && - (Math.abs(pt.x - p3.x) > thresh || - Math.abs(pt.y - p3.y) > thresh)) - { - var dx = pt.x - p0.x; - var dy = pt.y - p0.y; - var temp = {distSq: dx * dx + dy * dy, x: pt.x, y: pt.y}; - - // Intersections must be ordered by distance from start of segment - for (var t = 0; t < list.length; t++) - { - if (list[t].distSq > temp.distSq) - { - list.splice(t, 0, temp); - temp = null; - - break; - } - } - - // Ignores multiple intersections at segment joint - if (temp != null && (list.length == 0 || - list[list.length - 1].x !== temp.x || - list[list.length - 1].y !== temp.y)) - { - list.push(temp); - } - } - } - } - } - - // Adds ordered intersections to routed points - for (var j = 0; j < list.length; j++) - { - changed = addPoint(1, list[j].x, list[j].y) || changed; - } - } - - var pt = pts[pts.length - 1]; - changed = addPoint(0, pt.x, pt.y) || changed; - } - - state.routedPoints = actual; - - return changed; - } - else - { - return false; - } - }; - - /** - * Overrides painting the actual shape for taking into account jump style. - */ - var mxConnectorPaintLine = mxConnector.prototype.paintLine; - - mxConnector.prototype.paintLine = function (c, absPts, rounded) - { - // Required for checking dirty state - this.routedPoints = (this.state != null) ? this.state.routedPoints : null; - - if (this.outline || this.state == null || this.style == null || - this.state.routedPoints == null || this.state.routedPoints.length == 0) - { - mxConnectorPaintLine.apply(this, arguments); - } - else - { - var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, - mxConstants.LINE_ARCSIZE) / 2; - var size = (parseInt(mxUtils.getValue(this.style, 'jumpSize', - Graph.defaultJumpSize)) - 2) / 2 + this.strokewidth; - var style = mxUtils.getValue(this.style, 'jumpStyle', 'none'); - var f = Editor.jumpSizeRatio; - var moveTo = true; - var last = null; - var len = null; - var pts = []; - var n = null; - c.begin(); - - for (var i = 0; i < this.state.routedPoints.length; i++) - { - var rpt = this.state.routedPoints[i]; - var pt = new mxPoint(rpt.x / this.scale, rpt.y / this.scale); - - // Takes first and last point from passed-in array - if (i == 0) - { - pt = absPts[0]; - } - else if (i == this.state.routedPoints.length - 1) - { - pt = absPts[absPts.length - 1]; - } - - var done = false; - - // Type 1 is an intersection - if (last != null && rpt.type == 1) - { - // Checks if next/previous points are too close - var next = this.state.routedPoints[i + 1]; - var dx = next.x / this.scale - pt.x; - var dy = next.y / this.scale - pt.y; - var dist = dx * dx + dy * dy; - - if (n == null) - { - n = new mxPoint(pt.x - last.x, pt.y - last.y); - len = Math.sqrt(n.x * n.x + n.y * n.y); - n.x = n.x * size / len; - n.y = n.y * size / len; - } - - if (dist > size * size && len > 0) - { - var dx = last.x - pt.x; - var dy = last.y - pt.y; - var dist = dx * dx + dy * dy; - - if (dist > size * size) - { - var p0 = new mxPoint(pt.x - n.x, pt.y - n.y); - var p1 = new mxPoint(pt.x + n.x, pt.y + n.y); - pts.push(p0); - - this.addPoints(c, pts, rounded, arcSize, false, null, moveTo); - - var f = (Math.round(n.x) < 0 || (Math.round(n.x) == 0 - && Math.round(n.y) <= 0)) ? 1 : -1; - moveTo = false; - - if (style == 'sharp') - { - c.lineTo(p0.x - n.y * f, p0.y + n.x * f); - c.lineTo(p1.x - n.y * f, p1.y + n.x * f); - c.lineTo(p1.x, p1.y); - } - else if (style == 'arc') - { - f *= 1.3; - c.curveTo(p0.x - n.y * f, p0.y + n.x * f, - p1.x - n.y * f, p1.y + n.x * f, - p1.x, p1.y); - } - else - { - c.moveTo(p1.x, p1.y); - moveTo = true; - } - - pts = [p1]; - done = true; - } - } - } - else - { - n = null; - } - - if (!done) - { - pts.push(pt); - last = pt; - } - } - - this.addPoints(c, pts, rounded, arcSize, false, null, moveTo); - c.stroke(); - } - }; - - /** - * Adds support for snapToPoint style. - */ - var mxGraphViewUpdateFloatingTerminalPoint = mxGraphView.prototype.updateFloatingTerminalPoint; - - mxGraphView.prototype.updateFloatingTerminalPoint = function(edge, start, end, source) - { - if (start != null && edge != null && - (start.style['snapToPoint'] == '1' || - edge.style['snapToPoint'] == '1')) - { - start = this.getTerminalPort(edge, start, source); - var next = this.getNextPoint(edge, end, source); - - var orth = this.graph.isOrthogonal(edge); - var alpha = mxUtils.toRadians(Number(start.style[mxConstants.STYLE_ROTATION] || '0')); - var center = new mxPoint(start.getCenterX(), start.getCenterY()); - - if (alpha != 0) - { - var cos = Math.cos(-alpha); - var sin = Math.sin(-alpha); - next = mxUtils.getRotatedPoint(next, cos, sin, center); - } - - var border = parseFloat(edge.style[mxConstants.STYLE_PERIMETER_SPACING] || 0); - border += parseFloat(edge.style[(source) ? - mxConstants.STYLE_SOURCE_PERIMETER_SPACING : - mxConstants.STYLE_TARGET_PERIMETER_SPACING] || 0); - var pt = this.getPerimeterPoint(start, next, alpha == 0 && orth, border); - - if (alpha != 0) - { - var cos = Math.cos(alpha); - var sin = Math.sin(alpha); - pt = mxUtils.getRotatedPoint(pt, cos, sin, center); - } - - edge.setAbsoluteTerminalPoint(this.snapToAnchorPoint(edge, start, end, source, pt), source); - } - else - { - mxGraphViewUpdateFloatingTerminalPoint.apply(this, arguments); - } - }; - - mxGraphView.prototype.snapToAnchorPoint = function(edge, start, end, source, pt) - { - if (start != null && edge != null) - { - var constraints = this.graph.getAllConnectionConstraints(start) - var nearest = null; - var dist = null; - - if (constraints != null) - { - for (var i = 0; i < constraints.length; i++) - { - var cp = this.graph.getConnectionPoint(start, constraints[i]); - - if (cp != null) - { - var tmp = (cp.x - pt.x) * (cp.x - pt.x) + (cp.y - pt.y) * (cp.y - pt.y); - - if (dist == null || tmp < dist) - { - nearest = cp; - dist = tmp; - } - } - } - } - - if (nearest != null) - { - pt = nearest; - } - } - - return pt; - }; - - /** - * Adds support for placeholders in text elements of shapes. - */ - var mxStencilEvaluateTextAttribute = mxStencil.prototype.evaluateTextAttribute; - - mxStencil.prototype.evaluateTextAttribute = function(node, attribute, shape) - { - var result = mxStencilEvaluateTextAttribute.apply(this, arguments); - var placeholders = node.getAttribute('placeholders'); - - if (placeholders == '1' && shape.state != null) - { - result = shape.state.view.graph.replacePlaceholders(shape.state.cell, result); - } - - return result; - }; - - /** - * Adds custom stencils defined via shape=stencil(value) style. The value is a base64 encoded, compressed and - * URL encoded XML definition of the shape according to the stencil definition language of mxGraph. - * - * Needs to be in this file to make sure its part of the embed client code. Also the check for ZLib is - * different than for the Editor code. - */ - var mxCellRendererCreateShape = mxCellRenderer.prototype.createShape; - mxCellRenderer.prototype.createShape = function(state) - { - if (state.style != null && typeof(pako) !== 'undefined') - { - var shape = mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null); - - // Extracts and decodes stencil XML if shape has the form shape=stencil(value) - if (shape != null && shape.substring(0, 8) == 'stencil(') - { - try - { - var stencil = shape.substring(8, shape.length - 1); - var doc = mxUtils.parseXml(state.view.graph.decompress(stencil)); - - return new mxShape(new mxStencil(doc.documentElement)); - } - catch (e) - { - if (window.console != null) - { - console.log('Error in shape: ' + e); - } - } - } - } - - return mxCellRendererCreateShape.apply(this, arguments); - }; -})(); - -/** - * Overrides stencil registry for dynamic loading of stencils. - */ -/** - * Maps from library names to an array of Javascript filenames, - * which are synchronously loaded. Currently only stencil files - * (.xml) and JS files (.js) are supported. - * IMPORTANT: For embedded diagrams to work entries must also - * be added in EmbedServlet.java. - */ -mxStencilRegistry.libraries = {}; - -/** - * Global switch to disable dynamic loading. - */ -mxStencilRegistry.dynamicLoading = true; - -/** - * Global switch to disable eval for JS (preload all JS instead). - */ -mxStencilRegistry.allowEval = true; - -/** - * Stores all package names that have been dynamically loaded. - * Each package is only loaded once. - */ -mxStencilRegistry.packages = []; - -// Extends the default stencil registry to add dynamic loading -mxStencilRegistry.getStencil = function(name) -{ - var result = mxStencilRegistry.stencils[name]; - - if (result == null && mxCellRenderer.defaultShapes[name] == null && mxStencilRegistry.dynamicLoading) - { - var basename = mxStencilRegistry.getBasenameForStencil(name); - - // Loads stencil files and tries again - if (basename != null) - { - var libs = mxStencilRegistry.libraries[basename]; - - if (libs != null) - { - if (mxStencilRegistry.packages[basename] == null) - { - for (var i = 0; i < libs.length; i++) - { - var fname = libs[i]; - - if (fname.toLowerCase().substring(fname.length - 4, fname.length) == '.xml') - { - mxStencilRegistry.loadStencilSet(fname, null); - } - else if (fname.toLowerCase().substring(fname.length - 3, fname.length) == '.js') - { - try - { - if (mxStencilRegistry.allowEval) - { - var req = mxUtils.load(fname); - - if (req != null && req.getStatus() >= 200 && req.getStatus() <= 299) - { - eval.call(window, req.getText()); - } - } - } - catch (e) - { - if (window.console != null) - { - console.log('error in getStencil:', fname, e); - } - } - } - else - { - // FIXME: This does not yet work as the loading is triggered after - // the shape was used in the graph, at which point the keys have - // typically been translated in the calling method. - //mxResources.add(fname); - } - } - - mxStencilRegistry.packages[basename] = 1; - } - } - else - { - // Replaces '_-_' with '_' - basename = basename.replace('_-_', '_'); - mxStencilRegistry.loadStencilSet(STENCIL_PATH + '/' + basename + '.xml', null); - } - - result = mxStencilRegistry.stencils[name]; - } - } - - return result; -}; - -// Returns the basename for the given stencil or null if no file must be -// loaded to render the given stencil. -mxStencilRegistry.getBasenameForStencil = function(name) -{ - var tmp = null; - - if (name != null) - { - var parts = name.split('.'); - - if (parts.length > 0 && parts[0] == 'mxgraph') - { - tmp = parts[1]; - - for (var i = 2; i < parts.length - 1; i++) - { - tmp += '/' + parts[i]; - } - } - } - - return tmp; -}; - -// Loads the given stencil set -mxStencilRegistry.loadStencilSet = function(stencilFile, postStencilLoad, force, async) -{ - force = (force != null) ? force : false; - - // Uses additional cache for detecting previous load attempts - var xmlDoc = mxStencilRegistry.packages[stencilFile]; - - if (force || xmlDoc == null) - { - var install = false; - - if (xmlDoc == null) - { - try - { - if (async) - { - mxStencilRegistry.loadStencil(stencilFile, mxUtils.bind(this, function(xmlDoc2) - { - if (xmlDoc2 != null && xmlDoc2.documentElement != null) - { - mxStencilRegistry.packages[stencilFile] = xmlDoc2; - install = true; - mxStencilRegistry.parseStencilSet(xmlDoc2.documentElement, postStencilLoad, install); - } - })); - - return; - } - else - { - xmlDoc = mxStencilRegistry.loadStencil(stencilFile); - mxStencilRegistry.packages[stencilFile] = xmlDoc; - install = true; - } - } - catch (e) - { - if (window.console != null) - { - console.log('error in loadStencilSet:', stencilFile, e); - } - } - } - - if (xmlDoc != null && xmlDoc.documentElement != null) - { - mxStencilRegistry.parseStencilSet(xmlDoc.documentElement, postStencilLoad, install); - } - } -}; - -// Loads the given stencil XML file. -mxStencilRegistry.loadStencil = function(filename, fn) -{ - if (fn != null) - { - var req = mxUtils.get(filename, mxUtils.bind(this, function(req) - { - fn((req.getStatus() >= 200 && req.getStatus() <= 299) ? req.getXml() : null); - })); - } - else - { - return mxUtils.load(filename).getXml(); - } -}; - -// Takes array of strings -mxStencilRegistry.parseStencilSets = function(stencils) -{ - for (var i = 0; i < stencils.length; i++) - { - mxStencilRegistry.parseStencilSet(mxUtils.parseXml(stencils[i]).documentElement); - } -}; - -// Parses the given stencil set -mxStencilRegistry.parseStencilSet = function(root, postStencilLoad, install) -{ - if (root.nodeName == 'stencils') - { - var shapes = root.firstChild; - - while (shapes != null) - { - if (shapes.nodeName == 'shapes') - { - mxStencilRegistry.parseStencilSet(shapes, postStencilLoad, install); - } - - shapes = shapes.nextSibling; - } - } - else - { - install = (install != null) ? install : true; - var shape = root.firstChild; - var packageName = ''; - var name = root.getAttribute('name'); - - if (name != null) - { - packageName = name + '.'; - } - - while (shape != null) - { - if (shape.nodeType == mxConstants.NODETYPE_ELEMENT) - { - name = shape.getAttribute('name'); - - if (name != null) - { - packageName = packageName.toLowerCase(); - var stencilName = name.replace(/ /g,"_"); - - if (install) - { - mxStencilRegistry.addStencil(packageName + stencilName.toLowerCase(), new mxStencil(shape)); - } - - if (postStencilLoad != null) - { - var w = shape.getAttribute('w'); - var h = shape.getAttribute('h'); - - w = (w == null) ? 80 : parseInt(w, 10); - h = (h == null) ? 80 : parseInt(h, 10); - - postStencilLoad(packageName, stencilName, name, w, h); - } - } - } - - shape = shape.nextSibling; - } - } -}; - -/** - * These overrides are only added if mxVertexHandler is defined (ie. not in embedded graph) - */ -if (typeof mxVertexHandler != 'undefined') -{ - (function() - { - // Sets colors for handles - mxConstants.HANDLE_FILLCOLOR = '#29b6f2'; - mxConstants.HANDLE_STROKECOLOR = '#0088cf'; - mxConstants.VERTEX_SELECTION_COLOR = '#00a8ff'; - mxConstants.OUTLINE_COLOR = '#00a8ff'; - mxConstants.OUTLINE_HANDLE_FILLCOLOR = '#99ccff'; - mxConstants.OUTLINE_HANDLE_STROKECOLOR = '#00a8ff'; - mxConstants.CONNECT_HANDLE_FILLCOLOR = '#cee7ff'; - mxConstants.EDGE_SELECTION_COLOR = '#00a8ff'; - mxConstants.DEFAULT_VALID_COLOR = '#00a8ff'; - mxConstants.LABEL_HANDLE_FILLCOLOR = '#cee7ff'; - mxConstants.GUIDE_COLOR = '#0088cf'; - mxConstants.HIGHLIGHT_OPACITY = 30; - mxConstants.HIGHLIGHT_SIZE = 5; - - // Enables snapping to off-grid terminals for edge waypoints - mxEdgeHandler.prototype.snapToTerminals = true; - - // Enables guides - mxGraphHandler.prototype.guidesEnabled = true; - - // Enables fading of rubberband - mxRubberband.prototype.fadeOut = true; - - // Alt-move disables guides - mxGuide.prototype.isEnabledForEvent = function(evt) - { - return !mxEvent.isAltDown(evt); - }; - - // Extends connection handler to enable ctrl+drag for cloning source cell - // since copyOnConnect is now disabled by default - var mxConnectionHandlerCreateTarget = mxConnectionHandler.prototype.isCreateTarget; - mxConnectionHandler.prototype.isCreateTarget = function(evt) - { - return mxEvent.isControlDown(evt) || mxConnectionHandlerCreateTarget.apply(this, arguments); - }; - - // Overrides highlight shape for connection points - mxConstraintHandler.prototype.createHighlightShape = function() - { - var hl = new mxEllipse(null, this.highlightColor, this.highlightColor, 0); - hl.opacity = mxConstants.HIGHLIGHT_OPACITY; - - return hl; - }; - - // Overrides edge preview to use current edge shape and default style - mxConnectionHandler.prototype.livePreview = true; - mxConnectionHandler.prototype.cursor = 'crosshair'; - - // Uses current edge style for connect preview - mxConnectionHandler.prototype.createEdgeState = function(me) - { - var style = this.graph.createCurrentEdgeStyle(); - var edge = this.graph.createEdge(null, null, null, null, null, style); - var state = new mxCellState(this.graph.view, edge, this.graph.getCellStyle(edge)); - - for (var key in this.graph.currentEdgeStyle) - { - state.style[key] = this.graph.currentEdgeStyle[key]; - } - - return state; - }; - - // Overrides dashed state with current edge style - var connectionHandlerCreateShape = mxConnectionHandler.prototype.createShape; - mxConnectionHandler.prototype.createShape = function() - { - var shape = connectionHandlerCreateShape.apply(this, arguments); - - shape.isDashed = this.graph.currentEdgeStyle[mxConstants.STYLE_DASHED] == '1'; - - return shape; - } - - // Overrides live preview to keep current style - mxConnectionHandler.prototype.updatePreview = function(valid) - { - // do not change color of preview - }; - - // Overrides connection handler to ignore edges instead of not allowing connections - var mxConnectionHandlerCreateMarker = mxConnectionHandler.prototype.createMarker; - mxConnectionHandler.prototype.createMarker = function() - { - var marker = mxConnectionHandlerCreateMarker.apply(this, arguments); - - var markerGetCell = marker.getCell; - marker.getCell = mxUtils.bind(this, function(me) - { - var result = markerGetCell.apply(this, arguments); - - this.error = null; - - return result; - }); - - return marker; - }; - - /** - * Function: isCellLocked - * - * Returns true if the given cell does not allow new connections to be created. - * This implementation returns false. - */ - mxConnectionHandler.prototype.isCellEnabled = function(cell) - { - return !this.graph.isCellLocked(cell); - }; - - /** - * - */ - Graph.prototype.defaultVertexStyle = {}; - - /** - * Contains the default style for edges. - */ - Graph.prototype.defaultEdgeStyle = {'edgeStyle': 'orthogonalEdgeStyle', 'rounded': '0', - 'jettySize': 'auto', 'orthogonalLoop': '1'}; - - /** - * Returns the current edge style as a string. - */ - Graph.prototype.createCurrentEdgeStyle = function() - { - var style = 'edgeStyle=' + (this.currentEdgeStyle['edgeStyle'] || 'none') + ';'; - - if (this.currentEdgeStyle['shape'] != null) - { - style += 'shape=' + this.currentEdgeStyle['shape'] + ';'; - } - - if (this.currentEdgeStyle['curved'] != null) - { - style += 'curved=' + this.currentEdgeStyle['curved'] + ';'; - } - - if (this.currentEdgeStyle['rounded'] != null) - { - style += 'rounded=' + this.currentEdgeStyle['rounded'] + ';'; - } - - if (this.currentEdgeStyle['comic'] != null) - { - style += 'comic=' + this.currentEdgeStyle['comic'] + ';'; - } - - if (this.currentEdgeStyle['jumpStyle'] != null) - { - style += 'jumpStyle=' + this.currentEdgeStyle['jumpStyle'] + ';'; - } - - if (this.currentEdgeStyle['jumpSize'] != null) - { - style += 'jumpSize=' + this.currentEdgeStyle['jumpSize'] + ';'; - } - - // Special logic for custom property of elbowEdgeStyle - if (this.currentEdgeStyle['edgeStyle'] == 'elbowEdgeStyle' && this.currentEdgeStyle['elbow'] != null) - { - style += 'elbow=' + this.currentEdgeStyle['elbow'] + ';'; - } - - if (this.currentEdgeStyle['html'] != null) - { - style += 'html=' + this.currentEdgeStyle['html'] + ';'; - } - else - { - style += 'html=1;'; - } - - return style; - }; - - /** - * Hook for subclassers. - */ - Graph.prototype.getPagePadding = function() - { - return new mxPoint(0, 0); - }; - - /** - * Loads the stylesheet for this graph. - */ - Graph.prototype.loadStylesheet = function() - { - var node = (this.themes != null) ? this.themes[this.defaultThemeName] : - (!mxStyleRegistry.dynamicLoading) ? null : - mxUtils.load(STYLE_PATH + '/default.xml').getDocumentElement(); - - if (node != null) - { - var dec = new mxCodec(node.ownerDocument); - dec.decode(node, this.getStylesheet()); - } - }; - - /** - * - */ - Graph.prototype.importGraphModel = function(node, dx, dy, crop) - { - dx = (dx != null) ? dx : 0; - dy = (dy != null) ? dy : 0; - - var cells = [] - var model = new mxGraphModel(); - var codec = new mxCodec(node.ownerDocument); - codec.decode(node, model); - - var childCount = model.getChildCount(model.getRoot()); - var targetChildCount = this.model.getChildCount(this.model.getRoot()); - - // Merges into active layer if one layer is pasted - this.model.beginUpdate(); - try - { - // Mapping for multiple calls to cloneCells with the same set of cells - var mapping = new Object(); - - for (var i = 0; i < childCount; i++) - { - var parent = model.getChildAt(model.getRoot(), i); - - // Adds cells to existing layer if not locked - if (childCount == 1 && !this.isCellLocked(this.getDefaultParent())) - { - var children = model.getChildren(parent); - cells = cells.concat(this.importCells(children, dx, dy, this.getDefaultParent(), null, mapping)); - } - else - { - // Delta is non cascading, needs separate move for layers - parent = this.importCells([parent], 0, 0, this.model.getRoot(), null, mapping)[0]; - var children = this.model.getChildren(parent); - this.moveCells(children, dx, dy); - cells = cells.concat(children); - } - } - - if (crop) - { - if (this.isGridEnabled()) - { - dx = this.snap(dx); - dy = this.snap(dy); - } - - var bounds = this.getBoundingBoxFromGeometry(cells, true); - - if (bounds != null) - { - this.moveCells(cells, dx - bounds.x, dy - bounds.y); - } - } - } - finally - { - this.model.endUpdate(); - } - - return cells; - } - - /** - * Overrides method to provide connection constraints for shapes. - */ - Graph.prototype.getAllConnectionConstraints = function(terminal, source) - { - if (terminal != null) - { - var constraints = mxUtils.getValue(terminal.style, 'points', null); - - if (constraints != null) - { - // Requires an array of arrays with x, y (0..1) and an optional - // perimeter (0 or 1), eg. points=[[0,0,1],[0,1,0],[1,1]] - var result = []; - - try - { - var c = JSON.parse(constraints); - - for (var i = 0; i < c.length; i++) - { - var tmp = c[i]; - result.push(new mxConnectionConstraint(new mxPoint(tmp[0], tmp[1]), (tmp.length > 2) ? tmp[2] != '0' : true)); - } - } - catch (e) - { - // ignore - } - - return result; - } - else - { - if (terminal.shape != null) - { - if (terminal.shape.stencil != null) - { - if (terminal.shape.stencil != null) - { - return terminal.shape.stencil.constraints; - } - } - else if (terminal.shape.constraints != null) - { - return terminal.shape.constraints; - } - } - } - } - - return null; - }; - - /** - * Inverts the elbow edge style without removing existing styles. - */ - Graph.prototype.flipEdge = function(edge) - { - if (edge != null) - { - var state = this.view.getState(edge); - var style = (state != null) ? state.style : this.getCellStyle(edge); - - if (style != null) - { - var elbow = mxUtils.getValue(style, mxConstants.STYLE_ELBOW, - mxConstants.ELBOW_HORIZONTAL); - var value = (elbow == mxConstants.ELBOW_HORIZONTAL) ? - mxConstants.ELBOW_VERTICAL : mxConstants.ELBOW_HORIZONTAL; - this.setCellStyles(mxConstants.STYLE_ELBOW, value, [edge]); - } - } - }; - - /** - * Disables drill-down for non-swimlanes. - */ - Graph.prototype.isValidRoot = function(cell) - { - // Counts non-relative children - var childCount = this.model.getChildCount(cell); - var realChildCount = 0; - - for (var i = 0; i < childCount; i++) - { - var child = this.model.getChildAt(cell, i); - - if (this.model.isVertex(child)) - { - var geometry = this.getCellGeometry(child); - - if (geometry != null && !geometry.relative) - { - realChildCount++; - } - } - } - - return realChildCount > 0 || this.isContainer(cell); - }; - - /** - * Disables drill-down for non-swimlanes. - */ - Graph.prototype.isValidDropTarget = function(cell) - { - var state = this.view.getState(cell); - var style = (state != null) ? state.style : this.getCellStyle(cell); - - return mxUtils.getValue(style, 'part', '0') != '1' && (this.isContainer(cell) || - (mxGraph.prototype.isValidDropTarget.apply(this, arguments) && - mxUtils.getValue(style, 'dropTarget', '1') != '0')); - }; - - /** - * Overrides createGroupCell to set the group style for new groups to 'group'. - */ - Graph.prototype.createGroupCell = function() - { - var group = mxGraph.prototype.createGroupCell.apply(this, arguments); - group.setStyle('group'); - - return group; - }; - - /** - * Disables extending parents with stack layouts on add - */ - Graph.prototype.isExtendParentsOnAdd = function(cell) - { - var result = mxGraph.prototype.isExtendParentsOnAdd.apply(this, arguments); - - if (result && cell != null && this.layoutManager != null) - { - var parent = this.model.getParent(cell); - - if (parent != null) - { - var layout = this.layoutManager.getLayout(parent); - - if (layout != null && layout.constructor == mxStackLayout) - { - result = false; - } - } - } - - return result; - }; - - /** - * Overrides autosize to add a border. - */ - Graph.prototype.getPreferredSizeForCell = function(cell) - { - var result = mxGraph.prototype.getPreferredSizeForCell.apply(this, arguments); - - // Adds buffer - if (result != null) - { - result.width += 10; - result.height += 4; - - if (this.gridEnabled) - { - result.width = this.snap(result.width); - result.height = this.snap(result.height); - } - } - - return result; - } - - /** - * Turns the given cells and returns the changed cells. - */ - Graph.prototype.turnShapes = function(cells) - { - var model = this.getModel(); - var select = []; - - model.beginUpdate(); - try - { - for (var i = 0; i < cells.length; i++) - { - var cell = cells[i]; - - if (model.isEdge(cell)) - { - var src = model.getTerminal(cell, true); - var trg = model.getTerminal(cell, false); - - model.setTerminal(cell, trg, true); - model.setTerminal(cell, src, false); - - var geo = model.getGeometry(cell); - - if (geo != null) - { - geo = geo.clone(); - - if (geo.points != null) - { - geo.points.reverse(); - } - - var sp = geo.getTerminalPoint(true); - var tp = geo.getTerminalPoint(false) - - geo.setTerminalPoint(sp, false); - geo.setTerminalPoint(tp, true); - model.setGeometry(cell, geo); - - // Inverts constraints - var edgeState = this.view.getState(cell); - var sourceState = this.view.getState(src); - var targetState = this.view.getState(trg); - - if (edgeState != null) - { - var sc = (sourceState != null) ? this.getConnectionConstraint(edgeState, sourceState, true) : null; - var tc = (targetState != null) ? this.getConnectionConstraint(edgeState, targetState, false) : null; - - this.setConnectionConstraint(cell, src, true, tc); - this.setConnectionConstraint(cell, trg, false, sc); - } - - select.push(cell); - } - } - else if (model.isVertex(cell)) - { - var geo = this.getCellGeometry(cell); - - if (geo != null) - { - // Rotates the size and position in the geometry - geo = geo.clone(); - geo.x += geo.width / 2 - geo.height / 2; - geo.y += geo.height / 2 - geo.width / 2; - var tmp = geo.width; - geo.width = geo.height; - geo.height = tmp; - model.setGeometry(cell, geo); - - // Reads the current direction and advances by 90 degrees - var state = this.view.getState(cell); - - if (state != null) - { - var dir = state.style[mxConstants.STYLE_DIRECTION] || 'east'/*default*/; - - if (dir == 'east') - { - dir = 'south'; - } - else if (dir == 'south') - { - dir = 'west'; - } - else if (dir == 'west') - { - dir = 'north'; - } - else if (dir == 'north') - { - dir = 'east'; - } - - this.setCellStyles(mxConstants.STYLE_DIRECTION, dir, [cell]); - } - - select.push(cell); - } - } - } - } - finally - { - model.endUpdate(); - } - - return select; - }; - - /** - * Returns true if the given stencil contains any placeholder text. - */ - Graph.prototype.stencilHasPlaceholders = function(stencil) - { - if (stencil != null && stencil.fgNode != null) - { - var node = stencil.fgNode.firstChild; - - while (node != null) - { - if (node.nodeName == 'text' && node.getAttribute('placeholders') == '1') - { - return true; - } - - node = node.nextSibling; - } - } - - return false; - }; - - /** - * Updates the child cells with placeholders if metadata of a cell has changed. - */ - Graph.prototype.processChange = function(change) - { - mxGraph.prototype.processChange.apply(this, arguments); - - if (change instanceof mxValueChange && change.cell != null && - change.cell.value != null && typeof(change.cell.value) == 'object') - { - // Invalidates all descendants with placeholders - var desc = this.model.getDescendants(change.cell); - - // LATER: Check if only label or tooltip have changed - if (desc.length > 0) - { - for (var i = 0; i < desc.length; i++) - { - var state = this.view.getState(desc[i]); - - if (state != null && state.shape != null && state.shape.stencil != null && - this.stencilHasPlaceholders(state.shape.stencil)) - { - this.removeStateForCell(desc[i]); - } - else if (this.isReplacePlaceholders(desc[i])) - { - this.view.invalidate(desc[i], false, false); - } - } - } - } - }; - - /** - * Replaces the given element with a span. - */ - Graph.prototype.replaceElement = function(elt, tagName) - { - var span = elt.ownerDocument.createElement((tagName != null) ? tagName : 'span'); - var attributes = Array.prototype.slice.call(elt.attributes); - - while (attr = attributes.pop()) - { - span.setAttribute(attr.nodeName, attr.nodeValue); - } - - span.innerHTML = elt.innerHTML; - elt.parentNode.replaceChild(span, elt); - }; - - /** - * Handles label changes for XML user objects. - */ - Graph.prototype.updateLabelElements = function(cells, fn, tagName) - { - cells = (cells != null) ? cells : this.getSelectionCells(); - var div = document.createElement('div'); - - for (var i = 0; i < cells.length; i++) - { - // Changes font tags inside HTML labels - if (this.isHtmlLabel(cells[i])) - { - var label = this.convertValueToString(cells[i]); - - if (label != null && label.length > 0) - { - div.innerHTML = label; - var elts = div.getElementsByTagName((tagName != null) ? tagName : '*'); - - for (var j = 0; j < elts.length; j++) - { - fn(elts[j]); - } - - if (div.innerHTML != label) - { - this.cellLabelChanged(cells[i], div.innerHTML); - } - } - } - } - }; - - /** - * Handles label changes for XML user objects. - */ - Graph.prototype.cellLabelChanged = function(cell, value, autoSize) - { - // Removes all illegal control characters in user input - value = this.zapGremlins(value); - - this.model.beginUpdate(); - try - { - if (cell.value != null && typeof cell.value == 'object') - { - if (this.isReplacePlaceholders(cell) && - cell.getAttribute('placeholder') != null) - { - // LATER: Handle delete, name change - var name = cell.getAttribute('placeholder'); - var current = cell; - - while (current != null) - { - if (current == this.model.getRoot() || (current.value != null && - typeof(current.value) == 'object' && current.hasAttribute(name))) - { - this.setAttributeForCell(current, name, value); - - break; - } - - current = this.model.getParent(current); - } - } - - var tmp = cell.value.cloneNode(true); - tmp.setAttribute('label', value); - value = tmp; - } - - mxGraph.prototype.cellLabelChanged.apply(this, arguments); - } - finally - { - this.model.endUpdate(); - } - }; - - /** - * Removes transparent empty groups if all children are removed. - */ - Graph.prototype.cellsRemoved = function(cells) - { - if (cells != null) - { - var dict = new mxDictionary(); - - for (var i = 0; i < cells.length; i++) - { - dict.put(cells[i], true); - } - - // LATER: Recurse up the cell hierarchy - var parents = []; - - for (var i = 0; i < cells.length; i++) - { - var parent = this.model.getParent(cells[i]); - - if (parent != null && !dict.get(parent)) - { - dict.put(parent, true); - parents.push(parent); - } - } - - for (var i = 0; i < parents.length; i++) - { - var state = this.view.getState(parents[i]); - - if (state != null && (this.model.isEdge(state.cell) || this.model.isVertex(state.cell)) && this.isCellDeletable(state.cell)) - { - var stroke = mxUtils.getValue(state.style, mxConstants.STYLE_STROKECOLOR, mxConstants.NONE); - var fill = mxUtils.getValue(state.style, mxConstants.STYLE_FILLCOLOR, mxConstants.NONE); - - if (stroke == mxConstants.NONE && fill == mxConstants.NONE) - { - var allChildren = true; - - for (var j = 0; j < this.model.getChildCount(state.cell) && allChildren; j++) - { - if (!dict.get(this.model.getChildAt(state.cell, j))) - { - allChildren = false; - } - } - - if (allChildren) - { - cells.push(state.cell); - } - } - } - } - } - - mxGraph.prototype.cellsRemoved.apply(this, arguments); - }; - - /** - * Overrides ungroup to check if group should be removed. - */ - Graph.prototype.removeCellsAfterUngroup = function(cells) - { - var cellsToRemove = []; - - for (var i = 0; i < cells.length; i++) - { - if (this.isCellDeletable(cells[i])) - { - var state = this.view.getState(cells[i]); - - if (state != null) - { - var stroke = mxUtils.getValue(state.style, mxConstants.STYLE_STROKECOLOR, mxConstants.NONE); - var fill = mxUtils.getValue(state.style, mxConstants.STYLE_FILLCOLOR, mxConstants.NONE); - - if (stroke == mxConstants.NONE && fill == mxConstants.NONE) - { - cellsToRemove.push(cells[i]); - } - } - } - } - - cells = cellsToRemove; - - mxGraph.prototype.removeCellsAfterUngroup.apply(this, arguments); - }; - - /** - * Sets the link for the given cell. - */ - Graph.prototype.setLinkForCell = function(cell, link) - { - this.setAttributeForCell(cell, 'link', link); - }; - - /** - * Sets the link for the given cell. - */ - Graph.prototype.setTooltipForCell = function(cell, link) - { - this.setAttributeForCell(cell, 'tooltip', link); - }; - - /** - * Sets the link for the given cell. - */ - Graph.prototype.setAttributeForCell = function(cell, attributeName, attributeValue) - { - var value = null; - - if (cell.value != null && typeof(cell.value) == 'object') - { - value = cell.value.cloneNode(true); - } - else - { - var doc = mxUtils.createXmlDocument(); - - value = doc.createElement('UserObject'); - value.setAttribute('label', cell.value || ''); - } - - if (attributeValue != null && attributeValue.length > 0) - { - value.setAttribute(attributeName, attributeValue); - } - else - { - value.removeAttribute(attributeName); - } - - this.model.setValue(cell, value); - }; - - /** - * Overridden to stop moving edge labels between cells. - */ - Graph.prototype.getDropTarget = function(cells, evt, cell, clone) - { - var model = this.getModel(); - - // Disables drop into group if alt is pressed - if (mxEvent.isAltDown(evt)) - { - return null; - } - - // Disables dragging edge labels out of edges - for (var i = 0; i < cells.length; i++) - { - if (this.model.isEdge(this.model.getParent(cells[i]))) - { - return null; - } - } - - return mxGraph.prototype.getDropTarget.apply(this, arguments); - }; - - /** - * Overrides double click handling to avoid accidental inserts of new labels in dblClick below. - */ - Graph.prototype.click = function(me) - { - mxGraph.prototype.click.call(this, me); - - // Stores state and source for checking in dblClick - this.firstClickState = me.getState(); - this.firstClickSource = me.getSource(); - }; - - /** - * Overrides double click handling to add the tolerance and inserting text. - */ - Graph.prototype.dblClick = function(evt, cell) - { - if (this.isEnabled()) - { - var pt = mxUtils.convertPoint(this.container, mxEvent.getClientX(evt), mxEvent.getClientY(evt)); - - // Automatically adds new child cells to edges on double click - if (evt != null && !this.model.isVertex(cell)) - { - var state = (this.model.isEdge(cell)) ? this.view.getState(cell) : null; - var src = mxEvent.getSource(evt); - - if (this.firstClickState == state && this.firstClickSource == src) - { - if (state == null || (state.text == null || state.text.node == null || - (!mxUtils.contains(state.text.boundingBox, pt.x, pt.y) && - !mxUtils.isAncestorNode(state.text.node, mxEvent.getSource(evt))))) - { - if ((state == null && !this.isCellLocked(this.getDefaultParent())) || - (state != null && !this.isCellLocked(state.cell))) - { - // Avoids accidental inserts on background - if (state != null || (mxClient.IS_VML && src == this.view.getCanvas()) || - (mxClient.IS_SVG && src == this.view.getCanvas().ownerSVGElement)) - { - cell = this.addText(pt.x, pt.y, state); - } - } - } - } - } - - mxGraph.prototype.dblClick.call(this, evt, cell); - } - }; - - /** - * Returns a point that specifies the location for inserting cells. - */ - Graph.prototype.getInsertPoint = function() - { - var gs = this.getGridSize(); - var dx = this.container.scrollLeft / this.view.scale - this.view.translate.x; - var dy = this.container.scrollTop / this.view.scale - this.view.translate.y; - - if (this.pageVisible) - { - var layout = this.getPageLayout(); - var page = this.getPageSize(); - dx = Math.max(dx, layout.x * page.width); - dy = Math.max(dy, layout.y * page.height); - } - - return new mxPoint(this.snap(dx + gs), this.snap(dy + gs)); - }; - - /** - * - */ - Graph.prototype.getFreeInsertPoint = function() - { - var view = this.view; - var bds = this.getGraphBounds(); - var pt = this.getInsertPoint(); - - // Places at same x-coord and 2 grid sizes below existing graph - var x = this.snap(Math.round(Math.max(pt.x, bds.x / view.scale - view.translate.x + - ((bds.width == 0) ? 2 * this.gridSize : 0)))); - var y = this.snap(Math.round(Math.max(pt.y, (bds.y + bds.height) / view.scale - view.translate.y + - 2 * this.gridSize))); - - return new mxPoint(x, y); - }; - - /** - * Hook for subclassers to return true if the current insert point was defined - * using a mouse hover event. - */ - Graph.prototype.isMouseInsertPoint = function() - { - return false; - }; - - /** - * Adds a new label at the given position and returns the new cell. State is - * an optional edge state to be used as the parent for the label. Vertices - * are not allowed currently as states. - */ - Graph.prototype.addText = function(x, y, state) - { - // Creates a new edge label with a predefined text - var label = new mxCell(); - label.value = 'Text'; - label.style = 'text;html=1;resizable=0;points=[];' - label.geometry = new mxGeometry(0, 0, 0, 0); - label.vertex = true; - - if (state != null) - { - label.style += 'align=center;verticalAlign=middle;labelBackgroundColor=#ffffff;' - label.geometry.relative = true; - label.connectable = false; - - // Resets the relative location stored inside the geometry - var pt2 = this.view.getRelativePoint(state, x, y); - label.geometry.x = Math.round(pt2.x * 10000) / 10000; - label.geometry.y = Math.round(pt2.y); - - // Resets the offset inside the geometry to find the offset from the resulting point - label.geometry.offset = new mxPoint(0, 0); - pt2 = this.view.getPoint(state, label.geometry); - - var scale = this.view.scale; - label.geometry.offset = new mxPoint(Math.round((x - pt2.x) / scale), Math.round((y - pt2.y) / scale)); - } - else - { - label.style += 'autosize=1;align=left;verticalAlign=top;spacingTop=-4;' - - var tr = this.view.translate; - label.geometry.width = 40; - label.geometry.height = 20; - label.geometry.x = Math.round(x / this.view.scale) - tr.x; - label.geometry.y = Math.round(y / this.view.scale) - tr.y; - } - - this.getModel().beginUpdate(); - try - { - this.addCells([label], (state != null) ? state.cell : null); - this.fireEvent(new mxEventObject('textInserted', 'cells', [label])); - // Updates size of text after possible change of style via event - this.autoSizeCell(label); - } - finally - { - this.getModel().endUpdate(); - } - - return label; - }; - - /** - * - */ - Graph.prototype.getAbsoluteUrl = function(url) - { - if (url != null && this.isRelativeUrl(url)) - { - if (url.charAt(0) == '#') - { - url = this.baseUrl + url; - } - else if (url.charAt(0) == '/') - { - url = this.domainUrl + url; - } - else - { - url = this.domainPathUrl + url; - } - } - - return url; - }; - - /** - * Adds a handler for clicking on shapes with links. This replaces all links in labels. - */ - Graph.prototype.addClickHandler = function(highlight, beforeClick, onClick) - { - // Replaces links in labels for consistent right-clicks - var checkLinks = mxUtils.bind(this, function() - { - var links = this.container.getElementsByTagName('a'); - - if (links != null) - { - for (var i = 0; i < links.length; i++) - { - var href = this.getAbsoluteUrl(links[i].getAttribute('href')); - - if (href != null) - { - links[i].setAttribute('rel', 'nofollow noopener noreferrer'); - links[i].setAttribute('href', href); - - if (beforeClick != null) - { - mxEvent.addGestureListeners(links[i], null, null, beforeClick); - } - } - } - } - }); - - this.model.addListener(mxEvent.CHANGE, checkLinks); - checkLinks(); - - var cursor = this.container.style.cursor; - var tol = this.getTolerance(); - var graph = this; - - var mouseListener = - { - currentState: null, - currentLink: null, - highlight: (highlight != null && highlight != '' && highlight != mxConstants.NONE) ? - new mxCellHighlight(graph, highlight, 4) : null, - startX: 0, - startY: 0, - scrollLeft: 0, - scrollTop: 0, - updateCurrentState: function(me) - { - var tmp = me.sourceState; - - // Gets topmost intersecting cell with link - if (tmp == null || graph.getLinkForCell(tmp.cell) == null) - { - var cell = graph.getCellAt(me.getGraphX(), me.getGraphY(), null, null, null, function(state, x, y) - { - return graph.getLinkForCell(state.cell) == null; - }); - - tmp = graph.view.getState(cell); - } - - if (tmp != this.currentState) - { - if (this.currentState != null) - { - this.clear(); - } - - this.currentState = tmp; - - if (this.currentState != null) - { - this.activate(this.currentState); - } - } - }, - mouseDown: function(sender, me) - { - this.startX = me.getGraphX(); - this.startY = me.getGraphY(); - this.scrollLeft = graph.container.scrollLeft; - this.scrollTop = graph.container.scrollTop; - - if (this.currentLink == null && graph.container.style.overflow == 'auto') - { - graph.container.style.cursor = 'move'; - } - - this.updateCurrentState(me); - }, - mouseMove: function(sender, me) - { - if (graph.isMouseDown) - { - if (this.currentLink != null) - { - var dx = Math.abs(this.startX - me.getGraphX()); - var dy = Math.abs(this.startY - me.getGraphY()); - - if (dx > tol || dy > tol) - { - this.clear(); - } - } - } - else - { - // Checks for parent link - var linkNode = me.getSource(); - - while (linkNode != null && linkNode.nodeName.toLowerCase() != 'a') - { - linkNode = linkNode.parentNode; - } - - if (linkNode != null) - { - this.clear(); - } - else - { - if (graph.tooltipHandler != null && this.currentLink != null && this.currentState != null) - { - graph.tooltipHandler.reset(me, true, this.currentState); - } - - if (this.currentState != null && (me.getState() == this.currentState || me.sourceState == null) && - graph.intersects(this.currentState, me.getGraphX(), me.getGraphY())) - { - return; - } - - this.updateCurrentState(me); - } - } - }, - mouseUp: function(sender, me) - { - var source = me.getSource(); - var evt = me.getEvent(); - - // Checks for parent link - var linkNode = source; - - while (linkNode != null && linkNode.nodeName.toLowerCase() != 'a') - { - linkNode = linkNode.parentNode; - } - - // Ignores clicks on links and collapse/expand icon - if (linkNode == null && - (((Math.abs(this.scrollLeft - graph.container.scrollLeft) < tol && - Math.abs(this.scrollTop - graph.container.scrollTop) < tol) && - (me.sourceState == null || !me.isSource(me.sourceState.control))) && - (((mxEvent.isLeftMouseButton(evt) || mxEvent.isMiddleMouseButton(evt)) && - !mxEvent.isPopupTrigger(evt)) || mxEvent.isTouchEvent(evt)))) - { - if (this.currentLink != null) - { - var blank = graph.isBlankLink(this.currentLink); - - if ((this.currentLink.substring(0, 5) === 'data:' || - !blank) && beforeClick != null) - { - beforeClick(evt, this.currentLink); - } - - if (!mxEvent.isConsumed(evt)) - { - var target = (mxEvent.isMiddleMouseButton(evt)) ? '_blank' : - ((blank) ? graph.linkTarget : '_top'); - graph.openLink(this.currentLink, target); - me.consume(); - } - } - else if (onClick != null && !me.isConsumed() && - (Math.abs(this.scrollLeft - graph.container.scrollLeft) < tol && - Math.abs(this.scrollTop - graph.container.scrollTop) < tol) && - (Math.abs(this.startX - me.getGraphX()) < tol && - Math.abs(this.startY - me.getGraphY()) < tol)) - { - onClick(me.getEvent()); - } - } - - this.clear(); - }, - activate: function(state) - { - this.currentLink = graph.getAbsoluteUrl(graph.getLinkForCell(state.cell)); - - if (this.currentLink != null) - { - graph.container.style.cursor = 'pointer'; - - if (this.highlight != null) - { - this.highlight.highlight(state); - } - } - }, - clear: function() - { - if (graph.container != null) - { - graph.container.style.cursor = cursor; - } - - this.currentState = null; - this.currentLink = null; - - if (this.highlight != null) - { - this.highlight.hide(); - } - - if (graph.tooltipHandler != null) - { - graph.tooltipHandler.hide(); - } - } - }; - - // Ignores built-in click handling - graph.click = function(me) {}; - graph.addMouseListener(mouseListener); - - mxEvent.addListener(document, 'mouseleave', function(evt) - { - mouseListener.clear(); - }); - }; - - /** - * Duplicates the given cells and returns the duplicates. - */ - Graph.prototype.duplicateCells = function(cells, append) - { - cells = (cells != null) ? cells : this.getSelectionCells(); - append = (append != null) ? append : true; - - cells = this.model.getTopmostCells(cells); - - var model = this.getModel(); - var s = this.gridSize; - var select = []; - - model.beginUpdate(); - try - { - var clones = this.cloneCells(cells, false); - - for (var i = 0; i < cells.length; i++) - { - var parent = model.getParent(cells[i]); - var child = this.moveCells([clones[i]], s, s, false)[0]; - select.push(child); - - if (append) - { - model.add(parent, clones[i]); - } - else - { - // Maintains child index by inserting after clone in parent - var index = parent.getIndex(cells[i]); - model.add(parent, clones[i], index + 1); - } - } - } - finally - { - model.endUpdate(); - } - - return select; - }; - - /** - * Inserts the given image at the cursor in a content editable text box using - * the insertimage command on the document instance. - */ - Graph.prototype.insertImage = function(newValue, w, h) - { - // To find the new image, we create a list of all existing links first - if (newValue != null) - { - var tmp = this.cellEditor.textarea.getElementsByTagName('img'); - var oldImages = []; - - for (var i = 0; i < tmp.length; i++) - { - oldImages.push(tmp[i]); - } - - // LATER: Fix inserting link/image in IE8/quirks after focus lost - document.execCommand('insertimage', false, newValue); - - // Sets size of new image - var newImages = this.cellEditor.textarea.getElementsByTagName('img'); - - if (newImages.length == oldImages.length + 1) - { - // Inverse order in favor of appended images - for (var i = newImages.length - 1; i >= 0; i--) - { - if (i == 0 || newImages[i] != oldImages[i - 1]) - { - // Workaround for lost styles during undo and redo is using attributes - newImages[i].setAttribute('width', w); - newImages[i].setAttribute('height', h); - - break; - } - } - } - } - }; - - /** - * Inserts the given image at the cursor in a content editable text box using - * the insertimage command on the document instance. - */ - Graph.prototype.insertLink = function(value) - { - if (value.length == 0) - { - document.execCommand('unlink', false); - } - else if (mxClient.IS_FF) - { - // Workaround for Firefox that adds a new link and removes - // the href from the inner link if its parent is a span is - // to remove all inner links inside the new outer link - var tmp = this.cellEditor.textarea.getElementsByTagName('a'); - var oldLinks = []; - - for (var i = 0; i < tmp.length; i++) - { - oldLinks.push(tmp[i]); - } - - document.execCommand('createlink', false, mxUtils.trim(value)); - - // Finds the new link element - var newLinks = this.cellEditor.textarea.getElementsByTagName('a'); - - if (newLinks.length == oldLinks.length + 1) - { - // Inverse order in favor of appended links - for (var i = newLinks.length - 1; i >= 0; i--) - { - if (newLinks[i] != oldLinks[i - 1]) - { - // Removes all inner links from the new link and - // moves the children to the inner link parent - var tmp = newLinks[i].getElementsByTagName('a'); - - while (tmp.length > 0) - { - var parent = tmp[0].parentNode; - - while (tmp[0].firstChild != null) - { - parent.insertBefore(tmp[0].firstChild, tmp[0]); - } - - parent.removeChild(tmp[0]); - } - - break; - } - } - } - } - else - { - // LATER: Fix inserting link/image in IE8/quirks after focus lost - document.execCommand('createlink', false, mxUtils.trim(value)); - } - }; - - /** - * - * @param cell - * @returns {Boolean} - */ - Graph.prototype.isCellResizable = function(cell) - { - var result = mxGraph.prototype.isCellResizable.apply(this, arguments); - - var state = this.view.getState(cell); - var style = (state != null) ? state.style : this.getCellStyle(cell); - - return result || (mxUtils.getValue(style, mxConstants.STYLE_RESIZABLE, '1') != '0' && - style[mxConstants.STYLE_WHITE_SPACE] == 'wrap'); - }; - - /** - * Function: distributeCells - * - * Distribuets the centers of the given cells equally along the available - * horizontal or vertical space. - * - * Parameters: - * - * horizontal - Boolean that specifies the direction of the distribution. - * cells - Optional array of to be distributed. Edges are ignored. - */ - Graph.prototype.distributeCells = function(horizontal, cells) - { - if (cells == null) - { - cells = this.getSelectionCells(); - } - - if (cells != null && cells.length > 1) - { - var vertices = []; - var max = null; - var min = null; - - for (var i = 0; i < cells.length; i++) - { - if (this.getModel().isVertex(cells[i])) - { - var state = this.view.getState(cells[i]); - - if (state != null) - { - var tmp = (horizontal) ? state.getCenterX() : state.getCenterY(); - max = (max != null) ? Math.max(max, tmp) : tmp; - min = (min != null) ? Math.min(min, tmp) : tmp; - - vertices.push(state); - } - } - } - - if (vertices.length > 2) - { - vertices.sort(function(a, b) - { - return (horizontal) ? a.x - b.x : a.y - b.y; - }); - - var t = this.view.translate; - var s = this.view.scale; - - min = min / s - ((horizontal) ? t.x : t.y); - max = max / s - ((horizontal) ? t.x : t.y); - - this.getModel().beginUpdate(); - try - { - var dt = (max - min) / (vertices.length - 1); - var t0 = min; - - for (var i = 1; i < vertices.length - 1; i++) - { - var pstate = this.view.getState(this.model.getParent(vertices[i].cell)); - var geo = this.getCellGeometry(vertices[i].cell); - t0 += dt; - - if (geo != null && pstate != null) - { - geo = geo.clone(); - - if (horizontal) - { - geo.x = Math.round(t0 - geo.width / 2) - pstate.origin.x; - } - else - { - geo.y = Math.round(t0 - geo.height / 2) - pstate.origin.y; - } - - this.getModel().setGeometry(vertices[i].cell, geo); - } - } - } - finally - { - this.getModel().endUpdate(); - } - } - } - - return cells; - }; - - /** - * Adds meta-drag an Mac. - * @param evt - * @returns - */ - Graph.prototype.isCloneEvent = function(evt) - { - return (mxClient.IS_MAC && mxEvent.isMetaDown(evt)) || mxEvent.isControlDown(evt); - }; - - /** - * Translates this point by the given vector. - * - * @param {number} dx X-coordinate of the translation. - * @param {number} dy Y-coordinate of the translation. - */ - Graph.prototype.encodeCells = function(cells) - { - var clones = this.cloneCells(cells); - - // Creates a dictionary for fast lookups - var dict = new mxDictionary(); - - for (var i = 0; i < cells.length; i++) - { - dict.put(cells[i], true); - } - - // Checks for orphaned relative children and makes absolute - for (var i = 0; i < clones.length; i++) - { - var state = this.view.getState(cells[i]); - - if (state != null) - { - var geo = this.getCellGeometry(clones[i]); - - if (geo != null && geo.relative && !this.model.isEdge(cells[i]) && - !dict.get(this.model.getParent(cells[i]))) - { - geo.relative = false; - geo.x = state.x / state.view.scale - state.view.translate.x; - geo.y = state.y / state.view.scale - state.view.translate.y; - } - } - } - - var codec = new mxCodec(); - var model = new mxGraphModel(); - var parent = model.getChildAt(model.getRoot(), 0); - - for (var i = 0; i < cells.length; i++) - { - model.add(parent, clones[i]); - } - - return codec.encode(model); - }; - - /** - * Translates this point by the given vector. - * - * @param {number} dx X-coordinate of the translation. - * @param {number} dy Y-coordinate of the translation. - */ - Graph.prototype.createSvgImageExport = function() - { - var exp = new mxImageExport(); - - // Adds hyperlinks (experimental) - exp.getLinkForCellState = mxUtils.bind(this, function(state, canvas) - { - return this.getLinkForCell(state.cell); - }); - - return exp; - }; - - /** - * Translates this point by the given vector. - * - * @param {number} dx X-coordinate of the translation. - * @param {number} dy Y-coordinate of the translation. - */ - Graph.prototype.getSvg = function(background, scale, border, nocrop, crisp, ignoreSelection, showText, imgExport) - { - //Disable Css Transforms if it is used - var origUseCssTrans = this.useCssTransforms; - - if (origUseCssTrans) - { - this.useCssTransforms = false; - this.view.revalidate(); - this.sizeDidChange(); - } - - try - { - scale = (scale != null) ? scale : 1; - border = (border != null) ? border : 0; - crisp = (crisp != null) ? crisp : true; - ignoreSelection = (ignoreSelection != null) ? ignoreSelection : true; - showText = (showText != null) ? showText : true; - - var bounds = (ignoreSelection || nocrop) ? - this.getGraphBounds() : this.getBoundingBox(this.getSelectionCells()); - - if (bounds == null) - { - throw Error(mxResources.get('drawingEmpty')); - } - - var vs = this.view.scale; - - // Prepares SVG document that holds the output - var svgDoc = mxUtils.createXmlDocument(); - var root = (svgDoc.createElementNS != null) ? - svgDoc.createElementNS(mxConstants.NS_SVG, 'svg') : svgDoc.createElement('svg'); - - if (background != null) - { - if (root.style != null) - { - root.style.backgroundColor = background; - } - else - { - root.setAttribute('style', 'background-color:' + background); - } - } - - if (svgDoc.createElementNS == null) - { - root.setAttribute('xmlns', mxConstants.NS_SVG); - root.setAttribute('xmlns:xlink', mxConstants.NS_XLINK); - } - else - { - // KNOWN: Ignored in IE9-11, adds namespace for each image element instead. No workaround. - root.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xlink', mxConstants.NS_XLINK); - } - - var s = scale / vs; - root.setAttribute('width', Math.max(1, Math.ceil(bounds.width * s) + 2 * border) + 'px'); - root.setAttribute('height', Math.max(1, Math.ceil(bounds.height * s) + 2 * border) + 'px'); - root.setAttribute('version', '1.1'); - - // Adds group for anti-aliasing via transform - var node = root; - - if (crisp) - { - var group = (svgDoc.createElementNS != null) ? - svgDoc.createElementNS(mxConstants.NS_SVG, 'g') : svgDoc.createElement('g'); - group.setAttribute('transform', 'translate(0.5,0.5)'); - root.appendChild(group); - svgDoc.appendChild(root); - node = group; - } - else - { - svgDoc.appendChild(root); - } - - // Renders graph. Offset will be multiplied with state's scale when painting state. - // TextOffset only seems to affect FF output but used everywhere for consistency. - var svgCanvas = this.createSvgCanvas(node); - svgCanvas.foOffset = (crisp) ? -0.5 : 0; - svgCanvas.textOffset = (crisp) ? -0.5 : 0; - svgCanvas.imageOffset = (crisp) ? -0.5 : 0; - svgCanvas.translate(Math.floor((border / scale - bounds.x) / vs), Math.floor((border / scale - bounds.y) / vs)); - - // Convert HTML entities - var htmlConverter = document.createElement('textarea'); - - // Adds simple text fallback for viewers with no support for foreignObjects - var createAlternateContent = svgCanvas.createAlternateContent; - svgCanvas.createAlternateContent = function(fo, x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation) - { - var s = this.state; - - // Assumes a max character width of 0.2em - if (this.foAltText != null && (w == 0 || (s.fontSize != 0 && str.length < (w * 5) / s.fontSize))) - { - var alt = this.createElement('text'); - alt.setAttribute('x', Math.round(w / 2)); - alt.setAttribute('y', Math.round((h + s.fontSize) / 2)); - alt.setAttribute('fill', s.fontColor || 'black'); - alt.setAttribute('text-anchor', 'middle'); - alt.setAttribute('font-size', Math.round(s.fontSize) + 'px'); - alt.setAttribute('font-family', s.fontFamily); - - if ((s.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD) - { - alt.setAttribute('font-weight', 'bold'); - } - - if ((s.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC) - { - alt.setAttribute('font-style', 'italic'); - } - - if ((s.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE) - { - alt.setAttribute('text-decoration', 'underline'); - } - - try - { - htmlConverter.innerHTML = str; - alt.textContent = htmlConverter.value; - - return alt; - } - catch (e) - { - return createAlternateContent.apply(this, arguments); - } - } - else - { - return createAlternateContent.apply(this, arguments); - } - }; - - // Paints background image - var bgImg = this.backgroundImage; - - if (bgImg != null) - { - var s2 = vs / scale; - var tr = this.view.translate; - var tmp = new mxRectangle(tr.x * s2, tr.y * s2, bgImg.width * s2, bgImg.height * s2); - - // Checks if visible - if (mxUtils.intersects(bounds, tmp)) - { - svgCanvas.image(tr.x, tr.y, bgImg.width, bgImg.height, bgImg.src, true); - } - } - - svgCanvas.scale(s); - svgCanvas.textEnabled = showText; - - imgExport = (imgExport != null) ? imgExport : this.createSvgImageExport(); - var imgExportDrawCellState = imgExport.drawCellState; - - // Implements ignoreSelection flag - imgExport.drawCellState = function(state, canvas) - { - var graph = state.view.graph; - var selected = graph.isCellSelected(state.cell); - var parent = graph.model.getParent(state.cell); - - // Checks if parent cell is selected - while (!ignoreSelection && !selected && parent != null) - { - selected = graph.isCellSelected(parent); - parent = graph.model.getParent(parent); - } - - if (ignoreSelection || selected) - { - imgExportDrawCellState.apply(this, arguments); - } - }; - - imgExport.drawState(this.getView().getState(this.model.root), svgCanvas); - - return root; - } - finally - { - if (origUseCssTrans) - { - this.useCssTransforms = true; - this.view.revalidate(); - this.sizeDidChange(); - } - } - }; - - /** - * Hook for creating the canvas used in getSvg. - */ - Graph.prototype.createSvgCanvas = function(node) - { - return new mxSvgCanvas2D(node); - }; - - /** - * Returns the first ancestor of the current selection with the given name. - */ - Graph.prototype.getSelectedElement = function() - { - var node = null; - - if (window.getSelection) - { - var sel = window.getSelection(); - - if (sel.getRangeAt && sel.rangeCount) - { - var range = sel.getRangeAt(0); - node = range.commonAncestorContainer; - } - } - else if (document.selection) - { - node = document.selection.createRange().parentElement(); - } - - return node; - }; - - /** - * Returns the first ancestor of the current selection with the given name. - */ - Graph.prototype.getParentByName = function(node, name, stopAt) - { - while (node != null) - { - if (node.nodeName == name) - { - return node; - } - - if (node == stopAt) - { - return null; - } - - node = node.parentNode; - } - - return node; - }; - - /** - * Selects the given node. - */ - Graph.prototype.selectNode = function(node) - { - var sel = null; - - // IE9 and non-IE - if (window.getSelection) - { - sel = window.getSelection(); - - if (sel.getRangeAt && sel.rangeCount) - { - var range = document.createRange(); - range.selectNode(node); - sel.removeAllRanges(); - sel.addRange(range); - } - } - // IE < 9 - else if ((sel = document.selection) && sel.type != 'Control') - { - var originalRange = sel.createRange(); - originalRange.collapse(true); - var range = sel.createRange(); - range.setEndPoint('StartToStart', originalRange); - range.select(); - } - }; - - /** - * Inserts a new row into the given table. - */ - Graph.prototype.insertRow = function(table, index) - { - var bd = table.tBodies[0]; - var cells = bd.rows[0].cells; - var cols = 0; - - // Counts columns including colspans - for (var i = 0; i < cells.length; i++) - { - var colspan = cells[i].getAttribute('colspan'); - cols += (colspan != null) ? parseInt(colspan) : 1; - } - - var row = bd.insertRow(index); - - for (var i = 0; i < cols; i++) - { - mxUtils.br(row.insertCell(-1)); - } - - return row.cells[0]; - }; - - /** - * Deletes the given column. - */ - Graph.prototype.deleteRow = function(table, index) - { - table.tBodies[0].deleteRow(index); - }; - - /** - * Deletes the given column. - */ - Graph.prototype.insertColumn = function(table, index) - { - var hd = table.tHead; - - if (hd != null) - { - // TODO: use colIndex - for (var h = 0; h < hd.rows.length; h++) - { - var th = document.createElement('th'); - hd.rows[h].appendChild(th); - mxUtils.br(th); - } - } - - var bd = table.tBodies[0]; - - for (var i = 0; i < bd.rows.length; i++) - { - var cell = bd.rows[i].insertCell(index); - mxUtils.br(cell); - } - - return bd.rows[0].cells[(index >= 0) ? index : bd.rows[0].cells.length - 1]; - }; - - /** - * Deletes the given column. - */ - Graph.prototype.deleteColumn = function(table, index) - { - if (index >= 0) - { - var bd = table.tBodies[0]; - var rows = bd.rows; - - for (var i = 0; i < rows.length; i++) - { - if (rows[i].cells.length > index) - { - rows[i].deleteCell(index); - } - } - } - }; - - /** - * Inserts the given HTML at the caret position (no undo). - */ - Graph.prototype.pasteHtmlAtCaret = function(html) - { - var sel, range; - - // IE9 and non-IE - if (window.getSelection) - { - sel = window.getSelection(); - - if (sel.getRangeAt && sel.rangeCount) - { - range = sel.getRangeAt(0); - range.deleteContents(); - - // Range.createContextualFragment() would be useful here but is - // only relatively recently standardized and is not supported in - // some browsers (IE9, for one) - var el = document.createElement("div"); - el.innerHTML = html; - var frag = document.createDocumentFragment(), node; - - while ((node = el.firstChild)) - { - lastNode = frag.appendChild(node); - } - - range.insertNode(frag); - } - } - // IE < 9 - else if ((sel = document.selection) && sel.type != "Control") - { - // FIXME: Does not work if selection is empty - sel.createRange().pasteHTML(html); - } - }; - - /** - * Creates an anchor elements for handling the given link in the - * hint that is shown when the cell is selected. - */ - Graph.prototype.createLinkForHint = function(link, label) - { - link = (link != null) ? link : 'javascript:void(0);'; - - if (label == null || label.length == 0) - { - if (this.isCustomLink(link)) - { - label = this.getLinkTitle(link); - } - else - { - label = link; - } - } - - // Helper function to shorten strings - function short(str, max) - { - if (str.length > max) - { - str = str.substring(0, Math.round(max / 2)) + '...' + - str.substring(str.length - Math.round(max / 4)); - } - - return str; - }; - - var a = document.createElement('a'); - a.setAttribute('rel', 'nofollow noopener noreferrer'); - a.setAttribute('href', this.getAbsoluteUrl(link)); - a.setAttribute('title', short((this.isCustomLink(link)) ? - this.getLinkTitle(link) : link, 80)); - - if (this.linkTarget != null) - { - a.setAttribute('target', this.linkTarget); - } - - // Adds shortened label to link - mxUtils.write(a, short(label, 40)); - - // Handles custom links - if (this.isCustomLink(link)) - { - mxEvent.addListener(a, 'click', mxUtils.bind(this, function(evt) - { - this.customLinkClicked(link); - mxEvent.consume(evt); - })); - } - - return a; - }; - - /** - * Customized graph for touch devices. - */ - Graph.prototype.initTouch = function() - { - // Disables new connections via "hotspot" - this.connectionHandler.marker.isEnabled = function() - { - return this.graph.connectionHandler.first != null; - }; - - // Hides menu when editing starts - this.addListener(mxEvent.START_EDITING, function(sender, evt) - { - this.popupMenuHandler.hideMenu(); - }); - - // Adds custom hit detection if native hit detection found no cell - var graphUpdateMouseEvent = this.updateMouseEvent; - this.updateMouseEvent = function(me) - { - me = graphUpdateMouseEvent.apply(this, arguments); - - if (mxEvent.isTouchEvent(me.getEvent()) && me.getState() == null) - { - var cell = this.getCellAt(me.graphX, me.graphY); - - if (cell != null && this.isSwimlane(cell) && this.hitsSwimlaneContent(cell, me.graphX, me.graphY)) - { - cell = null; - } - else - { - me.state = this.view.getState(cell); - - if (me.state != null && me.state.shape != null) - { - this.container.style.cursor = me.state.shape.node.style.cursor; - } - } - } - - if (me.getState() == null && this.isEnabled()) - { - this.container.style.cursor = 'default'; - } - - return me; - }; - - // Context menu trigger implementation depending on current selection state - // combined with support for normal popup trigger. - var cellSelected = false; - var selectionEmpty = false; - var menuShowing = false; - - var oldFireMouseEvent = this.fireMouseEvent; - - this.fireMouseEvent = function(evtName, me, sender) - { - if (evtName == mxEvent.MOUSE_DOWN) - { - // For hit detection on edges - me = this.updateMouseEvent(me); - - cellSelected = this.isCellSelected(me.getCell()); - selectionEmpty = this.isSelectionEmpty(); - menuShowing = this.popupMenuHandler.isMenuShowing(); - } - - oldFireMouseEvent.apply(this, arguments); - }; - - // Shows popup menu if cell was selected or selection was empty and background was clicked - // FIXME: Conflicts with mxPopupMenuHandler.prototype.getCellForPopupEvent in Editor.js by - // selecting parent for selected children in groups before this check can be made. - this.popupMenuHandler.mouseUp = mxUtils.bind(this, function(sender, me) - { - this.popupMenuHandler.popupTrigger = !this.isEditing() && this.isEnabled() && - (me.getState() == null || !me.isSource(me.getState().control)) && - (this.popupMenuHandler.popupTrigger || (!menuShowing && !mxEvent.isMouseEvent(me.getEvent()) && - ((selectionEmpty && me.getCell() == null && this.isSelectionEmpty()) || - (cellSelected && this.isCellSelected(me.getCell()))))); - mxPopupMenuHandler.prototype.mouseUp.apply(this.popupMenuHandler, arguments); - }); - }; - - /** - * HTML in-place editor - */ - mxCellEditor.prototype.isContentEditing = function() - { - var state = this.graph.view.getState(this.editingCell); - - return state != null && state.style['html'] == 1; - }; - - /** - * Creates the keyboard event handler for the current graph and history. - */ - mxCellEditor.prototype.saveSelection = function() - { - if (window.getSelection) - { - var sel = window.getSelection(); - - if (sel.getRangeAt && sel.rangeCount) - { - var ranges = []; - - for (var i = 0, len = sel.rangeCount; i < len; ++i) - { - ranges.push(sel.getRangeAt(i)); - } - - return ranges; - } - } - else if (document.selection && document.selection.createRange) - { - return document.selection.createRange(); - } - - return null; - }; - - /** - * Creates the keyboard event handler for the current graph and history. - */ - mxCellEditor.prototype.restoreSelection = function(savedSel) - { - try - { - if (savedSel) - { - if (window.getSelection) - { - sel = window.getSelection(); - sel.removeAllRanges(); - - for (var i = 0, len = savedSel.length; i < len; ++i) - { - sel.addRange(savedSel[i]); - } - } - else if (document.selection && savedSel.select) - { - savedSel.select(); - } - } - } - catch (e) - { - // ignore - } - }; - - /** - * Handling of special nl2Br style for not converting newlines to breaks in HTML labels. - * NOTE: Since it's easier to set this when the label is created we assume that it does - * not change during the lifetime of the mxText instance. - */ - var mxCellRendererInitializeLabel = mxCellRenderer.prototype.initializeLabel; - mxCellRenderer.prototype.initializeLabel = function(state) - { - if (state.text != null) - { - state.text.replaceLinefeeds = mxUtils.getValue(state.style, 'nl2Br', '1') != '0'; - } - - mxCellRendererInitializeLabel.apply(this, arguments); - }; - - var mxConstraintHandlerUpdate = mxConstraintHandler.prototype.update; - mxConstraintHandler.prototype.update = function(me, source) - { - if (this.isKeepFocusEvent(me) || !mxEvent.isAltDown(me.getEvent())) - { - mxConstraintHandlerUpdate.apply(this, arguments); - } - else - { - this.reset(); - } - }; - - /** - * No dashed shapes. - */ - mxGuide.prototype.createGuideShape = function(horizontal) - { - var guide = new mxPolyline([], mxConstants.GUIDE_COLOR, mxConstants.GUIDE_STROKEWIDTH); - - return guide; - }; - - /** - * HTML in-place editor - */ - mxCellEditor.prototype.escapeCancelsEditing = false; - - var mxCellEditorStartEditing = mxCellEditor.prototype.startEditing; - mxCellEditor.prototype.startEditing = function(cell, trigger) - { - mxCellEditorStartEditing.apply(this, arguments); - - // Overrides class in case of HTML content to add - // dashed borders for divs and table cells - var state = this.graph.view.getState(cell); - - if (state != null && state.style['html'] == 1) - { - this.textarea.className = 'mxCellEditor geContentEditable'; - } - else - { - this.textarea.className = 'mxCellEditor mxPlainTextEditor'; - } - - // Toggles markup vs wysiwyg mode - this.codeViewMode = false; - - // Stores current selection range when switching between markup and code - this.switchSelectionState = null; - - // Selects editing cell - this.graph.setSelectionCell(cell); - - // Enables focus outline for edges and edge labels - var parent = this.graph.getModel().getParent(cell); - var geo = this.graph.getCellGeometry(cell); - - if ((this.graph.getModel().isEdge(parent) && geo != null && geo.relative) || - this.graph.getModel().isEdge(cell)) - { - // Quirks does not support outline at all so use border instead - if (mxClient.IS_QUIRKS) - { - this.textarea.style.border = 'gray dotted 1px'; - } - // IE>8 and FF on Windows uses outline default of none - else if (mxClient.IS_IE || mxClient.IS_IE11 || (mxClient.IS_FF && mxClient.IS_WIN)) - { - this.textarea.style.outline = 'gray dotted 1px'; - } - else - { - this.textarea.style.outline = ''; - } - } - else if (mxClient.IS_QUIRKS) - { - this.textarea.style.outline = 'none'; - this.textarea.style.border = ''; - } - } - - /** - * HTML in-place editor - */ - var cellEditorInstallListeners = mxCellEditor.prototype.installListeners; - mxCellEditor.prototype.installListeners = function(elt) - { - cellEditorInstallListeners.apply(this, arguments); - - // Adds a reference from the clone to the original node, recursively - function reference(node, clone) - { - clone.originalNode = node; - - node = node.firstChild; - var child = clone.firstChild; - - while (node != null && child != null) - { - reference(node, child); - node = node.nextSibling; - child = child.nextSibling; - } - - return clone; - }; - - // Checks the given node for new nodes, recursively - function checkNode(node, clone) - { - if (node != null) - { - if (clone.originalNode != node) - { - cleanNode(node); - } - else - { - node = node.firstChild; - clone = clone.firstChild; - - while (node != null) - { - var nextNode = node.nextSibling; - - if (clone == null) - { - cleanNode(node); - } - else - { - checkNode(node, clone); - clone = clone.nextSibling; - } - - node = nextNode; - } - } - } - }; - - // Removes unused DOM nodes and attributes, recursively - function cleanNode(node) - { - var child = node.firstChild; - - while (child != null) - { - var next = child.nextSibling; - cleanNode(child); - child = next; - } - - if ((node.nodeType != 1 || (node.nodeName !== 'BR' && node.firstChild == null)) && - (node.nodeType != 3 || mxUtils.trim(mxUtils.getTextContent(node)).length == 0)) - { - node.parentNode.removeChild(node); - } - else - { - // Removes linefeeds - if (node.nodeType == 3) - { - mxUtils.setTextContent(node, mxUtils.getTextContent(node).replace(/\n|\r/g, '')); - } - - // Removes CSS classes and styles (for Word and Excel) - if (node.nodeType == 1) - { - node.removeAttribute('style'); - node.removeAttribute('class'); - node.removeAttribute('width'); - node.removeAttribute('cellpadding'); - node.removeAttribute('cellspacing'); - node.removeAttribute('border'); - } - } - }; - - // Handles paste from Word, Excel etc by removing styles, classnames and unused nodes - // LATER: Fix undo/redo for paste - if (!mxClient.IS_QUIRKS && document.documentMode !== 7 && document.documentMode !== 8) - { - mxEvent.addListener(this.textarea, 'paste', mxUtils.bind(this, function(evt) - { - var clone = reference(this.textarea, this.textarea.cloneNode(true)); - - window.setTimeout(mxUtils.bind(this, function() - { - checkNode(this.textarea, clone); - }), 0); - })); - } - }; - - mxCellEditor.prototype.toggleViewMode = function() - { - var state = this.graph.view.getState(this.editingCell); - var nl2Br = state != null && mxUtils.getValue(state.style, 'nl2Br', '1') != '0'; - var tmp = this.saveSelection(); - - if (!this.codeViewMode) - { - // Clears the initial empty label on the first keystroke - if (this.clearOnChange && this.textarea.innerHTML == this.getEmptyLabelText()) - { - this.clearOnChange = false; - this.textarea.innerHTML = ''; - } - - // Removes newlines from HTML and converts breaks to newlines - // to match the HTML output in plain text - var content = mxUtils.htmlEntities(this.textarea.innerHTML); - - // Workaround for trailing line breaks being ignored in the editor - if (!mxClient.IS_QUIRKS && document.documentMode != 8) - { - content = mxUtils.replaceTrailingNewlines(content, '

'); - } - - content = this.graph.sanitizeHtml((nl2Br) ? content.replace(/\n/g, '').replace(/<br\s*.?>/g, '
') : content, true); - this.textarea.className = 'mxCellEditor mxPlainTextEditor'; - - var size = mxConstants.DEFAULT_FONTSIZE; - - this.textarea.style.lineHeight = (mxConstants.ABSOLUTE_LINE_HEIGHT) ? Math.round(size * mxConstants.LINE_HEIGHT) + 'px' : mxConstants.LINE_HEIGHT; - this.textarea.style.fontSize = Math.round(size) + 'px'; - this.textarea.style.textDecoration = ''; - this.textarea.style.fontWeight = 'normal'; - this.textarea.style.fontStyle = ''; - this.textarea.style.fontFamily = mxConstants.DEFAULT_FONTFAMILY; - this.textarea.style.textAlign = 'left'; - - // Adds padding to make cursor visible with borders - this.textarea.style.padding = '2px'; - - if (this.textarea.innerHTML != content) - { - this.textarea.innerHTML = content; - } - - this.codeViewMode = true; - } - else - { - var content = mxUtils.extractTextWithWhitespace(this.textarea.childNodes); - - // Strips trailing line break - if (content.length > 0 && content.charAt(content.length - 1) == '\n') - { - content = content.substring(0, content.length - 1); - } - - content = this.graph.sanitizeHtml((nl2Br) ? content.replace(/\n/g, '
') : content, true) - this.textarea.className = 'mxCellEditor geContentEditable'; - - var size = mxUtils.getValue(state.style, mxConstants.STYLE_FONTSIZE, mxConstants.DEFAULT_FONTSIZE); - var family = mxUtils.getValue(state.style, mxConstants.STYLE_FONTFAMILY, mxConstants.DEFAULT_FONTFAMILY); - var align = mxUtils.getValue(state.style, mxConstants.STYLE_ALIGN, mxConstants.ALIGN_LEFT); - var bold = (mxUtils.getValue(state.style, mxConstants.STYLE_FONTSTYLE, 0) & - mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD; - var italic = (mxUtils.getValue(state.style, mxConstants.STYLE_FONTSTYLE, 0) & - mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC; - var uline = (mxUtils.getValue(state.style, mxConstants.STYLE_FONTSTYLE, 0) & - mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE; - - this.textarea.style.lineHeight = (mxConstants.ABSOLUTE_LINE_HEIGHT) ? Math.round(size * mxConstants.LINE_HEIGHT) + 'px' : mxConstants.LINE_HEIGHT; - this.textarea.style.fontSize = Math.round(size) + 'px'; - this.textarea.style.textDecoration = (uline) ? 'underline' : ''; - this.textarea.style.fontWeight = (bold) ? 'bold' : 'normal'; - this.textarea.style.fontStyle = (italic) ? 'italic' : ''; - this.textarea.style.fontFamily = family; - this.textarea.style.textAlign = align; - this.textarea.style.padding = '0px'; - - if (this.textarea.innerHTML != content) - { - this.textarea.innerHTML = content; - - if (this.textarea.innerHTML.length == 0) - { - this.textarea.innerHTML = this.getEmptyLabelText(); - this.clearOnChange = this.textarea.innerHTML.length > 0; - } - } - - this.codeViewMode = false; - } - - this.textarea.focus(); - - if (this.switchSelectionState != null) - { - this.restoreSelection(this.switchSelectionState); - } - - this.switchSelectionState = tmp; - this.resize(); - }; - - var mxCellEditorResize = mxCellEditor.prototype.resize; - mxCellEditor.prototype.resize = function(state, trigger) - { - if (this.textarea != null) - { - var state = this.graph.getView().getState(this.editingCell); - - if (this.codeViewMode && state != null) - { - var scale = state.view.scale; - this.bounds = mxRectangle.fromRectangle(state); - - // General placement of code editor if cell has no size - // LATER: Fix HTML editor bounds for edge labels - if (this.bounds.width == 0 && this.bounds.height == 0) - { - this.bounds.width = 160 * scale; - this.bounds.height = 60 * scale; - - var m = (state.text != null) ? state.text.margin : null; - - if (m == null) - { - m = mxUtils.getAlignmentAsPoint(mxUtils.getValue(state.style, mxConstants.STYLE_ALIGN, mxConstants.ALIGN_CENTER), - mxUtils.getValue(state.style, mxConstants.STYLE_VERTICAL_ALIGN, mxConstants.ALIGN_MIDDLE)); - } - - this.bounds.x += m.x * this.bounds.width; - this.bounds.y += m.y * this.bounds.height; - } - - this.textarea.style.width = Math.round((this.bounds.width - 4) / scale) + 'px'; - this.textarea.style.height = Math.round((this.bounds.height - 4) / scale) + 'px'; - this.textarea.style.overflow = 'auto'; - - // Adds scrollbar offset if visible - if (this.textarea.clientHeight < this.textarea.offsetHeight) - { - this.textarea.style.height = Math.round((this.bounds.height / scale)) + (this.textarea.offsetHeight - this.textarea.clientHeight) + 'px'; - this.bounds.height = parseInt(this.textarea.style.height) * scale; - } - - if (this.textarea.clientWidth < this.textarea.offsetWidth) - { - this.textarea.style.width = Math.round((this.bounds.width / scale)) + (this.textarea.offsetWidth - this.textarea.clientWidth) + 'px'; - this.bounds.width = parseInt(this.textarea.style.width) * scale; - } - - this.textarea.style.left = Math.round(this.bounds.x) + 'px'; - this.textarea.style.top = Math.round(this.bounds.y) + 'px'; - - if (mxClient.IS_VML) - { - this.textarea.style.zoom = scale; - } - else - { - mxUtils.setPrefixedStyle(this.textarea.style, 'transform', 'scale(' + scale + ',' + scale + ')'); - } - } - else - { - this.textarea.style.height = ''; - this.textarea.style.overflow = ''; - mxCellEditorResize.apply(this, arguments); - } - } - }; - - mxCellEditorGetInitialValue = mxCellEditor.prototype.getInitialValue; - mxCellEditor.prototype.getInitialValue = function(state, trigger) - { - if (mxUtils.getValue(state.style, 'html', '0') == '0') - { - return mxCellEditorGetInitialValue.apply(this, arguments); - } - else - { - var result = this.graph.getEditingValue(state.cell, trigger) - - if (mxUtils.getValue(state.style, 'nl2Br', '1') == '1') - { - result = result.replace(/\n/g, '
'); - } - - result = this.graph.sanitizeHtml(result, true); - - return result; - } - }; - - mxCellEditorGetCurrentValue = mxCellEditor.prototype.getCurrentValue; - mxCellEditor.prototype.getCurrentValue = function(state) - { - if (mxUtils.getValue(state.style, 'html', '0') == '0') - { - return mxCellEditorGetCurrentValue.apply(this, arguments); - } - else - { - var result = this.graph.sanitizeHtml(this.textarea.innerHTML, true); - - if (mxUtils.getValue(state.style, 'nl2Br', '1') == '1') - { - result = result.replace(/\r\n/g, '
').replace(/\n/g, '
'); - } - else - { - result = result.replace(/\r\n/g, '').replace(/\n/g, ''); - } - - return result; - } - }; - - var mxCellEditorStopEditing = mxCellEditor.prototype.stopEditing; - mxCellEditor.prototype.stopEditing = function(cancel) - { - // Restores default view mode before applying value - if (this.codeViewMode) - { - this.toggleViewMode(); - } - - mxCellEditorStopEditing.apply(this, arguments); - - // Tries to move focus back to container after editing if possible - this.focusContainer(); - }; - - mxCellEditor.prototype.focusContainer = function() - { - try - { - this.graph.container.focus(); - } - catch (e) - { - // ignore - } - }; - - var mxCellEditorApplyValue = mxCellEditor.prototype.applyValue; - mxCellEditor.prototype.applyValue = function(state, value) - { - // Removes empty relative child labels in edges - this.graph.getModel().beginUpdate(); - - try - { - mxCellEditorApplyValue.apply(this, arguments); - - if (this.graph.isCellDeletable(state.cell) && this.graph.model.getChildCount(state.cell) == 0) - { - var stroke = mxUtils.getValue(state.style, mxConstants.STYLE_STROKECOLOR, mxConstants.NONE); - var fill = mxUtils.getValue(state.style, mxConstants.STYLE_FILLCOLOR, mxConstants.NONE); - - if (value == '' && stroke == mxConstants.NONE && fill == mxConstants.NONE) - { - this.graph.removeCells([state.cell], false); - } - } - } - finally - { - this.graph.getModel().endUpdate(); - } - }; - - /** - * Returns the background color to be used for the editing box. This returns - * the label background for edge labels and null for all other cases. - */ - mxCellEditor.prototype.getBackgroundColor = function(state) - { - var color = null; - - if (this.graph.getModel().isEdge(state.cell) || this.graph.getModel().isEdge(this.graph.getModel().getParent(state.cell))) - { - var color = mxUtils.getValue(state.style, mxConstants.STYLE_LABEL_BACKGROUNDCOLOR, null); - - if (color == mxConstants.NONE) - { - color = null; - } - } - - return color; - }; - - mxCellEditor.prototype.getMinimumSize = function(state) - { - var scale = this.graph.getView().scale; - - return new mxRectangle(0, 0, (state.text == null) ? 30 : state.text.size * scale + 20, 30); - }; - - // Hold alt to ignore drop target - var mxGraphHandlerMoveCells = mxGraphHandler.prototype.moveCells; - - mxGraphHandler.prototype.moveCells = function(cells, dx, dy, clone, target, evt) - { - if (mxEvent.isAltDown(evt)) - { - target = null; - } - - mxGraphHandlerMoveCells.apply(this, arguments); - }; - - /** - * Hints on handlers - */ - function createHint() - { - var hint = document.createElement('div'); - hint.className = 'geHint'; - hint.style.whiteSpace = 'nowrap'; - hint.style.position = 'absolute'; - - return hint; - }; - - /** - * Updates the hint for the current operation. - */ - mxGraphHandler.prototype.updateHint = function(me) - { - if (this.shape != null) - { - if (this.hint == null) - { - this.hint = createHint(); - this.graph.container.appendChild(this.hint); - } - - var t = this.graph.view.translate; - var s = this.graph.view.scale; - var x = this.roundLength((this.bounds.x + this.currentDx) / s - t.x); - var y = this.roundLength((this.bounds.y + this.currentDy) / s - t.y); - - this.hint.innerHTML = x + ', ' + y; - - this.hint.style.left = (this.shape.bounds.x + Math.round((this.shape.bounds.width - this.hint.clientWidth) / 2)) + 'px'; - this.hint.style.top = (this.shape.bounds.y + this.shape.bounds.height + 12) + 'px'; - } - }; - - /** - * Updates the hint for the current operation. - */ - mxGraphHandler.prototype.removeHint = function() - { - if (this.hint != null) - { - this.hint.parentNode.removeChild(this.hint); - this.hint = null; - } - }; - - /** - * Enables recursive resize for groups. - */ - mxVertexHandler.prototype.isRecursiveResize = function(state, me) - { - return !this.graph.isSwimlane(state.cell) && this.graph.model.getChildCount(state.cell) > 0 && - !mxEvent.isControlDown(me.getEvent()) && !this.graph.isCellCollapsed(state.cell) && - mxUtils.getValue(state.style, 'recursiveResize', '1') == '1' && - mxUtils.getValue(state.style, 'childLayout', null) == null; - }; - - /** - * Enables centered resize events. - */ - mxVertexHandler.prototype.isCenteredEvent = function(state, me) - { - return (!(!this.graph.isSwimlane(state.cell) && this.graph.model.getChildCount(state.cell) > 0 && - !this.graph.isCellCollapsed(state.cell) && - mxUtils.getValue(state.style, 'recursiveResize', '1') == '1' && - mxUtils.getValue(state.style, 'childLayout', null) == null) && - mxEvent.isControlDown(me.getEvent())) || - mxEvent.isMetaDown(me.getEvent()); - }; - - var vertexHandlerGetHandlePadding = mxVertexHandler.prototype.getHandlePadding; - mxVertexHandler.prototype.getHandlePadding = function() - { - var result = new mxPoint(0, 0); - var tol = this.tolerance; - - if (this.graph.cellEditor.getEditingCell() == this.state.cell && - this.sizers != null && this.sizers.length > 0 && this.sizers[0] != null) - { - tol /= 2; - - result.x = this.sizers[0].bounds.width + tol; - result.y = this.sizers[0].bounds.height + tol; - } - else - { - result = vertexHandlerGetHandlePadding.apply(this, arguments); - } - - return result; - }; - - /** - * Updates the hint for the current operation. - */ - mxVertexHandler.prototype.updateHint = function(me) - { - if (this.index != mxEvent.LABEL_HANDLE) - { - if (this.hint == null) - { - this.hint = createHint(); - this.state.view.graph.container.appendChild(this.hint); - } - - if (this.index == mxEvent.ROTATION_HANDLE) - { - this.hint.innerHTML = this.currentAlpha + '°'; - } - else - { - var s = this.state.view.scale; - this.hint.innerHTML = this.roundLength(this.bounds.width / s) + ' x ' + this.roundLength(this.bounds.height / s); - } - - var rot = (this.currentAlpha != null) ? this.currentAlpha : this.state.style[mxConstants.STYLE_ROTATION] || '0'; - var bb = mxUtils.getBoundingBox(this.bounds, rot); - - if (bb == null) - { - bb = this.bounds; - } - - this.hint.style.left = bb.x + Math.round((bb.width - this.hint.clientWidth) / 2) + 'px'; - this.hint.style.top = (bb.y + bb.height + 12) + 'px'; - - if (this.linkHint != null) - { - this.linkHint.style.display = 'none'; - } - } - }; - - /** - * Updates the hint for the current operation. - */ - mxVertexHandler.prototype.removeHint = function() - { - mxGraphHandler.prototype.removeHint.apply(this, arguments); - - if (this.linkHint != null) - { - this.linkHint.style.display = ''; - } - }; - - /** - * Updates the hint for the current operation. - */ - mxEdgeHandler.prototype.updateHint = function(me, point) - { - if (this.hint == null) - { - this.hint = createHint(); - this.state.view.graph.container.appendChild(this.hint); - } - - var t = this.graph.view.translate; - var s = this.graph.view.scale; - var x = this.roundLength(point.x / s - t.x); - var y = this.roundLength(point.y / s - t.y); - - this.hint.innerHTML = x + ', ' + y; - this.hint.style.visibility = 'visible'; - - if (this.isSource || this.isTarget) - { - if (this.constraintHandler.currentConstraint != null && - this.constraintHandler.currentFocus != null) - { - var pt = this.constraintHandler.currentConstraint.point; - this.hint.innerHTML = '[' + Math.round(pt.x * 100) + '%, '+ Math.round(pt.y * 100) + '%]'; - } - else if (this.marker.hasValidState()) - { - this.hint.style.visibility = 'hidden'; - } - } - - this.hint.style.left = Math.round(me.getGraphX() - this.hint.clientWidth / 2) + 'px'; - this.hint.style.top = (Math.max(me.getGraphY(), point.y) + this.state.view.graph.gridSize) + 'px'; - - if (this.linkHint != null) - { - this.linkHint.style.display = 'none'; - } - }; - - /** - * Updates the hint for the current operation. - */ - mxEdgeHandler.prototype.removeHint = mxVertexHandler.prototype.removeHint; - - /** - * Defines the handles for the UI. Uses data-URIs to speed-up loading time where supported. - */ - // TODO: Increase handle padding - HoverIcons.prototype.mainHandle = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/handle-main.png', 17, 17) : - Graph.createSvgImage(18, 18, ''); - HoverIcons.prototype.secondaryHandle = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/handle-secondary.png', 17, 17) : - Graph.createSvgImage(16, 16, ''); - HoverIcons.prototype.fixedHandle = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/handle-fixed.png', 17, 17) : - Graph.createSvgImage(18, 18, ''); - HoverIcons.prototype.terminalHandle = (!mxClient.IS_SVG) ? new mxImage(IMAGE_PATH + '/handle-terminal.png', 17, 17) : - Graph.createSvgImage(18, 18, ''); - HoverIcons.prototype.rotationHandle = new mxImage((mxClient.IS_SVG) ? 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAAVCAYAAACkCdXRAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKT2lDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVNnVFPpFj333vRCS4iAlEtvUhUIIFJCi4AUkSYqIQkQSoghodkVUcERRUUEG8igiAOOjoCMFVEsDIoK2AfkIaKOg6OIisr74Xuja9a89+bN/rXXPues852zzwfACAyWSDNRNYAMqUIeEeCDx8TG4eQuQIEKJHAAEAizZCFz/SMBAPh+PDwrIsAHvgABeNMLCADATZvAMByH/w/qQplcAYCEAcB0kThLCIAUAEB6jkKmAEBGAYCdmCZTAKAEAGDLY2LjAFAtAGAnf+bTAICd+Jl7AQBblCEVAaCRACATZYhEAGg7AKzPVopFAFgwABRmS8Q5ANgtADBJV2ZIALC3AMDOEAuyAAgMADBRiIUpAAR7AGDIIyN4AISZABRG8lc88SuuEOcqAAB4mbI8uSQ5RYFbCC1xB1dXLh4ozkkXKxQ2YQJhmkAuwnmZGTKBNA/g88wAAKCRFRHgg/P9eM4Ors7ONo62Dl8t6r8G/yJiYuP+5c+rcEAAAOF0ftH+LC+zGoA7BoBt/qIl7gRoXgugdfeLZrIPQLUAoOnaV/Nw+H48PEWhkLnZ2eXk5NhKxEJbYcpXff5nwl/AV/1s+X48/Pf14L7iJIEyXYFHBPjgwsz0TKUcz5IJhGLc5o9H/LcL//wd0yLESWK5WCoU41EScY5EmozzMqUiiUKSKcUl0v9k4t8s+wM+3zUAsGo+AXuRLahdYwP2SycQWHTA4vcAAPK7b8HUKAgDgGiD4c93/+8//UegJQCAZkmScQAAXkQkLlTKsz/HCAAARKCBKrBBG/TBGCzABhzBBdzBC/xgNoRCJMTCQhBCCmSAHHJgKayCQiiGzbAdKmAv1EAdNMBRaIaTcA4uwlW4Dj1wD/phCJ7BKLyBCQRByAgTYSHaiAFiilgjjggXmYX4IcFIBBKLJCDJiBRRIkuRNUgxUopUIFVIHfI9cgI5h1xGupE7yAAygvyGvEcxlIGyUT3UDLVDuag3GoRGogvQZHQxmo8WoJvQcrQaPYw2oefQq2gP2o8+Q8cwwOgYBzPEbDAuxsNCsTgsCZNjy7EirAyrxhqwVqwDu4n1Y8+xdwQSgUXACTYEd0IgYR5BSFhMWE7YSKggHCQ0EdoJNwkDhFHCJyKTqEu0JroR+cQYYjIxh1hILCPWEo8TLxB7iEPENyQSiUMyJ7mQAkmxpFTSEtJG0m5SI+ksqZs0SBojk8naZGuyBzmULCAryIXkneTD5DPkG+Qh8lsKnWJAcaT4U+IoUspqShnlEOU05QZlmDJBVaOaUt2ooVQRNY9aQq2htlKvUYeoEzR1mjnNgxZJS6WtopXTGmgXaPdpr+h0uhHdlR5Ol9BX0svpR+iX6AP0dwwNhhWDx4hnKBmbGAcYZxl3GK+YTKYZ04sZx1QwNzHrmOeZD5lvVVgqtip8FZHKCpVKlSaVGyovVKmqpqreqgtV81XLVI+pXlN9rkZVM1PjqQnUlqtVqp1Q61MbU2epO6iHqmeob1Q/pH5Z/YkGWcNMw09DpFGgsV/jvMYgC2MZs3gsIWsNq4Z1gTXEJrHN2Xx2KruY/R27iz2qqaE5QzNKM1ezUvOUZj8H45hx+Jx0TgnnKKeX836K3hTvKeIpG6Y0TLkxZVxrqpaXllirSKtRq0frvTau7aedpr1Fu1n7gQ5Bx0onXCdHZ4/OBZ3nU9lT3acKpxZNPTr1ri6qa6UbobtEd79up+6Ynr5egJ5Mb6feeb3n+hx9L/1U/W36p/VHDFgGswwkBtsMzhg8xTVxbzwdL8fb8VFDXcNAQ6VhlWGX4YSRudE8o9VGjUYPjGnGXOMk423GbcajJgYmISZLTepN7ppSTbmmKaY7TDtMx83MzaLN1pk1mz0x1zLnm+eb15vft2BaeFostqi2uGVJsuRaplnutrxuhVo5WaVYVVpds0atna0l1rutu6cRp7lOk06rntZnw7Dxtsm2qbcZsOXYBtuutm22fWFnYhdnt8Wuw+6TvZN9un2N/T0HDYfZDqsdWh1+c7RyFDpWOt6azpzuP33F9JbpL2dYzxDP2DPjthPLKcRpnVOb00dnF2e5c4PziIuJS4LLLpc+Lpsbxt3IveRKdPVxXeF60vWdm7Obwu2o26/uNu5p7ofcn8w0nymeWTNz0MPIQ+BR5dE/C5+VMGvfrH5PQ0+BZ7XnIy9jL5FXrdewt6V3qvdh7xc+9j5yn+M+4zw33jLeWV/MN8C3yLfLT8Nvnl+F30N/I/9k/3r/0QCngCUBZwOJgUGBWwL7+Hp8Ib+OPzrbZfay2e1BjKC5QRVBj4KtguXBrSFoyOyQrSH355jOkc5pDoVQfujW0Adh5mGLw34MJ4WHhVeGP45wiFga0TGXNXfR3ENz30T6RJZE3ptnMU85ry1KNSo+qi5qPNo3ujS6P8YuZlnM1VidWElsSxw5LiquNm5svt/87fOH4p3iC+N7F5gvyF1weaHOwvSFpxapLhIsOpZATIhOOJTwQRAqqBaMJfITdyWOCnnCHcJnIi/RNtGI2ENcKh5O8kgqTXqS7JG8NXkkxTOlLOW5hCepkLxMDUzdmzqeFpp2IG0yPTq9MYOSkZBxQqohTZO2Z+pn5mZ2y6xlhbL+xW6Lty8elQfJa7OQrAVZLQq2QqboVFoo1yoHsmdlV2a/zYnKOZarnivN7cyzytuQN5zvn//tEsIS4ZK2pYZLVy0dWOa9rGo5sjxxedsK4xUFK4ZWBqw8uIq2Km3VT6vtV5eufr0mek1rgV7ByoLBtQFr6wtVCuWFfevc1+1dT1gvWd+1YfqGnRs+FYmKrhTbF5cVf9go3HjlG4dvyr+Z3JS0qavEuWTPZtJm6ebeLZ5bDpaql+aXDm4N2dq0Dd9WtO319kXbL5fNKNu7g7ZDuaO/PLi8ZafJzs07P1SkVPRU+lQ27tLdtWHX+G7R7ht7vPY07NXbW7z3/T7JvttVAVVN1WbVZftJ+7P3P66Jqun4lvttXa1ObXHtxwPSA/0HIw6217nU1R3SPVRSj9Yr60cOxx++/p3vdy0NNg1VjZzG4iNwRHnk6fcJ3/ceDTradox7rOEH0x92HWcdL2pCmvKaRptTmvtbYlu6T8w+0dbq3nr8R9sfD5w0PFl5SvNUyWna6YLTk2fyz4ydlZ19fi753GDborZ752PO32oPb++6EHTh0kX/i+c7vDvOXPK4dPKy2+UTV7hXmq86X23qdOo8/pPTT8e7nLuarrlca7nuer21e2b36RueN87d9L158Rb/1tWeOT3dvfN6b/fF9/XfFt1+cif9zsu72Xcn7q28T7xf9EDtQdlD3YfVP1v+3Njv3H9qwHeg89HcR/cGhYPP/pH1jw9DBY+Zj8uGDYbrnjg+OTniP3L96fynQ89kzyaeF/6i/suuFxYvfvjV69fO0ZjRoZfyl5O/bXyl/erA6xmv28bCxh6+yXgzMV70VvvtwXfcdx3vo98PT+R8IH8o/2j5sfVT0Kf7kxmTk/8EA5jz/GMzLdsAAAAgY0hSTQAAeiUAAICDAAD5/wAAgOkAAHUwAADqYAAAOpgAABdvkl/FRgAAA6ZJREFUeNqM001IY1cUB/D/fYmm2sbR2lC1zYlgoRG6MpEyBlpxM9iFIGKFIm3s0lCKjOByhCLZCFqLBF1YFVJdSRbdFHRhBbULtRuFVBTzYRpJgo2mY5OX5N9Fo2TG+eiFA/dd3vvd8+65ByTxshARTdf1JySp6/oTEdFe9T5eg5lIcnBwkCSZyWS+exX40oyur68/KxaLf5Okw+H4X+A9JBaLfUySZ2dnnJqaosPhIAACeC34DJRKpb7IZrMcHx+nwWCgUopGo/EOKwf9fn/1CzERUevr6+9ls1mOjIwQAH0+H4PBIKPR6D2ofAQCgToRUeVYJUkuLy8TANfW1kiS8/PzCy84Mw4MDBAAZ2dnmc/nub+/X0MSEBF1cHDwMJVKsaGhgV6vl+l0mqOjo1+KyKfl1dze3l4NBoM/PZ+diFSLiIKIGBOJxA9bW1sEwNXVVSaTyQMRaRaRxrOzs+9J8ujoaE5EPhQRq67rcZ/PRwD0+/3Udf03EdEgIqZisZibnJykwWDg4eEhd3Z2xkXELCJvPpdBrYjUiEhL+Xo4HH4sIhUaAKNSqiIcDsNkMqG+vh6RSOQQQM7tdhsAQCkFAHC73UUATxcWFqypVApmsxnDw8OwWq2TADQNgAYAFosF+XweyWQSdru9BUBxcXFRB/4rEgDcPouIIx6P4+bmBi0tLSCpAzBqAIqnp6c/dnZ2IpfLYXNzE62traMADACKNputpr+/v8lms9UAKAAwiMjXe3t7KBQKqKurQy6Xi6K0i2l6evpROp1mbW0t29vbGY/Hb8/IVIqq2zlJXl1dsaOjg2azmefn5wwEAl+JSBVExCgi75PkzMwMlVJsbGxkIpFgPp8PX15ePopEIs3JZPITXdf/iEajbGpqolKKExMT1HWdHo/nIxGpgIgoEXnQ3d39kCTHxsYIgC6Xi3NzcwyHw8xkMozFYlxaWmJbWxuVUuzt7WUul6PX6/1cRN4WEe2uA0SkaWVl5XGpRVhdXU0A1DSNlZWVdz3qdDrZ09PDWCzG4+Pjn0XEWvp9KJKw2WwKwBsA3gHQHAqFfr24uMDGxgZ2d3cRiUQAAHa7HU6nE319fTg5Ofmlq6vrGwB/AngaCoWK6rbsNptNA1AJoA7Aux6Pp3NoaMhjsVg+QNmIRqO/u1yubwFEASRKUAEA7rASqABUAKgC8KAUb5XWCOAfAFcA/gJwDSB7C93DylCtdM8qABhLc5TumV6KQigUeubjfwcAHkQJ94ndWeYAAAAASUVORK5CYII=' : - IMAGE_PATH + '/handle-rotate.png', 19, 21); - - if (mxClient.IS_SVG) - { - mxConstraintHandler.prototype.pointImage = Graph.createSvgImage(5, 5, ''); - } - - mxVertexHandler.prototype.handleImage = HoverIcons.prototype.mainHandle; - mxVertexHandler.prototype.secondaryHandleImage = HoverIcons.prototype.secondaryHandle; - mxEdgeHandler.prototype.handleImage = HoverIcons.prototype.mainHandle; - mxEdgeHandler.prototype.terminalHandleImage = HoverIcons.prototype.terminalHandle; - mxEdgeHandler.prototype.fixedHandleImage = HoverIcons.prototype.fixedHandle; - mxEdgeHandler.prototype.labelHandleImage = HoverIcons.prototype.secondaryHandle; - mxOutline.prototype.sizerImage = HoverIcons.prototype.mainHandle; - - if (window.Sidebar != null) - { - Sidebar.prototype.triangleUp = HoverIcons.prototype.triangleUp; - Sidebar.prototype.triangleRight = HoverIcons.prototype.triangleRight; - Sidebar.prototype.triangleDown = HoverIcons.prototype.triangleDown; - Sidebar.prototype.triangleLeft = HoverIcons.prototype.triangleLeft; - Sidebar.prototype.refreshTarget = HoverIcons.prototype.refreshTarget; - Sidebar.prototype.roundDrop = HoverIcons.prototype.roundDrop; - } - - // Pre-fetches images (only needed for non data-uris) - if (!mxClient.IS_SVG) - { - new Image().src = HoverIcons.prototype.mainHandle.src; - new Image().src = HoverIcons.prototype.fixedHandle.src; - new Image().src = HoverIcons.prototype.terminalHandle.src; - new Image().src = HoverIcons.prototype.secondaryHandle.src; - new Image().src = HoverIcons.prototype.rotationHandle.src; - - new Image().src = HoverIcons.prototype.triangleUp.src; - new Image().src = HoverIcons.prototype.triangleRight.src; - new Image().src = HoverIcons.prototype.triangleDown.src; - new Image().src = HoverIcons.prototype.triangleLeft.src; - new Image().src = HoverIcons.prototype.refreshTarget.src; - new Image().src = HoverIcons.prototype.roundDrop.src; - } - - // Adds rotation handle and live preview - mxVertexHandler.prototype.rotationEnabled = true; - mxVertexHandler.prototype.manageSizers = true; - mxVertexHandler.prototype.livePreview = true; - - // Increases default rubberband opacity (default is 20) - mxRubberband.prototype.defaultOpacity = 30; - - // Enables connections along the outline, virtual waypoints, parent highlight etc - mxConnectionHandler.prototype.outlineConnect = true; - mxCellHighlight.prototype.keepOnTop = true; - mxVertexHandler.prototype.parentHighlightEnabled = true; - mxVertexHandler.prototype.rotationHandleVSpacing = -20; - - mxEdgeHandler.prototype.parentHighlightEnabled = true; - mxEdgeHandler.prototype.dblClickRemoveEnabled = true; - mxEdgeHandler.prototype.straightRemoveEnabled = true; - mxEdgeHandler.prototype.virtualBendsEnabled = true; - mxEdgeHandler.prototype.mergeRemoveEnabled = true; - mxEdgeHandler.prototype.manageLabelHandle = true; - mxEdgeHandler.prototype.outlineConnect = true; - - // Disables adding waypoints if shift is pressed - mxEdgeHandler.prototype.isAddVirtualBendEvent = function(me) - { - return !mxEvent.isShiftDown(me.getEvent()); - }; - - // Disables custom handles if shift is pressed - mxEdgeHandler.prototype.isCustomHandleEvent = function(me) - { - return !mxEvent.isShiftDown(me.getEvent()); - }; - - /** - * Implements touch style - */ - if (Graph.touchStyle) - { - // Larger tolerance for real touch devices - if (mxClient.IS_TOUCH || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0) - { - mxShape.prototype.svgStrokeTolerance = 18; - mxVertexHandler.prototype.tolerance = 12; - mxEdgeHandler.prototype.tolerance = 12; - Graph.prototype.tolerance = 12; - - mxVertexHandler.prototype.rotationHandleVSpacing = -24; - - // Implements a smaller tolerance for mouse events and a larger tolerance for touch - // events on touch devices. The default tolerance (4px) is used for mouse events. - mxConstraintHandler.prototype.getTolerance = function(me) - { - return (mxEvent.isMouseEvent(me.getEvent())) ? 4 : this.graph.getTolerance(); - }; - } - - // One finger pans (no rubberband selection) must start regardless of mouse button - mxPanningHandler.prototype.isPanningTrigger = function(me) - { - var evt = me.getEvent(); - - return (me.getState() == null && !mxEvent.isMouseEvent(evt)) || - (mxEvent.isPopupTrigger(evt) && (me.getState() == null || - mxEvent.isControlDown(evt) || mxEvent.isShiftDown(evt))); - }; - - // Don't clear selection if multiple cells selected - var graphHandlerMouseDown = mxGraphHandler.prototype.mouseDown; - mxGraphHandler.prototype.mouseDown = function(sender, me) - { - graphHandlerMouseDown.apply(this, arguments); - - if (mxEvent.isTouchEvent(me.getEvent()) && this.graph.isCellSelected(me.getCell()) && - this.graph.getSelectionCount() > 1) - { - this.delayedSelection = false; - } - }; - } - else - { - // Removes ctrl+shift as panning trigger for space splitting - mxPanningHandler.prototype.isPanningTrigger = function(me) - { - var evt = me.getEvent(); - - return (mxEvent.isLeftMouseButton(evt) && ((this.useLeftButtonForPanning && - me.getState() == null) || (mxEvent.isControlDown(evt) && - !mxEvent.isShiftDown(evt)))) || (this.usePopupTrigger && - mxEvent.isPopupTrigger(evt)); - }; - } - - // Overrides/extends rubberband for space handling with Ctrl+Shift(+Alt) drag ("scissors tool") - mxRubberband.prototype.isSpaceEvent = function(me) - { - return this.graph.isEnabled() && !this.graph.isCellLocked(this.graph.getDefaultParent()) && - mxEvent.isControlDown(me.getEvent()) && mxEvent.isShiftDown(me.getEvent()); - }; - - // Handles moving of cells in both half panes - mxRubberband.prototype.mouseUp = function(sender, me) - { - var execute = this.div != null && this.div.style.display != 'none'; - - var x0 = null; - var y0 = null; - var dx = null; - var dy = null; - - if (this.first != null && this.currentX != null && this.currentY != null) - { - x0 = this.first.x; - y0 = this.first.y; - dx = (this.currentX - x0) / this.graph.view.scale; - dy = (this.currentY - y0) / this.graph.view.scale; - - if (!mxEvent.isAltDown(me.getEvent())) - { - dx = this.graph.snap(dx); - dy = this.graph.snap(dy); - - if (!this.graph.isGridEnabled()) - { - if (Math.abs(dx) < this.graph.tolerance) - { - dx = 0; - } - - if (Math.abs(dy) < this.graph.tolerance) - { - dy = 0; - } - } - } - } - - this.reset(); - - if (execute) - { - if (mxEvent.isAltDown(me.getEvent()) && this.graph.isToggleEvent(me.getEvent())) - { - var rect = new mxRectangle(this.x, this.y, this.width, this.height); - var cells = this.graph.getCells(rect.x, rect.y, rect.width, rect.height); - - this.graph.removeSelectionCells(cells); - } - else if (this.isSpaceEvent(me)) - { - this.graph.model.beginUpdate(); - try - { - var cells = this.graph.getCellsBeyond(x0, y0, this.graph.getDefaultParent(), true, true); - - for (var i = 0; i < cells.length; i++) - { - if (this.graph.isCellMovable(cells[i])) - { - var tmp = this.graph.view.getState(cells[i]); - var geo = this.graph.getCellGeometry(cells[i]); - - if (tmp != null && geo != null) - { - geo = geo.clone(); - geo.translate(dx, dy); - this.graph.model.setGeometry(cells[i], geo); - } - } - } - } - finally - { - this.graph.model.endUpdate(); - } - } - else - { - var rect = new mxRectangle(this.x, this.y, this.width, this.height); - this.graph.selectRegion(rect, me.getEvent()); - } - - me.consume(); - } - }; - - // Handles preview for creating/removing space in diagram - mxRubberband.prototype.mouseMove = function(sender, me) - { - if (!me.isConsumed() && this.first != null) - { - var origin = mxUtils.getScrollOrigin(this.graph.container); - var offset = mxUtils.getOffset(this.graph.container); - origin.x -= offset.x; - origin.y -= offset.y; - var x = me.getX() + origin.x; - var y = me.getY() + origin.y; - var dx = this.first.x - x; - var dy = this.first.y - y; - var tol = this.graph.tolerance; - - if (this.div != null || Math.abs(dx) > tol || Math.abs(dy) > tol) - { - if (this.div == null) - { - this.div = this.createShape(); - } - - // Clears selection while rubberbanding. This is required because - // the event is not consumed in mouseDown. - mxUtils.clearSelection(); - this.update(x, y); - - if (this.isSpaceEvent(me)) - { - var right = this.x + this.width; - var bottom = this.y + this.height; - var scale = this.graph.view.scale; - - if (!mxEvent.isAltDown(me.getEvent())) - { - this.width = this.graph.snap(this.width / scale) * scale; - this.height = this.graph.snap(this.height / scale) * scale; - - if (!this.graph.isGridEnabled()) - { - if (this.width < this.graph.tolerance) - { - this.width = 0; - } - - if (this.height < this.graph.tolerance) - { - this.height = 0; - } - } - - if (this.x < this.first.x) - { - this.x = right - this.width; - } - - if (this.y < this.first.y) - { - this.y = bottom - this.height; - } - } - - this.div.style.borderStyle = 'dashed'; - this.div.style.backgroundColor = 'white'; - this.div.style.left = this.x + 'px'; - this.div.style.top = this.y + 'px'; - this.div.style.width = Math.max(0, this.width) + 'px'; - this.div.style.height = this.graph.container.clientHeight + 'px'; - this.div.style.borderWidth = (this.width <= 0) ? '0px 1px 0px 0px' : '0px 1px 0px 1px'; - - if (this.secondDiv == null) - { - this.secondDiv = this.div.cloneNode(true); - this.div.parentNode.appendChild(this.secondDiv); - } - - this.secondDiv.style.left = this.x + 'px'; - this.secondDiv.style.top = this.y + 'px'; - this.secondDiv.style.width = this.graph.container.clientWidth + 'px'; - this.secondDiv.style.height = Math.max(0, this.height) + 'px'; - this.secondDiv.style.borderWidth = (this.height <= 0) ? '1px 0px 0px 0px' : '1px 0px 1px 0px'; - } - else - { - // Hides second div and restores style - this.div.style.backgroundColor = ''; - this.div.style.borderWidth = ''; - this.div.style.borderStyle = ''; - - if (this.secondDiv != null) - { - this.secondDiv.parentNode.removeChild(this.secondDiv); - this.secondDiv = null; - } - } - - me.consume(); - } - } - }; - - // Removes preview - var mxRubberbandReset = mxRubberband.prototype.reset; - mxRubberband.prototype.reset = function() - { - if (this.secondDiv != null) - { - this.secondDiv.parentNode.removeChild(this.secondDiv); - this.secondDiv = null; - } - - mxRubberbandReset.apply(this, arguments); - }; - - // Timer-based activation of outline connect in connection handler - var startTime = new Date().getTime(); - var timeOnTarget = 0; - - var mxEdgeHandlerUpdatePreviewState = mxEdgeHandler.prototype.updatePreviewState; - - mxEdgeHandler.prototype.updatePreviewState = function(edge, point, terminalState, me) - { - mxEdgeHandlerUpdatePreviewState.apply(this, arguments); - - if (terminalState != this.currentTerminalState) - { - startTime = new Date().getTime(); - timeOnTarget = 0; - } - else - { - timeOnTarget = new Date().getTime() - startTime; - } - - this.currentTerminalState = terminalState; - }; - - // Timer-based outline connect - var mxEdgeHandlerIsOutlineConnectEvent = mxEdgeHandler.prototype.isOutlineConnectEvent; - - mxEdgeHandler.prototype.isOutlineConnectEvent = function(me) - { - return (this.currentTerminalState != null && me.getState() == this.currentTerminalState && timeOnTarget > 2000) || - ((this.currentTerminalState == null || mxUtils.getValue(this.currentTerminalState.style, 'outlineConnect', '1') != '0') && - mxEdgeHandlerIsOutlineConnectEvent.apply(this, arguments)); - }; - - // Disables custom handles if shift is pressed - mxVertexHandler.prototype.isCustomHandleEvent = function(me) - { - return !mxEvent.isShiftDown(me.getEvent()); - }; - - // Shows secondary handle for fixed connection points - mxEdgeHandler.prototype.createHandleShape = function(index, virtual) - { - var source = index != null && index == 0; - var terminalState = this.state.getVisibleTerminalState(source); - var c = (index != null && (index == 0 || index >= this.state.absolutePoints.length - 1 || - (this.constructor == mxElbowEdgeHandler && index == 2))) ? - this.graph.getConnectionConstraint(this.state, terminalState, source) : null; - var pt = (c != null) ? this.graph.getConnectionPoint(this.state.getVisibleTerminalState(source), c) : null; - var img = (pt != null) ? this.fixedHandleImage : ((c != null && terminalState != null) ? - this.terminalHandleImage : this.handleImage); - - if (img != null) - { - var shape = new mxImageShape(new mxRectangle(0, 0, img.width, img.height), img.src); - - // Allows HTML rendering of the images - shape.preserveImageAspect = false; - - return shape; - } - else - { - var s = mxConstants.HANDLE_SIZE; - - if (this.preferHtml) - { - s -= 1; - } - - return new mxRectangleShape(new mxRectangle(0, 0, s, s), mxConstants.HANDLE_FILLCOLOR, mxConstants.HANDLE_STROKECOLOR); - } - }; - - var vertexHandlerCreateSizerShape = mxVertexHandler.prototype.createSizerShape; - mxVertexHandler.prototype.createSizerShape = function(bounds, index, fillColor) - { - this.handleImage = (index == mxEvent.ROTATION_HANDLE) ? HoverIcons.prototype.rotationHandle : (index == mxEvent.LABEL_HANDLE) ? this.secondaryHandleImage : this.handleImage; - - return vertexHandlerCreateSizerShape.apply(this, arguments); - }; - - // Special case for single edge label handle moving in which case the text bounding box is used - var mxGraphHandlerGetBoundingBox = mxGraphHandler.prototype.getBoundingBox; - mxGraphHandler.prototype.getBoundingBox = function(cells) - { - if (cells != null && cells.length == 1) - { - var model = this.graph.getModel(); - var parent = model.getParent(cells[0]); - var geo = this.graph.getCellGeometry(cells[0]); - - if (model.isEdge(parent) && geo != null && geo.relative) - { - var state = this.graph.view.getState(cells[0]); - - if (state != null && state.width < 2 && state.height < 2 && state.text != null && state.text.boundingBox != null) - { - return mxRectangle.fromRectangle(state.text.boundingBox); - } - } - } - - return mxGraphHandlerGetBoundingBox.apply(this, arguments); - }; - - // Uses text bounding box for edge labels - var mxVertexHandlerGetSelectionBounds = mxVertexHandler.prototype.getSelectionBounds; - mxVertexHandler.prototype.getSelectionBounds = function(state) - { - var model = this.graph.getModel(); - var parent = model.getParent(state.cell); - var geo = this.graph.getCellGeometry(state.cell); - - if (model.isEdge(parent) && geo != null && geo.relative && state.width < 2 && state.height < 2 && state.text != null && state.text.boundingBox != null) - { - var bbox = state.text.unrotatedBoundingBox || state.text.boundingBox; - - return new mxRectangle(Math.round(bbox.x), Math.round(bbox.y), Math.round(bbox.width), Math.round(bbox.height)); - } - else - { - return mxVertexHandlerGetSelectionBounds.apply(this, arguments); - } - }; - - // Redirects moving of edge labels to mxGraphHandler by not starting here. - // This will use the move preview of mxGraphHandler (see above). - var mxVertexHandlerMouseDown = mxVertexHandler.prototype.mouseDown; - mxVertexHandler.prototype.mouseDown = function(sender, me) - { - var model = this.graph.getModel(); - var parent = model.getParent(this.state.cell); - var geo = this.graph.getCellGeometry(this.state.cell); - - // Lets rotation events through - var handle = this.getHandleForEvent(me); - - if (handle == mxEvent.ROTATION_HANDLE || !model.isEdge(parent) || geo == null || !geo.relative || - this.state == null || this.state.width >= 2 || this.state.height >= 2) - { - mxVertexHandlerMouseDown.apply(this, arguments); - } - }; - - // Shows rotation handle for edge labels. - mxVertexHandler.prototype.isRotationHandleVisible = function() - { - return this.graph.isEnabled() && this.rotationEnabled && this.graph.isCellRotatable(this.state.cell) && - (mxGraphHandler.prototype.maxCells <= 0 || this.graph.getSelectionCount() < mxGraphHandler.prototype.maxCells); - }; - - // Invokes turn on single click on rotation handle - mxVertexHandler.prototype.rotateClick = function() - { - this.state.view.graph.turnShapes([this.state.cell]); - }; - - var vertexHandlerMouseMove = mxVertexHandler.prototype.mouseMove; - - // Workaround for "isConsumed not defined" in MS Edge is to use arguments - mxVertexHandler.prototype.mouseMove = function(sender, me) - { - vertexHandlerMouseMove.apply(this, arguments); - - if (this.graph.graphHandler.first != null) - { - if (this.rotationShape != null && this.rotationShape.node != null) - { - this.rotationShape.node.style.display = 'none'; - } - } - }; - - var vertexHandlerMouseUp = mxVertexHandler.prototype.mouseUp; - mxVertexHandler.prototype.mouseUp = function(sender, me) - { - vertexHandlerMouseUp.apply(this, arguments); - - // Shows rotation handle only if one vertex is selected - if (this.rotationShape != null && this.rotationShape.node != null) - { - this.rotationShape.node.style.display = (this.graph.getSelectionCount() == 1) ? '' : 'none'; - } - }; - - var vertexHandlerInit = mxVertexHandler.prototype.init; - mxVertexHandler.prototype.init = function() - { - vertexHandlerInit.apply(this, arguments); - var redraw = false; - - if (this.rotationShape != null) - { - this.rotationShape.node.setAttribute('title', mxResources.get('rotateTooltip')); - } - - var update = mxUtils.bind(this, function() - { - // Shows rotation handle only if one vertex is selected - if (this.rotationShape != null && this.rotationShape.node != null) - { - this.rotationShape.node.style.display = (this.graph.getSelectionCount() == 1) ? '' : 'none'; - } - - if (this.specialHandle != null) - { - this.specialHandle.node.style.display = (this.graph.isEnabled() && this.graph.getSelectionCount() < this.graph.graphHandler.maxCells) ? '' : 'none'; - } - - this.redrawHandles(); - }); - - this.selectionHandler = mxUtils.bind(this, function(sender, evt) - { - update(); - }); - - this.graph.getSelectionModel().addListener(mxEvent.CHANGE, this.selectionHandler); - - this.changeHandler = mxUtils.bind(this, function(sender, evt) - { - this.updateLinkHint(this.graph.getLinkForCell(this.state.cell), - this.graph.getLinksForState(this.state)); - update(); - }); - - this.graph.getModel().addListener(mxEvent.CHANGE, this.changeHandler); - - // Repaint needed when editing stops and no change event is fired - this.editingHandler = mxUtils.bind(this, function(sender, evt) - { - this.redrawHandles(); - }); - - this.graph.addListener(mxEvent.EDITING_STOPPED, this.editingHandler); - - var link = this.graph.getLinkForCell(this.state.cell); - var links = this.graph.getLinksForState(this.state); - this.updateLinkHint(link, links); - - if (link != null || (links != null && links.length > 0)) - { - redraw = true; - } - - if (redraw) - { - this.redrawHandles(); - } - }; - - mxVertexHandler.prototype.updateLinkHint = function(link, links) - { - if ((link == null && (links == null || links.length == 0)) || - this.graph.getSelectionCount() > 1) - { - if (this.linkHint != null) - { - this.linkHint.parentNode.removeChild(this.linkHint); - this.linkHint = null; - } - } - else if (link != null || (links != null && links.length > 0)) - { - if (this.linkHint == null) - { - this.linkHint = createHint(); - this.linkHint.style.padding = '6px 8px 6px 8px'; - this.linkHint.style.opacity = '1'; - this.linkHint.style.filter = ''; - - this.graph.container.appendChild(this.linkHint); - } - - this.linkHint.innerHTML = ''; - - if (link != null) - { - this.linkHint.appendChild(this.graph.createLinkForHint(link)); - - if (this.graph.isEnabled() && typeof this.graph.editLink === 'function') - { - var changeLink = document.createElement('img'); - changeLink.setAttribute('src', Editor.editImage); - changeLink.setAttribute('title', mxResources.get('editLink')); - changeLink.setAttribute('width', '11'); - changeLink.setAttribute('height', '11'); - changeLink.style.marginLeft = '10px'; - changeLink.style.marginBottom = '-1px'; - changeLink.style.cursor = 'pointer'; - this.linkHint.appendChild(changeLink); - - mxEvent.addListener(changeLink, 'click', mxUtils.bind(this, function(evt) - { - this.graph.setSelectionCell(this.state.cell); - this.graph.editLink(); - mxEvent.consume(evt); - })); - - var removeLink = document.createElement('img'); - removeLink.setAttribute('src', Dialog.prototype.clearImage); - removeLink.setAttribute('title', mxResources.get('removeIt', [mxResources.get('link')])); - removeLink.setAttribute('width', '13'); - removeLink.setAttribute('height', '10'); - removeLink.style.marginLeft = '4px'; - removeLink.style.marginBottom = '-1px'; - removeLink.style.cursor = 'pointer'; - this.linkHint.appendChild(removeLink); - - mxEvent.addListener(removeLink, 'click', mxUtils.bind(this, function(evt) - { - this.graph.setLinkForCell(this.state.cell, null); - mxEvent.consume(evt); - })); - } - } - - if (links != null) - { - for (var i = 0; i < links.length; i++) - { - var div = document.createElement('div'); - div.style.marginTop = (link != null || i > 0) ? '6px' : '0px'; - div.appendChild(this.graph.createLinkForHint( - links[i].getAttribute('href'), - mxUtils.getTextContent(links[i]))); - - this.linkHint.appendChild(div); - } - } - } - }; - - mxEdgeHandler.prototype.updateLinkHint = mxVertexHandler.prototype.updateLinkHint; - - var edgeHandlerInit = mxEdgeHandler.prototype.init; - mxEdgeHandler.prototype.init = function() - { - edgeHandlerInit.apply(this, arguments); - - // Disables connection points - this.constraintHandler.isEnabled = mxUtils.bind(this, function() - { - return this.state.view.graph.connectionHandler.isEnabled(); - }); - - var update = mxUtils.bind(this, function() - { - if (this.linkHint != null) - { - this.linkHint.style.display = (this.graph.getSelectionCount() == 1) ? '' : 'none'; - } - - if (this.labelShape != null) - { - this.labelShape.node.style.display = (this.graph.isEnabled() && this.graph.getSelectionCount() < this.graph.graphHandler.maxCells) ? '' : 'none'; - } - }); - - this.selectionHandler = mxUtils.bind(this, function(sender, evt) - { - update(); - }); - - this.graph.getSelectionModel().addListener(mxEvent.CHANGE, this.selectionHandler); - - this.changeHandler = mxUtils.bind(this, function(sender, evt) - { - this.updateLinkHint(this.graph.getLinkForCell(this.state.cell), - this.graph.getLinksForState(this.state)); - update(); - this.redrawHandles(); - }); - - this.graph.getModel().addListener(mxEvent.CHANGE, this.changeHandler); - - var link = this.graph.getLinkForCell(this.state.cell); - var links = this.graph.getLinksForState(this.state); - - if (link != null || (links != null && links.length > 0)) - { - this.updateLinkHint(link, links); - this.redrawHandles(); - } - }; - - // Disables connection points - var connectionHandlerInit = mxConnectionHandler.prototype.init; - - mxConnectionHandler.prototype.init = function() - { - connectionHandlerInit.apply(this, arguments); - - this.constraintHandler.isEnabled = mxUtils.bind(this, function() - { - return this.graph.connectionHandler.isEnabled(); - }); - }; - - var vertexHandlerRedrawHandles = mxVertexHandler.prototype.redrawHandles; - mxVertexHandler.prototype.redrawHandles = function() - { - vertexHandlerRedrawHandles.apply(this); - - if (this.state != null && this.linkHint != null) - { - var c = new mxPoint(this.state.getCenterX(), this.state.getCenterY()); - var tmp = new mxRectangle(this.state.x, this.state.y - 22, this.state.width + 24, this.state.height + 22); - var bb = mxUtils.getBoundingBox(tmp, this.state.style[mxConstants.STYLE_ROTATION] || '0', c); - var rs = (bb != null) ? mxUtils.getBoundingBox(this.state, - this.state.style[mxConstants.STYLE_ROTATION] || '0') : this.state; - var tb = (this.state.text != null) ? this.state.text.boundingBox : null; - - if (bb == null) - { - bb = this.state; - } - - var b = bb.y + bb.height; - - if (tb != null) - { - b = Math.max(b, tb.y + tb.height); - } - - this.linkHint.style.left = Math.max(0, Math.round(rs.x + (rs.width - this.linkHint.clientWidth) / 2)) + 'px'; - this.linkHint.style.top = Math.round(b + this.verticalOffset / 2 + 6 + - this.state.view.graph.tolerance) + 'px'; - } - }; - - - var vertexHandlerReset = mxVertexHandler.prototype.reset; - mxVertexHandler.prototype.reset = function() - { - vertexHandlerReset.apply(this, arguments); - - // Shows rotation handle only if one vertex is selected - if (this.rotationShape != null && this.rotationShape.node != null) - { - this.rotationShape.node.style.display = (this.graph.getSelectionCount() == 1) ? '' : 'none'; - } - }; - - var vertexHandlerDestroy = mxVertexHandler.prototype.destroy; - mxVertexHandler.prototype.destroy = function() - { - vertexHandlerDestroy.apply(this, arguments); - - if (this.linkHint != null) - { - this.linkHint.parentNode.removeChild(this.linkHint); - this.linkHint = null; - } - - if (this.selectionHandler != null) - { - this.graph.getSelectionModel().removeListener(this.selectionHandler); - this.selectionHandler = null; - } - - if (this.changeHandler != null) - { - this.graph.getModel().removeListener(this.changeHandler); - this.changeHandler = null; - } - - if (this.editingHandler != null) - { - this.graph.removeListener(this.editingHandler); - this.editingHandler = null; - } - }; - - var edgeHandlerRedrawHandles = mxEdgeHandler.prototype.redrawHandles; - mxEdgeHandler.prototype.redrawHandles = function() - { - // Workaround for special case where handler - // is reset before this which leads to a NPE - if (this.marker != null) - { - edgeHandlerRedrawHandles.apply(this); - - if (this.state != null && this.linkHint != null) - { - var b = this.state; - - if (this.state.text != null && this.state.text.bounds != null) - { - b = new mxRectangle(b.x, b.y, b.width, b.height); - b.add(this.state.text.bounds); - } - - this.linkHint.style.left = Math.max(0, Math.round(b.x + (b.width - this.linkHint.clientWidth) / 2)) + 'px'; - this.linkHint.style.top = Math.round(b.y + b.height + 6 + this.state.view.graph.tolerance) + 'px'; - } - } - }; - - var edgeHandlerReset = mxEdgeHandler.prototype.reset; - mxEdgeHandler.prototype.reset = function() - { - edgeHandlerReset.apply(this, arguments); - - if (this.linkHint != null) - { - this.linkHint.style.visibility = ''; - } - }; - - var edgeHandlerDestroy = mxEdgeHandler.prototype.destroy; - mxEdgeHandler.prototype.destroy = function() - { - edgeHandlerDestroy.apply(this, arguments); - - if (this.linkHint != null) - { - this.linkHint.parentNode.removeChild(this.linkHint); - this.linkHint = null; - } - - if (this.selectionHandler != null) - { - this.graph.getSelectionModel().removeListener(this.selectionHandler); - this.selectionHandler = null; - } - - if (this.changeHandler != null) - { - this.graph.getModel().removeListener(this.changeHandler); - this.changeHandler = null; - } - }; - })(); -} diff --git a/media/grapheditor/js/Init.js b/media/grapheditor/js/Init.js deleted file mode 100644 index 32ab10b9ac..0000000000 --- a/media/grapheditor/js/Init.js +++ /dev/null @@ -1,29 +0,0 @@ -// urlParams is null when used for embedding -window.urlParams = window.urlParams || {}; - -// Public global variables -window.MAX_REQUEST_SIZE = window.MAX_REQUEST_SIZE || 10485760; -window.MAX_AREA = window.MAX_AREA || 15000 * 15000; - -// URLs for save and export -window.EXPORT_URL = window.EXPORT_URL || '/export'; -window.SAVE_URL = window.SAVE_URL || '/save'; -window.OPEN_URL = window.OPEN_URL || '/open'; -window.RESOURCES_PATH = window.RESOURCES_PATH || 'resources'; -window.RESOURCE_BASE = window.RESOURCE_BASE || window.RESOURCES_PATH + '/grapheditor'; -window.STENCIL_PATH = window.STENCIL_PATH || 'stencils'; -window.IMAGE_PATH = window.IMAGE_PATH || 'images'; -window.STYLE_PATH = window.STYLE_PATH || 'styles'; -window.CSS_PATH = window.CSS_PATH || 'styles'; -window.OPEN_FORM = window.OPEN_FORM || 'open.html'; - -// Sets the base path, the UI language via URL param and configures the -// supported languages to avoid 404s. The loading of all core language -// resources is disabled as all required resources are in grapheditor. -// properties. Note that in this example the loading of two resource -// files (the special bundle and the default bundle) is disabled to -// save a GET request. This requires that all resources be present in -// each properties file since only one file is loaded. -window.mxBasePath = window.mxBasePath || '../../../src'; -window.mxLanguage = window.mxLanguage || urlParams['lang']; -window.mxLanguages = window.mxLanguages || ['de']; diff --git a/media/grapheditor/js/Menus.js b/media/grapheditor/js/Menus.js deleted file mode 100644 index 9b0ac24e04..0000000000 --- a/media/grapheditor/js/Menus.js +++ /dev/null @@ -1,1315 +0,0 @@ -/** - * Copyright (c) 2006-2012, JGraph Ltd - */ -/** - * Constructs a new graph editor - */ -Menus = function(editorUi) -{ - this.editorUi = editorUi; - this.menus = new Object(); - this.init(); - - // Pre-fetches checkmark image - if (!mxClient.IS_SVG) - { - new Image().src = this.checkmarkImage; - } -}; - -/** - * Sets the default font family. - */ -Menus.prototype.defaultFont = 'Helvetica'; - -/** - * Sets the default font size. - */ -Menus.prototype.defaultFontSize = '12'; - -/** - * Sets the default font size. - */ -Menus.prototype.defaultMenuItems = ['file', 'edit', 'view', 'arrange', 'extras', 'help']; - -/** - * Adds the label menu items to the given menu and parent. - */ -Menus.prototype.defaultFonts = ['Helvetica', 'Verdana', 'Times New Roman', 'Garamond', 'Comic Sans MS', - 'Courier New', 'Georgia', 'Lucida Console', 'Tahoma']; - -/** - * Adds the label menu items to the given menu and parent. - */ -Menus.prototype.init = function() -{ - var graph = this.editorUi.editor.graph; - var isGraphEnabled = mxUtils.bind(graph, graph.isEnabled); - - this.customFonts = []; - this.customFontSizes = []; - - this.put('fontFamily', new Menu(mxUtils.bind(this, function(menu, parent) - { - var addItem = mxUtils.bind(this, function(fontname) - { - var tr = this.styleChange(menu, fontname, [mxConstants.STYLE_FONTFAMILY], [fontname], null, parent, function() - { - document.execCommand('fontname', false, fontname); - }, function() - { - graph.updateLabelElements(graph.getSelectionCells(), function(elt) - { - elt.removeAttribute('face'); - elt.style.fontFamily = null; - - if (elt.nodeName == 'PRE') - { - graph.replaceElement(elt, 'div'); - } - }); - }); - tr.firstChild.nextSibling.style.fontFamily = fontname; - }); - - for (var i = 0; i < this.defaultFonts.length; i++) - { - addItem(this.defaultFonts[i]); - } - - menu.addSeparator(parent); - - if (this.customFonts.length > 0) - { - for (var i = 0; i < this.customFonts.length; i++) - { - addItem(this.customFonts[i]); - } - - menu.addSeparator(parent); - - menu.addItem(mxResources.get('reset'), null, mxUtils.bind(this, function() - { - this.customFonts = []; - }), parent); - - menu.addSeparator(parent); - } - - this.promptChange(menu, mxResources.get('custom') + '...', '', mxConstants.DEFAULT_FONTFAMILY, mxConstants.STYLE_FONTFAMILY, parent, true, mxUtils.bind(this, function(newValue) - { - this.customFonts.push(newValue); - })); - }))); - this.put('formatBlock', new Menu(mxUtils.bind(this, function(menu, parent) - { - function addItem(label, tag) - { - return menu.addItem(label, null, mxUtils.bind(this, function() - { - // TODO: Check if visible - graph.cellEditor.textarea.focus(); - document.execCommand('formatBlock', false, '<' + tag + '>'); - }), parent); - }; - - addItem(mxResources.get('normal'), 'p'); - - addItem('', 'h1').firstChild.nextSibling.innerHTML = '

' + mxResources.get('heading') + ' 1

'; - addItem('', 'h2').firstChild.nextSibling.innerHTML = '

' + mxResources.get('heading') + ' 2

'; - addItem('', 'h3').firstChild.nextSibling.innerHTML = '

' + mxResources.get('heading') + ' 3

'; - addItem('', 'h4').firstChild.nextSibling.innerHTML = '

' + mxResources.get('heading') + ' 4

'; - addItem('', 'h5').firstChild.nextSibling.innerHTML = '
' + mxResources.get('heading') + ' 5
'; - addItem('', 'h6').firstChild.nextSibling.innerHTML = '
' + mxResources.get('heading') + ' 6
'; - - addItem('', 'pre').firstChild.nextSibling.innerHTML = '
' + mxResources.get('formatted') + '
'; - addItem('', 'blockquote').firstChild.nextSibling.innerHTML = '
' + mxResources.get('blockquote') + '
'; - }))); - this.put('fontSize', new Menu(mxUtils.bind(this, function(menu, parent) - { - var sizes = [6, 8, 9, 10, 11, 12, 14, 18, 24, 36, 48, 72]; - - var addItem = mxUtils.bind(this, function(fontsize) - { - this.styleChange(menu, fontsize, [mxConstants.STYLE_FONTSIZE], [fontsize], null, parent, function() - { - // Creates an element with arbitrary size 3 - document.execCommand('fontSize', false, '3'); - - // Changes the css font size of the first font element inside the in-place editor with size 3 - // hopefully the above element that we've just created. LATER: Check for new element using - // previous result of getElementsByTagName (see other actions) - var elts = graph.cellEditor.textarea.getElementsByTagName('font'); - - for (var i = 0; i < elts.length; i++) - { - if (elts[i].getAttribute('size') == '3') - { - elts[i].removeAttribute('size'); - elts[i].style.fontSize = fontsize + 'px'; - - break; - } - } - }); - }); - - for (var i = 0; i < sizes.length; i++) - { - addItem(sizes[i]); - } - - menu.addSeparator(parent); - - if (this.customFontSizes.length > 0) - { - for (var i = 0; i < this.customFontSizes.length; i++) - { - addItem(this.customFontSizes[i]); - } - - menu.addSeparator(parent); - - menu.addItem(mxResources.get('reset'), null, mxUtils.bind(this, function() - { - this.customFontSizes = []; - }), parent); - - menu.addSeparator(parent); - } - - this.promptChange(menu, mxResources.get('custom') + '...', '(pt)', '12', mxConstants.STYLE_FONTSIZE, parent, true, mxUtils.bind(this, function(newValue) - { - this.customFontSizes.push(newValue); - })); - }))); - this.put('direction', new Menu(mxUtils.bind(this, function(menu, parent) - { - menu.addItem(mxResources.get('flipH'), null, function() { graph.toggleCellStyles(mxConstants.STYLE_FLIPH, false); }, parent); - menu.addItem(mxResources.get('flipV'), null, function() { graph.toggleCellStyles(mxConstants.STYLE_FLIPV, false); }, parent); - this.addMenuItems(menu, ['-', 'rotation'], parent); - }))); - this.put('align', new Menu(mxUtils.bind(this, function(menu, parent) - { - menu.addItem(mxResources.get('leftAlign'), null, function() { graph.alignCells(mxConstants.ALIGN_LEFT); }, parent); - menu.addItem(mxResources.get('center'), null, function() { graph.alignCells(mxConstants.ALIGN_CENTER); }, parent); - menu.addItem(mxResources.get('rightAlign'), null, function() { graph.alignCells(mxConstants.ALIGN_RIGHT); }, parent); - menu.addSeparator(parent); - menu.addItem(mxResources.get('topAlign'), null, function() { graph.alignCells(mxConstants.ALIGN_TOP); }, parent); - menu.addItem(mxResources.get('middle'), null, function() { graph.alignCells(mxConstants.ALIGN_MIDDLE); }, parent); - menu.addItem(mxResources.get('bottomAlign'), null, function() { graph.alignCells(mxConstants.ALIGN_BOTTOM); }, parent); - }))); - this.put('distribute', new Menu(mxUtils.bind(this, function(menu, parent) - { - menu.addItem(mxResources.get('horizontal'), null, function() { graph.distributeCells(true); }, parent); - menu.addItem(mxResources.get('vertical'), null, function() { graph.distributeCells(false); }, parent); - }))); - this.put('layout', new Menu(mxUtils.bind(this, function(menu, parent) - { - var promptSpacing = mxUtils.bind(this, function(defaultValue, fn) - { - var dlg = new FilenameDialog(this.editorUi, defaultValue, mxResources.get('apply'), function(newValue) - { - fn(parseFloat(newValue)); - }, mxResources.get('spacing')); - this.editorUi.showDialog(dlg.container, 300, 80, true, true); - dlg.init(); - }); - - menu.addItem(mxResources.get('horizontalFlow'), null, mxUtils.bind(this, function() - { - var layout = new mxHierarchicalLayout(graph, mxConstants.DIRECTION_WEST); - - this.editorUi.executeLayout(function() - { - var selectionCells = graph.getSelectionCells(); - layout.execute(graph.getDefaultParent(), selectionCells.length == 0 ? null : selectionCells); - }, true); - }), parent); - menu.addItem(mxResources.get('verticalFlow'), null, mxUtils.bind(this, function() - { - var layout = new mxHierarchicalLayout(graph, mxConstants.DIRECTION_NORTH); - - this.editorUi.executeLayout(function() - { - var selectionCells = graph.getSelectionCells(); - layout.execute(graph.getDefaultParent(), selectionCells.length == 0 ? null : selectionCells); - }, true); - }), parent); - menu.addSeparator(parent); - menu.addItem(mxResources.get('horizontalTree'), null, mxUtils.bind(this, function() - { - var tmp = graph.getSelectionCell(); - var roots = null; - - if (tmp == null || graph.getModel().getChildCount(tmp) == 0) - { - if (graph.getModel().getEdgeCount(tmp) == 0) - { - roots = graph.findTreeRoots(graph.getDefaultParent()); - } - } - else - { - roots = graph.findTreeRoots(tmp); - } - - if (roots != null && roots.length > 0) - { - tmp = roots[0]; - } - - if (tmp != null) - { - var layout = new mxCompactTreeLayout(graph, true); - layout.edgeRouting = false; - layout.levelDistance = 30; - - promptSpacing(layout.levelDistance, mxUtils.bind(this, function(newValue) - { - layout.levelDistance = newValue; - - this.editorUi.executeLayout(function() - { - layout.execute(graph.getDefaultParent(), tmp); - }, true); - })); - } - }), parent); - menu.addItem(mxResources.get('verticalTree'), null, mxUtils.bind(this, function() - { - var tmp = graph.getSelectionCell(); - var roots = null; - - if (tmp == null || graph.getModel().getChildCount(tmp) == 0) - { - if (graph.getModel().getEdgeCount(tmp) == 0) - { - roots = graph.findTreeRoots(graph.getDefaultParent()); - } - } - else - { - roots = graph.findTreeRoots(tmp); - } - - if (roots != null && roots.length > 0) - { - tmp = roots[0]; - } - - if (tmp != null) - { - var layout = new mxCompactTreeLayout(graph, false); - layout.edgeRouting = false; - layout.levelDistance = 30; - - promptSpacing(layout.levelDistance, mxUtils.bind(this, function(newValue) - { - layout.levelDistance = newValue; - - this.editorUi.executeLayout(function() - { - layout.execute(graph.getDefaultParent(), tmp); - }, true); - })); - } - }), parent); - menu.addItem(mxResources.get('radialTree'), null, mxUtils.bind(this, function() - { - var tmp = graph.getSelectionCell(); - var roots = null; - - if (tmp == null || graph.getModel().getChildCount(tmp) == 0) - { - if (graph.getModel().getEdgeCount(tmp) == 0) - { - roots = graph.findTreeRoots(graph.getDefaultParent()); - } - } - else - { - roots = graph.findTreeRoots(tmp); - } - - if (roots != null && roots.length > 0) - { - tmp = roots[0]; - } - - if (tmp != null) - { - var layout = new mxRadialTreeLayout(graph, false); - layout.levelDistance = 80; - layout.autoRadius = true; - - promptSpacing(layout.levelDistance, mxUtils.bind(this, function(newValue) - { - layout.levelDistance = newValue; - - this.editorUi.executeLayout(function() - { - layout.execute(graph.getDefaultParent(), tmp); - - if (!graph.isSelectionEmpty()) - { - tmp = graph.getModel().getParent(tmp); - - if (graph.getModel().isVertex(tmp)) - { - graph.updateGroupBounds([tmp], graph.gridSize * 2, true); - } - } - }, true); - })); - } - }), parent); - menu.addSeparator(parent); - menu.addItem(mxResources.get('organic'), null, mxUtils.bind(this, function() - { - var layout = new mxFastOrganicLayout(graph); - - promptSpacing(layout.forceConstant, mxUtils.bind(this, function(newValue) - { - layout.forceConstant = newValue; - - this.editorUi.executeLayout(function() - { - var tmp = graph.getSelectionCell(); - - if (tmp == null || graph.getModel().getChildCount(tmp) == 0) - { - tmp = graph.getDefaultParent(); - } - - layout.execute(tmp); - - if (graph.getModel().isVertex(tmp)) - { - graph.updateGroupBounds([tmp], graph.gridSize * 2, true); - } - }, true); - })); - }), parent); - menu.addItem(mxResources.get('circle'), null, mxUtils.bind(this, function() - { - var layout = new mxCircleLayout(graph); - - this.editorUi.executeLayout(function() - { - var tmp = graph.getSelectionCell(); - - if (tmp == null || graph.getModel().getChildCount(tmp) == 0) - { - tmp = graph.getDefaultParent(); - } - - layout.execute(tmp); - - if (graph.getModel().isVertex(tmp)) - { - graph.updateGroupBounds([tmp], graph.gridSize * 2, true); - } - }, true); - }), parent); - }))); - this.put('navigation', new Menu(mxUtils.bind(this, function(menu, parent) - { - this.addMenuItems(menu, ['home', '-', 'exitGroup', 'enterGroup', '-', 'expand', 'collapse', '-', 'collapsible'], parent); - }))); - this.put('arrange', new Menu(mxUtils.bind(this, function(menu, parent) - { - this.addMenuItems(menu, ['toFront', 'toBack', '-'], parent); - this.addSubmenu('direction', menu, parent); - this.addMenuItems(menu, ['turn', '-'], parent); - this.addSubmenu('align', menu, parent); - this.addSubmenu('distribute', menu, parent); - menu.addSeparator(parent); - this.addSubmenu('navigation', menu, parent); - this.addSubmenu('insert', menu, parent); - this.addSubmenu('layout', menu, parent); - this.addMenuItems(menu, ['-', 'group', 'ungroup', 'removeFromGroup', '-', 'clearWaypoints', 'autosize'], parent); - }))).isEnabled = isGraphEnabled; - this.put('insert', new Menu(mxUtils.bind(this, function(menu, parent) - { - this.addMenuItems(menu, ['insertLink', 'insertImage'], parent); - }))); - this.put('view', new Menu(mxUtils.bind(this, function(menu, parent) - { - this.addMenuItems(menu, ((this.editorUi.format != null) ? ['formatPanel'] : []). - concat(['outline', 'layers', '-', 'pageView', 'pageScale', '-', 'scrollbars', 'tooltips', '-', - 'grid', 'guides', '-', 'connectionArrows', 'connectionPoints', '-', - 'resetView', 'zoomIn', 'zoomOut'], parent)); - }))); - // Two special dropdowns that are only used in the toolbar - this.put('viewPanels', new Menu(mxUtils.bind(this, function(menu, parent) - { - if (this.editorUi.format != null) - { - this.addMenuItems(menu, ['formatPanel'], parent); - } - - this.addMenuItems(menu, ['outline', 'layers'], parent); - }))); - this.put('viewZoom', new Menu(mxUtils.bind(this, function(menu, parent) - { - this.addMenuItems(menu, ['resetView', '-'], parent); - var scales = [0.25, 0.5, 0.75, 1, 1.25, 1.5, 2, 3, 4]; - - for (var i = 0; i < scales.length; i++) - { - (function(scale) - { - menu.addItem((scale * 100) + '%', null, function() - { - graph.zoomTo(scale); - }, parent); - })(scales[i]); - } - - this.addMenuItems(menu, ['-', 'fitWindow', 'fitPageWidth', 'fitPage', 'fitTwoPages', '-', 'customZoom'], parent); - }))); - this.put('file', new Menu(mxUtils.bind(this, function(menu, parent) - { - this.addMenuItems(menu, ['new', 'open', '-', 'save', 'saveAs', '-', 'import', 'export', '-', 'pageSetup', 'print'], parent); - }))); - this.put('edit', new Menu(mxUtils.bind(this, function(menu, parent) - { - this.addMenuItems(menu, ['undo', 'redo', '-', 'cut', 'copy', 'paste', 'delete', '-', 'duplicate', '-', - 'editData', 'editTooltip', 'editStyle', '-', 'edit', '-', 'editLink', 'openLink', '-', - 'selectVertices', 'selectEdges', 'selectAll', 'selectNone', '-', 'lockUnlock']); - }))); - this.put('extras', new Menu(mxUtils.bind(this, function(menu, parent) - { - this.addMenuItems(menu, ['copyConnect', 'collapseExpand', '-', 'editDiagram']); - }))); - this.put('help', new Menu(mxUtils.bind(this, function(menu, parent) - { - this.addMenuItems(menu, ['help', '-', 'about']); - }))); -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -Menus.prototype.put = function(name, menu) -{ - this.menus[name] = menu; - - return menu; -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -Menus.prototype.get = function(name) -{ - return this.menus[name]; -}; - -/** - * Adds the given submenu. - */ -Menus.prototype.addSubmenu = function(name, menu, parent, label) -{ - var entry = this.get(name); - - if (entry != null) - { - var enabled = entry.isEnabled(); - - if (menu.showDisabled || enabled) - { - var submenu = menu.addItem(label || mxResources.get(name), null, null, parent, null, enabled); - this.addMenu(name, menu, submenu); - } - } -}; - -/** - * Adds the label menu items to the given menu and parent. - */ -Menus.prototype.addMenu = function(name, popupMenu, parent) -{ - var menu = this.get(name); - - if (menu != null && (popupMenu.showDisabled || menu.isEnabled())) - { - this.get(name).execute(popupMenu, parent); - } -}; - -/** - * Adds a menu item to insert a table. - */ -Menus.prototype.addInsertTableItem = function(menu) -{ - // KNOWN: Does not work in IE8 standards and quirks - var graph = this.editorUi.editor.graph; - - function createTable(rows, cols) - { - var html = ['']; - - for (var i = 0; i < rows; i++) - { - html.push(''); - - for (var j = 0; j < cols; j++) - { - html.push(''); - } - - html.push(''); - } - - html.push('

'); - - return html.join(''); - }; - - // Show table size dialog - var elt2 = menu.addItem('', null, mxUtils.bind(this, function(evt) - { - var td = graph.getParentByName(mxEvent.getSource(evt), 'TD'); - - if (td != null) - { - var row2 = graph.getParentByName(td, 'TR'); - - // To find the new link, we create a list of all existing links first - // LATER: Refactor for reuse with code for finding inserted image below - var tmp = graph.cellEditor.textarea.getElementsByTagName('table'); - var oldTables = []; - - for (var i = 0; i < tmp.length; i++) - { - oldTables.push(tmp[i]); - } - - // Finding the new table will work with insertHTML, but IE does not support that - graph.container.focus(); - graph.pasteHtmlAtCaret(createTable(row2.sectionRowIndex + 1, td.cellIndex + 1)); - - // Moves cursor to first table cell - var newTables = graph.cellEditor.textarea.getElementsByTagName('table'); - - if (newTables.length == oldTables.length + 1) - { - // Inverse order in favor of appended tables - for (var i = newTables.length - 1; i >= 0; i--) - { - if (i == 0 || newTables[i] != oldTables[i - 1]) - { - graph.selectNode(newTables[i].rows[0].cells[0]); - break; - } - } - } - } - })); - - // Quirks mode does not add cell padding if cell is empty, needs good old spacer solution - var quirksCellHtml = ''; - - function createPicker(rows, cols) - { - var table2 = document.createElement('table'); - table2.setAttribute('border', '1'); - table2.style.borderCollapse = 'collapse'; - - if (!mxClient.IS_QUIRKS) - { - table2.setAttribute('cellPadding', '8'); - } - - for (var i = 0; i < rows; i++) - { - var row = table2.insertRow(i); - - for (var j = 0; j < cols; j++) - { - var cell = row.insertCell(-1); - - if (mxClient.IS_QUIRKS) - { - cell.innerHTML = quirksCellHtml; - } - } - } - - return table2; - }; - - function extendPicker(picker, rows, cols) - { - for (var i = picker.rows.length; i < rows; i++) - { - var row = picker.insertRow(i); - - for (var j = 0; j < picker.rows[0].cells.length; j++) - { - var cell = row.insertCell(-1); - - if (mxClient.IS_QUIRKS) - { - cell.innerHTML = quirksCellHtml; - } - } - } - - for (var i = 0; i < picker.rows.length; i++) - { - var row = picker.rows[i]; - - for (var j = row.cells.length; j < cols; j++) - { - var cell = row.insertCell(-1); - - if (mxClient.IS_QUIRKS) - { - cell.innerHTML = quirksCellHtml; - } - } - } - }; - - elt2.firstChild.innerHTML = ''; - var picker = createPicker(5, 5); - elt2.firstChild.appendChild(picker); - - var label = document.createElement('div'); - label.style.padding = '4px'; - label.style.fontSize = Menus.prototype.defaultFontSize + 'px'; - label.innerHTML = '1x1'; - elt2.firstChild.appendChild(label); - - mxEvent.addListener(picker, 'mouseover', function(e) - { - var td = graph.getParentByName(mxEvent.getSource(e), 'TD'); - - if (td != null) - { - var row2 = graph.getParentByName(td, 'TR'); - extendPicker(picker, Math.min(20, row2.sectionRowIndex + 2), Math.min(20, td.cellIndex + 2)); - label.innerHTML = (td.cellIndex + 1) + 'x' + (row2.sectionRowIndex + 1); - - for (var i = 0; i < picker.rows.length; i++) - { - var r = picker.rows[i]; - - for (var j = 0; j < r.cells.length; j++) - { - var cell = r.cells[j]; - - if (i <= row2.sectionRowIndex && j <= td.cellIndex) - { - cell.style.backgroundColor = 'blue'; - } - else - { - cell.style.backgroundColor = 'white'; - } - } - } - - mxEvent.consume(e); - } - }); -}; - -/** - * Adds a style change item to the given menu. - */ -Menus.prototype.edgeStyleChange = function(menu, label, keys, values, sprite, parent, reset) -{ - return menu.addItem(label, null, mxUtils.bind(this, function() - { - var graph = this.editorUi.editor.graph; - graph.stopEditing(false); - - graph.getModel().beginUpdate(); - try - { - var cells = graph.getSelectionCells(); - var edges = []; - - for (var i = 0; i < cells.length; i++) - { - var cell = cells[i]; - - if (graph.getModel().isEdge(cell)) - { - if (reset) - { - var geo = graph.getCellGeometry(cell); - - // Resets all edge points - if (geo != null) - { - geo = geo.clone(); - geo.points = null; - graph.getModel().setGeometry(cell, geo); - } - } - - for (var j = 0; j < keys.length; j++) - { - graph.setCellStyles(keys[j], values[j], [cell]); - } - - edges.push(cell); - } - } - - this.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', keys, - 'values', values, 'cells', edges)); - } - finally - { - graph.getModel().endUpdate(); - } - }), parent, sprite); -}; - -/** - * Adds a style change item to the given menu. - */ -Menus.prototype.styleChange = function(menu, label, keys, values, sprite, parent, fn, post) -{ - var apply = this.createStyleChangeFunction(keys, values); - - return menu.addItem(label, null, mxUtils.bind(this, function() - { - var graph = this.editorUi.editor.graph; - - if (fn != null && graph.cellEditor.isContentEditing()) - { - fn(); - } - else - { - apply(post); - } - }), parent, sprite); -}; - -/** - * - */ -Menus.prototype.createStyleChangeFunction = function(keys, values) -{ - return mxUtils.bind(this, function(post) - { - var graph = this.editorUi.editor.graph; - graph.stopEditing(false); - - graph.getModel().beginUpdate(); - try - { - for (var i = 0; i < keys.length; i++) - { - graph.setCellStyles(keys[i], values[i]); - } - - if (post != null) - { - post(); - } - - this.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', keys, 'values', values, - 'cells', graph.getSelectionCells())); - } - finally - { - graph.getModel().endUpdate(); - } - }); -}; - -/** - * Adds a style change item with a prompt to the given menu. - */ -Menus.prototype.promptChange = function(menu, label, hint, defaultValue, key, parent, enabled, fn, sprite) -{ - return menu.addItem(label, null, mxUtils.bind(this, function() - { - var graph = this.editorUi.editor.graph; - var value = defaultValue; - var state = graph.getView().getState(graph.getSelectionCell()); - - if (state != null) - { - value = state.style[key] || value; - } - - var dlg = new FilenameDialog(this.editorUi, value, mxResources.get('apply'), mxUtils.bind(this, function(newValue) - { - if (newValue != null && newValue.length > 0) - { - graph.getModel().beginUpdate(); - try - { - graph.stopEditing(false); - graph.setCellStyles(key, newValue); - } - finally - { - graph.getModel().endUpdate(); - } - - if (fn != null) - { - fn(newValue); - } - } - }), mxResources.get('enterValue') + ((hint.length > 0) ? (' ' + hint) : '')); - this.editorUi.showDialog(dlg.container, 300, 80, true, true); - dlg.init(); - }), parent, sprite, enabled); -}; - -/** - * Adds a handler for showing a menu in the given element. - */ -Menus.prototype.pickColor = function(key, cmd, defaultValue) -{ - var graph = this.editorUi.editor.graph; - - if (cmd != null && graph.cellEditor.isContentEditing()) - { - // Saves and restores text selection for in-place editor - var selState = graph.cellEditor.saveSelection(); - - var dlg = new ColorDialog(this.editorUi, defaultValue || '000000', mxUtils.bind(this, function(color) - { - graph.cellEditor.restoreSelection(selState); - document.execCommand(cmd, false, (color != mxConstants.NONE) ? color : 'transparent'); - }), function() - { - graph.cellEditor.restoreSelection(selState); - }); - this.editorUi.showDialog(dlg.container, 230, 430, true, true); - dlg.init(); - } - else - { - if (this.colorDialog == null) - { - this.colorDialog = new ColorDialog(this.editorUi); - } - - this.colorDialog.currentColorKey = key; - var state = graph.getView().getState(graph.getSelectionCell()); - var color = 'none'; - - if (state != null) - { - color = state.style[key] || color; - } - - if (color == 'none') - { - color = 'ffffff'; - this.colorDialog.picker.fromString('ffffff'); - this.colorDialog.colorInput.value = 'none'; - } - else - { - this.colorDialog.picker.fromString(color); - } - - this.editorUi.showDialog(this.colorDialog.container, 230, 430, true, true); - this.colorDialog.init(); - } -}; - -/** - * Adds a handler for showing a menu in the given element. - */ -Menus.prototype.toggleStyle = function(key, defaultValue) -{ - var graph = this.editorUi.editor.graph; - var value = graph.toggleCellStyles(key, defaultValue); - this.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', [key], 'values', [value], - 'cells', graph.getSelectionCells())); -}; - -/** - * Creates the keyboard event handler for the current graph and history. - */ -Menus.prototype.addMenuItem = function(menu, key, parent, trigger, sprite, label) -{ - var action = this.editorUi.actions.get(key); - - if (action != null && (menu.showDisabled || action.isEnabled()) && action.visible) - { - var item = menu.addItem(label || action.label, null, function() - { - action.funct(trigger); - }, parent, sprite, action.isEnabled()); - - // Adds checkmark image - if (action.toggleAction && action.isSelected()) - { - menu.addCheckmark(item, Editor.checkmarkImage); - } - - this.addShortcut(item, action); - - return item; - } - - return null; -}; - -/** - * Adds a checkmark to the given menuitem. - */ -Menus.prototype.addShortcut = function(item, action) -{ - if (action.shortcut != null) - { - var td = item.firstChild.nextSibling.nextSibling; - var span = document.createElement('span'); - span.style.color = 'gray'; - mxUtils.write(span, action.shortcut); - td.appendChild(span); - } -}; - -/** - * Creates the keyboard event handler for the current graph and history. - */ -Menus.prototype.addMenuItems = function(menu, keys, parent, trigger, sprites) -{ - for (var i = 0; i < keys.length; i++) - { - if (keys[i] == '-') - { - menu.addSeparator(parent); - } - else - { - this.addMenuItem(menu, keys[i], parent, trigger, (sprites != null) ? sprites[i] : null); - } - } -}; - -/** - * Creates the keyboard event handler for the current graph and history. - */ -Menus.prototype.createPopupMenu = function(menu, cell, evt) -{ - var graph = this.editorUi.editor.graph; - menu.smartSeparators = true; - - if (graph.isSelectionEmpty()) - { - this.addMenuItems(menu, ['undo', 'redo', 'pasteHere'], null, evt); - } - else - { - this.addMenuItems(menu, ['delete', '-', 'cut', 'copy', '-', 'duplicate'], null, evt); - } - - if (!graph.isSelectionEmpty()) - { - if (graph.getSelectionCount() == 1) - { - this.addMenuItems(menu, ['setAsDefaultStyle'], null, evt); - } - - menu.addSeparator(); - - cell = graph.getSelectionCell(); - var state = graph.view.getState(cell); - - if (state != null) - { - var hasWaypoints = false; - this.addMenuItems(menu, ['toFront', 'toBack', '-'], null, evt); - - if (graph.getModel().isEdge(cell) && mxUtils.getValue(state.style, mxConstants.STYLE_EDGE, null) != 'entityRelationEdgeStyle' && - mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE, null) != 'arrow') - { - var handler = graph.selectionCellsHandler.getHandler(cell); - var isWaypoint = false; - - if (handler instanceof mxEdgeHandler && handler.bends != null && handler.bends.length > 2) - { - var index = handler.getHandleForEvent(graph.updateMouseEvent(new mxMouseEvent(evt))); - - // Configures removeWaypoint action before execution - // Using trigger parameter is cleaner but have to find waypoint here anyway. - var rmWaypointAction = this.editorUi.actions.get('removeWaypoint'); - rmWaypointAction.handler = handler; - rmWaypointAction.index = index; - - isWaypoint = index > 0 && index < handler.bends.length - 1; - } - - menu.addSeparator(); - this.addMenuItem(menu, 'turn', null, evt, null, mxResources.get('reverse')); - this.addMenuItems(menu, [(isWaypoint) ? 'removeWaypoint' : 'addWaypoint'], null, evt); - - // Adds reset waypoints option if waypoints exist - var geo = graph.getModel().getGeometry(cell); - hasWaypoints = geo != null && geo.points != null && geo.points.length > 0; - } - - if (graph.getSelectionCount() == 1 && (hasWaypoints || (graph.getModel().isVertex(cell) && - graph.getModel().getEdgeCount(cell) > 0))) - { - this.addMenuItems(menu, ['clearWaypoints'], null, evt); - } - - if (graph.getSelectionCount() > 1) - { - menu.addSeparator(); - this.addMenuItems(menu, ['group'], null, evt); - } - else if (graph.getSelectionCount() == 1 && !graph.getModel().isEdge(cell) && !graph.isSwimlane(cell) && - graph.getModel().getChildCount(cell) > 0) - { - menu.addSeparator(); - this.addMenuItems(menu, ['ungroup'], null, evt); - } - - if (graph.getSelectionCount() == 1) - { - menu.addSeparator(); - this.addMenuItems(menu, ['editData', 'editLink'], null, evt); - - // Shows edit image action if there is an image in the style - if (graph.getModel().isVertex(cell) && mxUtils.getValue(state.style, mxConstants.STYLE_IMAGE, null) != null) - { - menu.addSeparator(); - this.addMenuItem(menu, 'image', null, evt).firstChild.nextSibling.innerHTML = mxResources.get('editImage') + '...'; - } - } - } - } - else - { - this.addMenuItems(menu, ['-', 'selectVertices', 'selectEdges', - 'selectAll', '-', 'clearDefaultStyle'], null, evt); - } -}; - -/** - * Creates the keyboard event handler for the current graph and history. - */ -Menus.prototype.createMenubar = function(container) -{ - var menubar = new Menubar(this.editorUi, container); - var menus = this.defaultMenuItems; - - for (var i = 0; i < menus.length; i++) - { - (mxUtils.bind(this, function(menu) - { - var elt = menubar.addMenu(mxResources.get(menus[i]), mxUtils.bind(this, function() - { - // Allows extensions of menu.funct - menu.funct.apply(this, arguments); - })); - - this.menuCreated(menu, elt); - }))(this.get(menus[i])); - } - - return menubar; -}; - -/** - * Creates the keyboard event handler for the current graph and history. - */ -Menus.prototype.menuCreated = function(menu, elt, className) -{ - if (elt != null) - { - className = (className != null) ? className : 'geItem'; - - menu.addListener('stateChanged', function() - { - elt.enabled = menu.enabled; - - if (!menu.enabled) - { - elt.className = className + ' mxDisabled'; - - if (document.documentMode == 8) - { - elt.style.color = '#c3c3c3'; - } - } - else - { - elt.className = className; - - if (document.documentMode == 8) - { - elt.style.color = ''; - } - } - }); - } -}; - -/** - * Construcs a new menubar for the given editor. - */ -function Menubar(editorUi, container) -{ - this.editorUi = editorUi; - this.container = container; -}; - -/** - * Adds the menubar elements. - */ -Menubar.prototype.hideMenu = function() -{ - this.editorUi.hideCurrentMenu(); -}; - -/** - * Adds a submenu to this menubar. - */ -Menubar.prototype.addMenu = function(label, funct, before) -{ - var elt = document.createElement('a'); - elt.setAttribute('href', 'javascript:void(0);'); - elt.className = 'geItem'; - mxUtils.write(elt, label); - this.addMenuHandler(elt, funct); - - if (before != null) - { - this.container.insertBefore(elt, before); - } - else - { - this.container.appendChild(elt); - } - - return elt; -}; - -/** - * Adds a handler for showing a menu in the given element. - */ -Menubar.prototype.addMenuHandler = function(elt, funct) -{ - if (funct != null) - { - var show = true; - - var clickHandler = mxUtils.bind(this, function(evt) - { - if (show && elt.enabled == null || elt.enabled) - { - this.editorUi.editor.graph.popupMenuHandler.hideMenu(); - var menu = new mxPopupMenu(funct); - menu.div.className += ' geMenubarMenu'; - menu.smartSeparators = true; - menu.showDisabled = true; - menu.autoExpand = true; - - // Disables autoexpand and destroys menu when hidden - menu.hideMenu = mxUtils.bind(this, function() - { - mxPopupMenu.prototype.hideMenu.apply(menu, arguments); - this.editorUi.resetCurrentMenu(); - menu.destroy(); - }); - - var offset = mxUtils.getOffset(elt); - menu.popup(offset.x, offset.y + elt.offsetHeight, null, evt); - this.editorUi.setCurrentMenu(menu, elt); - } - - mxEvent.consume(evt); - }); - - // Shows menu automatically while in expanded state - mxEvent.addListener(elt, 'mousemove', mxUtils.bind(this, function(evt) - { - if (this.editorUi.currentMenu != null && this.editorUi.currentMenuElt != elt) - { - this.editorUi.hideCurrentMenu(); - clickHandler(evt); - } - })); - - // Hides menu if already showing - mxEvent.addListener(elt, 'mousedown', mxUtils.bind(this, function() - { - show = this.currentElt != elt; - })); - - mxEvent.addListener(elt, 'click', mxUtils.bind(this, function(evt) - { - clickHandler(evt); - show = true; - })); - } -}; - -/** - * Creates the keyboard event handler for the current graph and history. - */ -Menubar.prototype.destroy = function() -{ - // do nothing -}; - -/** - * Constructs a new action for the given parameters. - */ -function Menu(funct, enabled) -{ - mxEventSource.call(this); - this.funct = funct; - this.enabled = (enabled != null) ? enabled : true; -}; - -// Menu inherits from mxEventSource -mxUtils.extend(Menu, mxEventSource); - -/** - * Sets the enabled state of the action and fires a stateChanged event. - */ -Menu.prototype.isEnabled = function() -{ - return this.enabled; -}; - -/** - * Sets the enabled state of the action and fires a stateChanged event. - */ -Menu.prototype.setEnabled = function(value) -{ - if (this.enabled != value) - { - this.enabled = value; - this.fireEvent(new mxEventObject('stateChanged')); - } -}; - -/** - * Sets the enabled state of the action and fires a stateChanged event. - */ -Menu.prototype.execute = function(menu, parent) -{ - this.funct(menu, parent); -}; - -/** - * "Installs" menus in EditorUi. - */ -EditorUi.prototype.createMenus = function() -{ - return new Menus(this); -}; diff --git a/media/grapheditor/js/Shapes.js b/media/grapheditor/js/Shapes.js deleted file mode 100644 index 2427fa3522..0000000000 --- a/media/grapheditor/js/Shapes.js +++ /dev/null @@ -1,3965 +0,0 @@ -/** - * Copyright (c) 2006-2015, JGraph Ltd - */ - -/** - * Registers shapes. - */ -(function() -{ - // Cube Shape, supports size style - function CubeShape() - { - mxCylinder.call(this); - }; - mxUtils.extend(CubeShape, mxCylinder); - CubeShape.prototype.size = 20; - CubeShape.prototype.redrawPath = function(path, x, y, w, h, isForeground) - { - var s = Math.max(0, Math.min(w, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'size', this.size))))); - - if (isForeground) - { - path.moveTo(s, h); - path.lineTo(s, s); - path.lineTo(0, 0); - path.moveTo(s, s); - path.lineTo(w, s); - path.end(); - } - else - { - path.moveTo(0, 0); - path.lineTo(w - s, 0); - path.lineTo(w, s); - path.lineTo(w, h); - path.lineTo(s, h); - path.lineTo(0, h - s); - path.lineTo(0, 0); - path.close(); - path.end(); - } - }; - CubeShape.prototype.getLabelMargins = function(rect) - { - if (mxUtils.getValue(this.style, 'boundedLbl', false)) - { - var s = parseFloat(mxUtils.getValue(this.style, 'size', this.size)) * this.scale; - - return new mxRectangle(s, s, 0, 0); - } - - return null; - }; - - mxCellRenderer.registerShape('cube', CubeShape); - - var tan30 = Math.tan(mxUtils.toRadians(30)); - var tan30Dx = (0.5 - tan30) / 2; - - // Cube Shape, supports size style - function IsoRectangleShape() - { - mxActor.call(this); - }; - mxUtils.extend(IsoRectangleShape, mxActor); - IsoRectangleShape.prototype.size = 20; - IsoRectangleShape.prototype.redrawPath = function(path, x, y, w, h) - { - var m = Math.min(w, h / tan30); - - path.translate((w - m) / 2, (h - m) / 2 + m / 4); - path.moveTo(0, 0.25 * m); - path.lineTo(0.5 * m, m * tan30Dx); - path.lineTo(m, 0.25 * m); - path.lineTo(0.5 * m, (0.5 - tan30Dx) * m); - path.lineTo(0, 0.25 * m); - path.close(); - path.end(); - }; - - mxCellRenderer.registerShape('isoRectangle', IsoRectangleShape); - - // Cube Shape, supports size style - function IsoCubeShape() - { - mxCylinder.call(this); - }; - mxUtils.extend(IsoCubeShape, mxCylinder); - IsoCubeShape.prototype.size = 20; - IsoCubeShape.prototype.redrawPath = function(path, x, y, w, h, isForeground) - { - var m = Math.min(w, h / (0.5 + tan30)); - - if (isForeground) - { - path.moveTo(0, 0.25 * m); - path.lineTo(0.5 * m, (0.5 - tan30Dx) * m); - path.lineTo(m, 0.25 * m); - path.moveTo(0.5 * m, (0.5 - tan30Dx) * m); - path.lineTo(0.5 * m, (1 - tan30Dx) * m); - path.end(); - } - else - { - path.translate((w - m) / 2, (h - m) / 2); - path.moveTo(0, 0.25 * m); - path.lineTo(0.5 * m, m * tan30Dx); - path.lineTo(m, 0.25 * m); - path.lineTo(m, 0.75 * m); - path.lineTo(0.5 * m, (1 - tan30Dx) * m); - path.lineTo(0, 0.75 * m); - path.close(); - path.end(); - } - }; - - mxCellRenderer.registerShape('isoCube', IsoCubeShape); - - // DataStore Shape, supports size style - function DataStoreShape() - { - mxCylinder.call(this); - }; - mxUtils.extend(DataStoreShape, mxCylinder); - - DataStoreShape.prototype.redrawPath = function(c, x, y, w, h, isForeground) - { - var dy = Math.min(h / 2, Math.round(h / 8) + this.strokewidth - 1); - - if ((isForeground && this.fill != null) || (!isForeground && this.fill == null)) - { - c.moveTo(0, dy); - c.curveTo(0, 2 * dy, w, 2 * dy, w, dy); - - // Needs separate shapes for correct hit-detection - if (!isForeground) - { - c.stroke(); - c.begin(); - } - - c.translate(0, dy / 2); - c.moveTo(0, dy); - c.curveTo(0, 2 * dy, w, 2 * dy, w, dy); - - // Needs separate shapes for correct hit-detection - if (!isForeground) - { - c.stroke(); - c.begin(); - } - - c.translate(0, dy / 2); - c.moveTo(0, dy); - c.curveTo(0, 2 * dy, w, 2 * dy, w, dy); - - // Needs separate shapes for correct hit-detection - if (!isForeground) - { - c.stroke(); - c.begin(); - } - - c.translate(0, -dy); - } - - if (!isForeground) - { - c.moveTo(0, dy); - c.curveTo(0, -dy / 3, w, -dy / 3, w, dy); - c.lineTo(w, h - dy); - c.curveTo(w, h + dy / 3, 0, h + dy / 3, 0, h - dy); - c.close(); - } - }; - DataStoreShape.prototype.getLabelMargins = function(rect) - { - return new mxRectangle(0, 2.5 * Math.min(rect.height / 2, Math.round(rect.height / 8) + - this.strokewidth - 1) * this.scale, 0, 0); - } - - mxCellRenderer.registerShape('datastore', DataStoreShape); - - // Note Shape, supports size style - function NoteShape() - { - mxCylinder.call(this); - }; - mxUtils.extend(NoteShape, mxCylinder); - NoteShape.prototype.size = 30; - NoteShape.prototype.redrawPath = function(path, x, y, w, h, isForeground) - { - var s = Math.max(0, Math.min(w, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'size', this.size))))); - - if (isForeground) - { - path.moveTo(w - s, 0); - path.lineTo(w - s, s); - path.lineTo(w, s); - path.end(); - } - else - { - path.moveTo(0, 0); - path.lineTo(w - s, 0); - path.lineTo(w, s); - path.lineTo(w, h); - path.lineTo(0, h); - path.lineTo(0, 0); - path.close(); - path.end(); - } - }; - - mxCellRenderer.registerShape('note', NoteShape); - - // Note Shape, supports size style - function SwitchShape() - { - mxActor.call(this); - }; - mxUtils.extend(SwitchShape, mxActor); - SwitchShape.prototype.redrawPath = function(c, x, y, w, h) - { - var curve = 0.5; - c.moveTo(0, 0); - c.quadTo(w / 2, h * curve, w, 0); - c.quadTo(w * (1 - curve), h / 2, w, h); - c.quadTo(w / 2, h * (1 - curve), 0, h); - c.quadTo(w * curve, h / 2, 0, 0); - c.end(); - }; - - mxCellRenderer.registerShape('switch', SwitchShape); - - // Folder Shape, supports tabWidth, tabHeight styles - function FolderShape() - { - mxCylinder.call(this); - }; - mxUtils.extend(FolderShape, mxCylinder); - FolderShape.prototype.tabWidth = 60; - FolderShape.prototype.tabHeight = 20; - FolderShape.prototype.tabPosition = 'right'; - FolderShape.prototype.redrawPath = function(path, x, y, w, h, isForeground) - { - var dx = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'tabWidth', this.tabWidth)))); - var dy = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'tabHeight', this.tabHeight)))); - var tp = mxUtils.getValue(this.style, 'tabPosition', this.tabPosition); - - if (isForeground) - { - if (tp == 'left') - { - path.moveTo(0, dy); - path.lineTo(dx, dy); - } - // Right is default - else - { - path.moveTo(w - dx, dy); - path.lineTo(w, dy); - } - - path.end(); - } - else - { - if (tp == 'left') - { - path.moveTo(0, 0); - path.lineTo(dx, 0); - path.lineTo(dx, dy); - path.lineTo(w, dy); - } - // Right is default - else - { - path.moveTo(0, dy); - path.lineTo(w - dx, dy); - path.lineTo(w - dx, 0); - path.lineTo(w, 0); - } - - path.lineTo(w, h); - path.lineTo(0, h); - path.lineTo(0, dy); - path.close(); - path.end(); - } - }; - - mxCellRenderer.registerShape('folder', FolderShape); - - // Card shape - function CardShape() - { - mxActor.call(this); - }; - mxUtils.extend(CardShape, mxActor); - CardShape.prototype.size = 30; - CardShape.prototype.isRoundable = function() - { - return true; - }; - CardShape.prototype.redrawPath = function(c, x, y, w, h) - { - var s = Math.max(0, Math.min(w, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'size', this.size))))); - var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; - this.addPoints(c, [new mxPoint(s, 0), new mxPoint(w, 0), new mxPoint(w, h), new mxPoint(0, h), new mxPoint(0, s)], - this.isRounded, arcSize, true); - c.end(); - }; - - mxCellRenderer.registerShape('card', CardShape); - - // Tape shape - function TapeShape() - { - mxActor.call(this); - }; - mxUtils.extend(TapeShape, mxActor); - TapeShape.prototype.size = 0.4; - TapeShape.prototype.redrawPath = function(c, x, y, w, h) - { - var dy = h * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); - var fy = 1.4; - - c.moveTo(0, dy / 2); - c.quadTo(w / 4, dy * fy, w / 2, dy / 2); - c.quadTo(w * 3 / 4, dy * (1 - fy), w, dy / 2); - c.lineTo(w, h - dy / 2); - c.quadTo(w * 3 / 4, h - dy * fy, w / 2, h - dy / 2); - c.quadTo(w / 4, h - dy * (1 - fy), 0, h - dy / 2); - c.lineTo(0, dy / 2); - c.close(); - c.end(); - }; - - TapeShape.prototype.getLabelBounds = function(rect) - { - if (mxUtils.getValue(this.style, 'boundedLbl', false)) - { - var size = mxUtils.getValue(this.style, 'size', this.size); - var w = rect.width; - var h = rect.height; - - if (this.direction == null || - this.direction == mxConstants.DIRECTION_EAST || - this.direction == mxConstants.DIRECTION_WEST) - { - var dy = h * size; - - return new mxRectangle(rect.x, rect.y + dy, w, h - 2 * dy); - } - else - { - var dx = w * size; - - return new mxRectangle(rect.x + dx, rect.y, w - 2 * dx, h); - } - } - - return rect; - }; - - mxCellRenderer.registerShape('tape', TapeShape); - - // Document shape - function DocumentShape() - { - mxActor.call(this); - }; - mxUtils.extend(DocumentShape, mxActor); - DocumentShape.prototype.size = 0.3; - DocumentShape.prototype.getLabelMargins = function(rect) - { - if (mxUtils.getValue(this.style, 'boundedLbl', false)) - { - return new mxRectangle(0, 0, 0, parseFloat(mxUtils.getValue( - this.style, 'size', this.size)) * rect.height); - } - - return null; - }; - DocumentShape.prototype.redrawPath = function(c, x, y, w, h) - { - var dy = h * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); - var fy = 1.4; - - c.moveTo(0, 0); - c.lineTo(w, 0); - c.lineTo(w, h - dy / 2); - c.quadTo(w * 3 / 4, h - dy * fy, w / 2, h - dy / 2); - c.quadTo(w / 4, h - dy * (1 - fy), 0, h - dy / 2); - c.lineTo(0, dy / 2); - c.close(); - c.end(); - }; - - mxCellRenderer.registerShape('document', DocumentShape); - - var cylinderGetCylinderSize = mxCylinder.prototype.getCylinderSize; - - mxCylinder.prototype.getCylinderSize = function(x, y, w, h) - { - var size = mxUtils.getValue(this.style, 'size'); - - if (size != null) - { - return h * Math.max(0, Math.min(1, size)); - } - else - { - return cylinderGetCylinderSize.apply(this, arguments); - } - }; - - mxCylinder.prototype.getLabelMargins = function(rect) - { - if (mxUtils.getValue(this.style, 'boundedLbl', false)) - { - var size = mxUtils.getValue(this.style, 'size', 0.15) * 2; - - return new mxRectangle(0, Math.min(this.maxHeight * this.scale, rect.height * size), 0, 0); - } - - return null; - }; - - // Parallelogram shape - function ParallelogramShape() - { - mxActor.call(this); - }; - mxUtils.extend(ParallelogramShape, mxActor); - ParallelogramShape.prototype.size = 0.2; - ParallelogramShape.prototype.isRoundable = function() - { - return true; - }; - ParallelogramShape.prototype.redrawPath = function(c, x, y, w, h) - { - var dx = w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); - var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; - this.addPoints(c, [new mxPoint(0, h), new mxPoint(dx, 0), new mxPoint(w, 0), new mxPoint(w - dx, h)], - this.isRounded, arcSize, true); - c.end(); - }; - - mxCellRenderer.registerShape('parallelogram', ParallelogramShape); - - // Trapezoid shape - function TrapezoidShape() - { - mxActor.call(this); - }; - mxUtils.extend(TrapezoidShape, mxActor); - TrapezoidShape.prototype.size = 0.2; - TrapezoidShape.prototype.isRoundable = function() - { - return true; - }; - TrapezoidShape.prototype.redrawPath = function(c, x, y, w, h) - { - var dx = w * Math.max(0, Math.min(0.5, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); - var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; - this.addPoints(c, [new mxPoint(0, h), new mxPoint(dx, 0), new mxPoint(w - dx, 0), new mxPoint(w, h)], - this.isRounded, arcSize, true); - }; - - mxCellRenderer.registerShape('trapezoid', TrapezoidShape); - - // Curly Bracket shape - function CurlyBracketShape() - { - mxActor.call(this); - }; - mxUtils.extend(CurlyBracketShape, mxActor); - CurlyBracketShape.prototype.size = 0.5; - CurlyBracketShape.prototype.redrawPath = function(c, x, y, w, h) - { - c.setFillColor(null); - var s = w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); - var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; - this.addPoints(c, [new mxPoint(w, 0), new mxPoint(s, 0), new mxPoint(s, h / 2), - new mxPoint(0, h / 2), new mxPoint(s, h / 2), new mxPoint(s, h), - new mxPoint(w, h)], this.isRounded, arcSize, false); - c.end(); - }; - - mxCellRenderer.registerShape('curlyBracket', CurlyBracketShape); - - // Parallel marker shape - function ParallelMarkerShape() - { - mxActor.call(this); - }; - mxUtils.extend(ParallelMarkerShape, mxActor); - ParallelMarkerShape.prototype.redrawPath = function(c, x, y, w, h) - { - c.setStrokeWidth(1); - c.setFillColor(this.stroke); - var w2 = w / 5; - c.rect(0, 0, w2, h); - c.fillAndStroke(); - c.rect(2 * w2, 0, w2, h); - c.fillAndStroke(); - c.rect(4 * w2, 0, w2, h); - c.fillAndStroke(); - }; - - mxCellRenderer.registerShape('parallelMarker', ParallelMarkerShape); - - /** - * Adds handJiggle style (jiggle=n sets jiggle) - */ - function HandJiggle(canvas, defaultVariation) - { - this.canvas = canvas; - - // Avoids "spikes" in the output - this.canvas.setLineJoin('round'); - this.canvas.setLineCap('round'); - - this.defaultVariation = defaultVariation; - - this.originalLineTo = this.canvas.lineTo; - this.canvas.lineTo = mxUtils.bind(this, HandJiggle.prototype.lineTo); - - this.originalMoveTo = this.canvas.moveTo; - this.canvas.moveTo = mxUtils.bind(this, HandJiggle.prototype.moveTo); - - this.originalClose = this.canvas.close; - this.canvas.close = mxUtils.bind(this, HandJiggle.prototype.close); - - this.originalQuadTo = this.canvas.quadTo; - this.canvas.quadTo = mxUtils.bind(this, HandJiggle.prototype.quadTo); - - this.originalCurveTo = this.canvas.curveTo; - this.canvas.curveTo = mxUtils.bind(this, HandJiggle.prototype.curveTo); - - this.originalArcTo = this.canvas.arcTo; - this.canvas.arcTo = mxUtils.bind(this, HandJiggle.prototype.arcTo); - }; - - HandJiggle.prototype.moveTo = function(endX, endY) - { - this.originalMoveTo.apply(this.canvas, arguments); - this.lastX = endX; - this.lastY = endY; - this.firstX = endX; - this.firstY = endY; - }; - - HandJiggle.prototype.close = function() - { - if (this.firstX != null && this.firstY != null) - { - this.lineTo(this.firstX, this.firstY); - this.originalClose.apply(this.canvas, arguments); - } - - this.originalClose.apply(this.canvas, arguments); - }; - - HandJiggle.prototype.quadTo = function(x1, y1, x2, y2) - { - this.originalQuadTo.apply(this.canvas, arguments); - this.lastX = x2; - this.lastY = y2; - }; - - HandJiggle.prototype.curveTo = function(x1, y1, x2, y2, x3, y3) - { - this.originalCurveTo.apply(this.canvas, arguments); - this.lastX = x3; - this.lastY = y3; - }; - - HandJiggle.prototype.arcTo = function(rx, ry, angle, largeArcFlag, sweepFlag, x, y) - { - this.originalArcTo.apply(this.canvas, arguments); - this.lastX = x; - this.lastY = y; - }; - - HandJiggle.prototype.lineTo = function(endX, endY) - { - // LATER: Check why this.canvas.lastX cannot be used - if (this.lastX != null && this.lastY != null) - { - var dx = Math.abs(endX - this.lastX); - var dy = Math.abs(endY - this.lastY); - var dist = Math.sqrt(dx * dx + dy * dy); - - if (dist < 2) - { - this.originalLineTo.apply(this.canvas, arguments); - this.lastX = endX; - this.lastY = endY; - - return; - } - - var segs = Math.round(dist / 10); - var variation = this.defaultVariation; - - if (segs < 5) - { - segs = 5; - variation /= 3; - } - - function sign(x) - { - return typeof x === 'number' ? x ? x < 0 ? -1 : 1 : x === x ? 0 : NaN : NaN; - } - - var stepX = sign(endX - this.lastX) * dx / segs; - var stepY = sign(endY - this.lastY) * dy / segs; - - var fx = dx / dist; - var fy = dy / dist; - - for (var s = 0; s < segs; s++) - { - var x = stepX * s + this.lastX; - var y = stepY * s + this.lastY; - - var offset = (Math.random() - 0.5) * variation; - this.originalLineTo.call(this.canvas, x - offset * fy, y - offset * fx); - } - - this.originalLineTo.call(this.canvas, endX, endY); - this.lastX = endX; - this.lastY = endY; - } - else - { - this.originalLineTo.apply(this.canvas, arguments); - this.lastX = endX; - this.lastY = endY; - } - }; - - HandJiggle.prototype.destroy = function() - { - this.canvas.lineTo = this.originalLineTo; - this.canvas.moveTo = this.originalMoveTo; - this.canvas.close = this.originalClose; - this.canvas.quadTo = this.originalQuadTo; - this.canvas.curveTo = this.originalCurveTo; - this.canvas.arcTo = this.originalArcTo; - }; - - // Installs hand jiggle in all shapes - var mxShapePaint0 = mxShape.prototype.paint; - mxShape.prototype.defaultJiggle = 1.5; - mxShape.prototype.paint = function(c) - { - // NOTE: getValue does not return a boolean value so !('0') would return true here and below - if (this.style != null && mxUtils.getValue(this.style, 'comic', '0') != '0' && c.handHiggle == null) - { - c.handJiggle = new HandJiggle(c, mxUtils.getValue(this.style, 'jiggle', this.defaultJiggle)); - } - - mxShapePaint0.apply(this, arguments); - - if (c.handJiggle != null) - { - c.handJiggle.destroy(); - delete c.handJiggle; - } - }; - - // Sets default jiggle for diamond - mxRhombus.prototype.defaultJiggle = 2; - - /** - * Overrides to avoid call to rect - */ - var mxRectangleShapeIsHtmlAllowed0 = mxRectangleShape.prototype.isHtmlAllowed; - mxRectangleShape.prototype.isHtmlAllowed = function() - { - return (this.style == null || mxUtils.getValue(this.style, 'comic', '0') == '0') && - mxRectangleShapeIsHtmlAllowed0.apply(this, arguments); - }; - - var mxRectangleShapePaintBackground0 = mxRectangleShape.prototype.paintBackground; - mxRectangleShape.prototype.paintBackground = function(c, x, y, w, h) - { - if (c.handJiggle == null) - { - mxRectangleShapePaintBackground0.apply(this, arguments); - } - else - { - var events = true; - - if (this.style != null) - { - events = mxUtils.getValue(this.style, mxConstants.STYLE_POINTER_EVENTS, '1') == '1'; - } - - if (events || (this.fill != null && this.fill != mxConstants.NONE) || - (this.stroke != null && this.stroke != mxConstants.NONE)) - { - if (!events && (this.fill == null || this.fill == mxConstants.NONE)) - { - c.pointerEvents = false; - } - - c.begin(); - - if (this.isRounded) - { - var r = 0; - - if (mxUtils.getValue(this.style, mxConstants.STYLE_ABSOLUTE_ARCSIZE, 0) == '1') - { - r = Math.min(w / 2, Math.min(h / 2, mxUtils.getValue(this.style, - mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2)); - } - else - { - var f = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, - mxConstants.RECTANGLE_ROUNDING_FACTOR * 100) / 100; - r = Math.min(w * f, h * f); - } - - c.moveTo(x + r, y); - c.lineTo(x + w - r, y); - c.quadTo(x + w, y, x + w, y + r); - c.lineTo(x + w, y + h - r); - c.quadTo(x + w, y + h, x + w - r, y + h); - c.lineTo(x + r, y + h); - c.quadTo(x, y + h, x, y + h - r); - c.lineTo(x, y + r); - c.quadTo(x, y, x + r, y); - } - else - { - - c.moveTo(x, y); - c.lineTo(x + w, y); - c.lineTo(x + w, y + h); - c.lineTo(x, y + h); - c.lineTo(x, y); - } - - // LATER: Check if close is needed here - c.close(); - c.end(); - - c.fillAndStroke(); - } - } - }; - - /** - * Disables glass effect with hand jiggle. - */ - var mxRectangleShapePaintForeground0 = mxRectangleShape.prototype.paintForeground; - mxRectangleShape.prototype.paintForeground = function(c, x, y, w, h) - { - if (c.handJiggle == null) - { - mxRectangleShapePaintForeground0.apply(this, arguments); - } - }; - - // End of hand jiggle integration - - // Process Shape - function ProcessShape() - { - mxRectangleShape.call(this); - }; - mxUtils.extend(ProcessShape, mxRectangleShape); - ProcessShape.prototype.size = 0.1; - ProcessShape.prototype.isHtmlAllowed = function() - { - return false; - }; - ProcessShape.prototype.getLabelBounds = function(rect) - { - if (mxUtils.getValue(this.state.style, mxConstants.STYLE_HORIZONTAL, true) == - (this.direction == null || - this.direction == mxConstants.DIRECTION_EAST || - this.direction == mxConstants.DIRECTION_WEST)) - { - var w = rect.width; - var h = rect.height; - var r = new mxRectangle(rect.x, rect.y, w, h); - - var inset = w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); - - if (this.isRounded) - { - var f = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, - mxConstants.RECTANGLE_ROUNDING_FACTOR * 100) / 100; - inset = Math.max(inset, Math.min(w * f, h * f)); - } - - r.x += Math.round(inset); - r.width -= Math.round(2 * inset); - - return r; - } - - return rect; - }; - ProcessShape.prototype.paintForeground = function(c, x, y, w, h) - { - var inset = w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); - - if (this.isRounded) - { - var f = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, - mxConstants.RECTANGLE_ROUNDING_FACTOR * 100) / 100; - inset = Math.max(inset, Math.min(w * f, h * f)); - } - - // Crisp rendering of inner lines - inset = Math.round(inset); - - c.begin(); - c.moveTo(x + inset, y); - c.lineTo(x + inset, y + h); - c.moveTo(x + w - inset, y); - c.lineTo(x + w - inset, y + h); - c.end(); - c.stroke(); - mxRectangleShape.prototype.paintForeground.apply(this, arguments); - }; - - mxCellRenderer.registerShape('process', ProcessShape); - - // Transparent Shape - function TransparentShape() - { - mxRectangleShape.call(this); - }; - mxUtils.extend(TransparentShape, mxRectangleShape); - TransparentShape.prototype.paintBackground = function(c, x, y, w, h) - { - c.setFillColor(mxConstants.NONE); - c.rect(x, y, w, h); - c.fill(); - }; - TransparentShape.prototype.paintForeground = function(c, x, y, w, h) { }; - - mxCellRenderer.registerShape('transparent', TransparentShape); - - // Callout shape - function CalloutShape() - { - mxActor.call(this); - }; - mxUtils.extend(CalloutShape, mxHexagon); - CalloutShape.prototype.size = 30; - CalloutShape.prototype.position = 0.5; - CalloutShape.prototype.position2 = 0.5; - CalloutShape.prototype.base = 20; - CalloutShape.prototype.getLabelMargins = function() - { - return new mxRectangle(0, 0, 0, parseFloat(mxUtils.getValue( - this.style, 'size', this.size)) * this.scale); - }; - CalloutShape.prototype.isRoundable = function() - { - return true; - }; - CalloutShape.prototype.redrawPath = function(c, x, y, w, h) - { - var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; - var s = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); - var dx = w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'position', this.position)))); - var dx2 = w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'position2', this.position2)))); - var base = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'base', this.base)))); - - this.addPoints(c, [new mxPoint(0, 0), new mxPoint(w, 0), new mxPoint(w, h - s), - new mxPoint(Math.min(w, dx + base), h - s), new mxPoint(dx2, h), - new mxPoint(Math.max(0, dx), h - s), new mxPoint(0, h - s)], - this.isRounded, arcSize, true, [4]); - }; - - mxCellRenderer.registerShape('callout', CalloutShape); - - // Step shape - function StepShape() - { - mxActor.call(this); - }; - mxUtils.extend(StepShape, mxActor); - StepShape.prototype.size = 0.2; - StepShape.prototype.fixedSize = 20; - StepShape.prototype.isRoundable = function() - { - return true; - }; - StepShape.prototype.redrawPath = function(c, x, y, w, h) - { - var fixed = mxUtils.getValue(this.style, 'fixedSize', '0') != '0'; - var s = (fixed) ? Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'size', this.fixedSize)))) : - w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); - var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; - this.addPoints(c, [new mxPoint(0, 0), new mxPoint(w - s, 0), new mxPoint(w, h / 2), new mxPoint(w - s, h), - new mxPoint(0, h), new mxPoint(s, h / 2)], this.isRounded, arcSize, true); - c.end(); - }; - - mxCellRenderer.registerShape('step', StepShape); - - // Hexagon shape - function HexagonShape() - { - mxActor.call(this); - }; - mxUtils.extend(HexagonShape, mxHexagon); - HexagonShape.prototype.size = 0.25; - HexagonShape.prototype.isRoundable = function() - { - return true; - }; - HexagonShape.prototype.redrawPath = function(c, x, y, w, h) - { - var s = w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); - var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; - this.addPoints(c, [new mxPoint(s, 0), new mxPoint(w - s, 0), new mxPoint(w, 0.5 * h), new mxPoint(w - s, h), - new mxPoint(s, h), new mxPoint(0, 0.5 * h)], this.isRounded, arcSize, true); - }; - - mxCellRenderer.registerShape('hexagon', HexagonShape); - - // Plus Shape - function PlusShape() - { - mxRectangleShape.call(this); - }; - mxUtils.extend(PlusShape, mxRectangleShape); - PlusShape.prototype.isHtmlAllowed = function() - { - return false; - }; - PlusShape.prototype.paintForeground = function(c, x, y, w, h) - { - var border = Math.min(w / 5, h / 5) + 1; - - c.begin(); - c.moveTo(x + w / 2, y + border); - c.lineTo(x + w / 2, y + h - border); - c.moveTo(x + border, y + h / 2); - c.lineTo(x + w - border, y + h / 2); - c.end(); - c.stroke(); - mxRectangleShape.prototype.paintForeground.apply(this, arguments); - }; - - mxCellRenderer.registerShape('plus', PlusShape); - - // Overrides painting of rhombus shape to allow for double style - var mxRhombusPaintVertexShape = mxRhombus.prototype.paintVertexShape; - mxRhombus.prototype.getLabelBounds = function(rect) - { - if (this.style['double'] == 1) - { - var margin = (Math.max(2, this.strokewidth + 1) * 2 + parseFloat( - this.style[mxConstants.STYLE_MARGIN] || 0)) * this.scale; - - return new mxRectangle(rect.x + margin, rect.y + margin, - rect.width - 2 * margin, rect.height - 2 * margin); - } - - return rect; - }; - mxRhombus.prototype.paintVertexShape = function(c, x, y, w, h) - { - mxRhombusPaintVertexShape.apply(this, arguments); - - if (!this.outline && this.style['double'] == 1) - { - var margin = Math.max(2, this.strokewidth + 1) * 2 + - parseFloat(this.style[mxConstants.STYLE_MARGIN] || 0); - x += margin; - y += margin; - w -= 2 * margin; - h -= 2 * margin; - - if (w > 0 && h > 0) - { - c.setShadow(false); - - // Workaround for closure compiler bug where the lines with x and y above - // are removed if arguments is used as second argument in call below. - mxRhombusPaintVertexShape.apply(this, [c, x, y, w, h]); - } - } - }; - - // CompositeShape - function ExtendedShape() - { - mxRectangleShape.call(this); - }; - mxUtils.extend(ExtendedShape, mxRectangleShape); - ExtendedShape.prototype.isHtmlAllowed = function() - { - return false; - }; - ExtendedShape.prototype.getLabelBounds = function(rect) - { - if (this.style['double'] == 1) - { - var margin = (Math.max(2, this.strokewidth + 1) + parseFloat( - this.style[mxConstants.STYLE_MARGIN] || 0)) * this.scale; - - return new mxRectangle(rect.x + margin, rect.y + margin, - rect.width - 2 * margin, rect.height - 2 * margin); - } - - return rect; - }; - - ExtendedShape.prototype.paintForeground = function(c, x, y, w, h) - { - if (this.style != null) - { - if (!this.outline && this.style['double'] == 1) - { - var margin = Math.max(2, this.strokewidth + 1) + parseFloat(this.style[mxConstants.STYLE_MARGIN] || 0); - x += margin; - y += margin; - w -= 2 * margin; - h -= 2 * margin; - - if (w > 0 && h > 0) - { - mxRectangleShape.prototype.paintBackground.apply(this, arguments); - } - } - - c.setDashed(false); - - // Draws the symbols defined in the style. The symbols are - // numbered from 1...n. Possible postfixes are align, - // verticalAlign, spacing, arcSpacing, width, height - var counter = 0; - var shape = null; - - do - { - shape = mxCellRenderer.defaultShapes[this.style['symbol' + counter]]; - - if (shape != null) - { - var align = this.style['symbol' + counter + 'Align']; - var valign = this.style['symbol' + counter + 'VerticalAlign']; - var width = this.style['symbol' + counter + 'Width']; - var height = this.style['symbol' + counter + 'Height']; - var spacing = this.style['symbol' + counter + 'Spacing'] || 0; - var vspacing = this.style['symbol' + counter + 'VSpacing'] || spacing; - var arcspacing = this.style['symbol' + counter + 'ArcSpacing']; - - if (arcspacing != null) - { - var arcSize = this.getArcSize(w + this.strokewidth, h + this.strokewidth) * arcspacing; - spacing += arcSize; - vspacing += arcSize; - } - - var x2 = x; - var y2 = y; - - if (align == mxConstants.ALIGN_CENTER) - { - x2 += (w - width) / 2; - } - else if (align == mxConstants.ALIGN_RIGHT) - { - x2 += w - width - spacing; - } - else - { - x2 += spacing; - } - - if (valign == mxConstants.ALIGN_MIDDLE) - { - y2 += (h - height) / 2; - } - else if (valign == mxConstants.ALIGN_BOTTOM) - { - y2 += h - height - vspacing; - } - else - { - y2 += vspacing; - } - - c.save(); - - // Small hack to pass style along into subshape - var tmp = new shape(); - // TODO: Clone style and override settings (eg. strokewidth) - tmp.style = this.style; - shape.prototype.paintVertexShape.call(tmp, c, x2, y2, width, height); - c.restore(); - } - - counter++; - } - while (shape != null); - } - - // Paints glass effect - mxRectangleShape.prototype.paintForeground.apply(this, arguments); - }; - - mxCellRenderer.registerShape('ext', ExtendedShape); - - // Tape Shape, supports size style - function MessageShape() - { - mxCylinder.call(this); - }; - mxUtils.extend(MessageShape, mxCylinder); - MessageShape.prototype.redrawPath = function(path, x, y, w, h, isForeground) - { - if (isForeground) - { - path.moveTo(0, 0); - path.lineTo(w / 2, h / 2); - path.lineTo(w, 0); - path.end(); - } - else - { - path.moveTo(0, 0); - path.lineTo(w, 0); - path.lineTo(w, h); - path.lineTo(0, h); - path.close(); - } - }; - - mxCellRenderer.registerShape('message', MessageShape); - - // UML Actor Shape - function UmlActorShape() - { - mxShape.call(this); - }; - mxUtils.extend(UmlActorShape, mxShape); - UmlActorShape.prototype.paintBackground = function(c, x, y, w, h) - { - c.translate(x, y); - - // Head - c.ellipse(w / 4, 0, w / 2, h / 4); - c.fillAndStroke(); - - c.begin(); - c.moveTo(w / 2, h / 4); - c.lineTo(w / 2, 2 * h / 3); - - // Arms - c.moveTo(w / 2, h / 3); - c.lineTo(0, h / 3); - c.moveTo(w / 2, h / 3); - c.lineTo(w, h / 3); - - // Legs - c.moveTo(w / 2, 2 * h / 3); - c.lineTo(0, h); - c.moveTo(w / 2, 2 * h / 3); - c.lineTo(w, h); - c.end(); - - c.stroke(); - }; - - // Replaces existing actor shape - mxCellRenderer.registerShape('umlActor', UmlActorShape); - - // UML Boundary Shape - function UmlBoundaryShape() - { - mxShape.call(this); - }; - mxUtils.extend(UmlBoundaryShape, mxShape); - UmlBoundaryShape.prototype.getLabelMargins = function(rect) - { - return new mxRectangle(rect.width / 6, 0, 0, 0); - }; - UmlBoundaryShape.prototype.paintBackground = function(c, x, y, w, h) - { - c.translate(x, y); - - // Base line - c.begin(); - c.moveTo(0, h / 4); - c.lineTo(0, h * 3 / 4); - c.end(); - c.stroke(); - - // Horizontal line - c.begin(); - c.moveTo(0, h / 2); - c.lineTo(w / 6, h / 2); - c.end(); - c.stroke(); - - // Circle - c.ellipse(w / 6, 0, w * 5 / 6, h); - c.fillAndStroke(); - }; - - // Replaces existing actor shape - mxCellRenderer.registerShape('umlBoundary', UmlBoundaryShape); - - // UML Entity Shape - function UmlEntityShape() - { - mxEllipse.call(this); - }; - mxUtils.extend(UmlEntityShape, mxEllipse); - UmlEntityShape.prototype.paintVertexShape = function(c, x, y, w, h) - { - mxEllipse.prototype.paintVertexShape.apply(this, arguments); - - c.begin(); - c.moveTo(x + w / 8, y + h); - c.lineTo(x + w * 7 / 8, y + h); - c.end(); - c.stroke(); - }; - - mxCellRenderer.registerShape('umlEntity', UmlEntityShape); - - // UML Destroy Shape - function UmlDestroyShape() - { - mxShape.call(this); - }; - mxUtils.extend(UmlDestroyShape, mxShape); - UmlDestroyShape.prototype.paintVertexShape = function(c, x, y, w, h) - { - c.translate(x, y); - - c.begin(); - c.moveTo(w, 0); - c.lineTo(0, h); - c.moveTo(0, 0); - c.lineTo(w, h); - c.end(); - c.stroke(); - }; - - mxCellRenderer.registerShape('umlDestroy', UmlDestroyShape); - - // UML Control Shape - function UmlControlShape() - { - mxShape.call(this); - }; - mxUtils.extend(UmlControlShape, mxShape); - UmlControlShape.prototype.getLabelBounds = function(rect) - { - return new mxRectangle(rect.x, rect.y + rect.height / 8, rect.width, rect.height * 7 / 8); - }; - UmlControlShape.prototype.paintBackground = function(c, x, y, w, h) - { - c.translate(x, y); - - // Upper line - c.begin(); - c.moveTo(w * 3 / 8, h / 8 * 1.1); - c.lineTo(w * 5 / 8, 0); - c.end(); - c.stroke(); - - // Circle - c.ellipse(0, h / 8, w, h * 7 / 8); - c.fillAndStroke(); - }; - UmlControlShape.prototype.paintForeground = function(c, x, y, w, h) - { - // Lower line - c.begin(); - c.moveTo(w * 3 / 8, h / 8 * 1.1); - c.lineTo(w * 5 / 8, h / 4); - c.end(); - c.stroke(); - }; - - // Replaces existing actor shape - mxCellRenderer.registerShape('umlControl', UmlControlShape); - - // UML Lifeline Shape - function UmlLifeline() - { - mxRectangleShape.call(this); - }; - mxUtils.extend(UmlLifeline, mxRectangleShape); - UmlLifeline.prototype.size = 40; - UmlLifeline.prototype.isHtmlAllowed = function() - { - return false; - }; - UmlLifeline.prototype.getLabelBounds = function(rect) - { - var size = Math.max(0, Math.min(rect.height, parseFloat( - mxUtils.getValue(this.style, 'size', this.size)) * this.scale)); - - return new mxRectangle(rect.x, rect.y, rect.width, size); - }; - UmlLifeline.prototype.paintBackground = function(c, x, y, w, h) - { - var size = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); - var participant = mxUtils.getValue(this.style, 'participant'); - - if (participant == null || this.state == null) - { - mxRectangleShape.prototype.paintBackground.call(this, c, x, y, w, size); - } - else - { - var ctor = this.state.view.graph.cellRenderer.getShape(participant); - - if (ctor != null && ctor != UmlLifeline) - { - var shape = new ctor(); - shape.apply(this.state); - c.save(); - shape.paintVertexShape(c, x, y, w, size); - c.restore(); - } - } - - if (size < h) - { - c.setDashed(true); - c.begin(); - c.moveTo(x + w / 2, y + size); - c.lineTo(x + w / 2, y + h); - c.end(); - c.stroke(); - } - }; - UmlLifeline.prototype.paintForeground = function(c, x, y, w, h) - { - var size = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); - mxRectangleShape.prototype.paintForeground.call(this, c, x, y, w, Math.min(h, size)); - }; - - mxCellRenderer.registerShape('umlLifeline', UmlLifeline); - - // UML Frame Shape - function UmlFrame() - { - mxShape.call(this); - }; - mxUtils.extend(UmlFrame, mxShape); - UmlFrame.prototype.width = 60; - UmlFrame.prototype.height = 30; - UmlFrame.prototype.corner = 10; - UmlFrame.prototype.getLabelMargins = function(rect) - { - return new mxRectangle(0, 0, - rect.width - (parseFloat(mxUtils.getValue(this.style, 'width', this.width) * this.scale)), - rect.height - (parseFloat(mxUtils.getValue(this.style, 'height', this.height) * this.scale))); - }; - UmlFrame.prototype.paintBackground = function(c, x, y, w, h) - { - var co = this.corner; - var w0 = Math.min(w, Math.max(co, parseFloat(mxUtils.getValue(this.style, 'width', this.width)))); - var h0 = Math.min(h, Math.max(co * 1.5, parseFloat(mxUtils.getValue(this.style, 'height', this.height)))); - var bg = mxUtils.getValue(this.style, mxConstants.STYLE_SWIMLANE_FILLCOLOR, mxConstants.NONE); - - if (bg != mxConstants.NONE) - { - c.setFillColor(bg); - c.rect(x, y, w, h); - c.fill(); - } - - if (this.fill != null && this.fill != mxConstants.NONE && this.gradient && this.gradient != mxConstants.NONE) - { - var b = this.getGradientBounds(c, x, y, w, h); - c.setGradient(this.fill, this.gradient, x, y, w, h, this.gradientDirection); - } - else - { - c.setFillColor(this.fill); - } - - c.begin(); - c.moveTo(x, y); - c.lineTo(x + w0, y); - c.lineTo(x + w0, y + Math.max(0, h0 - co * 1.5)); - c.lineTo(x + Math.max(0, w0 - co), y + h0); - c.lineTo(x, y + h0); - c.close(); - c.fillAndStroke(); - - c.begin(); - c.moveTo(x + w0, y); - c.lineTo(x + w, y); - c.lineTo(x + w, y + h); - c.lineTo(x, y + h); - c.lineTo(x, y + h0); - c.stroke(); - }; - - mxCellRenderer.registerShape('umlFrame', UmlFrame); - - mxPerimeter.LifelinePerimeter = function (bounds, vertex, next, orthogonal) - { - var size = UmlLifeline.prototype.size; - - if (vertex != null) - { - size = mxUtils.getValue(vertex.style, 'size', size) * vertex.view.scale; - } - - var sw = (parseFloat(vertex.style[mxConstants.STYLE_STROKEWIDTH] || 1) * vertex.view.scale / 2) - 1; - - if (next.x < bounds.getCenterX()) - { - sw += 1; - sw *= -1; - } - - return new mxPoint(bounds.getCenterX() + sw, Math.min(bounds.y + bounds.height, - Math.max(bounds.y + size, next.y))); - }; - - mxStyleRegistry.putValue('lifelinePerimeter', mxPerimeter.LifelinePerimeter); - - mxPerimeter.OrthogonalPerimeter = function (bounds, vertex, next, orthogonal) - { - orthogonal = true; - - return mxPerimeter.RectanglePerimeter.apply(this, arguments); - }; - - mxStyleRegistry.putValue('orthogonalPerimeter', mxPerimeter.OrthogonalPerimeter); - - mxPerimeter.BackbonePerimeter = function (bounds, vertex, next, orthogonal) - { - var sw = (parseFloat(vertex.style[mxConstants.STYLE_STROKEWIDTH] || 1) * vertex.view.scale / 2) - 1; - - if (vertex.style['backboneSize'] != null) - { - sw += (parseFloat(vertex.style['backboneSize']) * vertex.view.scale / 2) - 1; - } - - if (vertex.style[mxConstants.STYLE_DIRECTION] == 'south' || - vertex.style[mxConstants.STYLE_DIRECTION] == 'north') - { - if (next.x < bounds.getCenterX()) - { - sw += 1; - sw *= -1; - } - - return new mxPoint(bounds.getCenterX() + sw, Math.min(bounds.y + bounds.height, - Math.max(bounds.y, next.y))); - } - else - { - if (next.y < bounds.getCenterY()) - { - sw += 1; - sw *= -1; - } - - return new mxPoint(Math.min(bounds.x + bounds.width, Math.max(bounds.x, next.x)), - bounds.getCenterY() + sw); - } - }; - - mxStyleRegistry.putValue('backbonePerimeter', mxPerimeter.BackbonePerimeter); - - // Callout Perimeter - mxPerimeter.CalloutPerimeter = function (bounds, vertex, next, orthogonal) - { - return mxPerimeter.RectanglePerimeter(mxUtils.getDirectedBounds(bounds, new mxRectangle(0, 0, 0, - Math.max(0, Math.min(bounds.height, parseFloat(mxUtils.getValue(vertex.style, 'size', - CalloutShape.prototype.size)) * vertex.view.scale))), - vertex.style), vertex, next, orthogonal); - }; - - mxStyleRegistry.putValue('calloutPerimeter', mxPerimeter.CalloutPerimeter); - - // Parallelogram Perimeter - mxPerimeter.ParallelogramPerimeter = function (bounds, vertex, next, orthogonal) - { - var size = ParallelogramShape.prototype.size; - - if (vertex != null) - { - size = mxUtils.getValue(vertex.style, 'size', size); - } - - var x = bounds.x; - var y = bounds.y; - var w = bounds.width; - var h = bounds.height; - - var direction = (vertex != null) ? mxUtils.getValue( - vertex.style, mxConstants.STYLE_DIRECTION, - mxConstants.DIRECTION_EAST) : mxConstants.DIRECTION_EAST; - var vertical = direction == mxConstants.DIRECTION_NORTH || - direction == mxConstants.DIRECTION_SOUTH; - var points; - - if (vertical) - { - var dy = h * Math.max(0, Math.min(1, size)); - points = [new mxPoint(x, y), new mxPoint(x + w, y + dy), - new mxPoint(x + w, y + h), new mxPoint(x, y + h - dy), new mxPoint(x, y)]; - } - else - { - var dx = w * Math.max(0, Math.min(1, size)); - points = [new mxPoint(x + dx, y), new mxPoint(x + w, y), - new mxPoint(x + w - dx, y + h), new mxPoint(x, y + h), new mxPoint(x + dx, y)]; - } - - var cx = bounds.getCenterX(); - var cy = bounds.getCenterY(); - - var p1 = new mxPoint(cx, cy); - - if (orthogonal) - { - if (next.x < x || next.x > x + w) - { - p1.y = next.y; - } - else - { - p1.x = next.x; - } - } - - return mxUtils.getPerimeterPoint(points, p1, next); - }; - - mxStyleRegistry.putValue('parallelogramPerimeter', mxPerimeter.ParallelogramPerimeter); - - // Trapezoid Perimeter - mxPerimeter.TrapezoidPerimeter = function (bounds, vertex, next, orthogonal) - { - var size = TrapezoidShape.prototype.size; - - if (vertex != null) - { - size = mxUtils.getValue(vertex.style, 'size', size); - } - - var x = bounds.x; - var y = bounds.y; - var w = bounds.width; - var h = bounds.height; - - var direction = (vertex != null) ? mxUtils.getValue( - vertex.style, mxConstants.STYLE_DIRECTION, - mxConstants.DIRECTION_EAST) : mxConstants.DIRECTION_EAST; - var points; - - if (direction == mxConstants.DIRECTION_EAST) - { - var dx = w * Math.max(0, Math.min(1, size)); - points = [new mxPoint(x + dx, y), new mxPoint(x + w - dx, y), - new mxPoint(x + w, y + h), new mxPoint(x, y + h), new mxPoint(x + dx, y)]; - } - else if (direction == mxConstants.DIRECTION_WEST) - { - var dx = w * Math.max(0, Math.min(1, size)); - points = [new mxPoint(x, y), new mxPoint(x + w, y), - new mxPoint(x + w - dx, y + h), new mxPoint(x + dx, y + h), new mxPoint(x, y)]; - } - else if (direction == mxConstants.DIRECTION_NORTH) - { - var dy = h * Math.max(0, Math.min(1, size)); - points = [new mxPoint(x, y + dy), new mxPoint(x + w, y), - new mxPoint(x + w, y + h), new mxPoint(x, y + h - dy), new mxPoint(x, y + dy)]; - } - else - { - var dy = h * Math.max(0, Math.min(1, size)); - points = [new mxPoint(x, y), new mxPoint(x + w, y + dy), - new mxPoint(x + w, y + h - dy), new mxPoint(x, y + h), new mxPoint(x, y)]; - } - - var cx = bounds.getCenterX(); - var cy = bounds.getCenterY(); - - var p1 = new mxPoint(cx, cy); - - if (orthogonal) - { - if (next.x < x || next.x > x + w) - { - p1.y = next.y; - } - else - { - p1.x = next.x; - } - } - - return mxUtils.getPerimeterPoint(points, p1, next); - }; - - mxStyleRegistry.putValue('trapezoidPerimeter', mxPerimeter.TrapezoidPerimeter); - - // Step Perimeter - mxPerimeter.StepPerimeter = function (bounds, vertex, next, orthogonal) - { - var fixed = mxUtils.getValue(vertex.style, 'fixedSize', '0') != '0'; - var size = (fixed) ? StepShape.prototype.fixedSize : StepShape.prototype.size; - - if (vertex != null) - { - size = mxUtils.getValue(vertex.style, 'size', size); - } - - var x = bounds.x; - var y = bounds.y; - var w = bounds.width; - var h = bounds.height; - - var cx = bounds.getCenterX(); - var cy = bounds.getCenterY(); - - var direction = (vertex != null) ? mxUtils.getValue( - vertex.style, mxConstants.STYLE_DIRECTION, - mxConstants.DIRECTION_EAST) : mxConstants.DIRECTION_EAST; - var points; - - if (direction == mxConstants.DIRECTION_EAST) - { - var dx = (fixed) ? Math.max(0, Math.min(w, size)) : w * Math.max(0, Math.min(1, size)); - points = [new mxPoint(x, y), new mxPoint(x + w - dx, y), new mxPoint(x + w, cy), - new mxPoint(x + w - dx, y + h), new mxPoint(x, y + h), - new mxPoint(x + dx, cy), new mxPoint(x, y)]; - } - else if (direction == mxConstants.DIRECTION_WEST) - { - var dx = (fixed) ? Math.max(0, Math.min(w, size)) : w * Math.max(0, Math.min(1, size)); - points = [new mxPoint(x + dx, y), new mxPoint(x + w, y), new mxPoint(x + w - dx, cy), - new mxPoint(x + w, y + h), new mxPoint(x + dx, y + h), - new mxPoint(x, cy), new mxPoint(x + dx, y)]; - } - else if (direction == mxConstants.DIRECTION_NORTH) - { - var dy = (fixed) ? Math.max(0, Math.min(h, size)) : h * Math.max(0, Math.min(1, size)); - points = [new mxPoint(x, y + dy), new mxPoint(cx, y), new mxPoint(x + w, y + dy), - new mxPoint(x + w, y + h), new mxPoint(cx, y + h - dy), - new mxPoint(x, y + h), new mxPoint(x, y + dy)]; - } - else - { - var dy = (fixed) ? Math.max(0, Math.min(h, size)) : h * Math.max(0, Math.min(1, size)); - points = [new mxPoint(x, y), new mxPoint(cx, y + dy), new mxPoint(x + w, y), - new mxPoint(x + w, y + h - dy), new mxPoint(cx, y + h), - new mxPoint(x, y + h - dy), new mxPoint(x, y)]; - } - - var p1 = new mxPoint(cx, cy); - - if (orthogonal) - { - if (next.x < x || next.x > x + w) - { - p1.y = next.y; - } - else - { - p1.x = next.x; - } - } - - return mxUtils.getPerimeterPoint(points, p1, next); - }; - - mxStyleRegistry.putValue('stepPerimeter', mxPerimeter.StepPerimeter); - - // Hexagon Perimeter 2 (keep existing one) - mxPerimeter.HexagonPerimeter2 = function (bounds, vertex, next, orthogonal) - { - var size = HexagonShape.prototype.size; - - if (vertex != null) - { - size = mxUtils.getValue(vertex.style, 'size', size); - } - - var x = bounds.x; - var y = bounds.y; - var w = bounds.width; - var h = bounds.height; - - var cx = bounds.getCenterX(); - var cy = bounds.getCenterY(); - - var direction = (vertex != null) ? mxUtils.getValue( - vertex.style, mxConstants.STYLE_DIRECTION, - mxConstants.DIRECTION_EAST) : mxConstants.DIRECTION_EAST; - var vertical = direction == mxConstants.DIRECTION_NORTH || - direction == mxConstants.DIRECTION_SOUTH; - var points; - - if (vertical) - { - var dy = h * Math.max(0, Math.min(1, size)); - points = [new mxPoint(cx, y), new mxPoint(x + w, y + dy), new mxPoint(x + w, y + h - dy), - new mxPoint(cx, y + h), new mxPoint(x, y + h - dy), - new mxPoint(x, y + dy), new mxPoint(cx, y)]; - } - else - { - var dx = w * Math.max(0, Math.min(1, size)); - points = [new mxPoint(x + dx, y), new mxPoint(x + w - dx, y), new mxPoint(x + w, cy), - new mxPoint(x + w - dx, y + h), new mxPoint(x + dx, y + h), - new mxPoint(x, cy), new mxPoint(x + dx, y)]; - } - - var p1 = new mxPoint(cx, cy); - - if (orthogonal) - { - if (next.x < x || next.x > x + w) - { - p1.y = next.y; - } - else - { - p1.x = next.x; - } - } - - return mxUtils.getPerimeterPoint(points, p1, next); - }; - - mxStyleRegistry.putValue('hexagonPerimeter2', mxPerimeter.HexagonPerimeter2); - - // Provided Interface Shape (aka Lollipop) - function LollipopShape() - { - mxShape.call(this); - }; - mxUtils.extend(LollipopShape, mxShape); - LollipopShape.prototype.size = 10; - LollipopShape.prototype.paintBackground = function(c, x, y, w, h) - { - var sz = parseFloat(mxUtils.getValue(this.style, 'size', this.size)); - c.translate(x, y); - - c.ellipse((w - sz) / 2, 0, sz, sz); - c.fillAndStroke(); - - c.begin(); - c.moveTo(w / 2, sz); - c.lineTo(w / 2, h); - c.end(); - c.stroke(); - }; - - mxCellRenderer.registerShape('lollipop', LollipopShape); - - // Required Interface Shape - function RequiresShape() - { - mxShape.call(this); - }; - mxUtils.extend(RequiresShape, mxShape); - RequiresShape.prototype.size = 10; - RequiresShape.prototype.inset = 2; - RequiresShape.prototype.paintBackground = function(c, x, y, w, h) - { - var sz = parseFloat(mxUtils.getValue(this.style, 'size', this.size)); - var inset = parseFloat(mxUtils.getValue(this.style, 'inset', this.inset)) + this.strokewidth; - c.translate(x, y); - - c.begin(); - c.moveTo(w / 2, sz + inset); - c.lineTo(w / 2, h); - c.end(); - c.stroke(); - - c.begin(); - c.moveTo((w - sz) / 2 - inset, sz / 2); - c.quadTo((w - sz) / 2 - inset, sz + inset, w / 2, sz + inset); - c.quadTo((w + sz) / 2 + inset, sz + inset, (w + sz) / 2 + inset, sz / 2); - c.end(); - c.stroke(); - }; - - mxCellRenderer.registerShape('requires', RequiresShape); - - // Required Interface Shape - function RequiredInterfaceShape() - { - mxShape.call(this); - }; - mxUtils.extend(RequiredInterfaceShape, mxShape); - - RequiredInterfaceShape.prototype.paintBackground = function(c, x, y, w, h) - { - c.translate(x, y); - - c.begin(); - c.moveTo(0, 0); - c.quadTo(w, 0, w, h / 2); - c.quadTo(w, h, 0, h); - c.end(); - c.stroke(); - }; - - mxCellRenderer.registerShape('requiredInterface', RequiredInterfaceShape); - - // Provided and Required Interface Shape - function ProvidedRequiredInterfaceShape() - { - mxShape.call(this); - }; - mxUtils.extend(ProvidedRequiredInterfaceShape, mxShape); - ProvidedRequiredInterfaceShape.prototype.inset = 2; - ProvidedRequiredInterfaceShape.prototype.paintBackground = function(c, x, y, w, h) - { - var inset = parseFloat(mxUtils.getValue(this.style, 'inset', this.inset)) + this.strokewidth; - c.translate(x, y); - - c.ellipse(0, inset, w - 2 * inset, h - 2 * inset); - c.fillAndStroke(); - - c.begin(); - c.moveTo(w / 2, 0); - c.quadTo(w, 0, w, h / 2); - c.quadTo(w, h, w / 2, h); - c.end(); - c.stroke(); - }; - - mxCellRenderer.registerShape('providedRequiredInterface', ProvidedRequiredInterfaceShape); - - // Component shape - function ComponentShape() - { - mxCylinder.call(this); - }; - mxUtils.extend(ComponentShape, mxCylinder); - ComponentShape.prototype.jettyWidth = 32; - ComponentShape.prototype.jettyHeight = 12; - ComponentShape.prototype.redrawPath = function(path, x, y, w, h, isForeground) - { - var dx = parseFloat(mxUtils.getValue(this.style, 'jettyWidth', this.jettyWidth)); - var dy = parseFloat(mxUtils.getValue(this.style, 'jettyHeight', this.jettyHeight)); - var x0 = dx / 2; - var x1 = x0 + dx / 2; - var y0 = 0.3 * h - dy / 2; - var y1 = 0.7 * h - dy / 2; - - if (isForeground) - { - path.moveTo(x0, y0); - path.lineTo(x1, y0); - path.lineTo(x1, y0 + dy); - path.lineTo(x0, y0 + dy); - path.moveTo(x0, y1); - path.lineTo(x1, y1); - path.lineTo(x1, y1 + dy); - path.lineTo(x0, y1 + dy); - path.end(); - } - else - { - path.moveTo(x0, 0); - path.lineTo(w, 0); - path.lineTo(w, h); - path.lineTo(x0, h); - path.lineTo(x0, y1 + dy); - path.lineTo(0, y1 + dy); - path.lineTo(0, y1); - path.lineTo(x0, y1); - path.lineTo(x0, y0 + dy); - path.lineTo(0, y0 + dy); - path.lineTo(0, y0); - path.lineTo(x0, y0); - path.close(); - path.end(); - } - }; - - mxCellRenderer.registerShape('component', ComponentShape); - - // State Shapes derives from double ellipse - function StateShape() - { - mxDoubleEllipse.call(this); - }; - mxUtils.extend(StateShape, mxDoubleEllipse); - StateShape.prototype.outerStroke = true; - StateShape.prototype.paintVertexShape = function(c, x, y, w, h) - { - var inset = Math.min(4, Math.min(w / 5, h / 5)); - - if (w > 0 && h > 0) - { - c.ellipse(x + inset, y + inset, w - 2 * inset, h - 2 * inset); - c.fillAndStroke(); - } - - c.setShadow(false); - - if (this.outerStroke) - { - c.ellipse(x, y, w, h); - c.stroke(); - } - }; - - mxCellRenderer.registerShape('endState', StateShape); - - function StartStateShape() - { - StateShape.call(this); - }; - mxUtils.extend(StartStateShape, StateShape); - StartStateShape.prototype.outerStroke = false; - - mxCellRenderer.registerShape('startState', StartStateShape); - - // Link shape - function LinkShape() - { - mxArrowConnector.call(this); - this.spacing = 0; - }; - mxUtils.extend(LinkShape, mxArrowConnector); - LinkShape.prototype.defaultWidth = 4; - - LinkShape.prototype.isOpenEnded = function() - { - return true; - }; - - LinkShape.prototype.getEdgeWidth = function() - { - return mxUtils.getNumber(this.style, 'width', this.defaultWidth) + Math.max(0, this.strokewidth - 1); - }; - - LinkShape.prototype.isArrowRounded = function() - { - return this.isRounded; - }; - - // Registers the link shape - mxCellRenderer.registerShape('link', LinkShape); - - // Generic arrow - function FlexArrowShape() - { - mxArrowConnector.call(this); - this.spacing = 0; - }; - mxUtils.extend(FlexArrowShape, mxArrowConnector); - FlexArrowShape.prototype.defaultWidth = 10; - FlexArrowShape.prototype.defaultArrowWidth = 20; - - FlexArrowShape.prototype.getStartArrowWidth = function() - { - return this.getEdgeWidth() + mxUtils.getNumber(this.style, 'startWidth', this.defaultArrowWidth); - }; - - FlexArrowShape.prototype.getEndArrowWidth = function() - { - return this.getEdgeWidth() + mxUtils.getNumber(this.style, 'endWidth', this.defaultArrowWidth);; - }; - - FlexArrowShape.prototype.getEdgeWidth = function() - { - return mxUtils.getNumber(this.style, 'width', this.defaultWidth) + Math.max(0, this.strokewidth - 1); - }; - - // Registers the link shape - mxCellRenderer.registerShape('flexArrow', FlexArrowShape); - - // Manual Input shape - function ManualInputShape() - { - mxActor.call(this); - }; - mxUtils.extend(ManualInputShape, mxActor); - ManualInputShape.prototype.size = 30; - ManualInputShape.prototype.isRoundable = function() - { - return true; - }; - ManualInputShape.prototype.redrawPath = function(c, x, y, w, h) - { - var s = Math.min(h, parseFloat(mxUtils.getValue(this.style, 'size', this.size))); - var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; - this.addPoints(c, [new mxPoint(0, h), new mxPoint(0, s), new mxPoint(w, 0), new mxPoint(w, h)], - this.isRounded, arcSize, true); - c.end(); - }; - - mxCellRenderer.registerShape('manualInput', ManualInputShape); - - // Internal storage - function InternalStorageShape() - { - mxRectangleShape.call(this); - }; - mxUtils.extend(InternalStorageShape, mxRectangleShape); - InternalStorageShape.prototype.dx = 20; - InternalStorageShape.prototype.dy = 20; - InternalStorageShape.prototype.isHtmlAllowed = function() - { - return false; - }; - InternalStorageShape.prototype.paintForeground = function(c, x, y, w, h) - { - mxRectangleShape.prototype.paintForeground.apply(this, arguments); - var inset = 0; - - if (this.isRounded) - { - var f = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, - mxConstants.RECTANGLE_ROUNDING_FACTOR * 100) / 100; - inset = Math.max(inset, Math.min(w * f, h * f)); - } - - var dx = Math.max(inset, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx', this.dx)))); - var dy = Math.max(inset, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy', this.dy)))); - - c.begin(); - c.moveTo(x, y + dy); - c.lineTo(x + w, y + dy); - c.end(); - c.stroke(); - - c.begin(); - c.moveTo(x + dx, y); - c.lineTo(x + dx, y + h); - c.end(); - c.stroke(); - }; - - mxCellRenderer.registerShape('internalStorage', InternalStorageShape); - - // Internal storage - function CornerShape() - { - mxActor.call(this); - }; - mxUtils.extend(CornerShape, mxActor); - CornerShape.prototype.dx = 20; - CornerShape.prototype.dy = 20; - - // Corner - CornerShape.prototype.redrawPath = function(c, x, y, w, h) - { - var dx = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx', this.dx)))); - var dy = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy', this.dy)))); - - var s = Math.min(w / 2, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); - var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; - this.addPoints(c, [new mxPoint(0, 0), new mxPoint(w, 0), new mxPoint(w, dy), new mxPoint(dx, dy), - new mxPoint(dx, h), new mxPoint(0, h)], this.isRounded, arcSize, true); - c.end(); - }; - - mxCellRenderer.registerShape('corner', CornerShape); - - // Crossbar shape - function CrossbarShape() - { - mxActor.call(this); - }; - mxUtils.extend(CrossbarShape, mxActor); - - CrossbarShape.prototype.redrawPath = function(c, x, y, w, h) - { - c.moveTo(0, 0); - c.lineTo(0, h); - c.end(); - - c.moveTo(w, 0); - c.lineTo(w, h); - c.end(); - - c.moveTo(0, h / 2); - c.lineTo(w, h / 2); - c.end(); - }; - - mxCellRenderer.registerShape('crossbar', CrossbarShape); - - // Internal storage - function TeeShape() - { - mxActor.call(this); - }; - mxUtils.extend(TeeShape, mxActor); - TeeShape.prototype.dx = 20; - TeeShape.prototype.dy = 20; - - // Corner - TeeShape.prototype.redrawPath = function(c, x, y, w, h) - { - var dx = Math.max(0, Math.min(w, parseFloat(mxUtils.getValue(this.style, 'dx', this.dx)))); - var dy = Math.max(0, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'dy', this.dy)))); - var w2 = Math.abs(w - dx) / 2; - - var s = Math.min(w / 2, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); - var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; - this.addPoints(c, [new mxPoint(0, 0), new mxPoint(w, 0), new mxPoint(w, dy), new mxPoint((w + dx) / 2, dy), - new mxPoint((w + dx) / 2, h), new mxPoint((w - dx) / 2, h), new mxPoint((w - dx) / 2, dy), - new mxPoint(0, dy)], this.isRounded, arcSize, true); - c.end(); - }; - - mxCellRenderer.registerShape('tee', TeeShape); - - // Arrow - function SingleArrowShape() - { - mxActor.call(this); - }; - mxUtils.extend(SingleArrowShape, mxActor); - SingleArrowShape.prototype.arrowWidth = 0.3; - SingleArrowShape.prototype.arrowSize = 0.2; - SingleArrowShape.prototype.redrawPath = function(c, x, y, w, h) - { - var aw = h * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'arrowWidth', this.arrowWidth)))); - var as = w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'arrowSize', this.arrowSize)))); - var at = (h - aw) / 2; - var ab = at + aw; - - var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; - this.addPoints(c, [new mxPoint(0, at), new mxPoint(w - as, at), new mxPoint(w - as, 0), new mxPoint(w, h / 2), - new mxPoint(w - as, h), new mxPoint(w - as, ab), new mxPoint(0, ab)], - this.isRounded, arcSize, true); - c.end(); - }; - - mxCellRenderer.registerShape('singleArrow', SingleArrowShape); - - // Arrow - function DoubleArrowShape() - { - mxActor.call(this); - }; - mxUtils.extend(DoubleArrowShape, mxActor); - DoubleArrowShape.prototype.redrawPath = function(c, x, y, w, h) - { - var aw = h * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'arrowWidth', SingleArrowShape.prototype.arrowWidth)))); - var as = w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'arrowSize', SingleArrowShape.prototype.arrowSize)))); - var at = (h - aw) / 2; - var ab = at + aw; - - var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; - this.addPoints(c, [new mxPoint(0, h / 2), new mxPoint(as, 0), new mxPoint(as, at), new mxPoint(w - as, at), - new mxPoint(w - as, 0), new mxPoint(w, h / 2), new mxPoint(w - as, h), - new mxPoint(w - as, ab), new mxPoint(as, ab), new mxPoint(as, h)], - this.isRounded, arcSize, true); - c.end(); - }; - - mxCellRenderer.registerShape('doubleArrow', DoubleArrowShape); - - // Data storage - function DataStorageShape() - { - mxActor.call(this); - }; - mxUtils.extend(DataStorageShape, mxActor); - DataStorageShape.prototype.size = 0.1; - DataStorageShape.prototype.redrawPath = function(c, x, y, w, h) - { - var s = w * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); - - c.moveTo(s, 0); - c.lineTo(w, 0); - c.quadTo(w - s * 2, h / 2, w, h); - c.lineTo(s, h); - c.quadTo(s - s * 2, h / 2, s, 0); - c.close(); - c.end(); - }; - - mxCellRenderer.registerShape('dataStorage', DataStorageShape); - - // Or - function OrShape() - { - mxActor.call(this); - }; - mxUtils.extend(OrShape, mxActor); - OrShape.prototype.redrawPath = function(c, x, y, w, h) - { - c.moveTo(0, 0); - c.quadTo(w, 0, w, h / 2); - c.quadTo(w, h, 0, h); - c.close(); - c.end(); - }; - - mxCellRenderer.registerShape('or', OrShape); - - // Xor - function XorShape() - { - mxActor.call(this); - }; - mxUtils.extend(XorShape, mxActor); - XorShape.prototype.redrawPath = function(c, x, y, w, h) - { - c.moveTo(0, 0); - c.quadTo(w, 0, w, h / 2); - c.quadTo(w, h, 0, h); - c.quadTo(w / 2, h / 2, 0, 0); - c.close(); - c.end(); - }; - - mxCellRenderer.registerShape('xor', XorShape); - - // Loop limit - function LoopLimitShape() - { - mxActor.call(this); - }; - mxUtils.extend(LoopLimitShape, mxActor); - LoopLimitShape.prototype.size = 20; - LoopLimitShape.prototype.isRoundable = function() - { - return true; - }; - LoopLimitShape.prototype.redrawPath = function(c, x, y, w, h) - { - var s = Math.min(w / 2, Math.min(h, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); - var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; - this.addPoints(c, [new mxPoint(s, 0), new mxPoint(w - s, 0), new mxPoint(w, s * 0.8), new mxPoint(w, h), - new mxPoint(0, h), new mxPoint(0, s * 0.8)], this.isRounded, arcSize, true); - c.end(); - }; - - mxCellRenderer.registerShape('loopLimit', LoopLimitShape); - - // Off page connector - function OffPageConnectorShape() - { - mxActor.call(this); - }; - mxUtils.extend(OffPageConnectorShape, mxActor); - OffPageConnectorShape.prototype.size = 3 / 8; - OffPageConnectorShape.prototype.isRoundable = function() - { - return true; - }; - OffPageConnectorShape.prototype.redrawPath = function(c, x, y, w, h) - { - var s = h * Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); - var arcSize = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; - this.addPoints(c, [new mxPoint(0, 0), new mxPoint(w, 0), new mxPoint(w, h - s), new mxPoint(w / 2, h), - new mxPoint(0, h - s)], this.isRounded, arcSize, true); - c.end(); - }; - - mxCellRenderer.registerShape('offPageConnector', OffPageConnectorShape); - - // Internal storage - function TapeDataShape() - { - mxEllipse.call(this); - }; - mxUtils.extend(TapeDataShape, mxEllipse); - TapeDataShape.prototype.paintVertexShape = function(c, x, y, w, h) - { - mxEllipse.prototype.paintVertexShape.apply(this, arguments); - - c.begin(); - c.moveTo(x + w / 2, y + h); - c.lineTo(x + w, y + h); - c.end(); - c.stroke(); - }; - - mxCellRenderer.registerShape('tapeData', TapeDataShape); - - // OrEllipseShape - function OrEllipseShape() - { - mxEllipse.call(this); - }; - mxUtils.extend(OrEllipseShape, mxEllipse); - OrEllipseShape.prototype.paintVertexShape = function(c, x, y, w, h) - { - mxEllipse.prototype.paintVertexShape.apply(this, arguments); - - c.setShadow(false); - c.begin(); - c.moveTo(x, y + h / 2); - c.lineTo(x + w, y + h / 2); - c.end(); - c.stroke(); - - c.begin(); - c.moveTo(x + w / 2, y); - c.lineTo(x + w / 2, y + h); - c.end(); - c.stroke(); - }; - - mxCellRenderer.registerShape('orEllipse', OrEllipseShape); - - // SumEllipseShape - function SumEllipseShape() - { - mxEllipse.call(this); - }; - mxUtils.extend(SumEllipseShape, mxEllipse); - SumEllipseShape.prototype.paintVertexShape = function(c, x, y, w, h) - { - mxEllipse.prototype.paintVertexShape.apply(this, arguments); - var s2 = 0.145; - - c.setShadow(false); - c.begin(); - c.moveTo(x + w * s2, y + h * s2); - c.lineTo(x + w * (1 - s2), y + h * (1 - s2)); - c.end(); - c.stroke(); - - c.begin(); - c.moveTo(x + w * (1 - s2), y + h * s2); - c.lineTo(x + w * s2, y + h * (1 - s2)); - c.end(); - c.stroke(); - }; - - mxCellRenderer.registerShape('sumEllipse', SumEllipseShape); - - // SortShape - function SortShape() - { - mxRhombus.call(this); - }; - mxUtils.extend(SortShape, mxRhombus); - SortShape.prototype.paintVertexShape = function(c, x, y, w, h) - { - mxRhombus.prototype.paintVertexShape.apply(this, arguments); - - c.setShadow(false); - c.begin(); - c.moveTo(x, y + h / 2); - c.lineTo(x + w, y + h / 2); - c.end(); - c.stroke(); - }; - - mxCellRenderer.registerShape('sortShape', SortShape); - - // CollateShape - function CollateShape() - { - mxEllipse.call(this); - }; - mxUtils.extend(CollateShape, mxEllipse); - CollateShape.prototype.paintVertexShape = function(c, x, y, w, h) - { - c.begin(); - c.moveTo(x, y); - c.lineTo(x + w, y); - c.lineTo(x + w / 2, y + h / 2); - c.close(); - c.fillAndStroke(); - - c.begin(); - c.moveTo(x, y + h); - c.lineTo(x + w, y + h); - c.lineTo(x + w / 2, y + h / 2); - c.close(); - c.fillAndStroke(); - }; - - mxCellRenderer.registerShape('collate', CollateShape); - - // DimensionShape - function DimensionShape() - { - mxEllipse.call(this); - }; - mxUtils.extend(DimensionShape, mxEllipse); - DimensionShape.prototype.paintVertexShape = function(c, x, y, w, h) - { - // Arrow size - var al = 10; - var cy = y + h - al / 2; - - c.begin(); - c.moveTo(x, y); - c.lineTo(x, y + h); - c.moveTo(x, cy); - c.lineTo(x + al, cy - al / 2); - c.moveTo(x, cy); - c.lineTo(x + al, cy + al / 2); - c.moveTo(x, cy); - c.lineTo(x + w, cy); - - // Opposite side - c.moveTo(x + w, y); - c.lineTo(x + w, y + h); - c.moveTo(x + w, cy); - c.lineTo(x + w - al, cy - al / 2); - c.moveTo(x + w, cy); - c.lineTo(x + w - al, cy + al / 2); - c.end(); - c.stroke(); - }; - - mxCellRenderer.registerShape('dimension', DimensionShape); - - // PartialRectangleShape - function PartialRectangleShape() - { - mxEllipse.call(this); - }; - mxUtils.extend(PartialRectangleShape, mxEllipse); - PartialRectangleShape.prototype.paintVertexShape = function(c, x, y, w, h) - { - if (!this.outline) - { - c.setStrokeColor(null); - } - - mxRectangleShape.prototype.paintBackground.apply(this, arguments); - - if (this.style != null) - { - c.setStrokeColor(this.stroke); - c.rect(x, y, w, h); - c.fill(); - - c.begin(); - c.moveTo(x, y); - - if (mxUtils.getValue(this.style, 'top', '1') == '1') - { - c.lineTo(x + w, y); - } - else - { - c.moveTo(x + w, y); - } - - if (mxUtils.getValue(this.style, 'right', '1') == '1') - { - c.lineTo(x + w, y + h); - } - else - { - c.moveTo(x + w, y + h); - } - - if (mxUtils.getValue(this.style, 'bottom', '1') == '1') - { - c.lineTo(x, y + h); - } - else - { - c.moveTo(x, y + h); - } - - if (mxUtils.getValue(this.style, 'left', '1') == '1') - { - c.lineTo(x, y - this.strokewidth / 2); - } - - c.end(); - c.stroke(); - } - }; - - mxCellRenderer.registerShape('partialRectangle', PartialRectangleShape); - - // LineEllipseShape - function LineEllipseShape() - { - mxEllipse.call(this); - }; - mxUtils.extend(LineEllipseShape, mxEllipse); - LineEllipseShape.prototype.paintVertexShape = function(c, x, y, w, h) - { - mxEllipse.prototype.paintVertexShape.apply(this, arguments); - - c.setShadow(false); - c.begin(); - - if (mxUtils.getValue(this.style, 'line') == 'vertical') - { - c.moveTo(x + w / 2, y); - c.lineTo(x + w / 2, y + h); - } - else - { - c.moveTo(x, y + h / 2); - c.lineTo(x + w, y + h / 2); - } - - c.end(); - c.stroke(); - }; - - mxCellRenderer.registerShape('lineEllipse', LineEllipseShape); - - // Delay - function DelayShape() - { - mxActor.call(this); - }; - mxUtils.extend(DelayShape, mxActor); - DelayShape.prototype.redrawPath = function(c, x, y, w, h) - { - var dx = Math.min(w, h / 2); - c.moveTo(0, 0); - c.lineTo(w - dx, 0); - c.quadTo(w, 0, w, h / 2); - c.quadTo(w, h, w - dx, h); - c.lineTo(0, h); - c.close(); - c.end(); - }; - - mxCellRenderer.registerShape('delay', DelayShape); - - // Cross Shape - function CrossShape() - { - mxActor.call(this); - }; - mxUtils.extend(CrossShape, mxActor); - CrossShape.prototype.size = 0.2; - CrossShape.prototype.redrawPath = function(c, x, y, w, h) - { - var m = Math.min(h, w); - var size = Math.max(0, Math.min(m, m * parseFloat(mxUtils.getValue(this.style, 'size', this.size)))); - var t = (h - size) / 2; - var b = t + size; - var l = (w - size) / 2; - var r = l + size; - - c.moveTo(0, t); - c.lineTo(l, t); - c.lineTo(l, 0); - c.lineTo(r, 0); - c.lineTo(r, t); - c.lineTo(w, t); - c.lineTo(w, b); - c.lineTo(r, b); - c.lineTo(r, h); - c.lineTo(l, h); - c.lineTo(l, b); - c.lineTo(0, b); - c.close(); - c.end(); - }; - - mxCellRenderer.registerShape('cross', CrossShape); - - // Display - function DisplayShape() - { - mxActor.call(this); - }; - mxUtils.extend(DisplayShape, mxActor); - DisplayShape.prototype.size = 0.25; - DisplayShape.prototype.redrawPath = function(c, x, y, w, h) - { - var dx = Math.min(w, h / 2); - var s = Math.min(w - dx, Math.max(0, parseFloat(mxUtils.getValue(this.style, 'size', this.size))) * w); - - c.moveTo(0, h / 2); - c.lineTo(s, 0); - c.lineTo(w - dx, 0); - c.quadTo(w, 0, w, h / 2); - c.quadTo(w, h, w - dx, h); - c.lineTo(s, h); - c.close(); - c.end(); - }; - - mxCellRenderer.registerShape('display', DisplayShape); - - // FilledEdge shape - function FilledEdge() - { - mxConnector.call(this); - }; - mxUtils.extend(FilledEdge, mxConnector); - - FilledEdge.prototype.origPaintEdgeShape = FilledEdge.prototype.paintEdgeShape; - FilledEdge.prototype.paintEdgeShape = function(c, pts, rounded) - { - // Markers modify incoming points array - var temp = []; - - for (var i = 0; i < pts.length; i++) - { - temp.push(mxUtils.clone(pts[i])); - } - - // paintEdgeShape resets dashed to false - var dashed = c.state.dashed; - var fixDash = c.state.fixDash; - FilledEdge.prototype.origPaintEdgeShape.apply(this, [c, temp, rounded]); - - if (c.state.strokeWidth >= 3) - { - var fillClr = mxUtils.getValue(this.style, 'fillColor', null); - - if (fillClr != null) - { - c.setStrokeColor(fillClr); - c.setStrokeWidth(c.state.strokeWidth - 2); - c.setDashed(dashed, fixDash); - - FilledEdge.prototype.origPaintEdgeShape.apply(this, [c, pts, rounded]); - } - } - }; - - // Registers the link shape - mxCellRenderer.registerShape('filledEdge', FilledEdge); - - // Implements custom colors for shapes - if (typeof StyleFormatPanel !== 'undefined') - { - (function() - { - var styleFormatPanelGetCustomColors = StyleFormatPanel.prototype.getCustomColors; - - StyleFormatPanel.prototype.getCustomColors = function() - { - var ss = this.format.getSelectionState(); - var result = styleFormatPanelGetCustomColors.apply(this, arguments); - - if (ss.style.shape == 'umlFrame') - { - result.push({title: mxResources.get('laneColor'), key: 'swimlaneFillColor', defaultValue: '#ffffff'}); - } - - return result; - }; - })(); - } - - // Registers and defines the custom marker - mxMarker.addMarker('dash', function(c, shape, type, pe, unitX, unitY, size, source, sw, filled) - { - var nx = unitX * (size + sw + 1); - var ny = unitY * (size + sw + 1); - - return function() - { - c.begin(); - c.moveTo(pe.x - nx / 2 - ny / 2, pe.y - ny / 2 + nx / 2); - c.lineTo(pe.x + ny / 2 - 3 * nx / 2, pe.y - 3 * ny / 2 - nx / 2); - c.stroke(); - }; - }); - - // Registers and defines the custom marker - mxMarker.addMarker('cross', function(c, shape, type, pe, unitX, unitY, size, source, sw, filled) - { - var nx = unitX * (size + sw + 1); - var ny = unitY * (size + sw + 1); - - return function() - { - c.begin(); - c.moveTo(pe.x - nx / 2 - ny / 2, pe.y - ny / 2 + nx / 2); - c.lineTo(pe.x + ny / 2 - 3 * nx / 2, pe.y - 3 * ny / 2 - nx / 2); - c.moveTo(pe.x - nx / 2 + ny / 2, pe.y - ny / 2 - nx / 2); - c.lineTo(pe.x - ny / 2 - 3 * nx / 2, pe.y - 3 * ny / 2 + nx / 2); - c.stroke(); - }; - }); - - function circleMarker(c, shape, type, pe, unitX, unitY, size, source, sw, filled) - { - var a = size / 2; - var size = size + sw; - - var pt = pe.clone(); - - pe.x -= unitX * (2 * size + sw); - pe.y -= unitY * (2 * size + sw); - - unitX = unitX * (size + sw); - unitY = unitY * (size + sw); - - return function() - { - c.ellipse(pt.x - unitX - size, pt.y - unitY - size, 2 * size, 2 * size); - - if (filled) - { - c.fillAndStroke(); - } - else - { - c.stroke(); - } - }; - }; - - mxMarker.addMarker('circle', circleMarker); - mxMarker.addMarker('circlePlus', function(c, shape, type, pe, unitX, unitY, size, source, sw, filled) - { - var pt = pe.clone(); - var fn = circleMarker.apply(this, arguments); - var nx = unitX * (size + 2 * sw); // (size + sw + 1); - var ny = unitY * (size + 2 * sw); //(size + sw + 1); - - return function() - { - fn.apply(this, arguments); - - c.begin(); - c.moveTo(pt.x - unitX * (sw), pt.y - unitY * (sw)); - c.lineTo(pt.x - 2 * nx + unitX * (sw), pt.y - 2 * ny + unitY * (sw)); - c.moveTo(pt.x - nx - ny + unitY * sw, pt.y - ny + nx - unitX * sw); - c.lineTo(pt.x + ny - nx - unitY * sw, pt.y - ny - nx + unitX * sw); - c.stroke(); - }; - }); - - mxMarker.addMarker('async', function(c, shape, type, pe, unitX, unitY, size, source, sw, filled) - { - // The angle of the forward facing arrow sides against the x axis is - // 26.565 degrees, 1/sin(26.565) = 2.236 / 2 = 1.118 ( / 2 allows for - // only half the strokewidth is processed ). - var endOffsetX = unitX * sw * 1.118; - var endOffsetY = unitY * sw * 1.118; - - unitX = unitX * (size + sw); - unitY = unitY * (size + sw); - - var pt = pe.clone(); - pt.x -= endOffsetX; - pt.y -= endOffsetY; - - var f = 1; - pe.x += -unitX * f - endOffsetX; - pe.y += -unitY * f - endOffsetY; - - return function() - { - c.begin(); - c.moveTo(pt.x, pt.y); - - if (source) - { - c.lineTo(pt.x - unitX - unitY / 2, pt.y - unitY + unitX / 2); - } - else - { - c.lineTo(pt.x + unitY / 2 - unitX, pt.y - unitY - unitX / 2); - } - - c.lineTo(pt.x - unitX, pt.y - unitY); - c.close(); - - if (filled) - { - c.fillAndStroke(); - } - else - { - c.stroke(); - } - }; - }); - - function createOpenAsyncArrow(widthFactor) - { - widthFactor = (widthFactor != null) ? widthFactor : 2; - - return function(c, shape, type, pe, unitX, unitY, size, source, sw, filled) - { - unitX = unitX * (size + sw); - unitY = unitY * (size + sw); - - var pt = pe.clone(); - - return function() - { - c.begin(); - c.moveTo(pt.x, pt.y); - - if (source) - { - c.lineTo(pt.x - unitX - unitY / widthFactor, pt.y - unitY + unitX / widthFactor); - } - else - { - c.lineTo(pt.x + unitY / widthFactor - unitX, pt.y - unitY - unitX / widthFactor); - } - - c.stroke(); - }; - } - }; - - mxMarker.addMarker('openAsync', createOpenAsyncArrow(2)); - - function arrow(canvas, shape, type, pe, unitX, unitY, size, source, sw, filled) - { - // The angle of the forward facing arrow sides against the x axis is - // 26.565 degrees, 1/sin(26.565) = 2.236 / 2 = 1.118 ( / 2 allows for - // only half the strokewidth is processed ). - var endOffsetX = unitX * sw * 1.118; - var endOffsetY = unitY * sw * 1.118; - - unitX = unitX * (size + sw); - unitY = unitY * (size + sw); - - var pt = pe.clone(); - pt.x -= endOffsetX; - pt.y -= endOffsetY; - - var f = (type != mxConstants.ARROW_CLASSIC && type != mxConstants.ARROW_CLASSIC_THIN) ? 1 : 3 / 4; - pe.x += -unitX * f - endOffsetX; - pe.y += -unitY * f - endOffsetY; - - return function() - { - canvas.begin(); - canvas.moveTo(pt.x, pt.y); - canvas.lineTo(pt.x - unitX - unitY / widthFactor, pt.y - unitY + unitX / widthFactor); - - if (type == mxConstants.ARROW_CLASSIC || type == mxConstants.ARROW_CLASSIC_THIN) - { - canvas.lineTo(pt.x - unitX * 3 / 4, pt.y - unitY * 3 / 4); - } - - canvas.lineTo(pt.x + unitY / widthFactor - unitX, pt.y - unitY - unitX / widthFactor); - canvas.close(); - - if (filled) - { - canvas.fillAndStroke(); - } - else - { - canvas.stroke(); - } - }; - } - - // Handlers are only added if mxVertexHandler is defined (ie. not in embedded graph) - if (typeof mxVertexHandler !== 'undefined') - { - function createHandle(state, keys, getPositionFn, setPositionFn, ignoreGrid, redrawEdges) - { - var handle = new mxHandle(state, null, mxVertexHandler.prototype.secondaryHandleImage); - - handle.execute = function() - { - for (var i = 0; i < keys.length; i++) - { - this.copyStyle(keys[i]); - } - }; - - handle.getPosition = getPositionFn; - handle.setPosition = setPositionFn; - handle.ignoreGrid = (ignoreGrid != null) ? ignoreGrid : true; - - // Overridden to update connected edges - if (redrawEdges) - { - var positionChanged = handle.positionChanged; - - handle.positionChanged = function() - { - positionChanged.apply(this, arguments); - - // Redraws connected edges TODO: Include child edges - state.view.invalidate(this.state.cell); - state.view.validate(); - }; - } - - return handle; - }; - - function createArcHandle(state, yOffset) - { - return createHandle(state, [mxConstants.STYLE_ARCSIZE], function(bounds) - { - var tmp = (yOffset != null) ? yOffset : bounds.height / 8; - - if (mxUtils.getValue(state.style, mxConstants.STYLE_ABSOLUTE_ARCSIZE, 0) == '1') - { - var arcSize = mxUtils.getValue(state.style, mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2; - - return new mxPoint(bounds.x + bounds.width - Math.min(bounds.width / 2, arcSize), bounds.y + tmp); - } - else - { - var arcSize = Math.max(0, parseFloat(mxUtils.getValue(state.style, - mxConstants.STYLE_ARCSIZE, mxConstants.RECTANGLE_ROUNDING_FACTOR * 100))) / 100; - - return new mxPoint(bounds.x + bounds.width - Math.min(Math.max(bounds.width / 2, bounds.height / 2), - Math.min(bounds.width, bounds.height) * arcSize), bounds.y + tmp); - } - }, function(bounds, pt, me) - { - if (mxUtils.getValue(state.style, mxConstants.STYLE_ABSOLUTE_ARCSIZE, 0) == '1') - { - this.state.style[mxConstants.STYLE_ARCSIZE] = Math.round(Math.max(0, Math.min(bounds.width, - (bounds.x + bounds.width - pt.x) * 2))); - } - else - { - var f = Math.min(50, Math.max(0, (bounds.width - pt.x + bounds.x) * 100 / - Math.min(bounds.width, bounds.height))); - this.state.style[mxConstants.STYLE_ARCSIZE] = Math.round(f); - } - }); - } - - function createArcHandleFunction() - { - return function(state) - { - var handles = []; - - if (mxUtils.getValue(state.style, mxConstants.STYLE_ROUNDED, false)) - { - handles.push(createArcHandle(state)); - } - - return handles; - }; - }; - - function createTrapezoidHandleFunction(max) - { - return function(state) - { - var handles = [createHandle(state, ['size'], function(bounds) - { - var size = Math.max(0, Math.min(max, parseFloat(mxUtils.getValue(this.state.style, 'size', TrapezoidShape.prototype.size)))); - - return new mxPoint(bounds.x + size * bounds.width * 0.75, bounds.y + bounds.height / 4); - }, function(bounds, pt) - { - this.state.style['size'] = Math.max(0, Math.min(max, (pt.x - bounds.x) / (bounds.width * 0.75))); - }, null, true)]; - - if (mxUtils.getValue(state.style, mxConstants.STYLE_ROUNDED, false)) - { - handles.push(createArcHandle(state)); - } - - return handles; - }; - }; - - function createDisplayHandleFunction(defaultValue, allowArcHandle, max, redrawEdges, fixedDefaultValue) - { - max = (max != null) ? max : 1; - - return function(state) - { - var handles = [createHandle(state, ['size'], function(bounds) - { - var fixed = (fixedDefaultValue != null) ? mxUtils.getValue(this.state.style, 'fixedSize', '0') != '0' : null; - var size = parseFloat(mxUtils.getValue(this.state.style, 'size', (fixed) ? fixedDefaultValue : defaultValue)); - - return new mxPoint(bounds.x + Math.max(0, Math.min(bounds.width, size * ((fixed) ? 1 : bounds.width))), bounds.getCenterY()); - }, function(bounds, pt, me) - { - var fixed = (fixedDefaultValue != null) ? mxUtils.getValue(this.state.style, 'fixedSize', '0') != '0' : null; - var size = (fixed) ? (pt.x - bounds.x) : Math.max(0, Math.min(max, (pt.x - bounds.x) / bounds.width)); - - if (fixed && !mxEvent.isAltDown(me.getEvent())) - { - size = state.view.graph.snap(size); - } - - this.state.style['size'] = size; - }, null, redrawEdges)]; - - if (allowArcHandle && mxUtils.getValue(state.style, mxConstants.STYLE_ROUNDED, false)) - { - handles.push(createArcHandle(state)); - } - - return handles; - }; - }; - - function createCubeHandleFunction(factor, defaultValue, allowArcHandle) - { - return function(state) - { - var handles = [createHandle(state, ['size'], function(bounds) - { - var size = Math.max(0, Math.min(bounds.width, Math.min(bounds.height, parseFloat( - mxUtils.getValue(this.state.style, 'size', defaultValue))))) * factor; - - return new mxPoint(bounds.x + size, bounds.y + size); - }, function(bounds, pt) - { - this.state.style['size'] = Math.round(Math.max(0, Math.min(Math.min(bounds.width, pt.x - bounds.x), - Math.min(bounds.height, pt.y - bounds.y))) / factor); - })]; - - if (allowArcHandle && mxUtils.getValue(state.style, mxConstants.STYLE_ROUNDED, false)) - { - handles.push(createArcHandle(state)); - } - - return handles; - }; - }; - - function createArrowHandleFunction(maxSize) - { - return function(state) - { - return [createHandle(state, ['arrowWidth', 'arrowSize'], function(bounds) - { - var aw = Math.max(0, Math.min(1, mxUtils.getValue(this.state.style, 'arrowWidth', SingleArrowShape.prototype.arrowWidth))); - var as = Math.max(0, Math.min(maxSize, mxUtils.getValue(this.state.style, 'arrowSize', SingleArrowShape.prototype.arrowSize))); - - return new mxPoint(bounds.x + (1 - as) * bounds.width, bounds.y + (1 - aw) * bounds.height / 2); - }, function(bounds, pt) - { - this.state.style['arrowWidth'] = Math.max(0, Math.min(1, Math.abs(bounds.y + bounds.height / 2 - pt.y) / bounds.height * 2)); - this.state.style['arrowSize'] = Math.max(0, Math.min(maxSize, (bounds.x + bounds.width - pt.x) / (bounds.width))); - })]; - }; - }; - - function createEdgeHandle(state, keys, start, getPosition, setPosition) - { - return createHandle(state, keys, function(bounds) - { - var pts = state.absolutePoints; - var n = pts.length - 1; - - var tr = state.view.translate; - var s = state.view.scale; - - var p0 = (start) ? pts[0] : pts[n]; - var p1 = (start) ? pts[1] : pts[n - 1]; - var dx = (start) ? p1.x - p0.x : p1.x - p0.x; - var dy = (start) ? p1.y - p0.y : p1.y - p0.y; - - var dist = Math.sqrt(dx * dx + dy * dy); - - var pt = getPosition.call(this, dist, dx / dist, dy / dist, p0, p1); - - return new mxPoint(pt.x / s - tr.x, pt.y / s - tr.y); - }, function(bounds, pt, me) - { - var pts = state.absolutePoints; - var n = pts.length - 1; - - var tr = state.view.translate; - var s = state.view.scale; - - var p0 = (start) ? pts[0] : pts[n]; - var p1 = (start) ? pts[1] : pts[n - 1]; - var dx = (start) ? p1.x - p0.x : p1.x - p0.x; - var dy = (start) ? p1.y - p0.y : p1.y - p0.y; - - var dist = Math.sqrt(dx * dx + dy * dy); - pt.x = (pt.x + tr.x) * s; - pt.y = (pt.y + tr.y) * s; - - setPosition.call(this, dist, dx / dist, dy / dist, p0, p1, pt, me); - }); - }; - - function createEdgeWidthHandle(state, start, spacing) - { - return createEdgeHandle(state, ['width'], start, function(dist, nx, ny, p0, p1) - { - var w = state.shape.getEdgeWidth() * state.view.scale + spacing; - - return new mxPoint(p0.x + nx * dist / 4 + ny * w / 2, p0.y + ny * dist / 4 - nx * w / 2); - }, function(dist, nx, ny, p0, p1, pt) - { - var w = Math.sqrt(mxUtils.ptSegDistSq(p0.x, p0.y, p1.x, p1.y, pt.x, pt.y)); - state.style['width'] = Math.round(w * 2) / state.view.scale - spacing; - }); - }; - - function ptLineDistance(x1, y1, x2, y2, x0, y0) - { - return Math.abs((y2 - y1) * x0 - (x2 - x1) * y0 + x2 * y1 - y2 * x1) / Math.sqrt((y2 - y1) * (y2 - y1) + (x2 - x1) * (x2 - x1)); - } - - var handleFactory = { - 'link': function(state) - { - var spacing = 10; - - return [createEdgeWidthHandle(state, true, spacing), createEdgeWidthHandle(state, false, spacing)]; - }, - 'flexArrow': function(state) - { - // Do not use state.shape.startSize/endSize since it is cached - var tol = state.view.graph.gridSize / state.view.scale; - var handles = []; - - if (mxUtils.getValue(state.style, mxConstants.STYLE_STARTARROW, mxConstants.NONE) != mxConstants.NONE) - { - handles.push(createEdgeHandle(state, ['width', mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE], true, function(dist, nx, ny, p0, p1) - { - var w = (state.shape.getEdgeWidth() - state.shape.strokewidth) * state.view.scale; - var l = mxUtils.getNumber(state.style, mxConstants.STYLE_STARTSIZE, mxConstants.ARROW_SIZE / 5) * 3 * state.view.scale; - - return new mxPoint(p0.x + nx * (l + state.shape.strokewidth * state.view.scale) + ny * w / 2, - p0.y + ny * (l + state.shape.strokewidth * state.view.scale) - nx * w / 2); - }, function(dist, nx, ny, p0, p1, pt, me) - { - var w = Math.sqrt(mxUtils.ptSegDistSq(p0.x, p0.y, p1.x, p1.y, pt.x, pt.y)); - var l = mxUtils.ptLineDist(p0.x, p0.y, p0.x + ny, p0.y - nx, pt.x, pt.y); - - state.style[mxConstants.STYLE_STARTSIZE] = Math.round((l - state.shape.strokewidth) * 100 / 3) / 100 / state.view.scale; - state.style['width'] = Math.round(w * 2) / state.view.scale; - - // Applies to opposite side - if (mxEvent.isControlDown(me.getEvent())) - { - state.style[mxConstants.STYLE_ENDSIZE] = state.style[mxConstants.STYLE_STARTSIZE]; - } - - // Snaps to end geometry - if (!mxEvent.isAltDown(me.getEvent())) - { - if (Math.abs(parseFloat(state.style[mxConstants.STYLE_STARTSIZE]) - parseFloat(state.style[mxConstants.STYLE_ENDSIZE])) < tol / 6) - { - state.style[mxConstants.STYLE_STARTSIZE] = state.style[mxConstants.STYLE_ENDSIZE]; - } - } - })); - - handles.push(createEdgeHandle(state, ['startWidth', 'endWidth', mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE], true, function(dist, nx, ny, p0, p1) - { - var w = (state.shape.getStartArrowWidth() - state.shape.strokewidth) * state.view.scale; - var l = mxUtils.getNumber(state.style, mxConstants.STYLE_STARTSIZE, mxConstants.ARROW_SIZE / 5) * 3 * state.view.scale; - - return new mxPoint(p0.x + nx * (l + state.shape.strokewidth * state.view.scale) + ny * w / 2, - p0.y + ny * (l + state.shape.strokewidth * state.view.scale) - nx * w / 2); - }, function(dist, nx, ny, p0, p1, pt, me) - { - var w = Math.sqrt(mxUtils.ptSegDistSq(p0.x, p0.y, p1.x, p1.y, pt.x, pt.y)); - var l = mxUtils.ptLineDist(p0.x, p0.y, p0.x + ny, p0.y - nx, pt.x, pt.y); - - state.style[mxConstants.STYLE_STARTSIZE] = Math.round((l - state.shape.strokewidth) * 100 / 3) / 100 / state.view.scale; - state.style['startWidth'] = Math.max(0, Math.round(w * 2) - state.shape.getEdgeWidth()) / state.view.scale; - - // Applies to opposite side - if (mxEvent.isControlDown(me.getEvent())) - { - state.style[mxConstants.STYLE_ENDSIZE] = state.style[mxConstants.STYLE_STARTSIZE]; - state.style['endWidth'] = state.style['startWidth']; - } - - // Snaps to endWidth - if (!mxEvent.isAltDown(me.getEvent())) - { - if (Math.abs(parseFloat(state.style[mxConstants.STYLE_STARTSIZE]) - parseFloat(state.style[mxConstants.STYLE_ENDSIZE])) < tol / 6) - { - state.style[mxConstants.STYLE_STARTSIZE] = state.style[mxConstants.STYLE_ENDSIZE]; - } - - if (Math.abs(parseFloat(state.style['startWidth']) - parseFloat(state.style['endWidth'])) < tol) - { - state.style['startWidth'] = state.style['endWidth']; - } - } - })); - } - - if (mxUtils.getValue(state.style, mxConstants.STYLE_ENDARROW, mxConstants.NONE) != mxConstants.NONE) - { - handles.push(createEdgeHandle(state, ['width', mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE], false, function(dist, nx, ny, p0, p1) - { - var w = (state.shape.getEdgeWidth() - state.shape.strokewidth) * state.view.scale; - var l = mxUtils.getNumber(state.style, mxConstants.STYLE_ENDSIZE, mxConstants.ARROW_SIZE / 5) * 3 * state.view.scale; - - return new mxPoint(p0.x + nx * (l + state.shape.strokewidth * state.view.scale) - ny * w / 2, - p0.y + ny * (l + state.shape.strokewidth * state.view.scale) + nx * w / 2); - }, function(dist, nx, ny, p0, p1, pt, me) - { - var w = Math.sqrt(mxUtils.ptSegDistSq(p0.x, p0.y, p1.x, p1.y, pt.x, pt.y)); - var l = mxUtils.ptLineDist(p0.x, p0.y, p0.x + ny, p0.y - nx, pt.x, pt.y); - - state.style[mxConstants.STYLE_ENDSIZE] = Math.round((l - state.shape.strokewidth) * 100 / 3) / 100 / state.view.scale; - state.style['width'] = Math.round(w * 2) / state.view.scale; - - // Applies to opposite side - if (mxEvent.isControlDown(me.getEvent())) - { - state.style[mxConstants.STYLE_STARTSIZE] = state.style[mxConstants.STYLE_ENDSIZE]; - } - - // Snaps to start geometry - if (!mxEvent.isAltDown(me.getEvent())) - { - if (Math.abs(parseFloat(state.style[mxConstants.STYLE_ENDSIZE]) - parseFloat(state.style[mxConstants.STYLE_STARTSIZE])) < tol / 6) - { - state.style[mxConstants.STYLE_ENDSIZE] = state.style[mxConstants.STYLE_STARTSIZE]; - } - } - })); - - handles.push(createEdgeHandle(state, ['startWidth', 'endWidth', mxConstants.STYLE_STARTSIZE, mxConstants.STYLE_ENDSIZE], false, function(dist, nx, ny, p0, p1) - { - var w = (state.shape.getEndArrowWidth() - state.shape.strokewidth) * state.view.scale; - var l = mxUtils.getNumber(state.style, mxConstants.STYLE_ENDSIZE, mxConstants.ARROW_SIZE / 5) * 3 * state.view.scale; - - return new mxPoint(p0.x + nx * (l + state.shape.strokewidth * state.view.scale) - ny * w / 2, - p0.y + ny * (l + state.shape.strokewidth * state.view.scale) + nx * w / 2); - }, function(dist, nx, ny, p0, p1, pt, me) - { - var w = Math.sqrt(mxUtils.ptSegDistSq(p0.x, p0.y, p1.x, p1.y, pt.x, pt.y)); - var l = mxUtils.ptLineDist(p0.x, p0.y, p0.x + ny, p0.y - nx, pt.x, pt.y); - - state.style[mxConstants.STYLE_ENDSIZE] = Math.round((l - state.shape.strokewidth) * 100 / 3) / 100 / state.view.scale; - state.style['endWidth'] = Math.max(0, Math.round(w * 2) - state.shape.getEdgeWidth()) / state.view.scale; - - // Applies to opposite side - if (mxEvent.isControlDown(me.getEvent())) - { - state.style[mxConstants.STYLE_STARTSIZE] = state.style[mxConstants.STYLE_ENDSIZE]; - state.style['startWidth'] = state.style['endWidth']; - } - - // Snaps to start geometry - if (!mxEvent.isAltDown(me.getEvent())) - { - if (Math.abs(parseFloat(state.style[mxConstants.STYLE_ENDSIZE]) - parseFloat(state.style[mxConstants.STYLE_STARTSIZE])) < tol / 6) - { - state.style[mxConstants.STYLE_ENDSIZE] = state.style[mxConstants.STYLE_STARTSIZE]; - } - - if (Math.abs(parseFloat(state.style['endWidth']) - parseFloat(state.style['startWidth'])) < tol) - { - state.style['endWidth'] = state.style['startWidth']; - } - } - })); - } - - return handles; - }, - 'swimlane': function(state) - { - var handles = [createHandle(state, [mxConstants.STYLE_STARTSIZE], function(bounds) - { - var size = parseFloat(mxUtils.getValue(state.style, mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_STARTSIZE)); - - if (mxUtils.getValue(state.style, mxConstants.STYLE_HORIZONTAL, 1) == 1) - { - return new mxPoint(bounds.getCenterX(), bounds.y + Math.max(0, Math.min(bounds.height, size))); - } - else - { - return new mxPoint(bounds.x + Math.max(0, Math.min(bounds.width, size)), bounds.getCenterY()); - } - }, function(bounds, pt) - { - state.style[mxConstants.STYLE_STARTSIZE] = - (mxUtils.getValue(this.state.style, mxConstants.STYLE_HORIZONTAL, 1) == 1) ? - Math.round(Math.max(0, Math.min(bounds.height, pt.y - bounds.y))) : - Math.round(Math.max(0, Math.min(bounds.width, pt.x - bounds.x))); - })]; - - if (mxUtils.getValue(state.style, mxConstants.STYLE_ROUNDED)) - { - var size = parseFloat(mxUtils.getValue(state.style, mxConstants.STYLE_STARTSIZE, mxConstants.DEFAULT_STARTSIZE)); - handles.push(createArcHandle(state, size / 2)); - } - - return handles; - }, - 'label': createArcHandleFunction(), - 'ext': createArcHandleFunction(), - 'rectangle': createArcHandleFunction(), - 'triangle': createArcHandleFunction(), - 'rhombus': createArcHandleFunction(), - 'umlLifeline': function(state) - { - return [createHandle(state, ['size'], function(bounds) - { - var size = Math.max(0, Math.min(bounds.height, parseFloat(mxUtils.getValue(this.state.style, 'size', UmlLifeline.prototype.size)))); - - return new mxPoint(bounds.getCenterX(), bounds.y + size); - }, function(bounds, pt) - { - this.state.style['size'] = Math.round(Math.max(0, Math.min(bounds.height, pt.y - bounds.y))); - }, false)]; - }, - 'umlFrame': function(state) - { - var handles = [createHandle(state, ['width', 'height'], function(bounds) - { - var w0 = Math.max(UmlFrame.prototype.corner, Math.min(bounds.width, mxUtils.getValue(this.state.style, 'width', UmlFrame.prototype.width))); - var h0 = Math.max(UmlFrame.prototype.corner * 1.5, Math.min(bounds.height, mxUtils.getValue(this.state.style, 'height', UmlFrame.prototype.height))); - - return new mxPoint(bounds.x + w0, bounds.y + h0); - }, function(bounds, pt) - { - this.state.style['width'] = Math.round(Math.max(UmlFrame.prototype.corner, Math.min(bounds.width, pt.x - bounds.x))); - this.state.style['height'] = Math.round(Math.max(UmlFrame.prototype.corner * 1.5, Math.min(bounds.height, pt.y - bounds.y))); - }, false)]; - - return handles; - }, - 'process': function(state) - { - var handles = [createHandle(state, ['size'], function(bounds) - { - var size = Math.max(0, Math.min(0.5, parseFloat(mxUtils.getValue(this.state.style, 'size', ProcessShape.prototype.size)))); - - return new mxPoint(bounds.x + bounds.width * size, bounds.y + bounds.height / 4); - }, function(bounds, pt) - { - this.state.style['size'] = Math.max(0, Math.min(0.5, (pt.x - bounds.x) / bounds.width)); - })]; - - if (mxUtils.getValue(state.style, mxConstants.STYLE_ROUNDED, false)) - { - handles.push(createArcHandle(state)); - } - - return handles; - }, - 'cross': function(state) - { - return [createHandle(state, ['size'], function(bounds) - { - var m = Math.min(bounds.width, bounds.height); - var size = Math.max(0, Math.min(1, mxUtils.getValue(this.state.style, 'size', CrossShape.prototype.size))) * m / 2; - - return new mxPoint(bounds.getCenterX() - size, bounds.getCenterY() - size); - }, function(bounds, pt) - { - var m = Math.min(bounds.width, bounds.height); - this.state.style['size'] = Math.max(0, Math.min(1, Math.min((Math.max(0, bounds.getCenterY() - pt.y) / m) * 2, - (Math.max(0, bounds.getCenterX() - pt.x) / m) * 2))); - })]; - }, - 'note': function(state) - { - return [createHandle(state, ['size'], function(bounds) - { - var size = Math.max(0, Math.min(bounds.width, Math.min(bounds.height, parseFloat( - mxUtils.getValue(this.state.style, 'size', NoteShape.prototype.size))))); - - return new mxPoint(bounds.x + bounds.width - size, bounds.y + size); - }, function(bounds, pt) - { - this.state.style['size'] = Math.round(Math.max(0, Math.min(Math.min(bounds.width, bounds.x + bounds.width - pt.x), - Math.min(bounds.height, pt.y - bounds.y)))); - })]; - }, - 'manualInput': function(state) - { - var handles = [createHandle(state, ['size'], function(bounds) - { - var size = Math.max(0, Math.min(bounds.height, mxUtils.getValue(this.state.style, 'size', ManualInputShape.prototype.size))); - - return new mxPoint(bounds.x + bounds.width / 4, bounds.y + size * 3 / 4); - }, function(bounds, pt) - { - this.state.style['size'] = Math.round(Math.max(0, Math.min(bounds.height, (pt.y - bounds.y) * 4 / 3))); - })]; - - if (mxUtils.getValue(state.style, mxConstants.STYLE_ROUNDED, false)) - { - handles.push(createArcHandle(state)); - } - - return handles; - }, - 'dataStorage': function(state) - { - return [createHandle(state, ['size'], function(bounds) - { - var size = Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.state.style, 'size', DataStorageShape.prototype.size)))); - - return new mxPoint(bounds.x + (1 - size) * bounds.width, bounds.getCenterY()); - }, function(bounds, pt) - { - this.state.style['size'] = Math.max(0, Math.min(1, (bounds.x + bounds.width - pt.x) / bounds.width)); - })]; - }, - 'callout': function(state) - { - var handles = [createHandle(state, ['size', 'position'], function(bounds) - { - var size = Math.max(0, Math.min(bounds.height, mxUtils.getValue(this.state.style, 'size', CalloutShape.prototype.size))); - var position = Math.max(0, Math.min(1, mxUtils.getValue(this.state.style, 'position', CalloutShape.prototype.position))); - var base = Math.max(0, Math.min(bounds.width, mxUtils.getValue(this.state.style, 'base', CalloutShape.prototype.base))); - - return new mxPoint(bounds.x + position * bounds.width, bounds.y + bounds.height - size); - }, function(bounds, pt) - { - var base = Math.max(0, Math.min(bounds.width, mxUtils.getValue(this.state.style, 'base', CalloutShape.prototype.base))); - this.state.style['size'] = Math.round(Math.max(0, Math.min(bounds.height, bounds.y + bounds.height - pt.y))); - this.state.style['position'] = Math.round(Math.max(0, Math.min(1, (pt.x - bounds.x) / bounds.width)) * 100) / 100; - }), createHandle(state, ['position2'], function(bounds) - { - var position2 = Math.max(0, Math.min(1, mxUtils.getValue(this.state.style, 'position2', CalloutShape.prototype.position2))); - - return new mxPoint(bounds.x + position2 * bounds.width, bounds.y + bounds.height); - }, function(bounds, pt) - { - this.state.style['position2'] = Math.round(Math.max(0, Math.min(1, (pt.x - bounds.x) / bounds.width)) * 100) / 100; - }), createHandle(state, ['base'], function(bounds) - { - var size = Math.max(0, Math.min(bounds.height, mxUtils.getValue(this.state.style, 'size', CalloutShape.prototype.size))); - var position = Math.max(0, Math.min(1, mxUtils.getValue(this.state.style, 'position', CalloutShape.prototype.position))); - var base = Math.max(0, Math.min(bounds.width, mxUtils.getValue(this.state.style, 'base', CalloutShape.prototype.base))); - - return new mxPoint(bounds.x + Math.min(bounds.width, position * bounds.width + base), bounds.y + bounds.height - size); - }, function(bounds, pt) - { - var position = Math.max(0, Math.min(1, mxUtils.getValue(this.state.style, 'position', CalloutShape.prototype.position))); - - this.state.style['base'] = Math.round(Math.max(0, Math.min(bounds.width, pt.x - bounds.x - position * bounds.width))); - })]; - - if (mxUtils.getValue(state.style, mxConstants.STYLE_ROUNDED, false)) - { - handles.push(createArcHandle(state)); - } - - return handles; - }, - 'internalStorage': function(state) - { - var handles = [createHandle(state, ['dx', 'dy'], function(bounds) - { - var dx = Math.max(0, Math.min(bounds.width, mxUtils.getValue(this.state.style, 'dx', InternalStorageShape.prototype.dx))); - var dy = Math.max(0, Math.min(bounds.height, mxUtils.getValue(this.state.style, 'dy', InternalStorageShape.prototype.dy))); - - return new mxPoint(bounds.x + dx, bounds.y + dy); - }, function(bounds, pt) - { - this.state.style['dx'] = Math.round(Math.max(0, Math.min(bounds.width, pt.x - bounds.x))); - this.state.style['dy'] = Math.round(Math.max(0, Math.min(bounds.height, pt.y - bounds.y))); - })]; - - if (mxUtils.getValue(state.style, mxConstants.STYLE_ROUNDED, false)) - { - handles.push(createArcHandle(state)); - } - - return handles; - }, - 'corner': function(state) - { - return [createHandle(state, ['dx', 'dy'], function(bounds) - { - var dx = Math.max(0, Math.min(bounds.width, mxUtils.getValue(this.state.style, 'dx', CornerShape.prototype.dx))); - var dy = Math.max(0, Math.min(bounds.height, mxUtils.getValue(this.state.style, 'dy', CornerShape.prototype.dy))); - - return new mxPoint(bounds.x + dx, bounds.y + dy); - }, function(bounds, pt) - { - this.state.style['dx'] = Math.round(Math.max(0, Math.min(bounds.width, pt.x - bounds.x))); - this.state.style['dy'] = Math.round(Math.max(0, Math.min(bounds.height, pt.y - bounds.y))); - })]; - }, - 'tee': function(state) - { - return [createHandle(state, ['dx', 'dy'], function(bounds) - { - var dx = Math.max(0, Math.min(bounds.width, mxUtils.getValue(this.state.style, 'dx', TeeShape.prototype.dx))); - var dy = Math.max(0, Math.min(bounds.height, mxUtils.getValue(this.state.style, 'dy', TeeShape.prototype.dy))); - - return new mxPoint(bounds.x + (bounds.width + dx) / 2, bounds.y + dy); - }, function(bounds, pt) - { - this.state.style['dx'] = Math.round(Math.max(0, Math.min(bounds.width / 2, (pt.x - bounds.x - bounds.width / 2)) * 2)); - this.state.style['dy'] = Math.round(Math.max(0, Math.min(bounds.height, pt.y - bounds.y))); - })]; - }, - 'singleArrow': createArrowHandleFunction(1), - 'doubleArrow': createArrowHandleFunction(0.5), - 'folder': function(state) - { - return [createHandle(state, ['tabWidth', 'tabHeight'], function(bounds) - { - var tw = Math.max(0, Math.min(bounds.width, mxUtils.getValue(this.state.style, 'tabWidth', FolderShape.prototype.tabWidth))); - var th = Math.max(0, Math.min(bounds.height, mxUtils.getValue(this.state.style, 'tabHeight', FolderShape.prototype.tabHeight))); - - if (mxUtils.getValue(this.state.style, 'tabPosition', FolderShape.prototype.tabPosition) == mxConstants.ALIGN_RIGHT) - { - tw = bounds.width - tw; - } - - return new mxPoint(bounds.x + tw, bounds.y + th); - }, function(bounds, pt) - { - var tw = Math.max(0, Math.min(bounds.width, pt.x - bounds.x)); - - if (mxUtils.getValue(this.state.style, 'tabPosition', FolderShape.prototype.tabPosition) == mxConstants.ALIGN_RIGHT) - { - tw = bounds.width - tw; - } - - this.state.style['tabWidth'] = Math.round(tw); - this.state.style['tabHeight'] = Math.round(Math.max(0, Math.min(bounds.height, pt.y - bounds.y))); - })]; - }, - 'document': function(state) - { - return [createHandle(state, ['size'], function(bounds) - { - var size = Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.state.style, 'size', DocumentShape.prototype.size)))); - - return new mxPoint(bounds.x + 3 * bounds.width / 4, bounds.y + (1 - size) * bounds.height); - }, function(bounds, pt) - { - this.state.style['size'] = Math.max(0, Math.min(1, (bounds.y + bounds.height - pt.y) / bounds.height)); - })]; - }, - 'tape': function(state) - { - return [createHandle(state, ['size'], function(bounds) - { - var size = Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.state.style, 'size', TapeShape.prototype.size)))); - - return new mxPoint(bounds.getCenterX(), bounds.y + size * bounds.height / 2); - }, function(bounds, pt) - { - this.state.style['size'] = Math.max(0, Math.min(1, ((pt.y - bounds.y) / bounds.height) * 2)); - })]; - }, - 'offPageConnector': function(state) - { - return [createHandle(state, ['size'], function(bounds) - { - var size = Math.max(0, Math.min(1, parseFloat(mxUtils.getValue(this.state.style, 'size', OffPageConnectorShape.prototype.size)))); - - return new mxPoint(bounds.getCenterX(), bounds.y + (1 - size) * bounds.height); - }, function(bounds, pt) - { - this.state.style['size'] = Math.max(0, Math.min(1, (bounds.y + bounds.height - pt.y) / bounds.height)); - })]; - }, - 'step': createDisplayHandleFunction(StepShape.prototype.size, true, null, true, StepShape.prototype.fixedSize), - 'hexagon': createDisplayHandleFunction(HexagonShape.prototype.size, true, 0.5, true), - 'curlyBracket': createDisplayHandleFunction(CurlyBracketShape.prototype.size, false), - 'display': createDisplayHandleFunction(DisplayShape.prototype.size, false), - 'cube': createCubeHandleFunction(1, CubeShape.prototype.size, false), - 'card': createCubeHandleFunction(0.5, CardShape.prototype.size, true), - 'loopLimit': createCubeHandleFunction(0.5, LoopLimitShape.prototype.size, true), - 'trapezoid': createTrapezoidHandleFunction(0.5), - 'parallelogram': createTrapezoidHandleFunction(1) - }; - - // Exposes custom handles - Graph.createHandle = createHandle; - Graph.handleFactory = handleFactory; - - mxVertexHandler.prototype.createCustomHandles = function() - { - // Not rotatable means locked - if (this.state.view.graph.getSelectionCount() == 1) - { - if (this.graph.isCellRotatable(this.state.cell)) - // LATER: Make locked state independent of rotatable flag, fix toggle if default is false - //if (this.graph.isCellResizable(this.state.cell) || this.graph.isCellMovable(this.state.cell)) - { - var name = this.state.style['shape']; - - if (mxCellRenderer.defaultShapes[name] == null && - mxStencilRegistry.getStencil(name) == null) - { - name = mxConstants.SHAPE_RECTANGLE; - } - - var fn = handleFactory[name]; - - if (fn == null && this.state.shape != null && this.state.shape.isRoundable()) - { - fn = handleFactory[mxConstants.SHAPE_RECTANGLE]; - } - - if (fn != null) - { - return fn(this.state); - } - } - } - - return null; - }; - - mxEdgeHandler.prototype.createCustomHandles = function() - { - if (this.state.view.graph.getSelectionCount() == 1) - { - var name = this.state.style['shape']; - - if (mxCellRenderer.defaultShapes[name] == null && - mxStencilRegistry.getStencil(name) == null) - { - name = mxConstants.SHAPE_CONNECTOR; - } - - var fn = handleFactory[name]; - - if (fn != null) - { - return fn(this.state); - } - } - - return null; - } - } - else - { - // Dummy entries to avoid NPE in embed mode - Graph.createHandle = function() {}; - Graph.handleFactory = {}; - } - - var isoHVector = new mxPoint(1, 0); - var isoVVector = new mxPoint(1, 0); - - var alpha1 = mxUtils.toRadians(-30); - - var cos1 = Math.cos(alpha1); - var sin1 = Math.sin(alpha1); - - isoHVector = mxUtils.getRotatedPoint(isoHVector, cos1, sin1); - - var alpha2 = mxUtils.toRadians(-150); - - var cos2 = Math.cos(alpha2); - var sin2 = Math.sin(alpha2); - - isoVVector = mxUtils.getRotatedPoint(isoVVector, cos2, sin2); - - mxEdgeStyle.IsometricConnector = function (state, source, target, points, result) - { - var view = state.view; - var pt = (points != null && points.length > 0) ? points[0] : null; - var pts = state.absolutePoints; - var p0 = pts[0]; - var pe = pts[pts.length-1]; - - if (pt != null) - { - pt = view.transformControlPoint(state, pt); - } - - if (p0 == null) - { - if (source != null) - { - p0 = new mxPoint(source.getCenterX(), source.getCenterY()); - } - } - - if (pe == null) - { - if (target != null) - { - pe = new mxPoint(target.getCenterX(), target.getCenterY()); - } - } - - var a1 = isoHVector.x; - var a2 = isoHVector.y; - - var b1 = isoVVector.x; - var b2 = isoVVector.y; - - var elbow = mxUtils.getValue(state.style, 'elbow', 'horizontal') == 'horizontal'; - - if (pe != null && p0 != null) - { - var last = p0; - - function isoLineTo(x, y, ignoreFirst) - { - var c1 = x - last.x; - var c2 = y - last.y; - - // Solves for isometric base vectors - var h = (b2 * c1 - b1 * c2) / (a1 * b2 - a2 * b1); - var v = (a2 * c1 - a1 * c2) / (a2 * b1 - a1 * b2); - - if (elbow) - { - if (ignoreFirst) - { - last = new mxPoint(last.x + a1 * h, last.y + a2 * h); - result.push(last); - } - - last = new mxPoint(last.x + b1 * v, last.y + b2 * v); - result.push(last); - } - else - { - if (ignoreFirst) - { - last = new mxPoint(last.x + b1 * v, last.y + b2 * v); - result.push(last); - } - - last = new mxPoint(last.x + a1 * h, last.y + a2 * h); - result.push(last); - } - }; - - if (pt == null) - { - pt = new mxPoint(p0.x + (pe.x - p0.x) / 2, p0.y + (pe.y - p0.y) / 2); - } - - isoLineTo(pt.x, pt.y, true); - isoLineTo(pe.x, pe.y, false); - } - }; - - mxStyleRegistry.putValue('isometricEdgeStyle', mxEdgeStyle.IsometricConnector); - - var graphCreateEdgeHandler = Graph.prototype.createEdgeHandler; - Graph.prototype.createEdgeHandler = function(state, edgeStyle) - { - if (edgeStyle == mxEdgeStyle.IsometricConnector) - { - var handler = new mxElbowEdgeHandler(state); - handler.snapToTerminals = false; - - return handler; - } - - return graphCreateEdgeHandler.apply(this, arguments); - }; - - // Defines connection points for all shapes - IsoRectangleShape.prototype.constraints = []; - IsoCubeShape.prototype.constraints = []; - CalloutShape.prototype.constraints = []; - mxRectangleShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.25, 0), true), - new mxConnectionConstraint(new mxPoint(0.5, 0), true), - new mxConnectionConstraint(new mxPoint(0.75, 0), true), - new mxConnectionConstraint(new mxPoint(0, 0.25), true), - new mxConnectionConstraint(new mxPoint(0, 0.5), true), - new mxConnectionConstraint(new mxPoint(0, 0.75), true), - new mxConnectionConstraint(new mxPoint(1, 0.25), true), - new mxConnectionConstraint(new mxPoint(1, 0.5), true), - new mxConnectionConstraint(new mxPoint(1, 0.75), true), - new mxConnectionConstraint(new mxPoint(0.25, 1), true), - new mxConnectionConstraint(new mxPoint(0.5, 1), true), - new mxConnectionConstraint(new mxPoint(0.75, 1), true)]; - mxEllipse.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0), true), new mxConnectionConstraint(new mxPoint(1, 0), true), - new mxConnectionConstraint(new mxPoint(0, 1), true), new mxConnectionConstraint(new mxPoint(1, 1), true), - new mxConnectionConstraint(new mxPoint(0.5, 0), true), new mxConnectionConstraint(new mxPoint(0.5, 1), true), - new mxConnectionConstraint(new mxPoint(0, 0.5), true), new mxConnectionConstraint(new mxPoint(1, 0.5))]; - mxLabel.prototype.constraints = mxRectangleShape.prototype.constraints; - mxImageShape.prototype.constraints = mxRectangleShape.prototype.constraints; - mxSwimlane.prototype.constraints = mxRectangleShape.prototype.constraints; - PlusShape.prototype.constraints = mxRectangleShape.prototype.constraints; - NoteShape.prototype.constraints = mxRectangleShape.prototype.constraints; - CardShape.prototype.constraints = mxRectangleShape.prototype.constraints; - CubeShape.prototype.constraints = mxRectangleShape.prototype.constraints; - FolderShape.prototype.constraints = mxRectangleShape.prototype.constraints; - InternalStorageShape.prototype.constraints = mxRectangleShape.prototype.constraints; - DataStorageShape.prototype.constraints = mxRectangleShape.prototype.constraints; - TapeDataShape.prototype.constraints = mxEllipse.prototype.constraints; - OrEllipseShape.prototype.constraints = mxEllipse.prototype.constraints; - SumEllipseShape.prototype.constraints = mxEllipse.prototype.constraints; - LineEllipseShape.prototype.constraints = mxEllipse.prototype.constraints; - ManualInputShape.prototype.constraints = mxRectangleShape.prototype.constraints; - DelayShape.prototype.constraints = mxRectangleShape.prototype.constraints; - DisplayShape.prototype.constraints = mxRectangleShape.prototype.constraints; - LoopLimitShape.prototype.constraints = mxRectangleShape.prototype.constraints; - OffPageConnectorShape.prototype.constraints = mxRectangleShape.prototype.constraints; - mxCylinder.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.15, 0.05), false), - new mxConnectionConstraint(new mxPoint(0.5, 0), true), - new mxConnectionConstraint(new mxPoint(0.85, 0.05), false), - new mxConnectionConstraint(new mxPoint(0, 0.3), true), - new mxConnectionConstraint(new mxPoint(0, 0.5), true), - new mxConnectionConstraint(new mxPoint(0, 0.7), true), - new mxConnectionConstraint(new mxPoint(1, 0.3), true), - new mxConnectionConstraint(new mxPoint(1, 0.5), true), - new mxConnectionConstraint(new mxPoint(1, 0.7), true), - new mxConnectionConstraint(new mxPoint(0.15, 0.95), false), - new mxConnectionConstraint(new mxPoint(0.5, 1), true), - new mxConnectionConstraint(new mxPoint(0.85, 0.95), false)]; - UmlActorShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.25, 0.1), false), - new mxConnectionConstraint(new mxPoint(0.5, 0), false), - new mxConnectionConstraint(new mxPoint(0.75, 0.1), false), - new mxConnectionConstraint(new mxPoint(0, 1/3), false), - new mxConnectionConstraint(new mxPoint(0, 1), false), - new mxConnectionConstraint(new mxPoint(1, 1/3), false), - new mxConnectionConstraint(new mxPoint(1, 1), false), - new mxConnectionConstraint(new mxPoint(0.5, 0.5), false)]; - ComponentShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.25, 0), true), - new mxConnectionConstraint(new mxPoint(0.5, 0), true), - new mxConnectionConstraint(new mxPoint(0.75, 0), true), - new mxConnectionConstraint(new mxPoint(0, 0.3), true), - new mxConnectionConstraint(new mxPoint(0, 0.7), true), - new mxConnectionConstraint(new mxPoint(1, 0.25), true), - new mxConnectionConstraint(new mxPoint(1, 0.5), true), - new mxConnectionConstraint(new mxPoint(1, 0.75), true), - new mxConnectionConstraint(new mxPoint(0.25, 1), true), - new mxConnectionConstraint(new mxPoint(0.5, 1), true), - new mxConnectionConstraint(new mxPoint(0.75, 1), true)]; - mxActor.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.5, 0), true), - new mxConnectionConstraint(new mxPoint(0.25, 0.2), false), - new mxConnectionConstraint(new mxPoint(0.1, 0.5), false), - new mxConnectionConstraint(new mxPoint(0, 0.75), true), - new mxConnectionConstraint(new mxPoint(0.75, 0.25), false), - new mxConnectionConstraint(new mxPoint(0.9, 0.5), false), - new mxConnectionConstraint(new mxPoint(1, 0.75), true), - new mxConnectionConstraint(new mxPoint(0.25, 1), true), - new mxConnectionConstraint(new mxPoint(0.5, 1), true), - new mxConnectionConstraint(new mxPoint(0.75, 1), true)]; - SwitchShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0), false), - new mxConnectionConstraint(new mxPoint(0.5, 0.25), false), - new mxConnectionConstraint(new mxPoint(1, 0), false), - new mxConnectionConstraint(new mxPoint(0.25, 0.5), false), - new mxConnectionConstraint(new mxPoint(0.75, 0.5), false), - new mxConnectionConstraint(new mxPoint(0, 1), false), - new mxConnectionConstraint(new mxPoint(0.5, 0.75), false), - new mxConnectionConstraint(new mxPoint(1, 1), false)]; - TapeShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0.35), false), - new mxConnectionConstraint(new mxPoint(0, 0.5), false), - new mxConnectionConstraint(new mxPoint(0, 0.65), false), - new mxConnectionConstraint(new mxPoint(1, 0.35), false), - new mxConnectionConstraint(new mxPoint(1, 0.5), false), - new mxConnectionConstraint(new mxPoint(1, 0.65), false), - new mxConnectionConstraint(new mxPoint(0.25, 1), false), - new mxConnectionConstraint(new mxPoint(0.75, 0), false)]; - StepShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.25, 0), true), - new mxConnectionConstraint(new mxPoint(0.5, 0), true), - new mxConnectionConstraint(new mxPoint(0.75, 0), true), - new mxConnectionConstraint(new mxPoint(0.25, 1), true), - new mxConnectionConstraint(new mxPoint(0.5, 1), true), - new mxConnectionConstraint(new mxPoint(0.75, 1), true), - new mxConnectionConstraint(new mxPoint(0, 0.25), true), - new mxConnectionConstraint(new mxPoint(0, 0.5), true), - new mxConnectionConstraint(new mxPoint(0, 0.75), true), - new mxConnectionConstraint(new mxPoint(1, 0.25), true), - new mxConnectionConstraint(new mxPoint(1, 0.5), true), - new mxConnectionConstraint(new mxPoint(1, 0.75), true)]; - mxLine.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0.5), false), - new mxConnectionConstraint(new mxPoint(0.25, 0.5), false), - new mxConnectionConstraint(new mxPoint(0.75, 0.5), false), - new mxConnectionConstraint(new mxPoint(1, 0.5), false)]; - LollipopShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.5, 0), false), - new mxConnectionConstraint(new mxPoint(0.5, 1), false)]; - mxDoubleEllipse.prototype.constraints = mxEllipse.prototype.constraints; - mxRhombus.prototype.constraints = mxEllipse.prototype.constraints; - mxTriangle.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0.25), true), - new mxConnectionConstraint(new mxPoint(0, 0.5), true), - new mxConnectionConstraint(new mxPoint(0, 0.75), true), - new mxConnectionConstraint(new mxPoint(0.5, 0), true), - new mxConnectionConstraint(new mxPoint(0.5, 1), true), - new mxConnectionConstraint(new mxPoint(1, 0.5), true)]; - mxHexagon.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.375, 0), true), - new mxConnectionConstraint(new mxPoint(0.5, 0), true), - new mxConnectionConstraint(new mxPoint(0.625, 0), true), - new mxConnectionConstraint(new mxPoint(0, 0.25), true), - new mxConnectionConstraint(new mxPoint(0, 0.5), true), - new mxConnectionConstraint(new mxPoint(0, 0.75), true), - new mxConnectionConstraint(new mxPoint(1, 0.25), true), - new mxConnectionConstraint(new mxPoint(1, 0.5), true), - new mxConnectionConstraint(new mxPoint(1, 0.75), true), - new mxConnectionConstraint(new mxPoint(0.375, 1), true), - new mxConnectionConstraint(new mxPoint(0.5, 1), true), - new mxConnectionConstraint(new mxPoint(0.625, 1), true)]; - mxCloud.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.25, 0.25), false), - new mxConnectionConstraint(new mxPoint(0.4, 0.1), false), - new mxConnectionConstraint(new mxPoint(0.16, 0.55), false), - new mxConnectionConstraint(new mxPoint(0.07, 0.4), false), - new mxConnectionConstraint(new mxPoint(0.31, 0.8), false), - new mxConnectionConstraint(new mxPoint(0.13, 0.77), false), - new mxConnectionConstraint(new mxPoint(0.8, 0.8), false), - new mxConnectionConstraint(new mxPoint(0.55, 0.95), false), - new mxConnectionConstraint(new mxPoint(0.875, 0.5), false), - new mxConnectionConstraint(new mxPoint(0.96, 0.7), false), - new mxConnectionConstraint(new mxPoint(0.625, 0.2), false), - new mxConnectionConstraint(new mxPoint(0.88, 0.25), false)]; - ParallelogramShape.prototype.constraints = mxRectangleShape.prototype.constraints; - TrapezoidShape.prototype.constraints = mxRectangleShape.prototype.constraints; - DocumentShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.25, 0), true), - new mxConnectionConstraint(new mxPoint(0.5, 0), true), - new mxConnectionConstraint(new mxPoint(0.75, 0), true), - new mxConnectionConstraint(new mxPoint(0, 0.25), true), - new mxConnectionConstraint(new mxPoint(0, 0.5), true), - new mxConnectionConstraint(new mxPoint(0, 0.75), true), - new mxConnectionConstraint(new mxPoint(1, 0.25), true), - new mxConnectionConstraint(new mxPoint(1, 0.5), true), - new mxConnectionConstraint(new mxPoint(1, 0.75), true)]; - mxArrow.prototype.constraints = null; - TeeShape.prototype.constraints = null; - CornerShape.prototype.constraints = null; - CrossbarShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0), false), - new mxConnectionConstraint(new mxPoint(0, 0.5), false), - new mxConnectionConstraint(new mxPoint(0, 1), false), - new mxConnectionConstraint(new mxPoint(0.25, 0.5), false), - new mxConnectionConstraint(new mxPoint(0.5, 0.5), false), - new mxConnectionConstraint(new mxPoint(0.75, 0.5), false), - new mxConnectionConstraint(new mxPoint(1, 0), false), - new mxConnectionConstraint(new mxPoint(1, 0.5), false), - new mxConnectionConstraint(new mxPoint(1, 1), false)]; - - SingleArrowShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0.5), false), - new mxConnectionConstraint(new mxPoint(1, 0.5), false)]; - DoubleArrowShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0.5), false), - new mxConnectionConstraint(new mxPoint(1, 0.5), false)]; - CrossShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0.5), false), - new mxConnectionConstraint(new mxPoint(1, 0.5), false), - new mxConnectionConstraint(new mxPoint(0.5, 0), false), - new mxConnectionConstraint(new mxPoint(0.5, 1), false)]; - UmlLifeline.prototype.constraints = null; - OrShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0.25), false), - new mxConnectionConstraint(new mxPoint(0, 0.5), false), - new mxConnectionConstraint(new mxPoint(0, 0.75), false), - new mxConnectionConstraint(new mxPoint(1, 0.5), false), - new mxConnectionConstraint(new mxPoint(0.7, 0.1), false), - new mxConnectionConstraint(new mxPoint(0.7, 0.9), false)]; - XorShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0.175, 0.25), false), - new mxConnectionConstraint(new mxPoint(0.25, 0.5), false), - new mxConnectionConstraint(new mxPoint(0.175, 0.75), false), - new mxConnectionConstraint(new mxPoint(1, 0.5), false), - new mxConnectionConstraint(new mxPoint(0.7, 0.1), false), - new mxConnectionConstraint(new mxPoint(0.7, 0.9), false)]; - RequiredInterfaceShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0.5), false), - new mxConnectionConstraint(new mxPoint(1, 0.5), false)]; - ProvidedRequiredInterfaceShape.prototype.constraints = [new mxConnectionConstraint(new mxPoint(0, 0.5), false), - new mxConnectionConstraint(new mxPoint(1, 0.5), false)]; -})(); diff --git a/media/grapheditor/js/Sidebar.js b/media/grapheditor/js/Sidebar.js deleted file mode 100644 index 9de847b183..0000000000 --- a/media/grapheditor/js/Sidebar.js +++ /dev/null @@ -1,3620 +0,0 @@ -/** - * Copyright (c) 2006-2012, JGraph Ltd - */ -/** - * Construcs a new sidebar for the given editor. - */ -function Sidebar(editorUi, container) -{ - this.editorUi = editorUi; - this.container = container; - this.palettes = new Object(); - this.taglist = new Object(); - this.showTooltips = true; - this.graph = editorUi.createTemporaryGraph(this.editorUi.editor.graph.getStylesheet()); - this.graph.cellRenderer.antiAlias = false; - this.graph.foldingEnabled = false; - this.graph.container.style.visibility = 'hidden'; - document.body.appendChild(this.graph.container); - - this.pointerUpHandler = mxUtils.bind(this, function() - { - this.showTooltips = true; - }); - - mxEvent.addListener(document, (mxClient.IS_POINTER) ? 'pointerup' : 'mouseup', this.pointerUpHandler); - - this.pointerDownHandler = mxUtils.bind(this, function() - { - this.showTooltips = false; - this.hideTooltip(); - }); - - mxEvent.addListener(document, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown', this.pointerDownHandler); - - this.pointerMoveHandler = mxUtils.bind(this, function(evt) - { - var src = mxEvent.getSource(evt); - - while (src != null) - { - if (src == this.currentElt) - { - return; - } - - src = src.parentNode; - } - - this.hideTooltip(); - }); - - mxEvent.addListener(document, (mxClient.IS_POINTER) ? 'pointermove' : 'mousemove', this.pointerMoveHandler); - - // Handles mouse leaving the window - this.pointerOutHandler = mxUtils.bind(this, function(evt) - { - if (evt.toElement == null && evt.relatedTarget == null) - { - this.hideTooltip(); - } - }); - - mxEvent.addListener(document, (mxClient.IS_POINTER) ? 'pointerout' : 'mouseout', this.pointerOutHandler); - - // Enables tooltips after scroll - mxEvent.addListener(container, 'scroll', mxUtils.bind(this, function() - { - this.showTooltips = true; - this.hideTooltip(); - })); - - this.init(); - - // Pre-fetches tooltip image - if (!mxClient.IS_SVG) - { - new Image().src = IMAGE_PATH + '/tooltip.png'; - } -}; - -/** - * Adds all palettes to the sidebar. - */ -Sidebar.prototype.init = function() -{ - var dir = STENCIL_PATH; - - this.addSearchPalette(true); - this.addGeneralPalette(true); - this.addMiscPalette(false); - this.addAdvancedPalette(false); - this.addBasicPalette(dir); - this.addStencilPalette('arrows', mxResources.get('arrows'), dir + '/arrows.xml', - ';whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#000000;strokeWidth=2'); - this.addUmlPalette(false); - this.addBpmnPalette(dir, false); - this.addStencilPalette('flowchart', 'Flowchart', dir + '/flowchart.xml', - ';whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#000000;strokeWidth=2'); - this.addImagePalette('clipart', mxResources.get('clipart'), dir + '/clipart/', '_128x128.png', - ['Earth_globe', 'Empty_Folder', 'Full_Folder', 'Gear', 'Lock', 'Software', 'Virus', 'Email', - 'Database', 'Router_Icon', 'iPad', 'iMac', 'Laptop', 'MacBook', 'Monitor_Tower', 'Printer', - 'Server_Tower', 'Workstation', 'Firewall_02', 'Wireless_Router_N', 'Credit_Card', - 'Piggy_Bank', 'Graph', 'Safe', 'Shopping_Cart', 'Suit1', 'Suit2', 'Suit3', 'Pilot1', - 'Worker1', 'Soldier1', 'Doctor1', 'Tech1', 'Security1', 'Telesales1'], null, - {'Wireless_Router_N': 'wireless router switch wap wifi access point wlan', - 'Router_Icon': 'router switch'}); -}; - -/** - * Sets the default font size. - */ -Sidebar.prototype.collapsedImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/collapsed.gif' : 'data:image/gif;base64,R0lGODlhDQANAIABAJmZmf///yH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDozNUQyRTJFNjZGNUYxMUU1QjZEOThCNDYxMDQ2MzNCQiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDozNUQyRTJFNzZGNUYxMUU1QjZEOThCNDYxMDQ2MzNCQiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjFERjc3MEUxNkY1RjExRTVCNkQ5OEI0NjEwNDYzM0JCIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjFERjc3MEUyNkY1RjExRTVCNkQ5OEI0NjEwNDYzM0JCIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAIfkEAQAAAQAsAAAAAA0ADQAAAhSMj6lrwAjcC1GyahV+dcZJgeIIFgA7'; - -/** - * Sets the default font size. - */ -Sidebar.prototype.expandedImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/expanded.gif' : 'data:image/gif;base64,R0lGODlhDQANAIABAJmZmf///yH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxREY3NzBERjZGNUYxMUU1QjZEOThCNDYxMDQ2MzNCQiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxREY3NzBFMDZGNUYxMUU1QjZEOThCNDYxMDQ2MzNCQiI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjFERjc3MERENkY1RjExRTVCNkQ5OEI0NjEwNDYzM0JCIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjFERjc3MERFNkY1RjExRTVCNkQ5OEI0NjEwNDYzM0JCIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAIfkEAQAAAQAsAAAAAA0ADQAAAhGMj6nL3QAjVHIu6azbvPtWAAA7'; - -/** - * Sets the default font size. - */ -Sidebar.prototype.tooltipImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/tooltip.png' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAbCAMAAAB7jU7LAAAACVBMVEX///+ZmZn///9Y2COLAAAAA3RSTlP//wDXyg1BAAAAOElEQVR42mXQMQ4AMAgDsWv//+iutcJmIQSk+9dJpVKpVCqVSqVSqZTdncWzF8/NeP7FkxWenPEDOnUBiL3jWx0AAAAASUVORK5CYII='; - -/** - * - */ -Sidebar.prototype.searchImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/search.png' : 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAwAAAAMCAYAAABWdVznAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAEaSURBVHjabNGxS5VxFIfxz71XaWuQUJCG/gCHhgTD9VpEETg4aMOlQRp0EoezObgcd220KQiXmpretTAHQRBdojlQEJyukPdt+b1ywfvAGc7wnHP4nlZd1yKijQW8xzNc4Su+ZOYfQ3T6/f4YNvEJYzjELXp4VVXVz263+7cR2niBxAFeZ2YPi3iHR/gYERPDwhpOsd6sz8x/mfkNG3iOlWFhFj8y89J9KvzGXER0GuEaD42mgwHqUtoljbcRsTBCeINpfM/MgZLKPpaxFxGbOCqDXmILN7hoJrTKH+axhxmcYRxP0MIDnOBDZv5q1XUNIuJxifJp+UNV7t7BFM6xeic0RMQ4Bpl5W/ol7GISx/eEUUTECrbx+f8A8xhiZht9zsgAAAAASUVORK5CYII='; - -/** - * - */ -Sidebar.prototype.dragPreviewBorder = '1px dashed black'; - -/** - * Specifies if tooltips should be visible. Default is true. - */ -Sidebar.prototype.enableTooltips = true; - -/** - * Specifies the delay for the tooltip. Default is 16 px. - */ -Sidebar.prototype.tooltipBorder = 16; - -/** - * Specifies the delay for the tooltip. Default is 300 ms. - */ -Sidebar.prototype.tooltipDelay = 300; - -/** - * Specifies the delay for the drop target icons. Default is 200 ms. - */ -Sidebar.prototype.dropTargetDelay = 200; - -/** - * Specifies the URL of the gear image. - */ -Sidebar.prototype.gearImage = STENCIL_PATH + '/clipart/Gear_128x128.png'; - -/** - * Specifies the width of the thumbnails. - */ -Sidebar.prototype.thumbWidth = 36; - -/** - * Specifies the height of the thumbnails. - */ -Sidebar.prototype.thumbHeight = 36; - -/** - * Specifies the padding for the thumbnails. Default is 3. - */ -Sidebar.prototype.thumbPadding = (document.documentMode >= 5) ? 0 : 1; - -/** - * Specifies the delay for the tooltip. Default is 2 px. - */ -Sidebar.prototype.thumbBorder = 2; - -/** - * Specifies the size of the sidebar titles. - */ -Sidebar.prototype.sidebarTitleSize = 9; - -/** - * Specifies if titles in the sidebar should be enabled. - */ -Sidebar.prototype.sidebarTitles = false; - -/** - * Specifies if titles in the tooltips should be enabled. - */ -Sidebar.prototype.tooltipTitles = true; - -/** - * Specifies if titles in the tooltips should be enabled. - */ -Sidebar.prototype.maxTooltipWidth = 400; - -/** - * Specifies if titles in the tooltips should be enabled. - */ -Sidebar.prototype.maxTooltipHeight = 400; - -/** - * Specifies if stencil files should be loaded and added to the search index - * when stencil palettes are added. If this is false then the stencil files - * are lazy-loaded when the palette is shown. - */ -Sidebar.prototype.addStencilsToIndex = true; - -/** - * Specifies the width for clipart images. Default is 80. - */ -Sidebar.prototype.defaultImageWidth = 80; - -/** - * Specifies the height for clipart images. Default is 80. - */ -Sidebar.prototype.defaultImageHeight = 80; - -/** - * Adds all palettes to the sidebar. - */ -Sidebar.prototype.getTooltipOffset = function() -{ - return new mxPoint(0, 0); -}; - -/** - * Adds all palettes to the sidebar. - */ -Sidebar.prototype.showTooltip = function(elt, cells, w, h, title, showLabel) -{ - if (this.enableTooltips && this.showTooltips) - { - if (this.currentElt != elt) - { - if (this.thread != null) - { - window.clearTimeout(this.thread); - this.thread = null; - } - - var show = mxUtils.bind(this, function() - { - // Lazy creation of the DOM nodes and graph instance - if (this.tooltip == null) - { - this.tooltip = document.createElement('div'); - this.tooltip.className = 'geSidebarTooltip'; - this.tooltip.style.zIndex = mxPopupMenu.prototype.zIndex - 1; - document.body.appendChild(this.tooltip); - - this.graph2 = new Graph(this.tooltip, null, null, this.editorUi.editor.graph.getStylesheet()); - this.graph2.resetViewOnRootChange = false; - this.graph2.foldingEnabled = false; - this.graph2.gridEnabled = false; - this.graph2.autoScroll = false; - this.graph2.setTooltips(false); - this.graph2.setConnectable(false); - this.graph2.setEnabled(false); - - if (!mxClient.IS_SVG) - { - this.graph2.view.canvas.style.position = 'relative'; - } - - this.tooltipImage = mxUtils.createImage(this.tooltipImage); - this.tooltipImage.className = 'geSidebarTooltipImage'; - this.tooltipImage.style.zIndex = mxPopupMenu.prototype.zIndex - 1; - this.tooltipImage.style.position = 'absolute'; - this.tooltipImage.style.width = '14px'; - this.tooltipImage.style.height = '27px'; - - document.body.appendChild(this.tooltipImage); - } - - this.graph2.model.clear(); - this.graph2.view.setTranslate(this.tooltipBorder, this.tooltipBorder); - - if (w > this.maxTooltipWidth || h > this.maxTooltipHeight) - { - this.graph2.view.scale = Math.round(Math.min(this.maxTooltipWidth / w, this.maxTooltipHeight / h) * 100) / 100; - } - else - { - this.graph2.view.scale = 1; - } - - this.tooltip.style.display = 'block'; - this.graph2.labelsVisible = (showLabel == null || showLabel); - var fo = mxClient.NO_FO; - mxClient.NO_FO = Editor.prototype.originalNoForeignObject; - this.graph2.addCells(cells); - mxClient.NO_FO = fo; - - var bounds = this.graph2.getGraphBounds(); - var width = bounds.width + 2 * this.tooltipBorder + 4; - var height = bounds.height + 2 * this.tooltipBorder; - - if (mxClient.IS_QUIRKS) - { - height += 4; - this.tooltip.style.overflow = 'hidden'; - } - else - { - this.tooltip.style.overflow = 'visible'; - } - - this.tooltipImage.style.visibility = 'visible'; - this.tooltip.style.width = width + 'px'; - - // Adds title for entry - if (this.tooltipTitles && title != null && title.length > 0) - { - if (this.tooltipTitle == null) - { - this.tooltipTitle = document.createElement('div'); - this.tooltipTitle.style.borderTop = '1px solid gray'; - this.tooltipTitle.style.textAlign = 'center'; - this.tooltipTitle.style.width = '100%'; - - // Oversize titles are cut-off currently. Should make tooltip wider later. - this.tooltipTitle.style.overflow = 'hidden'; - this.tooltipTitle.style.position = 'absolute'; - this.tooltipTitle.style.paddingTop = '6px'; - this.tooltipTitle.style.bottom = '6px'; - - this.tooltip.appendChild(this.tooltipTitle); - } - else - { - this.tooltipTitle.innerHTML = ''; - } - - this.tooltipTitle.style.display = ''; - mxUtils.write(this.tooltipTitle, title); - - var ddy = this.tooltipTitle.offsetHeight + 10; - height += ddy; - - if (mxClient.IS_SVG) - { - this.tooltipTitle.style.marginTop = (2 - ddy) + 'px'; - } - else - { - height -= 6; - this.tooltipTitle.style.top = (height - ddy) + 'px'; - } - } - else if (this.tooltipTitle != null && this.tooltipTitle.parentNode != null) - { - this.tooltipTitle.style.display = 'none'; - } - - this.tooltip.style.height = height + 'px'; - var x0 = -Math.round(bounds.x - this.tooltipBorder); - var y0 = -Math.round(bounds.y - this.tooltipBorder); - - var b = document.body; - var d = document.documentElement; - var off = this.getTooltipOffset(); - var bottom = Math.max(b.clientHeight || 0, d.clientHeight); - var left = this.container.clientWidth + this.editorUi.splitSize + 3 + this.editorUi.container.offsetLeft + off.x; - var top = Math.min(bottom - height - 20 /*status bar*/, Math.max(0, (this.editorUi.container.offsetTop + - this.container.offsetTop + elt.offsetTop - this.container.scrollTop - height / 2 + 16))) + off.y; - - if (mxClient.IS_SVG) - { - if (x0 != 0 || y0 != 0) - { - this.graph2.view.canvas.setAttribute('transform', 'translate(' + x0 + ',' + y0 + ')'); - } - else - { - this.graph2.view.canvas.removeAttribute('transform'); - } - } - else - { - this.graph2.view.drawPane.style.left = x0 + 'px'; - this.graph2.view.drawPane.style.top = y0 + 'px'; - } - - // Workaround for ignored position CSS style in IE9 - // (changes to relative without the following line) - this.tooltip.style.position = 'absolute'; - this.tooltip.style.left = left + 'px'; - this.tooltip.style.top = top + 'px'; - this.tooltipImage.style.left = (left - 13) + 'px'; - this.tooltipImage.style.top = (top + height / 2 - 13) + 'px'; - }); - - if (this.tooltip != null && this.tooltip.style.display != 'none') - { - show(); - } - else - { - this.thread = window.setTimeout(show, this.tooltipDelay); - } - - this.currentElt = elt; - } - } -}; - -/** - * Hides the current tooltip. - */ -Sidebar.prototype.hideTooltip = function() -{ - if (this.thread != null) - { - window.clearTimeout(this.thread); - this.thread = null; - } - - if (this.tooltip != null) - { - this.tooltip.style.display = 'none'; - this.tooltipImage.style.visibility = 'hidden'; - this.currentElt = null; - } -}; - -/** - * Hides the current tooltip. - */ -Sidebar.prototype.addDataEntry = function(tags, width, height, title, data) -{ - return this.addEntry(tags, mxUtils.bind(this, function() - { - return this.createVertexTemplateFromData(data, width, height, title); - })); -}; - -/** - * Hides the current tooltip. - */ -Sidebar.prototype.addEntry = function(tags, fn) -{ - if (this.taglist != null && tags != null && tags.length > 0) - { - // Replaces special characters - var tmp = tags.toLowerCase().replace(/[\/\,\(\)]/g, ' ').split(' '); - - var doAddEntry = mxUtils.bind(this, function(tag) - { - if (tag.length > 1) - { - var entry = this.taglist[tag]; - - if (typeof entry !== 'object') - { - entry = {entries: [], dict: new mxDictionary()}; - this.taglist[tag] = entry; - } - - // Ignores duplicates - if (entry.dict.get(fn) == null) - { - entry.dict.put(fn, fn); - entry.entries.push(fn); - } - } - }); - - for (var i = 0; i < tmp.length; i++) - { - doAddEntry(tmp[i]); - - // Adds additional entry with removed trailing numbers - var normalized = tmp[i].replace(/\.*\d*$/, ''); - - if (normalized != tmp[i]) - { - doAddEntry(normalized); - } - } - } - - return fn; -}; - -/** - * Adds shape search UI. - */ -Sidebar.prototype.searchEntries = function(searchTerms, count, page, success, error) -{ - if (this.taglist != null && searchTerms != null) - { - var tmp = searchTerms.toLowerCase().split(' '); - var dict = new mxDictionary(); - var max = (page + 1) * count; - var results = []; - var index = 0; - - for (var i = 0; i < tmp.length; i++) - { - if (tmp[i].length > 0) - { - var entry = this.taglist[tmp[i]]; - var tmpDict = new mxDictionary(); - - if (entry != null) - { - var arr = entry.entries; - results = []; - - for (var j = 0; j < arr.length; j++) - { - var entry = arr[j]; - - // NOTE Array does not contain duplicates - if ((index == 0) == (dict.get(entry) == null)) - { - tmpDict.put(entry, entry); - results.push(entry); - - if (i == tmp.length - 1 && results.length == max) - { - success(results.slice(page * count, max), max, true, tmp); - - return; - } - } - } - } - else - { - results = []; - } - - dict = tmpDict; - index++; - } - } - - var len = results.length; - success(results.slice(page * count, (page + 1) * count), len, false, tmp); - } - else - { - success([], null, null, tmp); - } -}; - -/** - * Adds shape search UI. - */ -Sidebar.prototype.filterTags = function(tags) -{ - if (tags != null) - { - var arr = tags.split(' '); - var result = []; - var hash = {}; - - // Ignores tags with leading numbers, strips trailing numbers - for (var i = 0; i < arr.length; i++) - { - // Removes duplicates - if (hash[arr[i]] == null) - { - hash[arr[i]] = '1'; - result.push(arr[i]); - } - } - - return result.join(' '); - } - - return null; -}; - -/** - * Adds the general palette to the sidebar. - */ -Sidebar.prototype.cloneCell = function(cell, value) -{ - var clone = cell.clone(); - - if (value != null) - { - clone.value = value; - } - - return clone; -}; - -/** - * Adds shape search UI. - */ -Sidebar.prototype.addSearchPalette = function(expand) -{ - var elt = document.createElement('div'); - elt.style.visibility = 'hidden'; - this.container.appendChild(elt); - - var div = document.createElement('div'); - div.className = 'geSidebar'; - div.style.boxSizing = 'border-box'; - div.style.overflow = 'hidden'; - div.style.width = '100%'; - div.style.padding = '8px'; - div.style.paddingTop = '14px'; - div.style.paddingBottom = '0px'; - - if (!expand) - { - div.style.display = 'none'; - } - - var inner = document.createElement('div'); - inner.style.whiteSpace = 'nowrap'; - inner.style.textOverflow = 'clip'; - inner.style.paddingBottom = '8px'; - inner.style.cursor = 'default'; - - var input = document.createElement('input'); - input.setAttribute('placeholder', mxResources.get('searchShapes')); - input.setAttribute('type', 'text'); - input.style.fontSize = '12px'; - input.style.overflow = 'hidden'; - input.style.boxSizing = 'border-box'; - input.style.border = 'solid 1px #d5d5d5'; - input.style.borderRadius = '4px'; - input.style.width = '100%'; - input.style.outline = 'none'; - input.style.padding = '6px'; - inner.appendChild(input); - - var cross = document.createElement('img'); - cross.setAttribute('src', Sidebar.prototype.searchImage); - cross.setAttribute('title', mxResources.get('search')); - cross.style.position = 'relative'; - cross.style.left = '-18px'; - - if (mxClient.IS_QUIRKS) - { - input.style.height = '28px'; - cross.style.top = '-4px'; - } - else - { - cross.style.top = '1px'; - } - - // Needed to block event transparency in IE - cross.style.background = 'url(\'' + this.editorUi.editor.transparentImage + '\')'; - - var find; - - inner.appendChild(cross); - div.appendChild(inner); - - var center = document.createElement('center'); - var button = mxUtils.button(mxResources.get('moreResults'), function() - { - find(); - }); - button.style.display = 'none'; - - // Workaround for inherited line-height in quirks mode - button.style.lineHeight = 'normal'; - button.style.marginTop = '4px'; - button.style.marginBottom = '8px'; - center.style.paddingTop = '4px'; - center.style.paddingBottom = '8px'; - - center.appendChild(button); - div.appendChild(center); - - var searchTerm = ''; - var active = false; - var complete = false; - var page = 0; - var hash = new Object(); - - // Count is dynamically updated below - var count = 12; - - var clearDiv = mxUtils.bind(this, function() - { - active = false; - this.currentSearch = null; - var child = div.firstChild; - - while (child != null) - { - var next = child.nextSibling; - - if (child != inner && child != center) - { - child.parentNode.removeChild(child); - } - - child = next; - } - }); - - mxEvent.addListener(cross, 'click', function() - { - if (cross.getAttribute('src') == Dialog.prototype.closeImage) - { - cross.setAttribute('src', Sidebar.prototype.searchImage); - cross.setAttribute('title', mxResources.get('search')); - button.style.display = 'none'; - input.value = ''; - searchTerm = ''; - clearDiv(); - } - - input.focus(); - }); - - find = mxUtils.bind(this, function() - { - // Shows 4 rows (minimum 4 results) - count = 4 * Math.max(1, Math.floor(this.container.clientWidth / (this.thumbWidth + 10))); - this.hideTooltip(); - - if (input.value != '') - { - if (center.parentNode != null) - { - if (searchTerm != input.value) - { - clearDiv(); - searchTerm = input.value; - hash = new Object(); - complete = false; - page = 0; - } - - if (!active && !complete) - { - button.setAttribute('disabled', 'true'); - button.style.display = ''; - button.style.cursor = 'wait'; - button.innerHTML = mxResources.get('loading') + '...'; - active = true; - - // Ignores old results - var current = new Object(); - this.currentSearch = current; - - this.searchEntries(searchTerm, count, page, mxUtils.bind(this, function(results, len, more, terms) - { - if (this.currentSearch == current) - { - results = (results != null) ? results : []; - active = false; - page++; - center.parentNode.removeChild(center); - this.insertSearchHint(div, searchTerm, count, page, results, len, more, terms); - - for (var i = 0; i < results.length; i++) - { - var elt = results[i](); - - // Avoids duplicates in results - if (hash[elt.innerHTML] == null) - { - hash[elt.innerHTML] = '1'; - div.appendChild(results[i]()); - } - } - - if (more) - { - button.removeAttribute('disabled'); - button.innerHTML = mxResources.get('moreResults'); - } - else - { - button.innerHTML = mxResources.get('reset'); - button.style.display = 'none'; - complete = true; - } - - button.style.cursor = ''; - div.appendChild(center); - } - }), mxUtils.bind(this, function() - { - // TODO: Error handling - button.style.cursor = ''; - })); - } - } - } - else - { - clearDiv(); - input.value = ''; - searchTerm = ''; - hash = new Object(); - button.style.display = 'none'; - complete = false; - input.focus(); - } - }); - - mxEvent.addListener(input, 'keydown', mxUtils.bind(this, function(evt) - { - if (evt.keyCode == 13 /* Enter */) - { - find(); - mxEvent.consume(evt); - } - })); - - mxEvent.addListener(input, 'focus', function() - { - input.style.paddingRight = ''; - }); - - mxEvent.addListener(input, 'blur', function() - { - input.style.paddingRight = '20px'; - }); - - input.style.paddingRight = '20px'; - - mxEvent.addListener(input, 'keyup', mxUtils.bind(this, function(evt) - { - if (input.value == '') - { - cross.setAttribute('src', Sidebar.prototype.searchImage); - cross.setAttribute('title', mxResources.get('search')); - } - else - { - cross.setAttribute('src', Dialog.prototype.closeImage); - cross.setAttribute('title', mxResources.get('reset')); - } - - if (input.value == '') - { - complete = true; - button.style.display = 'none'; - } - else if (input.value != searchTerm) - { - button.style.display = 'none'; - complete = false; - } - else if (!active) - { - if (complete) - { - button.style.display = 'none'; - } - else - { - button.style.display = ''; - } - } - })); - - // Workaround for blocked text selection in Editor - mxEvent.addListener(input, 'mousedown', function(evt) - { - if (evt.stopPropagation) - { - evt.stopPropagation(); - } - - evt.cancelBubble = true; - }); - - // Workaround for blocked text selection in Editor - mxEvent.addListener(input, 'selectstart', function(evt) - { - if (evt.stopPropagation) - { - evt.stopPropagation(); - } - - evt.cancelBubble = true; - }); - - var outer = document.createElement('div'); - outer.appendChild(div); - this.container.appendChild(outer); - - // Keeps references to the DOM nodes - this.palettes['search'] = [elt, outer]; -}; - -/** - * Adds the general palette to the sidebar. - */ -Sidebar.prototype.insertSearchHint = function(div, searchTerm, count, page, results, len, more, terms) -{ - if (results.length == 0 && page == 1) - { - var err = document.createElement('div'); - err.className = 'geTitle'; - err.style.cssText = 'background-color:transparent;border-color:transparent;' + - 'color:gray;padding:6px 0px 0px 0px !important;margin:4px 8px 4px 8px;' + - 'text-align:center;cursor:default !important'; - - mxUtils.write(err, mxResources.get('noResultsFor', [searchTerm])); - div.appendChild(err); - } -}; - -/** - * Adds the general palette to the sidebar. - */ -Sidebar.prototype.addGeneralPalette = function(expand) -{ - var lineTags = 'line lines connector connectors connection connections arrow arrows '; - - var fns = [ - this.createVertexTemplateEntry('rounded=0;whiteSpace=wrap;html=1;', 120, 60, '', 'Rectangle', null, null, 'rect rectangle box'), - this.createVertexTemplateEntry('rounded=1;whiteSpace=wrap;html=1;', 120, 60, '', 'Rounded Rectangle', null, null, 'rounded rect rectangle box'), - // Explicit strokecolor/fillcolor=none is a workaround to maintain transparent background regardless of current style - this.createVertexTemplateEntry('text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;', - 40, 20, 'Text', 'Text', null, null, 'text textbox textarea label'), - this.createVertexTemplateEntry('text;html=1;strokeColor=none;fillColor=none;spacing=5;spacingTop=-20;whiteSpace=wrap;overflow=hidden;rounded=0;', 190, 120, - '

Heading

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

', - 'Textbox', null, null, 'text textbox textarea'), - this.createVertexTemplateEntry('ellipse;whiteSpace=wrap;html=1;', 120, 80, '', 'Ellipse', null, null, 'oval ellipse state'), - this.createVertexTemplateEntry('whiteSpace=wrap;html=1;aspect=fixed;', 80, 80, '', 'Square', null, null, 'square'), - this.createVertexTemplateEntry('ellipse;whiteSpace=wrap;html=1;aspect=fixed;', 80, 80, '', 'Circle', null, null, 'circle'), - this.createVertexTemplateEntry('shape=process;whiteSpace=wrap;html=1;backgroundOutline=1;', 120, 60, '', 'Process', null, null, 'process task'), - this.createVertexTemplateEntry('rhombus;whiteSpace=wrap;html=1;', 80, 80, '', 'Diamond', null, null, 'diamond rhombus if condition decision conditional question test'), - this.createVertexTemplateEntry('shape=parallelogram;perimeter=parallelogramPerimeter;whiteSpace=wrap;html=1;', 120, 60, '', 'Parallelogram'), - this.createVertexTemplateEntry('shape=hexagon;perimeter=hexagonPerimeter2;whiteSpace=wrap;html=1;', 120, 80, '', 'Hexagon', null, null, 'hexagon preparation'), - this.createVertexTemplateEntry('triangle;whiteSpace=wrap;html=1;', 60, 80, '', 'Triangle', null, null, 'triangle logic inverter buffer'), - this.createVertexTemplateEntry('shape=cylinder;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;', 60, 80, '', 'Cylinder', null, null, 'cylinder data database'), - this.createVertexTemplateEntry('ellipse;shape=cloud;whiteSpace=wrap;html=1;', 120, 80, '', 'Cloud', null, null, 'cloud network'), - this.createVertexTemplateEntry('shape=document;whiteSpace=wrap;html=1;boundedLbl=1;', 120, 80, '', 'Document'), - this.createVertexTemplateEntry('shape=internalStorage;whiteSpace=wrap;html=1;backgroundOutline=1;', 80, 80, '', 'Internal Storage'), - this.createVertexTemplateEntry('shape=cube;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;', 120, 80, '', 'Cube'), - this.createVertexTemplateEntry('shape=step;perimeter=stepPerimeter;whiteSpace=wrap;html=1;fixedSize=1;', 120, 80, '', 'Step'), - this.createVertexTemplateEntry('shape=trapezoid;perimeter=trapezoidPerimeter;whiteSpace=wrap;html=1;', 120, 60, '', 'Trapezoid'), - this.createVertexTemplateEntry('shape=tape;whiteSpace=wrap;html=1;', 120, 100, '', 'Tape'), - this.createVertexTemplateEntry('shape=note;whiteSpace=wrap;html=1;backgroundOutline=1;', 80, 100, '', 'Note'), - this.createVertexTemplateEntry('shape=card;whiteSpace=wrap;html=1;', 80, 100, '', 'Card'), - this.createVertexTemplateEntry('shape=callout;whiteSpace=wrap;html=1;perimeter=calloutPerimeter;', 120, 80, '', 'Callout', null, null, 'bubble chat thought speech message'), - this.createVertexTemplateEntry('shape=umlActor;verticalLabelPosition=bottom;labelBackgroundColor=#ffffff;verticalAlign=top;html=1;outlineConnect=0;', 30, 60, 'Actor', 'Actor', false, null, 'user person human stickman'), - this.addEntry('curve', mxUtils.bind(this, function() - { - var cell = new mxCell('', new mxGeometry(0, 0, 50, 50), 'curved=1;endArrow=classic;html=1;'); - cell.geometry.setTerminalPoint(new mxPoint(0, 50), true); - cell.geometry.setTerminalPoint(new mxPoint(50, 0), false); - cell.geometry.points = [new mxPoint(50, 50), new mxPoint(0, 0)]; - cell.geometry.relative = true; - cell.edge = true; - - return this.createEdgeTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Curve'); - })), - this.createEdgeTemplateEntry('shape=flexArrow;endArrow=classic;startArrow=classic;html=1;fillColor=#ffffff;', 50, 50, '', 'Bidirectional Arrow', null, lineTags + 'bidirectional'), - this.createEdgeTemplateEntry('shape=flexArrow;endArrow=classic;html=1;fillColor=#ffffff;', 50, 50, '', 'Arrow', null, lineTags + 'directional directed'), - this.createEdgeTemplateEntry('shape=link;html=1;', 50, 50, '', 'Link', null, lineTags + 'link'), - this.createEdgeTemplateEntry('endArrow=none;dashed=1;html=1;', 50, 50, '', 'Dashed Line', null, lineTags + 'dashed undirected no'), - this.createEdgeTemplateEntry('endArrow=none;html=1;', 50, 50, '', 'Line', null, lineTags + 'simple undirected plain blank no'), - this.createEdgeTemplateEntry('endArrow=classic;startArrow=classic;html=1;', 50, 50, '', 'Bidirectional Connector', null, lineTags + 'bidirectional'), - this.createEdgeTemplateEntry('endArrow=classic;html=1;', 50, 50, '', 'Directional Connector', null, lineTags + 'directional directed') - ]; - - this.addPaletteFunctions('general', mxResources.get('general'), (expand != null) ? expand : true, fns); -}; - -/** - * Adds the general palette to the sidebar. - */ -Sidebar.prototype.addBasicPalette = function(dir) -{ - this.addStencilPalette('basic', mxResources.get('basic'), dir + '/basic.xml', - ';whiteSpace=wrap;html=1;fillColor=#ffffff;strokeColor=#000000;strokeWidth=2', - null, null, null, null, [ - this.createVertexTemplateEntry('shape=partialRectangle;whiteSpace=wrap;html=1;top=0;bottom=0;fillColor=none;', 120, 60, '', 'Partial Rectangle'), - this.createVertexTemplateEntry('shape=partialRectangle;whiteSpace=wrap;html=1;right=0;top=0;bottom=0;fillColor=none;routingCenterX=-0.5;', 120, 60, '', 'Partial Rectangle'), - this.createVertexTemplateEntry('shape=partialRectangle;whiteSpace=wrap;html=1;bottom=0;right=0;fillColor=none;', 120, 60, '', 'Partial Rectangle'), - this.createVertexTemplateEntry('shape=partialRectangle;whiteSpace=wrap;html=1;top=0;left=0;fillColor=none;', 120, 60, '', 'Partial Rectangle') - ]); -}; - -/** - * Adds the general palette to the sidebar. - */ -Sidebar.prototype.addMiscPalette = function(expand) -{ - var sb = this; - var lineTags = 'line lines connector connectors connection connections arrow arrows ' - - var fns = [ - this.createVertexTemplateEntry('text;strokeColor=none;fillColor=none;html=1;fontSize=24;fontStyle=1;verticalAlign=middle;align=center;', 100, 40, 'Title', 'Title', null, null, 'text heading title'), - this.createVertexTemplateEntry('text;strokeColor=none;fillColor=none;html=1;whiteSpace=wrap;verticalAlign=middle;overflow=hidden;', 100, 80, - '
  • Value 1
  • Value 2
  • Value 3
', 'Unordered List'), - this.createVertexTemplateEntry('text;strokeColor=none;fillColor=none;html=1;whiteSpace=wrap;verticalAlign=middle;overflow=hidden;', 100, 80, - '
  1. Value 1
  2. Value 2
  3. Value 3
', 'Ordered List'), - this.createVertexTemplateEntry('text;html=1;strokeColor=#c0c0c0;fillColor=#ffffff;overflow=fill;rounded=0;', 280, 160, - '' + - '' + - '' + - '' + - '' + - '
Title 1Title 2Title 3
Value 1Value 2Value 3
Value 4Value 5Value 6
Value 7Value 8Value 9
Value 10Value 11Value 12
', 'Table 1'), - this.createVertexTemplateEntry('text;html=1;strokeColor=#c0c0c0;fillColor=none;overflow=fill;', 180, 140, - '' + - '' + - '' + - '
Value 1Value 2Value 3
Value 4Value 5Value 6
Value 7Value 8Value 9
', 'Table 2'), - this.createVertexTemplateEntry('text;html=1;strokeColor=none;fillColor=none;overflow=fill;', 180, 140, - '' + - '' + - '' + - '
Value 1Value 2Value 3
Value 4Value 5Value 6
Value 7Value 8Value 9
', 'Table 3'), - this.createVertexTemplateEntry('text;html=1;strokeColor=none;fillColor=none;overflow=fill;', 160, 140, - '' + - '' + - '' + - '
Title
Section 1.1\nSection 1.2\nSection 1.3
Section 2.1\nSection 2.2\nSection 2.3
', 'Table 4'), - this.addEntry('link hyperlink', mxUtils.bind(this, function() - { - var cell = new mxCell('Link', new mxGeometry(0, 0, 60, 40), 'text;html=1;strokeColor=none;fillColor=none;whiteSpace=wrap;align=center;verticalAlign=middle;fontColor=#0000EE;fontStyle=4;'); - cell.vertex = true; - this.graph.setLinkForCell(cell, 'https://www.draw.io'); - - return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Link'); - })), - this.addEntry('timestamp date time text label', mxUtils.bind(this, function() - { - var cell = new mxCell('%date{ddd mmm dd yyyy HH:MM:ss}%', new mxGeometry(0, 0, 160, 20), 'text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;overflow=hidden;'); - cell.vertex = true; - this.graph.setAttributeForCell(cell, 'placeholders', '1'); - - return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Timestamp'); - })), - this.addEntry('variable placeholder metadata hello world text label', mxUtils.bind(this, function() - { - var cell = new mxCell('%name% Text', new mxGeometry(0, 0, 80, 20), 'text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;overflow=hidden;'); - cell.vertex = true; - this.graph.setAttributeForCell(cell, 'placeholders', '1'); - this.graph.setAttributeForCell(cell, 'name', 'Variable'); - - return this.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Variable'); - })), - this.createVertexTemplateEntry('shape=ext;double=1;rounded=0;whiteSpace=wrap;html=1;', 120, 80, '', 'Double Rectangle', null, null, 'rect rectangle box double'), - this.createVertexTemplateEntry('shape=ext;double=1;rounded=1;whiteSpace=wrap;html=1;', 120, 80, '', 'Double Rounded Rectangle', null, null, 'rounded rect rectangle box double'), - this.createVertexTemplateEntry('ellipse;shape=doubleEllipse;whiteSpace=wrap;html=1;', 100, 60, '', 'Double Ellipse', null, null, 'oval ellipse start end state double'), - this.createVertexTemplateEntry('shape=ext;double=1;whiteSpace=wrap;html=1;aspect=fixed;', 80, 80, '', 'Double Square', null, null, 'double square'), - this.createVertexTemplateEntry('ellipse;shape=doubleEllipse;whiteSpace=wrap;html=1;aspect=fixed;', 80, 80, '', 'Double Circle', null, null, 'double circle'), - this.createEdgeTemplateEntry('rounded=0;comic=1;strokeWidth=2;endArrow=blockThin;html=1;fontFamily=Comic Sans MS;fontStyle=1;', 50, 50, '', 'Comic Arrow'), - this.createVertexTemplateEntry('html=1;whiteSpace=wrap;comic=1;strokeWidth=2;fontFamily=Comic Sans MS;fontStyle=1;', 120, 60, 'RECTANGLE', 'Comic Rectangle', true, null, 'comic rectangle rect box text retro'), - this.createVertexTemplateEntry('rhombus;html=1;align=center;whiteSpace=wrap;comic=1;strokeWidth=2;fontFamily=Comic Sans MS;fontStyle=1;', 100, 100, 'DIAMOND', 'Comic Diamond', true, null, 'comic diamond rhombus if condition decision conditional question test retro'), - this.createVertexTemplateEntry('html=1;whiteSpace=wrap;aspect=fixed;shape=isoRectangle;', 150, 90, '', 'Isometric Square', true, null, 'rectangle rect box iso isometric'), - this.createVertexTemplateEntry('html=1;whiteSpace=wrap;aspect=fixed;shape=isoCube;backgroundOutline=1;', 90, 100, '', 'Isometric Cube', true, null, 'cube box iso isometric'), - this.createEdgeTemplateEntry('edgeStyle=isometricEdgeStyle;endArrow=none;html=1;', 50, 100, '', 'Isometric Edge 1'), - this.createEdgeTemplateEntry('edgeStyle=isometricEdgeStyle;endArrow=none;html=1;elbow=vertical;', 50, 100, '', 'Isometric Edge 2'), - this.createVertexTemplateEntry('shape=curlyBracket;whiteSpace=wrap;html=1;rounded=1;', 20, 120, '', 'Curly Bracket'), - this.createVertexTemplateEntry('line;strokeWidth=2;html=1;', 160, 10, '', 'Horizontal Line'), - this.createVertexTemplateEntry('line;strokeWidth=2;direction=south;html=1;', 10, 160, '', 'Vertical Line'), - this.createVertexTemplateEntry('line;strokeWidth=4;html=1;perimeter=backbonePerimeter;points=[];outlineConnect=0;', 160, 10, '', 'Horizontal Backbone', false, null, 'backbone bus network'), - this.createVertexTemplateEntry('line;strokeWidth=4;direction=south;html=1;perimeter=backbonePerimeter;points=[];outlineConnect=0;', 10, 160, '', 'Vertical Backbone', false, null, 'backbone bus network'), - this.createVertexTemplateEntry('shape=crossbar;whiteSpace=wrap;html=1;rounded=1;', 120, 20, '', 'Crossbar', false, null, 'crossbar distance measure dimension unit'), - this.createVertexTemplateEntry('shape=image;html=1;verticalLabelPosition=bottom;labelBackgroundColor=#ffffff;verticalAlign=top;imageAspect=1;aspect=fixed;image=' + this.gearImage, 52, 61, '', 'Image (Fixed Aspect)', false, null, 'fixed image icon symbol'), - this.createVertexTemplateEntry('shape=image;html=1;verticalLabelPosition=bottom;labelBackgroundColor=#ffffff;verticalAlign=top;imageAspect=0;image=' + this.gearImage, 50, 60, '', 'Image (Variable Aspect)', false, null, 'strechted image icon symbol'), - this.createVertexTemplateEntry('icon;html=1;image=' + this.gearImage, 60, 60, 'Icon', 'Icon', false, null, 'icon image symbol'), - this.createVertexTemplateEntry('label;whiteSpace=wrap;html=1;image=' + this.gearImage, 140, 60, 'Label', 'Label 1', null, null, 'label image icon symbol'), - this.createVertexTemplateEntry('label;whiteSpace=wrap;html=1;align=center;verticalAlign=bottom;spacingLeft=0;spacingBottom=4;imageAlign=center;imageVerticalAlign=top;image=' + this.gearImage, 120, 80, 'Label', 'Label 2', null, null, 'label image icon symbol'), - this.addEntry('shape group container', function() - { - var cell = new mxCell('Label', new mxGeometry(0, 0, 160, 70), - 'html=1;whiteSpace=wrap;container=1;recursiveResize=0;collapsible=0;'); - cell.vertex = true; - - var symbol = new mxCell('', new mxGeometry(20, 20, 20, 30), 'triangle;html=1;whiteSpace=wrap;'); - symbol.vertex = true; - cell.insert(symbol); - - return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Shape Group'); - }), - this.createVertexTemplateEntry('shape=partialRectangle;whiteSpace=wrap;html=1;left=0;right=0;fillColor=none;', 120, 60, '', 'Partial Rectangle'), - this.createVertexTemplateEntry('shape=partialRectangle;whiteSpace=wrap;html=1;bottom=1;right=1;top=0;bottom=1;fillColor=none;routingCenterX=-0.5;', 120, 60, '', 'Partial Rectangle'), - this.createEdgeTemplateEntry('edgeStyle=segmentEdgeStyle;endArrow=classic;html=1;', 50, 50, '', 'Manual Line', null, lineTags + 'manual'), - this.createEdgeTemplateEntry('shape=filledEdge;rounded=0;fixDash=1;endArrow=none;strokeWidth=10;fillColor=#ffffff;edgeStyle=orthogonalEdgeStyle;', 60, 40, '', 'Filled Edge'), - this.createEdgeTemplateEntry('edgeStyle=elbowEdgeStyle;elbow=horizontal;endArrow=classic;html=1;', 50, 50, '', 'Horizontal Elbow', null, lineTags + 'elbow horizontal'), - this.createEdgeTemplateEntry('edgeStyle=elbowEdgeStyle;elbow=vertical;endArrow=classic;html=1;', 50, 50, '', 'Vertical Elbow', null, lineTags + 'elbow vertical') - ]; - - this.addPaletteFunctions('misc', mxResources.get('misc'), (expand != null) ? expand : true, fns); -}; -/** - * Adds the container palette to the sidebar. - */ -Sidebar.prototype.addAdvancedPalette = function(expand) -{ - this.addPaletteFunctions('advanced', mxResources.get('advanced'), (expand != null) ? expand : false, this.createAdvancedShapes()); -}; - -/** - * Adds the container palette to the sidebar. - */ -Sidebar.prototype.createAdvancedShapes = function() -{ - // Avoids having to bind all functions to "this" - var sb = this; - - // Reusable cells - var field = new mxCell('List Item', new mxGeometry(0, 0, 60, 26), 'text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;'); - field.vertex = true; - - return [ - this.createVertexTemplateEntry('shape=xor;whiteSpace=wrap;html=1;', 60, 80, '', 'Or', null, null, 'logic or'), - this.createVertexTemplateEntry('shape=or;whiteSpace=wrap;html=1;', 60, 80, '', 'And', null, null, 'logic and'), - this.createVertexTemplateEntry('shape=dataStorage;whiteSpace=wrap;html=1;', 100, 80, '', 'Data Storage'), - this.createVertexTemplateEntry('shape=tapeData;whiteSpace=wrap;html=1;perimeter=ellipsePerimeter;', 80, 80, '', 'Tape Data'), - this.createVertexTemplateEntry('shape=manualInput;whiteSpace=wrap;html=1;', 80, 80, '', 'Manual Input'), - this.createVertexTemplateEntry('shape=loopLimit;whiteSpace=wrap;html=1;', 100, 80, '', 'Loop Limit'), - this.createVertexTemplateEntry('shape=offPageConnector;whiteSpace=wrap;html=1;', 80, 80, '', 'Off Page Connector'), - this.createVertexTemplateEntry('shape=delay;whiteSpace=wrap;html=1;', 80, 40, '', 'Delay'), - this.createVertexTemplateEntry('shape=display;whiteSpace=wrap;html=1;', 80, 40, '', 'Display'), - this.createVertexTemplateEntry('shape=singleArrow;direction=west;whiteSpace=wrap;html=1;', 100, 60, '', 'Arrow Left'), - this.createVertexTemplateEntry('shape=singleArrow;whiteSpace=wrap;html=1;', 100, 60, '', 'Arrow Right'), - this.createVertexTemplateEntry('shape=singleArrow;direction=north;whiteSpace=wrap;html=1;', 60, 100, '', 'Arrow Up'), - this.createVertexTemplateEntry('shape=singleArrow;direction=south;whiteSpace=wrap;html=1;', 60, 100, '', 'Arrow Down'), - this.createVertexTemplateEntry('shape=doubleArrow;whiteSpace=wrap;html=1;', 100, 60, '', 'Double Arrow'), - this.createVertexTemplateEntry('shape=doubleArrow;direction=south;whiteSpace=wrap;html=1;', 60, 100, '', 'Double Arrow Vertical', null, null, 'double arrow'), - this.createVertexTemplateEntry('shape=actor;whiteSpace=wrap;html=1;', 40, 60, '', 'User', null, null, 'user person human'), - this.createVertexTemplateEntry('shape=cross;whiteSpace=wrap;html=1;', 80, 80, '', 'Cross'), - this.createVertexTemplateEntry('shape=corner;whiteSpace=wrap;html=1;', 80, 80, '', 'Corner'), - this.createVertexTemplateEntry('shape=tee;whiteSpace=wrap;html=1;', 80, 80, '', 'Tee'), - this.createVertexTemplateEntry('shape=datastore;whiteSpace=wrap;html=1;', 60, 60, '', 'Data Store', null, null, 'data store cylinder database'), - this.createVertexTemplateEntry('shape=orEllipse;perimeter=ellipsePerimeter;whiteSpace=wrap;html=1;backgroundOutline=1;', 80, 80, '', 'Or', null, null, 'or circle oval ellipse'), - this.createVertexTemplateEntry('shape=sumEllipse;perimeter=ellipsePerimeter;whiteSpace=wrap;html=1;backgroundOutline=1;', 80, 80, '', 'Sum', null, null, 'sum circle oval ellipse'), - this.createVertexTemplateEntry('shape=lineEllipse;perimeter=ellipsePerimeter;whiteSpace=wrap;html=1;backgroundOutline=1;', 80, 80, '', 'Ellipse with horizontal divider', null, null, 'circle oval ellipse'), - this.createVertexTemplateEntry('shape=lineEllipse;line=vertical;perimeter=ellipsePerimeter;whiteSpace=wrap;html=1;backgroundOutline=1;', 80, 80, '', 'Ellipse with vertical divider', null, null, 'circle oval ellipse'), - this.createVertexTemplateEntry('shape=sortShape;perimeter=rhombusPerimeter;whiteSpace=wrap;html=1;', 80, 80, '', 'Sort', null, null, 'sort'), - this.createVertexTemplateEntry('shape=collate;whiteSpace=wrap;html=1;', 80, 80, '', 'Collate', null, null, 'collate'), - this.createVertexTemplateEntry('shape=switch;whiteSpace=wrap;html=1;', 60, 60, '', 'Switch', null, null, 'switch router'), - this.addEntry('process bar', function() - { - return sb.createVertexTemplateFromData('zZXRaoMwFIafJpcDjbNrb2233rRQ8AkyPdPQaCRJV+3T7yTG2rUVBoOtgpDzn/xJzncCIdGyateKNeVW5iBI9EqipZLS9KOqXYIQhAY8J9GKUBrgT+jbRDZ02aBhCmrzEwPtDZ9MHKBXdkpmoDWKCVN9VptO+Kw+8kqwGqMkK7nIN6yTB7uTNizbD1FSSsVPsjYMC1qFKHxwIZZSSIVxLZ1/nJNar5+oQPMT7IYCrqUta1ENzuqGaeOFTArBGs3f3Vmtoo2Se7ja1h00kSoHK4bBIKUNy3hdoPYU0mF91i9mT8EEL2ocZ3gKa00ayWujLZY4IfHKFonVDLsRGgXuQ90zBmWgneyTk3yT1iArMKrDKUeem9L3ajHrbSXwohxsQd/ggOleKM7ese048J2/fwuim1uQGmhQCW8vQMkacP3GCQgBFMftHEsr7cYYe95CnmKTPMFbYD8CQ++DGQy+/M5X4ku5wHYmdIktfvk9tecpavThqS3m/0YtnqIWPTy1cD77K2wYjo+Ay317I74A', 296, 100, 'Process Bar'); - }), - this.createVertexTemplateEntry('swimlane;', 200, 200, 'Container', 'Container', null, null, 'container swimlane lane pool group'), - this.addEntry('list group erd table', function() - { - var cell = new mxCell('List', new mxGeometry(0, 0, 140, 110), - 'swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=none;horizontalStack=0;' + - 'resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;'); - cell.vertex = true; - cell.insert(sb.cloneCell(field, 'Item 1')); - cell.insert(sb.cloneCell(field, 'Item 2')); - cell.insert(sb.cloneCell(field, 'Item 3')); - - return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'List'); - }), - this.addEntry('list item entry value group erd table', function() - { - return sb.createVertexTemplateFromCells([sb.cloneCell(field, 'List Item')], field.geometry.width, field.geometry.height, 'List Item'); - }) - ]; -}; - -/** - * Adds the general palette to the sidebar. - */ -Sidebar.prototype.addUmlPalette = function(expand) -{ - // Avoids having to bind all functions to "this" - var sb = this; - - // Reusable cells - var field = new mxCell('+ field: type', new mxGeometry(0, 0, 100, 26), 'text;strokeColor=none;fillColor=none;align=left;verticalAlign=top;spacingLeft=4;spacingRight=4;overflow=hidden;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;'); - field.vertex = true; - - var divider = new mxCell('', new mxGeometry(0, 0, 40, 8), 'line;strokeWidth=1;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=3;spacingRight=3;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;'); - divider.vertex = true; - - // Default tags - var dt = 'uml static class '; - - var fns = [ - this.createVertexTemplateEntry('html=1;', 110, 50, 'Object', 'Object', null, null, dt + 'object instance'), - this.createVertexTemplateEntry('html=1;', 110, 50, '«interface»
Name', 'Interface', null, null, dt + 'interface object instance annotated annotation'), - this.addEntry(dt + 'object instance', function() - { - var cell = new mxCell('Classname', new mxGeometry(0, 0, 160, 90), - 'swimlane;fontStyle=1;align=center;verticalAlign=top;childLayout=stackLayout;horizontal=1;startSize=26;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;'); - cell.vertex = true; - cell.insert(field.clone()); - cell.insert(divider.clone()); - cell.insert(sb.cloneCell(field, '+ method(type): type')); - - return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Class'); - }), - this.addEntry(dt + 'section subsection', function() - { - var cell = new mxCell('Classname', new mxGeometry(0, 0, 140, 110), - 'swimlane;fontStyle=0;childLayout=stackLayout;horizontal=1;startSize=26;fillColor=none;horizontalStack=0;resizeParent=1;resizeParentMax=0;resizeLast=0;collapsible=1;marginBottom=0;swimlaneFillColor=#ffffff;'); - cell.vertex = true; - cell.insert(field.clone()); - cell.insert(field.clone()); - cell.insert(field.clone()); - - return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Class 2'); - }), - this.addEntry(dt + 'item member method function variable field attribute label', function() - { - return sb.createVertexTemplateFromCells([sb.cloneCell(field, '+ item: attribute')], field.geometry.width, field.geometry.height, 'Item 1'); - }), - this.addEntry(dt + 'item member method function variable field attribute label', function() - { - var cell = new mxCell('item: attribute', new mxGeometry(0, 0, 120, field.geometry.height), 'label;fontStyle=0;strokeColor=none;fillColor=none;align=left;verticalAlign=top;overflow=hidden;' + - 'spacingLeft=28;spacingRight=4;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;imageWidth=16;imageHeight=16;image=' + sb.gearImage); - cell.vertex = true; - - return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Item 2'); - }), - this.addEntry(dt + 'divider hline line separator', function() - { - return sb.createVertexTemplateFromCells([divider.clone()], divider.geometry.width, divider.geometry.height, 'Divider'); - }), - this.addEntry(dt + 'spacer space gap separator', function() - { - var cell = new mxCell('', new mxGeometry(0, 0, 20, 14), 'text;strokeColor=none;fillColor=none;align=left;verticalAlign=middle;spacingTop=-1;spacingLeft=4;spacingRight=4;rotatable=0;labelPosition=right;points=[];portConstraint=eastwest;'); - cell.vertex = true; - - return sb.createVertexTemplateFromCells([cell.clone()], cell.geometry.width, cell.geometry.height, 'Spacer'); - }), - this.createVertexTemplateEntry('text;align=center;fontStyle=1;verticalAlign=middle;spacingLeft=3;spacingRight=3;strokeColor=none;rotatable=0;points=[[0,0.5],[1,0.5]];portConstraint=eastwest;', - 80, 26, 'Title', 'Title', null, null, dt + 'title label'), - this.addEntry(dt + 'component', function() - { - var cell = new mxCell('«Annotation»
Component', new mxGeometry(0, 0, 180, 90), 'html=1;'); - cell.vertex = true; - - var symbol = new mxCell('', new mxGeometry(1, 0, 20, 20), 'shape=component;jettyWidth=8;jettyHeight=4;'); - symbol.vertex = true; - symbol.geometry.relative = true; - symbol.geometry.offset = new mxPoint(-27, 7); - cell.insert(symbol); - - return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Component'); - }), - this.addEntry(dt + 'component', function() - { - var cell = new mxCell('

Component

' + - '

+ Attribute1: Type
+ Attribute2: Type

', new mxGeometry(0, 0, 180, 90), - 'align=left;overflow=fill;html=1;'); - cell.vertex = true; - - var symbol = new mxCell('', new mxGeometry(1, 0, 20, 20), 'shape=component;jettyWidth=8;jettyHeight=4;'); - symbol.vertex = true; - symbol.geometry.relative = true; - symbol.geometry.offset = new mxPoint(-24, 4); - cell.insert(symbol); - - return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Component with Attributes'); - }), - this.createVertexTemplateEntry('verticalAlign=top;align=left;spacingTop=8;spacingLeft=2;spacingRight=12;shape=cube;size=10;direction=south;fontStyle=4;html=1;', - 180, 120, 'Block', 'Block', null, null, dt + 'block'), - this.createVertexTemplateEntry('shape=component;align=left;spacingLeft=36;', 120, 60, 'Module', 'Module', null, null, dt + 'module'), - this.createVertexTemplateEntry('shape=folder;fontStyle=1;spacingTop=10;tabWidth=40;tabHeight=14;tabPosition=left;html=1;', 70, 50, - 'package', 'Package', null, null, dt + 'package'), - this.createVertexTemplateEntry('verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;html=1;', - 160, 90, '

Object:Type


' + - '

field1 = value1
field2 = value2
field3 = value3

', 'Object', - null, null, dt + 'object instance'), - this.createVertexTemplateEntry('verticalAlign=top;align=left;overflow=fill;html=1;',180, 90, - '
Tablename
' + - '' + - '
PKuniqueId
FK1' + - 'foreignKey
fieldname
', 'Entity', null, null, 'er entity table'), - this.addEntry(dt + 'object instance', function() - { - var cell = new mxCell('

' + - 'Class

' + - '
', new mxGeometry(0, 0, 140, 60), - 'verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;html=1;'); - cell.vertex = true; - - return sb.createVertexTemplateFromCells([cell.clone()], cell.geometry.width, cell.geometry.height, 'Class 3'); - }), - this.addEntry(dt + 'object instance', function() - { - var cell = new mxCell('

' + - 'Class

' + - '

', new mxGeometry(0, 0, 140, 60), - 'verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;html=1;'); - cell.vertex = true; - - return sb.createVertexTemplateFromCells([cell.clone()], cell.geometry.width, cell.geometry.height, 'Class 4'); - }), - this.addEntry(dt + 'object instance', function() - { - var cell = new mxCell('

' + - 'Class

' + - '

+ field: Type


' + - '

+ method(): Type

', new mxGeometry(0, 0, 160, 90), - 'verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;html=1;'); - cell.vertex = true; - - return sb.createVertexTemplateFromCells([cell.clone()], cell.geometry.width, cell.geometry.height, 'Class 5'); - }), - this.addEntry(dt + 'object instance', function() - { - var cell = new mxCell('

' + - '<<Interface>>
Interface

' + - '

+ field1: Type
' + - '+ field2: Type

' + - '

' + - '+ method1(Type): Type
' + - '+ method2(Type, Type): Type

', new mxGeometry(0, 0, 190, 140), - 'verticalAlign=top;align=left;overflow=fill;fontSize=12;fontFamily=Helvetica;html=1;'); - cell.vertex = true; - - return sb.createVertexTemplateFromCells([cell.clone()], cell.geometry.width, cell.geometry.height, 'Interface 2'); - }), - this.createVertexTemplateEntry('shape=providedRequiredInterface;html=1;verticalLabelPosition=bottom;', 20, 20, '', 'Provided/Required Interface', null, null, dt + 'provided required interface'), - this.createVertexTemplateEntry('shape=requiredInterface;html=1;verticalLabelPosition=bottom;', 10, 20, '', 'Required Interface', null, null, dt + 'required interface'), - this.createVertexTemplateEntry('shape=umlBoundary;whiteSpace=wrap;html=1;', 100, 80, 'Boundary Object', 'Boundary Object', null, null, 'uml boundary object'), - this.createVertexTemplateEntry('ellipse;shape=umlEntity;whiteSpace=wrap;html=1;', 80, 80, 'Entity Object', 'Entity Object', null, null, 'uml entity object'), - this.createVertexTemplateEntry('ellipse;shape=umlControl;whiteSpace=wrap;html=1;', 70, 80, 'Control Object', 'Control Object', null, null, 'uml control object'), - this.createVertexTemplateEntry('shape=umlActor;verticalLabelPosition=bottom;labelBackgroundColor=#ffffff;verticalAlign=top;html=1;', 30, 60, 'Actor', 'Actor', false, null, 'uml actor'), - this.createVertexTemplateEntry('ellipse;whiteSpace=wrap;html=1;', 140, 70, 'Use Case', 'Use Case', null, null, 'uml use case usecase'), - this.addEntry('uml activity state start', function() - { - var cell = new mxCell('', new mxGeometry(0, 0, 30, 30), - 'ellipse;html=1;shape=startState;fillColor=#000000;strokeColor=#ff0000;'); - cell.vertex = true; - - var edge = new mxCell('', new mxGeometry(0, 0, 0, 0), 'edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;'); - edge.geometry.setTerminalPoint(new mxPoint(15, 90), false); - edge.geometry.relative = true; - edge.edge = true; - - cell.insertEdge(edge, true); - - return sb.createVertexTemplateFromCells([cell, edge], 30, 90, 'Start'); - }), - this.addEntry('uml activity state', function() - { - var cell = new mxCell('Activity', new mxGeometry(0, 0, 120, 40), - 'rounded=1;whiteSpace=wrap;html=1;arcSize=40;fillColor=#ffffc0;strokeColor=#ff0000;'); - cell.vertex = true; - - var edge = new mxCell('', new mxGeometry(0, 0, 0, 0), 'edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;'); - edge.geometry.setTerminalPoint(new mxPoint(60, 100), false); - edge.geometry.relative = true; - edge.edge = true; - - cell.insertEdge(edge, true); - - return sb.createVertexTemplateFromCells([cell, edge], 120, 100, 'Activity'); - }), - this.addEntry('uml activity composite state', function() - { - var cell = new mxCell('Composite State', new mxGeometry(0, 0, 160, 60), - 'swimlane;html=1;fontStyle=1;align=center;verticalAlign=middle;childLayout=stackLayout;horizontal=1;startSize=30;horizontalStack=0;resizeParent=0;resizeLast=1;container=0;collapsible=0;rounded=1;arcSize=30;strokeColor=#ff0000;fillColor=#ffffc0;swimlaneFillColor=#ffffc0;'); - cell.vertex = true; - - var cell1 = new mxCell('Subtitle', new mxGeometry(0, 0, 200, 26), 'text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;spacingLeft=4;spacingRight=4;whiteSpace=wrap;overflow=hidden;rotatable=0;'); - cell1.vertex = true; - cell.insert(cell1); - - var edge = new mxCell('', new mxGeometry(0, 0, 0, 0), 'edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;'); - edge.geometry.setTerminalPoint(new mxPoint(80, 120), false); - edge.geometry.relative = true; - edge.edge = true; - - cell.insertEdge(edge, true); - - return sb.createVertexTemplateFromCells([cell, edge], 160, 120, 'Composite State'); - }), - this.addEntry('uml activity condition', function() - { - var cell = new mxCell('Condition', new mxGeometry(0, 0, 80, 40), 'rhombus;whiteSpace=wrap;html=1;fillColor=#ffffc0;strokeColor=#ff0000;'); - cell.vertex = true; - - var edge1 = new mxCell('no', new mxGeometry(0, 0, 0, 0), 'edgeStyle=orthogonalEdgeStyle;html=1;align=left;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;'); - edge1.geometry.setTerminalPoint(new mxPoint(180, 20), false); - edge1.geometry.relative = true; - edge1.geometry.x = -1; - edge1.edge = true; - - cell.insertEdge(edge1, true); - - var edge2 = new mxCell('yes', new mxGeometry(0, 0, 0, 0), 'edgeStyle=orthogonalEdgeStyle;html=1;align=left;verticalAlign=top;endArrow=open;endSize=8;strokeColor=#ff0000;'); - edge2.geometry.setTerminalPoint(new mxPoint(40, 100), false); - edge2.geometry.relative = true; - edge2.geometry.x = -1; - edge2.edge = true; - - cell.insertEdge(edge2, true); - - return sb.createVertexTemplateFromCells([cell, edge1, edge2], 180, 100, 'Condition'); - }), - this.addEntry('uml activity fork join', function() - { - var cell = new mxCell('', new mxGeometry(0, 0, 200, 10), 'shape=line;html=1;strokeWidth=6;strokeColor=#ff0000;'); - cell.vertex = true; - - var edge = new mxCell('', new mxGeometry(0, 0, 0, 0), 'edgeStyle=orthogonalEdgeStyle;html=1;verticalAlign=bottom;endArrow=open;endSize=8;strokeColor=#ff0000;'); - edge.geometry.setTerminalPoint(new mxPoint(100, 80), false); - edge.geometry.relative = true; - edge.edge = true; - - cell.insertEdge(edge, true); - - return sb.createVertexTemplateFromCells([cell, edge], 200, 80, 'Fork/Join'); - }), - this.createVertexTemplateEntry('ellipse;html=1;shape=endState;fillColor=#000000;strokeColor=#ff0000;', 30, 30, '', 'End', null, null, 'uml activity state end'), - this.createVertexTemplateEntry('shape=umlLifeline;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=1;collapsible=0;recursiveResize=0;outlineConnect=0;', 100, 300, ':Object', 'Lifeline', null, null, 'uml sequence participant lifeline'), - this.createVertexTemplateEntry('shape=umlLifeline;participant=umlActor;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=1;collapsible=0;recursiveResize=0;verticalAlign=top;spacingTop=36;labelBackgroundColor=#ffffff;outlineConnect=0;', - 20, 300, '', 'Actor Lifeline', null, null, 'uml sequence participant lifeline actor'), - this.createVertexTemplateEntry('shape=umlLifeline;participant=umlBoundary;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=1;collapsible=0;recursiveResize=0;verticalAlign=top;spacingTop=36;labelBackgroundColor=#ffffff;outlineConnect=0;', - 50, 300, '', 'Boundary Lifeline', null, null, 'uml sequence participant lifeline boundary'), - this.createVertexTemplateEntry('shape=umlLifeline;participant=umlEntity;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=1;collapsible=0;recursiveResize=0;verticalAlign=top;spacingTop=36;labelBackgroundColor=#ffffff;outlineConnect=0;', - 40, 300, '', 'Entity Lifeline', null, null, 'uml sequence participant lifeline entity'), - this.createVertexTemplateEntry('shape=umlLifeline;participant=umlControl;perimeter=lifelinePerimeter;whiteSpace=wrap;html=1;container=1;collapsible=0;recursiveResize=0;verticalAlign=top;spacingTop=36;labelBackgroundColor=#ffffff;outlineConnect=0;', - 40, 300, '', 'Control Lifeline', null, null, 'uml sequence participant lifeline control'), - this.createVertexTemplateEntry('shape=umlFrame;whiteSpace=wrap;html=1;', 300, 200, 'frame', 'Frame', null, null, 'uml sequence frame'), - this.createVertexTemplateEntry('shape=umlDestroy;whiteSpace=wrap;html=1;strokeWidth=3;', 30, 30, '', 'Destruction', null, null, 'uml sequence destruction destroy'), - this.createVertexTemplateEntry('shape=note;whiteSpace=wrap;html=1;size=14;verticalAlign=top;align=left;spacingTop=-6;', 100, 70, 'Note', 'Note', null, null, 'uml note'), - this.addEntry('uml sequence invoke invocation call activation', function() - { - var cell = new mxCell('', new mxGeometry(0, 0, 10, 80), 'html=1;points=[];perimeter=orthogonalPerimeter;'); - cell.vertex = true; - - var edge = new mxCell('dispatch', new mxGeometry(0, 0, 0, 0), 'html=1;verticalAlign=bottom;startArrow=oval;endArrow=block;startSize=8;'); - edge.geometry.setTerminalPoint(new mxPoint(-60, 0), true); - edge.geometry.relative = true; - edge.edge = true; - - cell.insertEdge(edge, false); - - return sb.createVertexTemplateFromCells([cell, edge], 10, 80, 'Found Message'); - }), - this.addEntry('uml sequence invoke call delegation synchronous invocation activation', function() - { - var cell = new mxCell('', new mxGeometry(0, 0, 10, 80), 'html=1;points=[];perimeter=orthogonalPerimeter;'); - cell.vertex = true; - - var edge1 = new mxCell('dispatch', new mxGeometry(0, 0, 0, 0), 'html=1;verticalAlign=bottom;endArrow=block;entryX=0;entryY=0;'); - edge1.geometry.setTerminalPoint(new mxPoint(-70, 0), true); - edge1.geometry.relative = true; - edge1.edge = true; - - cell.insertEdge(edge1, false); - - var edge2 = new mxCell('return', new mxGeometry(0, 0, 0, 0), 'html=1;verticalAlign=bottom;endArrow=open;dashed=1;endSize=8;exitX=0;exitY=0.95;'); - edge2.geometry.setTerminalPoint(new mxPoint(-70, 76), false); - edge2.geometry.relative = true; - edge2.edge = true; - - cell.insertEdge(edge2, true); - - return sb.createVertexTemplateFromCells([cell, edge1, edge2], 10, 80, 'Synchronous Invocation'); - }), - this.addEntry('uml sequence self call recursion delegation activation', function() - { - var cell = new mxCell('', new mxGeometry(0, 20, 10, 40), 'html=1;points=[];perimeter=orthogonalPerimeter;'); - cell.vertex = true; - - var edge = new mxCell('self call', new mxGeometry(0, 0, 0, 0), 'edgeStyle=orthogonalEdgeStyle;html=1;align=left;spacingLeft=2;endArrow=block;rounded=0;entryX=1;entryY=0;'); - edge.geometry.setTerminalPoint(new mxPoint(5, 0), true); - edge.geometry.points = [new mxPoint(30, 0)]; - edge.geometry.relative = true; - edge.edge = true; - - cell.insertEdge(edge, false); - - return sb.createVertexTemplateFromCells([cell, edge], 10, 60, 'Self Call'); - }), - this.addEntry('uml sequence invoke call delegation callback activation', function() - { - // TODO: Check if more entries should be converted to compressed XML - return sb.createVertexTemplateFromData('xZRNT8MwDIZ/Ta6oaymD47rBTkiTuMAxW6wmIm0q19s6fj1OE3V0Y2iCA4dK8euP2I+riGxedUuUjX52CqzIHkU2R+conKpuDtaKNDFKZAuRpgl/In264J303qSRCDVdk5CGhJ20WwhKEFo62ChoqritxURkReNMTa2X80LkC68AmgoIkEWHpF3pamlXR7WIFwASdBeb7KXY4RIc5+KBQ/ZGkY4RYY5Egyl1zLqLmmyDXQ6Zx4n5EIf+HkB2BmAjrV3LzftPIPw4hgNn1pQ1a2tH5Cp2QK1miG7vNeu4iJe4pdeY2BtvbCQDGlAljMCQxBJotJ8rWCFYSWY3LvUdmZi68rvkkLiU6QnL1m1xAzHoBOdw61WEb88II9AW67/ydQ2wq1Cy1aAGvOrFfPh6997qDA3g+dxzv3nIL6MPU/8T+kMw8+m4QPgdfrEJNo8PSQj/+s58Ag==', - 10, 60, 'Callback'); - }), - this.createVertexTemplateEntry('html=1;points=[];perimeter=orthogonalPerimeter;', 10, 80, '', 'Activation', null, null, 'uml sequence activation'), - this.createEdgeTemplateEntry('html=1;verticalAlign=bottom;startArrow=oval;startFill=1;endArrow=block;startSize=8;', 60, 0, 'dispatch', 'Found Message 1', null, 'uml sequence message call invoke dispatch'), - this.createEdgeTemplateEntry('html=1;verticalAlign=bottom;startArrow=circle;startFill=1;endArrow=open;startSize=6;endSize=8;', 80, 0, 'dispatch', 'Found Message 2', null, 'uml sequence message call invoke dispatch'), - this.createEdgeTemplateEntry('html=1;verticalAlign=bottom;endArrow=block;', 80, 0, 'dispatch', 'Message', null, 'uml sequence message call invoke dispatch'), - this.addEntry('uml sequence return message', function() - { - var edge = new mxCell('return', new mxGeometry(0, 0, 0, 0), 'html=1;verticalAlign=bottom;endArrow=open;dashed=1;endSize=8;'); - edge.geometry.setTerminalPoint(new mxPoint(80, 0), true); - edge.geometry.setTerminalPoint(new mxPoint(0, 0), false); - edge.geometry.relative = true; - edge.edge = true; - - return sb.createEdgeTemplateFromCells([edge], 80, 0, 'Return'); - }), - this.addEntry('uml relation', function() - { - var edge = new mxCell('name', new mxGeometry(0, 0, 0, 0), 'endArrow=block;endFill=1;html=1;edgeStyle=orthogonalEdgeStyle;align=left;verticalAlign=top;'); - edge.geometry.setTerminalPoint(new mxPoint(0, 0), true); - edge.geometry.setTerminalPoint(new mxPoint(160, 0), false); - edge.geometry.relative = true; - edge.geometry.x = -1; - edge.edge = true; - - var cell = new mxCell('1', new mxGeometry(-1, 0, 0, 0), 'resizable=0;html=1;align=left;verticalAlign=bottom;labelBackgroundColor=#ffffff;fontSize=10;'); - cell.geometry.relative = true; - cell.setConnectable(false); - cell.vertex = true; - edge.insert(cell); - - return sb.createEdgeTemplateFromCells([edge], 160, 0, 'Relation 1'); - }), - this.addEntry('uml association', function() - { - var edge = new mxCell('', new mxGeometry(0, 0, 0, 0), 'endArrow=none;html=1;edgeStyle=orthogonalEdgeStyle;'); - edge.geometry.setTerminalPoint(new mxPoint(0, 0), true); - edge.geometry.setTerminalPoint(new mxPoint(160, 0), false); - edge.geometry.relative = true; - edge.edge = true; - - var cell1 = new mxCell('parent', new mxGeometry(-1, 0, 0, 0), 'resizable=0;html=1;align=left;verticalAlign=bottom;labelBackgroundColor=#ffffff;fontSize=10;'); - cell1.geometry.relative = true; - cell1.setConnectable(false); - cell1.vertex = true; - edge.insert(cell1); - - var cell2 = new mxCell('child', new mxGeometry(1, 0, 0, 0), 'resizable=0;html=1;align=right;verticalAlign=bottom;labelBackgroundColor=#ffffff;fontSize=10;'); - cell2.geometry.relative = true; - cell2.setConnectable(false); - cell2.vertex = true; - edge.insert(cell2); - - return sb.createEdgeTemplateFromCells([edge], 160, 0, 'Association 1'); - }), - this.addEntry('uml aggregation', function() - { - var edge = new mxCell('1', new mxGeometry(0, 0, 0, 0), 'endArrow=open;html=1;endSize=12;startArrow=diamondThin;startSize=14;startFill=0;edgeStyle=orthogonalEdgeStyle;align=left;verticalAlign=bottom;'); - edge.geometry.setTerminalPoint(new mxPoint(0, 0), true); - edge.geometry.setTerminalPoint(new mxPoint(160, 0), false); - edge.geometry.relative = true; - edge.geometry.x = -1; - edge.geometry.y = 3; - edge.edge = true; - - return sb.createEdgeTemplateFromCells([edge], 160, 0, 'Aggregation 1'); - }), - this.addEntry('uml composition', function() - { - var edge = new mxCell('1', new mxGeometry(0, 0, 0, 0), 'endArrow=open;html=1;endSize=12;startArrow=diamondThin;startSize=14;startFill=1;edgeStyle=orthogonalEdgeStyle;align=left;verticalAlign=bottom;'); - edge.geometry.setTerminalPoint(new mxPoint(0, 0), true); - edge.geometry.setTerminalPoint(new mxPoint(160, 0), false); - edge.geometry.relative = true; - edge.geometry.x = -1; - edge.geometry.y = 3; - edge.edge = true; - - return sb.createEdgeTemplateFromCells([edge], 160, 0, 'Composition 1'); - }), - this.addEntry('uml relation', function() - { - var edge = new mxCell('Relation', new mxGeometry(0, 0, 0, 0), 'endArrow=open;html=1;endSize=12;startArrow=diamondThin;startSize=14;startFill=0;edgeStyle=orthogonalEdgeStyle;'); - edge.geometry.setTerminalPoint(new mxPoint(0, 0), true); - edge.geometry.setTerminalPoint(new mxPoint(160, 0), false); - edge.geometry.relative = true; - edge.edge = true; - - var cell1 = new mxCell('0..n', new mxGeometry(-1, 0, 0, 0), 'resizable=0;html=1;align=left;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;'); - cell1.geometry.relative = true; - cell1.setConnectable(false); - cell1.vertex = true; - edge.insert(cell1); - - var cell2 = new mxCell('1', new mxGeometry(1, 0, 0, 0), 'resizable=0;html=1;align=right;verticalAlign=top;labelBackgroundColor=#ffffff;fontSize=10;'); - cell2.geometry.relative = true; - cell2.setConnectable(false); - cell2.vertex = true; - edge.insert(cell2); - - return sb.createEdgeTemplateFromCells([edge], 160, 0, 'Relation 2'); - }), - this.createEdgeTemplateEntry('endArrow=open;endSize=12;dashed=1;html=1;', 160, 0, 'Use', 'Dependency', null, 'uml dependency use'), - this.createEdgeTemplateEntry('endArrow=block;endSize=16;endFill=0;html=1;', 160, 0, 'Extends', 'Generalization', null, 'uml generalization extend'), - this.createEdgeTemplateEntry('endArrow=block;startArrow=block;endFill=1;startFill=1;html=1;', 160, 0, '', 'Association 2', null, 'uml association'), - this.createEdgeTemplateEntry('endArrow=open;startArrow=circlePlus;endFill=0;startFill=0;endSize=8;html=1;', 160, 0, '', 'Inner Class', null, 'inner class'), - this.createEdgeTemplateEntry('endArrow=open;startArrow=cross;endFill=0;startFill=0;endSize=8;startSize=10;html=1;', 160, 0, '', 'Terminate', null, 'terminate'), - this.createEdgeTemplateEntry('endArrow=block;dashed=1;endFill=0;endSize=12;html=1;', 160, 0, '', 'Implementation', null, 'realization implementation'), - this.createEdgeTemplateEntry('endArrow=diamondThin;endFill=0;endSize=24;html=1;', 160, 0, '', 'Aggregation 2', null, 'aggregation'), - this.createEdgeTemplateEntry('endArrow=diamondThin;endFill=1;endSize=24;html=1;', 160, 0, '', 'Composition 2', null, 'composition'), - this.createEdgeTemplateEntry('endArrow=open;endFill=1;endSize=12;html=1;', 160, 0, '', 'Association 3', null, 'association') - ]; - - this.addPaletteFunctions('uml', mxResources.get('uml'), expand || false, fns); -}; - -/** - * Adds the BPMN library to the sidebar. - */ -Sidebar.prototype.addBpmnPalette = function(dir, expand) -{ - // Avoids having to bind all functions to "this" - var sb = this; - - var fns = - [ - this.createVertexTemplateEntry('shape=ext;rounded=1;html=1;whiteSpace=wrap;', 120, 80, 'Task', 'Process', null, null, 'bpmn task process'), - this.createVertexTemplateEntry('shape=ext;rounded=1;html=1;whiteSpace=wrap;double=1;', 120, 80, 'Transaction', 'Transaction', null, null, 'bpmn transaction'), - this.createVertexTemplateEntry('shape=ext;rounded=1;html=1;whiteSpace=wrap;dashed=1;dashPattern=1 4;', 120, 80, 'Event\nSub-Process', 'Event Sub-Process', null, null, 'bpmn event subprocess sub process sub-process'), - this.createVertexTemplateEntry('shape=ext;rounded=1;html=1;whiteSpace=wrap;strokeWidth=3;', 120, 80, 'Call Activity', 'Call Activity', null, null, 'bpmn call activity'), - this.addEntry('bpmn subprocess sub process sub-process', function() - { - var cell = new mxCell('Sub-Process', new mxGeometry(0, 0, 120, 80), 'html=1;whiteSpace=wrap;rounded=1;'); - cell.vertex = true; - - var cell1 = new mxCell('', new mxGeometry(0.5, 1, 14, 14), 'html=1;shape=plus;outlineConnect=0;'); - cell1.vertex = true; - cell1.geometry.relative = true; - cell1.geometry.offset = new mxPoint(-7, -14); - cell.insert(cell1); - - return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Sub-Process'); - }), - this.addEntry(this.getTagsForStencil('mxgraph.bpmn', 'loop', 'subprocess sub process sub-process looped').join(' '), function() - { - var cell = new mxCell('Looped\nSub-Process', new mxGeometry(0, 0, 120, 80), 'html=1;whiteSpace=wrap;rounded=1'); - cell.vertex = true; - - var cell1 = new mxCell('', new mxGeometry(0.5, 1, 14, 14), 'html=1;shape=mxgraph.bpmn.loop;outlineConnect=0;'); - cell1.vertex = true; - cell1.geometry.relative = true; - cell1.geometry.offset = new mxPoint(-15, -14); - cell.insert(cell1); - - var cell2 = new mxCell('', new mxGeometry(0.5, 1, 14, 14), 'html=1;shape=plus;'); - cell2.vertex = true; - cell2.geometry.relative = true; - cell2.geometry.offset = new mxPoint(1, -14); - cell.insert(cell2); - - return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Looped Sub-Process'); - }), - this.addEntry('bpmn receive task', function() - { - var cell = new mxCell('Receive', new mxGeometry(0, 0, 120, 80), 'html=1;whiteSpace=wrap;rounded=1;'); - cell.vertex = true; - - var cell1 = new mxCell('', new mxGeometry(0, 0, 20, 14), 'html=1;shape=message;outlineConnect=0;'); - cell1.vertex = true; - cell1.geometry.relative = true; - cell1.geometry.offset = new mxPoint(7, 7); - cell.insert(cell1); - - return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Receive Task'); - }), - this.addEntry(this.getTagsForStencil('mxgraph.bpmn', 'user_task').join(' '), function() - { - var cell = new mxCell('User', new mxGeometry(0, 0, 120, 80), 'html=1;whiteSpace=wrap;rounded=1;'); - cell.vertex = true; - - var cell1 = new mxCell('', new mxGeometry(0, 0, 14, 14), 'html=1;shape=mxgraph.bpmn.user_task;outlineConnect=0;'); - cell1.vertex = true; - cell1.geometry.relative = true; - cell1.geometry.offset = new mxPoint(7, 7); - cell.insert(cell1); - - var cell2 = new mxCell('', new mxGeometry(0.5, 1, 14, 14), 'html=1;shape=plus;outlineConnect=0;'); - cell2.vertex = true; - cell2.geometry.relative = true; - cell2.geometry.offset = new mxPoint(-7, -14); - cell.insert(cell2); - - return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'User Task'); - }), - this.addEntry(this.getTagsForStencil('mxgraph.bpmn', 'timer_start', 'attached').join(' '), function() - { - var cell = new mxCell('Process', new mxGeometry(0, 0, 120, 80), 'html=1;whiteSpace=wrap;rounded=1;'); - cell.vertex = true; - - var cell1 = new mxCell('', new mxGeometry(1, 1, 30, 30), 'shape=mxgraph.bpmn.timer_start;perimeter=ellipsePerimeter;html=1;verticalLabelPosition=bottom;labelBackgroundColor=#ffffff;verticalAlign=top;outlineConnect=0;'); - cell1.vertex = true; - cell1.geometry.relative = true; - cell1.geometry.offset = new mxPoint(-40, -15); - cell.insert(cell1); - - return sb.createVertexTemplateFromCells([cell], 120, 95, 'Attached Timer Event 1'); - }), - this.addEntry(this.getTagsForStencil('mxgraph.bpmn', 'timer_start', 'attached').join(' '), function() - { - var cell = new mxCell('Process', new mxGeometry(0, 0, 120, 80), 'html=1;whiteSpace=wrap;rounded=1;'); - cell.vertex = true; - - var cell1 = new mxCell('', new mxGeometry(1, 0, 30, 30), 'shape=mxgraph.bpmn.timer_start;perimeter=ellipsePerimeter;html=1;labelPosition=right;labelBackgroundColor=#ffffff;align=left;outlineConnect=0;'); - cell1.vertex = true; - cell1.geometry.relative = true; - cell1.geometry.offset = new mxPoint(-15, 10); - cell.insert(cell1); - - return sb.createVertexTemplateFromCells([cell], 135, 80, 'Attached Timer Event 2'); - }), - this.createVertexTemplateEntry('swimlane;html=1;horizontal=0;startSize=20;', 320, 240, 'Pool', 'Pool', null, null, 'bpmn pool'), - this.createVertexTemplateEntry('swimlane;html=1;horizontal=0;swimlaneFillColor=white;swimlaneLine=0;', 300, 120, 'Lane', 'Lane', null, null, 'bpmn lane'), - this.createVertexTemplateEntry('shape=hexagon;html=1;whiteSpace=wrap;perimeter=hexagonPerimeter;rounded=0;', 60, 50, '', 'Conversation', null, null, 'bpmn conversation'), - this.createVertexTemplateEntry('shape=hexagon;html=1;whiteSpace=wrap;perimeter=hexagonPerimeter;strokeWidth=4;rounded=0;', 60, 50, '', 'Call Conversation', null, null, 'bpmn call conversation'), - this.addEntry('bpmn subconversation sub conversation sub-conversation', function() - { - var cell = new mxCell('', new mxGeometry(0, 0, 60, 50), 'shape=hexagon;whiteSpace=wrap;html=1;perimeter=hexagonPerimeter;rounded=0;'); - cell.vertex = true; - - var cell1 = new mxCell('', new mxGeometry(0.5, 1, 14, 14), 'html=1;shape=plus;'); - cell1.vertex = true; - cell1.geometry.relative = true; - cell1.geometry.offset = new mxPoint(-7, -14); - cell.insert(cell1); - - return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Sub-Conversation'); - }), - this.addEntry('bpmn data object', function() - { - var cell = new mxCell('', new mxGeometry(0, 0, 40, 60), 'shape=note;whiteSpace=wrap;size=16;html=1;'); - cell.vertex = true; - - var cell1 = new mxCell('', new mxGeometry(0, 0, 14, 14), 'html=1;shape=singleArrow;arrowWidth=0.4;arrowSize=0.4;outlineConnect=0;'); - cell1.vertex = true; - cell1.geometry.relative = true; - cell1.geometry.offset = new mxPoint(2, 2); - cell.insert(cell1); - - var cell2 = new mxCell('', new mxGeometry(0.5, 1, 14, 14), 'html=1;whiteSpace=wrap;shape=parallelMarker;outlineConnect=0;'); - cell2.vertex = true; - cell2.geometry.relative = true; - cell2.geometry.offset = new mxPoint(-7, -14); - cell.insert(cell2); - - return sb.createVertexTemplateFromCells([cell], cell.geometry.width, cell.geometry.height, 'Data Object'); - }), - this.createVertexTemplateEntry('shape=datastore;whiteSpace=wrap;html=1;', 60, 60, '', 'Data Store', null, null, 'bpmn data store'), - this.createVertexTemplateEntry('shape=plus;html=1;outlineConnect=0;', 14, 14, '', 'Sub-Process Marker', null, null, 'bpmn subprocess sub process sub-process marker'), - this.createVertexTemplateEntry('shape=mxgraph.bpmn.loop;html=1;outlineConnect=0;', 14, 14, '', 'Loop Marker', null, null, 'bpmn loop marker'), - this.createVertexTemplateEntry('shape=parallelMarker;html=1;outlineConnect=0;', 14, 14, '', 'Parallel MI Marker', null, null, 'bpmn parallel mi marker'), - this.createVertexTemplateEntry('shape=parallelMarker;direction=south;html=1;outlineConnect=0;', 14, 14, '', 'Sequential MI Marker', null, null, 'bpmn sequential mi marker'), - this.createVertexTemplateEntry('shape=mxgraph.bpmn.ad_hoc;fillColor=#000000;html=1;outlineConnect=0;', 14, 14, '', 'Ad Hoc Marker', null, null, 'bpmn ad hoc marker'), - this.createVertexTemplateEntry('shape=mxgraph.bpmn.compensation;html=1;outlineConnect=0;', 14, 14, '', 'Compensation Marker', null, null, 'bpmn compensation marker'), - this.createVertexTemplateEntry('shape=message;whiteSpace=wrap;html=1;outlineConnect=0;fillColor=#000000;strokeColor=#ffffff;strokeWidth=2;', 40, 30, '', 'Send Task', null, null, 'bpmn send task'), - this.createVertexTemplateEntry('shape=message;whiteSpace=wrap;html=1;outlineConnect=0;', 40, 30, '', 'Receive Task', null, null, 'bpmn receive task'), - this.createVertexTemplateEntry('shape=mxgraph.bpmn.user_task;html=1;outlineConnect=0;', 14, 14, '', 'User Task', null, null, this.getTagsForStencil('mxgraph.bpmn', 'user_task').join(' ')), - this.createVertexTemplateEntry('shape=mxgraph.bpmn.manual_task;html=1;outlineConnect=0;', 14, 14, '', 'Manual Task', null, null, this.getTagsForStencil('mxgraph.bpmn', 'user_task').join(' ')), - this.createVertexTemplateEntry('shape=mxgraph.bpmn.business_rule_task;html=1;outlineConnect=0;', 14, 14, '', 'Business Rule Task', null, null, this.getTagsForStencil('mxgraph.bpmn', 'business_rule_task').join(' ')), - this.createVertexTemplateEntry('shape=mxgraph.bpmn.service_task;html=1;outlineConnect=0;', 14, 14, '', 'Service Task', null, null, this.getTagsForStencil('mxgraph.bpmn', 'service_task').join(' ')), - this.createVertexTemplateEntry('shape=mxgraph.bpmn.script_task;html=1;outlineConnect=0;', 14, 14, '', 'Script Task', null, null, this.getTagsForStencil('mxgraph.bpmn', 'script_task').join(' ')), - this.createVertexTemplateEntry('html=1;shape=mxgraph.flowchart.annotation_2;align=left;', 50, 100, '', 'Annotation', null, null, this.getTagsForStencil('bpmn', 'annotation_1', 'bpmn business process model ').join(' ')), - this.createVertexTemplateEntry('rounded=1;arcSize=10;dashed=1;strokeColor=#000000;fillColor=none;gradientColor=none;dashPattern=8 3 1 3;strokeWidth=2;', - 200, 200, '', 'Group', null, null, this.getTagsForStencil('bpmn', 'group', 'bpmn business process model ').join(' ')), - this.createEdgeTemplateEntry('endArrow=block;endFill=1;endSize=6;html=1;', 100, 0, '', 'Sequence Flow', null, 'bpmn sequence flow'), - this.createEdgeTemplateEntry('startArrow=dash;startSize=8;endArrow=block;endFill=1;endSize=6;html=1;', 100, 0, '', 'Default Flow', null, 'bpmn default flow'), - this.createEdgeTemplateEntry('startArrow=diamondThin;startFill=0;startSize=14;endArrow=block;endFill=1;endSize=6;html=1;', 100, 0, '', 'Conditional Flow', null, 'bpmn conditional flow'), - this.createEdgeTemplateEntry('startArrow=oval;startFill=0;startSize=7;endArrow=block;endFill=0;endSize=10;dashed=1;html=1;', 100, 0, '', 'Message Flow 1', null, 'bpmn message flow'), - this.addEntry('bpmn message flow', function() - { - var edge = new mxCell('', new mxGeometry(0, 0, 0, 0), 'startArrow=oval;startFill=0;startSize=7;endArrow=block;endFill=0;endSize=10;dashed=1;html=1;'); - edge.geometry.setTerminalPoint(new mxPoint(0, 0), true); - edge.geometry.setTerminalPoint(new mxPoint(100, 0), false); - edge.geometry.relative = true; - edge.edge = true; - - var cell = new mxCell('', new mxGeometry(0, 0, 20, 14), 'shape=message;html=1;outlineConnect=0;'); - cell.geometry.relative = true; - cell.vertex = true; - cell.geometry.offset = new mxPoint(-10, -7); - edge.insert(cell); - - return sb.createEdgeTemplateFromCells([edge], 100, 0, 'Message Flow 2'); - }), - this.createEdgeTemplateEntry('shape=link;html=1;', 100, 0, '', 'Link', null, 'bpmn link') - ]; - - this.addPaletteFunctions('bpmn', 'BPMN ' + mxResources.get('general'), false, fns); -}; - -/** - * Creates and returns the given title element. - */ -Sidebar.prototype.createTitle = function(label) -{ - var elt = document.createElement('a'); - elt.setAttribute('href', 'javascript:void(0);'); - elt.setAttribute('title', mxResources.get('sidebarTooltip')); - elt.className = 'geTitle'; - mxUtils.write(elt, label); - - return elt; -}; - -/** - * Creates a thumbnail for the given cells. - */ -Sidebar.prototype.createThumb = function(cells, width, height, parent, title, showLabel, showTitle, realWidth, realHeight) -{ - this.graph.labelsVisible = (showLabel == null || showLabel); - var fo = mxClient.NO_FO; - mxClient.NO_FO = Editor.prototype.originalNoForeignObject; - this.graph.view.scaleAndTranslate(1, 0, 0); - this.graph.addCells(cells); - var bounds = this.graph.getGraphBounds(); - var s = Math.floor(Math.min((width - 2 * this.thumbBorder) / bounds.width, - (height - 2 * this.thumbBorder) / bounds.height) * 100) / 100; - this.graph.view.scaleAndTranslate(s, Math.floor((width - bounds.width * s) / 2 / s - bounds.x), - Math.floor((height - bounds.height * s) / 2 / s - bounds.y)); - var node = null; - - // For supporting HTML labels in IE9 standards mode the container is cloned instead - if (this.graph.dialect == mxConstants.DIALECT_SVG && !mxClient.NO_FO) - { - node = this.graph.view.getCanvas().ownerSVGElement.cloneNode(true); - } - // LATER: Check if deep clone can be used for quirks if container in DOM - else - { - node = this.graph.container.cloneNode(false); - node.innerHTML = this.graph.container.innerHTML; - - // Workaround for clipping in older IE versions - if (mxClient.IS_QUIRKS || document.documentMode == 8) - { - node.firstChild.style.overflow = 'visible'; - } - } - - this.graph.getModel().clear(); - mxClient.NO_FO = fo; - - // Catch-all event handling - if (mxClient.IS_IE6) - { - parent.style.backgroundImage = 'url(' + this.editorUi.editor.transparentImage + ')'; - } - - node.style.position = 'relative'; - node.style.overflow = 'hidden'; - node.style.cursor = 'move'; - node.style.left = this.thumbBorder + 'px'; - node.style.top = this.thumbBorder + 'px'; - node.style.width = width + 'px'; - node.style.height = height + 'px'; - node.style.visibility = ''; - node.style.minWidth = ''; - node.style.minHeight = ''; - - parent.appendChild(node); - - // Adds title for sidebar entries - if (this.sidebarTitles && title != null && showTitle != false) - { - var border = (mxClient.IS_QUIRKS) ? 2 * this.thumbPadding + 2: 0; - parent.style.height = (this.thumbHeight + border + this.sidebarTitleSize + 8) + 'px'; - - var div = document.createElement('div'); - div.style.fontSize = this.sidebarTitleSize + 'px'; - div.style.color = '#303030'; - div.style.textAlign = 'center'; - div.style.whiteSpace = 'nowrap'; - - if (mxClient.IS_IE) - { - div.style.height = (this.sidebarTitleSize + 12) + 'px'; - } - - div.style.paddingTop = '4px'; - mxUtils.write(div, title); - parent.appendChild(div); - } - - return bounds; -}; - -/** - * Creates and returns a new palette item for the given image. - */ -Sidebar.prototype.createItem = function(cells, title, showLabel, showTitle, width, height, allowCellsInserted) -{ - var elt = document.createElement('a'); - elt.setAttribute('href', 'javascript:void(0);'); - elt.className = 'geItem'; - elt.style.overflow = 'hidden'; - var border = (mxClient.IS_QUIRKS) ? 8 + 2 * this.thumbPadding : 2 * this.thumbBorder; - elt.style.width = (this.thumbWidth + border) + 'px'; - elt.style.height = (this.thumbHeight + border) + 'px'; - elt.style.padding = this.thumbPadding + 'px'; - - if (mxClient.IS_IE6) - { - elt.style.border = 'none'; - } - - // Blocks default click action - mxEvent.addListener(elt, 'click', function(evt) - { - mxEvent.consume(evt); - }); - - this.createThumb(cells, this.thumbWidth, this.thumbHeight, elt, title, showLabel, showTitle, width, height); - var bounds = new mxRectangle(0, 0, width, height); - - if (cells.length > 1 || cells[0].vertex) - { - var ds = this.createDragSource(elt, this.createDropHandler(cells, true, allowCellsInserted, - bounds), this.createDragPreview(width, height), cells, bounds); - this.addClickHandler(elt, ds, cells); - - // Uses guides for vertices only if enabled in graph - ds.isGuidesEnabled = mxUtils.bind(this, function() - { - return this.editorUi.editor.graph.graphHandler.guidesEnabled; - }); - } - else if (cells[0] != null && cells[0].edge) - { - var ds = this.createDragSource(elt, this.createDropHandler(cells, false, allowCellsInserted, - bounds), this.createDragPreview(width, height), cells, bounds); - this.addClickHandler(elt, ds, cells); - } - - // Shows a tooltip with the rendered cell - if (!mxClient.IS_IOS) - { - mxEvent.addGestureListeners(elt, null, mxUtils.bind(this, function(evt) - { - if (mxEvent.isMouseEvent(evt)) - { - this.showTooltip(elt, cells, bounds.width, bounds.height, title, showLabel); - } - })); - } - - return elt; -}; - -/** - * Creates a drop handler for inserting the given cells. - */ -Sidebar.prototype.updateShapes = function(source, targets) -{ - var graph = this.editorUi.editor.graph; - var sourceCellStyle = graph.getCellStyle(source); - var result = []; - - graph.model.beginUpdate(); - try - { - var cellStyle = graph.getModel().getStyle(source); - - // Lists the styles to carry over from the existing shape - var styles = ['shadow', 'dashed', 'dashPattern', 'fontFamily', 'fontSize', 'fontColor', 'align', 'startFill', - 'startSize', 'endFill', 'endSize', 'strokeColor', 'strokeWidth', 'fillColor', 'gradientColor', - 'html', 'part', 'noEdgeStyle', 'edgeStyle', 'elbow', 'childLayout', 'recursiveResize', - 'container', 'collapsible', 'connectable']; - - for (var i = 0; i < targets.length; i++) - { - var targetCell = targets[i]; - - if ((graph.getModel().isVertex(targetCell) == graph.getModel().isVertex(source)) || - (graph.getModel().isEdge(targetCell) == graph.getModel().isEdge(source))) - { - var state = graph.view.getState(targetCell); - var style = (state != null) ? state.style : graph.getCellStyle(targets[i]); - graph.getModel().setStyle(targetCell, cellStyle); - - // Removes all children of composite cells - if (state != null && mxUtils.getValue(state.style, 'composite', '0') == '1') - { - var childCount = graph.model.getChildCount(targetCell); - - for (var j = childCount; j >= 0; j--) - { - graph.model.remove(graph.model.getChildAt(targetCell, j)); - } - } - - if (style != null) - { - // Replaces the participant style in the lifeline shape with the target shape - if (style[mxConstants.STYLE_SHAPE] == 'umlLifeline' && - sourceCellStyle[mxConstants.STYLE_SHAPE] != 'umlLifeline') - { - graph.setCellStyles(mxConstants.STYLE_SHAPE, 'umlLifeline', [targetCell]); - graph.setCellStyles('participant', sourceCellStyle[mxConstants.STYLE_SHAPE], [targetCell]); - } - - for (var j = 0; j < styles.length; j++) - { - var value = style[styles[j]]; - - if (value != null) - { - graph.setCellStyles(styles[j], value, [targetCell]); - } - } - } - - result.push(targetCell); - } - } - } - finally - { - graph.model.endUpdate(); - } - - return result; -}; - -/** - * Creates a drop handler for inserting the given cells. - */ -Sidebar.prototype.createDropHandler = function(cells, allowSplit, allowCellsInserted, bounds) -{ - allowCellsInserted = (allowCellsInserted != null) ? allowCellsInserted : true; - - return mxUtils.bind(this, function(graph, evt, target, x, y, force) - { - var elt = (force) ? null : ((mxEvent.isTouchEvent(evt) || mxEvent.isPenEvent(evt)) ? - document.elementFromPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt)) : - mxEvent.getSource(evt)); - - while (elt != null && elt != this.container) - { - elt = elt.parentNode; - } - - if (elt == null && graph.isEnabled()) - { - cells = graph.getImportableCells(cells); - - if (cells.length > 0) - { - graph.stopEditing(); - - // Holding alt while mouse is released ignores drop target - var validDropTarget = (target != null && !mxEvent.isAltDown(evt)) ? - graph.isValidDropTarget(target, cells, evt) : false; - var select = null; - - if (target != null && !validDropTarget) - { - target = null; - } - - if (!graph.isCellLocked(target || graph.getDefaultParent())) - { - graph.model.beginUpdate(); - try - { - x = Math.round(x); - y = Math.round(y); - - // Splits the target edge or inserts into target group - if (allowSplit && graph.isSplitTarget(target, cells, evt)) - { - var clones = graph.cloneCells(cells); - graph.splitEdge(target, clones, null, - x - bounds.width / 2, y - bounds.height / 2); - select = clones; - } - else if (cells.length > 0) - { - select = graph.importCells(cells, x, y, target); - } - - // Executes parent layout hooks for position/order - if (graph.layoutManager != null) - { - var layout = graph.layoutManager.getLayout(target); - - if (layout != null) - { - var s = graph.view.scale; - var tr = graph.view.translate; - var tx = (x + tr.x) * s; - var ty = (y + tr.y) * s; - - for (var i = 0; i < select.length; i++) - { - layout.moveCell(select[i], tx, ty); - } - } - } - - if (allowCellsInserted) - { - graph.fireEvent(new mxEventObject('cellsInserted', 'cells', select)); - } - } - catch (e) - { - this.editorUi.handleError(e); - } - finally - { - graph.model.endUpdate(); - } - - if (select != null && select.length > 0) - { - graph.scrollCellToVisible(select[0]); - graph.setSelectionCells(select); - } - - if (graph.editAfterInsert && evt != null && mxEvent.isMouseEvent(evt) && - select != null && select.length == 1) - { - window.setTimeout(function() - { - graph.startEditing(select[0]); - }, 0); - } - } - } - - mxEvent.consume(evt); - } - }); -}; - -/** - * Creates and returns a preview element for the given width and height. - */ -Sidebar.prototype.createDragPreview = function(width, height) -{ - var elt = document.createElement('div'); - elt.style.border = this.dragPreviewBorder; - elt.style.width = width + 'px'; - elt.style.height = height + 'px'; - - return elt; -}; - -/** - * Creates a drag source for the given element. - */ -Sidebar.prototype.dropAndConnect = function(source, targets, direction, dropCellIndex, evt) -{ - var geo = this.getDropAndConnectGeometry(source, targets[dropCellIndex], direction, targets); - - // Targets without the new edge for selection - var tmp = []; - - if (geo != null) - { - var graph = this.editorUi.editor.graph; - var editingCell = null; - - graph.model.beginUpdate(); - try - { - var sourceGeo = graph.getCellGeometry(source); - var geo2 = graph.getCellGeometry(targets[dropCellIndex]); - - // Handles special case where target should be ignored for stack layouts - var targetParent = graph.model.getParent(source); - var validLayout = true; - - // Ignores parent if it has a stack layout - if (graph.layoutManager != null) - { - var layout = graph.layoutManager.getLayout(targetParent); - - // LATER: Use parent of parent if valid layout - if (layout != null && layout.constructor == mxStackLayout) - { - validLayout = false; - - var tmp = graph.view.getState(targetParent); - - // Offsets by parent position - if (tmp != null) - { - var offset = new mxPoint((tmp.x / graph.view.scale - graph.view.translate.x), - (tmp.y / graph.view.scale - graph.view.translate.y)); - geo.x += offset.x; - geo.y += offset.y; - var pt = geo.getTerminalPoint(false); - - if (pt != null) - { - pt.x += offset.x; - pt.y += offset.y; - } - } - } - } - - var dx = geo2.x; - var dy = geo2.y; - - // Ignores geometry of edges - if (graph.model.isEdge(targets[dropCellIndex])) - { - dx = 0; - dy = 0; - } - - var useParent = graph.model.isEdge(source) || (sourceGeo != null && !sourceGeo.relative && validLayout); - targets = graph.importCells(targets, (geo.x - (useParent ? dx : 0)), - (geo.y - (useParent ? dy : 0)), (useParent) ? targetParent : null); - tmp = targets; - - if (graph.model.isEdge(source)) - { - // Adds new terminal to edge - // LATER: Push new terminal out radially from edge start point - graph.model.setTerminal(source, targets[dropCellIndex], direction == mxConstants.DIRECTION_NORTH); - } - else if (graph.model.isEdge(targets[dropCellIndex])) - { - // Adds new outgoing connection to vertex and clears points - graph.model.setTerminal(targets[dropCellIndex], source, true); - var geo3 = graph.getCellGeometry(targets[dropCellIndex]); - geo3.points = null; - - if (geo3.getTerminalPoint(false) != null) - { - geo3.setTerminalPoint(geo.getTerminalPoint(false), false); - } - else if (useParent && graph.model.isVertex(targetParent)) - { - // Adds parent offset to other nodes - var tmpState = graph.view.getState(targetParent); - var offset = (tmpState.cell != graph.view.currentRoot) ? - new mxPoint((tmpState.x / graph.view.scale - graph.view.translate.x), - (tmpState.y / graph.view.scale - graph.view.translate.y)) : new mxPoint(0, 0); - - graph.cellsMoved(targets, offset.x, offset.y, null, null, true); - } - } - else - { - geo2 = graph.getCellGeometry(targets[dropCellIndex]); - dx = geo.x - Math.round(geo2.x); - dy = geo.y - Math.round(geo2.y); - geo.x = Math.round(geo2.x); - geo.y = Math.round(geo2.y); - graph.model.setGeometry(targets[dropCellIndex], geo); - graph.cellsMoved(targets, dx, dy, null, null, true); - tmp = targets.slice(); - editingCell = (tmp.length == 1) ? tmp[0] : null; - targets.push(graph.insertEdge(null, null, '', source, targets[dropCellIndex], - graph.createCurrentEdgeStyle())); - } - - graph.fireEvent(new mxEventObject('cellsInserted', 'cells', targets)); - } - catch (e) - { - this.editorUi.handleError(e); - } - finally - { - graph.model.endUpdate(); - } - - if (graph.editAfterInsert && evt != null && mxEvent.isMouseEvent(evt) && - editingCell != null) - { - window.setTimeout(function() - { - graph.startEditing(editingCell); - }, 0); - } - } - - return tmp; -}; - -/** - * Creates a drag source for the given element. - */ -Sidebar.prototype.getDropAndConnectGeometry = function(source, target, direction, targets) -{ - var graph = this.editorUi.editor.graph; - var view = graph.view; - var keepSize = targets.length > 1; - var geo = graph.getCellGeometry(source); - var geo2 = graph.getCellGeometry(target); - - if (geo != null && geo2 != null) - { - geo2 = geo2.clone(); - - if (graph.model.isEdge(source)) - { - var state = graph.view.getState(source); - var pts = state.absolutePoints; - var p0 = pts[0]; - var pe = pts[pts.length - 1]; - - if (direction == mxConstants.DIRECTION_NORTH) - { - geo2.x = p0.x / view.scale - view.translate.x - geo2.width / 2; - geo2.y = p0.y / view.scale - view.translate.y - geo2.height / 2; - } - else - { - geo2.x = pe.x / view.scale - view.translate.x - geo2.width / 2; - geo2.y = pe.y / view.scale - view.translate.y - geo2.height / 2; - } - } - else - { - if (geo.relative) - { - var state = graph.view.getState(source); - geo = geo.clone(); - geo.x = (state.x - view.translate.x) / view.scale; - geo.y = (state.y - view.translate.y) / view.scale; - } - - var length = graph.defaultEdgeLength; - - // Maintains edge length - if (graph.model.isEdge(target) && geo2.getTerminalPoint(true) != null && geo2.getTerminalPoint(false) != null) - { - var p0 = geo2.getTerminalPoint(true); - var pe = geo2.getTerminalPoint(false); - var dx = pe.x - p0.x; - var dy = pe.y - p0.y; - - length = Math.sqrt(dx * dx + dy * dy); - - geo2.x = geo.getCenterX(); - geo2.y = geo.getCenterY(); - geo2.width = 1; - geo2.height = 1; - - if (direction == mxConstants.DIRECTION_NORTH) - { - geo2.height = length - geo2.y = geo.y - length; - geo2.setTerminalPoint(new mxPoint(geo2.x, geo2.y), false); - } - else if (direction == mxConstants.DIRECTION_EAST) - { - geo2.width = length - geo2.x = geo.x + geo.width; - geo2.setTerminalPoint(new mxPoint(geo2.x + geo2.width, geo2.y), false); - } - else if (direction == mxConstants.DIRECTION_SOUTH) - { - geo2.height = length - geo2.y = geo.y + geo.height; - geo2.setTerminalPoint(new mxPoint(geo2.x, geo2.y + geo2.height), false); - } - else if (direction == mxConstants.DIRECTION_WEST) - { - geo2.width = length - geo2.x = geo.x - length; - geo2.setTerminalPoint(new mxPoint(geo2.x, geo2.y), false); - } - } - else - { - // Try match size or ignore if width or height < 45 which - // is considered special enough to be ignored here - if (!keepSize && geo2.width > 45 && geo2.height > 45 && - geo.width > 45 && geo.height > 45) - { - geo2.width = geo2.width * (geo.height / geo2.height); - geo2.height = geo.height; - } - - geo2.x = geo.x + geo.width / 2 - geo2.width / 2; - geo2.y = geo.y + geo.height / 2 - geo2.height / 2; - - if (direction == mxConstants.DIRECTION_NORTH) - { - geo2.y = geo2.y - geo.height / 2 - geo2.height / 2 - length; - } - else if (direction == mxConstants.DIRECTION_EAST) - { - geo2.x = geo2.x + geo.width / 2 + geo2.width / 2 + length; - } - else if (direction == mxConstants.DIRECTION_SOUTH) - { - geo2.y = geo2.y + geo.height / 2 + geo2.height / 2 + length; - } - else if (direction == mxConstants.DIRECTION_WEST) - { - geo2.x = geo2.x - geo.width / 2 - geo2.width / 2 - length; - } - - // Adds offset to match cells without connecting edge - if (graph.model.isEdge(target) && geo2.getTerminalPoint(true) != null && target.getTerminal(false) != null) - { - var targetGeo = graph.getCellGeometry(target.getTerminal(false)); - - if (targetGeo != null) - { - if (direction == mxConstants.DIRECTION_NORTH) - { - geo2.x -= targetGeo.getCenterX(); - geo2.y -= targetGeo.getCenterY() + targetGeo.height / 2; - } - else if (direction == mxConstants.DIRECTION_EAST) - { - geo2.x -= targetGeo.getCenterX() - targetGeo.width / 2; - geo2.y -= targetGeo.getCenterY(); - } - else if (direction == mxConstants.DIRECTION_SOUTH) - { - geo2.x -= targetGeo.getCenterX(); - geo2.y -= targetGeo.getCenterY() - targetGeo.height / 2; - } - else if (direction == mxConstants.DIRECTION_WEST) - { - geo2.x -= targetGeo.getCenterX() + targetGeo.width / 2; - geo2.y -= targetGeo.getCenterY(); - } - } - } - } - } - } - - return geo2; -}; - -/** - * Creates a drag source for the given element. - */ -Sidebar.prototype.createDragSource = function(elt, dropHandler, preview, cells, bounds) -{ - // Checks if the cells contain any vertices - var ui = this.editorUi; - var graph = ui.editor.graph; - var freeSourceEdge = null; - var firstVertex = null; - var sidebar = this; - - for (var i = 0; i < cells.length; i++) - { - if (firstVertex == null && this.editorUi.editor.graph.model.isVertex(cells[i])) - { - firstVertex = i; - } - else if (freeSourceEdge == null && this.editorUi.editor.graph.model.isEdge(cells[i]) && - this.editorUi.editor.graph.model.getTerminal(cells[i], true) == null) - { - freeSourceEdge = i; - } - - if (firstVertex != null && freeSourceEdge != null) - { - break; - } - } - - var dragSource = mxUtils.makeDraggable(elt, this.editorUi.editor.graph, mxUtils.bind(this, function(graph, evt, target, x, y) - { - if (this.updateThread != null) - { - window.clearTimeout(this.updateThread); - } - - if (cells != null && currentStyleTarget != null && activeArrow == styleTarget) - { - var tmp = graph.isCellSelected(currentStyleTarget.cell) ? graph.getSelectionCells() : [currentStyleTarget.cell]; - var updatedCells = this.updateShapes((graph.model.isEdge(currentStyleTarget.cell)) ? cells[0] : cells[firstVertex], tmp); - graph.setSelectionCells(updatedCells); - } - else if (cells != null && activeArrow != null && currentTargetState != null && activeArrow != styleTarget) - { - var index = (graph.model.isEdge(currentTargetState.cell) || freeSourceEdge == null) ? firstVertex : freeSourceEdge; - graph.setSelectionCells(this.dropAndConnect(currentTargetState.cell, cells, direction, index, evt)); - } - else - { - dropHandler.apply(this, arguments); - } - - if (this.editorUi.hoverIcons != null) - { - this.editorUi.hoverIcons.update(graph.view.getState(graph.getSelectionCell())); - } - }), preview, 0, 0, graph.autoscroll, true, true); - - // Stops dragging if cancel is pressed - graph.addListener(mxEvent.ESCAPE, function(sender, evt) - { - if (dragSource.isActive()) - { - dragSource.reset(); - } - }); - - // Overrides mouseDown to ignore popup triggers - var mouseDown = dragSource.mouseDown; - - dragSource.mouseDown = function(evt) - { - if (!mxEvent.isPopupTrigger(evt) && !mxEvent.isMultiTouchEvent(evt)) - { - graph.stopEditing(); - mouseDown.apply(this, arguments); - } - }; - - // Workaround for event redirection via image tag in quirks and IE8 - function createArrow(img, tooltip) - { - var arrow = null; - - if (mxClient.IS_IE && !mxClient.IS_SVG) - { - // Workaround for PNG images in IE6 - if (mxClient.IS_IE6 && document.compatMode != 'CSS1Compat') - { - arrow = document.createElement(mxClient.VML_PREFIX + ':image'); - arrow.setAttribute('src', img.src); - arrow.style.borderStyle = 'none'; - } - else - { - arrow = document.createElement('div'); - arrow.style.backgroundImage = 'url(' + img.src + ')'; - arrow.style.backgroundPosition = 'center'; - arrow.style.backgroundRepeat = 'no-repeat'; - } - - arrow.style.width = (img.width + 4) + 'px'; - arrow.style.height = (img.height + 4) + 'px'; - arrow.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; - } - else - { - arrow = mxUtils.createImage(img.src); - arrow.style.width = img.width + 'px'; - arrow.style.height = img.height + 'px'; - } - - if (tooltip != null) - { - arrow.setAttribute('title', tooltip); - } - - mxUtils.setOpacity(arrow, (img == this.refreshTarget) ? 30 : 20); - arrow.style.position = 'absolute'; - arrow.style.cursor = 'crosshair'; - - return arrow; - }; - - var currentTargetState = null; - var currentStateHandle = null; - var currentStyleTarget = null; - var activeTarget = false; - - var arrowUp = createArrow(this.triangleUp, mxResources.get('connect')); - var arrowRight = createArrow(this.triangleRight, mxResources.get('connect')); - var arrowDown = createArrow(this.triangleDown, mxResources.get('connect')); - var arrowLeft = createArrow(this.triangleLeft, mxResources.get('connect')); - var styleTarget = createArrow(this.refreshTarget, mxResources.get('replace')); - // Workaround for actual parentNode not being updated in old IE - var styleTargetParent = null; - var roundSource = createArrow(this.roundDrop); - var roundTarget = createArrow(this.roundDrop); - var direction = mxConstants.DIRECTION_NORTH; - var activeArrow = null; - - function checkArrow(x, y, bounds, arrow) - { - if (arrow.parentNode != null) - { - if (mxUtils.contains(bounds, x, y)) - { - mxUtils.setOpacity(arrow, 100); - activeArrow = arrow; - } - else - { - mxUtils.setOpacity(arrow, (arrow == styleTarget) ? 30 : 20); - } - } - - return bounds; - }; - - // Hides guides and preview if target is active - var dsCreatePreviewElement = dragSource.createPreviewElement; - - // Stores initial size of preview element - dragSource.createPreviewElement = function(graph) - { - var elt = dsCreatePreviewElement.apply(this, arguments); - - // Pass-through events required to tooltip on replace shape - if (mxClient.IS_SVG) - { - elt.style.pointerEvents = 'none'; - } - - this.previewElementWidth = elt.style.width; - this.previewElementHeight = elt.style.height; - - return elt; - }; - - // Shows/hides hover icons - var dragEnter = dragSource.dragEnter; - dragSource.dragEnter = function(graph, evt) - { - if (ui.hoverIcons != null) - { - ui.hoverIcons.setDisplay('none'); - } - - dragEnter.apply(this, arguments); - }; - - var dragExit = dragSource.dragExit; - dragSource.dragExit = function(graph, evt) - { - if (ui.hoverIcons != null) - { - ui.hoverIcons.setDisplay(''); - } - - dragExit.apply(this, arguments); - }; - - dragSource.dragOver = function(graph, evt) - { - mxDragSource.prototype.dragOver.apply(this, arguments); - - if (this.currentGuide != null && activeArrow != null) - { - this.currentGuide.hide(); - } - - if (this.previewElement != null) - { - var view = graph.view; - - if (currentStyleTarget != null && activeArrow == styleTarget) - { - this.previewElement.style.display = (graph.model.isEdge(currentStyleTarget.cell)) ? 'none' : ''; - - this.previewElement.style.left = currentStyleTarget.x + 'px'; - this.previewElement.style.top = currentStyleTarget.y + 'px'; - this.previewElement.style.width = currentStyleTarget.width + 'px'; - this.previewElement.style.height = currentStyleTarget.height + 'px'; - } - else if (currentTargetState != null && activeArrow != null) - { - var index = (graph.model.isEdge(currentTargetState.cell) || freeSourceEdge == null) ? firstVertex : freeSourceEdge; - var geo = sidebar.getDropAndConnectGeometry(currentTargetState.cell, cells[index], direction, cells); - var geo2 = (!graph.model.isEdge(currentTargetState.cell)) ? graph.getCellGeometry(currentTargetState.cell) : null; - var geo3 = graph.getCellGeometry(cells[index]); - var parent = graph.model.getParent(currentTargetState.cell); - var dx = view.translate.x * view.scale; - var dy = view.translate.y * view.scale; - - if (geo2 != null && !geo2.relative && graph.model.isVertex(parent) && parent != view.currentRoot) - { - var pState = view.getState(parent); - - dx = pState.x; - dy = pState.y; - } - - var dx2 = geo3.x; - var dy2 = geo3.y; - - // Ignores geometry of edges - if (graph.model.isEdge(cells[index])) - { - dx2 = 0; - dy2 = 0; - } - - // Shows preview at drop location - this.previewElement.style.left = ((geo.x - dx2) * view.scale + dx) + 'px'; - this.previewElement.style.top = ((geo.y - dy2) * view.scale + dy) + 'px'; - - if (cells.length == 1) - { - this.previewElement.style.width = (geo.width * view.scale) + 'px'; - this.previewElement.style.height = (geo.height * view.scale) + 'px'; - } - - this.previewElement.style.display = ''; - } - else if (dragSource.currentHighlight.state != null && - graph.model.isEdge(dragSource.currentHighlight.state.cell)) - { - // Centers drop cells when splitting edges - this.previewElement.style.left = Math.round(parseInt(this.previewElement.style.left) - - bounds.width * view.scale / 2) + 'px'; - this.previewElement.style.top = Math.round(parseInt(this.previewElement.style.top) - - bounds.height * view.scale / 2) + 'px'; - } - else - { - this.previewElement.style.width = this.previewElementWidth; - this.previewElement.style.height = this.previewElementHeight; - this.previewElement.style.display = ''; - } - } - }; - - var startTime = new Date().getTime(); - var timeOnTarget = 0; - var prev = null; - - // Gets source cell style to compare shape below - var sourceCellStyle = this.editorUi.editor.graph.getCellStyle(cells[0]); - - // Allows drop into cell only if target is a valid root - dragSource.getDropTarget = mxUtils.bind(this, function(graph, x, y, evt) - { - // Alt means no targets at all - // LATER: Show preview where result will go - var cell = (!mxEvent.isAltDown(evt) && cells != null) ? graph.getCellAt(x, y) : null; - - // Uses connectable parent vertex if one exists - if (cell != null && !this.graph.isCellConnectable(cell)) - { - var parent = this.graph.getModel().getParent(cell); - - if (this.graph.getModel().isVertex(parent) && this.graph.isCellConnectable(parent)) - { - cell = parent; - } - } - - // Ignores locked cells - if (graph.isCellLocked(cell)) - { - cell = null; - } - - var state = graph.view.getState(cell); - activeArrow = null; - var bbox = null; - - // Time on target - if (prev != state) - { - prev = state; - startTime = new Date().getTime(); - timeOnTarget = 0; - - if (this.updateThread != null) - { - window.clearTimeout(this.updateThread); - } - - if (state != null) - { - this.updateThread = window.setTimeout(function() - { - if (activeArrow == null) - { - prev = state; - dragSource.getDropTarget(graph, x, y, evt); - } - }, this.dropTargetDelay + 10); - } - } - else - { - timeOnTarget = new Date().getTime() - startTime; - } - - // Shift means disabled, delayed on cells with children, shows after this.dropTargetDelay, hides after 2500ms - if (timeOnTarget < 2500 && state != null && !mxEvent.isShiftDown(evt) && - // If shape is equal or target has no stroke, fill and gradient then use longer delay except for images - (((mxUtils.getValue(state.style, mxConstants.STYLE_SHAPE) != mxUtils.getValue(sourceCellStyle, mxConstants.STYLE_SHAPE) && - (mxUtils.getValue(state.style, mxConstants.STYLE_STROKECOLOR, mxConstants.NONE) != mxConstants.NONE || - mxUtils.getValue(state.style, mxConstants.STYLE_FILLCOLOR, mxConstants.NONE) != mxConstants.NONE || - mxUtils.getValue(state.style, mxConstants.STYLE_GRADIENTCOLOR, mxConstants.NONE) != mxConstants.NONE)) || - mxUtils.getValue(sourceCellStyle, mxConstants.STYLE_SHAPE) == 'image') || - timeOnTarget > 1500 || graph.model.isEdge(state.cell)) && (timeOnTarget > this.dropTargetDelay) && - ((graph.model.isVertex(state.cell) && firstVertex != null) || - (graph.model.isEdge(state.cell) && graph.model.isEdge(cells[0])))) - { - currentStyleTarget = state; - var tmp = (graph.model.isEdge(state.cell)) ? graph.view.getPoint(state) : - new mxPoint(state.getCenterX(), state.getCenterY()); - tmp = new mxRectangle(tmp.x - this.refreshTarget.width / 2, tmp.y - this.refreshTarget.height / 2, - this.refreshTarget.width, this.refreshTarget.height); - - styleTarget.style.left = Math.floor(tmp.x) + 'px'; - styleTarget.style.top = Math.floor(tmp.y) + 'px'; - - if (styleTargetParent == null) - { - graph.container.appendChild(styleTarget); - styleTargetParent = styleTarget.parentNode; - } - - checkArrow(x, y, tmp, styleTarget); - } - // Does not reset on ignored edges - else if (currentStyleTarget == null || !mxUtils.contains(currentStyleTarget, x, y) || - (timeOnTarget > 1500 && !mxEvent.isShiftDown(evt))) - { - currentStyleTarget = null; - - if (styleTargetParent != null) - { - styleTarget.parentNode.removeChild(styleTarget); - styleTargetParent = null; - } - } - else if (currentStyleTarget != null && styleTargetParent != null) - { - // Sets active Arrow as side effect - var tmp = (graph.model.isEdge(currentStyleTarget.cell)) ? graph.view.getPoint(currentStyleTarget) : new mxPoint(currentStyleTarget.getCenterX(), currentStyleTarget.getCenterY()); - tmp = new mxRectangle(tmp.x - this.refreshTarget.width / 2, tmp.y - this.refreshTarget.height / 2, - this.refreshTarget.width, this.refreshTarget.height); - checkArrow(x, y, tmp, styleTarget); - } - - // Checks if inside bounds - if (activeTarget && currentTargetState != null && !mxEvent.isAltDown(evt) && activeArrow == null) - { - // LATER: Use hit-detection for edges - bbox = mxRectangle.fromRectangle(currentTargetState); - - if (graph.model.isEdge(currentTargetState.cell)) - { - var pts = currentTargetState.absolutePoints; - - if (roundSource.parentNode != null) - { - var p0 = pts[0]; - bbox.add(checkArrow(x, y, new mxRectangle(p0.x - this.roundDrop.width / 2, - p0.y - this.roundDrop.height / 2, this.roundDrop.width, this.roundDrop.height), roundSource)); - } - - if (roundTarget.parentNode != null) - { - var pe = pts[pts.length - 1]; - bbox.add(checkArrow(x, y, new mxRectangle(pe.x - this.roundDrop.width / 2, - pe.y - this.roundDrop.height / 2, - this.roundDrop.width, this.roundDrop.height), roundTarget)); - } - } - else - { - var bds = mxRectangle.fromRectangle(currentTargetState); - - // Uses outer bounding box to take rotation into account - if (currentTargetState.shape != null && currentTargetState.shape.boundingBox != null) - { - bds = mxRectangle.fromRectangle(currentTargetState.shape.boundingBox); - } - - bds.grow(this.graph.tolerance); - bds.grow(HoverIcons.prototype.arrowSpacing); - - var handler = this.graph.selectionCellsHandler.getHandler(currentTargetState.cell); - - if (handler != null) - { - bds.x -= handler.horizontalOffset / 2; - bds.y -= handler.verticalOffset / 2; - bds.width += handler.horizontalOffset; - bds.height += handler.verticalOffset; - - // Adds bounding box of rotation handle to avoid overlap - if (handler.rotationShape != null && handler.rotationShape.node != null && - handler.rotationShape.node.style.visibility != 'hidden' && - handler.rotationShape.node.style.display != 'none' && - handler.rotationShape.boundingBox != null) - { - bds.add(handler.rotationShape.boundingBox); - } - } - - bbox.add(checkArrow(x, y, new mxRectangle(currentTargetState.getCenterX() - this.triangleUp.width / 2, - bds.y - this.triangleUp.height, this.triangleUp.width, this.triangleUp.height), arrowUp)); - bbox.add(checkArrow(x, y, new mxRectangle(bds.x + bds.width, - currentTargetState.getCenterY() - this.triangleRight.height / 2, - this.triangleRight.width, this.triangleRight.height), arrowRight)); - bbox.add(checkArrow(x, y, new mxRectangle(currentTargetState.getCenterX() - this.triangleDown.width / 2, - bds.y + bds.height, this.triangleDown.width, this.triangleDown.height), arrowDown)); - bbox.add(checkArrow(x, y, new mxRectangle(bds.x - this.triangleLeft.width, - currentTargetState.getCenterY() - this.triangleLeft.height / 2, - this.triangleLeft.width, this.triangleLeft.height), arrowLeft)); - } - - // Adds tolerance - if (bbox != null) - { - bbox.grow(10); - } - } - - direction = mxConstants.DIRECTION_NORTH; - - if (activeArrow == arrowRight) - { - direction = mxConstants.DIRECTION_EAST; - } - else if (activeArrow == arrowDown || activeArrow == roundTarget) - { - direction = mxConstants.DIRECTION_SOUTH; - } - else if (activeArrow == arrowLeft) - { - direction = mxConstants.DIRECTION_WEST; - } - - if (currentStyleTarget != null && activeArrow == styleTarget) - { - state = currentStyleTarget; - } - - var validTarget = (firstVertex == null || graph.isCellConnectable(cells[firstVertex])) && - ((graph.model.isEdge(cell) && firstVertex != null) || - (graph.model.isVertex(cell) && graph.isCellConnectable(cell))); - - // Drop arrows shown after this.dropTargetDelay, hidden after 5 secs, switches arrows after 500ms - if ((currentTargetState != null && timeOnTarget >= 5000) || - (currentTargetState != state && - (bbox == null || !mxUtils.contains(bbox, x, y) || - (timeOnTarget > 500 && activeArrow == null && validTarget)))) - { - activeTarget = false; - currentTargetState = ((timeOnTarget < 5000 && timeOnTarget > this.dropTargetDelay) || graph.model.isEdge(cell)) ? state : null; - - if (currentTargetState != null && validTarget) - { - var elts = [roundSource, roundTarget, arrowUp, arrowRight, arrowDown, arrowLeft]; - - for (var i = 0; i < elts.length; i++) - { - if (elts[i].parentNode != null) - { - elts[i].parentNode.removeChild(elts[i]); - } - } - - if (graph.model.isEdge(cell)) - { - var pts = state.absolutePoints; - - if (pts != null) - { - var p0 = pts[0]; - var pe = pts[pts.length - 1]; - var tol = graph.tolerance; - var box = new mxRectangle(x - tol, y - tol, 2 * tol, 2 * tol); - - roundSource.style.left = Math.floor(p0.x - this.roundDrop.width / 2) + 'px'; - roundSource.style.top = Math.floor(p0.y - this.roundDrop.height / 2) + 'px'; - - roundTarget.style.left = Math.floor(pe.x - this.roundDrop.width / 2) + 'px'; - roundTarget.style.top = Math.floor(pe.y - this.roundDrop.height / 2) + 'px'; - - if (graph.model.getTerminal(cell, true) == null) - { - graph.container.appendChild(roundSource); - } - - if (graph.model.getTerminal(cell, false) == null) - { - graph.container.appendChild(roundTarget); - } - } - } - else - { - var bds = mxRectangle.fromRectangle(state); - - // Uses outer bounding box to take rotation into account - if (state.shape != null && state.shape.boundingBox != null) - { - bds = mxRectangle.fromRectangle(state.shape.boundingBox); - } - - bds.grow(this.graph.tolerance); - bds.grow(HoverIcons.prototype.arrowSpacing); - - var handler = this.graph.selectionCellsHandler.getHandler(state.cell); - - if (handler != null) - { - bds.x -= handler.horizontalOffset / 2; - bds.y -= handler.verticalOffset / 2; - bds.width += handler.horizontalOffset; - bds.height += handler.verticalOffset; - - // Adds bounding box of rotation handle to avoid overlap - if (handler.rotationShape != null && handler.rotationShape.node != null && - handler.rotationShape.node.style.visibility != 'hidden' && - handler.rotationShape.node.style.display != 'none' && - handler.rotationShape.boundingBox != null) - { - bds.add(handler.rotationShape.boundingBox); - } - } - - arrowUp.style.left = Math.floor(state.getCenterX() - this.triangleUp.width / 2) + 'px'; - arrowUp.style.top = Math.floor(bds.y - this.triangleUp.height) + 'px'; - - arrowRight.style.left = Math.floor(bds.x + bds.width) + 'px'; - arrowRight.style.top = Math.floor(state.getCenterY() - this.triangleRight.height / 2) + 'px'; - - arrowDown.style.left = arrowUp.style.left - arrowDown.style.top = Math.floor(bds.y + bds.height) + 'px'; - - arrowLeft.style.left = Math.floor(bds.x - this.triangleLeft.width) + 'px'; - arrowLeft.style.top = arrowRight.style.top; - - if (state.style['portConstraint'] != 'eastwest') - { - graph.container.appendChild(arrowUp); - graph.container.appendChild(arrowDown); - } - - graph.container.appendChild(arrowRight); - graph.container.appendChild(arrowLeft); - } - - // Hides handle for cell under mouse - if (state != null) - { - currentStateHandle = graph.selectionCellsHandler.getHandler(state.cell); - - if (currentStateHandle != null && currentStateHandle.setHandlesVisible != null) - { - currentStateHandle.setHandlesVisible(false); - } - } - - activeTarget = true; - } - else - { - var elts = [roundSource, roundTarget, arrowUp, arrowRight, arrowDown, arrowLeft]; - - for (var i = 0; i < elts.length; i++) - { - if (elts[i].parentNode != null) - { - elts[i].parentNode.removeChild(elts[i]); - } - } - } - } - - if (!activeTarget && currentStateHandle != null) - { - currentStateHandle.setHandlesVisible(true); - } - - // Handles drop target - var target = ((!mxEvent.isAltDown(evt) || mxEvent.isShiftDown(evt)) && - !(currentStyleTarget != null && activeArrow == styleTarget)) ? - mxDragSource.prototype.getDropTarget.apply(this, arguments) : null; - var model = graph.getModel(); - - if (target != null) - { - if (activeArrow != null || !graph.isSplitTarget(target, cells, evt)) - { - // Selects parent group as drop target - while (target != null && !graph.isValidDropTarget(target, cells, evt) && model.isVertex(model.getParent(target))) - { - target = model.getParent(target); - } - - if (graph.view.currentRoot == target || (!graph.isValidRoot(target) && - graph.getModel().getChildCount(target) == 0) || - graph.isCellLocked(target) || model.isEdge(target)) - { - target = null; - } - } - } - - return target; - }); - - dragSource.stopDrag = function() - { - mxDragSource.prototype.stopDrag.apply(this, arguments); - - var elts = [roundSource, roundTarget, styleTarget, arrowUp, arrowRight, arrowDown, arrowLeft]; - - for (var i = 0; i < elts.length; i++) - { - if (elts[i].parentNode != null) - { - elts[i].parentNode.removeChild(elts[i]); - } - } - - if (currentTargetState != null && currentStateHandle != null) - { - currentStateHandle.reset(); - } - - currentStateHandle = null; - currentTargetState = null; - currentStyleTarget = null; - styleTargetParent = null; - activeArrow = null; - }; - - return dragSource; -}; - -/** - * Adds a handler for inserting the cell with a single click. - */ -Sidebar.prototype.itemClicked = function(cells, ds, evt, elt) -{ - var graph = this.editorUi.editor.graph; - graph.container.focus(); - - // Alt+Click inserts and connects - if (mxEvent.isAltDown(evt)) - { - if (graph.getSelectionCount() == 1 && graph.model.isVertex(graph.getSelectionCell())) - { - var firstVertex = null; - - for (var i = 0; i < cells.length && firstVertex == null; i++) - { - if (graph.model.isVertex(cells[i])) - { - firstVertex = i; - } - } - - if (firstVertex != null) - { - graph.setSelectionCells(this.dropAndConnect(graph.getSelectionCell(), cells, (mxEvent.isMetaDown(evt) || mxEvent.isControlDown(evt)) ? - (mxEvent.isShiftDown(evt) ? mxConstants.DIRECTION_WEST : mxConstants.DIRECTION_NORTH) : - (mxEvent.isShiftDown(evt) ? mxConstants.DIRECTION_EAST : mxConstants.DIRECTION_SOUTH), - firstVertex, evt)); - graph.scrollCellToVisible(graph.getSelectionCell()); - } - } - } - // Shift+Click updates shape - else if (mxEvent.isShiftDown(evt) && !graph.isSelectionEmpty()) - { - this.updateShapes(cells[0], graph.getSelectionCells()); - graph.scrollCellToVisible(graph.getSelectionCell()); - } - else - { - var pt = graph.getFreeInsertPoint(); - - if (mxEvent.isShiftDown(evt)) - { - var bounds = graph.getGraphBounds(); - var tr = graph.view.translate; - var s = graph.view.scale; - pt.x = bounds.x / s - tr.x + bounds.width / s + graph.gridSize; - pt.y = bounds.y / s - tr.y; - } - - ds.drop(graph, evt, null, pt.x, pt.y, true); - - if (this.editorUi.hoverIcons != null && (mxEvent.isTouchEvent(evt) || mxEvent.isPenEvent(evt))) - { - this.editorUi.hoverIcons.update(graph.view.getState(graph.getSelectionCell())); - } - } -}; - -/** - * Adds a handler for inserting the cell with a single click. - */ -Sidebar.prototype.addClickHandler = function(elt, ds, cells) -{ - var graph = this.editorUi.editor.graph; - var oldMouseDown = ds.mouseDown; - var oldMouseMove = ds.mouseMove; - var oldMouseUp = ds.mouseUp; - var tol = graph.tolerance; - var first = null; - var sb = this; - - ds.mouseDown =function(evt) - { - oldMouseDown.apply(this, arguments); - first = new mxPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt)); - - if (this.dragElement != null) - { - this.dragElement.style.display = 'none'; - mxUtils.setOpacity(elt, 50); - } - }; - - ds.mouseMove = function(evt) - { - if (this.dragElement != null && this.dragElement.style.display == 'none' && - first != null && (Math.abs(first.x - mxEvent.getClientX(evt)) > tol || - Math.abs(first.y - mxEvent.getClientY(evt)) > tol)) - { - this.dragElement.style.display = ''; - mxUtils.setOpacity(elt, 100); - } - - oldMouseMove.apply(this, arguments); - }; - - ds.mouseUp = function(evt) - { - if (!mxEvent.isPopupTrigger(evt) && this.currentGraph == null && - this.dragElement != null && this.dragElement.style.display == 'none') - { - sb.itemClicked(cells, ds, evt, elt); - } - - oldMouseUp.apply(ds, arguments); - mxUtils.setOpacity(elt, 100); - first = null; - - // Blocks tooltips on this element after single click - sb.currentElt = elt; - }; -}; - -/** - * Creates a drop handler for inserting the given cells. - */ -Sidebar.prototype.createVertexTemplateEntry = function(style, width, height, value, title, showLabel, showTitle, tags) -{ - tags = (tags != null && tags.length > 0) ? tags : title.toLowerCase(); - - return this.addEntry(tags, mxUtils.bind(this, function() - { - return this.createVertexTemplate(style, width, height, value, title, showLabel, showTitle); - })); -} - -/** - * Creates a drop handler for inserting the given cells. - */ -Sidebar.prototype.createVertexTemplate = function(style, width, height, value, title, showLabel, showTitle, allowCellsInserted) -{ - var cells = [new mxCell((value != null) ? value : '', new mxGeometry(0, 0, width, height), style)]; - cells[0].vertex = true; - - return this.createVertexTemplateFromCells(cells, width, height, title, showLabel, showTitle, allowCellsInserted); -}; - -/** - * Creates a drop handler for inserting the given cells. - */ -Sidebar.prototype.createVertexTemplateFromData = function(data, width, height, title, showLabel, showTitle, allowCellsInserted) -{ - var doc = mxUtils.parseXml(this.graph.decompress(data)); - var codec = new mxCodec(doc); - - var model = new mxGraphModel(); - codec.decode(doc.documentElement, model); - - var cells = this.graph.cloneCells(model.root.getChildAt(0).children); - - return this.createVertexTemplateFromCells(cells, width, height, title, showLabel, showTitle, allowCellsInserted); -}; - -/** - * Creates a drop handler for inserting the given cells. - */ -Sidebar.prototype.createVertexTemplateFromCells = function(cells, width, height, title, showLabel, showTitle, allowCellsInserted) -{ - // Use this line to convert calls to this function with lots of boilerplate code for creating cells - //console.trace('xml', this.graph.compress(mxUtils.getXml(this.graph.encodeCells(cells))), cells); - return this.createItem(cells, title, showLabel, showTitle, width, height, allowCellsInserted); -}; - -/** - * - */ -Sidebar.prototype.createEdgeTemplateEntry = function(style, width, height, value, title, showLabel, tags, allowCellsInserted) -{ - tags = (tags != null && tags.length > 0) ? tags : title.toLowerCase(); - - return this.addEntry(tags, mxUtils.bind(this, function() - { - return this.createEdgeTemplate(style, width, height, value, title, showLabel, allowCellsInserted); - })); -}; - -/** - * Creates a drop handler for inserting the given cells. - */ -Sidebar.prototype.createEdgeTemplate = function(style, width, height, value, title, showLabel, allowCellsInserted) -{ - var cell = new mxCell((value != null) ? value : '', new mxGeometry(0, 0, width, height), style); - cell.geometry.setTerminalPoint(new mxPoint(0, height), true); - cell.geometry.setTerminalPoint(new mxPoint(width, 0), false); - cell.geometry.relative = true; - cell.edge = true; - - return this.createEdgeTemplateFromCells([cell], width, height, title, showLabel, allowCellsInserted); -}; - -/** - * Creates a drop handler for inserting the given cells. - */ -Sidebar.prototype.createEdgeTemplateFromCells = function(cells, width, height, title, showLabel, allowCellsInserted) -{ - return this.createItem(cells, title, showLabel, true, width, height, allowCellsInserted); -}; - -/** - * Adds the given palette. - */ -Sidebar.prototype.addPaletteFunctions = function(id, title, expanded, fns) -{ - this.addPalette(id, title, expanded, mxUtils.bind(this, function(content) - { - for (var i = 0; i < fns.length; i++) - { - content.appendChild(fns[i](content)); - } - })); -}; - -/** - * Adds the given palette. - */ -Sidebar.prototype.addPalette = function(id, title, expanded, onInit) -{ - var elt = this.createTitle(title); - this.container.appendChild(elt); - - var div = document.createElement('div'); - div.className = 'geSidebar'; - - // Disables built-in pan and zoom in IE10 and later - if (mxClient.IS_POINTER) - { - div.style.touchAction = 'none'; - } - - if (expanded) - { - onInit(div); - onInit = null; - } - else - { - div.style.display = 'none'; - } - - this.addFoldingHandler(elt, div, onInit); - - var outer = document.createElement('div'); - outer.appendChild(div); - this.container.appendChild(outer); - - // Keeps references to the DOM nodes - if (id != null) - { - this.palettes[id] = [elt, outer]; - } - - return div; -}; - -/** - * Create the given title element. - */ -Sidebar.prototype.addFoldingHandler = function(title, content, funct) -{ - var initialized = false; - - // Avoids mixed content warning in IE6-8 - if (!mxClient.IS_IE || document.documentMode >= 8) - { - title.style.backgroundImage = (content.style.display == 'none') ? - 'url(\'' + this.collapsedImage + '\')' : 'url(\'' + this.expandedImage + '\')'; - } - - title.style.backgroundRepeat = 'no-repeat'; - title.style.backgroundPosition = '0% 50%'; - - mxEvent.addListener(title, 'click', mxUtils.bind(this, function(evt) - { - if (content.style.display == 'none') - { - if (!initialized) - { - initialized = true; - - if (funct != null) - { - // Wait cursor does not show up on Mac - title.style.cursor = 'wait'; - var prev = title.innerHTML; - title.innerHTML = mxResources.get('loading') + '...'; - - window.setTimeout(function() - { - var fo = mxClient.NO_FO; - mxClient.NO_FO = Editor.prototype.originalNoForeignObject; - funct(content); - mxClient.NO_FO = fo; - content.style.display = 'block'; - title.style.cursor = ''; - title.innerHTML = prev; - }, 0); - } - else - { - content.style.display = 'block'; - } - } - else - { - content.style.display = 'block'; - } - - title.style.backgroundImage = 'url(\'' + this.expandedImage + '\')'; - } - else - { - title.style.backgroundImage = 'url(\'' + this.collapsedImage + '\')'; - content.style.display = 'none'; - } - - mxEvent.consume(evt); - })); -}; - -/** - * Removes the palette for the given ID. - */ -Sidebar.prototype.removePalette = function(id) -{ - var elts = this.palettes[id]; - - if (elts != null) - { - this.palettes[id] = null; - - for (var i = 0; i < elts.length; i++) - { - this.container.removeChild(elts[i]); - } - - return true; - } - - return false; -}; - -/** - * Adds the given image palette. - */ -Sidebar.prototype.addImagePalette = function(id, title, prefix, postfix, items, titles, tags) -{ - var showTitles = titles != null; - var fns = []; - - for (var i = 0; i < items.length; i++) - { - (mxUtils.bind(this, function(item, title, tmpTags) - { - if (tmpTags == null) - { - var slash = item.lastIndexOf('/'); - var dot = item.lastIndexOf('.'); - tmpTags = item.substring((slash >= 0) ? slash + 1 : 0, (dot >= 0) ? dot : item.length).replace(/[-_]/g, ' '); - } - - fns.push(this.createVertexTemplateEntry('image;html=1;labelBackgroundColor=#ffffff;image=' + prefix + item + postfix, - this.defaultImageWidth, this.defaultImageHeight, '', title, title != null, null, this.filterTags(tmpTags))); - }))(items[i], (titles != null) ? titles[i] : null, (tags != null) ? tags[items[i]] : null); - } - - this.addPaletteFunctions(id, title, false, fns); -}; - -/** - * Creates the array of tags for the given stencil. Duplicates are allowed and will be filtered out later. - */ -Sidebar.prototype.getTagsForStencil = function(packageName, stencilName, moreTags) -{ - var tags = packageName.split('.'); - - for (var i = 1; i < tags.length; i++) - { - tags[i] = tags[i].replace(/_/g, ' ') - } - - tags.push(stencilName.replace(/_/g, ' ')); - - if (moreTags != null) - { - tags.push(moreTags); - } - - return tags.slice(1, tags.length); -}; - -/** - * Adds the given stencil palette. - */ -Sidebar.prototype.addStencilPalette = function(id, title, stencilFile, style, ignore, onInit, scale, tags, customFns) -{ - scale = (scale != null) ? scale : 1; - - if (this.addStencilsToIndex) - { - // LATER: Handle asynchronous loading dependency - var fns = []; - - if (customFns != null) - { - for (var i = 0; i < customFns.length; i++) - { - fns.push(customFns[i]); - } - } - - mxStencilRegistry.loadStencilSet(stencilFile, mxUtils.bind(this, function(packageName, stencilName, displayName, w, h) - { - if (ignore == null || mxUtils.indexOf(ignore, stencilName) < 0) - { - var tmp = this.getTagsForStencil(packageName, stencilName); - var tmpTags = (tags != null) ? tags[stencilName] : null; - - if (tmpTags != null) - { - tmp.push(tmpTags); - } - - fns.push(this.createVertexTemplateEntry('shape=' + packageName + stencilName.toLowerCase() + style, - Math.round(w * scale), Math.round(h * scale), '', stencilName.replace(/_/g, ' '), null, null, - this.filterTags(tmp.join(' ')))); - } - }), true, true); - - this.addPaletteFunctions(id, title, false, fns); - } - else - { - this.addPalette(id, title, false, mxUtils.bind(this, function(content) - { - if (style == null) - { - style = ''; - } - - if (onInit != null) - { - onInit.call(this, content); - } - - if (customFns != null) - { - for (var i = 0; i < customFns.length; i++) - { - customFns[i](content); - } - } - - mxStencilRegistry.loadStencilSet(stencilFile, mxUtils.bind(this, function(packageName, stencilName, displayName, w, h) - { - if (ignore == null || mxUtils.indexOf(ignore, stencilName) < 0) - { - content.appendChild(this.createVertexTemplate('shape=' + packageName + stencilName.toLowerCase() + style, - Math.round(w * scale), Math.round(h * scale), '', stencilName.replace(/_/g, ' '), true)); - } - }), true); - })); - } -}; - -/** - * Adds the given stencil palette. - */ -Sidebar.prototype.destroy = function() -{ - if (this.graph != null) - { - if (this.graph.container != null && this.graph.container.parentNode != null) - { - this.graph.container.parentNode.removeChild(this.graph.container); - } - - this.graph.destroy(); - this.graph = null; - } - - if (this.pointerUpHandler != null) - { - mxEvent.removeListener(document, (mxClient.IS_POINTER) ? 'pointerup' : 'mouseup', this.pointerUpHandler); - this.pointerUpHandler = null; - } - - if (this.pointerDownHandler != null) - { - mxEvent.removeListener(document, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown', this.pointerDownHandler); - this.pointerDownHandler = null; - } - - if (this.pointerMoveHandler != null) - { - mxEvent.removeListener(document, (mxClient.IS_POINTER) ? 'pointermove' : 'mousemove', this.pointerMoveHandler); - this.pointerMoveHandler = null; - } - - if (this.pointerOutHandler != null) - { - mxEvent.removeListener(document, (mxClient.IS_POINTER) ? 'pointerout' : 'mouseout', this.pointerOutHandler); - this.pointerOutHandler = null; - } -}; diff --git a/media/grapheditor/js/Toolbar.js b/media/grapheditor/js/Toolbar.js deleted file mode 100644 index be50f3bbaf..0000000000 --- a/media/grapheditor/js/Toolbar.js +++ /dev/null @@ -1,957 +0,0 @@ -/** - * Copyright (c) 2006-2012, JGraph Ltd - */ -/** - * Construcs a new toolbar for the given editor. - */ -function Toolbar(editorUi, container) -{ - this.editorUi = editorUi; - this.container = container; - this.staticElements = []; - this.init(); - - // Global handler to hide the current menu - this.gestureHandler = mxUtils.bind(this, function(evt) - { - if (this.editorUi.currentMenu != null && mxEvent.getSource(evt) != this.editorUi.currentMenu.div) - { - this.hideMenu(); - } - }); - - mxEvent.addGestureListeners(document, this.gestureHandler); -}; - -/** - * Image for the dropdown arrow. - */ -Toolbar.prototype.dropdownImage = (!mxClient.IS_SVG) ? IMAGE_PATH + '/dropdown.gif' : 'data:image/gif;base64,R0lGODlhDQANAIABAHt7e////yH/C1hNUCBEYXRhWE1QPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4gPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS4wLWMwNjAgNjEuMTM0Nzc3LCAyMDEwLzAyLzEyLTE3OjMyOjAwICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtbG5zOnhtcE1NPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvbW0vIiB4bWxuczpzdFJlZj0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL3NUeXBlL1Jlc291cmNlUmVmIyIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpCREM1NkJFMjE0NEMxMUU1ODk1Q0M5MjQ0MTA4QjNDMSIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpCREM1NkJFMzE0NEMxMUU1ODk1Q0M5MjQ0MTA4QjNDMSI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkQzOUMzMjZCMTQ0QjExRTU4OTVDQzkyNDQxMDhCM0MxIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkQzOUMzMjZDMTQ0QjExRTU4OTVDQzkyNDQxMDhCM0MxIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAIfkEAQAAAQAsAAAAAA0ADQAAAhGMj6nL3QAjVHIu6azbvPtWAAA7'; - -/** - * Image element for the dropdown arrow. - */ -Toolbar.prototype.dropdownImageHtml = ''; - -/** - * Defines the background for selected buttons. - */ -Toolbar.prototype.selectedBackground = '#d0d0d0'; - -/** - * Defines the background for selected buttons. - */ -Toolbar.prototype.unselectedBackground = 'none'; - -/** - * Array that contains the DOM nodes that should never be removed. - */ -Toolbar.prototype.staticElements = null; - -/** - * Adds the toolbar elements. - */ -Toolbar.prototype.init = function() -{ - var sw = screen.width; - - // Takes into account initial compact mode - sw -= (screen.height > 740) ? 56 : 0; - - if (sw >= 700) - { - var formatMenu = this.addMenu('', mxResources.get('view') + ' (' + mxResources.get('panTooltip') + ')', true, 'viewPanels', null, true); - this.addDropDownArrow(formatMenu, 'geSprite-formatpanel', 38, 50, -4, -3, 36, -8); - this.addSeparator(); - } - - var viewMenu = this.addMenu('', mxResources.get('zoom') + ' (Alt+Mousewheel)', true, 'viewZoom', null, true); - viewMenu.showDisabled = true; - viewMenu.style.whiteSpace = 'nowrap'; - viewMenu.style.position = 'relative'; - viewMenu.style.overflow = 'hidden'; - - if (EditorUi.compactUi) - { - viewMenu.style.width = (mxClient.IS_QUIRKS) ? '58px' : '50px'; - } - else - { - viewMenu.style.width = (mxClient.IS_QUIRKS) ? '62px' : '36px'; - } - - if (sw >= 420) - { - this.addSeparator(); - var elts = this.addItems(['zoomIn', 'zoomOut']); - elts[0].setAttribute('title', mxResources.get('zoomIn') + ' (' + this.editorUi.actions.get('zoomIn').shortcut + ')'); - elts[1].setAttribute('title', mxResources.get('zoomOut') + ' (' + this.editorUi.actions.get('zoomOut').shortcut + ')'); - } - - // Updates the label if the scale changes - this.updateZoom = mxUtils.bind(this, function() - { - viewMenu.innerHTML = Math.round(this.editorUi.editor.graph.view.scale * 100) + '%' + - this.dropdownImageHtml; - - if (EditorUi.compactUi) - { - viewMenu.getElementsByTagName('img')[0].style.right = '1px'; - viewMenu.getElementsByTagName('img')[0].style.top = '5px'; - } - }); - - this.editorUi.editor.graph.view.addListener(mxEvent.EVENT_SCALE, this.updateZoom); - this.editorUi.editor.addListener('resetGraphView', this.updateZoom); - - var elts = this.addItems(['-', 'undo', 'redo']); - elts[1].setAttribute('title', mxResources.get('undo') + ' (' + this.editorUi.actions.get('undo').shortcut + ')'); - elts[2].setAttribute('title', mxResources.get('redo') + ' (' + this.editorUi.actions.get('redo').shortcut + ')'); - - if (sw >= 320) - { - var elts = this.addItems(['-', 'delete']); - elts[1].setAttribute('title', mxResources.get('delete') + ' (' + this.editorUi.actions.get('delete').shortcut + ')'); - } - - if (sw >= 550) - { - this.addItems(['-', 'toFront', 'toBack']); - } - - if (sw >= 740) - { - this.addItems(['-', 'fillColor']); - - if (sw >= 780) - { - this.addItems(['strokeColor']); - - if (sw >= 820) - { - this.addItems(['shadow']); - } - } - } - - if (sw >= 400) - { - this.addSeparator(); - - if (sw >= 440) - { - this.edgeShapeMenu = this.addMenuFunction('', mxResources.get('connection'), false, mxUtils.bind(this, function(menu) - { - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_SHAPE, 'width'], [null, null], 'geIcon geSprite geSprite-connection', null, true).setAttribute('title', mxResources.get('line')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_SHAPE, 'width'], ['link', null], 'geIcon geSprite geSprite-linkedge', null, true).setAttribute('title', mxResources.get('link')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_SHAPE, 'width'], ['flexArrow', null], 'geIcon geSprite geSprite-arrow', null, true).setAttribute('title', mxResources.get('arrow')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_SHAPE, 'width'], ['arrow', null], 'geIcon geSprite geSprite-simplearrow', null, true).setAttribute('title', mxResources.get('simpleArrow')); - })); - - this.addDropDownArrow(this.edgeShapeMenu, 'geSprite-connection', 44, 50, 0, 0, 22, -4); - } - - this.edgeStyleMenu = this.addMenuFunction('geSprite-orthogonal', mxResources.get('waypoints'), false, mxUtils.bind(this, function(menu) - { - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], [null, null, null], 'geIcon geSprite geSprite-straight', null, true).setAttribute('title', mxResources.get('straight')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['orthogonalEdgeStyle', null, null], 'geIcon geSprite geSprite-orthogonal', null, true).setAttribute('title', mxResources.get('orthogonal')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['elbowEdgeStyle', null, null, null], 'geIcon geSprite geSprite-horizontalelbow', null, true).setAttribute('title', mxResources.get('simple')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['elbowEdgeStyle', 'vertical', null, null], 'geIcon geSprite geSprite-verticalelbow', null, true).setAttribute('title', mxResources.get('simple')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['isometricEdgeStyle', null, null, null], 'geIcon geSprite geSprite-horizontalisometric', null, true).setAttribute('title', mxResources.get('isometric')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_ELBOW, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['isometricEdgeStyle', 'vertical', null, null], 'geIcon geSprite geSprite-verticalisometric', null, true).setAttribute('title', mxResources.get('isometric')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['orthogonalEdgeStyle', '1', null], 'geIcon geSprite geSprite-curved', null, true).setAttribute('title', mxResources.get('curved')); - this.editorUi.menus.edgeStyleChange(menu, '', [mxConstants.STYLE_EDGE, mxConstants.STYLE_CURVED, mxConstants.STYLE_NOEDGESTYLE], ['entityRelationEdgeStyle', null, null], 'geIcon geSprite geSprite-entity', null, true).setAttribute('title', mxResources.get('entityRelation')); - })); - - this.addDropDownArrow(this.edgeStyleMenu, 'geSprite-orthogonal', 44, 50, 0, 0, 22, -4); - } - - this.addSeparator(); - - var insertMenu = this.addMenu('', mxResources.get('insert') + ' (' + mxResources.get('doubleClickTooltip') + ')', true, 'insert', null, true); - this.addDropDownArrow(insertMenu, 'geSprite-plus', 38, 48, -4, -3, 36, -8); -}; - -/** - * Adds the toolbar elements. - */ -Toolbar.prototype.addDropDownArrow = function(menu, sprite, width, atlasWidth, left, top, atlasDelta, atlasLeft) -{ - atlasDelta = (atlasDelta != null) ? atlasDelta : 32; - left = (EditorUi.compactUi) ? left : atlasLeft; - - menu.style.whiteSpace = 'nowrap'; - menu.style.overflow = 'hidden'; - menu.style.position = 'relative'; - menu.innerHTML = '
' + - this.dropdownImageHtml; - menu.style.width = (mxClient.IS_QUIRKS) ? atlasWidth + 'px' : (atlasWidth - atlasDelta) + 'px'; - - if (mxClient.IS_QUIRKS) - { - menu.style.height = (EditorUi.compactUi) ? '24px' : '26px'; - } - - // Fix for item size in kennedy theme - if (EditorUi.compactUi) - { - menu.getElementsByTagName('img')[0].style.left = '24px'; - menu.getElementsByTagName('img')[0].style.top = '5px'; - menu.style.width = (mxClient.IS_QUIRKS) ? width + 'px' : (width - 10) + 'px'; - } -}; - -/** - * Sets the current font name. - */ -Toolbar.prototype.setFontName = function(value) -{ - if (this.fontMenu != null) - { - this.fontMenu.innerHTML = '
' + - mxUtils.htmlEntities(value) + '
' + this.dropdownImageHtml; - } -}; - -/** - * Sets the current font name. - */ -Toolbar.prototype.setFontSize = function(value) -{ - if (this.sizeMenu != null) - { - this.sizeMenu.innerHTML = '
' + - value + '
' + this.dropdownImageHtml; - } -}; - -/** - * Hides the current menu. - */ -Toolbar.prototype.createTextToolbar = function() -{ - var graph = this.editorUi.editor.graph; - - var styleElt = this.addMenu('', mxResources.get('style'), true, 'formatBlock'); - styleElt.style.position = 'relative'; - styleElt.style.whiteSpace = 'nowrap'; - styleElt.style.overflow = 'hidden'; - styleElt.innerHTML = mxResources.get('style') + this.dropdownImageHtml; - - if (EditorUi.compactUi) - { - styleElt.style.paddingRight = '18px'; - styleElt.getElementsByTagName('img')[0].style.right = '1px'; - styleElt.getElementsByTagName('img')[0].style.top = '5px'; - } - - this.addSeparator(); - - this.fontMenu = this.addMenu('', mxResources.get('fontFamily'), true, 'fontFamily'); - this.fontMenu.style.position = 'relative'; - this.fontMenu.style.whiteSpace = 'nowrap'; - this.fontMenu.style.overflow = 'hidden'; - this.fontMenu.style.width = (mxClient.IS_QUIRKS) ? '80px' : '60px'; - - this.setFontName(Menus.prototype.defaultFont); - - if (EditorUi.compactUi) - { - this.fontMenu.style.paddingRight = '18px'; - this.fontMenu.getElementsByTagName('img')[0].style.right = '1px'; - this.fontMenu.getElementsByTagName('img')[0].style.top = '5px'; - } - - this.addSeparator(); - - this.sizeMenu = this.addMenu(Menus.prototype.defaultFontSize, mxResources.get('fontSize'), true, 'fontSize'); - this.sizeMenu.style.position = 'relative'; - this.sizeMenu.style.whiteSpace = 'nowrap'; - this.sizeMenu.style.overflow = 'hidden'; - this.sizeMenu.style.width = (mxClient.IS_QUIRKS) ? '44px' : '24px'; - - this.setFontSize(Menus.prototype.defaultFontSize); - - if (EditorUi.compactUi) - { - this.sizeMenu.style.paddingRight = '18px'; - this.sizeMenu.getElementsByTagName('img')[0].style.right = '1px'; - this.sizeMenu.getElementsByTagName('img')[0].style.top = '5px'; - } - - var elts = this.addItems(['-', 'undo', 'redo','-', 'bold', 'italic', 'underline']); - elts[1].setAttribute('title', mxResources.get('undo') + ' (' + this.editorUi.actions.get('undo').shortcut + ')'); - elts[2].setAttribute('title', mxResources.get('redo') + ' (' + this.editorUi.actions.get('redo').shortcut + ')'); - elts[4].setAttribute('title', mxResources.get('bold') + ' (' + this.editorUi.actions.get('bold').shortcut + ')'); - elts[5].setAttribute('title', mxResources.get('italic') + ' (' + this.editorUi.actions.get('italic').shortcut + ')'); - elts[6].setAttribute('title', mxResources.get('underline') + ' (' + this.editorUi.actions.get('underline').shortcut + ')'); - - // KNOWN: Lost focus after click on submenu with text (not icon) in quirks and IE8. This is because the TD seems - // to catch the focus on click in these browsers. NOTE: Workaround in mxPopupMenu for icon items (without text). - var alignMenu = this.addMenuFunction('', mxResources.get('align'), false, mxUtils.bind(this, function(menu) - { - elt = menu.addItem('', null, mxUtils.bind(this, function() - { - document.execCommand('justifyleft', false, null); - }), null, 'geIcon geSprite geSprite-left'); - elt.setAttribute('title', mxResources.get('left')); - - elt = menu.addItem('', null, mxUtils.bind(this, function() - { - document.execCommand('justifycenter', false, null); - }), null, 'geIcon geSprite geSprite-center'); - elt.setAttribute('title', mxResources.get('center')); - - elt = menu.addItem('', null, mxUtils.bind(this, function() - { - document.execCommand('justifyright', false, null); - }), null, 'geIcon geSprite geSprite-right'); - elt.setAttribute('title', mxResources.get('right')); - - elt = menu.addItem('', null, mxUtils.bind(this, function() - { - document.execCommand('justifyfull', false, null); - }), null, 'geIcon geSprite geSprite-justifyfull'); - elt.setAttribute('title', mxResources.get('justifyfull')); - - elt = menu.addItem('', null, mxUtils.bind(this, function() - { - document.execCommand('insertorderedlist', false, null); - }), null, 'geIcon geSprite geSprite-orderedlist'); - elt.setAttribute('title', mxResources.get('numberedList')); - - elt = menu.addItem('', null, mxUtils.bind(this, function() - { - document.execCommand('insertunorderedlist', false, null); - }), null, 'geIcon geSprite geSprite-unorderedlist'); - elt.setAttribute('title', mxResources.get('bulletedList')); - - elt = menu.addItem('', null, mxUtils.bind(this, function() - { - document.execCommand('outdent', false, null); - }), null, 'geIcon geSprite geSprite-outdent'); - elt.setAttribute('title', mxResources.get('decreaseIndent')); - - elt = menu.addItem('', null, mxUtils.bind(this, function() - { - document.execCommand('indent', false, null); - }), null, 'geIcon geSprite geSprite-indent'); - elt.setAttribute('title', mxResources.get('increaseIndent')); - })); - - alignMenu.style.position = 'relative'; - alignMenu.style.whiteSpace = 'nowrap'; - alignMenu.style.overflow = 'hidden'; - alignMenu.innerHTML = '
' + this.dropdownImageHtml; - alignMenu.style.width = (mxClient.IS_QUIRKS) ? '50px' : '30px'; - - if (EditorUi.compactUi) - { - alignMenu.getElementsByTagName('img')[0].style.left = '22px'; - alignMenu.getElementsByTagName('img')[0].style.top = '5px'; - } - - var formatMenu = this.addMenuFunction('', mxResources.get('format'), false, mxUtils.bind(this, function(menu) - { - elt = menu.addItem('', null, this.editorUi.actions.get('subscript').funct, - null, 'geIcon geSprite geSprite-subscript'); - elt.setAttribute('title', mxResources.get('subscript') + ' (' + Editor.ctrlKey + '+,)'); - - elt = menu.addItem('', null, this.editorUi.actions.get('superscript').funct, - null, 'geIcon geSprite geSprite-superscript'); - elt.setAttribute('title', mxResources.get('superscript') + ' (' + Editor.ctrlKey + '+.)'); - - // KNOWN: IE+FF don't return keyboard focus after color dialog (calling focus doesn't help) - elt = menu.addItem('', null, this.editorUi.actions.get('fontColor').funct, - null, 'geIcon geSprite geSprite-fontcolor'); - elt.setAttribute('title', mxResources.get('fontColor')); - - elt = menu.addItem('', null, this.editorUi.actions.get('backgroundColor').funct, - null, 'geIcon geSprite geSprite-fontbackground'); - elt.setAttribute('title', mxResources.get('backgroundColor')); - - elt = menu.addItem('', null, mxUtils.bind(this, function() - { - document.execCommand('removeformat', false, null); - }), null, 'geIcon geSprite geSprite-removeformat'); - elt.setAttribute('title', mxResources.get('removeFormat')); - })); - - formatMenu.style.position = 'relative'; - formatMenu.style.whiteSpace = 'nowrap'; - formatMenu.style.overflow = 'hidden'; - formatMenu.innerHTML = '
' + - this.dropdownImageHtml; - formatMenu.style.width = (mxClient.IS_QUIRKS) ? '50px' : '30px'; - - if (EditorUi.compactUi) - { - formatMenu.getElementsByTagName('img')[0].style.left = '22px'; - formatMenu.getElementsByTagName('img')[0].style.top = '5px'; - } - - this.addSeparator(); - - this.addButton('geIcon geSprite geSprite-code', mxResources.get('html'), function() - { - graph.cellEditor.toggleViewMode(); - - if (graph.cellEditor.textarea.innerHTML.length > 0 && (graph.cellEditor.textarea.innerHTML != ' ' || !graph.cellEditor.clearOnChange)) - { - window.setTimeout(function() - { - document.execCommand('selectAll', false, null); - }); - } - }); - - this.addSeparator(); - - // FIXME: Uses geButton here and geLabel in main menu - var insertMenu = this.addMenuFunction('', mxResources.get('insert'), true, mxUtils.bind(this, function(menu) - { - menu.addItem(mxResources.get('insertLink'), null, mxUtils.bind(this, function() - { - this.editorUi.actions.get('link').funct(); - })); - - menu.addItem(mxResources.get('insertImage'), null, mxUtils.bind(this, function() - { - this.editorUi.actions.get('image').funct(); - })); - - menu.addItem(mxResources.get('insertHorizontalRule'), null, mxUtils.bind(this, function() - { - document.execCommand('inserthorizontalrule', false, null); - })); - })); - - insertMenu.style.whiteSpace = 'nowrap'; - insertMenu.style.overflow = 'hidden'; - insertMenu.style.position = 'relative'; - insertMenu.innerHTML = '
' + - this.dropdownImageHtml; - insertMenu.style.width = (mxClient.IS_QUIRKS) ? '36px' : '16px'; - - // Fix for item size in kennedy theme - if (EditorUi.compactUi) - { - insertMenu.getElementsByTagName('img')[0].style.left = '24px'; - insertMenu.getElementsByTagName('img')[0].style.top = '5px'; - insertMenu.style.width = (mxClient.IS_QUIRKS) ? '50px' : '30px'; - } - - this.addSeparator(); - - // KNOWN: All table stuff does not work with undo/redo - // KNOWN: Lost focus after click on submenu with text (not icon) in quirks and IE8. This is because the TD seems - // to catch the focus on click in these browsers. NOTE: Workaround in mxPopupMenu for icon items (without text). - var elt = this.addMenuFunction('geIcon geSprite geSprite-table', mxResources.get('table'), false, mxUtils.bind(this, function(menu) - { - var elt = graph.getSelectedElement(); - var cell = graph.getParentByName(elt, 'TD', graph.cellEditor.text2); - var row = graph.getParentByName(elt, 'TR', graph.cellEditor.text2); - - if (row == null) - { - this.editorUi.menus.addInsertTableItem(menu); - } - else - { - var table = graph.getParentByName(row, 'TABLE', graph.cellEditor.text2); - - elt = menu.addItem('', null, mxUtils.bind(this, function() - { - try - { - graph.selectNode(graph.insertColumn(table, (cell != null) ? cell.cellIndex : 0)); - } - catch (e) - { - mxUtils.alert(mxResources.get('error') + ': ' + e.message); - } - }), null, 'geIcon geSprite geSprite-insertcolumnbefore'); - elt.setAttribute('title', mxResources.get('insertColumnBefore')); - - elt = menu.addItem('', null, mxUtils.bind(this, function() - { - try - { - graph.selectNode(graph.insertColumn(table, (cell != null) ? cell.cellIndex + 1 : -1)); - } - catch (e) - { - mxUtils.alert(mxResources.get('error') + ': ' + e.message); - } - }), null, 'geIcon geSprite geSprite-insertcolumnafter'); - elt.setAttribute('title', mxResources.get('insertColumnAfter')); - - elt = menu.addItem('Delete column', null, mxUtils.bind(this, function() - { - if (cell != null) - { - try - { - graph.deleteColumn(table, cell.cellIndex); - } - catch (e) - { - mxUtils.alert(mxResources.get('error') + ': ' + e.message); - } - } - }), null, 'geIcon geSprite geSprite-deletecolumn'); - elt.setAttribute('title', mxResources.get('deleteColumn')); - - elt = menu.addItem('', null, mxUtils.bind(this, function() - { - try - { - graph.selectNode(graph.insertRow(table, row.sectionRowIndex)); - } - catch (e) - { - mxUtils.alert(mxResources.get('error') + ': ' + e.message); - } - }), null, 'geIcon geSprite geSprite-insertrowbefore'); - elt.setAttribute('title', mxResources.get('insertRowBefore')); - - elt = menu.addItem('', null, mxUtils.bind(this, function() - { - try - { - graph.selectNode(graph.insertRow(table, row.sectionRowIndex + 1)); - } - catch (e) - { - mxUtils.alert(mxResources.get('error') + ': ' + e.message); - } - }), null, 'geIcon geSprite geSprite-insertrowafter'); - elt.setAttribute('title', mxResources.get('insertRowAfter')); - - elt = menu.addItem('', null, mxUtils.bind(this, function() - { - try - { - graph.deleteRow(table, row.sectionRowIndex); - } - catch (e) - { - mxUtils.alert(mxResources.get('error') + ': ' + e.message); - } - }), null, 'geIcon geSprite geSprite-deleterow'); - elt.setAttribute('title', mxResources.get('deleteRow')); - - elt = menu.addItem('', null, mxUtils.bind(this, function() - { - // Converts rgb(r,g,b) values - var color = table.style.borderColor.replace( - /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g, - function($0, $1, $2, $3) { - return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2); - }); - this.editorUi.pickColor(color, function(newColor) - { - if (newColor == null || newColor == mxConstants.NONE) - { - table.removeAttribute('border'); - table.style.border = ''; - table.style.borderCollapse = ''; - } - else - { - table.setAttribute('border', '1'); - table.style.border = '1px solid ' + newColor; - table.style.borderCollapse = 'collapse'; - } - }); - }), null, 'geIcon geSprite geSprite-strokecolor'); - elt.setAttribute('title', mxResources.get('borderColor')); - - elt = menu.addItem('', null, mxUtils.bind(this, function() - { - // Converts rgb(r,g,b) values - var color = table.style.backgroundColor.replace( - /\brgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g, - function($0, $1, $2, $3) { - return "#" + ("0"+Number($1).toString(16)).substr(-2) + ("0"+Number($2).toString(16)).substr(-2) + ("0"+Number($3).toString(16)).substr(-2); - }); - this.editorUi.pickColor(color, function(newColor) - { - if (newColor == null || newColor == mxConstants.NONE) - { - table.style.backgroundColor = ''; - } - else - { - table.style.backgroundColor = newColor; - } - }); - }), null, 'geIcon geSprite geSprite-fillcolor'); - elt.setAttribute('title', mxResources.get('backgroundColor')); - - elt = menu.addItem('', null, mxUtils.bind(this, function() - { - var value = table.getAttribute('cellPadding') || 0; - - var dlg = new FilenameDialog(this.editorUi, value, mxResources.get('apply'), mxUtils.bind(this, function(newValue) - { - if (newValue != null && newValue.length > 0) - { - table.setAttribute('cellPadding', newValue); - } - else - { - table.removeAttribute('cellPadding'); - } - }), mxResources.get('spacing')); - this.editorUi.showDialog(dlg.container, 300, 80, true, true); - dlg.init(); - }), null, 'geIcon geSprite geSprite-fit'); - elt.setAttribute('title', mxResources.get('spacing')); - - elt = menu.addItem('', null, mxUtils.bind(this, function() - { - table.setAttribute('align', 'left'); - }), null, 'geIcon geSprite geSprite-left'); - elt.setAttribute('title', mxResources.get('left')); - - elt = menu.addItem('', null, mxUtils.bind(this, function() - { - table.setAttribute('align', 'center'); - }), null, 'geIcon geSprite geSprite-center'); - elt.setAttribute('title', mxResources.get('center')); - - elt = menu.addItem('', null, mxUtils.bind(this, function() - { - table.setAttribute('align', 'right'); - }), null, 'geIcon geSprite geSprite-right'); - elt.setAttribute('title', mxResources.get('right')); - } - })); - - elt.style.position = 'relative'; - elt.style.whiteSpace = 'nowrap'; - elt.style.overflow = 'hidden'; - elt.innerHTML = '
' + this.dropdownImageHtml; - elt.style.width = (mxClient.IS_QUIRKS) ? '50px' : '30px'; - - // Fix for item size in kennedy theme - if (EditorUi.compactUi) - { - elt.getElementsByTagName('img')[0].style.left = '22px'; - elt.getElementsByTagName('img')[0].style.top = '5px'; - } -}; - -/** - * Hides the current menu. - */ -Toolbar.prototype.hideMenu = function() -{ - this.editorUi.hideCurrentMenu(); -}; - -/** - * Adds a label to the toolbar. - */ -Toolbar.prototype.addMenu = function(label, tooltip, showLabels, name, c, showAll, ignoreState) -{ - var menu = this.editorUi.menus.get(name); - var elt = this.addMenuFunction(label, tooltip, showLabels, function() - { - menu.funct.apply(menu, arguments); - }, c, showAll); - - if (!ignoreState) - { - menu.addListener('stateChanged', function() - { - elt.setEnabled(menu.enabled); - }); - } - - return elt; -}; - -/** - * Adds a label to the toolbar. - */ -Toolbar.prototype.addMenuFunction = function(label, tooltip, showLabels, funct, c, showAll) -{ - return this.addMenuFunctionInContainer((c != null) ? c : this.container, label, tooltip, showLabels, funct, showAll); -}; - -/** - * Adds a label to the toolbar. - */ -Toolbar.prototype.addMenuFunctionInContainer = function(container, label, tooltip, showLabels, funct, showAll) -{ - var elt = (showLabels) ? this.createLabel(label) : this.createButton(label); - this.initElement(elt, tooltip); - this.addMenuHandler(elt, showLabels, funct, showAll); - container.appendChild(elt); - - return elt; -}; - -/** - * Adds a separator to the separator. - */ -Toolbar.prototype.addSeparator = function(c) -{ - c = (c != null) ? c : this.container; - var elt = document.createElement('div'); - elt.className = 'geSeparator'; - c.appendChild(elt); - - return elt; -}; - -/** - * Adds given action item - */ -Toolbar.prototype.addItems = function(keys, c, ignoreDisabled) -{ - var items = []; - - for (var i = 0; i < keys.length; i++) - { - var key = keys[i]; - - if (key == '-') - { - items.push(this.addSeparator(c)); - } - else - { - items.push(this.addItem('geSprite-' + key.toLowerCase(), key, c, ignoreDisabled)); - } - } - - return items; -}; - -/** - * Adds given action item - */ -Toolbar.prototype.addItem = function(sprite, key, c, ignoreDisabled) -{ - var action = this.editorUi.actions.get(key); - var elt = null; - - if (action != null) - { - var tooltip = action.label; - - if (action.shortcut != null) - { - tooltip += ' (' + action.shortcut + ')'; - } - - elt = this.addButton(sprite, tooltip, action.funct, c); - - if (!ignoreDisabled) - { - elt.setEnabled(action.enabled); - - action.addListener('stateChanged', function() - { - elt.setEnabled(action.enabled); - }); - } - } - - return elt; -}; - -/** - * Adds a button to the toolbar. - */ -Toolbar.prototype.addButton = function(classname, tooltip, funct, c) -{ - var elt = this.createButton(classname); - c = (c != null) ? c : this.container; - - this.initElement(elt, tooltip); - this.addClickHandler(elt, funct); - c.appendChild(elt); - - return elt; -}; - -/** - * Initializes the given toolbar element. - */ -Toolbar.prototype.initElement = function(elt, tooltip) -{ - // Adds tooltip - if (tooltip != null) - { - elt.setAttribute('title', tooltip); - } - - this.addEnabledState(elt); -}; - -/** - * Adds enabled state with setter to DOM node (avoids JS wrapper). - */ -Toolbar.prototype.addEnabledState = function(elt) -{ - var classname = elt.className; - - elt.setEnabled = function(value) - { - elt.enabled = value; - - if (value) - { - elt.className = classname; - } - else - { - elt.className = classname + ' mxDisabled'; - } - }; - - elt.setEnabled(true); -}; - -/** - * Adds enabled state with setter to DOM node (avoids JS wrapper). - */ -Toolbar.prototype.addClickHandler = function(elt, funct) -{ - if (funct != null) - { - mxEvent.addListener(elt, 'click', function(evt) - { - if (elt.enabled) - { - funct(evt); - } - - mxEvent.consume(evt); - }); - - if (document.documentMode != null && document.documentMode >= 9) - { - // Prevents focus - mxEvent.addListener(elt, 'mousedown', function(evt) - { - evt.preventDefault(); - }); - } - } -}; - -/** - * Creates and returns a new button. - */ -Toolbar.prototype.createButton = function(classname) -{ - var elt = document.createElement('a'); - elt.setAttribute('href', 'javascript:void(0);'); - elt.className = 'geButton'; - - var inner = document.createElement('div'); - - if (classname != null) - { - inner.className = 'geSprite ' + classname; - } - - elt.appendChild(inner); - - return elt; -}; - -/** - * Creates and returns a new button. - */ -Toolbar.prototype.createLabel = function(label, tooltip) -{ - var elt = document.createElement('a'); - elt.setAttribute('href', 'javascript:void(0);'); - elt.className = 'geLabel'; - mxUtils.write(elt, label); - - return elt; -}; - -/** - * Adds a handler for showing a menu in the given element. - */ -Toolbar.prototype.addMenuHandler = function(elt, showLabels, funct, showAll) -{ - if (funct != null) - { - var graph = this.editorUi.editor.graph; - var menu = null; - var show = true; - - mxEvent.addListener(elt, 'click', mxUtils.bind(this, function(evt) - { - if (show && (elt.enabled == null || elt.enabled)) - { - graph.popupMenuHandler.hideMenu(); - menu = new mxPopupMenu(funct); - menu.div.className += ' geToolbarMenu'; - menu.showDisabled = showAll; - menu.labels = showLabels; - menu.autoExpand = true; - - var offset = mxUtils.getOffset(elt); - menu.popup(offset.x, offset.y + elt.offsetHeight, null, evt); - this.editorUi.setCurrentMenu(menu, elt); - - // Workaround for scrollbar hiding menu items - if (!showLabels && menu.div.scrollHeight > menu.div.clientHeight) - { - menu.div.style.width = '40px'; - } - - menu.hideMenu = mxUtils.bind(this, function() - { - mxPopupMenu.prototype.hideMenu.apply(menu, arguments); - this.editorUi.resetCurrentMenu(); - menu.destroy(); - }); - - // Extends destroy to reset global state - menu.addListener(mxEvent.EVENT_HIDE, mxUtils.bind(this, function() - { - this.currentElt = null; - })); - } - - show = true; - mxEvent.consume(evt); - })); - - // Hides menu if already showing - mxEvent.addListener(elt, 'mousedown', mxUtils.bind(this, function(evt) - { - show = this.currentElt != elt; - - // Prevents focus - if (document.documentMode != null && document.documentMode >= 9) - { - evt.preventDefault(); - } - })); - } -}; - -/** - * Adds a handler for showing a menu in the given element. - */ -Toolbar.prototype.destroy = function() -{ - if (this.gestureHandler != null) - { - mxEvent.removeGestureListeners(document, this.gestureHandler); - this.gestureHandler = null; - } -}; diff --git a/media/grapheditor/jscolor/arrow.gif b/media/grapheditor/jscolor/arrow.gif deleted file mode 100644 index 246478a864..0000000000 Binary files a/media/grapheditor/jscolor/arrow.gif and /dev/null differ diff --git a/media/grapheditor/jscolor/cross.gif b/media/grapheditor/jscolor/cross.gif deleted file mode 100644 index 0ee9c7ac51..0000000000 Binary files a/media/grapheditor/jscolor/cross.gif and /dev/null differ diff --git a/media/grapheditor/jscolor/hs.png b/media/grapheditor/jscolor/hs.png deleted file mode 100644 index 3d94486ced..0000000000 Binary files a/media/grapheditor/jscolor/hs.png and /dev/null differ diff --git a/media/grapheditor/jscolor/hv.png b/media/grapheditor/jscolor/hv.png deleted file mode 100644 index 1c5e01f8bc..0000000000 Binary files a/media/grapheditor/jscolor/hv.png and /dev/null differ diff --git a/media/grapheditor/jscolor/jscolor.js b/media/grapheditor/jscolor/jscolor.js deleted file mode 100644 index b8093d88c7..0000000000 --- a/media/grapheditor/jscolor/jscolor.js +++ /dev/null @@ -1,913 +0,0 @@ -/** - * jscolor, JavaScript Color Picker - * - * @version 1.3.13 - * @license GNU Lesser General Public License, http://www.gnu.org/copyleft/lesser.html - * @author Jan Odvarko, http://odvarko.cz - * @created 2008-06-15 - * @updated 2012-01-19 - * @link http://jscolor.com - */ - - -var jscolor = { - - - dir : '', // location of jscolor directory (leave empty to autodetect) - bindClass : 'color', // class name - binding : true, // automatic binding via - preloading : true, // use image preloading? - - - install : function() { - //jscolor.addEvent(window, 'load', jscolor.init); - }, - - - init : function() { - if(jscolor.preloading) { - jscolor.preload(); - } - }, - - - getDir : function() { - if(!jscolor.dir) { - var detected = jscolor.detectDir(); - jscolor.dir = detected!==false ? detected : 'jscolor/'; - } - return jscolor.dir; - }, - - - detectDir : function() { - var base = location.href; - - var e = document.getElementsByTagName('base'); - for(var i=0; i vs[a] ? - (-vp[a]+tp[a]+ts[a]/2 > vs[a]/2 && tp[a]+ts[a]-ps[a] >= 0 ? tp[a]+ts[a]-ps[a] : tp[a]) : - tp[a], - -vp[b]+tp[b]+ts[b]+ps[b]-l+l*c > vs[b] ? - (-vp[b]+tp[b]+ts[b]/2 > vs[b]/2 && tp[b]+ts[b]-l-l*c >= 0 ? tp[b]+ts[b]-l-l*c : tp[b]+ts[b]-l+l*c) : - (tp[b]+ts[b]-l+l*c >= 0 ? tp[b]+ts[b]-l+l*c : tp[b]+ts[b]-l-l*c) - ]; - } - drawPicker(0, 0); - } - }; - - - this.importColor = function() { - if(!valueElement) { - this.exportColor(); - } else { - if(!this.adjust) { - if(!this.fromString(valueElement.value, leaveValue)) { - styleElement.style.backgroundImage = styleElement.jscStyle.backgroundImage; - styleElement.style.backgroundColor = styleElement.jscStyle.backgroundColor; - styleElement.style.color = styleElement.jscStyle.color; - this.exportColor(leaveValue | leaveStyle); - } - } else if(!this.required && /^\s*$/.test(valueElement.value)) { - valueElement.value = ''; - styleElement.style.backgroundImage = styleElement.jscStyle.backgroundImage; - styleElement.style.backgroundColor = styleElement.jscStyle.backgroundColor; - styleElement.style.color = styleElement.jscStyle.color; - this.exportColor(leaveValue | leaveStyle); - - } else if(this.fromString(valueElement.value)) { - // OK - } else { - this.exportColor(); - } - } - }; - - - this.exportColor = function(flags) { - if(!(flags & leaveValue) && valueElement) { - var value = this.toString(); - if(this.caps) { value = value.toUpperCase(); } - if(this.hash) { value = '#'+value; } - valueElement.value = value; - } - if(!(flags & leaveStyle) && styleElement) { - styleElement.style.backgroundImage = "none"; - styleElement.style.backgroundColor = - '#'+this.toString(); - styleElement.style.color = - 0.213 * this.rgb[0] + - 0.715 * this.rgb[1] + - 0.072 * this.rgb[2] - < 0.5 ? '#FFF' : '#000'; - } - if(!(flags & leavePad) && isPickerOwner()) { - redrawPad(); - } - if(!(flags & leaveSld) && isPickerOwner()) { - redrawSld(); - } - }; - - - this.fromHSV = function(h, s, v, flags) { // null = don't change - h<0 && (h=0) || h>6 && (h=6); - s<0 && (s=0) || s>1 && (s=1); - v<0 && (v=0) || v>1 && (v=1); - this.rgb = HSV_RGB( - h===null ? this.hsv[0] : (this.hsv[0]=h), - s===null ? this.hsv[1] : (this.hsv[1]=s), - v===null ? this.hsv[2] : (this.hsv[2]=v) - ); - this.exportColor(flags); - }; - - - this.fromRGB = function(r, g, b, flags) { // null = don't change - r<0 && (r=0) || r>1 && (r=1); - g<0 && (g=0) || g>1 && (g=1); - b<0 && (b=0) || b>1 && (b=1); - var hsv = RGB_HSV( - r===null ? this.rgb[0] : (this.rgb[0]=r), - g===null ? this.rgb[1] : (this.rgb[1]=g), - b===null ? this.rgb[2] : (this.rgb[2]=b) - ); - if(hsv[0] !== null) { - this.hsv[0] = hsv[0]; - } - if(hsv[2] !== 0) { - this.hsv[1] = hsv[1]; - } - this.hsv[2] = hsv[2]; - this.exportColor(flags); - }; - - - this.fromString = function(hex, flags) { - var m = hex.match(/^\W*([0-9A-F]{3}([0-9A-F]{3})?)\W*$/i); - if(!m) { - return false; - } else { - if(m[1].length === 6) { // 6-char notation - this.fromRGB( - parseInt(m[1].substr(0,2),16) / 255, - parseInt(m[1].substr(2,2),16) / 255, - parseInt(m[1].substr(4,2),16) / 255, - flags - ); - } else { // 3-char notation - this.fromRGB( - parseInt(m[1].charAt(0)+m[1].charAt(0),16) / 255, - parseInt(m[1].charAt(1)+m[1].charAt(1),16) / 255, - parseInt(m[1].charAt(2)+m[1].charAt(2),16) / 255, - flags - ); - } - return true; - } - }; - - - this.toString = function() { - return ( - (0x100 | Math.round(255*this.rgb[0])).toString(16).substr(1) + - (0x100 | Math.round(255*this.rgb[1])).toString(16).substr(1) + - (0x100 | Math.round(255*this.rgb[2])).toString(16).substr(1) - ); - }; - - - function RGB_HSV(r, g, b) { - var n = Math.min(Math.min(r,g),b); - var v = Math.max(Math.max(r,g),b); - var m = v - n; - if(m === 0) { return [ null, 0, v ]; } - var h = r===n ? 3+(b-g)/m : (g===n ? 5+(r-b)/m : 1+(g-r)/m); - return [ h===6?0:h, m/v, v ]; - } - - - function HSV_RGB(h, s, v) { - if(h === null) { return [ v, v, v ]; } - var i = Math.floor(h); - var f = i%2 ? h-i : 1-(h-i); - var m = v * (1 - s); - var n = v * (1 - s*f); - switch(i) { - case 6: - case 0: return [v,n,m]; - case 1: return [n,v,m]; - case 2: return [m,v,n]; - case 3: return [m,n,v]; - case 4: return [n,m,v]; - case 5: return [v,m,n]; - } - } - - - function removePicker() { - delete jscolor.picker.owner; - document.getElementsByTagName('body')[0].removeChild(jscolor.picker.boxB); - } - - - function drawPicker(x, y) { - if(!jscolor.picker) { - jscolor.picker = { - box : document.createElement('div'), - boxB : document.createElement('div'), - pad : document.createElement('div'), - padB : document.createElement('div'), - padM : document.createElement('div'), - sld : document.createElement('div'), - sldB : document.createElement('div'), - sldM : document.createElement('div'), - btn : document.createElement('div'), - btnS : document.createElement('span'), - btnT : document.createTextNode(THIS.pickerCloseText) - }; - for(var i=0,segSize=4; i - * to detect IE 11. - */ - IS_IE: navigator.userAgent.indexOf('MSIE') >= 0, - - /** - * Variable: IS_IE6 - * - * True if the current browser is Internet Explorer 6.x. - */ - IS_IE6: navigator.userAgent.indexOf('MSIE 6') >= 0, - - /** - * Variable: IS_IE11 - * - * True if the current browser is Internet Explorer 11.x. - */ - IS_IE11: !!navigator.userAgent.match(/Trident\/7\./), - - /** - * Variable: IS_EDGE - * - * True if the current browser is Microsoft Edge. - */ - IS_EDGE: !!navigator.userAgent.match(/Edge\//), - - /** - * Variable: IS_QUIRKS - * - * True if the current browser is Internet Explorer and it is in quirks mode. - */ - IS_QUIRKS: navigator.userAgent.indexOf('MSIE') >= 0 && (document.documentMode == null || document.documentMode == 5), - - /** - * Variable: IS_EM - * - * True if the browser is IE11 in enterprise mode (IE8 standards mode). - */ - IS_EM: 'spellcheck' in document.createElement('textarea') && document.documentMode == 8, - - /** - * Variable: VML_PREFIX - * - * Prefix for VML namespace in node names. Default is 'v'. - */ - VML_PREFIX: 'v', - - /** - * Variable: OFFICE_PREFIX - * - * Prefix for VML office namespace in node names. Default is 'o'. - */ - OFFICE_PREFIX: 'o', - - /** - * Variable: IS_NS - * - * True if the current browser is Netscape (including Firefox). - */ - IS_NS: navigator.userAgent.indexOf('Mozilla/') >= 0 && - navigator.userAgent.indexOf('MSIE') < 0 && - navigator.userAgent.indexOf('Edge/') < 0, - - /** - * Variable: IS_OP - * - * True if the current browser is Opera. - */ - IS_OP: navigator.userAgent.indexOf('Opera/') >= 0 || - navigator.userAgent.indexOf('OPR/') >= 0, - - /** - * Variable: IS_OT - * - * True if -o-transform is available as a CSS style, ie for Opera browsers - * based on a Presto engine with version 2.5 or later. - */ - IS_OT: navigator.userAgent.indexOf('Presto/') >= 0 && - navigator.userAgent.indexOf('Presto/2.4.') < 0 && - navigator.userAgent.indexOf('Presto/2.3.') < 0 && - navigator.userAgent.indexOf('Presto/2.2.') < 0 && - navigator.userAgent.indexOf('Presto/2.1.') < 0 && - navigator.userAgent.indexOf('Presto/2.0.') < 0 && - navigator.userAgent.indexOf('Presto/1.') < 0, - - /** - * Variable: IS_SF - * - * True if the current browser is Safari. - */ - IS_SF: navigator.userAgent.indexOf('AppleWebKit/') >= 0 && - navigator.userAgent.indexOf('Chrome/') < 0 && - navigator.userAgent.indexOf('Edge/') < 0, - - /** - * Variable: IS_IOS - * - * Returns true if the user agent is an iPad, iPhone or iPod. - */ - IS_IOS: (navigator.userAgent.match(/(iPad|iPhone|iPod)/g) ? true : false), - - /** - * Variable: IS_GC - * - * True if the current browser is Google Chrome. - */ - IS_GC: navigator.userAgent.indexOf('Chrome/') >= 0 && - navigator.userAgent.indexOf('Edge/') < 0, - - /** - * Variable: IS_CHROMEAPP - * - * True if the this is running inside a Chrome App. - */ - IS_CHROMEAPP: window.chrome != null && chrome.app != null && chrome.app.runtime != null, - - /** - * Variable: IS_FF - * - * True if the current browser is Firefox. - */ - IS_FF: navigator.userAgent.indexOf('Firefox/') >= 0, - - /** - * Variable: IS_MT - * - * True if -moz-transform is available as a CSS style. This is the case - * for all Firefox-based browsers newer than or equal 3, such as Camino, - * Iceweasel, Seamonkey and Iceape. - */ - IS_MT: (navigator.userAgent.indexOf('Firefox/') >= 0 && - navigator.userAgent.indexOf('Firefox/1.') < 0 && - navigator.userAgent.indexOf('Firefox/2.') < 0) || - (navigator.userAgent.indexOf('Iceweasel/') >= 0 && - navigator.userAgent.indexOf('Iceweasel/1.') < 0 && - navigator.userAgent.indexOf('Iceweasel/2.') < 0) || - (navigator.userAgent.indexOf('SeaMonkey/') >= 0 && - navigator.userAgent.indexOf('SeaMonkey/1.') < 0) || - (navigator.userAgent.indexOf('Iceape/') >= 0 && - navigator.userAgent.indexOf('Iceape/1.') < 0), - - /** - * Variable: IS_SVG - * - * True if the browser supports SVG. - */ - IS_SVG: navigator.userAgent.indexOf('Firefox/') >= 0 || // FF and Camino - navigator.userAgent.indexOf('Iceweasel/') >= 0 || // Firefox on Debian - navigator.userAgent.indexOf('Seamonkey/') >= 0 || // Firefox-based - navigator.userAgent.indexOf('Iceape/') >= 0 || // Seamonkey on Debian - navigator.userAgent.indexOf('Galeon/') >= 0 || // Gnome Browser (old) - navigator.userAgent.indexOf('Epiphany/') >= 0 || // Gnome Browser (new) - navigator.userAgent.indexOf('AppleWebKit/') >= 0 || // Safari/Google Chrome - navigator.userAgent.indexOf('Gecko/') >= 0 || // Netscape/Gecko - navigator.userAgent.indexOf('Opera/') >= 0 || // Opera - (document.documentMode != null && document.documentMode >= 9), // IE9+ - - /** - * Variable: NO_FO - * - * True if foreignObject support is not available. This is the case for - * Opera, older SVG-based browsers and all versions of IE. - */ - NO_FO: !document.createElementNS || document.createElementNS('http://www.w3.org/2000/svg', - 'foreignObject') != '[object SVGForeignObjectElement]' || navigator.userAgent.indexOf('Opera/') >= 0, - - /** - * Variable: IS_VML - * - * True if the browser supports VML. - */ - IS_VML: navigator.appName.toUpperCase() == 'MICROSOFT INTERNET EXPLORER', - - /** - * Variable: IS_WIN - * - * True if the client is a Windows. - */ - IS_WIN: navigator.appVersion.indexOf('Win') > 0, - - /** - * Variable: IS_MAC - * - * True if the client is a Mac. - */ - IS_MAC: navigator.appVersion.indexOf('Mac') > 0, - - /** - * Variable: IS_TOUCH - * - * True if this device supports touchstart/-move/-end events (Apple iOS, - * Android, Chromebook and Chrome Browser on touch-enabled devices). - */ - IS_TOUCH: 'ontouchstart' in document.documentElement, - - /** - * Variable: IS_POINTER - * - * True if this device supports Microsoft pointer events (always false on Macs). - */ - IS_POINTER: window.PointerEvent != null && !(navigator.appVersion.indexOf('Mac') > 0), - - /** - * Variable: IS_LOCAL - * - * True if the documents location does not start with http:// or https://. - */ - IS_LOCAL: document.location.href.indexOf('http://') < 0 && - document.location.href.indexOf('https://') < 0, - - /** - * Variable: defaultBundles - * - * Contains the base names of the default bundles if mxLoadResources is false. - */ - defaultBundles: [], - - /** - * Function: isBrowserSupported - * - * Returns true if the current browser is supported, that is, if - * or is true. - * - * Example: - * - * (code) - * if (!mxClient.isBrowserSupported()) - * { - * mxUtils.error('Browser is not supported!', 200, false); - * } - * (end) - */ - isBrowserSupported: function() - { - return mxClient.IS_VML || mxClient.IS_SVG; - }, - - /** - * Function: link - * - * Adds a link node to the head of the document. Use this - * to add a stylesheet to the page as follows: - * - * (code) - * mxClient.link('stylesheet', filename); - * (end) - * - * where filename is the (relative) URL of the stylesheet. The charset - * is hardcoded to ISO-8859-1 and the type is text/css. - * - * Parameters: - * - * rel - String that represents the rel attribute of the link node. - * href - String that represents the href attribute of the link node. - * doc - Optional parent document of the link node. - */ - link: function(rel, href, doc) - { - doc = doc || document; - - // Workaround for Operation Aborted in IE6 if base tag is used in head - if (mxClient.IS_IE6) - { - doc.write(''); - } - else - { - var link = doc.createElement('link'); - - link.setAttribute('rel', rel); - link.setAttribute('href', href); - link.setAttribute('charset', 'UTF-8'); - link.setAttribute('type', 'text/css'); - - var head = doc.getElementsByTagName('head')[0]; - head.appendChild(link); - } - }, - - /** - * Function: loadResources - * - * Helper method to load the default bundles if mxLoadResources is false. - * - * Parameters: - * - * fn - Function to call after all resources have been loaded. - * lan - Optional string to pass to . - */ - loadResources: function(fn, lan) - { - var pending = mxClient.defaultBundles.length; - - function callback() - { - if (--pending == 0) - { - fn(); - } - } - - for (var i = 0; i < mxClient.defaultBundles.length; i++) - { - mxResources.add(mxClient.defaultBundles[i], lan, callback); - } - }, - - /** - * Function: include - * - * Dynamically adds a script node to the document header. - * - * In production environments, the includes are resolved in the mxClient.js - * file to reduce the number of requests required for client startup. This - * function should only be used in development environments, but not in - * production systems. - */ - include: function(src) - { - document.write(''); - } -}; - -/** - * Variable: mxLoadResources - * - * Optional global config variable to toggle loading of the two resource files - * in and . Default is true. NOTE: This is a global variable, - * not a variable of mxClient. If this is false, you can use - * with its callback to load the default bundles asynchronously. - * - * (code) - * - * - * (end) - */ -if (typeof(mxLoadResources) == 'undefined') -{ - mxLoadResources = true; -} - -/** - * Variable: mxForceIncludes - * - * Optional global config variable to force loading the JavaScript files in - * development mode. Default is undefined. NOTE: This is a global variable, - * not a variable of mxClient. - * - * (code) - * - * - * (end) - */ -if (typeof(mxForceIncludes) == 'undefined') -{ - mxForceIncludes = false; -} - -/** - * Variable: mxResourceExtension - * - * Optional global config variable to specify the extension of resource files. - * Default is true. NOTE: This is a global variable, not a variable of mxClient. - * - * (code) - * - * - * (end) - */ -if (typeof(mxResourceExtension) == 'undefined') -{ - mxResourceExtension = '.txt'; -} - -/** - * Variable: mxLoadStylesheets - * - * Optional global config variable to toggle loading of the CSS files when - * the library is initialized. Default is true. NOTE: This is a global variable, - * not a variable of mxClient. - * - * (code) - * - * - * (end) - */ -if (typeof(mxLoadStylesheets) == 'undefined') -{ - mxLoadStylesheets = true; -} - -/** - * Variable: basePath - * - * Basepath for all URLs in the core without trailing slash. Default is '.'. - * Set mxBasePath prior to loading the mxClient library as follows to override - * this setting: - * - * (code) - * - * - * (end) - * - * When using a relative path, the path is relative to the URL of the page that - * contains the assignment. Trailing slashes are automatically removed. - */ -if (typeof(mxBasePath) != 'undefined' && mxBasePath.length > 0) -{ - // Adds a trailing slash if required - if (mxBasePath.substring(mxBasePath.length - 1) == '/') - { - mxBasePath = mxBasePath.substring(0, mxBasePath.length - 1); - } - - mxClient.basePath = mxBasePath; -} -else -{ - mxClient.basePath = '.'; -} - -/** - * Variable: imageBasePath - * - * Basepath for all images URLs in the core without trailing slash. Default is - * + '/images'. Set mxImageBasePath prior to loading the - * mxClient library as follows to override this setting: - * - * (code) - * - * - * (end) - * - * When using a relative path, the path is relative to the URL of the page that - * contains the assignment. Trailing slashes are automatically removed. - */ -if (typeof(mxImageBasePath) != 'undefined' && mxImageBasePath.length > 0) -{ - // Adds a trailing slash if required - if (mxImageBasePath.substring(mxImageBasePath.length - 1) == '/') - { - mxImageBasePath = mxImageBasePath.substring(0, mxImageBasePath.length - 1); - } - - mxClient.imageBasePath = mxImageBasePath; -} -else -{ - mxClient.imageBasePath = mxClient.basePath + '/images'; -} - -/** - * Variable: language - * - * Defines the language of the client, eg. en for english, de for german etc. - * The special value 'none' will disable all built-in internationalization and - * resource loading. See for handling identifiers - * with and without a dash. - * - * Set mxLanguage prior to loading the mxClient library as follows to override - * this setting: - * - * (code) - * - * - * (end) - * - * If internationalization is disabled, then the following variables should be - * overridden to reflect the current language of the system. These variables are - * cleared when i18n is disabled. - * , , - * , , - * , , , - * , , - * , , - * , , - * , , - * and - * . - */ -if (typeof(mxLanguage) != 'undefined' && mxLanguage != null) -{ - mxClient.language = mxLanguage; -} -else -{ - mxClient.language = (mxClient.IS_IE) ? navigator.userLanguage : navigator.language; -} - -/** - * Variable: defaultLanguage - * - * Defines the default language which is used in the common resource files. Any - * resources for this language will only load the common resource file, but not - * the language-specific resource file. Default is 'en'. - * - * Set mxDefaultLanguage prior to loading the mxClient library as follows to override - * this setting: - * - * (code) - * - * - * (end) - */ -if (typeof(mxDefaultLanguage) != 'undefined' && mxDefaultLanguage != null) -{ - mxClient.defaultLanguage = mxDefaultLanguage; -} -else -{ - mxClient.defaultLanguage = 'en'; -} - -// Adds all required stylesheets and namespaces -if (mxLoadStylesheets) -{ - mxClient.link('stylesheet', '/media/grapheditor/common.css'); -} - -/** - * Variable: languages - * - * Defines the optional array of all supported language extensions. The default - * language does not have to be part of this list. See - * . - * - * (code) - * - * - * (end) - * - * This is used to avoid unnecessary requests to language files, ie. if a 404 - * will be returned. - */ -if (typeof(mxLanguages) != 'undefined' && mxLanguages != null) -{ - mxClient.languages = mxLanguages; -} - -// Adds required namespaces, stylesheets and memory handling for older IE browsers -if (mxClient.IS_VML) -{ - if (mxClient.IS_SVG) - { - mxClient.IS_VML = false; - } - else - { - // Enables support for IE8 standards mode. Note that this requires all attributes for VML - // elements to be set using direct notation, ie. node.attr = value. The use of setAttribute - // is not possible. - if (document.documentMode == 8) - { - document.namespaces.add(mxClient.VML_PREFIX, 'urn:schemas-microsoft-com:vml', '#default#VML'); - document.namespaces.add(mxClient.OFFICE_PREFIX, 'urn:schemas-microsoft-com:office:office', '#default#VML'); - } - else - { - document.namespaces.add(mxClient.VML_PREFIX, 'urn:schemas-microsoft-com:vml'); - document.namespaces.add(mxClient.OFFICE_PREFIX, 'urn:schemas-microsoft-com:office:office'); - } - - // Workaround for limited number of stylesheets in IE (does not work in standards mode) - if (mxClient.IS_QUIRKS && document.styleSheets.length >= 30) - { - (function() - { - var node = document.createElement('style'); - node.type = 'text/css'; - node.styleSheet.cssText = mxClient.VML_PREFIX + '\\:*{behavior:url(#default#VML)}' + - mxClient.OFFICE_PREFIX + '\\:*{behavior:url(#default#VML)}'; - document.getElementsByTagName('head')[0].appendChild(node); - })(); - } - else - { - document.createStyleSheet().cssText = mxClient.VML_PREFIX + '\\:*{behavior:url(#default#VML)}' + - mxClient.OFFICE_PREFIX + '\\:*{behavior:url(#default#VML)}'; - } - - if (mxLoadStylesheets) - { - mxClient.link('stylesheet', mxClient.basePath + '/css/explorer.css'); - } - } -} - -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -var mxLog = -{ - /** - * Class: mxLog - * - * A singleton class that implements a simple console. - * - * Variable: consoleName - * - * Specifies the name of the console window. Default is 'Console'. - */ - consoleName: 'Console', - - /** - * Variable: TRACE - * - * Specified if the output for and should be visible in the - * console. Default is false. - */ - TRACE: false, - - /** - * Variable: DEBUG - * - * Specifies if the output for should be visible in the console. - * Default is true. - */ - DEBUG: true, - - /** - * Variable: WARN - * - * Specifies if the output for should be visible in the console. - * Default is true. - */ - WARN: true, - - /** - * Variable: buffer - * - * Buffer for pre-initialized content. - */ - buffer: '', - - /** - * Function: init - * - * Initializes the DOM node for the console. This requires document.body to - * point to a non-null value. This is called from within if the - * log has not yet been initialized. - */ - init: function() - { - if (mxLog.window == null && document.body != null) - { - var title = mxLog.consoleName + ' - mxGraph ' + mxClient.VERSION; - - // Creates a table that maintains the layout - var table = document.createElement('table'); - table.setAttribute('width', '100%'); - table.setAttribute('height', '100%'); - - var tbody = document.createElement('tbody'); - var tr = document.createElement('tr'); - var td = document.createElement('td'); - td.style.verticalAlign = 'top'; - - // Adds the actual console as a textarea - mxLog.textarea = document.createElement('textarea'); - mxLog.textarea.setAttribute('wrap', 'off'); - mxLog.textarea.setAttribute('readOnly', 'true'); - mxLog.textarea.style.height = '100%'; - mxLog.textarea.style.resize = 'none'; - mxLog.textarea.value = mxLog.buffer; - - // Workaround for wrong width in standards mode - if (mxClient.IS_NS && document.compatMode != 'BackCompat') - { - mxLog.textarea.style.width = '99%'; - } - else - { - mxLog.textarea.style.width = '100%'; - } - - td.appendChild(mxLog.textarea); - tr.appendChild(td); - tbody.appendChild(tr); - - // Creates the container div - tr = document.createElement('tr'); - mxLog.td = document.createElement('td'); - mxLog.td.style.verticalAlign = 'top'; - mxLog.td.setAttribute('height', '30px'); - - tr.appendChild(mxLog.td); - tbody.appendChild(tr); - table.appendChild(tbody); - - // Adds various debugging buttons - mxLog.addButton('Info', function (evt) - { - mxLog.info(); - }); - - mxLog.addButton('DOM', function (evt) - { - var content = mxUtils.getInnerHtml(document.body); - mxLog.debug(content); - }); - - mxLog.addButton('Trace', function (evt) - { - mxLog.TRACE = !mxLog.TRACE; - - if (mxLog.TRACE) - { - mxLog.debug('Tracing enabled'); - } - else - { - mxLog.debug('Tracing disabled'); - } - }); - - mxLog.addButton('Copy', function (evt) - { - try - { - mxUtils.copy(mxLog.textarea.value); - } - catch (err) - { - mxUtils.alert(err); - } - }); - - mxLog.addButton('Show', function (evt) - { - try - { - mxUtils.popup(mxLog.textarea.value); - } - catch (err) - { - mxUtils.alert(err); - } - }); - - mxLog.addButton('Clear', function (evt) - { - mxLog.textarea.value = ''; - }); - - // Cross-browser code to get window size - var h = 0; - var w = 0; - - if (typeof(window.innerWidth) === 'number') - { - h = window.innerHeight; - w = window.innerWidth; - } - else - { - h = (document.documentElement.clientHeight || document.body.clientHeight); - w = document.body.clientWidth; - } - - mxLog.window = new mxWindow(title, table, Math.max(0, w - 320), Math.max(0, h - 210), 300, 160); - mxLog.window.setMaximizable(true); - mxLog.window.setScrollable(false); - mxLog.window.setResizable(true); - mxLog.window.setClosable(true); - mxLog.window.destroyOnClose = false; - - // Workaround for ignored textarea height in various setups - if (((mxClient.IS_NS || mxClient.IS_IE) && !mxClient.IS_GC && - !mxClient.IS_SF && document.compatMode != 'BackCompat') || - document.documentMode == 11) - { - var elt = mxLog.window.getElement(); - - var resizeHandler = function(sender, evt) - { - mxLog.textarea.style.height = Math.max(0, elt.offsetHeight - 70) + 'px'; - }; - - mxLog.window.addListener(mxEvent.RESIZE_END, resizeHandler); - mxLog.window.addListener(mxEvent.MAXIMIZE, resizeHandler); - mxLog.window.addListener(mxEvent.NORMALIZE, resizeHandler); - - mxLog.textarea.style.height = '92px'; - } - } - }, - - /** - * Function: info - * - * Writes the current navigator information to the console. - */ - info: function() - { - mxLog.writeln(mxUtils.toString(navigator)); - }, - - /** - * Function: addButton - * - * Adds a button to the console using the given label and function. - */ - addButton: function(lab, funct) - { - var button = document.createElement('button'); - mxUtils.write(button, lab); - mxEvent.addListener(button, 'click', funct); - mxLog.td.appendChild(button); - }, - - /** - * Function: isVisible - * - * Returns true if the console is visible. - */ - isVisible: function() - { - if (mxLog.window != null) - { - return mxLog.window.isVisible(); - } - - return false; - }, - - - /** - * Function: show - * - * Shows the console. - */ - show: function() - { - mxLog.setVisible(true); - }, - - /** - * Function: setVisible - * - * Shows or hides the console. - */ - setVisible: function(visible) - { - if (mxLog.window == null) - { - mxLog.init(); - } - - if (mxLog.window != null) - { - mxLog.window.setVisible(visible); - } - }, - - /** - * Function: enter - * - * Writes the specified string to the console - * if is true and returns the current - * time in milliseconds. - * - * Example: - * - * (code) - * mxLog.show(); - * var t0 = mxLog.enter('Hello'); - * // Do something - * mxLog.leave('World!', t0); - * (end) - */ - enter: function(string) - { - if (mxLog.TRACE) - { - mxLog.writeln('Entering '+string); - - return new Date().getTime(); - } - }, - - /** - * Function: leave - * - * Writes the specified string to the console - * if is true and computes the difference - * between the current time and t0 in milliseconds. - * See for an example. - */ - leave: function(string, t0) - { - if (mxLog.TRACE) - { - var dt = (t0 != 0) ? ' ('+(new Date().getTime() - t0)+' ms)' : ''; - mxLog.writeln('Leaving '+string+dt); - } - }, - - /** - * Function: debug - * - * Adds all arguments to the console if is enabled. - * - * Example: - * - * (code) - * mxLog.show(); - * mxLog.debug('Hello, World!'); - * (end) - */ - debug: function() - { - if (mxLog.DEBUG) - { - mxLog.writeln.apply(this, arguments); - } - }, - - /** - * Function: warn - * - * Adds all arguments to the console if is enabled. - * - * Example: - * - * (code) - * mxLog.show(); - * mxLog.warn('Hello, World!'); - * (end) - */ - warn: function() - { - if (mxLog.WARN) - { - mxLog.writeln.apply(this, arguments); - } - }, - - /** - * Function: write - * - * Adds the specified strings to the console. - */ - write: function() - { - var string = ''; - - for (var i = 0; i < arguments.length; i++) - { - string += arguments[i]; - - if (i < arguments.length - 1) - { - string += ' '; - } - } - - if (mxLog.textarea != null) - { - mxLog.textarea.value = mxLog.textarea.value + string; - - // Workaround for no update in Presto 2.5.22 (Opera 10.5) - if (navigator.userAgent.indexOf('Presto/2.5') >= 0) - { - mxLog.textarea.style.visibility = 'hidden'; - mxLog.textarea.style.visibility = 'visible'; - } - - mxLog.textarea.scrollTop = mxLog.textarea.scrollHeight; - } - else - { - mxLog.buffer += string; - } - }, - - /** - * Function: writeln - * - * Adds the specified strings to the console, appending a linefeed at the - * end of each string. - */ - writeln: function() - { - var string = ''; - - for (var i = 0; i < arguments.length; i++) - { - string += arguments[i]; - - if (i < arguments.length - 1) - { - string += ' '; - } - } - - mxLog.write(string + '\n'); - } - -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -var mxObjectIdentity = -{ - /** - * Class: mxObjectIdentity - * - * Identity for JavaScript objects and functions. This is implemented using - * a simple incrementing counter which is stored in each object under - * . - * - * The identity for an object does not change during its lifecycle. - * - * Variable: FIELD_NAME - * - * Name of the field to be used to store the object ID. Default is - * mxObjectId. - */ - FIELD_NAME: 'mxObjectId', - - /** - * Variable: counter - * - * Current counter. - */ - counter: 0, - - /** - * Function: get - * - * Returns the ID for the given object or function or null if no object - * is specified. - */ - get: function(obj) - { - if (obj != null) - { - if (obj[mxObjectIdentity.FIELD_NAME] == null) - { - if (typeof obj === 'object') - { - var ctor = mxUtils.getFunctionName(obj.constructor); - obj[mxObjectIdentity.FIELD_NAME] = ctor + '#' + mxObjectIdentity.counter++; - } - else if (typeof obj === 'function') - { - obj[mxObjectIdentity.FIELD_NAME] = 'Function#' + mxObjectIdentity.counter++; - } - } - - return obj[mxObjectIdentity.FIELD_NAME]; - } - - return null; - }, - - /** - * Function: clear - * - * Deletes the ID from the given object or function. - */ - clear: function(obj) - { - if (typeof(obj) === 'object' || typeof obj === 'function') - { - delete obj[mxObjectIdentity.FIELD_NAME]; - } - } - -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -/** - * Class: mxDictionary - * - * A wrapper class for an associative array with object keys. Note: This - * implementation uses to turn object keys into strings. - * - * Constructor: mxEventSource - * - * Constructs a new dictionary which allows object to be used as keys. - */ -function mxDictionary() -{ - this.clear(); -}; - -/** - * Function: map - * - * Stores the (key, value) pairs in this dictionary. - */ -mxDictionary.prototype.map = null; - -/** - * Function: clear - * - * Clears the dictionary. - */ -mxDictionary.prototype.clear = function() -{ - this.map = {}; -}; - -/** - * Function: get - * - * Returns the value for the given key. - */ -mxDictionary.prototype.get = function(key) -{ - var id = mxObjectIdentity.get(key); - - return this.map[id]; -}; - -/** - * Function: put - * - * Stores the value under the given key and returns the previous - * value for that key. - */ -mxDictionary.prototype.put = function(key, value) -{ - var id = mxObjectIdentity.get(key); - var previous = this.map[id]; - this.map[id] = value; - - return previous; -}; - -/** - * Function: remove - * - * Removes the value for the given key and returns the value that - * has been removed. - */ -mxDictionary.prototype.remove = function(key) -{ - var id = mxObjectIdentity.get(key); - var previous = this.map[id]; - delete this.map[id]; - - return previous; -}; - -/** - * Function: getKeys - * - * Returns all keys as an array. - */ -mxDictionary.prototype.getKeys = function() -{ - var result = []; - - for (var key in this.map) - { - result.push(key); - } - - return result; -}; - -/** - * Function: getValues - * - * Returns all values as an array. - */ -mxDictionary.prototype.getValues = function() -{ - var result = []; - - for (var key in this.map) - { - result.push(this.map[key]); - } - - return result; -}; - -/** - * Function: visit - * - * Visits all entries in the dictionary using the given function with the - * following signature: function(key, value) where key is a string and - * value is an object. - * - * Parameters: - * - * visitor - A function that takes the key and value as arguments. - */ -mxDictionary.prototype.visit = function(visitor) -{ - for (var key in this.map) - { - visitor(key, this.map[key]); - } -}; -/** - * Copyright (c) 2006-2016, JGraph Ltd - * Copyright (c) 2006-2016, Gaudenz Alder - */ -var mxResources = -{ - /** - * Class: mxResources - * - * Implements internationalization. You can provide any number of - * resource files on the server using the following format for the - * filename: name[-en].properties. The en stands for any lowercase - * 2-character language shortcut (eg. de for german, fr for french). - * - * If the optional language extension is omitted, then the file is used as a - * default resource which is loaded in all cases. If a properties file for a - * specific language exists, then it is used to override the settings in the - * default resource. All entries in the file are of the form key=value. The - * values may then be accessed in code via . Lines without - * equal signs in the properties files are ignored. - * - * Resource files may either be added programmatically using - * or via a resource tag in the UI section of the - * editor configuration file, eg: - * - * (code) - * - * - * - * (end) - * - * The above element will load examples/resources/mxWorkflow.properties as well - * as the language specific file for the current language, if it exists. - * - * Values may contain placeholders of the form {1}...{n} where each placeholder - * is replaced with the value of the corresponding array element in the params - * argument passed to . The placeholder {1} maps to the first - * element in the array (at index 0). - * - * See for more information on specifying the default - * language or disabling all loading of resources. - * - * Lines that start with a # sign will be ignored. - * - * Special characters - * - * To use unicode characters, use the standard notation (eg. \u8fd1) or %u as a - * prefix (eg. %u20AC will display a Euro sign). For normal hex encoded strings, - * use % as a prefix, eg. %F6 will display a "o umlaut" (ö). - * - * See to disable this. If you disable this, make sure that - * your files are UTF-8 encoded. - * - * Asynchronous loading - * - * By default, the core adds two resource files synchronously at load time. - * To load these files asynchronously, set to false - * before loading mxClient.js and use instead. - * - * Variable: resources - * - * Object that maps from keys to values. - */ - resources: {}, - - /** - * Variable: extension - * - * Specifies the extension used for language files. Default is . - */ - extension: mxResourceExtension, - - /** - * Variable: resourcesEncoded - * - * Specifies whether or not values in resource files are encoded with \u or - * percentage. Default is false. - */ - resourcesEncoded: false, - - /** - * Variable: loadDefaultBundle - * - * Specifies if the default file for a given basename should be loaded. - * Default is true. - */ - loadDefaultBundle: true, - - /** - * Variable: loadDefaultBundle - * - * Specifies if the specific language file file for a given basename should - * be loaded. Default is true. - */ - loadSpecialBundle: true, - - /** - * Function: isLanguageSupported - * - * Hook for subclassers to disable support for a given language. This - * implementation returns true if lan is in . - * - * Parameters: - * - * lan - The current language. - */ - isLanguageSupported: function(lan) - { - if (mxClient.languages != null) - { - return mxUtils.indexOf(mxClient.languages, lan) >= 0; - } - - return true; - }, - - /** - * Function: getDefaultBundle - * - * Hook for subclassers to return the URL for the special bundle. This - * implementation returns basename + or null if - * is false. - * - * Parameters: - * - * basename - The basename for which the file should be loaded. - * lan - The current language. - */ - getDefaultBundle: function(basename, lan) - { - if (mxResources.loadDefaultBundle || !mxResources.isLanguageSupported(lan)) - { - return basename + mxResources.extension; - } - else - { - return null; - } - }, - - /** - * Function: getSpecialBundle - * - * Hook for subclassers to return the URL for the special bundle. This - * implementation returns basename + '_' + lan + or null if - * is false or lan equals . - * - * If is not null and contains - * a dash, then this method checks if returns true - * for the full language (including the dash). If that returns false the - * first part of the language (up to the dash) will be tried as an extension. - * - * If is null then the first part of the language is - * used to maintain backwards compatibility. - * - * Parameters: - * - * basename - The basename for which the file should be loaded. - * lan - The language for which the file should be loaded. - */ - getSpecialBundle: function(basename, lan) - { - if (mxClient.languages == null || !this.isLanguageSupported(lan)) - { - var dash = lan.indexOf('-'); - - if (dash > 0) - { - lan = lan.substring(0, dash); - } - } - - if (mxResources.loadSpecialBundle && mxResources.isLanguageSupported(lan) && lan != mxClient.defaultLanguage) - { - return basename + '_' + lan + mxResources.extension; - } - else - { - return null; - } - }, - - /** - * Function: add - * - * Adds the default and current language properties file for the specified - * basename. Existing keys are overridden as new files are added. If no - * callback is used then the request is synchronous. - * - * Example: - * - * At application startup, additional resources may be - * added using the following code: - * - * (code) - * mxResources.add('resources/editor'); - * (end) - * - * Parameters: - * - * basename - The basename for which the file should be loaded. - * lan - The language for which the file should be loaded. - * callback - Optional callback for asynchronous loading. - */ - add: function(basename, lan, callback) - { - lan = (lan != null) ? lan : ((mxClient.language != null) ? - mxClient.language.toLowerCase() : mxConstants.NONE); - - if (lan != mxConstants.NONE) - { - var defaultBundle = mxResources.getDefaultBundle(basename, lan); - var specialBundle = mxResources.getSpecialBundle(basename, lan); - - var loadSpecialBundle = function() - { - if (specialBundle != null) - { - if (callback) - { - mxUtils.get(specialBundle, function(req) - { - mxResources.parse(req.getText()); - callback(); - }, function() - { - callback(); - }); - } - else - { - try - { - var req = mxUtils.load(specialBundle); - - if (req.isReady()) - { - mxResources.parse(req.getText()); - } - } - catch (e) - { - // ignore - } - } - } - else if (callback != null) - { - callback(); - } - } - - if (defaultBundle != null) - { - if (callback) - { - mxUtils.get(defaultBundle, function(req) - { - mxResources.parse(req.getText()); - loadSpecialBundle(); - }, function() - { - loadSpecialBundle(); - }); - } - else - { - try - { - var req = mxUtils.load(defaultBundle); - - if (req.isReady()) - { - mxResources.parse(req.getText()); - } - - loadSpecialBundle(); - } - catch (e) - { - // ignore - } - } - } - else - { - // Overlays the language specific file (_lan-extension) - loadSpecialBundle(); - } - } - }, - - /** - * Function: parse - * - * Parses the key, value pairs in the specified - * text and stores them as local resources. - */ - parse: function(text) - { - if (text != null) - { - var lines = text.split('\n'); - - for (var i = 0; i < lines.length; i++) - { - if (lines[i].charAt(0) != '#') - { - var index = lines[i].indexOf('='); - - if (index > 0) - { - var key = lines[i].substring(0, index); - var idx = lines[i].length; - - if (lines[i].charCodeAt(idx - 1) == 13) - { - idx--; - } - - var value = lines[i].substring(index + 1, idx); - - if (this.resourcesEncoded) - { - value = value.replace(/\\(?=u[a-fA-F\d]{4})/g,"%"); - mxResources.resources[key] = unescape(value); - } - else - { - mxResources.resources[key] = value; - } - } - } - } - } - }, - - /** - * Function: get - * - * Returns the value for the specified resource key. - * - * Example: - * To read the value for 'welomeMessage', use the following: - * (code) - * var result = mxResources.get('welcomeMessage') || ''; - * (end) - * - * This would require an entry of the following form in - * one of the English language resource files: - * (code) - * welcomeMessage=Welcome to mxGraph! - * (end) - * - * The part behind the || is the string value to be used if the given - * resource is not available. - * - * Parameters: - * - * key - String that represents the key of the resource to be returned. - * params - Array of the values for the placeholders of the form {1}...{n} - * to be replaced with in the resulting string. - * defaultValue - Optional string that specifies the default return value. - */ - get: function(key, params, defaultValue) - { - var value = mxResources.resources[key]; - - // Applies the default value if no resource was found - if (value == null) - { - value = defaultValue; - } - - // Replaces the placeholders with the values in the array - if (value != null && params != null) - { - value = mxResources.replacePlaceholders(value, params); - } - - return value; - }, - - /** - * Function: replacePlaceholders - * - * Replaces the given placeholders with the given parameters. - * - * Parameters: - * - * value - String that contains the placeholders. - * params - Array of the values for the placeholders of the form {1}...{n} - * to be replaced with in the resulting string. - */ - replacePlaceholders: function(value, params) - { - var result = []; - var index = null; - - for (var i = 0; i < value.length; i++) - { - var c = value.charAt(i); - - if (c == '{') - { - index = ''; - } - else if (index != null && c == '}') - { - index = parseInt(index)-1; - - if (index >= 0 && index < params.length) - { - result.push(params[index]); - } - - index = null; - } - else if (index != null) - { - index += c; - } - else - { - result.push(c); - } - } - - return result.join(''); - }, - - /** - * Function: loadResources - * - * Loads all required resources asynchronously. Use this to load the graph and - * editor resources if is false. - * - * Parameters: - * - * callback - Callback function for asynchronous loading. - */ - loadResources: function(callback) - { - mxResources.add(mxClient.basePath+'/resources/editor', null, function() - { - mxResources.add(mxClient.basePath+'/resources/graph', null, callback); - }); - } - -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -/** - * Class: mxPoint - * - * Implements a 2-dimensional vector with double precision coordinates. - * - * Constructor: mxPoint - * - * Constructs a new point for the optional x and y coordinates. If no - * coordinates are given, then the default values for and are used. - */ -function mxPoint(x, y) -{ - this.x = (x != null) ? x : 0; - this.y = (y != null) ? y : 0; -}; - -/** - * Variable: x - * - * Holds the x-coordinate of the point. Default is 0. - */ -mxPoint.prototype.x = null; - -/** - * Variable: y - * - * Holds the y-coordinate of the point. Default is 0. - */ -mxPoint.prototype.y = null; - -/** - * Function: equals - * - * Returns true if the given object equals this point. - */ -mxPoint.prototype.equals = function(obj) -{ - return obj != null && obj.x == this.x && obj.y == this.y; -}; - -/** - * Function: clone - * - * Returns a clone of this . - */ -mxPoint.prototype.clone = function() -{ - // Handles subclasses as well - return mxUtils.clone(this); -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -/** - * Class: mxRectangle - * - * Extends to implement a 2-dimensional rectangle with double - * precision coordinates. - * - * Constructor: mxRectangle - * - * Constructs a new rectangle for the optional parameters. If no parameters - * are given then the respective default values are used. - */ -function mxRectangle(x, y, width, height) -{ - mxPoint.call(this, x, y); - - this.width = (width != null) ? width : 0; - this.height = (height != null) ? height : 0; -}; - -/** - * Extends mxPoint. - */ -mxRectangle.prototype = new mxPoint(); -mxRectangle.prototype.constructor = mxRectangle; - -/** - * Variable: width - * - * Holds the width of the rectangle. Default is 0. - */ -mxRectangle.prototype.width = null; - -/** - * Variable: height - * - * Holds the height of the rectangle. Default is 0. - */ -mxRectangle.prototype.height = null; - -/** - * Function: setRect - * - * Sets this rectangle to the specified values - */ -mxRectangle.prototype.setRect = function(x, y, w, h) -{ - this.x = x; - this.y = y; - this.width = w; - this.height = h; -}; - -/** - * Function: getCenterX - * - * Returns the x-coordinate of the center point. - */ -mxRectangle.prototype.getCenterX = function () -{ - return this.x + this.width/2; -}; - -/** - * Function: getCenterY - * - * Returns the y-coordinate of the center point. - */ -mxRectangle.prototype.getCenterY = function () -{ - return this.y + this.height/2; -}; - -/** - * Function: add - * - * Adds the given rectangle to this rectangle. - */ -mxRectangle.prototype.add = function(rect) -{ - if (rect != null) - { - var minX = Math.min(this.x, rect.x); - var minY = Math.min(this.y, rect.y); - var maxX = Math.max(this.x + this.width, rect.x + rect.width); - var maxY = Math.max(this.y + this.height, rect.y + rect.height); - - this.x = minX; - this.y = minY; - this.width = maxX - minX; - this.height = maxY - minY; - } -}; - -/** - * Function: intersect - * - * Changes this rectangle to where it overlaps with the given rectangle. - */ -mxRectangle.prototype.intersect = function(rect) -{ - if (rect != null) - { - var r1 = this.x + this.width; - var r2 = rect.x + rect.width; - - var b1 = this.y + this.height; - var b2 = rect.y + rect.height; - - this.x = Math.max(this.x, rect.x); - this.y = Math.max(this.y, rect.y); - this.width = Math.min(r1, r2) - this.x; - this.height = Math.min(b1, b2) - this.y; - } -}; - -/** - * Function: grow - * - * Grows the rectangle by the given amount, that is, this method subtracts - * the given amount from the x- and y-coordinates and adds twice the amount - * to the width and height. - */ -mxRectangle.prototype.grow = function(amount) -{ - this.x -= amount; - this.y -= amount; - this.width += 2 * amount; - this.height += 2 * amount; -}; - -/** - * Function: getPoint - * - * Returns the top, left corner as a new . - */ -mxRectangle.prototype.getPoint = function() -{ - return new mxPoint(this.x, this.y); -}; - -/** - * Function: rotate90 - * - * Rotates this rectangle by 90 degree around its center point. - */ -mxRectangle.prototype.rotate90 = function() -{ - var t = (this.width - this.height) / 2; - this.x += t; - this.y -= t; - var tmp = this.width; - this.width = this.height; - this.height = tmp; -}; - -/** - * Function: equals - * - * Returns true if the given object equals this rectangle. - */ -mxRectangle.prototype.equals = function(obj) -{ - return obj != null && obj.x == this.x && obj.y == this.y && - obj.width == this.width && obj.height == this.height; -}; - -/** - * Function: fromRectangle - * - * Returns a new which is a copy of the given rectangle. - */ -mxRectangle.fromRectangle = function(rect) -{ - return new mxRectangle(rect.x, rect.y, rect.width, rect.height); -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -var mxEffects = -{ - - /** - * Class: mxEffects - * - * Provides animation effects. - */ - - /** - * Function: animateChanges - * - * Asynchronous animated move operation. See also: . - * - * Example: - * - * (code) - * graph.model.addListener(mxEvent.CHANGE, function(sender, evt) - * { - * var changes = evt.getProperty('edit').changes; - * - * if (changes.length < 10) - * { - * mxEffects.animateChanges(graph, changes); - * } - * }); - * (end) - * - * Parameters: - * - * graph - that received the changes. - * changes - Array of changes to be animated. - * done - Optional function argument that is invoked after the - * last step of the animation. - */ - animateChanges: function(graph, changes, done) - { - var maxStep = 10; - var step = 0; - - var animate = function() - { - var isRequired = false; - - for (var i = 0; i < changes.length; i++) - { - var change = changes[i]; - - if (change instanceof mxGeometryChange || - change instanceof mxTerminalChange || - change instanceof mxValueChange || - change instanceof mxChildChange || - change instanceof mxStyleChange) - { - var state = graph.getView().getState(change.cell || change.child, false); - - if (state != null) - { - isRequired = true; - - if (change.constructor != mxGeometryChange || graph.model.isEdge(change.cell)) - { - mxUtils.setOpacity(state.shape.node, 100 * step / maxStep); - } - else - { - var scale = graph.getView().scale; - - var dx = (change.geometry.x - change.previous.x) * scale; - var dy = (change.geometry.y - change.previous.y) * scale; - - var sx = (change.geometry.width - change.previous.width) * scale; - var sy = (change.geometry.height - change.previous.height) * scale; - - if (step == 0) - { - state.x -= dx; - state.y -= dy; - state.width -= sx; - state.height -= sy; - } - else - { - state.x += dx / maxStep; - state.y += dy / maxStep; - state.width += sx / maxStep; - state.height += sy / maxStep; - } - - graph.cellRenderer.redraw(state); - - // Fades all connected edges and children - mxEffects.cascadeOpacity(graph, change.cell, 100 * step / maxStep); - } - } - } - } - - if (step < maxStep && isRequired) - { - step++; - window.setTimeout(animate, delay); - } - else if (done != null) - { - done(); - } - }; - - var delay = 30; - animate(); - }, - - /** - * Function: cascadeOpacity - * - * Sets the opacity on the given cell and its descendants. - * - * Parameters: - * - * graph - that contains the cells. - * cell - to set the opacity for. - * opacity - New value for the opacity in %. - */ - cascadeOpacity: function(graph, cell, opacity) - { - // Fades all children - var childCount = graph.model.getChildCount(cell); - - for (var i=0; i 0) - { - window.setTimeout(f, delay); - } - else - { - node.style.visibility = 'hidden'; - - if (remove && node.parentNode) - { - node.parentNode.removeChild(node); - } - } - }; - window.setTimeout(f, delay); - } - else - { - node.style.visibility = 'hidden'; - - if (remove && node.parentNode) - { - node.parentNode.removeChild(node); - } - } - } - -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -var mxUtils = -{ - /** - * Class: mxUtils - * - * A singleton class that provides cross-browser helper methods. - * This is a global functionality. To access the functions in this - * class, use the global classname appended by the functionname. - * You may have to load chrome://global/content/contentAreaUtils.js - * to disable certain security restrictions in Mozilla for the , - * , and function. - * - * For example, the following code displays an error message: - * - * (code) - * mxUtils.error('Browser is not supported!', 200, false); - * (end) - * - * Variable: errorResource - * - * Specifies the resource key for the title of the error window. If the - * resource for this key does not exist then the value is used as - * the title. Default is 'error'. - */ - errorResource: (mxClient.language != 'none') ? 'error' : '', - - /** - * Variable: closeResource - * - * Specifies the resource key for the label of the close button. If the - * resource for this key does not exist then the value is used as - * the label. Default is 'close'. - */ - closeResource: (mxClient.language != 'none') ? 'close' : '', - - /** - * Variable: errorImage - * - * Defines the image used for error dialogs. - */ - errorImage: mxClient.imageBasePath + '/error.gif', - - /** - * Function: removeCursors - * - * Removes the cursors from the style of the given DOM node and its - * descendants. - * - * Parameters: - * - * element - DOM node to remove the cursor style from. - */ - removeCursors: function(element) - { - if (element.style != null) - { - element.style.cursor = ''; - } - - var children = element.childNodes; - - if (children != null) - { - var childCount = children.length; - - for (var i = 0; i < childCount; i += 1) - { - mxUtils.removeCursors(children[i]); - } - } - }, - - /** - * Function: getCurrentStyle - * - * Returns the current style of the specified element. - * - * Parameters: - * - * element - DOM node whose current style should be returned. - */ - getCurrentStyle: function() - { - if (mxClient.IS_IE && (document.documentMode == null || document.documentMode < 9)) - { - return function(element) - { - return (element != null) ? element.currentStyle : null; - }; - } - else - { - return function(element) - { - return (element != null) ? - window.getComputedStyle(element, '') : - null; - }; - } - }(), - - /** - * Function: parseCssNumber - * - * Parses the given CSS numeric value adding handling for the values thin, - * medium and thick (2, 4 and 6). - */ - parseCssNumber: function(value) - { - if (value == 'thin') - { - value = '2'; - } - else if (value == 'medium') - { - value = '4'; - } - else if (value == 'thick') - { - value = '6'; - } - - value = parseFloat(value); - - if (isNaN(value)) - { - value = 0; - } - - return value; - }, - - /** - * Function: setPrefixedStyle - * - * Adds the given style with the standard name and an optional vendor prefix for the current - * browser. - * - * (code) - * mxUtils.setPrefixedStyle(node.style, 'transformOrigin', '0% 0%'); - * (end) - */ - setPrefixedStyle: function() - { - var prefix = null; - - if (mxClient.IS_OT) - { - prefix = 'O'; - } - else if (mxClient.IS_SF || mxClient.IS_GC) - { - prefix = 'Webkit'; - } - else if (mxClient.IS_MT) - { - prefix = 'Moz'; - } - else if (mxClient.IS_IE && document.documentMode >= 9 && document.documentMode < 10) - { - prefix = 'ms'; - } - - return function(style, name, value) - { - style[name] = value; - - if (prefix != null && name.length > 0) - { - name = prefix + name.substring(0, 1).toUpperCase() + name.substring(1); - style[name] = value; - } - }; - }(), - - /** - * Function: hasScrollbars - * - * Returns true if the overflow CSS property of the given node is either - * scroll or auto. - * - * Parameters: - * - * node - DOM node whose style should be checked for scrollbars. - */ - hasScrollbars: function(node) - { - var style = mxUtils.getCurrentStyle(node); - - return style != null && (style.overflow == 'scroll' || style.overflow == 'auto'); - }, - - /** - * Function: bind - * - * Returns a wrapper function that locks the execution scope of the given - * function to the specified scope. Inside funct, the "this" keyword - * becomes a reference to that scope. - */ - bind: function(scope, funct) - { - return function() - { - return funct.apply(scope, arguments); - }; - }, - - /** - * Function: eval - * - * Evaluates the given expression using eval and returns the JavaScript - * object that represents the expression result. Supports evaluation of - * expressions that define functions and returns the function object for - * these expressions. - * - * Parameters: - * - * expr - A string that represents a JavaScript expression. - */ - eval: function(expr) - { - var result = null; - - if (expr.indexOf('function') >= 0) - { - try - { - eval('var _mxJavaScriptExpression='+expr); - result = _mxJavaScriptExpression; - // TODO: Use delete here? - _mxJavaScriptExpression = null; - } - catch (e) - { - mxLog.warn(e.message + ' while evaluating ' + expr); - } - } - else - { - try - { - result = eval(expr); - } - catch (e) - { - mxLog.warn(e.message + ' while evaluating ' + expr); - } - } - - return result; - }, - - /** - * Function: findNode - * - * Returns the first node where attr equals value. - * This implementation does not use XPath. - */ - findNode: function(node, attr, value) - { - if (node.nodeType == mxConstants.NODETYPE_ELEMENT) - { - var tmp = node.getAttribute(attr); - - if (tmp != null && tmp == value) - { - return node; - } - } - - node = node.firstChild; - - while (node != null) - { - var result = mxUtils.findNode(node, attr, value); - - if (result != null) - { - return result; - } - - node = node.nextSibling; - } - - return null; - }, - - /** - * Function: getFunctionName - * - * Returns the name for the given function. - * - * Parameters: - * - * f - JavaScript object that represents a function. - */ - getFunctionName: function(f) - { - var str = null; - - if (f != null) - { - if (f.name != null) - { - str = f.name; - } - else - { - str = mxUtils.trim(f.toString()); - - if (/^function\s/.test(str)) - { - str = mxUtils.ltrim(str.substring(9)); - var idx2 = str.indexOf('('); - - if (idx2 > 0) - { - str = str.substring(0, idx2); - } - } - } - } - - return str; - }, - - /** - * Function: indexOf - * - * Returns the index of obj in array or -1 if the array does not contain - * the given object. - * - * Parameters: - * - * array - Array to check for the given obj. - * obj - Object to find in the given array. - */ - indexOf: function(array, obj) - { - if (array != null && obj != null) - { - for (var i = 0; i < array.length; i++) - { - if (array[i] == obj) - { - return i; - } - } - } - - return -1; - }, - - /** - * Function: forEach - * - * Calls the given function for each element of the given array and returns - * the array. - * - * Parameters: - * - * array - Array that contains the elements. - * fn - Function to be called for each object. - */ - forEach: function(array, fn) - { - if (array != null && fn != null) - { - for (var i = 0; i < array.length; i++) - { - fn(array[i]); - } - } - - return array; - }, - - /** - * Function: remove - * - * Removes all occurrences of the given object in the given array or - * object. If there are multiple occurrences of the object, be they - * associative or as an array entry, all occurrences are removed from - * the array or deleted from the object. By removing the object from - * the array, all elements following the removed element are shifted - * by one step towards the beginning of the array. - * - * The length of arrays is not modified inside this function. - * - * Parameters: - * - * obj - Object to find in the given array. - * array - Array to check for the given obj. - */ - remove: function(obj, array) - { - var result = null; - - if (typeof(array) == 'object') - { - var index = mxUtils.indexOf(array, obj); - - while (index >= 0) - { - array.splice(index, 1); - result = obj; - index = mxUtils.indexOf(array, obj); - } - } - - for (var key in array) - { - if (array[key] == obj) - { - delete array[key]; - result = obj; - } - } - - return result; - }, - - /** - * Function: isNode - * - * Returns true if the given value is an XML node with the node name - * and if the optional attribute has the specified value. - * - * This implementation assumes that the given value is a DOM node if the - * nodeType property is numeric, that is, if isNaN returns false for - * value.nodeType. - * - * Parameters: - * - * value - Object that should be examined as a node. - * nodeName - String that specifies the node name. - * attributeName - Optional attribute name to check. - * attributeValue - Optional attribute value to check. - */ - isNode: function(value, nodeName, attributeName, attributeValue) - { - if (value != null && !isNaN(value.nodeType) && (nodeName == null || - value.nodeName.toLowerCase() == nodeName.toLowerCase())) - { - return attributeName == null || - value.getAttribute(attributeName) == attributeValue; - } - - return false; - }, - - /** - * Function: isAncestorNode - * - * Returns true if the given ancestor is an ancestor of the - * given DOM node in the DOM. This also returns true if the - * child is the ancestor. - * - * Parameters: - * - * ancestor - DOM node that represents the ancestor. - * child - DOM node that represents the child. - */ - isAncestorNode: function(ancestor, child) - { - var parent = child; - - while (parent != null) - { - if (parent == ancestor) - { - return true; - } - - parent = parent.parentNode; - } - - return false; - }, - - /** - * Function: getChildNodes - * - * Returns an array of child nodes that are of the given node type. - * - * Parameters: - * - * node - Parent DOM node to return the children from. - * nodeType - Optional node type to return. Default is - * . - */ - getChildNodes: function(node, nodeType) - { - nodeType = nodeType || mxConstants.NODETYPE_ELEMENT; - - var children = []; - var tmp = node.firstChild; - - while (tmp != null) - { - if (tmp.nodeType == nodeType) - { - children.push(tmp); - } - - tmp = tmp.nextSibling; - } - - return children; - }, - - /** - * Function: importNode - * - * Cross browser implementation for document.importNode. Uses document.importNode - * in all browsers but IE, where the node is cloned by creating a new node and - * copying all attributes and children into it using importNode, recursively. - * - * Parameters: - * - * doc - Document to import the node into. - * node - Node to be imported. - * allChildren - If all children should be imported. - */ - importNode: function(doc, node, allChildren) - { - if (mxClient.IS_IE && (document.documentMode == null || document.documentMode < 10)) - { - switch (node.nodeType) - { - case 1: /* element */ - { - var newNode = doc.createElement(node.nodeName); - - if (node.attributes && node.attributes.length > 0) - { - for (var i = 0; i < node.attributes.length; i++) - { - newNode.setAttribute(node.attributes[i].nodeName, - node.getAttribute(node.attributes[i].nodeName)); - } - - if (allChildren && node.childNodes && node.childNodes.length > 0) - { - for (var i = 0; i < node.childNodes.length; i++) - { - newNode.appendChild(mxUtils.importNode(doc, node.childNodes[i], allChildren)); - } - } - } - - return newNode; - break; - } - case 3: /* text */ - case 4: /* cdata-section */ - case 8: /* comment */ - { - return doc.createTextNode(node.value); - break; - } - }; - } - else - { - return doc.importNode(node, allChildren); - } - }, - - /** - * Function: createXmlDocument - * - * Returns a new, empty XML document. - */ - createXmlDocument: function() - { - var doc = null; - - if (document.implementation && document.implementation.createDocument) - { - doc = document.implementation.createDocument('', '', null); - } - else if (window.ActiveXObject) - { - doc = new ActiveXObject('Microsoft.XMLDOM'); - } - - return doc; - }, - - /** - * Function: parseXml - * - * Parses the specified XML string into a new XML document and returns the - * new document. - * - * Example: - * - * (code) - * var doc = mxUtils.parseXml( - * ''+ - * ''+ - * ''+ - * ''+ - * ''); - * (end) - * - * Parameters: - * - * xml - String that contains the XML data. - */ - parseXml: function() - { - if (window.DOMParser) - { - return function(xml) - { - var parser = new DOMParser(); - - return parser.parseFromString(xml, 'text/xml'); - }; - } - else // IE<=9 - { - return function(xml) - { - var result = mxUtils.createXmlDocument(); - result.async = false; - // Workaround for parsing errors with SVG DTD - result.validateOnParse = false; - result.resolveExternals = false; - result.loadXML(xml); - - return result; - }; - } - }(), - - /** - * Function: clearSelection - * - * Clears the current selection in the page. - */ - clearSelection: function() - { - if (document.selection) - { - return function() - { - document.selection.empty(); - }; - } - else if (window.getSelection) - { - return function() - { - if (window.getSelection().empty) - { - window.getSelection().empty(); - } - else if (window.getSelection().removeAllRanges) - { - window.getSelection().removeAllRanges(); - } - }; - } - else - { - return function() { }; - } - }(), - - /** - * Function: getPrettyXML - * - * Returns a pretty printed string that represents the XML tree for the - * given node. This method should only be used to print XML for reading, - * use instead to obtain a string for processing. - * - * Parameters: - * - * node - DOM node to return the XML for. - * tab - Optional string that specifies the indentation for one level. - * Default is two spaces. - * indent - Optional string that represents the current indentation. - * Default is an empty string. - */ - getPrettyXml: function(node, tab, indent) - { - var result = []; - - if (node != null) - { - tab = tab || ' '; - indent = indent || ''; - - if (node.nodeType == mxConstants.NODETYPE_TEXT) - { - var value = mxUtils.trim(mxUtils.getTextContent(node)); - - if (value.length > 0) - { - result.push(indent + mxUtils.htmlEntities(value) + '\n'); - } - } - else - { - result.push(indent + '<' + node.nodeName); - - // Creates the string with the node attributes - // and converts all HTML entities in the values - var attrs = node.attributes; - - if (attrs != null) - { - for (var i = 0; i < attrs.length; i++) - { - var val = mxUtils.htmlEntities(attrs[i].value); - result.push(' ' + attrs[i].nodeName + '="' + val + '"'); - } - } - - // Recursively creates the XML string for each - // child nodes and appends it here with an - // indentation - var tmp = node.firstChild; - - if (tmp != null) - { - result.push('>\n'); - - while (tmp != null) - { - result.push(mxUtils.getPrettyXml(tmp, tab, indent + tab)); - tmp = tmp.nextSibling; - } - - result.push(indent + '\n'); - } - else - { - result.push('/>\n'); - } - } - } - - return result.join(''); - }, - - /** - * Function: removeWhitespace - * - * Removes the sibling text nodes for the given node that only consists - * of tabs, newlines and spaces. - * - * Parameters: - * - * node - DOM node whose siblings should be removed. - * before - Optional boolean that specifies the direction of the traversal. - */ - removeWhitespace: function(node, before) - { - var tmp = (before) ? node.previousSibling : node.nextSibling; - - while (tmp != null && tmp.nodeType == mxConstants.NODETYPE_TEXT) - { - var next = (before) ? tmp.previousSibling : tmp.nextSibling; - var text = mxUtils.getTextContent(tmp); - - if (mxUtils.trim(text).length == 0) - { - tmp.parentNode.removeChild(tmp); - } - - tmp = next; - } - }, - - /** - * Function: htmlEntities - * - * Replaces characters (less than, greater than, newlines and quotes) with - * their HTML entities in the given string and returns the result. - * - * Parameters: - * - * s - String that contains the characters to be converted. - * newline - If newlines should be replaced. Default is true. - */ - htmlEntities: function(s, newline) - { - s = String(s || ''); - - s = s.replace(/&/g,'&'); // 38 26 - s = s.replace(/"/g,'"'); // 34 22 - s = s.replace(/\'/g,'''); // 39 27 - s = s.replace(//g,'>'); // 62 3E - - if (newline == null || newline) - { - s = s.replace(/\n/g, ' '); - } - - return s; - }, - - /** - * Function: isVml - * - * Returns true if the given node is in the VML namespace. - * - * Parameters: - * - * node - DOM node whose tag urn should be checked. - */ - isVml: function(node) - { - return node != null && node.tagUrn == 'urn:schemas-microsoft-com:vml'; - }, - - /** - * Function: getXml - * - * Returns the XML content of the specified node. For Internet Explorer, - * all \r\n\t[\t]* are removed from the XML string and the remaining \r\n - * are replaced by \n. All \n are then replaced with linefeed, or if - * no linefeed is defined. - * - * Parameters: - * - * node - DOM node to return the XML for. - * linefeed - Optional string that linefeeds are converted into. Default is - * - */ - getXml: function(node, linefeed) - { - var xml = ''; - - if (window.XMLSerializer != null) - { - var xmlSerializer = new XMLSerializer(); - xml = xmlSerializer.serializeToString(node); - } - else if (node.xml != null) - { - xml = node.xml.replace(/\r\n\t[\t]*/g, ''). - replace(/>\r\n/g, '>'). - replace(/\r\n/g, '\n'); - } - - // Replaces linefeeds with HTML Entities. - linefeed = linefeed || ' '; - xml = xml.replace(/\n/g, linefeed); - - return xml; - }, - - /** - * Function: extractTextWithWhitespace - * - * Returns the text content of the specified node. - * - * Parameters: - * - * elems - DOM nodes to return the text for. - */ - extractTextWithWhitespace: function(elems) - { - // Known block elements for handling linefeeds (list is not complete) - var blocks = ['BLOCKQUOTE', 'DIV', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6', 'OL', 'P', 'PRE', 'TABLE', 'UL']; - var ret = []; - - function doExtract(elts) - { - // Single break should be ignored - if (elts.length == 1 && (elts[0].nodeName == 'BR' || - elts[0].innerHTML == '\n')) - { - return; - } - - for (var i = 0; i < elts.length; i++) - { - var elem = elts[i]; - - // DIV with a br or linefeed forces a linefeed - if (elem.nodeName == 'BR' || elem.innerHTML == '\n' || - ((elts.length == 1 || i == 0) && (elem.nodeName == 'DIV' && - elem.innerHTML.toLowerCase() == '
'))) - { - ret.push('\n'); - } - else - { - if (elem.nodeType === 3 || elem.nodeType === 4) - { - if (elem.nodeValue.length > 0) - { - ret.push(elem.nodeValue); - } - } - else if (elem.nodeType !== 8 && elem.childNodes.length > 0) - { - doExtract(elem.childNodes); - } - - if (i < elts.length - 1 && mxUtils.indexOf(blocks, elts[i + 1].nodeName) >= 0) - { - ret.push('\n'); - } - } - } - }; - - doExtract(elems); - - return ret.join(''); - }, - - /** - * Function: replaceTrailingNewlines - * - * Replaces each trailing newline with the given pattern. - */ - replaceTrailingNewlines: function(str, pattern) - { - // LATER: Check is this can be done with a regular expression - var postfix = ''; - - while (str.length > 0 && str.charAt(str.length - 1) == '\n') - { - str = str.substring(0, str.length - 1); - postfix += pattern; - } - - return str + postfix; - }, - - /** - * Function: getTextContent - * - * Returns the text content of the specified node. - * - * Parameters: - * - * node - DOM node to return the text content for. - */ - getTextContent: function(node) - { - // Only IE10- - if (mxClient.IS_IE && node.innerText !== undefined) - { - return node.innerText; - } - else - { - return (node != null) ? node[(node.textContent === undefined) ? 'text' : 'textContent'] : ''; - } - }, - - /** - * Function: setTextContent - * - * Sets the text content of the specified node. - * - * Parameters: - * - * node - DOM node to set the text content for. - * text - String that represents the text content. - */ - setTextContent: function(node, text) - { - if (node.innerText !== undefined) - { - node.innerText = text; - } - else - { - node[(node.textContent === undefined) ? 'text' : 'textContent'] = text; - } - }, - - /** - * Function: getInnerHtml - * - * Returns the inner HTML for the given node as a string or an empty string - * if no node was specified. The inner HTML is the text representing all - * children of the node, but not the node itself. - * - * Parameters: - * - * node - DOM node to return the inner HTML for. - */ - getInnerHtml: function() - { - if (mxClient.IS_IE) - { - return function(node) - { - if (node != null) - { - return node.innerHTML; - } - - return ''; - }; - } - else - { - return function(node) - { - if (node != null) - { - var serializer = new XMLSerializer(); - return serializer.serializeToString(node); - } - - return ''; - }; - } - }(), - - /** - * Function: getOuterHtml - * - * Returns the outer HTML for the given node as a string or an empty - * string if no node was specified. The outer HTML is the text representing - * all children of the node including the node itself. - * - * Parameters: - * - * node - DOM node to return the outer HTML for. - */ - getOuterHtml: function() - { - if (mxClient.IS_IE) - { - return function(node) - { - if (node != null) - { - if (node.outerHTML != null) - { - return node.outerHTML; - } - else - { - var tmp = []; - tmp.push('<'+node.nodeName); - - var attrs = node.attributes; - - if (attrs != null) - { - for (var i = 0; i < attrs.length; i++) - { - var value = attrs[i].value; - - if (value != null && value.length > 0) - { - tmp.push(' '); - tmp.push(attrs[i].nodeName); - tmp.push('="'); - tmp.push(value); - tmp.push('"'); - } - } - } - - if (node.innerHTML.length == 0) - { - tmp.push('/>'); - } - else - { - tmp.push('>'); - tmp.push(node.innerHTML); - tmp.push(''); - } - - return tmp.join(''); - } - } - - return ''; - }; - } - else - { - return function(node) - { - if (node != null) - { - var serializer = new XMLSerializer(); - return serializer.serializeToString(node); - } - - return ''; - }; - } - }(), - - /** - * Function: write - * - * Creates a text node for the given string and appends it to the given - * parent. Returns the text node. - * - * Parameters: - * - * parent - DOM node to append the text node to. - * text - String representing the text to be added. - */ - write: function(parent, text) - { - var doc = parent.ownerDocument; - var node = doc.createTextNode(text); - - if (parent != null) - { - parent.appendChild(node); - } - - return node; - }, - - /** - * Function: writeln - * - * Creates a text node for the given string and appends it to the given - * parent with an additional linefeed. Returns the text node. - * - * Parameters: - * - * parent - DOM node to append the text node to. - * text - String representing the text to be added. - */ - writeln: function(parent, text) - { - var doc = parent.ownerDocument; - var node = doc.createTextNode(text); - - if (parent != null) - { - parent.appendChild(node); - parent.appendChild(document.createElement('br')); - } - - return node; - }, - - /** - * Function: br - * - * Appends a linebreak to the given parent and returns the linebreak. - * - * Parameters: - * - * parent - DOM node to append the linebreak to. - */ - br: function(parent, count) - { - count = count || 1; - var br = null; - - for (var i = 0; i < count; i++) - { - if (parent != null) - { - br = parent.ownerDocument.createElement('br'); - parent.appendChild(br); - } - } - - return br; - }, - - /** - * Function: button - * - * Returns a new button with the given level and function as an onclick - * event handler. - * - * (code) - * document.body.appendChild(mxUtils.button('Test', function(evt) - * { - * alert('Hello, World!'); - * })); - * (end) - * - * Parameters: - * - * label - String that represents the label of the button. - * funct - Function to be called if the button is pressed. - * doc - Optional document to be used for creating the button. Default is the - * current document. - */ - button: function(label, funct, doc) - { - doc = (doc != null) ? doc : document; - - var button = doc.createElement('button'); - mxUtils.write(button, label); - - mxEvent.addListener(button, 'click', function(evt) - { - funct(evt); - }); - - return button; - }, - - /** - * Function: para - * - * Appends a new paragraph with the given text to the specified parent and - * returns the paragraph. - * - * Parameters: - * - * parent - DOM node to append the text node to. - * text - String representing the text for the new paragraph. - */ - para: function(parent, text) - { - var p = document.createElement('p'); - mxUtils.write(p, text); - - if (parent != null) - { - parent.appendChild(p); - } - - return p; - }, - - /** - * Function: addTransparentBackgroundFilter - * - * Adds a transparent background to the filter of the given node. This - * background can be used in IE8 standards mode (native IE8 only) to pass - * events through the node. - */ - addTransparentBackgroundFilter: function(node) - { - node.style.filter += 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src=\'' + - mxClient.imageBasePath + '/transparent.gif\', sizingMethod=\'scale\')'; - }, - - /** - * Function: linkAction - * - * Adds a hyperlink to the specified parent that invokes action on the - * specified editor. - * - * Parameters: - * - * parent - DOM node to contain the new link. - * text - String that is used as the link label. - * editor - that will execute the action. - * action - String that defines the name of the action to be executed. - * pad - Optional left-padding for the link. Default is 0. - */ - linkAction: function(parent, text, editor, action, pad) - { - return mxUtils.link(parent, text, function() - { - editor.execute(action); - }, pad); - }, - - /** - * Function: linkInvoke - * - * Adds a hyperlink to the specified parent that invokes the specified - * function on the editor passing along the specified argument. The - * function name is the name of a function of the editor instance, - * not an action name. - * - * Parameters: - * - * parent - DOM node to contain the new link. - * text - String that is used as the link label. - * editor - instance to execute the function on. - * functName - String that represents the name of the function. - * arg - Object that represents the argument to the function. - * pad - Optional left-padding for the link. Default is 0. - */ - linkInvoke: function(parent, text, editor, functName, arg, pad) - { - return mxUtils.link(parent, text, function() - { - editor[functName](arg); - }, pad); - }, - - /** - * Function: link - * - * Adds a hyperlink to the specified parent and invokes the given function - * when the link is clicked. - * - * Parameters: - * - * parent - DOM node to contain the new link. - * text - String that is used as the link label. - * funct - Function to execute when the link is clicked. - * pad - Optional left-padding for the link. Default is 0. - */ - link: function(parent, text, funct, pad) - { - var a = document.createElement('span'); - - a.style.color = 'blue'; - a.style.textDecoration = 'underline'; - a.style.cursor = 'pointer'; - - if (pad != null) - { - a.style.paddingLeft = pad+'px'; - } - - mxEvent.addListener(a, 'click', funct); - mxUtils.write(a, text); - - if (parent != null) - { - parent.appendChild(a); - } - - return a; - }, - - /** - * Function: fit - * - * Makes sure the given node is inside the visible area of the window. This - * is done by setting the left and top in the style. - */ - fit: function(node) - { - var left = parseInt(node.offsetLeft); - var width = parseInt(node.offsetWidth); - - var offset = mxUtils.getDocumentScrollOrigin(node.ownerDocument); - var sl = offset.x; - var st = offset.y; - - var b = document.body; - var d = document.documentElement; - var right = (sl) + (b.clientWidth || d.clientWidth); - - if (left + width > right) - { - node.style.left = Math.max(sl, right - width) + 'px'; - } - - var top = parseInt(node.offsetTop); - var height = parseInt(node.offsetHeight); - - var bottom = st + Math.max(b.clientHeight || 0, d.clientHeight); - - if (top + height > bottom) - { - node.style.top = Math.max(st, bottom - height) + 'px'; - } - }, - - /** - * Function: load - * - * Loads the specified URL *synchronously* and returns the . - * Throws an exception if the file cannot be loaded. See for - * an asynchronous implementation. - * - * Example: - * - * (code) - * try - * { - * var req = mxUtils.load(filename); - * var root = req.getDocumentElement(); - * // Process XML DOM... - * } - * catch (ex) - * { - * mxUtils.alert('Cannot load '+filename+': '+ex); - * } - * (end) - * - * Parameters: - * - * url - URL to get the data from. - */ - load: function(url) - { - var req = new mxXmlRequest(url, null, 'GET', false); - req.send(); - - return req; - }, - - /** - * Function: get - * - * Loads the specified URL *asynchronously* and invokes the given functions - * depending on the request status. Returns the in use. Both - * functions take the as the only parameter. See - * for a synchronous implementation. - * - * Example: - * - * (code) - * mxUtils.get(url, function(req) - * { - * var node = req.getDocumentElement(); - * // Process XML DOM... - * }); - * (end) - * - * So for example, to load a diagram into an existing graph model, the - * following code is used. - * - * (code) - * mxUtils.get(url, function(req) - * { - * var node = req.getDocumentElement(); - * var dec = new mxCodec(node.ownerDocument); - * dec.decode(node, graph.getModel()); - * }); - * (end) - * - * Parameters: - * - * url - URL to get the data from. - * onload - Optional function to execute for a successful response. - * onerror - Optional function to execute on error. - * binary - Optional boolean parameter that specifies if the request is - * binary. - * timeout - Optional timeout in ms before calling ontimeout. - * ontimeout - Optional function to execute on timeout. - */ - get: function(url, onload, onerror, binary, timeout, ontimeout) - { - var req = new mxXmlRequest(url, null, 'GET'); - - if (binary != null) - { - req.setBinary(binary); - } - - req.send(onload, onerror, timeout, ontimeout); - - return req; - }, - - /** - * Function: getAll - * - * Loads the URLs in the given array *asynchronously* and invokes the given function - * if all requests returned with a valid 2xx status. The error handler is invoked - * once on the first error or invalid response. - * - * Parameters: - * - * urls - Array of URLs to be loaded. - * onload - Callback with array of . - * onerror - Optional function to execute on error. - */ - getAll: function(urls, onload, onerror) - { - var remain = urls.length; - var result = []; - var errors = 0; - var err = function() - { - if (errors == 0 && onerror != null) - { - onerror(); - } - - errors++; - }; - - for (var i = 0; i < urls.length; i++) - { - (function(url, index) - { - mxUtils.get(url, function(req) - { - var status = req.getStatus(); - - if (status < 200 || status > 299) - { - err(); - } - else - { - result[index] = req; - remain--; - - if (remain == 0) - { - onload(result); - } - } - }, err); - })(urls[i], i); - } - - if (remain == 0) - { - onload(result); - } - }, - - /** - * Function: post - * - * Posts the specified params to the given URL *asynchronously* and invokes - * the given functions depending on the request status. Returns the - * in use. Both functions take the as the - * only parameter. Make sure to use encodeURIComponent for the parameter - * values. - * - * Example: - * - * (code) - * mxUtils.post(url, 'key=value', function(req) - * { - * mxUtils.alert('Ready: '+req.isReady()+' Status: '+req.getStatus()); - * // Process req.getDocumentElement() using DOM API if OK... - * }); - * (end) - * - * Parameters: - * - * url - URL to get the data from. - * params - Parameters for the post request. - * onload - Optional function to execute for a successful response. - * onerror - Optional function to execute on error. - */ - post: function(url, params, onload, onerror) - { - return new mxXmlRequest(url, params).send(onload, onerror); - }, - - /** - * Function: submit - * - * Submits the given parameters to the specified URL using - * and returns the . - * Make sure to use encodeURIComponent for the parameter - * values. - * - * Parameters: - * - * url - URL to get the data from. - * params - Parameters for the form. - * doc - Document to create the form in. - * target - Target to send the form result to. - */ - submit: function(url, params, doc, target) - { - return new mxXmlRequest(url, params).simulate(doc, target); - }, - - /** - * Function: loadInto - * - * Loads the specified URL *asynchronously* into the specified document, - * invoking onload after the document has been loaded. This implementation - * does not use , but the document.load method. - * - * Parameters: - * - * url - URL to get the data from. - * doc - The document to load the URL into. - * onload - Function to execute when the URL has been loaded. - */ - loadInto: function(url, doc, onload) - { - if (mxClient.IS_IE) - { - doc.onreadystatechange = function () - { - if (doc.readyState == 4) - { - onload(); - } - }; - } - else - { - doc.addEventListener('load', onload, false); - } - - doc.load(url); - }, - - /** - * Function: getValue - * - * Returns the value for the given key in the given associative array or - * the given default value if the value is null. - * - * Parameters: - * - * array - Associative array that contains the value for the key. - * key - Key whose value should be returned. - * defaultValue - Value to be returned if the value for the given - * key is null. - */ - getValue: function(array, key, defaultValue) - { - var value = (array != null) ? array[key] : null; - - if (value == null) - { - value = defaultValue; - } - - return value; - }, - - /** - * Function: getNumber - * - * Returns the numeric value for the given key in the given associative - * array or the given default value (or 0) if the value is null. The value - * is converted to a numeric value using the Number function. - * - * Parameters: - * - * array - Associative array that contains the value for the key. - * key - Key whose value should be returned. - * defaultValue - Value to be returned if the value for the given - * key is null. Default is 0. - */ - getNumber: function(array, key, defaultValue) - { - var value = (array != null) ? array[key] : null; - - if (value == null) - { - value = defaultValue || 0; - } - - return Number(value); - }, - - /** - * Function: getColor - * - * Returns the color value for the given key in the given associative - * array or the given default value if the value is null. If the value - * is then null is returned. - * - * Parameters: - * - * array - Associative array that contains the value for the key. - * key - Key whose value should be returned. - * defaultValue - Value to be returned if the value for the given - * key is null. Default is null. - */ - getColor: function(array, key, defaultValue) - { - var value = (array != null) ? array[key] : null; - - if (value == null) - { - value = defaultValue; - } - else if (value == mxConstants.NONE) - { - value = null; - } - - return value; - }, - - /** - * Function: clone - * - * Recursively clones the specified object ignoring all fieldnames in the - * given array of transient fields. is always - * ignored by this function. - * - * Parameters: - * - * obj - Object to be cloned. - * transients - Optional array of strings representing the fieldname to be - * ignored. - * shallow - Optional boolean argument to specify if a shallow clone should - * be created, that is, one where all object references are not cloned or, - * in other words, one where only atomic (strings, numbers) values are - * cloned. Default is false. - */ - clone: function(obj, transients, shallow) - { - shallow = (shallow != null) ? shallow : false; - var clone = null; - - if (obj != null && typeof(obj.constructor) == 'function') - { - clone = new obj.constructor(); - - for (var i in obj) - { - if (i != mxObjectIdentity.FIELD_NAME && (transients == null || - mxUtils.indexOf(transients, i) < 0)) - { - if (!shallow && typeof(obj[i]) == 'object') - { - clone[i] = mxUtils.clone(obj[i]); - } - else - { - clone[i] = obj[i]; - } - } - } - } - - return clone; - }, - - /** - * Function: equalPoints - * - * Compares all mxPoints in the given lists. - * - * Parameters: - * - * a - Array of to be compared. - * b - Array of to be compared. - */ - equalPoints: function(a, b) - { - if ((a == null && b != null) || (a != null && b == null) || - (a != null && b != null && a.length != b.length)) - { - return false; - } - else if (a != null && b != null) - { - for (var i = 0; i < a.length; i++) - { - if (a[i] == b[i] || (a[i] != null && !a[i].equals(b[i]))) - { - return false; - } - } - } - - return true; - }, - - /** - * Function: equalEntries - * - * Returns true if all properties of the given objects are equal. Values - * with NaN are equal to NaN and unequal to any other value. - * - * Parameters: - * - * a - First object to be compared. - * b - Second object to be compared. - */ - equalEntries: function(a, b) - { - if ((a == null && b != null) || (a != null && b == null) || - (a != null && b != null && a.length != b.length)) - { - return false; - } - else if (a != null && b != null) - { - // Counts keys in b to check if all values have been compared - var count = 0; - - for (var key in b) - { - count++; - } - - for (var key in a) - { - count-- - - if ((!mxUtils.isNaN(a[key]) || !mxUtils.isNaN(b[key])) && a[key] != b[key]) - { - return false; - } - } - } - - return count == 0; - }, - - /** - * Function: removeDuplicates - * - * Removes all duplicates from the given array. - */ - removeDuplicates: function(arr) - { - var dict = new mxDictionary(); - var result = []; - - for (var i = 0; i < arr.length; i++) - { - if (!dict.get(arr[i])) - { - result.push(arr[i]); - dict.put(arr[i], true); - } - } - - return result; - }, - - /** - * Function: isNaN - * - * Returns true if the given value is of type number and isNaN returns true. - */ - isNaN: function(value) - { - return typeof(value) == 'number' && isNaN(value); - }, - - /** - * Function: extend - * - * Assigns a copy of the superclass prototype to the subclass prototype. - * Note that this does not call the constructor of the superclass at this - * point, the superclass constructor should be called explicitely in the - * subclass constructor. Below is an example. - * - * (code) - * MyGraph = function(container, model, renderHint, stylesheet) - * { - * mxGraph.call(this, container, model, renderHint, stylesheet); - * } - * - * mxUtils.extend(MyGraph, mxGraph); - * (end) - * - * Parameters: - * - * ctor - Constructor of the subclass. - * superCtor - Constructor of the superclass. - */ - extend: function(ctor, superCtor) - { - var f = function() {}; - f.prototype = superCtor.prototype; - - ctor.prototype = new f(); - ctor.prototype.constructor = ctor; - }, - - /** - * Function: toString - * - * Returns a textual representation of the specified object. - * - * Parameters: - * - * obj - Object to return the string representation for. - */ - toString: function(obj) - { - var output = ''; - - for (var i in obj) - { - try - { - if (obj[i] == null) - { - output += i + ' = [null]\n'; - } - else if (typeof(obj[i]) == 'function') - { - output += i + ' => [Function]\n'; - } - else if (typeof(obj[i]) == 'object') - { - var ctor = mxUtils.getFunctionName(obj[i].constructor); - output += i + ' => [' + ctor + ']\n'; - } - else - { - output += i + ' = ' + obj[i] + '\n'; - } - } - catch (e) - { - output += i + '=' + e.message; - } - } - - return output; - }, - - /** - * Function: toRadians - * - * Converts the given degree to radians. - */ - toRadians: function(deg) - { - return Math.PI * deg / 180; - }, - - /** - * Function: toDegree - * - * Converts the given radians to degree. - */ - toDegree: function(rad) - { - return rad * 180 / Math.PI; - }, - - /** - * Function: arcToCurves - * - * Converts the given arc to a series of curves. - */ - arcToCurves: function(x0, y0, r1, r2, angle, largeArcFlag, sweepFlag, x, y) - { - x -= x0; - y -= y0; - - if (r1 === 0 || r2 === 0) - { - return result; - } - - var fS = sweepFlag; - var psai = angle; - r1 = Math.abs(r1); - r2 = Math.abs(r2); - var ctx = -x / 2; - var cty = -y / 2; - var cpsi = Math.cos(psai * Math.PI / 180); - var spsi = Math.sin(psai * Math.PI / 180); - var rxd = cpsi * ctx + spsi * cty; - var ryd = -1 * spsi * ctx + cpsi * cty; - var rxdd = rxd * rxd; - var rydd = ryd * ryd; - var r1x = r1 * r1; - var r2y = r2 * r2; - var lamda = rxdd / r1x + rydd / r2y; - var sds; - - if (lamda > 1) - { - r1 = Math.sqrt(lamda) * r1; - r2 = Math.sqrt(lamda) * r2; - sds = 0; - } - else - { - var seif = 1; - - if (largeArcFlag === fS) - { - seif = -1; - } - - sds = seif * Math.sqrt((r1x * r2y - r1x * rydd - r2y * rxdd) / (r1x * rydd + r2y * rxdd)); - } - - var txd = sds * r1 * ryd / r2; - var tyd = -1 * sds * r2 * rxd / r1; - var tx = cpsi * txd - spsi * tyd + x / 2; - var ty = spsi * txd + cpsi * tyd + y / 2; - var rad = Math.atan2((ryd - tyd) / r2, (rxd - txd) / r1) - Math.atan2(0, 1); - var s1 = (rad >= 0) ? rad : 2 * Math.PI + rad; - rad = Math.atan2((-ryd - tyd) / r2, (-rxd - txd) / r1) - Math.atan2((ryd - tyd) / r2, (rxd - txd) / r1); - var dr = (rad >= 0) ? rad : 2 * Math.PI + rad; - - if (fS == 0 && dr > 0) - { - dr -= 2 * Math.PI; - } - else if (fS != 0 && dr < 0) - { - dr += 2 * Math.PI; - } - - var sse = dr * 2 / Math.PI; - var seg = Math.ceil(sse < 0 ? -1 * sse : sse); - var segr = dr / seg; - var t = 8/3 * Math.sin(segr / 4) * Math.sin(segr / 4) / Math.sin(segr / 2); - var cpsir1 = cpsi * r1; - var cpsir2 = cpsi * r2; - var spsir1 = spsi * r1; - var spsir2 = spsi * r2; - var mc = Math.cos(s1); - var ms = Math.sin(s1); - var x2 = -t * (cpsir1 * ms + spsir2 * mc); - var y2 = -t * (spsir1 * ms - cpsir2 * mc); - var x3 = 0; - var y3 = 0; - - var result = []; - - for (var n = 0; n < seg; ++n) - { - s1 += segr; - mc = Math.cos(s1); - ms = Math.sin(s1); - - x3 = cpsir1 * mc - spsir2 * ms + tx; - y3 = spsir1 * mc + cpsir2 * ms + ty; - var dx = -t * (cpsir1 * ms + spsir2 * mc); - var dy = -t * (spsir1 * ms - cpsir2 * mc); - - // CurveTo updates x0, y0 so need to restore it - var index = n * 6; - result[index] = Number(x2 + x0); - result[index + 1] = Number(y2 + y0); - result[index + 2] = Number(x3 - dx + x0); - result[index + 3] = Number(y3 - dy + y0); - result[index + 4] = Number(x3 + x0); - result[index + 5] = Number(y3 + y0); - - x2 = x3 + dx; - y2 = y3 + dy; - } - - return result; - }, - - /** - * Function: getBoundingBox - * - * Returns the bounding box for the rotated rectangle. - * - * Parameters: - * - * rect - to be rotated. - * angle - Number that represents the angle (in degrees). - * cx - Optional that represents the rotation center. If no - * rotation center is given then the center of rect is used. - */ - getBoundingBox: function(rect, rotation, cx) - { - var result = null; - - if (rect != null && rotation != null && rotation != 0) - { - var rad = mxUtils.toRadians(rotation); - var cos = Math.cos(rad); - var sin = Math.sin(rad); - - cx = (cx != null) ? cx : new mxPoint(rect.x + rect.width / 2, rect.y + rect.height / 2); - - var p1 = new mxPoint(rect.x, rect.y); - var p2 = new mxPoint(rect.x + rect.width, rect.y); - var p3 = new mxPoint(p2.x, rect.y + rect.height); - var p4 = new mxPoint(rect.x, p3.y); - - p1 = mxUtils.getRotatedPoint(p1, cos, sin, cx); - p2 = mxUtils.getRotatedPoint(p2, cos, sin, cx); - p3 = mxUtils.getRotatedPoint(p3, cos, sin, cx); - p4 = mxUtils.getRotatedPoint(p4, cos, sin, cx); - - result = new mxRectangle(p1.x, p1.y, 0, 0); - result.add(new mxRectangle(p2.x, p2.y, 0, 0)); - result.add(new mxRectangle(p3.x, p3.y, 0, 0)); - result.add(new mxRectangle(p4.x, p4.y, 0, 0)); - } - - return result; - }, - - /** - * Function: getRotatedPoint - * - * Rotates the given point by the given cos and sin. - */ - getRotatedPoint: function(pt, cos, sin, c) - { - c = (c != null) ? c : new mxPoint(); - var x = pt.x - c.x; - var y = pt.y - c.y; - - var x1 = x * cos - y * sin; - var y1 = y * cos + x * sin; - - return new mxPoint(x1 + c.x, y1 + c.y); - }, - - /** - * Returns an integer mask of the port constraints of the given map - * @param dict the style map to determine the port constraints for - * @param defaultValue Default value to return if the key is undefined. - * @return the mask of port constraint directions - * - * Parameters: - * - * terminal - that represents the terminal. - * edge - that represents the edge. - * source - Boolean that specifies if the terminal is the source terminal. - * defaultValue - Default value to be returned. - */ - getPortConstraints: function(terminal, edge, source, defaultValue) - { - var value = mxUtils.getValue(terminal.style, mxConstants.STYLE_PORT_CONSTRAINT, - mxUtils.getValue(edge.style, (source) ? mxConstants.STYLE_SOURCE_PORT_CONSTRAINT : - mxConstants.STYLE_TARGET_PORT_CONSTRAINT, null)); - - if (value == null) - { - return defaultValue; - } - else - { - var directions = value.toString(); - var returnValue = mxConstants.DIRECTION_MASK_NONE; - var constraintRotationEnabled = mxUtils.getValue(terminal.style, mxConstants.STYLE_PORT_CONSTRAINT_ROTATION, 0); - var rotation = 0; - - if (constraintRotationEnabled == 1) - { - rotation = mxUtils.getValue(terminal.style, mxConstants.STYLE_ROTATION, 0); - } - - var quad = 0; - - if (rotation > 45) - { - quad = 1; - - if (rotation >= 135) - { - quad = 2; - } - } - else if (rotation < -45) - { - quad = 3; - - if (rotation <= -135) - { - quad = 2; - } - } - - if (directions.indexOf(mxConstants.DIRECTION_NORTH) >= 0) - { - switch (quad) - { - case 0: - returnValue |= mxConstants.DIRECTION_MASK_NORTH; - break; - case 1: - returnValue |= mxConstants.DIRECTION_MASK_EAST; - break; - case 2: - returnValue |= mxConstants.DIRECTION_MASK_SOUTH; - break; - case 3: - returnValue |= mxConstants.DIRECTION_MASK_WEST; - break; - } - } - if (directions.indexOf(mxConstants.DIRECTION_WEST) >= 0) - { - switch (quad) - { - case 0: - returnValue |= mxConstants.DIRECTION_MASK_WEST; - break; - case 1: - returnValue |= mxConstants.DIRECTION_MASK_NORTH; - break; - case 2: - returnValue |= mxConstants.DIRECTION_MASK_EAST; - break; - case 3: - returnValue |= mxConstants.DIRECTION_MASK_SOUTH; - break; - } - } - if (directions.indexOf(mxConstants.DIRECTION_SOUTH) >= 0) - { - switch (quad) - { - case 0: - returnValue |= mxConstants.DIRECTION_MASK_SOUTH; - break; - case 1: - returnValue |= mxConstants.DIRECTION_MASK_WEST; - break; - case 2: - returnValue |= mxConstants.DIRECTION_MASK_NORTH; - break; - case 3: - returnValue |= mxConstants.DIRECTION_MASK_EAST; - break; - } - } - if (directions.indexOf(mxConstants.DIRECTION_EAST) >= 0) - { - switch (quad) - { - case 0: - returnValue |= mxConstants.DIRECTION_MASK_EAST; - break; - case 1: - returnValue |= mxConstants.DIRECTION_MASK_SOUTH; - break; - case 2: - returnValue |= mxConstants.DIRECTION_MASK_WEST; - break; - case 3: - returnValue |= mxConstants.DIRECTION_MASK_NORTH; - break; - } - } - - return returnValue; - } - }, - - /** - * Function: reversePortConstraints - * - * Reverse the port constraint bitmask. For example, north | east - * becomes south | west - */ - reversePortConstraints: function(constraint) - { - var result = 0; - - result = (constraint & mxConstants.DIRECTION_MASK_WEST) << 3; - result |= (constraint & mxConstants.DIRECTION_MASK_NORTH) << 1; - result |= (constraint & mxConstants.DIRECTION_MASK_SOUTH) >> 1; - result |= (constraint & mxConstants.DIRECTION_MASK_EAST) >> 3; - - return result; - }, - - /** - * Function: findNearestSegment - * - * Finds the index of the nearest segment on the given cell state for - * the specified coordinate pair. - */ - findNearestSegment: function(state, x, y) - { - var index = -1; - - if (state.absolutePoints.length > 0) - { - var last = state.absolutePoints[0]; - var min = null; - - for (var i = 1; i < state.absolutePoints.length; i++) - { - var current = state.absolutePoints[i]; - var dist = mxUtils.ptSegDistSq(last.x, last.y, - current.x, current.y, x, y); - - if (min == null || dist < min) - { - min = dist; - index = i - 1; - } - - last = current; - } - } - - return index; - }, - - /** - * Function: getDirectedBounds - * - * Adds the given margins to the given rectangle and rotates and flips the - * rectangle according to the respective styles in style. - */ - getDirectedBounds: function (rect, m, style, flipH, flipV) - { - var d = mxUtils.getValue(style, mxConstants.STYLE_DIRECTION, mxConstants.DIRECTION_EAST); - flipH = (flipH != null) ? flipH : mxUtils.getValue(style, mxConstants.STYLE_FLIPH, false); - flipV = (flipV != null) ? flipV : mxUtils.getValue(style, mxConstants.STYLE_FLIPV, false); - - m.x = Math.round(Math.max(0, Math.min(rect.width, m.x))); - m.y = Math.round(Math.max(0, Math.min(rect.height, m.y))); - m.width = Math.round(Math.max(0, Math.min(rect.width, m.width))); - m.height = Math.round(Math.max(0, Math.min(rect.height, m.height))); - - if ((flipV && (d == mxConstants.DIRECTION_SOUTH || d == mxConstants.DIRECTION_NORTH)) || - (flipH && (d == mxConstants.DIRECTION_EAST || d == mxConstants.DIRECTION_WEST))) - { - var tmp = m.x; - m.x = m.width; - m.width = tmp; - } - - if ((flipH && (d == mxConstants.DIRECTION_SOUTH || d == mxConstants.DIRECTION_NORTH)) || - (flipV && (d == mxConstants.DIRECTION_EAST || d == mxConstants.DIRECTION_WEST))) - { - var tmp = m.y; - m.y = m.height; - m.height = tmp; - } - - var m2 = mxRectangle.fromRectangle(m); - - if (d == mxConstants.DIRECTION_SOUTH) - { - m2.y = m.x; - m2.x = m.height; - m2.width = m.y; - m2.height = m.width; - } - else if (d == mxConstants.DIRECTION_WEST) - { - m2.y = m.height; - m2.x = m.width; - m2.width = m.x; - m2.height = m.y; - } - else if (d == mxConstants.DIRECTION_NORTH) - { - m2.y = m.width; - m2.x = m.y; - m2.width = m.height; - m2.height = m.x; - } - - return new mxRectangle(rect.x + m2.x, rect.y + m2.y, rect.width - m2.width - m2.x, rect.height - m2.height - m2.y); - }, - - /** - * Function: getPerimeterPoint - * - * Returns the intersection between the polygon defined by the array of - * points and the line between center and point. - */ - getPerimeterPoint: function (pts, center, point) - { - var min = null; - - for (var i = 0; i < pts.length - 1; i++) - { - var pt = mxUtils.intersection(pts[i].x, pts[i].y, pts[i + 1].x, pts[i + 1].y, - center.x, center.y, point.x, point.y); - - if (pt != null) - { - var dx = point.x - pt.x; - var dy = point.y - pt.y; - var ip = {p: pt, distSq: dy * dy + dx * dx}; - - if (ip != null && (min == null || min.distSq > ip.distSq)) - { - min = ip; - } - } - } - - return (min != null) ? min.p : null; - }, - - /** - * Function: rectangleIntersectsSegment - * - * Returns true if the given rectangle intersects the given segment. - * - * Parameters: - * - * bounds - that represents the rectangle. - * p1 - that represents the first point of the segment. - * p2 - that represents the second point of the segment. - */ - rectangleIntersectsSegment: function(bounds, p1, p2) - { - var top = bounds.y; - var left = bounds.x; - var bottom = top + bounds.height; - var right = left + bounds.width; - - // Find min and max X for the segment - var minX = p1.x; - var maxX = p2.x; - - if (p1.x > p2.x) - { - minX = p2.x; - maxX = p1.x; - } - - // Find the intersection of the segment's and rectangle's x-projections - if (maxX > right) - { - maxX = right; - } - - if (minX < left) - { - minX = left; - } - - if (minX > maxX) // If their projections do not intersect return false - { - return false; - } - - // Find corresponding min and max Y for min and max X we found before - var minY = p1.y; - var maxY = p2.y; - var dx = p2.x - p1.x; - - if (Math.abs(dx) > 0.0000001) - { - var a = (p2.y - p1.y) / dx; - var b = p1.y - a * p1.x; - minY = a * minX + b; - maxY = a * maxX + b; - } - - if (minY > maxY) - { - var tmp = maxY; - maxY = minY; - minY = tmp; - } - - // Find the intersection of the segment's and rectangle's y-projections - if (maxY > bottom) - { - maxY = bottom; - } - - if (minY < top) - { - minY = top; - } - - if (minY > maxY) // If Y-projections do not intersect return false - { - return false; - } - - return true; - }, - - /** - * Function: contains - * - * Returns true if the specified point (x, y) is contained in the given rectangle. - * - * Parameters: - * - * bounds - that represents the area. - * x - X-coordinate of the point. - * y - Y-coordinate of the point. - */ - contains: function(bounds, x, y) - { - return (bounds.x <= x && bounds.x + bounds.width >= x && - bounds.y <= y && bounds.y + bounds.height >= y); - }, - - /** - * Function: intersects - * - * Returns true if the two rectangles intersect. - * - * Parameters: - * - * a - to be checked for intersection. - * b - to be checked for intersection. - */ - intersects: function(a, b) - { - var tw = a.width; - var th = a.height; - var rw = b.width; - var rh = b.height; - - if (rw <= 0 || rh <= 0 || tw <= 0 || th <= 0) - { - return false; - } - - var tx = a.x; - var ty = a.y; - var rx = b.x; - var ry = b.y; - - rw += rx; - rh += ry; - tw += tx; - th += ty; - - return ((rw < rx || rw > tx) && - (rh < ry || rh > ty) && - (tw < tx || tw > rx) && - (th < ty || th > ry)); - }, - - /** - * Function: intersects - * - * Returns true if the two rectangles intersect. - * - * Parameters: - * - * a - to be checked for intersection. - * b - to be checked for intersection. - */ - intersectsHotspot: function(state, x, y, hotspot, min, max) - { - hotspot = (hotspot != null) ? hotspot : 1; - min = (min != null) ? min : 0; - max = (max != null) ? max : 0; - - if (hotspot > 0) - { - var cx = state.getCenterX(); - var cy = state.getCenterY(); - var w = state.width; - var h = state.height; - - var start = mxUtils.getValue(state.style, mxConstants.STYLE_STARTSIZE) * state.view.scale; - - if (start > 0) - { - if (mxUtils.getValue(state.style, mxConstants.STYLE_HORIZONTAL, true)) - { - cy = state.y + start / 2; - h = start; - } - else - { - cx = state.x + start / 2; - w = start; - } - } - - w = Math.max(min, w * hotspot); - h = Math.max(min, h * hotspot); - - if (max > 0) - { - w = Math.min(w, max); - h = Math.min(h, max); - } - - var rect = new mxRectangle(cx - w / 2, cy - h / 2, w, h); - var alpha = mxUtils.toRadians(mxUtils.getValue(state.style, mxConstants.STYLE_ROTATION) || 0); - - if (alpha != 0) - { - var cos = Math.cos(-alpha); - var sin = Math.sin(-alpha); - var cx = new mxPoint(state.getCenterX(), state.getCenterY()); - var pt = mxUtils.getRotatedPoint(new mxPoint(x, y), cos, sin, cx); - x = pt.x; - y = pt.y; - } - - return mxUtils.contains(rect, x, y); - } - - return true; - }, - - /** - * Function: getOffset - * - * Returns the offset for the specified container as an . The - * offset is the distance from the top left corner of the container to the - * top left corner of the document. - * - * Parameters: - * - * container - DOM node to return the offset for. - * scollOffset - Optional boolean to add the scroll offset of the document. - * Default is false. - */ - getOffset: function(container, scrollOffset) - { - var offsetLeft = 0; - var offsetTop = 0; - - // Ignores document scroll origin for fixed elements - var fixed = false; - var node = container; - var b = document.body; - var d = document.documentElement; - - while (node != null && node != b && node != d && !fixed) - { - var style = mxUtils.getCurrentStyle(node); - - if (style != null) - { - fixed = fixed || style.position == 'fixed'; - } - - node = node.parentNode; - } - - if (!scrollOffset && !fixed) - { - var offset = mxUtils.getDocumentScrollOrigin(container.ownerDocument); - offsetLeft += offset.x; - offsetTop += offset.y; - } - - var r = container.getBoundingClientRect(); - - if (r != null) - { - offsetLeft += r.left; - offsetTop += r.top; - } - - return new mxPoint(offsetLeft, offsetTop); - }, - - /** - * Function: getDocumentScrollOrigin - * - * Returns the scroll origin of the given document or the current document - * if no document is given. - */ - getDocumentScrollOrigin: function(doc) - { - if (mxClient.IS_QUIRKS) - { - return new mxPoint(doc.body.scrollLeft, doc.body.scrollTop); - } - else - { - var wnd = doc.defaultView || doc.parentWindow; - - var x = (wnd != null && window.pageXOffset !== undefined) ? window.pageXOffset : (document.documentElement || document.body.parentNode || document.body).scrollLeft; - var y = (wnd != null && window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop; - - return new mxPoint(x, y); - } - }, - - /** - * Function: getScrollOrigin - * - * Returns the top, left corner of the viewrect as an . - * - * Parameters: - * - * node - DOM node whose scroll origin should be returned. - * includeAncestors - Whether the scroll origin of the ancestors should be - * included. Default is false. - * includeDocument - Whether the scroll origin of the document should be - * included. Default is true. - */ - getScrollOrigin: function(node, includeAncestors, includeDocument) - { - includeAncestors = (includeAncestors != null) ? includeAncestors : false; - includeDocument = (includeDocument != null) ? includeDocument : true; - - var doc = (node != null) ? node.ownerDocument : document; - var b = doc.body; - var d = doc.documentElement; - var result = new mxPoint(); - var fixed = false; - - while (node != null && node != b && node != d) - { - if (!isNaN(node.scrollLeft) && !isNaN(node.scrollTop)) - { - result.x += node.scrollLeft; - result.y += node.scrollTop; - } - - var style = mxUtils.getCurrentStyle(node); - - if (style != null) - { - fixed = fixed || style.position == 'fixed'; - } - - node = (includeAncestors) ? node.parentNode : null; - } - - if (!fixed && includeDocument) - { - var origin = mxUtils.getDocumentScrollOrigin(doc); - - result.x += origin.x; - result.y += origin.y; - } - - return result; - }, - - /** - * Function: convertPoint - * - * Converts the specified point (x, y) using the offset of the specified - * container and returns a new with the result. - * - * (code) - * var pt = mxUtils.convertPoint(graph.container, - * mxEvent.getClientX(evt), mxEvent.getClientY(evt)); - * (end) - * - * Parameters: - * - * container - DOM node to use for the offset. - * x - X-coordinate of the point to be converted. - * y - Y-coordinate of the point to be converted. - */ - convertPoint: function(container, x, y) - { - var origin = mxUtils.getScrollOrigin(container, false); - var offset = mxUtils.getOffset(container); - - offset.x -= origin.x; - offset.y -= origin.y; - - return new mxPoint(x - offset.x, y - offset.y); - }, - - /** - * Function: ltrim - * - * Strips all whitespaces from the beginning of the string. Without the - * second parameter, this will trim these characters: - * - * - " " (ASCII 32 (0x20)), an ordinary space - * - "\t" (ASCII 9 (0x09)), a tab - * - "\n" (ASCII 10 (0x0A)), a new line (line feed) - * - "\r" (ASCII 13 (0x0D)), a carriage return - * - "\0" (ASCII 0 (0x00)), the NUL-byte - * - "\x0B" (ASCII 11 (0x0B)), a vertical tab - */ - ltrim: function(str, chars) - { - chars = chars || "\\s"; - - return (str != null) ? str.replace(new RegExp("^[" + chars + "]+", "g"), "") : null; - }, - - /** - * Function: rtrim - * - * Strips all whitespaces from the end of the string. Without the second - * parameter, this will trim these characters: - * - * - " " (ASCII 32 (0x20)), an ordinary space - * - "\t" (ASCII 9 (0x09)), a tab - * - "\n" (ASCII 10 (0x0A)), a new line (line feed) - * - "\r" (ASCII 13 (0x0D)), a carriage return - * - "\0" (ASCII 0 (0x00)), the NUL-byte - * - "\x0B" (ASCII 11 (0x0B)), a vertical tab - */ - rtrim: function(str, chars) - { - chars = chars || "\\s"; - - return (str != null) ? str.replace(new RegExp("[" + chars + "]+$", "g"), "") : null; - }, - - /** - * Function: trim - * - * Strips all whitespaces from both end of the string. - * Without the second parameter, Javascript function will trim these - * characters: - * - * - " " (ASCII 32 (0x20)), an ordinary space - * - "\t" (ASCII 9 (0x09)), a tab - * - "\n" (ASCII 10 (0x0A)), a new line (line feed) - * - "\r" (ASCII 13 (0x0D)), a carriage return - * - "\0" (ASCII 0 (0x00)), the NUL-byte - * - "\x0B" (ASCII 11 (0x0B)), a vertical tab - */ - trim: function(str, chars) - { - return mxUtils.ltrim(mxUtils.rtrim(str, chars), chars); - }, - - /** - * Function: isNumeric - * - * Returns true if the specified value is numeric, that is, if it is not - * null, not an empty string, not a HEX number and isNaN returns false. - * - * Parameters: - * - * n - String representing the possibly numeric value. - */ - isNumeric: function(n) - { - return !isNaN(parseFloat(n)) && isFinite(n) && (typeof(n) != 'string' || n.toLowerCase().indexOf('0x') < 0); - }, - - /** - * Function: isInteger - * - * Returns true if the given value is an valid integer number. - * - * Parameters: - * - * n - String representing the possibly numeric value. - */ - isInteger: function(n) - { - return String(parseInt(n)) === String(n); - }, - - /** - * Function: mod - * - * Returns the remainder of division of n by m. You should use this instead - * of the built-in operation as the built-in operation does not properly - * handle negative numbers. - */ - mod: function(n, m) - { - return ((n % m) + m) % m; - }, - - /** - * Function: intersection - * - * Returns the intersection of two lines as an . - * - * Parameters: - * - * x0 - X-coordinate of the first line's startpoint. - * y0 - X-coordinate of the first line's startpoint. - * x1 - X-coordinate of the first line's endpoint. - * y1 - Y-coordinate of the first line's endpoint. - * x2 - X-coordinate of the second line's startpoint. - * y2 - Y-coordinate of the second line's startpoint. - * x3 - X-coordinate of the second line's endpoint. - * y3 - Y-coordinate of the second line's endpoint. - */ - intersection: function (x0, y0, x1, y1, x2, y2, x3, y3) - { - var denom = ((y3 - y2) * (x1 - x0)) - ((x3 - x2) * (y1 - y0)); - var nume_a = ((x3 - x2) * (y0 - y2)) - ((y3 - y2) * (x0 - x2)); - var nume_b = ((x1 - x0) * (y0 - y2)) - ((y1 - y0) * (x0 - x2)); - - var ua = nume_a / denom; - var ub = nume_b / denom; - - if(ua >= 0.0 && ua <= 1.0 && ub >= 0.0 && ub <= 1.0) - { - // Get the intersection point - var x = x0 + ua * (x1 - x0); - var y = y0 + ua * (y1 - y0); - - return new mxPoint(x, y); - } - - // No intersection - return null; - }, - - /** - * Function: ptSegDistSq - * - * Returns the square distance between a segment and a point. To get the - * distance between a point and a line (with infinite length) use - * . - * - * Parameters: - * - * x1 - X-coordinate of the startpoint of the segment. - * y1 - Y-coordinate of the startpoint of the segment. - * x2 - X-coordinate of the endpoint of the segment. - * y2 - Y-coordinate of the endpoint of the segment. - * px - X-coordinate of the point. - * py - Y-coordinate of the point. - */ - ptSegDistSq: function(x1, y1, x2, y2, px, py) - { - x2 -= x1; - y2 -= y1; - - px -= x1; - py -= y1; - - var dotprod = px * x2 + py * y2; - var projlenSq; - - if (dotprod <= 0.0) - { - projlenSq = 0.0; - } - else - { - px = x2 - px; - py = y2 - py; - dotprod = px * x2 + py * y2; - - if (dotprod <= 0.0) - { - projlenSq = 0.0; - } - else - { - projlenSq = dotprod * dotprod / (x2 * x2 + y2 * y2); - } - } - - var lenSq = px * px + py * py - projlenSq; - - if (lenSq < 0) - { - lenSq = 0; - } - - return lenSq; - }, - - /** - * Function: ptLineDist - * - * Returns the distance between a line defined by two points and a point. - * To get the distance between a point and a segment (with a specific - * length) use . - * - * Parameters: - * - * x1 - X-coordinate of point 1 of the line. - * y1 - Y-coordinate of point 1 of the line. - * x2 - X-coordinate of point 1 of the line. - * y2 - Y-coordinate of point 1 of the line. - * px - X-coordinate of the point. - * py - Y-coordinate of the point. - */ - ptLineDist: function(x1, y1, x2, y2, px, py) - { - return Math.abs((y2 - y1) * px - (x2 - x1) * py + x2 * y1 - y2 * x1) / - Math.sqrt((y2 - y1) * (y2 - y1) + (x2 - x1) * (x2 - x1)); - }, - - /** - * Function: relativeCcw - * - * Returns 1 if the given point on the right side of the segment, 0 if its - * on the segment, and -1 if the point is on the left side of the segment. - * - * Parameters: - * - * x1 - X-coordinate of the startpoint of the segment. - * y1 - Y-coordinate of the startpoint of the segment. - * x2 - X-coordinate of the endpoint of the segment. - * y2 - Y-coordinate of the endpoint of the segment. - * px - X-coordinate of the point. - * py - Y-coordinate of the point. - */ - relativeCcw: function(x1, y1, x2, y2, px, py) - { - x2 -= x1; - y2 -= y1; - px -= x1; - py -= y1; - var ccw = px * y2 - py * x2; - - if (ccw == 0.0) - { - ccw = px * x2 + py * y2; - - if (ccw > 0.0) - { - px -= x2; - py -= y2; - ccw = px * x2 + py * y2; - - if (ccw < 0.0) - { - ccw = 0.0; - } - } - } - - return (ccw < 0.0) ? -1 : ((ccw > 0.0) ? 1 : 0); - }, - - /** - * Function: animateChanges - * - * See . This is for backwards compatibility and - * will be removed later. - */ - animateChanges: function(graph, changes) - { - // LATER: Deprecated, remove this function - mxEffects.animateChanges.apply(this, arguments); - }, - - /** - * Function: cascadeOpacity - * - * See . This is for backwards compatibility and - * will be removed later. - */ - cascadeOpacity: function(graph, cell, opacity) - { - mxEffects.cascadeOpacity.apply(this, arguments); - }, - - /** - * Function: fadeOut - * - * See . This is for backwards compatibility and - * will be removed later. - */ - fadeOut: function(node, from, remove, step, delay, isEnabled) - { - mxEffects.fadeOut.apply(this, arguments); - }, - - /** - * Function: setOpacity - * - * Sets the opacity of the specified DOM node to the given value in %. - * - * Parameters: - * - * node - DOM node to set the opacity for. - * value - Opacity in %. Possible values are between 0 and 100. - */ - setOpacity: function(node, value) - { - if (mxUtils.isVml(node)) - { - if (value >= 100) - { - node.style.filter = ''; - } - else - { - // TODO: Why is the division by 5 needed in VML? - node.style.filter = 'alpha(opacity=' + (value/5) + ')'; - } - } - else if (mxClient.IS_IE && (typeof(document.documentMode) === 'undefined' || document.documentMode < 9)) - { - if (value >= 100) - { - node.style.filter = ''; - } - else - { - node.style.filter = 'alpha(opacity=' + value + ')'; - } - } - else - { - node.style.opacity = (value / 100); - } - }, - - /** - * Function: createImage - * - * Creates and returns an image (IMG node) or VML image (v:image) in IE6 in - * quirks mode. - * - * Parameters: - * - * src - URL that points to the image to be displayed. - */ - createImage: function(src) - { - var imageNode = null; - - if (mxClient.IS_IE6 && document.compatMode != 'CSS1Compat') - { - imageNode = document.createElement(mxClient.VML_PREFIX + ':image'); - imageNode.setAttribute('src', src); - imageNode.style.borderStyle = 'none'; - } - else - { - imageNode = document.createElement('img'); - imageNode.setAttribute('src', src); - imageNode.setAttribute('border', '0'); - } - - return imageNode; - }, - - /** - * Function: sortCells - * - * Sorts the given cells according to the order in the cell hierarchy. - * Ascending is optional and defaults to true. - */ - sortCells: function(cells, ascending) - { - ascending = (ascending != null) ? ascending : true; - var lookup = new mxDictionary(); - cells.sort(function(o1, o2) - { - var p1 = lookup.get(o1); - - if (p1 == null) - { - p1 = mxCellPath.create(o1).split(mxCellPath.PATH_SEPARATOR); - lookup.put(o1, p1); - } - - var p2 = lookup.get(o2); - - if (p2 == null) - { - p2 = mxCellPath.create(o2).split(mxCellPath.PATH_SEPARATOR); - lookup.put(o2, p2); - } - - var comp = mxCellPath.compare(p1, p2); - - return (comp == 0) ? 0 : (((comp > 0) == ascending) ? 1 : -1); - }); - - return cells; - }, - - /** - * Function: getStylename - * - * Returns the stylename in a style of the form [(stylename|key=value);] or - * an empty string if the given style does not contain a stylename. - * - * Parameters: - * - * style - String of the form [(stylename|key=value);]. - */ - getStylename: function(style) - { - if (style != null) - { - var pairs = style.split(';'); - var stylename = pairs[0]; - - if (stylename.indexOf('=') < 0) - { - return stylename; - } - } - - return ''; - }, - - /** - * Function: getStylenames - * - * Returns the stylenames in a style of the form [(stylename|key=value);] - * or an empty array if the given style does not contain any stylenames. - * - * Parameters: - * - * style - String of the form [(stylename|key=value);]. - */ - getStylenames: function(style) - { - var result = []; - - if (style != null) - { - var pairs = style.split(';'); - - for (var i = 0; i < pairs.length; i++) - { - if (pairs[i].indexOf('=') < 0) - { - result.push(pairs[i]); - } - } - } - - return result; - }, - - /** - * Function: indexOfStylename - * - * Returns the index of the given stylename in the given style. This - * returns -1 if the given stylename does not occur (as a stylename) in the - * given style, otherwise it returns the index of the first character. - */ - indexOfStylename: function(style, stylename) - { - if (style != null && stylename != null) - { - var tokens = style.split(';'); - var pos = 0; - - for (var i = 0; i < tokens.length; i++) - { - if (tokens[i] == stylename) - { - return pos; - } - - pos += tokens[i].length + 1; - } - } - - return -1; - }, - - /** - * Function: addStylename - * - * Adds the specified stylename to the given style if it does not already - * contain the stylename. - */ - addStylename: function(style, stylename) - { - if (mxUtils.indexOfStylename(style, stylename) < 0) - { - if (style == null) - { - style = ''; - } - else if (style.length > 0 && style.charAt(style.length - 1) != ';') - { - style += ';'; - } - - style += stylename; - } - - return style; - }, - - /** - * Function: removeStylename - * - * Removes all occurrences of the specified stylename in the given style - * and returns the updated style. Trailing semicolons are not preserved. - */ - removeStylename: function(style, stylename) - { - var result = []; - - if (style != null) - { - var tokens = style.split(';'); - - for (var i = 0; i < tokens.length; i++) - { - if (tokens[i] != stylename) - { - result.push(tokens[i]); - } - } - } - - return result.join(';'); - }, - - /** - * Function: removeAllStylenames - * - * Removes all stylenames from the given style and returns the updated - * style. - */ - removeAllStylenames: function(style) - { - var result = []; - - if (style != null) - { - var tokens = style.split(';'); - - for (var i = 0; i < tokens.length; i++) - { - // Keeps the key, value assignments - if (tokens[i].indexOf('=') >= 0) - { - result.push(tokens[i]); - } - } - } - - return result.join(';'); - }, - - /** - * Function: setCellStyles - * - * Assigns the value for the given key in the styles of the given cells, or - * removes the key from the styles if the value is null. - * - * Parameters: - * - * model - to execute the transaction in. - * cells - Array of to be updated. - * key - Key of the style to be changed. - * value - New value for the given key. - */ - setCellStyles: function(model, cells, key, value) - { - if (cells != null && cells.length > 0) - { - model.beginUpdate(); - try - { - for (var i = 0; i < cells.length; i++) - { - if (cells[i] != null) - { - var style = mxUtils.setStyle(model.getStyle(cells[i]), key, value); - model.setStyle(cells[i], style); - } - } - } - finally - { - model.endUpdate(); - } - } - }, - - /** - * Function: setStyle - * - * Adds or removes the given key, value pair to the style and returns the - * new style. If value is null or zero length then the key is removed from - * the style. This is for cell styles, not for CSS styles. - * - * Parameters: - * - * style - String of the form [(stylename|key=value);]. - * key - Key of the style to be changed. - * value - New value for the given key. - */ - setStyle: function(style, key, value) - { - var isValue = value != null && (typeof(value.length) == 'undefined' || value.length > 0); - - if (style == null || style.length == 0) - { - if (isValue) - { - style = key + '=' + value + ';'; - } - } - else - { - if (style.substring(0, key.length + 1) == key + '=') - { - var next = style.indexOf(';'); - - if (isValue) - { - style = key + '=' + value + ((next < 0) ? ';' : style.substring(next)); - } - else - { - style = (next < 0 || next == style.length - 1) ? '' : style.substring(next + 1); - } - } - else - { - var index = style.indexOf(';' + key + '='); - - if (index < 0) - { - if (isValue) - { - var sep = (style.charAt(style.length - 1) == ';') ? '' : ';'; - style = style + sep + key + '=' + value + ';'; - } - } - else - { - var next = style.indexOf(';', index + 1); - - if (isValue) - { - style = style.substring(0, index + 1) + key + '=' + value + ((next < 0) ? ';' : style.substring(next)); - } - else - { - style = style.substring(0, index) + ((next < 0) ? ';' : style.substring(next)); - } - } - } - } - - return style; - }, - - /** - * Function: setCellStyleFlags - * - * Sets or toggles the flag bit for the given key in the cell's styles. - * If value is null then the flag is toggled. - * - * Example: - * - * (code) - * var cells = graph.getSelectionCells(); - * mxUtils.setCellStyleFlags(graph.model, - * cells, - * mxConstants.STYLE_FONTSTYLE, - * mxConstants.FONT_BOLD); - * (end) - * - * Toggles the bold font style. - * - * Parameters: - * - * model - that contains the cells. - * cells - Array of to change the style for. - * key - Key of the style to be changed. - * flag - Integer for the bit to be changed. - * value - Optional boolean value for the flag. - */ - setCellStyleFlags: function(model, cells, key, flag, value) - { - if (cells != null && cells.length > 0) - { - model.beginUpdate(); - try - { - for (var i = 0; i < cells.length; i++) - { - if (cells[i] != null) - { - var style = mxUtils.setStyleFlag( - model.getStyle(cells[i]), - key, flag, value); - model.setStyle(cells[i], style); - } - } - } - finally - { - model.endUpdate(); - } - } - }, - - /** - * Function: setStyleFlag - * - * Sets or removes the given key from the specified style and returns the - * new style. If value is null then the flag is toggled. - * - * Parameters: - * - * style - String of the form [(stylename|key=value);]. - * key - Key of the style to be changed. - * flag - Integer for the bit to be changed. - * value - Optional boolean value for the given flag. - */ - setStyleFlag: function(style, key, flag, value) - { - if (style == null || style.length == 0) - { - if (value || value == null) - { - style = key+'='+flag; - } - else - { - style = key+'=0'; - } - } - else - { - var index = style.indexOf(key+'='); - - if (index < 0) - { - var sep = (style.charAt(style.length-1) == ';') ? '' : ';'; - - if (value || value == null) - { - style = style + sep + key + '=' + flag; - } - else - { - style = style + sep + key + '=0'; - } - } - else - { - var cont = style.indexOf(';', index); - var tmp = ''; - - if (cont < 0) - { - tmp = style.substring(index+key.length+1); - } - else - { - tmp = style.substring(index+key.length+1, cont); - } - - if (value == null) - { - tmp = parseInt(tmp) ^ flag; - } - else if (value) - { - tmp = parseInt(tmp) | flag; - } - else - { - tmp = parseInt(tmp) & ~flag; - } - - style = style.substring(0, index) + key + '=' + tmp + - ((cont >= 0) ? style.substring(cont) : ''); - } - } - - return style; - }, - - /** - * Function: getAlignmentAsPoint - * - * Returns an that represents the horizontal and vertical alignment - * for numeric computations. X is -0.5 for center, -1 for right and 0 for - * left alignment. Y is -0.5 for middle, -1 for bottom and 0 for top - * alignment. Default values for missing arguments is top, left. - */ - getAlignmentAsPoint: function(align, valign) - { - var dx = 0; - var dy = 0; - - // Horizontal alignment - if (align == mxConstants.ALIGN_CENTER) - { - dx = -0.5; - } - else if (align == mxConstants.ALIGN_RIGHT) - { - dx = -1; - } - - // Vertical alignment - if (valign == mxConstants.ALIGN_MIDDLE) - { - dy = -0.5; - } - else if (valign == mxConstants.ALIGN_BOTTOM) - { - dy = -1; - } - - return new mxPoint(dx, dy); - }, - - /** - * Function: getSizeForString - * - * Returns an with the size (width and height in pixels) of - * the given string. The string may contain HTML markup. Newlines should be - * converted to
before calling this method. The caller is responsible - * for sanitizing the HTML markup. - * - * Example: - * - * (code) - * var label = graph.getLabel(cell).replace(/\n/g, "
"); - * var size = graph.getSizeForString(label); - * (end) - * - * Parameters: - * - * text - String whose size should be returned. - * fontSize - Integer that specifies the font size in pixels. Default is - * . - * fontFamily - String that specifies the name of the font family. Default - * is . - * textWidth - Optional width for text wrapping. - */ - getSizeForString: function(text, fontSize, fontFamily, textWidth) - { - fontSize = (fontSize != null) ? fontSize : mxConstants.DEFAULT_FONTSIZE; - fontFamily = (fontFamily != null) ? fontFamily : mxConstants.DEFAULT_FONTFAMILY; - var div = document.createElement('div'); - - // Sets the font size and family - div.style.fontFamily = fontFamily; - div.style.fontSize = Math.round(fontSize) + 'px'; - div.style.lineHeight = Math.round(fontSize * mxConstants.LINE_HEIGHT) + 'px'; - - // Disables block layout and outside wrapping and hides the div - div.style.position = 'absolute'; - div.style.visibility = 'hidden'; - div.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; - div.style.zoom = '1'; - - if (textWidth != null) - { - div.style.width = textWidth + 'px'; - div.style.whiteSpace = 'normal'; - } - else - { - div.style.whiteSpace = 'nowrap'; - } - - // Adds the text and inserts into DOM for updating of size - div.innerHTML = text; - document.body.appendChild(div); - - // Gets the size and removes from DOM - var size = new mxRectangle(0, 0, div.offsetWidth, div.offsetHeight); - document.body.removeChild(div); - - return size; - }, - - /** - * Function: getViewXml - */ - getViewXml: function(graph, scale, cells, x0, y0) - { - x0 = (x0 != null) ? x0 : 0; - y0 = (y0 != null) ? y0 : 0; - scale = (scale != null) ? scale : 1; - - if (cells == null) - { - var model = graph.getModel(); - cells = [model.getRoot()]; - } - - var view = graph.getView(); - var result = null; - - // Disables events on the view - var eventsEnabled = view.isEventsEnabled(); - view.setEventsEnabled(false); - - // Workaround for label bounds not taken into account for image export. - // Creates a temporary draw pane which is used for rendering the text. - // Text rendering is required for finding the bounds of the labels. - var drawPane = view.drawPane; - var overlayPane = view.overlayPane; - - if (graph.dialect == mxConstants.DIALECT_SVG) - { - view.drawPane = document.createElementNS(mxConstants.NS_SVG, 'g'); - view.canvas.appendChild(view.drawPane); - - // Redirects cell overlays into temporary container - view.overlayPane = document.createElementNS(mxConstants.NS_SVG, 'g'); - view.canvas.appendChild(view.overlayPane); - } - else - { - view.drawPane = view.drawPane.cloneNode(false); - view.canvas.appendChild(view.drawPane); - - // Redirects cell overlays into temporary container - view.overlayPane = view.overlayPane.cloneNode(false); - view.canvas.appendChild(view.overlayPane); - } - - // Resets the translation - var translate = view.getTranslate(); - view.translate = new mxPoint(x0, y0); - - // Creates the temporary cell states in the view - var temp = new mxTemporaryCellStates(graph.getView(), scale, cells); - - try - { - var enc = new mxCodec(); - result = enc.encode(graph.getView()); - } - finally - { - temp.destroy(); - view.translate = translate; - view.canvas.removeChild(view.drawPane); - view.canvas.removeChild(view.overlayPane); - view.drawPane = drawPane; - view.overlayPane = overlayPane; - view.setEventsEnabled(eventsEnabled); - } - - return result; - }, - - /** - * Function: getScaleForPageCount - * - * Returns the scale to be used for printing the graph with the given - * bounds across the specifies number of pages with the given format. The - * scale is always computed such that it given the given amount or fewer - * pages in the print output. See for an example. - * - * Parameters: - * - * pageCount - Specifies the number of pages in the print output. - * graph - that should be printed. - * pageFormat - Optional that specifies the page format. - * Default is . - * border - The border along each side of every page. - */ - getScaleForPageCount: function(pageCount, graph, pageFormat, border) - { - if (pageCount < 1) - { - // We can't work with less than 1 page, return no scale - // change - return 1; - } - - pageFormat = (pageFormat != null) ? pageFormat : mxConstants.PAGE_FORMAT_A4_PORTRAIT; - border = (border != null) ? border : 0; - - var availablePageWidth = pageFormat.width - (border * 2); - var availablePageHeight = pageFormat.height - (border * 2); - - // Work out the number of pages required if the - // graph is not scaled. - var graphBounds = graph.getGraphBounds().clone(); - var sc = graph.getView().getScale(); - graphBounds.width /= sc; - graphBounds.height /= sc; - var graphWidth = graphBounds.width; - var graphHeight = graphBounds.height; - - var scale = 1; - - // The ratio of the width/height for each printer page - var pageFormatAspectRatio = availablePageWidth / availablePageHeight; - // The ratio of the width/height for the graph to be printer - var graphAspectRatio = graphWidth / graphHeight; - - // The ratio of horizontal pages / vertical pages for this - // graph to maintain its aspect ratio on this page format - var pagesAspectRatio = graphAspectRatio / pageFormatAspectRatio; - - // Factor the square root of the page count up and down - // by the pages aspect ratio to obtain a horizontal and - // vertical page count that adds up to the page count - // and has the correct aspect ratio - var pageRoot = Math.sqrt(pageCount); - var pagesAspectRatioSqrt = Math.sqrt(pagesAspectRatio); - var numRowPages = pageRoot * pagesAspectRatioSqrt; - var numColumnPages = pageRoot / pagesAspectRatioSqrt; - - // These value are rarely more than 2 rounding downs away from - // a total that meets the page count. In cases of one being less - // than 1 page, the other value can be too high and take more iterations - // In this case, just change that value to be the page count, since - // we know the other value is 1 - if (numRowPages < 1 && numColumnPages > pageCount) - { - var scaleChange = numColumnPages / pageCount; - numColumnPages = pageCount; - numRowPages /= scaleChange; - } - - if (numColumnPages < 1 && numRowPages > pageCount) - { - var scaleChange = numRowPages / pageCount; - numRowPages = pageCount; - numColumnPages /= scaleChange; - } - - var currentTotalPages = Math.ceil(numRowPages) * Math.ceil(numColumnPages); - - var numLoops = 0; - - // Iterate through while the rounded up number of pages comes to - // a total greater than the required number - while (currentTotalPages > pageCount) - { - // Round down the page count (rows or columns) that is - // closest to its next integer down in percentage terms. - // i.e. Reduce the page total by reducing the total - // page area by the least possible amount - - var roundRowDownProportion = Math.floor(numRowPages) / numRowPages; - var roundColumnDownProportion = Math.floor(numColumnPages) / numColumnPages; - - // If the round down proportion is, work out the proportion to - // round down to 1 page less - if (roundRowDownProportion == 1) - { - roundRowDownProportion = Math.floor(numRowPages-1) / numRowPages; - } - if (roundColumnDownProportion == 1) - { - roundColumnDownProportion = Math.floor(numColumnPages-1) / numColumnPages; - } - - // Check which rounding down is smaller, but in the case of very small roundings - // try the other dimension instead - var scaleChange = 1; - - // Use the higher of the two values - if (roundRowDownProportion > roundColumnDownProportion) - { - scaleChange = roundRowDownProportion; - } - else - { - scaleChange = roundColumnDownProportion; - } - - numRowPages = numRowPages * scaleChange; - numColumnPages = numColumnPages * scaleChange; - currentTotalPages = Math.ceil(numRowPages) * Math.ceil(numColumnPages); - - numLoops++; - - if (numLoops > 10) - { - break; - } - } - - // Work out the scale from the number of row pages required - // The column pages will give the same value - var posterWidth = availablePageWidth * numRowPages; - scale = posterWidth / graphWidth; - - // Allow for rounding errors - return scale * 0.99999; - }, - - /** - * Function: show - * - * Copies the styles and the markup from the graph's container into the - * given document and removes all cursor styles. The document is returned. - * - * This function should be called from within the document with the graph. - * If you experience problems with missing stylesheets in IE then try adding - * the domain to the trusted sites. - * - * Parameters: - * - * graph - to be copied. - * doc - Document where the new graph is created. - * x0 - X-coordinate of the graph view origin. Default is 0. - * y0 - Y-coordinate of the graph view origin. Default is 0. - * w - Optional width of the graph view. - * h - Optional height of the graph view. - */ - show: function(graph, doc, x0, y0, w, h) - { - x0 = (x0 != null) ? x0 : 0; - y0 = (y0 != null) ? y0 : 0; - - if (doc == null) - { - var wnd = window.open(); - doc = wnd.document; - } - else - { - doc.open(); - } - - // Workaround for missing print output in IE9 standards - if (document.documentMode == 9) - { - doc.writeln(''); - } - - var bounds = graph.getGraphBounds(); - var dx = Math.ceil(x0 - bounds.x); - var dy = Math.ceil(y0 - bounds.y); - - if (w == null) - { - w = Math.ceil(bounds.width + x0) + Math.ceil(Math.ceil(bounds.x) - bounds.x); - } - - if (h == null) - { - h = Math.ceil(bounds.height + y0) + Math.ceil(Math.ceil(bounds.y) - bounds.y); - } - - // Needs a special way of creating the page so that no click is required - // to refresh the contents after the external CSS styles have been loaded. - // To avoid a click or programmatic refresh, the styleSheets[].cssText - // property is copied over from the original document. - if (mxClient.IS_IE || document.documentMode == 11) - { - var html = ''; - - var base = document.getElementsByTagName('base'); - - for (var i = 0; i < base.length; i++) - { - html += base[i].outerHTML; - } - - html += ''; - - // Copies the contents of the graph container - html += '
'; - html += graph.container.innerHTML; - html += '
'; - - doc.writeln(html); - doc.close(); - } - else - { - doc.writeln(''); - - var base = document.getElementsByTagName('base'); - - for (var i = 0; i < base.length; i++) - { - doc.writeln(mxUtils.getOuterHtml(base[i])); - } - - var links = document.getElementsByTagName('link'); - - for (var i = 0; i < links.length; i++) - { - doc.writeln(mxUtils.getOuterHtml(links[i])); - } - - var styles = document.getElementsByTagName('style'); - - for (var i = 0; i < styles.length; i++) - { - doc.writeln(mxUtils.getOuterHtml(styles[i])); - } - - doc.writeln(''); - doc.close(); - - var outer = doc.createElement('div'); - outer.position = 'absolute'; - outer.overflow = 'hidden'; - outer.style.width = w + 'px'; - outer.style.height = h + 'px'; - - // Required for HTML labels if foreignObjects are disabled - var div = doc.createElement('div'); - div.style.position = 'absolute'; - div.style.left = dx + 'px'; - div.style.top = dy + 'px'; - - var node = graph.container.firstChild; - var svg = null; - - while (node != null) - { - var clone = node.cloneNode(true); - - if (node == graph.view.drawPane.ownerSVGElement) - { - outer.appendChild(clone); - svg = clone; - } - else - { - div.appendChild(clone); - } - - node = node.nextSibling; - } - - doc.body.appendChild(outer); - - if (div.firstChild != null) - { - doc.body.appendChild(div); - } - - if (svg != null) - { - svg.style.minWidth = ''; - svg.style.minHeight = ''; - svg.firstChild.setAttribute('transform', 'translate(' + dx + ',' + dy + ')'); - } - } - - mxUtils.removeCursors(doc.body); - - return doc; - }, - - /** - * Function: printScreen - * - * Prints the specified graph using a new window and the built-in print - * dialog. - * - * This function should be called from within the document with the graph. - * - * Parameters: - * - * graph - to be printed. - */ - printScreen: function(graph) - { - var wnd = window.open(); - var bounds = graph.getGraphBounds(); - mxUtils.show(graph, wnd.document); - - var print = function() - { - wnd.focus(); - wnd.print(); - wnd.close(); - }; - - // Workaround for Google Chrome which needs a bit of a - // delay in order to render the SVG contents - if (mxClient.IS_GC) - { - wnd.setTimeout(print, 500); - } - else - { - print(); - } - }, - - /** - * Function: popup - * - * Shows the specified text content in a new or a new browser - * window if isInternalWindow is false. - * - * Parameters: - * - * content - String that specifies the text to be displayed. - * isInternalWindow - Optional boolean indicating if an mxWindow should be - * used instead of a new browser window. Default is false. - */ - popup: function(content, isInternalWindow) - { - if (isInternalWindow) - { - var div = document.createElement('div'); - - div.style.overflow = 'scroll'; - div.style.width = '636px'; - div.style.height = '460px'; - - var pre = document.createElement('pre'); - pre.innerHTML = mxUtils.htmlEntities(content, false). - replace(/\n/g,'
').replace(/ /g, ' '); - - div.appendChild(pre); - - var w = document.body.clientWidth; - var h = Math.max(document.body.clientHeight || 0, document.documentElement.clientHeight) - var wnd = new mxWindow('Popup Window', div, - w/2-320, h/2-240, 640, 480, false, true); - - wnd.setClosable(true); - wnd.setVisible(true); - } - else - { - // Wraps up the XML content in a textarea - if (mxClient.IS_NS) - { - var wnd = window.open(); - wnd.document.writeln('
'+mxUtils.htmlEntities(content)+'').replace(/ /g, ' ');
-			   	wnd.document.body.appendChild(pre);
-			}
-	   	}
-	},
-
-	/**
-	 * Function: alert
-	 *
-	 * Displayss the given alert in a new dialog. This implementation uses the
-	 * built-in alert function. This is used to display validation errors when
-	 * connections cannot be changed or created.
-	 *
-	 * Parameters:
-	 *
-	 * message - String specifying the message to be displayed.
-	 */
-	alert: function(message)
-	{
-		alert(message);
-	},
-
-	/**
-	 * Function: prompt
-	 *
-	 * Displays the given message in a prompt dialog. This implementation uses
-	 * the built-in prompt function.
-	 *
-	 * Parameters:
-	 *
-	 * message - String specifying the message to be displayed.
-	 * defaultValue - Optional string specifying the default value.
-	 */
-	prompt: function(message, defaultValue)
-	{
-		return prompt(message, (defaultValue != null) ? defaultValue : '');
-	},
-
-	/**
-	 * Function: confirm
-	 *
-	 * Displays the given message in a confirm dialog. This implementation uses
-	 * the built-in confirm function.
-	 *
-	 * Parameters:
-	 *
-	 * message - String specifying the message to be displayed.
-	 */
-	confirm: function(message)
-	{
-		return confirm(message);
-	},
-
-	/**
-	 * Function: error
-	 *
-	 * Displays the given error message in a new  of the given width.
-	 * If close is true then an additional close button is added to the window.
-	 * The optional icon specifies the icon to be used for the window. Default
-	 * is .
-	 *
-	 * Parameters:
-	 *
-	 * message - String specifying the message to be displayed.
-	 * width - Integer specifying the width of the window.
-	 * close - Optional boolean indicating whether to add a close button.
-	 * icon - Optional icon for the window decoration.
-	 */
-	error: function(message, width, close, icon)
-	{
-		var div = document.createElement('div');
-		div.style.padding = '20px';
-
-		var img = document.createElement('img');
-		img.setAttribute('src', icon || mxUtils.errorImage);
-		img.setAttribute('valign', 'bottom');
-		img.style.verticalAlign = 'middle';
-		div.appendChild(img);
-
-		div.appendChild(document.createTextNode('\u00a0')); //  
-		div.appendChild(document.createTextNode('\u00a0')); //  
-		div.appendChild(document.createTextNode('\u00a0')); //  
-		mxUtils.write(div, message);
-
-		var w = document.body.clientWidth;
-		var h = (document.body.clientHeight || document.documentElement.clientHeight);
-		var warn = new mxWindow(mxResources.get(mxUtils.errorResource) ||
-			mxUtils.errorResource, div, (w-width)/2, h/4, width, null,
-			false, true);
-
-		if (close)
-		{
-			mxUtils.br(div);
-
-			var tmp = document.createElement('p');
-			var button = document.createElement('button');
-
-			if (mxClient.IS_IE)
-			{
-				button.style.cssText = 'float:right';
-			}
-			else
-			{
-				button.setAttribute('style', 'float:right');
-			}
-
-			mxEvent.addListener(button, 'click', function(evt)
-			{
-				warn.destroy();
-			});
-
-			mxUtils.write(button, mxResources.get(mxUtils.closeResource) ||
-				mxUtils.closeResource);
-
-			tmp.appendChild(button);
-			div.appendChild(tmp);
-
-			mxUtils.br(div);
-
-			warn.setClosable(true);
-		}
-
-		warn.setVisible(true);
-
-		return warn;
-	},
-
-	/**
-	 * Function: makeDraggable
-	 *
-	 * Configures the given DOM element to act as a drag source for the
-	 * specified graph. Returns a a new . If
-	 *  is enabled then the x and y arguments must
-	 * be used in funct to match the preview location.
-	 *
-	 * Example:
-	 *
-	 * (code)
-	 * var funct = function(graph, evt, cell, x, y)
-	 * {
-	 *   if (graph.canImportCell(cell))
-	 *   {
-	 *     var parent = graph.getDefaultParent();
-	 *     var vertex = null;
-	 *
-	 *     graph.getModel().beginUpdate();
-	 *     try
-	 *     {
-	 * 	     vertex = graph.insertVertex(parent, null, 'Hello', x, y, 80, 30);
-	 *     }
-	 *     finally
-	 *     {
-	 *       graph.getModel().endUpdate();
-	 *     }
-	 *
-	 *     graph.setSelectionCell(vertex);
-	 *   }
-	 * }
-	 *
-	 * var img = document.createElement('img');
-	 * img.setAttribute('src', 'editors/images/rectangle.gif');
-	 * img.style.position = 'absolute';
-	 * img.style.left = '0px';
-	 * img.style.top = '0px';
-	 * img.style.width = '16px';
-	 * img.style.height = '16px';
-	 *
-	 * var dragImage = img.cloneNode(true);
-	 * dragImage.style.width = '32px';
-	 * dragImage.style.height = '32px';
-	 * mxUtils.makeDraggable(img, graph, funct, dragImage);
-	 * document.body.appendChild(img);
-	 * (end)
-	 *
-	 * Parameters:
-	 *
-	 * element - DOM element to make draggable.
-	 * graphF -  that acts as the drop target or a function that takes a
-	 * mouse event and returns the current .
-	 * funct - Function to execute on a successful drop.
-	 * dragElement - Optional DOM node to be used for the drag preview.
-	 * dx - Optional horizontal offset between the cursor and the drag
-	 * preview.
-	 * dy - Optional vertical offset between the cursor and the drag
-	 * preview.
-	 * autoscroll - Optional boolean that specifies if autoscroll should be
-	 * used. Default is mxGraph.autoscroll.
-	 * scalePreview - Optional boolean that specifies if the preview element
-	 * should be scaled according to the graph scale. If this is true, then
-	 * the offsets will also be scaled. Default is false.
-	 * highlightDropTargets - Optional boolean that specifies if dropTargets
-	 * should be highlighted. Default is true.
-	 * getDropTarget - Optional function to return the drop target for a given
-	 * location (x, y). Default is mxGraph.getCellAt.
-	 */
-	makeDraggable: function(element, graphF, funct, dragElement, dx, dy, autoscroll,
-			scalePreview, highlightDropTargets, getDropTarget)
-	{
-		var dragSource = new mxDragSource(element, funct);
-		dragSource.dragOffset = new mxPoint((dx != null) ? dx : 0,
-			(dy != null) ? dy : mxConstants.TOOLTIP_VERTICAL_OFFSET);
-		dragSource.autoscroll = autoscroll;
-
-		// Cannot enable this by default. This needs to be enabled in the caller
-		// if the funct argument uses the new x- and y-arguments.
-		dragSource.setGuidesEnabled(false);
-
-		if (highlightDropTargets != null)
-		{
-			dragSource.highlightDropTargets = highlightDropTargets;
-		}
-
-		// Overrides function to find drop target cell
-		if (getDropTarget != null)
-		{
-			dragSource.getDropTarget = getDropTarget;
-		}
-
-		// Overrides function to get current graph
-		dragSource.getGraphForEvent = function(evt)
-		{
-			return (typeof(graphF) == 'function') ? graphF(evt) : graphF;
-		};
-
-		// Translates switches into dragSource customizations
-		if (dragElement != null)
-		{
-			dragSource.createDragElement = function()
-			{
-				return dragElement.cloneNode(true);
-			};
-
-			if (scalePreview)
-			{
-				dragSource.createPreviewElement = function(graph)
-				{
-					var elt = dragElement.cloneNode(true);
-
-					var w = parseInt(elt.style.width);
-					var h = parseInt(elt.style.height);
-					elt.style.width = Math.round(w * graph.view.scale) + 'px';
-					elt.style.height = Math.round(h * graph.view.scale) + 'px';
-
-					return elt;
-				};
-			}
-		}
-
-		return dragSource;
-	}
-
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
- var mxConstants =
- {
-	/**
-	 * Class: mxConstants
-	 *
-	 * Defines various global constants.
-	 *
-	 * Variable: DEFAULT_HOTSPOT
-	 *
-	 * Defines the portion of the cell which is to be used as a connectable
-	 * region. Default is 0.3. Possible values are 0 < x <= 1.
-	 */
-	DEFAULT_HOTSPOT: 0.3,
-
-	/**
-	 * Variable: MIN_HOTSPOT_SIZE
-	 *
-	 * Defines the minimum size in pixels of the portion of the cell which is
-	 * to be used as a connectable region. Default is 8.
-	 */
-	MIN_HOTSPOT_SIZE: 8,
-
-	/**
-	 * Variable: MAX_HOTSPOT_SIZE
-	 *
-	 * Defines the maximum size in pixels of the portion of the cell which is
-	 * to be used as a connectable region. Use 0 for no maximum. Default is 0.
-	 */
-	MAX_HOTSPOT_SIZE: 0,
-
-	/**
-	 * Variable: RENDERING_HINT_EXACT
-	 *
-	 * Defines the exact rendering hint.
-	 */
-	RENDERING_HINT_EXACT: 'exact',
-
-	/**
-	 * Variable: RENDERING_HINT_FASTER
-	 *
-	 * Defines the faster rendering hint.
-	 */
-	RENDERING_HINT_FASTER: 'faster',
-
-	/**
-	 * Variable: RENDERING_HINT_FASTEST
-	 *
-	 * Defines the fastest rendering hint.
-	 */
-	RENDERING_HINT_FASTEST: 'fastest',
-
-	/**
-	 * Variable: DIALECT_SVG
-	 *
-	 * Defines the SVG display dialect name.
-	 */
-	DIALECT_SVG: 'svg',
-
-	/**
-	 * Variable: DIALECT_VML
-	 *
-	 * Defines the VML display dialect name.
-	 */
-	DIALECT_VML: 'vml',
-
-	/**
-	 * Variable: DIALECT_MIXEDHTML
-	 *
-	 * Defines the mixed HTML display dialect name.
-	 */
-	DIALECT_MIXEDHTML: 'mixedHtml',
-
-	/**
-	 * Variable: DIALECT_PREFERHTML
-	 *
-	 * Defines the preferred HTML display dialect name.
-	 */
-	DIALECT_PREFERHTML: 'preferHtml',
-
-	/**
-	 * Variable: DIALECT_STRICTHTML
-	 *
-	 * Defines the strict HTML display dialect.
-	 */
-	DIALECT_STRICTHTML: 'strictHtml',
-
-	/**
-	 * Variable: NS_SVG
-	 *
-	 * Defines the SVG namespace.
-	 */
-	NS_SVG: 'http://www.w3.org/2000/svg',
-
-	/**
-	 * Variable: NS_XHTML
-	 *
-	 * Defines the XHTML namespace.
-	 */
-	NS_XHTML: 'http://www.w3.org/1999/xhtml',
-
-	/**
-	 * Variable: NS_XLINK
-	 *
-	 * Defines the XLink namespace.
-	 */
-	NS_XLINK: 'http://www.w3.org/1999/xlink',
-
-	/**
-	 * Variable: SHADOWCOLOR
-	 *
-	 * Defines the color to be used to draw shadows in shapes and windows.
-	 * Default is gray.
-	 */
-	SHADOWCOLOR: 'gray',
-
-	/**
-	 * Variable: VML_SHADOWCOLOR
-	 *
-	 * Used for shadow color in filters where transparency is not supported
-	 * (Microsoft Internet Explorer). Default is gray.
-	 */
-	VML_SHADOWCOLOR: 'gray',
-
-	/**
-	 * Variable: SHADOW_OFFSET_X
-	 *
-	 * Specifies the x-offset of the shadow. Default is 2.
-	 */
-	SHADOW_OFFSET_X: 2,
-
-	/**
-	 * Variable: SHADOW_OFFSET_Y
-	 *
-	 * Specifies the y-offset of the shadow. Default is 3.
-	 */
-	SHADOW_OFFSET_Y: 3,
-
-	/**
-	 * Variable: SHADOW_OPACITY
-	 *
-	 * Defines the opacity for shadows. Default is 1.
-	 */
-	SHADOW_OPACITY: 1,
-
-	/**
-	 * Variable: NODETYPE_ELEMENT
-	 *
-	 * DOM node of type ELEMENT.
-	 */
-	NODETYPE_ELEMENT: 1,
-
-	/**
-	 * Variable: NODETYPE_ATTRIBUTE
-	 *
-	 * DOM node of type ATTRIBUTE.
-	 */
-	NODETYPE_ATTRIBUTE: 2,
-
-	/**
-	 * Variable: NODETYPE_TEXT
-	 *
-	 * DOM node of type TEXT.
-	 */
-	NODETYPE_TEXT: 3,
-
-	/**
-	 * Variable: NODETYPE_CDATA
-	 *
-	 * DOM node of type CDATA.
-	 */
-	NODETYPE_CDATA: 4,
-
-	/**
-	 * Variable: NODETYPE_ENTITY_REFERENCE
-	 *
-	 * DOM node of type ENTITY_REFERENCE.
-	 */
-	NODETYPE_ENTITY_REFERENCE: 5,
-
-	/**
-	 * Variable: NODETYPE_ENTITY
-	 *
-	 * DOM node of type ENTITY.
-	 */
-	NODETYPE_ENTITY: 6,
-
-	/**
-	 * Variable: NODETYPE_PROCESSING_INSTRUCTION
-	 *
-	 * DOM node of type PROCESSING_INSTRUCTION.
-	 */
-	NODETYPE_PROCESSING_INSTRUCTION: 7,
-
-	/**
-	 * Variable: NODETYPE_COMMENT
-	 *
-	 * DOM node of type COMMENT.
-	 */
-	NODETYPE_COMMENT: 8,
-
-	/**
-	 * Variable: NODETYPE_DOCUMENT
-	 *
-	 * DOM node of type DOCUMENT.
-	 */
-	NODETYPE_DOCUMENT: 9,
-
-	/**
-	 * Variable: NODETYPE_DOCUMENTTYPE
-	 *
-	 * DOM node of type DOCUMENTTYPE.
-	 */
-	NODETYPE_DOCUMENTTYPE: 10,
-
-	/**
-	 * Variable: NODETYPE_DOCUMENT_FRAGMENT
-	 *
-	 * DOM node of type DOCUMENT_FRAGMENT.
-	 */
-	NODETYPE_DOCUMENT_FRAGMENT: 11,
-
-	/**
-	 * Variable: NODETYPE_NOTATION
-	 *
-	 * DOM node of type NOTATION.
-	 */
-	NODETYPE_NOTATION: 12,
-
-	/**
-	 * Variable: TOOLTIP_VERTICAL_OFFSET
-	 *
-	 * Defines the vertical offset for the tooltip.
-	 * Default is 16.
-	 */
-	TOOLTIP_VERTICAL_OFFSET: 16,
-
-	/**
-	 * Variable: DEFAULT_VALID_COLOR
-	 *
-	 * Specifies the default valid color. Default is #0000FF.
-	 */
-	DEFAULT_VALID_COLOR: '#00FF00',
-
-	/**
-	 * Variable: DEFAULT_INVALID_COLOR
-	 *
-	 * Specifies the default invalid color. Default is #FF0000.
-	 */
-	DEFAULT_INVALID_COLOR: '#FF0000',
-
-	/**
-	 * Variable: OUTLINE_HIGHLIGHT_COLOR
-	 *
-	 * Specifies the default highlight color for shape outlines.
-	 * Default is #0000FF. This is used in .
-	 */
-	OUTLINE_HIGHLIGHT_COLOR: '#00FF00',
-
-	/**
-	 * Variable: OUTLINE_HIGHLIGHT_COLOR
-	 *
-	 * Defines the strokewidth to be used for shape outlines.
-	 * Default is 5. This is used in .
-	 */
-	OUTLINE_HIGHLIGHT_STROKEWIDTH: 5,
-
-	/**
-	 * Variable: HIGHLIGHT_STROKEWIDTH
-	 *
-	 * Defines the strokewidth to be used for the highlights.
-	 * Default is 3.
-	 */
-	HIGHLIGHT_STROKEWIDTH: 3,
-
-	/**
-	 * Variable: CONSTRAINT_HIGHLIGHT_SIZE
-	 *
-	 * Size of the constraint highlight (in px). Default is 2.
-	 */
-	HIGHLIGHT_SIZE: 2,
-
-	/**
-	 * Variable: HIGHLIGHT_OPACITY
-	 *
-	 * Opacity (in %) used for the highlights (including outline).
-	 * Default is 100.
-	 */
-	HIGHLIGHT_OPACITY: 100,
-
-	/**
-	 * Variable: CURSOR_MOVABLE_VERTEX
-	 *
-	 * Defines the cursor for a movable vertex. Default is 'move'.
-	 */
-	CURSOR_MOVABLE_VERTEX: 'move',
-
-	/**
-	 * Variable: CURSOR_MOVABLE_EDGE
-	 *
-	 * Defines the cursor for a movable edge. Default is 'move'.
-	 */
-	CURSOR_MOVABLE_EDGE: 'move',
-
-	/**
-	 * Variable: CURSOR_LABEL_HANDLE
-	 *
-	 * Defines the cursor for a movable label. Default is 'default'.
-	 */
-	CURSOR_LABEL_HANDLE: 'default',
-
-	/**
-	 * Variable: CURSOR_TERMINAL_HANDLE
-	 *
-	 * Defines the cursor for a terminal handle. Default is 'pointer'.
-	 */
-	CURSOR_TERMINAL_HANDLE: 'pointer',
-
-	/**
-	 * Variable: CURSOR_BEND_HANDLE
-	 *
-	 * Defines the cursor for a movable bend. Default is 'crosshair'.
-	 */
-	CURSOR_BEND_HANDLE: 'crosshair',
-
-	/**
-	 * Variable: CURSOR_VIRTUAL_BEND_HANDLE
-	 *
-	 * Defines the cursor for a movable bend. Default is 'crosshair'.
-	 */
-	CURSOR_VIRTUAL_BEND_HANDLE: 'crosshair',
-
-	/**
-	 * Variable: CURSOR_CONNECT
-	 *
-	 * Defines the cursor for a connectable state. Default is 'pointer'.
-	 */
-	CURSOR_CONNECT: 'pointer',
-
-	/**
-	 * Variable: HIGHLIGHT_COLOR
-	 *
-	 * Defines the color to be used for the cell highlighting.
-	 * Use 'none' for no color. Default is #00FF00.
-	 */
-	HIGHLIGHT_COLOR: '#00FF00',
-
-	/**
-	 * Variable: TARGET_HIGHLIGHT_COLOR
-	 *
-	 * Defines the color to be used for highlighting a target cell for a new
-	 * or changed connection. Note that this may be either a source or
-	 * target terminal in the graph. Use 'none' for no color.
-	 * Default is #0000FF.
-	 */
-	CONNECT_TARGET_COLOR: '#0000FF',
-
-	/**
-	 * Variable: INVALID_CONNECT_TARGET_COLOR
-	 *
-	 * Defines the color to be used for highlighting a invalid target cells
-	 * for a new or changed connections. Note that this may be either a source
-	 * or target terminal in the graph. Use 'none' for no color. Default is
-	 * #FF0000.
-	 */
-	INVALID_CONNECT_TARGET_COLOR: '#FF0000',
-
-	/**
-	 * Variable: DROP_TARGET_COLOR
-	 *
-	 * Defines the color to be used for the highlighting target parent cells
-	 * (for drag and drop). Use 'none' for no color. Default is #0000FF.
-	 */
-	DROP_TARGET_COLOR: '#0000FF',
-
-	/**
-	 * Variable: VALID_COLOR
-	 *
-	 * Defines the color to be used for the coloring valid connection
-	 * previews. Use 'none' for no color. Default is #FF0000.
-	 */
-	VALID_COLOR: '#00FF00',
-
-	/**
-	 * Variable: INVALID_COLOR
-	 *
-	 * Defines the color to be used for the coloring invalid connection
-	 * previews. Use 'none' for no color. Default is #FF0000.
-	 */
-	INVALID_COLOR: '#FF0000',
-
-	/**
-	 * Variable: EDGE_SELECTION_COLOR
-	 *
-	 * Defines the color to be used for the selection border of edges. Use
-	 * 'none' for no color. Default is #00FF00.
-	 */
-	EDGE_SELECTION_COLOR: '#00FF00',
-
-	/**
-	 * Variable: VERTEX_SELECTION_COLOR
-	 *
-	 * Defines the color to be used for the selection border of vertices. Use
-	 * 'none' for no color. Default is #00FF00.
-	 */
-	VERTEX_SELECTION_COLOR: '#00FF00',
-
-	/**
-	 * Variable: VERTEX_SELECTION_STROKEWIDTH
-	 *
-	 * Defines the strokewidth to be used for vertex selections.
-	 * Default is 1.
-	 */
-	VERTEX_SELECTION_STROKEWIDTH: 1,
-
-	/**
-	 * Variable: EDGE_SELECTION_STROKEWIDTH
-	 *
-	 * Defines the strokewidth to be used for edge selections.
-	 * Default is 1.
-	 */
-	EDGE_SELECTION_STROKEWIDTH: 1,
-
-	/**
-	 * Variable: SELECTION_DASHED
-	 *
-	 * Defines the dashed state to be used for the vertex selection
-	 * border. Default is true.
-	 */
-	VERTEX_SELECTION_DASHED: true,
-
-	/**
-	 * Variable: SELECTION_DASHED
-	 *
-	 * Defines the dashed state to be used for the edge selection
-	 * border. Default is true.
-	 */
-	EDGE_SELECTION_DASHED: true,
-
-	/**
-	 * Variable: GUIDE_COLOR
-	 *
-	 * Defines the color to be used for the guidelines in mxGraphHandler.
-	 * Default is #FF0000.
-	 */
-	GUIDE_COLOR: '#FF0000',
-
-	/**
-	 * Variable: GUIDE_STROKEWIDTH
-	 *
-	 * Defines the strokewidth to be used for the guidelines in mxGraphHandler.
-	 * Default is 1.
-	 */
-	GUIDE_STROKEWIDTH: 1,
-
-	/**
-	 * Variable: OUTLINE_COLOR
-	 *
-	 * Defines the color to be used for the outline rectangle
-	 * border.  Use 'none' for no color. Default is #0099FF.
-	 */
-	OUTLINE_COLOR: '#0099FF',
-
-	/**
-	 * Variable: OUTLINE_STROKEWIDTH
-	 *
-	 * Defines the strokewidth to be used for the outline rectangle
-	 * stroke width. Default is 3.
-	 */
-	OUTLINE_STROKEWIDTH: (mxClient.IS_IE) ? 2 : 3,
-
-	/**
-	 * Variable: HANDLE_SIZE
-	 *
-	 * Defines the default size for handles. Default is 6.
-	 */
-	HANDLE_SIZE: 6,
-
-	/**
-	 * Variable: LABEL_HANDLE_SIZE
-	 *
-	 * Defines the default size for label handles. Default is 4.
-	 */
-	LABEL_HANDLE_SIZE: 4,
-
-	/**
-	 * Variable: HANDLE_FILLCOLOR
-	 *
-	 * Defines the color to be used for the handle fill color. Use 'none' for
-	 * no color. Default is #00FF00 (green).
-	 */
-	HANDLE_FILLCOLOR: '#00FF00',
-
-	/**
-	 * Variable: HANDLE_STROKECOLOR
-	 *
-	 * Defines the color to be used for the handle stroke color. Use 'none' for
-	 * no color. Default is black.
-	 */
-	HANDLE_STROKECOLOR: 'black',
-
-	/**
-	 * Variable: LABEL_HANDLE_FILLCOLOR
-	 *
-	 * Defines the color to be used for the label handle fill color. Use 'none'
-	 * for no color. Default is yellow.
-	 */
-	LABEL_HANDLE_FILLCOLOR: 'yellow',
-
-	/**
-	 * Variable: CONNECT_HANDLE_FILLCOLOR
-	 *
-	 * Defines the color to be used for the connect handle fill color. Use
-	 * 'none' for no color. Default is #0000FF (blue).
-	 */
-	CONNECT_HANDLE_FILLCOLOR: '#0000FF',
-
-	/**
-	 * Variable: LOCKED_HANDLE_FILLCOLOR
-	 *
-	 * Defines the color to be used for the locked handle fill color. Use
-	 * 'none' for no color. Default is #FF0000 (red).
-	 */
-	LOCKED_HANDLE_FILLCOLOR: '#FF0000',
-
-	/**
-	 * Variable: OUTLINE_HANDLE_FILLCOLOR
-	 *
-	 * Defines the color to be used for the outline sizer fill color. Use
-	 * 'none' for no color. Default is #00FFFF.
-	 */
-	OUTLINE_HANDLE_FILLCOLOR: '#00FFFF',
-
-	/**
-	 * Variable: OUTLINE_HANDLE_STROKECOLOR
-	 *
-	 * Defines the color to be used for the outline sizer stroke color. Use
-	 * 'none' for no color. Default is #0033FF.
-	 */
-	OUTLINE_HANDLE_STROKECOLOR: '#0033FF',
-
-	/**
-	 * Variable: DEFAULT_FONTFAMILY
-	 *
-	 * Defines the default family for all fonts. Default is Arial,Helvetica.
-	 */
-	DEFAULT_FONTFAMILY: 'Arial,Helvetica',
-
-	/**
-	 * Variable: DEFAULT_FONTSIZE
-	 *
-	 * Defines the default size (in px). Default is 11.
-	 */
-	DEFAULT_FONTSIZE: 11,
-
-	/**
-	 * Variable: DEFAULT_TEXT_DIRECTION
-	 *
-	 * Defines the default value for the  if no value is
-	 * defined for it in the style. Default value is an empty string which means
-	 * the default system setting is used and no direction is set.
-	 */
-	DEFAULT_TEXT_DIRECTION: '',
-
-	/**
-	 * Variable: LINE_HEIGHT
-	 *
-	 * Defines the default line height for text labels. Default is 1.2.
-	 */
-	LINE_HEIGHT: 1.2,
-
-	/**
-	 * Variable: WORD_WRAP
-	 *
-	 * Defines the CSS value for the word-wrap property. Default is "normal".
-	 * Change this to "break-word" to allow long words to be able to be broken
-	 * and wrap onto the next line.
-	 */
-	WORD_WRAP: 'normal',
-
-	/**
-	 * Variable: ABSOLUTE_LINE_HEIGHT
-	 *
-	 * Specifies if absolute line heights should be used (px) in CSS. Default
-	 * is false. Set this to true for backwards compatibility.
-	 */
-	ABSOLUTE_LINE_HEIGHT: false,
-
-	/**
-	 * Variable: DEFAULT_FONTSTYLE
-	 *
-	 * Defines the default style for all fonts. Default is 0. This can be set
-	 * to any combination of font styles as follows.
-	 *
-	 * (code)
-	 * mxConstants.DEFAULT_FONTSTYLE = mxConstants.FONT_BOLD | mxConstants.FONT_ITALIC;
-	 * (end)
-	 */
-	DEFAULT_FONTSTYLE: 0,
-
-	/**
-	 * Variable: DEFAULT_STARTSIZE
-	 *
-	 * Defines the default start size for swimlanes. Default is 40.
-	 */
-	DEFAULT_STARTSIZE: 40,
-
-	/**
-	 * Variable: DEFAULT_MARKERSIZE
-	 *
-	 * Defines the default size for all markers. Default is 6.
-	 */
-	DEFAULT_MARKERSIZE: 6,
-
-	/**
-	 * Variable: DEFAULT_IMAGESIZE
-	 *
-	 * Defines the default width and height for images used in the
-	 * label shape. Default is 24.
-	 */
-	DEFAULT_IMAGESIZE: 24,
-
-	/**
-	 * Variable: ENTITY_SEGMENT
-	 *
-	 * Defines the length of the horizontal segment of an Entity Relation.
-	 * This can be overridden using  style.
-	 * Default is 30.
-	 */
-	ENTITY_SEGMENT: 30,
-
-	/**
-	 * Variable: RECTANGLE_ROUNDING_FACTOR
-	 *
-	 * Defines the rounding factor for rounded rectangles in percent between
-	 * 0 and 1. Values should be smaller than 0.5. Default is 0.15.
-	 */
-	RECTANGLE_ROUNDING_FACTOR: 0.15,
-
-	/**
-	 * Variable: LINE_ARCSIZE
-	 *
-	 * Defines the size of the arcs for rounded edges. Default is 20.
-	 */
-	LINE_ARCSIZE: 20,
-
-	/**
-	 * Variable: ARROW_SPACING
-	 *
-	 * Defines the spacing between the arrow shape and its terminals. Default is 0.
-	 */
-	ARROW_SPACING: 0,
-
-	/**
-	 * Variable: ARROW_WIDTH
-	 *
-	 * Defines the width of the arrow shape. Default is 30.
-	 */
-	ARROW_WIDTH: 30,
-
-	/**
-	 * Variable: ARROW_SIZE
-	 *
-	 * Defines the size of the arrowhead in the arrow shape. Default is 30.
-	 */
-	ARROW_SIZE: 30,
-
-	/**
-	 * Variable: PAGE_FORMAT_A4_PORTRAIT
-	 *
-	 * Defines the rectangle for the A4 portrait page format. The dimensions
-	 * of this page format are 826x1169 pixels.
-	 */
-	PAGE_FORMAT_A4_PORTRAIT: new mxRectangle(0, 0, 827, 1169),
-
-	/**
-	 * Variable: PAGE_FORMAT_A4_PORTRAIT
-	 *
-	 * Defines the rectangle for the A4 portrait page format. The dimensions
-	 * of this page format are 826x1169 pixels.
-	 */
-	PAGE_FORMAT_A4_LANDSCAPE: new mxRectangle(0, 0, 1169, 827),
-
-	/**
-	 * Variable: PAGE_FORMAT_LETTER_PORTRAIT
-	 *
-	 * Defines the rectangle for the Letter portrait page format. The
-	 * dimensions of this page format are 850x1100 pixels.
-	 */
-	PAGE_FORMAT_LETTER_PORTRAIT: new mxRectangle(0, 0, 850, 1100),
-
-	/**
-	 * Variable: PAGE_FORMAT_LETTER_PORTRAIT
-	 *
-	 * Defines the rectangle for the Letter portrait page format. The dimensions
-	 * of this page format are 850x1100 pixels.
-	 */
-	PAGE_FORMAT_LETTER_LANDSCAPE: new mxRectangle(0, 0, 1100, 850),
-
-	/**
-	 * Variable: NONE
-	 *
-	 * Defines the value for none. Default is "none".
-	 */
-	NONE: 'none',
-
-	/**
-	 * Variable: STYLE_PERIMETER
-	 *
-	 * Defines the key for the perimeter style. This is a function that defines
-	 * the perimeter around a particular shape. Possible values are the
-	 * functions defined in . Alternatively, the constants in this
-	 * class that start with "PERIMETER_" may be used to access
-	 * perimeter styles in . Value is "perimeter".
-	 */
-	STYLE_PERIMETER: 'perimeter',
-
-	/**
-	 * Variable: STYLE_SOURCE_PORT
-	 *
-	 * Defines the ID of the cell that should be used for computing the
-	 * perimeter point of the source for an edge. This allows for graphically
-	 * connecting to a cell while keeping the actual terminal of the edge.
-	 * Value is "sourcePort".
-	 */
-	STYLE_SOURCE_PORT: 'sourcePort',
-
-	/**
-	 * Variable: STYLE_TARGET_PORT
-	 *
-	 * Defines the ID of the cell that should be used for computing the
-	 * perimeter point of the target for an edge. This allows for graphically
-	 * connecting to a cell while keeping the actual terminal of the edge.
-	 * Value is "targetPort".
-	 */
-	STYLE_TARGET_PORT: 'targetPort',
-
-	/**
-	 * Variable: STYLE_PORT_CONSTRAINT
-	 *
-	 * Defines the direction(s) that edges are allowed to connect to cells in.
-	 * Possible values are "DIRECTION_NORTH, DIRECTION_SOUTH,
-	 * DIRECTION_EAST" and "DIRECTION_WEST". Value is
-	 * "portConstraint".
-	 */
-	STYLE_PORT_CONSTRAINT: 'portConstraint',
-
-	/**
-	 * Variable: STYLE_PORT_CONSTRAINT_ROTATION
-	 *
-	 * Define whether port constraint directions are rotated with vertex
-	 * rotation. 0 (default) causes port constraints to remain absolute,
-	 * relative to the graph, 1 causes the constraints to rotate with
-	 * the vertex. Value is "portConstraintRotation".
-	 */
-	STYLE_PORT_CONSTRAINT_ROTATION: 'portConstraintRotation',
-
-	/**
-	 * Variable: STYLE_SOURCE_PORT_CONSTRAINT
-	 *
-	 * Defines the direction(s) that edges are allowed to connect to sources in.
-	 * Possible values are "DIRECTION_NORTH, DIRECTION_SOUTH, DIRECTION_EAST"
-	 * and "DIRECTION_WEST". Value is "sourcePortConstraint".
-	 */
-	STYLE_SOURCE_PORT_CONSTRAINT: 'sourcePortConstraint',
-
-	/**
-	 * Variable: STYLE_TARGET_PORT_CONSTRAINT
-	 *
-	 * Defines the direction(s) that edges are allowed to connect to targets in.
-	 * Possible values are "DIRECTION_NORTH, DIRECTION_SOUTH, DIRECTION_EAST"
-	 * and "DIRECTION_WEST". Value is "targetPortConstraint".
-	 */
-	STYLE_TARGET_PORT_CONSTRAINT: 'targetPortConstraint',
-
-	/**
-	 * Variable: STYLE_OPACITY
-	 *
-	 * Defines the key for the opacity style. The type of the value is
-	 * numeric and the possible range is 0-100. Value is "opacity".
-	 */
-	STYLE_OPACITY: 'opacity',
-
-	/**
-	 * Variable: STYLE_FILL_OPACITY
-	 *
-	 * Defines the key for the fill opacity style. The type of the value is
-	 * numeric and the possible range is 0-100. Value is "fillOpacity".
-	 */
-	STYLE_FILL_OPACITY: 'fillOpacity',
-
-	/**
-	 * Variable: STYLE_STROKE_OPACITY
-	 *
-	 * Defines the key for the stroke opacity style. The type of the value is
-	 * numeric and the possible range is 0-100. Value is "strokeOpacity".
-	 */
-	STYLE_STROKE_OPACITY: 'strokeOpacity',
-
-	/**
-	 * Variable: STYLE_TEXT_OPACITY
-	 *
-	 * Defines the key for the text opacity style. The type of the value is
-	 * numeric and the possible range is 0-100. Value is "textOpacity".
-	 */
-	STYLE_TEXT_OPACITY: 'textOpacity',
-
-	/**
-	 * Variable: STYLE_TEXT_DIRECTION
-	 *
-	 * Defines the key for the text direction style. Possible values are
-	 * "TEXT_DIRECTION_DEFAULT, TEXT_DIRECTION_AUTO, TEXT_DIRECTION_LTR"
-	 * and "TEXT_DIRECTION_RTL". Value is "textDirection".
-	 * The default value for the style is defined in .
-	 * It is used is no value is defined for this key in a given style. This is
-	 * an experimental style that is currently ignored in the backends.
-	 */
-	STYLE_TEXT_DIRECTION: 'textDirection',
-
-	/**
-	 * Variable: STYLE_OVERFLOW
-	 *
-	 * Defines the key for the overflow style. Possible values are 'visible',
-	 * 'hidden', 'fill' and 'width'. The default value is 'visible'. This value
-	 * specifies how overlapping vertex labels are handled. A value of
-	 * 'visible' will show the complete label. A value of 'hidden' will clip
-	 * the label so that it does not overlap the vertex bounds. A value of
-	 * 'fill' will use the vertex bounds and a value of 'width' will use the
-	 * the vertex width for the label. See . Note that
-	 * the vertical alignment is ignored for overflow fill and for horizontal
-	 * alignment, left should be used to avoid pixel offsets in Internet Explorer
-	 * 11 and earlier or if foreignObjects are disabled. Value is "overflow".
-	 */
-	STYLE_OVERFLOW: 'overflow',
-
-	/**
-	 * Variable: STYLE_ORTHOGONAL
-	 *
-	 * Defines if the connection points on either end of the edge should be
-	 * computed so that the edge is vertical or horizontal if possible and
-	 * if the point is not at a fixed location. Default is false. This is
-	 * used in , which also returns true if the edgeStyle
-	 * of the edge is an elbow or entity. Value is "orthogonal".
-	 */
-	STYLE_ORTHOGONAL: 'orthogonal',
-
-	/**
-	 * Variable: STYLE_EXIT_X
-	 *
-	 * Defines the key for the horizontal relative coordinate connection point
-	 * of an edge with its source terminal. Value is "exitX".
-	 */
-	STYLE_EXIT_X: 'exitX',
-
-	/**
-	 * Variable: STYLE_EXIT_Y
-	 *
-	 * Defines the key for the vertical relative coordinate connection point
-	 * of an edge with its source terminal. Value is "exitY".
-	 */
-	STYLE_EXIT_Y: 'exitY',
-
-	/**
-	 * Variable: STYLE_EXIT_PERIMETER
-	 *
-	 * Defines if the perimeter should be used to find the exact entry point
-	 * along the perimeter of the source. Possible values are 0 (false) and
-	 * 1 (true). Default is 1 (true). Value is "exitPerimeter".
-	 */
-	STYLE_EXIT_PERIMETER: 'exitPerimeter',
-
-	/**
-	 * Variable: STYLE_ENTRY_X
-	 *
-	 * Defines the key for the horizontal relative coordinate connection point
-	 * of an edge with its target terminal. Value is "entryX".
-	 */
-	STYLE_ENTRY_X: 'entryX',
-
-	/**
-	 * Variable: STYLE_ENTRY_Y
-	 *
-	 * Defines the key for the vertical relative coordinate connection point
-	 * of an edge with its target terminal. Value is "entryY".
-	 */
-	STYLE_ENTRY_Y: 'entryY',
-
-	/**
-	 * Variable: STYLE_ENTRY_PERIMETER
-	 *
-	 * Defines if the perimeter should be used to find the exact entry point
-	 * along the perimeter of the target. Possible values are 0 (false) and
-	 * 1 (true). Default is 1 (true). Value is "entryPerimeter".
-	 */
-	STYLE_ENTRY_PERIMETER: 'entryPerimeter',
-
-	/**
-	 * Variable: STYLE_WHITE_SPACE
-	 *
-	 * Defines the key for the white-space style. Possible values are 'nowrap'
-	 * and 'wrap'. The default value is 'nowrap'. This value specifies how
-	 * white-space inside a HTML vertex label should be handled. A value of
-	 * 'nowrap' means the text will never wrap to the next line until a
-	 * linefeed is encountered. A value of 'wrap' means text will wrap when
-	 * necessary. This style is only used for HTML labels.
-	 * See . Value is "whiteSpace".
-	 */
-	STYLE_WHITE_SPACE: 'whiteSpace',
-
-	/**
-	 * Variable: STYLE_ROTATION
-	 *
-	 * Defines the key for the rotation style. The type of the value is
-	 * numeric and the possible range is 0-360. Value is "rotation".
-	 */
-	STYLE_ROTATION: 'rotation',
-
-	/**
-	 * Variable: STYLE_FILLCOLOR
-	 *
-	 * Defines the key for the fill color. Possible values are all HTML color
-	 * names or HEX codes, as well as special keywords such as 'swimlane,
-	 * 'inherit' or 'indicated' to use the color code of a related cell or the
-	 * indicator shape. Value is "fillColor".
-	 */
-	STYLE_FILLCOLOR: 'fillColor',
-
-	/**
-	 * Variable: STYLE_POINTER_EVENTS
-	 *
-	 * Specifies if pointer events should be fired on transparent backgrounds.
-	 * This style is currently only supported in . Default
-	 * is true. Value is "pointerEvents". This is typically set to
-	 * false in groups where the transparent part should allow any underlying
-	 * cells to be clickable.
-	 */
-	STYLE_POINTER_EVENTS: 'pointerEvents',
-
-	/**
-	 * Variable: STYLE_SWIMLANE_FILLCOLOR
-	 *
-	 * Defines the key for the fill color of the swimlane background. Possible
-	 * values are all HTML color names or HEX codes. Default is no background.
-	 * Value is "swimlaneFillColor".
-	 */
-	STYLE_SWIMLANE_FILLCOLOR: 'swimlaneFillColor',
-
-	/**
-	 * Variable: STYLE_MARGIN
-	 *
-	 * Defines the key for the margin between the ellipses in the double ellipse shape.
-	 * Possible values are all positive numbers. Value is "margin".
-	 */
-	STYLE_MARGIN: 'margin',
-
-	/**
-	 * Variable: STYLE_GRADIENTCOLOR
-	 *
-	 * Defines the key for the gradient color. Possible values are all HTML color
-	 * names or HEX codes, as well as special keywords such as 'swimlane,
-	 * 'inherit' or 'indicated' to use the color code of a related cell or the
-	 * indicator shape. This is ignored if no fill color is defined. Value is
-	 * "gradientColor".
-	 */
-	STYLE_GRADIENTCOLOR: 'gradientColor',
-
-	/**
-	 * Variable: STYLE_GRADIENT_DIRECTION
-	 *
-	 * Defines the key for the gradient direction. Possible values are
-	 * , ,  and
-	 * . Default is . Generally, and by
-	 * default in mxGraph, gradient painting is done from the value of
-	 *  to the value of . Taking the
-	 * example of , this means  color at the
-	 * bottom of paint pattern and  at top, with a
-	 * gradient in-between. Value is "gradientDirection".
-	 */
-	STYLE_GRADIENT_DIRECTION: 'gradientDirection',
-
-	/**
-	 * Variable: STYLE_STROKECOLOR
-	 *
-	 * Defines the key for the strokeColor style. Possible values are all HTML
-	 * color names or HEX codes, as well as special keywords such as 'swimlane,
-	 * 'inherit', 'indicated' to use the color code of a related cell or the
-	 * indicator shape or 'none' for no color. Value is "strokeColor".
-	 */
-	STYLE_STROKECOLOR: 'strokeColor',
-
-	/**
-	 * Variable: STYLE_SEPARATORCOLOR
-	 *
-	 * Defines the key for the separatorColor style. Possible values are all
-	 * HTML color names or HEX codes. This style is only used for
-	 *  shapes. Value is "separatorColor".
-	 */
-	STYLE_SEPARATORCOLOR: 'separatorColor',
-
-	/**
-	 * Variable: STYLE_STROKEWIDTH
-	 *
-	 * Defines the key for the strokeWidth style. The type of the value is
-	 * numeric and the possible range is any non-negative value larger or equal
-	 * to 1. The value defines the stroke width in pixels. Note: To hide a
-	 * stroke use strokeColor none. Value is "strokeWidth".
-	 */
-	STYLE_STROKEWIDTH: 'strokeWidth',
-
-	/**
-	 * Variable: STYLE_ALIGN
-	 *
-	 * Defines the key for the align style. Possible values are ,
-	 *  and . This value defines how the lines of
-	 * the label are horizontally aligned.  mean label text lines
-	 * are aligned to left of the label bounds,  to the right of
-	 * the label bounds and  means the center of the text lines
-	 * are aligned in the center of the label bounds. Note this value doesn't
-	 * affect the positioning of the overall label bounds relative to the
-	 * vertex, to move the label bounds horizontally, use
-	 * . Value is "align".
-	 */
-	STYLE_ALIGN: 'align',
-
-	/**
-	 * Variable: STYLE_VERTICAL_ALIGN
-	 *
-	 * Defines the key for the verticalAlign style. Possible values are
-	 * ,  and . This value defines how
-	 * the lines of the label are vertically aligned.  means the
-	 * topmost label text line is aligned against the top of the label bounds,
-	 *  means the bottom-most label text line is aligned against
-	 * the bottom of the label bounds and  means there is equal
-	 * spacing between the topmost text label line and the top of the label
-	 * bounds and the bottom-most text label line and the bottom of the label
-	 * bounds. Note this value doesn't affect the positioning of the overall
-	 * label bounds relative to the vertex, to move the label bounds
-	 * vertically, use . Value is "verticalAlign".
-	 */
-	STYLE_VERTICAL_ALIGN: 'verticalAlign',
-
-	/**
-	 * Variable: STYLE_LABEL_WIDTH
-	 *
-	 * Defines the key for the width of the label if the label position is not
-	 * center. Value is "labelWidth".
-	 */
-	STYLE_LABEL_WIDTH: 'labelWidth',
-
-	/**
-	 * Variable: STYLE_LABEL_POSITION
-	 *
-	 * Defines the key for the horizontal label position of vertices. Possible
-	 * values are ,  and . Default is
-	 * . The label align defines the position of the label
-	 * relative to the cell.  means the entire label bounds is
-	 * placed completely just to the left of the vertex,  means
-	 * adjust to the right and  means the label bounds are
-	 * vertically aligned with the bounds of the vertex. Note this value
-	 * doesn't affect the positioning of label within the label bounds, to move
-	 * the label horizontally within the label bounds, use .
-	 * Value is "labelPosition".
-	 */
-	STYLE_LABEL_POSITION: 'labelPosition',
-
-	/**
-	 * Variable: STYLE_VERTICAL_LABEL_POSITION
-	 *
-	 * Defines the key for the vertical label position of vertices. Possible
-	 * values are ,  and . Default is
-	 * . The label align defines the position of the label
-	 * relative to the cell.  means the entire label bounds is
-	 * placed completely just on the top of the vertex,  means
-	 * adjust on the bottom and  means the label bounds are
-	 * horizontally aligned with the bounds of the vertex. Note this value
-	 * doesn't affect the positioning of label within the label bounds, to move
-	 * the label vertically within the label bounds, use
-	 * . Value is "verticalLabelPosition".
-	 */
-	STYLE_VERTICAL_LABEL_POSITION: 'verticalLabelPosition',
-
-	/**
-	 * Variable: STYLE_IMAGE_ASPECT
-	 *
-	 * Defines the key for the image aspect style. Possible values are 0 (do
-	 * not preserve aspect) or 1 (keep aspect). This is only used in
-	 * . Default is 1. Value is "imageAspect".
-	 */
-	STYLE_IMAGE_ASPECT: 'imageAspect',
-
-	/**
-	 * Variable: STYLE_IMAGE_ALIGN
-	 *
-	 * Defines the key for the align style. Possible values are ,
-	 *  and . The value defines how any image in the
-	 * vertex label is aligned horizontally within the label bounds of a
-	 *  shape. Value is "imageAlign".
-	 */
-	STYLE_IMAGE_ALIGN: 'imageAlign',
-
-	/**
-	 * Variable: STYLE_IMAGE_VERTICAL_ALIGN
-	 *
-	 * Defines the key for the verticalAlign style. Possible values are
-	 * ,  and . The value defines how
-	 * any image in the vertex label is aligned vertically within the label
-	 * bounds of a  shape. Value is "imageVerticalAlign".
-	 */
-	STYLE_IMAGE_VERTICAL_ALIGN: 'imageVerticalAlign',
-
-	/**
-	 * Variable: STYLE_GLASS
-	 *
-	 * Defines the key for the glass style. Possible values are 0 (disabled) and
-	 * 1(enabled). The default value is 0. This is used in . Value is
-	 * "glass".
-	 */
-	STYLE_GLASS: 'glass',
-
-	/**
-	 * Variable: STYLE_IMAGE
-	 *
-	 * Defines the key for the image style. Possible values are any image URL,
-	 * the type of the value is String. This is the path to the image that is
-	 * to be displayed within the label of a vertex. Data URLs should use the
-	 * following format: data:image/png,xyz where xyz is the base64 encoded
-	 * data (without the "base64"-prefix). Note that Data URLs are only
-	 * supported in modern browsers. Value is "image".
-	 */
-	STYLE_IMAGE: 'image',
-
-	/**
-	 * Variable: STYLE_IMAGE_WIDTH
-	 *
-	 * Defines the key for the imageWidth style. The type of this value is
-	 * int, the value is the image width in pixels and must be greater than 0.
-	 * Value is "imageWidth".
-	 */
-	STYLE_IMAGE_WIDTH: 'imageWidth',
-
-	/**
-	 * Variable: STYLE_IMAGE_HEIGHT
-	 *
-	 * Defines the key for the imageHeight style. The type of this value is
-	 * int, the value is the image height in pixels and must be greater than 0.
-	 * Value is "imageHeight".
-	 */
-	STYLE_IMAGE_HEIGHT: 'imageHeight',
-
-	/**
-	 * Variable: STYLE_IMAGE_BACKGROUND
-	 *
-	 * Defines the key for the image background color. This style is only used
-	 * in . Possible values are all HTML color names or HEX
-	 * codes. Value is "imageBackground".
-	 */
-	STYLE_IMAGE_BACKGROUND: 'imageBackground',
-
-	/**
-	 * Variable: STYLE_IMAGE_BORDER
-	 *
-	 * Defines the key for the image border color. This style is only used in
-	 * . Possible values are all HTML color names or HEX codes.
-	 * Value is "imageBorder".
-	 */
-	STYLE_IMAGE_BORDER: 'imageBorder',
-
-	/**
-	 * Variable: STYLE_FLIPH
-	 *
-	 * Defines the key for the horizontal image flip. This style is only used
-	 * in . Possible values are 0 and 1. Default is 0. Value is
-	 * "flipH".
-	 */
-	STYLE_FLIPH: 'flipH',
-
-	/**
-	 * Variable: STYLE_FLIPV
-	 *
-	 * Defines the key for the vertical flip. Possible values are 0 and 1.
-	 * Default is 0. Value is "flipV".
-	 */
-	STYLE_FLIPV: 'flipV',
-
-	/**
-	 * Variable: STYLE_NOLABEL
-	 *
-	 * Defines the key for the noLabel style. If this is true then no label is
-	 * visible for a given cell. Possible values are true or false (1 or 0).
-	 * Default is false. Value is "noLabel".
-	 */
-	STYLE_NOLABEL: 'noLabel',
-
-	/**
-	 * Variable: STYLE_NOEDGESTYLE
-	 *
-	 * Defines the key for the noEdgeStyle style. If this is true then no edge
-	 * style is applied for a given edge. Possible values are true or false
-	 * (1 or 0). Default is false. Value is "noEdgeStyle".
-	 */
-	STYLE_NOEDGESTYLE: 'noEdgeStyle',
-
-	/**
-	 * Variable: STYLE_LABEL_BACKGROUNDCOLOR
-	 *
-	 * Defines the key for the label background color. Possible values are all
-	 * HTML color names or HEX codes. Value is "labelBackgroundColor".
-	 */
-	STYLE_LABEL_BACKGROUNDCOLOR: 'labelBackgroundColor',
-
-	/**
-	 * Variable: STYLE_LABEL_BORDERCOLOR
-	 *
-	 * Defines the key for the label border color. Possible values are all
-	 * HTML color names or HEX codes. Value is "labelBorderColor".
-	 */
-	STYLE_LABEL_BORDERCOLOR: 'labelBorderColor',
-
-	/**
-	 * Variable: STYLE_LABEL_PADDING
-	 *
-	 * Defines the key for the label padding, ie. the space between the label
-	 * border and the label. Value is "labelPadding".
-	 */
-	STYLE_LABEL_PADDING: 'labelPadding',
-
-	/**
-	 * Variable: STYLE_INDICATOR_SHAPE
-	 *
-	 * Defines the key for the indicator shape used within an .
-	 * Possible values are all SHAPE_* constants or the names of any new
-	 * shapes. The indicatorShape has precedence over the indicatorImage.
-	 * Value is "indicatorShape".
-	 */
-	STYLE_INDICATOR_SHAPE: 'indicatorShape',
-
-	/**
-	 * Variable: STYLE_INDICATOR_IMAGE
-	 *
-	 * Defines the key for the indicator image used within an .
-	 * Possible values are all image URLs. The indicatorShape has
-	 * precedence over the indicatorImage. Value is "indicatorImage".
-	 */
-	STYLE_INDICATOR_IMAGE: 'indicatorImage',
-
-	/**
-	 * Variable: STYLE_INDICATOR_COLOR
-	 *
-	 * Defines the key for the indicatorColor style. Possible values are all
-	 * HTML color names or HEX codes, as well as the special 'swimlane' keyword
-	 * to refer to the color of the parent swimlane if one exists. Value is
-	 * "indicatorColor".
-	 */
-	STYLE_INDICATOR_COLOR: 'indicatorColor',
-
-	/**
-	 * Variable: STYLE_INDICATOR_STROKECOLOR
-	 *
-	 * Defines the key for the indicator stroke color in .
-	 * Possible values are all color codes. Value is "indicatorStrokeColor".
-	 */
-	STYLE_INDICATOR_STROKECOLOR: 'indicatorStrokeColor',
-
-	/**
-	 * Variable: STYLE_INDICATOR_GRADIENTCOLOR
-	 *
-	 * Defines the key for the indicatorGradientColor style. Possible values
-	 * are all HTML color names or HEX codes. This style is only supported in
-	 *  shapes. Value is "indicatorGradientColor".
-	 */
-	STYLE_INDICATOR_GRADIENTCOLOR: 'indicatorGradientColor',
-
-	/**
-	 * Variable: STYLE_INDICATOR_SPACING
-	 *
-	 * The defines the key for the spacing between the label and the
-	 * indicator in . Possible values are in pixels. Value is
-	 * "indicatorSpacing".
-	 */
-	STYLE_INDICATOR_SPACING: 'indicatorSpacing',
-
-	/**
-	 * Variable: STYLE_INDICATOR_WIDTH
-	 *
-	 * Defines the key for the indicator width. Possible values start at 0 (in
-	 * pixels). Value is "indicatorWidth".
-	 */
-	STYLE_INDICATOR_WIDTH: 'indicatorWidth',
-
-	/**
-	 * Variable: STYLE_INDICATOR_HEIGHT
-	 *
-	 * Defines the key for the indicator height. Possible values start at 0 (in
-	 * pixels). Value is "indicatorHeight".
-	 */
-	STYLE_INDICATOR_HEIGHT: 'indicatorHeight',
-
-	/**
-	 * Variable: STYLE_INDICATOR_DIRECTION
-	 *
-	 * Defines the key for the indicatorDirection style. The direction style is
-	 * used to specify the direction of certain shapes (eg. ).
-	 * Possible values are  (default), ,
-	 *  and . Value is "indicatorDirection".
-	 */
-	STYLE_INDICATOR_DIRECTION: 'indicatorDirection',
-
-	/**
-	 * Variable: STYLE_SHADOW
-	 *
-	 * Defines the key for the shadow style. The type of the value is Boolean.
-	 * Value is "shadow".
-	 */
-	STYLE_SHADOW: 'shadow',
-
-	/**
-	 * Variable: STYLE_SEGMENT
-	 *
-	 * Defines the key for the segment style. The type of this value is float
-	 * and the value represents the size of the horizontal segment of the
-	 * entity relation style. Default is ENTITY_SEGMENT. Value is "segment".
-	 */
-	STYLE_SEGMENT: 'segment',
-
-	/**
-	 * Variable: STYLE_ENDARROW
-	 *
-	 * Defines the key for the end arrow marker. Possible values are all
-	 * constants with an ARROW-prefix. This is only used in .
-	 * Value is "endArrow".
-	 *
-	 * Example:
-	 * (code)
-	 * style[mxConstants.STYLE_ENDARROW] = mxConstants.ARROW_CLASSIC;
-	 * (end)
-	 */
-	STYLE_ENDARROW: 'endArrow',
-
-	/**
-	 * Variable: STYLE_STARTARROW
-	 *
-	 * Defines the key for the start arrow marker. Possible values are all
-	 * constants with an ARROW-prefix. This is only used in .
-	 * See . Value is "startArrow".
-	 */
-	STYLE_STARTARROW: 'startArrow',
-
-	/**
-	 * Variable: STYLE_ENDSIZE
-	 *
-	 * Defines the key for the endSize style. The type of this value is numeric
-	 * and the value represents the size of the end marker in pixels. Value is
-	 * "endSize".
-	 */
-	STYLE_ENDSIZE: 'endSize',
-
-	/**
-	 * Variable: STYLE_STARTSIZE
-	 *
-	 * Defines the key for the startSize style. The type of this value is
-	 * numeric and the value represents the size of the start marker or the
-	 * size of the swimlane title region depending on the shape it is used for.
-	 * Value is "startSize".
-	 */
-	STYLE_STARTSIZE: 'startSize',
-
-	/**
-	 * Variable: STYLE_SWIMLANE_LINE
-	 *
-	 * Defines the key for the swimlaneLine style. This style specifies whether
-	 * the line between the title regio of a swimlane should be visible. Use 0
-	 * for hidden or 1 (default) for visible. Value is "swimlaneLine".
-	 */
-	STYLE_SWIMLANE_LINE: 'swimlaneLine',
-
-	/**
-	 * Variable: STYLE_ENDFILL
-	 *
-	 * Defines the key for the endFill style. Use 0 for no fill or 1 (default)
-	 * for fill. (This style is only exported via .) Value is
-	 * "endFill".
-	 */
-	STYLE_ENDFILL: 'endFill',
-
-	/**
-	 * Variable: STYLE_STARTFILL
-	 *
-	 * Defines the key for the startFill style. Use 0 for no fill or 1 (default)
-	 * for fill. (This style is only exported via .) Value is
-	 * "startFill".
-	 */
-	STYLE_STARTFILL: 'startFill',
-
-	/**
-	 * Variable: STYLE_DASHED
-	 *
-	 * Defines the key for the dashed style. Use 0 (default) for non-dashed or 1
-	 * for dashed. Value is "dashed".
-	 */
-	STYLE_DASHED: 'dashed',
-
-	/**
-	 * Defines the key for the dashed pattern style in SVG and image exports.
-	 * The type of this value is a space separated list of numbers that specify
-	 * a custom-defined dash pattern. Dash styles are defined in terms of the
-	 * length of the dash (the drawn part of the stroke) and the length of the
-	 * space between the dashes. The lengths are relative to the line width: a
-	 * length of "1" is equal to the line width. VML ignores this style and
-	 * uses dashStyle instead as defined in the VML specification. This style
-	 * is only used in the  shape. Value is "dashPattern".
-	 */
-	STYLE_DASH_PATTERN: 'dashPattern',
-
-	/**
-	 * Variable: STYLE_FIX_DASH
-	 *
-	 * Defines the key for the fixDash style. Use 0 (default) for dash patterns
-	 * that depend on the linewidth and 1 for dash patterns that ignore the
-	 * line width. Value is "fixDash".
-	 */
-	STYLE_FIX_DASH: 'fixDash',
-
-	/**
-	 * Variable: STYLE_ROUNDED
-	 *
-	 * Defines the key for the rounded style. The type of this value is
-	 * Boolean. For edges this determines whether or not joins between edges
-	 * segments are smoothed to a rounded finish. For vertices that have the
-	 * rectangle shape, this determines whether or not the rectangle is
-	 * rounded. Use 0 (default) for non-rounded or 1 for rounded. Value is
-	 * "rounded".
-	 */
-	STYLE_ROUNDED: 'rounded',
-
-	/**
-	 * Variable: STYLE_CURVED
-	 *
-	 * Defines the key for the curved style. The type of this value is
-	 * Boolean. It is only applicable for connector shapes. Use 0 (default)
-	 * for non-curved or 1 for curved. Value is "curved".
-	 */
-	STYLE_CURVED: 'curved',
-
-	/**
-	 * Variable: STYLE_ARCSIZE
-	 *
-	 * Defines the rounding factor for a rounded rectangle in percent (without
-	 * the percent sign). Possible values are between 0 and 100. If this value
-	 * is not specified then RECTANGLE_ROUNDING_FACTOR * 100 is used. For
-	 * edges, this defines the absolute size of rounded corners in pixels. If
-	 * this values is not specified then LINE_ARCSIZE is used.
-	 * (This style is only exported via .) Value is "arcSize".
-	 */
-	STYLE_ARCSIZE: 'arcSize',
-
-	/**
-	 * Variable: STYLE_ABSOLUTE_ARCSIZE
-	 *
-	 * Defines the key for the absolute arc size style. This specifies if
-	 * arcSize for rectangles is abolute or relative. Possible values are 1
-	 * and 0 (default). Value is "absoluteArcSize".
-	 */
-	STYLE_ABSOLUTE_ARCSIZE: 'absoluteArcSize',
-
-	/**
-	 * Variable: STYLE_SOURCE_PERIMETER_SPACING
-	 *
-	 * Defines the key for the source perimeter spacing. The type of this value
-	 * is numeric. This is the distance between the source connection point of
-	 * an edge and the perimeter of the source vertex in pixels. This style
-	 * only applies to edges. Value is "sourcePerimeterSpacing".
-	 */
-	STYLE_SOURCE_PERIMETER_SPACING: 'sourcePerimeterSpacing',
-
-	/**
-	 * Variable: STYLE_TARGET_PERIMETER_SPACING
-	 *
-	 * Defines the key for the target perimeter spacing. The type of this value
-	 * is numeric. This is the distance between the target connection point of
-	 * an edge and the perimeter of the target vertex in pixels. This style
-	 * only applies to edges. Value is "targetPerimeterSpacing".
-	 */
-	STYLE_TARGET_PERIMETER_SPACING: 'targetPerimeterSpacing',
-
-	/**
-	 * Variable: STYLE_PERIMETER_SPACING
-	 *
-	 * Defines the key for the perimeter spacing. This is the distance between
-	 * the connection point and the perimeter in pixels. When used in a vertex
-	 * style, this applies to all incoming edges to floating ports (edges that
-	 * terminate on the perimeter of the vertex). When used in an edge style,
-	 * this spacing applies to the source and target separately, if they
-	 * terminate in floating ports (on the perimeter of the vertex). Value is
-	 * "perimeterSpacing".
-	 */
-	STYLE_PERIMETER_SPACING: 'perimeterSpacing',
-
-	/**
-	 * Variable: STYLE_SPACING
-	 *
-	 * Defines the key for the spacing. The value represents the spacing, in
-	 * pixels, added to each side of a label in a vertex (style applies to
-	 * vertices only). Value is "spacing".
-	 */
-	STYLE_SPACING: 'spacing',
-
-	/**
-	 * Variable: STYLE_SPACING_TOP
-	 *
-	 * Defines the key for the spacingTop style. The value represents the
-	 * spacing, in pixels, added to the top side of a label in a vertex (style
-	 * applies to vertices only). Value is "spacingTop".
-	 */
-	STYLE_SPACING_TOP: 'spacingTop',
-
-	/**
-	 * Variable: STYLE_SPACING_LEFT
-	 *
-	 * Defines the key for the spacingLeft style. The value represents the
-	 * spacing, in pixels, added to the left side of a label in a vertex (style
-	 * applies to vertices only). Value is "spacingLeft".
-	 */
-	STYLE_SPACING_LEFT: 'spacingLeft',
-
-	/**
-	 * Variable: STYLE_SPACING_BOTTOM
-	 *
-	 * Defines the key for the spacingBottom style The value represents the
-	 * spacing, in pixels, added to the bottom side of a label in a vertex
-	 * (style applies to vertices only). Value is "spacingBottom".
-	 */
-	STYLE_SPACING_BOTTOM: 'spacingBottom',
-
-	/**
-	 * Variable: STYLE_SPACING_RIGHT
-	 *
-	 * Defines the key for the spacingRight style The value represents the
-	 * spacing, in pixels, added to the right side of a label in a vertex (style
-	 * applies to vertices only). Value is "spacingRight".
-	 */
-	STYLE_SPACING_RIGHT: 'spacingRight',
-
-	/**
-	 * Variable: STYLE_HORIZONTAL
-	 *
-	 * Defines the key for the horizontal style. Possible values are
-	 * true or false. This value only applies to vertices. If the 
-	 * is "SHAPE_SWIMLANE" a value of false indicates that the
-	 * swimlane should be drawn vertically, true indicates to draw it
-	 * horizontally. If the shape style does not indicate that this vertex is a
-	 * swimlane, this value affects only whether the label is drawn
-	 * horizontally or vertically. Value is "horizontal".
-	 */
-	STYLE_HORIZONTAL: 'horizontal',
-
-	/**
-	 * Variable: STYLE_DIRECTION
-	 *
-	 * Defines the key for the direction style. The direction style is used
-	 * to specify the direction of certain shapes (eg. ).
-	 * Possible values are  (default), ,
-	 *  and . Value is "direction".
-	 */
-	STYLE_DIRECTION: 'direction',
-
-	/**
-	 * Variable: STYLE_ANCHOR_POINT_DIRECTION
-	 *
-	 * Defines the key for the anchorPointDirection style. The defines if the
-	 * direction style should be taken into account when computing the fixed
-	 * point location for connected edges. Default is 1 (yes). Set this to 0
-	 * to ignore the direction style for fixed connection points. Value is
-	 * "anchorPointDirection".
-	 */
-	STYLE_ANCHOR_POINT_DIRECTION: 'anchorPointDirection',
-
-	/**
-	 * Variable: STYLE_ELBOW
-	 *
-	 * Defines the key for the elbow style. Possible values are
-	 *  and . Default is .
-	 * This defines how the three segment orthogonal edge style leaves its
-	 * terminal vertices. The vertical style leaves the terminal vertices at
-	 * the top and bottom sides. Value is "elbow".
-	 */
-	STYLE_ELBOW: 'elbow',
-
-	/**
-	 * Variable: STYLE_FONTCOLOR
-	 *
-	 * Defines the key for the fontColor style. Possible values are all HTML
-	 * color names or HEX codes. Value is "fontColor".
-	 */
-	STYLE_FONTCOLOR: 'fontColor',
-
-	/**
-	 * Variable: STYLE_FONTFAMILY
-	 *
-	 * Defines the key for the fontFamily style. Possible values are names such
-	 * as Arial; Dialog; Verdana; Times New Roman. The value is of type String.
-	 * Value is fontFamily.
-	 */
-	STYLE_FONTFAMILY: 'fontFamily',
-
-	/**
-	 * Variable: STYLE_FONTSIZE
-	 *
-	 * Defines the key for the fontSize style (in px). The type of the value
-	 * is int. Value is "fontSize".
-	 */
-	STYLE_FONTSIZE: 'fontSize',
-
-	/**
-	 * Variable: STYLE_FONTSTYLE
-	 *
-	 * Defines the key for the fontStyle style. Values may be any logical AND
-	 * (sum) of ,  and .
-	 * The type of the value is int. Value is "fontStyle".
-	 */
-	STYLE_FONTSTYLE: 'fontStyle',
-
-	/**
-	 * Variable: STYLE_ASPECT
-	 *
-	 * Defines the key for the aspect style. Possible values are empty or fixed.
-	 * If fixed is used then the aspect ratio of the cell will be maintained
-	 * when resizing. Default is empty. Value is "aspect".
-	 */
-	STYLE_ASPECT: 'aspect',
-
-	/**
-	 * Variable: STYLE_AUTOSIZE
-	 *
-	 * Defines the key for the autosize style. This specifies if a cell should be
-	 * resized automatically if the value has changed. Possible values are 0 or 1.
-	 * Default is 0. See . This is normally combined with
-	 *  to disable manual sizing. Value is "autosize".
-	 */
-	STYLE_AUTOSIZE: 'autosize',
-
-	/**
-	 * Variable: STYLE_FOLDABLE
-	 *
-	 * Defines the key for the foldable style. This specifies if a cell is foldable
-	 * using a folding icon. Possible values are 0 or 1. Default is 1. See
-	 * . Value is "foldable".
-	 */
-	STYLE_FOLDABLE: 'foldable',
-
-	/**
-	 * Variable: STYLE_EDITABLE
-	 *
-	 * Defines the key for the editable style. This specifies if the value of
-	 * a cell can be edited using the in-place editor. Possible values are 0 or
-	 * 1. Default is 1. See . Value is "editable".
-	 */
-	STYLE_EDITABLE: 'editable',
-
-	/**
-	 * Variable: STYLE_BACKGROUND_OUTLINE
-	 *
-	 * Defines the key for the backgroundOutline style. This specifies if a
-	 * only the background of a cell should be painted when it is highlighted.
-	 * Possible values are 0 or 1. Default is 0. Value is "backgroundOutline".
-	 */
-	STYLE_BACKGROUND_OUTLINE: 'backgroundOutline',
-
-	/**
-	 * Variable: STYLE_BENDABLE
-	 *
-	 * Defines the key for the bendable style. This specifies if the control
-	 * points of an edge can be moved. Possible values are 0 or 1. Default is
-	 * 1. See . Value is "bendable".
-	 */
-	STYLE_BENDABLE: 'bendable',
-
-	/**
-	 * Variable: STYLE_MOVABLE
-	 *
-	 * Defines the key for the movable style. This specifies if a cell can
-	 * be moved. Possible values are 0 or 1. Default is 1. See
-	 * . Value is "movable".
-	 */
-	STYLE_MOVABLE: 'movable',
-
-	/**
-	 * Variable: STYLE_RESIZABLE
-	 *
-	 * Defines the key for the resizable style. This specifies if a cell can
-	 * be resized. Possible values are 0 or 1. Default is 1. See
-	 * . Value is "resizable".
-	 */
-	STYLE_RESIZABLE: 'resizable',
-
-	/**
-	 * Variable: STYLE_RESIZE_WIDTH
-	 *
-	 * Defines the key for the resizeWidth style. This specifies if a cell's
-	 * width is resized if the parent is resized. If this is 1 then the width
-	 * will be resized even if the cell's geometry is relative. If this is 0
-	 * then the cell's width will not be resized. Default is not defined. Value
-	 * is "resizeWidth".
-	 */
-	STYLE_RESIZE_WIDTH: 'resizeWidth',
-
-	/**
-	 * Variable: STYLE_RESIZE_WIDTH
-	 *
-	 * Defines the key for the resizeHeight style. This specifies if a cell's
-	 * height if resize if the parent is resized. If this is 1 then the height
-	 * will be resized even if the cell's geometry is relative. If this is 0
-	 * then the cell's height will not be resized. Default is not defined. Value
-	 * is "resizeHeight".
-	 */
-	STYLE_RESIZE_HEIGHT: 'resizeHeight',
-
-	/**
-	 * Variable: STYLE_ROTATABLE
-	 *
-	 * Defines the key for the rotatable style. This specifies if a cell can
-	 * be rotated. Possible values are 0 or 1. Default is 1. See
-	 * . Value is "rotatable".
-	 */
-	STYLE_ROTATABLE: 'rotatable',
-
-	/**
-	 * Variable: STYLE_CLONEABLE
-	 *
-	 * Defines the key for the cloneable style. This specifies if a cell can
-	 * be cloned. Possible values are 0 or 1. Default is 1. See
-	 * . Value is "cloneable".
-	 */
-	STYLE_CLONEABLE: 'cloneable',
-
-	/**
-	 * Variable: STYLE_DELETABLE
-	 *
-	 * Defines the key for the deletable style. This specifies if a cell can be
-	 * deleted. Possible values are 0 or 1. Default is 1. See
-	 * . Value is "deletable".
-	 */
-	STYLE_DELETABLE: 'deletable',
-
-	/**
-	 * Variable: STYLE_SHAPE
-	 *
-	 * Defines the key for the shape. Possible values are all constants with
-	 * a SHAPE-prefix or any newly defined shape names. Value is "shape".
-	 */
-	STYLE_SHAPE: 'shape',
-
-	/**
-	 * Variable: STYLE_EDGE
-	 *
-	 * Defines the key for the edge style. Possible values are the functions
-	 * defined in . Value is "edgeStyle".
-	 */
-	STYLE_EDGE: 'edgeStyle',
-
-	/**
-	 * Variable: STYLE_JETTY_SIZE
-	 *
-	 * Defines the key for the jetty size in .
-	 * Default is 10. Possible values are all numeric values or "auto".
-	 * Value is "jettySize".
-	 */
-	STYLE_JETTY_SIZE: 'jettySize',
-
-	/**
-	 * Variable: STYLE_SOURCE_JETTY_SIZE
-	 *
-	 * Defines the key for the jetty size in .
-	 * Default is 10. Possible values are numeric values or "auto". This has
-	 * precedence over . Value is "sourceJettySize".
-	 */
-	STYLE_SOURCE_JETTY_SIZE: 'sourceJettySize',
-
-	/**
-	 * Variable: targetJettySize
-	 *
-	 * Defines the key for the jetty size in .
-	 * Default is 10. Possible values are numeric values or "auto". This has
-	 * precedence over . Value is "targetJettySize".
-	 */
-	STYLE_TARGET_JETTY_SIZE: 'targetJettySize',
-
-	/**
-	 * Variable: STYLE_LOOP
-	 *
-	 * Defines the key for the loop style. Possible values are the functions
-	 * defined in . Value is "loopStyle".
-	 */
-	STYLE_LOOP: 'loopStyle',
-
-	/**
-	 * Variable: STYLE_ORTHOGONAL_LOOP
-	 *
-	 * Defines the key for the orthogonal loop style. Possible values are 0 and
-	 * 1. Default is 0. Value is "orthogonalLoop". Use this style to specify
-	 * if loops should be routed using an orthogonal router. Currently, this
-	 * uses  but will be replaced with a dedicated
-	 * orthogonal loop router in later releases.
-	 */
-	STYLE_ORTHOGONAL_LOOP: 'orthogonalLoop',
-
-	/**
-	 * Variable: STYLE_ROUTING_CENTER_X
-	 *
-	 * Defines the key for the horizontal routing center. Possible values are
-	 * between -0.5 and 0.5. This is the relative offset from the center used
-	 * for connecting edges. The type of this value is numeric. Value is
-	 * "routingCenterX".
-	 */
-	STYLE_ROUTING_CENTER_X: 'routingCenterX',
-
-	/**
-	 * Variable: STYLE_ROUTING_CENTER_Y
-	 *
-	 * Defines the key for the vertical routing center. Possible values are
-	 * between -0.5 and 0.5. This is the relative offset from the center used
-	 * for connecting edges. The type of this value is numeric. Value is
-	 * "routingCenterY".
-	 */
-	STYLE_ROUTING_CENTER_Y: 'routingCenterY',
-
-	/**
-	 * Variable: FONT_BOLD
-	 *
-	 * Constant for bold fonts. Default is 1.
-	 */
-	FONT_BOLD: 1,
-
-	/**
-	 * Variable: FONT_ITALIC
-	 *
-	 * Constant for italic fonts. Default is 2.
-	 */
-	FONT_ITALIC: 2,
-
-	/**
-	 * Variable: FONT_UNDERLINE
-	 *
-	 * Constant for underlined fonts. Default is 4.
-	 */
-	FONT_UNDERLINE: 4,
-
-	/**
-	 * Variable: SHAPE_RECTANGLE
-	 *
-	 * Name under which  is registered in .
-	 * Default is rectangle.
-	 */
-	SHAPE_RECTANGLE: 'rectangle',
-
-	/**
-	 * Variable: SHAPE_ELLIPSE
-	 *
-	 * Name under which  is registered in .
-	 * Default is ellipse.
-	 */
-	SHAPE_ELLIPSE: 'ellipse',
-
-	/**
-	 * Variable: SHAPE_DOUBLE_ELLIPSE
-	 *
-	 * Name under which  is registered in .
-	 * Default is doubleEllipse.
-	 */
-	SHAPE_DOUBLE_ELLIPSE: 'doubleEllipse',
-
-	/**
-	 * Variable: SHAPE_RHOMBUS
-	 *
-	 * Name under which  is registered in .
-	 * Default is rhombus.
-	 */
-	SHAPE_RHOMBUS: 'rhombus',
-
-	/**
-	 * Variable: SHAPE_LINE
-	 *
-	 * Name under which  is registered in .
-	 * Default is line.
-	 */
-	SHAPE_LINE: 'line',
-
-	/**
-	 * Variable: SHAPE_IMAGE
-	 *
-	 * Name under which  is registered in .
-	 * Default is image.
-	 */
-	SHAPE_IMAGE: 'image',
-
-	/**
-	 * Variable: SHAPE_ARROW
-	 *
-	 * Name under which  is registered in .
-	 * Default is arrow.
-	 */
-	SHAPE_ARROW: 'arrow',
-
-	/**
-	 * Variable: SHAPE_ARROW_CONNECTOR
-	 *
-	 * Name under which  is registered in .
-	 * Default is arrowConnector.
-	 */
-	SHAPE_ARROW_CONNECTOR: 'arrowConnector',
-
-	/**
-	 * Variable: SHAPE_LABEL
-	 *
-	 * Name under which  is registered in .
-	 * Default is label.
-	 */
-	SHAPE_LABEL: 'label',
-
-	/**
-	 * Variable: SHAPE_CYLINDER
-	 *
-	 * Name under which  is registered in .
-	 * Default is cylinder.
-	 */
-	SHAPE_CYLINDER: 'cylinder',
-
-	/**
-	 * Variable: SHAPE_SWIMLANE
-	 *
-	 * Name under which  is registered in .
-	 * Default is swimlane.
-	 */
-	SHAPE_SWIMLANE: 'swimlane',
-
-	/**
-	 * Variable: SHAPE_CONNECTOR
-	 *
-	 * Name under which  is registered in .
-	 * Default is connector.
-	 */
-	SHAPE_CONNECTOR: 'connector',
-
-	/**
-	 * Variable: SHAPE_ACTOR
-	 *
-	 * Name under which  is registered in .
-	 * Default is actor.
-	 */
-	SHAPE_ACTOR: 'actor',
-
-	/**
-	 * Variable: SHAPE_CLOUD
-	 *
-	 * Name under which  is registered in .
-	 * Default is cloud.
-	 */
-	SHAPE_CLOUD: 'cloud',
-
-	/**
-	 * Variable: SHAPE_TRIANGLE
-	 *
-	 * Name under which  is registered in .
-	 * Default is triangle.
-	 */
-	SHAPE_TRIANGLE: 'triangle',
-
-	/**
-	 * Variable: SHAPE_HEXAGON
-	 *
-	 * Name under which  is registered in .
-	 * Default is hexagon.
-	 */
-	SHAPE_HEXAGON: 'hexagon',
-
-	/**
-	 * Variable: ARROW_CLASSIC
-	 *
-	 * Constant for classic arrow markers.
-	 */
-	ARROW_CLASSIC: 'classic',
-
-	/**
-	 * Variable: ARROW_CLASSIC_THIN
-	 *
-	 * Constant for thin classic arrow markers.
-	 */
-	ARROW_CLASSIC_THIN: 'classicThin',
-
-	/**
-	 * Variable: ARROW_BLOCK
-	 *
-	 * Constant for block arrow markers.
-	 */
-	ARROW_BLOCK: 'block',
-
-	/**
-	 * Variable: ARROW_BLOCK_THIN
-	 *
-	 * Constant for thin block arrow markers.
-	 */
-	ARROW_BLOCK_THIN: 'blockThin',
-
-	/**
-	 * Variable: ARROW_OPEN
-	 *
-	 * Constant for open arrow markers.
-	 */
-	ARROW_OPEN: 'open',
-
-	/**
-	 * Variable: ARROW_OPEN_THIN
-	 *
-	 * Constant for thin open arrow markers.
-	 */
-	ARROW_OPEN_THIN: 'openThin',
-
-	/**
-	 * Variable: ARROW_OVAL
-	 *
-	 * Constant for oval arrow markers.
-	 */
-	ARROW_OVAL: 'oval',
-
-	/**
-	 * Variable: ARROW_DIAMOND
-	 *
-	 * Constant for diamond arrow markers.
-	 */
-	ARROW_DIAMOND: 'diamond',
-
-	/**
-	 * Variable: ARROW_DIAMOND_THIN
-	 *
-	 * Constant for thin diamond arrow markers.
-	 */
-	ARROW_DIAMOND_THIN: 'diamondThin',
-
-	/**
-	 * Variable: ALIGN_LEFT
-	 *
-	 * Constant for left horizontal alignment. Default is left.
-	 */
-	ALIGN_LEFT: 'left',
-
-	/**
-	 * Variable: ALIGN_CENTER
-	 *
-	 * Constant for center horizontal alignment. Default is center.
-	 */
-	ALIGN_CENTER: 'center',
-
-	/**
-	 * Variable: ALIGN_RIGHT
-	 *
-	 * Constant for right horizontal alignment. Default is right.
-	 */
-	ALIGN_RIGHT: 'right',
-
-	/**
-	 * Variable: ALIGN_TOP
-	 *
-	 * Constant for top vertical alignment. Default is top.
-	 */
-	ALIGN_TOP: 'top',
-
-	/**
-	 * Variable: ALIGN_MIDDLE
-	 *
-	 * Constant for middle vertical alignment. Default is middle.
-	 */
-	ALIGN_MIDDLE: 'middle',
-
-	/**
-	 * Variable: ALIGN_BOTTOM
-	 *
-	 * Constant for bottom vertical alignment. Default is bottom.
-	 */
-	ALIGN_BOTTOM: 'bottom',
-
-	/**
-	 * Variable: DIRECTION_NORTH
-	 *
-	 * Constant for direction north. Default is north.
-	 */
-	DIRECTION_NORTH: 'north',
-
-	/**
-	 * Variable: DIRECTION_SOUTH
-	 *
-	 * Constant for direction south. Default is south.
-	 */
-	DIRECTION_SOUTH: 'south',
-
-	/**
-	 * Variable: DIRECTION_EAST
-	 *
-	 * Constant for direction east. Default is east.
-	 */
-	DIRECTION_EAST: 'east',
-
-	/**
-	 * Variable: DIRECTION_WEST
-	 *
-	 * Constant for direction west. Default is west.
-	 */
-	DIRECTION_WEST: 'west',
-
-	/**
-	 * Variable: TEXT_DIRECTION_DEFAULT
-	 *
-	 * Constant for text direction default. Default is an empty string. Use
-	 * this value to use the default text direction of the operating system.
-	 */
-	TEXT_DIRECTION_DEFAULT: '',
-
-	/**
-	 * Variable: TEXT_DIRECTION_AUTO
-	 *
-	 * Constant for text direction automatic. Default is auto. Use this value
-	 * to find the direction for a given text with .
-	 */
-	TEXT_DIRECTION_AUTO: 'auto',
-
-	/**
-	 * Variable: TEXT_DIRECTION_LTR
-	 *
-	 * Constant for text direction left to right. Default is ltr. Use this
-	 * value for left to right text direction.
-	 */
-	TEXT_DIRECTION_LTR: 'ltr',
-
-	/**
-	 * Variable: TEXT_DIRECTION_RTL
-	 *
-	 * Constant for text direction right to left. Default is rtl. Use this
-	 * value for right to left text direction.
-	 */
-	TEXT_DIRECTION_RTL: 'rtl',
-
-	/**
-	 * Variable: DIRECTION_MASK_NONE
-	 *
-	 * Constant for no direction.
-	 */
-	DIRECTION_MASK_NONE: 0,
-
-	/**
-	 * Variable: DIRECTION_MASK_WEST
-	 *
-	 * Bitwise mask for west direction.
-	 */
-	DIRECTION_MASK_WEST: 1,
-
-	/**
-	 * Variable: DIRECTION_MASK_NORTH
-	 *
-	 * Bitwise mask for north direction.
-	 */
-	DIRECTION_MASK_NORTH: 2,
-
-	/**
-	 * Variable: DIRECTION_MASK_SOUTH
-	 *
-	 * Bitwise mask for south direction.
-	 */
-	DIRECTION_MASK_SOUTH: 4,
-
-	/**
-	 * Variable: DIRECTION_MASK_EAST
-	 *
-	 * Bitwise mask for east direction.
-	 */
-	DIRECTION_MASK_EAST: 8,
-
-	/**
-	 * Variable: DIRECTION_MASK_ALL
-	 *
-	 * Bitwise mask for all directions.
-	 */
-	DIRECTION_MASK_ALL: 15,
-
-	/**
-	 * Variable: ELBOW_VERTICAL
-	 *
-	 * Constant for elbow vertical. Default is horizontal.
-	 */
-	ELBOW_VERTICAL: 'vertical',
-
-	/**
-	 * Variable: ELBOW_HORIZONTAL
-	 *
-	 * Constant for elbow horizontal. Default is horizontal.
-	 */
-	ELBOW_HORIZONTAL: 'horizontal',
-
-	/**
-	 * Variable: EDGESTYLE_ELBOW
-	 *
-	 * Name of the elbow edge style. Can be used as a string value
-	 * for the STYLE_EDGE style.
-	 */
-	EDGESTYLE_ELBOW: 'elbowEdgeStyle',
-
-	/**
-	 * Variable: EDGESTYLE_ENTITY_RELATION
-	 *
-	 * Name of the entity relation edge style. Can be used as a string value
-	 * for the STYLE_EDGE style.
-	 */
-	EDGESTYLE_ENTITY_RELATION: 'entityRelationEdgeStyle',
-
-	/**
-	 * Variable: EDGESTYLE_LOOP
-	 *
-	 * Name of the loop edge style. Can be used as a string value
-	 * for the STYLE_EDGE style.
-	 */
-	EDGESTYLE_LOOP: 'loopEdgeStyle',
-
-	/**
-	 * Variable: EDGESTYLE_SIDETOSIDE
-	 *
-	 * Name of the side to side edge style. Can be used as a string value
-	 * for the STYLE_EDGE style.
-	 */
-	EDGESTYLE_SIDETOSIDE: 'sideToSideEdgeStyle',
-
-	/**
-	 * Variable: EDGESTYLE_TOPTOBOTTOM
-	 *
-	 * Name of the top to bottom edge style. Can be used as a string value
-	 * for the STYLE_EDGE style.
-	 */
-	EDGESTYLE_TOPTOBOTTOM: 'topToBottomEdgeStyle',
-
-	/**
-	 * Variable: EDGESTYLE_ORTHOGONAL
-	 *
-	 * Name of the generic orthogonal edge style. Can be used as a string value
-	 * for the STYLE_EDGE style.
-	 */
-	EDGESTYLE_ORTHOGONAL: 'orthogonalEdgeStyle',
-
-	/**
-	 * Variable: EDGESTYLE_SEGMENT
-	 *
-	 * Name of the generic segment edge style. Can be used as a string value
-	 * for the STYLE_EDGE style.
-	 */
-	EDGESTYLE_SEGMENT: 'segmentEdgeStyle',
-
-	/**
-	 * Variable: PERIMETER_ELLIPSE
-	 *
-	 * Name of the ellipse perimeter. Can be used as a string value
-	 * for the STYLE_PERIMETER style.
-	 */
-	PERIMETER_ELLIPSE: 'ellipsePerimeter',
-
-	/**
-	 * Variable: PERIMETER_RECTANGLE
-	 *
-	 * Name of the rectangle perimeter. Can be used as a string value
-	 * for the STYLE_PERIMETER style.
-	 */
-	PERIMETER_RECTANGLE: 'rectanglePerimeter',
-
-	/**
-	 * Variable: PERIMETER_RHOMBUS
-	 *
-	 * Name of the rhombus perimeter. Can be used as a string value
-	 * for the STYLE_PERIMETER style.
-	 */
-	PERIMETER_RHOMBUS: 'rhombusPerimeter',
-
-	/**
-	 * Variable: PERIMETER_HEXAGON
-	 *
-	 * Name of the hexagon perimeter. Can be used as a string value
-	 * for the STYLE_PERIMETER style.
-	 */
-	PERIMETER_HEXAGON: 'hexagonPerimeter',
-
-	/**
-	 * Variable: PERIMETER_TRIANGLE
-	 *
-	 * Name of the triangle perimeter. Can be used as a string value
-	 * for the STYLE_PERIMETER style.
-	 */
-	PERIMETER_TRIANGLE: 'trianglePerimeter'
-
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxEventObject
- *
- * The mxEventObject is a wrapper for all properties of a single event.
- * Additionally, it also offers functions to consume the event and check if it
- * was consumed as follows:
- *
- * (code)
- * evt.consume();
- * INV: evt.isConsumed() == true
- * (end)
- *
- * Constructor: mxEventObject
- *
- * Constructs a new event object with the specified name. An optional
- * sequence of key, value pairs can be appended to define properties.
- *
- * Example:
- *
- * (code)
- * new mxEventObject("eventName", key1, val1, .., keyN, valN)
- * (end)
- */
-function mxEventObject(name)
-{
-	this.name = name;
-	this.properties = [];
-
-	for (var i = 1; i < arguments.length; i += 2)
-	{
-		if (arguments[i + 1] != null)
-		{
-			this.properties[arguments[i]] = arguments[i + 1];
-		}
-	}
-};
-
-/**
- * Variable: name
- *
- * Holds the name.
- */
-mxEventObject.prototype.name = null;
-
-/**
- * Variable: properties
- *
- * Holds the properties as an associative array.
- */
-mxEventObject.prototype.properties = null;
-
-/**
- * Variable: consumed
- *
- * Holds the consumed state. Default is false.
- */
-mxEventObject.prototype.consumed = false;
-
-/**
- * Function: getName
- *
- * Returns .
- */
-mxEventObject.prototype.getName = function()
-{
-	return this.name;
-};
-
-/**
- * Function: getProperties
- *
- * Returns .
- */
-mxEventObject.prototype.getProperties = function()
-{
-	return this.properties;
-};
-
-/**
- * Function: getProperty
- *
- * Returns the property for the given key.
- */
-mxEventObject.prototype.getProperty = function(key)
-{
-	return this.properties[key];
-};
-
-/**
- * Function: isConsumed
- *
- * Returns true if the event has been consumed.
- */
-mxEventObject.prototype.isConsumed = function()
-{
-	return this.consumed;
-};
-
-/**
- * Function: consume
- *
- * Consumes the event.
- */
-mxEventObject.prototype.consume = function()
-{
-	this.consumed = true;
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxMouseEvent
- *
- * Base class for all mouse events in mxGraph. A listener for this event should
- * implement the following methods:
- *
- * (code)
- * graph.addMouseListener(
- * {
- *   mouseDown: function(sender, evt)
- *   {
- *     mxLog.debug('mouseDown');
- *   },
- *   mouseMove: function(sender, evt)
- *   {
- *     mxLog.debug('mouseMove');
- *   },
- *   mouseUp: function(sender, evt)
- *   {
- *     mxLog.debug('mouseUp');
- *   }
- * });
- * (end)
- *
- * Constructor: mxMouseEvent
- *
- * Constructs a new event object for the given arguments.
- *
- * Parameters:
- *
- * evt - Native mouse event.
- * state - Optional  under the mouse.
- *
- */
-function mxMouseEvent(evt, state)
-{
-	this.evt = evt;
-	this.state = state;
-	this.sourceState = state;
-};
-
-/**
- * Variable: consumed
- *
- * Holds the consumed state of this event.
- */
-mxMouseEvent.prototype.consumed = false;
-
-/**
- * Variable: evt
- *
- * Holds the inner event object.
- */
-mxMouseEvent.prototype.evt = null;
-
-/**
- * Variable: graphX
- *
- * Holds the x-coordinate of the event in the graph. This value is set in
- * .
- */
-mxMouseEvent.prototype.graphX = null;
-
-/**
- * Variable: graphY
- *
- * Holds the y-coordinate of the event in the graph. This value is set in
- * .
- */
-mxMouseEvent.prototype.graphY = null;
-
-/**
- * Variable: state
- *
- * Holds the optional  associated with this event.
- */
-mxMouseEvent.prototype.state = null;
-
-/**
- * Variable: sourceState
- *
- * Holds the  that was passed to the constructor. This can be
- * different from  depending on the result of .
- */
-mxMouseEvent.prototype.sourceState = null;
-
-/**
- * Function: getEvent
- *
- * Returns .
- */
-mxMouseEvent.prototype.getEvent = function()
-{
-	return this.evt;
-};
-
-/**
- * Function: getSource
- *
- * Returns the target DOM element using  for .
- */
-mxMouseEvent.prototype.getSource = function()
-{
-	return mxEvent.getSource(this.evt);
-};
-
-/**
- * Function: isSource
- *
- * Returns true if the given  is the source of .
- */
-mxMouseEvent.prototype.isSource = function(shape)
-{
-	if (shape != null)
-	{
-		return mxUtils.isAncestorNode(shape.node, this.getSource());
-	}
-
-	return false;
-};
-
-/**
- * Function: getX
- *
- * Returns .
- */
-mxMouseEvent.prototype.getX = function()
-{
-	return mxEvent.getClientX(this.getEvent());
-};
-
-/**
- * Function: getY
- *
- * Returns .
- */
-mxMouseEvent.prototype.getY = function()
-{
-	return mxEvent.getClientY(this.getEvent());
-};
-
-/**
- * Function: getGraphX
- *
- * Returns .
- */
-mxMouseEvent.prototype.getGraphX = function()
-{
-	return this.graphX;
-};
-
-/**
- * Function: getGraphY
- *
- * Returns .
- */
-mxMouseEvent.prototype.getGraphY = function()
-{
-	return this.graphY;
-};
-
-/**
- * Function: getState
- *
- * Returns .
- */
-mxMouseEvent.prototype.getState = function()
-{
-	return this.state;
-};
-
-/**
- * Function: getCell
- *
- * Returns the  in  is not null.
- */
-mxMouseEvent.prototype.getCell = function()
-{
-	var state = this.getState();
-
-	if (state != null)
-	{
-		return state.cell;
-	}
-
-	return null;
-};
-
-/**
- * Function: isPopupTrigger
- *
- * Returns true if the event is a popup trigger.
- */
-mxMouseEvent.prototype.isPopupTrigger = function()
-{
-	return mxEvent.isPopupTrigger(this.getEvent());
-};
-
-/**
- * Function: isConsumed
- *
- * Returns .
- */
-mxMouseEvent.prototype.isConsumed = function()
-{
-	return this.consumed;
-};
-
-/**
- * Function: consume
- *
- * Sets  to true and invokes preventDefault on the native event
- * if such a method is defined. This is used mainly to avoid the cursor from
- * being changed to a text cursor in Webkit. You can use the preventDefault
- * flag to disable this functionality.
- *
- * Parameters:
- *
- * preventDefault - Specifies if the native event should be canceled. Default
- * is true.
- */
-mxMouseEvent.prototype.consume = function(preventDefault)
-{
-	preventDefault = (preventDefault != null) ? preventDefault : true;
-
-	if (preventDefault && this.evt.preventDefault)
-	{
-		this.evt.preventDefault();
-	}
-
-	// Workaround for images being dragged in IE
-	// Does not change returnValue in Opera
-	if (mxClient.IS_IE)
-	{
-		this.evt.returnValue = true;
-	}
-
-	// Sets local consumed state
-	this.consumed = true;
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxEventSource
- *
- * Base class for objects that dispatch named events. To create a subclass that
- * inherits from mxEventSource, the following code is used.
- *
- * (code)
- * function MyClass() { };
- *
- * MyClass.prototype = new mxEventSource();
- * MyClass.prototype.constructor = MyClass;
- * (end)
- *
- * Known Subclasses:
- *
- * , , , , ,
- * , 
- *
- * Constructor: mxEventSource
- *
- * Constructs a new event source.
- */
-function mxEventSource(eventSource)
-{
-	this.setEventSource(eventSource);
-};
-
-/**
- * Variable: eventListeners
- *
- * Holds the event names and associated listeners in an array. The array
- * contains the event name followed by the respective listener for each
- * registered listener.
- */
-mxEventSource.prototype.eventListeners = null;
-
-/**
- * Variable: eventsEnabled
- *
- * Specifies if events can be fired. Default is true.
- */
-mxEventSource.prototype.eventsEnabled = true;
-
-/**
- * Variable: eventSource
- *
- * Optional source for events. Default is null.
- */
-mxEventSource.prototype.eventSource = null;
-
-/**
- * Function: isEventsEnabled
- *
- * Returns .
- */
-mxEventSource.prototype.isEventsEnabled = function()
-{
-	return this.eventsEnabled;
-};
-
-/**
- * Function: setEventsEnabled
- *
- * Sets .
- */
-mxEventSource.prototype.setEventsEnabled = function(value)
-{
-	this.eventsEnabled = value;
-};
-
-/**
- * Function: getEventSource
- *
- * Returns .
- */
-mxEventSource.prototype.getEventSource = function()
-{
-	return this.eventSource;
-};
-
-/**
- * Function: setEventSource
- *
- * Sets .
- */
-mxEventSource.prototype.setEventSource = function(value)
-{
-	this.eventSource = value;
-};
-
-/**
- * Function: addListener
- *
- * Binds the specified function to the given event name. If no event name
- * is given, then the listener is registered for all events.
- *
- * The parameters of the listener are the sender and an .
- */
-mxEventSource.prototype.addListener = function(name, funct)
-{
-	if (this.eventListeners == null)
-	{
-		this.eventListeners = [];
-	}
-
-	this.eventListeners.push(name);
-	this.eventListeners.push(funct);
-};
-
-/**
- * Function: removeListener
- *
- * Removes all occurrences of the given listener from .
- */
-mxEventSource.prototype.removeListener = function(funct)
-{
-	if (this.eventListeners != null)
-	{
-		var i = 0;
-
-		while (i < this.eventListeners.length)
-		{
-			if (this.eventListeners[i+1] == funct)
-			{
-				this.eventListeners.splice(i, 2);
-			}
-			else
-			{
-				i += 2;
-			}
-		}
-	}
-};
-
-/**
- * Function: fireEvent
- *
- * Dispatches the given event to the listeners which are registered for
- * the event. The sender argument is optional. The current execution scope
- * ("this") is used for the listener invocation (see ).
- *
- * Example:
- *
- * (code)
- * fireEvent(new mxEventObject("eventName", key1, val1, .., keyN, valN))
- * (end)
- *
- * Parameters:
- *
- * evt -  that represents the event.
- * sender - Optional sender to be passed to the listener. Default value is
- * the return value of .
- */
-mxEventSource.prototype.fireEvent = function(evt, sender)
-{
-	if (this.eventListeners != null && this.isEventsEnabled())
-	{
-		if (evt == null)
-		{
-			evt = new mxEventObject();
-		}
-
-		if (sender == null)
-		{
-			sender = this.getEventSource();
-		}
-
-		if (sender == null)
-		{
-			sender = this;
-		}
-
-		var args = [sender, evt];
-
-		for (var i = 0; i < this.eventListeners.length; i += 2)
-		{
-			var listen = this.eventListeners[i];
-
-			if (listen == null || listen == evt.getName())
-			{
-				this.eventListeners[i+1].apply(this, args);
-			}
-		}
-	}
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-var mxEvent =
-{
-
-	/**
-	 * Class: mxEvent
-	 *
-	 * Cross-browser DOM event support. For internal event handling,
-	 *  and the graph event dispatch loop in  are used.
-	 *
-	 * Memory Leaks:
-	 *
-	 * Use this class for adding and removing listeners to/from DOM nodes. The
-	 *  function is provided to remove all listeners that
-	 * have been added using . The function should be invoked when
-	 * the last reference is removed in the JavaScript code, typically when the
-	 * referenced DOM node is removed from the DOM.
-	 *
-	 * Function: addListener
-	 *
-	 * Binds the function to the specified event on the given element. Use
-	 *  in order to bind the "this" keyword inside the function
-	 * to a given execution scope.
-	 */
-	addListener: function()
-	{
-		var updateListenerList = function(element, eventName, funct)
-		{
-			if (element.mxListenerList == null)
-			{
-				element.mxListenerList = [];
-			}
-
-			var entry = {name: eventName, f: funct};
-			element.mxListenerList.push(entry);
-		};
-
-		if (window.addEventListener)
-		{
-			return function(element, eventName, funct)
-			{
-				element.addEventListener(eventName, funct, false);
-				updateListenerList(element, eventName, funct);
-			};
-		}
-		else
-		{
-			return function(element, eventName, funct)
-			{
-				element.attachEvent('on' + eventName, funct);
-				updateListenerList(element, eventName, funct);
-			};
-		}
-	}(),
-
-	/**
-	 * Function: removeListener
-	 *
-	 * Removes the specified listener from the given element.
-	 */
-	removeListener: function()
-	{
-		var updateListener = function(element, eventName, funct)
-		{
-			if (element.mxListenerList != null)
-			{
-				var listenerCount = element.mxListenerList.length;
-
-				for (var i = 0; i < listenerCount; i++)
-				{
-					var entry = element.mxListenerList[i];
-
-					if (entry.f == funct)
-					{
-						element.mxListenerList.splice(i, 1);
-						break;
-					}
-				}
-
-				if (element.mxListenerList.length == 0)
-				{
-					element.mxListenerList = null;
-				}
-			}
-		};
-
-		if (window.removeEventListener)
-		{
-			return function(element, eventName, funct)
-			{
-				element.removeEventListener(eventName, funct, false);
-				updateListener(element, eventName, funct);
-			};
-		}
-		else
-		{
-			return function(element, eventName, funct)
-			{
-				element.detachEvent('on' + eventName, funct);
-				updateListener(element, eventName, funct);
-			};
-		}
-	}(),
-
-	/**
-	 * Function: removeAllListeners
-	 *
-	 * Removes all listeners from the given element.
-	 */
-	removeAllListeners: function(element)
-	{
-		var list = element.mxListenerList;
-
-		if (list != null)
-		{
-			while (list.length > 0)
-			{
-				var entry = list[0];
-				mxEvent.removeListener(element, entry.name, entry.f);
-			}
-		}
-	},
-
-	/**
-	 * Function: addGestureListeners
-	 *
-	 * Adds the given listeners for touch, mouse and/or pointer events. If
-	 *  is true then pointer events will be registered,
-	 * else the respective mouse events will be registered. If 
-	 * is false and  is true then the respective touch events
-	 * will be registered as well as the mouse events.
-	 */
-	addGestureListeners: function(node, startListener, moveListener, endListener)
-	{
-		if (startListener != null)
-		{
-			mxEvent.addListener(node, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown', startListener);
-		}
-
-		if (moveListener != null)
-		{
-			mxEvent.addListener(node, (mxClient.IS_POINTER) ? 'pointermove' : 'mousemove', moveListener);
-		}
-
-		if (endListener != null)
-		{
-			mxEvent.addListener(node, (mxClient.IS_POINTER) ? 'pointerup' : 'mouseup', endListener);
-		}
-
-		if (!mxClient.IS_POINTER && mxClient.IS_TOUCH)
-		{
-			if (startListener != null)
-			{
-				mxEvent.addListener(node, 'touchstart', startListener);
-			}
-
-			if (moveListener != null)
-			{
-				mxEvent.addListener(node, 'touchmove', moveListener);
-			}
-
-			if (endListener != null)
-			{
-				mxEvent.addListener(node, 'touchend', endListener);
-			}
-		}
-	},
-
-	/**
-	 * Function: removeGestureListeners
-	 *
-	 * Removes the given listeners from mousedown, mousemove, mouseup and the
-	 * respective touch events if  is true.
-	 */
-	removeGestureListeners: function(node, startListener, moveListener, endListener)
-	{
-		if (startListener != null)
-		{
-			mxEvent.removeListener(node, (mxClient.IS_POINTER) ? 'pointerdown' : 'mousedown', startListener);
-		}
-
-		if (moveListener != null)
-		{
-			mxEvent.removeListener(node, (mxClient.IS_POINTER) ? 'pointermove' : 'mousemove', moveListener);
-		}
-
-		if (endListener != null)
-		{
-			mxEvent.removeListener(node, (mxClient.IS_POINTER) ? 'pointerup' : 'mouseup', endListener);
-		}
-
-		if (!mxClient.IS_POINTER && mxClient.IS_TOUCH)
-		{
-			if (startListener != null)
-			{
-				mxEvent.removeListener(node, 'touchstart', startListener);
-			}
-
-			if (moveListener != null)
-			{
-				mxEvent.removeListener(node, 'touchmove', moveListener);
-			}
-
-			if (endListener != null)
-			{
-				mxEvent.removeListener(node, 'touchend', endListener);
-			}
-		}
-	},
-
-	/**
-	 * Function: redirectMouseEvents
-	 *
-	 * Redirects the mouse events from the given DOM node to the graph dispatch
-	 * loop using the event and given state as event arguments. State can
-	 * either be an instance of  or a function that returns an
-	 * . The down, move, up and dblClick arguments are optional
-	 * functions that take the trigger event as arguments and replace the
-	 * default behaviour.
-	 */
-	redirectMouseEvents: function(node, graph, state, down, move, up, dblClick)
-	{
-		var getState = function(evt)
-		{
-			return (typeof(state) == 'function') ? state(evt) : state;
-		};
-
-		mxEvent.addGestureListeners(node, function (evt)
-		{
-			if (down != null)
-			{
-				down(evt);
-			}
-			else if (!mxEvent.isConsumed(evt))
-			{
-				graph.fireMouseEvent(mxEvent.MOUSE_DOWN, new mxMouseEvent(evt, getState(evt)));
-			}
-		},
-		function (evt)
-		{
-			if (move != null)
-			{
-				move(evt);
-			}
-			else if (!mxEvent.isConsumed(evt))
-			{
-				graph.fireMouseEvent(mxEvent.MOUSE_MOVE, new mxMouseEvent(evt, getState(evt)));
-			}
-		},
-		function (evt)
-		{
-			if (up != null)
-			{
-				up(evt);
-			}
-			else if (!mxEvent.isConsumed(evt))
-			{
-				graph.fireMouseEvent(mxEvent.MOUSE_UP, new mxMouseEvent(evt, getState(evt)));
-			}
-		});
-
-		mxEvent.addListener(node, 'dblclick', function (evt)
-		{
-			if (dblClick != null)
-			{
-				dblClick(evt);
-			}
-			else if (!mxEvent.isConsumed(evt))
-			{
-				var tmp = getState(evt);
-				graph.dblClick(evt, (tmp != null) ? tmp.cell : null);
-			}
-		});
-	},
-
-	/**
-	 * Function: release
-	 *
-	 * Removes the known listeners from the given DOM node and its descendants.
-	 *
-	 * Parameters:
-	 *
-	 * element - DOM node to remove the listeners from.
-	 */
-	release: function(element)
-	{
-		if (element != null)
-		{
-			mxEvent.removeAllListeners(element);
-
-			var children = element.childNodes;
-
-			if (children != null)
-			{
-		        var childCount = children.length;
-
-		        for (var i = 0; i < childCount; i += 1)
-		        {
-		        	mxEvent.release(children[i]);
-		        }
-		    }
-		}
-	},
-
-	/**
-	 * Function: addMouseWheelListener
-	 *
-	 * Installs the given function as a handler for mouse wheel events. The
-	 * function has two arguments: the mouse event and a boolean that specifies
-	 * if the wheel was moved up or down.
-	 *
-	 * This has been tested with IE 6 and 7, Firefox (all versions), Opera and
-	 * Safari. It does currently not work on Safari for Mac.
-	 *
-	 * Example:
-	 *
-	 * (code)
-	 * mxEvent.addMouseWheelListener(function (evt, up)
-	 * {
-	 *   mxLog.show();
-	 *   mxLog.debug('mouseWheel: up='+up);
-	 * });
-	 *(end)
-	 *
-	 * Parameters:
-	 *
-	 * funct - Handler function that takes the event argument and a boolean up
-	 * argument for the mousewheel direction.
-	 */
-	addMouseWheelListener: function(funct)
-	{
-		if (funct != null)
-		{
-			var wheelHandler = function(evt)
-			{
-				// IE does not give an event object but the
-				// global event object is the mousewheel event
-				// at this point in time.
-				if (evt == null)
-				{
-					evt = window.event;
-				}
-
-				var delta = 0;
-
-				if (mxClient.IS_FF)
-				{
-					delta = -evt.detail / 2;
-				}
-				else
-				{
-					delta = evt.wheelDelta / 120;
-				}
-
-				// Handles the event using the given function
-				if (delta != 0)
-				{
-					funct(evt, delta > 0);
-				}
-			};
-
-			// Webkit has NS event API, but IE event name and details
-			if (mxClient.IS_NS && document.documentMode == null)
-			{
-				var eventName = (mxClient.IS_SF || 	mxClient.IS_GC) ? 'mousewheel' : 'DOMMouseScroll';
-				mxEvent.addListener(window, eventName, wheelHandler);
-			}
-			else
-			{
-				mxEvent.addListener(document, 'mousewheel', wheelHandler);
-			}
-		}
-	},
-
-	/**
-	 * Function: disableContextMenu
-	 *
-	 * Disables the context menu for the given element.
-	 */
-	disableContextMenu: function(element)
-	{
-		mxEvent.addListener(element, 'contextmenu', function(evt)
-		{
-			if (evt.preventDefault)
-			{
-				evt.preventDefault();
-			}
-
-			return false;
-		});
-	},
-
-	/**
-	 * Function: getSource
-	 *
-	 * Returns the event's target or srcElement depending on the browser.
-	 */
-	getSource: function(evt)
-	{
-		return (evt.srcElement != null) ? evt.srcElement : evt.target;
-	},
-
-	/**
-	 * Function: isConsumed
-	 *
-	 * Returns true if the event has been consumed using .
-	 */
-	isConsumed: function(evt)
-	{
-		return evt.isConsumed != null && evt.isConsumed;
-	},
-
-	/**
-	 * Function: isTouchEvent
-	 *
-	 * Returns true if the event was generated using a touch device (not a pen or mouse).
-	 */
-	isTouchEvent: function(evt)
-	{
-		return (evt.pointerType != null) ? (evt.pointerType == 'touch' || evt.pointerType ===
-			evt.MSPOINTER_TYPE_TOUCH) : ((evt.mozInputSource != null) ?
-					evt.mozInputSource == 5 : evt.type.indexOf('touch') == 0);
-	},
-
-	/**
-	 * Function: isPenEvent
-	 *
-	 * Returns true if the event was generated using a pen (not a touch device or mouse).
-	 */
-	isPenEvent: function(evt)
-	{
-		return (evt.pointerType != null) ? (evt.pointerType == 'pen' || evt.pointerType ===
-			evt.MSPOINTER_TYPE_PEN) : ((evt.mozInputSource != null) ?
-					evt.mozInputSource == 2 : evt.type.indexOf('pen') == 0);
-	},
-
-	/**
-	 * Function: isMultiTouchEvent
-	 *
-	 * Returns true if the event was generated using a touch device (not a pen or mouse).
-	 */
-	isMultiTouchEvent: function(evt)
-	{
-		return (evt.type != null && evt.type.indexOf('touch') == 0 && evt.touches != null && evt.touches.length > 1);
-	},
-
-	/**
-	 * Function: isMouseEvent
-	 *
-	 * Returns true if the event was generated using a mouse (not a pen or touch device).
-	 */
-	isMouseEvent: function(evt)
-	{
-		return (evt.pointerType != null) ? (evt.pointerType == 'mouse' || evt.pointerType ===
-			evt.MSPOINTER_TYPE_MOUSE) : ((evt.mozInputSource != null) ?
-				evt.mozInputSource == 1 : evt.type.indexOf('mouse') == 0);
-	},
-
-	/**
-	 * Function: isLeftMouseButton
-	 *
-	 * Returns true if the left mouse button is pressed for the given event.
-	 * To check if a button is pressed during a mouseMove you should use the
-	 *  property. Note that this returns true in Firefox
-	 * for control+left-click on the Mac.
-	 */
-	isLeftMouseButton: function(evt)
-	{
-		// Special case for mousemove and mousedown we check the buttons
-		// if it exists because which is 0 even if no button is pressed
-		if ('buttons' in evt && (evt.type == 'mousedown' || evt.type == 'mousemove'))
-		{
-			return evt.buttons == 1;
-		}
-		else if ('which' in evt)
-		{
-	        return evt.which === 1;
-	    }
-		else
-		{
-	        return evt.button === 1;
-	    }
-	},
-
-	/**
-	 * Function: isMiddleMouseButton
-	 *
-	 * Returns true if the middle mouse button is pressed for the given event.
-	 * To check if a button is pressed during a mouseMove you should use the
-	 *  property.
-	 */
-	isMiddleMouseButton: function(evt)
-	{
-		if ('which' in evt)
-		{
-	        return evt.which === 2;
-	    }
-		else
-		{
-	        return evt.button === 4;
-	    }
-	},
-
-	/**
-	 * Function: isRightMouseButton
-	 *
-	 * Returns true if the right mouse button was pressed. Note that this
-	 * button might not be available on some systems. For handling a popup
-	 * trigger  should be used.
-	 */
-	isRightMouseButton: function(evt)
-	{
-		if ('which' in evt)
-		{
-	        return evt.which === 3;
-	    }
-		else
-		{
-	        return evt.button === 2;
-	    }
-	},
-
-	/**
-	 * Function: isPopupTrigger
-	 *
-	 * Returns true if the event is a popup trigger. This implementation
-	 * returns true if the right button or the left button and control was
-	 * pressed on a Mac.
-	 */
-	isPopupTrigger: function(evt)
-	{
-		return mxEvent.isRightMouseButton(evt) || (mxClient.IS_MAC && mxEvent.isControlDown(evt) &&
-			!mxEvent.isShiftDown(evt) && !mxEvent.isMetaDown(evt) && !mxEvent.isAltDown(evt));
-	},
-
-	/**
-	 * Function: isShiftDown
-	 *
-	 * Returns true if the shift key is pressed for the given event.
-	 */
-	isShiftDown: function(evt)
-	{
-		return (evt != null) ? evt.shiftKey : false;
-	},
-
-	/**
-	 * Function: isAltDown
-	 *
-	 * Returns true if the alt key is pressed for the given event.
-	 */
-	isAltDown: function(evt)
-	{
-		return (evt != null) ? evt.altKey : false;
-	},
-
-	/**
-	 * Function: isControlDown
-	 *
-	 * Returns true if the control key is pressed for the given event.
-	 */
-	isControlDown: function(evt)
-	{
-		return (evt != null) ? evt.ctrlKey : false;
-	},
-
-	/**
-	 * Function: isMetaDown
-	 *
-	 * Returns true if the meta key is pressed for the given event.
-	 */
-	isMetaDown: function(evt)
-	{
-		return (evt != null) ? evt.metaKey : false;
-	},
-
-	/**
-	 * Function: getMainEvent
-	 *
-	 * Returns the touch or mouse event that contains the mouse coordinates.
-	 */
-	getMainEvent: function(e)
-	{
-		if ((e.type == 'touchstart' || e.type == 'touchmove') && e.touches != null && e.touches[0] != null)
-		{
-			e = e.touches[0];
-		}
-		else if (e.type == 'touchend' && e.changedTouches != null && e.changedTouches[0] != null)
-		{
-			e = e.changedTouches[0];
-		}
-
-		return e;
-	},
-
-	/**
-	 * Function: getClientX
-	 *
-	 * Returns true if the meta key is pressed for the given event.
-	 */
-	getClientX: function(e)
-	{
-		return mxEvent.getMainEvent(e).clientX;
-	},
-
-	/**
-	 * Function: getClientY
-	 *
-	 * Returns true if the meta key is pressed for the given event.
-	 */
-	getClientY: function(e)
-	{
-		return mxEvent.getMainEvent(e).clientY;
-	},
-
-	/**
-	 * Function: consume
-	 *
-	 * Consumes the given event.
-	 *
-	 * Parameters:
-	 *
-	 * evt - Native event to be consumed.
-	 * preventDefault - Optional boolean to prevent the default for the event.
-	 * Default is true.
-	 * stopPropagation - Option boolean to stop event propagation. Default is
-	 * true.
-	 */
-	consume: function(evt, preventDefault, stopPropagation)
-	{
-		preventDefault = (preventDefault != null) ? preventDefault : true;
-		stopPropagation = (stopPropagation != null) ? stopPropagation : true;
-
-		if (preventDefault)
-		{
-			if (evt.preventDefault)
-			{
-				if (stopPropagation)
-				{
-					evt.stopPropagation();
-				}
-
-				evt.preventDefault();
-			}
-			else if (stopPropagation)
-			{
-				evt.cancelBubble = true;
-			}
-		}
-
-		// Opera
-		evt.isConsumed = true;
-
-		// Other browsers
-		if (!evt.preventDefault)
-		{
-			evt.returnValue = false;
-		}
-	},
-
-	//
-	// Special handles in mouse events
-	//
-
-	/**
-	 * Variable: LABEL_HANDLE
-	 *
-	 * Index for the label handle in an mxMouseEvent. This should be a negative
-	 * value that does not interfere with any possible handle indices. Default
-	 * is -1.
-	 */
-	LABEL_HANDLE: -1,
-
-	/**
-	 * Variable: ROTATION_HANDLE
-	 *
-	 * Index for the rotation handle in an mxMouseEvent. This should be a
-	 * negative value that does not interfere with any possible handle indices.
-	 * Default is -2.
-	 */
-	ROTATION_HANDLE: -2,
-
-	/**
-	 * Variable: CUSTOM_HANDLE
-	 *
-	 * Start index for the custom handles in an mxMouseEvent. This should be a
-	 * negative value and is the start index which is decremented for each
-	 * custom handle. Default is -100.
-	 */
-	CUSTOM_HANDLE: -100,
-
-	/**
-	 * Variable: VIRTUAL_HANDLE
-	 *
-	 * Start index for the virtual handles in an mxMouseEvent. This should be a
-	 * negative value and is the start index which is decremented for each
-	 * virtual handle. Default is -100000. This assumes that there are no more
-	 * than VIRTUAL_HANDLE - CUSTOM_HANDLE custom handles.
-	 *
-	 */
-	VIRTUAL_HANDLE: -100000,
-
-	//
-	// Event names
-	//
-
-	/**
-	 * Variable: MOUSE_DOWN
-	 *
-	 * Specifies the event name for mouseDown.
-	 */
-	MOUSE_DOWN: 'mouseDown',
-
-	/**
-	 * Variable: MOUSE_MOVE
-	 *
-	 * Specifies the event name for mouseMove.
-	 */
-	MOUSE_MOVE: 'mouseMove',
-
-	/**
-	 * Variable: MOUSE_UP
-	 *
-	 * Specifies the event name for mouseUp.
-	 */
-	MOUSE_UP: 'mouseUp',
-
-	/**
-	 * Variable: ACTIVATE
-	 *
-	 * Specifies the event name for activate.
-	 */
-	ACTIVATE: 'activate',
-
-	/**
-	 * Variable: RESIZE_START
-	 *
-	 * Specifies the event name for resizeStart.
-	 */
-	RESIZE_START: 'resizeStart',
-
-	/**
-	 * Variable: RESIZE
-	 *
-	 * Specifies the event name for resize.
-	 */
-	RESIZE: 'resize',
-
-	/**
-	 * Variable: RESIZE_END
-	 *
-	 * Specifies the event name for resizeEnd.
-	 */
-	RESIZE_END: 'resizeEnd',
-
-	/**
-	 * Variable: MOVE_START
-	 *
-	 * Specifies the event name for moveStart.
-	 */
-	MOVE_START: 'moveStart',
-
-	/**
-	 * Variable: MOVE
-	 *
-	 * Specifies the event name for move.
-	 */
-	MOVE: 'move',
-
-	/**
-	 * Variable: MOVE_END
-	 *
-	 * Specifies the event name for moveEnd.
-	 */
-	MOVE_END: 'moveEnd',
-
-	/**
-	 * Variable: PAN_START
-	 *
-	 * Specifies the event name for panStart.
-	 */
-	PAN_START: 'panStart',
-
-	/**
-	 * Variable: PAN
-	 *
-	 * Specifies the event name for pan.
-	 */
-	PAN: 'pan',
-
-	/**
-	 * Variable: PAN_END
-	 *
-	 * Specifies the event name for panEnd.
-	 */
-	PAN_END: 'panEnd',
-
-	/**
-	 * Variable: MINIMIZE
-	 *
-	 * Specifies the event name for minimize.
-	 */
-	MINIMIZE: 'minimize',
-
-	/**
-	 * Variable: NORMALIZE
-	 *
-	 * Specifies the event name for normalize.
-	 */
-	NORMALIZE: 'normalize',
-
-	/**
-	 * Variable: MAXIMIZE
-	 *
-	 * Specifies the event name for maximize.
-	 */
-	MAXIMIZE: 'maximize',
-
-	/**
-	 * Variable: HIDE
-	 *
-	 * Specifies the event name for hide.
-	 */
-	HIDE: 'hide',
-
-	/**
-	 * Variable: SHOW
-	 *
-	 * Specifies the event name for show.
-	 */
-	SHOW: 'show',
-
-	/**
-	 * Variable: CLOSE
-	 *
-	 * Specifies the event name for close.
-	 */
-	CLOSE: 'close',
-
-	/**
-	 * Variable: DESTROY
-	 *
-	 * Specifies the event name for destroy.
-	 */
-	DESTROY: 'destroy',
-
-	/**
-	 * Variable: REFRESH
-	 *
-	 * Specifies the event name for refresh.
-	 */
-	REFRESH: 'refresh',
-
-	/**
-	 * Variable: SIZE
-	 *
-	 * Specifies the event name for size.
-	 */
-	SIZE: 'size',
-
-	/**
-	 * Variable: SELECT
-	 *
-	 * Specifies the event name for select.
-	 */
-	SELECT: 'select',
-
-	/**
-	 * Variable: FIRED
-	 *
-	 * Specifies the event name for fired.
-	 */
-	FIRED: 'fired',
-
-	/**
-	 * Variable: FIRE_MOUSE_EVENT
-	 *
-	 * Specifies the event name for fireMouseEvent.
-	 */
-	FIRE_MOUSE_EVENT: 'fireMouseEvent',
-
-	/**
-	 * Variable: GESTURE
-	 *
-	 * Specifies the event name for gesture.
-	 */
-	GESTURE: 'gesture',
-
-	/**
-	 * Variable: TAP_AND_HOLD
-	 *
-	 * Specifies the event name for tapAndHold.
-	 */
-	TAP_AND_HOLD: 'tapAndHold',
-
-	/**
-	 * Variable: GET
-	 *
-	 * Specifies the event name for get.
-	 */
-	GET: 'get',
-
-	/**
-	 * Variable: RECEIVE
-	 *
-	 * Specifies the event name for receive.
-	 */
-	RECEIVE: 'receive',
-
-	/**
-	 * Variable: CONNECT
-	 *
-	 * Specifies the event name for connect.
-	 */
-	CONNECT: 'connect',
-
-	/**
-	 * Variable: DISCONNECT
-	 *
-	 * Specifies the event name for disconnect.
-	 */
-	DISCONNECT: 'disconnect',
-
-	/**
-	 * Variable: SUSPEND
-	 *
-	 * Specifies the event name for suspend.
-	 */
-	SUSPEND: 'suspend',
-
-	/**
-	 * Variable: RESUME
-	 *
-	 * Specifies the event name for suspend.
-	 */
-	RESUME: 'resume',
-
-	/**
-	 * Variable: MARK
-	 *
-	 * Specifies the event name for mark.
-	 */
-	MARK: 'mark',
-
-	/**
-	 * Variable: ROOT
-	 *
-	 * Specifies the event name for root.
-	 */
-	ROOT: 'root',
-
-	/**
-	 * Variable: POST
-	 *
-	 * Specifies the event name for post.
-	 */
-	POST: 'post',
-
-	/**
-	 * Variable: OPEN
-	 *
-	 * Specifies the event name for open.
-	 */
-	OPEN: 'open',
-
-	/**
-	 * Variable: SAVE
-	 *
-	 * Specifies the event name for open.
-	 */
-	SAVE: 'save',
-
-	/**
-	 * Variable: BEFORE_ADD_VERTEX
-	 *
-	 * Specifies the event name for beforeAddVertex.
-	 */
-	BEFORE_ADD_VERTEX: 'beforeAddVertex',
-
-	/**
-	 * Variable: ADD_VERTEX
-	 *
-	 * Specifies the event name for addVertex.
-	 */
-	ADD_VERTEX: 'addVertex',
-
-	/**
-	 * Variable: AFTER_ADD_VERTEX
-	 *
-	 * Specifies the event name for afterAddVertex.
-	 */
-	AFTER_ADD_VERTEX: 'afterAddVertex',
-
-	/**
-	 * Variable: DONE
-	 *
-	 * Specifies the event name for done.
-	 */
-	DONE: 'done',
-
-	/**
-	 * Variable: EXECUTE
-	 *
-	 * Specifies the event name for execute.
-	 */
-	EXECUTE: 'execute',
-
-	/**
-	 * Variable: EXECUTED
-	 *
-	 * Specifies the event name for executed.
-	 */
-	EXECUTED: 'executed',
-
-	/**
-	 * Variable: BEGIN_UPDATE
-	 *
-	 * Specifies the event name for beginUpdate.
-	 */
-	BEGIN_UPDATE: 'beginUpdate',
-
-	/**
-	 * Variable: START_EDIT
-	 *
-	 * Specifies the event name for startEdit.
-	 */
-	START_EDIT: 'startEdit',
-
-	/**
-	 * Variable: END_UPDATE
-	 *
-	 * Specifies the event name for endUpdate.
-	 */
-	END_UPDATE: 'endUpdate',
-
-	/**
-	 * Variable: END_EDIT
-	 *
-	 * Specifies the event name for endEdit.
-	 */
-	END_EDIT: 'endEdit',
-
-	/**
-	 * Variable: BEFORE_UNDO
-	 *
-	 * Specifies the event name for beforeUndo.
-	 */
-	BEFORE_UNDO: 'beforeUndo',
-
-	/**
-	 * Variable: UNDO
-	 *
-	 * Specifies the event name for undo.
-	 */
-	UNDO: 'undo',
-
-	/**
-	 * Variable: REDO
-	 *
-	 * Specifies the event name for redo.
-	 */
-	REDO: 'redo',
-
-	/**
-	 * Variable: CHANGE
-	 *
-	 * Specifies the event name for change.
-	 */
-	CHANGE: 'change',
-
-	/**
-	 * Variable: NOTIFY
-	 *
-	 * Specifies the event name for notify.
-	 */
-	NOTIFY: 'notify',
-
-	/**
-	 * Variable: LAYOUT_CELLS
-	 *
-	 * Specifies the event name for layoutCells.
-	 */
-	LAYOUT_CELLS: 'layoutCells',
-
-	/**
-	 * Variable: CLICK
-	 *
-	 * Specifies the event name for click.
-	 */
-	CLICK: 'click',
-
-	/**
-	 * Variable: SCALE
-	 *
-	 * Specifies the event name for scale.
-	 */
-	SCALE: 'scale',
-
-	/**
-	 * Variable: TRANSLATE
-	 *
-	 * Specifies the event name for translate.
-	 */
-	TRANSLATE: 'translate',
-
-	/**
-	 * Variable: SCALE_AND_TRANSLATE
-	 *
-	 * Specifies the event name for scaleAndTranslate.
-	 */
-	SCALE_AND_TRANSLATE: 'scaleAndTranslate',
-
-	/**
-	 * Variable: UP
-	 *
-	 * Specifies the event name for up.
-	 */
-	UP: 'up',
-
-	/**
-	 * Variable: DOWN
-	 *
-	 * Specifies the event name for down.
-	 */
-	DOWN: 'down',
-
-	/**
-	 * Variable: ADD
-	 *
-	 * Specifies the event name for add.
-	 */
-	ADD: 'add',
-
-	/**
-	 * Variable: REMOVE
-	 *
-	 * Specifies the event name for remove.
-	 */
-	REMOVE: 'remove',
-
-	/**
-	 * Variable: CLEAR
-	 *
-	 * Specifies the event name for clear.
-	 */
-	CLEAR: 'clear',
-
-	/**
-	 * Variable: ADD_CELLS
-	 *
-	 * Specifies the event name for addCells.
-	 */
-	ADD_CELLS: 'addCells',
-
-	/**
-	 * Variable: CELLS_ADDED
-	 *
-	 * Specifies the event name for cellsAdded.
-	 */
-	CELLS_ADDED: 'cellsAdded',
-
-	/**
-	 * Variable: MOVE_CELLS
-	 *
-	 * Specifies the event name for moveCells.
-	 */
-	MOVE_CELLS: 'moveCells',
-
-	/**
-	 * Variable: CELLS_MOVED
-	 *
-	 * Specifies the event name for cellsMoved.
-	 */
-	CELLS_MOVED: 'cellsMoved',
-
-	/**
-	 * Variable: RESIZE_CELLS
-	 *
-	 * Specifies the event name for resizeCells.
-	 */
-	RESIZE_CELLS: 'resizeCells',
-
-	/**
-	 * Variable: CELLS_RESIZED
-	 *
-	 * Specifies the event name for cellsResized.
-	 */
-	CELLS_RESIZED: 'cellsResized',
-
-	/**
-	 * Variable: TOGGLE_CELLS
-	 *
-	 * Specifies the event name for toggleCells.
-	 */
-	TOGGLE_CELLS: 'toggleCells',
-
-	/**
-	 * Variable: CELLS_TOGGLED
-	 *
-	 * Specifies the event name for cellsToggled.
-	 */
-	CELLS_TOGGLED: 'cellsToggled',
-
-	/**
-	 * Variable: ORDER_CELLS
-	 *
-	 * Specifies the event name for orderCells.
-	 */
-	ORDER_CELLS: 'orderCells',
-
-	/**
-	 * Variable: CELLS_ORDERED
-	 *
-	 * Specifies the event name for cellsOrdered.
-	 */
-	CELLS_ORDERED: 'cellsOrdered',
-
-	/**
-	 * Variable: REMOVE_CELLS
-	 *
-	 * Specifies the event name for removeCells.
-	 */
-	REMOVE_CELLS: 'removeCells',
-
-	/**
-	 * Variable: CELLS_REMOVED
-	 *
-	 * Specifies the event name for cellsRemoved.
-	 */
-	CELLS_REMOVED: 'cellsRemoved',
-
-	/**
-	 * Variable: GROUP_CELLS
-	 *
-	 * Specifies the event name for groupCells.
-	 */
-	GROUP_CELLS: 'groupCells',
-
-	/**
-	 * Variable: UNGROUP_CELLS
-	 *
-	 * Specifies the event name for ungroupCells.
-	 */
-	UNGROUP_CELLS: 'ungroupCells',
-
-	/**
-	 * Variable: REMOVE_CELLS_FROM_PARENT
-	 *
-	 * Specifies the event name for removeCellsFromParent.
-	 */
-	REMOVE_CELLS_FROM_PARENT: 'removeCellsFromParent',
-
-	/**
-	 * Variable: FOLD_CELLS
-	 *
-	 * Specifies the event name for foldCells.
-	 */
-	FOLD_CELLS: 'foldCells',
-
-	/**
-	 * Variable: CELLS_FOLDED
-	 *
-	 * Specifies the event name for cellsFolded.
-	 */
-	CELLS_FOLDED: 'cellsFolded',
-
-	/**
-	 * Variable: ALIGN_CELLS
-	 *
-	 * Specifies the event name for alignCells.
-	 */
-	ALIGN_CELLS: 'alignCells',
-
-	/**
-	 * Variable: LABEL_CHANGED
-	 *
-	 * Specifies the event name for labelChanged.
-	 */
-	LABEL_CHANGED: 'labelChanged',
-
-	/**
-	 * Variable: CONNECT_CELL
-	 *
-	 * Specifies the event name for connectCell.
-	 */
-	CONNECT_CELL: 'connectCell',
-
-	/**
-	 * Variable: CELL_CONNECTED
-	 *
-	 * Specifies the event name for cellConnected.
-	 */
-	CELL_CONNECTED: 'cellConnected',
-
-	/**
-	 * Variable: SPLIT_EDGE
-	 *
-	 * Specifies the event name for splitEdge.
-	 */
-	SPLIT_EDGE: 'splitEdge',
-
-	/**
-	 * Variable: FLIP_EDGE
-	 *
-	 * Specifies the event name for flipEdge.
-	 */
-	FLIP_EDGE: 'flipEdge',
-
-	/**
-	 * Variable: START_EDITING
-	 *
-	 * Specifies the event name for startEditing.
-	 */
-	START_EDITING: 'startEditing',
-
-	/**
-	 * Variable: EDITING_STARTED
-	 *
-	 * Specifies the event name for editingStarted.
-	 */
-	EDITING_STARTED: 'editingStarted',
-
-	/**
-	 * Variable: EDITING_STOPPED
-	 *
-	 * Specifies the event name for editingStopped.
-	 */
-	EDITING_STOPPED: 'editingStopped',
-
-	/**
-	 * Variable: ADD_OVERLAY
-	 *
-	 * Specifies the event name for addOverlay.
-	 */
-	ADD_OVERLAY: 'addOverlay',
-
-	/**
-	 * Variable: REMOVE_OVERLAY
-	 *
-	 * Specifies the event name for removeOverlay.
-	 */
-	REMOVE_OVERLAY: 'removeOverlay',
-
-	/**
-	 * Variable: UPDATE_CELL_SIZE
-	 *
-	 * Specifies the event name for updateCellSize.
-	 */
-	UPDATE_CELL_SIZE: 'updateCellSize',
-
-	/**
-	 * Variable: ESCAPE
-	 *
-	 * Specifies the event name for escape.
-	 */
-	ESCAPE: 'escape',
-
-	/**
-	 * Variable: DOUBLE_CLICK
-	 *
-	 * Specifies the event name for doubleClick.
-	 */
-	DOUBLE_CLICK: 'doubleClick',
-
-	/**
-	 * Variable: START
-	 *
-	 * Specifies the event name for start.
-	 */
-	START: 'start',
-
-	/**
-	 * Variable: RESET
-	 *
-	 * Specifies the event name for reset.
-	 */
-	RESET: 'reset'
-
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxXmlRequest
- *
- * XML HTTP request wrapper. See also: ,  and
- * . This class provides a cross-browser abstraction for Ajax
- * requests.
- *
- * Encoding:
- *
- * For encoding parameter values, the built-in encodeURIComponent JavaScript
- * method must be used. For automatic encoding of post data in  the
- *  switch can be set to true (default). The encoding
- * will be carried out using the conte type of the page. That is, the page
- * containting the editor should contain a meta tag in the header, eg.
- * 
- *
- * Example:
- *
- * (code)
- * var onload = function(req)
- * {
- *   mxUtils.alert(req.getDocumentElement());
- * }
- *
- * var onerror = function(req)
- * {
- *   mxUtils.alert('Error');
- * }
- * new mxXmlRequest(url, 'key=value').send(onload, onerror);
- * (end)
- *
- * Sends an asynchronous POST request to the specified URL.
- *
- * Example:
- *
- * (code)
- * var req = new mxXmlRequest(url, 'key=value', 'POST', false);
- * req.send();
- * mxUtils.alert(req.getDocumentElement());
- * (end)
- *
- * Sends a synchronous POST request to the specified URL.
- *
- * Example:
- *
- * (code)
- * var encoder = new mxCodec();
- * var result = encoder.encode(graph.getModel());
- * var xml = encodeURIComponent(mxUtils.getXml(result));
- * new mxXmlRequest(url, 'xml='+xml).send();
- * (end)
- *
- * Sends an encoded graph model to the specified URL using xml as the
- * parameter name. The parameter can then be retrieved in C# as follows:
- *
- * (code)
- * string xml = HttpUtility.UrlDecode(context.Request.Params["xml"]);
- * (end)
- *
- * Or in Java as follows:
- *
- * (code)
- * String xml = URLDecoder.decode(request.getParameter("xml"), "UTF-8").replace("\n", "
");
- * (end)
- *
- * Note that the linefeeds should only be replaced if the XML is
- * processed in Java, for example when creating an image.
- *
- * Constructor: mxXmlRequest
- *
- * Constructs an XML HTTP request.
- *
- * Parameters:
- *
- * url - Target URL of the request.
- * params - Form encoded parameters to send with a POST request.
- * method - String that specifies the request method. Possible values are
- * POST and GET. Default is POST.
- * async - Boolean specifying if an asynchronous request should be used.
- * Default is true.
- * username - String specifying the username to be used for the request.
- * password - String specifying the password to be used for the request.
- */
-function mxXmlRequest(url, params, method, async, username, password)
-{
-	this.url = url;
-	this.params = params;
-	this.method = method || 'POST';
-	this.async = (async != null) ? async : true;
-	this.username = username;
-	this.password = password;
-};
-
-/**
- * Variable: url
- *
- * Holds the target URL of the request.
- */
-mxXmlRequest.prototype.url = null;
-
-/**
- * Variable: params
- *
- * Holds the form encoded data for the POST request.
- */
-mxXmlRequest.prototype.params = null;
-
-/**
- * Variable: method
- *
- * Specifies the request method. Possible values are POST and GET. Default
- * is POST.
- */
-mxXmlRequest.prototype.method = null;
-
-/**
- * Variable: async
- *
- * Boolean indicating if the request is asynchronous.
- */
-mxXmlRequest.prototype.async = null;
-
-/**
- * Variable: binary
- *
- * Boolean indicating if the request is binary. This option is ignored in IE.
- * In all other browsers the requested mime type is set to
- * text/plain; charset=x-user-defined. Default is false.
- */
-mxXmlRequest.prototype.binary = false;
-
-/**
- * Variable: withCredentials
- *
- * Specifies if withCredentials should be used in HTML5-compliant browsers. Default is
- * false.
- */
-mxXmlRequest.prototype.withCredentials = false;
-
-/**
- * Variable: username
- *
- * Specifies the username to be used for authentication.
- */
-mxXmlRequest.prototype.username = null;
-
-/**
- * Variable: password
- *
- * Specifies the password to be used for authentication.
- */
-mxXmlRequest.prototype.password = null;
-
-/**
- * Variable: request
- *
- * Holds the inner, browser-specific request object.
- */
-mxXmlRequest.prototype.request = null;
-
-/**
- * Variable: decodeSimulateValues
- *
- * Specifies if request values should be decoded as URIs before setting the
- * textarea value in . Defaults to false for backwards compatibility,
- * to avoid another decode on the server this should be set to true.
- */
-mxXmlRequest.prototype.decodeSimulateValues = false;
-
-/**
- * Function: isBinary
- *
- * Returns .
- */
-mxXmlRequest.prototype.isBinary = function()
-{
-	return this.binary;
-};
-
-/**
- * Function: setBinary
- *
- * Sets .
- */
-mxXmlRequest.prototype.setBinary = function(value)
-{
-	this.binary = value;
-};
-
-/**
- * Function: getText
- *
- * Returns the response as a string.
- */
-mxXmlRequest.prototype.getText = function()
-{
-	return this.request.responseText;
-};
-
-/**
- * Function: isReady
- *
- * Returns true if the response is ready.
- */
-mxXmlRequest.prototype.isReady = function()
-{
-	return this.request.readyState == 4;
-};
-
-/**
- * Function: getDocumentElement
- *
- * Returns the document element of the response XML document.
- */
-mxXmlRequest.prototype.getDocumentElement = function()
-{
-	var doc = this.getXml();
-
-	if (doc != null)
-	{
-		return doc.documentElement;
-	}
-
-	return null;
-};
-
-/**
- * Function: getXml
- *
- * Returns the response as an XML document. Use  to get
- * the document element of the XML document.
- */
-mxXmlRequest.prototype.getXml = function()
-{
-	var xml = this.request.responseXML;
-
-	// Handles missing response headers in IE, the first condition handles
-	// the case where responseXML is there, but using its nodes leads to
-	// type errors in the mxCellCodec when putting the nodes into a new
-	// document. This happens in IE9 standards mode and with XML user
-	// objects only, as they are used directly as values in cells.
-	if (document.documentMode >= 9 || xml == null || xml.documentElement == null)
-	{
-		xml = mxUtils.parseXml(this.request.responseText);
-	}
-
-	return xml;
-};
-
-/**
- * Function: getText
- *
- * Returns the response as a string.
- */
-mxXmlRequest.prototype.getText = function()
-{
-	return this.request.responseText;
-};
-
-/**
- * Function: getStatus
- *
- * Returns the status as a number, eg. 404 for "Not found" or 200 for "OK".
- * Note: The NS_ERROR_NOT_AVAILABLE for invalid responses cannot be cought.
- */
-mxXmlRequest.prototype.getStatus = function()
-{
-	return this.request.status;
-};
-
-/**
- * Function: create
- *
- * Creates and returns the inner  object.
- */
-mxXmlRequest.prototype.create = function()
-{
-	if (window.XMLHttpRequest)
-	{
-		return function()
-		{
-			var req = new XMLHttpRequest();
-
-			// TODO: Check for overrideMimeType required here?
-			if (this.isBinary() && req.overrideMimeType)
-			{
-				req.overrideMimeType('text/plain; charset=x-user-defined');
-			}
-
-			return req;
-		};
-	}
-	else if (typeof(ActiveXObject) != 'undefined')
-	{
-		return function()
-		{
-			// TODO: Implement binary option
-			return new ActiveXObject('Microsoft.XMLHTTP');
-		};
-	}
-}();
-
-/**
- * Function: send
- *
- * Send the  to the target URL using the specified functions to
- * process the response asychronously.
- *
- * Note: Due to technical limitations, onerror is currently ignored.
- *
- * Parameters:
- *
- * onload - Function to be invoked if a successful response was received.
- * onerror - Function to be called on any error.
- * timeout - Optional timeout in ms before calling ontimeout.
- * ontimeout - Optional function to execute on timeout.
- */
-mxXmlRequest.prototype.send = function(onload, onerror, timeout, ontimeout)
-{
-	this.request = this.create();
-
-	if (this.request != null)
-	{
-		if (onload != null)
-		{
-			this.request.onreadystatechange = mxUtils.bind(this, function()
-			{
-				if (this.isReady())
-				{
-					onload(this);
-					this.request.onreadystatechaange = null;
-				}
-			});
-		}
-
-		this.request.open(this.method, this.url, this.async,
-			this.username, this.password);
-		this.setRequestHeaders(this.request, this.params);
-
-		if (window.XMLHttpRequest && this.withCredentials)
-		{
-			this.request.withCredentials = 'true';
-		}
-
-		if (!mxClient.IS_QUIRKS && (document.documentMode == null || document.documentMode > 9) &&
-			window.XMLHttpRequest && timeout != null && ontimeout != null)
-		{
-			this.request.timeout = timeout;
-			this.request.ontimeout = ontimeout;
-		}
-
-		this.request.send(this.params);
-	}
-};
-
-/**
- * Function: setRequestHeaders
- *
- * Sets the headers for the given request and parameters. This sets the
- * content-type to application/x-www-form-urlencoded if any params exist.
- *
- * Example:
- *
- * (code)
- * request.setRequestHeaders = function(request, params)
- * {
- *   if (params != null)
- *   {
- *     request.setRequestHeader('Content-Type',
- *             'multipart/form-data');
- *     request.setRequestHeader('Content-Length',
- *             params.length);
- *   }
- * };
- * (end)
- *
- * Use the code above before calling  if you require a
- * multipart/form-data request.
- */
-mxXmlRequest.prototype.setRequestHeaders = function(request, params)
-{
-	if (params != null)
-	{
-		request.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
-	}
-};
-
-/**
- * Function: simulate
- *
- * Creates and posts a request to the given target URL using a dynamically
- * created form inside the given document.
- *
- * Parameters:
- *
- * docs - Document that contains the form element.
- * target - Target to send the form result to.
- */
-mxXmlRequest.prototype.simulate = function(doc, target)
-{
-	doc = doc || document;
-	var old = null;
-
-	if (doc == document)
-	{
-		old = window.onbeforeunload;
-		window.onbeforeunload = null;
-	}
-
-	var form = doc.createElement('form');
-	form.setAttribute('method', this.method);
-	form.setAttribute('action', this.url);
-
-	if (target != null)
-	{
-		form.setAttribute('target', target);
-	}
-
-	form.style.display = 'none';
-	form.style.visibility = 'hidden';
-
-	var pars = (this.params.indexOf('&') > 0) ?
-		this.params.split('&') :
-		this.params.split();
-
-	// Adds the parameters as textareas to the form
-	for (var i=0; i 0)
-		{
-			var name = pars[i].substring(0, pos);
-			var value = pars[i].substring(pos+1);
-
-			if (this.decodeSimulateValues)
-			{
-				value = decodeURIComponent(value);
-			}
-
-			var textarea = doc.createElement('textarea');
-			textarea.setAttribute('wrap', 'off');
-			textarea.setAttribute('name', name);
-			mxUtils.write(textarea, value);
-			form.appendChild(textarea);
-		}
-	}
-
-	doc.body.appendChild(form);
-	form.submit();
-
-	if (form.parentNode != null)
-	{
-		form.parentNode.removeChild(form);
-	}
-
-	if (old != null)
-	{
-		window.onbeforeunload = old;
-	}
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-var mxClipboard =
-{
-	/**
-	 * Class: mxClipboard
-	 *
-	 * Singleton that implements a clipboard for graph cells.
-	 *
-	 * Example:
-	 *
-	 * (code)
-	 * mxClipboard.copy(graph);
-	 * mxClipboard.paste(graph2);
-	 * (end)
-	 *
-	 * This copies the selection cells from the graph to the clipboard and
-	 * pastes them into graph2.
-	 *
-	 * For fine-grained control of the clipboard data the 
-	 * and  functions can be overridden.
-	 *
-	 * To restore previous parents for pasted cells, the implementation for
-	 *  and  can be changed as follows.
-	 *
-	 * (code)
-	 * mxClipboard.copy = function(graph, cells)
-	 * {
-	 *   cells = cells || graph.getSelectionCells();
-	 *   var result = graph.getExportableCells(cells);
-	 *
-	 *   mxClipboard.parents = new Object();
-	 *
-	 *   for (var i = 0; i < result.length; i++)
-	 *   {
-	 *     mxClipboard.parents[i] = graph.model.getParent(cells[i]);
-	 *   }
-	 *
-	 *   mxClipboard.insertCount = 1;
-	 *   mxClipboard.setCells(graph.cloneCells(result));
-	 *
-	 *   return result;
-	 * };
-	 *
-	 * mxClipboard.paste = function(graph)
-	 * {
-	 *   if (!mxClipboard.isEmpty())
-	 *   {
-	 *     var cells = graph.getImportableCells(mxClipboard.getCells());
-	 *     var delta = mxClipboard.insertCount * mxClipboard.STEPSIZE;
-	 *     var parent = graph.getDefaultParent();
-	 *
-	 *     graph.model.beginUpdate();
-	 *     try
-	 *     {
-	 *       for (var i = 0; i < cells.length; i++)
-	 *       {
-	 *         var tmp = (mxClipboard.parents != null && graph.model.contains(mxClipboard.parents[i])) ?
-	 *              mxClipboard.parents[i] : parent;
-	 *         cells[i] = graph.importCells([cells[i]], delta, delta, tmp)[0];
-	 *       }
-	 *     }
-	 *     finally
-	 *     {
-	 *       graph.model.endUpdate();
-	 *     }
-	 *
-	 *     // Increments the counter and selects the inserted cells
-	 *     mxClipboard.insertCount++;
-	 *     graph.setSelectionCells(cells);
-	 *   }
-	 * };
-	 * (end)
-	 *
-	 * Variable: STEPSIZE
-	 *
-	 * Defines the step size to offset the cells after each paste operation.
-	 * Default is 10.
-	 */
-	STEPSIZE: 10,
-
-	/**
-	 * Variable: insertCount
-	 *
-	 * Counts the number of times the clipboard data has been inserted.
-	 */
-	insertCount: 1,
-
-	/**
-	 * Variable: cells
-	 *
-	 * Holds the array of  currently in the clipboard.
-	 */
-	cells: null,
-
-	/**
-	 * Function: setCells
-	 *
-	 * Sets the cells in the clipboard. Fires a  event.
-	 */
-	setCells: function(cells)
-	{
-		mxClipboard.cells = cells;
-	},
-
-	/**
-	 * Function: getCells
-	 *
-	 * Returns  the cells in the clipboard.
-	 */
-	getCells: function()
-	{
-		return mxClipboard.cells;
-	},
-
-	/**
-	 * Function: isEmpty
-	 *
-	 * Returns true if the clipboard currently has not data stored.
-	 */
-	isEmpty: function()
-	{
-		return mxClipboard.getCells() == null;
-	},
-
-	/**
-	 * Function: cut
-	 *
-	 * Cuts the given array of  from the specified graph.
-	 * If cells is null then the selection cells of the graph will
-	 * be used. Returns the cells that have been cut from the graph.
-	 *
-	 * Parameters:
-	 *
-	 * graph -  that contains the cells to be cut.
-	 * cells - Optional array of  to be cut.
-	 */
-	cut: function(graph, cells)
-	{
-		cells = mxClipboard.copy(graph, cells);
-		mxClipboard.insertCount = 0;
-		mxClipboard.removeCells(graph, cells);
-
-		return cells;
-	},
-
-	/**
-	 * Function: removeCells
-	 *
-	 * Hook to remove the given cells from the given graph after
-	 * a cut operation.
-	 *
-	 * Parameters:
-	 *
-	 * graph -  that contains the cells to be cut.
-	 * cells - Array of  to be cut.
-	 */
-	removeCells: function(graph, cells)
-	{
-		graph.removeCells(cells);
-	},
-
-	/**
-	 * Function: copy
-	 *
-	 * Copies the given array of  from the specified
-	 * graph to . Returns the original array of cells that has
-	 * been cloned. Descendants of cells in the array are ignored.
-	 *
-	 * Parameters:
-	 *
-	 * graph -  that contains the cells to be copied.
-	 * cells - Optional array of  to be copied.
-	 */
-	copy: function(graph, cells)
-	{
-		cells = cells || graph.getSelectionCells();
-		var result = graph.getExportableCells(graph.model.getTopmostCells(cells));
-		mxClipboard.insertCount = 1;
-		mxClipboard.setCells(graph.cloneCells(result));
-
-		return result;
-	},
-
-	/**
-	 * Function: paste
-	 *
-	 * Pastes the  into the specified graph restoring
-	 * the relation to , if possible. If the parents
-	 * are no longer in the graph or invisible then the
-	 * cells are added to the graph's default or into the
-	 * swimlane under the cell's new location if one exists.
-	 * The cells are added to the graph using 
-	 * and returned.
-	 *
-	 * Parameters:
-	 *
-	 * graph -  to paste the  into.
-	 */
-	paste: function(graph)
-	{
-		var cells = null;
-
-		if (!mxClipboard.isEmpty())
-		{
-			cells = graph.getImportableCells(mxClipboard.getCells());
-			var delta = mxClipboard.insertCount * mxClipboard.STEPSIZE;
-			var parent = graph.getDefaultParent();
-			cells = graph.importCells(cells, delta, delta, parent);
-
-			// Increments the counter and selects the inserted cells
-			mxClipboard.insertCount++;
-			graph.setSelectionCells(cells);
-		}
-
-		return cells;
-	}
-
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxWindow
- *
- * Basic window inside a document.
- *
- * Examples:
- *
- * Creating a simple window.
- *
- * (code)
- * var tb = document.createElement('div');
- * var wnd = new mxWindow('Title', tb, 100, 100, 200, 200, true, true);
- * wnd.setVisible(true);
- * (end)
- *
- * Creating a window that contains an iframe.
- *
- * (code)
- * var frame = document.createElement('iframe');
- * frame.setAttribute('width', '192px');
- * frame.setAttribute('height', '172px');
- * frame.setAttribute('src', 'http://www.example.com/');
- * frame.style.backgroundColor = 'white';
- *
- * var w = document.body.clientWidth;
- * var h = (document.body.clientHeight || document.documentElement.clientHeight);
- * var wnd = new mxWindow('Title', frame, (w-200)/2, (h-200)/3, 200, 200);
- * wnd.setVisible(true);
- * (end)
- *
- * To limit the movement of a window, eg. to keep it from being moved beyond
- * the top, left corner the following method can be overridden (recommended):
- *
- * (code)
- * wnd.setLocation = function(x, y)
- * {
- *   x = Math.max(0, x);
- *   y = Math.max(0, y);
- *   mxWindow.prototype.setLocation.apply(this, arguments);
- * };
- * (end)
- *
- * Or the following event handler can be used:
- *
- * (code)
- * wnd.addListener(mxEvent.MOVE, function(e)
- * {
- *   wnd.setLocation(Math.max(0, wnd.getX()), Math.max(0, wnd.getY()));
- * });
- * (end)
- *
- * To keep a window inside the current window:
- *
- * (code)
- * mxEvent.addListener(window, 'resize', mxUtils.bind(this, function()
- * {
- *   var iw = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
- *   var ih = window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight;
- *
- *   var x = this.window.getX();
- *   var y = this.window.getY();
- *
- *   if (x + this.window.table.clientWidth > iw)
- *   {
- *     x = Math.max(0, iw - this.window.table.clientWidth);
- *   }
- *
- *   if (y + this.window.table.clientHeight > ih)
- *   {
- *     y = Math.max(0, ih - this.window.table.clientHeight);
- *   }
- *
- *   if (this.window.getX() != x || this.window.getY() != y)
- *   {
- *     this.window.setLocation(x, y);
- *   }
- * }));
- * (end)
- *
- * Event: mxEvent.MOVE_START
- *
- * Fires before the window is moved. The event property contains
- * the corresponding mouse event.
- *
- * Event: mxEvent.MOVE
- *
- * Fires while the window is being moved. The event property
- * contains the corresponding mouse event.
- *
- * Event: mxEvent.MOVE_END
- *
- * Fires after the window is moved. The event property contains
- * the corresponding mouse event.
- *
- * Event: mxEvent.RESIZE_START
- *
- * Fires before the window is resized. The event property contains
- * the corresponding mouse event.
- *
- * Event: mxEvent.RESIZE
- *
- * Fires while the window is being resized. The event property
- * contains the corresponding mouse event.
- *
- * Event: mxEvent.RESIZE_END
- *
- * Fires after the window is resized. The event property contains
- * the corresponding mouse event.
- *
- * Event: mxEvent.MAXIMIZE
- *
- * Fires after the window is maximized. The event property
- * contains the corresponding mouse event.
- *
- * Event: mxEvent.MINIMIZE
- *
- * Fires after the window is minimized. The event property
- * contains the corresponding mouse event.
- *
- * Event: mxEvent.NORMALIZE
- *
- * Fires after the window is normalized, that is, it returned from
- * maximized or minimized state. The event property contains the
- * corresponding mouse event.
- *
- * Event: mxEvent.ACTIVATE
- *
- * Fires after a window is activated. The previousWindow property
- * contains the previous window. The event sender is the active window.
- *
- * Event: mxEvent.SHOW
- *
- * Fires after the window is shown. This event has no properties.
- *
- * Event: mxEvent.HIDE
- *
- * Fires after the window is hidden. This event has no properties.
- *
- * Event: mxEvent.CLOSE
- *
- * Fires before the window is closed. The event property contains
- * the corresponding mouse event.
- *
- * Event: mxEvent.DESTROY
- *
- * Fires before the window is destroyed. This event has no properties.
- *
- * Constructor: mxWindow
- *
- * Constructs a new window with the given dimension and title to display
- * the specified content. The window elements use the given style as a
- * prefix for the classnames of the respective window elements, namely,
- * the window title and window pane. The respective postfixes are appended
- * to the given stylename as follows:
- *
- *   style - Base style for the window.
- *   style+Title - Style for the window title.
- *   style+Pane - Style for the window pane.
- *
- * The default value for style is mxWindow, resulting in the following
- * classnames for the window elements: mxWindow, mxWindowTitle and
- * mxWindowPane.
- *
- * If replaceNode is given then the window replaces the given DOM node in
- * the document.
- *
- * Parameters:
- *
- * title - String that represents the title of the new window.
- * content - DOM node that is used as the window content.
- * x - X-coordinate of the window location.
- * y - Y-coordinate of the window location.
- * width - Width of the window.
- * height - Optional height of the window. Default is to match the height
- * of the content at the specified width.
- * minimizable - Optional boolean indicating if the window is minimizable.
- * Default is true.
- * movable - Optional boolean indicating if the window is movable. Default
- * is true.
- * replaceNode - Optional DOM node that the window should replace.
- * style - Optional base classname for the window elements. Default is
- * mxWindow.
- */
-function mxWindow(title, content, x, y, width, height, minimizable, movable, replaceNode, style)
-{
-	if (content != null)
-	{
-		minimizable = (minimizable != null) ? minimizable : true;
-		this.content = content;
-		this.init(x, y, width, height, style);
-
-		this.installMaximizeHandler();
-		this.installMinimizeHandler();
-		this.installCloseHandler();
-		this.setMinimizable(minimizable);
-		this.setTitle(title);
-
-		if (movable == null || movable)
-		{
-			this.installMoveHandler();
-		}
-
-		if (replaceNode != null && replaceNode.parentNode != null)
-		{
-			replaceNode.parentNode.replaceChild(this.div, replaceNode);
-		}
-		else
-		{
-			document.body.appendChild(this.div);
-		}
-	}
-};
-
-/**
- * Extends mxEventSource.
- */
-mxWindow.prototype = new mxEventSource();
-mxWindow.prototype.constructor = mxWindow;
-
-/**
- * Variable: closeImage
- *
- * URL of the image to be used for the close icon in the titlebar.
- */
-mxWindow.prototype.closeImage = mxClient.imageBasePath + '/close.gif';
-
-/**
- * Variable: minimizeImage
- *
- * URL of the image to be used for the minimize icon in the titlebar.
- */
-mxWindow.prototype.minimizeImage = mxClient.imageBasePath + '/minimize.gif';
-
-/**
- * Variable: normalizeImage
- *
- * URL of the image to be used for the normalize icon in the titlebar.
- */
-mxWindow.prototype.normalizeImage = mxClient.imageBasePath + '/normalize.gif';
-
-/**
- * Variable: maximizeImage
- *
- * URL of the image to be used for the maximize icon in the titlebar.
- */
-mxWindow.prototype.maximizeImage = mxClient.imageBasePath + '/maximize.gif';
-
-/**
- * Variable: normalizeImage
- *
- * URL of the image to be used for the resize icon.
- */
-mxWindow.prototype.resizeImage = mxClient.imageBasePath + '/resize.gif';
-
-/**
- * Variable: visible
- *
- * Boolean flag that represents the visible state of the window.
- */
-mxWindow.prototype.visible = false;
-
-/**
- * Variable: minimumSize
- *
- *  that specifies the minimum width and height of the window.
- * Default is (50, 40).
- */
-mxWindow.prototype.minimumSize = new mxRectangle(0, 0, 50, 40);
-
-/**
- * Variable: destroyOnClose
- *
- * Specifies if the window should be destroyed when it is closed. If this
- * is false then the window is hidden using . Default is true.
- */
-mxWindow.prototype.destroyOnClose = true;
-
-/**
- * Variable: contentHeightCorrection
- *
- * Defines the correction factor for computing the height of the contentWrapper.
- * Default is 6 for IE 7/8 standards mode and 2 for all other browsers and modes.
- */
-mxWindow.prototype.contentHeightCorrection = (document.documentMode == 8 || document.documentMode == 7) ? 6 : 2;
-
-/**
- * Variable: title
- *
- * Reference to the DOM node (TD) that contains the title.
- */
-mxWindow.prototype.title = null;
-
-/**
- * Variable: content
- *
- * Reference to the DOM node that represents the window content.
- */
-mxWindow.prototype.content = null;
-
-/**
- * Function: init
- *
- * Initializes the DOM tree that represents the window.
- */
-mxWindow.prototype.init = function(x, y, width, height, style)
-{
-	style = (style != null) ? style : 'mxWindow';
-
-	this.div = document.createElement('div');
-	this.div.className = style;
-
-	this.div.style.left = x + 'px';
-	this.div.style.top = y + 'px';
-	this.table = document.createElement('table');
-	this.table.className = style;
-
-	// Disables built-in pan and zoom in IE10 and later
-	if (mxClient.IS_POINTER)
-	{
-		this.div.style.touchAction = 'none';
-	}
-
-	// Workaround for table size problems in FF
-	if (width != null)
-	{
-		if (!mxClient.IS_QUIRKS)
-		{
-			this.div.style.width = width + 'px';
-		}
-
-		this.table.style.width = width + 'px';
-	}
-
-	if (height != null)
-	{
-		if (!mxClient.IS_QUIRKS)
-		{
-			this.div.style.height = height + 'px';
-		}
-
-		this.table.style.height = height + 'px';
-	}
-
-	// Creates title row
-	var tbody = document.createElement('tbody');
-	var tr = document.createElement('tr');
-
-	this.title = document.createElement('td');
-	this.title.className = style + 'Title';
-
-	this.buttons = document.createElement('div');
-	this.buttons.style.position = 'absolute';
-	this.buttons.style.display = 'inline-block';
-	this.buttons.style.right = '4px';
-	this.buttons.style.top = '5px';
-	this.title.appendChild(this.buttons);
-
-	tr.appendChild(this.title);
-	tbody.appendChild(tr);
-
-	// Creates content row and table cell
-	tr = document.createElement('tr');
-	this.td = document.createElement('td');
-	this.td.className = style + 'Pane';
-
-	if (document.documentMode == 7)
-	{
-		this.td.style.height = '100%';
-	}
-
-	this.contentWrapper = document.createElement('div');
-	this.contentWrapper.className = style + 'Pane';
-	this.contentWrapper.style.width = '100%';
-	this.contentWrapper.appendChild(this.content);
-
-	// Workaround for div around div restricts height
-	// of inner div if outerdiv has hidden overflow
-	if (mxClient.IS_QUIRKS || this.content.nodeName.toUpperCase() != 'DIV')
-	{
-		this.contentWrapper.style.height = '100%';
-	}
-
-	// Puts all content into the DOM
-	this.td.appendChild(this.contentWrapper);
-	tr.appendChild(this.td);
-	tbody.appendChild(tr);
-	this.table.appendChild(tbody);
-	this.div.appendChild(this.table);
-
-	// Puts the window on top of other windows when clicked
-	var activator = mxUtils.bind(this, function(evt)
-	{
-		this.activate();
-	});
-
-	mxEvent.addGestureListeners(this.title, activator);
-	mxEvent.addGestureListeners(this.table, activator);
-
-	this.hide();
-};
-
-/**
- * Function: setTitle
- *
- * Sets the window title to the given string. HTML markup inside the title
- * will be escaped.
- */
-mxWindow.prototype.setTitle = function(title)
-{
-	// Removes all text content nodes (normally just one)
-	var child = this.title.firstChild;
-
-	while (child != null)
-	{
-		var next = child.nextSibling;
-
-		if (child.nodeType == mxConstants.NODETYPE_TEXT)
-		{
-			child.parentNode.removeChild(child);
-		}
-
-		child = next;
-	}
-
-	mxUtils.write(this.title, title || '');
-	this.title.appendChild(this.buttons);
-};
-
-/**
- * Function: setScrollable
- *
- * Sets if the window contents should be scrollable.
- */
-mxWindow.prototype.setScrollable = function(scrollable)
-{
-	// Workaround for hang in Presto 2.5.22 (Opera 10.5)
-	if (navigator.userAgent.indexOf('Presto/2.5') < 0)
-	{
-		if (scrollable)
-		{
-			this.contentWrapper.style.overflow = 'auto';
-		}
-		else
-		{
-			this.contentWrapper.style.overflow = 'hidden';
-		}
-	}
-};
-
-/**
- * Function: activate
- *
- * Puts the window on top of all other windows.
- */
-mxWindow.prototype.activate = function()
-{
-	if (mxWindow.activeWindow != this)
-	{
-		var style = mxUtils.getCurrentStyle(this.getElement());
-		var index = (style != null) ? style.zIndex : 3;
-
-		if (mxWindow.activeWindow)
-		{
-			var elt = mxWindow.activeWindow.getElement();
-
-			if (elt != null && elt.style != null)
-			{
-				elt.style.zIndex = index;
-			}
-		}
-
-		var previousWindow = mxWindow.activeWindow;
-		this.getElement().style.zIndex = parseInt(index) + 1;
-		mxWindow.activeWindow = this;
-
-		this.fireEvent(new mxEventObject(mxEvent.ACTIVATE, 'previousWindow', previousWindow));
-	}
-};
-
-/**
- * Function: getElement
- *
- * Returuns the outermost DOM node that makes up the window.
- */
-mxWindow.prototype.getElement = function()
-{
-	return this.div;
-};
-
-/**
- * Function: fit
- *
- * Makes sure the window is inside the client area of the window.
- */
-mxWindow.prototype.fit = function()
-{
-	mxUtils.fit(this.div);
-};
-
-/**
- * Function: isResizable
- *
- * Returns true if the window is resizable.
- */
-mxWindow.prototype.isResizable = function()
-{
-	if (this.resize != null)
-	{
-		return this.resize.style.display != 'none';
-	}
-
-	return false;
-};
-
-/**
- * Function: setResizable
- *
- * Sets if the window should be resizable. To avoid interference with some
- * built-in features of IE10 and later, the use of the following code is
- * recommended if there are resizable s in the page:
- *
- * (code)
- * if (mxClient.IS_POINTER)
- * {
- *   document.body.style.msTouchAction = 'none';
- * }
- * (end)
- */
-mxWindow.prototype.setResizable = function(resizable)
-{
-	if (resizable)
-	{
-		if (this.resize == null)
-		{
-			this.resize = document.createElement('img');
-			this.resize.style.position = 'absolute';
-			this.resize.style.bottom = '2px';
-			this.resize.style.right = '2px';
-
-			this.resize.setAttribute('src', this.resizeImage);
-			this.resize.style.cursor = 'nw-resize';
-
-			var startX = null;
-			var startY = null;
-			var width = null;
-			var height = null;
-
-			var start = mxUtils.bind(this, function(evt)
-			{
-				// LATER: pointerdown starting on border of resize does start
-				// the drag operation but does not fire consecutive events via
-				// one of the listeners below (does pan instead).
-				// Workaround: document.body.style.msTouchAction = 'none'
-				this.activate();
-				startX = mxEvent.getClientX(evt);
-				startY = mxEvent.getClientY(evt);
-				width = this.div.offsetWidth;
-				height = this.div.offsetHeight;
-
-				mxEvent.addGestureListeners(document, null, dragHandler, dropHandler);
-				this.fireEvent(new mxEventObject(mxEvent.RESIZE_START, 'event', evt));
-				mxEvent.consume(evt);
-			});
-
-			// Adds a temporary pair of listeners to intercept
-			// the gesture event in the document
-			var dragHandler = mxUtils.bind(this, function(evt)
-			{
-				if (startX != null && startY != null)
-				{
-					var dx = mxEvent.getClientX(evt) - startX;
-					var dy = mxEvent.getClientY(evt) - startY;
-
-					this.setSize(width + dx, height + dy);
-
-					this.fireEvent(new mxEventObject(mxEvent.RESIZE, 'event', evt));
-					mxEvent.consume(evt);
-				}
-			});
-
-			var dropHandler = mxUtils.bind(this, function(evt)
-			{
-				if (startX != null && startY != null)
-				{
-					startX = null;
-					startY = null;
-					mxEvent.removeGestureListeners(document, null, dragHandler, dropHandler);
-					this.fireEvent(new mxEventObject(mxEvent.RESIZE_END, 'event', evt));
-					mxEvent.consume(evt);
-				}
-			});
-
-			mxEvent.addGestureListeners(this.resize, start, dragHandler, dropHandler);
-			this.div.appendChild(this.resize);
-		}
-		else
-		{
-			this.resize.style.display = 'inline';
-		}
-	}
-	else if (this.resize != null)
-	{
-		this.resize.style.display = 'none';
-	}
-};
-
-/**
- * Function: setSize
- *
- * Sets the size of the window.
- */
-mxWindow.prototype.setSize = function(width, height)
-{
-	width = Math.max(this.minimumSize.width, width);
-	height = Math.max(this.minimumSize.height, height);
-
-	// Workaround for table size problems in FF
-	if (!mxClient.IS_QUIRKS)
-	{
-		this.div.style.width =  width + 'px';
-		this.div.style.height = height + 'px';
-	}
-
-	this.table.style.width =  width + 'px';
-	this.table.style.height = height + 'px';
-
-	if (!mxClient.IS_QUIRKS)
-	{
-		this.contentWrapper.style.height = (this.div.offsetHeight -
-			this.title.offsetHeight - this.contentHeightCorrection) + 'px';
-	}
-};
-
-/**
- * Function: setMinimizable
- *
- * Sets if the window is minimizable.
- */
-mxWindow.prototype.setMinimizable = function(minimizable)
-{
-	this.minimize.style.display = (minimizable) ? '' : 'none';
-};
-
-/**
- * Function: getMinimumSize
- *
- * Returns an  that specifies the size for the minimized window.
- * A width or height of 0 means keep the existing width or height. This
- * implementation returns the height of the window title and keeps the width.
- */
-mxWindow.prototype.getMinimumSize = function()
-{
-	return new mxRectangle(0, 0, 0, this.title.offsetHeight);
-};
-
-/**
- * Function: installMinimizeHandler
- *
- * Installs the event listeners required for minimizing the window.
- */
-mxWindow.prototype.installMinimizeHandler = function()
-{
-	this.minimize = document.createElement('img');
-
-	this.minimize.setAttribute('src', this.minimizeImage);
-	this.minimize.setAttribute('title', 'Minimize');
-	this.minimize.style.cursor = 'pointer';
-	this.minimize.style.marginLeft = '2px';
-	this.minimize.style.display = 'none';
-
-	this.buttons.appendChild(this.minimize);
-
-	var minimized = false;
-	var maxDisplay = null;
-	var height = null;
-
-	var funct = mxUtils.bind(this, function(evt)
-	{
-		this.activate();
-
-		if (!minimized)
-		{
-			minimized = true;
-
-			this.minimize.setAttribute('src', this.normalizeImage);
-			this.minimize.setAttribute('title', 'Normalize');
-			this.contentWrapper.style.display = 'none';
-			maxDisplay = this.maximize.style.display;
-
-			this.maximize.style.display = 'none';
-			height = this.table.style.height;
-
-			var minSize = this.getMinimumSize();
-
-			if (minSize.height > 0)
-			{
-				if (!mxClient.IS_QUIRKS)
-				{
-					this.div.style.height = minSize.height + 'px';
-				}
-
-				this.table.style.height = minSize.height + 'px';
-			}
-
-			if (minSize.width > 0)
-			{
-				if (!mxClient.IS_QUIRKS)
-				{
-					this.div.style.width = minSize.width + 'px';
-				}
-
-				this.table.style.width = minSize.width + 'px';
-			}
-
-			if (this.resize != null)
-			{
-				this.resize.style.visibility = 'hidden';
-			}
-
-			this.fireEvent(new mxEventObject(mxEvent.MINIMIZE, 'event', evt));
-		}
-		else
-		{
-			minimized = false;
-
-			this.minimize.setAttribute('src', this.minimizeImage);
-			this.minimize.setAttribute('title', 'Minimize');
-			this.contentWrapper.style.display = ''; // default
-			this.maximize.style.display = maxDisplay;
-
-			if (!mxClient.IS_QUIRKS)
-			{
-				this.div.style.height = height;
-			}
-
-			this.table.style.height = height;
-
-			if (this.resize != null)
-			{
-				this.resize.style.visibility = '';
-			}
-
-			this.fireEvent(new mxEventObject(mxEvent.NORMALIZE, 'event', evt));
-		}
-
-		mxEvent.consume(evt);
-	});
-
-	mxEvent.addGestureListeners(this.minimize, funct);
-};
-
-/**
- * Function: setMaximizable
- *
- * Sets if the window is maximizable.
- */
-mxWindow.prototype.setMaximizable = function(maximizable)
-{
-	this.maximize.style.display = (maximizable) ? '' : 'none';
-};
-
-/**
- * Function: installMaximizeHandler
- *
- * Installs the event listeners required for maximizing the window.
- */
-mxWindow.prototype.installMaximizeHandler = function()
-{
-	this.maximize = document.createElement('img');
-
-	this.maximize.setAttribute('src', this.maximizeImage);
-	this.maximize.setAttribute('title', 'Maximize');
-	this.maximize.style.cursor = 'default';
-	this.maximize.style.marginLeft = '2px';
-	this.maximize.style.cursor = 'pointer';
-	this.maximize.style.display = 'none';
-
-	this.buttons.appendChild(this.maximize);
-
-	var maximized = false;
-	var x = null;
-	var y = null;
-	var height = null;
-	var width = null;
-	var minDisplay = null;
-
-	var funct = mxUtils.bind(this, function(evt)
-	{
-		this.activate();
-
-		if (this.maximize.style.display != 'none')
-		{
-			if (!maximized)
-			{
-				maximized = true;
-
-				this.maximize.setAttribute('src', this.normalizeImage);
-				this.maximize.setAttribute('title', 'Normalize');
-				this.contentWrapper.style.display = '';
-				minDisplay = this.minimize.style.display;
-				this.minimize.style.display = 'none';
-
-				// Saves window state
-				x = parseInt(this.div.style.left);
-				y = parseInt(this.div.style.top);
-				height = this.table.style.height;
-				width = this.table.style.width;
-
-				this.div.style.left = '0px';
-				this.div.style.top = '0px';
-				var docHeight = Math.max(document.body.clientHeight || 0, document.documentElement.clientHeight || 0);
-
-				if (!mxClient.IS_QUIRKS)
-				{
-					this.div.style.width = (document.body.clientWidth - 2) + 'px';
-					this.div.style.height = (docHeight - 2) + 'px';
-				}
-
-				this.table.style.width = (document.body.clientWidth - 2) + 'px';
-				this.table.style.height = (docHeight - 2) + 'px';
-
-				if (this.resize != null)
-				{
-					this.resize.style.visibility = 'hidden';
-				}
-
-				if (!mxClient.IS_QUIRKS)
-				{
-					var style = mxUtils.getCurrentStyle(this.contentWrapper);
-
-					if (style.overflow == 'auto' || this.resize != null)
-					{
-						this.contentWrapper.style.height = (this.div.offsetHeight -
-							this.title.offsetHeight - this.contentHeightCorrection) + 'px';
-					}
-				}
-
-				this.fireEvent(new mxEventObject(mxEvent.MAXIMIZE, 'event', evt));
-			}
-			else
-			{
-				maximized = false;
-
-				this.maximize.setAttribute('src', this.maximizeImage);
-				this.maximize.setAttribute('title', 'Maximize');
-				this.contentWrapper.style.display = '';
-				this.minimize.style.display = minDisplay;
-
-				// Restores window state
-				this.div.style.left = x+'px';
-				this.div.style.top = y+'px';
-
-				if (!mxClient.IS_QUIRKS)
-				{
-					this.div.style.height = height;
-					this.div.style.width = width;
-
-					var style = mxUtils.getCurrentStyle(this.contentWrapper);
-
-					if (style.overflow == 'auto' || this.resize != null)
-					{
-						this.contentWrapper.style.height = (this.div.offsetHeight -
-							this.title.offsetHeight - this.contentHeightCorrection) + 'px';
-					}
-				}
-
-				this.table.style.height = height;
-				this.table.style.width = width;
-
-				if (this.resize != null)
-				{
-					this.resize.style.visibility = '';
-				}
-
-				this.fireEvent(new mxEventObject(mxEvent.NORMALIZE, 'event', evt));
-			}
-
-			mxEvent.consume(evt);
-		}
-	});
-
-	mxEvent.addGestureListeners(this.maximize, funct);
-	mxEvent.addListener(this.title, 'dblclick', funct);
-};
-
-/**
- * Function: installMoveHandler
- *
- * Installs the event listeners required for moving the window.
- */
-mxWindow.prototype.installMoveHandler = function()
-{
-	this.title.style.cursor = 'move';
-
-	mxEvent.addGestureListeners(this.title,
-		mxUtils.bind(this, function(evt)
-		{
-			var startX = mxEvent.getClientX(evt);
-			var startY = mxEvent.getClientY(evt);
-			var x = this.getX();
-			var y = this.getY();
-
-			// Adds a temporary pair of listeners to intercept
-			// the gesture event in the document
-			var dragHandler = mxUtils.bind(this, function(evt)
-			{
-				var dx = mxEvent.getClientX(evt) - startX;
-				var dy = mxEvent.getClientY(evt) - startY;
-				this.setLocation(x + dx, y + dy);
-				this.fireEvent(new mxEventObject(mxEvent.MOVE, 'event', evt));
-				mxEvent.consume(evt);
-			});
-
-			var dropHandler = mxUtils.bind(this, function(evt)
-			{
-				mxEvent.removeGestureListeners(document, null, dragHandler, dropHandler);
-				this.fireEvent(new mxEventObject(mxEvent.MOVE_END, 'event', evt));
-				mxEvent.consume(evt);
-			});
-
-			mxEvent.addGestureListeners(document, null, dragHandler, dropHandler);
-			this.fireEvent(new mxEventObject(mxEvent.MOVE_START, 'event', evt));
-			mxEvent.consume(evt);
-		}));
-
-	// Disables built-in pan and zoom in IE10 and later
-	if (mxClient.IS_POINTER)
-	{
-		this.title.style.touchAction = 'none';
-	}
-};
-
-/**
- * Function: setLocation
- *
- * Sets the upper, left corner of the window.
- */
- mxWindow.prototype.setLocation = function(x, y)
- {
-	this.div.style.left = x + 'px';
-	this.div.style.top = y + 'px';
- };
-
-/**
- * Function: getX
- *
- * Returns the current position on the x-axis.
- */
-mxWindow.prototype.getX = function()
-{
-	return parseInt(this.div.style.left);
-};
-
-/**
- * Function: getY
- *
- * Returns the current position on the y-axis.
- */
-mxWindow.prototype.getY = function()
-{
-	return parseInt(this.div.style.top);
-};
-
-/**
- * Function: installCloseHandler
- *
- * Adds the  as a new image node in  and installs the
- *  event.
- */
-mxWindow.prototype.installCloseHandler = function()
-{
-	this.closeImg = document.createElement('img');
-
-	this.closeImg.setAttribute('src', this.closeImage);
-	this.closeImg.setAttribute('title', 'Close');
-	this.closeImg.style.marginLeft = '2px';
-	this.closeImg.style.cursor = 'pointer';
-	this.closeImg.style.display = 'none';
-
-	this.buttons.appendChild(this.closeImg);
-
-	mxEvent.addGestureListeners(this.closeImg,
-		mxUtils.bind(this, function(evt)
-		{
-			this.fireEvent(new mxEventObject(mxEvent.CLOSE, 'event', evt));
-
-			if (this.destroyOnClose)
-			{
-				this.destroy();
-			}
-			else
-			{
-				this.setVisible(false);
-			}
-
-			mxEvent.consume(evt);
-		}));
-};
-
-/**
- * Function: setImage
- *
- * Sets the image associated with the window.
- *
- * Parameters:
- *
- * image - URL of the image to be used.
- */
-mxWindow.prototype.setImage = function(image)
-{
-	this.image = document.createElement('img');
-	this.image.setAttribute('src', image);
-	this.image.setAttribute('align', 'left');
-	this.image.style.marginRight = '4px';
-	this.image.style.marginLeft = '0px';
-	this.image.style.marginTop = '-2px';
-
-	this.title.insertBefore(this.image, this.title.firstChild);
-};
-
-/**
- * Function: setClosable
- *
- * Sets the image associated with the window.
- *
- * Parameters:
- *
- * closable - Boolean specifying if the window should be closable.
- */
-mxWindow.prototype.setClosable = function(closable)
-{
-	this.closeImg.style.display = (closable) ? '' : 'none';
-};
-
-/**
- * Function: isVisible
- *
- * Returns true if the window is visible.
- */
-mxWindow.prototype.isVisible = function()
-{
-	if (this.div != null)
-	{
-		return this.div.style.display != 'none';
-	}
-
-	return false;
-};
-
-/**
- * Function: setVisible
- *
- * Shows or hides the window depending on the given flag.
- *
- * Parameters:
- *
- * visible - Boolean indicating if the window should be made visible.
- */
-mxWindow.prototype.setVisible = function(visible)
-{
-	if (this.div != null && this.isVisible() != visible)
-	{
-		if (visible)
-		{
-			this.show();
-		}
-		else
-		{
-			this.hide();
-		}
-	}
-};
-
-/**
- * Function: show
- *
- * Shows the window.
- */
-mxWindow.prototype.show = function()
-{
-	this.div.style.display = '';
-	this.activate();
-
-	var style = mxUtils.getCurrentStyle(this.contentWrapper);
-
-	if (!mxClient.IS_QUIRKS && (style.overflow == 'auto' || this.resize != null) &&
-		this.contentWrapper.style.display != 'none')
-	{
-		this.contentWrapper.style.height = (this.div.offsetHeight -
-				this.title.offsetHeight - this.contentHeightCorrection) + 'px';
-	}
-
-	this.fireEvent(new mxEventObject(mxEvent.SHOW));
-};
-
-/**
- * Function: hide
- *
- * Hides the window.
- */
-mxWindow.prototype.hide = function()
-{
-	this.div.style.display = 'none';
-	this.fireEvent(new mxEventObject(mxEvent.HIDE));
-};
-
-/**
- * Function: destroy
- *
- * Destroys the window and removes all associated resources. Fires a
- *  event prior to destroying the window.
- */
-mxWindow.prototype.destroy = function()
-{
-	this.fireEvent(new mxEventObject(mxEvent.DESTROY));
-
-	if (this.div != null)
-	{
-		mxEvent.release(this.div);
-		this.div.parentNode.removeChild(this.div);
-		this.div = null;
-	}
-
-	this.title = null;
-	this.content = null;
-	this.contentWrapper = null;
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxForm
- *
- * A simple class for creating HTML forms.
- *
- * Constructor: mxForm
- *
- * Creates a HTML table using the specified classname.
- */
-function mxForm(className)
-{
-	this.table = document.createElement('table');
-	this.table.className = className;
-	this.body = document.createElement('tbody');
-
-	this.table.appendChild(this.body);
-};
-
-/**
- * Variable: table
- *
- * Holds the DOM node that represents the table.
- */
-mxForm.prototype.table = null;
-
-/**
- * Variable: body
- *
- * Holds the DOM node that represents the tbody (table body). New rows
- * can be added to this object using DOM API.
- */
-mxForm.prototype.body = false;
-
-/**
- * Function: getTable
- *
- * Returns the table that contains this form.
- */
-mxForm.prototype.getTable = function()
-{
-	return this.table;
-};
-
-/**
- * Function: addButtons
- *
- * Helper method to add an OK and Cancel button using the respective
- * functions.
- */
-mxForm.prototype.addButtons = function(okFunct, cancelFunct)
-{
-	var tr = document.createElement('tr');
-	var td = document.createElement('td');
-	tr.appendChild(td);
-	td = document.createElement('td');
-
-	// Adds the ok button
-	var button = document.createElement('button');
-	mxUtils.write(button, mxResources.get('ok') || 'OK');
-	td.appendChild(button);
-
-	mxEvent.addListener(button, 'click', function()
-	{
-		okFunct();
-	});
-
-	// Adds the cancel button
-	button = document.createElement('button');
-	mxUtils.write(button, mxResources.get('cancel') || 'Cancel');
-	td.appendChild(button);
-
-	mxEvent.addListener(button, 'click', function()
-	{
-		cancelFunct();
-	});
-
-	tr.appendChild(td);
-	this.body.appendChild(tr);
-};
-
-/**
- * Function: addText
- *
- * Adds an input for the given name, type and value and returns it.
- */
-mxForm.prototype.addText = function(name, value, type)
-{
-	var input = document.createElement('input');
-
-	input.setAttribute('type', type || 'text');
-	input.value = value;
-
-	return this.addField(name, input);
-};
-
-/**
- * Function: addCheckbox
- *
- * Adds a checkbox for the given name and value and returns the textfield.
- */
-mxForm.prototype.addCheckbox = function(name, value)
-{
-	var input = document.createElement('input');
-
-	input.setAttribute('type', 'checkbox');
-	this.addField(name, input);
-
-	// IE can only change the checked value if the input is inside the DOM
-	if (value)
-	{
-		input.checked = true;
-	}
-
-	return input;
-};
-
-/**
- * Function: addTextarea
- *
- * Adds a textarea for the given name and value and returns the textarea.
- */
-mxForm.prototype.addTextarea = function(name, value, rows)
-{
-	var input = document.createElement('textarea');
-
-	if (mxClient.IS_NS)
-	{
-		rows--;
-	}
-
-	input.setAttribute('rows', rows || 2);
-	input.value = value;
-
-	return this.addField(name, input);
-};
-
-/**
- * Function: addCombo
- *
- * Adds a combo for the given name and returns the combo.
- */
-mxForm.prototype.addCombo = function(name, isMultiSelect, size)
-{
-	var select = document.createElement('select');
-
-	if (size != null)
-	{
-		select.setAttribute('size', size);
-	}
-
-	if (isMultiSelect)
-	{
-		select.setAttribute('multiple', 'true');
-	}
-
-	return this.addField(name, select);
-};
-
-/**
- * Function: addOption
- *
- * Adds an option for the given label to the specified combo.
- */
-mxForm.prototype.addOption = function(combo, label, value, isSelected)
-{
-	var option = document.createElement('option');
-
-	mxUtils.writeln(option, label);
-	option.setAttribute('value', value);
-
-	if (isSelected)
-	{
-		option.setAttribute('selected', isSelected);
-	}
-
-	combo.appendChild(option);
-};
-
-/**
- * Function: addField
- *
- * Adds a new row with the name and the input field in two columns and
- * returns the given input.
- */
-mxForm.prototype.addField = function(name, input)
-{
-	var tr = document.createElement('tr');
-	var td = document.createElement('td');
-	mxUtils.write(td, name);
-	tr.appendChild(td);
-
-	td = document.createElement('td');
-	td.appendChild(input);
-	tr.appendChild(td);
-	this.body.appendChild(tr);
-
-	return input;
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxImage
- *
- * Encapsulates the URL, width and height of an image.
- *
- * Constructor: mxImage
- *
- * Constructs a new image.
- */
-function mxImage(src, width, height)
-{
-	this.src = src;
-	this.width = width;
-	this.height = height;
-};
-
-/**
- * Variable: src
- *
- * String that specifies the URL of the image.
- */
-mxImage.prototype.src = null;
-
-/**
- * Variable: width
- *
- * Integer that specifies the width of the image.
- */
-mxImage.prototype.width = null;
-
-/**
- * Variable: height
- *
- * Integer that specifies the height of the image.
- */
-mxImage.prototype.height = null;
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxDivResizer
- *
- * Maintains the size of a div element in Internet Explorer. This is a
- * workaround for the right and bottom style being ignored in IE.
- *
- * If you need a div to cover the scrollwidth and -height of a document,
- * then you can use this class as follows:
- *
- * (code)
- * var resizer = new mxDivResizer(background);
- * resizer.getDocumentHeight = function()
- * {
- *   return document.body.scrollHeight;
- * }
- * resizer.getDocumentWidth = function()
- * {
- *   return document.body.scrollWidth;
- * }
- * resizer.resize();
- * (end)
- *
- * Constructor: mxDivResizer
- *
- * Constructs an object that maintains the size of a div
- * element when the window is being resized. This is only
- * required for Internet Explorer as it ignores the respective
- * stylesheet information for DIV elements.
- *
- * Parameters:
- *
- * div - Reference to the DOM node whose size should be maintained.
- * container - Optional Container that contains the div. Default is the
- * window.
- */
-function mxDivResizer(div, container)
-{
-	if (div.nodeName.toLowerCase() == 'div')
-	{
-		if (container == null)
-		{
-			container = window;
-		}
-
-		this.div = div;
-		var style = mxUtils.getCurrentStyle(div);
-
-		if (style != null)
-		{
-			this.resizeWidth = style.width == 'auto';
-			this.resizeHeight = style.height == 'auto';
-		}
-
-		mxEvent.addListener(container, 'resize',
-			mxUtils.bind(this, function(evt)
-			{
-				if (!this.handlingResize)
-				{
-					this.handlingResize = true;
-					this.resize();
-					this.handlingResize = false;
-				}
-			})
-		);
-
-		this.resize();
-	}
-};
-
-/**
- * Function: resizeWidth
- *
- * Boolean specifying if the width should be updated.
- */
-mxDivResizer.prototype.resizeWidth = true;
-
-/**
- * Function: resizeHeight
- *
- * Boolean specifying if the height should be updated.
- */
-mxDivResizer.prototype.resizeHeight = true;
-
-/**
- * Function: handlingResize
- *
- * Boolean specifying if the width should be updated.
- */
-mxDivResizer.prototype.handlingResize = false;
-
-/**
- * Function: resize
- *
- * Updates the style of the DIV after the window has been resized.
- */
-mxDivResizer.prototype.resize = function()
-{
-	var w = this.getDocumentWidth();
-	var h = this.getDocumentHeight();
-
-	var l = parseInt(this.div.style.left);
-	var r = parseInt(this.div.style.right);
-	var t = parseInt(this.div.style.top);
-	var b = parseInt(this.div.style.bottom);
-
-	if (this.resizeWidth &&
-		!isNaN(l) &&
-		!isNaN(r) &&
-		l >= 0 &&
-		r >= 0 &&
-		w - r - l > 0)
-	{
-		this.div.style.width = (w - r - l)+'px';
-	}
-
-	if (this.resizeHeight &&
-		!isNaN(t) &&
-		!isNaN(b) &&
-		t >= 0 &&
-		b >= 0 &&
-		h - t - b > 0)
-	{
-		this.div.style.height = (h - t - b)+'px';
-	}
-};
-
-/**
- * Function: getDocumentWidth
- *
- * Hook for subclassers to return the width of the document (without
- * scrollbars).
- */
-mxDivResizer.prototype.getDocumentWidth = function()
-{
-	return document.body.clientWidth;
-};
-
-/**
- * Function: getDocumentHeight
- *
- * Hook for subclassers to return the height of the document (without
- * scrollbars).
- */
-mxDivResizer.prototype.getDocumentHeight = function()
-{
-	return document.body.clientHeight;
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxDragSource
- *
- * Wrapper to create a drag source from a DOM element so that the element can
- * be dragged over a graph and dropped into the graph as a new cell.
- *
- * Problem is that in the dropHandler the current preview location is not
- * available, so the preview and the dropHandler must match.
- *
- * Constructor: mxDragSource
- *
- * Constructs a new drag source for the given element.
- */
-function mxDragSource(element, dropHandler)
-{
-	this.element = element;
-	this.dropHandler = dropHandler;
-
-	// Handles a drag gesture on the element
-	mxEvent.addGestureListeners(element, mxUtils.bind(this, function(evt)
-	{
-		this.mouseDown(evt);
-	}));
-
-	// Prevents native drag and drop
-	mxEvent.addListener(element, 'dragstart', function(evt)
-	{
-		mxEvent.consume(evt);
-	});
-
-	this.eventConsumer = function(sender, evt)
-	{
-		var evtName = evt.getProperty('eventName');
-		var me = evt.getProperty('event');
-
-		if (evtName != mxEvent.MOUSE_DOWN)
-		{
-			me.consume();
-		}
-	};
-};
-
-/**
- * Variable: element
- *
- * Reference to the DOM node which was made draggable.
- */
-mxDragSource.prototype.element = null;
-
-/**
- * Variable: dropHandler
- *
- * Holds the DOM node that is used to represent the drag preview. If this is
- * null then the source element will be cloned and used for the drag preview.
- */
-mxDragSource.prototype.dropHandler = null;
-
-/**
- * Variable: dragOffset
- *
- *  that specifies the offset of the . Default is null.
- */
-mxDragSource.prototype.dragOffset = null;
-
-/**
- * Variable: dragElement
- *
- * Holds the DOM node that is used to represent the drag preview. If this is
- * null then the source element will be cloned and used for the drag preview.
- */
-mxDragSource.prototype.dragElement = null;
-
-/**
- * Variable: previewElement
- *
- * Optional  that specifies the unscaled size of the preview.
- */
-mxDragSource.prototype.previewElement = null;
-
-/**
- * Variable: enabled
- *
- * Specifies if this drag source is enabled. Default is true.
- */
-mxDragSource.prototype.enabled = true;
-
-/**
- * Variable: currentGraph
- *
- * Reference to the  that is the current drop target.
- */
-mxDragSource.prototype.currentGraph = null;
-
-/**
- * Variable: currentDropTarget
- *
- * Holds the current drop target under the mouse.
- */
-mxDragSource.prototype.currentDropTarget = null;
-
-/**
- * Variable: currentPoint
- *
- * Holds the current drop location.
- */
-mxDragSource.prototype.currentPoint = null;
-
-/**
- * Variable: currentGuide
- *
- * Holds an  for the  if  is not null.
- */
-mxDragSource.prototype.currentGuide = null;
-
-/**
- * Variable: currentGuide
- *
- * Holds an  for the  if  is not null.
- */
-mxDragSource.prototype.currentHighlight = null;
-
-/**
- * Variable: autoscroll
- *
- * Specifies if the graph should scroll automatically. Default is true.
- */
-mxDragSource.prototype.autoscroll = true;
-
-/**
- * Variable: guidesEnabled
- *
- * Specifies if  should be enabled. Default is true.
- */
-mxDragSource.prototype.guidesEnabled = true;
-
-/**
- * Variable: gridEnabled
- *
- * Specifies if the grid should be allowed. Default is true.
- */
-mxDragSource.prototype.gridEnabled = true;
-
-/**
- * Variable: highlightDropTargets
- *
- * Specifies if drop targets should be highlighted. Default is true.
- */
-mxDragSource.prototype.highlightDropTargets = true;
-
-/**
- * Variable: dragElementZIndex
- *
- * ZIndex for the drag element. Default is 100.
- */
-mxDragSource.prototype.dragElementZIndex = 100;
-
-/**
- * Variable: dragElementOpacity
- *
- * Opacity of the drag element in %. Default is 70.
- */
-mxDragSource.prototype.dragElementOpacity = 70;
-
-/**
- * Variable: checkEventSource
- *
- * Whether the event source should be checked in . Default
- * is true.
- */
-mxDragSource.prototype.checkEventSource = true;
-
-/**
- * Function: isEnabled
- *
- * Returns .
- */
-mxDragSource.prototype.isEnabled = function()
-{
-	return this.enabled;
-};
-
-/**
- * Function: setEnabled
- *
- * Sets .
- */
-mxDragSource.prototype.setEnabled = function(value)
-{
-	this.enabled = value;
-};
-
-/**
- * Function: isGuidesEnabled
- *
- * Returns .
- */
-mxDragSource.prototype.isGuidesEnabled = function()
-{
-	return this.guidesEnabled;
-};
-
-/**
- * Function: setGuidesEnabled
- *
- * Sets .
- */
-mxDragSource.prototype.setGuidesEnabled = function(value)
-{
-	this.guidesEnabled = value;
-};
-
-/**
- * Function: isGridEnabled
- *
- * Returns .
- */
-mxDragSource.prototype.isGridEnabled = function()
-{
-	return this.gridEnabled;
-};
-
-/**
- * Function: setGridEnabled
- *
- * Sets .
- */
-mxDragSource.prototype.setGridEnabled = function(value)
-{
-	this.gridEnabled = value;
-};
-
-/**
- * Function: getGraphForEvent
- *
- * Returns the graph for the given mouse event. This implementation returns
- * null.
- */
-mxDragSource.prototype.getGraphForEvent = function(evt)
-{
-	return null;
-};
-
-/**
- * Function: getDropTarget
- *
- * Returns the drop target for the given graph and coordinates. This
- * implementation uses .
- */
-mxDragSource.prototype.getDropTarget = function(graph, x, y, evt)
-{
-	return graph.getCellAt(x, y);
-};
-
-/**
- * Function: createDragElement
- *
- * Creates and returns a clone of the  or the 
- * if the former is not defined.
- */
-mxDragSource.prototype.createDragElement = function(evt)
-{
-	return this.element.cloneNode(true);
-};
-
-/**
- * Function: createPreviewElement
- *
- * Creates and returns an element which can be used as a preview in the given
- * graph.
- */
-mxDragSource.prototype.createPreviewElement = function(graph)
-{
-	return null;
-};
-
-/**
- * Function: isActive
- *
- * Returns true if this drag source is active.
- */
-mxDragSource.prototype.isActive = function()
-{
-	return this.mouseMoveHandler != null;
-};
-
-/**
- * Function: reset
- *
- * Stops and removes everything and restores the state of the object.
- */
-mxDragSource.prototype.reset = function()
-{
-	if (this.currentGraph != null)
-	{
-		this.dragExit(this.currentGraph);
-		this.currentGraph = null;
-	}
-
-	this.removeDragElement();
-	this.removeListeners();
-	this.stopDrag();
-};
-
-/**
- * Function: mouseDown
- *
- * Returns the drop target for the given graph and coordinates. This
- * implementation uses .
- *
- * To ignore popup menu events for a drag source, this function can be
- * overridden as follows.
- *
- * (code)
- * var mouseDown = dragSource.mouseDown;
- *
- * dragSource.mouseDown = function(evt)
- * {
- *   if (!mxEvent.isPopupTrigger(evt))
- *   {
- *     mouseDown.apply(this, arguments);
- *   }
- * };
- * (end)
- */
-mxDragSource.prototype.mouseDown = function(evt)
-{
-	if (this.enabled && !mxEvent.isConsumed(evt) && this.mouseMoveHandler == null)
-	{
-		this.startDrag(evt);
-		this.mouseMoveHandler = mxUtils.bind(this, this.mouseMove);
-		this.mouseUpHandler = mxUtils.bind(this, this.mouseUp);
-		mxEvent.addGestureListeners(document, null, this.mouseMoveHandler, this.mouseUpHandler);
-
-		if (mxClient.IS_TOUCH && !mxEvent.isMouseEvent(evt))
-		{
-			this.eventSource = mxEvent.getSource(evt);
-			mxEvent.addGestureListeners(this.eventSource, null, this.mouseMoveHandler, this.mouseUpHandler);
-		}
-	}
-};
-
-/**
- * Function: startDrag
- *
- * Creates the  using .
- */
-mxDragSource.prototype.startDrag = function(evt)
-{
-	this.dragElement = this.createDragElement(evt);
-	this.dragElement.style.position = 'absolute';
-	this.dragElement.style.zIndex = this.dragElementZIndex;
-	mxUtils.setOpacity(this.dragElement, this.dragElementOpacity);
-
-	if (this.checkEventSource && mxClient.IS_SVG)
-	{
-		this.dragElement.style.pointerEvents = 'none';
-	}
-};
-
-/**
- * Function: stopDrag
- *
- * Invokes .
- */
-mxDragSource.prototype.stopDrag = function()
-{
-	// LATER: This used to have a mouse event. If that is still needed we need to add another
-	// final call to the DnD protocol to add a cleanup step in the case of escape press, which
-	// is not associated with a mouse event and which currently calles this method.
-	this.removeDragElement();
-};
-
-/**
- * Function: removeDragElement
- *
- * Removes and destroys the .
- */
-mxDragSource.prototype.removeDragElement = function()
-{
-	if (this.dragElement != null)
-	{
-		if (this.dragElement.parentNode != null)
-		{
-			this.dragElement.parentNode.removeChild(this.dragElement);
-		}
-
-		this.dragElement = null;
-	}
-};
-
-/**
- * Function: getElementForEvent
- *
- * Returns the topmost element under the given event.
- */
-mxDragSource.prototype.getElementForEvent = function(evt)
-{
-	return ((mxEvent.isTouchEvent(evt) || mxEvent.isPenEvent(evt)) ?
-			document.elementFromPoint(mxEvent.getClientX(evt), mxEvent.getClientY(evt)) :
-				mxEvent.getSource(evt));
-};
-
-/**
- * Function: graphContainsEvent
- *
- * Returns true if the given graph contains the given event.
- */
-mxDragSource.prototype.graphContainsEvent = function(graph, evt)
-{
-	var x = mxEvent.getClientX(evt);
-	var y = mxEvent.getClientY(evt);
-	var offset = mxUtils.getOffset(graph.container);
-	var origin = mxUtils.getScrollOrigin();
-	var elt = this.getElementForEvent(evt);
-
-	if (this.checkEventSource)
-	{
-		while (elt != null && elt != graph.container)
-		{
-			elt = elt.parentNode;
-		}
-	}
-
-	// Checks if event is inside the bounds of the graph container
-	return elt != null && x >= offset.x - origin.x && y >= offset.y - origin.y &&
-		x <= offset.x - origin.x + graph.container.offsetWidth &&
-		y <= offset.y - origin.y + graph.container.offsetHeight;
-};
-
-/**
- * Function: mouseMove
- *
- * Gets the graph for the given event using , updates the
- * , calling  and  on the new and old graph,
- * respectively, and invokes  if  is not null.
- */
-mxDragSource.prototype.mouseMove = function(evt)
-{
-	var graph = this.getGraphForEvent(evt);
-
-	// Checks if event is inside the bounds of the graph container
-	if (graph != null && !this.graphContainsEvent(graph, evt))
-	{
-		graph = null;
-	}
-
-	if (graph != this.currentGraph)
-	{
-		if (this.currentGraph != null)
-		{
-			this.dragExit(this.currentGraph, evt);
-		}
-
-		this.currentGraph = graph;
-
-		if (this.currentGraph != null)
-		{
-			this.dragEnter(this.currentGraph, evt);
-		}
-	}
-
-	if (this.currentGraph != null)
-	{
-		this.dragOver(this.currentGraph, evt);
-	}
-
-	if (this.dragElement != null && (this.previewElement == null || this.previewElement.style.visibility != 'visible'))
-	{
-		var x = mxEvent.getClientX(evt);
-		var y = mxEvent.getClientY(evt);
-
-		if (this.dragElement.parentNode == null)
-		{
-			document.body.appendChild(this.dragElement);
-		}
-
-		this.dragElement.style.visibility = 'visible';
-
-		if (this.dragOffset != null)
-		{
-			x += this.dragOffset.x;
-			y += this.dragOffset.y;
-		}
-
-		var offset = mxUtils.getDocumentScrollOrigin(document);
-
-		this.dragElement.style.left = (x + offset.x) + 'px';
-		this.dragElement.style.top = (y + offset.y) + 'px';
-	}
-	else if (this.dragElement != null)
-	{
-		this.dragElement.style.visibility = 'hidden';
-	}
-
-	mxEvent.consume(evt);
-};
-
-/**
- * Function: mouseUp
- *
- * Processes the mouse up event and invokes ,  and 
- * as required.
- */
-mxDragSource.prototype.mouseUp = function(evt)
-{
-	if (this.currentGraph != null)
-	{
-		if (this.currentPoint != null && (this.previewElement == null ||
-			this.previewElement.style.visibility != 'hidden'))
-		{
-			var scale = this.currentGraph.view.scale;
-			var tr = this.currentGraph.view.translate;
-			var x = this.currentPoint.x / scale - tr.x;
-			var y = this.currentPoint.y / scale - tr.y;
-
-			this.drop(this.currentGraph, evt, this.currentDropTarget, x, y);
-		}
-
-		this.dragExit(this.currentGraph);
-		this.currentGraph = null;
-	}
-
-	this.stopDrag();
-	this.removeListeners();
-
-	mxEvent.consume(evt);
-};
-
-/**
- * Function: removeListeners
- *
- * Actives the given graph as a drop target.
- */
-mxDragSource.prototype.removeListeners = function()
-{
-	if (this.eventSource != null)
-	{
-		mxEvent.removeGestureListeners(this.eventSource, null, this.mouseMoveHandler, this.mouseUpHandler);
-		this.eventSource = null;
-	}
-
-	mxEvent.removeGestureListeners(document, null, this.mouseMoveHandler, this.mouseUpHandler);
-	this.mouseMoveHandler = null;
-	this.mouseUpHandler = null;
-};
-
-/**
- * Function: dragEnter
- *
- * Actives the given graph as a drop target.
- */
-mxDragSource.prototype.dragEnter = function(graph, evt)
-{
-	graph.isMouseDown = true;
-	graph.isMouseTrigger = mxEvent.isMouseEvent(evt);
-	this.previewElement = this.createPreviewElement(graph);
-
-	if (this.previewElement != null && this.checkEventSource && mxClient.IS_SVG)
-	{
-		this.previewElement.style.pointerEvents = 'none';
-	}
-
-	// Guide is only needed if preview element is used
-	if (this.isGuidesEnabled() && this.previewElement != null)
-	{
-		this.currentGuide = new mxGuide(graph, graph.graphHandler.getGuideStates());
-	}
-
-	if (this.highlightDropTargets)
-	{
-		this.currentHighlight = new mxCellHighlight(graph, mxConstants.DROP_TARGET_COLOR);
-	}
-
-	// Consumes all events in the current graph before they are fired
-	graph.addListener(mxEvent.FIRE_MOUSE_EVENT, this.eventConsumer);
-};
-
-/**
- * Function: dragExit
- *
- * Deactivates the given graph as a drop target.
- */
-mxDragSource.prototype.dragExit = function(graph, evt)
-{
-	this.currentDropTarget = null;
-	this.currentPoint = null;
-	graph.isMouseDown = false;
-
-	// Consumes all events in the current graph before they are fired
-	graph.removeListener(this.eventConsumer);
-
-	if (this.previewElement != null)
-	{
-		if (this.previewElement.parentNode != null)
-		{
-			this.previewElement.parentNode.removeChild(this.previewElement);
-		}
-
-		this.previewElement = null;
-	}
-
-	if (this.currentGuide != null)
-	{
-		this.currentGuide.destroy();
-		this.currentGuide = null;
-	}
-
-	if (this.currentHighlight != null)
-	{
-		this.currentHighlight.destroy();
-		this.currentHighlight = null;
-	}
-};
-
-/**
- * Function: dragOver
- *
- * Implements autoscroll, updates the , highlights any drop
- * targets and updates the preview.
- */
-mxDragSource.prototype.dragOver = function(graph, evt)
-{
-	var offset = mxUtils.getOffset(graph.container);
-	var origin = mxUtils.getScrollOrigin(graph.container);
-	var x = mxEvent.getClientX(evt) - offset.x + origin.x - graph.panDx;
-	var y = mxEvent.getClientY(evt) - offset.y + origin.y - graph.panDy;
-
-	if (graph.autoScroll && (this.autoscroll == null || this.autoscroll))
-	{
-		graph.scrollPointToVisible(x, y, graph.autoExtend);
-	}
-
-	// Highlights the drop target under the mouse
-	if (this.currentHighlight != null && graph.isDropEnabled())
-	{
-		this.currentDropTarget = this.getDropTarget(graph, x, y, evt);
-		var state = graph.getView().getState(this.currentDropTarget);
-		this.currentHighlight.highlight(state);
-	}
-
-	// Updates the location of the preview
-	if (this.previewElement != null)
-	{
-		if (this.previewElement.parentNode == null)
-		{
-			graph.container.appendChild(this.previewElement);
-
-			this.previewElement.style.zIndex = '3';
-			this.previewElement.style.position = 'absolute';
-		}
-
-		var gridEnabled = this.isGridEnabled() && graph.isGridEnabledEvent(evt);
-		var hideGuide = true;
-
-		// Grid and guides
-		if (this.currentGuide != null && this.currentGuide.isEnabledForEvent(evt))
-		{
-			// LATER: HTML preview appears smaller than SVG preview
-			var w = parseInt(this.previewElement.style.width);
-			var h = parseInt(this.previewElement.style.height);
-			var bounds = new mxRectangle(0, 0, w, h);
-			var delta = new mxPoint(x, y);
-			delta = this.currentGuide.move(bounds, delta, gridEnabled);
-			hideGuide = false;
-			x = delta.x;
-			y = delta.y;
-		}
-		else if (gridEnabled)
-		{
-			var scale = graph.view.scale;
-			var tr = graph.view.translate;
-			var off = graph.gridSize / 2;
-			x = (graph.snap(x / scale - tr.x - off) + tr.x) * scale;
-			y = (graph.snap(y / scale - tr.y - off) + tr.y) * scale;
-		}
-
-		if (this.currentGuide != null && hideGuide)
-		{
-			this.currentGuide.hide();
-		}
-
-		if (this.previewOffset != null)
-		{
-			x += this.previewOffset.x;
-			y += this.previewOffset.y;
-		}
-
-		this.previewElement.style.left = Math.round(x) + 'px';
-		this.previewElement.style.top = Math.round(y) + 'px';
-		this.previewElement.style.visibility = 'visible';
-	}
-
-	this.currentPoint = new mxPoint(x, y);
-};
-
-/**
- * Function: drop
- *
- * Returns the drop target for the given graph and coordinates. This
- * implementation uses .
- */
-mxDragSource.prototype.drop = function(graph, evt, dropTarget, x, y)
-{
-	this.dropHandler.apply(this, arguments);
-
-	// Had to move this to after the insert because it will
-	// affect the scrollbars of the window in IE to try and
-	// make the complete container visible.
-	// LATER: Should be made optional.
-	if (graph.container.style.visibility != 'hidden')
-	{
-		graph.container.focus();
-	}
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxToolbar
- *
- * Creates a toolbar inside a given DOM node. The toolbar may contain icons,
- * buttons and combo boxes.
- *
- * Event: mxEvent.SELECT
- *
- * Fires when an item was selected in the toolbar. The function
- * property contains the function that was selected in .
- *
- * Constructor: mxToolbar
- *
- * Constructs a toolbar in the specified container.
- *
- * Parameters:
- *
- * container - DOM node that contains the toolbar.
- */
-function mxToolbar(container)
-{
-	this.container = container;
-};
-
-/**
- * Extends mxEventSource.
- */
-mxToolbar.prototype = new mxEventSource();
-mxToolbar.prototype.constructor = mxToolbar;
-
-/**
- * Variable: container
- *
- * Reference to the DOM nodes that contains the toolbar.
- */
-mxToolbar.prototype.container = null;
-
-/**
- * Variable: enabled
- *
- * Specifies if events are handled. Default is true.
- */
-mxToolbar.prototype.enabled = true;
-
-/**
- * Variable: noReset
- *
- * Specifies if  requires a forced flag of true for resetting
- * the current mode in the toolbar. Default is false. This is set to true
- * if the toolbar item is double clicked to avoid a reset after a single
- * use of the item.
- */
-mxToolbar.prototype.noReset = false;
-
-/**
- * Variable: updateDefaultMode
- *
- * Boolean indicating if the default mode should be the last selected
- * switch mode or the first inserted switch mode. Default is true, that
- * is the last selected switch mode is the default mode. The default mode
- * is the mode to be selected after a reset of the toolbar. If this is
- * false, then the default mode is the first inserted mode item regardless
- * of what was last selected. Otherwise, the selected item after a reset is
- * the previously selected item.
- */
-mxToolbar.prototype.updateDefaultMode = true;
-
-/**
- * Function: addItem
- *
- * Adds the given function as an image with the specified title and icon
- * and returns the new image node.
- *
- * Parameters:
- *
- * title - Optional string that is used as the tooltip.
- * icon - Optional URL of the image to be used. If no URL is given, then a
- * button is created.
- * funct - Function to execute on a mouse click.
- * pressedIcon - Optional URL of the pressed image. Default is a gray
- * background.
- * style - Optional style classname. Default is mxToolbarItem.
- * factoryMethod - Optional factory method for popup menu, eg.
- * function(menu, evt, cell) { menu.addItem('Hello, World!'); }
- */
-mxToolbar.prototype.addItem = function(title, icon, funct, pressedIcon, style, factoryMethod)
-{
-	var img = document.createElement((icon != null) ? 'img' : 'button');
-	var initialClassName = style || ((factoryMethod != null) ?
-			'mxToolbarMode' : 'mxToolbarItem');
-	img.className = initialClassName;
-	img.setAttribute('src', icon);
-
-	if (title != null)
-	{
-		if (icon != null)
-		{
-			img.setAttribute('title', title);
-		}
-		else
-		{
-			mxUtils.write(img, title);
-		}
-	}
-
-	this.container.appendChild(img);
-
-	// Invokes the function on a click on the toolbar item
-	if (funct != null)
-	{
-		mxEvent.addListener(img, 'click', funct);
-
-		if (mxClient.IS_TOUCH)
-		{
-			mxEvent.addListener(img, 'touchend', funct);
-		}
-	}
-
-	var mouseHandler = mxUtils.bind(this, function(evt)
-	{
-		if (pressedIcon != null)
-		{
-			img.setAttribute('src', icon);
-		}
-		else
-		{
-			img.style.backgroundColor = '';
-		}
-	});
-
-	// Highlights the toolbar item with a gray background
-	// while it is being clicked with the mouse
-	mxEvent.addGestureListeners(img, mxUtils.bind(this, function(evt)
-	{
-		if (pressedIcon != null)
-		{
-			img.setAttribute('src', pressedIcon);
-		}
-		else
-		{
-			img.style.backgroundColor = 'gray';
-		}
-
-		// Popup Menu
-		if (factoryMethod != null)
-		{
-			if (this.menu == null)
-			{
-				this.menu = new mxPopupMenu();
-				this.menu.init();
-			}
-
-			var last = this.currentImg;
-
-			if (this.menu.isMenuShowing())
-			{
-				this.menu.hideMenu();
-			}
-
-			if (last != img)
-			{
-				// Redirects factory method to local factory method
-				this.currentImg = img;
-				this.menu.factoryMethod = factoryMethod;
-
-				var point = new mxPoint(
-					img.offsetLeft,
-					img.offsetTop + img.offsetHeight);
-				this.menu.popup(point.x, point.y, null, evt);
-
-				// Sets and overrides to restore classname
-				if (this.menu.isMenuShowing())
-				{
-					img.className = initialClassName + 'Selected';
-
-					this.menu.hideMenu = function()
-					{
-						mxPopupMenu.prototype.hideMenu.apply(this);
-						img.className = initialClassName;
-						this.currentImg = null;
-					};
-				}
-			}
-		}
-	}), null, mouseHandler);
-
-	mxEvent.addListener(img, 'mouseout', mouseHandler);
-
-	return img;
-};
-
-/**
- * Function: addCombo
- *
- * Adds and returns a new SELECT element using the given style. The element
- * is placed inside a DIV with the mxToolbarComboContainer style classname.
- *
- * Parameters:
- *
- * style - Optional style classname. Default is mxToolbarCombo.
- */
-mxToolbar.prototype.addCombo = function(style)
-{
-	var div = document.createElement('div');
-	div.style.display = 'inline';
-	div.className = 'mxToolbarComboContainer';
-
-	var select = document.createElement('select');
-	select.className = style || 'mxToolbarCombo';
-	div.appendChild(select);
-
-	this.container.appendChild(div);
-
-	return select;
-};
-
-/**
- * Function: addCombo
- *
- * Adds and returns a new SELECT element using the given title as the
- * default element. The selection is reset to this element after each
- * change.
- *
- * Parameters:
- *
- * title - String that specifies the title of the default element.
- * style - Optional style classname. Default is mxToolbarCombo.
- */
-mxToolbar.prototype.addActionCombo = function(title, style)
-{
-	var select = document.createElement('select');
-	select.className = style || 'mxToolbarCombo';
-	this.addOption(select, title, null);
-
-	mxEvent.addListener(select, 'change', function(evt)
-	{
-		var value = select.options[select.selectedIndex];
-		select.selectedIndex = 0;
-
-		if (value.funct != null)
-		{
-			value.funct(evt);
-		}
-	});
-
-	this.container.appendChild(select);
-
-	return select;
-};
-
-/**
- * Function: addOption
- *
- * Adds and returns a new OPTION element inside the given SELECT element.
- * If the given value is a function then it is stored in the option's funct
- * field.
- *
- * Parameters:
- *
- * combo - SELECT element that will contain the new entry.
- * title - String that specifies the title of the option.
- * value - Specifies the value associated with this option.
- */
-mxToolbar.prototype.addOption = function(combo, title, value)
-{
-	var option = document.createElement('option');
-	mxUtils.writeln(option, title);
-
-	if (typeof(value) == 'function')
-	{
-		option.funct = value;
-	}
-	else
-	{
-		option.setAttribute('value', value);
-	}
-
-	combo.appendChild(option);
-
-	return option;
-};
-
-/**
- * Function: addSwitchMode
- *
- * Adds a new selectable item to the toolbar. Only one switch mode item may
- * be selected at a time. The currently selected item is the default item
- * after a reset of the toolbar.
- */
-mxToolbar.prototype.addSwitchMode = function(title, icon, funct, pressedIcon, style)
-{
-	var img = document.createElement('img');
-	img.initialClassName = style || 'mxToolbarMode';
-	img.className = img.initialClassName;
-	img.setAttribute('src', icon);
-	img.altIcon = pressedIcon;
-
-	if (title != null)
-	{
-		img.setAttribute('title', title);
-	}
-
-	mxEvent.addListener(img, 'click', mxUtils.bind(this, function(evt)
-	{
-		var tmp = this.selectedMode.altIcon;
-
-		if (tmp != null)
-		{
-			this.selectedMode.altIcon = this.selectedMode.getAttribute('src');
-			this.selectedMode.setAttribute('src', tmp);
-		}
-		else
-		{
-			this.selectedMode.className = this.selectedMode.initialClassName;
-		}
-
-		if (this.updateDefaultMode)
-		{
-			this.defaultMode = img;
-		}
-
-		this.selectedMode = img;
-
-		var tmp = img.altIcon;
-
-		if (tmp != null)
-		{
-			img.altIcon = img.getAttribute('src');
-			img.setAttribute('src', tmp);
-		}
-		else
-		{
-			img.className = img.initialClassName+'Selected';
-		}
-
-		this.fireEvent(new mxEventObject(mxEvent.SELECT));
-		funct();
-	}));
-
-	this.container.appendChild(img);
-
-	if (this.defaultMode == null)
-	{
-		this.defaultMode = img;
-
-		// Function should fire only once so
-		// do not pass it with the select event
-		this.selectMode(img);
-		funct();
-	}
-
-	return img;
-};
-
-/**
- * Function: addMode
- *
- * Adds a new item to the toolbar. The selection is typically reset after
- * the item has been consumed, for example by adding a new vertex to the
- * graph. The reset is not carried out if the item is double clicked.
- *
- * The function argument uses the following signature: funct(evt, cell) where
- * evt is the native mouse event and cell is the cell under the mouse.
- */
-mxToolbar.prototype.addMode = function(title, icon, funct, pressedIcon, style, toggle)
-{
-	toggle = (toggle != null) ? toggle : true;
-	var img = document.createElement((icon != null) ? 'img' : 'button');
-
-	img.initialClassName = style || 'mxToolbarMode';
-	img.className = img.initialClassName;
-	img.setAttribute('src', icon);
-	img.altIcon = pressedIcon;
-
-	if (title != null)
-	{
-		img.setAttribute('title', title);
-	}
-
-	if (this.enabled && toggle)
-	{
-		mxEvent.addListener(img, 'click', mxUtils.bind(this, function(evt)
-		{
-			this.selectMode(img, funct);
-			this.noReset = false;
-		}));
-
-		mxEvent.addListener(img, 'dblclick', mxUtils.bind(this, function(evt)
-		{
-			this.selectMode(img, funct);
-			this.noReset = true;
-		}));
-
-		if (this.defaultMode == null)
-		{
-			this.defaultMode = img;
-			this.defaultFunction = funct;
-			this.selectMode(img, funct);
-		}
-	}
-
-	this.container.appendChild(img);
-
-	return img;
-};
-
-/**
- * Function: selectMode
- *
- * Resets the state of the previously selected mode and displays the given
- * DOM node as selected. This function fires a select event with the given
- * function as a parameter.
- */
-mxToolbar.prototype.selectMode = function(domNode, funct)
-{
-	if (this.selectedMode != domNode)
-	{
-		if (this.selectedMode != null)
-		{
-			var tmp = this.selectedMode.altIcon;
-
-			if (tmp != null)
-			{
-				this.selectedMode.altIcon = this.selectedMode.getAttribute('src');
-				this.selectedMode.setAttribute('src', tmp);
-			}
-			else
-			{
-				this.selectedMode.className = this.selectedMode.initialClassName;
-			}
-		}
-
-		this.selectedMode = domNode;
-		var tmp = this.selectedMode.altIcon;
-
-		if (tmp != null)
-		{
-			this.selectedMode.altIcon = this.selectedMode.getAttribute('src');
-			this.selectedMode.setAttribute('src', tmp);
-		}
-		else
-		{
-			this.selectedMode.className = this.selectedMode.initialClassName+'Selected';
-		}
-
-		this.fireEvent(new mxEventObject(mxEvent.SELECT, "function", funct));
-	}
-};
-
-/**
- * Function: resetMode
- *
- * Selects the default mode and resets the state of the previously selected
- * mode.
- */
-mxToolbar.prototype.resetMode = function(forced)
-{
-	if ((forced || !this.noReset) && this.selectedMode != this.defaultMode)
-	{
-		// The last selected switch mode will be activated
-		// so the function was already executed and is
-		// no longer required here
-		this.selectMode(this.defaultMode, this.defaultFunction);
-	}
-};
-
-/**
- * Function: addSeparator
- *
- * Adds the specifies image as a separator.
- *
- * Parameters:
- *
- * icon - URL of the separator icon.
- */
-mxToolbar.prototype.addSeparator = function(icon)
-{
-	return this.addItem(null, icon, null);
-};
-
-/**
- * Function: addBreak
- *
- * Adds a break to the container.
- */
-mxToolbar.prototype.addBreak = function()
-{
-	mxUtils.br(this.container);
-};
-
-/**
- * Function: addLine
- *
- * Adds a horizontal line to the container.
- */
-mxToolbar.prototype.addLine = function()
-{
-	var hr = document.createElement('hr');
-
-	hr.style.marginRight = '6px';
-	hr.setAttribute('size', '1');
-
-	this.container.appendChild(hr);
-};
-
-/**
- * Function: destroy
- *
- * Removes the toolbar and all its associated resources.
- */
-mxToolbar.prototype.destroy = function ()
-{
-	mxEvent.release(this.container);
-	this.container = null;
-	this.defaultMode = null;
-	this.defaultFunction = null;
-	this.selectedMode = null;
-
-	if (this.menu != null)
-	{
-		this.menu.destroy();
-	}
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxUndoableEdit
- *
- * Implements a composite undoable edit. Here is an example for a custom change
- * which gets executed via the model:
- *
- * (code)
- * function CustomChange(model, name)
- * {
- *   this.model = model;
- *   this.name = name;
- *   this.previous = name;
- * };
- *
- * CustomChange.prototype.execute = function()
- * {
- *   var tmp = this.model.name;
- *   this.model.name = this.previous;
- *   this.previous = tmp;
- * };
- *
- * var name = prompt('Enter name');
- * graph.model.execute(new CustomChange(graph.model, name));
- * (end)
- *
- * Event: mxEvent.EXECUTED
- *
- * Fires between START_EDIT and END_EDIT after an atomic change was executed.
- * The change property contains the change that was executed.
- *
- * Event: mxEvent.START_EDIT
- *
- * Fires before a set of changes will be executed in  or .
- * This event contains no properties.
- *
- * Event: mxEvent.END_EDIT
- *
- * Fires after a set of changeswas executed in  or .
- * This event contains no properties.
- *
- * Constructor: mxUndoableEdit
- *
- * Constructs a new undoable edit for the given source.
- */
-function mxUndoableEdit(source, significant)
-{
-	this.source = source;
-	this.changes = [];
-	this.significant = (significant != null) ? significant : true;
-};
-
-/**
- * Variable: source
- *
- * Specifies the source of the edit.
- */
-mxUndoableEdit.prototype.source = null;
-
-/**
- * Variable: changes
- *
- * Array that contains the changes that make up this edit. The changes are
- * expected to either have an undo and redo function, or an execute
- * function. Default is an empty array.
- */
-mxUndoableEdit.prototype.changes = null;
-
-/**
- * Variable: significant
- *
- * Specifies if the undoable change is significant.
- * Default is true.
- */
-mxUndoableEdit.prototype.significant = null;
-
-/**
- * Variable: undone
- *
- * Specifies if this edit has been undone. Default is false.
- */
-mxUndoableEdit.prototype.undone = false;
-
-/**
- * Variable: redone
- *
- * Specifies if this edit has been redone. Default is false.
- */
-mxUndoableEdit.prototype.redone = false;
-
-/**
- * Function: isEmpty
- *
- * Returns true if the this edit contains no changes.
- */
-mxUndoableEdit.prototype.isEmpty = function()
-{
-	return this.changes.length == 0;
-};
-
-/**
- * Function: isSignificant
- *
- * Returns .
- */
-mxUndoableEdit.prototype.isSignificant = function()
-{
-	return this.significant;
-};
-
-/**
- * Function: add
- *
- * Adds the specified change to this edit. The change is an object that is
- * expected to either have an undo and redo, or an execute function.
- */
-mxUndoableEdit.prototype.add = function(change)
-{
-	this.changes.push(change);
-};
-
-/**
- * Function: notify
- *
- * Hook to notify any listeners of the changes after an  or 
- * has been carried out. This implementation is empty.
- */
-mxUndoableEdit.prototype.notify = function() { };
-
-/**
- * Function: die
- *
- * Hook to free resources after the edit has been removed from the command
- * history. This implementation is empty.
- */
-mxUndoableEdit.prototype.die = function() { };
-
-/**
- * Function: undo
- *
- * Undoes all changes in this edit.
- */
-mxUndoableEdit.prototype.undo = function()
-{
-	if (!this.undone)
-	{
-		this.source.fireEvent(new mxEventObject(mxEvent.START_EDIT));
-		var count = this.changes.length;
-
-		for (var i = count - 1; i >= 0; i--)
-		{
-			var change = this.changes[i];
-
-			if (change.execute != null)
-			{
-				change.execute();
-			}
-			else if (change.undo != null)
-			{
-				change.undo();
-			}
-
-			// New global executed event
-			this.source.fireEvent(new mxEventObject(mxEvent.EXECUTED, 'change', change));
-		}
-
-		this.undone = true;
-		this.redone = false;
-		this.source.fireEvent(new mxEventObject(mxEvent.END_EDIT));
-	}
-
-	this.notify();
-};
-
-/**
- * Function: redo
- *
- * Redoes all changes in this edit.
- */
-mxUndoableEdit.prototype.redo = function()
-{
-	if (!this.redone)
-	{
-		this.source.fireEvent(new mxEventObject(mxEvent.START_EDIT));
-		var count = this.changes.length;
-
-		for (var i = 0; i < count; i++)
-		{
-			var change = this.changes[i];
-
-			if (change.execute != null)
-			{
-				change.execute();
-			}
-			else if (change.redo != null)
-			{
-				change.redo();
-			}
-
-			// New global executed event
-			this.source.fireEvent(new mxEventObject(mxEvent.EXECUTED, 'change', change));
-		}
-
-		this.undone = false;
-		this.redone = true;
-		this.source.fireEvent(new mxEventObject(mxEvent.END_EDIT));
-	}
-
-	this.notify();
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxUndoManager
- *
- * Implements a command history. When changing the graph model, an
- *  object is created at the start of the transaction (when
- * model.beginUpdate is called). All atomic changes are then added to this
- * object until the last model.endUpdate call, at which point the
- *  is dispatched in an event, and added to the history inside
- * . This is done by an event listener in
- * .
- *
- * Each atomic change of the model is represented by an object (eg.
- * , ,  etc) which contains the
- * complete undo information. The  also listens to the
- *  and stores it's changes to the current root as insignificant
- * undoable changes, so that drilling (step into, step up) is undone.
- *
- * This means when you execute an atomic change on the model, then change the
- * current root on the view and click undo, the change of the root will be
- * undone together with the change of the model so that the display represents
- * the state at which the model was changed. However, these changes are not
- * transmitted for sharing as they do not represent a state change.
- *
- * Example:
- *
- * When adding an undo manager to a graph, make sure to add it
- * to the model and the view as well to maintain a consistent
- * display across multiple undo/redo steps.
- *
- * (code)
- * var undoManager = new mxUndoManager();
- * var listener = function(sender, evt)
- * {
- *   undoManager.undoableEditHappened(evt.getProperty('edit'));
- * };
- * graph.getModel().addListener(mxEvent.UNDO, listener);
- * graph.getView().addListener(mxEvent.UNDO, listener);
- * (end)
- *
- * The code creates a function that informs the undoManager
- * of an undoable edit and binds it to the undo event of
- *  and  using
- * .
- *
- * Event: mxEvent.CLEAR
- *
- * Fires after  was invoked. This event has no properties.
- *
- * Event: mxEvent.UNDO
- *
- * Fires afer a significant edit was undone in . The edit
- * property contains the  that was undone.
- *
- * Event: mxEvent.REDO
- *
- * Fires afer a significant edit was redone in . The edit
- * property contains the  that was redone.
- *
- * Event: mxEvent.ADD
- *
- * Fires after an undoable edit was added to the history. The edit
- * property contains the  that was added.
- *
- * Constructor: mxUndoManager
- *
- * Constructs a new undo manager with the given history size. If no history
- * size is given, then a default size of 100 steps is used.
- */
-function mxUndoManager(size)
-{
-	this.size = (size != null) ? size : 100;
-	this.clear();
-};
-
-/**
- * Extends mxEventSource.
- */
-mxUndoManager.prototype = new mxEventSource();
-mxUndoManager.prototype.constructor = mxUndoManager;
-
-/**
- * Variable: size
- *
- * Maximum command history size. 0 means unlimited history. Default is
- * 100.
- */
-mxUndoManager.prototype.size = null;
-
-/**
- * Variable: history
- *
- * Array that contains the steps of the command history.
- */
-mxUndoManager.prototype.history = null;
-
-/**
- * Variable: indexOfNextAdd
- *
- * Index of the element to be added next.
- */
-mxUndoManager.prototype.indexOfNextAdd = 0;
-
-/**
- * Function: isEmpty
- *
- * Returns true if the history is empty.
- */
-mxUndoManager.prototype.isEmpty = function()
-{
-	return this.history.length == 0;
-};
-
-/**
- * Function: clear
- *
- * Clears the command history.
- */
-mxUndoManager.prototype.clear = function()
-{
-	this.history = [];
-	this.indexOfNextAdd = 0;
-	this.fireEvent(new mxEventObject(mxEvent.CLEAR));
-};
-
-/**
- * Function: canUndo
- *
- * Returns true if an undo is possible.
- */
-mxUndoManager.prototype.canUndo = function()
-{
-	return this.indexOfNextAdd > 0;
-};
-
-/**
- * Function: undo
- *
- * Undoes the last change.
- */
-mxUndoManager.prototype.undo = function()
-{
-    while (this.indexOfNextAdd > 0)
-    {
-        var edit = this.history[--this.indexOfNextAdd];
-        edit.undo();
-
-		if (edit.isSignificant())
-        {
-        	this.fireEvent(new mxEventObject(mxEvent.UNDO, 'edit', edit));
-            break;
-        }
-    }
-};
-
-/**
- * Function: canRedo
- *
- * Returns true if a redo is possible.
- */
-mxUndoManager.prototype.canRedo = function()
-{
-	return this.indexOfNextAdd < this.history.length;
-};
-
-/**
- * Function: redo
- *
- * Redoes the last change.
- */
-mxUndoManager.prototype.redo = function()
-{
-    var n = this.history.length;
-
-    while (this.indexOfNextAdd < n)
-    {
-        var edit =  this.history[this.indexOfNextAdd++];
-        edit.redo();
-
-        if (edit.isSignificant())
-        {
-        	this.fireEvent(new mxEventObject(mxEvent.REDO, 'edit', edit));
-            break;
-        }
-    }
-};
-
-/**
- * Function: undoableEditHappened
- *
- * Method to be called to add new undoable edits to the .
- */
-mxUndoManager.prototype.undoableEditHappened = function(undoableEdit)
-{
-	this.trim();
-
-	if (this.size > 0 &&
-		this.size == this.history.length)
-	{
-		this.history.shift();
-	}
-
-	this.history.push(undoableEdit);
-	this.indexOfNextAdd = this.history.length;
-	this.fireEvent(new mxEventObject(mxEvent.ADD, 'edit', undoableEdit));
-};
-
-/**
- * Function: trim
- *
- * Removes all pending steps after  from the history,
- * invoking die on each edit. This is called from .
- */
-mxUndoManager.prototype.trim = function()
-{
-	if (this.history.length > this.indexOfNextAdd)
-	{
-		var edits = this.history.splice(this.indexOfNextAdd,
-			this.history.length - this.indexOfNextAdd);
-
-		for (var i = 0; i < edits.length; i++)
-		{
-			edits[i].die();
-		}
-	}
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- *
- * Class: mxUrlConverter
- *
- * Converts relative and absolute URLs to absolute URLs with protocol and domain.
- */
-var mxUrlConverter = function()
-{
-	// Empty constructor
-};
-
-/**
- * Variable: enabled
- *
- * Specifies if the converter is enabled. Default is true.
- */
-mxUrlConverter.prototype.enabled = true;
-
-/**
- * Variable: baseUrl
- *
- * Specifies the base URL to be used as a prefix for relative URLs.
- */
-mxUrlConverter.prototype.baseUrl = null;
-
-/**
- * Variable: baseDomain
- *
- * Specifies the base domain to be used as a prefix for absolute URLs.
- */
-mxUrlConverter.prototype.baseDomain = null;
-
-/**
- * Function: updateBaseUrl
- *
- * Private helper function to update the base URL.
- */
-mxUrlConverter.prototype.updateBaseUrl = function()
-{
-	this.baseDomain = location.protocol + '//' + location.host;
-	this.baseUrl = this.baseDomain + location.pathname;
-	var tmp = this.baseUrl.lastIndexOf('/');
-
-	// Strips filename etc
-	if (tmp > 0)
-	{
-		this.baseUrl = this.baseUrl.substring(0, tmp + 1);
-	}
-};
-
-/**
- * Function: isEnabled
- *
- * Returns .
- */
-mxUrlConverter.prototype.isEnabled = function()
-{
-	return this.enabled;
-};
-
-/**
- * Function: setEnabled
- *
- * Sets .
- */
-mxUrlConverter.prototype.setEnabled = function(value)
-{
-	this.enabled = value;
-};
-
-/**
- * Function: getBaseUrl
- *
- * Returns .
- */
-mxUrlConverter.prototype.getBaseUrl = function()
-{
-	return this.baseUrl;
-};
-
-/**
- * Function: setBaseUrl
- *
- * Sets .
- */
-mxUrlConverter.prototype.setBaseUrl = function(value)
-{
-	this.baseUrl = value;
-};
-
-/**
- * Function: getBaseDomain
- *
- * Returns .
- */
-mxUrlConverter.prototype.getBaseDomain = function()
-{
-	return this.baseDomain;
-},
-
-/**
- * Function: setBaseDomain
- *
- * Sets .
- */
-mxUrlConverter.prototype.setBaseDomain = function(value)
-{
-	this.baseDomain = value;
-},
-
-/**
- * Function: isRelativeUrl
- *
- * Returns true if the given URL is relative.
- */
-mxUrlConverter.prototype.isRelativeUrl = function(url)
-{
-	return url.substring(0, 2) != '//' && url.substring(0, 7) != 'http://' &&
-		url.substring(0, 8) != 'https://' && url.substring(0, 10) != 'data:image' &&
-		url.substring(0, 7) != 'file://';
-};
-
-/**
- * Function: convert
- *
- * Converts the given URL to an absolute URL with protol and domain.
- * Relative URLs are first converted to absolute URLs.
- */
-mxUrlConverter.prototype.convert = function(url)
-{
-	if (this.isEnabled() && this.isRelativeUrl(url))
-	{
-		if (this.getBaseUrl() == null)
-		{
-			this.updateBaseUrl();
-		}
-
-		if (url.charAt(0) == '/')
-		{
-			url = this.getBaseDomain() + url;
-		}
-		else
-		{
-			url = this.getBaseUrl() + url;
-		}
-	}
-
-	return url;
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxPanningManager
- *
- * Implements a handler for panning.
- */
-function mxPanningManager(graph)
-{
-	this.thread = null;
-	this.active = false;
-	this.tdx = 0;
-	this.tdy = 0;
-	this.t0x = 0;
-	this.t0y = 0;
-	this.dx = 0;
-	this.dy = 0;
-	this.scrollbars = false;
-	this.scrollLeft = 0;
-	this.scrollTop = 0;
-
-	this.mouseListener =
-	{
-	    mouseDown: function(sender, me) { },
-	    mouseMove: function(sender, me) { },
-	    mouseUp: mxUtils.bind(this, function(sender, me)
-	    {
-	    	if (this.active)
-	    	{
-	    		this.stop();
-	    	}
-	    })
-	};
-
-	graph.addMouseListener(this.mouseListener);
-
-	this.mouseUpListener = mxUtils.bind(this, function()
-	{
-	    	if (this.active)
-	    	{
-	    		this.stop();
-	    	}
-	});
-
-	// Stops scrolling on every mouseup anywhere in the document
-	mxEvent.addListener(document, 'mouseup', this.mouseUpListener);
-
-	var createThread = mxUtils.bind(this, function()
-	{
-	    	this.scrollbars = mxUtils.hasScrollbars(graph.container);
-	    	this.scrollLeft = graph.container.scrollLeft;
-	    	this.scrollTop = graph.container.scrollTop;
-
-	    	return window.setInterval(mxUtils.bind(this, function()
-		{
-			this.tdx -= this.dx;
-			this.tdy -= this.dy;
-
-			if (this.scrollbars)
-			{
-				var left = -graph.container.scrollLeft - Math.ceil(this.dx);
-				var top = -graph.container.scrollTop - Math.ceil(this.dy);
-				graph.panGraph(left, top);
-				graph.panDx = this.scrollLeft - graph.container.scrollLeft;
-				graph.panDy = this.scrollTop - graph.container.scrollTop;
-				graph.fireEvent(new mxEventObject(mxEvent.PAN));
-				// TODO: Implement graph.autoExtend
-			}
-			else
-			{
-				graph.panGraph(this.getDx(), this.getDy());
-			}
-		}), this.delay);
-	});
-
-	this.isActive = function()
-	{
-		return active;
-	};
-
-	this.getDx = function()
-	{
-		return Math.round(this.tdx);
-	};
-
-	this.getDy = function()
-	{
-		return Math.round(this.tdy);
-	};
-
-	this.start = function()
-	{
-		this.t0x = graph.view.translate.x;
-		this.t0y = graph.view.translate.y;
-		this.active = true;
-	};
-
-	this.panTo = function(x, y, w, h)
-	{
-		if (!this.active)
-		{
-			this.start();
-		}
-
-    	this.scrollLeft = graph.container.scrollLeft;
-    	this.scrollTop = graph.container.scrollTop;
-
-		w = (w != null) ? w : 0;
-		h = (h != null) ? h : 0;
-
-		var c = graph.container;
-		this.dx = x + w - c.scrollLeft - c.clientWidth;
-
-		if (this.dx < 0 && Math.abs(this.dx) < this.border)
-		{
-			this.dx = this.border + this.dx;
-		}
-		else if (this.handleMouseOut)
-		{
-			this.dx = Math.max(this.dx, 0);
-		}
-		else
-		{
-			this.dx = 0;
-		}
-
-		if (this.dx == 0)
-		{
-			this.dx = x - c.scrollLeft;
-
-			if (this.dx > 0 && this.dx < this.border)
-			{
-				this.dx = this.dx - this.border;
-			}
-			else if (this.handleMouseOut)
-			{
-				this.dx = Math.min(0, this.dx);
-			}
-			else
-			{
-				this.dx = 0;
-			}
-		}
-
-		this.dy = y + h - c.scrollTop - c.clientHeight;
-
-		if (this.dy < 0 && Math.abs(this.dy) < this.border)
-		{
-			this.dy = this.border + this.dy;
-		}
-		else if (this.handleMouseOut)
-		{
-			this.dy = Math.max(this.dy, 0);
-		}
-		else
-		{
-			this.dy = 0;
-		}
-
-		if (this.dy == 0)
-		{
-			this.dy = y - c.scrollTop;
-
-			if (this.dy > 0 && this.dy < this.border)
-			{
-				this.dy = this.dy - this.border;
-			}
-			else if (this.handleMouseOut)
-			{
-				this.dy = Math.min(0, this.dy);
-			}
-			else
-			{
-				this.dy = 0;
-			}
-		}
-
-		if (this.dx != 0 || this.dy != 0)
-		{
-			this.dx *= this.damper;
-			this.dy *= this.damper;
-
-			if (this.thread == null)
-			{
-				this.thread = createThread();
-			}
-		}
-		else if (this.thread != null)
-		{
-			window.clearInterval(this.thread);
-			this.thread = null;
-		}
-	};
-
-	this.stop = function()
-	{
-		if (this.active)
-		{
-			this.active = false;
-
-			if (this.thread != null)
-	    	{
-				window.clearInterval(this.thread);
-				this.thread = null;
-	    	}
-
-			this.tdx = 0;
-			this.tdy = 0;
-
-			if (!this.scrollbars)
-			{
-				var px = graph.panDx;
-				var py = graph.panDy;
-
-		    	if (px != 0 || py != 0)
-		    	{
-		    		graph.panGraph(0, 0);
-			    	graph.view.setTranslate(this.t0x + px / graph.view.scale, this.t0y + py / graph.view.scale);
-		    	}
-			}
-			else
-			{
-				graph.panDx = 0;
-				graph.panDy = 0;
-				graph.fireEvent(new mxEventObject(mxEvent.PAN));
-			}
-		}
-	};
-
-	this.destroy = function()
-	{
-		graph.removeMouseListener(this.mouseListener);
-		mxEvent.removeListener(document, 'mouseup', this.mouseUpListener);
-	};
-};
-
-/**
- * Variable: damper
- *
- * Damper value for the panning. Default is 1/6.
- */
-mxPanningManager.prototype.damper = 1/6;
-
-/**
- * Variable: delay
- *
- * Delay in milliseconds for the panning. Default is 10.
- */
-mxPanningManager.prototype.delay = 10;
-
-/**
- * Variable: handleMouseOut
- *
- * Specifies if mouse events outside of the component should be handled. Default is true.
- */
-mxPanningManager.prototype.handleMouseOut = true;
-
-/**
- * Variable: border
- *
- * Border to handle automatic panning inside the component. Default is 0 (disabled).
- */
-mxPanningManager.prototype.border = 0;
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxPopupMenu
- *
- * Basic popup menu. To add a vertical scrollbar to a given submenu, the
- * following code can be used.
- *
- * (code)
- * var mxPopupMenuShowMenu = mxPopupMenu.prototype.showMenu;
- * mxPopupMenu.prototype.showMenu = function()
- * {
- *   mxPopupMenuShowMenu.apply(this, arguments);
- *
- *   this.div.style.overflowY = 'auto';
- *   this.div.style.overflowX = 'hidden';
- *   this.div.style.maxHeight = '160px';
- * };
- * (end)
- *
- * Constructor: mxPopupMenu
- *
- * Constructs a popupmenu.
- *
- * Event: mxEvent.SHOW
- *
- * Fires after the menu has been shown in .
- */
-function mxPopupMenu(factoryMethod)
-{
-	this.factoryMethod = factoryMethod;
-
-	if (factoryMethod != null)
-	{
-		this.init();
-	}
-};
-
-/**
- * Extends mxEventSource.
- */
-mxPopupMenu.prototype = new mxEventSource();
-mxPopupMenu.prototype.constructor = mxPopupMenu;
-
-/**
- * Variable: submenuImage
- *
- * URL of the image to be used for the submenu icon.
- */
-mxPopupMenu.prototype.submenuImage = mxClient.imageBasePath + '/submenu.gif';
-
-/**
- * Variable: zIndex
- *
- * Specifies the zIndex for the popupmenu and its shadow. Default is 1006.
- */
-mxPopupMenu.prototype.zIndex = 10006;
-
-/**
- * Variable: factoryMethod
- *
- * Function that is used to create the popup menu. The function takes the
- * current panning handler, the  under the mouse and the mouse
- * event that triggered the call as arguments.
- */
-mxPopupMenu.prototype.factoryMethod = null;
-
-/**
- * Variable: useLeftButtonForPopup
- *
- * Specifies if popupmenus should be activated by clicking the left mouse
- * button. Default is false.
- */
-mxPopupMenu.prototype.useLeftButtonForPopup = false;
-
-/**
- * Variable: enabled
- *
- * Specifies if events are handled. Default is true.
- */
-mxPopupMenu.prototype.enabled = true;
-
-/**
- * Variable: itemCount
- *
- * Contains the number of times  has been called for a new menu.
- */
-mxPopupMenu.prototype.itemCount = 0;
-
-/**
- * Variable: autoExpand
- *
- * Specifies if submenus should be expanded on mouseover. Default is false.
- */
-mxPopupMenu.prototype.autoExpand = false;
-
-/**
- * Variable: smartSeparators
- *
- * Specifies if separators should only be added if a menu item follows them.
- * Default is false.
- */
-mxPopupMenu.prototype.smartSeparators = false;
-
-/**
- * Variable: labels
- *
- * Specifies if any labels should be visible. Default is true.
- */
-mxPopupMenu.prototype.labels = true;
-
-/**
- * Function: init
- *
- * Initializes the shapes required for this vertex handler.
- */
-mxPopupMenu.prototype.init = function()
-{
-	// Adds the inner table
-	this.table = document.createElement('table');
-	this.table.className = 'mxPopupMenu';
-
-	this.tbody = document.createElement('tbody');
-	this.table.appendChild(this.tbody);
-
-	// Adds the outer div
-	this.div = document.createElement('div');
-	this.div.className = 'mxPopupMenu';
-	this.div.style.display = 'inline';
-	this.div.style.zIndex = this.zIndex;
-	this.div.appendChild(this.table);
-
-	// Disables the context menu on the outer div
-	mxEvent.disableContextMenu(this.div);
-};
-
-/**
- * Function: isEnabled
- *
- * Returns true if events are handled. This implementation
- * returns .
- */
-mxPopupMenu.prototype.isEnabled = function()
-{
-	return this.enabled;
-};
-
-/**
- * Function: setEnabled
- *
- * Enables or disables event handling. This implementation
- * updates .
- */
-mxPopupMenu.prototype.setEnabled = function(enabled)
-{
-	this.enabled = enabled;
-};
-
-/**
- * Function: isPopupTrigger
- *
- * Returns true if the given event is a popupmenu trigger for the optional
- * given cell.
- *
- * Parameters:
- *
- * me -  that represents the mouse event.
- */
-mxPopupMenu.prototype.isPopupTrigger = function(me)
-{
-	return me.isPopupTrigger() || (this.useLeftButtonForPopup && mxEvent.isLeftMouseButton(me.getEvent()));
-};
-
-/**
- * Function: addItem
- *
- * Adds the given item to the given parent item. If no parent item is specified
- * then the item is added to the top-level menu. The return value may be used
- * as the parent argument, ie. as a submenu item. The return value is the table
- * row that represents the item.
- *
- * Paramters:
- *
- * title - String that represents the title of the menu item.
- * image - Optional URL for the image icon.
- * funct - Function associated that takes a mouseup or touchend event.
- * parent - Optional item returned by .
- * iconCls - Optional string that represents the CSS class for the image icon.
- * IconsCls is ignored if image is given.
- * enabled - Optional boolean indicating if the item is enabled. Default is true.
- * active - Optional boolean indicating if the menu should implement any event handling.
- * Default is true.
- */
-mxPopupMenu.prototype.addItem = function(title, image, funct, parent, iconCls, enabled, active)
-{
-	parent = parent || this;
-	this.itemCount++;
-
-	// Smart separators only added if element contains items
-	if (parent.willAddSeparator)
-	{
-		if (parent.containsItems)
-		{
-			this.addSeparator(parent, true);
-		}
-
-		parent.willAddSeparator = false;
-	}
-
-	parent.containsItems = true;
-	var tr = document.createElement('tr');
-	tr.className = 'mxPopupMenuItem';
-	var col1 = document.createElement('td');
-	col1.className = 'mxPopupMenuIcon';
-
-	// Adds the given image into the first column
-	if (image != null)
-	{
-		var img = document.createElement('img');
-		img.src = image;
-		col1.appendChild(img);
-	}
-	else if (iconCls != null)
-	{
-		var div = document.createElement('div');
-		div.className = iconCls;
-		col1.appendChild(div);
-	}
-
-	tr.appendChild(col1);
-
-	if (this.labels)
-	{
-		var col2 = document.createElement('td');
-		col2.className = 'mxPopupMenuItem' +
-			((enabled != null && !enabled) ? ' mxDisabled' : '');
-
-		mxUtils.write(col2, title);
-		col2.align = 'left';
-		tr.appendChild(col2);
-
-		var col3 = document.createElement('td');
-		col3.className = 'mxPopupMenuItem' +
-			((enabled != null && !enabled) ? ' mxDisabled' : '');
-		col3.style.paddingRight = '6px';
-		col3.style.textAlign = 'right';
-
-		tr.appendChild(col3);
-
-		if (parent.div == null)
-		{
-			this.createSubmenu(parent);
-		}
-	}
-
-	parent.tbody.appendChild(tr);
-
-	if (active != false && enabled != false)
-	{
-		var currentSelection = null;
-
-		mxEvent.addGestureListeners(tr,
-			mxUtils.bind(this, function(evt)
-			{
-				this.eventReceiver = tr;
-
-				if (parent.activeRow != tr && parent.activeRow != parent)
-				{
-					if (parent.activeRow != null && parent.activeRow.div.parentNode != null)
-					{
-						this.hideSubmenu(parent);
-					}
-
-					if (tr.div != null)
-					{
-						this.showSubmenu(parent, tr);
-						parent.activeRow = tr;
-					}
-				}
-
-				// Workaround for lost current selection in page because of focus in IE
-				if (mxClient.IS_QUIRKS || document.documentMode == 8)
-				{
-					currentSelection = document.selection.createRange();
-				}
-
-				mxEvent.consume(evt);
-			}),
-			mxUtils.bind(this, function(evt)
-			{
-				if (parent.activeRow != tr && parent.activeRow != parent)
-				{
-					if (parent.activeRow != null && parent.activeRow.div.parentNode != null)
-					{
-						this.hideSubmenu(parent);
-					}
-
-					if (this.autoExpand && tr.div != null)
-					{
-						this.showSubmenu(parent, tr);
-						parent.activeRow = tr;
-					}
-				}
-
-				// Sets hover style because TR in IE doesn't have hover
-				tr.className = 'mxPopupMenuItemHover';
-			}),
-			mxUtils.bind(this, function(evt)
-			{
-				// EventReceiver avoids clicks on a submenu item
-				// which has just been shown in the mousedown
-				if (this.eventReceiver == tr)
-				{
-					if (parent.activeRow != tr)
-					{
-						this.hideMenu();
-					}
-
-					// Workaround for lost current selection in page because of focus in IE
-					if (currentSelection != null)
-					{
-						// Workaround for "unspecified error" in IE8 standards
-						try
-						{
-							currentSelection.select();
-						}
-						catch (e)
-						{
-							// ignore
-						}
-
-						currentSelection = null;
-					}
-
-					if (funct != null)
-					{
-						funct(evt);
-					}
-				}
-
-				this.eventReceiver = null;
-				mxEvent.consume(evt);
-			})
-		);
-
-		// Resets hover style because TR in IE doesn't have hover
-		mxEvent.addListener(tr, 'mouseout',
-			mxUtils.bind(this, function(evt)
-			{
-				tr.className = 'mxPopupMenuItem';
-			})
-		);
-	}
-
-	return tr;
-};
-
-/**
- * Adds a checkmark to the given menuitem.
- */
-mxPopupMenu.prototype.addCheckmark = function(item, img)
-{
-	var td = item.firstChild.nextSibling;
-	td.style.backgroundImage = 'url(\'' + img + '\')';
-	td.style.backgroundRepeat = 'no-repeat';
-	td.style.backgroundPosition = '2px 50%';
-};
-
-/**
- * Function: createSubmenu
- *
- * Creates the nodes required to add submenu items inside the given parent
- * item. This is called in  if a parent item is used for the first
- * time. This adds various DOM nodes and a  to the parent.
- *
- * Parameters:
- *
- * parent - An item returned by .
- */
-mxPopupMenu.prototype.createSubmenu = function(parent)
-{
-	parent.table = document.createElement('table');
-	parent.table.className = 'mxPopupMenu';
-
-	parent.tbody = document.createElement('tbody');
-	parent.table.appendChild(parent.tbody);
-
-	parent.div = document.createElement('div');
-	parent.div.className = 'mxPopupMenu';
-
-	parent.div.style.position = 'absolute';
-	parent.div.style.display = 'inline';
-	parent.div.style.zIndex = this.zIndex;
-
-	parent.div.appendChild(parent.table);
-
-	var img = document.createElement('img');
-	img.setAttribute('src', this.submenuImage);
-
-	// Last column of the submenu item in the parent menu
-	td = parent.firstChild.nextSibling.nextSibling;
-	td.appendChild(img);
-};
-
-/**
- * Function: showSubmenu
- *
- * Shows the submenu inside the given parent row.
- */
-mxPopupMenu.prototype.showSubmenu = function(parent, row)
-{
-	if (row.div != null)
-	{
-		row.div.style.left = (parent.div.offsetLeft +
-			row.offsetLeft+row.offsetWidth - 1) + 'px';
-		row.div.style.top = (parent.div.offsetTop+row.offsetTop) + 'px';
-		document.body.appendChild(row.div);
-
-		// Moves the submenu to the left side if there is no space
-		var left = parseInt(row.div.offsetLeft);
-		var width = parseInt(row.div.offsetWidth);
-		var offset = mxUtils.getDocumentScrollOrigin(document);
-
-		var b = document.body;
-		var d = document.documentElement;
-
-		var right = offset.x + (b.clientWidth || d.clientWidth);
-
-		if (left + width > right)
-		{
-			row.div.style.left = Math.max(0, (parent.div.offsetLeft - width + ((mxClient.IS_IE) ? 6 : -6))) + 'px';
-		}
-
-		mxUtils.fit(row.div);
-	}
-};
-
-/**
- * Function: addSeparator
- *
- * Adds a horizontal separator in the given parent item or the top-level menu
- * if no parent is specified.
- *
- * Parameters:
- *
- * parent - Optional item returned by .
- * force - Optional boolean to ignore . Default is false.
- */
-mxPopupMenu.prototype.addSeparator = function(parent, force)
-{
-	parent = parent || this;
-
-	if (this.smartSeparators && !force)
-	{
-		parent.willAddSeparator = true;
-	}
-	else if (parent.tbody != null)
-	{
-		parent.willAddSeparator = false;
-		var tr = document.createElement('tr');
-
-		var col1 = document.createElement('td');
-		col1.className = 'mxPopupMenuIcon';
-		col1.style.padding = '0 0 0 0px';
-
-		tr.appendChild(col1);
-
-		var col2 = document.createElement('td');
-		col2.style.padding = '0 0 0 0px';
-		col2.setAttribute('colSpan', '2');
-
-		var hr = document.createElement('hr');
-		hr.setAttribute('size', '1');
-		col2.appendChild(hr);
-
-		tr.appendChild(col2);
-
-		parent.tbody.appendChild(tr);
-	}
-};
-
-/**
- * Function: popup
- *
- * Shows the popup menu for the given event and cell.
- *
- * Example:
- *
- * (code)
- * graph.panningHandler.popup = function(x, y, cell, evt)
- * {
- *   mxUtils.alert('Hello, World!');
- * }
- * (end)
- */
-mxPopupMenu.prototype.popup = function(x, y, cell, evt)
-{
-	if (this.div != null && this.tbody != null && this.factoryMethod != null)
-	{
-		this.div.style.left = x + 'px';
-		this.div.style.top = y + 'px';
-
-		// Removes all child nodes from the existing menu
-		while (this.tbody.firstChild != null)
-		{
-			mxEvent.release(this.tbody.firstChild);
-			this.tbody.removeChild(this.tbody.firstChild);
-		}
-
-		this.itemCount = 0;
-		this.factoryMethod(this, cell, evt);
-
-		if (this.itemCount > 0)
-		{
-			this.showMenu();
-			this.fireEvent(new mxEventObject(mxEvent.SHOW));
-		}
-	}
-};
-
-/**
- * Function: isMenuShowing
- *
- * Returns true if the menu is showing.
- */
-mxPopupMenu.prototype.isMenuShowing = function()
-{
-	return this.div != null && this.div.parentNode == document.body;
-};
-
-/**
- * Function: showMenu
- *
- * Shows the menu.
- */
-mxPopupMenu.prototype.showMenu = function()
-{
-	// Disables filter-based shadow in IE9 standards mode
-	if (document.documentMode >= 9)
-	{
-		this.div.style.filter = 'none';
-	}
-
-	// Fits the div inside the viewport
-	document.body.appendChild(this.div);
-	mxUtils.fit(this.div);
-};
-
-/**
- * Function: hideMenu
- *
- * Removes the menu and all submenus.
- */
-mxPopupMenu.prototype.hideMenu = function()
-{
-	if (this.div != null)
-	{
-		if (this.div.parentNode != null)
-		{
-			this.div.parentNode.removeChild(this.div);
-		}
-
-		this.hideSubmenu(this);
-		this.containsItems = false;
-		this.fireEvent(new mxEventObject(mxEvent.HIDE));
-	}
-};
-
-/**
- * Function: hideSubmenu
- *
- * Removes all submenus inside the given parent.
- *
- * Parameters:
- *
- * parent - An item returned by .
- */
-mxPopupMenu.prototype.hideSubmenu = function(parent)
-{
-	if (parent.activeRow != null)
-	{
-		this.hideSubmenu(parent.activeRow);
-
-		if (parent.activeRow.div.parentNode != null)
-		{
-			parent.activeRow.div.parentNode.removeChild(parent.activeRow.div);
-		}
-
-		parent.activeRow = null;
-	}
-};
-
-/**
- * Function: destroy
- *
- * Destroys the handler and all its resources and DOM nodes.
- */
-mxPopupMenu.prototype.destroy = function()
-{
-	if (this.div != null)
-	{
-		mxEvent.release(this.div);
-
-		if (this.div.parentNode != null)
-		{
-			this.div.parentNode.removeChild(this.div);
-		}
-
-		this.div = null;
-	}
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxAutoSaveManager
- *
- * Manager for automatically saving diagrams. The  hook must be
- * implemented.
- *
- * Example:
- *
- * (code)
- * var mgr = new mxAutoSaveManager(editor.graph);
- * mgr.save = function()
- * {
- *   mxLog.show();
- *   mxLog.debug('save');
- * };
- * (end)
- *
- * Constructor: mxAutoSaveManager
- *
- * Constructs a new automatic layout for the given graph.
- *
- * Arguments:
- *
- * graph - Reference to the enclosing graph.
- */
-function mxAutoSaveManager(graph)
-{
-	// Notifies the manager of a change
-	this.changeHandler = mxUtils.bind(this, function(sender, evt)
-	{
-		if (this.isEnabled())
-		{
-			this.graphModelChanged(evt.getProperty('edit').changes);
-		}
-	});
-
-	this.setGraph(graph);
-};
-
-/**
- * Extends mxEventSource.
- */
-mxAutoSaveManager.prototype = new mxEventSource();
-mxAutoSaveManager.prototype.constructor = mxAutoSaveManager;
-
-/**
- * Variable: graph
- *
- * Reference to the enclosing .
- */
-mxAutoSaveManager.prototype.graph = null;
-
-/**
- * Variable: autoSaveDelay
- *
- * Minimum amount of seconds between two consecutive autosaves. Eg. a
- * value of 1 (s) means the graph is not stored more than once per second.
- * Default is 10.
- */
-mxAutoSaveManager.prototype.autoSaveDelay = 10;
-
-/**
- * Variable: autoSaveThrottle
- *
- * Minimum amount of seconds between two consecutive autosaves triggered by
- * more than  changes within a timespan of less than
- *  seconds. Eg. a value of 1 (s) means the graph is not
- * stored more than once per second even if there are more than
- *  changes within that timespan. Default is 2.
- */
-mxAutoSaveManager.prototype.autoSaveThrottle = 2;
-
-/**
- * Variable: autoSaveThreshold
- *
- * Minimum amount of ignored changes before an autosave. Eg. a value of 2
- * means after 2 change of the graph model the autosave will trigger if the
- * condition below is true. Default is 5.
- */
-mxAutoSaveManager.prototype.autoSaveThreshold = 5;
-
-/**
- * Variable: ignoredChanges
- *
- * Counter for ignored changes in autosave.
- */
-mxAutoSaveManager.prototype.ignoredChanges = 0;
-
-/**
- * Variable: lastSnapshot
- *
- * Used for autosaving. See .
- */
-mxAutoSaveManager.prototype.lastSnapshot = 0;
-
-/**
- * Variable: enabled
- *
- * Specifies if event handling is enabled. Default is true.
- */
-mxAutoSaveManager.prototype.enabled = true;
-
-/**
- * Variable: changeHandler
- *
- * Holds the function that handles graph model changes.
- */
-mxAutoSaveManager.prototype.changeHandler = null;
-
-/**
- * Function: isEnabled
- *
- * Returns true if events are handled. This implementation
- * returns .
- */
-mxAutoSaveManager.prototype.isEnabled = function()
-{
-	return this.enabled;
-};
-
-/**
- * Function: setEnabled
- *
- * Enables or disables event handling. This implementation
- * updates .
- *
- * Parameters:
- *
- * enabled - Boolean that specifies the new enabled state.
- */
-mxAutoSaveManager.prototype.setEnabled = function(value)
-{
-	this.enabled = value;
-};
-
-/**
- * Function: setGraph
- *
- * Sets the graph that the layouts operate on.
- */
-mxAutoSaveManager.prototype.setGraph = function(graph)
-{
-	if (this.graph != null)
-	{
-		this.graph.getModel().removeListener(this.changeHandler);
-	}
-
-	this.graph = graph;
-
-	if (this.graph != null)
-	{
-		this.graph.getModel().addListener(mxEvent.CHANGE, this.changeHandler);
-	}
-};
-
-/**
- * Function: save
- *
- * Empty hook that is called if the graph should be saved.
- */
-mxAutoSaveManager.prototype.save = function()
-{
-	// empty
-};
-
-/**
- * Function: graphModelChanged
- *
- * Invoked when the graph model has changed.
- */
-mxAutoSaveManager.prototype.graphModelChanged = function(changes)
-{
-	var now = new Date().getTime();
-	var dt = (now - this.lastSnapshot) / 1000;
-
-	if (dt > this.autoSaveDelay ||
-		(this.ignoredChanges >= this.autoSaveThreshold &&
-		 dt > this.autoSaveThrottle))
-	{
-		this.save();
-		this.reset();
-	}
-	else
-	{
-		// Increments the number of ignored changes
-		this.ignoredChanges++;
-	}
-};
-
-/**
- * Function: reset
- *
- * Resets all counters.
- */
-mxAutoSaveManager.prototype.reset = function()
-{
-	this.lastSnapshot = new Date().getTime();
-	this.ignoredChanges = 0;
-};
-
-/**
- * Function: destroy
- *
- * Removes all handlers from the  and deletes the reference to it.
- */
-mxAutoSaveManager.prototype.destroy = function()
-{
-	this.setGraph(null);
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- *
- * Class: mxAnimation
- *
- * Implements a basic animation in JavaScript.
- *
- * Constructor: mxAnimation
- *
- * Constructs an animation.
- *
- * Parameters:
- *
- * graph - Reference to the enclosing .
- */
-function mxAnimation(delay)
-{
-	this.delay = (delay != null) ? delay : 20;
-};
-
-/**
- * Extends mxEventSource.
- */
-mxAnimation.prototype = new mxEventSource();
-mxAnimation.prototype.constructor = mxAnimation;
-
-/**
- * Variable: delay
- *
- * Specifies the delay between the animation steps. Defaul is 30ms.
- */
-mxAnimation.prototype.delay = null;
-
-/**
- * Variable: thread
- *
- * Reference to the thread while the animation is running.
- */
-mxAnimation.prototype.thread = null;
-
-/**
- * Function: isRunning
- *
- * Returns true if the animation is running.
- */
-mxAnimation.prototype.isRunning = function()
-{
-	return this.thread != null;
-};
-
-/**
- * Function: startAnimation
- *
- * Starts the animation by repeatedly invoking updateAnimation.
- */
-mxAnimation.prototype.startAnimation = function()
-{
-	if (this.thread == null)
-	{
-		this.thread = window.setInterval(mxUtils.bind(this, this.updateAnimation), this.delay);
-	}
-};
-
-/**
- * Function: updateAnimation
- *
- * Hook for subclassers to implement the animation. Invoke stopAnimation
- * when finished, startAnimation to resume. This is called whenever the
- * timer fires and fires an mxEvent.EXECUTE event with no properties.
- */
-mxAnimation.prototype.updateAnimation = function()
-{
-	this.fireEvent(new mxEventObject(mxEvent.EXECUTE));
-};
-
-/**
- * Function: stopAnimation
- *
- * Stops the animation by deleting the timer and fires an .
- */
-mxAnimation.prototype.stopAnimation = function()
-{
-	if (this.thread != null)
-	{
-		window.clearInterval(this.thread);
-		this.thread = null;
-		this.fireEvent(new mxEventObject(mxEvent.DONE));
-	}
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- *
- * Class: mxMorphing
- *
- * Implements animation for morphing cells. Here is an example of
- * using this class for animating the result of a layout algorithm:
- *
- * (code)
- * graph.getModel().beginUpdate();
- * try
- * {
- *   var circleLayout = new mxCircleLayout(graph);
- *   circleLayout.execute(graph.getDefaultParent());
- * }
- * finally
- * {
- *   var morph = new mxMorphing(graph);
- *   morph.addListener(mxEvent.DONE, function()
- *   {
- *     graph.getModel().endUpdate();
- *   });
- *
- *   morph.startAnimation();
- * }
- * (end)
- *
- * Constructor: mxMorphing
- *
- * Constructs an animation.
- *
- * Parameters:
- *
- * graph - Reference to the enclosing .
- * steps - Optional number of steps in the morphing animation. Default is 6.
- * ease - Optional easing constant for the animation. Default is 1.5.
- * delay - Optional delay between the animation steps. Passed to .
- */
-function mxMorphing(graph, steps, ease, delay)
-{
-	mxAnimation.call(this, delay);
-	this.graph = graph;
-	this.steps = (steps != null) ? steps : 6;
-	this.ease = (ease != null) ? ease : 1.5;
-};
-
-/**
- * Extends mxEventSource.
- */
-mxMorphing.prototype = new mxAnimation();
-mxMorphing.prototype.constructor = mxMorphing;
-
-/**
- * Variable: graph
- *
- * Specifies the delay between the animation steps. Defaul is 30ms.
- */
-mxMorphing.prototype.graph = null;
-
-/**
- * Variable: steps
- *
- * Specifies the maximum number of steps for the morphing.
- */
-mxMorphing.prototype.steps = null;
-
-/**
- * Variable: step
- *
- * Contains the current step.
- */
-mxMorphing.prototype.step = 0;
-
-/**
- * Variable: ease
- *
- * Ease-off for movement towards the given vector. Larger values are
- * slower and smoother. Default is 4.
- */
-mxMorphing.prototype.ease = null;
-
-/**
- * Variable: cells
- *
- * Optional array of cells to be animated. If this is not specified
- * then all cells are checked and animated if they have been moved
- * in the current transaction.
- */
-mxMorphing.prototype.cells = null;
-
-/**
- * Function: updateAnimation
- *
- * Animation step.
- */
-mxMorphing.prototype.updateAnimation = function()
-{
-	mxAnimation.prototype.updateAnimation.apply(this, arguments);
-	var move = new mxCellStatePreview(this.graph);
-
-	if (this.cells != null)
-	{
-		// Animates the given cells individually without recursion
-		for (var i = 0; i < this.cells.length; i++)
-		{
-			this.animateCell(this.cells[i], move, false);
-		}
-	}
-	else
-	{
-		// Animates all changed cells by using recursion to find
-		// the changed cells but not for the animation itself
-		this.animateCell(this.graph.getModel().getRoot(), move, true);
-	}
-
-	this.show(move);
-
-	if (move.isEmpty() || this.step++ >= this.steps)
-	{
-		this.stopAnimation();
-	}
-};
-
-/**
- * Function: show
- *
- * Shows the changes in the given .
- */
-mxMorphing.prototype.show = function(move)
-{
-	move.show();
-};
-
-/**
- * Function: animateCell
- *
- * Animates the given cell state using .
- */
-mxMorphing.prototype.animateCell = function(cell, move, recurse)
-{
-	var state = this.graph.getView().getState(cell);
-	var delta = null;
-
-	if (state != null)
-	{
-		// Moves the animated state from where it will be after the model
-		// change by subtracting the given delta vector from that location
-		delta = this.getDelta(state);
-
-		if (this.graph.getModel().isVertex(cell) && (delta.x != 0 || delta.y != 0))
-		{
-			var translate = this.graph.view.getTranslate();
-			var scale = this.graph.view.getScale();
-
-			delta.x += translate.x * scale;
-			delta.y += translate.y * scale;
-
-			move.moveState(state, -delta.x / this.ease, -delta.y / this.ease);
-		}
-	}
-
-	if (recurse && !this.stopRecursion(state, delta))
-	{
-		var childCount = this.graph.getModel().getChildCount(cell);
-
-		for (var i = 0; i < childCount; i++)
-		{
-			this.animateCell(this.graph.getModel().getChildAt(cell, i), move, recurse);
-		}
-	}
-};
-
-/**
- * Function: stopRecursion
- *
- * Returns true if the animation should not recursively find more
- * deltas for children if the given parent state has been animated.
- */
-mxMorphing.prototype.stopRecursion = function(state, delta)
-{
-	return delta != null && (delta.x != 0 || delta.y != 0);
-};
-
-/**
- * Function: getDelta
- *
- * Returns the vector between the current rendered state and the future
- * location of the state after the display will be updated.
- */
-mxMorphing.prototype.getDelta = function(state)
-{
-	var origin = this.getOriginForCell(state.cell);
-	var translate = this.graph.getView().getTranslate();
-	var scale = this.graph.getView().getScale();
-	var x = state.x / scale - translate.x;
-	var y = state.y / scale - translate.y;
-
-	return new mxPoint((origin.x - x) * scale, (origin.y - y) * scale);
-};
-
-/**
- * Function: getOriginForCell
- *
- * Returns the top, left corner of the given cell. TODO: Improve performance
- * by using caching inside this method as the result per cell never changes
- * during the lifecycle of this object.
- */
-mxMorphing.prototype.getOriginForCell = function(cell)
-{
-	var result = null;
-
-	if (cell != null)
-	{
-		var parent = this.graph.getModel().getParent(cell);
-		var geo = this.graph.getCellGeometry(cell);
-		result = this.getOriginForCell(parent);
-
-		// TODO: Handle offsets
-		if (geo != null)
-		{
-			if (geo.relative)
-			{
-				var pgeo = this.graph.getCellGeometry(parent);
-
-				if (pgeo != null)
-				{
-					result.x += geo.x * pgeo.width;
-					result.y += geo.y * pgeo.height;
-				}
-			}
-			else
-			{
-				result.x += geo.x;
-				result.y += geo.y;
-			}
-		}
-	}
-
-	if (result == null)
-	{
-		var t = this.graph.view.getTranslate();
-		result = new mxPoint(-t.x, -t.y);
-	}
-
-	return result;
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxImageBundle
- *
- * Maps from keys to base64 encoded images or file locations. All values must
- * be URLs or use the format data:image/format followed by a comma and the base64
- * encoded image data, eg. "data:image/gif,XYZ", where XYZ is the base64 encoded
- * image data.
- *
- * To add a new image bundle to an existing graph, the following code is used:
- *
- * (code)
- * var bundle = new mxImageBundle(alt);
- * bundle.putImage('myImage', 'data:image/gif,R0lGODlhEAAQAMIGAAAAAICAAICAgP' +
- *   '//AOzp2O3r2////////yH+FUNyZWF0ZWQgd2l0aCBUaGUgR0lNUAAh+QQBCgAHACwAAAAA' +
- *   'EAAQAAADTXi63AowynnAMDfjPUDlnAAJhmeBFxAEloliKltWmiYCQvfVr6lBPB1ggxN1hi' +
- *   'laSSASFQpIV5HJBDyHpqK2ejVRm2AAgZCdmCGO9CIBADs=', fallback);
- * bundle.putImage('mySvgImage', 'data:image/svg+xml,' + encodeURIComponent(
- *   '' +
- *   '' +
- *   '' +
- *   ''), fallback);
- * graph.addImageBundle(bundle);
- * (end);
- *
- * Alt is an optional boolean (default is false) that specifies if the value
- * or the fallback should be returned in .
- *
- * The image can then be referenced in any cell style using image=myImage.
- * If you are using mxOutline, you should use the same image bundles in the
- * graph that renders the outline.
- *
- * The keys for images are resolved in  and
- * turned into a data URI if the returned value has a short data URI format
- * as specified above.
- *
- * A typical value for the fallback is a MTHML link as defined in RFC 2557.
- * Note that this format requires a file to be dynamically created on the
- * server-side, or the page that contains the graph to be modified to contain
- * the resources, this can be done by adding a comment that contains the
- * resource in the HEAD section of the page after the title tag.
- *
- * This type of fallback mechanism should be used in IE6 and IE7. IE8 does
- * support data URIs, but the maximum size is limited to 32 KB, which means
- * all data URIs should be limited to 32 KB.
- */
-function mxImageBundle(alt)
-{
-	this.images = [];
-	this.alt = (alt != null) ? alt : false;
-};
-
-/**
- * Variable: images
- *
- * Maps from keys to images.
- */
-mxImageBundle.prototype.images = null;
-
-/**
- * Variable: alt
- *
- * Specifies if the fallback representation should be returned.
- */
-mxImageBundle.prototype.images = null;
-
-/**
- * Function: putImage
- *
- * Adds the specified entry to the map. The entry is an object with a value and
- * fallback property as specified in the arguments.
- */
-mxImageBundle.prototype.putImage = function(key, value, fallback)
-{
-	this.images[key] = {value: value, fallback: fallback};
-};
-
-/**
- * Function: getImage
- *
- * Returns the value for the given key. This returns the value
- * or fallback, depending on . The fallback is returned if
- *  is true, the value is returned otherwise.
- */
-mxImageBundle.prototype.getImage = function(key)
-{
-	var result = null;
-
-	if (key != null)
-	{
-		var img = this.images[key];
-
-		if (img != null)
-		{
-			result = (this.alt) ? img.fallback : img.value;
-		}
-	}
-
-	return result;
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxImageExport
- *
- * Creates a new image export instance to be used with an export canvas. Here
- * is an example that uses this class to create an image via a backend using
- * .
- *
- * (code)
- * var xmlDoc = mxUtils.createXmlDocument();
- * var root = xmlDoc.createElement('output');
- * xmlDoc.appendChild(root);
- *
- * var xmlCanvas = new mxXmlCanvas2D(root);
- * var imgExport = new mxImageExport();
- * imgExport.drawState(graph.getView().getState(graph.model.root), xmlCanvas);
- *
- * var bounds = graph.getGraphBounds();
- * var w = Math.ceil(bounds.x + bounds.width);
- * var h = Math.ceil(bounds.y + bounds.height);
- *
- * var xml = mxUtils.getXml(root);
- * new mxXmlRequest('export', 'format=png&w=' + w +
- * 		'&h=' + h + '&bg=#F9F7ED&xml=' + encodeURIComponent(xml))
- * 		.simulate(document, '_blank');
- * (end)
- *
- * Constructor: mxImageExport
- *
- * Constructs a new image export.
- */
-function mxImageExport() { };
-
-/**
- * Variable: includeOverlays
- *
- * Specifies if overlays should be included in the export. Default is false.
- */
-mxImageExport.prototype.includeOverlays = false;
-
-/**
- * Function: drawState
- *
- * Draws the given state and all its descendants to the given canvas.
- */
-mxImageExport.prototype.drawState = function(state, canvas)
-{
-	if (state != null)
-	{
-		this.visitStatesRecursive(state, canvas, mxUtils.bind(this, function()
-		{
-			this.drawCellState.apply(this, arguments);
-		}));
-
-		// Paints the overlays
-		if (this.includeOverlays)
-		{
-			this.visitStatesRecursive(state, canvas, mxUtils.bind(this, function()
-			{
-				this.drawOverlays.apply(this, arguments);
-			}));
-		}
-	}
-};
-
-/**
- * Function: drawState
- *
- * Draws the given state and all its descendants to the given canvas.
- */
-mxImageExport.prototype.visitStatesRecursive = function(state, canvas, visitor)
-{
-	if (state != null)
-	{
-		visitor(state, canvas);
-
-		var graph = state.view.graph;
-		var childCount = graph.model.getChildCount(state.cell);
-
-		for (var i = 0; i < childCount; i++)
-		{
-			var childState = graph.view.getState(graph.model.getChildAt(state.cell, i));
-			this.visitStatesRecursive(childState, canvas, visitor);
-		}
-	}
-};
-
-/**
- * Function: getLinkForCellState
- *
- * Returns the link for the given cell state and canvas. This returns null.
- */
-mxImageExport.prototype.getLinkForCellState = function(state, canvas)
-{
-	return null;
-};
-
-/**
- * Function: drawCellState
- *
- * Draws the given state to the given canvas.
- */
-mxImageExport.prototype.drawCellState = function(state, canvas)
-{
-	// Experimental feature
-	var link = this.getLinkForCellState(state, canvas);
-
-	if (link != null)
-	{
-		canvas.setLink(link);
-	}
-
-	// Paints the shape and text
-	this.drawShape(state, canvas);
-	this.drawText(state, canvas);
-
-	if (link != null)
-	{
-		canvas.setLink(null);
-	}
-};
-
-/**
- * Function: drawShape
- *
- * Draws the shape of the given state.
- */
-mxImageExport.prototype.drawShape = function(state, canvas)
-{
-	if (state.shape instanceof mxShape && state.shape.checkBounds())
-	{
-		canvas.save();
-		state.shape.paint(canvas);
-		canvas.restore();
-	}
-};
-
-/**
- * Function: drawText
- *
- * Draws the text of the given state.
- */
-mxImageExport.prototype.drawText = function(state, canvas)
-{
-	if (state.text != null && state.text.checkBounds())
-	{
-		canvas.save();
-		state.text.paint(canvas);
-		canvas.restore();
-	}
-};
-
-/**
- * Function: drawOverlays
- *
- * Draws the overlays for the given state. This is called if 
- * is true.
- */
-mxImageExport.prototype.drawOverlays = function(state, canvas)
-{
-	if (state.overlays != null)
-	{
-		state.overlays.visit(function(id, shape)
-		{
-			if (shape instanceof mxShape)
-			{
-				shape.paint(canvas);
-			}
-		});
-	}
-};
-
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxAbstractCanvas2D
- *
- * Base class for all canvases. A description of the public API is available in .
- * All color values of  will be converted to null in the state.
- *
- * Constructor: mxAbstractCanvas2D
- *
- * Constructs a new abstract canvas.
- */
-function mxAbstractCanvas2D()
-{
-	/**
-	 * Variable: converter
-	 *
-	 * Holds the  to convert image URLs.
-	 */
-	this.converter = this.createUrlConverter();
-
-	this.reset();
-};
-
-/**
- * Variable: state
- *
- * Holds the current state.
- */
-mxAbstractCanvas2D.prototype.state = null;
-
-/**
- * Variable: states
- *
- * Stack of states.
- */
-mxAbstractCanvas2D.prototype.states = null;
-
-/**
- * Variable: path
- *
- * Holds the current path as an array.
- */
-mxAbstractCanvas2D.prototype.path = null;
-
-/**
- * Variable: rotateHtml
- *
- * Switch for rotation of HTML. Default is false.
- */
-mxAbstractCanvas2D.prototype.rotateHtml = true;
-
-/**
- * Variable: lastX
- *
- * Holds the last x coordinate.
- */
-mxAbstractCanvas2D.prototype.lastX = 0;
-
-/**
- * Variable: lastY
- *
- * Holds the last y coordinate.
- */
-mxAbstractCanvas2D.prototype.lastY = 0;
-
-/**
- * Variable: moveOp
- *
- * Contains the string used for moving in paths. Default is 'M'.
- */
-mxAbstractCanvas2D.prototype.moveOp = 'M';
-
-/**
- * Variable: lineOp
- *
- * Contains the string used for moving in paths. Default is 'L'.
- */
-mxAbstractCanvas2D.prototype.lineOp = 'L';
-
-/**
- * Variable: quadOp
- *
- * Contains the string used for quadratic paths. Default is 'Q'.
- */
-mxAbstractCanvas2D.prototype.quadOp = 'Q';
-
-/**
- * Variable: curveOp
- *
- * Contains the string used for bezier curves. Default is 'C'.
- */
-mxAbstractCanvas2D.prototype.curveOp = 'C';
-
-/**
- * Variable: closeOp
- *
- * Holds the operator for closing curves. Default is 'Z'.
- */
-mxAbstractCanvas2D.prototype.closeOp = 'Z';
-
-/**
- * Variable: pointerEvents
- *
- * Boolean value that specifies if events should be handled. Default is false.
- */
-mxAbstractCanvas2D.prototype.pointerEvents = false;
-
-/**
- * Function: createUrlConverter
- *
- * Create a new  and returns it.
- */
-mxAbstractCanvas2D.prototype.createUrlConverter = function()
-{
-	return new mxUrlConverter();
-};
-
-/**
- * Function: reset
- *
- * Resets the state of this canvas.
- */
-mxAbstractCanvas2D.prototype.reset = function()
-{
-	this.state = this.createState();
-	this.states = [];
-};
-
-/**
- * Function: createState
- *
- * Creates the state of the this canvas.
- */
-mxAbstractCanvas2D.prototype.createState = function()
-{
-	return {
-		dx: 0,
-		dy: 0,
-		scale: 1,
-		alpha: 1,
-		fillAlpha: 1,
-		strokeAlpha: 1,
-		fillColor: null,
-		gradientFillAlpha: 1,
-		gradientColor: null,
-		gradientAlpha: 1,
-		gradientDirection: null,
-		strokeColor: null,
-		strokeWidth: 1,
-		dashed: false,
-		dashPattern: '3 3',
-		fixDash: false,
-		lineCap: 'flat',
-		lineJoin: 'miter',
-		miterLimit: 10,
-		fontColor: '#000000',
-		fontBackgroundColor: null,
-		fontBorderColor: null,
-		fontSize: mxConstants.DEFAULT_FONTSIZE,
-		fontFamily: mxConstants.DEFAULT_FONTFAMILY,
-		fontStyle: 0,
-		shadow: false,
-		shadowColor: mxConstants.SHADOWCOLOR,
-		shadowAlpha: mxConstants.SHADOW_OPACITY,
-		shadowDx: mxConstants.SHADOW_OFFSET_X,
-		shadowDy: mxConstants.SHADOW_OFFSET_Y,
-		rotation: 0,
-		rotationCx: 0,
-		rotationCy: 0
-	};
-};
-
-/**
- * Function: format
- *
- * Rounds all numbers to integers.
- */
-mxAbstractCanvas2D.prototype.format = function(value)
-{
-	return Math.round(parseFloat(value));
-};
-
-/**
- * Function: addOp
- *
- * Adds the given operation to the path.
- */
-mxAbstractCanvas2D.prototype.addOp = function()
-{
-	if (this.path != null)
-	{
-		this.path.push(arguments[0]);
-
-		if (arguments.length > 2)
-		{
-			var s = this.state;
-
-			for (var i = 2; i < arguments.length; i += 2)
-			{
-				this.lastX = arguments[i - 1];
-				this.lastY = arguments[i];
-
-				this.path.push(this.format((this.lastX + s.dx) * s.scale));
-				this.path.push(this.format((this.lastY + s.dy) * s.scale));
-			}
-		}
-	}
-};
-
-/**
- * Function: rotatePoint
- *
- * Rotates the given point and returns the result as an .
- */
-mxAbstractCanvas2D.prototype.rotatePoint = function(x, y, theta, cx, cy)
-{
-	var rad = theta * (Math.PI / 180);
-
-	return mxUtils.getRotatedPoint(new mxPoint(x, y), Math.cos(rad),
-		Math.sin(rad), new mxPoint(cx, cy));
-};
-
-/**
- * Function: save
- *
- * Saves the current state.
- */
-mxAbstractCanvas2D.prototype.save = function()
-{
-	this.states.push(this.state);
-	this.state = mxUtils.clone(this.state);
-};
-
-/**
- * Function: restore
- *
- * Restores the current state.
- */
-mxAbstractCanvas2D.prototype.restore = function()
-{
-	if (this.states.length > 0)
-	{
-		this.state = this.states.pop();
-	}
-};
-
-/**
- * Function: setLink
- *
- * Sets the current link. Hook for subclassers.
- */
-mxAbstractCanvas2D.prototype.setLink = function(link)
-{
-	// nop
-};
-
-/**
- * Function: scale
- *
- * Scales the current state.
- */
-mxAbstractCanvas2D.prototype.scale = function(value)
-{
-	this.state.scale *= value;
-	this.state.strokeWidth *= value;
-};
-
-/**
- * Function: translate
- *
- * Translates the current state.
- */
-mxAbstractCanvas2D.prototype.translate = function(dx, dy)
-{
-	this.state.dx += dx;
-	this.state.dy += dy;
-};
-
-/**
- * Function: rotate
- *
- * Rotates the current state.
- */
-mxAbstractCanvas2D.prototype.rotate = function(theta, flipH, flipV, cx, cy)
-{
-	// nop
-};
-
-/**
- * Function: setAlpha
- *
- * Sets the current alpha.
- */
-mxAbstractCanvas2D.prototype.setAlpha = function(value)
-{
-	this.state.alpha = value;
-};
-
-/**
- * Function: setFillAlpha
- *
- * Sets the current solid fill alpha.
- */
-mxAbstractCanvas2D.prototype.setFillAlpha = function(value)
-{
-	this.state.fillAlpha = value;
-};
-
-/**
- * Function: setStrokeAlpha
- *
- * Sets the current stroke alpha.
- */
-mxAbstractCanvas2D.prototype.setStrokeAlpha = function(value)
-{
-	this.state.strokeAlpha = value;
-};
-
-/**
- * Function: setFillColor
- *
- * Sets the current fill color.
- */
-mxAbstractCanvas2D.prototype.setFillColor = function(value)
-{
-	if (value == mxConstants.NONE)
-	{
-		value = null;
-	}
-
-	this.state.fillColor = value;
-	this.state.gradientColor = null;
-};
-
-/**
- * Function: setGradient
- *
- * Sets the current gradient.
- */
-mxAbstractCanvas2D.prototype.setGradient = function(color1, color2, x, y, w, h, direction, alpha1, alpha2)
-{
-	var s = this.state;
-	s.fillColor = color1;
-	s.gradientFillAlpha = (alpha1 != null) ? alpha1 : 1;
-	s.gradientColor = color2;
-	s.gradientAlpha = (alpha2 != null) ? alpha2 : 1;
-	s.gradientDirection = direction;
-};
-
-/**
- * Function: setStrokeColor
- *
- * Sets the current stroke color.
- */
-mxAbstractCanvas2D.prototype.setStrokeColor = function(value)
-{
-	if (value == mxConstants.NONE)
-	{
-		value = null;
-	}
-
-	this.state.strokeColor = value;
-};
-
-/**
- * Function: setStrokeWidth
- *
- * Sets the current stroke width.
- */
-mxAbstractCanvas2D.prototype.setStrokeWidth = function(value)
-{
-	this.state.strokeWidth = value;
-};
-
-/**
- * Function: setDashed
- *
- * Enables or disables dashed lines.
- */
-mxAbstractCanvas2D.prototype.setDashed = function(value, fixDash)
-{
-	this.state.dashed = value;
-	this.state.fixDash = fixDash;
-};
-
-/**
- * Function: setDashPattern
- *
- * Sets the current dash pattern.
- */
-mxAbstractCanvas2D.prototype.setDashPattern = function(value)
-{
-	this.state.dashPattern = value;
-};
-
-/**
- * Function: setLineCap
- *
- * Sets the current line cap.
- */
-mxAbstractCanvas2D.prototype.setLineCap = function(value)
-{
-	this.state.lineCap = value;
-};
-
-/**
- * Function: setLineJoin
- *
- * Sets the current line join.
- */
-mxAbstractCanvas2D.prototype.setLineJoin = function(value)
-{
-	this.state.lineJoin = value;
-};
-
-/**
- * Function: setMiterLimit
- *
- * Sets the current miter limit.
- */
-mxAbstractCanvas2D.prototype.setMiterLimit = function(value)
-{
-	this.state.miterLimit = value;
-};
-
-/**
- * Function: setFontColor
- *
- * Sets the current font color.
- */
-mxAbstractCanvas2D.prototype.setFontColor = function(value)
-{
-	if (value == mxConstants.NONE)
-	{
-		value = null;
-	}
-
-	this.state.fontColor = value;
-};
-
-/**
- * Function: setFontColor
- *
- * Sets the current font color.
- */
-mxAbstractCanvas2D.prototype.setFontBackgroundColor = function(value)
-{
-	if (value == mxConstants.NONE)
-	{
-		value = null;
-	}
-
-	this.state.fontBackgroundColor = value;
-};
-
-/**
- * Function: setFontColor
- *
- * Sets the current font color.
- */
-mxAbstractCanvas2D.prototype.setFontBorderColor = function(value)
-{
-	if (value == mxConstants.NONE)
-	{
-		value = null;
-	}
-
-	this.state.fontBorderColor = value;
-};
-
-/**
- * Function: setFontSize
- *
- * Sets the current font size.
- */
-mxAbstractCanvas2D.prototype.setFontSize = function(value)
-{
-	this.state.fontSize = parseFloat(value);
-};
-
-/**
- * Function: setFontFamily
- *
- * Sets the current font family.
- */
-mxAbstractCanvas2D.prototype.setFontFamily = function(value)
-{
-	this.state.fontFamily = value;
-};
-
-/**
- * Function: setFontStyle
- *
- * Sets the current font style.
- */
-mxAbstractCanvas2D.prototype.setFontStyle = function(value)
-{
-	if (value == null)
-	{
-		value = 0;
-	}
-
-	this.state.fontStyle = value;
-};
-
-/**
- * Function: setShadow
- *
- * Enables or disables and configures the current shadow.
- */
-mxAbstractCanvas2D.prototype.setShadow = function(enabled)
-{
-	this.state.shadow = enabled;
-};
-
-/**
- * Function: setShadowColor
- *
- * Enables or disables and configures the current shadow.
- */
-mxAbstractCanvas2D.prototype.setShadowColor = function(value)
-{
-	if (value == mxConstants.NONE)
-	{
-		value = null;
-	}
-
-	this.state.shadowColor = value;
-};
-
-/**
- * Function: setShadowAlpha
- *
- * Enables or disables and configures the current shadow.
- */
-mxAbstractCanvas2D.prototype.setShadowAlpha = function(value)
-{
-	this.state.shadowAlpha = value;
-};
-
-/**
- * Function: setShadowOffset
- *
- * Enables or disables and configures the current shadow.
- */
-mxAbstractCanvas2D.prototype.setShadowOffset = function(dx, dy)
-{
-	this.state.shadowDx = dx;
-	this.state.shadowDy = dy;
-};
-
-/**
- * Function: begin
- *
- * Starts a new path.
- */
-mxAbstractCanvas2D.prototype.begin = function()
-{
-	this.lastX = 0;
-	this.lastY = 0;
-	this.path = [];
-};
-
-/**
- * Function: moveTo
- *
- *  Moves the current path the given coordinates.
- */
-mxAbstractCanvas2D.prototype.moveTo = function(x, y)
-{
-	this.addOp(this.moveOp, x, y);
-};
-
-/**
- * Function: lineTo
- *
- * Draws a line to the given coordinates. Uses moveTo with the op argument.
- */
-mxAbstractCanvas2D.prototype.lineTo = function(x, y)
-{
-	this.addOp(this.lineOp, x, y);
-};
-
-/**
- * Function: quadTo
- *
- * Adds a quadratic curve to the current path.
- */
-mxAbstractCanvas2D.prototype.quadTo = function(x1, y1, x2, y2)
-{
-	this.addOp(this.quadOp, x1, y1, x2, y2);
-};
-
-/**
- * Function: curveTo
- *
- * Adds a bezier curve to the current path.
- */
-mxAbstractCanvas2D.prototype.curveTo = function(x1, y1, x2, y2, x3, y3)
-{
-	this.addOp(this.curveOp, x1, y1, x2, y2, x3, y3);
-};
-
-/**
- * Function: arcTo
- *
- * Adds the given arc to the current path. This is a synthetic operation that
- * is broken down into curves.
- */
-mxAbstractCanvas2D.prototype.arcTo = function(rx, ry, angle, largeArcFlag, sweepFlag, x, y)
-{
-	var curves = mxUtils.arcToCurves(this.lastX, this.lastY, rx, ry, angle, largeArcFlag, sweepFlag, x, y);
-
-	if (curves != null)
-	{
-		for (var i = 0; i < curves.length; i += 6)
-		{
-			this.curveTo(curves[i], curves[i + 1], curves[i + 2],
-				curves[i + 3], curves[i + 4], curves[i + 5]);
-		}
-	}
-};
-
-/**
- * Function: close
- *
- * Closes the current path.
- */
-mxAbstractCanvas2D.prototype.close = function(x1, y1, x2, y2, x3, y3)
-{
-	this.addOp(this.closeOp);
-};
-
-/**
- * Function: end
- *
- * Empty implementation for backwards compatibility. This will be removed.
- */
-mxAbstractCanvas2D.prototype.end = function() { };
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxXmlCanvas2D
- *
- * Base class for all canvases. The following methods make up the public
- * interface of the canvas 2D for all painting in mxGraph:
- *
- * - , 
- * - , , 
- * - , , , , ,
- *   , , , , ,
- *   , 
- * - , , , ,
- *   , 
- * - , , , 
- * - , , , , 
- * - , , , , 
- * - , , 
- *
- *  is an additional method for drawing paths. This is
- * a synthetic method, meaning that it is turned into a sequence of curves by
- * default. Subclassers may add native support for arcs.
- *
- * Constructor: mxXmlCanvas2D
- *
- * Constructs a new abstract canvas.
- */
-function mxXmlCanvas2D(root)
-{
-	mxAbstractCanvas2D.call(this);
-
-	/**
-	 * Variable: root
-	 *
-	 * Reference to the container for the SVG content.
-	 */
-	this.root = root;
-
-	// Writes default settings;
-	this.writeDefaults();
-};
-
-/**
- * Extends mxAbstractCanvas2D
- */
-mxUtils.extend(mxXmlCanvas2D, mxAbstractCanvas2D);
-
-/**
- * Variable: textEnabled
- *
- * Specifies if text output should be enabled. Default is true.
- */
-mxXmlCanvas2D.prototype.textEnabled = true;
-
-/**
- * Variable: compressed
- *
- * Specifies if the output should be compressed by removing redundant calls.
- * Default is true.
- */
-mxXmlCanvas2D.prototype.compressed = true;
-
-/**
- * Function: writeDefaults
- *
- * Writes the rendering defaults to :
- */
-mxXmlCanvas2D.prototype.writeDefaults = function()
-{
-	var elem;
-
-	// Writes font defaults
-	elem = this.createElement('fontfamily');
-	elem.setAttribute('family', mxConstants.DEFAULT_FONTFAMILY);
-	this.root.appendChild(elem);
-
-	elem = this.createElement('fontsize');
-	elem.setAttribute('size', mxConstants.DEFAULT_FONTSIZE);
-	this.root.appendChild(elem);
-
-	// Writes shadow defaults
-	elem = this.createElement('shadowcolor');
-	elem.setAttribute('color', mxConstants.SHADOWCOLOR);
-	this.root.appendChild(elem);
-
-	elem = this.createElement('shadowalpha');
-	elem.setAttribute('alpha', mxConstants.SHADOW_OPACITY);
-	this.root.appendChild(elem);
-
-	elem = this.createElement('shadowoffset');
-	elem.setAttribute('dx', mxConstants.SHADOW_OFFSET_X);
-	elem.setAttribute('dy', mxConstants.SHADOW_OFFSET_Y);
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: format
- *
- * Returns a formatted number with 2 decimal places.
- */
-mxXmlCanvas2D.prototype.format = function(value)
-{
-	return parseFloat(parseFloat(value).toFixed(2));
-};
-
-/**
- * Function: createElement
- *
- * Creates the given element using the owner document of .
- */
-mxXmlCanvas2D.prototype.createElement = function(name)
-{
-	return this.root.ownerDocument.createElement(name);
-};
-
-/**
- * Function: save
- *
- * Saves the drawing state.
- */
-mxXmlCanvas2D.prototype.save = function()
-{
-	if (this.compressed)
-	{
-		mxAbstractCanvas2D.prototype.save.apply(this, arguments);
-	}
-
-	this.root.appendChild(this.createElement('save'));
-};
-
-/**
- * Function: restore
- *
- * Restores the drawing state.
- */
-mxXmlCanvas2D.prototype.restore = function()
-{
-	if (this.compressed)
-	{
-		mxAbstractCanvas2D.prototype.restore.apply(this, arguments);
-	}
-
-	this.root.appendChild(this.createElement('restore'));
-};
-
-/**
- * Function: scale
- *
- * Scales the output.
- *
- * Parameters:
- *
- * scale - Number that represents the scale where 1 is equal to 100%.
- */
-mxXmlCanvas2D.prototype.scale = function(value)
-{
-        var elem = this.createElement('scale');
-        elem.setAttribute('scale', value);
-        this.root.appendChild(elem);
-};
-
-/**
- * Function: translate
- *
- * Translates the output.
- *
- * Parameters:
- *
- * dx - Number that specifies the horizontal translation.
- * dy - Number that specifies the vertical translation.
- */
-mxXmlCanvas2D.prototype.translate = function(dx, dy)
-{
-	var elem = this.createElement('translate');
-	elem.setAttribute('dx', this.format(dx));
-	elem.setAttribute('dy', this.format(dy));
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: rotate
- *
- * Rotates and/or flips the output around a given center. (Note: Due to
- * limitations in VML, the rotation cannot be concatenated.)
- *
- * Parameters:
- *
- * theta - Number that represents the angle of the rotation (in degrees).
- * flipH - Boolean indicating if the output should be flipped horizontally.
- * flipV - Boolean indicating if the output should be flipped vertically.
- * cx - Number that represents the x-coordinate of the rotation center.
- * cy - Number that represents the y-coordinate of the rotation center.
- */
-mxXmlCanvas2D.prototype.rotate = function(theta, flipH, flipV, cx, cy)
-{
-	var elem = this.createElement('rotate');
-
-	if (theta != 0 || flipH || flipV)
-	{
-		elem.setAttribute('theta', this.format(theta));
-		elem.setAttribute('flipH', (flipH) ? '1' : '0');
-		elem.setAttribute('flipV', (flipV) ? '1' : '0');
-		elem.setAttribute('cx', this.format(cx));
-		elem.setAttribute('cy', this.format(cy));
-		this.root.appendChild(elem);
-	}
-};
-
-/**
- * Function: setAlpha
- *
- * Sets the current alpha.
- *
- * Parameters:
- *
- * value - Number that represents the new alpha. Possible values are between
- * 1 (opaque) and 0 (transparent).
- */
-mxXmlCanvas2D.prototype.setAlpha = function(value)
-{
-	if (this.compressed)
-	{
-		if (this.state.alpha == value)
-		{
-			return;
-		}
-
-		mxAbstractCanvas2D.prototype.setAlpha.apply(this, arguments);
-	}
-
-	var elem = this.createElement('alpha');
-	elem.setAttribute('alpha', this.format(value));
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setFillAlpha
- *
- * Sets the current fill alpha.
- *
- * Parameters:
- *
- * value - Number that represents the new fill alpha. Possible values are between
- * 1 (opaque) and 0 (transparent).
- */
-mxXmlCanvas2D.prototype.setFillAlpha = function(value)
-{
-	if (this.compressed)
-	{
-		if (this.state.fillAlpha == value)
-		{
-			return;
-		}
-
-		mxAbstractCanvas2D.prototype.setFillAlpha.apply(this, arguments);
-	}
-
-	var elem = this.createElement('fillalpha');
-	elem.setAttribute('alpha', this.format(value));
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setStrokeAlpha
- *
- * Sets the current stroke alpha.
- *
- * Parameters:
- *
- * value - Number that represents the new stroke alpha. Possible values are between
- * 1 (opaque) and 0 (transparent).
- */
-mxXmlCanvas2D.prototype.setStrokeAlpha = function(value)
-{
-	if (this.compressed)
-	{
-		if (this.state.strokeAlpha == value)
-		{
-			return;
-		}
-
-		mxAbstractCanvas2D.prototype.setStrokeAlpha.apply(this, arguments);
-	}
-
-	var elem = this.createElement('strokealpha');
-	elem.setAttribute('alpha', this.format(value));
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setFillColor
- *
- * Sets the current fill color.
- *
- * Parameters:
- *
- * value - Hexadecimal representation of the color or 'none'.
- */
-mxXmlCanvas2D.prototype.setFillColor = function(value)
-{
-	if (value == mxConstants.NONE)
-	{
-		value = null;
-	}
-
-	if (this.compressed)
-	{
-		if (this.state.fillColor == value)
-		{
-			return;
-		}
-
-		mxAbstractCanvas2D.prototype.setFillColor.apply(this, arguments);
-	}
-
-	var elem = this.createElement('fillcolor');
-	elem.setAttribute('color', (value != null) ? value : mxConstants.NONE);
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setGradient
- *
- * Sets the gradient. Note that the coordinates may be ignored by some implementations.
- *
- * Parameters:
- *
- * color1 - Hexadecimal representation of the start color.
- * color2 - Hexadecimal representation of the end color.
- * x - X-coordinate of the gradient region.
- * y - y-coordinate of the gradient region.
- * w - Width of the gradient region.
- * h - Height of the gradient region.
- * direction - One of , ,
- *  or .
- * alpha1 - Optional alpha of the start color. Default is 1. Possible values
- * are between 1 (opaque) and 0 (transparent).
- * alpha2 - Optional alpha of the end color. Default is 1. Possible values
- * are between 1 (opaque) and 0 (transparent).
- */
-mxXmlCanvas2D.prototype.setGradient = function(color1, color2, x, y, w, h, direction, alpha1, alpha2)
-{
-	if (color1 != null && color2 != null)
-	{
-		mxAbstractCanvas2D.prototype.setGradient.apply(this, arguments);
-
-		var elem = this.createElement('gradient');
-		elem.setAttribute('c1', color1);
-		elem.setAttribute('c2', color2);
-		elem.setAttribute('x', this.format(x));
-		elem.setAttribute('y', this.format(y));
-		elem.setAttribute('w', this.format(w));
-		elem.setAttribute('h', this.format(h));
-
-		// Default direction is south
-		if (direction != null)
-		{
-			elem.setAttribute('direction', direction);
-		}
-
-		if (alpha1 != null)
-		{
-			elem.setAttribute('alpha1', alpha1);
-		}
-
-		if (alpha2 != null)
-		{
-			elem.setAttribute('alpha2', alpha2);
-		}
-
-		this.root.appendChild(elem);
-	}
-};
-
-/**
- * Function: setStrokeColor
- *
- * Sets the current stroke color.
- *
- * Parameters:
- *
- * value - Hexadecimal representation of the color or 'none'.
- */
-mxXmlCanvas2D.prototype.setStrokeColor = function(value)
-{
-	if (value == mxConstants.NONE)
-	{
-		value = null;
-	}
-
-	if (this.compressed)
-	{
-		if (this.state.strokeColor == value)
-		{
-			return;
-		}
-
-		mxAbstractCanvas2D.prototype.setStrokeColor.apply(this, arguments);
-	}
-
-	var elem = this.createElement('strokecolor');
-	elem.setAttribute('color', (value != null) ? value : mxConstants.NONE);
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setStrokeWidth
- *
- * Sets the current stroke width.
- *
- * Parameters:
- *
- * value - Numeric representation of the stroke width.
- */
-mxXmlCanvas2D.prototype.setStrokeWidth = function(value)
-{
-	if (this.compressed)
-	{
-		if (this.state.strokeWidth == value)
-		{
-			return;
-		}
-
-		mxAbstractCanvas2D.prototype.setStrokeWidth.apply(this, arguments);
-	}
-
-	var elem = this.createElement('strokewidth');
-	elem.setAttribute('width', this.format(value));
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setDashed
- *
- * Enables or disables dashed lines.
- *
- * Parameters:
- *
- * value - Boolean that specifies if dashed lines should be enabled.
- * value - Boolean that specifies if the stroke width should be ignored
- * for the dash pattern. Default is false.
- */
-mxXmlCanvas2D.prototype.setDashed = function(value, fixDash)
-{
-	if (this.compressed)
-	{
-		if (this.state.dashed == value)
-		{
-			return;
-		}
-
-		mxAbstractCanvas2D.prototype.setDashed.apply(this, arguments);
-	}
-
-	var elem = this.createElement('dashed');
-	elem.setAttribute('dashed', (value) ? '1' : '0');
-
-	if (fixDash != null)
-	{
-		elem.setAttribute('fixDash', (fixDash) ? '1' : '0');
-	}
-
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setDashPattern
- *
- * Sets the current dash pattern. Default is '3 3'.
- *
- * Parameters:
- *
- * value - String that represents the dash pattern, which is a sequence of
- * numbers defining the length of the dashes and the length of the spaces
- * between the dashes. The lengths are relative to the line width - a length
- * of 1 is equals to the line width.
- */
-mxXmlCanvas2D.prototype.setDashPattern = function(value)
-{
-	if (this.compressed)
-	{
-		if (this.state.dashPattern == value)
-		{
-			return;
-		}
-
-		mxAbstractCanvas2D.prototype.setDashPattern.apply(this, arguments);
-	}
-
-	var elem = this.createElement('dashpattern');
-	elem.setAttribute('pattern', value);
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setLineCap
- *
- * Sets the line cap. Default is 'flat' which corresponds to 'butt' in SVG.
- *
- * Parameters:
- *
- * value - String that represents the line cap. Possible values are flat, round
- * and square.
- */
-mxXmlCanvas2D.prototype.setLineCap = function(value)
-{
-	if (this.compressed)
-	{
-		if (this.state.lineCap == value)
-		{
-			return;
-		}
-
-		mxAbstractCanvas2D.prototype.setLineCap.apply(this, arguments);
-	}
-
-	var elem = this.createElement('linecap');
-	elem.setAttribute('cap', value);
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setLineJoin
- *
- * Sets the line join. Default is 'miter'.
- *
- * Parameters:
- *
- * value - String that represents the line join. Possible values are miter,
- * round and bevel.
- */
-mxXmlCanvas2D.prototype.setLineJoin = function(value)
-{
-	if (this.compressed)
-	{
-		if (this.state.lineJoin == value)
-		{
-			return;
-		}
-
-		mxAbstractCanvas2D.prototype.setLineJoin.apply(this, arguments);
-	}
-
-	var elem = this.createElement('linejoin');
-	elem.setAttribute('join', value);
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setMiterLimit
- *
- * Sets the miter limit. Default is 10.
- *
- * Parameters:
- *
- * value - Number that represents the miter limit.
- */
-mxXmlCanvas2D.prototype.setMiterLimit = function(value)
-{
-	if (this.compressed)
-	{
-		if (this.state.miterLimit == value)
-		{
-			return;
-		}
-
-		mxAbstractCanvas2D.prototype.setMiterLimit.apply(this, arguments);
-	}
-
-	var elem = this.createElement('miterlimit');
-	elem.setAttribute('limit', value);
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setFontColor
- *
- * Sets the current font color. Default is '#000000'.
- *
- * Parameters:
- *
- * value - Hexadecimal representation of the color or 'none'.
- */
-mxXmlCanvas2D.prototype.setFontColor = function(value)
-{
-	if (this.textEnabled)
-	{
-		if (value == mxConstants.NONE)
-		{
-			value = null;
-		}
-
-		if (this.compressed)
-		{
-			if (this.state.fontColor == value)
-			{
-				return;
-			}
-
-			mxAbstractCanvas2D.prototype.setFontColor.apply(this, arguments);
-		}
-
-		var elem = this.createElement('fontcolor');
-		elem.setAttribute('color', (value != null) ? value : mxConstants.NONE);
-		this.root.appendChild(elem);
-	}
-};
-
-/**
- * Function: setFontBackgroundColor
- *
- * Sets the current font background color.
- *
- * Parameters:
- *
- * value - Hexadecimal representation of the color or 'none'.
- */
-mxXmlCanvas2D.prototype.setFontBackgroundColor = function(value)
-{
-	if (this.textEnabled)
-	{
-		if (value == mxConstants.NONE)
-		{
-			value = null;
-		}
-
-		if (this.compressed)
-		{
-			if (this.state.fontBackgroundColor == value)
-			{
-				return;
-			}
-
-			mxAbstractCanvas2D.prototype.setFontBackgroundColor.apply(this, arguments);
-		}
-
-		var elem = this.createElement('fontbackgroundcolor');
-		elem.setAttribute('color', (value != null) ? value : mxConstants.NONE);
-		this.root.appendChild(elem);
-	}
-};
-
-/**
- * Function: setFontBorderColor
- *
- * Sets the current font border color.
- *
- * Parameters:
- *
- * value - Hexadecimal representation of the color or 'none'.
- */
-mxXmlCanvas2D.prototype.setFontBorderColor = function(value)
-{
-	if (this.textEnabled)
-	{
-		if (value == mxConstants.NONE)
-		{
-			value = null;
-		}
-
-		if (this.compressed)
-		{
-			if (this.state.fontBorderColor == value)
-			{
-				return;
-			}
-
-			mxAbstractCanvas2D.prototype.setFontBorderColor.apply(this, arguments);
-		}
-
-		var elem = this.createElement('fontbordercolor');
-		elem.setAttribute('color', (value != null) ? value : mxConstants.NONE);
-		this.root.appendChild(elem);
-	}
-};
-
-/**
- * Function: setFontSize
- *
- * Sets the current font size. Default is .
- *
- * Parameters:
- *
- * value - Numeric representation of the font size.
- */
-mxXmlCanvas2D.prototype.setFontSize = function(value)
-{
-	if (this.textEnabled)
-	{
-		if (this.compressed)
-		{
-			if (this.state.fontSize == value)
-			{
-				return;
-			}
-
-			mxAbstractCanvas2D.prototype.setFontSize.apply(this, arguments);
-		}
-
-		var elem = this.createElement('fontsize');
-		elem.setAttribute('size', value);
-		this.root.appendChild(elem);
-	}
-};
-
-/**
- * Function: setFontFamily
- *
- * Sets the current font family. Default is .
- *
- * Parameters:
- *
- * value - String representation of the font family. This handles the same
- * values as the CSS font-family property.
- */
-mxXmlCanvas2D.prototype.setFontFamily = function(value)
-{
-	if (this.textEnabled)
-	{
-		if (this.compressed)
-		{
-			if (this.state.fontFamily == value)
-			{
-				return;
-			}
-
-			mxAbstractCanvas2D.prototype.setFontFamily.apply(this, arguments);
-		}
-
-		var elem = this.createElement('fontfamily');
-		elem.setAttribute('family', value);
-		this.root.appendChild(elem);
-	}
-};
-
-/**
- * Function: setFontStyle
- *
- * Sets the current font style.
- *
- * Parameters:
- *
- * value - Numeric representation of the font family. This is the sum of the
- * font styles from .
- */
-mxXmlCanvas2D.prototype.setFontStyle = function(value)
-{
-	if (this.textEnabled)
-	{
-		if (value == null)
-		{
-			value = 0;
-		}
-
-		if (this.compressed)
-		{
-			if (this.state.fontStyle == value)
-			{
-				return;
-			}
-
-			mxAbstractCanvas2D.prototype.setFontStyle.apply(this, arguments);
-		}
-
-		var elem = this.createElement('fontstyle');
-		elem.setAttribute('style', value);
-		this.root.appendChild(elem);
-	}
-};
-
-/**
- * Function: setShadow
- *
- * Enables or disables shadows.
- *
- * Parameters:
- *
- * value - Boolean that specifies if shadows should be enabled.
- */
-mxXmlCanvas2D.prototype.setShadow = function(value)
-{
-	if (this.compressed)
-	{
-		if (this.state.shadow == value)
-		{
-			return;
-		}
-
-		mxAbstractCanvas2D.prototype.setShadow.apply(this, arguments);
-	}
-
-	var elem = this.createElement('shadow');
-	elem.setAttribute('enabled', (value) ? '1' : '0');
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setShadowColor
- *
- * Sets the current shadow color. Default is .
- *
- * Parameters:
- *
- * value - Hexadecimal representation of the color or 'none'.
- */
-mxXmlCanvas2D.prototype.setShadowColor = function(value)
-{
-	if (this.compressed)
-	{
-		if (value == mxConstants.NONE)
-		{
-			value = null;
-		}
-
-		if (this.state.shadowColor == value)
-		{
-			return;
-		}
-
-		mxAbstractCanvas2D.prototype.setShadowColor.apply(this, arguments);
-	}
-
-	var elem = this.createElement('shadowcolor');
-	elem.setAttribute('color', (value != null) ? value : mxConstants.NONE);
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: setShadowAlpha
- *
- * Sets the current shadows alpha. Default is .
- *
- * Parameters:
- *
- * value - Number that represents the new alpha. Possible values are between
- * 1 (opaque) and 0 (transparent).
- */
-mxXmlCanvas2D.prototype.setShadowAlpha = function(value)
-{
-	if (this.compressed)
-	{
-		if (this.state.shadowAlpha == value)
-		{
-			return;
-		}
-
-		mxAbstractCanvas2D.prototype.setShadowAlpha.apply(this, arguments);
-	}
-
-	var elem = this.createElement('shadowalpha');
-	elem.setAttribute('alpha', value);
-	this.root.appendChild(elem);
-
-};
-
-/**
- * Function: setShadowOffset
- *
- * Sets the current shadow offset.
- *
- * Parameters:
- *
- * dx - Number that represents the horizontal offset of the shadow.
- * dy - Number that represents the vertical offset of the shadow.
- */
-mxXmlCanvas2D.prototype.setShadowOffset = function(dx, dy)
-{
-	if (this.compressed)
-	{
-		if (this.state.shadowDx == dx && this.state.shadowDy == dy)
-		{
-			return;
-		}
-
-		mxAbstractCanvas2D.prototype.setShadowOffset.apply(this, arguments);
-	}
-
-	var elem = this.createElement('shadowoffset');
-	elem.setAttribute('dx', dx);
-	elem.setAttribute('dy', dy);
-	this.root.appendChild(elem);
-
-};
-
-/**
- * Function: rect
- *
- * Puts a rectangle into the drawing buffer.
- *
- * Parameters:
- *
- * x - Number that represents the x-coordinate of the rectangle.
- * y - Number that represents the y-coordinate of the rectangle.
- * w - Number that represents the width of the rectangle.
- * h - Number that represents the height of the rectangle.
- */
-mxXmlCanvas2D.prototype.rect = function(x, y, w, h)
-{
-	var elem = this.createElement('rect');
-	elem.setAttribute('x', this.format(x));
-	elem.setAttribute('y', this.format(y));
-	elem.setAttribute('w', this.format(w));
-	elem.setAttribute('h', this.format(h));
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: roundrect
- *
- * Puts a rounded rectangle into the drawing buffer.
- *
- * Parameters:
- *
- * x - Number that represents the x-coordinate of the rectangle.
- * y - Number that represents the y-coordinate of the rectangle.
- * w - Number that represents the width of the rectangle.
- * h - Number that represents the height of the rectangle.
- * dx - Number that represents the horizontal rounding.
- * dy - Number that represents the vertical rounding.
- */
-mxXmlCanvas2D.prototype.roundrect = function(x, y, w, h, dx, dy)
-{
-	var elem = this.createElement('roundrect');
-	elem.setAttribute('x', this.format(x));
-	elem.setAttribute('y', this.format(y));
-	elem.setAttribute('w', this.format(w));
-	elem.setAttribute('h', this.format(h));
-	elem.setAttribute('dx', this.format(dx));
-	elem.setAttribute('dy', this.format(dy));
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: ellipse
- *
- * Puts an ellipse into the drawing buffer.
- *
- * Parameters:
- *
- * x - Number that represents the x-coordinate of the ellipse.
- * y - Number that represents the y-coordinate of the ellipse.
- * w - Number that represents the width of the ellipse.
- * h - Number that represents the height of the ellipse.
- */
-mxXmlCanvas2D.prototype.ellipse = function(x, y, w, h)
-{
-	var elem = this.createElement('ellipse');
-	elem.setAttribute('x', this.format(x));
-	elem.setAttribute('y', this.format(y));
-	elem.setAttribute('w', this.format(w));
-	elem.setAttribute('h', this.format(h));
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: image
- *
- * Paints an image.
- *
- * Parameters:
- *
- * x - Number that represents the x-coordinate of the image.
- * y - Number that represents the y-coordinate of the image.
- * w - Number that represents the width of the image.
- * h - Number that represents the height of the image.
- * src - String that specifies the URL of the image.
- * aspect - Boolean indicating if the aspect of the image should be preserved.
- * flipH - Boolean indicating if the image should be flipped horizontally.
- * flipV - Boolean indicating if the image should be flipped vertically.
- */
-mxXmlCanvas2D.prototype.image = function(x, y, w, h, src, aspect, flipH, flipV)
-{
-	src = this.converter.convert(src);
-
-	// LATER: Add option for embedding images as base64.
-	var elem = this.createElement('image');
-	elem.setAttribute('x', this.format(x));
-	elem.setAttribute('y', this.format(y));
-	elem.setAttribute('w', this.format(w));
-	elem.setAttribute('h', this.format(h));
-	elem.setAttribute('src', src);
-	elem.setAttribute('aspect', (aspect) ? '1' : '0');
-	elem.setAttribute('flipH', (flipH) ? '1' : '0');
-	elem.setAttribute('flipV', (flipV) ? '1' : '0');
-	this.root.appendChild(elem);
-};
-
-/**
- * Function: begin
- *
- * Starts a new path and puts it into the drawing buffer.
- */
-mxXmlCanvas2D.prototype.begin = function()
-{
-	this.root.appendChild(this.createElement('begin'));
-	this.lastX = 0;
-	this.lastY = 0;
-};
-
-/**
- * Function: moveTo
- *
- * Moves the current path the given point.
- *
- * Parameters:
- *
- * x - Number that represents the x-coordinate of the point.
- * y - Number that represents the y-coordinate of the point.
- */
-mxXmlCanvas2D.prototype.moveTo = function(x, y)
-{
-	var elem = this.createElement('move');
-	elem.setAttribute('x', this.format(x));
-	elem.setAttribute('y', this.format(y));
-	this.root.appendChild(elem);
-	this.lastX = x;
-	this.lastY = y;
-};
-
-/**
- * Function: lineTo
- *
- * Draws a line to the given coordinates.
- *
- * Parameters:
- *
- * x - Number that represents the x-coordinate of the endpoint.
- * y - Number that represents the y-coordinate of the endpoint.
- */
-mxXmlCanvas2D.prototype.lineTo = function(x, y)
-{
-	var elem = this.createElement('line');
-	elem.setAttribute('x', this.format(x));
-	elem.setAttribute('y', this.format(y));
-	this.root.appendChild(elem);
-	this.lastX = x;
-	this.lastY = y;
-};
-
-/**
- * Function: quadTo
- *
- * Adds a quadratic curve to the current path.
- *
- * Parameters:
- *
- * x1 - Number that represents the x-coordinate of the control point.
- * y1 - Number that represents the y-coordinate of the control point.
- * x2 - Number that represents the x-coordinate of the endpoint.
- * y2 - Number that represents the y-coordinate of the endpoint.
- */
-mxXmlCanvas2D.prototype.quadTo = function(x1, y1, x2, y2)
-{
-	var elem = this.createElement('quad');
-	elem.setAttribute('x1', this.format(x1));
-	elem.setAttribute('y1', this.format(y1));
-	elem.setAttribute('x2', this.format(x2));
-	elem.setAttribute('y2', this.format(y2));
-	this.root.appendChild(elem);
-	this.lastX = x2;
-	this.lastY = y2;
-};
-
-/**
- * Function: curveTo
- *
- * Adds a bezier curve to the current path.
- *
- * Parameters:
- *
- * x1 - Number that represents the x-coordinate of the first control point.
- * y1 - Number that represents the y-coordinate of the first control point.
- * x2 - Number that represents the x-coordinate of the second control point.
- * y2 - Number that represents the y-coordinate of the second control point.
- * x3 - Number that represents the x-coordinate of the endpoint.
- * y3 - Number that represents the y-coordinate of the endpoint.
- */
-mxXmlCanvas2D.prototype.curveTo = function(x1, y1, x2, y2, x3, y3)
-{
-	var elem = this.createElement('curve');
-	elem.setAttribute('x1', this.format(x1));
-	elem.setAttribute('y1', this.format(y1));
-	elem.setAttribute('x2', this.format(x2));
-	elem.setAttribute('y2', this.format(y2));
-	elem.setAttribute('x3', this.format(x3));
-	elem.setAttribute('y3', this.format(y3));
-	this.root.appendChild(elem);
-	this.lastX = x3;
-	this.lastY = y3;
-};
-
-/**
- * Function: close
- *
- * Closes the current path.
- */
-mxXmlCanvas2D.prototype.close = function()
-{
-	this.root.appendChild(this.createElement('close'));
-};
-
-/**
- * Function: text
- *
- * Paints the given text. Possible values for format are empty string for
- * plain text and html for HTML markup. Background and border color as well
- * as clipping is not available in plain text labels for VML. HTML labels
- * are not available as part of shapes with no foreignObject support in SVG
- * (eg. IE9, IE10).
- *
- * Parameters:
- *
- * x - Number that represents the x-coordinate of the text.
- * y - Number that represents the y-coordinate of the text.
- * w - Number that represents the available width for the text or 0 for automatic width.
- * h - Number that represents the available height for the text or 0 for automatic height.
- * str - String that specifies the text to be painted.
- * align - String that represents the horizontal alignment.
- * valign - String that represents the vertical alignment.
- * wrap - Boolean that specifies if word-wrapping is enabled. Requires w > 0.
- * format - Empty string for plain text or 'html' for HTML markup.
- * overflow - Specifies the overflow behaviour of the label. Requires w > 0 and/or h > 0.
- * clip - Boolean that specifies if the label should be clipped. Requires w > 0 and/or h > 0.
- * rotation - Number that specifies the angle of the rotation around the anchor point of the text.
- * dir - Optional string that specifies the text direction. Possible values are rtl and lrt.
- */
-mxXmlCanvas2D.prototype.text = function(x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation, dir)
-{
-	if (this.textEnabled && str != null)
-	{
-		if (mxUtils.isNode(str))
-		{
-			str = mxUtils.getOuterHtml(str);
-		}
-
-		var elem = this.createElement('text');
-		elem.setAttribute('x', this.format(x));
-		elem.setAttribute('y', this.format(y));
-		elem.setAttribute('w', this.format(w));
-		elem.setAttribute('h', this.format(h));
-		elem.setAttribute('str', str);
-
-		if (align != null)
-		{
-			elem.setAttribute('align', align);
-		}
-
-		if (valign != null)
-		{
-			elem.setAttribute('valign', valign);
-		}
-
-		elem.setAttribute('wrap', (wrap) ? '1' : '0');
-
-		if (format == null)
-		{
-			format = '';
-		}
-
-		elem.setAttribute('format', format);
-
-		if (overflow != null)
-		{
-			elem.setAttribute('overflow', overflow);
-		}
-
-		if (clip != null)
-		{
-			elem.setAttribute('clip', (clip) ? '1' : '0');
-		}
-
-		if (rotation != null)
-		{
-			elem.setAttribute('rotation', rotation);
-		}
-
-		if (dir != null)
-		{
-			elem.setAttribute('dir', dir);
-		}
-
-		this.root.appendChild(elem);
-	}
-};
-
-/**
- * Function: stroke
- *
- * Paints the outline of the current drawing buffer.
- */
-mxXmlCanvas2D.prototype.stroke = function()
-{
-	this.root.appendChild(this.createElement('stroke'));
-};
-
-/**
- * Function: fill
- *
- * Fills the current drawing buffer.
- */
-mxXmlCanvas2D.prototype.fill = function()
-{
-	this.root.appendChild(this.createElement('fill'));
-};
-
-/**
- * Function: fillAndStroke
- *
- * Fills the current drawing buffer and its outline.
- */
-mxXmlCanvas2D.prototype.fillAndStroke = function()
-{
-	this.root.appendChild(this.createElement('fillstroke'));
-};
-/**
- * Copyright (c) 2006-2015, JGraph Ltd
- * Copyright (c) 2006-2015, Gaudenz Alder
- */
-/**
- * Class: mxSvgCanvas2D
- *
- * Extends  to implement a canvas for SVG. This canvas writes all
- * calls as SVG output to the given SVG root node.
- *
- * (code)
- * var svgDoc = mxUtils.createXmlDocument();
- * var root = (svgDoc.createElementNS != null) ?
- * 		svgDoc.createElementNS(mxConstants.NS_SVG, 'svg') : svgDoc.createElement('svg');
- *
- * if (svgDoc.createElementNS == null)
- * {
- *   root.setAttribute('xmlns', mxConstants.NS_SVG);
- *   root.setAttribute('xmlns:xlink', mxConstants.NS_XLINK);
- * }
- * else
- * {
- *   root.setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:xlink', mxConstants.NS_XLINK);
- * }
- *
- * var bounds = graph.getGraphBounds();
- * root.setAttribute('width', (bounds.x + bounds.width + 4) + 'px');
- * root.setAttribute('height', (bounds.y + bounds.height + 4) + 'px');
- * root.setAttribute('version', '1.1');
- *
- * svgDoc.appendChild(root);
- *
- * var svgCanvas = new mxSvgCanvas2D(root);
- * (end)
- *
- * A description of the public API is available in .
- *
- * To disable anti-aliasing in the output, use the following code.
- *
- * (code)
- * graph.view.canvas.ownerSVGElement.setAttribute('shape-rendering', 'crispEdges');
- * (end)
- *
- * Or set the respective attribute in the SVG element directly.
- *
- * Constructor: mxSvgCanvas2D
- *
- * Constructs a new SVG canvas.
- *
- * Parameters:
- *
- * root - SVG container for the output.
- * styleEnabled - Optional boolean that specifies if a style section should be
- * added. The style section sets the default font-size, font-family and
- * stroke-miterlimit globally. Default is false.
- */
-function mxSvgCanvas2D(root, styleEnabled)
-{
-	mxAbstractCanvas2D.call(this);
-
-	/**
-	 * Variable: root
-	 *
-	 * Reference to the container for the SVG content.
-	 */
-	this.root = root;
-
-	/**
-	 * Variable: gradients
-	 *
-	 * Local cache of gradients for quick lookups.
-	 */
-	this.gradients = [];
-
-	/**
-	 * Variable: defs
-	 *
-	 * Reference to the defs section of the SVG document. Only for export.
-	 */
-	this.defs = null;
-
-	/**
-	 * Variable: styleEnabled
-	 *
-	 * Stores the value of styleEnabled passed to the constructor.
-	 */
-	this.styleEnabled = (styleEnabled != null) ? styleEnabled : false;
-
-	var svg = null;
-
-	// Adds optional defs section for export
-	if (root.ownerDocument != document)
-	{
-		var node = root;
-
-		// Finds owner SVG element in XML DOM
-		while (node != null && node.nodeName != 'svg')
-		{
-			node = node.parentNode;
-		}
-
-		svg = node;
-	}
-
-	if (svg != null)
-	{
-		// Tries to get existing defs section
-		var tmp = svg.getElementsByTagName('defs');
-
-		if (tmp.length > 0)
-		{
-			this.defs = svg.getElementsByTagName('defs')[0];
-		}
-
-		// Adds defs section if none exists
-		if (this.defs == null)
-		{
-			this.defs = this.createElement('defs');
-
-			if (svg.firstChild != null)
-			{
-				svg.insertBefore(this.defs, svg.firstChild);
-			}
-			else
-			{
-				svg.appendChild(this.defs);
-			}
-		}
-
-		// Adds stylesheet
-		if (this.styleEnabled)
-		{
-			this.defs.appendChild(this.createStyle());
-		}
-	}
-};
-
-/**
- * Extends mxAbstractCanvas2D
- */
-mxUtils.extend(mxSvgCanvas2D, mxAbstractCanvas2D);
-
-/**
- * Capability check for DOM parser.
- */
-(function()
-{
-	mxSvgCanvas2D.prototype.useDomParser = !mxClient.IS_IE && typeof DOMParser === 'function' && typeof XMLSerializer === 'function';
-
-	if (mxSvgCanvas2D.prototype.useDomParser)
-	{
-		// Checks using a generic test text if the parsing actually works. This is a workaround
-		// for older browsers where the capability check returns true but the parsing fails.
-		try
-		{
-			var doc = new DOMParser().parseFromString('test text', 'text/html');
-			mxSvgCanvas2D.prototype.useDomParser = doc != null;
-		}
-		catch (e)
-		{
-			mxSvgCanvas2D.prototype.useDomParser = false;
-		}
-	}
-})();
-
-/**
- * Variable: path
- *
- * Holds the current DOM node.
- */
-mxSvgCanvas2D.prototype.node = null;
-
-/**
- * Variable: matchHtmlAlignment
- *
- * Specifies if plain text output should match the vertical HTML alignment.
- * Defaul is true.
- */
-mxSvgCanvas2D.prototype.matchHtmlAlignment = true;
-
-/**
- * Variable: textEnabled
- *
- * Specifies if text output should be enabled. Default is true.
- */
-mxSvgCanvas2D.prototype.textEnabled = true;
-
-/**
- * Variable: foEnabled
- *
- * Specifies if use of foreignObject for HTML markup is allowed. Default is true.
- */
-mxSvgCanvas2D.prototype.foEnabled = true;
-
-/**
- * Variable: foAltText
- *
- * Specifies the fallback text for unsupported foreignObjects in exported
- * documents. Default is '[Object]'. If this is set to null then no fallback
- * text is added to the exported document.
- */
-mxSvgCanvas2D.prototype.foAltText = '[Object]';
-
-/**
- * Variable: foOffset
- *
- * Offset to be used for foreignObjects.
- */
-mxSvgCanvas2D.prototype.foOffset = 0;
-
-/**
- * Variable: textOffset
- *
- * Offset to be used for text elements.
- */
-mxSvgCanvas2D.prototype.textOffset = 0;
-
-/**
- * Variable: imageOffset
- *
- * Offset to be used for image elements.
- */
-mxSvgCanvas2D.prototype.imageOffset = 0;
-
-/**
- * Variable: strokeTolerance
- *
- * Adds transparent paths for strokes.
- */
-mxSvgCanvas2D.prototype.strokeTolerance = 0;
-
-/**
- * Variable: minStrokeWidth
- *
- * Minimum stroke width for output.
- */
-mxSvgCanvas2D.prototype.minStrokeWidth = 1;
-
-/**
- * Variable: refCount
- *
- * Local counter for references in SVG export.
- */
-mxSvgCanvas2D.prototype.refCount = 0;
-
-/**
- * Variable: blockImagePointerEvents
- *
- * Specifies if a transparent rectangle should be added on top of images to absorb
- * all pointer events. Default is false. This is only needed in Firefox to disable
- * control-clicks on images.
- */
-mxSvgCanvas2D.prototype.blockImagePointerEvents = false;
-
-/**
- * Variable: lineHeightCorrection
- *
- * Correction factor for  in HTML output. Default is 1.
- */
-mxSvgCanvas2D.prototype.lineHeightCorrection = 1;
-
-/**
- * Variable: pointerEventsValue
- *
- * Default value for active pointer events. Default is all.
- */
-mxSvgCanvas2D.prototype.pointerEventsValue = 'all';
-
-/**
- * Variable: fontMetricsPadding
- *
- * Padding to be added for text that is not wrapped to account for differences
- * in font metrics on different platforms in pixels. Default is 10.
- */
-mxSvgCanvas2D.prototype.fontMetricsPadding = 10;
-
-/**
- * Variable: cacheOffsetSize
- *
- * Specifies if offsetWidth and offsetHeight should be cached. Default is true.
- * This is used to speed up repaint of text in .
- */
-mxSvgCanvas2D.prototype.cacheOffsetSize = true;
-
-/**
- * Function: format
- *
- * Rounds all numbers to 2 decimal points.
- */
-mxSvgCanvas2D.prototype.format = function(value)
-{
-	return parseFloat(parseFloat(value).toFixed(2));
-};
-
-/**
- * Function: getBaseUrl
- *
- * Returns the URL of the page without the hash part. This needs to use href to
- * include any search part with no params (ie question mark alone). This is a
- * workaround for the fact that window.location.search is empty if there is
- * no search string behind the question mark.
- */
-mxSvgCanvas2D.prototype.getBaseUrl = function()
-{
-	var href = window.location.href;
-	var hash = href.lastIndexOf('#');
-
-	if (hash > 0)
-	{
-		href = href.substring(0, hash);
-	}
-
-	return href;
-};
-
-/**
- * Function: reset
- *
- * Returns any offsets for rendering pixels.
- */
-mxSvgCanvas2D.prototype.reset = function()
-{
-	mxAbstractCanvas2D.prototype.reset.apply(this, arguments);
-	this.gradients = [];
-};
-
-/**
- * Function: createStyle
- *
- * Creates the optional style section.
- */
-mxSvgCanvas2D.prototype.createStyle = function(x)
-{
-	var style = this.createElement('style');
-	style.setAttribute('type', 'text/css');
-	mxUtils.write(style, 'svg{font-family:' + mxConstants.DEFAULT_FONTFAMILY +
-			';font-size:' + mxConstants.DEFAULT_FONTSIZE +
-			';fill:none;stroke-miterlimit:10}');
-
-	return style;
-};
-
-/**
- * Function: createElement
- *
- * Private helper function to create SVG elements
- */
-mxSvgCanvas2D.prototype.createElement = function(tagName, namespace)
-{
-	if (this.root.ownerDocument.createElementNS != null)
-	{
-		return this.root.ownerDocument.createElementNS(namespace || mxConstants.NS_SVG, tagName);
-	}
-	else
-	{
-		var elt = this.root.ownerDocument.createElement(tagName);
-
-		if (namespace != null)
-		{
-			elt.setAttribute('xmlns', namespace);
-		}
-
-		return elt;
-	}
-};
-
-/**
- * Function: getAlternateContent
- *
- * Returns the alternate content for the given foreignObject.
- */
-mxSvgCanvas2D.prototype.createAlternateContent = function(fo, x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation)
-{
-	if (this.foAltText != null)
-	{
-		var s = this.state;
-		var alt = this.createElement('text');
-		alt.setAttribute('x', Math.round(w / 2));
-		alt.setAttribute('y', Math.round((h + s.fontSize) / 2));
-		alt.setAttribute('fill', s.fontColor || 'black');
-		alt.setAttribute('text-anchor', 'middle');
-		alt.setAttribute('font-size', s.fontSize + 'px');
-		alt.setAttribute('font-family', s.fontFamily);
-
-		if ((s.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD)
-		{
-			alt.setAttribute('font-weight', 'bold');
-		}
-
-		if ((s.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC)
-		{
-			alt.setAttribute('font-style', 'italic');
-		}
-
-		if ((s.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE)
-		{
-			alt.setAttribute('text-decoration', 'underline');
-		}
-
-		mxUtils.write(alt, this.foAltText);
-
-		return alt;
-	}
-	else
-	{
-		return null;
-	}
-};
-
-/**
- * Function: createGradientId
- *
- * Private helper function to create SVG elements
- */
-mxSvgCanvas2D.prototype.createGradientId = function(start, end, alpha1, alpha2, direction)
-{
-	// Removes illegal characters from gradient ID
-	if (start.charAt(0) == '#')
-	{
-		start = start.substring(1);
-	}
-
-	if (end.charAt(0) == '#')
-	{
-		end = end.substring(1);
-	}
-
-	// Workaround for gradient IDs not working in Safari 5 / Chrome 6
-	// if they contain uppercase characters
-	start = start.toLowerCase() + '-' + alpha1;
-	end = end.toLowerCase() + '-' + alpha2;
-
-	// Wrong gradient directions possible?
-	var dir = null;
-
-	if (direction == null || direction == mxConstants.DIRECTION_SOUTH)
-	{
-		dir = 's';
-	}
-	else if (direction == mxConstants.DIRECTION_EAST)
-	{
-		dir = 'e';
-	}
-	else
-	{
-		var tmp = start;
-		start = end;
-		end = tmp;
-
-		if (direction == mxConstants.DIRECTION_NORTH)
-		{
-			dir = 's';
-		}
-		else if (direction == mxConstants.DIRECTION_WEST)
-		{
-			dir = 'e';
-		}
-	}
-
-	return 'mx-gradient-' + start + '-' + end + '-' + dir;
-};
-
-/**
- * Function: getSvgGradient
- *
- * Private helper function to create SVG elements
- */
-mxSvgCanvas2D.prototype.getSvgGradient = function(start, end, alpha1, alpha2, direction)
-{
-	var id = this.createGradientId(start, end, alpha1, alpha2, direction);
-	var gradient = this.gradients[id];
-
-	if (gradient == null)
-	{
-		var svg = this.root.ownerSVGElement;
-
-		var counter = 0;
-		var tmpId = id + '-' + counter;
-
-		if (svg != null)
-		{
-			gradient = svg.ownerDocument.getElementById(tmpId);
-
-			while (gradient != null && gradient.ownerSVGElement != svg)
-			{
-				tmpId = id + '-' + counter++;
-				gradient = svg.ownerDocument.getElementById(tmpId);
-			}
-		}
-		else
-		{
-			// Uses shorter IDs for export
-			tmpId = 'id' + (++this.refCount);
-		}
-
-		if (gradient == null)
-		{
-			gradient = this.createSvgGradient(start, end, alpha1, alpha2, direction);
-			gradient.setAttribute('id', tmpId);
-
-			if (this.defs != null)
-			{
-				this.defs.appendChild(gradient);
-			}
-			else
-			{
-				svg.appendChild(gradient);
-			}
-		}
-
-		this.gradients[id] = gradient;
-	}
-
-	return gradient.getAttribute('id');
-};
-
-/**
- * Function: createSvgGradient
- *
- * Creates the given SVG gradient.
- */
-mxSvgCanvas2D.prototype.createSvgGradient = function(start, end, alpha1, alpha2, direction)
-{
-	var gradient = this.createElement('linearGradient');
-	gradient.setAttribute('x1', '0%');
-	gradient.setAttribute('y1', '0%');
-	gradient.setAttribute('x2', '0%');
-	gradient.setAttribute('y2', '0%');
-
-	if (direction == null || direction == mxConstants.DIRECTION_SOUTH)
-	{
-		gradient.setAttribute('y2', '100%');
-	}
-	else if (direction == mxConstants.DIRECTION_EAST)
-	{
-		gradient.setAttribute('x2', '100%');
-	}
-	else if (direction == mxConstants.DIRECTION_NORTH)
-	{
-		gradient.setAttribute('y1', '100%');
-	}
-	else if (direction == mxConstants.DIRECTION_WEST)
-	{
-		gradient.setAttribute('x1', '100%');
-	}
-
-	var op = (alpha1 < 1) ? ';stop-opacity:' + alpha1 : '';
-
-	var stop = this.createElement('stop');
-	stop.setAttribute('offset', '0%');
-	stop.setAttribute('style', 'stop-color:' + start + op);
-	gradient.appendChild(stop);
-
-	op = (alpha2 < 1) ? ';stop-opacity:' + alpha2 : '';
-
-	stop = this.createElement('stop');
-	stop.setAttribute('offset', '100%');
-	stop.setAttribute('style', 'stop-color:' + end + op);
-	gradient.appendChild(stop);
-
-	return gradient;
-};
-
-/**
- * Function: addNode
- *
- * Private helper function to create SVG elements
- */
-mxSvgCanvas2D.prototype.addNode = function(filled, stroked)
-{
-	var node = this.node;
-	var s = this.state;
-
-	if (node != null)
-	{
-		if (node.nodeName == 'path')
-		{
-			// Checks if the path is not empty
-			if (this.path != null && this.path.length > 0)
-			{
-				node.setAttribute('d', this.path.join(' '));
-			}
-			else
-			{
-				return;
-			}
-		}
-
-		if (filled && s.fillColor != null)
-		{
-			this.updateFill();
-		}
-		else if (!this.styleEnabled)
-		{
-			// Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=814952
-			if (node.nodeName == 'ellipse' && mxClient.IS_FF)
-			{
-				node.setAttribute('fill', 'transparent');
-			}
-			else
-			{
-				node.setAttribute('fill', 'none');
-			}
-
-			// Sets the actual filled state for stroke tolerance
-			filled = false;
-		}
-
-		if (stroked && s.strokeColor != null)
-		{
-			this.updateStroke();
-		}
-		else if (!this.styleEnabled)
-		{
-			node.setAttribute('stroke', 'none');
-		}
-
-		if (s.transform != null && s.transform.length > 0)
-		{
-			node.setAttribute('transform', s.transform);
-		}
-
-		if (s.shadow)
-		{
-			this.root.appendChild(this.createShadow(node));
-		}
-
-		// Adds stroke tolerance
-		if (this.strokeTolerance > 0 && !filled)
-		{
-			this.root.appendChild(this.createTolerance(node));
-		}
-
-		// Adds pointer events
-		if (this.pointerEvents && (node.nodeName != 'path' ||
-			this.path[this.path.length - 1] == this.closeOp))
-		{
-			node.setAttribute('pointer-events', this.pointerEventsValue);
-		}
-		// Enables clicks for nodes inside a link element
-		else if (!this.pointerEvents && this.originalRoot == null)
-		{
-			node.setAttribute('pointer-events', 'none');
-		}
-
-		// Removes invisible nodes from output if they don't handle events
-		if ((node.nodeName != 'rect' && node.nodeName != 'path' && node.nodeName != 'ellipse') ||
-			(node.getAttribute('fill') != 'none' && node.getAttribute('fill') != 'transparent') ||
-			node.getAttribute('stroke') != 'none' || node.getAttribute('pointer-events') != 'none')
-		{
-			// LATER: Update existing DOM for performance
-			this.root.appendChild(node);
-		}
-
-		this.node = null;
-	}
-};
-
-/**
- * Function: updateFill
- *
- * Transfers the stroke attributes from  to .
- */
-mxSvgCanvas2D.prototype.updateFill = function()
-{
-	var s = this.state;
-
-	if (s.alpha < 1 || s.fillAlpha < 1)
-	{
-		this.node.setAttribute('fill-opacity', s.alpha * s.fillAlpha);
-	}
-
-	if (s.fillColor != null)
-	{
-		if (s.gradientColor != null)
-		{
-			var id = this.getSvgGradient(s.fillColor, s.gradientColor, s.gradientFillAlpha, s.gradientAlpha, s.gradientDirection);
-
-			if (!mxClient.IS_CHROME_APP && !mxClient.IS_IE && !mxClient.IS_IE11 &&
-				!mxClient.IS_EDGE && this.root.ownerDocument == document)
-			{
-				// Workaround for potential base tag and brackets must be escaped
-				var base = this.getBaseUrl().replace(/([\(\)])/g, '\\$1');
-				this.node.setAttribute('fill', 'url(' + base + '#' + id + ')');
-			}
-			else
-			{
-				this.node.setAttribute('fill', 'url(#' + id + ')');
-			}
-		}
-		else
-		{
-			this.node.setAttribute('fill', s.fillColor.toLowerCase());
-		}
-	}
-};
-
-/**
- * Function: getCurrentStrokeWidth
- *
- * Returns the current stroke width (>= 1), ie. max(1, this.format(this.state.strokeWidth * this.state.scale)).
- */
-mxSvgCanvas2D.prototype.getCurrentStrokeWidth = function()
-{
-	return Math.max(this.minStrokeWidth, Math.max(0.01, this.format(this.state.strokeWidth * this.state.scale)));
-};
-
-/**
- * Function: updateStroke
- *
- * Transfers the stroke attributes from  to .
- */
-mxSvgCanvas2D.prototype.updateStroke = function()
-{
-	var s = this.state;
-
-	this.node.setAttribute('stroke', s.strokeColor.toLowerCase());
-
-	if (s.alpha < 1 || s.strokeAlpha < 1)
-	{
-		this.node.setAttribute('stroke-opacity', s.alpha * s.strokeAlpha);
-	}
-
-	var sw = this.getCurrentStrokeWidth();
-
-	if (sw != 1)
-	{
-		this.node.setAttribute('stroke-width', sw);
-	}
-
-	if (this.node.nodeName == 'path')
-	{
-		this.updateStrokeAttributes();
-	}
-
-	if (s.dashed)
-	{
-		this.node.setAttribute('stroke-dasharray', this.createDashPattern(
-			((s.fixDash) ? 1 : s.strokeWidth) * s.scale));
-	}
-};
-
-/**
- * Function: updateStrokeAttributes
- *
- * Transfers the stroke attributes from  to .
- */
-mxSvgCanvas2D.prototype.updateStrokeAttributes = function()
-{
-	var s = this.state;
-
-	// Linejoin miter is default in SVG
-	if (s.lineJoin != null && s.lineJoin != 'miter')
-	{
-		this.node.setAttribute('stroke-linejoin', s.lineJoin);
-	}
-
-	if (s.lineCap != null)
-	{
-		// flat is called butt in SVG
-		var value = s.lineCap;
-
-		if (value == 'flat')
-		{
-			value = 'butt';
-		}
-
-		// Linecap butt is default in SVG
-		if (value != 'butt')
-		{
-			this.node.setAttribute('stroke-linecap', value);
-		}
-	}
-
-	// Miterlimit 10 is default in our document
-	if (s.miterLimit != null && (!this.styleEnabled || s.miterLimit != 10))
-	{
-		this.node.setAttribute('stroke-miterlimit', s.miterLimit);
-	}
-};
-
-/**
- * Function: createDashPattern
- *
- * Creates the SVG dash pattern for the given state.
- */
-mxSvgCanvas2D.prototype.createDashPattern = function(scale)
-{
-	var pat = [];
-
-	if (typeof(this.state.dashPattern) === 'string')
-	{
-		var dash = this.state.dashPattern.split(' ');
-
-		if (dash.length > 0)
-		{
-			for (var i = 0; i < dash.length; i++)
-			{
-				pat[i] = Number(dash[i]) * scale;
-			}
-		}
-	}
-
-	return pat.join(' ');
-};
-
-/**
- * Function: createTolerance
- *
- * Creates a hit detection tolerance shape for the given node.
- */
-mxSvgCanvas2D.prototype.createTolerance = function(node)
-{
-	var tol = node.cloneNode(true);
-	var sw = parseFloat(tol.getAttribute('stroke-width') || 1) + this.strokeTolerance;
-	tol.setAttribute('pointer-events', 'stroke');
-	tol.setAttribute('visibility', 'hidden');
-	tol.removeAttribute('stroke-dasharray');
-	tol.setAttribute('stroke-width', sw);
-	tol.setAttribute('fill', 'none');
-
-	// Workaround for Opera ignoring the visiblity attribute above while
-	// other browsers need a stroke color to perform the hit-detection but
-	// do not ignore the visibility attribute. Side-effect is that Opera's
-	// hit detection for horizontal/vertical edges seems to ignore the tol.
-	tol.setAttribute('stroke', (mxClient.IS_OT) ? 'none' : 'white');
-
-	return tol;
-};
-
-/**
- * Function: createShadow
- *
- * Creates a shadow for the given node.
- */
-mxSvgCanvas2D.prototype.createShadow = function(node)
-{
-	var shadow = node.cloneNode(true);
-	var s = this.state;
-
-	// Firefox uses transparent for no fill in ellipses
-	if (shadow.getAttribute('fill') != 'none' && (!mxClient.IS_FF || shadow.getAttribute('fill') != 'transparent'))
-	{
-		shadow.setAttribute('fill', s.shadowColor);
-	}
-
-	if (shadow.getAttribute('stroke') != 'none')
-	{
-		shadow.setAttribute('stroke', s.shadowColor);
-	}
-
-	shadow.setAttribute('transform', 'translate(' + this.format(s.shadowDx * s.scale) +
-		',' + this.format(s.shadowDy * s.scale) + ')' + (s.transform || ''));
-	shadow.setAttribute('opacity', s.shadowAlpha);
-
-	return shadow;
-};
-
-/**
- * Function: setLink
- *
- * Experimental implementation for hyperlinks.
- */
-mxSvgCanvas2D.prototype.setLink = function(link)
-{
-	if (link == null)
-	{
-		this.root = this.originalRoot;
-	}
-	else
-	{
-		this.originalRoot = this.root;
-
-		var node = this.createElement('a');
-
-		// Workaround for implicit namespace handling in HTML5 export, IE adds NS1 namespace so use code below
-		// in all IE versions except quirks mode. KNOWN: Adds xlink namespace to each image tag in output.
-		if (node.setAttributeNS == null || (this.root.ownerDocument != document && document.documentMode == null))
-		{
-			node.setAttribute('xlink:href', link);
-		}
-		else
-		{
-			node.setAttributeNS(mxConstants.NS_XLINK, 'xlink:href', link);
-		}
-
-		this.root.appendChild(node);
-		this.root = node;
-	}
-};
-
-/**
- * Function: rotate
- *
- * Sets the rotation of the canvas. Note that rotation cannot be concatenated.
- */
-mxSvgCanvas2D.prototype.rotate = function(theta, flipH, flipV, cx, cy)
-{
-	if (theta != 0 || flipH || flipV)
-	{
-		var s = this.state;
-		cx += s.dx;
-		cy += s.dy;
-
-		cx *= s.scale;
-		cy *= s.scale;
-
-		s.transform = s.transform || '';
-
-		// This implementation uses custom scale/translate and built-in rotation
-		// Rotation state is part of the AffineTransform in state.transform
-		if (flipH && flipV)
-		{
-			theta += 180;
-		}
-		else if (flipH != flipV)
-		{
-			var tx = (flipH) ? cx : 0;
-			var sx = (flipH) ? -1 : 1;
-
-			var ty = (flipV) ? cy : 0;
-			var sy = (flipV) ? -1 : 1;
-
-			s.transform += 'translate(' + this.format(tx) + ',' + this.format(ty) + ')' +
-				'scale(' + this.format(sx) + ',' + this.format(sy) + ')' +
-				'translate(' + this.format(-tx) + ',' + this.format(-ty) + ')';
-		}
-
-		if (flipH ? !flipV : flipV)
-		{
-			theta *= -1;
-		}
-
-		if (theta != 0)
-		{
-			s.transform += 'rotate(' + this.format(theta) + ',' + this.format(cx) + ',' + this.format(cy) + ')';
-		}
-
-		s.rotation = s.rotation + theta;
-		s.rotationCx = cx;
-		s.rotationCy = cy;
-	}
-};
-
-/**
- * Function: begin
- *
- * Extends superclass to create path.
- */
-mxSvgCanvas2D.prototype.begin = function()
-{
-	mxAbstractCanvas2D.prototype.begin.apply(this, arguments);
-	this.node = this.createElement('path');
-};
-
-/**
- * Function: rect
- *
- * Private helper function to create SVG elements
- */
-mxSvgCanvas2D.prototype.rect = function(x, y, w, h)
-{
-	var s = this.state;
-	var n = this.createElement('rect');
-	n.setAttribute('x', this.format((x + s.dx) * s.scale));
-	n.setAttribute('y', this.format((y + s.dy) * s.scale));
-	n.setAttribute('width', this.format(w * s.scale));
-	n.setAttribute('height', this.format(h * s.scale));
-
-	this.node = n;
-};
-
-/**
- * Function: roundrect
- *
- * Private helper function to create SVG elements
- */
-mxSvgCanvas2D.prototype.roundrect = function(x, y, w, h, dx, dy)
-{
-	this.rect(x, y, w, h);
-
-	if (dx > 0)
-	{
-		this.node.setAttribute('rx', this.format(dx * this.state.scale));
-	}
-
-	if (dy > 0)
-	{
-		this.node.setAttribute('ry', this.format(dy * this.state.scale));
-	}
-};
-
-/**
- * Function: ellipse
- *
- * Private helper function to create SVG elements
- */
-mxSvgCanvas2D.prototype.ellipse = function(x, y, w, h)
-{
-	var s = this.state;
-	var n = this.createElement('ellipse');
-	// No rounding for consistent output with 1.x
-	n.setAttribute('cx', Math.round((x + w / 2 + s.dx) * s.scale));
-	n.setAttribute('cy', Math.round((y + h / 2 + s.dy) * s.scale));
-	n.setAttribute('rx', w / 2 * s.scale);
-	n.setAttribute('ry', h / 2 * s.scale);
-	this.node = n;
-};
-
-/**
- * Function: image
- *
- * Private helper function to create SVG elements
- */
-mxSvgCanvas2D.prototype.image = function(x, y, w, h, src, aspect, flipH, flipV)
-{
-	src = this.converter.convert(src);
-
-	// LATER: Add option for embedding images as base64.
-	aspect = (aspect != null) ? aspect : true;
-	flipH = (flipH != null) ? flipH : false;
-	flipV = (flipV != null) ? flipV : false;
-
-	var s = this.state;
-	x += s.dx;
-	y += s.dy;
-
-	var node = this.createElement('image');
-	node.setAttribute('x', this.format(x * s.scale) + this.imageOffset);
-	node.setAttribute('y', this.format(y * s.scale) + this.imageOffset);
-	node.setAttribute('width', this.format(w * s.scale));
-	node.setAttribute('height', this.format(h * s.scale));
-
-	// Workaround for missing namespace support
-	if (node.setAttributeNS == null)
-	{
-		node.setAttribute('xlink:href', src);
-	}
-	else
-	{
-		node.setAttributeNS(mxConstants.NS_XLINK, 'xlink:href', src);
-	}
-
-	if (!aspect)
-	{
-		node.setAttribute('preserveAspectRatio', 'none');
-	}
-
-	if (s.alpha < 1 || s.fillAlpha < 1)
-	{
-		node.setAttribute('opacity', s.alpha * s.fillAlpha);
-	}
-
-	var tr = this.state.transform || '';
-
-	if (flipH || flipV)
-	{
-		var sx = 1;
-		var sy = 1;
-		var dx = 0;
-		var dy = 0;
-
-		if (flipH)
-		{
-			sx = -1;
-			dx = -w - 2 * x;
-		}
-
-		if (flipV)
-		{
-			sy = -1;
-			dy = -h - 2 * y;
-		}
-
-		// Adds image tansformation to existing transform
-		tr += 'scale(' + sx + ',' + sy + ')translate(' + (dx * s.scale) + ',' + (dy * s.scale) + ')';
-	}
-
-	if (tr.length > 0)
-	{
-		node.setAttribute('transform', tr);
-	}
-
-	if (!this.pointerEvents)
-	{
-		node.setAttribute('pointer-events', 'none');
-	}
-
-	this.root.appendChild(node);
-
-	// Disables control-clicks on images in Firefox to open in new tab
-	// by putting a rect in the foreground that absorbs all events and
-	// disabling all pointer-events on the original image tag.
-	if (this.blockImagePointerEvents)
-	{
-		node.setAttribute('style', 'pointer-events:none');
-
-		node = this.createElement('rect');
-		node.setAttribute('visibility', 'hidden');
-		node.setAttribute('pointer-events', 'fill');
-		node.setAttribute('x', this.format(x * s.scale));
-		node.setAttribute('y', this.format(y * s.scale));
-		node.setAttribute('width', this.format(w * s.scale));
-		node.setAttribute('height', this.format(h * s.scale));
-		this.root.appendChild(node);
-	}
-};
-
-/**
- * Function: convertHtml
- *
- * Converts the given HTML string to XHTML.
- */
-mxSvgCanvas2D.prototype.convertHtml = function(val)
-{
-	if (this.useDomParser)
-	{
-		var doc = new DOMParser().parseFromString(val, 'text/html');
-
-		if (doc != null)
-		{
-			val = new XMLSerializer().serializeToString(doc.body);
-
-			// Extracts body content from DOM
-			if (val.substring(0, 5) == '', 5) + 1);
-			}
-
-			if (val.substring(val.length - 7, val.length) == '')
-			{
-				val = val.substring(0, val.length - 7);
-			}
-		}
-	}
-	else if (document.implementation != null && document.implementation.createDocument != null)
-	{
-		var xd = document.implementation.createDocument('http://www.w3.org/1999/xhtml', 'html', null);
-		var xb = xd.createElement('body');
-		xd.documentElement.appendChild(xb);
-
-		var div = document.createElement('div');
-		div.innerHTML = val;
-		var child = div.firstChild;
-
-		while (child != null)
-		{
-			var next = child.nextSibling;
-			xb.appendChild(xd.adoptNode(child));
-			child = next;
-		}
-
-		return xb.innerHTML;
-	}
-	else
-	{
-		var ta = document.createElement('textarea');
-
-		// Handles special HTML entities < and > and double escaping
-		// and converts unclosed br, hr and img tags to XHTML
-		// LATER: Convert all unclosed tags
-		ta.innerHTML = val.replace(/&/g, '&amp;').
-			replace(/</g, '&lt;').replace(/>/g, '&gt;').
-			replace(/</g, '&lt;').replace(/>/g, '&gt;').
-			replace(//g, '>');
-		val = ta.value.replace(/&/g, '&').replace(/&lt;/g, '<').
-			replace(/&gt;/g, '>').replace(/&amp;/g, '&').
-			replace(/
/g, '
').replace(/
/g, '
'). - replace(/(]+)>/gm, "$1 />"); - } - - return val; -}; - -/** - * Function: createDiv - * - * Private helper function to create SVG elements - */ -mxSvgCanvas2D.prototype.createDiv = function(str, align, valign, style, overflow) -{ - var s = this.state; - - // Inline block for rendering HTML background over SVG in Safari - var lh = (mxConstants.ABSOLUTE_LINE_HEIGHT) ? (s.fontSize * mxConstants.LINE_HEIGHT) + 'px' : - (mxConstants.LINE_HEIGHT * this.lineHeightCorrection); - - style = 'display:inline-block;font-size:' + s.fontSize + 'px;font-family:' + s.fontFamily + - ';color:' + s.fontColor + ';line-height:' + lh + ';' + style; - - if ((s.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD) - { - style += 'font-weight:bold;'; - } - - if ((s.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC) - { - style += 'font-style:italic;'; - } - - if ((s.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE) - { - style += 'text-decoration:underline;'; - } - - if (align == mxConstants.ALIGN_CENTER) - { - style += 'text-align:center;'; - } - else if (align == mxConstants.ALIGN_RIGHT) - { - style += 'text-align:right;'; - } - - var css = ''; - - if (s.fontBackgroundColor != null) - { - css += 'background-color:' + s.fontBackgroundColor + ';'; - } - - if (s.fontBorderColor != null) - { - css += 'border:1px solid ' + s.fontBorderColor + ';'; - } - - var val = str; - - if (!mxUtils.isNode(val)) - { - val = this.convertHtml(val); - - if (overflow != 'fill' && overflow != 'width') - { - // Inner div always needed to measure wrapped text - val = '
' + val + '
'; - } - else - { - style += css; - } - } - - // Uses DOM API where available. This cannot be used in IE to avoid - // an opening and two (!) closing TBODY tags being added to tables. - if (!mxClient.IS_IE && document.createElementNS) - { - var div = document.createElementNS('http://www.w3.org/1999/xhtml', 'div'); - div.setAttribute('style', style); - - if (mxUtils.isNode(val)) - { - // Creates a copy for export - if (this.root.ownerDocument != document) - { - div.appendChild(val.cloneNode(true)); - } - else - { - div.appendChild(val); - } - } - else - { - div.innerHTML = val; - } - - return div; - } - else - { - // Serializes for export - if (mxUtils.isNode(val) && this.root.ownerDocument != document) - { - val = val.outerHTML; - } - - // NOTE: FF 3.6 crashes if content CSS contains "height:100%" - return mxUtils.parseXml('
' + val + '
').documentElement; - } -}; - -/** - * Invalidates the cached offset size for the given node. - */ -mxSvgCanvas2D.prototype.invalidateCachedOffsetSize = function(node) -{ - delete node.firstChild.mxCachedOffsetWidth; - delete node.firstChild.mxCachedFinalOffsetWidth; - delete node.firstChild.mxCachedFinalOffsetHeight; -}; - -/** - * Updates existing DOM nodes for text rendering. LATER: Merge common parts with text function below. - */ -mxSvgCanvas2D.prototype.updateText = function(x, y, w, h, align, valign, wrap, overflow, clip, rotation, node) -{ - if (node != null && node.firstChild != null && node.firstChild.firstChild != null && - node.firstChild.firstChild.firstChild != null) - { - // Uses outer group for opacity and transforms to - // fix rendering order in Chrome - var group = node.firstChild; - var fo = group.firstChild; - var div = fo.firstChild; - - rotation = (rotation != null) ? rotation : 0; - - var s = this.state; - x += s.dx; - y += s.dy; - - if (clip) - { - div.style.maxHeight = Math.round(h) + 'px'; - div.style.maxWidth = Math.round(w) + 'px'; - } - else if (overflow == 'fill') - { - div.style.width = Math.round(w + 1) + 'px'; - div.style.height = Math.round(h + 1) + 'px'; - } - else if (overflow == 'width') - { - div.style.width = Math.round(w + 1) + 'px'; - - if (h > 0) - { - div.style.maxHeight = Math.round(h) + 'px'; - } - } - - if (wrap && w > 0) - { - div.style.width = Math.round(w + 1) + 'px'; - } - - // Code that depends on the size which is computed after - // the element was added to the DOM. - var ow = 0; - var oh = 0; - - // Padding avoids clipping on border and wrapping for differing font metrics on platforms - var padX = 0; - var padY = 2; - - var sizeDiv = div; - - if (sizeDiv.firstChild != null && sizeDiv.firstChild.nodeName == 'DIV') - { - sizeDiv = sizeDiv.firstChild; - } - - var tmp = (group.mxCachedOffsetWidth != null) ? group.mxCachedOffsetWidth : sizeDiv.offsetWidth; - ow = tmp + padX; - - // Recomputes the height of the element for wrapped width - if (wrap && overflow != 'fill') - { - if (clip) - { - ow = Math.min(ow, w); - } - - div.style.width = Math.round(ow + 1) + 'px'; - } - - ow = (group.mxCachedFinalOffsetWidth != null) ? group.mxCachedFinalOffsetWidth : sizeDiv.offsetWidth; - oh = (group.mxCachedFinalOffsetHeight != null) ? group.mxCachedFinalOffsetHeight : sizeDiv.offsetHeight; - - if (this.cacheOffsetSize) - { - group.mxCachedOffsetWidth = tmp; - group.mxCachedFinalOffsetWidth = ow; - group.mxCachedFinalOffsetHeight = oh; - } - - ow += padX; - oh -= 2; - - if (clip) - { - oh = Math.min(oh, h); - ow = Math.min(ow, w); - } - - if (overflow == 'width') - { - h = oh; - } - else if (overflow != 'fill') - { - w = ow; - h = oh; - } - - var dx = 0; - var dy = 0; - - if (align == mxConstants.ALIGN_CENTER) - { - dx -= w / 2; - } - else if (align == mxConstants.ALIGN_RIGHT) - { - dx -= w; - } - - x += dx; - - // FIXME: LINE_HEIGHT not ideal for all text sizes, fix for export - if (valign == mxConstants.ALIGN_MIDDLE) - { - dy -= h / 2; - } - else if (valign == mxConstants.ALIGN_BOTTOM) - { - dy -= h; - } - - // Workaround for rendering offsets - // TODO: Check if export needs these fixes, too - if (overflow != 'fill' && mxClient.IS_FF && mxClient.IS_WIN) - { - dy -= 2; - } - - y += dy; - - var tr = (s.scale != 1) ? 'scale(' + s.scale + ')' : ''; - - if (s.rotation != 0 && this.rotateHtml) - { - tr += 'rotate(' + (s.rotation) + ',' + (w / 2) + ',' + (h / 2) + ')'; - var pt = this.rotatePoint((x + w / 2) * s.scale, (y + h / 2) * s.scale, - s.rotation, s.rotationCx, s.rotationCy); - x = pt.x - w * s.scale / 2; - y = pt.y - h * s.scale / 2; - } - else - { - x *= s.scale; - y *= s.scale; - } - - if (rotation != 0) - { - tr += 'rotate(' + (rotation) + ',' + (-dx) + ',' + (-dy) + ')'; - } - - group.setAttribute('transform', 'translate(' + Math.round(x) + ',' + Math.round(y) + ')' + tr); - fo.setAttribute('width', Math.round(Math.max(1, w))); - fo.setAttribute('height', Math.round(Math.max(1, h))); - } -}; - -/** - * Function: text - * - * Paints the given text. Possible values for format are empty string for plain - * text and html for HTML markup. Note that HTML markup is only supported if - * foreignObject is supported and is true. (This means IE9 and later - * does currently not support HTML text as part of shapes.) - */ -mxSvgCanvas2D.prototype.text = function(x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation, dir) -{ - if (this.textEnabled && str != null) - { - rotation = (rotation != null) ? rotation : 0; - - var s = this.state; - x += s.dx; - y += s.dy; - - if (this.foEnabled && format == 'html') - { - var style = 'vertical-align:top;'; - - if (clip) - { - style += 'overflow:hidden;max-height:' + Math.round(h) + 'px;max-width:' + Math.round(w) + 'px;'; - } - else if (overflow == 'fill') - { - style += 'width:' + Math.round(w + 1) + 'px;height:' + Math.round(h + 1) + 'px;overflow:hidden;'; - } - else if (overflow == 'width') - { - style += 'width:' + Math.round(w + 1) + 'px;'; - - if (h > 0) - { - style += 'max-height:' + Math.round(h) + 'px;overflow:hidden;'; - } - } - - if (wrap && w > 0) - { - style += 'width:' + Math.round(w + 1) + 'px;white-space:normal;word-wrap:' + - mxConstants.WORD_WRAP + ';'; - } - else - { - style += 'white-space:nowrap;'; - } - - // Uses outer group for opacity and transforms to - // fix rendering order in Chrome - var group = this.createElement('g'); - - if (s.alpha < 1) - { - group.setAttribute('opacity', s.alpha); - } - - var fo = this.createElement('foreignObject'); - fo.setAttribute('style', 'overflow:visible;'); - fo.setAttribute('pointer-events', 'all'); - - var div = this.createDiv(str, align, valign, style, overflow); - - // Ignores invalid XHTML labels - if (div == null) - { - return; - } - else if (dir != null) - { - div.setAttribute('dir', dir); - } - - group.appendChild(fo); - this.root.appendChild(group); - - // Code that depends on the size which is computed after - // the element was added to the DOM. - var ow = 0; - var oh = 0; - - // Padding avoids clipping on border and wrapping for differing font metrics on platforms - var padX = 2; - var padY = 2; - - // NOTE: IE is always export as it does not support foreign objects - if (mxClient.IS_IE && (document.documentMode == 9 || !mxClient.IS_SVG)) - { - // Handles non-standard namespace for getting size in IE - var clone = document.createElement('div'); - - clone.style.cssText = div.getAttribute('style'); - clone.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; - clone.style.position = 'absolute'; - clone.style.visibility = 'hidden'; - - // Inner DIV is needed for text measuring - var div2 = document.createElement('div'); - div2.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; - div2.style.wordWrap = mxConstants.WORD_WRAP; - div2.innerHTML = (mxUtils.isNode(str)) ? str.outerHTML : str; - clone.appendChild(div2); - - document.body.appendChild(clone); - - // Workaround for different box models - if (document.documentMode != 8 && document.documentMode != 9 && s.fontBorderColor != null) - { - padX += 2; - padY += 2; - } - - if (wrap && w > 0) - { - var tmp = div2.offsetWidth; - - // Workaround for adding padding twice in IE8/IE9 standards mode if label is wrapped - padDx = 0; - - // For export, if no wrapping occurs, we add a large padding to make - // sure there is no wrapping even if the text metrics are different. - // This adds support for text metrics on different operating systems. - // Disables wrapping if text is not wrapped for given width - if (!clip && wrap && w > 0 && this.root.ownerDocument != document && overflow != 'fill') - { - var ws = clone.style.whiteSpace; - div2.style.whiteSpace = 'nowrap'; - - if (tmp < div2.offsetWidth) - { - clone.style.whiteSpace = ws; - } - } - - if (clip) - { - tmp = Math.min(tmp, w); - } - - clone.style.width = tmp + 'px'; - - // Padding avoids clipping on border - ow = div2.offsetWidth + padX + padDx; - oh = div2.offsetHeight + padY; - - // Overrides the width of the DIV via XML DOM by using the - // clone DOM style, getting the CSS text for that and - // then setting that on the DIV via setAttribute - clone.style.display = 'inline-block'; - clone.style.position = ''; - clone.style.visibility = ''; - clone.style.width = ow + 'px'; - - div.setAttribute('style', clone.style.cssText); - } - else - { - // Padding avoids clipping on border - ow = div2.offsetWidth + padX; - oh = div2.offsetHeight + padY; - } - - clone.parentNode.removeChild(clone); - fo.appendChild(div); - } - else - { - // Uses document for text measuring during export - if (this.root.ownerDocument != document) - { - div.style.visibility = 'hidden'; - document.body.appendChild(div); - } - else - { - fo.appendChild(div); - } - - var sizeDiv = div; - - if (sizeDiv.firstChild != null && sizeDiv.firstChild.nodeName == 'DIV') - { - sizeDiv = sizeDiv.firstChild; - - if (wrap && div.style.wordWrap == 'break-word') - { - sizeDiv.style.width = '100%'; - } - } - - var tmp = sizeDiv.offsetWidth; - - // Workaround for text measuring in hidden containers - if (tmp == 0 && div.parentNode == fo) - { - div.style.visibility = 'hidden'; - document.body.appendChild(div); - - tmp = sizeDiv.offsetWidth; - } - - if (this.cacheOffsetSize) - { - group.mxCachedOffsetWidth = tmp; - } - - // Disables wrapping if text is not wrapped for given width - if (!clip && wrap && w > 0 && this.root.ownerDocument != document && - overflow != 'fill' && overflow != 'width') - { - var ws = div.style.whiteSpace; - div.style.whiteSpace = 'nowrap'; - - if (tmp < sizeDiv.offsetWidth) - { - div.style.whiteSpace = ws; - } - } - - ow = tmp + padX - 1; - - // Recomputes the height of the element for wrapped width - if (wrap && overflow != 'fill' && overflow != 'width') - { - if (clip) - { - ow = Math.min(ow, w); - } - - div.style.width = ow + 'px'; - } - - ow = sizeDiv.offsetWidth; - oh = sizeDiv.offsetHeight; - - if (this.cacheOffsetSize) - { - group.mxCachedFinalOffsetWidth = ow; - group.mxCachedFinalOffsetHeight = oh; - } - - oh -= padY; - - if (div.parentNode != fo) - { - fo.appendChild(div); - div.style.visibility = ''; - } - } - - if (clip) - { - oh = Math.min(oh, h); - ow = Math.min(ow, w); - } - - if (overflow == 'width') - { - h = oh; - } - else if (overflow != 'fill') - { - w = ow; - h = oh; - } - - if (s.alpha < 1) - { - group.setAttribute('opacity', s.alpha); - } - - var dx = 0; - var dy = 0; - - if (align == mxConstants.ALIGN_CENTER) - { - dx -= w / 2; - } - else if (align == mxConstants.ALIGN_RIGHT) - { - dx -= w; - } - - x += dx; - - // FIXME: LINE_HEIGHT not ideal for all text sizes, fix for export - if (valign == mxConstants.ALIGN_MIDDLE) - { - dy -= h / 2; - } - else if (valign == mxConstants.ALIGN_BOTTOM) - { - dy -= h; - } - - // Workaround for rendering offsets - // TODO: Check if export needs these fixes, too - //if (this.root.ownerDocument == document) - if (overflow != 'fill' && mxClient.IS_FF && mxClient.IS_WIN) - { - dy -= 2; - } - - y += dy; - - var tr = (s.scale != 1) ? 'scale(' + s.scale + ')' : ''; - - if (s.rotation != 0 && this.rotateHtml) - { - tr += 'rotate(' + (s.rotation) + ',' + (w / 2) + ',' + (h / 2) + ')'; - var pt = this.rotatePoint((x + w / 2) * s.scale, (y + h / 2) * s.scale, - s.rotation, s.rotationCx, s.rotationCy); - x = pt.x - w * s.scale / 2; - y = pt.y - h * s.scale / 2; - } - else - { - x *= s.scale; - y *= s.scale; - } - - if (rotation != 0) - { - tr += 'rotate(' + (rotation) + ',' + (-dx) + ',' + (-dy) + ')'; - } - - group.setAttribute('transform', 'translate(' + (Math.round(x) + this.foOffset) + ',' + - (Math.round(y) + this.foOffset) + ')' + tr); - fo.setAttribute('width', Math.round(Math.max(1, w))); - fo.setAttribute('height', Math.round(Math.max(1, h))); - - // Adds alternate content if foreignObject not supported in viewer - if (this.root.ownerDocument != document) - { - var alt = this.createAlternateContent(fo, x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation); - - if (alt != null) - { - fo.setAttribute('requiredFeatures', 'http://www.w3.org/TR/SVG11/feature#Extensibility'); - var sw = this.createElement('switch'); - sw.appendChild(fo); - sw.appendChild(alt); - group.appendChild(sw); - } - } - } - else - { - this.plainText(x, y, w, h, str, align, valign, wrap, overflow, clip, rotation, dir); - } - } -}; - -/** - * Function: createClip - * - * Creates a clip for the given coordinates. - */ -mxSvgCanvas2D.prototype.createClip = function(x, y, w, h) -{ - x = Math.round(x); - y = Math.round(y); - w = Math.round(w); - h = Math.round(h); - - var id = 'mx-clip-' + x + '-' + y + '-' + w + '-' + h; - - var counter = 0; - var tmp = id + '-' + counter; - - // Resolves ID conflicts - while (document.getElementById(tmp) != null) - { - tmp = id + '-' + (++counter); - } - - clip = this.createElement('clipPath'); - clip.setAttribute('id', tmp); - - var rect = this.createElement('rect'); - rect.setAttribute('x', x); - rect.setAttribute('y', y); - rect.setAttribute('width', w); - rect.setAttribute('height', h); - - clip.appendChild(rect); - - return clip; -}; - -/** - * Function: text - * - * Paints the given text. Possible values for format are empty string for - * plain text and html for HTML markup. - */ -mxSvgCanvas2D.prototype.plainText = function(x, y, w, h, str, align, valign, wrap, overflow, clip, rotation, dir) -{ - rotation = (rotation != null) ? rotation : 0; - var s = this.state; - var size = s.fontSize; - var node = this.createElement('g'); - var tr = s.transform || ''; - this.updateFont(node); - - // Non-rotated text - if (rotation != 0) - { - tr += 'rotate(' + rotation + ',' + this.format(x * s.scale) + ',' + this.format(y * s.scale) + ')'; - } - - if (dir != null) - { - node.setAttribute('direction', dir); - } - - if (clip && w > 0 && h > 0) - { - var cx = x; - var cy = y; - - if (align == mxConstants.ALIGN_CENTER) - { - cx -= w / 2; - } - else if (align == mxConstants.ALIGN_RIGHT) - { - cx -= w; - } - - if (overflow != 'fill') - { - if (valign == mxConstants.ALIGN_MIDDLE) - { - cy -= h / 2; - } - else if (valign == mxConstants.ALIGN_BOTTOM) - { - cy -= h; - } - } - - // LATER: Remove spacing from clip rectangle - var c = this.createClip(cx * s.scale - 2, cy * s.scale - 2, w * s.scale + 4, h * s.scale + 4); - - if (this.defs != null) - { - this.defs.appendChild(c); - } - else - { - // Makes sure clip is removed with referencing node - this.root.appendChild(c); - } - - if (!mxClient.IS_CHROME_APP && !mxClient.IS_IE && !mxClient.IS_IE11 && - !mxClient.IS_EDGE && this.root.ownerDocument == document) - { - // Workaround for potential base tag - var base = this.getBaseUrl().replace(/([\(\)])/g, '\\$1'); - node.setAttribute('clip-path', 'url(' + base + '#' + c.getAttribute('id') + ')'); - } - else - { - node.setAttribute('clip-path', 'url(#' + c.getAttribute('id') + ')'); - } - } - - // Default is left - var anchor = (align == mxConstants.ALIGN_RIGHT) ? 'end' : - (align == mxConstants.ALIGN_CENTER) ? 'middle' : - 'start'; - - // Text-anchor start is default in SVG - if (anchor != 'start') - { - node.setAttribute('text-anchor', anchor); - } - - if (!this.styleEnabled || size != mxConstants.DEFAULT_FONTSIZE) - { - node.setAttribute('font-size', (size * s.scale) + 'px'); - } - - if (tr.length > 0) - { - node.setAttribute('transform', tr); - } - - if (s.alpha < 1) - { - node.setAttribute('opacity', s.alpha); - } - - var lines = str.split('\n'); - var lh = Math.round(size * mxConstants.LINE_HEIGHT); - var textHeight = size + (lines.length - 1) * lh; - - var cy = y + size - 1; - - if (valign == mxConstants.ALIGN_MIDDLE) - { - if (overflow == 'fill') - { - cy -= h / 2; - } - else - { - var dy = ((this.matchHtmlAlignment && clip && h > 0) ? Math.min(textHeight, h) : textHeight) / 2; - cy -= dy + 1; - } - } - else if (valign == mxConstants.ALIGN_BOTTOM) - { - if (overflow == 'fill') - { - cy -= h; - } - else - { - var dy = (this.matchHtmlAlignment && clip && h > 0) ? Math.min(textHeight, h) : textHeight; - cy -= dy + 2; - } - } - - for (var i = 0; i < lines.length; i++) - { - // Workaround for bounding box of empty lines and spaces - if (lines[i].length > 0 && mxUtils.trim(lines[i]).length > 0) - { - var text = this.createElement('text'); - // LATER: Match horizontal HTML alignment - text.setAttribute('x', this.format(x * s.scale) + this.textOffset); - text.setAttribute('y', this.format(cy * s.scale) + this.textOffset); - - mxUtils.write(text, lines[i]); - node.appendChild(text); - } - - cy += lh; - } - - this.root.appendChild(node); - this.addTextBackground(node, str, x, y, w, (overflow == 'fill') ? h : textHeight, align, valign, overflow); -}; - -/** - * Function: updateFont - * - * Updates the text properties for the given node. (NOTE: For this to work in - * IE, the given node must be a text or tspan element.) - */ -mxSvgCanvas2D.prototype.updateFont = function(node) -{ - var s = this.state; - - node.setAttribute('fill', s.fontColor); - - if (!this.styleEnabled || s.fontFamily != mxConstants.DEFAULT_FONTFAMILY) - { - node.setAttribute('font-family', s.fontFamily); - } - - if ((s.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD) - { - node.setAttribute('font-weight', 'bold'); - } - - if ((s.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC) - { - node.setAttribute('font-style', 'italic'); - } - - if ((s.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE) - { - node.setAttribute('text-decoration', 'underline'); - } -}; - -/** - * Function: addTextBackground - * - * Background color and border - */ -mxSvgCanvas2D.prototype.addTextBackground = function(node, str, x, y, w, h, align, valign, overflow) -{ - var s = this.state; - - if (s.fontBackgroundColor != null || s.fontBorderColor != null) - { - var bbox = null; - - if (overflow == 'fill' || overflow == 'width') - { - if (align == mxConstants.ALIGN_CENTER) - { - x -= w / 2; - } - else if (align == mxConstants.ALIGN_RIGHT) - { - x -= w; - } - - if (valign == mxConstants.ALIGN_MIDDLE) - { - y -= h / 2; - } - else if (valign == mxConstants.ALIGN_BOTTOM) - { - y -= h; - } - - bbox = new mxRectangle((x + 1) * s.scale, y * s.scale, (w - 2) * s.scale, (h + 2) * s.scale); - } - else if (node.getBBox != null && this.root.ownerDocument == document) - { - // Uses getBBox only if inside document for correct size - try - { - bbox = node.getBBox(); - var ie = mxClient.IS_IE && mxClient.IS_SVG; - bbox = new mxRectangle(bbox.x, bbox.y + ((ie) ? 0 : 1), bbox.width, bbox.height + ((ie) ? 1 : 0)); - } - catch (e) - { - // Ignores NS_ERROR_FAILURE in FF if container display is none. - } - } - else - { - // Computes size if not in document or no getBBox available - var div = document.createElement('div'); - - // Wrapping and clipping can be ignored here - div.style.lineHeight = (mxConstants.ABSOLUTE_LINE_HEIGHT) ? (s.fontSize * mxConstants.LINE_HEIGHT) + 'px' : mxConstants.LINE_HEIGHT; - div.style.fontSize = s.fontSize + 'px'; - div.style.fontFamily = s.fontFamily; - div.style.whiteSpace = 'nowrap'; - div.style.position = 'absolute'; - div.style.visibility = 'hidden'; - div.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; - div.style.zoom = '1'; - - if ((s.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD) - { - div.style.fontWeight = 'bold'; - } - - if ((s.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC) - { - div.style.fontStyle = 'italic'; - } - - str = mxUtils.htmlEntities(str, false); - div.innerHTML = str.replace(/\n/g, '
'); - - document.body.appendChild(div); - var w = div.offsetWidth; - var h = div.offsetHeight; - div.parentNode.removeChild(div); - - if (align == mxConstants.ALIGN_CENTER) - { - x -= w / 2; - } - else if (align == mxConstants.ALIGN_RIGHT) - { - x -= w; - } - - if (valign == mxConstants.ALIGN_MIDDLE) - { - y -= h / 2; - } - else if (valign == mxConstants.ALIGN_BOTTOM) - { - y -= h; - } - - bbox = new mxRectangle((x + 1) * s.scale, (y + 2) * s.scale, w * s.scale, (h + 1) * s.scale); - } - - if (bbox != null) - { - var n = this.createElement('rect'); - n.setAttribute('fill', s.fontBackgroundColor || 'none'); - n.setAttribute('stroke', s.fontBorderColor || 'none'); - n.setAttribute('x', Math.floor(bbox.x - 1)); - n.setAttribute('y', Math.floor(bbox.y - 1)); - n.setAttribute('width', Math.ceil(bbox.width + 2)); - n.setAttribute('height', Math.ceil(bbox.height)); - - var sw = (s.fontBorderColor != null) ? Math.max(1, this.format(s.scale)) : 0; - n.setAttribute('stroke-width', sw); - - // Workaround for crisp rendering - only required if not exporting - if (this.root.ownerDocument == document && mxUtils.mod(sw, 2) == 1) - { - n.setAttribute('transform', 'translate(0.5, 0.5)'); - } - - node.insertBefore(n, node.firstChild); - } - } -}; - -/** - * Function: stroke - * - * Paints the outline of the current path. - */ -mxSvgCanvas2D.prototype.stroke = function() -{ - this.addNode(false, true); -}; - -/** - * Function: fill - * - * Fills the current path. - */ -mxSvgCanvas2D.prototype.fill = function() -{ - this.addNode(true, false); -}; - -/** - * Function: fillAndStroke - * - * Fills and paints the outline of the current path. - */ -mxSvgCanvas2D.prototype.fillAndStroke = function() -{ - this.addNode(true, true); -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -/** - * - * Class: mxVmlCanvas2D - * - * Implements a canvas to be used for rendering VML. Here is an example of implementing a - * fallback for SVG images which are not supported in VML-based browsers. - * - * (code) - * var mxVmlCanvas2DImage = mxVmlCanvas2D.prototype.image; - * mxVmlCanvas2D.prototype.image = function(x, y, w, h, src, aspect, flipH, flipV) - * { - * if (src.substring(src.length - 4, src.length) == '.svg') - * { - * src = 'http://www.jgraph.com/images/mxgraph.gif'; - * } - * - * mxVmlCanvas2DImage.apply(this, arguments); - * }; - * (end) - * - * To disable anti-aliasing in the output, use the following code. - * - * (code) - * document.createStyleSheet().cssText = mxClient.VML_PREFIX + '\\:*{antialias:false;)}'; - * (end) - * - * A description of the public API is available in . Note that - * there is a known issue in VML where gradients are painted using the outer - * bounding box of rotated shapes, not the actual bounds of the shape. See - * also for plain text label restrictions in shapes for VML. - */ -var mxVmlCanvas2D = function(root) -{ - mxAbstractCanvas2D.call(this); - - /** - * Variable: root - * - * Reference to the container for the SVG content. - */ - this.root = root; -}; - -/** - * Extends mxAbstractCanvas2D - */ -mxUtils.extend(mxVmlCanvas2D, mxAbstractCanvas2D); - -/** - * Variable: path - * - * Holds the current DOM node. - */ -mxVmlCanvas2D.prototype.node = null; - -/** - * Variable: textEnabled - * - * Specifies if text output should be enabledetB. Default is true. - */ -mxVmlCanvas2D.prototype.textEnabled = true; - -/** - * Variable: moveOp - * - * Contains the string used for moving in paths. Default is 'm'. - */ -mxVmlCanvas2D.prototype.moveOp = 'm'; - -/** - * Variable: lineOp - * - * Contains the string used for moving in paths. Default is 'l'. - */ -mxVmlCanvas2D.prototype.lineOp = 'l'; - -/** - * Variable: curveOp - * - * Contains the string used for bezier curves. Default is 'c'. - */ -mxVmlCanvas2D.prototype.curveOp = 'c'; - -/** - * Variable: closeOp - * - * Holds the operator for closing curves. Default is 'x e'. - */ -mxVmlCanvas2D.prototype.closeOp = 'x'; - -/** - * Variable: rotatedHtmlBackground - * - * Background color for rotated HTML. Default is ''. This can be set to eg. - * white to improve rendering of rotated text in VML for IE9. - */ -mxVmlCanvas2D.prototype.rotatedHtmlBackground = ''; - -/** - * Variable: vmlScale - * - * Specifies the scale used to draw VML shapes. - */ -mxVmlCanvas2D.prototype.vmlScale = 1; - -/** - * Function: createElement - * - * Creates the given element using the document. - */ -mxVmlCanvas2D.prototype.createElement = function(name) -{ - return document.createElement(name); -}; - -/** - * Function: createVmlElement - * - * Creates a new element using and prefixes the given name with - * . - */ -mxVmlCanvas2D.prototype.createVmlElement = function(name) -{ - return this.createElement(mxClient.VML_PREFIX + ':' + name); -}; - -/** - * Function: addNode - * - * Adds the current node to the . - */ -mxVmlCanvas2D.prototype.addNode = function(filled, stroked) -{ - var node = this.node; - var s = this.state; - - if (node != null) - { - if (node.nodeName == 'shape') - { - // Checks if the path is not empty - if (this.path != null && this.path.length > 0) - { - node.path = this.path.join(' ') + ' e'; - node.style.width = this.root.style.width; - node.style.height = this.root.style.height; - node.coordsize = parseInt(node.style.width) + ' ' + parseInt(node.style.height); - } - else - { - return; - } - } - - node.strokeweight = this.format(Math.max(1, s.strokeWidth * s.scale / this.vmlScale)) + 'px'; - - if (s.shadow) - { - this.root.appendChild(this.createShadow(node, - filled && s.fillColor != null, - stroked && s.strokeColor != null)); - } - - if (stroked && s.strokeColor != null) - { - node.stroked = 'true'; - node.strokecolor = s.strokeColor; - } - else - { - node.stroked = 'false'; - } - - node.appendChild(this.createStroke()); - - if (filled && s.fillColor != null) - { - node.appendChild(this.createFill()); - } - else if (this.pointerEvents && (node.nodeName != 'shape' || - this.path[this.path.length - 1] == this.closeOp)) - { - node.appendChild(this.createTransparentFill()); - } - else - { - node.filled = 'false'; - } - - // LATER: Update existing DOM for performance - this.root.appendChild(node); - } -}; - -/** - * Function: createTransparentFill - * - * Creates a transparent fill. - */ -mxVmlCanvas2D.prototype.createTransparentFill = function() -{ - var fill = this.createVmlElement('fill'); - fill.src = mxClient.imageBasePath + '/transparent.gif'; - fill.type = 'tile'; - - return fill; -}; - -/** - * Function: createFill - * - * Creates a fill for the current state. - */ -mxVmlCanvas2D.prototype.createFill = function() -{ - var s = this.state; - - // Gradients in foregrounds not supported because special gradients - // with bounds must be created for each element in graphics-canvases - var fill = this.createVmlElement('fill'); - fill.color = s.fillColor; - - if (s.gradientColor != null) - { - fill.type = 'gradient'; - fill.method = 'none'; - fill.color2 = s.gradientColor; - var angle = 180 - s.rotation; - - if (s.gradientDirection == mxConstants.DIRECTION_WEST) - { - angle -= 90 + ((this.root.style.flip == 'x') ? 180 : 0); - } - else if (s.gradientDirection == mxConstants.DIRECTION_EAST) - { - angle += 90 + ((this.root.style.flip == 'x') ? 180 : 0); - } - else if (s.gradientDirection == mxConstants.DIRECTION_NORTH) - { - angle -= 180 + ((this.root.style.flip == 'y') ? -180 : 0); - } - else - { - angle += ((this.root.style.flip == 'y') ? -180 : 0); - } - - if (this.root.style.flip == 'x' || this.root.style.flip == 'y') - { - angle *= -1; - } - - // LATER: Fix outer bounding box for rotated shapes used in VML. - fill.angle = mxUtils.mod(angle, 360); - fill.opacity = (s.alpha * s.gradientFillAlpha * 100) + '%'; - fill.setAttribute(mxClient.OFFICE_PREFIX + ':opacity2', (s.alpha * s.gradientAlpha * 100) + '%'); - } - else if (s.alpha < 1 || s.fillAlpha < 1) - { - fill.opacity = (s.alpha * s.fillAlpha * 100) + '%'; - } - - return fill; -}; -/** - * Function: createStroke - * - * Creates a fill for the current state. - */ -mxVmlCanvas2D.prototype.createStroke = function() -{ - var s = this.state; - var stroke = this.createVmlElement('stroke'); - stroke.endcap = s.lineCap || 'flat'; - stroke.joinstyle = s.lineJoin || 'miter'; - stroke.miterlimit = s.miterLimit || '10'; - - if (s.alpha < 1 || s.strokeAlpha < 1) - { - stroke.opacity = (s.alpha * s.strokeAlpha * 100) + '%'; - } - - if (s.dashed) - { - stroke.dashstyle = this.getVmlDashStyle(); - } - - return stroke; -}; - -/** - * Function: getVmlDashPattern - * - * Returns a VML dash pattern for the current dashPattern. - * See http://msdn.microsoft.com/en-us/library/bb264085(v=vs.85).aspx - */ -mxVmlCanvas2D.prototype.getVmlDashStyle = function() -{ - var result = 'dash'; - - if (typeof(this.state.dashPattern) === 'string') - { - var tok = this.state.dashPattern.split(' '); - - if (tok.length > 0 && tok[0] == 1) - { - result = '0 2'; - } - } - - return result; -}; - -/** - * Function: createShadow - * - * Creates a shadow for the given node. - */ -mxVmlCanvas2D.prototype.createShadow = function(node, filled, stroked) -{ - var s = this.state; - var rad = -s.rotation * (Math.PI / 180); - var cos = Math.cos(rad); - var sin = Math.sin(rad); - - var dx = s.shadowDx * s.scale; - var dy = s.shadowDy * s.scale; - - if (this.root.style.flip == 'x') - { - dx *= -1; - } - else if (this.root.style.flip == 'y') - { - dy *= -1; - } - - var shadow = node.cloneNode(true); - shadow.style.marginLeft = Math.round(dx * cos - dy * sin) + 'px'; - shadow.style.marginTop = Math.round(dx * sin + dy * cos) + 'px'; - - // Workaround for wrong cloning in IE8 standards mode - if (document.documentMode == 8) - { - shadow.strokeweight = node.strokeweight; - - if (node.nodeName == 'shape') - { - shadow.path = this.path.join(' ') + ' e'; - shadow.style.width = this.root.style.width; - shadow.style.height = this.root.style.height; - shadow.coordsize = parseInt(node.style.width) + ' ' + parseInt(node.style.height); - } - } - - if (stroked) - { - shadow.strokecolor = s.shadowColor; - shadow.appendChild(this.createShadowStroke()); - } - else - { - shadow.stroked = 'false'; - } - - if (filled) - { - shadow.appendChild(this.createShadowFill()); - } - else - { - shadow.filled = 'false'; - } - - return shadow; -}; - -/** - * Function: createShadowFill - * - * Creates the fill for the shadow. - */ -mxVmlCanvas2D.prototype.createShadowFill = function() -{ - var fill = this.createVmlElement('fill'); - fill.color = this.state.shadowColor; - fill.opacity = (this.state.alpha * this.state.shadowAlpha * 100) + '%'; - - return fill; -}; - -/** - * Function: createShadowStroke - * - * Creates the stroke for the shadow. - */ -mxVmlCanvas2D.prototype.createShadowStroke = function() -{ - var stroke = this.createStroke(); - stroke.opacity = (this.state.alpha * this.state.shadowAlpha * 100) + '%'; - - return stroke; -}; - -/** - * Function: rotate - * - * Sets the rotation of the canvas. Note that rotation cannot be concatenated. - */ -mxVmlCanvas2D.prototype.rotate = function(theta, flipH, flipV, cx, cy) -{ - if (flipH && flipV) - { - theta += 180; - } - else if (flipH) - { - this.root.style.flip = 'x'; - } - else if (flipV) - { - this.root.style.flip = 'y'; - } - - if (flipH ? !flipV : flipV) - { - theta *= -1; - } - - this.root.style.rotation = theta; - this.state.rotation = this.state.rotation + theta; - this.state.rotationCx = cx; - this.state.rotationCy = cy; -}; - -/** - * Function: begin - * - * Extends superclass to create path. - */ -mxVmlCanvas2D.prototype.begin = function() -{ - mxAbstractCanvas2D.prototype.begin.apply(this, arguments); - this.node = this.createVmlElement('shape'); - this.node.style.position = 'absolute'; -}; - -/** - * Function: quadTo - * - * Replaces quadratic curve with bezier curve in VML. - */ -mxVmlCanvas2D.prototype.quadTo = function(x1, y1, x2, y2) -{ - var s = this.state; - - var cpx0 = (this.lastX + s.dx) * s.scale; - var cpy0 = (this.lastY + s.dy) * s.scale; - var qpx1 = (x1 + s.dx) * s.scale; - var qpy1 = (y1 + s.dy) * s.scale; - var cpx3 = (x2 + s.dx) * s.scale; - var cpy3 = (y2 + s.dy) * s.scale; - - var cpx1 = cpx0 + 2/3 * (qpx1 - cpx0); - var cpy1 = cpy0 + 2/3 * (qpy1 - cpy0); - - var cpx2 = cpx3 + 2/3 * (qpx1 - cpx3); - var cpy2 = cpy3 + 2/3 * (qpy1 - cpy3); - - this.path.push('c ' + this.format(cpx1) + ' ' + this.format(cpy1) + - ' ' + this.format(cpx2) + ' ' + this.format(cpy2) + - ' ' + this.format(cpx3) + ' ' + this.format(cpy3)); - this.lastX = (cpx3 / s.scale) - s.dx; - this.lastY = (cpy3 / s.scale) - s.dy; - -}; - -/** - * Function: createRect - * - * Sets the glass gradient. - */ -mxVmlCanvas2D.prototype.createRect = function(nodeName, x, y, w, h) -{ - var s = this.state; - var n = this.createVmlElement(nodeName); - n.style.position = 'absolute'; - n.style.left = this.format((x + s.dx) * s.scale) + 'px'; - n.style.top = this.format((y + s.dy) * s.scale) + 'px'; - n.style.width = this.format(w * s.scale) + 'px'; - n.style.height = this.format(h * s.scale) + 'px'; - - return n; -}; - -/** - * Function: rect - * - * Sets the current path to a rectangle. - */ -mxVmlCanvas2D.prototype.rect = function(x, y, w, h) -{ - this.node = this.createRect('rect', x, y, w, h); -}; - -/** - * Function: roundrect - * - * Sets the current path to a rounded rectangle. - */ -mxVmlCanvas2D.prototype.roundrect = function(x, y, w, h, dx, dy) -{ - this.node = this.createRect('roundrect', x, y, w, h); - // SetAttribute needed here for IE8 - this.node.setAttribute('arcsize', Math.max(dx * 100 / w, dy * 100 / h) + '%'); -}; - -/** - * Function: ellipse - * - * Sets the current path to an ellipse. - */ -mxVmlCanvas2D.prototype.ellipse = function(x, y, w, h) -{ - this.node = this.createRect('oval', x, y, w, h); -}; - -/** - * Function: image - * - * Paints an image. - */ -mxVmlCanvas2D.prototype.image = function(x, y, w, h, src, aspect, flipH, flipV) -{ - var node = null; - - if (!aspect) - { - node = this.createRect('image', x, y, w, h); - node.src = src; - } - else - { - // Uses fill with aspect to avoid asynchronous update of size - node = this.createRect('rect', x, y, w, h); - node.stroked = 'false'; - - // Handles image aspect via fill - var fill = this.createVmlElement('fill'); - fill.aspect = (aspect) ? 'atmost' : 'ignore'; - fill.rotate = 'true'; - fill.type = 'frame'; - fill.src = src; - - node.appendChild(fill); - } - - if (flipH && flipV) - { - node.style.rotation = '180'; - } - else if (flipH) - { - node.style.flip = 'x'; - } - else if (flipV) - { - node.style.flip = 'y'; - } - - if (this.state.alpha < 1 || this.state.fillAlpha < 1) - { - // KNOWN: Borders around transparent images in IE<9. Using fill.opacity - // fixes this problem by adding a white background in all IE versions. - node.style.filter += 'alpha(opacity=' + (this.state.alpha * this.state.fillAlpha * 100) + ')'; - } - - this.root.appendChild(node); -}; - -/** - * Function: createText - * - * Creates the innermost element that contains the HTML text. - */ -mxVmlCanvas2D.prototype.createDiv = function(str, align, valign, overflow) -{ - var div = this.createElement('div'); - var state = this.state; - - var css = ''; - - if (state.fontBackgroundColor != null) - { - css += 'background-color:' + state.fontBackgroundColor + ';'; - } - - if (state.fontBorderColor != null) - { - css += 'border:1px solid ' + state.fontBorderColor + ';'; - } - - if (mxUtils.isNode(str)) - { - div.appendChild(str); - } - else - { - if (overflow != 'fill' && overflow != 'width') - { - var div2 = this.createElement('div'); - div2.style.cssText = css; - div2.style.display = (mxClient.IS_QUIRKS) ? 'inline' : 'inline-block'; - div2.style.zoom = '1'; - div2.style.textDecoration = 'inherit'; - div2.innerHTML = str; - div.appendChild(div2); - } - else - { - div.style.cssText = css; - div.innerHTML = str; - } - } - - var style = div.style; - - style.fontSize = (state.fontSize / this.vmlScale) + 'px'; - style.fontFamily = state.fontFamily; - style.color = state.fontColor; - style.verticalAlign = 'top'; - style.textAlign = align || 'left'; - style.lineHeight = (mxConstants.ABSOLUTE_LINE_HEIGHT) ? (state.fontSize * mxConstants.LINE_HEIGHT / this.vmlScale) + 'px' : mxConstants.LINE_HEIGHT; - - if ((state.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD) - { - style.fontWeight = 'bold'; - } - - if ((state.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC) - { - style.fontStyle = 'italic'; - } - - if ((state.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE) - { - style.textDecoration = 'underline'; - } - - return div; -}; - -/** - * Function: text - * - * Paints the given text. Possible values for format are empty string for plain - * text and html for HTML markup. Clipping, text background and border are not - * supported for plain text in VML. - */ -mxVmlCanvas2D.prototype.text = function(x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation, dir) -{ - if (this.textEnabled && str != null) - { - var s = this.state; - - if (format == 'html') - { - if (s.rotation != null) - { - var pt = this.rotatePoint(x, y, s.rotation, s.rotationCx, s.rotationCy); - - x = pt.x; - y = pt.y; - } - - if (document.documentMode == 8 && !mxClient.IS_EM) - { - x += s.dx; - y += s.dy; - - // Workaround for rendering offsets - if (overflow != 'fill' && valign == mxConstants.ALIGN_TOP) - { - y -= 1; - } - } - else - { - x *= s.scale; - y *= s.scale; - } - - // Adds event transparency in IE8 standards without the transparent background - // filter which cannot be used due to bugs in the zoomed bounding box (too slow) - // FIXME: No event transparency if inside v:rect (ie part of shape) - // KNOWN: Offset wrong for rotated text with word that are longer than the wrapping - // width in IE8 because real width of text cannot be determined here. - // This should be fixed in mxText.updateBoundingBox by calling before this and - // passing the real width to this method if not clipped and wrapped. - var abs = (document.documentMode == 8 && !mxClient.IS_EM) ? this.createVmlElement('group') : this.createElement('div'); - abs.style.position = 'absolute'; - abs.style.display = 'inline'; - abs.style.left = this.format(x) + 'px'; - abs.style.top = this.format(y) + 'px'; - abs.style.zoom = s.scale; - - var box = this.createElement('div'); - box.style.position = 'relative'; - box.style.display = 'inline'; - - var margin = mxUtils.getAlignmentAsPoint(align, valign); - var dx = margin.x; - var dy = margin.y; - - var div = this.createDiv(str, align, valign, overflow); - var inner = this.createElement('div'); - - if (dir != null) - { - div.setAttribute('dir', dir); - } - - if (wrap && w > 0) - { - if (!clip) - { - div.style.width = Math.round(w) + 'px'; - } - - div.style.wordWrap = mxConstants.WORD_WRAP; - div.style.whiteSpace = 'normal'; - - // LATER: Check if other cases need to be handled - if (div.style.wordWrap == 'break-word') - { - var tmp = div; - - if (tmp.firstChild != null && tmp.firstChild.nodeName == 'DIV') - { - tmp.firstChild.style.width = '100%'; - } - } - } - else - { - div.style.whiteSpace = 'nowrap'; - } - - var rot = s.rotation + (rotation || 0); - - if (this.rotateHtml && rot != 0) - { - inner.style.display = 'inline'; - inner.style.zoom = '1'; - inner.appendChild(div); - - // Box not needed for rendering in IE8 standards - if (document.documentMode == 8 && !mxClient.IS_EM && this.root.nodeName != 'DIV') - { - box.appendChild(inner); - abs.appendChild(box); - } - else - { - abs.appendChild(inner); - } - } - else if (document.documentMode == 8 && !mxClient.IS_EM) - { - box.appendChild(div); - abs.appendChild(box); - } - else - { - div.style.display = 'inline'; - abs.appendChild(div); - } - - // Inserts the node into the DOM - if (this.root.nodeName != 'DIV') - { - // Rectangle to fix position in group - var rect = this.createVmlElement('rect'); - rect.stroked = 'false'; - rect.filled = 'false'; - - rect.appendChild(abs); - this.root.appendChild(rect); - } - else - { - this.root.appendChild(abs); - } - - if (clip) - { - div.style.overflow = 'hidden'; - div.style.width = Math.round(w) + 'px'; - - if (!mxClient.IS_QUIRKS) - { - div.style.maxHeight = Math.round(h) + 'px'; - } - } - else if (overflow == 'fill') - { - // KNOWN: Affects horizontal alignment in quirks - // but fill should only be used with align=left - div.style.overflow = 'hidden'; - div.style.width = (Math.max(0, w) + 1) + 'px'; - div.style.height = (Math.max(0, h) + 1) + 'px'; - } - else if (overflow == 'width') - { - // KNOWN: Affects horizontal alignment in quirks - // but fill should only be used with align=left - div.style.overflow = 'hidden'; - div.style.width = (Math.max(0, w) + 1) + 'px'; - div.style.maxHeight = (Math.max(0, h) + 1) + 'px'; - } - - if (this.rotateHtml && rot != 0) - { - var rad = rot * (Math.PI / 180); - - // Precalculate cos and sin for the rotation - var real_cos = parseFloat(parseFloat(Math.cos(rad)).toFixed(8)); - var real_sin = parseFloat(parseFloat(Math.sin(-rad)).toFixed(8)); - - rad %= 2 * Math.PI; - if (rad < 0) rad += 2 * Math.PI; - rad %= Math.PI; - if (rad > Math.PI / 2) rad = Math.PI - rad; - - var cos = Math.cos(rad); - var sin = Math.sin(rad); - - // Adds div to document to measure size - if (document.documentMode == 8 && !mxClient.IS_EM) - { - div.style.display = 'inline-block'; - inner.style.display = 'inline-block'; - box.style.display = 'inline-block'; - } - - div.style.visibility = 'hidden'; - div.style.position = 'absolute'; - document.body.appendChild(div); - - var sizeDiv = div; - - if (sizeDiv.firstChild != null && sizeDiv.firstChild.nodeName == 'DIV') - { - sizeDiv = sizeDiv.firstChild; - } - - var tmp = sizeDiv.offsetWidth + 3; - var oh = sizeDiv.offsetHeight; - - if (clip) - { - w = Math.min(w, tmp); - oh = Math.min(oh, h); - } - else - { - w = tmp; - } - - // Handles words that are longer than the given wrapping width - if (wrap) - { - div.style.width = w + 'px'; - } - - // Simulates max-height in quirks - if (mxClient.IS_QUIRKS && (clip || overflow == 'width') && oh > h) - { - oh = h; - - // Quirks does not support maxHeight - div.style.height = oh + 'px'; - } - - h = oh; - - var top_fix = (h - h * cos + w * -sin) / 2 - real_sin * w * (dx + 0.5) + real_cos * h * (dy + 0.5); - var left_fix = (w - w * cos + h * -sin) / 2 + real_cos * w * (dx + 0.5) + real_sin * h * (dy + 0.5); - - if (abs.nodeName == 'group' && this.root.nodeName == 'DIV') - { - // Workaround for bug where group gets moved away if left and top are non-zero in IE8 standards - var pos = this.createElement('div'); - pos.style.display = 'inline-block'; - pos.style.position = 'absolute'; - pos.style.left = this.format(x + (left_fix - w / 2) * s.scale) + 'px'; - pos.style.top = this.format(y + (top_fix - h / 2) * s.scale) + 'px'; - - abs.parentNode.appendChild(pos); - pos.appendChild(abs); - } - else - { - var sc = (document.documentMode == 8 && !mxClient.IS_EM) ? 1 : s.scale; - - abs.style.left = this.format(x + (left_fix - w / 2) * sc) + 'px'; - abs.style.top = this.format(y + (top_fix - h / 2) * sc) + 'px'; - } - - // KNOWN: Rotated text rendering quality is bad for IE9 quirks - inner.style.filter = "progid:DXImageTransform.Microsoft.Matrix(M11="+real_cos+", M12="+ - real_sin+", M21="+(-real_sin)+", M22="+real_cos+", sizingMethod='auto expand')"; - inner.style.backgroundColor = this.rotatedHtmlBackground; - - if (this.state.alpha < 1) - { - inner.style.filter += 'alpha(opacity=' + (this.state.alpha * 100) + ')'; - } - - // Restore parent node for DIV - inner.appendChild(div); - div.style.position = ''; - div.style.visibility = ''; - } - else if (document.documentMode != 8 || mxClient.IS_EM) - { - div.style.verticalAlign = 'top'; - - if (this.state.alpha < 1) - { - abs.style.filter = 'alpha(opacity=' + (this.state.alpha * 100) + ')'; - } - - // Adds div to document to measure size - var divParent = div.parentNode; - div.style.visibility = 'hidden'; - document.body.appendChild(div); - - w = div.offsetWidth; - var oh = div.offsetHeight; - - // Simulates max-height in quirks - if (mxClient.IS_QUIRKS && clip && oh > h) - { - oh = h; - - // Quirks does not support maxHeight - div.style.height = oh + 'px'; - } - - h = oh; - - div.style.visibility = ''; - divParent.appendChild(div); - - abs.style.left = this.format(x + w * dx * this.state.scale) + 'px'; - abs.style.top = this.format(y + h * dy * this.state.scale) + 'px'; - } - else - { - if (this.state.alpha < 1) - { - div.style.filter = 'alpha(opacity=' + (this.state.alpha * 100) + ')'; - } - - // Faster rendering in IE8 without offsetWidth/Height - box.style.left = (dx * 100) + '%'; - box.style.top = (dy * 100) + '%'; - } - } - else - { - this.plainText(x, y, w, h, mxUtils.htmlEntities(str, false), align, valign, wrap, format, overflow, clip, rotation, dir); - } - } -}; - -/** - * Function: plainText - * - * Paints the outline of the current path. - */ -mxVmlCanvas2D.prototype.plainText = function(x, y, w, h, str, align, valign, wrap, format, overflow, clip, rotation, dir) -{ - // TextDirection is ignored since this code is not used (format is always HTML in the text function) - var s = this.state; - x = (x + s.dx) * s.scale; - y = (y + s.dy) * s.scale; - - var node = this.createVmlElement('shape'); - node.style.width = '1px'; - node.style.height = '1px'; - node.stroked = 'false'; - - var fill = this.createVmlElement('fill'); - fill.color = s.fontColor; - fill.opacity = (s.alpha * 100) + '%'; - node.appendChild(fill); - - var path = this.createVmlElement('path'); - path.textpathok = 'true'; - path.v = 'm ' + this.format(0) + ' ' + this.format(0) + ' l ' + this.format(1) + ' ' + this.format(0); - - node.appendChild(path); - - // KNOWN: Font family and text decoration ignored - var tp = this.createVmlElement('textpath'); - tp.style.cssText = 'v-text-align:' + align; - tp.style.align = align; - tp.style.fontFamily = s.fontFamily; - tp.string = str; - tp.on = 'true'; - - // Scale via fontsize instead of node.style.zoom for correct offsets in IE8 - var size = s.fontSize * s.scale / this.vmlScale; - tp.style.fontSize = size + 'px'; - - // Bold - if ((s.fontStyle & mxConstants.FONT_BOLD) == mxConstants.FONT_BOLD) - { - tp.style.fontWeight = 'bold'; - } - - // Italic - if ((s.fontStyle & mxConstants.FONT_ITALIC) == mxConstants.FONT_ITALIC) - { - tp.style.fontStyle = 'italic'; - } - - // Underline - if ((s.fontStyle & mxConstants.FONT_UNDERLINE) == mxConstants.FONT_UNDERLINE) - { - tp.style.textDecoration = 'underline'; - } - - var lines = str.split('\n'); - var textHeight = size + (lines.length - 1) * size * mxConstants.LINE_HEIGHT; - var dx = 0; - var dy = 0; - - if (valign == mxConstants.ALIGN_BOTTOM) - { - dy = - textHeight / 2; - } - else if (valign != mxConstants.ALIGN_MIDDLE) // top - { - dy = textHeight / 2; - } - - if (rotation != null) - { - node.style.rotation = rotation; - var rad = rotation * (Math.PI / 180); - dx = Math.sin(rad) * dy; - dy = Math.cos(rad) * dy; - } - - // FIXME: Clipping is relative to bounding box - /*if (clip) - { - node.style.clip = 'rect(0px ' + this.format(w) + 'px ' + this.format(h) + 'px 0px)'; - }*/ - - node.appendChild(tp); - node.style.left = this.format(x - dx) + 'px'; - node.style.top = this.format(y + dy) + 'px'; - - this.root.appendChild(node); -}; - -/** - * Function: stroke - * - * Paints the outline of the current path. - */ -mxVmlCanvas2D.prototype.stroke = function() -{ - this.addNode(false, true); -}; - -/** - * Function: fill - * - * Fills the current path. - */ -mxVmlCanvas2D.prototype.fill = function() -{ - this.addNode(true, false); -}; - -/** - * Function: fillAndStroke - * - * Fills and paints the outline of the current path. - */ -mxVmlCanvas2D.prototype.fillAndStroke = function() -{ - this.addNode(true, true); -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -/** - * Class: mxGuide - * - * Implements the alignment of selection cells to other cells in the graph. - * - * Constructor: mxGuide - * - * Constructs a new guide object. - */ -function mxGuide(graph, states) -{ - this.graph = graph; - this.setStates(states); -}; - -/** - * Variable: graph - * - * Reference to the enclosing instance. - */ -mxGuide.prototype.graph = null; - -/** - * Variable: states - * - * Contains the that are used for alignment. - */ -mxGuide.prototype.states = null; - -/** - * Variable: horizontal - * - * Specifies if horizontal guides are enabled. Default is true. - */ -mxGuide.prototype.horizontal = true; - -/** - * Variable: vertical - * - * Specifies if vertical guides are enabled. Default is true. - */ -mxGuide.prototype.vertical = true; - -/** - * Variable: vertical - * - * Holds the for the horizontal guide. - */ -mxGuide.prototype.guideX = null; - -/** - * Variable: vertical - * - * Holds the for the vertical guide. - */ -mxGuide.prototype.guideY = null; - -/** - * Function: setStates - * - * Sets the that should be used for alignment. - */ -mxGuide.prototype.setStates = function(states) -{ - this.states = states; -}; - -/** - * Function: isEnabledForEvent - * - * Returns true if the guide should be enabled for the given native event. This - * implementation always returns true. - */ -mxGuide.prototype.isEnabledForEvent = function(evt) -{ - return true; -}; - -/** - * Function: getGuideTolerance - * - * Returns the tolerance for the guides. Default value is gridSize / 2. - */ -mxGuide.prototype.getGuideTolerance = function() -{ - return this.graph.gridSize / 2; -}; - -/** - * Function: createGuideShape - * - * Returns the mxShape to be used for painting the respective guide. This - * implementation returns a new, dashed and crisp using - * and as the format. - * - * Parameters: - * - * horizontal - Boolean that specifies which guide should be created. - */ -mxGuide.prototype.createGuideShape = function(horizontal) -{ - var guide = new mxPolyline([], mxConstants.GUIDE_COLOR, mxConstants.GUIDE_STROKEWIDTH); - guide.isDashed = true; - - return guide; -}; - -/** - * Function: move - * - * Moves the by the given and returnt the snapped point. - */ -mxGuide.prototype.move = function(bounds, delta, gridEnabled) -{ - if (this.states != null && (this.horizontal || this.vertical) && bounds != null && delta != null) - { - var trx = this.graph.getView().translate; - var scale = this.graph.getView().scale; - var dx = delta.x; - var dy = delta.y; - - var overrideX = false; - var stateX = null; - var valueX = null; - var overrideY = false; - var stateY = null; - var valueY = null; - - var tt = this.getGuideTolerance(); - var ttX = tt; - var ttY = tt; - - var b = bounds.clone(); - b.x += delta.x; - b.y += delta.y; - - var left = b.x; - var right = b.x + b.width; - var center = b.getCenterX(); - var top = b.y; - var bottom = b.y + b.height; - var middle = b.getCenterY(); - - // Snaps the left, center and right to the given x-coordinate - function snapX(x, state) - { - x += this.graph.panDx; - var override = false; - - if (Math.abs(x - center) < ttX) - { - dx = x - bounds.getCenterX(); - ttX = Math.abs(x - center); - override = true; - } - else if (Math.abs(x - left) < ttX) - { - dx = x - bounds.x; - ttX = Math.abs(x - left); - override = true; - } - else if (Math.abs(x - right) < ttX) - { - dx = x - bounds.x - bounds.width; - ttX = Math.abs(x - right); - override = true; - } - - if (override) - { - stateX = state; - valueX = Math.round(x - this.graph.panDx); - - if (this.guideX == null) - { - this.guideX = this.createGuideShape(true); - - // Makes sure to use either VML or SVG shapes in order to implement - // event-transparency on the background area of the rectangle since - // HTML shapes do not let mouseevents through even when transparent - this.guideX.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ? - mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG; - this.guideX.pointerEvents = false; - this.guideX.init(this.graph.getView().getOverlayPane()); - } - } - - overrideX = overrideX || override; - }; - - // Snaps the top, middle or bottom to the given y-coordinate - function snapY(y) - { - y += this.graph.panDy; - var override = false; - - if (Math.abs(y - middle) < ttY) - { - dy = y - bounds.getCenterY(); - ttY = Math.abs(y - middle); - override = true; - } - else if (Math.abs(y - top) < ttY) - { - dy = y - bounds.y; - ttY = Math.abs(y - top); - override = true; - } - else if (Math.abs(y - bottom) < ttY) - { - dy = y - bounds.y - bounds.height; - ttY = Math.abs(y - bottom); - override = true; - } - - if (override) - { - stateY = state; - valueY = Math.round(y - this.graph.panDy); - - if (this.guideY == null) - { - this.guideY = this.createGuideShape(false); - - // Makes sure to use either VML or SVG shapes in order to implement - // event-transparency on the background area of the rectangle since - // HTML shapes do not let mouseevents through even when transparent - this.guideY.dialect = (this.graph.dialect != mxConstants.DIALECT_SVG) ? - mxConstants.DIALECT_VML : mxConstants.DIALECT_SVG; - this.guideY.pointerEvents = false; - this.guideY.init(this.graph.getView().getOverlayPane()); - } - } - - overrideY = overrideY || override; - }; - - for (var i = 0; i < this.states.length; i++) - { - var state = this.states[i]; - - if (state != null) - { - // Align x - if (this.horizontal) - { - snapX.call(this, state.getCenterX(), state); - snapX.call(this, state.x, state); - snapX.call(this, state.x + state.width, state); - } - - // Align y - if (this.vertical) - { - snapY.call(this, state.getCenterY(), state); - snapY.call(this, state.y, state); - snapY.call(this, state.y + state.height, state); - } - } - } - - // Moves cells that are off-grid back to the grid on move - if (gridEnabled) - { - if (!overrideX) - { - var tx = bounds.x - (this.graph.snap(bounds.x / - scale - trx.x) + trx.x) * scale; - dx = this.graph.snap(dx / scale) * scale - tx; - } - - if (!overrideY) - { - var ty = bounds.y - (this.graph.snap(bounds.y / - scale - trx.y) + trx.y) * scale; - dy = this.graph.snap(dy / scale) * scale - ty; - } - } - - // Redraws the guides - var c = this.graph.container; - - if (!overrideX && this.guideX != null) - { - this.guideX.node.style.visibility = 'hidden'; - } - else if (this.guideX != null) - { - if (stateX != null && bounds != null) - { - minY = Math.min(bounds.y + dy - this.graph.panDy, stateX.y); - maxY = Math.max(bounds.y + bounds.height + dy - this.graph.panDy, stateX.y + stateX.height); - } - - if (minY != null && maxY != null) - { - this.guideX.points = [new mxPoint(valueX, minY), new mxPoint(valueX, maxY)]; - } - else - { - this.guideX.points = [new mxPoint(valueX, -this.graph.panDy), new mxPoint(valueX, c.scrollHeight - 3 - this.graph.panDy)]; - } - - this.guideX.stroke = this.getGuideColor(stateX, true); - this.guideX.node.style.visibility = 'visible'; - this.guideX.redraw(); - } - - if (!overrideY && this.guideY != null) - { - this.guideY.node.style.visibility = 'hidden'; - } - else if (this.guideY != null) - { - if (stateY != null && bounds != null) - { - minX = Math.min(bounds.x + dx - this.graph.panDx, stateY.x); - maxX = Math.max(bounds.x + bounds.width + dx - this.graph.panDx, stateY.x + stateY.width); - } - - if (minX != null && maxX != null) - { - this.guideY.points = [new mxPoint(minX, valueY), new mxPoint(maxX, valueY)]; - } - else - { - this.guideY.points = [new mxPoint(-this.graph.panDx, valueY), new mxPoint(c.scrollWidth - 3 - this.graph.panDx, valueY)]; - } - - this.guideY.stroke = this.getGuideColor(stateY, false); - this.guideY.node.style.visibility = 'visible'; - this.guideY.redraw(); - } - - delta = new mxPoint(dx, dy); - } - - return delta; -}; - -/** - * Function: hide - * - * Hides all current guides. - */ -mxGuide.prototype.getGuideColor = function(state, horizontal) -{ - return mxConstants.GUIDE_COLOR; -}; - -/** - * Function: hide - * - * Hides all current guides. - */ -mxGuide.prototype.hide = function() -{ - this.setVisible(false); -}; - -/** - * Function: setVisible - * - * Shows or hides the current guides. - */ -mxGuide.prototype.setVisible = function(visible) -{ - if (this.guideX != null) - { - this.guideX.node.style.visibility = (visible) ? 'visible' : 'hidden'; - } - - if (this.guideY != null) - { - this.guideY.node.style.visibility = (visible) ? 'visible' : 'hidden'; - } -}; - -/** - * Function: destroy - * - * Destroys all resources that this object uses. - */ -mxGuide.prototype.destroy = function() -{ - if (this.guideX != null) - { - this.guideX.destroy(); - this.guideX = null; - } - - if (this.guideY != null) - { - this.guideY.destroy(); - this.guideY = null; - } -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -/** - * Class: mxStencil - * - * Implements a generic shape which is based on a XML node as a description. - * - * shape: - * - * The outer element is *shape*, that has attributes: - * - * - "name", string, required. The stencil name that uniquely identifies the shape. - * - "w" and "h" are optional decimal view bounds. This defines your co-ordinate - * system for the graphics operations in the shape. The default is 100,100. - * - "aspect", optional string. Either "variable", the default, or "fixed". Fixed - * means always render the shape with the aspect ratio defined by the ratio w/h. - * Variable causes the ratio to match that of the geometry of the current vertex. - * - "strokewidth", optional string. Either an integer or the string "inherit". - * "inherit" indicates that the strokeWidth of the cell is only changed on scaling, - * not on resizing. Default is "1". - * If numeric values are used, the strokeWidth of the cell is changed on both - * scaling and resizing and the value defines the multiple that is applied to - * the width. - * - * connections: - * - * If you want to define specific fixed connection points on the shape use the - * *connections* element. Each *constraint* element within connections defines - * a fixed connection point on the shape. Constraints have attributes: - * - * - "perimeter", required. 1 or 0. 0 sets the connection point where specified - * by x,y. 1 Causes the position of the connection point to be extrapolated from - * the center of the shape, through x,y to the point of intersection with the - * perimeter of the shape. - * - "x" and "y" are the position of the fixed point relative to the bounds of - * the shape. They can be automatically adjusted if perimeter=1. So, (0,0) is top - * left, (0.5,0.5) the center, (1,0.5) the center of the right hand edge of the - * bounds, etc. Values may be less than 0 or greater than 1 to be positioned - * outside of the shape. - * - "name", optional string. A unique identifier for the port on the shape. - * - * background and foreground: - * - * The path of the graphics drawing is split into two elements, *foreground* and - * *background*. The split is to define which part any shadow applied to the shape - * is derived from (the background). This, generally, means the background is the - * line tracing of the outside of the shape, but not always. - * - * Any stroke, fill or fillstroke of a background must be the first element of the - * foreground element, they must not be used within *background*. If the background - * is empty, this is not required. - * - * Because the background cannot have any fill or stroke, it can contain only one - * *path*, *rect*, *roundrect* or *ellipse* element (or none). It can also not - * include *image*, *text* or *include-shape*. - * - * Note that the state, styling and drawing in mxGraph stencils is very close in - * design to that of HTML 5 canvas. Tutorials on this subject, if you're not - * familiar with the topic, will give a good high-level introduction to the - * concepts used. - * - * State: - * - * Rendering within the foreground and background elements has the concept of - * state. There are two types of operations other than state save/load, styling - * and drawing. The styling operations change the current state, so you can save - * the current state with and pull the last saved state from the state - * stack using . - * - * Styling: - * - * The elements that change colors within the current state all take a hash - * prefixed hex color code ("#FFEA80"). - * - * - *strokecolor*, this sets the color that drawing paths will be rendered in - * when a stroke or fillstroke command is issued. - * - *fillcolor*, this sets the color that the inside of closed paths will be - * rendered in when a fill or fillstroke command is issued. - * - *fontcolor*, this sets the color that fonts are rendered in when text is drawn. - * - * *alpha* defines the degree of transparency used between 1.0 for fully opaque - * and 0.0 for fully transparent. - * - * *strokewidth* defines the integer thickness of drawing elements rendered by - * stroking. Use fixed="1" to apply the value as-is, without scaling. - * - * *dashed* is "1" for dashing enabled and "0" for disabled. - * - * When *dashed* is enabled the current dash pattern, defined by *dashpattern*, - * is used on strokes. dashpattern is a sequence of space separated "on, off" - * lengths that define what distance to paint the stroke for, then what distance - * to paint nothing for, repeat... The default is "3 3". You could define a more - * complex pattern with "5 3 2 6", for example. Generally, it makes sense to have - * an even number of elements in the dashpattern, but that's not required. - * - * *linejoin*, *linecap* and *miterlimit* are best explained by the Mozilla page - * on Canvas styling (about halfway down). The values are all the same except we - * use "flat" for linecap, instead of Canvas' "butt". - * - * For font styling there are. - * - * - *fontsize*, an integer, - * - *fontstyle*, an ORed bit pattern of bold (1), italic (2) and underline (4), - * i.e bold underline is "5". - * - *fontfamily*, is a string defining the typeface to be used. - * - * Drawing: - * - * Most drawing is contained within a *path* element. Again, the graphic - * primitives are very similar to that of HTML 5 canvas. - * - * - *move* to attributes required decimals (x,y). - * - *line* to attributes required decimals (x,y). - * - *quad* to required decimals (x2,y2) via control point required decimals - * (x1,y1). - * - *curve* to required decimals (x3,y3), via control points required decimals - * (x1,y1) and (x2,y2). - * - *arc*, this doesn't follow the HTML Canvas signatures, instead it's a copy - * of the SVG arc command. The SVG specification documentation gives the best - * description of its behaviors. The attributes are named identically, they are - * decimals and all required. - * - *close* ends the current subpath and causes an automatic straight line to - * be drawn from the current point to the initial point of the current subpath. - * - * Complex drawing: - * - * In addition to the graphics primitive operations there are non-primitive - * operations. These provide an easy method to draw some basic shapes. - * - * - *rect*, attributes "x", "y", "w", "h", all required decimals - * - *roundrect*, attributes "x", "y", "w", "h", all required decimals. Also - * "arcsize" an optional decimal attribute defining how large, the corner curves - * are. - * - *ellipse*, attributes "x", "y", "w", "h", all required decimals. - * - * Note that these 3 shapes and all paths must be followed by either a fill, - * stroke, or fillstroke. - * - * Text: - * - * *text* elements have the following attributes. - * - * - "str", the text string to display, required. - * - "x" and "y", the decimal location (x,y) of the text element, required. - * - "align", the horizontal alignment of the text element, either "left", - * "center" or "right". Optional, default is "left". - * - "valign", the vertical alignment of the text element, either "top", "middle" - * or "bottom". Optional, default is "top". - * - "localized", 0 or 1, if 1 then the "str" actually contains a key to use to - * fetch the value out of mxResources. Optional, default is - * . - * - "vertical", 0 or 1, if 1 the label is rendered vertically (rotated by 90 - * degrees). Optional, default is 0. - * - "rotation", angle in degrees (0 to 360). The angle to rotate the text by. - * Optional, default is 0. - * - "align-shape", 0 or 1, if 0 ignore the rotation of the shape when setting - * the text rotation. Optional, default is 1. - * - * If is true, then the text content of the this element can define - * a function which is invoked with the shape as the only argument and returns - * the value for the text element (ignored if the str attribute is not null). - * - * Images: - * - * *image* elements can either be external URLs, or data URIs, where supported - * (not in IE 7-). Attributes are: - * - * - "src", required string. Either a data URI or URL. - * - "x", "y", required decimals. The (x,y) position of the image. - * - "w", "h", required decimals. The width and height of the image. - * - "flipH" and "flipV", optional 0 or 1. Whether to flip the image along the - * horizontal/vertical axis. Default is 0 for both. - * - * If is true, then the text content of the this element can define - * a function which is invoked with the shape as the only argument and returns - * the value for the image source (ignored if the src attribute is not null). - * - * Sub-shapes: - * - * *include-shape* allow stencils to be rendered within the current stencil by - * referencing the sub-stencil by name. Attributes are: - * - * - "name", required string. The unique shape name of the stencil. - * - "x", "y", "w", "h", required decimals. The (x,y) position of the sub-shape - * and its width and height. - * - * Constructor: mxStencil - * - * Constructs a new generic shape by setting to the given XML node and - * invoking and . - * - * Parameters: - * - * desc - XML node that contains the stencil description. - */ -function mxStencil(desc) -{ - this.desc = desc; - this.parseDescription(); - this.parseConstraints(); -}; - -/** - * Variable: defaultLocalized - * - * Static global variable that specifies the default value for the localized - * attribute of the text element. Default is false. - */ -mxStencil.defaultLocalized = false; - -/** - * Function: allowEval - * - * Static global switch that specifies if the use of eval is allowed for - * evaluating text content and images. Default is false. Set this to true - * if stencils can not contain user input. - */ -mxStencil.allowEval = false; - -/** - * Variable: desc - * - * Holds the XML node with the stencil description. - */ -mxStencil.prototype.desc = null; - -/** - * Variable: constraints - * - * Holds an array of as defined in the shape. - */ -mxStencil.prototype.constraints = null; - -/** - * Variable: aspect - * - * Holds the aspect of the shape. Default is 'auto'. - */ -mxStencil.prototype.aspect = null; - -/** - * Variable: w0 - * - * Holds the width of the shape. Default is 100. - */ -mxStencil.prototype.w0 = null; - -/** - * Variable: h0 - * - * Holds the height of the shape. Default is 100. - */ -mxStencil.prototype.h0 = null; - -/** - * Variable: bgNodes - * - * Holds the XML node with the stencil description. - */ -mxStencil.prototype.bgNode = null; - -/** - * Variable: fgNodes - * - * Holds the XML node with the stencil description. - */ -mxStencil.prototype.fgNode = null; - -/** - * Variable: strokewidth - * - * Holds the strokewidth direction from the description. - */ -mxStencil.prototype.strokewidth = null; - -/** - * Function: parseDescription - * - * Reads , , , and from . - */ -mxStencil.prototype.parseDescription = function() -{ - // LATER: Preprocess nodes for faster painting - this.fgNode = this.desc.getElementsByTagName('foreground')[0]; - this.bgNode = this.desc.getElementsByTagName('background')[0]; - this.w0 = Number(this.desc.getAttribute('w') || 100); - this.h0 = Number(this.desc.getAttribute('h') || 100); - - // Possible values for aspect are: variable and fixed where - // variable means fill the available space and fixed means - // use w0 and h0 to compute the aspect. - var aspect = this.desc.getAttribute('aspect'); - this.aspect = (aspect != null) ? aspect : 'variable'; - - // Possible values for strokewidth are all numbers and "inherit" - // where the inherit means take the value from the style (ie. the - // user-defined stroke-width). Note that the strokewidth is scaled - // by the minimum scaling that is used to draw the shape (sx, sy). - var sw = this.desc.getAttribute('strokewidth'); - this.strokewidth = (sw != null) ? sw : '1'; -}; - -/** - * Function: parseConstraints - * - * Reads the constraints from into using - * . - */ -mxStencil.prototype.parseConstraints = function() -{ - var conns = this.desc.getElementsByTagName('connections')[0]; - - if (conns != null) - { - var tmp = mxUtils.getChildNodes(conns); - - if (tmp != null && tmp.length > 0) - { - this.constraints = []; - - for (var i = 0; i < tmp.length; i++) - { - this.constraints.push(this.parseConstraint(tmp[i])); - } - } - } -}; - -/** - * Function: parseConstraint - * - * Parses the given XML node and returns its . - */ -mxStencil.prototype.parseConstraint = function(node) -{ - var x = Number(node.getAttribute('x')); - var y = Number(node.getAttribute('y')); - var perimeter = node.getAttribute('perimeter') == '1'; - var name = node.getAttribute('name'); - - return new mxConnectionConstraint(new mxPoint(x, y), perimeter, name); -}; - -/** - * Function: evaluateTextAttribute - * - * Gets the given attribute as a text. The return value from - * is used as a key to if the localized attribute in the text - * node is 1 or if is true. - */ -mxStencil.prototype.evaluateTextAttribute = function(node, attribute, shape) -{ - var result = this.evaluateAttribute(node, attribute, shape); - var loc = node.getAttribute('localized'); - - if ((mxStencil.defaultLocalized && loc == null) || loc == '1') - { - result = mxResources.get(result); - } - - return result; -}; - -/** - * Function: evaluateAttribute - * - * Gets the attribute for the given name from the given node. If the attribute - * does not exist then the text content of the node is evaluated and if it is - * a function it is invoked with as the only argument and the return - * value is used as the attribute value to be returned. - */ -mxStencil.prototype.evaluateAttribute = function(node, attribute, shape) -{ - var result = node.getAttribute(attribute); - - if (result == null) - { - var text = mxUtils.getTextContent(node); - - if (text != null && mxStencil.allowEval) - { - var funct = mxUtils.eval(text); - - if (typeof(funct) == 'function') - { - result = funct(shape); - } - } - } - - return result; -}; - -/** - * Function: drawShape - * - * Draws this stencil inside the given bounds. - */ -mxStencil.prototype.drawShape = function(canvas, shape, x, y, w, h) -{ - // TODO: Internal structure (array of special structs?), relative and absolute - // coordinates (eg. note shape, process vs star, actor etc.), text rendering - // and non-proportional scaling, how to implement pluggable edge shapes - // (start, segment, end blocks), pluggable markers, how to implement - // swimlanes (title area) with this API, add icon, horizontal/vertical - // label, indicator for all shapes, rotation - var direction = mxUtils.getValue(shape.style, mxConstants.STYLE_DIRECTION, null); - var aspect = this.computeAspect(shape.style, x, y, w, h, direction); - var minScale = Math.min(aspect.width, aspect.height); - var sw = (this.strokewidth == 'inherit') ? - Number(mxUtils.getNumber(shape.style, mxConstants.STYLE_STROKEWIDTH, 1)) : - Number(this.strokewidth) * minScale; - canvas.setStrokeWidth(sw); - - this.drawChildren(canvas, shape, x, y, w, h, this.bgNode, aspect, false, true); - this.drawChildren(canvas, shape, x, y, w, h, this.fgNode, aspect, true, - !shape.outline || shape.style == null || mxUtils.getValue( - shape.style, mxConstants.STYLE_BACKGROUND_OUTLINE, 0) == 0); -}; - -/** - * Function: drawChildren - * - * Draws this stencil inside the given bounds. - */ -mxStencil.prototype.drawChildren = function(canvas, shape, x, y, w, h, node, aspect, disableShadow, paint) -{ - if (node != null && w > 0 && h > 0) - { - var tmp = node.firstChild; - - while (tmp != null) - { - if (tmp.nodeType == mxConstants.NODETYPE_ELEMENT) - { - this.drawNode(canvas, shape, tmp, aspect, disableShadow, paint); - } - - tmp = tmp.nextSibling; - } - } -}; - -/** - * Function: computeAspect - * - * Returns a rectangle that contains the offset in x and y and the horizontal - * and vertical scale in width and height used to draw this shape inside the - * given . - * - * Parameters: - * - * shape - to be drawn. - * bounds - that should contain the stencil. - * direction - Optional direction of the shape to be darwn. - */ -mxStencil.prototype.computeAspect = function(shape, x, y, w, h, direction) -{ - var x0 = x; - var y0 = y; - var sx = w / this.w0; - var sy = h / this.h0; - - var inverse = (direction == mxConstants.DIRECTION_NORTH || direction == mxConstants.DIRECTION_SOUTH); - - if (inverse) - { - sy = w / this.h0; - sx = h / this.w0; - - var delta = (w - h) / 2; - - x0 += delta; - y0 -= delta; - } - - if (this.aspect == 'fixed') - { - sy = Math.min(sx, sy); - sx = sy; - - // Centers the shape inside the available space - if (inverse) - { - x0 += (h - this.w0 * sx) / 2; - y0 += (w - this.h0 * sy) / 2; - } - else - { - x0 += (w - this.w0 * sx) / 2; - y0 += (h - this.h0 * sy) / 2; - } - } - - return new mxRectangle(x0, y0, sx, sy); -}; - -/** - * Function: drawNode - * - * Draws this stencil inside the given bounds. - */ -mxStencil.prototype.drawNode = function(canvas, shape, node, aspect, disableShadow, paint) -{ - var name = node.nodeName; - var x0 = aspect.x; - var y0 = aspect.y; - var sx = aspect.width; - var sy = aspect.height; - var minScale = Math.min(sx, sy); - - if (name == 'save') - { - canvas.save(); - } - else if (name == 'restore') - { - canvas.restore(); - } - else if (paint) - { - if (name == 'path') - { - canvas.begin(); - - // Renders the elements inside the given path - var childNode = node.firstChild; - - while (childNode != null) - { - if (childNode.nodeType == mxConstants.NODETYPE_ELEMENT) - { - this.drawNode(canvas, shape, childNode, aspect, disableShadow, paint); - } - - childNode = childNode.nextSibling; - } - } - else if (name == 'close') - { - canvas.close(); - } - else if (name == 'move') - { - canvas.moveTo(x0 + Number(node.getAttribute('x')) * sx, y0 + Number(node.getAttribute('y')) * sy); - } - else if (name == 'line') - { - canvas.lineTo(x0 + Number(node.getAttribute('x')) * sx, y0 + Number(node.getAttribute('y')) * sy); - } - else if (name == 'quad') - { - canvas.quadTo(x0 + Number(node.getAttribute('x1')) * sx, - y0 + Number(node.getAttribute('y1')) * sy, - x0 + Number(node.getAttribute('x2')) * sx, - y0 + Number(node.getAttribute('y2')) * sy); - } - else if (name == 'curve') - { - canvas.curveTo(x0 + Number(node.getAttribute('x1')) * sx, - y0 + Number(node.getAttribute('y1')) * sy, - x0 + Number(node.getAttribute('x2')) * sx, - y0 + Number(node.getAttribute('y2')) * sy, - x0 + Number(node.getAttribute('x3')) * sx, - y0 + Number(node.getAttribute('y3')) * sy); - } - else if (name == 'arc') - { - canvas.arcTo(Number(node.getAttribute('rx')) * sx, - Number(node.getAttribute('ry')) * sy, - Number(node.getAttribute('x-axis-rotation')), - Number(node.getAttribute('large-arc-flag')), - Number(node.getAttribute('sweep-flag')), - x0 + Number(node.getAttribute('x')) * sx, - y0 + Number(node.getAttribute('y')) * sy); - } - else if (name == 'rect') - { - canvas.rect(x0 + Number(node.getAttribute('x')) * sx, - y0 + Number(node.getAttribute('y')) * sy, - Number(node.getAttribute('w')) * sx, - Number(node.getAttribute('h')) * sy); - } - else if (name == 'roundrect') - { - var arcsize = Number(node.getAttribute('arcsize')); - - if (arcsize == 0) - { - arcsize = mxConstants.RECTANGLE_ROUNDING_FACTOR * 100; - } - - var w = Number(node.getAttribute('w')) * sx; - var h = Number(node.getAttribute('h')) * sy; - var factor = Number(arcsize) / 100; - var r = Math.min(w * factor, h * factor); - - canvas.roundrect(x0 + Number(node.getAttribute('x')) * sx, - y0 + Number(node.getAttribute('y')) * sy, - w, h, r, r); - } - else if (name == 'ellipse') - { - canvas.ellipse(x0 + Number(node.getAttribute('x')) * sx, - y0 + Number(node.getAttribute('y')) * sy, - Number(node.getAttribute('w')) * sx, - Number(node.getAttribute('h')) * sy); - } - else if (name == 'image') - { - if (!shape.outline) - { - var src = this.evaluateAttribute(node, 'src', shape); - - canvas.image(x0 + Number(node.getAttribute('x')) * sx, - y0 + Number(node.getAttribute('y')) * sy, - Number(node.getAttribute('w')) * sx, - Number(node.getAttribute('h')) * sy, - src, false, node.getAttribute('flipH') == '1', - node.getAttribute('flipV') == '1'); - } - } - else if (name == 'text') - { - if (!shape.outline) - { - var str = this.evaluateTextAttribute(node, 'str', shape); - var rotation = node.getAttribute('vertical') == '1' ? -90 : 0; - - if (node.getAttribute('align-shape') == '0') - { - var dr = shape.rotation; - - // Depends on flipping - var flipH = mxUtils.getValue(shape.style, mxConstants.STYLE_FLIPH, 0) == 1; - var flipV = mxUtils.getValue(shape.style, mxConstants.STYLE_FLIPV, 0) == 1; - - if (flipH && flipV) - { - rotation -= dr; - } - else if (flipH || flipV) - { - rotation += dr; - } - else - { - rotation -= dr; - } - } - - rotation -= node.getAttribute('rotation'); - - canvas.text(x0 + Number(node.getAttribute('x')) * sx, - y0 + Number(node.getAttribute('y')) * sy, - 0, 0, str, node.getAttribute('align') || 'left', - node.getAttribute('valign') || 'top', false, '', - null, false, rotation); - } - } - else if (name == 'include-shape') - { - var stencil = mxStencilRegistry.getStencil(node.getAttribute('name')); - - if (stencil != null) - { - var x = x0 + Number(node.getAttribute('x')) * sx; - var y = y0 + Number(node.getAttribute('y')) * sy; - var w = Number(node.getAttribute('w')) * sx; - var h = Number(node.getAttribute('h')) * sy; - - stencil.drawShape(canvas, shape, x, y, w, h); - } - } - else if (name == 'fillstroke') - { - canvas.fillAndStroke(); - } - else if (name == 'fill') - { - canvas.fill(); - } - else if (name == 'stroke') - { - canvas.stroke(); - } - else if (name == 'strokewidth') - { - var s = (node.getAttribute('fixed') == '1') ? 1 : minScale; - canvas.setStrokeWidth(Number(node.getAttribute('width')) * s); - } - else if (name == 'dashed') - { - canvas.setDashed(node.getAttribute('dashed') == '1'); - } - else if (name == 'dashpattern') - { - var value = node.getAttribute('pattern'); - - if (value != null) - { - var tmp = value.split(' '); - var pat = []; - - for (var i = 0; i < tmp.length; i++) - { - if (tmp[i].length > 0) - { - pat.push(Number(tmp[i]) * minScale); - } - } - - value = pat.join(' '); - canvas.setDashPattern(value); - } - } - else if (name == 'strokecolor') - { - canvas.setStrokeColor(node.getAttribute('color')); - } - else if (name == 'linecap') - { - canvas.setLineCap(node.getAttribute('cap')); - } - else if (name == 'linejoin') - { - canvas.setLineJoin(node.getAttribute('join')); - } - else if (name == 'miterlimit') - { - canvas.setMiterLimit(Number(node.getAttribute('limit'))); - } - else if (name == 'fillcolor') - { - canvas.setFillColor(node.getAttribute('color')); - } - else if (name == 'alpha') - { - canvas.setAlpha(node.getAttribute('alpha')); - } - else if (name == 'fontcolor') - { - canvas.setFontColor(node.getAttribute('color')); - } - else if (name == 'fontstyle') - { - canvas.setFontStyle(node.getAttribute('style')); - } - else if (name == 'fontfamily') - { - canvas.setFontFamily(node.getAttribute('family')); - } - else if (name == 'fontsize') - { - canvas.setFontSize(Number(node.getAttribute('size')) * minScale); - } - - if (disableShadow && (name == 'fillstroke' || name == 'fill' || name == 'stroke')) - { - disableShadow = false; - canvas.setShadow(false); - } - } -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -/** - * Class: mxShape - * - * Base class for all shapes. A shape in mxGraph is a - * separate implementation for SVG, VML and HTML. Which - * implementation to use is controlled by the - * property which is assigned from within the - * when the shape is created. The dialect must be assigned - * for a shape, and it does normally depend on the browser and - * the confiuration of the graph (see rendering hint). - * - * For each supported shape in SVG and VML, a corresponding - * shape exists in mxGraph, namely for text, image, rectangle, - * rhombus, ellipse and polyline. The other shapes are a - * combination of these shapes (eg. label and swimlane) - * or they consist of one or more (filled) path objects - * (eg. actor and cylinder). The HTML implementation is - * optional but may be required for a HTML-only view of - * the graph. - * - * Custom Shapes: - * - * To extend from this class, the basic code looks as follows. - * In the special case where the custom shape consists only of - * one filled region or one filled region and an additional stroke - * the and should be subclassed, - * respectively. - * - * (code) - * function CustomShape() { } - * - * CustomShape.prototype = new mxShape(); - * CustomShape.prototype.constructor = CustomShape; - * (end) - * - * To register a custom shape in an existing graph instance, - * one must register the shape under a new name in the graph's - * cell renderer as follows: - * - * (code) - * mxCellRenderer.registerShape('customShape', CustomShape); - * (end) - * - * The second argument is the name of the constructor. - * - * In order to use the shape you can refer to the given name above - * in a stylesheet. For example, to change the shape for the default - * vertex style, the following code is used: - * - * (code) - * var style = graph.getStylesheet().getDefaultVertexStyle(); - * style[mxConstants.STYLE_SHAPE] = 'customShape'; - * (end) - * - * Constructor: mxShape - * - * Constructs a new shape. - */ -function mxShape(stencil) -{ - this.stencil = stencil; - this.initStyles(); -}; - -/** - * Variable: dialect - * - * Holds the dialect in which the shape is to be painted. - * This can be one of the DIALECT constants in . - */ -mxShape.prototype.dialect = null; - -/** - * Variable: scale - * - * Holds the scale in which the shape is being painted. - */ -mxShape.prototype.scale = 1; - -/** - * Variable: antiAlias - * - * Rendering hint for configuring the canvas. - */ -mxShape.prototype.antiAlias = true; - -/** - * Variable: minSvgStrokeWidth - * - * Minimum stroke width for SVG output. - */ -mxShape.prototype.minSvgStrokeWidth = 1; - -/** - * Variable: bounds - * - * Holds the that specifies the bounds of this shape. - */ -mxShape.prototype.bounds = null; - -/** - * Variable: points - * - * Holds the array of that specify the points of this shape. - */ -mxShape.prototype.points = null; - -/** - * Variable: node - * - * Holds the outermost DOM node that represents this shape. - */ -mxShape.prototype.node = null; - -/** - * Variable: state - * - * Optional reference to the corresponding . - */ -mxShape.prototype.state = null; - -/** - * Variable: style - * - * Optional reference to the style of the corresponding . - */ -mxShape.prototype.style = null; - -/** - * Variable: boundingBox - * - * Contains the bounding box of the shape, that is, the smallest rectangle - * that includes all pixels of the shape. - */ -mxShape.prototype.boundingBox = null; - -/** - * Variable: stencil - * - * Holds the that defines the shape. - */ -mxShape.prototype.stencil = null; - -/** - * Variable: svgStrokeTolerance - * - * Event-tolerance for SVG strokes (in px). Default is 8. This is only passed - * to the canvas in if is true. - */ -mxShape.prototype.svgStrokeTolerance = 8; - -/** - * Variable: pointerEvents - * - * Specifies if pointer events should be handled. Default is true. - */ -mxShape.prototype.pointerEvents = true; - -/** - * Variable: svgPointerEvents - * - * Specifies if pointer events should be handled. Default is true. - */ -mxShape.prototype.svgPointerEvents = 'all'; - -/** - * Variable: shapePointerEvents - * - * Specifies if pointer events outside of shape should be handled. Default - * is false. - */ -mxShape.prototype.shapePointerEvents = false; - -/** - * Variable: stencilPointerEvents - * - * Specifies if pointer events outside of stencils should be handled. Default - * is false. Set this to true for backwards compatibility with the 1.x branch. - */ -mxShape.prototype.stencilPointerEvents = false; - -/** - * Variable: vmlScale - * - * Scale for improving the precision of VML rendering. Default is 1. - */ -mxShape.prototype.vmlScale = 1; - -/** - * Variable: outline - * - * Specifies if the shape should be drawn as an outline. This disables all - * fill colors and can be used to disable other drawing states that should - * not be painted for outlines. Default is false. This should be set before - * calling . - */ -mxShape.prototype.outline = false; - -/** - * Variable: visible - * - * Specifies if the shape is visible. Default is true. - */ -mxShape.prototype.visible = true; - -/** - * Variable: useSvgBoundingBox - * - * Allows to use the SVG bounding box in SVG. Default is false for performance - * reasons. - */ -mxShape.prototype.useSvgBoundingBox = false; - -/** - * Function: init - * - * Initializes the shape by creaing the DOM node using - * and adding it into the given container. - * - * Parameters: - * - * container - DOM node that will contain the shape. - */ -mxShape.prototype.init = function(container) -{ - if (this.node == null) - { - this.node = this.create(container); - - if (container != null) - { - container.appendChild(this.node); - } - } -}; - -/** - * Function: initStyles - * - * Sets the styles to their default values. - */ -mxShape.prototype.initStyles = function(container) -{ - this.strokewidth = 1; - this.rotation = 0; - this.opacity = 100; - this.fillOpacity = 100; - this.strokeOpacity = 100; - this.flipH = false; - this.flipV = false; -}; - -/** - * Function: isParseVml - * - * Specifies if any VML should be added via insertAdjacentHtml to the DOM. This - * is only needed in IE8 and only if the shape contains VML markup. This method - * returns true. - */ -mxShape.prototype.isParseVml = function() -{ - return true; -}; - -/** - * Function: isHtmlAllowed - * - * Returns true if HTML is allowed for this shape. This implementation always - * returns false. - */ -mxShape.prototype.isHtmlAllowed = function() -{ - return false; -}; - -/** - * Function: getSvgScreenOffset - * - * Returns 0, or 0.5 if % 2 == 1. - */ -mxShape.prototype.getSvgScreenOffset = function() -{ - var sw = this.stencil && this.stencil.strokewidth != 'inherit' ? Number(this.stencil.strokewidth) : this.strokewidth; - - return (mxUtils.mod(Math.max(1, Math.round(sw * this.scale)), 2) == 1) ? 0.5 : 0; -}; - -/** - * Function: create - * - * Creates and returns the DOM node(s) for the shape in - * the given container. This implementation invokes - * , or depending - * on the and style settings. - * - * Parameters: - * - * container - DOM node that will contain the shape. - */ -mxShape.prototype.create = function(container) -{ - var node = null; - - if (container != null && container.ownerSVGElement != null) - { - node = this.createSvg(container); - } - else if (document.documentMode == 8 || !mxClient.IS_VML || - (this.dialect != mxConstants.DIALECT_VML && this.isHtmlAllowed())) - { - node = this.createHtml(container); - } - else - { - node = this.createVml(container); - } - - return node; -}; - -/** - * Function: createSvg - * - * Creates and returns the SVG node(s) to represent this shape. - */ -mxShape.prototype.createSvg = function() -{ - return document.createElementNS(mxConstants.NS_SVG, 'g'); -}; - -/** - * Function: createVml - * - * Creates and returns the VML node to represent this shape. - */ -mxShape.prototype.createVml = function() -{ - var node = document.createElement(mxClient.VML_PREFIX + ':group'); - node.style.position = 'absolute'; - - return node; -}; - -/** - * Function: createHtml - * - * Creates and returns the HTML DOM node(s) to represent - * this shape. This implementation falls back to - * so that the HTML creation is optional. - */ -mxShape.prototype.createHtml = function() -{ - var node = document.createElement('div'); - node.style.position = 'absolute'; - - return node; -}; - -/** - * Function: reconfigure - * - * Reconfigures this shape. This will update the colors etc in - * addition to the bounds or points. - */ -mxShape.prototype.reconfigure = function() -{ - this.redraw(); -}; - -/** - * Function: redraw - * - * Creates and returns the SVG node(s) to represent this shape. - */ -mxShape.prototype.redraw = function() -{ - this.updateBoundsFromPoints(); - - if (this.visible && this.checkBounds()) - { - this.node.style.visibility = 'visible'; - this.clear(); - - if (this.node.nodeName == 'DIV' && (this.isHtmlAllowed() || !mxClient.IS_VML)) - { - this.redrawHtmlShape(); - } - else - { - this.redrawShape(); - } - - this.updateBoundingBox(); - } - else - { - this.node.style.visibility = 'hidden'; - this.boundingBox = null; - } -}; - -/** - * Function: clear - * - * Removes all child nodes and resets all CSS. - */ -mxShape.prototype.clear = function() -{ - if (this.node.ownerSVGElement != null) - { - while (this.node.lastChild != null) - { - this.node.removeChild(this.node.lastChild); - } - } - else - { - this.node.style.cssText = 'position:absolute;' + ((this.cursor != null) ? - ('cursor:' + this.cursor + ';') : ''); - this.node.innerHTML = ''; - } -}; - -/** - * Function: updateBoundsFromPoints - * - * Updates the bounds based on the points. - */ -mxShape.prototype.updateBoundsFromPoints = function() -{ - var pts = this.points; - - if (pts != null && pts.length > 0 && pts[0] != null) - { - this.bounds = new mxRectangle(Number(pts[0].x), Number(pts[0].y), 1, 1); - - for (var i = 1; i < this.points.length; i++) - { - if (pts[i] != null) - { - this.bounds.add(new mxRectangle(Number(pts[i].x), Number(pts[i].y), 1, 1)); - } - } - } -}; - -/** - * Function: getLabelBounds - * - * Returns the for the label bounds of this shape, based on the - * given scaled and translated bounds of the shape. This method should not - * change the rectangle in-place. This implementation returns the given rect. - */ -mxShape.prototype.getLabelBounds = function(rect) -{ - var d = mxUtils.getValue(this.style, mxConstants.STYLE_DIRECTION, mxConstants.DIRECTION_EAST); - var bounds = rect; - - // Normalizes argument for getLabelMargins hook - if (d != mxConstants.DIRECTION_SOUTH && d != mxConstants.DIRECTION_NORTH && - this.state != null && this.state.text != null && - this.state.text.isPaintBoundsInverted()) - { - bounds = bounds.clone(); - var tmp = bounds.width; - bounds.width = bounds.height; - bounds.height = tmp; - } - - var m = this.getLabelMargins(bounds); - - if (m != null) - { - var flipH = mxUtils.getValue(this.style, mxConstants.STYLE_FLIPH, false) == '1'; - var flipV = mxUtils.getValue(this.style, mxConstants.STYLE_FLIPV, false) == '1'; - - // Handles special case for vertical labels - if (this.state != null && this.state.text != null && - this.state.text.isPaintBoundsInverted()) - { - var tmp = m.x; - m.x = m.height; - m.height = m.width; - m.width = m.y; - m.y = tmp; - - tmp = flipH; - flipH = flipV; - flipV = tmp; - } - - return mxUtils.getDirectedBounds(rect, m, this.style, flipH, flipV); - } - - return rect; -}; - -/** - * Function: getLabelMargins - * - * Returns the scaled top, left, bottom and right margin to be used for - * computing the label bounds as an , where the bottom and right - * margin are defined in the width and height of the rectangle, respectively. - */ -mxShape.prototype.getLabelMargins= function(rect) -{ - return null; -}; - -/** - * Function: checkBounds - * - * Returns true if the bounds are not null and all of its variables are numeric. - */ -mxShape.prototype.checkBounds = function() -{ - return (!isNaN(this.scale) && isFinite(this.scale) && this.scale > 0 && - this.bounds != null && !isNaN(this.bounds.x) && !isNaN(this.bounds.y) && - !isNaN(this.bounds.width) && !isNaN(this.bounds.height) && - this.bounds.width > 0 && this.bounds.height > 0); -}; - -/** - * Function: createVmlGroup - * - * Returns the temporary element used for rendering in IE8 standards mode. - */ -mxShape.prototype.createVmlGroup = function() -{ - var node = document.createElement(mxClient.VML_PREFIX + ':group'); - node.style.position = 'absolute'; - node.style.width = this.node.style.width; - node.style.height = this.node.style.height; - - return node; -}; - -/** - * Function: redrawShape - * - * Updates the SVG or VML shape. - */ -mxShape.prototype.redrawShape = function() -{ - var canvas = this.createCanvas(); - - if (canvas != null) - { - // Specifies if events should be handled - canvas.pointerEvents = this.pointerEvents; - - this.paint(canvas); - - if (this.node != canvas.root) - { - // Forces parsing in IE8 standards mode - slow! avoid - this.node.insertAdjacentHTML('beforeend', canvas.root.outerHTML); - } - - if (this.node.nodeName == 'DIV' && document.documentMode == 8) - { - // Makes DIV transparent to events for IE8 in IE8 standards - // mode (Note: Does not work for IE9 in IE8 standards mode - // and not for IE11 in enterprise mode) - this.node.style.filter = ''; - - // Adds event transparency in IE8 standards - mxUtils.addTransparentBackgroundFilter(this.node); - } - - this.destroyCanvas(canvas); - } -}; - -/** - * Function: createCanvas - * - * Creates a new canvas for drawing this shape. May return null. - */ -mxShape.prototype.createCanvas = function() -{ - var canvas = null; - - // LATER: Check if reusing existing DOM nodes improves performance - if (this.node.ownerSVGElement != null) - { - canvas = this.createSvgCanvas(); - } - else if (mxClient.IS_VML) - { - this.updateVmlContainer(); - canvas = this.createVmlCanvas(); - } - - if (canvas != null && this.outline) - { - canvas.setStrokeWidth(this.strokewidth); - canvas.setStrokeColor(this.stroke); - - if (this.isDashed != null) - { - canvas.setDashed(this.isDashed); - } - - canvas.setStrokeWidth = function() {}; - canvas.setStrokeColor = function() {}; - canvas.setFillColor = function() {}; - canvas.setGradient = function() {}; - canvas.setDashed = function() {}; - canvas.text = function() {}; - } - - return canvas; -}; - -/** - * Function: createSvgCanvas - * - * Creates and returns an for rendering this shape. - */ -mxShape.prototype.createSvgCanvas = function() -{ - var canvas = new mxSvgCanvas2D(this.node, false); - canvas.strokeTolerance = (this.pointerEvents) ? this.svgStrokeTolerance : 0; - canvas.pointerEventsValue = this.svgPointerEvents; - canvas.blockImagePointerEvents = mxClient.IS_FF; - var off = this.getSvgScreenOffset(); - - if (off != 0) - { - this.node.setAttribute('transform', 'translate(' + off + ',' + off + ')'); - } - else - { - this.node.removeAttribute('transform'); - } - - canvas.minStrokeWidth = this.minSvgStrokeWidth; - - if (!this.antiAlias) - { - // Rounds all numbers in the SVG output to integers - canvas.format = function(value) - { - return Math.round(parseFloat(value)); - }; - } - - return canvas; -}; - -/** - * Function: createVmlCanvas - * - * Creates and returns an for rendering this shape. - */ -mxShape.prototype.createVmlCanvas = function() -{ - // Workaround for VML rendering bug in IE8 standards mode - var node = (document.documentMode == 8 && this.isParseVml()) ? this.createVmlGroup() : this.node; - var canvas = new mxVmlCanvas2D(node, false); - - if (node.tagUrn != '') - { - var w = Math.max(1, Math.round(this.bounds.width)); - var h = Math.max(1, Math.round(this.bounds.height)); - node.coordsize = (w * this.vmlScale) + ',' + (h * this.vmlScale); - canvas.scale(this.vmlScale); - canvas.vmlScale = this.vmlScale; - } - - // Painting relative to top, left shape corner - var s = this.scale; - canvas.translate(-Math.round(this.bounds.x / s), -Math.round(this.bounds.y / s)); - - return canvas; -}; - -/** - * Function: updateVmlContainer - * - * Updates the bounds of the VML container. - */ -mxShape.prototype.updateVmlContainer = function() -{ - this.node.style.left = Math.round(this.bounds.x) + 'px'; - this.node.style.top = Math.round(this.bounds.y) + 'px'; - var w = Math.max(1, Math.round(this.bounds.width)); - var h = Math.max(1, Math.round(this.bounds.height)); - this.node.style.width = w + 'px'; - this.node.style.height = h + 'px'; - this.node.style.overflow = 'visible'; -}; - -/** - * Function: redrawHtml - * - * Allow optimization by replacing VML with HTML. - */ -mxShape.prototype.redrawHtmlShape = function() -{ - // LATER: Refactor methods - this.updateHtmlBounds(this.node); - this.updateHtmlFilters(this.node); - this.updateHtmlColors(this.node); -}; - -/** - * Function: updateHtmlFilters - * - * Allow optimization by replacing VML with HTML. - */ -mxShape.prototype.updateHtmlFilters = function(node) -{ - var f = ''; - - if (this.opacity < 100) - { - f += 'alpha(opacity=' + (this.opacity) + ')'; - } - - if (this.isShadow) - { - // FIXME: Cannot implement shadow transparency with filter - f += 'progid:DXImageTransform.Microsoft.dropShadow (' + - 'OffX=\'' + Math.round(mxConstants.SHADOW_OFFSET_X * this.scale) + '\', ' + - 'OffY=\'' + Math.round(mxConstants.SHADOW_OFFSET_Y * this.scale) + '\', ' + - 'Color=\'' + mxConstants.VML_SHADOWCOLOR + '\')'; - } - - if (this.fill != null && this.fill != mxConstants.NONE && this.gradient && this.gradient != mxConstants.NONE) - { - var start = this.fill; - var end = this.gradient; - var type = '0'; - - var lookup = {east:0,south:1,west:2,north:3}; - var dir = (this.direction != null) ? lookup[this.direction] : 0; - - if (this.gradientDirection != null) - { - dir = mxUtils.mod(dir + lookup[this.gradientDirection] - 1, 4); - } - - if (dir == 1) - { - type = '1'; - var tmp = start; - start = end; - end = tmp; - } - else if (dir == 2) - { - var tmp = start; - start = end; - end = tmp; - } - else if (dir == 3) - { - type = '1'; - } - - f += 'progid:DXImageTransform.Microsoft.gradient(' + - 'startColorStr=\'' + start + '\', endColorStr=\'' + end + - '\', gradientType=\'' + type + '\')'; - } - - node.style.filter = f; -}; - -/** - * Function: mixedModeHtml - * - * Allow optimization by replacing VML with HTML. - */ -mxShape.prototype.updateHtmlColors = function(node) -{ - var color = this.stroke; - - if (color != null && color != mxConstants.NONE) - { - node.style.borderColor = color; - - if (this.isDashed) - { - node.style.borderStyle = 'dashed'; - } - else if (this.strokewidth > 0) - { - node.style.borderStyle = 'solid'; - } - - node.style.borderWidth = Math.max(1, Math.ceil(this.strokewidth * this.scale)) + 'px'; - } - else - { - node.style.borderWidth = '0px'; - } - - color = (this.outline) ? null : this.fill; - - if (color != null && color != mxConstants.NONE) - { - node.style.backgroundColor = color; - node.style.backgroundImage = 'none'; - } - else if (this.pointerEvents) - { - node.style.backgroundColor = 'transparent'; - } - else if (document.documentMode == 8) - { - mxUtils.addTransparentBackgroundFilter(node); - } - else - { - this.setTransparentBackgroundImage(node); - } -}; - -/** - * Function: mixedModeHtml - * - * Allow optimization by replacing VML with HTML. - */ -mxShape.prototype.updateHtmlBounds = function(node) -{ - var sw = (document.documentMode >= 9) ? 0 : Math.ceil(this.strokewidth * this.scale); - node.style.borderWidth = Math.max(1, sw) + 'px'; - node.style.overflow = 'hidden'; - - node.style.left = Math.round(this.bounds.x - sw / 2) + 'px'; - node.style.top = Math.round(this.bounds.y - sw / 2) + 'px'; - - if (document.compatMode == 'CSS1Compat') - { - sw = -sw; - } - - node.style.width = Math.round(Math.max(0, this.bounds.width + sw)) + 'px'; - node.style.height = Math.round(Math.max(0, this.bounds.height + sw)) + 'px'; -}; - -/** - * Function: destroyCanvas - * - * Destroys the given canvas which was used for drawing. This implementation - * increments the reference counts on all shared gradients used in the canvas. - */ -mxShape.prototype.destroyCanvas = function(canvas) -{ - // Manages reference counts - if (canvas instanceof mxSvgCanvas2D) - { - // Increments ref counts - for (var key in canvas.gradients) - { - var gradient = canvas.gradients[key]; - - if (gradient != null) - { - gradient.mxRefCount = (gradient.mxRefCount || 0) + 1; - } - } - - this.releaseSvgGradients(this.oldGradients); - this.oldGradients = canvas.gradients; - } -}; - -/** - * Function: paint - * - * Generic rendering code. - */ -mxShape.prototype.paint = function(c) -{ - var strokeDrawn = false; - - if (c != null && this.outline) - { - var stroke = c.stroke; - - c.stroke = function() - { - strokeDrawn = true; - stroke.apply(this, arguments); - }; - - var fillAndStroke = c.fillAndStroke; - - c.fillAndStroke = function() - { - strokeDrawn = true; - fillAndStroke.apply(this, arguments); - }; - } - - // Scale is passed-through to canvas - var s = this.scale; - var x = this.bounds.x / s; - var y = this.bounds.y / s; - var w = this.bounds.width / s; - var h = this.bounds.height / s; - - if (this.isPaintBoundsInverted()) - { - var t = (w - h) / 2; - x += t; - y -= t; - var tmp = w; - w = h; - h = tmp; - } - - this.updateTransform(c, x, y, w, h); - this.configureCanvas(c, x, y, w, h); - - // Adds background rectangle to capture events - var bg = null; - - if ((this.stencil == null && this.points == null && this.shapePointerEvents) || - (this.stencil != null && this.stencilPointerEvents)) - { - var bb = this.createBoundingBox(); - - if (this.dialect == mxConstants.DIALECT_SVG) - { - bg = this.createTransparentSvgRectangle(bb.x, bb.y, bb.width, bb.height); - this.node.appendChild(bg); - } - else - { - var rect = c.createRect('rect', bb.x / s, bb.y / s, bb.width / s, bb.height / s); - rect.appendChild(c.createTransparentFill()); - rect.stroked = 'false'; - c.root.appendChild(rect); - } - } - - if (this.stencil != null) - { - this.stencil.drawShape(c, this, x, y, w, h); - } - else - { - // Stencils have separate strokewidth - c.setStrokeWidth(this.strokewidth); - - if (this.points != null) - { - // Paints edge shape - var pts = []; - - for (var i = 0; i < this.points.length; i++) - { - if (this.points[i] != null) - { - pts.push(new mxPoint(this.points[i].x / s, this.points[i].y / s)); - } - } - - this.paintEdgeShape(c, pts); - } - else - { - // Paints vertex shape - this.paintVertexShape(c, x, y, w, h); - } - } - - if (bg != null && c.state != null && c.state.transform != null) - { - bg.setAttribute('transform', c.state.transform); - } - - // Draws highlight rectangle if no stroke was used - if (c != null && this.outline && !strokeDrawn) - { - c.rect(x, y, w, h); - c.stroke(); - } -}; - -/** - * Function: configureCanvas - * - * Sets the state of the canvas for drawing the shape. - */ -mxShape.prototype.configureCanvas = function(c, x, y, w, h) -{ - var dash = null; - - if (this.style != null) - { - dash = this.style['dashPattern']; - } - - c.setAlpha(this.opacity / 100); - c.setFillAlpha(this.fillOpacity / 100); - c.setStrokeAlpha(this.strokeOpacity / 100); - - // Sets alpha, colors and gradients - if (this.isShadow != null) - { - c.setShadow(this.isShadow); - } - - // Dash pattern - if (this.isDashed != null) - { - c.setDashed(this.isDashed, (this.style != null) ? - mxUtils.getValue(this.style, mxConstants.STYLE_FIX_DASH, false) == 1 : false); - } - - if (dash != null) - { - c.setDashPattern(dash); - } - - if (this.fill != null && this.fill != mxConstants.NONE && this.gradient && this.gradient != mxConstants.NONE) - { - var b = this.getGradientBounds(c, x, y, w, h); - c.setGradient(this.fill, this.gradient, b.x, b.y, b.width, b.height, this.gradientDirection); - } - else - { - c.setFillColor(this.fill); - } - - c.setStrokeColor(this.stroke); -}; - -/** - * Function: getGradientBounds - * - * Returns the bounding box for the gradient box for this shape. - */ -mxShape.prototype.getGradientBounds = function(c, x, y, w, h) -{ - return new mxRectangle(x, y, w, h); -}; - -/** - * Function: updateTransform - * - * Sets the scale and rotation on the given canvas. - */ -mxShape.prototype.updateTransform = function(c, x, y, w, h) -{ - // NOTE: Currently, scale is implemented in state and canvas. This will - // move to canvas in a later version, so that the states are unscaled - // and untranslated and do not need an update after zooming or panning. - c.scale(this.scale); - c.rotate(this.getShapeRotation(), this.flipH, this.flipV, x + w / 2, y + h / 2); -}; - -/** - * Function: paintVertexShape - * - * Paints the vertex shape. - */ -mxShape.prototype.paintVertexShape = function(c, x, y, w, h) -{ - this.paintBackground(c, x, y, w, h); - - if (!this.outline || this.style == null || mxUtils.getValue( - this.style, mxConstants.STYLE_BACKGROUND_OUTLINE, 0) == 0) - { - c.setShadow(false); - this.paintForeground(c, x, y, w, h); - } -}; - -/** - * Function: paintBackground - * - * Hook for subclassers. This implementation is empty. - */ -mxShape.prototype.paintBackground = function(c, x, y, w, h) { }; - -/** - * Function: paintForeground - * - * Hook for subclassers. This implementation is empty. - */ -mxShape.prototype.paintForeground = function(c, x, y, w, h) { }; - -/** - * Function: paintEdgeShape - * - * Hook for subclassers. This implementation is empty. - */ -mxShape.prototype.paintEdgeShape = function(c, pts) { }; - -/** - * Function: getArcSize - * - * Returns the arc size for the given dimension. - */ -mxShape.prototype.getArcSize = function(w, h) -{ - var r = 0; - - if (mxUtils.getValue(this.style, mxConstants.STYLE_ABSOLUTE_ARCSIZE, 0) == '1') - { - r = Math.min(w / 2, Math.min(h / 2, mxUtils.getValue(this.style, - mxConstants.STYLE_ARCSIZE, mxConstants.LINE_ARCSIZE) / 2)); - } - else - { - var f = mxUtils.getValue(this.style, mxConstants.STYLE_ARCSIZE, - mxConstants.RECTANGLE_ROUNDING_FACTOR * 100) / 100; - r = Math.min(w * f, h * f); - } - - return r; -}; - -/** - * Function: paintGlassEffect - * - * Paints the glass gradient effect. - */ -mxShape.prototype.paintGlassEffect = function(c, x, y, w, h, arc) -{ - var sw = Math.ceil(this.strokewidth / 2); - var size = 0.4; - - c.setGradient('#ffffff', '#ffffff', x, y, w, h * 0.6, 'south', 0.9, 0.1); - c.begin(); - arc += 2 * sw; - - if (this.isRounded) - { - c.moveTo(x - sw + arc, y - sw); - c.quadTo(x - sw, y - sw, x - sw, y - sw + arc); - c.lineTo(x - sw, y + h * size); - c.quadTo(x + w * 0.5, y + h * 0.7, x + w + sw, y + h * size); - c.lineTo(x + w + sw, y - sw + arc); - c.quadTo(x + w + sw, y - sw, x + w + sw - arc, y - sw); - } - else - { - c.moveTo(x - sw, y - sw); - c.lineTo(x - sw, y + h * size); - c.quadTo(x + w * 0.5, y + h * 0.7, x + w + sw, y + h * size); - c.lineTo(x + w + sw, y - sw); - } - - c.close(); - c.fill(); -}; - -/** - * Function: addPoints - * - * Paints the given points with rounded corners. - */ -mxShape.prototype.addPoints = function(c, pts, rounded, arcSize, close, exclude, initialMove) -{ - if (pts != null && pts.length > 0) - { - initialMove = (initialMove != null) ? initialMove : true; - var pe = pts[pts.length - 1]; - - // Adds virtual waypoint in the center between start and end point - if (close && rounded) - { - pts = pts.slice(); - var p0 = pts[0]; - var wp = new mxPoint(pe.x + (p0.x - pe.x) / 2, pe.y + (p0.y - pe.y) / 2); - pts.splice(0, 0, wp); - } - - var pt = pts[0]; - var i = 1; - - // Draws the line segments - if (initialMove) - { - c.moveTo(pt.x, pt.y); - } - else - { - c.lineTo(pt.x, pt.y); - } - - while (i < ((close) ? pts.length : pts.length - 1)) - { - var tmp = pts[mxUtils.mod(i, pts.length)]; - var dx = pt.x - tmp.x; - var dy = pt.y - tmp.y; - - if (rounded && (dx != 0 || dy != 0) && (exclude == null || mxUtils.indexOf(exclude, i - 1) < 0)) - { - // Draws a line from the last point to the current - // point with a spacing of size off the current point - // into direction of the last point - var dist = Math.sqrt(dx * dx + dy * dy); - var nx1 = dx * Math.min(arcSize, dist / 2) / dist; - var ny1 = dy * Math.min(arcSize, dist / 2) / dist; - - var x1 = tmp.x + nx1; - var y1 = tmp.y + ny1; - c.lineTo(x1, y1); - - // Draws a curve from the last point to the current - // point with a spacing of size off the current point - // into direction of the next point - var next = pts[mxUtils.mod(i + 1, pts.length)]; - - // Uses next non-overlapping point - while (i < pts.length - 2 && Math.round(next.x - tmp.x) == 0 && Math.round(next.y - tmp.y) == 0) - { - next = pts[mxUtils.mod(i + 2, pts.length)]; - i++; - } - - dx = next.x - tmp.x; - dy = next.y - tmp.y; - - dist = Math.max(1, Math.sqrt(dx * dx + dy * dy)); - var nx2 = dx * Math.min(arcSize, dist / 2) / dist; - var ny2 = dy * Math.min(arcSize, dist / 2) / dist; - - var x2 = tmp.x + nx2; - var y2 = tmp.y + ny2; - - c.quadTo(tmp.x, tmp.y, x2, y2); - tmp = new mxPoint(x2, y2); - } - else - { - c.lineTo(tmp.x, tmp.y); - } - - pt = tmp; - i++; - } - - if (close) - { - c.close(); - } - else - { - c.lineTo(pe.x, pe.y); - } - } -}; - -/** - * Function: resetStyles - * - * Resets all styles. - */ -mxShape.prototype.resetStyles = function() -{ - this.initStyles(); - - this.spacing = 0; - - delete this.fill; - delete this.gradient; - delete this.gradientDirection; - delete this.stroke; - delete this.startSize; - delete this.endSize; - delete this.startArrow; - delete this.endArrow; - delete this.direction; - delete this.isShadow; - delete this.isDashed; - delete this.isRounded; - delete this.glass; -}; - -/** - * Function: apply - * - * Applies the style of the given to the shape. This - * implementation assigns the following styles to local fields: - * - * - => fill - * - => gradient - * - => gradientDirection - * - => opacity - * - => fillOpacity - * - => strokeOpacity - * - => stroke - * - => strokewidth - * - => isShadow - * - => isDashed - * - => spacing - * - => startSize - * - => endSize - * - => isRounded - * - => startArrow - * - => endArrow - * - => rotation - * - => direction - * - => glass - * - * This keeps a reference to the '); - * }; - * (end) - * - * Headers: - * - * Apart from setting the title argument in the mxPrintPreview constructor you - * can override as follows to add a header to any page: - * - * (code) - * var oldRenderPage = mxPrintPreview.prototype.renderPage; - * mxPrintPreview.prototype.renderPage = function(w, h, x, y, content, pageNumber) - * { - * var div = oldRenderPage.apply(this, arguments); - * - * var header = document.createElement('div'); - * header.style.position = 'absolute'; - * header.style.top = '0px'; - * header.style.width = '100%'; - * header.style.textAlign = 'right'; - * mxUtils.write(header, 'Your header here'); - * div.firstChild.appendChild(header); - * - * return div; - * }; - * (end) - * - * The pageNumber argument contains the number of the current page, starting at - * 1. To display a header on the first page only, check pageNumber and add a - * vertical offset in the constructor call for the height of the header. - * - * Page Format: - * - * For landscape printing, use as - * the pageFormat in and . - * Keep in mind that one can not set the defaults for the print dialog - * of the operating system from JavaScript so the user must manually choose - * a page format that matches this setting. - * - * You can try passing the following CSS directive to to set the - * page format in the print dialog to landscape. However, this CSS - * directive seems to be ignored in most major browsers, including IE. - * - * (code) - * @page { - * size: landscape; - * } - * (end) - * - * Note that the print preview behaves differently in IE when used from the - * filesystem or via HTTP so printing should always be tested via HTTP. - * - * If you are using a DOCTYPE in the source page you can override - * and provide the same DOCTYPE for the print preview if required. Here is - * an example for IE8 standards mode. - * - * (code) - * var preview = new mxPrintPreview(graph); - * preview.getDoctype = function() - * { - * return ''; - * }; - * preview.open(); - * (end) - * - * Constructor: mxPrintPreview - * - * Constructs a new print preview for the given parameters. - * - * Parameters: - * - * graph - to be previewed. - * scale - Optional scale of the output. Default is 1 / . - * border - Border in pixels along each side of every page. Note that the - * actual print function in the browser will add another border for - * printing. - * pageFormat - that specifies the page format (in pixels). - * This should match the page format of the printer. Default uses the - * of the given graph. - * x0 - Optional left offset of the output. Default is 0. - * y0 - Optional top offset of the output. Default is 0. - * borderColor - Optional color of the page border. Default is no border. - * Note that a border is sometimes useful to highlight the printed page - * border in the print preview of the browser. - * title - Optional string that is used for the window title. Default - * is 'Printer-friendly version'. - * pageSelector - Optional boolean that specifies if the page selector - * should appear in the window with the print preview. Default is true. - */ -function mxPrintPreview(graph, scale, pageFormat, border, x0, y0, borderColor, title, pageSelector) -{ - this.graph = graph; - this.scale = (scale != null) ? scale : 1 / graph.pageScale; - this.border = (border != null) ? border : 0; - this.pageFormat = mxRectangle.fromRectangle((pageFormat != null) ? pageFormat : graph.pageFormat); - this.title = (title != null) ? title : 'Printer-friendly version'; - this.x0 = (x0 != null) ? x0 : 0; - this.y0 = (y0 != null) ? y0 : 0; - this.borderColor = borderColor; - this.pageSelector = (pageSelector != null) ? pageSelector : true; -}; - -/** - * Variable: graph - * - * Reference to the that should be previewed. - */ -mxPrintPreview.prototype.graph = null; - -/** - * Variable: pageFormat - * - * Holds the that defines the page format. - */ -mxPrintPreview.prototype.pageFormat = null; - -/** - * Variable: scale - * - * Holds the scale of the print preview. - */ -mxPrintPreview.prototype.scale = null; - -/** - * Variable: border - * - * The border inset around each side of every page in the preview. This is set - * to 0 if autoOrigin is false. - */ -mxPrintPreview.prototype.border = 0; - -/** - * Variable: marginTop - * - * The margin at the top of the page (number). Default is 0. - */ -mxPrintPreview.prototype.marginTop = 0; - -/** - * Variable: marginBottom - * - * The margin at the bottom of the page (number). Default is 0. - */ -mxPrintPreview.prototype.marginBottom = 0; - -/** - * Variable: x0 - * - * Holds the horizontal offset of the output. - */ -mxPrintPreview.prototype.x0 = 0; - -/** - * Variable: y0 - * - * Holds the vertical offset of the output. - */ -mxPrintPreview.prototype.y0 = 0; - -/** - * Variable: autoOrigin - * - * Specifies if the origin should be automatically computed based on the top, - * left corner of the actual diagram contents. The required offset will be added - * to and in . Default is true. - */ -mxPrintPreview.prototype.autoOrigin = true; - -/** - * Variable: printOverlays - * - * Specifies if overlays should be printed. Default is false. - */ -mxPrintPreview.prototype.printOverlays = false; - -/** - * Variable: printControls - * - * Specifies if controls (such as folding icons) should be printed. Default is - * false. - */ -mxPrintPreview.prototype.printControls = false; - -/** - * Variable: printBackgroundImage - * - * Specifies if the background image should be printed. Default is false. - */ -mxPrintPreview.prototype.printBackgroundImage = false; - -/** - * Variable: backgroundColor - * - * Holds the color value for the page background color. Default is #ffffff. - */ -mxPrintPreview.prototype.backgroundColor = '#ffffff'; - -/** - * Variable: borderColor - * - * Holds the color value for the page border. - */ -mxPrintPreview.prototype.borderColor = null; - -/** - * Variable: title - * - * Holds the title of the preview window. - */ -mxPrintPreview.prototype.title = null; - -/** - * Variable: pageSelector - * - * Boolean that specifies if the page selector should be - * displayed. Default is true. - */ -mxPrintPreview.prototype.pageSelector = null; - -/** - * Variable: wnd - * - * Reference to the preview window. - */ -mxPrintPreview.prototype.wnd = null; - -/** - * Variable: targetWindow - * - * Assign any window here to redirect the rendering in . - */ -mxPrintPreview.prototype.targetWindow = null; - -/** - * Variable: pageCount - * - * Holds the actual number of pages in the preview. - */ -mxPrintPreview.prototype.pageCount = 0; - -/** - * Variable: clipping - * - * Specifies is clipping should be used to avoid creating too many cell states - * in large diagrams. The bounding box of the cells in the original diagram is - * used if this is enabled. Default is true. - */ -mxPrintPreview.prototype.clipping = true; - -/** - * Function: getWindow - * - * Returns . - */ -mxPrintPreview.prototype.getWindow = function() -{ - return this.wnd; -}; - -/** - * Function: getDocType - * - * Returns the string that should go before the HTML tag in the print preview - * page. This implementation returns an X-UA meta tag for IE5 in quirks mode, - * IE8 in IE8 standards mode and edge in IE9 standards mode. - */ -mxPrintPreview.prototype.getDoctype = function() -{ - var dt = ''; - - if (document.documentMode == 5) - { - dt = ''; - } - else if (document.documentMode == 8) - { - dt = ''; - } - else if (document.documentMode > 8) - { - // Comment needed to make standards doctype apply in IE - dt = ''; - } - - return dt; -}; - -/** - * Function: appendGraph - * - * Adds the given graph to the existing print preview. - * - * Parameters: - * - * css - Optional CSS string to be used in the head section. - * targetWindow - Optional window that should be used for rendering. If - * this is specified then no HEAD tag, CSS and BODY tag will be written. - */ -mxPrintPreview.prototype.appendGraph = function(graph, scale, x0, y0, forcePageBreaks, keepOpen) -{ - this.graph = graph; - this.scale = (scale != null) ? scale : 1 / graph.pageScale; - this.x0 = x0; - this.y0 = y0; - this.open(null, null, forcePageBreaks, keepOpen); -}; - -/** - * Function: open - * - * Shows the print preview window. The window is created here if it does - * not exist. - * - * Parameters: - * - * css - Optional CSS string to be used in the head section. - * targetWindow - Optional window that should be used for rendering. If - * this is specified then no HEAD tag, CSS and BODY tag will be written. - */ -mxPrintPreview.prototype.open = function(css, targetWindow, forcePageBreaks, keepOpen) -{ - // Closing the window while the page is being rendered may cause an - // exception in IE. This and any other exceptions are simply ignored. - var previousInitializeOverlay = this.graph.cellRenderer.initializeOverlay; - var div = null; - - try - { - // Temporarily overrides the method to redirect rendering of overlays - // to the draw pane so that they are visible in the printout - if (this.printOverlays) - { - this.graph.cellRenderer.initializeOverlay = function(state, overlay) - { - overlay.init(state.view.getDrawPane()); - }; - } - - if (this.printControls) - { - this.graph.cellRenderer.initControl = function(state, control, handleEvents, clickHandler) - { - control.dialect = state.view.graph.dialect; - control.init(state.view.getDrawPane()); - }; - } - - this.wnd = (targetWindow != null) ? targetWindow : this.wnd; - var isNewWindow = false; - - if (this.wnd == null) - { - isNewWindow = true; - this.wnd = window.open(); - } - - var doc = this.wnd.document; - - if (isNewWindow) - { - var dt = this.getDoctype(); - - if (dt != null && dt.length > 0) - { - doc.writeln(dt); - } - - if (mxClient.IS_VML) - { - doc.writeln(''); - } - else - { - if (document.compatMode === 'CSS1Compat') - { - doc.writeln(''); - } - - doc.writeln(''); - } - - doc.writeln(''); - this.writeHead(doc, css); - doc.writeln(''); - doc.writeln(''); - } - - // Computes the horizontal and vertical page count - var bounds = this.graph.getGraphBounds().clone(); - var currentScale = this.graph.getView().getScale(); - var sc = currentScale / this.scale; - var tr = this.graph.getView().getTranslate(); - - // Uses the absolute origin with no offset for all printing - if (!this.autoOrigin) - { - this.x0 -= tr.x * this.scale; - this.y0 -= tr.y * this.scale; - bounds.width += bounds.x; - bounds.height += bounds.y; - bounds.x = 0; - bounds.y = 0; - this.border = 0; - } - - // Store the available page area - var availableWidth = this.pageFormat.width - (this.border * 2); - var availableHeight = this.pageFormat.height - (this.border * 2); - - // Adds margins to page format - this.pageFormat.height += this.marginTop + this.marginBottom; - - // Compute the unscaled, untranslated bounds to find - // the number of vertical and horizontal pages - bounds.width /= sc; - bounds.height /= sc; - - var hpages = Math.max(1, Math.ceil((bounds.width + this.x0) / availableWidth)); - var vpages = Math.max(1, Math.ceil((bounds.height + this.y0) / availableHeight)); - this.pageCount = hpages * vpages; - - var writePageSelector = mxUtils.bind(this, function() - { - if (this.pageSelector && (vpages > 1 || hpages > 1)) - { - var table = this.createPageSelector(vpages, hpages); - doc.body.appendChild(table); - - // Implements position: fixed in IE quirks mode - if (mxClient.IS_IE && doc.documentMode == null || doc.documentMode == 5 || doc.documentMode == 8 || doc.documentMode == 7) - { - table.style.position = 'absolute'; - - var update = function() - { - table.style.top = ((doc.body.scrollTop || doc.documentElement.scrollTop) + 10) + 'px'; - }; - - mxEvent.addListener(this.wnd, 'scroll', function(evt) - { - update(); - }); - - mxEvent.addListener(this.wnd, 'resize', function(evt) - { - update(); - }); - } - } - }); - - var addPage = mxUtils.bind(this, function(div, addBreak) - { - // Border of the DIV (aka page) inside the document - if (this.borderColor != null) - { - div.style.borderColor = this.borderColor; - div.style.borderStyle = 'solid'; - div.style.borderWidth = '1px'; - } - - // Needs to be assigned directly because IE doesn't support - // child selectors, eg. body > div { background: white; } - div.style.background = this.backgroundColor; - - if (forcePageBreaks || addBreak) - { - div.style.pageBreakAfter = 'always'; - } - - // NOTE: We are dealing with cross-window DOM here, which - // is a problem in IE, so we copy the HTML markup instead. - // The underlying problem is that the graph display markup - // creation (in mxShape, mxGraphView) is hardwired to using - // document.createElement and hence we must use this document - // to create the complete page and then copy it over to the - // new window.document. This can be fixed later by using the - // ownerDocument of the container in mxShape and mxGraphView. - if (isNewWindow && (mxClient.IS_IE || document.documentMode >= 11 || mxClient.IS_EDGE)) - { - // For some obscure reason, removing the DIV from the - // parent before fetching its outerHTML has missing - // fillcolor properties and fill children, so the div - // must be removed afterwards to keep the fillcolors. - doc.writeln(div.outerHTML); - div.parentNode.removeChild(div); - } - else if (mxClient.IS_IE || document.documentMode >= 11 || mxClient.IS_EDGE) - { - var clone = doc.createElement('div'); - clone.innerHTML = div.outerHTML; - clone = clone.getElementsByTagName('div')[0]; - doc.body.appendChild(clone); - div.parentNode.removeChild(div); - } - else - { - div.parentNode.removeChild(div); - doc.body.appendChild(div); - } - - if (forcePageBreaks || addBreak) - { - this.addPageBreak(doc); - } - }); - - var cov = this.getCoverPages(this.pageFormat.width, this.pageFormat.height); - - if (cov != null) - { - for (var i = 0; i < cov.length; i++) - { - addPage(cov[i], true); - } - } - - var apx = this.getAppendices(this.pageFormat.width, this.pageFormat.height); - - // Appends each page to the page output for printing, making - // sure there will be a page break after each page (ie. div) - for (var i = 0; i < vpages; i++) - { - var dy = i * availableHeight / this.scale - this.y0 / this.scale + - (bounds.y - tr.y * currentScale) / currentScale; - - for (var j = 0; j < hpages; j++) - { - if (this.wnd == null) - { - return null; - } - - var dx = j * availableWidth / this.scale - this.x0 / this.scale + - (bounds.x - tr.x * currentScale) / currentScale; - var pageNum = i * hpages + j + 1; - var clip = new mxRectangle(dx, dy, availableWidth, availableHeight); - div = this.renderPage(this.pageFormat.width, this.pageFormat.height, 0, 0, mxUtils.bind(this, function(div) - { - this.addGraphFragment(-dx, -dy, this.scale, pageNum, div, clip); - - if (this.printBackgroundImage) - { - this.insertBackgroundImage(div, -dx, -dy); - } - }), pageNum); - - // Gives the page a unique ID for later accessing the page - div.setAttribute('id', 'mxPage-'+pageNum); - - addPage(div, apx != null || i < vpages - 1 || j < hpages - 1); - } - } - - if (apx != null) - { - for (var i = 0; i < apx.length; i++) - { - addPage(apx[i], i < apx.length - 1); - } - } - - if (isNewWindow && !keepOpen) - { - this.closeDocument(); - writePageSelector(); - } - - this.wnd.focus(); - } - catch (e) - { - // Removes the DIV from the document in case of an error - if (div != null && div.parentNode != null) - { - div.parentNode.removeChild(div); - } - } - finally - { - this.graph.cellRenderer.initializeOverlay = previousInitializeOverlay; - } - - return this.wnd; -}; - -/** - * Function: addPageBreak - * - * Adds a page break to the given document. - */ -mxPrintPreview.prototype.addPageBreak = function(doc) -{ - var hr = doc.createElement('hr'); - hr.className = 'mxPageBreak'; - doc.body.appendChild(hr); -}; - -/** - * Function: closeDocument - * - * Writes the closing tags for body and page after calling . - */ -mxPrintPreview.prototype.closeDocument = function() -{ - if (this.wnd != null && this.wnd.document != null) - { - var doc = this.wnd.document; - - this.writePostfix(doc); - doc.writeln(''); - doc.writeln(''); - doc.close(); - - // Removes all event handlers in the print output - mxEvent.release(doc.body); - } -}; - -/** - * Function: writeHead - * - * Writes the HEAD section into the given document, without the opening - * and closing HEAD tags. - */ -mxPrintPreview.prototype.writeHead = function(doc, css) -{ - if (this.title != null) - { - doc.writeln('' + this.title + ''); - } - - // Adds required namespaces - if (mxClient.IS_VML) - { - doc.writeln(''); - } - - // Adds all required stylesheets - mxClient.link('stylesheet', mxClient.basePath + '/css/common.css', doc); - - // Removes horizontal rules and page selector from print output - doc.writeln(''); -}; - -/** - * Function: writePostfix - * - * Called before closing the body of the page. This implementation is empty. - */ -mxPrintPreview.prototype.writePostfix = function(doc) -{ - // empty -}; - -/** - * Function: createPageSelector - * - * Creates the page selector table. - */ -mxPrintPreview.prototype.createPageSelector = function(vpages, hpages) -{ - var doc = this.wnd.document; - var table = doc.createElement('table'); - table.className = 'mxPageSelector'; - table.setAttribute('border', '0'); - - var tbody = doc.createElement('tbody'); - - for (var i = 0; i < vpages; i++) - { - var row = doc.createElement('tr'); - - for (var j = 0; j < hpages; j++) - { - var pageNum = i * hpages + j + 1; - var cell = doc.createElement('td'); - var a = doc.createElement('a'); - a.setAttribute('href', '#mxPage-' + pageNum); - - // Workaround for FF where the anchor is appended to the URL of the original document - if (mxClient.IS_NS && !mxClient.IS_SF && !mxClient.IS_GC) - { - var js = 'var page = document.getElementById(\'mxPage-' + pageNum + '\');page.scrollIntoView(true);event.preventDefault();'; - a.setAttribute('onclick', js); - } - - mxUtils.write(a, pageNum, doc); - cell.appendChild(a); - row.appendChild(cell); - } - - tbody.appendChild(row); - } - - table.appendChild(tbody); - - return table; -}; - -/** - * Function: renderPage - * - * Creates a DIV that prints a single page of the given - * graph using the given scale and returns the DIV that - * represents the page. - * - * Parameters: - * - * w - Width of the page in pixels. - * h - Height of the page in pixels. - * dx - Optional horizontal page offset in pixels (used internally). - * dy - Optional vertical page offset in pixels (used internally). - * content - Callback that adds the HTML content to the inner div of a page. - * Takes the inner div as the argument. - * pageNumber - Integer representing the page number. - */ -mxPrintPreview.prototype.renderPage = function(w, h, dx, dy, content, pageNumber) -{ - var doc = this.wnd.document; - var div = document.createElement('div'); - var arg = null; - - try - { - // Workaround for ignored clipping in IE 9 standards - // when printing with page breaks and HTML labels. - if (dx != 0 || dy != 0) - { - div.style.position = 'relative'; - div.style.width = w + 'px'; - div.style.height = h + 'px'; - div.style.pageBreakInside = 'avoid'; - - var innerDiv = document.createElement('div'); - innerDiv.style.position = 'relative'; - innerDiv.style.top = this.border + 'px'; - innerDiv.style.left = this.border + 'px'; - innerDiv.style.width = (w - 2 * this.border) + 'px'; - innerDiv.style.height = (h - 2 * this.border) + 'px'; - innerDiv.style.overflow = 'hidden'; - - var viewport = document.createElement('div'); - viewport.style.position = 'relative'; - viewport.style.marginLeft = dx + 'px'; - viewport.style.marginTop = dy + 'px'; - - // FIXME: IE8 standards output problems - if (doc.documentMode == 8) - { - innerDiv.style.position = 'absolute'; - viewport.style.position = 'absolute'; - } - - if (doc.documentMode == 10) - { - viewport.style.width = '100%'; - viewport.style.height = '100%'; - } - - innerDiv.appendChild(viewport); - div.appendChild(innerDiv); - document.body.appendChild(div); - arg = viewport; - } - // FIXME: IE10/11 too many pages - else - { - div.style.width = w + 'px'; - div.style.height = h + 'px'; - div.style.overflow = 'hidden'; - div.style.pageBreakInside = 'avoid'; - - // IE8 uses above branch currently - if (doc.documentMode == 8) - { - div.style.position = 'relative'; - } - - var innerDiv = document.createElement('div'); - innerDiv.style.width = (w - 2 * this.border) + 'px'; - innerDiv.style.height = (h - 2 * this.border) + 'px'; - innerDiv.style.overflow = 'hidden'; - - if (mxClient.IS_IE && (doc.documentMode == null || doc.documentMode == 5 || doc.documentMode == 8 || doc.documentMode == 7)) - { - innerDiv.style.marginTop = this.border + 'px'; - innerDiv.style.marginLeft = this.border + 'px'; - } - else - { - innerDiv.style.top = this.border + 'px'; - innerDiv.style.left = this.border + 'px'; - } - - if (this.graph.dialect == mxConstants.DIALECT_VML) - { - innerDiv.style.position = 'absolute'; - } - - div.appendChild(innerDiv); - document.body.appendChild(div); - arg = innerDiv; - } - } - catch (e) - { - div.parentNode.removeChild(div); - div = null; - - throw e; - } - - content(arg); - - return div; -}; - -/** - * Function: getRoot - * - * Returns the root cell for painting the graph. - */ -mxPrintPreview.prototype.getRoot = function() -{ - var root = this.graph.view.currentRoot; - - if (root == null) - { - root = this.graph.getModel().getRoot(); - } - - return root; -}; - -/** - * Function: addGraphFragment - * - * Adds a graph fragment to the given div. - * - * Parameters: - * - * dx - Horizontal translation for the diagram. - * dy - Vertical translation for the diagram. - * scale - Scale for the diagram. - * pageNumber - Number of the page to be rendered. - * div - Div that contains the output. - * clip - Contains the clipping rectangle as an . - */ -mxPrintPreview.prototype.addGraphFragment = function(dx, dy, scale, pageNumber, div, clip) -{ - var view = this.graph.getView(); - var previousContainer = this.graph.container; - this.graph.container = div; - - var canvas = view.getCanvas(); - var backgroundPane = view.getBackgroundPane(); - var drawPane = view.getDrawPane(); - var overlayPane = view.getOverlayPane(); - - if (this.graph.dialect == mxConstants.DIALECT_SVG) - { - view.createSvg(); - - // Uses CSS transform for scaling - if (!mxClient.NO_FO) - { - var g = view.getDrawPane().parentNode; - var prev = g.getAttribute('transform'); - g.setAttribute('transformOrigin', '0 0'); - g.setAttribute('transform', 'scale(' + scale + ',' + scale + ')' + - 'translate(' + dx + ',' + dy + ')'); - - scale = 1; - dx = 0; - dy = 0; - } - } - else if (this.graph.dialect == mxConstants.DIALECT_VML) - { - view.createVml(); - } - else - { - view.createHtml(); - } - - // Disables events on the view - var eventsEnabled = view.isEventsEnabled(); - view.setEventsEnabled(false); - - // Disables the graph to avoid cursors - var graphEnabled = this.graph.isEnabled(); - this.graph.setEnabled(false); - - // Resets the translation - var translate = view.getTranslate(); - view.translate = new mxPoint(dx, dy); - - // Redraws only states that intersect the clip - var redraw = this.graph.cellRenderer.redraw; - var states = view.states; - var s = view.scale; - - // Gets the transformed clip for intersection check below - if (this.clipping) - { - var tempClip = new mxRectangle((clip.x + translate.x) * s, (clip.y + translate.y) * s, - clip.width * s / scale, clip.height * s / scale); - - // Checks clipping rectangle for speedup - // Must create terminal states for edge clipping even if terminal outside of clip - this.graph.cellRenderer.redraw = function(state, force, rendering) - { - if (state != null) - { - // Gets original state from graph to find bounding box - var orig = states.get(state.cell); - - if (orig != null) - { - var bbox = view.getBoundingBox(orig, false); - - // Stops rendering if outside clip for speedup - if (bbox != null && !mxUtils.intersects(tempClip, bbox)) - { - //return; - } - } - } - - redraw.apply(this, arguments); - }; - } - - var temp = null; - - try - { - // Creates the temporary cell states in the view and - // draws them onto the temporary DOM nodes in the view - var cells = [this.getRoot()]; - temp = new mxTemporaryCellStates(view, scale, cells, null, mxUtils.bind(this, function(state) - { - return this.getLinkForCellState(state); - })); - } - finally - { - // Removes overlay pane with selection handles - // controls and icons from the print output - if (mxClient.IS_IE) - { - view.overlayPane.innerHTML = ''; - view.canvas.style.overflow = 'hidden'; - view.canvas.style.position = 'relative'; - view.canvas.style.top = this.marginTop + 'px'; - view.canvas.style.width = clip.width + 'px'; - view.canvas.style.height = clip.height + 'px'; - } - else - { - // Removes everything but the SVG node - var tmp = div.firstChild; - - while (tmp != null) - { - var next = tmp.nextSibling; - var name = tmp.nodeName.toLowerCase(); - - // Note: Width and height are required in FF 11 - if (name == 'svg') - { - tmp.style.overflow = 'hidden'; - tmp.style.position = 'relative'; - tmp.style.top = this.marginTop + 'px'; - tmp.setAttribute('width', clip.width); - tmp.setAttribute('height', clip.height); - tmp.style.width = ''; - tmp.style.height = ''; - } - // Tries to fetch all text labels and only text labels - else if (tmp.style.cursor != 'default' && name != 'div') - { - tmp.parentNode.removeChild(tmp); - } - - tmp = next; - } - } - - // Puts background image behind SVG output - if (this.printBackgroundImage) - { - var svgs = div.getElementsByTagName('svg'); - - if (svgs.length > 0) - { - svgs[0].style.position = 'absolute'; - } - } - - // Completely removes the overlay pane to remove more handles - view.overlayPane.parentNode.removeChild(view.overlayPane); - - // Restores the state of the view - this.graph.setEnabled(graphEnabled); - this.graph.container = previousContainer; - this.graph.cellRenderer.redraw = redraw; - view.canvas = canvas; - view.backgroundPane = backgroundPane; - view.drawPane = drawPane; - view.overlayPane = overlayPane; - view.translate = translate; - temp.destroy(); - view.setEventsEnabled(eventsEnabled); - } -}; - -/** - * Function: getLinkForCellState - * - * Returns the link for the given cell state. This returns null. - */ -mxPrintPreview.prototype.getLinkForCellState = function(state) -{ - return this.graph.getLinkForCell(state.cell); -}; - -/** - * Function: insertBackgroundImage - * - * Inserts the background image into the given div. - */ -mxPrintPreview.prototype.insertBackgroundImage = function(div, dx, dy) -{ - var bg = this.graph.backgroundImage; - - if (bg != null) - { - var img = document.createElement('img'); - img.style.position = 'absolute'; - img.style.marginLeft = Math.round(dx * this.scale) + 'px'; - img.style.marginTop = Math.round(dy * this.scale) + 'px'; - img.setAttribute('width', Math.round(this.scale * bg.width)); - img.setAttribute('height', Math.round(this.scale * bg.height)); - img.src = bg.src; - - div.insertBefore(img, div.firstChild); - } -}; - -/** - * Function: getCoverPages - * - * Returns the pages to be added before the print output. This returns null. - */ -mxPrintPreview.prototype.getCoverPages = function() -{ - return null; -}; - -/** - * Function: getAppendices - * - * Returns the pages to be added after the print output. This returns null. - */ -mxPrintPreview.prototype.getAppendices = function() -{ - return null; -}; - -/** - * Function: print - * - * Opens the print preview and shows the print dialog. - * - * Parameters: - * - * css - Optional CSS string to be used in the head section. - */ -mxPrintPreview.prototype.print = function(css) -{ - var wnd = this.open(css); - - if (wnd != null) - { - wnd.print(); - } -}; - -/** - * Function: close - * - * Closes the print preview window. - */ -mxPrintPreview.prototype.close = function() -{ - if (this.wnd != null) - { - this.wnd.close(); - this.wnd = null; - } -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -/** - * Class: mxStylesheet - * - * Defines the appearance of the cells in a graph. See for an - * example of creating a new cell style. It is recommended to use objects, not - * arrays for holding cell styles. Existing styles can be cloned using - * and turned into a string for debugging using - * . - * - * Default Styles: - * - * The stylesheet contains two built-in styles, which are used if no style is - * defined for a cell: - * - * defaultVertex - Default style for vertices - * defaultEdge - Default style for edges - * - * Example: - * - * (code) - * var vertexStyle = stylesheet.getDefaultVertexStyle(); - * vertexStyle[mxConstants.ROUNDED] = true; - * var edgeStyle = stylesheet.getDefaultEdgeStyle(); - * edgeStyle[mxConstants.STYLE_EDGE] = mxEdgeStyle.EntityRelation; - * (end) - * - * Modifies the built-in default styles. - * - * To avoid the default style for a cell, add a leading semicolon - * to the style definition, eg. - * - * (code) - * ;shadow=1 - * (end) - * - * Removing keys: - * - * For removing a key in a cell style of the form [stylename;|key=value;] the - * special value none can be used, eg. highlight;fillColor=none - * - * See also the helper methods in mxUtils to modify strings of this format, - * namely , , - * , , - * and . - * - * Constructor: mxStylesheet - * - * Constructs a new stylesheet and assigns default styles. - */ -function mxStylesheet() -{ - this.styles = new Object(); - - this.putDefaultVertexStyle(this.createDefaultVertexStyle()); - this.putDefaultEdgeStyle(this.createDefaultEdgeStyle()); -}; - -/** - * Function: styles - * - * Maps from names to cell styles. Each cell style is a map of key, - * value pairs. - */ -mxStylesheet.prototype.styles; - -/** - * Function: createDefaultVertexStyle - * - * Creates and returns the default vertex style. - */ -mxStylesheet.prototype.createDefaultVertexStyle = function() -{ - var style = new Object(); - - style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_RECTANGLE; - style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter; - style[mxConstants.STYLE_VERTICAL_ALIGN] = mxConstants.ALIGN_MIDDLE; - style[mxConstants.STYLE_ALIGN] = mxConstants.ALIGN_CENTER; - style[mxConstants.STYLE_FILLCOLOR] = '#C3D9FF'; - style[mxConstants.STYLE_STROKECOLOR] = '#6482B9'; - style[mxConstants.STYLE_FONTCOLOR] = '#774400'; - - return style; -}; - -/** - * Function: createDefaultEdgeStyle - * - * Creates and returns the default edge style. - */ -mxStylesheet.prototype.createDefaultEdgeStyle = function() -{ - var style = new Object(); - - style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_CONNECTOR; - style[mxConstants.STYLE_ENDARROW] = mxConstants.ARROW_CLASSIC; - style[mxConstants.STYLE_VERTICAL_ALIGN] = mxConstants.ALIGN_MIDDLE; - style[mxConstants.STYLE_ALIGN] = mxConstants.ALIGN_CENTER; - style[mxConstants.STYLE_STROKECOLOR] = '#6482B9'; - style[mxConstants.STYLE_FONTCOLOR] = '#446299'; - - return style; -}; - -/** - * Function: putDefaultVertexStyle - * - * Sets the default style for vertices using defaultVertex as the - * stylename. - * - * Parameters: - * style - Key, value pairs that define the style. - */ -mxStylesheet.prototype.putDefaultVertexStyle = function(style) -{ - this.putCellStyle('defaultVertex', style); -}; - -/** - * Function: putDefaultEdgeStyle - * - * Sets the default style for edges using defaultEdge as the stylename. - */ -mxStylesheet.prototype.putDefaultEdgeStyle = function(style) -{ - this.putCellStyle('defaultEdge', style); -}; - -/** - * Function: getDefaultVertexStyle - * - * Returns the default style for vertices. - */ -mxStylesheet.prototype.getDefaultVertexStyle = function() -{ - return this.styles['defaultVertex']; -}; - -/** - * Function: getDefaultEdgeStyle - * - * Sets the default style for edges. - */ -mxStylesheet.prototype.getDefaultEdgeStyle = function() -{ - return this.styles['defaultEdge']; -}; - -/** - * Function: putCellStyle - * - * Stores the given map of key, value pairs under the given name in - * . - * - * Example: - * - * The following example adds a new style called 'rounded' into an - * existing stylesheet: - * - * (code) - * var style = new Object(); - * style[mxConstants.STYLE_SHAPE] = mxConstants.SHAPE_RECTANGLE; - * style[mxConstants.STYLE_PERIMETER] = mxPerimeter.RectanglePerimeter; - * style[mxConstants.STYLE_ROUNDED] = true; - * graph.getStylesheet().putCellStyle('rounded', style); - * (end) - * - * In the above example, the new style is an object. The possible keys of - * the object are all the constants in that start with STYLE - * and the values are either JavaScript objects, such as - * (which is in fact a function) - * or expressions, such as true. Note that not all keys will be - * interpreted by all shapes (eg. the line shape ignores the fill color). - * The final call to this method associates the style with a name in the - * stylesheet. The style is used in a cell with the following code: - * - * (code) - * model.setStyle(cell, 'rounded'); - * (end) - * - * Parameters: - * - * name - Name for the style to be stored. - * style - Key, value pairs that define the style. - */ -mxStylesheet.prototype.putCellStyle = function(name, style) -{ - this.styles[name] = style; -}; - -/** - * Function: getCellStyle - * - * Returns the cell style for the specified stylename or the given - * defaultStyle if no style can be found for the given stylename. - * - * Parameters: - * - * name - String of the form [(stylename|key=value);] that represents the - * style. - * defaultStyle - Default style to be returned if no style can be found. - */ -mxStylesheet.prototype.getCellStyle = function(name, defaultStyle) -{ - var style = defaultStyle; - - if (name != null && name.length > 0) - { - var pairs = name.split(';'); - - if (style != null && - name.charAt(0) != ';') - { - style = mxUtils.clone(style); - } - else - { - style = new Object(); - } - - // Parses each key, value pair into the existing style - for (var i = 0; i < pairs.length; i++) - { - var tmp = pairs[i]; - var pos = tmp.indexOf('='); - - if (pos >= 0) - { - var key = tmp.substring(0, pos); - var value = tmp.substring(pos + 1); - - if (value == mxConstants.NONE) - { - delete style[key]; - } - else if (mxUtils.isNumeric(value)) - { - style[key] = parseFloat(value); - } - else - { - style[key] = value; - } - } - else - { - // Merges the entries from a named style - var tmpStyle = this.styles[tmp]; - - if (tmpStyle != null) - { - for (var key in tmpStyle) - { - style[key] = tmpStyle[key]; - } - } - } - } - } - - return style; -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -/** - * Class: mxCellState - * - * Represents the current state of a cell in a given . - * - * For edges, the edge label position is stored in . - * - * The size for oversize labels can be retrieved using the boundingBox property - * of the field as shown below. - * - * (code) - * var bbox = (state.text != null) ? state.text.boundingBox : null; - * (end) - * - * Constructor: mxCellState - * - * Constructs a new object that represents the current state of the given - * cell in the specified view. - * - * Parameters: - * - * view - that contains the state. - * cell - that this state represents. - * style - Array of key, value pairs that constitute the style. - */ -function mxCellState(view, cell, style) -{ - this.view = view; - this.cell = cell; - this.style = style; - - this.origin = new mxPoint(); - this.absoluteOffset = new mxPoint(); -}; - -/** - * Extends mxRectangle. - */ -mxCellState.prototype = new mxRectangle(); -mxCellState.prototype.constructor = mxCellState; - -/** - * Variable: view - * - * Reference to the enclosing . - */ -mxCellState.prototype.view = null; - -/** - * Variable: cell - * - * Reference to the that is represented by this state. - */ -mxCellState.prototype.cell = null; - -/** - * Variable: style - * - * Contains an array of key, value pairs that represent the style of the - * cell. - */ -mxCellState.prototype.style = null; - -/** - * Variable: invalid - * - * Specifies if the state is invalid. Default is true. - */ -mxCellState.prototype.invalid = true; - -/** - * Variable: origin - * - * that holds the origin for all child cells. Default is a new - * empty . - */ -mxCellState.prototype.origin = null; - -/** - * Variable: absolutePoints - * - * Holds an array of that represent the absolute points of an - * edge. - */ -mxCellState.prototype.absolutePoints = null; - -/** - * Variable: absoluteOffset - * - * that holds the absolute offset. For edges, this is the - * absolute coordinates of the label position. For vertices, this is the - * offset of the label relative to the top, left corner of the vertex. - */ -mxCellState.prototype.absoluteOffset = null; - -/** - * Variable: visibleSourceState - * - * Caches the visible source terminal state. - */ -mxCellState.prototype.visibleSourceState = null; - -/** - * Variable: visibleTargetState - * - * Caches the visible target terminal state. - */ -mxCellState.prototype.visibleTargetState = null; - -/** - * Variable: terminalDistance - * - * Caches the distance between the end points for an edge. - */ -mxCellState.prototype.terminalDistance = 0; - -/** - * Variable: length - * - * Caches the length of an edge. - */ -mxCellState.prototype.length = 0; - -/** - * Variable: segments - * - * Array of numbers that represent the cached length of each segment of the - * edge. - */ -mxCellState.prototype.segments = null; - -/** - * Variable: shape - * - * Holds the that represents the cell graphically. - */ -mxCellState.prototype.shape = null; - -/** - * Variable: text - * - * Holds the that represents the label of the cell. Thi smay be - * null if the cell has no label. - */ -mxCellState.prototype.text = null; - -/** - * Variable: unscaledWidth - * - * Holds the unscaled width of the state. - */ -mxCellState.prototype.unscaledWidth = null; - -/** - * Function: getPerimeterBounds - * - * Returns the that should be used as the perimeter of the - * cell. - * - * Parameters: - * - * border - Optional border to be added around the perimeter bounds. - * bounds - Optional to be used as the initial bounds. - */ -mxCellState.prototype.getPerimeterBounds = function(border, bounds) -{ - border = border || 0; - bounds = (bounds != null) ? bounds : new mxRectangle(this.x, this.y, this.width, this.height); - - if (this.shape != null && this.shape.stencil != null && this.shape.stencil.aspect == 'fixed') - { - var aspect = this.shape.stencil.computeAspect(this.style, bounds.x, bounds.y, bounds.width, bounds.height); - - bounds.x = aspect.x; - bounds.y = aspect.y; - bounds.width = this.shape.stencil.w0 * aspect.width; - bounds.height = this.shape.stencil.h0 * aspect.height; - } - - if (border != 0) - { - bounds.grow(border); - } - - return bounds; -}; - -/** - * Function: setAbsoluteTerminalPoint - * - * Sets the first or last point in depending on isSource. - * - * Parameters: - * - * point - that represents the terminal point. - * isSource - Boolean that specifies if the first or last point should - * be assigned. - */ -mxCellState.prototype.setAbsoluteTerminalPoint = function(point, isSource) -{ - if (isSource) - { - if (this.absolutePoints == null) - { - this.absolutePoints = []; - } - - if (this.absolutePoints.length == 0) - { - this.absolutePoints.push(point); - } - else - { - this.absolutePoints[0] = point; - } - } - else - { - if (this.absolutePoints == null) - { - this.absolutePoints = []; - this.absolutePoints.push(null); - this.absolutePoints.push(point); - } - else if (this.absolutePoints.length == 1) - { - this.absolutePoints.push(point); - } - else - { - this.absolutePoints[this.absolutePoints.length - 1] = point; - } - } -}; - -/** - * Function: setCursor - * - * Sets the given cursor on the shape and text shape. - */ -mxCellState.prototype.setCursor = function(cursor) -{ - if (this.shape != null) - { - this.shape.setCursor(cursor); - } - - if (this.text != null) - { - this.text.setCursor(cursor); - } -}; - -/** - * Function: getVisibleTerminal - * - * Returns the visible source or target terminal cell. - * - * Parameters: - * - * source - Boolean that specifies if the source or target cell should be - * returned. - */ -mxCellState.prototype.getVisibleTerminal = function(source) -{ - var tmp = this.getVisibleTerminalState(source); - - return (tmp != null) ? tmp.cell : null; -}; - -/** - * Function: getVisibleTerminalState - * - * Returns the visible source or target terminal state. - * - * Parameters: - * - * source - Boolean that specifies if the source or target state should be - * returned. - */ -mxCellState.prototype.getVisibleTerminalState = function(source) -{ - return (source) ? this.visibleSourceState : this.visibleTargetState; -}; - -/** - * Function: setVisibleTerminalState - * - * Sets the visible source or target terminal state. - * - * Parameters: - * - * terminalState - that represents the terminal. - * source - Boolean that specifies if the source or target state should be set. - */ -mxCellState.prototype.setVisibleTerminalState = function(terminalState, source) -{ - if (source) - { - this.visibleSourceState = terminalState; - } - else - { - this.visibleTargetState = terminalState; - } -}; - -/** - * Function: getCellBounds - * - * Returns the unscaled, untranslated bounds. - */ -mxCellState.prototype.getCellBounds = function() -{ - return this.cellBounds; -}; - -/** - * Function: getPaintBounds - * - * Returns the unscaled, untranslated paint bounds. This is the same as - * but with a 90 degree rotation if the shape's - * isPaintBoundsInverted returns true. - */ -mxCellState.prototype.getPaintBounds = function() -{ - return this.paintBounds; -}; - -/** - * Function: updateCachedBounds - * - * Updates the cellBounds and paintBounds. - */ -mxCellState.prototype.updateCachedBounds = function() -{ - var tr = this.view.translate; - var s = this.view.scale; - this.cellBounds = new mxRectangle(this.x / s - tr.x, this.y / s - tr.y, this.width / s, this.height / s); - this.paintBounds = mxRectangle.fromRectangle(this.cellBounds); - - if (this.shape != null && this.shape.isPaintBoundsInverted()) - { - this.paintBounds.rotate90(); - } -}; - -/** - * Destructor: setState - * - * Copies all fields from the given state to this state. - */ -mxCellState.prototype.setState = function(state) -{ - this.view = state.view; - this.cell = state.cell; - this.style = state.style; - this.absolutePoints = state.absolutePoints; - this.origin = state.origin; - this.absoluteOffset = state.absoluteOffset; - this.boundingBox = state.boundingBox; - this.terminalDistance = state.terminalDistance; - this.segments = state.segments; - this.length = state.length; - this.x = state.x; - this.y = state.y; - this.width = state.width; - this.height = state.height; - this.unscaledWidth = state.unscaledWidth; -}; - -/** - * Function: clone - * - * Returns a clone of this . - */ -mxCellState.prototype.clone = function() -{ - var clone = new mxCellState(this.view, this.cell, this.style); - - // Clones the absolute points - if (this.absolutePoints != null) - { - clone.absolutePoints = []; - - for (var i = 0; i < this.absolutePoints.length; i++) - { - clone.absolutePoints[i] = this.absolutePoints[i].clone(); - } - } - - if (this.origin != null) - { - clone.origin = this.origin.clone(); - } - - if (this.absoluteOffset != null) - { - clone.absoluteOffset = this.absoluteOffset.clone(); - } - - if (this.boundingBox != null) - { - clone.boundingBox = this.boundingBox.clone(); - } - - clone.terminalDistance = this.terminalDistance; - clone.segments = this.segments; - clone.length = this.length; - clone.x = this.x; - clone.y = this.y; - clone.width = this.width; - clone.height = this.height; - clone.unscaledWidth = this.unscaledWidth; - - return clone; -}; - -/** - * Destructor: destroy - * - * Destroys the state and all associated resources. - */ -mxCellState.prototype.destroy = function() -{ - this.view.graph.cellRenderer.destroy(this); -}; -/** - * Copyright (c) 2006-2015, JGraph Ltd - * Copyright (c) 2006-2015, Gaudenz Alder - */ -/** - * Class: mxGraphSelectionModel - * - * Implements the selection model for a graph. Here is a listener that handles - * all removed selection cells. - * - * (code) - * graph.getSelectionModel().addListener(mxEvent.CHANGE, function(sender, evt) - * { - * var cells = evt.getProperty('added'); - * - * for (var i = 0; i < cells.length; i++) - * { - * // Handle cells[i]... - * } - * }); - * (end) - * - * Event: mxEvent.UNDO - * - * Fires after the selection was changed in . The - * edit property contains the which contains the - * . - * - * Event: mxEvent.CHANGE - * - * Fires after the selection changes by executing an . The - * added and removed properties contain arrays of - * cells that have been added to or removed from the selection, respectively. - * The names are inverted due to historic reasons. This cannot be changed. - * - * Constructor: mxGraphSelectionModel - * - * Constructs a new graph selection model for the given . - * - * Parameters: - * - * graph - Reference to the enclosing . - */ -function mxGraphSelectionModel(graph) -{ - this.graph = graph; - this.cells = []; -}; - -/** - * Extends mxEventSource. - */ -mxGraphSelectionModel.prototype = new mxEventSource(); -mxGraphSelectionModel.prototype.constructor = mxGraphSelectionModel; - -/** - * Variable: doneResource - * - * Specifies the resource key for the status message after a long operation. - * If the resource for this key does not exist then the value is used as - * the status message. Default is 'done'. - */ -mxGraphSelectionModel.prototype.doneResource = (mxClient.language != 'none') ? 'done' : ''; - -/** - * Variable: updatingSelectionResource - * - * Specifies the resource key for the status message while the selection is - * being updated. If the resource for this key does not exist then the - * value is used as the status message. Default is 'updatingSelection'. - */ -mxGraphSelectionModel.prototype.updatingSelectionResource = (mxClient.language != 'none') ? 'updatingSelection' : ''; - -/** - * Variable: graph - * - * Reference to the enclosing . - */ -mxGraphSelectionModel.prototype.graph = null; - -/** - * Variable: singleSelection - * - * Specifies if only one selected item at a time is allowed. - * Default is false. - */ -mxGraphSelectionModel.prototype.singleSelection = false; - -/** - * Function: isSingleSelection - * - * Returns as a boolean. - */ -mxGraphSelectionModel.prototype.isSingleSelection = function() -{ - return this.singleSelection; -}; - -/** - * Function: setSingleSelection - * - * Sets the flag. - * - * Parameters: - * - * singleSelection - Boolean that specifies the new value for - * . - */ -mxGraphSelectionModel.prototype.setSingleSelection = function(singleSelection) -{ - this.singleSelection = singleSelection; -}; - -/** - * Function: isSelected - * - * Returns true if the given is selected. - */ -mxGraphSelectionModel.prototype.isSelected = function(cell) -{ - if (cell != null) - { - return mxUtils.indexOf(this.cells, cell) >= 0; - } - - return false; -}; - -/** - * Function: isEmpty - * - * Returns true if no cells are currently selected. - */ -mxGraphSelectionModel.prototype.isEmpty = function() -{ - return this.cells.length == 0; -}; - -/** - * Function: clear - * - * Clears the selection and fires a event if the selection was not - * empty. - */ -mxGraphSelectionModel.prototype.clear = function() -{ - this.changeSelection(null, this.cells); -}; - -/** - * Function: setCell - * - * Selects the specified using . - * - * Parameters: - * - * cell - to be selected. - */ -mxGraphSelectionModel.prototype.setCell = function(cell) -{ - if (cell != null) - { - this.setCells([cell]); - } -}; - -/** - * Function: setCells - * - * Selects the given array of and fires a event. - * - * Parameters: - * - * cells - Array of to be selected. - */ -mxGraphSelectionModel.prototype.setCells = function(cells) -{ - if (cells != null) - { - if (this.singleSelection) - { - cells = [this.getFirstSelectableCell(cells)]; - } - - var tmp = []; - - for (var i = 0; i < cells.length; i++) - { - if (this.graph.isCellSelectable(cells[i])) - { - tmp.push(cells[i]); - } - } - - this.changeSelection(tmp, this.cells); - } -}; - -/** - * Function: getFirstSelectableCell - * - * Returns the first selectable cell in the given array of cells. - */ -mxGraphSelectionModel.prototype.getFirstSelectableCell = function(cells) -{ - if (cells != null) - { - for (var i = 0; i < cells.length; i++) - { - if (this.graph.isCellSelectable(cells[i])) - { - return cells[i]; - } - } - } - - return null; -}; - -/** - * Function: addCell - * - * Adds the given to the selection and fires a - * event. - * - * Parameters: - * - * cells - Array of to add to the selection. - */ -mxGraphSelectionModel.prototype.addCells = function(cells) -{ - if (cells != null) - { - var remove = null; - - if (this.singleSelection) - { - remove = this.cells; - cells = [this.getFirstSelectableCell(cells)]; - } - - var tmp = []; - - for (var i = 0; i < cells.length; i++) - { - if (!this.isSelected(cells[i]) && - this.graph.isCellSelectable(cells[i])) - { - tmp.push(cells[i]); - } - } - - this.changeSelection(tmp, remove); - } -}; - -/** - * Function: removeCell - * - * Removes the specified from the selection and fires a -
- -
- - - diff --git a/seahub/templates/view_file_draw.html b/seahub/templates/view_file_draw.html deleted file mode 100644 index 820dad837b..0000000000 --- a/seahub/templates/view_file_draw.html +++ /dev/null @@ -1,166 +0,0 @@ -{% load seahub_tags i18n staticfiles %} -{% load render_bundle from webpack_loader %} - - - - -{{doc_title}} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -{% render_bundle 'draw' %} - - - - - - diff --git a/seahub/templates/view_file_draw_read.html b/seahub/templates/view_file_draw_read.html deleted file mode 100644 index c39cd37e0d..0000000000 --- a/seahub/templates/view_file_draw_read.html +++ /dev/null @@ -1,130 +0,0 @@ -{% load seahub_tags i18n staticfiles %} -{% load render_bundle from webpack_loader %} - - - - -{{ site_title }} - - - - - - - - - - - - - - - - - - - - - - - -
-
- - -{% render_bundle 'draw' %} - - - diff --git a/seahub/utils/__init__.py b/seahub/utils/__init__.py index 03a14902d1..253088b0c6 100644 --- a/seahub/utils/__init__.py +++ b/seahub/utils/__init__.py @@ -127,7 +127,6 @@ PREVIEW_FILEEXT = { DOCUMENT: ('doc', 'docx', 'ppt', 'pptx', 'odt', 'fodt', 'odp', 'fodp'), SPREADSHEET: ('xls', 'xlsx', 'ods', 'fods'), SVG: ('svg',), - DRAW: ('draw',), PDF: ('pdf', 'ai'), MARKDOWN: ('markdown', 'md'), VIDEO: ('mp4', 'ogv', 'webm', 'mov'), diff --git a/seahub/utils/file_types.py b/seahub/utils/file_types.py index b46e2deca4..6f3f0ced03 100644 --- a/seahub/utils/file_types.py +++ b/seahub/utils/file_types.py @@ -8,6 +8,5 @@ MARKDOWN = 'Markdown' VIDEO = 'Video' AUDIO = 'Audio' SPREADSHEET = 'SpreadSheet' -DRAW = 'Draw' XMIND = 'XMind' CDOC = 'cdoc' diff --git a/seahub/views/file.py b/seahub/views/file.py index 7be76c983f..f2877dbc7b 100644 --- a/seahub/views/file.py +++ b/seahub/views/file.py @@ -63,7 +63,7 @@ from seahub.utils import render_error, is_org_context, \ from seahub.utils.ip import get_remote_ip from seahub.utils.timeutils import utc_to_local from seahub.utils.file_types import (IMAGE, PDF, SVG, - DOCUMENT, SPREADSHEET, AUDIO, MARKDOWN, TEXT, VIDEO, DRAW, XMIND, CDOC) + DOCUMENT, SPREADSHEET, AUDIO, MARKDOWN, TEXT, VIDEO, XMIND, CDOC) from seahub.utils.star import is_file_starred from seahub.utils.http import json_response, \ BadRequestException, RequestForbbiddenException @@ -332,16 +332,13 @@ def can_preview_file(file_name, file_size, repo): filetype, fileext = get_file_type_and_ext(file_name) # Seafile defines 10 kinds of filetype: - # TEXT, MARKDOWN, IMAGE, DOCUMENT, SPREADSHEET, VIDEO, AUDIO, PDF, SVG, DRAW + # TEXT, MARKDOWN, IMAGE, DOCUMENT, SPREADSHEET, VIDEO, AUDIO, PDF, SVG if filetype in (TEXT, MARKDOWN, IMAGE) or fileext in get_conf_text_ext(): if file_size > FILE_PREVIEW_MAX_SIZE: error_msg = _('File size surpasses %s, can not be opened online.') % \ filesizeformat(FILE_PREVIEW_MAX_SIZE) return False, error_msg - elif filetype in (DRAW): - pass - elif filetype in (DOCUMENT, SPREADSHEET): if repo.encrypted: @@ -725,15 +722,6 @@ def view_lib_file(request, repo_id, path): send_file_access_msg(request, repo, path, 'web') return render(request, template, return_dict) - elif filetype == DRAW: - return_dict['raw_path'] = raw_path - if permission == 'r': - template = 'view_file_draw_read.html' - return render(request, template, return_dict) - else: - template = 'view_file_%s.html' % filetype.lower() - return render(request, template, return_dict) - elif filetype == XMIND: xmind_image_path = get_thumbnail_image_path(file_id, XMIND_IMAGE_SIZE) if not os.path.exists(xmind_image_path) and not extract_xmind_image(repo_id, path)[0]: