diff options
Diffstat (limited to 'src/js/util/mxUtils.js')
-rw-r--r-- | src/js/util/mxUtils.js | 3920 |
1 files changed, 0 insertions, 3920 deletions
diff --git a/src/js/util/mxUtils.js b/src/js/util/mxUtils.js deleted file mode 100644 index 34c0318..0000000 --- a/src/js/util/mxUtils.js +++ /dev/null @@ -1,3920 +0,0 @@ -/** - * $Id: mxUtils.js,v 1.297 2012-12-07 19:47:29 gaudenz Exp $ - * Copyright (c) 2006-2010, JGraph Ltd - */ -var mxUtils = -{ - /** - * Class: mxUtils - * - * A singleton class that provides cross-browser helper methods. - * This is a global functionality. To access the functions in this - * class, use the global classname appended by the functionname. - * You may have to load chrome://global/content/contentAreaUtils.js - * to disable certain security restrictions in Mozilla for the <open>, - * <save>, <saveAs> and <copy> function. - * - * For example, the following code displays an error message: - * - * (code) - * mxUtils.error('Browser is not supported!', 200, false); - * (end) - * - * Variable: errorResource - * - * Specifies the resource key for the title of the error window. If the - * resource for this key does not exist then the value is used as - * the title. Default is 'error'. - */ - errorResource: (mxClient.language != 'none') ? 'error' : '', - - /** - * Variable: closeResource - * - * Specifies the resource key for the label of the close button. If the - * resource for this key does not exist then the value is used as - * the label. Default is 'close'. - */ - closeResource: (mxClient.language != 'none') ? 'close' : '', - - /** - * Variable: errorImage - * - * Defines the image used for error dialogs. - */ - errorImage: mxClient.imageBasePath + '/error.gif', - - /** - * Function: removeCursors - * - * Removes the cursors from the style of the given DOM node and its - * descendants. - * - * Parameters: - * - * element - DOM node to remove the cursor style from. - */ - removeCursors: function(element) - { - if (element.style != null) - { - element.style.cursor = ''; - } - - var children = element.childNodes; - - if (children != null) - { - var childCount = children.length; - - for (var i = 0; i < childCount; i += 1) - { - mxUtils.removeCursors(children[i]); - } - } - }, - - /** - * Function: repaintGraph - * - * Normally not required, this contains the code to workaround a repaint - * issue and force a repaint of the graph container in AppleWebKit. - * - * Parameters: - * - * graph - <mxGraph> to be repainted. - * pt - <mxPoint> where the dummy element should be placed. - */ - repaintGraph: function(graph, pt) - { - if (mxClient.IS_GC || mxClient.IS_SF || mxClient.IS_OP) - { - var c = graph.container; - - if (c != null && pt != null && (c.scrollLeft > 0 || c.scrollTop > 0)) - { - var dummy = document.createElement('div'); - dummy.style.position = 'absolute'; - dummy.style.left = pt.x + 'px'; - dummy.style.top = pt.y + 'px'; - dummy.style.width = '1px'; - dummy.style.height = '1px'; - - c.appendChild(dummy); - c.removeChild(dummy); - } - } - }, - - /** - * Function: getCurrentStyle - * - * Returns the current style of the specified element. - * - * Parameters: - * - * element - DOM node whose current style should be returned. - */ - getCurrentStyle: function() - { - if (mxClient.IS_IE) - { - return function(element) - { - return (element != null) ? element.currentStyle : null; - }; - } - else - { - return function(element) - { - return (element != null) ? - window.getComputedStyle(element, '') : - null; - }; - } - }(), - - /** - * Function: hasScrollbars - * - * Returns true if the overflow CSS property of the given node is either - * scroll or auto. - * - * Parameters: - * - * node - DOM node whose style should be checked for scrollbars. - */ - hasScrollbars: function(node) - { - var style = mxUtils.getCurrentStyle(node); - - return style != null && (style.overflow == 'scroll' || style.overflow == 'auto'); - }, - - /** - * Function: bind - * - * Returns a wrapper function that locks the execution scope of the given - * function to the specified scope. Inside funct, the "this" keyword - * becomes a reference to that scope. - */ - bind: function(scope, funct) - { - return function() - { - return funct.apply(scope, arguments); - }; - }, - - /** - * Function: eval - * - * Evaluates the given expression using eval and returns the JavaScript - * object that represents the expression result. Supports evaluation of - * expressions that define functions and returns the function object for - * these expressions. - * - * Parameters: - * - * expr - A string that represents a JavaScript expression. - */ - eval: function(expr) - { - var result = null; - - if (expr.indexOf('function') >= 0) - { - try - { - eval('var _mxJavaScriptExpression='+expr); - result = _mxJavaScriptExpression; - // TODO: Use delete here? - _mxJavaScriptExpression = null; - } - catch (e) - { - mxLog.warn(e.message + ' while evaluating ' + expr); - } - } - else - { - try - { - result = eval(expr); - } - catch (e) - { - mxLog.warn(e.message + ' while evaluating ' + expr); - } - } - - return result; - }, - - /** - * Function: findNode - * - * Returns the first node where attr equals value. - * This implementation does not use XPath. - */ - findNode: function(node, attr, value) - { - var tmp = node.getAttribute(attr); - - if (tmp != null && tmp == value) - { - return node; - } - - node = node.firstChild; - - while (node != null) - { - var result = mxUtils.findNode(node, attr, value); - - if (result != null) - { - return result; - } - - node = node.nextSibling; - } - - return null; - }, - - /** - * Function: findNodeByAttribute - * - * Returns the first node where the given attribute matches the given value. - * - * Parameters: - * - * node - Root node where the search should start. - * attr - Name of the attribute to be checked. - * value - Value of the attribute to match. - */ - findNodeByAttribute: function() - { - // Workaround for missing XPath support in IE9 - if (document.documentMode >= 9) - { - return function(node, attr, value) - { - var result = null; - - if (node != null) - { - if (node.nodeType == mxConstants.NODETYPE_ELEMENT && node.getAttribute(attr) == value) - { - result = node; - } - else - { - var child = node.firstChild; - - while (child != null && result == null) - { - result = mxUtils.findNodeByAttribute(child, attr, value); - child = child.nextSibling; - } - } - } - - return result; - }; - } - else if (mxClient.IS_IE) - { - return function(node, attr, value) - { - if (node == null) - { - return null; - } - else - { - var expr = '//*[@' + attr + '=\'' + value + '\']'; - - return node.ownerDocument.selectSingleNode(expr); - } - }; - } - else - { - return function(node, attr, value) - { - if (node == null) - { - return null; - } - else - { - var result = node.ownerDocument.evaluate( - '//*[@' + attr + '=\'' + value + '\']', - node.ownerDocument, null, - XPathResult.ANY_TYPE, null); - - return result.iterateNext(); - } - }; - } - }(), - - /** - * Function: getFunctionName - * - * Returns the name for the given function. - * - * Parameters: - * - * f - JavaScript object that represents a function. - */ - getFunctionName: function(f) - { - var str = null; - - if (f != null) - { - if (f.name != null) - { - str = f.name; - } - else - { - var tmp = f.toString(); - var idx1 = 9; - - while (tmp.charAt(idx1) == ' ') - { - idx1++; - } - - var idx2 = tmp.indexOf('(', idx1); - str = tmp.substring(idx1, idx2); - } - } - - return str; - }, - - /** - * Function: indexOf - * - * Returns the index of obj in array or -1 if the array does not contains - * the given object. - * - * Parameters: - * - * array - Array to check for the given obj. - * obj - Object to find in the given array. - */ - indexOf: function(array, obj) - { - if (array != null && obj != null) - { - for (var i = 0; i < array.length; i++) - { - if (array[i] == obj) - { - return i; - } - } - } - - return -1; - }, - - /** - * Function: remove - * - * Removes all occurrences of the given object in the given array or - * object. If there are multiple occurrences of the object, be they - * associative or as an array entry, all occurrences are removed from - * the array or deleted from the object. By removing the object from - * the array, all elements following the removed element are shifted - * by one step towards the beginning of the array. - * - * The length of arrays is not modified inside this function. - * - * Parameters: - * - * obj - Object to find in the given array. - * array - Array to check for the given obj. - */ - remove: function(obj, array) - { - var result = null; - - if (typeof(array) == 'object') - { - var index = mxUtils.indexOf(array, obj); - - while (index >= 0) - { - array.splice(index, 1); - result = obj; - index = mxUtils.indexOf(array, obj); - } - } - - for (var key in array) - { - if (array[key] == obj) - { - delete array[key]; - result = obj; - } - } - - return result; - }, - - /** - * Function: isNode - * - * Returns true if the given value is an XML node with the node name - * and if the optional attribute has the specified value. - * - * This implementation assumes that the given value is a DOM node if the - * nodeType property is numeric, that is, if isNaN returns false for - * value.nodeType. - * - * Parameters: - * - * value - Object that should be examined as a node. - * nodeName - String that specifies the node name. - * attributeName - Optional attribute name to check. - * attributeValue - Optional attribute value to check. - */ - isNode: function(value, nodeName, attributeName, attributeValue) - { - if (value != null && !isNaN(value.nodeType) && (nodeName == null || - value.nodeName.toLowerCase() == nodeName.toLowerCase())) - { - return attributeName == null || - value.getAttribute(attributeName) == attributeValue; - } - - return false; - }, - - /** - * Function: getChildNodes - * - * Returns an array of child nodes that are of the given node type. - * - * Parameters: - * - * node - Parent DOM node to return the children from. - * nodeType - Optional node type to return. Default is - * <mxConstants.NODETYPE_ELEMENT>. - */ - getChildNodes: function(node, nodeType) - { - nodeType = nodeType || mxConstants.NODETYPE_ELEMENT; - - var children = []; - var tmp = node.firstChild; - - while (tmp != null) - { - if (tmp.nodeType == nodeType) - { - children.push(tmp); - } - - tmp = tmp.nextSibling; - } - - return children; - }, - - /** - * Function: createXmlDocument - * - * Returns a new, empty XML document. - */ - createXmlDocument: function() - { - var doc = null; - - if (document.implementation && document.implementation.createDocument) - { - doc = document.implementation.createDocument('', '', null); - } - else if (window.ActiveXObject) - { - doc = new ActiveXObject('Microsoft.XMLDOM'); - } - - return doc; - }, - - /** - * Function: parseXml - * - * Parses the specified XML string into a new XML document and returns the - * new document. - * - * Example: - * - * (code) - * var doc = mxUtils.parseXml( - * '<mxGraphModel><root><MyDiagram id="0"><mxCell/></MyDiagram>'+ - * '<MyLayer id="1"><mxCell parent="0" /></MyLayer><MyObject id="2">'+ - * '<mxCell style="strokeColor=blue;fillColor=red" parent="1" vertex="1">'+ - * '<mxGeometry x="10" y="10" width="80" height="30" as="geometry"/>'+ - * '</mxCell></MyObject></root></mxGraphModel>'); - * (end) - * - * Parameters: - * - * xml - String that contains the XML data. - */ - parseXml: function() - { - if (mxClient.IS_IE && (typeof(document.documentMode) === 'undefined' || document.documentMode < 9)) - { - return function(xml) - { - var result = mxUtils.createXmlDocument(); - - result.async = 'false'; - result.loadXML(xml); - - return result; - }; - } - else - { - return function(xml) - { - var parser = new DOMParser(); - - return parser.parseFromString(xml, 'text/xml'); - }; - } - }(), - - /** - * Function: clearSelection - * - * Clears the current selection in the page. - */ - clearSelection: function() - { - if (document.selection) - { - return function() - { - document.selection.empty(); - }; - } - else if (window.getSelection) - { - return function() - { - window.getSelection().removeAllRanges(); - }; - } - }(), - - /** - * Function: getPrettyXML - * - * Returns a pretty printed string that represents the XML tree for the - * given node. This method should only be used to print XML for reading, - * use <getXml> instead to obtain a string for processing. - * - * Parameters: - * - * node - DOM node to return the XML for. - * tab - Optional string that specifies the indentation for one level. - * Default is two spaces. - * indent - Optional string that represents the current indentation. - * Default is an empty string. - */ - getPrettyXml: function(node, tab, indent) - { - var result = []; - - if (node != null) - { - tab = tab || ' '; - indent = indent || ''; - - if (node.nodeType == mxConstants.NODETYPE_TEXT) - { - result.push(node.nodeValue); - } - else - { - result.push(indent + '<'+node.nodeName); - - // Creates the string with the node attributes - // and converts all HTML entities in the values - var attrs = node.attributes; - - if (attrs != null) - { - for (var i = 0; i < attrs.length; i++) - { - var val = mxUtils.htmlEntities(attrs[i].nodeValue); - result.push(' ' + attrs[i].nodeName + - '="' + val + '"'); - } - } - - // Recursively creates the XML string for each - // child nodes and appends it here with an - // indentation - var tmp = node.firstChild; - - if (tmp != null) - { - result.push('>\n'); - - while (tmp != null) - { - result.push(mxUtils.getPrettyXml( - tmp, tab, indent + tab)); - tmp = tmp.nextSibling; - } - - result.push(indent + '</'+node.nodeName+'>\n'); - } - else - { - result.push('/>\n'); - } - } - } - - return result.join(''); - }, - - /** - * Function: removeWhitespace - * - * Removes the sibling text nodes for the given node that only consists - * of tabs, newlines and spaces. - * - * Parameters: - * - * node - DOM node whose siblings should be removed. - * before - Optional boolean that specifies the direction of the traversal. - */ - removeWhitespace: function(node, before) - { - var tmp = (before) ? node.previousSibling : node.nextSibling; - - while (tmp != null && tmp.nodeType == mxConstants.NODETYPE_TEXT) - { - var next = (before) ? tmp.previousSibling : tmp.nextSibling; - var text = mxUtils.getTextContent(tmp); - - if (mxUtils.trim(text).length == 0) - { - tmp.parentNode.removeChild(tmp); - } - - tmp = next; - } - }, - - /** - * Function: htmlEntities - * - * Replaces characters (less than, greater than, newlines and quotes) with - * their HTML entities in the given string and returns the result. - * - * Parameters: - * - * s - String that contains the characters to be converted. - * newline - If newlines should be replaced. Default is true. - */ - htmlEntities: function(s, newline) - { - s = s || ''; - - s = s.replace(/&/g,'&'); // 38 26 - s = s.replace(/"/g,'"'); // 34 22 - s = s.replace(/\'/g,'''); // 39 27 - s = s.replace(/</g,'<'); // 60 3C - s = s.replace(/>/g,'>'); // 62 3E - - if (newline == null || newline) - { - s = s.replace(/\n/g, '
'); - } - - return s; - }, - - /** - * Function: isVml - * - * Returns true if the given node is in the VML namespace. - * - * Parameters: - * - * node - DOM node whose tag urn should be checked. - */ - isVml: function(node) - { - return node != null && node.tagUrn == 'urn:schemas-microsoft-com:vml'; - }, - - /** - * Function: getXml - * - * Returns the XML content of the specified node. For Internet Explorer, - * all \r\n\t[\t]* are removed from the XML string and the remaining \r\n - * are replaced by \n. All \n are then replaced with linefeed, or 
 if - * no linefeed is defined. - * - * Parameters: - * - * node - DOM node to return the XML for. - * linefeed - Optional string that linefeeds are converted into. Default is - * 
 - */ - getXml: function(node, linefeed) - { - var xml = ''; - - if (node != null) - { - xml = node.xml; - - if (xml == null) - { - if (node.innerHTML) - { - xml = node.innerHTML; - } - else - { - var xmlSerializer = new XMLSerializer(); - xml = xmlSerializer.serializeToString(node); - } - } - else - { - xml = xml.replace(/\r\n\t[\t]*/g, ''). - replace(/>\r\n/g, '>'). - replace(/\r\n/g, '\n'); - } - } - - // Replaces linefeeds with HTML Entities. - linefeed = linefeed || '
'; - xml = xml.replace(/\n/g, linefeed); - - return xml; - }, - - /** - * Function: getTextContent - * - * Returns the text content of the specified node. - * - * Parameters: - * - * node - DOM node to return the text content for. - */ - getTextContent: function(node) - { - var result = ''; - - if (node != null) - { - if (node.firstChild != null) - { - node = node.firstChild; - } - - result = node.nodeValue || ''; - } - - return result; - }, - - /** - * Function: getInnerHtml - * - * Returns the inner HTML for the given node as a string or an empty string - * if no node was specified. The inner HTML is the text representing all - * children of the node, but not the node itself. - * - * Parameters: - * - * node - DOM node to return the inner HTML for. - */ - getInnerHtml: function() - { - if (mxClient.IS_IE) - { - return function(node) - { - if (node != null) - { - return node.innerHTML; - } - - return ''; - }; - } - else - { - return function(node) - { - if (node != null) - { - var serializer = new XMLSerializer(); - return serializer.serializeToString(node); - } - - return ''; - }; - } - }(), - - /** - * Function: getOuterHtml - * - * Returns the outer HTML for the given node as a string or an empty - * string if no node was specified. The outer HTML is the text representing - * all children of the node including the node itself. - * - * Parameters: - * - * node - DOM node to return the outer HTML for. - */ - getOuterHtml: function() - { - if (mxClient.IS_IE) - { - return function(node) - { - if (node != null) - { - if (node.outerHTML != null) - { - return node.outerHTML; - } - else - { - var tmp = []; - tmp.push('<'+node.nodeName); - - var attrs = node.attributes; - - if (attrs != null) - { - for (var i = 0; i < attrs.length; i++) - { - var value = attrs[i].nodeValue; - - if (value != null && value.length > 0) - { - tmp.push(' '); - tmp.push(attrs[i].nodeName); - tmp.push('="'); - tmp.push(value); - tmp.push('"'); - } - } - } - - if (node.innerHTML.length == 0) - { - tmp.push('/>'); - } - else - { - tmp.push('>'); - tmp.push(node.innerHTML); - tmp.push('</'+node.nodeName+'>'); - } - - return tmp.join(''); - } - } - - return ''; - }; - } - else - { - return function(node) - { - if (node != null) - { - var serializer = new XMLSerializer(); - return serializer.serializeToString(node); - } - - return ''; - }; - } - }(), - - /** - * Function: write - * - * Creates a text node for the given string and appends it to the given - * parent. Returns the text node. - * - * Parameters: - * - * parent - DOM node to append the text node to. - * text - String representing the text to be added. - */ - write: function(parent, text) - { - var doc = parent.ownerDocument; - var node = doc.createTextNode(text); - - if (parent != null) - { - parent.appendChild(node); - } - - return node; - }, - - /** - * Function: writeln - * - * Creates a text node for the given string and appends it to the given - * parent with an additional linefeed. Returns the text node. - * - * Parameters: - * - * parent - DOM node to append the text node to. - * text - String representing the text to be added. - */ - writeln: function(parent, text) - { - var doc = parent.ownerDocument; - var node = doc.createTextNode(text); - - if (parent != null) - { - parent.appendChild(node); - parent.appendChild(document.createElement('br')); - } - - return node; - }, - - /** - * Function: br - * - * Appends a linebreak to the given parent and returns the linebreak. - * - * Parameters: - * - * parent - DOM node to append the linebreak to. - */ - br: function(parent, count) - { - count = count || 1; - var br = null; - - for (var i = 0; i < count; i++) - { - if (parent != null) - { - br = parent.ownerDocument.createElement('br'); - parent.appendChild(br); - } - } - - return br; - }, - - /** - * Function: button - * - * Returns a new button with the given level and function as an onclick - * event handler. - * - * (code) - * document.body.appendChild(mxUtils.button('Test', function(evt) - * { - * alert('Hello, World!'); - * })); - * (end) - * - * Parameters: - * - * label - String that represents the label of the button. - * funct - Function to be called if the button is pressed. - * doc - Optional document to be used for creating the button. Default is the - * current document. - */ - button: function(label, funct, doc) - { - doc = (doc != null) ? doc : document; - - var button = doc.createElement('button'); - mxUtils.write(button, label); - - mxEvent.addListener(button, 'click', function(evt) - { - funct(evt); - }); - - return button; - }, - - /** - * Function: para - * - * Appends a new paragraph with the given text to the specified parent and - * returns the paragraph. - * - * Parameters: - * - * parent - DOM node to append the text node to. - * text - String representing the text for the new paragraph. - */ - para: function(parent, text) - { - var p = document.createElement('p'); - mxUtils.write(p, text); - - if (parent != null) - { - parent.appendChild(p); - } - - return p; - }, - - /** - * Function: linkAction - * - * Adds a hyperlink to the specified parent that invokes action on the - * specified editor. - * - * Parameters: - * - * parent - DOM node to contain the new link. - * text - String that is used as the link label. - * editor - <mxEditor> that will execute the action. - * action - String that defines the name of the action to be executed. - * pad - Optional left-padding for the link. Default is 0. - */ - linkAction: function(parent, text, editor, action, pad) - { - return mxUtils.link(parent, text, function() - { - editor.execute(action); - }, pad); - }, - - /** - * Function: linkInvoke - * - * Adds a hyperlink to the specified parent that invokes the specified - * function on the editor passing along the specified argument. The - * function name is the name of a function of the editor instance, - * not an action name. - * - * Parameters: - * - * parent - DOM node to contain the new link. - * text - String that is used as the link label. - * editor - <mxEditor> instance to execute the function on. - * functName - String that represents the name of the function. - * arg - Object that represents the argument to the function. - * pad - Optional left-padding for the link. Default is 0. - */ - linkInvoke: function(parent, text, editor, functName, arg, pad) - { - return mxUtils.link(parent, text, function() - { - editor[functName](arg); - }, pad); - }, - - /** - * Function: link - * - * Adds a hyperlink to the specified parent and invokes the given function - * when the link is clicked. - * - * Parameters: - * - * parent - DOM node to contain the new link. - * text - String that is used as the link label. - * funct - Function to execute when the link is clicked. - * pad - Optional left-padding for the link. Default is 0. - */ - link: function(parent, text, funct, pad) - { - var a = document.createElement('span'); - - a.style.color = 'blue'; - a.style.textDecoration = 'underline'; - a.style.cursor = 'pointer'; - - if (pad != null) - { - a.style.paddingLeft = pad+'px'; - } - - mxEvent.addListener(a, 'click', funct); - mxUtils.write(a, text); - - if (parent != null) - { - parent.appendChild(a); - } - - return a; - }, - - /** - * Function: fit - * - * Makes sure the given node is inside the visible area of the window. This - * is done by setting the left and top in the style. - */ - fit: function(node) - { - var left = parseInt(node.offsetLeft); - var width = parseInt(node.offsetWidth); - - var b = document.body; - var d = document.documentElement; - - var right = (b.scrollLeft || d.scrollLeft) + - (b.clientWidth || d.clientWidth); - - if (left + width > right) - { - node.style.left = Math.max((b.scrollLeft || d.scrollLeft), - right - width)+'px'; - } - - var top = parseInt(node.offsetTop); - var height = parseInt(node.offsetHeight); - - var bottom = (b.scrollTop || d.scrollTop) + - Math.max(b.clientHeight || 0, d.clientHeight); - - if (top + height > bottom) - { - node.style.top = Math.max((b.scrollTop || d.scrollTop), - bottom - height)+'px'; - } - }, - - /** - * Function: open - * - * Opens the specified file from the local filesystem and returns the - * contents of the file as a string. This implementation requires an - * ActiveX object in IE and special privileges in Firefox. Relative - * filenames are only supported in IE and will go onto the users' - * Desktop. You may have to load - * chrome://global/content/contentAreaUtils.js to disable certain - * security restrictions in Mozilla for this to work. - * - * See known-issues before using this function. - * - * Example: - * (code) - * var data = mxUtils.open('C:\\temp\\test.txt'); - * mxUtils.alert('Data: '+data); - * (end) - * - * Parameters: - * - * filename - String representing the local file name. - */ - open: function(filename) - { - // Requests required privileges in Firefox - if (mxClient.IS_NS) - { - try - { - netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); - } - catch (e) - { - mxUtils.alert('Permission to read file denied.'); - - return ''; - } - - var file = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile); - file.initWithPath(filename); - - if (!file.exists()) - { - mxUtils.alert('File not found.'); - return ''; - } - - var is = Components.classes['@mozilla.org/network/file-input-stream;1'].createInstance(Components.interfaces.nsIFileInputStream); - is.init(file,0x01, 00004, null); - - var sis = Components.classes['@mozilla.org/scriptableinputstream;1'].createInstance(Components.interfaces.nsIScriptableInputStream); - sis.init(is); - - var output = sis.read(sis.available()); - - return output; - } - else - { - var activeXObject = new ActiveXObject('Scripting.FileSystemObject'); - - var newStream = activeXObject.OpenTextFile(filename, 1); - var text = newStream.readAll(); - newStream.close(); - - return text; - } - }, - - /** - * Function: save - * - * Saves the specified content in the given file on the local file system. - * This implementation requires an ActiveX object in IE and special - * privileges in Firefox. Relative filenames are only supported in IE and - * will be loaded from the users' Desktop. You may have to load - * chrome://global/content/contentAreaUtils.js to disable certain - * security restrictions in Mozilla for this to work. - * - * See known-issues before using this function. - * - * Example: - * - * (code) - * var data = 'Hello, World!'; - * mxUtils.save('C:\\test.txt', data); - * (end) - * - * Parameters: - * - * filename - String representing the local file name. - */ - save: function(filename, content) - { - if (mxClient.IS_NS) - { - try - { - netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); - } - catch (e) - { - mxUtils.alert('Permission to write file denied.'); - return; - } - - var file = Components.classes['@mozilla.org/file/local;1'].createInstance(Components.interfaces.nsILocalFile); - file.initWithPath(filename); - - if (!file.exists()) - { - file.create(0x00, 0644); - } - - var outputStream = Components.classes['@mozilla.org/network/file-output-stream;1'].createInstance(Components.interfaces.nsIFileOutputStream); - - outputStream.init(file, 0x20 | 0x02,00004, null); - outputStream.write(content, content.length); - outputStream.flush(); - outputStream.close(); - } - else - { - var fso = new ActiveXObject('Scripting.FileSystemObject'); - - var file = fso.CreateTextFile(filename, true); - file.Write(content); - file.Close(); - } - }, - - /** - * Function: saveAs - * - * Saves the specified content by displaying a dialog to save the content - * as a file on the local filesystem. This implementation does not use an - * ActiveX object in IE, however, it does require special privileges in - * Firefox. You may have to load - * chrome://global/content/contentAreaUtils.js to disable certain - * security restrictions in Mozilla for this to work. - * - * See known-issues before using this function. It is not recommended using - * this function in production environment as access to the filesystem - * cannot be guaranteed in Firefox. The following code is used in - * Firefox to try and enable saving to the filesystem. - * - * (code) - * netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); - * (end) - * - * Example: - * - * (code) - * mxUtils.saveAs('Hello, World!'); - * (end) - * - * Parameters: - * - * content - String representing the file's content. - */ - saveAs: function(content) - { - var iframe = document.createElement('iframe'); - iframe.setAttribute('src', ''); - iframe.style.visibility = 'hidden'; - document.body.appendChild(iframe); - - try - { - if (mxClient.IS_NS) - { - var doc = iframe.contentDocument; - - doc.open(); - doc.write(content); - doc.close(); - - try - { - netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); - // LATER: Remove existing HTML markup in file - iframe.focus(); - saveDocument(doc); - } - catch (e) - { - mxUtils.alert('Permission to save document denied.'); - } - } - else - { - var doc = iframe.contentWindow.document; - doc.write(content); - doc.execCommand('SaveAs', false, document.location); - } - } - finally - { - document.body.removeChild(iframe); - } - }, - - /** - * Function: copy - * - * Copies the specified content to the local clipboard. This implementation - * requires special privileges in Firefox. You may have to load - * chrome://global/content/contentAreaUtils.js to disable certain - * security restrictions in Mozilla for this to work. - * - * Parameters: - * - * content - String to be copied to the clipboard. - */ - copy: function(content) - { - if (window.clipboardData) - { - window.clipboardData.setData('Text', content); - } - else - { - netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); - - var clip = Components.classes['@mozilla.org/widget/clipboard;1'] - .createInstance(Components.interfaces.nsIClipboard); - - if (!clip) - { - return; - } - - var trans = Components.classes['@mozilla.org/widget/transferable;1'] - .createInstance(Components.interfaces.nsITransferable); - - if (!trans) - { - return; - } - - trans.addDataFlavor('text/unicode'); - var str = Components.classes['@mozilla.org/supports-string;1'] - .createInstance(Components.interfaces.nsISupportsString); - - var copytext=content; - str.data=copytext; - trans.setTransferData('text/unicode', str, copytext.length*2); - var clipid=Components.interfaces.nsIClipboard; - - clip.setData(trans,null,clipid.kGlobalClipboard); - } - }, - - /** - * Function: load - * - * Loads the specified URL *synchronously* and returns the <mxXmlRequest>. - * Throws an exception if the file cannot be loaded. See <mxUtils.get> for - * an asynchronous implementation. - * - * Example: - * - * (code) - * try - * { - * var req = mxUtils.load(filename); - * var root = req.getDocumentElement(); - * // Process XML DOM... - * } - * catch (ex) - * { - * mxUtils.alert('Cannot load '+filename+': '+ex); - * } - * (end) - * - * Parameters: - * - * url - URL to get the data from. - */ - load: function(url) - { - var req = new mxXmlRequest(url, null, 'GET', false); - req.send(); - - return req; - }, - - /** - * Function: get - * - * Loads the specified URL *asynchronously* and invokes the given functions - * depending on the request status. Returns the <mxXmlRequest> in use. Both - * functions take the <mxXmlRequest> as the only parameter. See - * <mxUtils.load> for a synchronous implementation. - * - * Example: - * - * (code) - * mxUtils.get(url, function(req) - * { - * var node = req.getDocumentElement(); - * // Process XML DOM... - * }); - * (end) - * - * So for example, to load a diagram into an existing graph model, the - * following code is used. - * - * (code) - * mxUtils.get(url, function(req) - * { - * var node = req.getDocumentElement(); - * var dec = new mxCodec(node.ownerDocument); - * dec.decode(node, graph.getModel()); - * }); - * (end) - * - * Parameters: - * - * url - URL to get the data from. - * onload - Optional function to execute for a successful response. - * onerror - Optional function to execute on error. - */ - get: function(url, onload, onerror) - { - return new mxXmlRequest(url, null, 'GET').send(onload, onerror); - }, - - /** - * Function: post - * - * Posts the specified params to the given URL *asynchronously* and invokes - * the given functions depending on the request status. Returns the - * <mxXmlRequest> in use. Both functions take the <mxXmlRequest> as the - * only parameter. Make sure to use encodeURIComponent for the parameter - * values. - * - * Example: - * - * (code) - * mxUtils.post(url, 'key=value', function(req) - * { - * mxUtils.alert('Ready: '+req.isReady()+' Status: '+req.getStatus()); - * // Process req.getDocumentElement() using DOM API if OK... - * }); - * (end) - * - * Parameters: - * - * url - URL to get the data from. - * params - Parameters for the post request. - * onload - Optional function to execute for a successful response. - * onerror - Optional function to execute on error. - */ - post: function(url, params, onload, onerror) - { - return new mxXmlRequest(url, params).send(onload, onerror); - }, - - /** - * Function: submit - * - * Submits the given parameters to the specified URL using - * <mxXmlRequest.simulate> and returns the <mxXmlRequest>. - * Make sure to use encodeURIComponent for the parameter - * values. - * - * Parameters: - * - * url - URL to get the data from. - * params - Parameters for the form. - * doc - Document to create the form in. - * target - Target to send the form result to. - */ - submit: function(url, params, doc, target) - { - return new mxXmlRequest(url, params).simulate(doc, target); - }, - - /** - * Function: loadInto - * - * Loads the specified URL *asynchronously* into the specified document, - * invoking onload after the document has been loaded. This implementation - * does not use <mxXmlRequest>, but the document.load method. - * - * Parameters: - * - * url - URL to get the data from. - * doc - The document to load the URL into. - * onload - Function to execute when the URL has been loaded. - */ - loadInto: function(url, doc, onload) - { - if (mxClient.IS_IE) - { - doc.onreadystatechange = function () - { - if (doc.readyState == 4) - { - onload(); - } - }; - } - else - { - doc.addEventListener('load', onload, false); - } - - doc.load(url); - }, - - /** - * Function: getValue - * - * Returns the value for the given key in the given associative array or - * the given default value if the value is null. - * - * Parameters: - * - * array - Associative array that contains the value for the key. - * key - Key whose value should be returned. - * defaultValue - Value to be returned if the value for the given - * key is null. - */ - getValue: function(array, key, defaultValue) - { - var value = (array != null) ? array[key] : null; - - if (value == null) - { - value = defaultValue; - } - - return value; - }, - - /** - * Function: getNumber - * - * Returns the numeric value for the given key in the given associative - * array or the given default value (or 0) if the value is null. The value - * is converted to a numeric value using the Number function. - * - * Parameters: - * - * array - Associative array that contains the value for the key. - * key - Key whose value should be returned. - * defaultValue - Value to be returned if the value for the given - * key is null. Default is 0. - */ - getNumber: function(array, key, defaultValue) - { - var value = (array != null) ? array[key] : null; - - if (value == null) - { - value = defaultValue || 0; - } - - return Number(value); - }, - - /** - * Function: getColor - * - * Returns the color value for the given key in the given associative - * array or the given default value if the value is null. If the value - * is <mxConstants.NONE> then null is returned. - * - * Parameters: - * - * array - Associative array that contains the value for the key. - * key - Key whose value should be returned. - * defaultValue - Value to be returned if the value for the given - * key is null. Default is null. - */ - getColor: function(array, key, defaultValue) - { - var value = (array != null) ? array[key] : null; - - if (value == null) - { - value = defaultValue; - } - else if (value == mxConstants.NONE) - { - value = null; - } - - return value; - }, - - /** - * Function: clone - * - * Recursively clones the specified object ignoring all fieldnames in the - * given array of transient fields. <mxObjectIdentity.FIELD_NAME> is always - * ignored by this function. - * - * Parameters: - * - * obj - Object to be cloned. - * transients - Optional array of strings representing the fieldname to be - * ignored. - * shallow - Optional boolean argument to specify if a shallow clone should - * be created, that is, one where all object references are not cloned or, - * in other words, one where only atomic (strings, numbers) values are - * cloned. Default is false. - */ - clone: function(obj, transients, shallow) - { - shallow = (shallow != null) ? shallow : false; - var clone = null; - - if (obj != null && typeof(obj.constructor) == 'function') - { - clone = new obj.constructor(); - - for (var i in obj) - { - if (i != mxObjectIdentity.FIELD_NAME && (transients == null || - mxUtils.indexOf(transients, i) < 0)) - { - if (!shallow && typeof(obj[i]) == 'object') - { - clone[i] = mxUtils.clone(obj[i]); - } - else - { - clone[i] = obj[i]; - } - } - } - } - - return clone; - }, - - /** - * Function: equalPoints - * - * Compares all mxPoints in the given lists. - * - * Parameters: - * - * a - Array of <mxPoints> to be compared. - * b - Array of <mxPoints> to be compared. - */ - equalPoints: function(a, b) - { - if ((a == null && b != null) || (a != null && b == null) || - (a != null && b != null && a.length != b.length)) - { - return false; - } - else if (a != null && b != null) - { - for (var i = 0; i < a.length; i++) - { - if (a[i] == b[i] || (a[i] != null && !a[i].equals(b[i]))) - { - return false; - } - } - } - - return true; - }, - - /** - * Function: equalEntries - * - * Compares all entries in the given dictionaries. - * - * Parameters: - * - * a - <mxRectangle> to be compared. - * b - <mxRectangle> to be compared. - */ - equalEntries: function(a, b) - { - if ((a == null && b != null) || (a != null && b == null) || - (a != null && b != null && a.length != b.length)) - { - return false; - } - else if (a != null && b != null) - { - for (var key in a) - { - if (a[key] != b[key]) - { - return false; - } - } - } - - return true; - }, - - /** - * Function: extend - * - * Assigns a copy of the superclass prototype to the subclass prototype. - * Note that this does not call the constructor of the superclass at this - * point, the superclass constructor should be called explicitely in the - * subclass constructor. Below is an example. - * - * (code) - * MyGraph = function(container, model, renderHint, stylesheet) - * { - * mxGraph.call(this, container, model, renderHint, stylesheet); - * } - * - * mxUtils.extend(MyGraph, mxGraph); - * (end) - * - * Parameters: - * - * ctor - Constructor of the subclass. - * superCtor - Constructor of the superclass. - */ - extend: function(ctor, superCtor) - { - var f = function() {}; - f.prototype = superCtor.prototype; - - ctor.prototype = new f(); - ctor.prototype.constructor = ctor; - }, - - /** - * Function: toString - * - * Returns a textual representation of the specified object. - * - * Parameters: - * - * obj - Object to return the string representation for. - */ - toString: function(obj) - { - var output = ''; - - for (var i in obj) - { - try - { - if (obj[i] == null) - { - output += i + ' = [null]\n'; - } - else if (typeof(obj[i]) == 'function') - { - output += i + ' => [Function]\n'; - } - else if (typeof(obj[i]) == 'object') - { - var ctor = mxUtils.getFunctionName(obj[i].constructor); - output += i + ' => [' + ctor + ']\n'; - } - else - { - output += i + ' = ' + obj[i] + '\n'; - } - } - catch (e) - { - output += i + '=' + e.message; - } - } - - return output; - }, - - /** - * Function: toRadians - * - * Converts the given degree to radians. - */ - toRadians: function(deg) - { - return Math.PI * deg / 180; - }, - - /** - * Function: arcToCurves - * - * Converts the given arc to a series of curves. - */ - arcToCurves: function(x0, y0, r1, r2, angle, largeArcFlag, sweepFlag, x, y) - { - x -= x0; - y -= y0; - - if (r1 === 0 || r2 === 0) - { - return result; - } - - var fS = sweepFlag; - var psai = angle; - r1 = Math.abs(r1); - r2 = Math.abs(r2); - var ctx = -x / 2; - var cty = -y / 2; - var cpsi = Math.cos(psai * Math.PI / 180); - var spsi = Math.sin(psai * Math.PI / 180); - var rxd = cpsi * ctx + spsi * cty; - var ryd = -1 * spsi * ctx + cpsi * cty; - var rxdd = rxd * rxd; - var rydd = ryd * ryd; - var r1x = r1 * r1; - var r2y = r2 * r2; - var lamda = rxdd / r1x + rydd / r2y; - var sds; - - if (lamda > 1) - { - r1 = Math.sqrt(lamda) * r1; - r2 = Math.sqrt(lamda) * r2; - sds = 0; - } - else - { - var seif = 1; - - if (largeArcFlag === fS) - { - seif = -1; - } - - sds = seif * Math.sqrt((r1x * r2y - r1x * rydd - r2y * rxdd) / (r1x * rydd + r2y * rxdd)); - } - - var txd = sds * r1 * ryd / r2; - var tyd = -1 * sds * r2 * rxd / r1; - var tx = cpsi * txd - spsi * tyd + x / 2; - var ty = spsi * txd + cpsi * tyd + y / 2; - var rad = Math.atan2((ryd - tyd) / r2, (rxd - txd) / r1) - Math.atan2(0, 1); - var s1 = (rad >= 0) ? rad : 2 * Math.PI + rad; - rad = Math.atan2((-ryd - tyd) / r2, (-rxd - txd) / r1) - Math.atan2((ryd - tyd) / r2, (rxd - txd) / r1); - var dr = (rad >= 0) ? rad : 2 * Math.PI + rad; - - if (fS == 0 && dr > 0) - { - dr -= 2 * Math.PI; - } - else if (fS != 0 && dr < 0) - { - dr += 2 * Math.PI; - } - - var sse = dr * 2 / Math.PI; - var seg = Math.ceil(sse < 0 ? -1 * sse : sse); - var segr = dr / seg; - var t = 8/3 * Math.sin(segr / 4) * Math.sin(segr / 4) / Math.sin(segr / 2); - var cpsir1 = cpsi * r1; - var cpsir2 = cpsi * r2; - var spsir1 = spsi * r1; - var spsir2 = spsi * r2; - var mc = Math.cos(s1); - var ms = Math.sin(s1); - var x2 = -t * (cpsir1 * ms + spsir2 * mc); - var y2 = -t * (spsir1 * ms - cpsir2 * mc); - var x3 = 0; - var y3 = 0; - - var result = []; - - for (var n = 0; n < seg; ++n) - { - s1 += segr; - mc = Math.cos(s1); - ms = Math.sin(s1); - - x3 = cpsir1 * mc - spsir2 * ms + tx; - y3 = spsir1 * mc + cpsir2 * ms + ty; - var dx = -t * (cpsir1 * ms + spsir2 * mc); - var dy = -t * (spsir1 * ms - cpsir2 * mc); - - // CurveTo updates x0, y0 so need to restore it - var index = n * 6; - result[index] = Number(x2 + x0); - result[index + 1] = Number(y2 + y0); - result[index + 2] = Number(x3 - dx + x0); - result[index + 3] = Number(y3 - dy + y0); - result[index + 4] = Number(x3 + x0); - result[index + 5] = Number(y3 + y0); - - x2 = x3 + dx; - y2 = y3 + dy; - } - - return result; - }, - - /** - * Function: getBoundingBox - * - * Returns the bounding box for the rotated rectangle. - */ - getBoundingBox: function(rect, rotation) - { - var result = null; - - if (rect != null && rotation != null && rotation != 0) - { - var rad = mxUtils.toRadians(rotation); - var cos = Math.cos(rad); - var sin = Math.sin(rad); - - var cx = new mxPoint( - rect.x + rect.width / 2, - rect.y + rect.height / 2); - - var p1 = new mxPoint(rect.x, rect.y); - var p2 = new mxPoint(rect.x + rect.width, rect.y); - var p3 = new mxPoint(p2.x, rect.y + rect.height); - var p4 = new mxPoint(rect.x, p3.y); - - p1 = mxUtils.getRotatedPoint(p1, cos, sin, cx); - p2 = mxUtils.getRotatedPoint(p2, cos, sin, cx); - p3 = mxUtils.getRotatedPoint(p3, cos, sin, cx); - p4 = mxUtils.getRotatedPoint(p4, cos, sin, cx); - - result = new mxRectangle(p1.x, p1.y, 0, 0); - result.add(new mxRectangle(p2.x, p2.y, 0, 0)); - result.add(new mxRectangle(p3.x, p3.y, 0, 0)); - result.add(new mxRectangle(p4.x, p4.y, 0, 0)); - } - - return result; - }, - - /** - * Function: getRotatedPoint - * - * Rotates the given point by the given cos and sin. - */ - getRotatedPoint: function(pt, cos, sin, c) - { - c = (c != null) ? c : new mxPoint(); - var x = pt.x - c.x; - var y = pt.y - c.y; - - var x1 = x * cos - y * sin; - var y1 = y * cos + x * sin; - - return new mxPoint(x1 + c.x, y1 + c.y); - }, - - /** - * Returns an integer mask of the port constraints of the given map - * @param dict the style map to determine the port constraints for - * @param defaultValue Default value to return if the key is undefined. - * @return the mask of port constraint directions - * - * Parameters: - * - * terminal - <mxCelState> that represents the terminal. - * edge - <mxCellState> that represents the edge. - * source - Boolean that specifies if the terminal is the source terminal. - * defaultValue - Default value to be returned. - */ - getPortConstraints: function(terminal, edge, source, defaultValue) - { - var value = mxUtils.getValue(terminal.style, mxConstants.STYLE_PORT_CONSTRAINT, null); - - if (value == null) - { - return defaultValue; - } - else - { - var directions = value.toString(); - var returnValue = mxConstants.DIRECTION_MASK_NONE; - - if (directions.indexOf(mxConstants.DIRECTION_NORTH) >= 0) - { - returnValue |= mxConstants.DIRECTION_MASK_NORTH; - } - if (directions.indexOf(mxConstants.DIRECTION_WEST) >= 0) - { - returnValue |= mxConstants.DIRECTION_MASK_WEST; - } - if (directions.indexOf(mxConstants.DIRECTION_SOUTH) >= 0) - { - returnValue |= mxConstants.DIRECTION_MASK_SOUTH; - } - if (directions.indexOf(mxConstants.DIRECTION_EAST) >= 0) - { - returnValue |= mxConstants.DIRECTION_MASK_EAST; - } - - return returnValue; - } - }, - - /** - * Function: reversePortConstraints - * - * Reverse the port constraint bitmask. For example, north | east - * becomes south | west - */ - reversePortConstraints: function(constraint) - { - var result = 0; - - result = (constraint & mxConstants.DIRECTION_MASK_WEST) << 3; - result |= (constraint & mxConstants.DIRECTION_MASK_NORTH) << 1; - result |= (constraint & mxConstants.DIRECTION_MASK_SOUTH) >> 1; - result |= (constraint & mxConstants.DIRECTION_MASK_EAST) >> 3; - - return result; - }, - - /** - * Function: findNearestSegment - * - * Finds the index of the nearest segment on the given cell state for - * the specified coordinate pair. - */ - findNearestSegment: function(state, x, y) - { - var index = -1; - - if (state.absolutePoints.length > 0) - { - var last = state.absolutePoints[0]; - var min = null; - - for (var i = 1; i < state.absolutePoints.length; i++) - { - var current = state.absolutePoints[i]; - var dist = mxUtils.ptSegDistSq(last.x, last.y, - current.x, current.y, x, y); - - if (min == null || dist < min) - { - min = dist; - index = i - 1; - } - - last = current; - } - } - - return index; - }, - - /** - * Function: rectangleIntersectsSegment - * - * Returns true if the given rectangle intersects the given segment. - * - * Parameters: - * - * bounds - <mxRectangle> that represents the rectangle. - * p1 - <mxPoint> that represents the first point of the segment. - * p2 - <mxPoint> that represents the second point of the segment. - */ - rectangleIntersectsSegment: function(bounds, p1, p2) - { - var top = bounds.y; - var left = bounds.x; - var bottom = top + bounds.height; - var right = left + bounds.width; - - // Find min and max X for the segment - var minX = p1.x; - var maxX = p2.x; - - if (p1.x > p2.x) - { - minX = p2.x; - maxX = p1.x; - } - - // Find the intersection of the segment's and rectangle's x-projections - if (maxX > right) - { - maxX = right; - } - - if (minX < left) - { - minX = left; - } - - if (minX > maxX) // If their projections do not intersect return false - { - return false; - } - - // Find corresponding min and max Y for min and max X we found before - var minY = p1.y; - var maxY = p2.y; - var dx = p2.x - p1.x; - - if (Math.abs(dx) > 0.0000001) - { - var a = (p2.y - p1.y) / dx; - var b = p1.y - a * p1.x; - minY = a * minX + b; - maxY = a * maxX + b; - } - - if (minY > maxY) - { - var tmp = maxY; - maxY = minY; - minY = tmp; - } - - // Find the intersection of the segment's and rectangle's y-projections - if (maxY > bottom) - { - maxY = bottom; - } - - if (minY < top) - { - minY = top; - } - - if (minY > maxY) // If Y-projections do not intersect return false - { - return false; - } - - return true; - }, - - /** - * Function: contains - * - * Returns true if the specified point (x, y) is contained in the given rectangle. - * - * Parameters: - * - * bounds - <mxRectangle> that represents the area. - * x - X-coordinate of the point. - * y - Y-coordinate of the point. - */ - contains: function(bounds, x, y) - { - return (bounds.x <= x && bounds.x + bounds.width >= x && - bounds.y <= y && bounds.y + bounds.height >= y); - }, - - /** - * Function: intersects - * - * Returns true if the two rectangles intersect. - * - * Parameters: - * - * a - <mxRectangle> to be checked for intersection. - * b - <mxRectangle> to be checked for intersection. - */ - intersects: function(a, b) - { - var tw = a.width; - var th = a.height; - var rw = b.width; - var rh = b.height; - - if (rw <= 0 || rh <= 0 || tw <= 0 || th <= 0) - { - return false; - } - - var tx = a.x; - var ty = a.y; - var rx = b.x; - var ry = b.y; - - rw += rx; - rh += ry; - tw += tx; - th += ty; - - return ((rw < rx || rw > tx) && - (rh < ry || rh > ty) && - (tw < tx || tw > rx) && - (th < ty || th > ry)); - }, - - /** - * Function: intersects - * - * Returns true if the two rectangles intersect. - * - * Parameters: - * - * a - <mxRectangle> to be checked for intersection. - * b - <mxRectangle> to be checked for intersection. - */ - intersectsHotspot: function(state, x, y, hotspot, min, max) - { - hotspot = (hotspot != null) ? hotspot : 1; - min = (min != null) ? min : 0; - max = (max != null) ? max : 0; - - if (hotspot > 0) - { - var cx = state.getCenterX(); - var cy = state.getCenterY(); - var w = state.width; - var h = state.height; - - var start = mxUtils.getValue(state.style, mxConstants.STYLE_STARTSIZE) * state.view.scale; - - if (start > 0) - { - if (mxUtils.getValue(state.style, - mxConstants.STYLE_HORIZONTAL, true)) - { - cy = state.y + start / 2; - h = start; - } - else - { - cx = state.x + start / 2; - w = start; - } - } - - w = Math.max(min, w * hotspot); - h = Math.max(min, h * hotspot); - - if (max > 0) - { - w = Math.min(w, max); - h = Math.min(h, max); - } - - var rect = new mxRectangle(cx - w / 2, cy - h / 2, w, h); - - return mxUtils.contains(rect, x, y); - } - - return true; - }, - - /** - * Function: getOffset - * - * Returns the offset for the specified container as an <mxPoint>. The - * offset is the distance from the top left corner of the container to the - * top left corner of the document. - * - * Parameters: - * - * container - DOM node to return the offset for. - * scollOffset - Optional boolean to add the scroll offset of the document. - * Default is false. - */ - getOffset: function(container, scrollOffset) - { - var offsetLeft = 0; - var offsetTop = 0; - - if (scrollOffset != null && scrollOffset) - { - var b = document.body; - var d = document.documentElement; - offsetLeft += (b.scrollLeft || d.scrollLeft); - offsetTop += (b.scrollTop || d.scrollTop); - } - - while (container.offsetParent) - { - offsetLeft += container.offsetLeft; - offsetTop += container.offsetTop; - - container = container.offsetParent; - } - - return new mxPoint(offsetLeft, offsetTop); - }, - - /** - * Function: getScrollOrigin - * - * Returns the top, left corner of the viewrect as an <mxPoint>. - */ - getScrollOrigin: function(node) - { - var b = document.body; - var d = document.documentElement; - var sl = (b.scrollLeft || d.scrollLeft); - var st = (b.scrollTop || d.scrollTop); - - var result = new mxPoint(sl, st); - - while (node != null && node != b && node != d) - { - if (!isNaN(node.scrollLeft) && !isNaN(node.scrollTop)) - { - result.x += node.scrollLeft; - result.y += node.scrollTop; - } - - node = node.parentNode; - } - - return result; - }, - - /** - * Function: convertPoint - * - * Converts the specified point (x, y) using the offset of the specified - * container and returns a new <mxPoint> with the result. - * - * Parameters: - * - * container - DOM node to use for the offset. - * x - X-coordinate of the point to be converted. - * y - Y-coordinate of the point to be converted. - */ - convertPoint: function(container, x, y) - { - var origin = mxUtils.getScrollOrigin(container); - var offset = mxUtils.getOffset(container); - - offset.x -= origin.x; - offset.y -= origin.y; - - return new mxPoint(x - offset.x, y - offset.y); - }, - - /** - * Function: ltrim - * - * Strips all whitespaces from the beginning of the string. - * Without the second parameter, Javascript function will trim these - * characters: - * - * - " " (ASCII 32 (0x20)), an ordinary space - * - "\t" (ASCII 9 (0x09)), a tab - * - "\n" (ASCII 10 (0x0A)), a new line (line feed) - * - "\r" (ASCII 13 (0x0D)), a carriage return - * - "\0" (ASCII 0 (0x00)), the NUL-byte - * - "\x0B" (ASCII 11 (0x0B)), a vertical tab - */ - ltrim: function(str, chars) - { - chars = chars || "\\s"; - - return str.replace(new RegExp("^[" + chars + "]+", "g"), ""); - }, - - /** - * Function: rtrim - * - * Strips all whitespaces from the end of the string. - * Without the second parameter, Javascript function will trim these - * characters: - * - * - " " (ASCII 32 (0x20)), an ordinary space - * - "\t" (ASCII 9 (0x09)), a tab - * - "\n" (ASCII 10 (0x0A)), a new line (line feed) - * - "\r" (ASCII 13 (0x0D)), a carriage return - * - "\0" (ASCII 0 (0x00)), the NUL-byte - * - "\x0B" (ASCII 11 (0x0B)), a vertical tab - */ - rtrim: function(str, chars) - { - chars = chars || "\\s"; - - return str.replace(new RegExp("[" + chars + "]+$", "g"), ""); - }, - - /** - * Function: trim - * - * Strips all whitespaces from both end of the string. - * Without the second parameter, Javascript function will trim these - * characters: - * - * - " " (ASCII 32 (0x20)), an ordinary space - * - "\t" (ASCII 9 (0x09)), a tab - * - "\n" (ASCII 10 (0x0A)), a new line (line feed) - * - "\r" (ASCII 13 (0x0D)), a carriage return - * - "\0" (ASCII 0 (0x00)), the NUL-byte - * - "\x0B" (ASCII 11 (0x0B)), a vertical tab - */ - trim: function(str, chars) - { - return mxUtils.ltrim(mxUtils.rtrim(str, chars), chars); - }, - - /** - * Function: isNumeric - * - * Returns true if the specified value is numeric, that is, if it is not - * null, not an empty string, not a HEX number and isNaN returns false. - * - * Parameters: - * - * str - String representing the possibly numeric value. - */ - isNumeric: function(str) - { - return str != null && (str.length == null || (str.length > 0 && - str.indexOf('0x') < 0) && str.indexOf('0X') < 0) && !isNaN(str); - }, - - /** - * Function: mod - * - * Returns the remainder of division of n by m. You should use this instead - * of the built-in operation as the built-in operation does not properly - * handle negative numbers. - */ - mod: function(n, m) - { - return ((n % m) + m) % m; - }, - - /** - * Function: intersection - * - * Returns the intersection of two lines as an <mxPoint>. - * - * Parameters: - * - * x0 - X-coordinate of the first line's startpoint. - * y0 - X-coordinate of the first line's startpoint. - * x1 - X-coordinate of the first line's endpoint. - * y1 - Y-coordinate of the first line's endpoint. - * x2 - X-coordinate of the second line's startpoint. - * y2 - Y-coordinate of the second line's startpoint. - * x3 - X-coordinate of the second line's endpoint. - * y3 - Y-coordinate of the second line's endpoint. - */ - intersection: function (x0, y0, x1, y1, x2, y2, x3, y3) - { - var denom = ((y3 - y2)*(x1 - x0)) - ((x3 - x2)*(y1 - y0)); - var nume_a = ((x3 - x2)*(y0 - y2)) - ((y3 - y2)*(x0 - x2)); - var nume_b = ((x1 - x0)*(y0 - y2)) - ((y1 - y0)*(x0 - x2)); - - var ua = nume_a / denom; - var ub = nume_b / denom; - - if(ua >= 0.0 && ua <= 1.0 && ub >= 0.0 && ub <= 1.0) - { - // Get the intersection point - var intersectionX = x0 + ua*(x1 - x0); - var intersectionY = y0 + ua*(y1 - y0); - - return new mxPoint(intersectionX, intersectionY); - } - - // No intersection - return null; - }, - - /** - * Function: ptSeqDistSq - * - * Returns the square distance between a segment and a point. - * - * Parameters: - * - * x1 - X-coordinate of the startpoint of the segment. - * y1 - Y-coordinate of the startpoint of the segment. - * x2 - X-coordinate of the endpoint of the segment. - * y2 - Y-coordinate of the endpoint of the segment. - * px - X-coordinate of the point. - * py - Y-coordinate of the point. - */ - ptSegDistSq: function(x1, y1, x2, y2, px, py) - { - x2 -= x1; - y2 -= y1; - - px -= x1; - py -= y1; - - var dotprod = px * x2 + py * y2; - var projlenSq; - - if (dotprod <= 0.0) - { - projlenSq = 0.0; - } - else - { - px = x2 - px; - py = y2 - py; - dotprod = px * x2 + py * y2; - - if (dotprod <= 0.0) - { - projlenSq = 0.0; - } - else - { - projlenSq = dotprod * dotprod / (x2 * x2 + y2 * y2); - } - } - - var lenSq = px * px + py * py - projlenSq; - - if (lenSq < 0) - { - lenSq = 0; - } - - return lenSq; - }, - - /** - * Function: relativeCcw - * - * Returns 1 if the given point on the right side of the segment, 0 if its - * on the segment, and -1 if the point is on the left side of the segment. - * - * Parameters: - * - * x1 - X-coordinate of the startpoint of the segment. - * y1 - Y-coordinate of the startpoint of the segment. - * x2 - X-coordinate of the endpoint of the segment. - * y2 - Y-coordinate of the endpoint of the segment. - * px - X-coordinate of the point. - * py - Y-coordinate of the point. - */ - relativeCcw: function(x1, y1, x2, y2, px, py) - { - x2 -= x1; - y2 -= y1; - px -= x1; - py -= y1; - var ccw = px * y2 - py * x2; - - if (ccw == 0.0) - { - ccw = px * x2 + py * y2; - - if (ccw > 0.0) - { - px -= x2; - py -= y2; - ccw = px * x2 + py * y2; - - if (ccw < 0.0) - { - ccw = 0.0; - } - } - } - - return (ccw < 0.0) ? -1 : ((ccw > 0.0) ? 1 : 0); - }, - - /** - * Function: animateChanges - * - * See <mxEffects.animateChanges>. This is for backwards compatibility and - * will be removed later. - */ - animateChanges: function(graph, changes) - { - // LATER: Deprecated, remove this function - mxEffects.animateChanges.apply(this, arguments); - }, - - /** - * Function: cascadeOpacity - * - * See <mxEffects.cascadeOpacity>. This is for backwards compatibility and - * will be removed later. - */ - cascadeOpacity: function(graph, cell, opacity) - { - mxEffects.cascadeOpacity.apply(this, arguments); - }, - - /** - * Function: fadeOut - * - * See <mxEffects.fadeOut>. This is for backwards compatibility and - * will be removed later. - */ - fadeOut: function(node, from, remove, step, delay, isEnabled) - { - mxEffects.fadeOut.apply(this, arguments); - }, - - /** - * Function: setOpacity - * - * Sets the opacity of the specified DOM node to the given value in %. - * - * Parameters: - * - * node - DOM node to set the opacity for. - * value - Opacity in %. Possible values are between 0 and 100. - */ - setOpacity: function(node, value) - { - if (mxUtils.isVml(node)) - { - if (value >= 100) - { - node.style.filter = null; - } - else - { - // TODO: Why is the division by 5 needed in VML? - node.style.filter = 'alpha(opacity=' + (value/5) + ')'; - } - } - else if (mxClient.IS_IE && (typeof(document.documentMode) === 'undefined' || document.documentMode < 9)) - { - if (value >= 100) - { - node.style.filter = null; - } - else - { - node.style.filter = 'alpha(opacity=' + value + ')'; - } - } - else - { - node.style.opacity = (value / 100); - } - }, - - /** - * Function: createImage - * - * Creates and returns an image (IMG node) or VML image (v:image) in IE6 in - * quirs mode. - * - * Parameters: - * - * src - URL that points to the image to be displayed. - */ - createImage: function(src) - { - var imageNode = null; - - if (mxClient.IS_IE6 && document.compatMode != 'CSS1Compat') - { - imageNode = document.createElement('v:image'); - imageNode.setAttribute('src', src); - imageNode.style.borderStyle = 'none'; - } - else - { - imageNode = document.createElement('img'); - imageNode.setAttribute('src', src); - imageNode.setAttribute('border', '0'); - } - - return imageNode; - }, - - /** - * Function: sortCells - * - * Sorts the given cells according to the order in the cell hierarchy. - * Ascending is optional and defaults to true. - */ - sortCells: function(cells, ascending) - { - ascending = (ascending != null) ? ascending : true; - var lookup = new mxDictionary(); - cells.sort(function(o1, o2) - { - var p1 = lookup.get(o1); - - if (p1 == null) - { - p1 = mxCellPath.create(o1).split(mxCellPath.PATH_SEPARATOR); - lookup.put(o1, p1); - } - - var p2 = lookup.get(o2); - - if (p2 == null) - { - p2 = mxCellPath.create(o2).split(mxCellPath.PATH_SEPARATOR); - lookup.put(o2, p2); - } - - var comp = mxCellPath.compare(p1, p2); - - return (comp == 0) ? 0 : (((comp > 0) == ascending) ? 1 : -1); - }); - - return cells; - }, - - /** - * Function: getStylename - * - * Returns the stylename in a style of the form [(stylename|key=value);] or - * an empty string if the given style does not contain a stylename. - * - * Parameters: - * - * style - String of the form [(stylename|key=value);]. - */ - getStylename: function(style) - { - if (style != null) - { - var pairs = style.split(';'); - var stylename = pairs[0]; - - if (stylename.indexOf('=') < 0) - { - return stylename; - } - } - - return ''; - }, - - /** - * Function: getStylenames - * - * Returns the stylenames in a style of the form [(stylename|key=value);] - * or an empty array if the given style does not contain any stylenames. - * - * Parameters: - * - * style - String of the form [(stylename|key=value);]. - */ - getStylenames: function(style) - { - var result = []; - - if (style != null) - { - var pairs = style.split(';'); - - for (var i = 0; i < pairs.length; i++) - { - if (pairs[i].indexOf('=') < 0) - { - result.push(pairs[i]); - } - } - } - - return result; - }, - - /** - * Function: indexOfStylename - * - * Returns the index of the given stylename in the given style. This - * returns -1 if the given stylename does not occur (as a stylename) in the - * given style, otherwise it returns the index of the first character. - */ - indexOfStylename: function(style, stylename) - { - if (style != null && stylename != null) - { - var tokens = style.split(';'); - var pos = 0; - - for (var i = 0; i < tokens.length; i++) - { - if (tokens[i] == stylename) - { - return pos; - } - - pos += tokens[i].length + 1; - } - } - - return -1; - }, - - /** - * Function: addStylename - * - * Adds the specified stylename to the given style if it does not already - * contain the stylename. - */ - addStylename: function(style, stylename) - { - if (mxUtils.indexOfStylename(style, stylename) < 0) - { - if (style == null) - { - style = ''; - } - else if (style.length > 0 && style.charAt(style.length - 1) != ';') - { - style += ';'; - } - - style += stylename; - } - - return style; - }, - - /** - * Function: removeStylename - * - * Removes all occurrences of the specified stylename in the given style - * and returns the updated style. Trailing semicolons are not preserved. - */ - removeStylename: function(style, stylename) - { - var result = []; - - if (style != null) - { - var tokens = style.split(';'); - - for (var i = 0; i < tokens.length; i++) - { - if (tokens[i] != stylename) - { - result.push(tokens[i]); - } - } - } - - return result.join(';'); - }, - - /** - * Function: removeAllStylenames - * - * Removes all stylenames from the given style and returns the updated - * style. - */ - removeAllStylenames: function(style) - { - var result = []; - - if (style != null) - { - var tokens = style.split(';'); - - for (var i = 0; i < tokens.length; i++) - { - // Keeps the key, value assignments - if (tokens[i].indexOf('=') >= 0) - { - result.push(tokens[i]); - } - } - } - - return result.join(';'); - }, - - /** - * Function: setCellStyles - * - * Assigns the value for the given key in the styles of the given cells, or - * removes the key from the styles if the value is null. - * - * Parameters: - * - * model - <mxGraphModel> to execute the transaction in. - * cells - Array of <mxCells> to be updated. - * key - Key of the style to be changed. - * value - New value for the given key. - */ - setCellStyles: function(model, cells, key, value) - { - if (cells != null && cells.length > 0) - { - model.beginUpdate(); - try - { - for (var i = 0; i < cells.length; i++) - { - if (cells[i] != null) - { - var style = mxUtils.setStyle( - model.getStyle(cells[i]), - key, value); - model.setStyle(cells[i], style); - } - } - } - finally - { - model.endUpdate(); - } - } - }, - - /** - * Function: setStyle - * - * Adds or removes the given key, value pair to the style and returns the - * new style. If value is null or zero length then the key is removed from - * the style. This is for cell styles, not for CSS styles. - * - * Parameters: - * - * style - String of the form [(stylename|key=value);]. - * key - Key of the style to be changed. - * value - New value for the given key. - */ - setStyle: function(style, key, value) - { - var isValue = value != null && (typeof(value.length) == 'undefined' || value.length > 0); - - if (style == null || style.length == 0) - { - if (isValue) - { - style = key+'='+value; - } - } - else - { - var index = style.indexOf(key+'='); - - if (index < 0) - { - if (isValue) - { - var sep = (style.charAt(style.length-1) == ';') ? '' : ';'; - style = style + sep + key+'='+value; - } - } - else - { - var tmp = (isValue) ? (key + '=' + value) : ''; - var cont = style.indexOf(';', index); - - if (!isValue) - { - cont++; - } - - style = style.substring(0, index) + tmp + - ((cont > index) ? style.substring(cont) : ''); - } - } - - return style; - }, - - /** - * Function: setCellStyleFlags - * - * Sets or toggles the flag bit for the given key in the cell's styles. - * If value is null then the flag is toggled. - * - * Example: - * - * (code) - * var cells = graph.getSelectionCells(); - * mxUtils.setCellStyleFlags(graph.model, - * cells, - * mxConstants.STYLE_FONTSTYLE, - * mxConstants.FONT_BOLD); - * (end) - * - * Toggles the bold font style. - * - * Parameters: - * - * model - <mxGraphModel> that contains the cells. - * cells - Array of <mxCells> to change the style for. - * key - Key of the style to be changed. - * flag - Integer for the bit to be changed. - * value - Optional boolean value for the flag. - */ - setCellStyleFlags: function(model, cells, key, flag, value) - { - if (cells != null && cells.length > 0) - { - model.beginUpdate(); - try - { - for (var i = 0; i < cells.length; i++) - { - if (cells[i] != null) - { - var style = mxUtils.setStyleFlag( - model.getStyle(cells[i]), - key, flag, value); - model.setStyle(cells[i], style); - } - } - } - finally - { - model.endUpdate(); - } - } - }, - - /** - * Function: setStyleFlag - * - * Sets or removes the given key from the specified style and returns the - * new style. If value is null then the flag is toggled. - * - * Parameters: - * - * style - String of the form [(stylename|key=value);]. - * key - Key of the style to be changed. - * flag - Integer for the bit to be changed. - * value - Optional boolean value for the given flag. - */ - setStyleFlag: function(style, key, flag, value) - { - if (style == null || style.length == 0) - { - if (value || value == null) - { - style = key+'='+flag; - } - else - { - style = key+'=0'; - } - } - else - { - var index = style.indexOf(key+'='); - - if (index < 0) - { - var sep = (style.charAt(style.length-1) == ';') ? '' : ';'; - - if (value || value == null) - { - style = style + sep + key + '=' + flag; - } - else - { - style = style + sep + key + '=0'; - } - } - else - { - var cont = style.indexOf(';', index); - var tmp = ''; - - if (cont < 0) - { - tmp = style.substring(index+key.length+1); - } - else - { - tmp = style.substring(index+key.length+1, cont); - } - - if (value == null) - { - tmp = parseInt(tmp) ^ flag; - } - else if (value) - { - tmp = parseInt(tmp) | flag; - } - else - { - tmp = parseInt(tmp) & ~flag; - } - - style = style.substring(0, index) + key + '=' + tmp + - ((cont >= 0) ? style.substring(cont) : ''); - } - } - - return style; - }, - - /** - * Function: getSizeForString - * - * Returns an <mxRectangle> with the size (width and height in pixels) of - * the given string. The string may contain HTML markup. Newlines should be - * converted to <br> before calling this method. - * - * Example: - * - * (code) - * var label = graph.getLabel(cell).replace(/\n/g, "<br>"); - * var size = graph.getSizeForString(label); - * (end) - * - * Parameters: - * - * text - String whose size should be returned. - * fontSize - Integer that specifies the font size in pixels. Default is - * <mxConstants.DEFAULT_FONTSIZE>. - * fontFamily - String that specifies the name of the font family. Default - * is <mxConstants.DEFAULT_FONTFAMILY>. - */ - getSizeForString: function(text, fontSize, fontFamily) - { - var div = document.createElement('div'); - - // Sets the font size and family if non-default - div.style.fontSize = (fontSize || mxConstants.DEFAULT_FONTSIZE) + 'px'; - div.style.fontFamily = fontFamily || mxConstants.DEFAULT_FONTFAMILY; - - // Disables block layout and outside wrapping and hides the div - div.style.position = 'absolute'; - div.style.display = 'inline'; - div.style.visibility = 'hidden'; - - // Adds the text and inserts into DOM for updating of size - div.innerHTML = text; - document.body.appendChild(div); - - // Gets the size and removes from DOM - var size = new mxRectangle(0, 0, div.offsetWidth, div.offsetHeight); - document.body.removeChild(div); - - return size; - }, - - /** - * Function: getViewXml - */ - getViewXml: function(graph, scale, cells, x0, y0) - { - x0 = (x0 != null) ? x0 : 0; - y0 = (y0 != null) ? y0 : 0; - scale = (scale != null) ? scale : 1; - - if (cells == null) - { - var model = graph.getModel(); - cells = [model.getRoot()]; - } - - var view = graph.getView(); - var result = null; - - // Disables events on the view - var eventsEnabled = view.isEventsEnabled(); - view.setEventsEnabled(false); - - // Workaround for label bounds not taken into account for image export. - // Creates a temporary draw pane which is used for rendering the text. - // Text rendering is required for finding the bounds of the labels. - var drawPane = view.drawPane; - var overlayPane = view.overlayPane; - - if (graph.dialect == mxConstants.DIALECT_SVG) - { - view.drawPane = document.createElementNS(mxConstants.NS_SVG, 'g'); - view.canvas.appendChild(view.drawPane); - - // Redirects cell overlays into temporary container - view.overlayPane = document.createElementNS(mxConstants.NS_SVG, 'g'); - view.canvas.appendChild(view.overlayPane); - } - else - { - view.drawPane = view.drawPane.cloneNode(false); - view.canvas.appendChild(view.drawPane); - - // Redirects cell overlays into temporary container - view.overlayPane = view.overlayPane.cloneNode(false); - view.canvas.appendChild(view.overlayPane); - } - - // Resets the translation - var translate = view.getTranslate(); - view.translate = new mxPoint(x0, y0); - - // Creates the temporary cell states in the view - var temp = new mxTemporaryCellStates(graph.getView(), scale, cells); - - try - { - var enc = new mxCodec(); - result = enc.encode(graph.getView()); - } - finally - { - temp.destroy(); - view.translate = translate; - view.canvas.removeChild(view.drawPane); - view.canvas.removeChild(view.overlayPane); - view.drawPane = drawPane; - view.overlayPane = overlayPane; - view.setEventsEnabled(eventsEnabled); - } - - return result; - }, - - /** - * Function: getScaleForPageCount - * - * Returns the scale to be used for printing the graph with the given - * bounds across the specifies number of pages with the given format. The - * scale is always computed such that it given the given amount or fewer - * pages in the print output. See <mxPrintPreview> for an example. - * - * Parameters: - * - * pageCount - Specifies the number of pages in the print output. - * graph - <mxGraph> that should be printed. - * pageFormat - Optional <mxRectangle> that specifies the page format. - * Default is <mxConstants.PAGE_FORMAT_A4_PORTRAIT>. - * border - The border along each side of every page. - */ - getScaleForPageCount: function(pageCount, graph, pageFormat, border) - { - if (pageCount < 1) - { - // We can't work with less than 1 page, return no scale - // change - return 1; - } - - pageFormat = (pageFormat != null) ? pageFormat : mxConstants.PAGE_FORMAT_A4_PORTRAIT; - border = (border != null) ? border : 0; - - var availablePageWidth = pageFormat.width - (border * 2); - var availablePageHeight = pageFormat.height - (border * 2); - - // Work out the number of pages required if the - // graph is not scaled. - var graphBounds = graph.getGraphBounds().clone(); - var sc = graph.getView().getScale(); - graphBounds.width /= sc; - graphBounds.height /= sc; - var graphWidth = graphBounds.width; - var graphHeight = graphBounds.height; - - var scale = 1; - - // The ratio of the width/height for each printer page - var pageFormatAspectRatio = availablePageWidth / availablePageHeight; - // The ratio of the width/height for the graph to be printer - var graphAspectRatio = graphWidth / graphHeight; - - // The ratio of horizontal pages / vertical pages for this - // graph to maintain its aspect ratio on this page format - var pagesAspectRatio = graphAspectRatio / pageFormatAspectRatio; - - // Factor the square root of the page count up and down - // by the pages aspect ratio to obtain a horizontal and - // vertical page count that adds up to the page count - // and has the correct aspect ratio - var pageRoot = Math.sqrt(pageCount); - var pagesAspectRatioSqrt = Math.sqrt(pagesAspectRatio); - var numRowPages = pageRoot * pagesAspectRatioSqrt; - var numColumnPages = pageRoot / pagesAspectRatioSqrt; - - // These value are rarely more than 2 rounding downs away from - // a total that meets the page count. In cases of one being less - // than 1 page, the other value can be too high and take more iterations - // In this case, just change that value to be the page count, since - // we know the other value is 1 - if (numRowPages < 1 && numColumnPages > pageCount) - { - var scaleChange = numColumnPages / pageCount; - numColumnPages = pageCount; - numRowPages /= scaleChange; - } - - if (numColumnPages < 1 && numRowPages > pageCount) - { - var scaleChange = numRowPages / pageCount; - numRowPages = pageCount; - numColumnPages /= scaleChange; - } - - var currentTotalPages = Math.ceil(numRowPages) * Math.ceil(numColumnPages); - - var numLoops = 0; - - // Iterate through while the rounded up number of pages comes to - // a total greater than the required number - while (currentTotalPages > pageCount) - { - // Round down the page count (rows or columns) that is - // closest to its next integer down in percentage terms. - // i.e. Reduce the page total by reducing the total - // page area by the least possible amount - - var roundRowDownProportion = Math.floor(numRowPages) / numRowPages; - var roundColumnDownProportion = Math.floor(numColumnPages) / numColumnPages; - - // If the round down proportion is, work out the proportion to - // round down to 1 page less - if (roundRowDownProportion == 1) - { - roundRowDownProportion = Math.floor(numRowPages-1) / numRowPages; - } - if (roundColumnDownProportion == 1) - { - roundColumnDownProportion = Math.floor(numColumnPages-1) / numColumnPages; - } - - // Check which rounding down is smaller, but in the case of very small roundings - // try the other dimension instead - var scaleChange = 1; - - // Use the higher of the two values - if (roundRowDownProportion > roundColumnDownProportion) - { - scaleChange = roundRowDownProportion; - } - else - { - scaleChange = roundColumnDownProportion; - } - - numRowPages = numRowPages * scaleChange; - numColumnPages = numColumnPages * scaleChange; - currentTotalPages = Math.ceil(numRowPages) * Math.ceil(numColumnPages); - - numLoops++; - - if (numLoops > 10) - { - break; - } - } - - // Work out the scale from the number of row pages required - // The column pages will give the same value - var posterWidth = availablePageWidth * numRowPages; - scale = posterWidth / graphWidth; - - // Allow for rounding errors - return scale * 0.99999; - }, - - /** - * Function: show - * - * Copies the styles and the markup from the graph's container into the - * given document and removes all cursor styles. The document is returned. - * - * This function should be called from within the document with the graph. - * If you experience problems with missing stylesheets in IE then try adding - * the domain to the trusted sites. - * - * Parameters: - * - * graph - <mxGraph> to be copied. - * doc - Document where the new graph is created. - * x0 - X-coordinate of the graph view origin. Default is 0. - * y0 - Y-coordinate of the graph view origin. Default is 0. - */ - show: function(graph, doc, x0, y0) - { - x0 = (x0 != null) ? x0 : 0; - y0 = (y0 != null) ? y0 : 0; - - if (doc == null) - { - var wnd = window.open(); - doc = wnd.document; - } - else - { - doc.open(); - } - - var bounds = graph.getGraphBounds(); - var dx = -bounds.x + x0; - var dy = -bounds.y + y0; - - // Needs a special way of creating the page so that no click is required - // to refresh the contents after the external CSS styles have been loaded. - // To avoid a click or programmatic refresh, the styleSheets[].cssText - // property is copied over from the original document. - if (mxClient.IS_IE) - { - var html = '<html>'; - html += '<head>'; - - var base = document.getElementsByTagName('base'); - - for (var i = 0; i < base.length; i++) - { - html += base[i].outerHTML; - } - - html += '<style>'; - - // Copies the stylesheets without having to load them again - for (var i = 0; i < document.styleSheets.length; i++) - { - try - { - html += document.styleSheets(i).cssText; - } - catch (e) - { - // ignore security exception - } - } - - html += '</style>'; - - html += '</head>'; - html += '<body>'; - - // Copies the contents of the graph container - html += graph.container.innerHTML; - - html += '</body>'; - html += '<html>'; - - doc.writeln(html); - doc.close(); - - // Makes sure the inner container is on the top, left - var node = doc.body.getElementsByTagName('DIV')[0]; - - if (node != null) - { - node.style.position = 'absolute'; - node.style.left = dx + 'px'; - node.style.top = dy + 'px'; - } - } - else - { - doc.writeln('<html'); - doc.writeln('<head>'); - - var base = document.getElementsByTagName('base'); - - for (var i=0; i<base.length; i++) - { - doc.writeln(mxUtils.getOuterHtml(base[i])); - } - - var links = document.getElementsByTagName('link'); - - for (var i=0; i<links.length; i++) - { - doc.writeln(mxUtils.getOuterHtml(links[i])); - } - - var styles = document.getElementsByTagName('style'); - - for (var i=0; i<styles.length; i++) - { - doc.writeln(mxUtils.getOuterHtml(styles[i])); - } - - doc.writeln('</head>'); - doc.writeln('</html>'); - doc.close(); - - // Workaround for FF2 which has no body element in a document where - // the body has been added using document.write. - if (doc.body == null) - { - doc.documentElement.appendChild(doc.createElement('body')); - } - - // Workaround for missing scrollbars in FF - doc.body.style.overflow = 'auto'; - - var node = graph.container.firstChild; - - while (node != null) - { - var clone = node.cloneNode(true); - doc.body.appendChild(clone); - node = node.nextSibling; - } - - // Shifts negative coordinates into visible space - var node = doc.getElementsByTagName('g')[0]; - - if (node != null) - { - node.setAttribute('transform', 'translate(' + dx + ',' + dy + ')'); - - // Updates the size of the SVG container - var root = node.ownerSVGElement; - root.setAttribute('width', bounds.width + Math.max(bounds.x, 0) + 3); - root.setAttribute('height', bounds.height + Math.max(bounds.y, 0) + 3); - } - } - - mxUtils.removeCursors(doc.body); - - return doc; - }, - - /** - * Function: printScreen - * - * Prints the specified graph using a new window and the built-in print - * dialog. - * - * This function should be called from within the document with the graph. - * - * Parameters: - * - * graph - <mxGraph> to be printed. - */ - printScreen: function(graph) - { - var wnd = window.open(); - mxUtils.show(graph, wnd.document); - - var print = function() - { - wnd.focus(); - wnd.print(); - wnd.close(); - }; - - // Workaround for Google Chrome which needs a bit of a - // delay in order to render the SVG contents - if (mxClient.IS_GC) - { - wnd.setTimeout(print, 500); - } - else - { - print(); - } - }, - - /** - * Function: popup - * - * Shows the specified text content in a new <mxWindow> or a new browser - * window if isInternalWindow is false. - * - * Parameters: - * - * content - String that specifies the text to be displayed. - * isInternalWindow - Optional boolean indicating if an mxWindow should be - * used instead of a new browser window. Default is false. - */ - popup: function(content, isInternalWindow) - { - if (isInternalWindow) - { - var div = document.createElement('div'); - - div.style.overflow = 'scroll'; - div.style.width = '636px'; - div.style.height = '460px'; - - var pre = document.createElement('pre'); - pre.innerHTML = mxUtils.htmlEntities(content, false). - replace(/\n/g,'<br>').replace(/ /g, ' '); - - div.appendChild(pre); - - var w = document.body.clientWidth; - var h = (document.body.clientHeight || document.documentElement.clientHeight); - var wnd = new mxWindow('Popup Window', div, - w/2-320, h/2-240, 640, 480, false, true); - - wnd.setClosable(true); - wnd.setVisible(true); - } - else - { - // Wraps up the XML content in a textarea - if (mxClient.IS_NS) - { - var wnd = window.open(); - wnd.document.writeln('<pre>'+mxUtils.htmlEntities(content)+'</pre'); - wnd.document.close(); - } - else - { - var wnd = window.open(); - var pre = wnd.document.createElement('pre'); - pre.innerHTML = mxUtils.htmlEntities(content, false). - replace(/\n/g,'<br>').replace(/ /g, ' '); - wnd.document.body.appendChild(pre); - } - } - }, - - /** - * Function: alert - * - * Displayss the given alert in a new dialog. This implementation uses the - * built-in alert function. This is used to display validation errors when - * connections cannot be changed or created. - * - * Parameters: - * - * message - String specifying the message to be displayed. - */ - alert: function(message) - { - alert(message); - }, - - /** - * Function: prompt - * - * Displays the given message in a prompt dialog. This implementation uses - * the built-in prompt function. - * - * Parameters: - * - * message - String specifying the message to be displayed. - * defaultValue - Optional string specifying the default value. - */ - prompt: function(message, defaultValue) - { - return prompt(message, defaultValue); - }, - - /** - * Function: confirm - * - * Displays the given message in a confirm dialog. This implementation uses - * the built-in confirm function. - * - * Parameters: - * - * message - String specifying the message to be displayed. - */ - confirm: function(message) - { - return confirm(message); - }, - - /** - * Function: error - * - * Displays the given error message in a new <mxWindow> of the given width. - * If close is true then an additional close button is added to the window. - * The optional icon specifies the icon to be used for the window. Default - * is <mxUtils.errorImage>. - * - * Parameters: - * - * message - String specifying the message to be displayed. - * width - Integer specifying the width of the window. - * close - Optional boolean indicating whether to add a close button. - * icon - Optional icon for the window decoration. - */ - error: function(message, width, close, icon) - { - var div = document.createElement('div'); - div.style.padding = '20px'; - - var img = document.createElement('img'); - img.setAttribute('src', icon || mxUtils.errorImage); - img.setAttribute('valign', 'bottom'); - img.style.verticalAlign = 'middle'; - div.appendChild(img); - - div.appendChild(document.createTextNode('\u00a0')); // - div.appendChild(document.createTextNode('\u00a0')); // - div.appendChild(document.createTextNode('\u00a0')); // - mxUtils.write(div, message); - - var w = document.body.clientWidth; - var h = (document.body.clientHeight || document.documentElement.clientHeight); - var warn = new mxWindow(mxResources.get(mxUtils.errorResource) || - mxUtils.errorResource, div, (w-width)/2, h/4, width, null, - false, true); - - if (close) - { - mxUtils.br(div); - - var tmp = document.createElement('p'); - var button = document.createElement('button'); - - if (mxClient.IS_IE) - { - button.style.cssText = 'float:right'; - } - else - { - button.setAttribute('style', 'float:right'); - } - - mxEvent.addListener(button, 'click', function(evt) - { - warn.destroy(); - }); - - mxUtils.write(button, mxResources.get(mxUtils.closeResource) || - mxUtils.closeResource); - - tmp.appendChild(button); - div.appendChild(tmp); - - mxUtils.br(div); - - warn.setClosable(true); - } - - warn.setVisible(true); - - return warn; - }, - - /** - * Function: makeDraggable - * - * Configures the given DOM element to act as a drag source for the - * specified graph. Returns a a new <mxDragSource>. If - * <mxDragSource.guideEnabled> is enabled then the x and y arguments must - * be used in funct to match the preview location. - * - * Example: - * - * (code) - * var funct = function(graph, evt, cell, x, y) - * { - * if (graph.canImportCell(cell)) - * { - * var parent = graph.getDefaultParent(); - * var vertex = null; - * - * graph.getModel().beginUpdate(); - * try - * { - * vertex = graph.insertVertex(parent, null, 'Hello', x, y, 80, 30); - * } - * finally - * { - * graph.getModel().endUpdate(); - * } - * - * graph.setSelectionCell(vertex); - * } - * } - * - * var img = document.createElement('img'); - * img.setAttribute('src', 'editors/images/rectangle.gif'); - * img.style.position = 'absolute'; - * img.style.left = '0px'; - * img.style.top = '0px'; - * img.style.width = '16px'; - * img.style.height = '16px'; - * - * var dragImage = img.cloneNode(true); - * dragImage.style.width = '32px'; - * dragImage.style.height = '32px'; - * mxUtils.makeDraggable(img, graph, funct, dragImage); - * document.body.appendChild(img); - * (end) - * - * Parameters: - * - * element - DOM element to make draggable. - * graphF - <mxGraph> that acts as the drop target or a function that takes a - * mouse event and returns the current <mxGraph>. - * funct - Function to execute on a successful drop. - * dragElement - Optional DOM node to be used for the drag preview. - * dx - Optional horizontal offset between the cursor and the drag - * preview. - * dy - Optional vertical offset between the cursor and the drag - * preview. - * autoscroll - Optional boolean that specifies if autoscroll should be - * used. Default is mxGraph.autoscroll. - * scalePreview - Optional boolean that specifies if the preview element - * should be scaled according to the graph scale. If this is true, then - * the offsets will also be scaled. Default is false. - * highlightDropTargets - Optional boolean that specifies if dropTargets - * should be highlighted. Default is true. - * getDropTarget - Optional function to return the drop target for a given - * location (x, y). Default is mxGraph.getCellAt. - */ - makeDraggable: function(element, graphF, funct, dragElement, dx, dy, autoscroll, - scalePreview, highlightDropTargets, getDropTarget) - { - var dragSource = new mxDragSource(element, funct); - dragSource.dragOffset = new mxPoint((dx != null) ? dx : 0, - (dy != null) ? dy : mxConstants.TOOLTIP_VERTICAL_OFFSET); - dragSource.autoscroll = autoscroll; - - // Cannot enable this by default. This needs to be enabled in the caller - // if the funct argument uses the new x- and y-arguments. - dragSource.setGuidesEnabled(false); - - if (highlightDropTargets != null) - { - dragSource.highlightDropTargets = highlightDropTargets; - } - - // Overrides function to find drop target cell - if (getDropTarget != null) - { - dragSource.getDropTarget = getDropTarget; - } - - // Overrides function to get current graph - dragSource.getGraphForEvent = function(evt) - { - return (typeof(graphF) == 'function') ? graphF(evt) : graphF; - }; - - // Translates switches into dragSource customizations - if (dragElement != null) - { - dragSource.createDragElement = function() - { - return dragElement.cloneNode(true); - }; - - if (scalePreview) - { - dragSource.createPreviewElement = function(graph) - { - var elt = dragElement.cloneNode(true); - - var w = parseInt(elt.style.width); - var h = parseInt(elt.style.height); - elt.style.width = Math.round(w * graph.view.scale) + 'px'; - elt.style.height = Math.round(h * graph.view.scale) + 'px'; - - return elt; - }; - } - } - - return dragSource; - } - -}; |