Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

xml-stream

Package Overview
Dependencies
Maintainers
1
Versions
11
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

xml-stream - npm Package Compare versions

Comparing version 0.2.0 to 0.3.0

examples/http-stream.js

28

lib/finite-automata.js

@@ -10,3 +10,4 @@ module.exports = FiniteAutomata;

leave: {},
state: {}
state: {},
flag: {}
};

@@ -23,3 +24,3 @@ this._stack = [];

function run(args, type) {
function run(type, args) {
var cbs = this._callbacks[type];

@@ -39,3 +40,3 @@ for (var cb in this._state) if (this._state.hasOwnProperty(cb)) {

return this._deterministic;
}
};

@@ -52,10 +53,10 @@ FiniteAutomata.prototype.on = function(type, state, cb) {

return this;
}
};
FiniteAutomata.prototype.setState = function(state, args) {
this._state = state;
run.call(this, args, 'enter');
run.call(this, args, 'state');
run.call(this, 'enter', args);
run.call(this, 'state', args);
return this;
}
};

@@ -76,3 +77,3 @@ FiniteAutomata.prototype.nextState = function(symbol) {

return newState;
}
};

@@ -87,3 +88,3 @@ FiniteAutomata.prototype.go = function(symbol, args) {

this._stack[this._stackPtr] = undefined;
run.call(this, args, 'leave');
run.call(this, 'leave', args);
this._state = this._stack[--this._stackPtr];

@@ -100,9 +101,10 @@ return this;

this._state = next;
run.call(this, args, 'enter');
run.call(this, 'flag');
run.call(this, 'enter', args);
return this;
};
FiniteAutomata.prototype.run = function(args) {
run.call(this, args, 'state');
}
FiniteAutomata.prototype.run = function(state, args) {
run.call(this, state, args);
};

