| /** |
| * Copyright (c) 2006-2015, JGraph Ltd |
| * Copyright (c) 2006-2015, Gaudenz Alder |
| */ |
| /** |
| * Class: mxCodec |
| * |
| * XML codec for JavaScript object graphs. See <mxObjectCodec> for a |
| * description of the general encoding/decoding scheme. This class uses the |
| * codecs registered in <mxCodecRegistry> for encoding/decoding each object. |
| * |
| * References: |
| * |
| * In order to resolve references, especially forward references, the mxCodec |
| * constructor must be given the document that contains the referenced |
| * elements. |
| * |
| * Examples: |
| * |
| * The following code is used to encode a graph model. |
| * |
| * (code) |
| * var encoder = new mxCodec(); |
| * var result = encoder.encode(graph.getModel()); |
| * var xml = mxUtils.getXml(result); |
| * (end) |
| * |
| * Example: |
| * |
| * Using the code below, an XML document is decoded into an existing model. The |
| * document may be obtained using one of the functions in mxUtils for loading |
| * an XML file, eg. <mxUtils.get>, or using <mxUtils.parseXml> for parsing an |
| * XML string. |
| * |
| * (code) |
| * var doc = mxUtils.parseXml(xmlString); |
| * var codec = new mxCodec(doc); |
| * codec.decode(doc.documentElement, graph.getModel()); |
| * (end) |
| * |
| * Example: |
| * |
| * This example demonstrates parsing a list of isolated cells into an existing |
| * graph model. Note that the cells do not have a parent reference so they can |
| * be added anywhere in the cell hierarchy after parsing. |
| * |
| * (code) |
| * var xml = '<root><mxCell id="2" value="Hello," vertex="1"><mxGeometry x="20" y="20" width="80" height="30" as="geometry"/></mxCell><mxCell id="3" value="World!" vertex="1"><mxGeometry x="200" y="150" width="80" height="30" as="geometry"/></mxCell><mxCell id="4" value="" edge="1" source="2" target="3"><mxGeometry relative="1" as="geometry"/></mxCell></root>'; |
| * var doc = mxUtils.parseXml(xml); |
| * var codec = new mxCodec(doc); |
| * var elt = doc.documentElement.firstChild; |
| * var cells = []; |
| * |
| * while (elt != null) |
| * { |
| * cells.push(codec.decode(elt)); |
| * elt = elt.nextSibling; |
| * } |
| * |
| * graph.addCells(cells); |
| * (end) |
| * |
| * Example: |
| * |
| * Using the following code, the selection cells of a graph are encoded and the |
| * output is displayed in a dialog box. |
| * |
| * (code) |
| * var enc = new mxCodec(); |
| * var cells = graph.getSelectionCells(); |
| * mxUtils.alert(mxUtils.getPrettyXml(enc.encode(cells))); |
| * (end) |
| * |
| * Newlines in the XML can be converted to <br>, in which case a '<br>' argument |
| * must be passed to <mxUtils.getXml> as the second argument. |
| * |
| * Debugging: |
| * |
| * For debugging I/O you can use the following code to get the sequence of |
| * encoded objects: |
| * |
| * (code) |
| * var oldEncode = mxCodec.prototype.encode; |
| * mxCodec.prototype.encode = function(obj) |
| * { |
| * mxLog.show(); |
| * mxLog.debug('mxCodec.encode: obj='+mxUtils.getFunctionName(obj.constructor)); |
| * |
| * return oldEncode.apply(this, arguments); |
| * }; |
| * (end) |
| * |
| * Note that the I/O system adds object codecs for new object automatically. For |
| * decoding those objects, the constructor should be written as follows: |
| * |
| * (code) |
| * var MyObj = function(name) |
| * { |
| * // ... |
| * }; |
| * (end) |
| * |
| * Constructor: mxCodec |
| * |
| * Constructs an XML encoder/decoder for the specified |
| * owner document. |
| * |
| * Parameters: |
| * |
| * document - Optional XML document that contains the data. |
| * If no document is specified then a new document is created |
| * using <mxUtils.createXmlDocument>. |
| */ |
| function mxCodec(document) |
| { |
| this.document = document || mxUtils.createXmlDocument(); |
| this.objects = []; |
| }; |
| |
| /** |
| * Variable: document |
| * |
| * The owner document of the codec. |
| */ |
| mxCodec.prototype.document = null; |
| |
| /** |
| * Variable: objects |
| * |
| * Maps from IDs to objects. |
| */ |
| mxCodec.prototype.objects = null; |
| |
| /** |
| * Variable: elements |
| * |
| * Lookup table for resolving IDs to elements. |
| */ |
| mxCodec.prototype.elements = null; |
| |
| /** |
| * Variable: encodeDefaults |
| * |
| * Specifies if default values should be encoded. Default is false. |
| */ |
| mxCodec.prototype.encodeDefaults = false; |
| |
| |
| /** |
| * Function: putObject |
| * |
| * Assoiates the given object with the given ID and returns the given object. |
| * |
| * Parameters |
| * |
| * id - ID for the object to be associated with. |
| * obj - Object to be associated with the ID. |
| */ |
| mxCodec.prototype.putObject = function(id, obj) |
| { |
| this.objects[id] = obj; |
| |
| return obj; |
| }; |
| |
| /** |
| * Function: getObject |
| * |
| * Returns the decoded object for the element with the specified ID in |
| * <document>. If the object is not known then <lookup> is used to find an |
| * object. If no object is found, then the element with the respective ID |
| * from the document is parsed using <decode>. |
| */ |
| mxCodec.prototype.getObject = function(id) |
| { |
| var obj = null; |
| |
| if (id != null) |
| { |
| obj = this.objects[id]; |
| |
| if (obj == null) |
| { |
| obj = this.lookup(id); |
| |
| if (obj == null) |
| { |
| var node = this.getElementById(id); |
| |
| if (node != null) |
| { |
| obj = this.decode(node); |
| } |
| } |
| } |
| } |
| |
| return obj; |
| }; |
| |
| /** |
| * Function: lookup |
| * |
| * Hook for subclassers to implement a custom lookup mechanism for cell IDs. |
| * This implementation always returns null. |
| * |
| * Example: |
| * |
| * (code) |
| * var codec = new mxCodec(); |
| * codec.lookup = function(id) |
| * { |
| * return model.getCell(id); |
| * }; |
| * (end) |
| * |
| * Parameters: |
| * |
| * id - ID of the object to be returned. |
| */ |
| mxCodec.prototype.lookup = function(id) |
| { |
| return null; |
| }; |
| |
| /** |
| * Function: getElementById |
| * |
| * Returns the element with the given ID from <document>. |
| * |
| * Parameters: |
| * |
| * id - String that contains the ID. |
| */ |
| mxCodec.prototype.getElementById = function(id) |
| { |
| if (this.elements == null) |
| { |
| // Throws custom error for cases where a reference should be resolved |
| // in an empty document. This happens if an XML node is decoded without |
| // passing the owner document to the codec constructor. |
| if (this.document.documentElement == null) |
| { |
| throw new Error('mxCodec constructor needs document parameter'); |
| } |
| |
| this.elements = new Object(); |
| this.addElement(this.document.documentElement); |
| } |
| |
| return this.elements[id]; |
| }; |
| |
| /** |
| * Function: addElement |
| * |
| * Adds the given element to <elements> if it has an ID. |
| */ |
| mxCodec.prototype.addElement = function(node) |
| { |
| if (node.nodeType == mxConstants.NODETYPE_ELEMENT) |
| { |
| var id = node.getAttribute('id'); |
| |
| if (id != null && this.elements[id] == null) |
| { |
| this.elements[id] = node; |
| } |
| } |
| |
| node = node.firstChild; |
| |
| while (node != null) |
| { |
| this.addElement(node); |
| node = node.nextSibling; |
| } |
| }; |
| |
| /** |
| * Function: getId |
| * |
| * Returns the ID of the specified object. This implementation |
| * calls <reference> first and if that returns null handles |
| * the object as an <mxCell> by returning their IDs using |
| * <mxCell.getId>. If no ID exists for the given cell, then |
| * an on-the-fly ID is generated using <mxCellPath.create>. |
| * |
| * Parameters: |
| * |
| * obj - Object to return the ID for. |
| */ |
| mxCodec.prototype.getId = function(obj) |
| { |
| var id = null; |
| |
| if (obj != null) |
| { |
| id = this.reference(obj); |
| |
| if (id == null && obj instanceof mxCell) |
| { |
| id = obj.getId(); |
| |
| if (id == null) |
| { |
| // Uses an on-the-fly Id |
| id = mxCellPath.create(obj); |
| |
| if (id.length == 0) |
| { |
| id = 'root'; |
| } |
| } |
| } |
| } |
| |
| return id; |
| }; |
| |
| /** |
| * Function: reference |
| * |
| * Hook for subclassers to implement a custom method |
| * for retrieving IDs from objects. This implementation |
| * always returns null. |
| * |
| * Example: |
| * |
| * (code) |
| * var codec = new mxCodec(); |
| * codec.reference = function(obj) |
| * { |
| * return obj.getCustomId(); |
| * }; |
| * (end) |
| * |
| * Parameters: |
| * |
| * obj - Object whose ID should be returned. |
| */ |
| mxCodec.prototype.reference = function(obj) |
| { |
| return null; |
| }; |
| |
| /** |
| * Function: encode |
| * |
| * Encodes the specified object and returns the resulting |
| * XML node. |
| * |
| * Parameters: |
| * |
| * obj - Object to be encoded. |
| */ |
| mxCodec.prototype.encode = function(obj) |
| { |
| var node = null; |
| |
| if (obj != null && obj.constructor != null) |
| { |
| var enc = mxCodecRegistry.getCodec(obj.constructor); |
| |
| if (enc != null) |
| { |
| node = enc.encode(this, obj); |
| } |
| else |
| { |
| if (mxUtils.isNode(obj)) |
| { |
| node = mxUtils.importNode(this.document, obj, true); |
| } |
| else |
| { |
| mxLog.warn('mxCodec.encode: No codec for ' + mxUtils.getFunctionName(obj.constructor)); |
| } |
| } |
| } |
| |
| return node; |
| }; |
| |
| /** |
| * Function: decode |
| * |
| * Decodes the given XML node. The optional "into" |
| * argument specifies an existing object to be |
| * used. If no object is given, then a new instance |
| * is created using the constructor from the codec. |
| * |
| * The function returns the passed in object or |
| * the new instance if no object was given. |
| * |
| * Parameters: |
| * |
| * node - XML node to be decoded. |
| * into - Optional object to be decodec into. |
| */ |
| mxCodec.prototype.decode = function(node, into) |
| { |
| var obj = null; |
| |
| if (node != null && node.nodeType == mxConstants.NODETYPE_ELEMENT) |
| { |
| var ctor = null; |
| |
| try |
| { |
| ctor = window[node.nodeName]; |
| } |
| catch (err) |
| { |
| // ignore |
| } |
| |
| var dec = mxCodecRegistry.getCodec(ctor); |
| |
| if (dec != null) |
| { |
| obj = dec.decode(this, node, into); |
| } |
| else |
| { |
| obj = node.cloneNode(true); |
| obj.removeAttribute('as'); |
| } |
| } |
| |
| return obj; |
| }; |
| |
| /** |
| * Function: encodeCell |
| * |
| * Encoding of cell hierarchies is built-into the core, but |
| * is a higher-level function that needs to be explicitely |
| * used by the respective object encoders (eg. <mxModelCodec>, |
| * <mxChildChangeCodec> and <mxRootChangeCodec>). This |
| * implementation writes the given cell and its children as a |
| * (flat) sequence into the given node. The children are not |
| * encoded if the optional includeChildren is false. The |
| * function is in charge of adding the result into the |
| * given node and has no return value. |
| * |
| * Parameters: |
| * |
| * cell - <mxCell> to be encoded. |
| * node - Parent XML node to add the encoded cell into. |
| * includeChildren - Optional boolean indicating if the |
| * function should include all descendents. Default is true. |
| */ |
| mxCodec.prototype.encodeCell = function(cell, node, includeChildren) |
| { |
| node.appendChild(this.encode(cell)); |
| |
| if (includeChildren == null || includeChildren) |
| { |
| var childCount = cell.getChildCount(); |
| |
| for (var i = 0; i < childCount; i++) |
| { |
| this.encodeCell(cell.getChildAt(i), node); |
| } |
| } |
| }; |
| |
| /** |
| * Function: isCellCodec |
| * |
| * Returns true if the given codec is a cell codec. This uses |
| * <mxCellCodec.isCellCodec> to check if the codec is of the |
| * given type. |
| */ |
| mxCodec.prototype.isCellCodec = function(codec) |
| { |
| if (codec != null && typeof(codec.isCellCodec) == 'function') |
| { |
| return codec.isCellCodec(); |
| } |
| |
| return false; |
| }; |
| |
| /** |
| * Function: decodeCell |
| * |
| * Decodes cells that have been encoded using inversion, ie. |
| * where the user object is the enclosing node in the XML, |
| * and restores the group and graph structure in the cells. |
| * Returns a new <mxCell> instance that represents the |
| * given node. |
| * |
| * Parameters: |
| * |
| * node - XML node that contains the cell data. |
| * restoreStructures - Optional boolean indicating whether |
| * the graph structure should be restored by calling insert |
| * and insertEdge on the parent and terminals, respectively. |
| * Default is true. |
| */ |
| mxCodec.prototype.decodeCell = function(node, restoreStructures) |
| { |
| restoreStructures = (restoreStructures != null) ? restoreStructures : true; |
| var cell = null; |
| |
| if (node != null && node.nodeType == mxConstants.NODETYPE_ELEMENT) |
| { |
| // Tries to find a codec for the given node name. If that does |
| // not return a codec then the node is the user object (an XML node |
| // that contains the mxCell, aka inversion). |
| var decoder = mxCodecRegistry.getCodec(node.nodeName); |
| |
| // Tries to find the codec for the cell inside the user object. |
| // This assumes all node names inside the user object are either |
| // not registered or they correspond to a class for cells. |
| if (!this.isCellCodec(decoder)) |
| { |
| var child = node.firstChild; |
| |
| while (child != null && !this.isCellCodec(decoder)) |
| { |
| decoder = mxCodecRegistry.getCodec(child.nodeName); |
| child = child.nextSibling; |
| } |
| } |
| |
| if (!this.isCellCodec(decoder)) |
| { |
| decoder = mxCodecRegistry.getCodec(mxCell); |
| } |
| |
| cell = decoder.decode(this, node); |
| |
| if (restoreStructures) |
| { |
| this.insertIntoGraph(cell); |
| } |
| } |
| |
| return cell; |
| }; |
| |
| /** |
| * Function: insertIntoGraph |
| * |
| * Inserts the given cell into its parent and terminal cells. |
| */ |
| mxCodec.prototype.insertIntoGraph = function(cell) |
| { |
| var parent = cell.parent; |
| var source = cell.getTerminal(true); |
| var target = cell.getTerminal(false); |
| |
| // Fixes possible inconsistencies during insert into graph |
| cell.setTerminal(null, false); |
| cell.setTerminal(null, true); |
| cell.parent = null; |
| |
| if (parent != null) |
| { |
| parent.insert(cell); |
| } |
| |
| if (source != null) |
| { |
| source.insertEdge(cell, true); |
| } |
| |
| if (target != null) |
| { |
| target.insertEdge(cell, false); |
| } |
| }; |
| |
| /** |
| * Function: setAttribute |
| * |
| * Sets the attribute on the specified node to value. This is a |
| * helper method that makes sure the attribute and value arguments |
| * are not null. |
| * |
| * Parameters: |
| * |
| * node - XML node to set the attribute for. |
| * attributes - Attributename to be set. |
| * value - New value of the attribute. |
| */ |
| mxCodec.prototype.setAttribute = function(node, attribute, value) |
| { |
| if (attribute != null && value != null) |
| { |
| node.setAttribute(attribute, value); |
| } |
| }; |