Buckets:
| var helper = require('./options-helper'); | |
| var isArray = require('./array-helper').isArray; | |
| var currentElement, currentElementName; | |
| function validateOptions(userOptions) { | |
| var options = helper.copyOptions(userOptions); | |
| helper.ensureFlagExists('ignoreDeclaration', options); | |
| helper.ensureFlagExists('ignoreInstruction', options); | |
| helper.ensureFlagExists('ignoreAttributes', options); | |
| helper.ensureFlagExists('ignoreText', options); | |
| helper.ensureFlagExists('ignoreComment', options); | |
| helper.ensureFlagExists('ignoreCdata', options); | |
| helper.ensureFlagExists('ignoreDoctype', options); | |
| helper.ensureFlagExists('compact', options); | |
| helper.ensureFlagExists('indentText', options); | |
| helper.ensureFlagExists('indentCdata', options); | |
| helper.ensureFlagExists('indentAttributes', options); | |
| helper.ensureFlagExists('indentInstruction', options); | |
| helper.ensureFlagExists('fullTagEmptyElement', options); | |
| helper.ensureFlagExists('noQuotesForNativeAttributes', options); | |
| helper.ensureSpacesExists(options); | |
| if (typeof options.spaces === 'number') { | |
| options.spaces = Array(options.spaces + 1).join(' '); | |
| } | |
| helper.ensureKeyExists('declaration', options); | |
| helper.ensureKeyExists('instruction', options); | |
| helper.ensureKeyExists('attributes', options); | |
| helper.ensureKeyExists('text', options); | |
| helper.ensureKeyExists('comment', options); | |
| helper.ensureKeyExists('cdata', options); | |
| helper.ensureKeyExists('doctype', options); | |
| helper.ensureKeyExists('type', options); | |
| helper.ensureKeyExists('name', options); | |
| helper.ensureKeyExists('elements', options); | |
| helper.checkFnExists('doctype', options); | |
| helper.checkFnExists('instruction', options); | |
| helper.checkFnExists('cdata', options); | |
| helper.checkFnExists('comment', options); | |
| helper.checkFnExists('text', options); | |
| helper.checkFnExists('instructionName', options); | |
| helper.checkFnExists('elementName', options); | |
| helper.checkFnExists('attributeName', options); | |
| helper.checkFnExists('attributeValue', options); | |
| helper.checkFnExists('attributes', options); | |
| helper.checkFnExists('fullTagEmptyElement', options); | |
| return options; | |
| } | |
| function writeIndentation(options, depth, firstLine) { | |
| return (!firstLine && options.spaces ? '\n' : '') + Array(depth + 1).join(options.spaces); | |
| } | |
| function writeAttributes(attributes, options, depth) { | |
| if (options.ignoreAttributes) { | |
| return ''; | |
| } | |
| if ('attributesFn' in options) { | |
| attributes = options.attributesFn(attributes, currentElementName, currentElement); | |
| } | |
| var key, attr, attrName, quote, result = []; | |
| for (key in attributes) { | |
| if (attributes.hasOwnProperty(key) && attributes[key] !== null && attributes[key] !== undefined) { | |
| quote = options.noQuotesForNativeAttributes && typeof attributes[key] !== 'string' ? '' : '"'; | |
| attr = '' + attributes[key]; // ensure number and boolean are converted to String | |
| attr = attr.replace(/"/g, '"'); | |
| attrName = 'attributeNameFn' in options ? options.attributeNameFn(key, attr, currentElementName, currentElement) : key; | |
| result.push((options.spaces && options.indentAttributes? writeIndentation(options, depth+1, false) : ' ')); | |
| result.push(attrName + '=' + quote + ('attributeValueFn' in options ? options.attributeValueFn(attr, key, currentElementName, currentElement) : attr) + quote); | |
| } | |
| } | |
| if (attributes && Object.keys(attributes).length && options.spaces && options.indentAttributes) { | |
| result.push(writeIndentation(options, depth, false)); | |
| } | |
| return result.join(''); | |
| } | |
| function writeDeclaration(declaration, options, depth) { | |
| currentElement = declaration; | |
| currentElementName = 'xml'; | |
| return options.ignoreDeclaration ? '' : '<?' + 'xml' + writeAttributes(declaration[options.attributesKey], options, depth) + '?>'; | |
| } | |
| function writeInstruction(instruction, options, depth) { | |
| if (options.ignoreInstruction) { | |
| return ''; | |
| } | |
| var key; | |
| for (key in instruction) { | |
| if (instruction.hasOwnProperty(key)) { | |
| break; | |
| } | |
| } | |
| var instructionName = 'instructionNameFn' in options ? options.instructionNameFn(key, instruction[key], currentElementName, currentElement) : key; | |
| if (typeof instruction[key] === 'object') { | |
| currentElement = instruction; | |
| currentElementName = instructionName; | |
| return '<?' + instructionName + writeAttributes(instruction[key][options.attributesKey], options, depth) + '?>'; | |
| } else { | |
| var instructionValue = instruction[key] ? instruction[key] : ''; | |
| if ('instructionFn' in options) instructionValue = options.instructionFn(instructionValue, key, currentElementName, currentElement); | |
| return '<?' + instructionName + (instructionValue ? ' ' + instructionValue : '') + '?>'; | |
| } | |
| } | |
| function writeComment(comment, options) { | |
| return options.ignoreComment ? '' : '<!--' + ('commentFn' in options ? options.commentFn(comment, currentElementName, currentElement) : comment) + '-->'; | |
| } | |
| function writeCdata(cdata, options) { | |
| return options.ignoreCdata ? '' : '<![CDATA[' + ('cdataFn' in options ? options.cdataFn(cdata, currentElementName, currentElement) : cdata.replace(']]>', ']]]]><![CDATA[>')) + ']]>'; | |
| } | |
| function writeDoctype(doctype, options) { | |
| return options.ignoreDoctype ? '' : '<!DOCTYPE ' + ('doctypeFn' in options ? options.doctypeFn(doctype, currentElementName, currentElement) : doctype) + '>'; | |
| } | |
| function writeText(text, options) { | |
| if (options.ignoreText) return ''; | |
| text = '' + text; // ensure Number and Boolean are converted to String | |
| text = text.replace(/&/g, '&'); // desanitize to avoid double sanitization | |
| text = text.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>'); | |
| return 'textFn' in options ? options.textFn(text, currentElementName, currentElement) : text; | |
| } | |
| function hasContent(element, options) { | |
| var i; | |
| if (element.elements && element.elements.length) { | |
| for (i = 0; i < element.elements.length; ++i) { | |
| switch (element.elements[i][options.typeKey]) { | |
| case 'text': | |
| if (options.indentText) { | |
| return true; | |
| } | |
| break; // skip to next key | |
| case 'cdata': | |
| if (options.indentCdata) { | |
| return true; | |
| } | |
| break; // skip to next key | |
| case 'instruction': | |
| if (options.indentInstruction) { | |
| return true; | |
| } | |
| break; // skip to next key | |
| case 'doctype': | |
| case 'comment': | |
| case 'element': | |
| return true; | |
| default: | |
| return true; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| function writeElement(element, options, depth) { | |
| currentElement = element; | |
| currentElementName = element.name; | |
| var xml = [], elementName = 'elementNameFn' in options ? options.elementNameFn(element.name, element) : element.name; | |
| xml.push('<' + elementName); | |
| if (element[options.attributesKey]) { | |
| xml.push(writeAttributes(element[options.attributesKey], options, depth)); | |
| } | |
| var withClosingTag = element[options.elementsKey] && element[options.elementsKey].length || element[options.attributesKey] && element[options.attributesKey]['xml:space'] === 'preserve'; | |
| if (!withClosingTag) { | |
| if ('fullTagEmptyElementFn' in options) { | |
| withClosingTag = options.fullTagEmptyElementFn(element.name, element); | |
| } else { | |
| withClosingTag = options.fullTagEmptyElement; | |
| } | |
| } | |
| if (withClosingTag) { | |
| xml.push('>'); | |
| if (element[options.elementsKey] && element[options.elementsKey].length) { | |
| xml.push(writeElements(element[options.elementsKey], options, depth + 1)); | |
| currentElement = element; | |
| currentElementName = element.name; | |
| } | |
| xml.push(options.spaces && hasContent(element, options) ? '\n' + Array(depth + 1).join(options.spaces) : ''); | |
| xml.push('</' + elementName + '>'); | |
| } else { | |
| xml.push('/>'); | |
| } | |
| return xml.join(''); | |
| } | |
| function writeElements(elements, options, depth, firstLine) { | |
| return elements.reduce(function (xml, element) { | |
| var indent = writeIndentation(options, depth, firstLine && !xml); | |
| switch (element.type) { | |
| case 'element': return xml + indent + writeElement(element, options, depth); | |
| case 'comment': return xml + indent + writeComment(element[options.commentKey], options); | |
| case 'doctype': return xml + indent + writeDoctype(element[options.doctypeKey], options); | |
| case 'cdata': return xml + (options.indentCdata ? indent : '') + writeCdata(element[options.cdataKey], options); | |
| case 'text': return xml + (options.indentText ? indent : '') + writeText(element[options.textKey], options); | |
| case 'instruction': | |
| var instruction = {}; | |
| instruction[element[options.nameKey]] = element[options.attributesKey] ? element : element[options.instructionKey]; | |
| return xml + (options.indentInstruction ? indent : '') + writeInstruction(instruction, options, depth); | |
| } | |
| }, ''); | |
| } | |
| function hasContentCompact(element, options, anyContent) { | |
| var key; | |
| for (key in element) { | |
| if (element.hasOwnProperty(key)) { | |
| switch (key) { | |
| case options.parentKey: | |
| case options.attributesKey: | |
| break; // skip to next key | |
| case options.textKey: | |
| if (options.indentText || anyContent) { | |
| return true; | |
| } | |
| break; // skip to next key | |
| case options.cdataKey: | |
| if (options.indentCdata || anyContent) { | |
| return true; | |
| } | |
| break; // skip to next key | |
| case options.instructionKey: | |
| if (options.indentInstruction || anyContent) { | |
| return true; | |
| } | |
| break; // skip to next key | |
| case options.doctypeKey: | |
| case options.commentKey: | |
| return true; | |
| default: | |
| return true; | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| function writeElementCompact(element, name, options, depth, indent) { | |
| currentElement = element; | |
| currentElementName = name; | |
| var elementName = 'elementNameFn' in options ? options.elementNameFn(name, element) : name; | |
| if (typeof element === 'undefined' || element === null || element === '') { | |
| return 'fullTagEmptyElementFn' in options && options.fullTagEmptyElementFn(name, element) || options.fullTagEmptyElement ? '<' + elementName + '></' + elementName + '>' : '<' + elementName + '/>'; | |
| } | |
| var xml = []; | |
| if (name) { | |
| xml.push('<' + elementName); | |
| if (typeof element !== 'object') { | |
| xml.push('>' + writeText(element,options) + '</' + elementName + '>'); | |
| return xml.join(''); | |
| } | |
| if (element[options.attributesKey]) { | |
| xml.push(writeAttributes(element[options.attributesKey], options, depth)); | |
| } | |
| var withClosingTag = hasContentCompact(element, options, true) || element[options.attributesKey] && element[options.attributesKey]['xml:space'] === 'preserve'; | |
| if (!withClosingTag) { | |
| if ('fullTagEmptyElementFn' in options) { | |
| withClosingTag = options.fullTagEmptyElementFn(name, element); | |
| } else { | |
| withClosingTag = options.fullTagEmptyElement; | |
| } | |
| } | |
| if (withClosingTag) { | |
| xml.push('>'); | |
| } else { | |
| xml.push('/>'); | |
| return xml.join(''); | |
| } | |
| } | |
| xml.push(writeElementsCompact(element, options, depth + 1, false)); | |
| currentElement = element; | |
| currentElementName = name; | |
| if (name) { | |
| xml.push((indent ? writeIndentation(options, depth, false) : '') + '</' + elementName + '>'); | |
| } | |
| return xml.join(''); | |
| } | |
| function writeElementsCompact(element, options, depth, firstLine) { | |
| var i, key, nodes, xml = []; | |
| for (key in element) { | |
| if (element.hasOwnProperty(key)) { | |
| nodes = isArray(element[key]) ? element[key] : [element[key]]; | |
| for (i = 0; i < nodes.length; ++i) { | |
| switch (key) { | |
| case options.declarationKey: xml.push(writeDeclaration(nodes[i], options, depth)); break; | |
| case options.instructionKey: xml.push((options.indentInstruction ? writeIndentation(options, depth, firstLine) : '') + writeInstruction(nodes[i], options, depth)); break; | |
| case options.attributesKey: case options.parentKey: break; // skip | |
| case options.textKey: xml.push((options.indentText ? writeIndentation(options, depth, firstLine) : '') + writeText(nodes[i], options)); break; | |
| case options.cdataKey: xml.push((options.indentCdata ? writeIndentation(options, depth, firstLine) : '') + writeCdata(nodes[i], options)); break; | |
| case options.doctypeKey: xml.push(writeIndentation(options, depth, firstLine) + writeDoctype(nodes[i], options)); break; | |
| case options.commentKey: xml.push(writeIndentation(options, depth, firstLine) + writeComment(nodes[i], options)); break; | |
| default: xml.push(writeIndentation(options, depth, firstLine) + writeElementCompact(nodes[i], key, options, depth, hasContentCompact(nodes[i], options))); | |
| } | |
| firstLine = firstLine && !xml.length; | |
| } | |
| } | |
| } | |
| return xml.join(''); | |
| } | |
| module.exports = function (js, options) { | |
| options = validateOptions(options); | |
| var xml = []; | |
| currentElement = js; | |
| currentElementName = '_root_'; | |
| if (options.compact) { | |
| xml.push(writeElementsCompact(js, options, 0, true)); | |
| } else { | |
| if (js[options.declarationKey]) { | |
| xml.push(writeDeclaration(js[options.declarationKey], options, 0)); | |
| } | |
| if (js[options.elementsKey] && js[options.elementsKey].length) { | |
| xml.push(writeElements(js[options.elementsKey], options, 0, !xml.length)); | |
| } | |
| } | |
| return xml.join(''); | |
| }; | |
Xet Storage Details
- Size:
- 13.3 kB
- Xet hash:
- 59ad4c17c9d64fb05dbbc24117bcbf4cd752a900a33becf9f2a7c1a25dcc3f38
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.