@@ -109,0 +111,0 @@ FiniteAutomata.prototype.transition = function(stateFrom, symbol, stateTo) {

@@ -6,3 +6,3 @@ var events = require('events')

// Test if object is empty (has no own properties).
// Tests if object is empty (has no own properties).
function isEmpty(obj) {

@@ -52,2 +52,4 @@ for (var key in obj) if (obj.hasOwnProperty(key)) {

this._emitData = false;
this._bufferLevel = 0;
this._collect = false;
parse.call(this);

@@ -68,10 +70,13 @@ }

// Supported events:
//
// * `data` on outgoing data chunk,
// * `end` when parsing has ended,
// * `startElement[: selector]` on opening tag for selector match,
// * `updateElement[: selector]` on finished node for selector match
// with its contents buffered,
// * `endElement[: selector]` on closing tag for selector match,
// * `text[: selector]` on tag text for selector match.
//
// When adding listeners for `startElement` and `text`, the callback
// can modify the provided node, before it is sent to the consumer.
// When adding listeners for `startElement`, `updateElement`, and `text` the
// callback can modify the provided node, before it is sent to the consumer.
//

@@ -82,43 +87,151 @@ // Selector syntax is CSS-like and currently supports:

// * `parent > child`
XmlStream.prototype.on = function(event, listener) {
XmlStream.super_.prototype.on.call(this, event, listener);
if (event === 'data') {
XmlStream.prototype.on = function(eventName, listener) {
XmlStream.super_.prototype.on.call(this, eventName, listener);
if (eventName === 'data') {
this._emitData = true;
}
var eventParts = event.match(/^(startElement|endElement|text):?(.*)/);
if (eventParts !== null) {
var event = parseEvent(eventName);
if (event !== null) {
// If we're dealing with a selector event,
// continue with selector-specific processing logic.
var evtType = eventParts[1];
var selector = eventParts[2];
var parts = selector.match(/[a-z0-9\:]+|>/ig);
selector = parts.join(' ');
event = evtType + ': ' + selector;
var finalState;
if (this._finalStates.hasOwnProperty(selector)) {
finalState = this._finalStates[selector];
var finalState = getFinalState.call(this, event.selector);
var self = this;
if (event.type === 'updateElement') {
this._fa.on('enter', finalState, function() {
self._bufferLevel++;
});
this._fa.on('leave', finalState, function(element, context, trace) {
self.emit(event.name, element, context, trace);
if (!--self._bufferLevel && self._emitData) {
emitElement.call(self, element, element.$name, true);
}
});
} else {
var n = parts.length;
var immediate = false;
this._startState[this._lastState] = true;
for (var i = 0; i < n; i++) {
if (parts[i] === '>') {
immediate = true;
var fn = function(element, context, trace) {
self.emit(event.name, element, context, trace);
};
this._fa.on(faModes[event.type], finalState, fn);
}
}
};
//
XmlStream.prototype.collect = function(selector) {
selector = normalizeSelector(selector);
var finalState = getFinalState.call(this, selector);
var self = this;
this._fa.on('flag', finalState, function() {
self._collect = true;
});
};
// Normalizes the selector and returns the new version and its parts.
function normalizeSelector(selector) {
var parts = selector.match(/[a-z0-9\:]+|>/ig);
selector = parts.join(' ');
return {
normalized: parts.join(' '),
parts: parts
};
}
// Parses the selector event string and returns event information.
function parseEvent(event) {
var eventParts = event.match(/^((?:start|end|update)Element|text):?(.*)/);
if (eventParts === null) {
return null;
}
var eventType = eventParts[1];
var selector = normalizeSelector(eventParts[2]);
return {
selector: selector,
type: eventType,
name: eventType + ': ' + selector.normalized
};
}
// Compiles a given selector object to a finite automata
// and returns its last state.
function getFinalState(selector) {
if (this._finalStates.hasOwnProperty(selector.normalized)) {
finalState = this._finalStates[selector.normalized];
} else {
var n = selector.parts.length;
var immediate = false;
this._startState[this._lastState] = true;
for (var i = 0; i < n; i++) {
var part = selector.parts[i];
if (part === '>') {
immediate = true;
} else {
if (!immediate) {
this._fa.transition(this._lastState, '', this._lastState);
}
this._fa.transition(this._lastState, part, ++this._lastState);
immediate = false;
}
}
finalState = this._lastState++;
this._finalStates[selector.normalized] = finalState;
}
return finalState;
}
// Emits XML for element opening tag.
function emitStart(name, attrs) {
this.emit('data', '<' + name);
for (var attr in attrs) if (attrs.hasOwnProperty(attr)) {
this.emit('data', ' ' + attr + '="' + escape(attrs[attr]) + '"');
}
this.emit('data', '>');
}
// Emits XML for element closing tag.
function emitEnd(name) {
this.emit('data', '</' + name + '>');
}
// Emits XML for element text.
function emitText(text) {
this.emit('data', escape(text));
}
// Emits a single element and its descendants, or an array of elements.
function emitElement(element, name, onLeave) {
if (Array.isArray(element)) {
var i;
for (i = 0; i < element.length - 1; i++) {
emitOneElement.call(this, element[i], name);
}
emitOneElement.call(this, element[i], name, onLeave);
} else {
emitOneElement.call(this, element, name, onLeave);
}
}
// Recursively emits a given element and its descendants.
function emitOneElement(element, name, onLeave) {
if (typeof element === 'object') {
emitStart.call(this, name, element.$);
var hasText = false;
for (var child in element) {
if (element.hasOwnProperty(child) && child !== '$' && child != '$name') {
if (child === '$text') {
hasText = true;
} else {
if (!immediate) {
this._fa.transition(this._lastState, '', this._lastState);
}
this._fa.transition(this._lastState, parts[i], ++this._lastState);
immediate = false;
emitElement.call(this, element[child], child);
}
}
finalState = this._lastState++;
this._finalStates[selector] = finalState;
}
var self = this;
this._fa.on(faModes[evtType], finalState, function(item, context, trace) {
self.emit(event, item, context, trace);
});
if (hasText) {
emitText.call(this, element.$text);
}
} else {
emitStart.call(this, name, element.$);
emitText.call(this, element);
}
};
if(!onLeave) {
emitEnd.call(this, name);
}
}

@@ -133,8 +246,6 @@ // Starts parsing the source stream and emitting various events.

var curr = {
node: {
element: {},
text: '',
name: 'root'
},
element: {},
collect: this._collect,
fullText: '',
space: 0,
path: '',

@@ -151,28 +262,36 @@ context: {}

stack.push(curr);
trace[curr.path] = curr.node.element;
trace[curr.path] = curr.element;
var context = Object.create(curr.context);
var element = {$: attr};
curr.node.element[name] = element;
var element = {
$: attr,
$name: name,
$text: ''
};
var parent = curr.element;
curr = {
node: {
element: element,
text: '',
name: name
},
element: element,
collect: false,
fullText: '',
path: curr.path + '/' + name
space: 0,
path: curr.path + '/' + name,
context: context
};
context[name] = curr.node.element;
curr.context = context;
fa.enter(name, [curr.node, curr.context, trace]);
if (self._emitData) {
self.emit('data', '<' + curr.node.name);
var attrs = curr.node.element.$;
for (var attr in attrs) if (attrs.hasOwnProperty(attr)) {
self.emit('data', ' ' + attr + '="' + escape(attrs[attr]) + '"');
fa.enter(name, [element, context, trace]);
name = element.$name;
curr.collect = self._collect;
if (curr.collect) {
var container;
if (Object.hasOwnProperty.call(parent, name)) {
container = parent[name];
container.push(element);
} else {
container = [element];
parent[name] = container;
}
self.emit('data', '>');
} else {
parent[name] = element;
context[name] = element;
}
if (isEmpty(curr.node.element.$)) {
delete curr.node.element.$;
if (self._bufferLevel === 0 && self._emitData) {
emitStart.call(self, element.$name, element.$);
}

@@ -187,22 +306,37 @@ });

var prev = stack.pop();
var node = curr.node;
node.text = curr.fullText;
var val;
if (isEmpty(node.element)) {
val = node.text;
} else if (node.text !== '') {
val = new String(node.text);
for(var prop in node.element) if (node.element.hasOwnProperty(prop)) {
val[prop] = node.element[prop];
var element = curr.element;
var text = curr.fullText;
var attr = element.$;
if (typeof attr !== 'object') {
attr = {};
}
var name = element.$name;
delete element.$;
delete element.$text;
delete element.$name;
var val = element;
if (isEmpty(element) && isEmpty(attr)) {
val = text;
} else {
if (text !== '') {
element.$text = text;
}
} else {
val = node.element;
if (attr !== null) {
element.$ = attr;
}
}
element.$name = name;
curr.context[name] = val;
prev.node.element[node.name] = val;
fa.leave([node, curr.context, trace]);
if (self._emitData) {
self.emit('data', '</' + node.name + '>');
if (this._collect) {
var container = prev.element[name];
container[container.length - 1] = val;
} else {
prev.element[name] = val;
}
fa.leave([element, curr.context, trace]);
if (self._bufferLevel === 0 && self._emitData) {
emitEnd.call(self, name);
}
curr = prev;
this._collect = curr.collect;
});

@@ -213,8 +347,23 @@

xml.on('text', function(text) {
curr.node.text = text;
fa.run([curr.node, curr.context, trace]);
if (self._emitData) {
self.emit('data', escape(curr.node.text));
curr.element.$text = text;
fa.run('state', [curr.element, curr.context, trace]);
if (self._bufferLevel === 0 && self._emitData) {
emitText.call(self, text);
}
curr.fullText += curr.node.text.trim();
var trimmed = curr.element.$text.trim();
if (curr.space == 0) {
if (trimmed != '') {
curr.space = 1;
}
} else if (curr.space == 1) {
if (trimmed == '') {
curr.space = 2;
}
} else if (curr.space == 2) {
if (trimmed != '') {
curr.space = 1;
curr.fullText += ' ';
}
}
curr.fullText += trimmed;
});

@@ -232,2 +381,2 @@

});
};
}

@@ -9,3 +9,3 @@ {

],
"version": "0.2.0",
"version": "0.3.0",
"author": "AssistUnion <info@assistunion.com>",

@@ -23,5 +23,2 @@ "maintainers": [

},
"devDependencies": {
"vows": "~0.5.8"
},
"engines": {

@@ -34,2 +31,2 @@ "node": ">=0.4.7"

"main": "index"
}
}
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc