summaryrefslogtreecommitdiff
path: root/lib/xmlreader.js
blob: e5ed672cef7ae7d7b13f4ecf236e8174882958af (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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
var promises = require("./promises");
var sax = require("sax");
var _ = require("underscore");

exports.read = read;
exports.Element = Element;

function read(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({
                type: "text",
                value: text
            });
        }
    };
    
    parser.onend = function() {
        if (!finished) {
            finished = true;
            deferred.resolve({
                root: 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.call(null, value, key, input);
        result[mappedKey] = valueFunc.call(null, value, key, input);
        return result;
    }, {});
}

function Element(name, attributes, children) {
    this.type = "element";
    this.name = name;
    this.attributes = attributes || {};
    this.children = children || [];
}

Element.prototype.first = function(name) {
    return _.find(this.children, function(child) {
        return child.name == name;
    });
}

Element.prototype.getElementsByTagName = function(name) {
    var elements = _.filter(this.children, function(child) {
        return child.name == name;
    });
    return toElementList(elements);
};

var elementListPrototype = {
    getElementsByTagName: function(name) {
        return toElementList(_.flatten(this.map(function(element) {
            return element.getElementsByTagName(name);
        }, true)));
    }
};

function toElementList(array) {
    return _.extend(array, elementListPrototype);
}