@longsightgroup/qti3-core 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/dist/catalog.d.ts +32 -0
- package/dist/catalog.d.ts.map +1 -0
- package/dist/catalog.js +126 -0
- package/dist/catalog.js.map +1 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/parser.d.ts.map +1 -1
- package/dist/parser.js +293 -6
- package/dist/parser.js.map +1 -1
- package/dist/session.d.ts +3 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +68 -3
- package/dist/session.js.map +1 -1
- package/dist/support.js +7 -1
- package/dist/support.js.map +1 -1
- package/dist/tts.d.ts +61 -0
- package/dist/tts.d.ts.map +1 -0
- package/dist/tts.js +368 -0
- package/dist/tts.js.map +1 -0
- package/dist/types.d.ts +61 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/validation.d.ts.map +1 -1
- package/dist/validation.js +243 -11
- package/dist/validation.js.map +1 -1
- package/package.json +4 -2
- package/src/catalog.ts +193 -0
- package/src/index.ts +85 -0
- package/src/parser.ts +1859 -0
- package/src/session.ts +2284 -0
- package/src/support.ts +253 -0
- package/src/tts.ts +555 -0
- package/src/types.ts +703 -0
- package/src/validation.ts +2449 -0
- package/src/xml.ts +139 -0
package/src/xml.ts
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { StaxXmlParserSync, XmlEventType } from "stax-xml";
|
|
2
|
+
|
|
3
|
+
export interface XmlNode {
|
|
4
|
+
name: string;
|
|
5
|
+
localName: string;
|
|
6
|
+
attributes: Record<string, string>;
|
|
7
|
+
children: XmlNode[];
|
|
8
|
+
content: Array<string | XmlNode>;
|
|
9
|
+
text: string;
|
|
10
|
+
source: XmlSourceLocation;
|
|
11
|
+
parent?: XmlNode;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface XmlSourceLocation {
|
|
15
|
+
line: number;
|
|
16
|
+
column: number;
|
|
17
|
+
offset: number;
|
|
18
|
+
path: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function parseXmlTree(xml: string): { root: XmlNode | undefined; errors: Error[] } {
|
|
22
|
+
const parser = new StaxXmlParserSync(xml, {
|
|
23
|
+
autoDecodeEntities: true,
|
|
24
|
+
});
|
|
25
|
+
const stack: XmlNode[] = [];
|
|
26
|
+
const errors: Error[] = [];
|
|
27
|
+
let root: XmlNode | undefined;
|
|
28
|
+
let searchOffset = 0;
|
|
29
|
+
|
|
30
|
+
for (const event of parser) {
|
|
31
|
+
if (event.type === XmlEventType.ERROR) {
|
|
32
|
+
errors.push(event.error);
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (event.type === XmlEventType.START_ELEMENT) {
|
|
37
|
+
const parent = stack.at(-1);
|
|
38
|
+
const offset = findStartElementOffset(xml, event.name, searchOffset);
|
|
39
|
+
searchOffset = offset >= 0 ? offset + 1 : searchOffset;
|
|
40
|
+
const node: XmlNode = {
|
|
41
|
+
name: event.name,
|
|
42
|
+
localName: event.localName ?? event.name,
|
|
43
|
+
attributes: event.attributes,
|
|
44
|
+
children: [],
|
|
45
|
+
content: [],
|
|
46
|
+
text: "",
|
|
47
|
+
source: sourceLocation(xml, offset, nodePath(parent, event.localName ?? event.name)),
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
if (parent) {
|
|
51
|
+
node.parent = parent;
|
|
52
|
+
parent.children.push(node);
|
|
53
|
+
parent.content.push(node);
|
|
54
|
+
} else {
|
|
55
|
+
root = node;
|
|
56
|
+
}
|
|
57
|
+
stack.push(node);
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (event.type === XmlEventType.END_ELEMENT) {
|
|
62
|
+
stack.pop();
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (event.type === XmlEventType.CHARACTERS || event.type === XmlEventType.CDATA) {
|
|
67
|
+
const node = stack.at(-1);
|
|
68
|
+
if (node) {
|
|
69
|
+
node.text += event.value;
|
|
70
|
+
node.content.push(event.value);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { root, errors };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function childElements(node: XmlNode, localName?: string): XmlNode[] {
|
|
79
|
+
return node.children.filter((child) => !localName || child.localName === localName);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function descendants(node: XmlNode, predicate: (node: XmlNode) => boolean): XmlNode[] {
|
|
83
|
+
const found: XmlNode[] = [];
|
|
84
|
+
for (const child of node.children) {
|
|
85
|
+
if (predicate(child)) found.push(child);
|
|
86
|
+
found.push(...descendants(child, predicate));
|
|
87
|
+
}
|
|
88
|
+
return found;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function textContent(node: XmlNode): string {
|
|
92
|
+
const parts = node.content.map((entry) =>
|
|
93
|
+
typeof entry === "string" ? entry : textContent(entry),
|
|
94
|
+
);
|
|
95
|
+
return parts.join(" ").replace(/\s+/g, " ").trim();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function findStartElementOffset(xml: string, name: string, from: number): number {
|
|
99
|
+
let offset = from;
|
|
100
|
+
while (offset < xml.length) {
|
|
101
|
+
const start = xml.indexOf("<", offset);
|
|
102
|
+
if (start === -1) return -1;
|
|
103
|
+
const next = xml.charAt(start + 1);
|
|
104
|
+
if (next === "/" || next === "!" || next === "?") {
|
|
105
|
+
offset = start + 1;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
const afterName = start + 1 + name.length;
|
|
109
|
+
if (
|
|
110
|
+
xml.slice(start + 1, afterName) === name &&
|
|
111
|
+
(afterName >= xml.length || /[\s/>]/.test(xml.charAt(afterName)))
|
|
112
|
+
) {
|
|
113
|
+
return start;
|
|
114
|
+
}
|
|
115
|
+
offset = start + 1;
|
|
116
|
+
}
|
|
117
|
+
return -1;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function sourceLocation(xml: string, offset: number, path: string): XmlSourceLocation {
|
|
121
|
+
if (offset < 0) return { line: 1, column: 1, offset: 0, path };
|
|
122
|
+
let line = 1;
|
|
123
|
+
let column = 1;
|
|
124
|
+
for (let index = 0; index < offset; index += 1) {
|
|
125
|
+
if (xml.charAt(index) === "\n") {
|
|
126
|
+
line += 1;
|
|
127
|
+
column = 1;
|
|
128
|
+
} else {
|
|
129
|
+
column += 1;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return { line, column, offset, path };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function nodePath(parent: XmlNode | undefined, localName: string): string {
|
|
136
|
+
if (!parent) return `/${localName}`;
|
|
137
|
+
const index = parent.children.filter((child) => child.localName === localName).length + 1;
|
|
138
|
+
return `${parent.source.path}/${localName}[${index}]`;
|
|
139
|
+
}
|