You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
165 lines
2.3 KiB
JavaScript
165 lines
2.3 KiB
JavaScript
|
2 years ago
|
|
||
|
|
/**
|
||
|
|
* Module dependencies.
|
||
|
|
*/
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Expose `parse`.
|
||
|
|
*/
|
||
|
|
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Parse the given string of `xml`.
|
||
|
|
*
|
||
|
|
* @param {String} xml
|
||
|
|
* @return {Object}
|
||
|
|
* @api public
|
||
|
|
*/
|
||
|
|
|
||
|
|
function parse(xml) {
|
||
|
|
xml = xml.trim()
|
||
|
|
|
||
|
|
// strip comments
|
||
|
|
xml = xml.replace(/<!--[\s\S]*?-->/g, '')
|
||
|
|
|
||
|
|
return document()
|
||
|
|
|
||
|
|
/**
|
||
|
|
* XML document.
|
||
|
|
*/
|
||
|
|
|
||
|
|
function document() {
|
||
|
|
return {
|
||
|
|
declaration: declaration(),
|
||
|
|
root: tag()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Declaration.
|
||
|
|
*/
|
||
|
|
|
||
|
|
function declaration() {
|
||
|
|
const m = match(/^<\?xml\s*/)
|
||
|
|
if (!m) return
|
||
|
|
|
||
|
|
// tag
|
||
|
|
const node = {
|
||
|
|
attributes: {}
|
||
|
|
}
|
||
|
|
|
||
|
|
// attributes
|
||
|
|
while (!(eos() || is('?>'))) {
|
||
|
|
const attr = attribute()
|
||
|
|
if (!attr) return node
|
||
|
|
node.attributes[attr.name] = attr.value
|
||
|
|
}
|
||
|
|
|
||
|
|
match(/\?>\s*/)
|
||
|
|
|
||
|
|
return node
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Tag.
|
||
|
|
*/
|
||
|
|
|
||
|
|
function tag() {
|
||
|
|
const m = match(/^<([\w-:.]+)\s*/)
|
||
|
|
if (!m) return
|
||
|
|
|
||
|
|
// name
|
||
|
|
const node = {
|
||
|
|
name: m[1],
|
||
|
|
attributes: {},
|
||
|
|
children: []
|
||
|
|
}
|
||
|
|
|
||
|
|
// attributes
|
||
|
|
while (!(eos() || is('>') || is('?>') || is('/>'))) {
|
||
|
|
const attr = attribute()
|
||
|
|
if (!attr) return node
|
||
|
|
node.attributes[attr.name] = attr.value
|
||
|
|
}
|
||
|
|
|
||
|
|
// self closing tag
|
||
|
|
if (match(/^\s*\/>\s*/)) {
|
||
|
|
return node
|
||
|
|
}
|
||
|
|
|
||
|
|
match(/\??>\s*/)
|
||
|
|
|
||
|
|
// content
|
||
|
|
node.content = content()
|
||
|
|
|
||
|
|
// children
|
||
|
|
let child
|
||
|
|
while (child = tag()) {
|
||
|
|
node.children.push(child)
|
||
|
|
}
|
||
|
|
|
||
|
|
// closing
|
||
|
|
match(/^<\/[\w-:.]+>\s*/)
|
||
|
|
|
||
|
|
return node
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Text content.
|
||
|
|
*/
|
||
|
|
|
||
|
|
function content() {
|
||
|
|
const m = match(/^([^<]*)/)
|
||
|
|
if (m) return m[1]
|
||
|
|
return ''
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Attribute.
|
||
|
|
*/
|
||
|
|
|
||
|
|
function attribute() {
|
||
|
|
const m = match(/([\w:-]+)\s*=\s*("[^"]*"|'[^']*'|\w+)\s*/)
|
||
|
|
if (!m) return
|
||
|
|
return {name: m[1], value: strip(m[2])}
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Strip quotes from `val`.
|
||
|
|
*/
|
||
|
|
|
||
|
|
function strip(val) {
|
||
|
|
return val.replace(/^['"]|['"]$/g, '')
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Match `re` and advance the string.
|
||
|
|
*/
|
||
|
|
|
||
|
|
function match(re) {
|
||
|
|
const m = xml.match(re)
|
||
|
|
if (!m) return
|
||
|
|
xml = xml.slice(m[0].length)
|
||
|
|
return m
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* End-of-source.
|
||
|
|
*/
|
||
|
|
|
||
|
|
function eos() {
|
||
|
|
return xml.length == 0
|
||
|
|
}
|
||
|
|
|
||
|
|
/**
|
||
|
|
* Check for `prefix`.
|
||
|
|
*/
|
||
|
|
|
||
|
|
function is(prefix) {
|
||
|
|
return xml.indexOf(prefix) == 0
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
module.exports = parse
|