-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathxmlcheck.js
More file actions
120 lines (119 loc) · 4.33 KB
/
xmlcheck.js
File metadata and controls
120 lines (119 loc) · 4.33 KB
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
/**
* XMLCheck is a tool for quickly doing minimal parsing/well-formedness checking
* on an XML document (it really just checks for proper tag nesting). Parsing
* can run just to the current cursor position and then stop. When a parse
* has been run, the object can report on the list of open elements, the current
* element name, the position in the document that it parsed to, document well-
* formedness (if it parsed to the end), and whether the position parsed to is
* inside a tag.
*/
var XMLCheck = {
stack: [],
position: 0,
insideTag: false,
wf: true,
current: '',
posMark: '\u202F',
tokenize: function(str) {
return str.split('<');
},
/**
* The parse function does not do a true XML well-formedness check.
* In fact, it only does an extremely dumb, though fast, check that
* tags are properly nested. Takes a string containing the XML doc.
*/
parse: function(doc) {
this.stack = [];
this.position = 0;
this.wf = true;
this.insideTag = false;
var stream = this.tokenize(doc);
if (stream[0].length > 0) {
this.wf = false;
}
for (var i = 1; i < stream.length; i++) {
var token = stream[i];
this.position += 4;
var len = token.length;
if (token.indexOf('>') > 0) {
//get tag internals
token = token.substring(0, token.indexOf('>')).trim();
//ignore PIs and comments
if (token.charAt(0) == '?' || token.charAt(0) == '!') {
continue;
}
//if it's a close tag, pop the element off the stack
if (token.charAt(0) == '/') {
token = token.substring(1).trim();
if (this.stack[this.stack.length - 1] == token) {
this.stack.pop();
this.current = this.stack[this.stack.length - 1];
} else {
this.wf = false;
break;
}
continue;
}
//if it's a self-closing tag, ignore it
if (token.charAt(token.length - 1) == '/') {
continue;
}
//if it's an open tag, put the name on the stack
if (token.indexOf(' ') > 0) {
this.stack.push(token.substring(0, token.indexOf(' ')));
} else {
this.stack.push(token);
}
this.current = this.stack[this.stack.length - 1];
} else {
this.insideTag = true;
token = token.trim();
if (token.length > 0) {
if (token.charAt(0) == '?' || token.charAt(0) == '!' || token.charAt(0) == '/') {
this.current = this.stack[this.stack.length - 1];
} else {
if (token.indexOf(' ') >= 0) {
token = token.substring(0, token.indexOf(' '));
this.current = token;
} else if (token.length > 0) {
this.current = token;
}
}
}
}
this.position += len;
}
if (this.stack.length == 0 && this.wf == true) {
this.wf = true;
}
return this.wf;
},
// Parse up to the point in the doc string given by the location
parseToLocation: function(doc, location) {
var state = this.parse(doc.substring(0, location));
return this.current;
},
// Parse the contents of an element, given by the id parameter
parseElementContents: function(id) {
return this.parse(jQuery(id).text());
},
/*
* Parse the contents of an element, given by the id parameter up to the first
* occurrence of the character given by the posMark member variable.
*/
parseElementContentsToCursor: function(id) {
var sel = rangy.getSelection();
if (sel.rangeCount) {
var node = document.createTextNode(this.posMark);
var range = sel.getRangeAt(0);
range.insertNode(node);
var doc = jQuery(id).text();
this.parseToLocation(doc, doc.indexOf(this.posMark));
range.selectNode(node);
range.deleteContents();
}
},
getCurrentElement: function() {
return this.current;
}
};