@@ -27,14 +27,20 @@ export default function readDocType(xmlData, i){
27
27
val : val
28
28
} ;
29
29
}
30
- else if ( hasBody && isElement ( xmlData , i ) ) i += 8 ; //Not supported
31
- else if ( hasBody && isAttlist ( xmlData , i ) ) i += 8 ; //Not supported
32
- else if ( hasBody && isNotation ( xmlData , i ) ) {
30
+ else if ( hasBody && isElement ( xmlData , i ) ) {
31
+ i += 8 ; //Not supported
32
+ const { index} = readElementExp ( xmlData , i + 1 ) ;
33
+ i = index ;
34
+ } else if ( hasBody && isAttlist ( xmlData , i ) ) {
35
+ i += 8 ; //Not supported
36
+ // const {index} = readAttlistExp(xmlData,i+1);
37
+ // i = index;
38
+ } else if ( hasBody && isNotation ( xmlData , i ) ) {
33
39
i += 9 ; //Not supported
34
- const { name , publicIdentifier , systemIdentifier , index} = readNotationExp ( xmlData , i + 1 ) ;
40
+ const { index} = readNotationExp ( xmlData , i + 1 ) ;
35
41
i = index ;
36
- } else if ( isComment ) comment = true ;
37
- else throw new Error ( "Invalid DOCTYPE" ) ;
42
+ } else if ( isComment ) comment = true ;
43
+ else throw new Error ( "Invalid DOCTYPE" ) ;
38
44
39
45
angleBracketsCount ++ ;
40
46
exp = "" ;
@@ -181,6 +187,167 @@ function readIdentifierVal(xmlData, i, type) {
181
187
return [ i , identifierVal ] ;
182
188
}
183
189
190
+ function readElementExp ( xmlData , i ) {
191
+ // <!ELEMENT name (content-model)>
192
+
193
+ // Skip leading whitespace after <!ELEMENT
194
+ i = skipWhitespace ( xmlData , i ) ;
195
+
196
+ // Read element name
197
+ let elementName = "" ;
198
+ while ( i < xmlData . length && ! / \s / . test ( xmlData [ i ] ) ) {
199
+ elementName += xmlData [ i ] ;
200
+ i ++ ;
201
+ }
202
+
203
+ // Validate element name
204
+ if ( ! validateEntityName ( elementName ) ) {
205
+ throw new Error ( `Invalid element name: "${ elementName } "` ) ;
206
+ }
207
+
208
+ // Skip whitespace after element name
209
+ i = skipWhitespace ( xmlData , i ) ;
210
+
211
+ // Expect '(' to start content model
212
+ if ( xmlData [ i ] !== "(" ) {
213
+ throw new Error ( `Expected '(', found "${ xmlData [ i ] } "` ) ;
214
+ }
215
+ i ++ ; // Move past '('
216
+
217
+ // Read content model
218
+ let contentModel = "" ;
219
+ while ( i < xmlData . length && xmlData [ i ] !== ")" ) {
220
+ contentModel += xmlData [ i ] ;
221
+ i ++ ;
222
+ }
223
+
224
+ if ( xmlData [ i ] !== ")" ) {
225
+ throw new Error ( "Unterminated content model" ) ;
226
+ }
227
+
228
+ return {
229
+ elementName,
230
+ contentModel : contentModel . trim ( ) ,
231
+ index : i
232
+ } ;
233
+ }
234
+
235
+ function readAttlistExp ( xmlData , i ) {
236
+ // Skip leading whitespace after <!ATTLIST
237
+ i = skipWhitespace ( xmlData , i ) ;
238
+
239
+ // Read element name
240
+ let elementName = "" ;
241
+ while ( i < xmlData . length && ! / \s / . test ( xmlData [ i ] ) ) {
242
+ elementName += xmlData [ i ] ;
243
+ i ++ ;
244
+ }
245
+
246
+ // Validate element name
247
+ validateEntityName ( elementName )
248
+
249
+ // Skip whitespace after element name
250
+ i = skipWhitespace ( xmlData , i ) ;
251
+
252
+ // Read attribute name
253
+ let attributeName = "" ;
254
+ while ( i < xmlData . length && ! / \s / . test ( xmlData [ i ] ) ) {
255
+ attributeName += xmlData [ i ] ;
256
+ i ++ ;
257
+ }
258
+
259
+ // Validate attribute name
260
+ if ( ! validateEntityName ( attributeName ) ) {
261
+ throw new Error ( `Invalid attribute name: "${ attributeName } "` ) ;
262
+ }
263
+
264
+ // Skip whitespace after attribute name
265
+ i = skipWhitespace ( xmlData , i ) ;
266
+
267
+ // Read attribute type
268
+ let attributeType = "" ;
269
+ if ( xmlData . substring ( i , i + 8 ) . toUpperCase ( ) === "NOTATION" ) {
270
+ attributeType = "NOTATION" ;
271
+ i += 8 ; // Move past "NOTATION"
272
+
273
+ // Skip whitespace after "NOTATION"
274
+ i = skipWhitespace ( xmlData , i ) ;
275
+
276
+ // Expect '(' to start the list of notations
277
+ if ( xmlData [ i ] !== "(" ) {
278
+ throw new Error ( `Expected '(', found "${ xmlData [ i ] } "` ) ;
279
+ }
280
+ i ++ ; // Move past '('
281
+
282
+ // Read the list of allowed notations
283
+ let allowedNotations = [ ] ;
284
+ while ( i < xmlData . length && xmlData [ i ] !== ")" ) {
285
+ let notation = "" ;
286
+ while ( i < xmlData . length && xmlData [ i ] !== "|" && xmlData [ i ] !== ")" ) {
287
+ notation += xmlData [ i ] ;
288
+ i ++ ;
289
+ }
290
+
291
+ // Validate notation name
292
+ notation = notation . trim ( ) ;
293
+ if ( ! validateEntityName ( notation ) ) {
294
+ throw new Error ( `Invalid notation name: "${ notation } "` ) ;
295
+ }
296
+
297
+ allowedNotations . push ( notation ) ;
298
+
299
+ // Skip '|' separator or exit loop
300
+ if ( xmlData [ i ] === "|" ) {
301
+ i ++ ; // Move past '|'
302
+ i = skipWhitespace ( xmlData , i ) ; // Skip optional whitespace after '|'
303
+ }
304
+ }
305
+
306
+ if ( xmlData [ i ] !== ")" ) {
307
+ throw new Error ( "Unterminated list of notations" ) ;
308
+ }
309
+ i ++ ; // Move past ')'
310
+
311
+ // Store the allowed notations as part of the attribute type
312
+ attributeType += " (" + allowedNotations . join ( "|" ) + ")" ;
313
+ } else {
314
+ // Handle simple types (e.g., CDATA, ID, IDREF, etc.)
315
+ while ( i < xmlData . length && ! / \s / . test ( xmlData [ i ] ) ) {
316
+ attributeType += xmlData [ i ] ;
317
+ i ++ ;
318
+ }
319
+
320
+ // Validate simple attribute type
321
+ const validTypes = [ "CDATA" , "ID" , "IDREF" , "IDREFS" , "ENTITY" , "ENTITIES" , "NMTOKEN" , "NMTOKENS" ] ;
322
+ if ( ! validTypes . includes ( attributeType . toUpperCase ( ) ) ) {
323
+ throw new Error ( `Invalid attribute type: "${ attributeType } "` ) ;
324
+ }
325
+ }
326
+
327
+ // Skip whitespace after attribute type
328
+ i = skipWhitespace ( xmlData , i ) ;
329
+
330
+ // Read default value
331
+ let defaultValue = "" ;
332
+ if ( xmlData . substring ( i , i + 8 ) . toUpperCase ( ) === "#REQUIRED" ) {
333
+ defaultValue = "#REQUIRED" ;
334
+ i += 8 ;
335
+ } else if ( xmlData . substring ( i , i + 7 ) . toUpperCase ( ) === "#IMPLIED" ) {
336
+ defaultValue = "#IMPLIED" ;
337
+ i += 7 ;
338
+ } else {
339
+ [ i , defaultValue ] = readIdentifierVal ( xmlData , i , "ATTLIST" ) ;
340
+ }
341
+
342
+ return {
343
+ elementName,
344
+ attributeName,
345
+ attributeType,
346
+ defaultValue,
347
+ index : i
348
+ }
349
+ }
350
+
184
351
function isComment ( xmlData , i ) {
185
352
if ( xmlData [ i + 1 ] === '!' &&
186
353
xmlData [ i + 2 ] === '-' &&
0 commit comments