import camelize from './camelize'; let debug = require('./debug')('dav:parser'); let DOMParser = require('xmldom').DOMParser; export function multistatus(string) { let parser = new DOMParser(); let doc = parser.parseFromString(string, 'text/xml'); let result = traverse.multistatus(child(doc, 'multistatus')); debug(`input:\n${string}\noutput:\n${JSON.stringify(result)}\n`); return result; } let traverse = { // { response: [x, y, z] } multistatus: node => complex(node, { response: true }), // { propstat: [x, y, z] } response: node => complex(node, { propstat: true, href: false }), // { prop: x } propstat: node => complex(node, { prop: false }), // { // resourcetype: x // supportedCalendarComponentSet: y, // supportedReportSet: z // } prop: node => { return complex(node, { resourcetype: false, supportedCalendarComponentSet: false, supportedReportSet: false, currentUserPrincipal: false, groups: false, invite: false }); }, resourcetype: node => { return childNodes(node).map(childNode => childNode.localName); }, groups: node => complex(node, { group: true }, 'group'), group: node => { return childNodes(node).map(childNode => childNode.nodeValue); }, invite: node => complex(node, { user: true }, 'user'), user: node => complex(node, { href: false, access:false }), access: node => complex(node, {}), //access: node => { // return childNodes(node).map(childNode => childNode.localName); //}, // [x, y, z] supportedCalendarComponentSet: node => complex(node, { comp: true }, 'comp'), // [x, y, z] supportedReportSet: node => { return complex(node, { supportedReport: true }, 'supportedReport'); }, comp: node => node.getAttribute('name'), // x supportedReport: node => complex(node, { report: false }, 'report'), report: node => { return childNodes(node).map(childNode => childNode.localName); }, href: node => { return decodeURIComponent(childNodes(node)[0].nodeValue); }, currentUserPrincipal: node => { return complex(node, {href: false}, 'href'); } }; function complex(node, childspec, collapse) { let result = {}; for (let key in childspec) { if (childspec[key]) { // Create array since we're expecting multiple. result[key] = []; } } childNodes(node).forEach( childNode => traverseChild(node, childNode, childspec, result) ); return maybeCollapse(result, childspec, collapse); } /** * Parse child childNode of node with childspec and write outcome to result. */ function traverseChild(node, childNode, childspec, result) { if (childNode.nodeType === 3 && /^\s+$/.test(childNode.nodeValue)) { // Whitespace... nothing to do. return; } let localName = camelize(childNode.localName, '-'); if (!(localName in childspec)) { debug('Unexpected node of type ' + localName + ' encountered while ' + 'parsing ' + node.localName + ' node!'); let value = childNode.textContent; if (localName in result) { if (!Array.isArray(result[camelCase])) { // Since we've already encountered this node type and we haven't yet // made an array for it, make an array now. result[localName] = [result[localName]]; } result[localName].push(value); return; } // First time we're encountering this node. result[localName] = value; return; } let traversal = traverse[localName](childNode); if (childspec[localName]) { // Expect multiple. result[localName].push(traversal); } else { // Expect single. result[localName] = traversal; } } function maybeCollapse(result, childspec, collapse) { if (!collapse) { return result; } if (!childspec[collapse]) { return result[collapse]; } // Collapse array. return result[collapse].reduce((a, b) => a.concat(b), []); } function childNodes(node) { let result = node.childNodes; if (!Array.isArray(result)) { result = Array.prototype.slice.call(result); } return result; } function children(node, localName) { return childNodes(node).filter(childNode => childNode.localName === localName); } function child(node, localName) { return children(node, localName)[0]; }