79 lines
2.5 KiB
JavaScript
79 lines
2.5 KiB
JavaScript
// Parse a WebDAV multistatus (PROPFIND) response into flat entries.
|
|
// Pure ES5, no deps. Functions are TOP-LEVEL so the QML namespace import
|
|
// exposes them directly (see Markdown.js): import "PropfindParser.js" as Prop
|
|
// ... Prop.parse(xml, baseUrl) ...
|
|
// The module.exports guard at the bottom is for node tests only.
|
|
|
|
function decodePath(u) {
|
|
try {
|
|
return decodeURIComponent(u);
|
|
} catch (e) {
|
|
return u;
|
|
}
|
|
}
|
|
|
|
function urlPath(u) {
|
|
var m = /^[a-z]+:\/\/[^/]*(\/.*)$/i.exec(u);
|
|
return m ? m[1] : u;
|
|
}
|
|
|
|
// entries: [{name, path, isDir, size, mtime}] sorted dirs-first, alpha.
|
|
// path is relative to baseUrl, URL-decoded; dirs end with "/".
|
|
function parse(xml, baseUrl) {
|
|
var entries = [];
|
|
if (!xml) return entries;
|
|
|
|
var basePath = decodePath(urlPath(String(baseUrl || "")));
|
|
if (basePath.charAt(basePath.length - 1) === "/") {
|
|
basePath = basePath.slice(0, -1);
|
|
}
|
|
|
|
var re = /<(?:[A-Za-z0-9_]+:)?response(?:\s[^>]*)?>([\s\S]*?)<\/(?:[A-Za-z0-9_]+:)?response>/gi;
|
|
var blk;
|
|
while ((blk = re.exec(xml)) !== null) {
|
|
var body = blk[1];
|
|
|
|
var hrefMatch = /(?:[A-Za-z0-9_]+:)?href>([\s\S]*?)<\//.exec(body);
|
|
if (!hrefMatch) continue;
|
|
var href = hrefMatch[1].trim();
|
|
if (!href) continue;
|
|
|
|
var decoded = decodePath(href);
|
|
var rel = decoded;
|
|
if (basePath && decoded.indexOf(basePath) === 0) {
|
|
rel = decoded.substring(basePath.length);
|
|
}
|
|
if (rel.charAt(0) === "/") rel = rel.substring(1);
|
|
if (rel === "" || rel === "/") continue;
|
|
|
|
var isDir = rel.charAt(rel.length - 1) === "/";
|
|
var segs = rel.split("/");
|
|
var name = isDir ? segs[segs.length - 2] : segs[segs.length - 1];
|
|
if (!name) continue;
|
|
|
|
var mtimeMatch = /(?:[A-Za-z0-9_]+:)?getlastmodified>([\s\S]*?)<\//.exec(body);
|
|
var sizeMatch = /(?:[A-Za-z0-9_]+:)?getcontentlength>([\s\S]*?)<\//.exec(body);
|
|
|
|
entries.push({
|
|
name: name,
|
|
path: rel,
|
|
isDir: isDir,
|
|
size: sizeMatch ? parseInt(sizeMatch[1].trim(), 10) : 0,
|
|
mtime: mtimeMatch ? mtimeMatch[1].trim() : ""
|
|
});
|
|
}
|
|
|
|
entries.sort(function (a, b) {
|
|
if (a.isDir !== b.isDir) return a.isDir ? -1 : 1;
|
|
var an = a.name.toLowerCase();
|
|
var bn = b.name.toLowerCase();
|
|
return an < bn ? -1 : (an > bn ? 1 : 0);
|
|
});
|
|
|
|
return entries;
|
|
}
|
|
|
|
if (typeof module !== "undefined" && module.exports) {
|
|
module.exports = { parse: parse };
|
|
}
|