[d24f17c] | 1 | 'use strict';
|
---|
| 2 |
|
---|
| 3 | var repeat = require('repeat-string');
|
---|
| 4 |
|
---|
| 5 | var splitOnTags = function splitOnTags(str) {
|
---|
| 6 | return str.split(/(<\/?[^>]+>)/g).filter(function (line) {
|
---|
| 7 | return line.trim() !== '';
|
---|
| 8 | });
|
---|
| 9 | };
|
---|
| 10 | var isTag = function isTag(str) {
|
---|
| 11 | return (/<[^>!]+>/.test(str)
|
---|
| 12 | );
|
---|
| 13 | };
|
---|
| 14 | var isClosingTag = function isClosingTag(str) {
|
---|
| 15 | return (/<\/+[^>]+>/.test(str)
|
---|
| 16 | );
|
---|
| 17 | };
|
---|
| 18 | var isSelfClosingTag = function isSelfClosingTag(str) {
|
---|
| 19 | return (/<[^>]+\/>/.test(str)
|
---|
| 20 | );
|
---|
| 21 | };
|
---|
| 22 | var isOpeningTag = function isOpeningTag(str) {
|
---|
| 23 | return isTag(str) && !isClosingTag(str) && !isSelfClosingTag(str);
|
---|
| 24 | };
|
---|
| 25 |
|
---|
| 26 | module.exports = function (xml) {
|
---|
| 27 | var config = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
|
---|
| 28 | var indentor = config.indentor,
|
---|
| 29 | textNodesOnSameLine = config.textNodesOnSameLine;
|
---|
| 30 |
|
---|
| 31 | var depth = 0;
|
---|
| 32 | var indicesToRemove = [];
|
---|
| 33 | indentor = indentor || ' ';
|
---|
| 34 |
|
---|
| 35 | var rawResult = lexer(xml).map(function (element, i, arr) {
|
---|
| 36 | var value = element.value,
|
---|
| 37 | type = element.type;
|
---|
| 38 |
|
---|
| 39 | if (type === 'ClosingTag') {
|
---|
| 40 | depth--;
|
---|
| 41 | }
|
---|
| 42 |
|
---|
| 43 | var indentation = repeat(indentor, depth);
|
---|
| 44 | var line = indentation + value;
|
---|
| 45 |
|
---|
| 46 | if (type === 'OpeningTag') {
|
---|
| 47 | depth++;
|
---|
| 48 | }
|
---|
| 49 |
|
---|
| 50 | if (textNodesOnSameLine) {
|
---|
| 51 | // Lookbehind for [OpeningTag][Text][ClosingTag]
|
---|
| 52 | var oneBefore = arr[i - 1];
|
---|
| 53 | var twoBefore = arr[i - 2];
|
---|
| 54 |
|
---|
| 55 | if (type === "ClosingTag" && oneBefore.type === "Text" && twoBefore.type === "OpeningTag") {
|
---|
| 56 | // collapse into a single line
|
---|
| 57 | line = '' + indentation + twoBefore.value + oneBefore.value + value;
|
---|
| 58 | indicesToRemove.push(i - 2, i - 1);
|
---|
| 59 | }
|
---|
| 60 | }
|
---|
| 61 |
|
---|
| 62 | return line;
|
---|
| 63 | });
|
---|
| 64 |
|
---|
| 65 | indicesToRemove.forEach(function (idx) {
|
---|
| 66 | return rawResult[idx] = null;
|
---|
| 67 | });
|
---|
| 68 |
|
---|
| 69 | return rawResult.filter(function (val) {
|
---|
| 70 | return !!val;
|
---|
| 71 | }).join('\n');
|
---|
| 72 | };
|
---|
| 73 |
|
---|
| 74 | function lexer(xmlStr) {
|
---|
| 75 | var values = splitOnTags(xmlStr);
|
---|
| 76 | return values.map(function (value) {
|
---|
| 77 | return {
|
---|
| 78 | value: value,
|
---|
| 79 | type: getType(value)
|
---|
| 80 | };
|
---|
| 81 | });
|
---|
| 82 | }
|
---|
| 83 |
|
---|
| 84 | // Helpers
|
---|
| 85 |
|
---|
| 86 | function getType(str) {
|
---|
| 87 | if (isClosingTag(str)) {
|
---|
| 88 | return 'ClosingTag';
|
---|
| 89 | }
|
---|
| 90 |
|
---|
| 91 | if (isOpeningTag(str)) {
|
---|
| 92 | return 'OpeningTag';
|
---|
| 93 | }
|
---|
| 94 |
|
---|
| 95 | if (isSelfClosingTag(str)) {
|
---|
| 96 | return 'SelfClosingTag';
|
---|
| 97 | }
|
---|
| 98 |
|
---|
| 99 | return 'Text';
|
---|
| 100 | } |
---|