pos)
{
result = f.substring(pos, end);
break;
}
}
// Creates table for lookup if no inline data is found
if (b == 10)
{
if (curline == 'endobj')
{
buf = null;
}
else if (curline.substring(curline.length - 3, curline.length) == 'obj' ||
curline == 'xref' || curline == 'trailer')
{
buf = [];
obj[curline.split(' ')[0]] = buf;
}
else if (buf != null)
{
buf.push(curline);
}
curline = '';
}
}
}
// Extract XML via references
if (result == null && obj != null)
{
result = Editor.extractGraphModelFromXref(obj);
}
if (result != null)
{
result = decodeURIComponent(result.
replace(/\\\(/g, "(").
replace(/\\\)/g, ")"));
}
return result;
};
/**
* Static method for extracting Subject via references of the form
*
* << /Size 33 /Root 20 0 R /Info 1 0 R and 1 0 obj << /Subject 22 0 R
*
* Where Info is the metadata block and Subject is the data block.
*/
Editor.extractGraphModelFromXref = function(obj)
{
var trailer = obj['trailer'];
var result = null;
// Gets Info object
if (trailer != null)
{
var arr = /.* \/Info (\d+) (\d+) R/g.exec(trailer.join('\n'));
if (arr != null && arr.length > 0)
{
var info = obj[arr[1]];
if (info != null)
{
arr = /.* \/Subject (\d+) (\d+) R/g.exec(info.join('\n'));
if (arr != null && arr.length > 0)
{
var subj = obj[arr[1]];
if (subj != null)
{
subj = subj.join('\n');
result = subj.substring(1, subj.length - 1);
}
}
}
}
}
return result;
};
/**
* Extracts any parsers errors in the given XML.
*/
Editor.extractParserError = function(node, defaultCause)
{
var cause = null;
var errors = (node != null) ? node.getElementsByTagName('parsererror') : null;
if (errors != null && errors.length > 0)
{
cause = defaultCause || mxResources.get('invalidChars');
var divs = errors[0].getElementsByTagName('div');
if (divs.length > 0)
{
cause = mxUtils.getTextContent(divs[0]);
}
}
return (cause != null) ? mxUtils.trim(cause) : cause;
};
/**
* Adds the given retry function to the given error.
*/
Editor.addRetryToError = function(err, retry)
{
if (err != null)
{
var e = (err.error != null) ? err.error : err;
if (e.retry == null)
{
e.retry = retry;
}
}
};
/**
*
* Hook for mermaid to draw.io converter.
*/
Editor.mermaidToDrawio = function(graph, diagramtype, exta)
{
if (typeof mxMermaidToDrawio === 'function')
{
return mxMermaidToDrawio(graph, diagramtype, exta);
}
};
/**
* Global configuration of the Editor
* see https://www.drawio.com/doc/faq/configure-diagram-editor
*
* For defaultVertexStyle, defaultEdgeStyle and defaultLibraries, this must be called before
* mxSettings.load via global config variable window.mxLoadSettings = false.
*/
Editor.configure = function(config)
{
if (config != null)
{
Editor.config = config;
Editor.configVersion = config.version;
// Enables debug output
if (config.debug)
{
urlParams['test'] = '1'
}
if (config.customCss != null)
{
var style = document.createElement('style');
style.type = 'text/css';
style.appendChild(document.createTextNode(config.customCss));
document.head.appendChild(style);
}
if (config.defaultFonts != null)
{
Menus.prototype.defaultFonts = config.defaultFonts
}
if (config.presetColors != null)
{
ColorDialog.prototype.presetColors = config.presetColors
}
if (config.defaultColors != null)
{
ColorDialog.prototype.defaultColors = config.defaultColors
}
if (config.colorNames != null)
{
ColorDialog.prototype.colorNames = config.colorNames
}
if (config.defaultColorSchemes != null)
{
StyleFormatPanel.prototype.defaultColorSchemes = config.defaultColorSchemes
}
if (config.defaultEdgeLength != null)
{
Graph.prototype.defaultEdgeLength = config.defaultEdgeLength
}
if (config.selectParentLayer != null)
{
Graph.selectParentLayer = config.selectParentLayer
}
if (config.autosaveDelay != null)
{
DrawioFile.prototype.autosaveDelay = config.autosaveDelay
}
if (config.templateFile != null)
{
EditorUi.templateFile = config.templateFile;
}
if (config.styles != null)
{
if (Array.isArray(config.styles))
{
Editor.styles = config.styles;
}
else
{
EditorUi.debug('Configuration Error: Array expected for styles');
}
}
if (config.globalVars != null)
{
Editor.globalVars = config.globalVars;
}
if (config.compressXml != null)
{
Editor.defaultCompressed = config.compressXml;
Editor.compressXml = config.compressXml;
}
if (config.includeDiagram != null)
{
Editor.defaultIncludeDiagram = config.includeDiagram;
}
if (config.simpleLabels != null)
{
Editor.simpleLabels = config.simpleLabels;
}
if (config.pasteAtMousePointer != null)
{
Editor.pasteAtMousePointer = config.pasteAtMousePointer;
}
if (config.oneDriveInlinePicker != null)
{
Editor.oneDriveInlinePicker = config.oneDriveInlinePicker;
}
if (config.defaultAdaptiveColors != null)
{
Graph.defaultAdaptiveColors = config.defaultAdaptiveColors;
}
else if (config.enableCssDarkMode == false)
{
// Backwards compatibility
Graph.defaultAdaptiveColors = 'simple';
}
if (config.enableLightDarkColors != null)
{
mxUtils.lightDarkColorSupported = config.enableLightDarkColors;
}
if (config.darkColor != null)
{
Editor.darkColor = config.darkColor;
}
if (config.darkColorVar != null)
{
Editor.darkColorVar = config.darkColorVar;
}
// Updates colors that depend on Editor.darkColor
// LATER: Add event to update darkColor dependencies
Graph.prototype.defaultPageBackgroundColor = 'light-dark(#ffffff, ' + Editor.darkColor + ')';
Graph.prototype.shapeBackgroundColor = 'light-dark(#ffffff, var(' +
Editor.darkColorVar + ', ' + Editor.darkColor + '))';
if (config.settingsName != null)
{
Editor.configurationKey = '.' + config.settingsName + '-configuration';
Editor.settingsKey = '.' + config.settingsName + '-config';
mxSettings.key = Editor.settingsKey;
}
if (config.customFonts != null)
{
Menus.prototype.defaultFonts = config.customFonts.
concat(Menus.prototype.defaultFonts);
}
if (config.customPresetColors != null)
{
ColorDialog.prototype.presetColors = config.customPresetColors.
concat(ColorDialog.prototype.presetColors);
}
if (config.customColorSchemes != null)
{
StyleFormatPanel.prototype.defaultColorSchemes = config.customColorSchemes.
concat(StyleFormatPanel.prototype.defaultColorSchemes);
}
// Custom CSS injected directly into the page
if (config.css != null)
{
var s = document.createElement('style');
s.setAttribute('type', 'text/css');
s.appendChild(document.createTextNode(config.css));
var t = document.getElementsByTagName('script')[0];
t.parentNode.insertBefore(s, t);
}
if (config.expandLibraries != null)
{
Sidebar.prototype.expandLibraries = config.expandLibraries;
}
if (config.appendCustomLibraries != null)
{
Sidebar.prototype.appendCustomLibraries = config.appendCustomLibraries;
}
// Configures the custom libraries
if (config.libraries != null)
{
Sidebar.prototype.customEntries = config.libraries;
}
// Defines the enabled built-in libraries.
if (config.enabledLibraries != null)
{
if (Array.isArray(config.enabledLibraries))
{
Sidebar.prototype.enabledLibraries = config.enabledLibraries;
}
else
{
EditorUi.debug('Configuration Error: Array expected for enabledLibraries');
}
}
// Overrides default libraries
if (config.defaultLibraries != null)
{
Sidebar.prototype.defaultEntries = config.defaultLibraries;
}
// Overrides default custom libraries
if (config.defaultCustomLibraries != null)
{
Editor.defaultCustomLibraries = config.defaultCustomLibraries;
}
// Disables custom libraries
if (config.enableCustomLibraries != null)
{
Editor.enableCustomLibraries = config.enableCustomLibraries;
}
// Overrides default vertex style
if (config.defaultVertexStyle != null)
{
Graph.prototype.defaultVertexStyle = config.defaultVertexStyle;
}
// Overrides default edge style
if (config.defaultEdgeStyle != null)
{
Graph.prototype.defaultEdgeStyle = config.defaultEdgeStyle;
}
// Overrides default page visible
if (config.defaultPageVisible != null)
{
Graph.prototype.defaultPageVisible = config.defaultPageVisible;
}
// Overrides default grid enabled
if (config.defaultGridEnabled != null)
{
Graph.prototype.defaultGridEnabled = config.defaultGridEnabled;
}
// Overrides mouse wheel function
if (config.zoomWheel != null)
{
Graph.zoomWheel = config.zoomWheel;
}
// Overrides zoom factor
if (config.zoomFactor != null)
{
var val = parseFloat(config.zoomFactor);
if (!isNaN(val) && val > 1)
{
Graph.prototype.zoomFactor = val;
}
else
{
EditorUi.debug('Configuration Error: Float > 1 expected for zoomFactor');
}
}
// Overrides default grid size
if (config.defaultGridSize != null)
{
var val = parseInt(config.defaultGridSize);
if (!isNaN(val) && val > 0)
{
mxGraph.prototype.gridSize = val;
}
else
{
EditorUi.debug('Configuration Error: Int > 0 expected for defaultGridSize');
}
}
// Overrides grid steps
if (config.gridSteps != null)
{
var val = parseInt(config.gridSteps);
if (!isNaN(val) && val > 0)
{
mxGraphView.prototype.gridSteps = val;
}
else
{
EditorUi.debug('Configuration Error: Int > 0 expected for gridSteps');
}
}
if (config.pageFormat != null)
{
var w = parseInt(config.pageFormat.width);
var h = parseInt(config.pageFormat.height);
if (!isNaN(w) && w > 0 && !isNaN(h) && h > 0)
{
mxGraph.prototype.defaultPageFormat = new mxRectangle(0, 0, w, h);
mxGraph.prototype.pageFormat = mxGraph.prototype.defaultPageFormat;
}
else
{
EditorUi.debug('Configuration Error: {width: int, height: int} expected for pageFormat');
}
}
if (config.thumbWidth != null)
{
Sidebar.prototype.thumbWidth = config.thumbWidth;
}
if (config.thumbHeight != null)
{
Sidebar.prototype.thumbHeight = config.thumbHeight;
}
if (config.emptyLibraryXml != null)
{
EditorUi.prototype.emptyLibraryXml = config.emptyLibraryXml;
}
if (config.emptyDiagramXml != null)
{
EditorUi.prototype.emptyDiagramXml = config.emptyDiagramXml;
}
if (config.sidebarWidth != null)
{
EditorUi.prototype.hsplitPosition = config.sidebarWidth;
}
if (config.updateDefaultStyle != null)
{
EditorUi.prototype.updateDefaultStyle = config.updateDefaultStyle;
}
if (config.sidebarTitles != null)
{
Sidebar.prototype.sidebarTitles = config.sidebarTitles;
}
if (config.sidebarTitleSize != null)
{
var val = parseInt(config.sidebarTitleSize);
if (!isNaN(val) && val > 0)
{
Sidebar.prototype.sidebarTitleSize = val;
}
else
{
EditorUi.debug('Configuration Error: Int > 0 expected for sidebarTitleSize');
}
}
if (config.fontCss != null)
{
if (typeof config.fontCss === 'string')
{
Editor.configureFontCss(config.fontCss);
}
else
{
EditorUi.debug('Configuration Error: String expected for fontCss');
}
}
if (config.autosaveDelay != null)
{
var val = parseInt(config.autosaveDelay);
if (!isNaN(val) && val > 0)
{
DrawioFile.prototype.autosaveDelay = val;
}
else
{
EditorUi.debug('Configuration Error: Int > 0 expected for autosaveDelay');
}
}
if(config.maxImageBytes != null)
{
EditorUi.prototype.maxImageBytes = config.maxImageBytes;
}
if(config.maxImageSize != null)
{
EditorUi.prototype.maxImageSize = config.maxImageSize;
}
if (config.shareCursorPosition != null)
{
EditorUi.prototype.shareCursorPosition = config.shareCursorPosition;
}
if (config.showRemoteCursors != null)
{
EditorUi.prototype.showRemoteCursors = config.showRemoteCursors;
}
if (config.restrictExport != null)
{
DrawioFile.RESTRICT_EXPORT = config.restrictExport;
}
if (config.replaceSvgDataUris != null)
{
Editor.replaceSvgDataUris = config.replaceSvgDataUris;
}
if (config.foreignObjectImages != null)
{
Editor.foreignObjectImages = config.foreignObjectImages;
}
if (config.shadowColor != null)
{
mxConstants.SHADOW_COLOR = config.shadowColor;
}
if (config.shadowOpacity != null)
{
mxConstants.SHADOW_OPACITY = config.shadowOpacity;
}
if (config.shadowOffsetX != null)
{
mxConstants.SHADOW_OFFSET_X = config.shadowOffsetX;
}
if (config.shadowOffsetY != null)
{
mxConstants.SHADOW_OFFSET_Y = config.shadowOffsetY;
}
if (config.shadowBlur != null)
{
mxConstants.SHADOW_BLUR = config.shadowBlur;
}
if (config.enableAnimations != null)
{
Editor.enableAnimations = config.enableAnimations;
}
if (config.enableChatGpt != null)
{
Editor.enableChatGpt = config.enableChatGpt;
}
if (config.gptApiKey != null)
{
Editor.gptApiKey = config.gptApiKey;
}
if (config.gptModel != null)
{
Editor.gptModel = config.gptModel;
}
if (config.gptUrl != null)
{
Editor.gptUrl = config.gptUrl;
}
}
};
/**
*
*/
Editor.isSettingsEnabled = function()
{
return typeof window.mxSettings !== 'undefined' && (isLocalStorage || mxClient.IS_CHROMEAPP);
};
/**
* Adds the global fontCss configuration.
*/
Editor.configureFontCss = function(fontCss)
{
if (fontCss != null)
{
Editor.prototype.fontCss = fontCss;
var t = document.getElementsByTagName('script')[0];
if (t != null && t.parentNode != null)
{
var s = document.createElement('style');
s.setAttribute('type', 'text/css');
s.appendChild(document.createTextNode(fontCss));
t.parentNode.insertBefore(s, t);
// Preloads fonts where supported
var parts = fontCss.split('url(');
for (var i = 1; i < parts.length; i++)
{
var idx = parts[i].indexOf(')');
var url = Editor.trimCssUrl(parts[i].substring(0, idx));
var l = document.createElement('link');
l.setAttribute('rel', 'preload');
l.setAttribute('href', url);
l.setAttribute('as', 'font');
l.setAttribute('crossorigin', '');
t.parentNode.insertBefore(l, t);
}
}
}
};
/**
* Strips leading and trailing quotes and spaces
*/
Editor.trimCssUrl = function(str)
{
return str.replace(new RegExp("^[\\s\"']+", "g"), "").replace(new RegExp("[\\s\"']+$", "g"), "");
}
/**
* Prefix for URLs that reference Google fonts.
*/
Editor.GOOGLE_FONTS = 'https://fonts.googleapis.com/css?family=';
/**
* Prefix for URLs that reference Google fonts with CSS2.
*/
Editor.GOOGLE_FONTS_CSS2 = 'https://fonts.googleapis.com/css2?family=';
/**
* Alphabet for global unique IDs.
*/
Editor.GUID_ALPHABET = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-_';
/**
* Default length for global unique IDs.
*/
Editor.GUID_LENGTH = 20;
/**
* Default length for global unique IDs.
*/
Editor.guid = function(length)
{
var len = (length != null) ? length : Editor.GUID_LENGTH;
var rtn = [];
for (var i = 0; i < len; i++)
{
rtn.push(Editor.GUID_ALPHABET.charAt(Math.floor(Math.random() * Editor.GUID_ALPHABET.length)));
}
return rtn.join('');
};
/**
* Interval for updating the file status.
*/
Editor.updateStatusInterval = 10000;
/**
* Specifies the app name. Default is document.title.
*/
Editor.prototype.appName = 'draw.io';
/**
* Known file types.
*/
Editor.prototype.diagramFileTypes = [
{description: 'diagramXmlDesc', extension: 'drawio', mimeType: 'text/xml'},
{description: 'diagramPngDesc', extension: 'png', mimeType: 'image/png'},
{description: 'diagramSvgDesc', extension: 'svg', mimeType: 'image/svg'},
{description: 'diagramHtmlDesc', extension: 'html', mimeType: 'text/html'}];
/**
* Known file types.
*/
Editor.prototype.libraryFileTypes = [{description: 'Library (.drawiolib, .xml)', extensions: ['drawiolib', 'xml']}];
/**
* Additional help text for special file extensions.
*/
Editor.prototype.fileExtensions = [
{ext: 'html', title: 'filetypeHtml'},
{ext: 'png', title: 'filetypePng'},
{ext: 'svg', title: 'filetypeSvg'}];
/**
* General timeout is 25 seconds.
*/
Editor.prototype.timeout = 25000;
/**
* Executes the first step for connecting to Google Drive.
*/
Editor.prototype.editButtonLink = (urlParams['edit'] != null) ? decodeURIComponent(urlParams['edit']) : null;
/**
* Specifies if img.crossOrigin is supported. This is true for all browsers except IE10 and earlier.
*/
Editor.prototype.crossOriginImages = !mxClient.IS_IE;
/**
* Adds support for old stylesheets and compressed files
*/
var editorSetGraphXml = Editor.prototype.setGraphXml;
Editor.prototype.setGraphXml = function(node)
{
node = (node != null && node.nodeName != 'mxlibrary') ? this.extractGraphModel(node) : null;
if (node != null)
{
// Checks for parser errors
var cause = Editor.extractParserError(node, mxResources.get('invalidOrMissingFile'));
if (cause)
{
EditorUi.debug('Editor.setGraphXml ParserError', [this],
'node', [node], 'cause', [cause]);
throw new Error(mxResources.get('notADiagramFile') + ' (' + cause + ')');
}
else if (node.nodeName == 'mxGraphModel')
{
var style = node.getAttribute('style') || 'default-style2';
// Decodes the style if required
if (urlParams['embed'] != '1' && (style == null || style == ''))
{
var node2 = (this.graph.themes != null) ?
this.graph.themes['default-old'] :
mxUtils.load(STYLE_PATH + '/default-old.xml').getDocumentElement();
if (node2 != null)
{
var dec2 = new mxCodec(node2.ownerDocument);
dec2.decode(node2, this.graph.getStylesheet());
}
}
else if (style != this.graph.currentStyle)
{
var node2 = (this.graph.themes != null) ?
this.graph.themes[style] :
mxUtils.load(STYLE_PATH + '/' + style + '.xml').getDocumentElement()
if (node2 != null)
{
var dec2 = new mxCodec(node2.ownerDocument);
dec2.decode(node2, this.graph.getStylesheet());
}
}
this.graph.currentStyle = style;
this.graph.mathEnabled = (urlParams['math'] == '1' || node.getAttribute('math') == '1');
this.graph.setAdaptiveColors(node.getAttribute('adaptiveColors'));
var bgImg = node.getAttribute('backgroundImage');
if (bgImg != null)
{
this.graph.setBackgroundImage(this.graph.parseBackgroundImage(bgImg));
}
else
{
this.graph.setBackgroundImage(null);
}
this.graph.useCssTransforms = !mxClient.NO_FO &&
this.isChromelessView() &&
this.graph.isCssTransformsSupported();
this.graph.updateCssTransform();
this.graph.setShadowVisible(node.getAttribute('shadow') == '1', false);
var extFonts = node.getAttribute('extFonts');
if (extFonts)
{
try
{
extFonts = extFonts.split('|').map(function(ef)
{
var parts = ef.split('^');
return {name: parts[0], url: parts[1]};
});
for (var i = 0; i < extFonts.length; i++)
{
this.graph.addExtFont(extFonts[i].name, extFonts[i].url);
}
}
catch(e)
{
console.log('ExtFonts format error: ' + e.message);
}
}
else if (this.graph.extFonts != null && this.graph.extFonts.length > 0)
{
this.graph.extFonts = [];
}
}
// Calls updateGraphComponents
editorSetGraphXml.apply(this, arguments);
}
else
{
throw {
message: mxResources.get('notADiagramFile') || 'Invalid data',
toString: function() { return this.message; }
};
}
};
/**
* Adds persistent style to file
*/
var editorGetGraphXml = Editor.prototype.getGraphXml;
Editor.prototype.getGraphXml = function(ignoreSelection, resolveReferences)
{
ignoreSelection = (ignoreSelection != null) ? ignoreSelection : true;
var node = editorGetGraphXml.apply(this, arguments);
// Adds the current style
if (this.graph.currentStyle != null && this.graph.currentStyle != 'default-style2')
{
node.setAttribute('style', this.graph.currentStyle);
}
var bgImg = this.graph.getBackgroundImageObject(
this.graph.backgroundImage,
resolveReferences);
// Adds the background image
if (bgImg != null)
{
node.setAttribute('backgroundImage', JSON.stringify(bgImg));
}
node.setAttribute('math', (this.graph.mathEnabled) ? '1' : '0');
node.setAttribute('shadow', (this.graph.shadowVisible) ? '1' : '0');
if (this.graph.adaptiveColors != null)
{
node.setAttribute('adaptiveColors', this.graph.adaptiveColors);
}
else
{
node.removeAttribute('adaptiveColors');
}
if (this.graph.extFonts != null && this.graph.extFonts.length > 0)
{
var strExtFonts = this.graph.extFonts.map(function(ef)
{
return ef.name + '^' + ef.url;
});
node.setAttribute('extFonts', strExtFonts.join('|'));
}
return node;
};
/**
* Helper function to extract the graph model XML node.
*/
Editor.prototype.isDataSvg = function(svg)
{
try
{
var svgRoot = mxUtils.parseXml(svg).documentElement;
var tmp = svgRoot.getAttribute('content');
if (tmp != null)
{
if (tmp != null && tmp.charAt(0) != '<' && tmp.charAt(0) != '%')
{
tmp = unescape((window.atob) ? atob(tmp) : Base64.decode(cont, tmp));
}
if (tmp != null && tmp.charAt(0) == '%')
{
tmp = decodeURIComponent(tmp);
}
if (tmp != null && tmp.length > 0)
{
var node = mxUtils.parseXml(tmp).documentElement;
return node.nodeName == 'mxfile' || node.nodeName == 'mxGraphModel';
}
}
}
catch (e)
{
// ignore
}
return false;
};
/**
* Helper function to extract the graph model XML node.
*/
Editor.prototype.extractGraphModel = function(node, allowMxFile, checked)
{
return Editor.extractGraphModel.apply(this, arguments);
};
/**
* Overrides reset graph.
*/
var editorResetGraph = Editor.prototype.resetGraph;
Editor.prototype.resetGraph = function()
{
this.graph.mathEnabled = (urlParams['math'] == '1');
this.graph.setAdaptiveColors(null);
this.graph.view.x0 = null;
this.graph.view.y0 = null;
this.graph.useCssTransforms = !mxClient.NO_FO &&
this.isChromelessView() &&
this.graph.isCssTransformsSupported();
this.graph.updateCssTransform();
editorResetGraph.apply(this, arguments);
};
/**
* Math support.
*/
var editorUpdateGraphComponents = Editor.prototype.updateGraphComponents;
Editor.prototype.updateGraphComponents = function()
{
editorUpdateGraphComponents.apply(this, arguments);
this.graph.useCssTransforms = !mxClient.NO_FO &&
this.isChromelessView() &&
this.graph.isCssTransformsSupported();
this.graph.updateCssTransform();
};
/**
* Initializes math typesetting and loads respective code.
*/
Editor.initMath = function(src, config)
{
if (typeof window.MathJax === 'undefined' && !mxClient.IS_IE && !mxClient.IS_IE11)
{
src = (src != null) ? src : DRAW_MATH_URL + '/startup.js';
Editor.mathJaxQueue = [];
// Blocks concurrent rendering while
// async rendering is in progress
var rendering = null;
function mathJaxDone()
{
rendering = null;
if (Editor.mathJaxQueue.length > 0)
{
Editor.doMathJaxRender(Editor.mathJaxQueue.shift());
}
else
{
Editor.onMathJaxDone();
}
};
Editor.doMathJaxRender = function(container)
{
try
{
if (rendering == null)
{
MathJax.typesetClear([container]);
MathJax.typeset([container]);
mathJaxDone();
}
else if (rendering != container)
{
Editor.mathJaxQueue.push(container);
}
}
catch (e)
{
MathJax.typesetClear([container]);
if (e.retry != null)
{
rendering = container;
e.retry.then(function()
{
MathJax.typesetPromise([container]).then(mathJaxDone)['catch'](function(e)
{
console.log('Error in MathJax.typesetPromise: ' + e.toString());
mathJaxDone();
});
})['catch'](function(e)
{
console.log('Error in MathJax.retry: ' + e.toString());
mathJaxDone();
});;
}
else if (window.console != null)
{
console.log('Error in MathJax.typeset: ' + e.toString());
}
}
};
window.MathJax = (config != null) ? config :
{
options:
{
skipHtmlTags: {'[+]': ['text']},
ignoreHtmlClass: 'geDisableMathJax'
},
loader:
{
load: [(urlParams['math-output'] == 'html') ?
'output/chtml' : 'output/svg', 'input/tex',
'input/asciimath', 'ui/safe']
},
startup:
{
pageReady: function()
{
for (var i = 0; i < Editor.mathJaxQueue.length; i++)
{
Editor.doMathJaxRender(Editor.mathJaxQueue[i]);
}
}
}
};
// Adds global enqueue method for async rendering
Editor.MathJaxRender = function(container)
{
if (typeof MathJax !== 'undefined' && typeof MathJax.typeset === 'function')
{
Editor.doMathJaxRender(container);
}
else
{
Editor.mathJaxQueue.push(container);
}
};
// Adds global MathJax render callback
Editor.onMathJaxDone = function()
{
// Hook for listeners
};
// Updates math typesetting after changes
var editorInit = Editor.prototype.init;
Editor.prototype.init = function()
{
editorInit.apply(this, arguments);
var renderMath = mxUtils.bind(this, function(sender, evt)
{
if (this.graph.container != null &&
this.graph.mathEnabled)
{
Editor.MathJaxRender(this.graph.container);
}
});
this.graph.model.addListener(mxEvent.CHANGE, renderMath);
this.graph.addListener(mxEvent.REFRESH, renderMath);
};
var tags = document.getElementsByTagName('script');
if (tags != null && tags.length > 0)
{
var s = document.createElement('script');
s.setAttribute('type', 'text/javascript');
s.setAttribute('src', src);
tags[0].parentNode.appendChild(s);
}
}
};
/**
* Parses line of CSV values according to RFC 4180.
*/
Editor.prototype.csvToArray = function(text)
{
if (text.length > 0)
{
var p = '', row = [''], i = 0, s = !0, l;
for (l of text)
{
if ('"' === l)
{
if (s && l === p)
{
row[i] += l;
}
s = !s;
}
else if (',' === l && s)
{
l = row[++i] = '';
}
else
{
row[i] += l;
}
p = l;
}
return row;
}
else
{
return [];
}
};
/**
* Returns an URL that is proxied if the given URL is blocked. Uses
* direct URL if no CSP is used as proxy blocks unknown text content.
*/
Editor.prototype.getProxiedUrl = function(url)
{
if ((/test\.draw\.io$/.test(window.location.hostname) ||
/app\.diagrams\.net$/.test(window.location.hostname)) &&
!this.isCorsEnabledForUrl(url))
{
var isVisioFilename = EditorUi.isVisioFilename(url);
var binary = /\.png$/i.test(url) || /\.pdf$/i.test(url);
var base64 = binary || isVisioFilename;
var nocache = 't=' + new Date().getTime();
url = PROXY_URL + '?url=' + encodeURIComponent(url) +
'&' + nocache + ((base64) ? '&base64=1' : '');
}
return url;
};
/**
* Returns true if the given URL is known to have CORS headers and is
* allowed by CSP.
*/
Editor.prototype.isCorsEnabledForUrl = function(url)
{
// Disables proxy for desktop and chrome app as it is served locally
if (mxClient.IS_CHROMEAPP || EditorUi.isElectronApp)
{
return true;
}
// Does not use proxy for same domain
if (url.substring(0, window.location.origin.length) == window.location.origin)
{
return true;
}
// Blocked by CSP in production but allowed for hosted deployment
if (urlParams['cors'] != null && this.corsRegExp == null)
{
this.corsRegExp = new RegExp(decodeURIComponent(urlParams['cors']));
}
// No access-control-allow-origin for some Iconfinder images, add this when fixed:
// /^https?:\/\/[^\/]*\.iconfinder.com\//.test(url) ||
return (this.corsRegExp != null && this.corsRegExp.test(url)) ||
url.substring(0, 34) === 'https://raw.githubusercontent.com/' ||
url.substring(0, 29) === 'https://fonts.googleapis.com/' ||
url.substring(0, 26) === 'https://fonts.gstatic.com/';
};
/**
* Converts all images in the SVG output to data URIs for immediate rendering
*/
Editor.prototype.createImageUrlConverter = function()
{
var converter = new mxUrlConverter();
converter.updateBaseUrl();
// Extends convert to avoid CORS using an image proxy server where needed
var convert = converter.convert;
var self = this;
converter.convert = function(src)
{
if (src != null && navigator.onLine)
{
var remote = src.substring(0, 7) == 'http://' || src.substring(0, 8) == 'https://';
if (remote && src.substring(0, converter.baseUrl.length) != converter.baseUrl &&
(!self.crossOriginImages || !self.isCorsEnabledForUrl(src)))
{
src = PROXY_URL + '?url=' + encodeURIComponent(src);
}
else if (src.substring(0, 19) != 'chrome-extension://')
{
src = convert.apply(this, arguments);
}
}
return src;
};
return converter;
};
/**
*
*/
Editor.createSvgDataUri = function(svg)
{
return 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svg)));
};
/**
*
*/
Editor.prototype.convertImageToDataUri = function(url, callback, error, convertScale, forceConvert)
{
try
{
var acceptResponse = true;
var timeoutThread = window.setTimeout(mxUtils.bind(this, function()
{
acceptResponse = false;
callback(url);
}), this.timeout);
// Fallback to raster image if SVG cannot be loaded
var svgError = mxUtils.bind(this, function()
{
if (convertScale != null)
{
this.convertImageToDataUri(url, callback, error, convertScale, true);
}
else
{
callback(url);
}
});
if (/(\.svg)$/i.test(url) && !forceConvert)
{
mxUtils.get(url, mxUtils.bind(this, function(req)
{
window.clearTimeout(timeoutThread);
if (acceptResponse)
{
if (req.getStatus() < 200 || req.getStatus() > 299)
{
svgError();
}
else
{
callback(Editor.createSvgDataUri(req.getText()));
}
}
}),
mxUtils.bind(this, function()
{
window.clearTimeout(timeoutThread);
if (acceptResponse)
{
svgError();
}
}));
}
else
{
var img = new Image();
if (this.crossOriginImages)
{
img.crossOrigin = 'anonymous';
}
img.onload = function()
{
window.clearTimeout(timeoutThread);
if (acceptResponse)
{
try
{
convertScale = (convertScale != null &&
forceConvert) ? convertScale : 1;
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
ctx.scale(convertScale, convertScale);
canvas.height = img.height * convertScale;
canvas.width = img.width * convertScale;
ctx.drawImage(img, 0, 0);
callback(canvas.toDataURL());
}
catch (e)
{
callback(url);
}
}
};
img.onerror = function()
{
window.clearTimeout(timeoutThread);
if (acceptResponse)
{
if (error != null)
{
error();
}
else
{
callback(url);
}
}
};
img.src = url;
}
}
catch (e)
{
if (error != null)
{
error();
}
else
{
callback(url);
}
}
};
/**
* Converts all images in the SVG output to data URIs for immediate rendering
*/
Editor.prototype.convertImages = function(svgRoot, callback, imageCache, converter)
{
// Converts images to data URLs for immediate painting
if (converter == null)
{
converter = this.createImageUrlConverter();
}
// Queues image conversion and executes in order
var pending = [];
function next()
{
if (pending.length > 0)
{
pending.shift()();
}
else
{
callback(svgRoot);
}
};
var cache = imageCache || new Object();
var convertImages = mxUtils.bind(this, function(tagName, srcAttr)
{
var images = svgRoot.getElementsByTagName(tagName);
for (var i = 0; i < images.length; i++)
{
(mxUtils.bind(this, function(img)
{
pending.push(mxUtils.bind(this, function()
{
try
{
if (img != null)
{
var src = converter.convert(img.getAttribute(srcAttr));
// Data URIs are pass-through
if (src != null && src.substring(0, 5) != 'data:')
{
var tmp = cache[src];
if (tmp == null)
{
this.convertImageToDataUri(src, function(uri)
{
if (uri != null)
{
cache[src] = uri;
img.setAttribute(srcAttr, uri);
}
next();
}, null, Editor.svgRasterScale);
}
else
{
img.setAttribute(srcAttr, tmp);
next();
}
}
else
{
if (src != null)
{
img.setAttribute(srcAttr, src);
}
next();
}
}
}
catch (e)
{
next();
}
}));
}))(images[i]);
}
});
// Converts all known image tags in output
// LATER: Add support for images in CSS
convertImages('image', 'xlink:href');
convertImages('img', 'src');
next();
};
/**
* Base64 encodes the given string. This method seems to be more
* robust for encoding PNG from binary AJAX responses.
*/
Editor.base64Encode = function(str)
{
var CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var out = "", i = 0, len = str.length, c1, c2, c3;
while (i < len)
{
c1 = str.charCodeAt(i++) & 0xff;
if (i == len)
{
out += CHARS.charAt(c1 >> 2);
out += CHARS.charAt((c1 & 0x3) << 4);
out += "==";
break;
}
c2 = str.charCodeAt(i++);
if (i == len)
{
out += CHARS.charAt(c1 >> 2);
out += CHARS.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
out += CHARS.charAt((c2 & 0xF) << 2);
out += "=";
break;
}
c3 = str.charCodeAt(i++);
out += CHARS.charAt(c1 >> 2);
out += CHARS.charAt(((c1 & 0x3) << 4) | ((c2 & 0xF0) >> 4));
out += CHARS.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >> 6));
out += CHARS.charAt(c3 & 0x3F);
}
return out;
};
/**
* Checks if the client is authorized and calls the next step.
*/
Editor.prototype.loadUrl = function(url, success, error, forceBinary, retry, dataUriPrefix, noBinary, headers)
{
try
{
var binary = !noBinary && (forceBinary || /(\.png)($|\?)/i.test(url) ||
/(\.jpe?g)($|\?)/i.test(url) || /(\.gif)($|\?)/i.test(url) ||
/(\.pdf)($|\?)/i.test(url));
retry = (retry != null) ? retry : true;
var fn = mxUtils.bind(this, function()
{
mxUtils.get(url, mxUtils.bind(this, function(req)
{
if (req.getStatus() >= 200 && req.getStatus() <= 299)
{
if (success != null)
{
var data = req.getText();
// Returns PNG as base64 encoded data URI
if (binary)
{
// LATER: Could be JPG but modern browsers
// ignore the mime type in the data URI
dataUriPrefix = (dataUriPrefix != null) ?
dataUriPrefix : 'data:image/png;base64,';
data = dataUriPrefix + Editor.base64Encode(data);
}
success(data);
}
}
else if (error != null)
{
if (req.getStatus() == 0)
{
// Handles CORS errors
error({message: mxResources.get('accessDenied')}, req);
}
else if (req.getStatus() == 404)
{
error({message: mxResources.get('fileNotFound'),
code: req.getStatus()}, req);
}
else
{
error({message: this.getErrorMessage(req)}, req);
}
}
}), function(req)
{
if (error != null)
{
error({message: mxResources.get('error') + ' ' + req.getStatus()});
}
}, binary, this.timeout, function()
{
if (retry && error != null)
{
error({code: App.ERROR_TIMEOUT, retry: fn});
}
}, headers);
});
fn();
}
catch (e)
{
if (error != null)
{
error(e);
}
}
};
/**
* Adds the listener for automatically saving the diagram for local changes.
*/
Editor.prototype.getErrorMessage = function(req)
{
var msg = mxResources.get('error') + ' ' + req.getStatus();
try
{
var data = req.getText();
var obj = JSON.parse(data);
if (obj != null && obj.error != null &&
obj.error.message != null)
{
msg = obj.error.message + ' (' + msg + ')';
}
}
catch (e)
{
// ignore
}
return msg;
};
/**
* Makes all relative font URLs absolute in the given font CSS.
*/
Editor.prototype.absoluteCssFonts = function(fontCss)
{
var result = null;
if (fontCss != null)
{
var parts = fontCss.split('url(');
if (parts.length > 0)
{
result = [parts[0]];
// Gets path for URL
var path = window.location.pathname;
var idx = (path != null) ? path.lastIndexOf('/') : -1;
if (idx >= 0)
{
path = path.substring(0, idx + 1);
}
// Gets base tag from head
var temp = document.getElementsByTagName('base');
var base = null;
if (temp != null && temp.length > 0)
{
base = temp[0].getAttribute('href');
}
for (var i = 1; i < parts.length; i++)
{
var idx = parts[i].indexOf(')');
if (idx > 0)
{
var url = Editor.trimCssUrl(parts[i].substring(0, idx));
if (this.graph.isRelativeUrl(url))
{
url = (base != null) ? base + url : (window.location.protocol + '//' + window.location.hostname +
((url.charAt(0) == '/') ? '' : path) + url);
}
result.push('url("' + url + '"' + parts[i].substring(idx));
}
else
{
result.push(parts[i]);
}
}
}
else
{
result = [fontCss]
}
}
return (result != null) ? result.join('') : null;
};
/**
* Returns the URL and mime type to be used for the given font.
*/
Editor.prototype.mapFontUrl = function(mime, url, fn)
{
if ((/^https?:\/\//.test(url)) && !this.isCorsEnabledForUrl(url))
{
url = PROXY_URL + '?url=' + encodeURIComponent(url);
}
fn(mime, url);
};
/**
* For the fonts in CSS to be applied when rendering images on canvas, the actual
* font data must be made available via a data URI encoding of the file.
*/
Editor.prototype.embedCssFonts = function(fontCss, then)
{
var parts = fontCss.split('url(');
var waiting = 0;
if (this.cachedFonts == null)
{
this.cachedFonts = {};
}
var finish = mxUtils.bind(this, function()
{
if (waiting == 0)
{
// Constructs string
var result = [parts[0]];
for (var j = 1; j < parts.length; j++)
{
var idx = parts[j].indexOf(')');
result.push('url("');
result.push(this.cachedFonts[Editor.trimCssUrl(parts[j].substring(0, idx))]);
result.push('"' + parts[j].substring(idx));
}
then(result.join(''));
}
});
if (parts.length > 0)
{
for (var i = 1; i < parts.length; i++)
{
var idx = parts[i].indexOf(')');
var format = null;
// Checks if there is a format directive
var fmtIdx = parts[i].indexOf('format(', idx);
if (fmtIdx > 0)
{
format = Editor.trimCssUrl(parts[i].substring(fmtIdx + 7, parts[i].indexOf(')', fmtIdx)));
}
(mxUtils.bind(this, function(url)
{
if (this.cachedFonts[url] == null)
{
// Mark font as being fetched and fetch it
this.cachedFonts[url] = url;
waiting++;
var mime = 'application/x-font-ttf';
// See https://stackoverflow.com/questions/2871655/proper-mime-type-for-fonts
if (format == 'svg' || /(\.svg)($|\?)/i.test(url))
{
mime = 'image/svg+xml';
}
else if (format == 'otf' || format == 'embedded-opentype' || /(\.otf)($|\?)/i.test(url))
{
mime = 'application/x-font-opentype';
}
else if (format == 'woff' || /(\.woff)($|\?)/i.test(url))
{
mime = 'application/font-woff';
}
else if (format == 'woff2' || /(\.woff2)($|\?)/i.test(url))
{
mime = 'application/font-woff2';
}
else if (format == 'eot' || /(\.eot)($|\?)/i.test(url))
{
mime = 'application/vnd.ms-fontobject';
}
else if (format == 'sfnt' || /(\.sfnt)($|\?)/i.test(url))
{
mime = 'application/font-sfnt';
}
this.mapFontUrl(mime, url, mxUtils.bind(this, function(realMime, realUrl)
{
// LATER: Remove cache-control header
this.loadUrl(realUrl, mxUtils.bind(this, function(uri)
{
this.cachedFonts[url] = uri;
waiting--;
finish();
}), mxUtils.bind(this, function(err)
{
// LATER: handle error
waiting--;
finish();
}), true, null, 'data:' + realMime + ';charset=utf-8;base64,');
}));
}
}))(Editor.trimCssUrl(parts[i].substring(0, idx)), format);
}
//In case all fonts are cached
finish();
}
else
{
//No font urls found
then(fontCss);
}
};
/**
* For the fontCSS to be applied when rendering images on canvas, the actual
* font data must be made available via a data URI encoding of the file.
*/
Editor.prototype.loadFonts = function(then)
{
if (this.fontCss != null && this.resolvedFontCss == null)
{
this.embedCssFonts(this.fontCss, mxUtils.bind(this, function(resolvedFontCss)
{
this.resolvedFontCss = resolvedFontCss;
if (then != null)
{
then();
}
}));
}
else if (then != null)
{
then();
}
};
/**
* Returns a CSS mapping for the given CSS URL.
*/
Editor.prototype.createGoogleFontCache = function()
{
var cache = {};
for (var key in Graph.fontMapping)
{
if (Graph.isCssFontUrl(key))
{
cache[key] = Graph.fontMapping[key];
}
}
return cache;
};
/**
* Embeds external fonts
*/
Editor.prototype.embedExtFonts = function(callback)
{
var extFonts = this.graph.getCustomFonts();
if (extFonts.length > 0)
{
var content = [];
var waiting = 0;
if (this.cachedGoogleFonts == null)
{
this.cachedGoogleFonts = this.createGoogleFontCache();
}
var googleCssDone = mxUtils.bind(this, function()
{
if (waiting == 0)
{
this.embedCssFonts(content.join(''), callback);
}
});
for (var i = 0; i < extFonts.length; i++)
{
(mxUtils.bind(this, function(fontName, fontUrl)
{
if (Graph.isCssFontUrl(fontUrl))
{
if (this.cachedGoogleFonts[fontUrl] == null)
{
waiting++;
this.loadUrl(Graph.rewriteGoogleFontUrl(fontUrl), mxUtils.bind(this, function(css)
{
this.cachedGoogleFonts[fontUrl] = css;
content.push(css + '\n');
waiting--;
googleCssDone();
}), mxUtils.bind(this, function(err)
{
waiting--;
content.push('@import url(' + fontUrl + ');\n');
googleCssDone();
}));
}
else
{
content.push(this.cachedGoogleFonts[fontUrl] + '\n');
}
}
else
{
content.push('@font-face {' +
'font-family: "' + fontName + '";' +
'src: url("' + fontUrl + '")}\n');
}
}))(extFonts[i].name, extFonts[i].url);
}
googleCssDone();
}
else
{
callback();
}
};
/**
* Copies MathJax CSS into the SVG output.
*/
Editor.prototype.addMathCss = function(svgRoot)
{
var defs = svgRoot.getElementsByTagName('defs');
if (defs != null && defs.length > 0)
{
var styles = document.getElementsByTagName('style');
for (var i = 0; i < styles.length; i++)
{
// Ignores style elements with no MathJax CSS
var content = mxUtils.getTextContent(styles[i]);
if (content.indexOf('mxPageSelector') < 0 &&
content.indexOf('MathJax') > 0)
{
defs[0].appendChild(styles[i].cloneNode(true));
}
}
}
};
/**
* Adds the global fontCss configuration.
*/
Editor.prototype.addFontCss = function(svgRoot, fontCss)
{
fontCss = (fontCss != null) ? fontCss : this.absoluteCssFonts(this.fontCss);
// Creates defs element if not available
if (fontCss != null)
{
var defsElt = mxUtils.getSvgDefs(svgRoot);
// Moves imports to separate style element
var lines = fontCss.split('\n');
var imports = [];
var other = [];
for (var i = 0; i < lines.length; i++)
{
if (lines[i].substring(0, 7) == '@import')
{
imports.push(lines[i]);
}
else
{
other.push(lines[i]);
}
}
var svgDoc = svgRoot.ownerDocument;
var style = (svgDoc.createElementNS != null) ?
svgDoc.createElementNS(mxConstants.NS_SVG, 'style') :
svgDoc.createElement('style');
style.setAttribute('type', 'text/css');
if (imports.length > 0)
{
mxUtils.setTextContent(style, imports.join('\n'));
defsElt.appendChild(style);
}
if (other.length > 0)
{
style = style.cloneNode(false);
mxUtils.setTextContent(style, other.join('\n'));
defsElt.appendChild(style);
}
}
};
/**
* Disables client-side image export if math is enabled.
*/
Editor.prototype.isExportToCanvas = function()
{
return mxClient.IS_CHROMEAPP || Editor.useCanvasForExport;
};
/**
* Returns the maximum possible scale for the given canvas dimension and scale.
* This will return the given scale or the maximum scale that can be used to
* generate a valid image in the current browser.
*
* See https://developer.mozilla.org/en-US/docs/Web/HTML/Element/canvas
*/
Editor.prototype.getMaxCanvasScale = function(w, h, scale)
{
var max = (mxClient.IS_FF) ? 8192 : 16384;
return Math.min(scale, Math.min(max / w, max / h));
};
/**
*
*/
Editor.prototype.exportToCanvas = function(callback, width, imageCache, background, error, limitHeight,
ignoreSelection, scale, transparentBackground, addShadow, converter, graph, border, noCrop, grid,
theme, exportType, cells)
{
try
{
limitHeight = (limitHeight != null) ? limitHeight : true;
ignoreSelection = (ignoreSelection != null) ? ignoreSelection : true;
graph = (graph != null) ? graph : this.graph;
border = (border != null) ? border : 0;
var bg = (transparentBackground) ? null : graph.background;
if (bg == mxConstants.NONE)
{
bg = null;
}
if (bg == null)
{
bg = background;
}
// Handles special case where background is null but transparent is false
if (bg == null && transparentBackground == false)
{
bg = 'light-dark(#ffffff,' + Editor.darkColor + ')';
}
this.convertImages(graph.getSvg(null, null, border, noCrop, null, ignoreSelection,
null, null, null, addShadow, null, theme, exportType, cells),
mxUtils.bind(this, function(svgRoot)
{
try
{
var img = new Image();
img.onload = mxUtils.bind(this, function()
{
try
{
var canvas = document.createElement('canvas');
var w = parseInt(svgRoot.getAttribute('width'));
var h = parseInt(svgRoot.getAttribute('height'));
scale = (scale != null) ? scale : 1;
if (width != null)
{
scale = (!limitHeight) ? width / w : Math.min(1, Math.min((width * 3) / (h * 4), width / w));
}
scale = this.getMaxCanvasScale(w, h, scale);
w = Math.ceil(scale * w);
h = Math.ceil(scale * h);
canvas.setAttribute('width', w);
canvas.setAttribute('height', h);
var ctx = canvas.getContext('2d');
if (bg != null)
{
var cssBg = mxUtils.getLightDarkColor(bg);
ctx.beginPath();
ctx.rect(0, 0, w, h);
ctx.fillStyle = (theme == 'dark' &&
graph.getAdaptiveColors() != 'none') ?
cssBg.dark : cssBg.light;
ctx.fill();
}
if (scale != 1)
{
ctx.scale(scale, scale);
}
function drawImage()
{
// Workaround for broken data URI images in Safari on first export
if (mxClient.IS_SF)
{
window.setTimeout(function()
{
ctx.drawImage(img, 0, 0);
callback(canvas, svgRoot);
}, 0);
}
else
{
ctx.drawImage(img, 0, 0);
callback(canvas, svgRoot);
}
};
if (grid)
{
var view = graph.view;
var curViewScale = view.scale;
view.scale = 1; //Reset the scale temporary to generate unscaled grid image which is then scaled
var gridImage = btoa(unescape(encodeURIComponent(view.createSvgGrid(view.gridColor))));
view.scale = curViewScale;
gridImage = 'data:image/svg+xml;base64,' + gridImage;
var phase = graph.gridSize * view.gridSteps * scale;
var b = graph.getGraphBounds();
var tx = view.translate.x * curViewScale;
var ty = view.translate.y * curViewScale;
var x0 = tx + (b.x - tx) / curViewScale - border;
var y0 = ty + (b.y - ty) / curViewScale - border;
var background = new Image();
background.onload = function()
{
try
{
var x = -Math.round(phase - mxUtils.mod((tx - x0) * scale, phase));
var y = -Math.round(phase - mxUtils.mod((ty - y0) * scale, phase));
for (var i = x; i < w; i += phase)
{
for (var j = y; j < h; j += phase)
{
ctx.drawImage(background, i / scale, j / scale);
}
}
drawImage();
}
catch (e)
{
if (error != null)
{
error(e);
}
}
};
background.onerror = function(e)
{
if (error != null)
{
error(e);
}
};
background.src = gridImage;
}
else
{
drawImage();
}
}
catch (e)
{
if (error != null)
{
error(e);
}
}
});
img.onerror = function(e)
{
//console.log('img', e, img.src);
if (error != null)
{
error(e);
}
};
if (addShadow)
{
this.graph.addSvgShadow(svgRoot);
}
if (this.graph.mathEnabled)
{
this.addMathCss(svgRoot);
}
var done = mxUtils.bind(this, function()
{
try
{
if (this.resolvedFontCss != null)
{
this.addFontCss(svgRoot, this.resolvedFontCss);
}
img.src = Editor.createSvgDataUri(mxUtils.getXml(svgRoot));
}
catch (e)
{
if (error != null)
{
error(e);
}
}
});
this.embedExtFonts(mxUtils.bind(this, function(extFontsEmbeddedCss)
{
try
{
if (extFontsEmbeddedCss != null)
{
this.addFontCss(svgRoot, extFontsEmbeddedCss);
}
this.loadFonts(done);
}
catch (e)
{
if (error != null)
{
error(e);
}
}
}));
}
catch (e)
{
//console.log('src', e, img.src);
if (error != null)
{
error(e);
}
}
}), imageCache, converter);
}
catch (e)
{
if (error != null)
{
error(e);
}
}
};
Editor.crcTable = [];
for (var n = 0; n < 256; n++)
{
var c = n;
for (var k = 0; k < 8; k++)
{
if ((c & 1) == 1)
{
c = 0xedb88320 ^ (c >>> 1);
}
else
{
c >>>= 1;
}
Editor.crcTable[n] = c;
}
}
Editor.updateCRC = function(crc, data, off, len)
{
var c = crc;
for (var n = 0; n < len; n++)
{
c = Editor.crcTable[(c ^ data.charCodeAt(off + n)) & 0xff] ^ (c >>> 8);
}
return c;
};
Editor.crc32 = function(str)
{
var crc = 0 ^ (-1);
for (var i = 0; i < str.length; i++ )
{
crc = (crc >>> 8) ^ Editor.crcTable[(crc ^ str.charCodeAt(i)) & 0xFF];
}
return (crc ^ (-1)) >>> 0;
};
/**
* Adds the given text to the compressed or non-compressed text chunk.
*/
Editor.writeGraphModelToPng = function(data, type, key, value, error)
{
var base64 = data.substring(data.indexOf(',') + 1);
var f = (window.atob) ? atob(base64) : Base64.decode(base64, true);
var pos = 0;
function fread(d, count)
{
var start = pos;
pos += count;
return d.substring(start, pos);
};
// Reads unsigned long 32 bit big endian
function _freadint(d)
{
var bytes = fread(d, 4);
return bytes.charCodeAt(3) + (bytes.charCodeAt(2) << 8) +
(bytes.charCodeAt(1) << 16) + (bytes.charCodeAt(0) << 24);
};
function writeInt(num)
{
return String.fromCharCode((num >> 24) & 0x000000ff, (num >> 16) & 0x000000ff,
(num >> 8) & 0x000000ff, num & 0x000000ff);
};
// Checks signature
if (fread(f,8) != String.fromCharCode(137) + 'PNG' + String.fromCharCode(13, 10, 26, 10))
{
if (error != null)
{
error();
}
return;
}
// Reads header chunk
fread(f,4);
if (fread(f,4) != 'IHDR')
{
if (error != null)
{
error();
}
return;
}
fread(f, 17);
var result = f.substring(0, pos);
do
{
var n = _freadint(f);
var chunk = fread(f,4);
if (chunk == 'IDAT')
{
result = f.substring(0, pos - 8);
if (type == 'pHYs' && key == 'dpi')
{
var dpm = Math.round(value / 0.0254); //One inch is equal to exactly 0.0254 meters.
var chunkData = writeInt(dpm) + writeInt(dpm) + String.fromCharCode(1);
}
else
{
var chunkData = key + String.fromCharCode(0) +
((type == 'zTXt') ? String.fromCharCode(0) : '') +
value;
}
var crc = 0xffffffff;
crc = Editor.updateCRC(crc, type, 0, 4);
crc = Editor.updateCRC(crc, chunkData, 0, chunkData.length);
result += writeInt(chunkData.length) + type + chunkData + writeInt(crc ^ 0xffffffff);
result += f.substring(pos - 8, f.length);
break;
}
result += f.substring(pos - 8, pos - 4 + n);
fread(f,n);
fread(f,4);
}
while (n);
return 'data:image/png;base64,' + ((window.btoa) ? btoa(result) : Base64.encode(result, true));
};
/**
* Adds persistence for recent colors
*/
if (window.ColorDialog)
{
FilenameDialog.filenameHelpLink = 'https://www.drawio.com/doc/faq/save-file-formats';
var colorDialogAddRecentColor = ColorDialog.addRecentColor;
ColorDialog.addRecentColor = function(color, max)
{
colorDialogAddRecentColor.apply(this, arguments);
mxSettings.setRecentColors(ColorDialog.recentColors);
mxSettings.save();
};
var colorDialogResetRecentColors = ColorDialog.resetRecentColors;
ColorDialog.resetRecentColors = function()
{
colorDialogResetRecentColors.apply(this, arguments);
mxSettings.setRecentColors(ColorDialog.recentColors);
mxSettings.save();
};
}
// Overrides ID for pages
if (typeof window.EditDataDialog !== 'undefined')
{
EditDataDialog.getDisplayIdForCell = function(ui, cell)
{
var id = null;
if (ui.editor.graph.getModel().getParent(cell) != null)
{
id = cell.getId();
}
else if (ui.currentPage != null)
{
id = ui.currentPage.getId();
}
return id;
};
}
var AddCustomPropertyDialog = function(editorUi, callback)
{
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('name', null, 'Name') + ':');
row.appendChild(td);
var nameInput = document.createElement('input');
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('type', null, 'Type') + ':');
row.appendChild(td);
var typeSelect = document.createElement('select');
typeSelect.style.width = '180px';
var boolOption = document.createElement('option');
boolOption.setAttribute('value', 'bool');
mxUtils.write(boolOption, mxResources.get('bool', null, 'Boolean'));
typeSelect.appendChild(boolOption);
var clrOption = document.createElement('option');
clrOption.setAttribute('value', 'color');
mxUtils.write(clrOption, mxResources.get('color', null, 'Color'));
typeSelect.appendChild(clrOption);
var enumOption = document.createElement('option');
enumOption.setAttribute('value', 'enum');
mxUtils.write(enumOption, mxResources.get('enum', null, 'Enumeration'));
typeSelect.appendChild(enumOption);
var floatOption = document.createElement('option');
floatOption.setAttribute('value', 'float');
mxUtils.write(floatOption, mxResources.get('float', null, 'Float'));
typeSelect.appendChild(floatOption);
var intOption = document.createElement('option');
intOption.setAttribute('value', 'int');
mxUtils.write(intOption, mxResources.get('int', null, 'Int'));
typeSelect.appendChild(intOption);
var strOption = document.createElement('option');
strOption.setAttribute('value', 'string');
mxUtils.write(strOption, mxResources.get('string', null, 'String'));
typeSelect.appendChild(strOption);
td = document.createElement('td');
td.appendChild(typeSelect);
row.appendChild(td);
tbody.appendChild(row);
row = document.createElement('tr');
td = document.createElement('td');
td.style.fontSize = '10pt';
mxUtils.write(td, mxResources.get('dispName', null, 'Display Name') + ':');
row.appendChild(td);
var dispNameInput = document.createElement('input');
dispNameInput.style.width = '180px';
td = document.createElement('td');
td.appendChild(dispNameInput);
row.appendChild(td);
tbody.appendChild(row);
var listRow = document.createElement('tr');
td = document.createElement('td');
td.style.fontSize = '10pt';
mxUtils.write(td, mxResources.get('enumList', null, 'Enum List') + ' (csv):');
listRow.appendChild(td);
var enumListInput = document.createElement('input');
enumListInput.style.width = '180px';
td = document.createElement('td');
td.appendChild(enumListInput);
listRow.appendChild(td);
listRow.style.display = 'none';
tbody.appendChild(listRow);
table.appendChild(tbody);
function typeChanged()
{
if (typeSelect.value === 'enum')
{
listRow.style.display = '';
this.container.parentNode.style.height = "150px";
}
else
{
listRow.style.display = 'none';
this.container.parentNode.style.height = "130px";
}
};
mxEvent.addListener(typeSelect, 'change', mxUtils.bind(this, typeChanged));
row = document.createElement('tr');
td = document.createElement('td');
td.setAttribute('align', 'right');
td.style.paddingTop = '22px';
td.colSpan = 2;
var addBtn = mxUtils.button(mxResources.get('add', null, 'Add'), mxUtils.bind(this, function()
{
var name = nameInput.value;
if (name == "")
{
nameInput.style.border = "1px solid red";
return;
}
var type = typeSelect.value;
var dispName = dispNameInput.value;
if (dispName == "")
{
dispNameInput.style.border = "1px solid red";
return;
}
var enumList = enumListInput.value;
if (enumList == "" && type == "enum")
{
enumListInput.style.border = "1px solid red";
return;
}
if (enumList != null)
{
enumList = enumList.split(',');
for (var i = 0; i < enumList.length; i++)
{
enumList[i] = enumList[i].trim();
}
}
if (callback)
{
callback(editorUi, name, type, dispName, enumList);
editorUi.hideDialog();
}
}));
addBtn.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(addBtn);
}
else
{
td.appendChild(addBtn);
td.appendChild(cancelBtn);
}
row.appendChild(td);
tbody.appendChild(row);
table.appendChild(tbody);
this.container = table;
};
if (window.StyleFormatPanel != null)
{
var formatInit = Format.prototype.init;
Format.prototype.init = function()
{
formatInit.apply(this, arguments);
this.editorUi.editor.addListener('fileLoaded', this.update);
};
var formatRefresh = Format.prototype.refresh;
Format.prototype.refresh = function()
{
if (this.editorUi.getCurrentFile() != null || urlParams['embed'] == '1' ||
this.editorUi.editor.chromeless)
{
formatRefresh.apply(this, arguments);
}
else
{
this.clear();
}
};
/**
* Option is not visible in default theme.
*/
DiagramFormatPanel.prototype.isMathOptionVisible = function(div)
{
return (Editor.currentTheme == 'simple' ||
Editor.currentTheme == 'sketch' ||
Editor.currentTheme == 'min');
};
/**
* Adds autosave and math typesetting options.
*/
var diagramFormatPanelAddOptions = DiagramFormatPanel.prototype.addOptions;
DiagramFormatPanel.prototype.addOptions = function(div)
{
div = diagramFormatPanelAddOptions.apply(this, arguments);
var ui = this.editorUi;
var editor = ui.editor;
var graph = editor.graph;
// Adds autosave option
if (graph.isEnabled())
{
var file = ui.getCurrentFile();
if (file != null && file.isAutosaveOptional())
{
div.appendChild(this.createOption(mxResources.get('autosave'), function()
{
return ui.editor.autosave;
}, function(checked)
{
ui.editor.setAutosave(checked);
if (ui.editor.autosave && file.isModified())
{
file.fileChanged();
}
},
{
install: function(apply)
{
this.listener = function()
{
apply(ui.editor.autosave);
};
ui.editor.addListener('autosaveChanged', this.listener);
},
destroy: function()
{
ui.editor.removeListener(this.listener);
}
}));
}
}
// Adds math option
if (this.isMathOptionVisible() && graph.isEnabled() && typeof(MathJax) !== 'undefined')
{
var option = this.createOption(mxResources.get('mathematicalTypesetting'), function()
{
return graph.mathEnabled;
}, function(checked)
{
ui.actions.get('mathematicalTypesetting').funct();
},
{
install: function(apply)
{
this.listener = function()
{
apply(graph.mathEnabled);
};
ui.addListener('mathEnabledChanged', this.listener);
},
destroy: function()
{
ui.removeListener(this.listener);
}
});
div.appendChild(option);
if (!ui.isOffline() || mxClient.IS_CHROMEAPP || EditorUi.isElectronApp)
{
option.appendChild(ui.menus.createHelpLink(
'https://www.drawio.com/doc/faq/math-typesetting'));
}
}
return div;
};
mxCellRenderer.prototype.defaultVertexShape.prototype.customProperties = [
{name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE},
{name: 'absoluteArcSize', dispName: 'Abs. Arc Size', type: 'bool', defVal: false}
];
mxCellRenderer.defaultShapes['link'].prototype.customProperties = [
{name: 'width', dispName: 'Width', type: 'float', min:0, defVal: 4}
];
mxCellRenderer.defaultShapes['flexArrow'].prototype.customProperties = [
{name: 'width', dispName: 'Width', type: 'float', min:0, defVal: 10},
{name: 'startWidth', dispName: 'Start Width', type: 'float', min:0, defVal: 20},
{name: 'endWidth', dispName: 'End Width', type: 'float', min:0, defVal: 20}
];
mxCellRenderer.defaultShapes['process'].prototype.customProperties = [
{name: 'size', dispName: 'Indent', type: 'float', min: 0, max: 0.5, defVal: 0.1}
];
mxCellRenderer.defaultShapes['rhombus'].prototype.customProperties = [
{name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, max: 50, defVal: mxConstants.LINE_ARCSIZE},
{name: 'double', dispName: 'Double', type: 'bool', defVal: false}
];
mxCellRenderer.defaultShapes['partialRectangle'].prototype.customProperties = [
{name: 'top', dispName: 'Top Line', type: 'bool', defVal: true},
{name: 'bottom', dispName: 'Bottom Line', type: 'bool', defVal: true},
{name: 'left', dispName: 'Left Line', type: 'bool', defVal: true},
{name: 'right', dispName: 'Right Line', type: 'bool', defVal: true}
];
mxCellRenderer.defaultShapes['parallelogram'].prototype.customProperties = [
{name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE},
{name: 'size', dispName: 'Slope Angle', type: 'float', min:0, max: 1, defVal: 0.2, isVisible: function(state)
{
return mxUtils.getValue(state.style, 'fixedSize', '0') == '0';
}},
{name: 'size', dispName: 'Slope Angle', type: 'float', min:0, defVal: 20, isVisible: function(state)
{
return mxUtils.getValue(state.style, 'fixedSize', '0') == '1';
}}
];
mxCellRenderer.defaultShapes['hexagon'].prototype.customProperties = [
{name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE},
{name: 'size', dispName: 'Slope Angle', type: 'float', min:0, max: 1, defVal: 0.25, isVisible: function(state)
{
return mxUtils.getValue(state.style, 'fixedSize', '0') == '0';
}},
{name: 'size', dispName: 'Slope Angle', type: 'float', min:0, defVal: 25, isVisible: function(state)
{
return mxUtils.getValue(state.style, 'fixedSize', '0') == '1';
}}
];
mxCellRenderer.defaultShapes['triangle'].prototype.customProperties = [
{name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE}
];
mxCellRenderer.defaultShapes['document'].prototype.customProperties = [
{name: 'size', dispName: 'Size', type: 'float', defVal: 0.3, min:0, max:1}
];
mxCellRenderer.defaultShapes['internalStorage'].prototype.customProperties = [
{name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE},
{name: 'dx', dispName: 'Left Line', type: 'float', min:0, defVal: 20},
{name: 'dy', dispName: 'Top Line', type: 'float', min:0, defVal: 20}
];
mxCellRenderer.defaultShapes['cube'].prototype.customProperties = [
{name: 'size', dispName: 'Size', type: 'float', min:0, defVal:20 },
{name: 'darkOpacity', dispName: 'Dark Opacity', type: 'float', min:-1, max:1, defVal:0 },
{name: 'darkOpacity2', dispName: 'Dark Opacity 2', type: 'float', min:-1, max:1, defVal:0 }
];
mxCellRenderer.defaultShapes['step'].prototype.customProperties = [
{name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE},
{name: 'size', dispName: 'Notch Size', type: 'float', min:0, defVal:20},
{name: 'fixedSize', dispName: 'Fixed Size', type: 'bool', defVal:true}
];
mxCellRenderer.defaultShapes['trapezoid'].prototype.customProperties = [
{name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE},
{name: 'size', dispName: 'Slope Angle', type: 'float', min:0, max: 1, defVal: 0.2}
];
mxCellRenderer.defaultShapes['tape'].prototype.customProperties = [
{name: 'size', dispName: 'Size', type: 'float', min:0, max:1, defVal:0.4 }
];
mxCellRenderer.defaultShapes['note'].prototype.customProperties = [
{name: 'size', dispName: 'Fold Size', type: 'float', min:0, defVal: 30},
{name: 'darkOpacity', dispName: 'Dark Opacity', type: 'float', min:-1, max:1, defVal:0 },
];
mxCellRenderer.defaultShapes['card'].prototype.customProperties = [
{name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE},
{name: 'size', dispName: 'Cutoff Size', type: 'float', min:0, defVal: 30}
];
mxCellRenderer.defaultShapes['callout'].prototype.customProperties = [
{name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: mxConstants.LINE_ARCSIZE},
{name: 'base', dispName: 'Callout Width', type: 'float', min:0, defVal: 20},
{name: 'size', dispName: 'Callout Length', type: 'float', min:0, defVal: 30},
{name: 'position', dispName: 'Callout Position', type: 'float', min:0, max:1, defVal: 0.5},
{name: 'position2', dispName: 'Callout Tip Position', type: 'float', min:0, max:1, defVal: 0.5}
];
mxCellRenderer.defaultShapes['folder'].prototype.customProperties = [
{name: 'tabWidth', dispName: 'Tab Width', type: 'float'},
{name: 'tabHeight', dispName: 'Tab Height', type: 'float'},
{name: 'tabPosition', dispName: 'Tap Position', type: 'enum',
enumList: [{val: 'left', dispName: 'Left'}, {val: 'right', dispName: 'Right'}]
}
];
mxCellRenderer.defaultShapes['swimlane'].prototype.customProperties = [
{name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: 15},
{name: 'absoluteArcSize', dispName: 'Abs. Arc Size', type: 'bool', defVal: false},
{name: 'startSize', dispName: 'Header Size', type: 'float'},
{name: 'swimlaneHead', dispName: 'Head Border', type: 'bool', defVal: true},
{name: 'swimlaneBody', dispName: 'Body Border', type: 'bool', defVal: true},
{name: 'horizontal', dispName: 'Horizontal', type: 'bool', defVal: true},
{name: 'separatorColor', dispName: 'Separator Color', type: 'color', defVal: null},
];
mxCellRenderer.defaultShapes['table'].prototype.customProperties = [
{name: 'rowLines', dispName: 'Row Lines', type: 'bool', defVal: true},
{name: 'columnLines', dispName: 'Column Lines', type: 'bool', defVal: true},
{name: 'fixedRows', dispName: 'Fixed Rows', type: 'bool', defVal: false},
{name: 'resizeLast', dispName: 'Resize Last Column', type: 'bool', defVal: false},
{name: 'resizeLastRow', dispName: 'Resize Last Row', type: 'bool', defVal: false}].
concat(mxCellRenderer.defaultShapes['swimlane'].prototype.customProperties).
concat(mxCellRenderer.defaultShapes['partialRectangle'].prototype.customProperties);
mxCellRenderer.defaultShapes['tableRow'].prototype.customProperties =
mxCellRenderer.defaultShapes['swimlane'].prototype.customProperties.
concat(mxCellRenderer.defaultShapes['partialRectangle'].prototype.customProperties);
mxCellRenderer.defaultShapes['doubleEllipse'].prototype.customProperties = [
{name: 'margin', dispName: 'Indent', type: 'float', min:0, defVal:4}
];
mxCellRenderer.defaultShapes['ext'].prototype.customProperties = [
{name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: 15},
{name: 'double', dispName: 'Double', type: 'bool', defVal: false},
{name: 'margin', dispName: 'Indent', type: 'float', min: 0, defVal:0}
];
mxCellRenderer.defaultShapes['curlyBracket'].prototype.customProperties = [
{name: 'rounded', dispName: 'Rounded', type: 'bool', defVal: true},
{name: 'size', dispName: 'Size', type: 'float', min:0, max: 1, defVal: 0.5}
];
mxCellRenderer.defaultShapes['image'].prototype.customProperties = [
{name: 'imageAspect', dispName: 'Fixed Image Aspect', type: 'bool', defVal:true}
];
mxCellRenderer.defaultShapes['label'].prototype.customProperties = [
{name: 'imageAspect', dispName: 'Fixed Image Aspect', type: 'bool', defVal:true},
{name: 'imageAlign', dispName: 'Image Align', type: 'enum',
enumList: [{val: 'left', dispName: 'Left'},
{val: 'center', dispName: 'Center'},
{val: 'right', dispName: 'Right'}], defVal: 'left'},
{name: 'imageVerticalAlign', dispName: 'Image Vertical Align', type: 'enum',
enumList: [{val: 'top', dispName: 'Top'},
{val: 'middle', dispName: 'Middle'},
{val: 'bottom', dispName: 'Bottom'}], defVal: 'middle'},
{name: 'imageWidth', dispName: 'Image Width', type: 'float', min:0, defVal: 24},
{name: 'imageHeight', dispName: 'Image Height', type: 'float', min:0, defVal: 24},
{name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: 12},
{name: 'absoluteArcSize', dispName: 'Abs. Arc Size', type: 'bool', defVal: false}
];
mxCellRenderer.defaultShapes['dataStorage'].prototype.customProperties = [
{name: 'size', dispName: 'Size', type: 'float', min:0, max:1, defVal:0.1 }
];
mxCellRenderer.defaultShapes['manualInput'].prototype.customProperties = [
{name: 'size', dispName: 'Size', type: 'float', min:0, defVal:30 },
{name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: 20}
];
mxCellRenderer.defaultShapes['loopLimit'].prototype.customProperties = [
{name: 'size', dispName: 'Size', type: 'float', min:0, defVal:20 },
{name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: 20}
];
mxCellRenderer.defaultShapes['offPageConnector'].prototype.customProperties = [
{name: 'size', dispName: 'Size', type: 'float', min:0, defVal:38 },
{name: 'arcSize', dispName: 'Arc Size', type: 'float', min:0, defVal: 20}
];
mxCellRenderer.defaultShapes['display'].prototype.customProperties = [
{name: 'size', dispName: 'Size', type: 'float', min: 0, max: 1, defVal: 0.25 }
];
mxCellRenderer.defaultShapes['singleArrow'].prototype.customProperties = [
{name: 'arrowWidth', dispName: 'Arrow Width', type: 'float', min: 0, max: 1, defVal: 0.3 },
{name: 'arrowSize', dispName: 'Arrowhead Length', type: 'float', min: 0, max: 1, defVal: 0.2 }
];
mxCellRenderer.defaultShapes['doubleArrow'].prototype.customProperties = [
{name: 'arrowWidth', dispName: 'Arrow Width', type: 'float', min: 0, max: 1, defVal: 0.3 },
{name: 'arrowSize', dispName: 'Arrowhead Length', type: 'float', min: 0, max: 1, defVal: 0.2 }
];
mxCellRenderer.defaultShapes['cross'].prototype.customProperties = [
{name: 'size', dispName: 'Size', type: 'float', min: 0, max: 1, defVal: 0.2 }
];
mxCellRenderer.defaultShapes['corner'].prototype.customProperties = [
{name: 'dx', dispName: 'Width1', type: 'float', min: 0, defVal: 20 },
{name: 'dy', dispName: 'Width2', type: 'float', min: 0, defVal: 20 }
];
mxCellRenderer.defaultShapes['tee'].prototype.customProperties = [
{name: 'dx', dispName: 'Width1', type: 'float', min: 0, defVal: 20 },
{name: 'dy', dispName: 'Width2', type: 'float', min: 0, defVal: 20 }
];
mxCellRenderer.defaultShapes['umlLifeline'].prototype.customProperties = [
{name: 'participant', dispName:'Participant', type:'enum', defVal:'none', enumList:[
{val:'none', dispName: 'Default'},
{val:'umlActor', dispName: 'Actor'},
{val:'umlBoundary', dispName: 'Boundary'},
{val:'umlEntity', dispName: 'Entity'},
{val:'umlControl', dispName: 'Control'},
]},
{name: 'size', dispName:'Height', type:'float', defVal:40, min:0}
];
mxCellRenderer.defaultShapes['umlFrame'].prototype.customProperties = [
{name: 'width', dispName:'Title Width', type:'float', defVal:60, min:0},
{name: 'height', dispName:'Title Height', type:'float', defVal:30, min:0}
];
/**
* Configures global color schemes.
*/
StyleFormatPanel.prototype.defaultColorSchemes = [[{fill: '', stroke: ''}, {fill: '#f5f5f5', stroke: '#666666', font: '#333333'},
{fill: '#dae8fc', stroke: '#6c8ebf'}, {fill: '#d5e8d4', stroke: '#82b366'},
{fill: '#ffe6cc', stroke: '#d79b00'}, {fill: '#fff2cc', stroke: '#d6b656'},
{fill: '#f8cecc', stroke: '#b85450'}, {fill: '#e1d5e7', stroke: '#9673a6'}],
[{fill: '', stroke: ''}, {fill: '#60a917', stroke: '#2D7600', font: '#ffffff'},
{fill: '#008a00', stroke: '#005700', font: '#ffffff'}, {fill: '#1ba1e2', stroke: '#006EAF', font: '#ffffff'},
{fill: '#0050ef', stroke: '#001DBC', font: '#ffffff'}, {fill: '#6a00ff', stroke: '#3700CC', font: '#ffffff'},
//{fill: '#aa00ff', stroke: '#7700CC', font: '#ffffff'},
{fill: '#d80073', stroke: '#A50040', font: '#ffffff'}, {fill: '#a20025', stroke: '#6F0000', font: '#ffffff'}],
[{fill: '#e51400', stroke: '#B20000', font: '#ffffff'}, {fill: '#fa6800', stroke: '#C73500', font: '#000000'},
{fill: '#f0a30a', stroke: '#BD7000', font: '#000000'}, {fill: '#e3c800', stroke: '#B09500', font: '#000000'},
{fill: '#6d8764', stroke: '#3A5431', font: '#ffffff'}, {fill: '#647687', stroke: '#314354', font: '#ffffff'},
{fill: '#76608a', stroke: '#432D57', font: '#ffffff'}, {fill: '#a0522d', stroke: '#6D1F00', font: '#ffffff'}],
[{fill: '', stroke: ''}, {fill: mxConstants.NONE, stroke: ''},
{fill: '#fad7ac', stroke: '#b46504'}, {fill: '#fad9d5', stroke: '#ae4132'},
{fill: '#b0e3e6', stroke: '#0e8088'}, {fill: '#b1ddf0', stroke: '#10739e'},
{fill: '#d0cee2', stroke: '#56517e'}, {fill: '#bac8d3', stroke: '#23445d'}],
[{fill: '', stroke: ''},
{fill: '#f5f5f5', stroke: '#666666', gradient: '#b3b3b3'},
{fill: '#dae8fc', stroke: '#6c8ebf', gradient: '#7ea6e0'},
{fill: '#d5e8d4', stroke: '#82b366', gradient: '#97d077'},
{fill: '#ffcd28', stroke: '#d79b00', gradient: '#ffa500'},
{fill: '#fff2cc', stroke: '#d6b656', gradient: '#ffd966'},
{fill: '#f8cecc', stroke: '#b85450', gradient: '#ea6b66'},
{fill: '#e6d0de', stroke: '#996185', gradient: '#d5739d'}],
[{fill: '', stroke: ''}, {fill: '#eeeeee', stroke: '#36393d'},
{fill: '#f9f7ed', stroke: '#36393d'}, {fill: '#ffcc99', stroke: '#36393d'},
{fill: '#cce5ff', stroke: '#36393d'}, {fill: '#ffff88', stroke: '#36393d'},
{fill: '#cdeb8b', stroke: '#36393d'}, {fill: '#ffcccc', stroke: '#36393d'}]];
/**
* Configures custom color schemes.
*/
StyleFormatPanel.prototype.customColorSchemes = null;
/**
* Adds predefiend styles.
*/
var styleFormatPanelInit = StyleFormatPanel.prototype.init;
StyleFormatPanel.prototype.init = function()
{
var sstate = this.editorUi.getSelectionState();
if (this.defaultColorSchemes != null && this.defaultColorSchemes.length > 0 &&
sstate.style.shape != 'image' && !sstate.containsLabel &&
sstate.cells.length > 0)
{
this.container.appendChild(this.addStyles(this.createPanel()));
}
styleFormatPanelInit.apply(this, arguments);
if (sstate.customProperties != null)
{
this.container.appendChild(this.addProperties(
this.createPanel(), sstate.customProperties,
sstate));
}
};
/**
* Overridden to add copy and paste style.
*/
var styleFormatPanelAddStyleOps = StyleFormatPanel.prototype.addStyleOps;
StyleFormatPanel.prototype.addStyleOps = function(div)
{
var ss = this.editorUi.getSelectionState();
if (ss.cells.length == 1)
{
this.addActions(div, ['copyStyle', 'pasteStyle']);
}
else if (ss.cells.length >= 1)
{
this.addActions(div, ['pasteStyle', 'pasteData']);
}
styleFormatPanelAddStyleOps.apply(this, arguments);
return div;
};
/**
* Initial collapsed state of the properties panel.
*/
EditorUi.prototype.propertiesCollapsed = true;
/**
* Create Properties Panel
*/
BaseFormatPanel.prototype.addProperties = function(div, properties, state, hideId)
{
var that = this;
var graph = this.editorUi.editor.graph;
var secondLevel = [];
function insertAfter(newElem, curElem)
{
curElem.parentNode.insertBefore(newElem, curElem.nextSibling);
};
function applyStyleVal(pName, newVal, prop, delIndex, input)
{
if (prop.valueChanged != null)
{
prop.valueChanged(newVal, input, state, that);
}
else
{
graph.getModel().beginUpdate();
try
{
var changedProps = [];
var changedVals = [];
if (prop.index != null)
{
var allVals = [];
var curVal = prop.parentRow.nextSibling;
while(curVal && curVal.getAttribute('data-pName') == pName)
{
allVals.push(curVal.getAttribute('data-pValue'));
curVal = curVal.nextSibling;
}
if (prop.index < allVals.length)
{
if (delIndex != null)
{
allVals.splice(delIndex, 1);
}
else
{
allVals[prop.index] = newVal;
}
}
else
{
allVals.push(newVal);
}
if (prop.size != null && allVals.length > prop.size) //trim the array to the specifies size
{
allVals = allVals.slice(0, prop.size);
}
newVal = allVals.join(',');
if (prop.countProperty != null)
{
graph.setCellStyles(prop.countProperty, allVals.length, graph.getSelectionCells());
changedProps.push(prop.countProperty);
changedVals.push(allVals.length);
}
}
graph.setCellStyles(pName, newVal, graph.getSelectionCells());
changedProps.push(pName);
changedVals.push(newVal);
if (prop.dependentProps != null)
{
for (var i = 0; i < prop.dependentProps.length; i++)
{
var defVal = prop.dependentPropsDefVal[i];
var vals = prop.dependentPropsVals[i];
if (vals.length > newVal)
{
vals = vals.slice(0, newVal);
}
else
{
for (var j = vals.length; j < newVal; j++)
{
vals.push(defVal);
}
}
vals = vals.join(',');
graph.setCellStyles(prop.dependentProps[i], vals, graph.getSelectionCells());
changedProps.push(prop.dependentProps[i]);
changedVals.push(vals);
}
}
if (typeof(prop.onChange) == 'function')
{
prop.onChange(graph, newVal);
}
that.editorUi.fireEvent(new mxEventObject('styleChanged', 'keys', changedProps,
'values', changedVals, 'cells', graph.getSelectionCells()));
}
finally
{
graph.getModel().endUpdate();
}
}
}
function setElementPos(td, elem)
{
elem.style.left = '0px';
elem.style.top = '0px';
elem.style.position = 'relative';
elem.style.right = '0px';
elem.style.bottom = '0px';
elem.style.width = '100%';
elem.style.maxWidth = '100%';
elem.style.maxHeight = '100%';
elem.style.textAlign = 'left';
};
function createColorBtn(pName, pValue, prop)
{
var clrDiv = document.createElement('div');
clrDiv.style.width = '32px';
clrDiv.style.height = '8px';
clrDiv.style.margin = '2px';
clrDiv.style.borderStyle = 'solid';
clrDiv.style.borderWidth = '1px';
function updateBackground(color)
{
if (color == null || color == mxConstants.NONE)
{
clrDiv.style.background = 'url(\'' + Dialog.prototype.noColorImage + '\')';
}
else if (color == 'default')
{
// Default is not supported for properties, this is a fallback
clrDiv.style.background = '';
}
else
{
var cssColor = mxUtils.getLightDarkColor(color);
if (mxUtils.isLightDarkColor(color) &&
cssColor.light != cssColor.dark)
{
clrDiv.style.background = 'linear-gradient(to right bottom, ' +
cssColor.cssText + ' 50%, ' + mxUtils.invertLightDarkColor(cssColor).
cssText + ' 50.3%)';
}
else
{
clrDiv.style.background = color;
}
}
};
updateBackground(pValue);
btn = mxUtils.button('', mxUtils.bind(that, function(evt)
{
this.editorUi.pickColor(pValue, function(color)
{
applyStyleVal(pName, color, prop);
updateBackground(color);
});
mxEvent.consume(evt);
}));
btn.style.height = '18px';
btn.style.width = '40px';
btn.style.marginTop = '1px';
btn.style.marginLeft = '1px';
btn.className = 'geColorBtn';
btn.appendChild(clrDiv);
return btn;
};
function createDynArrList(pName, pValue, subType, defVal, countProperty, myRow, flipBkg)
{
if (pValue != null)
{
var vals = pValue.split(',');
secondLevel.push({name: pName, values: vals, type: subType,
defVal: defVal, countProperty: countProperty,
parentRow: myRow, isDeletable: true, flipBkg: flipBkg});
}
btn = mxUtils.button('+', mxUtils.bind(that, function(evt)
{
var beforeElem = myRow;
var index = 0;
while (beforeElem.nextSibling != null)
{
var cur = beforeElem.nextSibling;
var elemPName = cur.getAttribute('data-pName');
if (elemPName == pName)
{
beforeElem = beforeElem.nextSibling;
index++;
}
else
{
break;
}
}
var newProp = {type: subType, parentRow: myRow, index: index,
isDeletable: true, defVal: defVal, countProperty: countProperty};
var arrItem = createPropertyRow(pName, '', newProp, index % 2 == 0, flipBkg);
applyStyleVal(pName, defVal, newProp);
insertAfter(arrItem, beforeElem);
mxEvent.consume(evt);
}));
btn.style.height = '16px';
btn.style.width = '25px';
btn.className = 'geColorBtn';
return btn;
};
function createStaticArrList(pName, pValue, subType, defVal, size, myRow, flipBkg)
{
if (size > 0)
{
var vals = new Array(size);
var curVals = pValue != null? pValue.split(',') : [];
for (var i = 0; i < size; i++)
{
vals[i] = curVals[i] != null? curVals[i] : (defVal != null? defVal : '');
}
secondLevel.push({name: pName, values: vals, type: subType, defVal: defVal, parentRow: myRow, flipBkg: flipBkg, size: size});
}
return document.createElement('div'); //empty cell
};
function createCheckbox(pName, pValue, prop)
{
var input = document.createElement('input');
input.type = 'checkbox';
input.checked = pValue == '1';
mxEvent.addListener(input, 'change', function()
{
applyStyleVal(pName, input.checked? '1' : '0', prop, null, input);
});
return input;
};
function createPropertyRow(pName, pValue, prop, isOdd, flipBkg)
{
var pDiplayName = prop.dispName;
var pType = prop.type;
var row = document.createElement('tr');
row.className = 'gePropRow' + (flipBkg? 'Dark' : '') + (isOdd? 'Alt' : '') + ' gePropNonHeaderRow';
row.setAttribute('data-pName', pName);
row.setAttribute('data-pValue', pValue);
var rightAlig = false;
if (prop.index != null)
{
row.setAttribute('data-index', prop.index);
pDiplayName = (pDiplayName != null? pDiplayName : '') + '[' + prop.index + ']';
rightAlig = true;
}
var td = document.createElement('td');
td.className = 'gePropRowCell';
var label = mxResources.get(pDiplayName, null, pDiplayName);
mxUtils.write(td, label);
td.setAttribute('title', label);
if (rightAlig)
{
td.style.textAlign = 'right';
}
row.appendChild(td);
td = document.createElement('td');
td.className = 'gePropRowCell';
td.setAttribute('title', (pValue != null) ?
decodeURIComponent(pValue) : mxResources.get('none'));
mxEvent.addListener(td, 'click', mxUtils.bind(that, function(e)
{
// Stops current property editor and triggers DOM update
if (!mxUtils.isAncestorNode(row, document.activeElement) &&
mxUtils.isAncestorNode(row.parentNode, document.activeElement) &&
document.activeElement != null && document.activeElement.blur != null)
{
document.activeElement.blur();
mxEvent.consume(e);
}
}));
if (pType == 'color')
{
td.appendChild(createColorBtn(pName, pValue, prop));
}
else if (pType == 'bool' || pType == 'boolean')
{
td.appendChild(createCheckbox(pName, pValue, prop));
}
else if (pType == 'enum')
{
var pEnumList = prop.enumList;
td.innerHTML = '';
let valueDiv = document.createElement('div');
valueDiv.className = 'gePropValue';
td.appendChild(valueDiv);
for (var i = 0; i < pEnumList.length; i++)
{
var op = pEnumList[i];
if (op.val == pValue)
{
mxUtils.write(valueDiv, mxResources.get(op.dispName, null, op.dispName));
break;
}
}
mxEvent.addListener(td, 'click', mxUtils.bind(that, function(e)
{
// Ignores event if currently editing this property
var source = mxEvent.getSource(e);
if ((source != td && source != valueDiv) ||
mxEvent.isConsumed(e))
{
return;
}
valueDiv.innerHTML = '';
var select = document.createElement('select');
var nullValue = 'null';
var nullOption = null;
setElementPos(td, select);
for (var i = 0; i < pEnumList.length; i++)
{
var op = pEnumList[i];
var opElem = document.createElement('option');
opElem.value = mxUtils.htmlEntities(op.val);
mxUtils.write(opElem, mxResources.get(op.dispName, null, op.dispName));
select.appendChild(opElem);
if (op.val == null)
{
opElem.value = nullValue;
nullOption = opElem;
}
}
select.value = (pValue == null && nullOption != null) ? nullValue : pValue;
valueDiv.appendChild(select);
mxEvent.addListener(select, 'change', function()
{
var newVal = mxUtils.htmlEntities(select.value);
if (select[select.selectedIndex] == nullOption ||
newVal.value == nullValue)
{
newVal = null;
}
applyStyleVal(pName, newVal, prop);
//set value triggers a redraw of the panel which removes the select and updates the row
});
select.focus();
//FF calls blur on focus! so set the event after focusing (not with selects but to be safe)
mxEvent.addListener(select, 'blur', function()
{
valueDiv.innerHTML = '';
for (let i = 0; i < pEnumList.length; i++)
{
let op = pEnumList[i];
if (op.val == select.value)
{
mxUtils.write(valueDiv, mxResources.get(op.dispName, null, op.dispName));
break;
}
}
});
}));
}
else if (pType == 'dynamicArr')
{
td.appendChild(createDynArrList(pName, pValue, prop.subType, prop.subDefVal, prop.countProperty, row, flipBkg));
}
else if (pType == 'staticArr')
{
td.appendChild(createStaticArrList(pName, pValue, prop.subType, prop.subDefVal, prop.size, row, flipBkg));
}
else if (pType == 'readOnly')
{
var inp = document.createElement('input');
inp.setAttribute('readonly', '');
inp.value = pValue;
inp.style.borderWidth = '0px';
if (pName == 'id')
{
row.firstChild.innerHTML = '';
row.firstChild.setAttribute('colspan', '2');
inp.style.flexGrow = '1';
inp.style.marginLeft = '4px';
inp.style.textAlign = 'center';
inp.setAttribute('title', pValue);
var div = document.createElement('div');
mxUtils.write(div, mxResources.get('id'));
div.style.display = 'inline-flex';
div.style.alignItems = 'center';
div.style.width = '100%';
div.appendChild(inp);
row.firstChild.appendChild(div);
}
else
{
inp.style.width = '96px';
td.appendChild(inp);
}
}
else
{
td.innerHTML = '';
let valueDiv = document.createElement('div');
valueDiv.className = 'gePropValue';
td.appendChild(valueDiv);
valueDiv.innerHTML = mxUtils.htmlEntities(decodeURIComponent(pValue));
mxEvent.addListener(td, 'click', mxUtils.bind(that, function(e)
{
// Ignores event if currently editing this property
var source = mxEvent.getSource(e);
if ((source != td && source != valueDiv) ||
mxEvent.isConsumed(e))
{
return;
}
valueDiv.innerHTML = '';
var input = document.createElement('input');
setElementPos(valueDiv, input);
input.value = decodeURIComponent(pValue);
input.className = 'gePropEditor';
if ((pType == 'int' || pType == 'float') && !prop.allowAuto)
{
input.type = 'number';
input.step = pType == 'int'? '1' : 'any';
if (prop.min != null)
{
input.min = parseFloat(prop.min);
}
if (prop.max != null)
{
input.max = parseFloat(prop.max);
}
}
valueDiv.appendChild(input);
function setInputVal()
{
var inputVal = input.value;
inputVal = inputVal.length == 0 && pType != 'string' &&
pType != 'numbers'? 0 : inputVal;
if (prop.allowAuto)
{
if (inputVal.trim != null && inputVal.trim().
toLowerCase() == 'auto')
{
inputVal = 'auto';
pType = 'string';
}
else
{
inputVal = parseFloat(inputVal);
inputVal = isNaN(inputVal)? 0 : inputVal;
}
}
if (prop.min != null && inputVal < prop.min)
{
inputVal = prop.min;
}
else if (prop.max != null && inputVal > prop.max)
{
inputVal = prop.max;
}
var newVal = null;
try
{
newVal = (pType == 'numbers') ? inputVal.match(/\d+/g).map(Number).join(' ') :
encodeURIComponent((pType == 'int'? parseInt(inputVal) : inputVal) + '');
}
catch(e)
{
// ignores parsing errors
}
applyStyleVal(pName, newVal, prop, null, input);
}
mxEvent.addListener(input, 'keypress', function(e)
{
if (e.keyCode == 13)
{
setInputVal();
//set value triggers a redraw of the panel which removes the input
}
});
input.focus();
//FF calls blur on focus! so set the event after focusing
mxEvent.addListener(input, 'blur', function()
{
setInputVal();
});
}));
}
if (prop.isDeletable)
{
var delBtn = mxUtils.button('-', mxUtils.bind(that, function(evt)
{
//delete the node by refreshing the properties
applyStyleVal(pName, '', prop, prop.index);
mxEvent.consume(evt);
}));
delBtn.style.height = '16px';
delBtn.style.width = '25px';
delBtn.style.float = 'right';
delBtn.className = 'geColorBtn';
td.appendChild(delBtn);
}
row.appendChild(td);
return row;
};
var grid = document.createElement('table');
grid.className = 'geProperties geFullWidthElement';
//create header row
var hrow = document.createElement('tr');
hrow.className = 'gePropHeader';
var th = document.createElement('th');
th.className = 'gePropHeaderCell';
th.style.paddingLeft = '16px';
th.style.backgroundRepeat = 'no-repeat';
th.style.backgroundPosition = '-2px 50%';
th.style.backgroundSize = '20px';
mxUtils.write(th, mxResources.get('property'));
hrow.style.cursor = 'pointer';
var onFold = function()
{
var rows = grid.querySelectorAll('.gePropNonHeaderRow');
var display;
if (!that.editorUi.propertiesCollapsed)
{
th.style.backgroundImage = 'url(\'' + Editor.arrowDownImage + '\')';
display = '';
}
else
{
th.style.backgroundImage = 'url(\'' + Editor.arrowRightImage + '\')';
display = 'none';
for (var e = div.childNodes.length - 1; e >= 0 ; e--)
{
//Blur can be executed concurrently with this method and the element is removed before removing it here
try
{
var child = div.childNodes[e];
var nodeName = child.nodeName.toUpperCase();
if (nodeName == 'INPUT' || nodeName == 'SELECT')
{
div.removeChild(child);
}
}
catch(ex){}
}
}
for (var r = 0; r < rows.length; r++)
{
rows[r].style.display = display;
}
};
mxEvent.addListener(hrow, 'click', function()
{
that.editorUi.propertiesCollapsed = !that.editorUi.propertiesCollapsed;
onFold();
});
hrow.appendChild(th);
var th2 = document.createElement('th');
th2.className = 'gePropHeaderCell';
th2.style.paddingLeft = '4px';
mxUtils.write(th2, mxResources.get('value'));
hrow.appendChild(th2);
grid.appendChild(hrow);
var isOdd = false;
var flipBkg = false;
var cellId = null;
if (state.vertices.length == 1 && state.edges.length == 0)
{
cellId = state.vertices[0].id;
}
else if (state.vertices.length == 0 && state.edges.length == 1)
{
cellId = state.edges[0].id;
}
//Add it to top (always)
if (cellId != null && !hideId)
{
grid.appendChild(createPropertyRow('id', mxUtils.htmlEntities(cellId),
{dispName: 'id', type: 'readOnly'}, true, false));
}
for (var key in properties)
{
var prop = properties[key];
if (!prop.primary)
{
if (typeof(prop.isVisible) == 'function')
{
if (!prop.isVisible(state, this)) continue;
}
var pValue = (prop.getValue != null) ? prop.getValue(state, this) : (state.style[key] != null? mxUtils.htmlEntities(state.style[key] + '') :
((prop.getDefaultValue != null) ? prop.getDefaultValue(state, this) : prop.defVal)); //or undefined if defVal is undefined
if (prop.type == 'separator')
{
flipBkg = !flipBkg;
continue;
}
else if (prop.type == 'staticArr') //if dynamic values are needed, a more elegant technique is needed to replace such values
{
prop.size = parseInt(state.style[prop.sizeProperty] || properties[prop.sizeProperty].defVal) || 0;
}
else if (prop.dependentProps != null)
{
var dependentProps = prop.dependentProps;
var dependentPropsVals = [];
var dependentPropsDefVal = [];
for (var i = 0; i < dependentProps.length; i++)
{
var propVal = state.style[dependentProps[i]];
dependentPropsDefVal.push(properties[dependentProps[i]].subDefVal);
dependentPropsVals.push(propVal != null? propVal.split(',') : []);
}
prop.dependentPropsDefVal = dependentPropsDefVal;
prop.dependentPropsVals = dependentPropsVals;
}
grid.appendChild(createPropertyRow(key, pValue, prop, isOdd, flipBkg));
isOdd = !isOdd;
}
}
for (var i = 0; i < secondLevel.length; i++)
{
var prop = secondLevel[i];
var insertElem = prop.parentRow;
for (var j = 0; j < prop.values.length; j++)
{
//mxUtils.clone failed because of the HTM element, so manual cloning is used
var iProp = {type: prop.type, parentRow: prop.parentRow, isDeletable: prop.isDeletable, index: j,
defVal: prop.defVal, countProperty: prop.countProperty, size: prop.size};
var arrItem = createPropertyRow(prop.name, prop.values[j], iProp, j % 2 == 0, prop.flipBkg);
insertAfter(arrItem, insertElem);
insertElem = arrItem;
}
}
div.appendChild(grid);
onFold();
return div;
};
/**
* Creates the buttons for the predefined styles.
*/
StyleFormatPanel.prototype.addStyles = function(div)
{
if (this.defaultColorSchemes != null)
{
var ui = this.editorUi;
var graph = ui.editor.graph;
var picker = document.createElement('div');
picker.style.padding = '6px 20px 0 24px';
div.appendChild(picker);
// Maximum palettes to switch the switcher
var maxEntries = 10;
// Selector
var switcher = document.createElement('div');
switcher.className = 'geSwitcher';
switcher.style.padding = '4px 18px 6px 0px';
var dots = [];
for (var i = 0; i < this.defaultColorSchemes.length; i++)
{
var dot = document.createElement('div');
dot.className = 'geSwitcherDot';
(mxUtils.bind(this, function(index)
{
mxEvent.addListener(dot, 'click', mxUtils.bind(this, function()
{
setScheme(index);
}));
}))(i);
dots.push(dot);
switcher.appendChild(dot);
}
var setScheme = mxUtils.bind(this, function(index)
{
if (dots[index] != null)
{
if (this.format.currentScheme != null && dots[this.format.currentScheme] != null)
{
dots[this.format.currentScheme].style.background = 'transparent';
}
this.format.currentScheme = index;
updateScheme(this.defaultColorSchemes[this.format.currentScheme]);
dots[this.format.currentScheme].style.background = '#84d7ff';
}
});
var updateScheme = mxUtils.bind(this, function(colorsets)
{
var defaults = mxUtils.clone(graph.defaultVertexStyle);
defaults[mxConstants.STYLE_STROKECOLOR] = (defaults[mxConstants.STYLE_STROKECOLOR] != null) ?
defaults[mxConstants.STYLE_STROKECOLOR] : 'default';
defaults[mxConstants.STYLE_FILLCOLOR] = (defaults[mxConstants.STYLE_FILLCOLOR] != null) ?
defaults[mxConstants.STYLE_FILLCOLOR] : 'default';
defaults[mxConstants.STYLE_FONTCOLOR] = (defaults[mxConstants.STYLE_FONTCOLOR] != null) ?
defaults[mxConstants.STYLE_FONTCOLOR] : 'default';
graph.replaceDefaultColors(new mxCell(), defaults);
var addButton = mxUtils.bind(this, function(colorset)
{
var btn = mxUtils.button('', mxUtils.bind(this, function(evt)
{
graph.getModel().beginUpdate();
try
{
var cells = ui.getSelectionState().cells;
for (var i = 0; i < cells.length; i++)
{
var style = graph.getModel().getStyle(cells[i]);
if (colorset != null)
{
if (!mxEvent.isShiftDown(evt))
{
if (colorset['fill'] == '' || colorset['fill'] == null)
{
style = mxUtils.setStyle(style, mxConstants.STYLE_FILLCOLOR, null);
}
else
{
style = mxUtils.setStyle(style, mxConstants.STYLE_FILLCOLOR, colorset['fill']);
}
if (colorset['gradient'] == '' || colorset['gradient'] == null)
{
style = mxUtils.setStyle(style, mxConstants.STYLE_GRADIENTCOLOR, null);
}
else
{
style = mxUtils.setStyle(style, mxConstants.STYLE_GRADIENTCOLOR, colorset['gradient']);
}
if (!mxEvent.isControlDown(evt) && (!mxClient.IS_MAC || !mxEvent.isMetaDown(evt)) &&
graph.getModel().isVertex(cells[i]))
{
if (colorset['font'] == '' || colorset['font'] == null)
{
style = mxUtils.setStyle(style, mxConstants.STYLE_FONTCOLOR, null);
}
else
{
style = mxUtils.setStyle(style, mxConstants.STYLE_FONTCOLOR, colorset['font']);
}
}
}
if (!mxEvent.isAltDown(evt))
{
if (colorset['stroke'] == '' || colorset['fill'] == null)
{
style = mxUtils.setStyle(style, mxConstants.STYLE_STROKECOLOR, null);
}
else
{
style = mxUtils.setStyle(style, mxConstants.STYLE_STROKECOLOR, colorset['stroke']);
}
}
}
else
{
style = mxUtils.setStyle(style, mxConstants.STYLE_FILLCOLOR, null);
style = mxUtils.setStyle(style, mxConstants.STYLE_STROKECOLOR, null);
style = mxUtils.setStyle(style, mxConstants.STYLE_GRADIENTCOLOR, null);
if (graph.getModel().isVertex(cells[i]))
{
style = mxUtils.setStyle(style, mxConstants.STYLE_FONTCOLOR, null);
}
}
graph.getModel().setStyle(cells[i], style);
}
}
finally
{
graph.getModel().endUpdate();
}
}));
btn.className = 'geStyleButton';
btn.style.width = '36px';
btn.style.height = (this.defaultColorSchemes.length <= maxEntries) ? '24px' : '30px';
btn.style.margin = '0px 6px 6px 0px';
if (colorset != null)
{
var b = '1px solid';
if (colorset['border'] != null)
{
b = colorset['border'];
}
if (colorset['gradient'] != null)
{
btn.style.backgroundImage = 'linear-gradient(' +
mxUtils.getLightDarkColor(colorset['fill']).cssText + ' 0px,' +
mxUtils.getLightDarkColor(colorset['gradient']).cssText + ' 100%)';
}
else if (colorset['fill'] == mxConstants.NONE)
{
btn.style.background = 'url(\'' + Dialog.prototype.noColorImage + '\')';
}
else if (colorset['fill'] == null || colorset['fill'] == '')
{
btn.style.backgroundColor = mxUtils.getLightDarkColor(
mxUtils.getValue(defaults, mxConstants.STYLE_FILLCOLOR,
'#ffffff')).cssText;
}
else
{
var cssColor = mxUtils.getLightDarkColor(colorset['fill']);
btn.style.backgroundImage = 'linear-gradient(to right bottom, ' +
cssColor.cssText + ' 50%, ' + cssColor.light + ' 50.3%)';
}
if (colorset['stroke'] == null || colorset['stroke'] == mxConstants.NONE)
{
btn.style.border = b + ' transparent';
}
else if (colorset['stroke'] == '')
{
btn.style.border = '1px solid ' + mxUtils.getLightDarkColor(
mxUtils.getValue(defaults, mxConstants.STYLE_STROKECOLOR,
'#000000')).cssText;
}
else
{
var cssColor = mxUtils.getLightDarkColor(colorset['stroke']);
btn.style.border = b + ' ' + cssColor.cssText;
btn.style.borderRightColor = cssColor.light;
btn.style.borderBottomColor = btn.style.borderRightColor;
}
if (colorset['title'] != null)
{
btn.setAttribute('title', colorset['title']);
}
}
else
{
var bg = mxUtils.getValue(defaults, mxConstants.STYLE_FILLCOLOR, '#ffffff');
var bd = mxUtils.getValue(defaults, mxConstants.STYLE_STROKECOLOR, '#000000');
btn.style.backgroundColor = bg;
btn.style.border = '1px solid ' + bd;
}
btn.style.borderRadius = '0';
picker.appendChild(btn);
if (colorset != null && colorset['gradient'] != null)
{
var lightBtn = btn.cloneNode(false);
lightBtn.style.backgroundImage = 'linear-gradient(light-dark(transparent, ' +
mxUtils.getLightDarkColor(colorset['fill']).light + ') 0px, ' +
'light-dark(transparent, ' + mxUtils.getLightDarkColor(
colorset['gradient']).light + ') 100%)';
lightBtn.style.clipPath = 'polygon(0 100%, 100% 0, 100% 100%)';
lightBtn.style.backgroundColor = 'transparent';
picker.appendChild(lightBtn);
lightBtn.style.marginLeft = '-42px';
mxEvent.addListener(lightBtn, 'click', function()
{
btn.click();
});
}
});
picker.innerText = '';
if (colorsets != null)
{
for (var i = 0; i < colorsets.length; i++)
{
if (i > 0 && mxUtils.mod(i, 4) == 0)
{
mxUtils.br(picker);
}
addButton(colorsets[i]);
}
}
});
if (this.format.currentScheme == null)
{
setScheme(Math.min(dots.length - 1, Editor.isDarkMode()
? 1 : (urlParams['sketch'] == '1' ? 5 : 0)));
}
else
{
setScheme(this.format.currentScheme);
}
var bottom = (this.defaultColorSchemes.length <= maxEntries) ? 43 : 23;
var left = document.createElement('div');
left.className = 'geButton';
left.style.cssText = 'position:absolute;left:0px;bottom:' + bottom + 'px;width:20px;' +
'background-image:url(' + Editor.chevronLeftImage + ');';
mxEvent.addListener(left, 'click', mxUtils.bind(this, function()
{
setScheme(mxUtils.mod(this.format.currentScheme - 1, this.defaultColorSchemes.length));
}));
var right = document.createElement('div');
right.className = 'geButton';
right.style.cssText = 'position:absolute;left:186px;bottom:' + bottom + 'px;width:20px;' +
'background-image:url(' + Editor.chevronRightImage + ');';
if (this.defaultColorSchemes.length > 1)
{
div.appendChild(left);
div.appendChild(right);
}
mxEvent.addListener(right, 'click', mxUtils.bind(this, function()
{
setScheme(mxUtils.mod(this.format.currentScheme + 1, this.defaultColorSchemes.length));
}));
updateScheme(this.defaultColorSchemes[this.format.currentScheme]);
if (this.defaultColorSchemes.length <= maxEntries)
{
div.appendChild(switcher);
}
}
return div;
};
}
/**
* Maps fonts to font-face CSS.
*/
Graph.fontMapping = {'https://fonts.googleapis.com/css?family=Architects+Daughter':
'@font-face { font-family: "Architects Daughter"; ' +
'src: url(' + STYLE_PATH + '/fonts/ArchitectsDaughter-Regular.ttf) format("truetype"); }'};
/**
* Lookup table for mapping from font URL and name to elements in the DOM.
*/
Graph.customFontElements = {};
/**
* Returns true if the given font URL references a Google font.
*/
Graph.isGoogleFontUrl = function(url)
{
return url.substring(0, Editor.GOOGLE_FONTS.length) == Editor.GOOGLE_FONTS ||
url.substring(0, Editor.GOOGLE_FONTS_CSS2.length) == Editor.GOOGLE_FONTS_CSS2;
};
/**
* Returns true if the given font URL is a CSS file.
*/
Graph.isCssFontUrl = function(url)
{
return Graph.isGoogleFontUrl(url);
};
/**
* Uses CSS2 for Google fonts to support bold font style eg.
* https://fonts.googleapis.com/css?family=IBM+Plex+Sans is rewritten as
* https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;500
*/
Graph.rewriteGoogleFontUrl = function(url)
{
if (url != null && url.substring(0, Editor.GOOGLE_FONTS.length) == Editor.GOOGLE_FONTS)
{
url = Editor.GOOGLE_FONTS_CSS2 + url.substring(Editor.GOOGLE_FONTS.length) + ':wght@400;500';
}
return url;
};
/**
* Creates the DOM node for the custom font.
*/
Graph.createFontElement = function(name, url)
{
var elt = null;
var style = Graph.fontMapping[url];
if (style == null && Graph.isCssFontUrl(url))
{
elt = document.createElement('link');
elt.setAttribute('rel', 'stylesheet');
elt.setAttribute('type', 'text/css');
elt.setAttribute('charset', 'UTF-8');
elt.setAttribute('href', Graph.rewriteGoogleFontUrl(url));
}
else
{
if (style == null)
{
style = '@font-face {\n' +
'font-family: "' + name + '";\n' +
'src: url("' + url + '");\n}'
}
elt = document.createElement('style');
mxUtils.write(elt, style);
}
return elt;
};
/**
* Adds an entry to the recent custom fonts list.
*/
Graph.addRecentCustomFont = function(key, entry)
{
// Hook for registering recent custom fonts in the UI
};
/**
* Adds a font to the document.
*/
Graph.addFont = function(name, url, callback, elementLookup)
{
if (name != null && name.length > 0 && url != null && url.length > 0)
{
elementLookup = (elementLookup != null) ?
elementLookup : Graph.customFontElements;
var key = name.toLowerCase();
// Blocks UI fonts from being overwritten
if (key != 'helvetica' && name != 'arial' && key != 'sans-serif')
{
var entry = elementLookup[key];
// Replaces element if URL has changed
if (entry != null && entry.url != url)
{
entry.elt.parentNode.removeChild(entry.elt);
entry = null;
}
if (entry == null)
{
var realUrl = url;
// Fixes possible mixed content by using proxy
if (url.substring(0, 5) == 'http:')
{
realUrl = PROXY_URL + '?url=' + encodeURIComponent(url);
}
entry = {name: name, url: url, elt: Graph.createFontElement(name, realUrl)};
elementLookup[key] = entry;
var head = (elementLookup == Graph.customFontElements) ?
document.getElementsByTagName('head')[0] : null;
if (callback != null)
{
if (entry.elt.nodeName.toLowerCase() == 'link')
{
entry.elt.onload = callback;
entry.elt.onerror = callback;
}
else
{
callback();
}
}
if (head != null)
{
head.appendChild(entry.elt);
}
}
else if (callback != null)
{
callback();
}
}
else if (callback != null)
{
callback();
}
}
else if (callback != null)
{
callback();
}
return name;
};
/**
* Returns the URL for the given font name if it exists in the document.
* Otherwise it returns the given URL.
*/
Graph.getFontUrl = function(name, url)
{
var font = Graph.customFontElements[name.toLowerCase()];
if (font != null)
{
url = font.url;
}
return url;
};
/**
* Processes the fonts in the given element and its descendants.
*/
Graph.processFontAttributes = function(elt)
{
var elts = elt.getElementsByTagName('*');
for (var i = 0; i < elts.length; i++)
{
var url = elts[i].getAttribute('data-font-src');
if (url != null)
{
var name = (elts[i].nodeName == 'FONT') ?
elts[i].getAttribute('face') :
elts[i].style.fontFamily;
if (name != null)
{
Graph.addFont(name, url);
}
}
}
};
/**
* Processes the font in the given cell style.
*/
Graph.processFontStyle = function(style)
{
if (style != null)
{
var url = mxUtils.getValue(style, 'fontSource', null);
if (url != null)
{
var name = mxUtils.getValue(style, mxConstants.STYLE_FONTFAMILY, null);
if (name != null)
{
try
{
Graph.addFont(name, decodeURIComponent(url));
}
catch (e)
{
// ignore
}
}
}
}
return style;
};
/**
* Changes the default stylename so that it matches the old named style
* if one was specified in the XML.
*/
Graph.prototype.defaultThemeName = 'default-style2';
/**
* Contains the last XML that was pasted.
*/
Graph.prototype.lastPasteXml = null;
/**
* Contains the number of times the last XML was pasted.
*/
Graph.prototype.pasteCounter = 0;
/**
* Graph Overrides
*/
Graph.prototype.defaultScrollbars = urlParams['sb'] != '0';
/**
* Specifies if the page should be visible for new files. Default is true.
*/
Graph.prototype.defaultPageVisible = urlParams['pv'] != '0';
/**
* Properties for the SVG shadow effect.
*/
Graph.prototype.svgShadowColor = '#3D4574';
/**
* Properties for the SVG shadow effect.
*/
Graph.prototype.svgShadowOpacity = '0.4';
/**
* Properties for the SVG shadow effect.
*/
Graph.prototype.svgShadowBlur = '1.7';
/**
* Properties for the SVG shadow effect.
*/
Graph.prototype.svgShadowSize = '3';
/**
* Enables move of bends/segments without selecting.
*/
Graph.prototype.hiddenTags = [];
/**
* Enables move of bends/segments without selecting.
*/
Graph.prototype.defaultMathEnabled = false;
/**
* Adds rack child layout style.
*/
var graphInit = Graph.prototype.init;
Graph.prototype.init = function()
{
graphInit.apply(this, arguments);
// Array of hidden tags used in isCellVisible override
this.hiddenTags = [];
//TODO initialize Freehand in the correct location!
if (window.mxFreehand)
{
this.freehand = new mxFreehand(this);
}
// Override insert location for current mouse point
var mouseEvent = null;
function setMouseEvent(evt)
{
mouseEvent = evt;
};
mxEvent.addListener(this.container, 'mouseenter', setMouseEvent);
mxEvent.addListener(this.container, 'mousemove', setMouseEvent);
mxEvent.addListener(this.container, 'mouseleave', function(evt)
{
mouseEvent = null;
});
// Extends getInsertPoint to use the current mouse location
this.isMouseInsertPoint = function()
{
return mouseEvent != null;
};
var getInsertPoint = this.getInsertPoint;
this.getInsertPoint = function()
{
if (mouseEvent != null)
{
return this.getPointForEvent(mouseEvent);
}
return getInsertPoint.apply(this, arguments);
};
var layoutManagerGetLayout = this.layoutManager.getLayout;
this.layoutManager.getLayout = function(cell)
{
// Workaround for possible invalid style after change and before view validation
var style = this.graph.getCellStyle(cell);
// mxRackContainer may be undefined as it is dynamically loaded at render time
if (style != null)
{
if (style['childLayout'] == 'rack')
{
var rackLayout = new mxStackLayout(this.graph, false);
var unitSize = 20;
if (style['rackUnitSize'] != null)
{
rackLayout.gridSize = parseFloat(style['rackUnitSize']);
}
else
{
rackLayout.gridSize = (typeof mxRackContainer !== 'undefined') ? mxRackContainer.unitSize : unitSize;
}
rackLayout.marginLeft = style['marginLeft'] || 0;
rackLayout.marginRight = style['marginRight'] || 0;
rackLayout.marginTop = style['marginTop'] || 0;
rackLayout.marginBottom = style['marginBottom'] || 0;
rackLayout.allowGaps = style['allowGaps'] || 0;
rackLayout.horizontal = mxUtils.getValue(style, 'horizontalRack', '0') == '1';
rackLayout.resizeParent = false;
rackLayout.fill = true;
return rackLayout;
}
}
return layoutManagerGetLayout.apply(this, arguments);
}
this.updateGlobalUrlVariables();
};
/**
* Adds support for custom fonts in cell styles.
*/
var graphPostProcessCellStyle = Graph.prototype.postProcessCellStyle;
Graph.prototype.postProcessCellStyle = function(cell, style)
{
return Graph.processFontStyle(graphPostProcessCellStyle.apply(this, arguments));
};
/**
* Handles custom fonts in labels.
*/
var mxSvgCanvas2DUpdateTextNodes = mxSvgCanvas2D.prototype.updateTextNodes;
mxSvgCanvas2D.prototype.updateTextNodes = function(x, y, w, h, align, valign, wrap, overflow, clip, rotation, dir, g)
{
mxSvgCanvas2DUpdateTextNodes.apply(this, arguments);
Graph.processFontAttributes(g);
};
/**
* Handles custom fonts in labels.
*/
var mxTextRedraw = mxText.prototype.redraw;
mxText.prototype.redraw = function()
{
mxTextRedraw.apply(this, arguments);
// Handles label rendered without foreign object
if (this.node != null && this.node.nodeName == 'DIV')
{
Graph.processFontAttributes(this.node);
}
};
/**
* Creates the tags dialog.
*/
Graph.prototype.createTagsDialog = function(isEnabled, invert, addFn, helpButton)
{
var graph = this;
var allTags = graph.hiddenTags.slice();
var div = document.createElement('div');
div.style.userSelect = 'none';
div.style.overflow = 'hidden';
div.style.padding = '10px';
div.style.height = '100%';
var tagCloud = document.createElement('div');
tagCloud.style.position = 'absolute';
tagCloud.style.boxSizing = 'border-box';
tagCloud.style.userSelect = 'none';
tagCloud.style.overflow = 'auto';
tagCloud.style.padding = '3px';
tagCloud.style.left = '0px';
tagCloud.style.right = '0px';
tagCloud.style.top = '0px';
tagCloud.style.bottom = '0px';
div.appendChild(tagCloud);
function removeInvisibleSelectionCells()
{
var cells = graph.getSelectionCells();
var visible = [];
for (var i = 0; i < cells.length; i++)
{
if (graph.isCellVisible(cells[i]))
{
visible.push(cells[i]);
}
}
graph.setSelectionCells(visible);
};
function setAllVisible(visible)
{
graph.setHiddenTags(visible ? [] : allTags.slice());
removeInvisibleSelectionCells();
graph.refresh();
};
graph.addListener(mxEvent.ROOT, function()
{
allTags = graph.hiddenTags.slice();
});
function refreshTags(tags, selected)
{
tagCloud.innerText = '';
if (tags.length > 0)
{
var table = document.createElement('table');
table.setAttribute('cellpadding', '2');
table.style.boxSizing = 'border-box';
table.style.tableLayout = 'fixed';
table.style.width = '100%';
var tbody = document.createElement('tbody');
if (tags != null && tags.length > 0)
{
for (var i = 0; i < tags.length; i++)
{
(function(tag)
{
function setTagVisible()
{
var temp = allTags.slice();
var index = mxUtils.indexOf(temp, tag);
temp.splice(index, 1);
graph.setHiddenTags(temp);
removeInvisibleSelectionCells();
graph.refresh();
};
function selectCells()
{
var cells = graph.getCellsForTags(
[tag], null, null, true);
if (graph.isEnabled())
{
graph.setSelectionCells(cells);
}
else
{
graph.highlightCells(cells, null, null, 70);
}
};
var visible = mxUtils.indexOf(graph.hiddenTags, tag) < 0;
var row = document.createElement('tr');
var td = document.createElement('td');
td.style.align = 'center';
td.style.width = '16px';
var img = document.createElement('img');
img.setAttribute('src', visible ? Editor.visibleImage : Editor.hiddenImage);
img.setAttribute('title', mxResources.get(visible ? 'hideIt' : 'show', [tag]));
mxUtils.setOpacity(img, visible ? 75 : 25);
img.className = 'geAdaptiveAsset';
img.style.verticalAlign = 'middle';
img.style.cursor = 'pointer';
img.style.width = '16px';
if (invert || Editor.isDarkMode())
{
img.style.filter = 'invert(100%)';
}
td.appendChild(img);
mxEvent.addListener(img, 'click', function(evt)
{
if (mxEvent.isShiftDown(evt))
{
setAllVisible(mxUtils.indexOf(graph.hiddenTags, tag) >= 0);
}
else
{
graph.toggleHiddenTag(tag);
removeInvisibleSelectionCells();
graph.refresh();
}
mxEvent.consume(evt);
});
row.appendChild(td);
td = document.createElement('td');
td.style.align = 'center';
td.style.width = '16px';
var img = document.createElement('img');
img.setAttribute('src', Editor.selectImage);
img.setAttribute('title', mxResources.get('select'));
mxUtils.setOpacity(img, visible ? 75 : 25);
img.className = 'geAdaptiveAsset';
img.style.verticalAlign = 'middle';
img.style.cursor = 'pointer';
img.style.width = '16px';
if (invert || Editor.isDarkMode())
{
img.style.filter = 'invert(100%)';
}
mxEvent.addListener(img, 'click', function(evt)
{
setAllVisible(true);
selectCells();
mxEvent.consume(evt);
});
td.appendChild(img);
row.appendChild(td);
td = document.createElement('td');
td.style.overflow = 'hidden';
td.style.whiteSpace = 'nowrap';
td.style.textOverflow = 'ellipsis';
td.style.verticalAlign = 'middle';
td.style.cursor = 'pointer';
td.setAttribute('title', tag);
a = document.createElement('a');
mxUtils.write(a, tag);
a.style.textOverflow = 'ellipsis';
a.style.position = 'relative';
mxUtils.setOpacity(a, visible ? 100 : 40);
td.appendChild(a);
mxEvent.addListener(td, 'click', (function(evt)
{
if (mxEvent.isShiftDown(evt))
{
setAllVisible(true);
selectCells();
}
else
{
if (visible && graph.hiddenTags.length > 0)
{
setAllVisible(true);
}
else
{
setTagVisible();
}
}
mxEvent.consume(evt);
}));
row.appendChild(td);
if (graph.isEnabled())
{
td = document.createElement('td');
td.style.verticalAlign = 'middle';
td.style.textAlign = 'center';
td.style.width = '18px';
if (selected == null)
{
td.style.align = 'center';
td.style.width = '16px';
var img = document.createElement('img');
img.setAttribute('src', Editor.trashImage);
img.setAttribute('title', mxResources.get('removeIt', [tag]));
mxUtils.setOpacity(img, visible ? 75 : 25);
img.className = 'geAdaptiveAsset';
img.style.verticalAlign = 'middle';
img.style.cursor = 'pointer';
img.style.width = '16px';
if (invert || Editor.isDarkMode())
{
img.style.filter = 'invert(100%)';
}
mxEvent.addListener(img, 'click', function(evt)
{
var idx = mxUtils.indexOf(allTags, tag);
if (idx >= 0)
{
allTags.splice(idx, 1);
}
graph.removeTagsForCells(
graph.model.getDescendants(
graph.model.getRoot()), [tag]);
graph.refresh();
mxEvent.consume(evt);
});
td.appendChild(img);
}
else
{
var cb2 = document.createElement('input');
cb2.setAttribute('type', 'checkbox');
cb2.style.margin = '0px';
cb2.defaultChecked = (selected != null &&
mxUtils.indexOf(selected, tag) >= 0);
cb2.checked = cb2.defaultChecked;
cb2.style.background = 'transparent';
cb2.setAttribute('title', mxResources.get(
cb2.defaultChecked ?
'removeIt' : 'add', [tag]));
mxEvent.addListener(cb2, 'change', function(evt)
{
if (cb2.checked)
{
graph.addTagsForCells(graph.getSelectionCells(), [tag]);
}
else
{
graph.removeTagsForCells(graph.getSelectionCells(), [tag]);
}
mxEvent.consume(evt);
});
td.appendChild(cb2);
}
row.appendChild(td);
}
tbody.appendChild(row);
})(tags[i]);
}
}
table.appendChild(tbody);
tagCloud.appendChild(table);
}
};
var refreshUi = mxUtils.bind(this, function(sender, evt)
{
if (isEnabled())
{
var tags = graph.getAllTags();
for (var i = 0; i < tags.length; i++)
{
if (mxUtils.indexOf(allTags, tags[i]) < 0)
{
allTags.push(tags[i]);
}
}
allTags.sort();
if (graph.isSelectionEmpty())
{
refreshTags(allTags);
}
else
{
refreshTags(allTags, graph.getCommonTagsForCells(
graph.getSelectionCells()));
}
}
});
graph.selectionModel.addListener(mxEvent.CHANGE, refreshUi);
graph.model.addListener(mxEvent.CHANGE, refreshUi);
graph.addListener(mxEvent.REFRESH, refreshUi);
var footer = document.createElement('div');
footer.className = 'geToolbarContainer geDialogToolbar';
footer.style.position = 'absolute';
footer.style.display = 'flex';
footer.style.bottom = '0px';
footer.style.left = '0px';
footer.style.right = '0px';
footer.style.height = '32px';
footer.style.overflow = 'hidden';
footer.style.padding = '3px 4px 4px 4px';
footer.style.borderWidth = '1px 0px 0px 0px';
footer.style.borderStyle = 'solid';
footer.style.whiteSpace = 'nowrap';
if (graph.isEnabled())
{
tagCloud.style.bottom = '32px';
var link = document.createElement('a');
link.className = 'geButton';
var resetLink = link.cloneNode(false);
resetLink.style.backgroundImage = 'url(' + Editor.visibleImage + ')';
resetLink.setAttribute('title', mxResources.get('reset'));
mxEvent.addListener(resetLink, 'click', function(evt)
{
graph.setHiddenTags([]);
if (!mxEvent.isShiftDown(evt))
{
allTags = graph.hiddenTags.slice();
}
removeInvisibleSelectionCells();
graph.refresh();
mxEvent.consume(evt);
});
footer.appendChild(resetLink);
if (addFn != null)
{
var addLink = link.cloneNode(false);
addLink.style.backgroundImage = 'url(' + Editor.plusImage + ')';
addLink.setAttribute('title', mxResources.get('add'));
mxEvent.addListener(addLink, 'click', function(evt)
{
// Takes all tags and callback to update all tags
addFn(allTags, function(newAllTags)
{
allTags = newAllTags;
refreshUi();
});
mxEvent.consume(evt);
});
footer.appendChild(addLink);
}
div.appendChild(footer);
}
if (helpButton != null)
{
footer.appendChild(helpButton);
}
return {div: div, refresh: refreshUi};
};
/**
* Returns all custom fonts (old and new).
*/
Graph.prototype.getCustomFonts = function()
{
var fonts = this.extFonts;
if (fonts != null)
{
fonts = fonts.slice();
}
else
{
fonts = [];
}
for (var key in Graph.customFontElements)
{
var font = Graph.customFontElements[key];
fonts.push({name: font.name, url: font.url});
}
return fonts;
};
/**
* Assigns the given custom font to the selected text.
*/
Graph.prototype.setFont = function(name, url)
{
// Adds the font element to the document
Graph.addFont(name, url);
// Marks the element with a random font name so
// that it can be found in the code below
var temp = Editor.guid();
document.execCommand('fontname', false, temp);
// Finds the new or updated element and changes or
// removes is data-font-src attribute as required
var fonts = this.cellEditor.textarea.getElementsByTagName('font');
var found = false;
for (var i = 0; i < fonts.length; i++)
{
if (fonts[i].getAttribute('face') == temp)
{
found = true;
fonts[i].setAttribute('face', name);
if (url != null)
{
fonts[i].setAttribute('data-font-src', url);
}
else
{
fonts[i].removeAttribute('data-font-src');
}
}
}
if (!found)
{
// Fallback to use the real font name if a new
// or updated element can not be found above
document.execCommand('fontname', false, name);
}
};
/**
* Disables fast zoom with shadow in lightbox for Safari
* to work around blank output on retina screen.
*/
var graphIsFastZoomEnabled = Graph.prototype.isFastZoomEnabled;
Graph.prototype.isFastZoomEnabled = function()
{
return graphIsFastZoomEnabled.apply(this, arguments) && (!this.shadowVisible || !mxClient.IS_SF);
};
/**
* Updates the global variables from the vars URL parameter.
*/
Graph.prototype.updateGlobalUrlVariables = function()
{
this.globalVars = Editor.globalVars;
if (urlParams['vars'] != null)
{
try
{
this.globalVars = (this.globalVars != null) ? mxUtils.clone(this.globalVars) : {};
var vars = JSON.parse(decodeURIComponent(urlParams['vars']));
if (vars != null)
{
for (var key in vars)
{
this.globalVars[key] = vars[key];
}
}
}
catch (e)
{
if (window.console != null)
{
console.log('Error in vars URL parameter: ' + e);
}
}
}
};
/**
* Returns all global variables used for export. This function never returns null.
* This can be overridden by plugins to return global variables for export.
*/
Graph.prototype.getExportVariables = function()
{
return (this.globalVars != null) ? mxUtils.clone(this.globalVars) : {};
};
/**
* Adds support for vars URL parameter.
*/
var graphGetGlobalVariable = Graph.prototype.getGlobalVariable;
Graph.prototype.getGlobalVariable = function(name)
{
var val = graphGetGlobalVariable.apply(this, arguments);
if (val == null && this.globalVars != null)
{
val = this.globalVars[name];
}
return val;
};
/**
* Cached default stylesheet for image export in dark mode.
*/
Graph.prototype.getDefaultStylesheet = function()
{
if (this.defaultStylesheet == null)
{
var node = this.themes['default-style2'];
var dec = new mxCodec(node.ownerDocument);
this.defaultStylesheet = dec.decode(node);
}
return this.defaultStylesheet;
};
/**
* Overiddes function to use url parameter
*/
Graph.prototype.isViewer = function()
{
return urlParams['viewer'];
};
/**
* Temporarily overrides stylesheet during image export in dark mode.
*/
var graphGetSvg = Graph.prototype.getSvg;
Graph.prototype.getSvg = function(background, scale, border, nocrop, crisp,
ignoreSelection, showText, imgExport, linkTarget, hasShadow,
incExtFonts, theme, exportType, cells, noCssClass, disableLinks)
{
var result = graphGetSvg.apply(this, arguments);
if (theme != null)
{
// Uses style attribute to bypass removing fallback styles
var style = result.getAttribute('style');
if (style == null)
{
style = '';
}
var theme = (theme == 'auto' || this.getAdaptiveColors() == 'none') ?
((this.getAdaptiveColors() == 'none') ?
'light' : 'light dark') : theme;
style += ' color-scheme: ' + theme + ';';
result.setAttribute('style', style);
}
var extFonts = this.getCustomFonts();
// Adds external fonts
if (incExtFonts && extFonts.length > 0)
{
var svgDoc = result.ownerDocument;
var style = (svgDoc.createElementNS != null) ?
svgDoc.createElementNS(mxConstants.NS_SVG, 'style') : svgDoc.createElement('style');
(svgDoc.setAttributeNS != null) ? style.setAttributeNS('type', 'text/css') :
style.setAttribute('type', 'text/css');
var prefix = '';
var postfix = '';
for (var i = 0; i < extFonts.length; i++)
{
var fontName = extFonts[i].name, fontUrl = extFonts[i].url;
if (Graph.isCssFontUrl(fontUrl))
{
prefix += '@import url(' + Graph.rewriteGoogleFontUrl(fontUrl) + ');\n';
}
else
{
postfix += '@font-face {\n' +
'font-family: "' + fontName + '";\n' +
'src: url("' + fontUrl + '");\n}\n';
}
}
style.appendChild(svgDoc.createTextNode(prefix + postfix));
result.getElementsByTagName('defs')[0].appendChild(style);
}
// Converts background pages to subtrees
if (Editor.replaceSvgDataUris && this.backgroundImage != null &&
this.backgroundImage.originalSrc != null)
{
EditorUi.embedSvgImages(result);
}
// SVG element must be added to DOM for MathJax to work
if (this.mathEnabled)
{
document.body.appendChild(result);
Editor.MathJaxRender(result);
result.parentNode.removeChild(result);
// Copies MathJax CSS to output
var style = result.ownerDocument.getElementById('MJX-SVG-styles');
if (style != null)
{
result.getElementsByTagName('defs')[0].appendChild(style.cloneNode(true));
}
}
return result;
};
/**
* Overridden to destroy the shape number.
*/
var cellRendererDestroy = mxCellRenderer.prototype.destroy;
mxCellRenderer.prototype.destroy = function(state)
{
cellRendererDestroy.apply(this, arguments);
if (state.secondLabel != null)
{
state.secondLabel.destroy();
state.secondLabel = null;
}
};
/**
* Includes the shape number in the return value.
*/
mxCellRenderer.prototype.getShapesForState = function(state)
{
return [state.shape, state.text, state.secondLabel, state.control];
};
/**
* Resets the global shape counter.
*/
var graphViewResetValidationState = mxGraphView.prototype.resetValidationState;
mxGraphView.prototype.resetValidationState = function()
{
graphViewResetValidationState.apply(this, arguments);
this.enumerationState = 0;
};
/**
* Adds shape number update the validation step.
*/
var graphViewStateValidated = mxGraphView.prototype.stateValidated;
mxGraphView.prototype.stateValidated = function(state)
{
if (state.shape != null)
{
this.redrawEnumerationState(state);
}
return graphViewStateValidated.apply(this, arguments);
};
/**
* Returns the markup to be used for the enumeration shape.
*/
mxGraphView.prototype.createEnumerationValue = function(state)
{
var value = decodeURIComponent(mxUtils.getValue(state.style, 'enumerateValue', ''));
if (value == '')
{
value = ++this.enumerationState;
}
return '' +
mxUtils.htmlEntities(value) + '
';
};
/**
* Adds drawing and update of the shape number.
*/
mxGraphView.prototype.redrawEnumerationState = function(state)
{
var enumerate = mxUtils.getValue(state.style, 'enumerate', 0) == '1';
if (enumerate && state.secondLabel == null)
{
state.secondLabel = new mxText('', new mxRectangle(),
mxConstants.ALIGN_LEFT, mxConstants.ALIGN_BOTTOM);
state.secondLabel.size = 12;
state.secondLabel.state = state;
state.secondLabel.dialect = mxConstants.DIALECT_STRICTHTML;
this.graph.cellRenderer.initializeLabel(state, state.secondLabel);
}
else if (!enumerate && state.secondLabel != null)
{
state.secondLabel.destroy();
state.secondLabel = null;
}
var shape = state.secondLabel;
if (shape != null)
{
var s = state.view.scale;
var value = this.createEnumerationValue(state);
var bounds = this.graph.model.isVertex(state.cell) ?
new mxRectangle(state.x + state.width - 4 * s, state.y + 4 * s, 0, 0) :
mxRectangle.fromPoint(state.view.getPoint(state));
if (!shape.bounds.equals(bounds) || shape.value != value || shape.scale != s)
{
shape.bounds = bounds;
shape.value = value;
shape.scale = s;
shape.redraw();
}
}
};
/**
* Updates the SVG for the background image if it references another page.
*/
var graphRefresh = Graph.prototype.refresh;
Graph.prototype.refresh = function()
{
this.refreshBackgroundImage();
graphRefresh.apply(this, arguments);
};
/**
* Updates the SVG for the background image if it references another page.
*/
Graph.prototype.refreshBackgroundImage = function()
{
if (this.backgroundImage != null && this.backgroundImage.originalSrc != null)
{
this.setBackgroundImage(this.backgroundImage);
this.view.validateBackgroundImage();
}
};
/**
* Sets default style (used in editor.get/setGraphXml below)
*/
var graphLoadStylesheet = Graph.prototype.loadStylesheet;
Graph.prototype.loadStylesheet = function()
{
graphLoadStylesheet.apply(this, arguments);
this.currentStyle = 'default-style2';
};
/**
* Adds support for data:action/json,{"actions":[actions]} where actions is
* a comma-separated list of JSON objects.
*
* An example action is:
*
* data:action/json,{"actions":[{"toggle": {"cells": ["3", "4"]}}]}
*
* This toggles the visible state of the cells with ID 3 and 4.
*/
Graph.prototype.handleCustomLink = function(href, cell)
{
if (href.substring(0, 17) == 'data:action/json,')
{
var link = JSON.parse(href.substring(17));
if (link.actions != null)
{
this.executeCustomActions(link.actions, null, cell);
}
}
};
/**
* Runs the given actions and invokes done when all actions have been executed.
* When adding new actions that reference cell IDs support for updating
* those cell IDs must be handled in Graph.updateCustomLinkActions
*/
Graph.prototype.executeCustomActions = function(actions, done, cell)
{
if (!this.executingCustomActions)
{
this.executingCustomActions = true;
var updatingModel = false;
var waitCounter = 0;
var index = 0;
var beginUpdate = mxUtils.bind(this, function()
{
if (!updatingModel)
{
updatingModel = true;
this.model.beginUpdate();
}
});
var endUpdate = mxUtils.bind(this, function()
{
if (updatingModel)
{
updatingModel = false;
this.model.endUpdate();
}
});
var waitAndExecute = mxUtils.bind(this, function()
{
if (waitCounter > 0)
{
waitCounter--;
}
if (waitCounter == 0)
{
executeNextAction()
}
});
var executeNextAction = mxUtils.bind(this, function()
{
if (index < actions.length)
{
var stop = this.stoppingCustomActions;
var action = actions[index++];
var animations = [];
// Executes open actions before starting transaction
if (action.open != null)
{
endUpdate();
if (this.isCustomLink(action.open))
{
if (!this.customLinkClicked(action.open, cell))
{
return;
}
}
else
{
this.openLink(action.open);
}
}
if (action.wait != null && !stop)
{
this.pendingExecuteNextAction = mxUtils.bind(this, function()
{
this.pendingExecuteNextAction = null;
this.pendingWaitThread = null;
waitAndExecute();
});
waitCounter++;
this.pendingWaitThread = window.setTimeout(this.pendingExecuteNextAction,
(action.wait != '') ? parseInt(action.wait) : 1000);
endUpdate();
}
if (action.opacity != null && action.opacity.value != null)
{
Graph.setOpacityForNodes(this.getNodesForCells(
this.getCellsForAction(action.opacity, true)),
action.opacity.value);
}
if (action.fadeIn != null)
{
waitCounter++;
Graph.fadeNodes(this.getNodesForCells(
this.getCellsForAction(action.fadeIn, true)),
0, 1, waitAndExecute, (stop) ?
0 : action.fadeIn.delay);
}
if (action.fadeOut != null)
{
waitCounter++;
Graph.fadeNodes(this.getNodesForCells(
this.getCellsForAction(action.fadeOut, true)),
1, 0, waitAndExecute, (stop) ?
0 : action.fadeOut.delay);
}
if (action.wipeIn != null)
{
animations = animations.concat(this.createWipeAnimations(
this.getCellsForAction(action.wipeIn, true), true));
}
if (action.wipeOut != null)
{
animations = animations.concat(this.createWipeAnimations(
this.getCellsForAction(action.wipeOut, true), false));
}
// Executes all actions that change cell states
if (action.toggle != null)
{
beginUpdate();
this.toggleCells(this.getCellsForAction(action.toggle, true));
}
if (action.show != null)
{
beginUpdate();
var temp = this.getCellsForAction(action.show, true);
Graph.setOpacityForNodes(this.getNodesForCells(temp), 1);
this.setCellsVisible(temp, true);
}
if (action.hide != null)
{
beginUpdate();
var temp = this.getCellsForAction(action.hide, true);
Graph.setOpacityForNodes(this.getNodesForCells(temp), 0);
this.setCellsVisible(temp, false);
}
if (action.toggleStyle != null && action.toggleStyle.key != null)
{
beginUpdate();
this.toggleCellStyles(action.toggleStyle.key, (action.toggleStyle.defaultValue != null) ?
action.toggleStyle.defaultValue : '0', this.getCellsForAction(action.toggleStyle, true));
}
if (action.style != null && action.style.key != null)
{
beginUpdate();
this.setCellStyles(action.style.key, action.style.value,
this.getCellsForAction(action.style, true));
}
// Executes stateless actions on cells
var cells = [];
if (action.select != null && this.isEnabled())
{
cells = this.getCellsForAction(action.select);
this.setSelectionCells(cells);
}
if (action.highlight != null)
{
cells = this.getCellsForAction(action.highlight);
this.highlightCells(cells, action.highlight.color,
action.highlight.duration,
action.highlight.opacity);
}
if (action.scroll != null)
{
cells = this.getCellsForAction(action.scroll);
}
if (action.viewbox != null)
{
this.fitWindow(action.viewbox, action.viewbox.border);
}
if (cells.length > 0)
{
this.scrollCellToVisible(cells[0]);
}
if (cell != null && action.explore != null)
{
Graph.exploreFromCell(this, cell, action.explore);
}
if (action.tags != null)
{
if (action.tags.toggle != null)
{
var tags = action.tags.toggle;
if (tags.length == 0)
{
tags = this.getAllTags();
}
for (var i = 0; i < tags.length; i++)
{
this.toggleHiddenTag(tags[i]);
}
}
var hidden = null;
if (action.tags.hidden != null)
{
if (hidden == null)
{
hidden = [];
}
hidden = hidden.concat(action.tags.hidden);
}
if (action.tags.visible != null)
{
if (hidden == null)
{
hidden = [];
}
var all = this.getAllTags();
for (var i = 0; i < all.length; i++)
{
if (mxUtils.indexOf(action.tags.visible, all[i]) < 0 &&
mxUtils.indexOf(hidden, all[i]) < 0)
{
hidden.push(all[i]);
}
}
}
if (hidden != null)
{
this.setHiddenTags(hidden);
}
this.refresh();
}
if (animations.length > 0)
{
waitCounter++;
this.executeAnimations(animations, waitAndExecute,
(stop) ? 1 : action.steps,
(stop) ? 0 : action.delay);
}
if (waitCounter == 0)
{
executeNextAction();
}
else
{
endUpdate();
}
}
else
{
this.executingCustomActions = false;
this.stoppingCustomActions = false;
endUpdate();
if (done != null)
{
done();
}
}
});
executeNextAction();
}
else
{
this.stoppingCustomActions = true;
if (this.pendingWaitThread != null)
{
window.clearTimeout(this.pendingWaitThread);
}
if (this.pendingExecuteNextAction != null)
{
this.pendingExecuteNextAction();
}
this.fireEvent(new mxEventObject('stopExecutingCustomActions'));
}
};
/**
* Updates cell IDs in custom links on the given cell and its label.
*/
Graph.prototype.doUpdateCustomLinksForCell = function(mapping, cell)
{
var href = this.getLinkForCell(cell);
if (href != null && href.substring(0, 17) == 'data:action/json,')
{
this.setLinkForCell(cell, this.updateCustomLink(mapping, href));
}
if (this.isHtmlLabel(cell))
{
var temp = document.createElement('div');
temp.innerHTML = Graph.sanitizeHtml(this.getLabel(cell));
var links = temp.getElementsByTagName('a');
var changed = false;
for (var i = 0; i < links.length; i++)
{
href = links[i].getAttribute('href');
if (href != null && href.substring(0, 17) == 'data:action/json,')
{
links[i].setAttribute('href', this.updateCustomLink(mapping, href));
changed = true;
}
}
if (changed)
{
this.labelChanged(cell, temp.innerHTML);
}
}
};
/**
* Updates cell IDs in the given custom link and returns the updated link.
*/
Graph.prototype.updateCustomLink = function(mapping, href)
{
if (href.substring(0, 17) == 'data:action/json,')
{
try
{
var link = JSON.parse(href.substring(17));
if (link.actions != null)
{
this.updateCustomLinkActions(mapping, link.actions);
href = 'data:action/json,' + JSON.stringify(link);
}
}
catch (e)
{
// Ignore
}
}
return href;
};
/**
* Updates cell IDs in the given custom link actions.
*/
Graph.prototype.updateCustomLinkActions = function(mapping, actions)
{
for (var i = 0; i < actions.length; i++)
{
var action = actions[i];
for (var name in action)
{
this.updateCustomLinkAction(mapping, action[name], 'cells');
this.updateCustomLinkAction(mapping, action[name], 'excludeCells');
}
}
};
/**
* Updates cell IDs in the given custom link action.
*/
Graph.prototype.updateCustomLinkAction = function(mapping, action, name)
{
if (action != null && action[name] != null)
{
var result = [];
for (var i = 0; i < action[name].length; i++)
{
if (action[name][i] == '*')
{
result.push(action[name][i]);
}
else
{
var temp = mapping[action[name][i]];
if (temp != null)
{
if (temp != '')
{
result.push(temp);
}
}
else
{
result.push(action[name][i]);
}
}
}
action[name] = result;
}
};
/**
* Handles each action in the action array of a custom link. This code
* handles toggle actions for cell IDs.
*/
Graph.prototype.getCellsForAction = function(action, layers)
{
var result = this.getCellsById(action.cells).concat(
this.getCellsForTags(action.tags, null, layers));
// Removes excluded cells
if (action.excludeCells != null)
{
var temp = [];
for (var i = 0; i < result.length; i++)
{
if (action.excludeCells.indexOf(result[i].id) < 0)
{
temp.push(result[i]);
}
}
result = temp;
}
return result;
};
/**
* Returns the cells in the model (or given array) that have all of the
* given tags in their tags property.
*/
Graph.prototype.getCellsById = function(ids)
{
var result = [];
if (ids != null)
{
for (var i = 0; i < ids.length; i++)
{
if (ids[i] == '*')
{
var parent = this.model.getRoot();
result = result.concat(this.model.filterDescendants(function(cell)
{
return cell != parent;
}, parent));
}
else
{
var cell = this.model.getCell(ids[i]);
if (cell != null)
{
result.push(cell);
}
}
}
}
return result;
};
/**
* Adds support for custom fonts in cell styles.
*/
var graphIsCellVisible = Graph.prototype.isCellVisible;
Graph.prototype.isCellVisible = function(cell)
{
return graphIsCellVisible.apply(this, arguments) &&
!this.isAllTagsHidden(this.getTagsForCell(cell));
};
/**
* Returns the tags for the given cell as a string.
*/
Graph.prototype.setHiddenTags = function(tags)
{
this.hiddenTags = tags;
this.fireEvent(new mxEventObject('hiddenTagsChanged'));
};
/**
* Returns the tags for the given cell as a string.
*/
Graph.prototype.toggleHiddenTag = function(tag)
{
var idx = mxUtils.indexOf(this.hiddenTags, tag);
if (idx < 0)
{
this.hiddenTags.push(tag);
}
else if (idx >= 0)
{
this.hiddenTags.splice(idx, 1);
}
this.fireEvent(new mxEventObject('hiddenTagsChanged'));
};
/**
* Returns the cells in the model (or given array) that have all of the
* given tags in their tags property.
*/
Graph.prototype.isAllTagsHidden = function(tags)
{
if (tags == null || tags.length == 0 ||
this.hiddenTags.length == 0)
{
return false;
}
else
{
var tmp = tags.split(' ');
if (tmp.length > this.hiddenTags.length)
{
return false;
}
else
{
for (var i = 0; i < tmp.length; i++)
{
if (mxUtils.indexOf(this.hiddenTags, tmp[i]) < 0)
{
return false;
}
}
return true;
}
}
};
/**
* Returns the cells in the model (or given array) that have all of the
* given tags in their tags property.
*/
Graph.prototype.getCellsForTags = function(tagList, cells, includeLayers, checkVisible)
{
var result = [];
if (tagList != null)
{
cells = (cells != null) ? cells : this.model.getDescendants(this.model.getRoot());
var tagCount = 0;
var lookup = {};
for (var i = 0; i < tagList.length; i++)
{
if (tagList[i].length > 0)
{
lookup[tagList[i]] = true;
tagCount++;
}
}
for (var i = 0; i < cells.length; i++)
{
if ((includeLayers && this.model.getParent(cells[i]) == this.model.root) ||
this.model.isVertex(cells[i]) || this.model.isEdge(cells[i]))
{
var tags = this.getTagsForCell(cells[i]);
var match = false;
if (tags.length > 0)
{
var tmp = tags.split(' ');
if (tmp.length >= tagList.length)
{
var matchCount = 0;
for (var j = 0; j < tmp.length && (matchCount < tagCount); j++)
{
if (lookup[tmp[j]] != null)
{
matchCount++;
}
}
match = matchCount == tagCount;
}
}
if (match && ((checkVisible != true) || this.isCellVisible(cells[i])))
{
result.push(cells[i]);
}
}
}
}
return result;
};
/**
* Returns all tags in the diagram.
*/
Graph.prototype.getAllTags = function()
{
return this.getTagsForCells(
this.model.getDescendants(
this.model.getRoot()));
};
/**
* Returns the common tags for the given cells as a array.
*/
Graph.prototype.getCommonTagsForCells = function(cells)
{
var commonTokens = null;
var validTags = [];
for (var i = 0; i < cells.length; i++)
{
var tags = this.getTagsForCell(cells[i]);
validTags = [];
if (tags.length > 0)
{
var tokens = tags.split(' ');
var temp = {};
for (var j = 0; j < tokens.length; j++)
{
if (commonTokens == null || commonTokens[tokens[j]] != null)
{
temp[tokens[j]] = true;
validTags.push(tokens[j]);
}
}
commonTokens = temp;
}
else
{
return [];
}
}
return validTags;
};
/**
* Returns all tags for the given cells as an array.
*/
Graph.prototype.getTagsForCells = function(cells)
{
var tokens = [];
var temp = {};
for (var i = 0; i < cells.length; i++)
{
var tags = this.getTagsForCell(cells[i]);
if (tags.length > 0)
{
var t = tags.split(' ');
for (var j = 0; j < t.length; j++)
{
if (temp[t[j]] == null)
{
temp[t[j]] = true;
tokens.push(t[j]);
}
}
}
}
return tokens;
};
/**
* Returns the tags for the given cell as a string.
*/
Graph.prototype.getTagsForCell = function(cell)
{
return this.getAttributeForCell(cell, 'tags', '');
};
/**
* Adds the given array of tags to the given array cells.
*/
Graph.prototype.addTagsForCells = function(cells, tagList)
{
if (cells.length > 0 && tagList.length > 0)
{
this.model.beginUpdate();
try
{
for (var i = 0; i < cells.length; i++)
{
var temp = this.getTagsForCell(cells[i]);
var tags = temp.split(' ');
var changed = false;
for (var j = 0; j < tagList.length; j++)
{
var tag = mxUtils.trim(tagList[j]);
if (tag != '' && mxUtils.indexOf(tags, tag) < 0)
{
temp = (temp.length > 0) ? temp + ' ' + tag : tag;
changed = true;
}
}
if (changed)
{
this.setAttributeForCell(cells[i], 'tags', temp);
}
}
}
finally
{
this.model.endUpdate();
}
}
};
/**
* Removes the given array of tags from the given array cells.
*/
Graph.prototype.removeTagsForCells = function(cells, tagList)
{
if (cells.length > 0 && tagList.length > 0)
{
this.model.beginUpdate();
try
{
for (var i = 0; i < cells.length; i++)
{
var tags = this.getTagsForCell(cells[i]);
if (tags.length > 0)
{
var tokens = tags.split(' ');
var changed = false;
for (var j = 0; j < tagList.length; j++)
{
var idx = mxUtils.indexOf(tokens, tagList[j]);
if (idx >= 0)
{
tokens.splice(idx, 1);
changed = true;
}
}
if (changed)
{
this.setAttributeForCell(cells[i], 'tags', tokens.join(' '));
}
}
}
}
finally
{
this.model.endUpdate();
}
}
};
/**
* Shows or hides the given cells.
*/
Graph.prototype.toggleCells = function(cells)
{
this.model.beginUpdate();
try
{
for (var i = 0; i < cells.length; i++)
{
this.model.setVisible(cells[i], !this.model.isVisible(cells[i]))
}
}
finally
{
this.model.endUpdate();
}
};
/**
* Shows or hides the given cells.
*/
Graph.prototype.setCellsVisible = function(cells, visible)
{
this.model.beginUpdate();
try
{
for (var i = 0; i < cells.length; i++)
{
this.model.setVisible(cells[i], visible);
}
}
finally
{
this.model.endUpdate();
}
};
/**
* Highlights the given cell.
*/
Graph.prototype.highlightCells = function(cells, color, duration, opacity)
{
for (var i = 0; i < cells.length; i++)
{
this.highlightCell(cells[i], color, duration, opacity);
}
};
/**
* Highlights the given cell.
*/
Graph.prototype.highlightCell = function(cell, color, duration, opacity, strokeWidth)
{
color = (color != null) ? color : mxConstants.DEFAULT_VALID_COLOR;
duration = (duration != null) ? duration : 1000;
var state = this.view.getState(cell);
var hl = null;
if (state != null)
{
strokeWidth = (strokeWidth != null) ? strokeWidth : 4;
var sw = Math.max(strokeWidth + 1, mxUtils.getValue(state.style,
mxConstants.STYLE_STROKEWIDTH, 1) + strokeWidth);
hl = new mxCellHighlight(this, color, sw, false);
if (opacity != null)
{
hl.opacity = opacity;
}
hl.highlight(state);
// Fades out the highlight after a duration
window.setTimeout(function()
{
if (hl.shape != null)
{
mxUtils.setPrefixedStyle(hl.shape.node.style,
'transition', 'all 1200ms ease-in-out');
hl.shape.node.style.opacity = 0;
}
// Destroys the highlight after the fade
window.setTimeout(function()
{
hl.destroy();
}, 1200);
}, duration);
}
return hl;
};
/**
* Adds a shadow filter to the given svg root.
*/
Graph.prototype.addSvgShadow = function(svgRoot, group, createOnly, extend)
{
createOnly = (createOnly != null) ? createOnly : false;
extend = (extend != null) ? extend : true;
var size = Math.ceil(this.svgShadowSize * this.svgShadowBlur);
if (!createOnly)
{
group = (group != null) ? group : svgRoot.getElementsByTagName('g')[0];
if (group != null)
{
this.updateShadowFilter(group, true);
if (!isNaN(parseInt(svgRoot.getAttribute('width'))) && extend)
{
svgRoot.setAttribute('width', parseInt(svgRoot.getAttribute('width')) + size);
svgRoot.setAttribute('height', parseInt(svgRoot.getAttribute('height')) + size);
// Updates viewbox if one exists
var vb = svgRoot.getAttribute('viewBox');
if (vb != null && vb.length > 0)
{
var tokens = vb.split(' ');
if (tokens.length > 3)
{
w = parseFloat(tokens[2]) + size;
h = parseFloat(tokens[3]) + size;
svgRoot.setAttribute('viewBox', tokens[0] + ' ' + tokens[1] + ' ' + w + ' ' + h);
}
}
}
}
}
return size;
};
/**
* Loads the stylesheet for this graph.
* LATER: Update shadow filter after zoom?
*/
Graph.prototype.updateShadowFilter = function(elt, visible)
{
if (!mxClient.IS_SF)
{
if (visible)
{
var cssColor = mxUtils.getLightDarkColor(
this.svgShadowColor, this.svgShadowOpacity);
elt.style.filter = 'drop-shadow(' +
Math.round(this.svgShadowSize * 100) / 100 + 'px ' +
Math.round(this.svgShadowSize * 100) / 100 + 'px ' +
Math.round(this.svgShadowBlur * 100) / 100 + 'px ' +
cssColor.cssText + ')'
}
else
{
elt.style.filter = '';
}
}
};
/**
* Loads the stylesheet for this graph.
*/
Graph.prototype.setShadowVisible = function(value, fireEvent)
{
fireEvent = (fireEvent != null) ? fireEvent : true;
this.shadowVisible = value;
this.updateShadowFilter(this.view.getDrawPane(), this.shadowVisible);
if (fireEvent)
{
this.fireEvent(new mxEventObject('shadowVisibleChanged'));
}
};
/**
* Selects first unlocked layer if one exists
*/
Graph.prototype.checkDefaultParent = function()
{
if (this.defaultParent != null &&
!this.model.contains(this.defaultParent))
{
this.setDefaultParent(null);
this.selectUnlockedLayer();
}
};
/**
* Selects first unlocked layer if one exists
*/
Graph.prototype.selectUnlockedLayer = function()
{
if (this.defaultParent == null)
{
var childCount = this.model.getChildCount(this.model.root);
var cell = null;
var index = 0;
do
{
cell = this.model.getChildAt(this.model.root, index);
} while (index++ < childCount && mxUtils.getValue(this.getCellStyle(cell), 'locked', '0') == '1')
if (cell != null)
{
this.setDefaultParent(cell);
}
}
};
/**
* Specifies special libraries that are loaded via dynamic JS. Add cases
* where the filename cannot be worked out from the package name. The
* standard scheme for this mapping is stencils/packagename.xml. If there
* are multiple XML files, any JS files or any anomalies in the filename or
* directory that contains the file, then an entry must be added here and
* in EmbedServlet2 for the loading of the shapes to work.
*/
// Required to avoid 404 for mockup.xml since naming of mxgraph.mockup.anchor does not contain
// buttons even though it is defined in the mxMockupButtons.js file. This could only be fixed
// with aliases for existing shapes or aliases for basenames, but this is essentially the same.
mxStencilRegistry.libraries['mockup'] = [SHAPES_PATH + '/mockup/mxMockupButtons.js'];
mxStencilRegistry.libraries['arrows2'] = [SHAPES_PATH + '/mxArrows.js'];
mxStencilRegistry.libraries['atlassian'] = [STENCIL_PATH + '/atlassian.xml', SHAPES_PATH + '/mxAtlassian.js'];
mxStencilRegistry.libraries['bpmn'] = [SHAPES_PATH + '/mxBasic.js', STENCIL_PATH + '/bpmn.xml', SHAPES_PATH + '/bpmn/mxBpmnShape2.js'];
mxStencilRegistry.libraries['bpmn2'] = [SHAPES_PATH + '/mxBasic.js', STENCIL_PATH + '/bpmn.xml', SHAPES_PATH + '/bpmn/mxBpmnShape2.js'];
mxStencilRegistry.libraries['c4'] = [SHAPES_PATH + '/mxC4.js'];
mxStencilRegistry.libraries['cisco19'] = [SHAPES_PATH + '/mxCisco19.js', STENCIL_PATH + '/cisco19.xml'];
mxStencilRegistry.libraries['cisco_safe'] = [SHAPES_PATH + '/mxCiscoSafe.js', STENCIL_PATH + '/cisco_safe/architecture.xml', STENCIL_PATH + '/cisco_safe/business_icons.xml', STENCIL_PATH + '/cisco_safe/capability.xml', STENCIL_PATH + '/cisco_safe/design.xml', STENCIL_PATH + '/cisco_safe/iot_things_icons.xml', STENCIL_PATH + '/cisco_safe/people_places_things_icons.xml', STENCIL_PATH + '/cisco_safe/security_icons.xml', STENCIL_PATH + '/cisco_safe/technology_icons.xml', STENCIL_PATH + '/cisco_safe/threat.xml'];
mxStencilRegistry.libraries['dfd'] = [SHAPES_PATH + '/mxDFD.js'];
mxStencilRegistry.libraries['er'] = [SHAPES_PATH + '/er/mxER.js'];
mxStencilRegistry.libraries['kubernetes'] = [SHAPES_PATH + '/mxKubernetes.js', STENCIL_PATH + '/kubernetes.xml', STENCIL_PATH + '/kubernetes2.xml'];
mxStencilRegistry.libraries['flowchart'] = [SHAPES_PATH + '/mxFlowchart.js', STENCIL_PATH + '/flowchart.xml'];
mxStencilRegistry.libraries['ios'] = [SHAPES_PATH + '/mockup/mxMockupiOS.js'];
mxStencilRegistry.libraries['rackGeneral'] = [SHAPES_PATH + '/rack/mxRack.js', STENCIL_PATH + '/rack/general.xml'];
mxStencilRegistry.libraries['rackF5'] = [STENCIL_PATH + '/rack/f5.xml'];
mxStencilRegistry.libraries['lean_mapping'] = [SHAPES_PATH + '/mxLeanMap.js', STENCIL_PATH + '/lean_mapping.xml'];
mxStencilRegistry.libraries['basic'] = [SHAPES_PATH + '/mxBasic.js', STENCIL_PATH + '/basic.xml'];
mxStencilRegistry.libraries['ios7icons'] = [STENCIL_PATH + '/ios7/icons.xml'];
mxStencilRegistry.libraries['ios7ui'] = [SHAPES_PATH + '/ios7/mxIOS7Ui.js', STENCIL_PATH + '/ios7/misc.xml'];
mxStencilRegistry.libraries['android'] = [SHAPES_PATH + '/mxAndroid.js', STENCIL_PATH + '/android/android.xml'];
mxStencilRegistry.libraries['electrical/abstract'] = [SHAPES_PATH + '/mxElectrical.js', STENCIL_PATH + '/electrical/abstract.xml'];
mxStencilRegistry.libraries['electrical/logic_gates'] = [SHAPES_PATH + '/mxElectrical.js', STENCIL_PATH + '/electrical/logic_gates.xml'];
mxStencilRegistry.libraries['electrical/miscellaneous'] = [SHAPES_PATH + '/mxElectrical.js', STENCIL_PATH + '/electrical/miscellaneous.xml'];
mxStencilRegistry.libraries['electrical/signal_sources'] = [SHAPES_PATH + '/mxElectrical.js', STENCIL_PATH + '/electrical/signal_sources.xml'];
mxStencilRegistry.libraries['electrical/electro-mechanical'] = [SHAPES_PATH + '/mxElectrical.js', STENCIL_PATH + '/electrical/electro-mechanical.xml'];
mxStencilRegistry.libraries['electrical/transmission'] = [SHAPES_PATH + '/mxElectrical.js', STENCIL_PATH + '/electrical/transmission.xml'];
mxStencilRegistry.libraries['infographic'] = [SHAPES_PATH + '/mxInfographic.js'];
mxStencilRegistry.libraries['mockup/buttons'] = [SHAPES_PATH + '/mockup/mxMockupButtons.js'];
mxStencilRegistry.libraries['mockup/containers'] = [SHAPES_PATH + '/mockup/mxMockupContainers.js'];
mxStencilRegistry.libraries['mockup/forms'] = [SHAPES_PATH + '/mockup/mxMockupForms.js'];
mxStencilRegistry.libraries['mockup/graphics'] = [SHAPES_PATH + '/mockup/mxMockupGraphics.js', STENCIL_PATH + '/mockup/misc.xml'];
mxStencilRegistry.libraries['mockup/markup'] = [SHAPES_PATH + '/mockup/mxMockupMarkup.js'];
mxStencilRegistry.libraries['mockup/misc'] = [SHAPES_PATH + '/mockup/mxMockupMisc.js', STENCIL_PATH + '/mockup/misc.xml'];
mxStencilRegistry.libraries['mockup/navigation'] = [SHAPES_PATH + '/mockup/mxMockupNavigation.js', STENCIL_PATH + '/mockup/misc.xml'];
mxStencilRegistry.libraries['mockup/text'] = [SHAPES_PATH + '/mockup/mxMockupText.js'];
mxStencilRegistry.libraries['floorplan'] = [SHAPES_PATH + '/mxFloorplan.js', STENCIL_PATH + '/floorplan.xml'];
mxStencilRegistry.libraries['bootstrap'] = [SHAPES_PATH + '/mxBootstrap.js', SHAPES_PATH + '/mxBasic.js', STENCIL_PATH + '/bootstrap.xml'];
mxStencilRegistry.libraries['gmdl'] = [SHAPES_PATH + '/mxGmdl.js', STENCIL_PATH + '/gmdl.xml'];
mxStencilRegistry.libraries['gcp2'] = [SHAPES_PATH + '/mxGCP2.js', STENCIL_PATH + '/gcp2.xml'];
mxStencilRegistry.libraries['ibm'] = [SHAPES_PATH + '/mxIBM.js', STENCIL_PATH + '/ibm.xml'];
mxStencilRegistry.libraries['ibmcloud'] = [STENCIL_PATH + '/ibm_cloud.xml'];
mxStencilRegistry.libraries['cabinets'] = [SHAPES_PATH + '/mxCabinets.js', STENCIL_PATH + '/cabinets.xml'];
mxStencilRegistry.libraries['archimate'] = [SHAPES_PATH + '/mxArchiMate.js'];
mxStencilRegistry.libraries['archimate3'] = [SHAPES_PATH + '/mxArchiMate3.js'];
mxStencilRegistry.libraries['sysml'] = [SHAPES_PATH + '/mxSysML.js'];
mxStencilRegistry.libraries['eip'] = [SHAPES_PATH + '/mxEip.js', STENCIL_PATH + '/eip.xml'];
mxStencilRegistry.libraries['networks'] = [SHAPES_PATH + '/mxNetworks.js', STENCIL_PATH + '/networks.xml'];
mxStencilRegistry.libraries['networks2'] = [SHAPES_PATH + '/mxNetworks2.js', STENCIL_PATH + '/networks2.xml'];
mxStencilRegistry.libraries['aws3d'] = [SHAPES_PATH + '/mxAWS3D.js', STENCIL_PATH + '/aws3d.xml'];
mxStencilRegistry.libraries['aws4'] = [SHAPES_PATH + '/mxAWS4.js', STENCIL_PATH + '/aws4.xml'];
mxStencilRegistry.libraries['aws4b'] = [SHAPES_PATH + '/mxAWS4.js', STENCIL_PATH + '/aws4.xml'];
mxStencilRegistry.libraries['uml25'] = [SHAPES_PATH + '/mxUML25.js'];
mxStencilRegistry.libraries['veeam'] = [STENCIL_PATH + '/veeam/2d.xml', STENCIL_PATH + '/veeam/3d.xml', STENCIL_PATH + '/veeam/veeam.xml'];
mxStencilRegistry.libraries['veeam2'] = [STENCIL_PATH + '/veeam/2d.xml', STENCIL_PATH + '/veeam/3d.xml', STENCIL_PATH + '/veeam/veeam2.xml'];
mxStencilRegistry.libraries['pid2inst'] = [SHAPES_PATH + '/pid2/mxPidInstruments.js'];
mxStencilRegistry.libraries['pid2misc'] = [SHAPES_PATH + '/pid2/mxPidMisc.js', STENCIL_PATH + '/pid/misc.xml'];
mxStencilRegistry.libraries['pid2valves'] = [SHAPES_PATH + '/pid2/mxPidValves.js'];
mxStencilRegistry.libraries['pidFlowSensors'] = [STENCIL_PATH + '/pid/flow_sensors.xml'];
mxStencilRegistry.libraries['salesforce'] = [STENCIL_PATH + '/salesforce.xml'];
mxStencilRegistry.libraries['sap'] = [SHAPES_PATH + '/mxSAP.js', STENCIL_PATH + '/sap.xml'];
mxStencilRegistry.libraries['emoji'] = [SHAPES_PATH + '/emoji/mxEmoji.js'];
// Triggers dynamic loading for markers
mxMarker.getPackageForType = function(type)
{
var name = null;
if (type != null && type.length > 0)
{
if (type.substring(0, 2) == 'ER')
{
name = 'mxgraph.er';
}
else if (type.substring(0, 5) == 'sysML')
{
name = 'mxgraph.sysml';
}
}
return name;
};
var mxMarkerCreateMarker = mxMarker.createMarker;
mxMarker.createMarker = function(canvas, shape, type, pe, unitX, unitY, size, source, sw, filled)
{
if (type != null)
{
var f = mxMarker.markers[type];
if (f == null)
{
var name = this.getPackageForType(type);
if (name != null)
{
mxStencilRegistry.getStencil(name);
}
}
}
return mxMarkerCreateMarker.apply(this, arguments);
};
/**
* Adds style to mark stencils as lines.
*/
var mxStencilDrawShape = mxStencil.prototype.drawShape;
mxStencil.prototype.drawShape = function(canvas, shape, x, y, w, h)
{
if (mxUtils.getValue(shape.style, 'lineShape', null) == '1')
{
canvas.setFillColor(mxUtils.getValue(shape.style,
mxConstants.STYLE_STROKECOLOR, this.stroke));
}
return mxStencilDrawShape.apply(this, arguments);
};
/**
* Constructs a new print dialog.
*/
PrintDialog.prototype.create = function(editorUi, titleText, fn, btnTitle)
{
var graph = editorUi.editor.graph;
var div = document.createElement('div');
var title = document.createElement('h3');
title.style.width = '100%';
title.style.textAlign = 'center';
title.style.marginTop = '0px';
mxUtils.write(title, titleText || mxResources.get('print'));
div.appendChild(title);
var currentPage = 1;
var pageCount = 1;
// Pages
var pagesSection = document.createElement('div');
pagesSection.style.whiteSpace = 'nowrap';
var allPagesRadio = document.createElement('input');
allPagesRadio.style.marginRight = '8px';
allPagesRadio.style.marginBottom = '8px';
allPagesRadio.setAttribute('type', 'radio');
allPagesRadio.setAttribute('name', 'pages-printdialog');
pagesSection.appendChild(allPagesRadio);
var span = document.createElement('span');
mxUtils.write(span, mxResources.get('allPages'));
mxEvent.addListener(span, 'click', function()
{
allPagesRadio.checked = true;
});
pagesSection.appendChild(span);
mxUtils.br(pagesSection);
// Page range
var pagesRadio = allPagesRadio.cloneNode(true);
pagesSection.appendChild(pagesRadio);
var span = document.createElement('span');
mxUtils.write(span, mxResources.get('pages') + ':');
pagesSection.appendChild(span);
mxEvent.addListener(span, 'click', function()
{
pagesRadio.checked = true;
});
var pagesFromInput = document.createElement('input');
pagesFromInput.style.margin = '0 4px';
pagesFromInput.setAttribute('value', '1');
pagesFromInput.setAttribute('type', 'number');
pagesFromInput.setAttribute('min', '1');
pagesFromInput.style.width = '40px';
pagesSection.appendChild(pagesFromInput);
var span = document.createElement('span');
mxUtils.write(span, mxResources.get('to'));
pagesSection.appendChild(span);
var pagesToInput = pagesFromInput.cloneNode(true);
pagesSection.appendChild(pagesToInput);
mxEvent.addListener(pagesFromInput, 'focus', function()
{
pagesRadio.checked = true;
});
mxEvent.addListener(pagesToInput, 'focus', function()
{
pagesRadio.checked = true;
});
function validatePageRange()
{
pagesToInput.value = Math.max(1, Math.min(pageCount,
Math.max(parseInt(pagesToInput.value), parseInt(pagesFromInput.value))));
pagesFromInput.value = Math.max(1, Math.min(pageCount,
Math.min(parseInt(pagesToInput.value), parseInt(pagesFromInput.value))));
};
mxEvent.addListener(pagesFromInput, 'change', validatePageRange);
mxEvent.addListener(pagesToInput, 'change', validatePageRange);
if (editorUi.pages != null)
{
pageCount = editorUi.pages.length;
if (editorUi.currentPage != null)
{
for (var i = 0; i < pageCount; i++)
{
if (editorUi.currentPage == editorUi.pages[i])
{
currentPage = i + 1;
break;
}
}
}
}
if (editorUi.lastPrintPagesFromInput != null &&
editorUi.lastPrintPagesToInput != null)
{
pagesFromInput.value = Math.min(pageCount,
Math.max(1, editorUi.lastPrintPagesFromInput));
pagesToInput.value = Math.min(pageCount,
Math.max(1, editorUi.lastPrintPagesToInput));
}
else
{
pagesFromInput.value = currentPage;
pagesToInput.value = currentPage;
}
pagesFromInput.setAttribute('max', pageCount);
pagesToInput.setAttribute('max', pageCount);
var currPage = mxUtils.button(mxResources.get('currentPage'), function(evt)
{
pagesFromInput.value = currentPage;
pagesToInput.value = currentPage;
pagesRadio.checked = true;
});
currPage.setAttribute('title', mxResources.get('currentPage'));
currPage.style.marginLeft = '4px';
currPage.style.maxWidth = '100px';
currPage.style.overflow = 'hidden';
currPage.style.textOverflow = 'ellipsis';
currPage.style.whiteSpace = 'nowrap';
pagesSection.appendChild(currPage);
if (pageCount > 1)
{
div.appendChild(pagesSection);
}
// Selection only
var selectionSection = document.createElement('div');
selectionSection.style.borderBottom = '1px solid lightGray';
selectionSection.style.paddingBottom = '12px';
selectionSection.style.marginBottom = '12px';
selectionSection.style.whiteSpace = 'nowrap';
var selectionOnlyRadio = document.createElement('input');
selectionOnlyRadio.setAttribute('name', 'pages-printdialog');
selectionOnlyRadio.setAttribute('type', (pageCount == 1) ? 'checkbox' : 'radio');
selectionOnlyRadio.style.marginRight = '8px';
if (graph.isSelectionEmpty())
{
selectionOnlyRadio.setAttribute('disabled', 'disabled');
}
if (graph.isEnabled())
{
selectionSection.appendChild(selectionOnlyRadio);
var span = document.createElement('span');
mxUtils.write(span, mxResources.get('selectionOnly'));
selectionSection.appendChild(span);
}
if (graph.isEnabled() || pageCount > 1)
{
div.appendChild(selectionSection);
}
if (!editorUi.isPagesEnabled() || editorUi.lastPrintPagesRadioChecked)
{
pagesRadio.checked = true;
}
else if (!graph.isSelectionEmpty() && editorUi.lastPrintSelectionOnlyChecked)
{
selectionOnlyRadio.checked = true;
}
else
{
allPagesRadio.checked = true;
}
if (!graph.isSelectionEmpty())
{
mxEvent.addListener(span, 'click', function()
{
selectionOnlyRadio.checked = !selectionOnlyRadio.checked;
});
}
// Page view
var pageViewSection = document.createElement('div');
pageViewSection.style.whiteSpace = 'nowrap';
var pageViewRadio = document.createElement('input');
pageViewRadio.style.marginBottom = '8px';
pageViewRadio.style.marginRight = '8px';
pageViewRadio.setAttribute('type', 'radio');
pageViewRadio.setAttribute('name', 'printSize');
pageViewSection.appendChild(pageViewRadio);
var span = document.createElement('span');
mxUtils.write(span, mxResources.get('pageView'));
pageViewSection.appendChild(span);
mxEvent.addListener(pageViewSection, 'click', function()
{
pageViewRadio.checked = true;
});
div.appendChild(pageViewSection);
// Crop
var cropSection = document.createElement('div');
cropSection.style.whiteSpace = 'nowrap';
var cropRadio = document.createElement('input');
cropRadio.style.marginBottom = '8px';
cropRadio.style.marginRight = '8px';
cropRadio.setAttribute('type', 'radio');
cropRadio.setAttribute('name', 'printSize');
cropSection.appendChild(cropRadio);
var span = document.createElement('span');
mxUtils.write(span, mxResources.get('crop'));
cropSection.appendChild(span);
mxEvent.addListener(cropSection, 'click', function()
{
cropRadio.checked = true;
});
div.appendChild(cropSection);
// Fit to ...
var fitSection = document.createElement('div');
fitSection.style.whiteSpace = 'nowrap';
var fitRadio = document.createElement('input');
fitRadio.style.marginBottom = '8px';
fitRadio.style.marginRight = '8px';
fitRadio.setAttribute('type', 'radio');
fitRadio.setAttribute('name', 'printSize');
var spanFitRadio = document.createElement('div');
spanFitRadio.style.display = 'inline-block';
spanFitRadio.style.verticalAlign = 'top';
spanFitRadio.style.paddingTop = '2px';
spanFitRadio.appendChild(fitRadio);
fitSection.appendChild(spanFitRadio);
var table = document.createElement('table');
table.style.display = 'inline-block';
table.style.borderSpacing = '0';
var tbody = document.createElement('tbody');
var row1 = document.createElement('tr');
var row2 = row1.cloneNode(true);
var td1 = document.createElement('td');
var td2 = td1.cloneNode(true);
var td3 = td1.cloneNode(true);
var td4 = td1.cloneNode(true);
var td5 = td1.cloneNode(true);
var td6 = td1.cloneNode(true);
td1.style.textAlign = 'right';
td4.style.textAlign = 'right';
mxUtils.write(td1, mxResources.get('fitTo'));
mxEvent.addListener(fitSection, 'click', function(e)
{
if (mxEvent.getSource(e) != fitRadio)
{
fitRadio.checked = !fitRadio.checked;
}
});
var sheetsAcrossInput = document.createElement('input');
sheetsAcrossInput.style.margin = '0 2px';
sheetsAcrossInput.style.boxSizing = 'border-box';
sheetsAcrossInput.setAttribute('value', editorUi.lastPrintSheetsAcross || '1');
sheetsAcrossInput.setAttribute('min', '1');
sheetsAcrossInput.setAttribute('type', 'number');
sheetsAcrossInput.style.width = '40px';
td2.appendChild(sheetsAcrossInput);
var span = document.createElement('span');
mxUtils.write(span, mxResources.get('fitToSheetsAcross'));
td3.appendChild(span);
mxUtils.write(td4, mxResources.get('fitToBy'));
var sheetsDownInput = sheetsAcrossInput.cloneNode(true);
sheetsDownInput.setAttribute('value', editorUi.lastPrintSheetsDown || '1');
td5.appendChild(sheetsDownInput);
mxEvent.addListener(sheetsAcrossInput, 'click', function(e)
{
fitRadio.checked = true;
mxEvent.consume(e);
});
mxEvent.addListener(sheetsDownInput, 'click', function(e)
{
fitRadio.checked = true;
mxEvent.consume(e);
});
if (editorUi.lastPrintCropRadioChecked)
{
cropRadio.checked = true;
}
else if (editorUi.lastPrintFitRadioChecked)
{
fitRadio.checked = true;
}
else
{
pageViewRadio.checked = true;
}
var span = document.createElement('span');
mxUtils.write(span, mxResources.get('fitToSheetsDown'));
td6.appendChild(span);
row1.appendChild(td1);
row1.appendChild(td2);
row1.appendChild(td3);
row2.appendChild(td4);
row2.appendChild(td5);
row2.appendChild(td6);
tbody.appendChild(row1);
tbody.appendChild(row2);
table.appendChild(tbody);
fitSection.appendChild(table);
div.appendChild(fitSection);
// Border and zoom
var optionsSection = document.createElement('div');
optionsSection.style.borderTop = '1px solid lightGray';
optionsSection.style.whiteSpace = 'nowrap';
optionsSection.style.paddingTop = '12px';
optionsSection.style.marginTop = '12px';
optionsSection.style.paddingLeft = '8px';
mxUtils.write(optionsSection, mxResources.get('borderWidth') + ':');
var borderInput = document.createElement('input');
borderInput.setAttribute('type', 'number');
borderInput.setAttribute('min', '0');
borderInput.style.width = '40px';
borderInput.style.marginLeft = '4px';
borderInput.value = (editorUi.lastPrintBorder != null) ?
editorUi.lastPrintBorder : mxPrintPreview.prototype.pageMargin;
optionsSection.appendChild(borderInput);
var span = document.createElement('span');
span.style.marginLeft = '8px';
mxUtils.write(span, mxResources.get('zoom') + ':');
optionsSection.appendChild(span);
var zoomInput = document.createElement('input');
zoomInput.style.width = '60px';
zoomInput.style.marginLeft = '4px';
zoomInput.value = (editorUi.lastPrintZoom != null) ?
editorUi.lastPrintZoom : '100%';
optionsSection.appendChild(zoomInput);
mxUtils.br(optionsSection);
// Grid
var gridInput = document.createElement('input');
gridInput.setAttribute('type', 'checkbox');
gridInput.style.marginTop = '12px';
gridInput.checked = (editorUi.lastPrintGrid != null) ?
editorUi.lastPrintGrid : false;
optionsSection.appendChild(gridInput);
var span = document.createElement('span');
span.style.marginLeft = '4px';
span.style.marginRight = '8px';
mxUtils.write(span, mxResources.get('grid'));
optionsSection.appendChild(span);
mxEvent.addListener(span, 'click', function(e)
{
gridInput.checked = true;
mxEvent.consume(e);
});
// Shadows enabled
var shadowsInput = document.createElement('input');
shadowsInput.setAttribute('type', 'checkbox');
shadowsInput.style.marginTop = '12px';
shadowsInput.checked = (editorUi.lastPrintShadow != null) ?
editorUi.lastPrintShadow : false;
optionsSection.appendChild(shadowsInput);
var span = document.createElement('span');
span.style.marginLeft = '4px';
span.style.marginRight = '8px';
mxUtils.write(span, mxResources.get('shadows'));
optionsSection.appendChild(span);
if (!editorUi.isOffline() || mxClient.IS_CHROMEAPP)
{
span.appendChild(editorUi.createHelpIcon(
'https://github.com/jgraph/drawio/discussions/5136'));
}
mxEvent.addListener(span, 'click', function(e)
{
if (mxEvent.getSource(e).nodeName != 'IMG')
{
shadowsInput.checked = true;
mxEvent.consume(e);
}
});
// Hides shadows option if not supported
if (!Editor.enableShadowOption)
{
shadowsInput.style.display = 'none';
span.style.display = 'none';
}
else if (fn != null)
{
mxUtils.br(optionsSection);
}
// Transparent background
var transparentInput = document.createElement('input');
transparentInput.setAttribute('type', 'checkbox');
transparentInput.style.marginTop = '10px';
transparentInput.checked = (editorUi.lastPrintTransparent != null) ?
editorUi.lastPrintTransparent : false;
// Export
if (fn != null)
{
optionsSection.appendChild(transparentInput);
var span = document.createElement('span');
span.style.marginLeft = '4px';
mxUtils.write(span, mxResources.get('transparentBackground'));
optionsSection.appendChild(span);
mxEvent.addListener(span, 'click', function(e)
{
transparentInput.checked = true;
mxEvent.consume(e);
});
}
// Include diagram
var includeInput = document.createElement('input');
includeInput.setAttribute('type', 'checkbox');
includeInput.style.marginTop = '10px';
includeInput.checked = (editorUi.lastPrintInclude != null) ?
editorUi.lastPrintInclude : Editor.defaultIncludeDiagram;
if (fn != null && !mxClient.IS_CHROMEAPP &&
editorUi.getServiceName() == 'draw.io')
{
mxUtils.br(optionsSection);
optionsSection.appendChild(includeInput);
var span = document.createElement('span');
span.style.marginLeft = '4px';
mxUtils.write(span, mxResources.get('includeCopyOfMyDiagram'));
optionsSection.appendChild(span);
mxEvent.addListener(span, 'click', function(e)
{
includeInput.checked = true;
mxEvent.consume(e);
});
}
div.appendChild(optionsSection);
// Buttons
var buttons = document.createElement('div');
buttons.style.marginTop = '30px';
buttons.style.textAlign = 'right';
buttons.style.whiteSpace = 'nowrap';
// Preview or print
function preview(print)
{
editorUi.lastPrintPagesRadioChecked = pagesRadio.checked;
editorUi.lastPrintSelectionOnlyChecked = selectionOnlyRadio.checked;
editorUi.lastPrintCropRadioChecked = cropRadio.checked;
editorUi.lastPrintFitRadioChecked = fitRadio.checked;
if (pagesRadio.checked && pagesFromInput.value < pagesToInput.value)
{
editorUi.lastPrintPagesFromInput = pagesFromInput.value;
editorUi.lastPrintPagesToInput = pagesToInput.value;
}
else
{
editorUi.lastPrintPagesFromInput = null;
editorUi.lastPrintPagesToInput = null;
}
zoomInput.value = Math.max(1, Math.min(1600, parseInt(zoomInput.value))) + '%';
editorUi.lastPrintZoom = zoomInput.value;
editorUi.lastPrintBorder = borderInput.value;
editorUi.lastPrintGrid = gridInput.checked;
editorUi.lastPrintTransparent = transparentInput.checked;
editorUi.lastPrintInclude = includeInput.checked;
editorUi.lastPrintSheetsAcross = sheetsAcrossInput.value;
editorUi.lastPrintSheetsDown = sheetsDownInput.value;
var args = {};
args.border = parseInt(borderInput.value);
args.scale = parseInt(zoomInput.value) / 100;
args.grid = gridInput.checked;
args.shadows = shadowsInput.checked;
args.transparent = transparentInput.checked;
args.includeCopy = includeInput.checked;
args.sheetsAcross = parseInt(sheetsAcrossInput.value);
args.sheetsDown = parseInt(sheetsDownInput.value);
args.pagesFrom = parseInt(pagesFromInput.value);
args.pagesTo = parseInt(pagesToInput.value);
args.allPages = allPagesRadio.checked;
args.selection = selectionOnlyRadio.checked;
args.pageView = pageViewRadio.checked;
args.crop = cropRadio.checked;
args.fit = fitRadio.checked;
return (fn != null) ? fn(!print, args) : editorUi.print(!print, args);
};
if ((!editorUi.isOffline() || mxClient.IS_CHROMEAPP) && fn == null)
{
buttons.appendChild(editorUi.createHelpIcon(
'https://www.drawio.com/doc/faq/print-diagram'));
}
var cancelBtn = mxUtils.button(mxResources.get('cancel'), function()
{
editorUi.hideDialog();
});
cancelBtn.className = 'geBtn';
if (editorUi.editor.cancelFirst)
{
buttons.appendChild(cancelBtn);
}
if (PrintDialog.previewEnabled && fn == null)
{
var previewBtn = mxUtils.button(mxResources.get('preview'), function()
{
try
{
editorUi.hideDialog();
preview(false);
}
catch (e)
{
editorUi.handleError(e);
}
});
previewBtn.className = 'geBtn';
buttons.appendChild(previewBtn);
}
btnTitle = (btnTitle != null) ? btnTitle : (fn != null) ? 'export' :
(!PrintDialog.previewEnabled) ? 'ok' : 'print';
var printBtn = mxUtils.button(mxResources.get(btnTitle), function()
{
try
{
editorUi.hideDialog();
preview(true);
}
catch (e)
{
editorUi.handleError(e);
}
});
printBtn.className = 'geBtn gePrimaryBtn';
buttons.appendChild(printBtn);
if (!editorUi.editor.cancelFirst)
{
buttons.appendChild(cancelBtn);
}
div.appendChild(buttons);
// Handles dark mode for standalone print dialog in lightbox
if (editorUi.editor.isChromelessView())
{
function updateDarkMode()
{
if (Editor.isDarkMode())
{
div.classList.add('geDarkMode');
div.style.colorScheme = 'dark';
}
else
{
div.classList.remove('geDarkMode');
div.style.colorScheme = 'light';
}
};
editorUi.addListener('darkModeChanged', updateDarkMode);
updateDarkMode();
// Removes listeners after dialog was closed
var removeListener = function(sender, evt)
{
var dialog = evt.getProperty('dialog');
if (div.parentNode == dialog.container)
{
editorUi.editor.removeListener(removeListener);
editorUi.removeListener(updateDarkMode);
}
};
editorUi.editor.addListener('hideDialog', removeListener);
}
this.container = div;
};
// Execute fit page on page setup changes
var changePageSetupExecute = ChangePageSetup.prototype.execute;
ChangePageSetup.prototype.execute = function()
{
if (this.page == null)
{
this.page = this.ui.currentPage;
}
// Workaround for redo existing change with different current page
if (this.page != this.ui.currentPage)
{
if (this.page.viewState != null)
{
if (!this.ignoreColor)
{
this.page.viewState.background = this.color;
}
if (!this.ignoreImage)
{
var img = this.image;
if (img != null && img.src != null && Graph.isPageLink(img.src))
{
img = {originalSrc: img.src};
}
this.page.viewState.backgroundImage = img;
}
if (this.format != null)
{
this.page.viewState.pageFormat = this.format;
}
if (this.mathEnabled != null)
{
this.page.viewState.mathEnabled = this.mathEnabled;
}
if (this.adaptiveColors != null)
{
this.page.viewState.adaptiveColors = this.adaptiveColors;
}
if (this.shadowVisible != null)
{
this.page.viewState.shadowVisible = this.shadowVisible;
}
}
}
else
{
changePageSetupExecute.apply(this, arguments);
if (this.mathEnabled != null && this.mathEnabled != this.ui.isMathEnabled())
{
this.ui.setMathEnabled(this.mathEnabled);
this.mathEnabled = !this.mathEnabled;
}
if (this.adaptiveColors != null && this.adaptiveColors != this.ui.editor.graph.adaptiveColors)
{
var temp = this.ui.editor.graph.adaptiveColors
this.ui.setAdaptiveColors(this.adaptiveColors);
this.adaptiveColors = (temp != null) ? temp : 'default';
}
if (this.shadowVisible != null && this.shadowVisible != this.ui.editor.graph.shadowVisible)
{
this.ui.editor.graph.setShadowVisible(this.shadowVisible);
this.shadowVisible = !this.shadowVisible;
}
}
};
})();