// Generated by CoffeeScript 2.4.1 (function() { var NodeType, WriterState, XMLAttribute, XMLCData, XMLComment, XMLDTDAttList, XMLDTDElement, XMLDTDEntity, XMLDTDNotation, XMLDeclaration, XMLDocType, XMLDocument, XMLDocumentCB, XMLElement, XMLProcessingInstruction, XMLRaw, XMLStringWriter, XMLStringifier, XMLText, getValue, isFunction, isObject, isPlainObject, hasProp = {}.hasOwnProperty; ({isObject, isFunction, isPlainObject, getValue} = require('./Utility')); NodeType = require('./NodeType'); XMLDocument = require('./XMLDocument'); XMLElement = require('./XMLElement'); XMLCData = require('./XMLCData'); XMLComment = require('./XMLComment'); XMLRaw = require('./XMLRaw'); XMLText = require('./XMLText'); XMLProcessingInstruction = require('./XMLProcessingInstruction'); XMLDeclaration = require('./XMLDeclaration'); XMLDocType = require('./XMLDocType'); XMLDTDAttList = require('./XMLDTDAttList'); XMLDTDEntity = require('./XMLDTDEntity'); XMLDTDElement = require('./XMLDTDElement'); XMLDTDNotation = require('./XMLDTDNotation'); XMLAttribute = require('./XMLAttribute'); XMLStringifier = require('./XMLStringifier'); XMLStringWriter = require('./XMLStringWriter'); WriterState = require('./WriterState'); // Represents an XML builder module.exports = XMLDocumentCB = class XMLDocumentCB { // Initializes a new instance of `XMLDocumentCB` // `options.keepNullNodes` whether nodes with null values will be kept // or ignored: true or false // `options.keepNullAttributes` whether attributes with null values will be // kept or ignored: true or false // `options.ignoreDecorators` whether decorator strings will be ignored when // converting JS objects: true or false // `options.separateArrayItems` whether array items are created as separate // nodes when passed as an object value: true or false // `options.noDoubleEncoding` whether existing html entities are encoded: // true or false // `options.stringify` a set of functions to use for converting values to // strings // `options.writer` the default XML writer to use for converting nodes to // string. If the default writer is not set, the built-in XMLStringWriter // will be used instead. // `onData` the function to be called when a new chunk of XML is output. The // string containing the XML chunk is passed to `onData` as its first // argument, and the current indentation level as its second argument. // `onEnd` the function to be called when the XML document is completed with // `end`. `onEnd` does not receive any arguments. constructor(options, onData, onEnd) { var writerOptions; this.name = "?xml"; this.type = NodeType.Document; options || (options = {}); writerOptions = {}; if (!options.writer) { options.writer = new XMLStringWriter(); } else if (isPlainObject(options.writer)) { writerOptions = options.writer; options.writer = new XMLStringWriter(); } this.options = options; this.writer = options.writer; this.writerOptions = this.writer.filterOptions(writerOptions); this.stringify = new XMLStringifier(options); this.onDataCallback = onData || function() {}; this.onEndCallback = onEnd || function() {}; this.currentNode = null; this.currentLevel = -1; this.openTags = {}; this.documentStarted = false; this.documentCompleted = false; this.root = null; } // Creates a child element node from the given XMLNode // `node` the child node createChildNode(node) { var att, attName, attributes, child, i, len, ref, ref1; switch (node.type) { case NodeType.CData: this.cdata(node.value); break; case NodeType.Comment: this.comment(node.value); break; case NodeType.Element: attributes = {}; ref = node.attribs; for (attName in ref) { if (!hasProp.call(ref, attName)) continue; att = ref[attName]; attributes[attName] = att.value; } this.node(node.name, attributes); break; case NodeType.Dummy: this.dummy(); break; case NodeType.Raw: this.raw(node.value); break; case NodeType.Text: this.text(node.value); break; case NodeType.ProcessingInstruction: this.instruction(node.target, node.value); break; default: throw new Error("This XML node type is not supported in a JS object: " + node.constructor.name); } ref1 = node.children; // write child nodes recursively for (i = 0, len = ref1.length; i < len; i++) { child = ref1[i]; this.createChildNode(child); if (child.type === NodeType.Element) { this.up(); } } return this; } // Creates a dummy node dummy() { // no-op, just return this return this; } // Creates a node // `name` name of the node // `attributes` an object containing name/value pairs of attributes // `text` element text node(name, attributes, text) { if (name == null) { throw new Error("Missing node name."); } if (this.root && this.currentLevel === -1) { throw new Error("Document can only have one root node. " + this.debugInfo(name)); } this.openCurrent(); name = getValue(name); if (attributes == null) { attributes = {}; } attributes = getValue(attributes); // swap argument order: text <-> attributes if (!isObject(attributes)) { [text, attributes] = [attributes, text]; } this.currentNode = new XMLElement(this, name, attributes); this.currentNode.children = false; this.currentLevel++; this.openTags[this.currentLevel] = this.currentNode; if (text != null) { this.text(text); } return this; } // Creates a child element node or an element type declaration when called // inside the DTD // `name` name of the node // `attributes` an object containing name/value pairs of attributes // `text` element text element(name, attributes, text) { var child, i, len, oldValidationFlag, ref, root; if (this.currentNode && this.currentNode.type === NodeType.DocType) { this.dtdElement(...arguments); } else { if (Array.isArray(name) || isObject(name) || isFunction(name)) { oldValidationFlag = this.options.noValidation; this.options.noValidation = true; root = new XMLDocument(this.options).element('TEMP_ROOT'); root.element(name); this.options.noValidation = oldValidationFlag; ref = root.children; for (i = 0, len = ref.length; i < len; i++) { child = ref[i]; this.createChildNode(child); if (child.type === NodeType.Element) { this.up(); } } } else { this.node(name, attributes, text); } } return this; } // Adds or modifies an attribute // `name` attribute name // `value` attribute value attribute(name, value) { var attName, attValue; if (!this.currentNode || this.currentNode.children) { throw new Error("att() can only be used immediately after an ele() call in callback mode. " + this.debugInfo(name)); } if (name != null) { name = getValue(name); } if (isObject(name)) { // expand if object for (attName in name) { if (!hasProp.call(name, attName)) continue; attValue = name[attName]; this.attribute(attName, attValue); } } else { if (isFunction(value)) { value = value.apply(); } if (this.options.keepNullAttributes && (value == null)) { this.currentNode.attribs[name] = new XMLAttribute(this, name, ""); } else if (value != null) { this.currentNode.attribs[name] = new XMLAttribute(this, name, value); } } return this; } // Creates a text node // `value` element text text(value) { var node; this.openCurrent(); node = new XMLText(this, value); this.onData(this.writer.text(node, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1); return this; } // Creates a CDATA node // `value` element text without CDATA delimiters cdata(value) { var node; this.openCurrent(); node = new XMLCData(this, value); this.onData(this.writer.cdata(node, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1); return this; } // Creates a comment node // `value` comment text comment(value) { var node; this.openCurrent(); node = new XMLComment(this, value); this.onData(this.writer.comment(node, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1); return this; } // Adds unescaped raw text // `value` text raw(value) { var node; this.openCurrent(); node = new XMLRaw(this, value); this.onData(this.writer.raw(node, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1); return this; } // Adds a processing instruction // `target` instruction target // `value` instruction value instruction(target, value) { var i, insTarget, insValue, len, node; this.openCurrent(); if (target != null) { target = getValue(target); } if (value != null) { value = getValue(value); } if (Array.isArray(target)) { // expand if array for (i = 0, len = target.length; i < len; i++) { insTarget = target[i]; this.instruction(insTarget); } } else if (isObject(target)) { // expand if object for (insTarget in target) { if (!hasProp.call(target, insTarget)) continue; insValue = target[insTarget]; this.instruction(insTarget, insValue); } } else { if (isFunction(value)) { value = value.apply(); } node = new XMLProcessingInstruction(this, target, value); this.onData(this.writer.processingInstruction(node, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1); } return this; } // Creates the xml declaration // `version` A version number string, e.g. 1.0 // `encoding` Encoding declaration, e.g. UTF-8 // `standalone` standalone document declaration: true or false declaration(version, encoding, standalone) { var node; this.openCurrent(); if (this.documentStarted) { throw new Error("declaration() must be the first node."); } node = new XMLDeclaration(this, version, encoding, standalone); this.onData(this.writer.declaration(node, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1); return this; } // Creates the document type declaration // `root` the name of the root node // `pubID` the public identifier of the external subset // `sysID` the system identifier of the external subset doctype(root, pubID, sysID) { this.openCurrent(); if (root == null) { throw new Error("Missing root node name."); } if (this.root) { throw new Error("dtd() must come before the root node."); } this.currentNode = new XMLDocType(this, pubID, sysID); this.currentNode.rootNodeName = root; this.currentNode.children = false; this.currentLevel++; this.openTags[this.currentLevel] = this.currentNode; return this; } // Creates an element type declaration // `name` element name // `value` element content (defaults to #PCDATA) dtdElement(name, value) { var node; this.openCurrent(); node = new XMLDTDElement(this, name, value); this.onData(this.writer.dtdElement(node, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1); return this; } // Creates an attribute declaration // `elementName` the name of the element containing this attribute // `attributeName` attribute name // `attributeType` type of the attribute (defaults to CDATA) // `defaultValueType` default value type (either #REQUIRED, #IMPLIED, #FIXED or // #DEFAULT) (defaults to #IMPLIED) // `defaultValue` default value of the attribute // (only used for #FIXED or #DEFAULT) attList(elementName, attributeName, attributeType, defaultValueType, defaultValue) { var node; this.openCurrent(); node = new XMLDTDAttList(this, elementName, attributeName, attributeType, defaultValueType, defaultValue); this.onData(this.writer.dtdAttList(node, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1); return this; } // Creates a general entity declaration // `name` the name of the entity // `value` internal entity value or an object with external entity details // `value.pubID` public identifier // `value.sysID` system identifier // `value.nData` notation declaration entity(name, value) { var node; this.openCurrent(); node = new XMLDTDEntity(this, false, name, value); this.onData(this.writer.dtdEntity(node, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1); return this; } // Creates a parameter entity declaration // `name` the name of the entity // `value` internal entity value or an object with external entity details // `value.pubID` public identifier // `value.sysID` system identifier pEntity(name, value) { var node; this.openCurrent(); node = new XMLDTDEntity(this, true, name, value); this.onData(this.writer.dtdEntity(node, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1); return this; } // Creates a NOTATION declaration // `name` the name of the notation // `value` an object with external entity details // `value.pubID` public identifier // `value.sysID` system identifier notation(name, value) { var node; this.openCurrent(); node = new XMLDTDNotation(this, name, value); this.onData(this.writer.dtdNotation(node, this.writerOptions, this.currentLevel + 1), this.currentLevel + 1); return this; } // Gets the parent node up() { if (this.currentLevel < 0) { throw new Error("The document node has no parent."); } if (this.currentNode) { if (this.currentNode.children) { this.closeNode(this.currentNode); } else { this.openNode(this.currentNode); } this.currentNode = null; } else { this.closeNode(this.openTags[this.currentLevel]); } delete this.openTags[this.currentLevel]; this.currentLevel--; return this; } // Ends the document end() { while (this.currentLevel >= 0) { this.up(); } return this.onEnd(); } // Opens the current parent node openCurrent() { if (this.currentNode) { this.currentNode.children = true; return this.openNode(this.currentNode); } } // Writes the opening tag of the current node or the entire node if it has // no child nodes openNode(node) { var att, chunk, name, ref; if (!node.isOpen) { if (!this.root && this.currentLevel === 0 && node.type === NodeType.Element) { this.root = node; } chunk = ''; if (node.type === NodeType.Element) { this.writerOptions.state = WriterState.OpenTag; chunk = this.writer.indent(node, this.writerOptions, this.currentLevel) + '<' + node.name; ref = node.attribs; for (name in ref) { if (!hasProp.call(ref, name)) continue; att = ref[name]; chunk += this.writer.attribute(att, this.writerOptions, this.currentLevel); } chunk += (node.children ? '>' : '/>') + this.writer.endline(node, this.writerOptions, this.currentLevel); this.writerOptions.state = WriterState.InsideTag; // if node.type is NodeType.DocType } else { this.writerOptions.state = WriterState.OpenTag; chunk = this.writer.indent(node, this.writerOptions, this.currentLevel) + ''; } chunk += this.writer.endline(node, this.writerOptions, this.currentLevel); } this.onData(chunk, this.currentLevel); return node.isOpen = true; } } // Writes the closing tag of the current node closeNode(node) { var chunk; if (!node.isClosed) { chunk = ''; this.writerOptions.state = WriterState.CloseTag; if (node.type === NodeType.Element) { chunk = this.writer.indent(node, this.writerOptions, this.currentLevel) + '' + this.writer.endline(node, this.writerOptions, this.currentLevel); // if node.type is NodeType.DocType } else { chunk = this.writer.indent(node, this.writerOptions, this.currentLevel) + ']>' + this.writer.endline(node, this.writerOptions, this.currentLevel); } this.writerOptions.state = WriterState.None; this.onData(chunk, this.currentLevel); return node.isClosed = true; } } // Called when a new chunk of XML is output // `chunk` a string containing the XML chunk // `level` current indentation level onData(chunk, level) { this.documentStarted = true; return this.onDataCallback(chunk, level + 1); } // Called when the XML document is completed onEnd() { this.documentCompleted = true; return this.onEndCallback(); } // Returns debug string debugInfo(name) { if (name == null) { return ""; } else { return "node: <" + name + ">"; } } // Node aliases ele() { return this.element(...arguments); } nod(name, attributes, text) { return this.node(name, attributes, text); } txt(value) { return this.text(value); } dat(value) { return this.cdata(value); } com(value) { return this.comment(value); } ins(target, value) { return this.instruction(target, value); } dec(version, encoding, standalone) { return this.declaration(version, encoding, standalone); } dtd(root, pubID, sysID) { return this.doctype(root, pubID, sysID); } e(name, attributes, text) { return this.element(name, attributes, text); } n(name, attributes, text) { return this.node(name, attributes, text); } t(value) { return this.text(value); } d(value) { return this.cdata(value); } c(value) { return this.comment(value); } r(value) { return this.raw(value); } i(target, value) { return this.instruction(target, value); } // Attribute aliases att() { if (this.currentNode && this.currentNode.type === NodeType.DocType) { return this.attList(...arguments); } else { return this.attribute(...arguments); } } a() { if (this.currentNode && this.currentNode.type === NodeType.DocType) { return this.attList(...arguments); } else { return this.attribute(...arguments); } } // DTD aliases // att() and ele() are defined above ent(name, value) { return this.entity(name, value); } pent(name, value) { return this.pEntity(name, value); } not(name, value) { return this.notation(name, value); } }; }).call(this);