123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623 |
- /**
- * 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)
- {
- this.updateElements();
-
- return this.elements[id];
- };
- /**
- * Function: updateElements
- *
- * Returns the element with the given ID from <document>.
- *
- * Parameters:
- *
- * id - String that contains the ID.
- */
- mxCodec.prototype.updateElements = function()
- {
- if (this.elements == null)
- {
- this.elements = new Object();
-
- if (this.document.documentElement != null)
- {
- this.addElement(this.document.documentElement);
- }
- }
- };
- /**
- * 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)
- {
- if (this.elements[id] == null)
- {
- this.elements[id] = node;
- }
- else if (this.elements[id] != node)
- {
- throw new Error(id + ': Duplicate ID');
- }
- }
- }
-
- 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)
- {
- this.updateElements();
- 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)
- {
- if (parent == cell)
- {
- throw new Error(parent.id + ': Self Reference');
- }
- else
- {
- 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);
- }
- };
- __mxOutput.mxCodec = typeof mxCodec !== 'undefined' ? mxCodec : undefined;
|