index.js 24 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', { value: true });
  3. var fromCharCode = String.fromCharCode;
  4. var hasOwnProperty = Object.prototype.hasOwnProperty;
  5. var ENTITY_PATTERN = /&#(\d+);|&#x([0-9a-f]+);|&(\w+);/ig;
  6. var ENTITY_MAPPING = {
  7. 'amp': '&',
  8. 'apos': '\'',
  9. 'gt': '>',
  10. 'lt': '<',
  11. 'quot': '"'
  12. };
  13. // map UPPERCASE variants of supported special chars
  14. Object.keys(ENTITY_MAPPING).forEach(function(k) {
  15. ENTITY_MAPPING[k.toUpperCase()] = ENTITY_MAPPING[k];
  16. });
  17. function replaceEntities(_, d, x, z) {
  18. // reserved names, i.e. &nbsp;
  19. if (z) {
  20. if (hasOwnProperty.call(ENTITY_MAPPING, z)) {
  21. return ENTITY_MAPPING[z];
  22. } else {
  23. // fall back to original value
  24. return '&' + z + ';';
  25. }
  26. }
  27. // decimal encoded char
  28. if (d) {
  29. return fromCharCode(d);
  30. }
  31. // hex encoded char
  32. return fromCharCode(parseInt(x, 16));
  33. }
  34. /**
  35. * A basic entity decoder that can decode a minimal
  36. * sub-set of reserved names (&amp;) as well as
  37. * hex (&#xaaf;) and decimal (&#1231;) encoded characters.
  38. *
  39. * @param {string} str
  40. *
  41. * @return {string} decoded string
  42. */
  43. function decodeEntities(s) {
  44. if (s.length > 3 && s.indexOf('&') !== -1) {
  45. return s.replace(ENTITY_PATTERN, replaceEntities);
  46. }
  47. return s;
  48. }
  49. var XSI_URI = 'http://www.w3.org/2001/XMLSchema-instance';
  50. var XSI_PREFIX = 'xsi';
  51. var XSI_TYPE = 'xsi:type';
  52. var NON_WHITESPACE_OUTSIDE_ROOT_NODE = 'non-whitespace outside of root node';
  53. function error(msg) {
  54. return new Error(msg);
  55. }
  56. function missingNamespaceForPrefix(prefix) {
  57. return 'missing namespace for prefix <' + prefix + '>';
  58. }
  59. function getter(getFn) {
  60. return {
  61. 'get': getFn,
  62. 'enumerable': true
  63. };
  64. }
  65. function cloneNsMatrix(nsMatrix) {
  66. var clone = {}, key;
  67. for (key in nsMatrix) {
  68. clone[key] = nsMatrix[key];
  69. }
  70. return clone;
  71. }
  72. function uriPrefix(prefix) {
  73. return prefix + '$uri';
  74. }
  75. function buildNsMatrix(nsUriToPrefix) {
  76. var nsMatrix = {},
  77. uri,
  78. prefix;
  79. for (uri in nsUriToPrefix) {
  80. prefix = nsUriToPrefix[uri];
  81. nsMatrix[prefix] = prefix;
  82. nsMatrix[uriPrefix(prefix)] = uri;
  83. }
  84. return nsMatrix;
  85. }
  86. function noopGetContext() {
  87. return { 'line': 0, 'column': 0 };
  88. }
  89. function throwFunc(err) {
  90. throw err;
  91. }
  92. /**
  93. * Creates a new parser with the given options.
  94. *
  95. * @constructor
  96. *
  97. * @param {!Object<string, ?>=} options
  98. */
  99. function Parser(options) {
  100. if (!this) {
  101. return new Parser(options);
  102. }
  103. var proxy = options && options['proxy'];
  104. var onText,
  105. onOpenTag,
  106. onCloseTag,
  107. onCDATA,
  108. onError = throwFunc,
  109. onWarning,
  110. onComment,
  111. onQuestion,
  112. onAttention;
  113. var getContext = noopGetContext;
  114. /**
  115. * Do we need to parse the current elements attributes for namespaces?
  116. *
  117. * @type {boolean}
  118. */
  119. var maybeNS = false;
  120. /**
  121. * Do we process namespaces at all?
  122. *
  123. * @type {boolean}
  124. */
  125. var isNamespace = false;
  126. /**
  127. * The caught error returned on parse end
  128. *
  129. * @type {Error}
  130. */
  131. var returnError = null;
  132. /**
  133. * Should we stop parsing?
  134. *
  135. * @type {boolean}
  136. */
  137. var parseStop = false;
  138. /**
  139. * A map of { uri: prefix } used by the parser.
  140. *
  141. * This map will ensure we can normalize prefixes during processing;
  142. * for each uri, only one prefix will be exposed to the handlers.
  143. *
  144. * @type {!Object<string, string>}}
  145. */
  146. var nsUriToPrefix;
  147. /**
  148. * Handle parse error.
  149. *
  150. * @param {string|Error} err
  151. */
  152. function handleError(err) {
  153. if (!(err instanceof Error)) {
  154. err = error(err);
  155. }
  156. returnError = err;
  157. onError(err, getContext);
  158. }
  159. /**
  160. * Handle parse error.
  161. *
  162. * @param {string|Error} err
  163. */
  164. function handleWarning(err) {
  165. if (!onWarning) {
  166. return;
  167. }
  168. if (!(err instanceof Error)) {
  169. err = error(err);
  170. }
  171. onWarning(err, getContext);
  172. }
  173. /**
  174. * Register parse listener.
  175. *
  176. * @param {string} name
  177. * @param {Function} cb
  178. *
  179. * @return {Parser}
  180. */
  181. this['on'] = function(name, cb) {
  182. if (typeof cb !== 'function') {
  183. throw error('required args <name, cb>');
  184. }
  185. switch (name) {
  186. case 'openTag': onOpenTag = cb; break;
  187. case 'text': onText = cb; break;
  188. case 'closeTag': onCloseTag = cb; break;
  189. case 'error': onError = cb; break;
  190. case 'warn': onWarning = cb; break;
  191. case 'cdata': onCDATA = cb; break;
  192. case 'attention': onAttention = cb; break; // <!XXXXX zzzz="eeee">
  193. case 'question': onQuestion = cb; break; // <? .... ?>
  194. case 'comment': onComment = cb; break;
  195. default:
  196. throw error('unsupported event: ' + name);
  197. }
  198. return this;
  199. };
  200. /**
  201. * Set the namespace to prefix mapping.
  202. *
  203. * @example
  204. *
  205. * parser.ns({
  206. * 'http://foo': 'foo',
  207. * 'http://bar': 'bar'
  208. * });
  209. *
  210. * @param {!Object<string, string>} nsMap
  211. *
  212. * @return {Parser}
  213. */
  214. this['ns'] = function(nsMap) {
  215. if (typeof nsMap === 'undefined') {
  216. nsMap = {};
  217. }
  218. if (typeof nsMap !== 'object') {
  219. throw error('required args <nsMap={}>');
  220. }
  221. var _nsUriToPrefix = {}, k;
  222. for (k in nsMap) {
  223. _nsUriToPrefix[k] = nsMap[k];
  224. }
  225. // FORCE default mapping for schema instance
  226. _nsUriToPrefix[XSI_URI] = XSI_PREFIX;
  227. isNamespace = true;
  228. nsUriToPrefix = _nsUriToPrefix;
  229. return this;
  230. };
  231. /**
  232. * Parse xml string.
  233. *
  234. * @param {string} xml
  235. *
  236. * @return {Error} returnError, if not thrown
  237. */
  238. this['parse'] = function(xml) {
  239. if (typeof xml !== 'string') {
  240. throw error('required args <xml=string>');
  241. }
  242. returnError = null;
  243. parse(xml);
  244. getContext = noopGetContext;
  245. parseStop = false;
  246. return returnError;
  247. };
  248. /**
  249. * Stop parsing.
  250. */
  251. this['stop'] = function() {
  252. parseStop = true;
  253. };
  254. /**
  255. * Parse string, invoking configured listeners on element.
  256. *
  257. * @param {string} xml
  258. */
  259. function parse(xml) {
  260. var nsMatrixStack = isNamespace ? [] : null,
  261. nsMatrix = isNamespace ? buildNsMatrix(nsUriToPrefix) : null,
  262. _nsMatrix,
  263. nodeStack = [],
  264. anonymousNsCount = 0,
  265. tagStart = false,
  266. tagEnd = false,
  267. i = 0, j = 0,
  268. x, y, q, w, v,
  269. xmlns,
  270. elementName,
  271. _elementName,
  272. elementProxy
  273. ;
  274. var attrsString = '',
  275. attrsStart = 0,
  276. cachedAttrs // false = parsed with errors, null = needs parsing
  277. ;
  278. /**
  279. * Parse attributes on demand and returns the parsed attributes.
  280. *
  281. * Return semantics: (1) `false` on attribute parse error,
  282. * (2) object hash on extracted attrs.
  283. *
  284. * @return {boolean|Object}
  285. */
  286. function getAttrs() {
  287. if (cachedAttrs !== null) {
  288. return cachedAttrs;
  289. }
  290. var nsUri,
  291. nsUriPrefix,
  292. nsName,
  293. defaultAlias = isNamespace && nsMatrix['xmlns'],
  294. attrList = isNamespace && maybeNS ? [] : null,
  295. i = attrsStart,
  296. s = attrsString,
  297. l = s.length,
  298. hasNewMatrix,
  299. newalias,
  300. value,
  301. alias,
  302. name,
  303. attrs = {},
  304. seenAttrs = {},
  305. skipAttr,
  306. w,
  307. j;
  308. parseAttr:
  309. for (; i < l; i++) {
  310. skipAttr = false;
  311. w = s.charCodeAt(i);
  312. if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE={ \f\n\r\t\v}
  313. continue;
  314. }
  315. // wait for non whitespace character
  316. if (w < 65 || w > 122 || (w > 90 && w < 97)) {
  317. if (w !== 95 && w !== 58) { // char 95"_" 58":"
  318. handleWarning('illegal first char attribute name');
  319. skipAttr = true;
  320. }
  321. }
  322. // parse attribute name
  323. for (j = i + 1; j < l; j++) {
  324. w = s.charCodeAt(j);
  325. if (
  326. w > 96 && w < 123 ||
  327. w > 64 && w < 91 ||
  328. w > 47 && w < 59 ||
  329. w === 46 || // '.'
  330. w === 45 || // '-'
  331. w === 95 // '_'
  332. ) {
  333. continue;
  334. }
  335. // unexpected whitespace
  336. if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE
  337. handleWarning('missing attribute value');
  338. i = j;
  339. continue parseAttr;
  340. }
  341. // expected "="
  342. if (w === 61) { // "=" == 61
  343. break;
  344. }
  345. handleWarning('illegal attribute name char');
  346. skipAttr = true;
  347. }
  348. name = s.substring(i, j);
  349. if (name === 'xmlns:xmlns') {
  350. handleWarning('illegal declaration of xmlns');
  351. skipAttr = true;
  352. }
  353. w = s.charCodeAt(j + 1);
  354. if (w === 34) { // '"'
  355. j = s.indexOf('"', i = j + 2);
  356. if (j === -1) {
  357. j = s.indexOf('\'', i);
  358. if (j !== -1) {
  359. handleWarning('attribute value quote missmatch');
  360. skipAttr = true;
  361. }
  362. }
  363. } else if (w === 39) { // "'"
  364. j = s.indexOf('\'', i = j + 2);
  365. if (j === -1) {
  366. j = s.indexOf('"', i);
  367. if (j !== -1) {
  368. handleWarning('attribute value quote missmatch');
  369. skipAttr = true;
  370. }
  371. }
  372. } else {
  373. handleWarning('missing attribute value quotes');
  374. skipAttr = true;
  375. // skip to next space
  376. for (j = j + 1; j < l; j++) {
  377. w = s.charCodeAt(j + 1);
  378. if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE
  379. break;
  380. }
  381. }
  382. }
  383. if (j === -1) {
  384. handleWarning('missing closing quotes');
  385. j = l;
  386. skipAttr = true;
  387. }
  388. if (!skipAttr) {
  389. value = s.substring(i, j);
  390. }
  391. i = j;
  392. // ensure SPACE follows attribute
  393. // skip illegal content otherwise
  394. // example a="b"c
  395. for (; j + 1 < l; j++) {
  396. w = s.charCodeAt(j + 1);
  397. if (w === 32 || (w < 14 && w > 8)) { // WHITESPACE
  398. break;
  399. }
  400. // FIRST ILLEGAL CHAR
  401. if (i === j) {
  402. handleWarning('illegal character after attribute end');
  403. skipAttr = true;
  404. }
  405. }
  406. // advance cursor to next attribute
  407. i = j + 1;
  408. if (skipAttr) {
  409. continue parseAttr;
  410. }
  411. // check attribute re-declaration
  412. if (name in seenAttrs) {
  413. handleWarning('attribute <' + name + '> already defined');
  414. continue;
  415. }
  416. seenAttrs[name] = true;
  417. if (!isNamespace) {
  418. attrs[name] = value;
  419. continue;
  420. }
  421. // try to extract namespace information
  422. if (maybeNS) {
  423. newalias = (
  424. name === 'xmlns'
  425. ? 'xmlns'
  426. : (name.charCodeAt(0) === 120 && name.substr(0, 6) === 'xmlns:')
  427. ? name.substr(6)
  428. : null
  429. );
  430. // handle xmlns(:alias) assignment
  431. if (newalias !== null) {
  432. nsUri = decodeEntities(value);
  433. nsUriPrefix = uriPrefix(newalias);
  434. alias = nsUriToPrefix[nsUri];
  435. if (!alias) {
  436. // no prefix defined or prefix collision
  437. if (
  438. (newalias === 'xmlns') ||
  439. (nsUriPrefix in nsMatrix && nsMatrix[nsUriPrefix] !== nsUri)
  440. ) {
  441. // alocate free ns prefix
  442. do {
  443. alias = 'ns' + (anonymousNsCount++);
  444. } while (typeof nsMatrix[alias] !== 'undefined');
  445. } else {
  446. alias = newalias;
  447. }
  448. nsUriToPrefix[nsUri] = alias;
  449. }
  450. if (nsMatrix[newalias] !== alias) {
  451. if (!hasNewMatrix) {
  452. nsMatrix = cloneNsMatrix(nsMatrix);
  453. hasNewMatrix = true;
  454. }
  455. nsMatrix[newalias] = alias;
  456. if (newalias === 'xmlns') {
  457. nsMatrix[uriPrefix(alias)] = nsUri;
  458. defaultAlias = alias;
  459. }
  460. nsMatrix[nsUriPrefix] = nsUri;
  461. }
  462. // expose xmlns(:asd)="..." in attributes
  463. attrs[name] = value;
  464. continue;
  465. }
  466. // collect attributes until all namespace
  467. // declarations are processed
  468. attrList.push(name, value);
  469. continue;
  470. } /** end if (maybeNs) */
  471. // handle attributes on element without
  472. // namespace declarations
  473. w = name.indexOf(':');
  474. if (w === -1) {
  475. attrs[name] = value;
  476. continue;
  477. }
  478. // normalize ns attribute name
  479. if (!(nsName = nsMatrix[name.substring(0, w)])) {
  480. handleWarning(missingNamespaceForPrefix(name.substring(0, w)));
  481. continue;
  482. }
  483. name = defaultAlias === nsName
  484. ? name.substr(w + 1)
  485. : nsName + name.substr(w);
  486. // end: normalize ns attribute name
  487. // normalize xsi:type ns attribute value
  488. if (name === XSI_TYPE) {
  489. w = value.indexOf(':');
  490. if (w !== -1) {
  491. nsName = value.substring(0, w);
  492. // handle default prefixes, i.e. xs:String gracefully
  493. nsName = nsMatrix[nsName] || nsName;
  494. value = nsName + value.substring(w);
  495. } else {
  496. value = defaultAlias + ':' + value;
  497. }
  498. }
  499. // end: normalize xsi:type ns attribute value
  500. attrs[name] = value;
  501. }
  502. // handle deferred, possibly namespaced attributes
  503. if (maybeNS) {
  504. // normalize captured attributes
  505. for (i = 0, l = attrList.length; i < l; i++) {
  506. name = attrList[i++];
  507. value = attrList[i];
  508. w = name.indexOf(':');
  509. if (w !== -1) {
  510. // normalize ns attribute name
  511. if (!(nsName = nsMatrix[name.substring(0, w)])) {
  512. handleWarning(missingNamespaceForPrefix(name.substring(0, w)));
  513. continue;
  514. }
  515. name = defaultAlias === nsName
  516. ? name.substr(w + 1)
  517. : nsName + name.substr(w);
  518. // end: normalize ns attribute name
  519. // normalize xsi:type ns attribute value
  520. if (name === XSI_TYPE) {
  521. w = value.indexOf(':');
  522. if (w !== -1) {
  523. nsName = value.substring(0, w);
  524. // handle default prefixes, i.e. xs:String gracefully
  525. nsName = nsMatrix[nsName] || nsName;
  526. value = nsName + value.substring(w);
  527. } else {
  528. value = defaultAlias + ':' + value;
  529. }
  530. }
  531. // end: normalize xsi:type ns attribute value
  532. }
  533. attrs[name] = value;
  534. }
  535. // end: normalize captured attributes
  536. }
  537. return cachedAttrs = attrs;
  538. }
  539. /**
  540. * Extract the parse context { line, column, part }
  541. * from the current parser position.
  542. *
  543. * @return {Object} parse context
  544. */
  545. function getParseContext() {
  546. var splitsRe = /(\r\n|\r|\n)/g;
  547. var line = 0;
  548. var column = 0;
  549. var startOfLine = 0;
  550. var endOfLine = j;
  551. var match;
  552. var data;
  553. while (i >= startOfLine) {
  554. match = splitsRe.exec(xml);
  555. if (!match) {
  556. break;
  557. }
  558. // end of line = (break idx + break chars)
  559. endOfLine = match[0].length + match.index;
  560. if (endOfLine > i) {
  561. break;
  562. }
  563. // advance to next line
  564. line += 1;
  565. startOfLine = endOfLine;
  566. }
  567. // EOF errors
  568. if (i == -1) {
  569. column = endOfLine;
  570. data = xml.substring(j);
  571. } else
  572. // start errors
  573. if (j === 0) {
  574. data = xml.substring(j, i);
  575. }
  576. // other errors
  577. else {
  578. column = i - startOfLine;
  579. data = (j == -1 ? xml.substring(i) : xml.substring(i, j + 1));
  580. }
  581. return {
  582. 'data': data,
  583. 'line': line,
  584. 'column': column
  585. };
  586. }
  587. getContext = getParseContext;
  588. if (proxy) {
  589. elementProxy = Object.create({}, {
  590. 'name': getter(function() {
  591. return elementName;
  592. }),
  593. 'originalName': getter(function() {
  594. return _elementName;
  595. }),
  596. 'attrs': getter(getAttrs),
  597. 'ns': getter(function() {
  598. return nsMatrix;
  599. })
  600. });
  601. }
  602. // actual parse logic
  603. while (j !== -1) {
  604. if (xml.charCodeAt(j) === 60) { // "<"
  605. i = j;
  606. } else {
  607. i = xml.indexOf('<', j);
  608. }
  609. // parse end
  610. if (i === -1) {
  611. if (nodeStack.length) {
  612. return handleError('unexpected end of file');
  613. }
  614. if (j === 0) {
  615. return handleError('missing start tag');
  616. }
  617. if (j < xml.length) {
  618. if (xml.substring(j).trim()) {
  619. handleWarning(NON_WHITESPACE_OUTSIDE_ROOT_NODE);
  620. }
  621. }
  622. return;
  623. }
  624. // parse text
  625. if (j !== i) {
  626. if (nodeStack.length) {
  627. if (onText) {
  628. onText(xml.substring(j, i), decodeEntities, getContext);
  629. if (parseStop) {
  630. return;
  631. }
  632. }
  633. } else {
  634. if (xml.substring(j, i).trim()) {
  635. handleWarning(NON_WHITESPACE_OUTSIDE_ROOT_NODE);
  636. if (parseStop) {
  637. return;
  638. }
  639. }
  640. }
  641. }
  642. w = xml.charCodeAt(i+1);
  643. // parse comments + CDATA
  644. if (w === 33) { // "!"
  645. q = xml.charCodeAt(i+2);
  646. // CDATA section
  647. if (q === 91 && xml.substr(i + 3, 6) === 'CDATA[') { // 91 == "["
  648. j = xml.indexOf(']]>', i);
  649. if (j === -1) {
  650. return handleError('unclosed cdata');
  651. }
  652. if (onCDATA) {
  653. onCDATA(xml.substring(i + 9, j), getContext);
  654. if (parseStop) {
  655. return;
  656. }
  657. }
  658. j += 3;
  659. continue;
  660. }
  661. // comment
  662. if (q === 45 && xml.charCodeAt(i + 3) === 45) { // 45 == "-"
  663. j = xml.indexOf('-->', i);
  664. if (j === -1) {
  665. return handleError('unclosed comment');
  666. }
  667. if (onComment) {
  668. onComment(xml.substring(i + 4, j), decodeEntities, getContext);
  669. if (parseStop) {
  670. return;
  671. }
  672. }
  673. j += 3;
  674. continue;
  675. }
  676. }
  677. // parse question <? ... ?>
  678. if (w === 63) { // "?"
  679. j = xml.indexOf('?>', i);
  680. if (j === -1) {
  681. return handleError('unclosed question');
  682. }
  683. if (onQuestion) {
  684. onQuestion(xml.substring(i, j + 2), getContext);
  685. if (parseStop) {
  686. return;
  687. }
  688. }
  689. j += 2;
  690. continue;
  691. }
  692. // find matching closing tag for attention or standard tags
  693. // for that we must skip through attribute values
  694. // (enclosed in single or double quotes)
  695. for (x = i + 1; ; x++) {
  696. v = xml.charCodeAt(x);
  697. if (isNaN(v)) {
  698. j = -1;
  699. return handleError('unclosed tag');
  700. }
  701. // [10] AttValue ::= '"' ([^<&"] | Reference)* '"' | "'" ([^<&'] | Reference)* "'"
  702. // skips the quoted string
  703. // (double quotes) does not appear in a literal enclosed by (double quotes)
  704. // (single quote) does not appear in a literal enclosed by (single quote)
  705. if (v === 34) { // '"'
  706. q = xml.indexOf('"', x + 1);
  707. x = q !== -1 ? q : x;
  708. } else if (v === 39) { // "'"
  709. q = xml.indexOf("'", x + 1);
  710. x = q !== -1 ? q : x;
  711. } else if (v === 62) { // '>'
  712. j = x;
  713. break;
  714. }
  715. }
  716. // parse attention <! ...>
  717. // previously comment and CDATA have already been parsed
  718. if (w === 33) { // "!"
  719. if (onAttention) {
  720. onAttention(xml.substring(i, j + 1), decodeEntities, getContext);
  721. if (parseStop) {
  722. return;
  723. }
  724. }
  725. j += 1;
  726. continue;
  727. }
  728. // don't process attributes;
  729. // there are none
  730. cachedAttrs = {};
  731. // if (xml.charCodeAt(i+1) === 47) { // </...
  732. if (w === 47) { // </...
  733. tagStart = false;
  734. tagEnd = true;
  735. if (!nodeStack.length) {
  736. return handleError('missing open tag');
  737. }
  738. // verify open <-> close tag match
  739. x = elementName = nodeStack.pop();
  740. q = i + 2 + x.length;
  741. if (xml.substring(i + 2, q) !== x) {
  742. return handleError('closing tag mismatch');
  743. }
  744. // verify chars in close tag
  745. for (; q < j; q++) {
  746. w = xml.charCodeAt(q);
  747. if (w === 32 || (w > 8 && w < 14)) { // \f\n\r\t\v space
  748. continue;
  749. }
  750. return handleError('close tag');
  751. }
  752. } else {
  753. if (xml.charCodeAt(j - 1) === 47) { // .../>
  754. x = elementName = xml.substring(i + 1, j - 1);
  755. tagStart = true;
  756. tagEnd = true;
  757. } else {
  758. x = elementName = xml.substring(i + 1, j);
  759. tagStart = true;
  760. tagEnd = false;
  761. }
  762. if (!(w > 96 && w < 123 || w > 64 && w < 91 || w === 95 || w === 58)) { // char 95"_" 58":"
  763. return handleError('illegal first char nodeName');
  764. }
  765. for (q = 1, y = x.length; q < y; q++) {
  766. w = x.charCodeAt(q);
  767. if (w > 96 && w < 123 || w > 64 && w < 91 || w > 47 && w < 59 || w === 45 || w === 95 || w == 46) {
  768. continue;
  769. }
  770. if (w === 32 || (w < 14 && w > 8)) { // \f\n\r\t\v space
  771. elementName = x.substring(0, q);
  772. // maybe there are attributes
  773. cachedAttrs = null;
  774. break;
  775. }
  776. return handleError('invalid nodeName');
  777. }
  778. if (!tagEnd) {
  779. nodeStack.push(elementName);
  780. }
  781. }
  782. if (isNamespace) {
  783. _nsMatrix = nsMatrix;
  784. if (tagStart) {
  785. // remember old namespace
  786. // unless we're self-closing
  787. if (!tagEnd) {
  788. nsMatrixStack.push(_nsMatrix);
  789. }
  790. if (cachedAttrs === null) {
  791. // quick check, whether there may be namespace
  792. // declarations on the node; if that is the case
  793. // we need to eagerly parse the node attributes
  794. if ((maybeNS = x.indexOf('xmlns', q) !== -1)) {
  795. attrsStart = q;
  796. attrsString = x;
  797. getAttrs();
  798. maybeNS = false;
  799. }
  800. }
  801. }
  802. _elementName = elementName;
  803. w = elementName.indexOf(':');
  804. if (w !== -1) {
  805. xmlns = nsMatrix[elementName.substring(0, w)];
  806. // prefix given; namespace must exist
  807. if (!xmlns) {
  808. return handleError('missing namespace on <' + _elementName + '>');
  809. }
  810. elementName = elementName.substr(w + 1);
  811. } else {
  812. xmlns = nsMatrix['xmlns'];
  813. // if no default namespace is defined,
  814. // we'll import the element as anonymous.
  815. //
  816. // it is up to users to correct that to the document defined
  817. // targetNamespace, or whatever their undersanding of the
  818. // XML spec mandates.
  819. }
  820. // adjust namespace prefixs as configured
  821. if (xmlns) {
  822. elementName = xmlns + ':' + elementName;
  823. }
  824. }
  825. if (tagStart) {
  826. attrsStart = q;
  827. attrsString = x;
  828. if (onOpenTag) {
  829. if (proxy) {
  830. onOpenTag(elementProxy, decodeEntities, tagEnd, getContext);
  831. } else {
  832. onOpenTag(elementName, getAttrs, decodeEntities, tagEnd, getContext);
  833. }
  834. if (parseStop) {
  835. return;
  836. }
  837. }
  838. }
  839. if (tagEnd) {
  840. if (onCloseTag) {
  841. onCloseTag(proxy ? elementProxy : elementName, decodeEntities, tagStart, getContext);
  842. if (parseStop) {
  843. return;
  844. }
  845. }
  846. // restore old namespace
  847. if (isNamespace) {
  848. if (!tagStart) {
  849. nsMatrix = nsMatrixStack.pop();
  850. } else {
  851. nsMatrix = _nsMatrix;
  852. }
  853. }
  854. }
  855. j += 1;
  856. }
  857. } /** end parse */
  858. }
  859. exports.Parser = Parser;
  860. exports.decode = decodeEntities;