UNPKG

4.42 kBJavaScriptView Raw
1var EventEmitter = require('events').EventEmitter
2 , sax = require('sax')
3 , helper = require('./helper')
4 , flow
5;
6
7/**
8 * @param ReadSteam inStream the stream to be parsed
9 */
10flow = module.exports = function(inStream, options) {
11 var emitter = new EventEmitter()
12 , saxOptions = {}
13 , saxStream = null
14 , stack = []
15 , topNode = null
16 ;
17
18 options = options || {};
19 options.preserveMarkup = options.preserveMarkup || flow.SOMETIMES;
20 options.simplifyNodes = options.hasOwnProperty('simplifyNodes') ? options.simplifyNodes : true;
21
22 saxOptions.lowercase = options.hasOwnProperty('lowercase') ? options.lowercase : true;
23 saxOptions.trim = options.hasOwnProperty('trim') ? options.trim : true;
24 saxOptions.normalize = options.hasOwnProperty('normalize') ? options.normalize : true;
25
26 saxStream = sax.createStream(options.strict || false, saxOptions);
27
28 saxStream.on('opentag', function(node) {
29 //Ignore nodes we don't care about.
30 if(stack.length === 0 && !emitter.listeners('tag:' + node.name).length) {
31 return;
32 }
33
34 topNode = {
35 $name: node.name,
36 $attrs: node.attributes,
37 $markup: []
38 };
39 stack.push(topNode);
40 });
41
42 saxStream.on('text', function(text) {
43 if(topNode) {
44 topNode.$markup.push(text);
45 }
46 });
47
48 saxStream.on('script', function(script) {
49 topNode.$script = script;
50 });
51
52 saxStream.on('closetag', function(tagName) {
53 var compressed
54 , newTop = null
55 ;
56
57 //If we're not going to send out a node, goodbye!
58 if(stack.length === 0) { return; }
59
60 //Objectify Markup if needed...
61 if(options.preserveMarkup <= flow.NEVER) {
62 topNode.$markup = helper.condenseArray(topNode.$markup);
63 topNode = helper.objectifyMarkup(topNode);
64 } else if(options.preserveMarkup === flow.SOMETIMES) {
65 compressed = helper.condenseArray(topNode.$markup);
66 if(helper.shouldObjectifyMarkup(compressed)) {
67 topNode.$markup = compressed;
68 topNode = helper.objectifyMarkup(topNode);
69 }
70 }
71
72 //emit the node
73 emitter.emit(
74 'tag:' + tagName,
75 options.simplifyNodes ? helper.simplifyNode(topNode) : topNode
76 );
77
78 //Pop stack, and add to parent node
79 stack.pop();
80 if(stack.length > 0) {
81 newTop = stack[stack.length-1];
82 newTop.$markup.push(topNode);
83 }
84 topNode = newTop;
85 });
86
87 saxStream.on('end', function(){
88 emitter.emit('end');
89 });
90
91 inStream.pipe(saxStream);
92
93 emitter.pause = function(){
94 inStream.pause();
95 };
96
97 emitter.resume = function() {
98 inStream.resume();
99 };
100
101 return emitter;
102};
103
104flow.ALWAYS = 1;
105flow.SOMETIMES = 0;
106flow.NEVER = -1;
107
108flow.toXml = function(node, name) {
109 var output = ''
110 , keys
111 ;
112
113 if(node.constructor === Array) {
114 node.forEach(function(subNode){
115 output += flow.toXml(subNode, name);
116 });
117 return output;
118 }
119
120 if(!name && node.$name) {name = node.$name;}
121
122 if(name) {
123 output = '<' + name;
124
125 if(node.$attrs && typeof node.$attrs === 'object') {
126 keys = Object.keys(node.$attrs);
127 keys.forEach(function(key){
128 output += ' ' + key + '=' + JSON.stringify('' + node.$attrs[key]);
129 });
130 }
131 output += '>';
132 }
133
134 if(node === null || node === undefined) {
135 //do nothing. Empty on purpose
136 } else if (typeof node === 'object') {
137 keys = Object.keys(node);
138 keys.forEach(function(key){
139 var value = node[key];
140 switch(key) {
141 case '$name':
142 case '$attrs':
143 //Do nothing. Already taken care of
144 break;
145
146 case '$text':
147 case '$markup':
148 output += flow.toXml(value);
149 break;
150
151 case '$script':
152 output += flow.toXml(value, 'script');
153 break;
154
155 default:
156 output += flow.toXml(value, key);
157 }
158 });
159 } else {
160 output += node;
161 }
162
163 if(name) { output += '</' + name + '>'; }
164 return output;
165};
166