index.esm.js 24 KB

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