This repository has been archived by the owner on Aug 22, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 13
/
saxpath.js
126 lines (95 loc) · 3.43 KB
/
saxpath.js
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
119
120
121
122
123
124
125
126
var util = require('util');
var events = require('events');
var XPathParser = require('./xpath_parser');
var State = require('./state');
var Recorder = require('./recorder');
function SaXPath(saxParser, xpath, recorder) {
this.saxParser = saxParser;
this.saxParser.on('opentag', this.onOpenTag.bind(this));
this.saxParser.on('closetag', this.onCloseTag.bind(this));
this.saxParser.on('text', this.onText.bind(this));
this.saxParser.on('end', this.onEnd.bind(this));
this.recorder = recorder || new Recorder();
this.saxParser.on('opentag', this.recorder.onOpenTag.bind(this.recorder));
this.saxParser.on('closetag', this.recorder.onCloseTag.bind(this.recorder));
this.saxParser.on('text', this.recorder.onText.bind(this.recorder));
this.xpathExpr = XPathParser.parse(xpath);
this.states = this.parseXPathExpr(this.xpathExpr);
this.currentDepth = -1;
this.currentState = 0;
}
util.inherits(SaXPath, events.EventEmitter);
SaXPath.prototype.parseXPathExpr = function(expr) {
var stack = [];
var i;
var previousState = null;
for (i = 0; i < expr.length; ++i) {
var part = expr[i];
var state = new State(part);
if (previousState) {
state.previous = previousState;
previousState.next = state;
} else {
state.previous = null;
}
stack.push(state);
previousState = state;
}
// activate first state
stack[0].enter(-1);
return stack;
};
SaXPath.prototype.onOpenTag = function(node) {
this.currentDepth += 1;
console.log('=== open tag, node:', node, 'depth:', this.currentDepth, 'state:', this.currentState);
if (this.currentState < this.states.length) {
// still matching states
var state = this.states[this.currentState];
console.log('current state:', state.name);
if (state.matches(node, this.currentDepth)) {
// move forward a state
this.currentState += 1;
if (this.currentState !== this.states.length) {
var nextState = this.states[this.currentState];
nextState.enter(this.currentDepth);
}
}
}
// have we reached the top of the state-stack?
if (this.currentState === this.states.length && !this.recorder.isRecording()) {
this.recorder.start();
}
};
SaXPath.prototype.onCloseTag = function(tag) {
this.currentDepth -= 1;
console.log('=== close tag, node:', tag, 'depth:', this.currentDepth, 'state:', this.currentState);
var state;
if (this.currentState === this.states.length) {
state = this.states[this.currentState - 1];
if (state.unmatches(tag, this.currentDepth)) {
state.leave(this.currentDepth);
if (this.recorder.isRecording()) {
this.recorder.stop();
}
// go back a state
this.currentState -= 1;
}
}
if (this.currentState < this.states.length) {
state = this.states[this.currentState];
if (state.unmatches(tag, this.currentDepth)) {
state.leave(this.currentDepth);
// go back a state
this.currentState -= 1;
}
}
};
SaXPath.prototype.onText = function(text) {
// console.log('=== text', text);
var state = this.states[this.currentState];
};
SaXPath.prototype.onEnd = function() {
console.log('=== end');
this.emit('end');
};
module.exports = SaXPath;