1 | var EventEmitter = require('events').EventEmitter
|
2 | , sax = require('sax')
|
3 | , helper = require('./helper')
|
4 | , flow
|
5 | ;
|
6 |
|
7 |
|
8 |
|
9 |
|
10 | flow = 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 |
|
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 |
|
58 | if(stack.length === 0) { return; }
|
59 |
|
60 |
|
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 |
|
73 | emitter.emit(
|
74 | 'tag:' + tagName,
|
75 | options.simplifyNodes ? helper.simplifyNode(topNode) : topNode
|
76 | );
|
77 |
|
78 |
|
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 |
|
104 | flow.ALWAYS = 1;
|
105 | flow.SOMETIMES = 0;
|
106 | flow.NEVER = -1;
|
107 |
|
108 | flow.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 |
|
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 |
|
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 |
|