mirror of https://github.com/nextcloud/contacts
167 lines
4.2 KiB
JavaScript
167 lines
4.2 KiB
JavaScript
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];
|
|
}
|