/** * Copyright (c) 2006-2015, JGraph Ltd * Copyright (c) 2006-2015, Gaudenz Alder */ mxCodecRegistry.register(function() { /** * Class: mxChildChangeCodec * * Codec for s. This class is created and registered * dynamically at load time and used implicitly via and * the . * * Transient Fields: * * - model * - previous * - previousIndex * - child * * Reference Fields: * * - parent */ var codec = new mxObjectCodec(new mxChildChange(), ['model', 'child', 'previousIndex'], ['parent', 'previous']); /** * Function: isReference * * Returns true for the child attribute if the child * cell had a previous parent or if we're reading the * child as an attribute rather than a child node, in * which case it's always a reference. */ codec.isReference = function(obj, attr, value, isWrite) { if (attr == 'child' && (!isWrite || obj.model.contains(obj.previous))) { return true; } return mxUtils.indexOf(this.idrefs, attr) >= 0; }; /** * Function: isExcluded * * Excludes references to parent or previous if not in the model. */ codec.isExcluded = function(obj, attr, value, write) { return mxObjectCodec.prototype.isExcluded.apply(this, arguments) || (write && value != null && (attr == 'previous' || attr == 'parent') && !obj.model.contains(value)); }; /** * Function: afterEncode * * Encodes the child recusively and adds the result * to the given node. */ codec.afterEncode = function(enc, obj, node) { if (this.isReference(obj, 'child', obj.child, true)) { // Encodes as reference (id) node.setAttribute('child', enc.getId(obj.child)); } else { // At this point, the encoder is no longer able to know which cells // are new, so we have to encode the complete cell hierarchy and // ignore the ones that are already there at decoding time. Note: // This can only be resolved by moving the notify event into the // execute of the edit. enc.encodeCell(obj.child, node); } return node; }; /** * Function: beforeDecode * * Decodes the any child nodes as using the respective * codec from the registry. */ codec.beforeDecode = function(dec, node, obj) { if (node.firstChild != null && node.firstChild.nodeType == mxConstants.NODETYPE_ELEMENT) { // Makes sure the original node isn't modified node = node.cloneNode(true); var tmp = node.firstChild; obj.child = dec.decodeCell(tmp, false); var tmp2 = tmp.nextSibling; tmp.parentNode.removeChild(tmp); tmp = tmp2; while (tmp != null) { tmp2 = tmp.nextSibling; if (tmp.nodeType == mxConstants.NODETYPE_ELEMENT) { // Ignores all existing cells because those do not need to // be re-inserted into the model. Since the encoded version // of these cells contains the new parent, this would leave // to an inconsistent state on the model (ie. a parent // change without a call to parentForCellChanged). var id = tmp.getAttribute('id'); if (dec.lookup(id) == null) { dec.decodeCell(tmp); } } tmp.parentNode.removeChild(tmp); tmp = tmp2; } } else { var childRef = node.getAttribute('child'); obj.child = dec.getObject(childRef); } return node; }; /** * Function: afterDecode * * Restores object state in the child change. */ codec.afterDecode = function(dec, node, obj) { // Cells are decoded here after a complete transaction so the previous // parent must be restored on the cell for the case where the cell was // added. This is needed for the local model to identify the cell as a // new cell and register the ID. if (obj.child != null) { if (obj.child.parent != null && obj.previous != null && obj.child.parent != obj.previous) { obj.previous = obj.child.parent; } obj.child.parent = obj.previous; obj.previous = obj.parent; obj.previousIndex = obj.index; } return obj; }; // Returns the codec into the registry return codec; }());