summaryrefslogtreecommitdiff
path: root/lib/xml/reader.js
blob: 0c2e7f5842c68bc193baa2e802179308bf2f148d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
var promises = require("../promises");
var sax = require("sax");
var _ = require("underscore");

var nodes = require("./nodes");
var Element = nodes.Element;

exports.readString = readString;

function readString(xmlString, namespaceMap) {
    namespaceMap = namespaceMap || {};
    
    var finished = false;
    var parser = sax.parser(true, {xmlns: true, position: false});
    
    var rootElement = {children: []};
    var currentElement = rootElement;
    var stack = [];
    
    var deferred = promises.defer();
    
    parser.onopentag = function(node) {
        var attributes = mapObject(node.attributes, function(attribute) {
            return attribute.value;
        }, mapName);
        
        var element = new Element(mapName(node), attributes);
        currentElement.children.push(element);
        stack.push(currentElement);
        currentElement = element;
    };
    
    function mapName(node) {
        if (node.uri) {
            var mappedPrefix = namespaceMap[node.uri];
            var prefix;
            if (mappedPrefix) {
                prefix = mappedPrefix + ":";
            } else {
                prefix = "{" + node.uri + "}";
            }
            return prefix + node.local;
        } else {
            return node.local;
        }
    }
    
    parser.onclosetag = function(node) {
        currentElement = stack.pop();
    };
    
    parser.ontext = function(text) {
        if (currentElement !== rootElement) {
            currentElement.children.push(nodes.text(text));
        }
    };
    
    parser.onend = function() {
        if (!finished) {
            finished = true;
            deferred.resolve(rootElement.children[0]);
        }
    };
    
    parser.onerror = function(error) {
        if (!finished) {
            finished = true;
            deferred.reject(error);
        }
    };
    
    parser.write(xmlString).close();
    
    return deferred.promise;
}

function mapObject(input, valueFunc, keyFunc) {
    return _.reduce(input, function(result, value, key) {
        var mappedKey = keyFunc(value, key, input);
        result[mappedKey] = valueFunc(value, key, input);
        return result;
    }, {});
}