UNPKG

43.9 kBJavaScriptView Raw
1;(function (sax) { // wrapper for non-node envs
2 sax.parser = function (strict, opt) { return new SAXParser(strict, opt) }
3 sax.SAXParser = SAXParser
4 sax.SAXStream = SAXStream
5 sax.createStream = createStream
6
7 // When we pass the MAX_BUFFER_LENGTH position, start checking for buffer overruns.
8 // When we check, schedule the next check for MAX_BUFFER_LENGTH - (max(buffer lengths)),
9 // since that's the earliest that a buffer overrun could occur. This way, checks are
10 // as rare as required, but as often as necessary to ensure never crossing this bound.
11 // Furthermore, buffers are only tested at most once per write(), so passing a very
12 // large string into write() might have undesirable effects, but this is manageable by
13 // the caller, so it is assumed to be safe. Thus, a call to write() may, in the extreme
14 // edge case, result in creating at most one complete copy of the string passed in.
15 // Set to Infinity to have unlimited buffers.
16 sax.MAX_BUFFER_LENGTH = 64 * 1024
17
18 var buffers = [
19 'comment', 'sgmlDecl', 'textNode', 'tagName', 'doctype',
20 'procInstName', 'procInstBody', 'entity', 'attribName',
21 'attribValue', 'cdata', 'script'
22 ]
23
24 sax.EVENTS = [
25 'text',
26 'processinginstruction',
27 'sgmldeclaration',
28 'doctype',
29 'comment',
30 'opentagstart',
31 'attribute',
32 'opentag',
33 'closetag',
34 'opencdata',
35 'cdata',
36 'closecdata',
37 'error',
38 'end',
39 'ready',
40 'script',
41 'opennamespace',
42 'closenamespace'
43 ]
44
45 function SAXParser (strict, opt) {
46 if (!(this instanceof SAXParser)) {
47 return new SAXParser(strict, opt)
48 }
49
50 var parser = this
51 clearBuffers(parser)
52 parser.q = parser.c = ''
53 parser.bufferCheckPosition = sax.MAX_BUFFER_LENGTH
54 parser.opt = opt || {}
55 parser.opt.lowercase = parser.opt.lowercase || parser.opt.lowercasetags
56 parser.looseCase = parser.opt.lowercase ? 'toLowerCase' : 'toUpperCase'
57 parser.tags = []
58 parser.closed = parser.closedRoot = parser.sawRoot = false
59 parser.tag = parser.error = null
60 parser.strict = !!strict
61 parser.noscript = !!(strict || parser.opt.noscript)
62 parser.state = S.BEGIN
63 parser.strictEntities = parser.opt.strictEntities
64 parser.ENTITIES = parser.strictEntities ? Object.create(sax.XML_ENTITIES) : Object.create(sax.ENTITIES)
65 parser.attribList = []
66
67 // namespaces form a prototype chain.
68 // it always points at the current tag,
69 // which protos to its parent tag.
70 if (parser.opt.xmlns) {
71 parser.ns = Object.create(rootNS)
72 }
73
74 // mostly just for error reporting
75 parser.trackPosition = parser.opt.position !== false
76 if (parser.trackPosition) {
77 parser.position = parser.line = parser.column = 0
78 }
79 emit(parser, 'onready')
80 }
81
82 if (!Object.create) {
83 Object.create = function (o) {
84 function F () {}
85 F.prototype = o
86 var newf = new F()
87 return newf
88 }
89 }
90
91 if (!Object.keys) {
92 Object.keys = function (o) {
93 var a = []
94 for (var i in o) if (o.hasOwnProperty(i)) a.push(i)
95 return a
96 }
97 }
98
99 function checkBufferLength (parser) {
100 var maxAllowed = Math.max(sax.MAX_BUFFER_LENGTH, 10)
101 var maxActual = 0
102 for (var i = 0, l = buffers.length; i < l; i++) {
103 var len = parser[buffers[i]].length
104 if (len > maxAllowed) {
105 // Text/cdata nodes can get big, and since they're buffered,
106 // we can get here under normal conditions.
107 // Avoid issues by emitting the text node now,
108 // so at least it won't get any bigger.
109 switch (buffers[i]) {
110 case 'textNode':
111 closeText(parser)
112 break
113
114 case 'cdata':
115 emitNode(parser, 'oncdata', parser.cdata)
116 parser.cdata = ''
117 break
118
119 case 'script':
120 emitNode(parser, 'onscript', parser.script)
121 parser.script = ''
122 break
123
124 default:
125 error(parser, 'Max buffer length exceeded: ' + buffers[i])
126 }
127 }
128 maxActual = Math.max(maxActual, len)
129 }
130 // schedule the next check for the earliest possible buffer overrun.
131 var m = sax.MAX_BUFFER_LENGTH - maxActual
132 parser.bufferCheckPosition = m + parser.position
133 }
134
135 function clearBuffers (parser) {
136 for (var i = 0, l = buffers.length; i < l; i++) {
137 parser[buffers[i]] = ''
138 }
139 }
140
141 function flushBuffers (parser) {
142 closeText(parser)
143 if (parser.cdata !== '') {
144 emitNode(parser, 'oncdata', parser.cdata)
145 parser.cdata = ''
146 }
147 if (parser.script !== '') {
148 emitNode(parser, 'onscript', parser.script)
149 parser.script = ''
150 }
151 }
152
153 SAXParser.prototype = {
154 end: function () { end(this) },
155 write: write,
156 resume: function () { this.error = null; return this },
157 close: function () { return this.write(null) },
158 flush: function () { flushBuffers(this) }
159 }
160
161 var Stream
162 try {
163 Stream = require('stream').Stream
164 } catch (ex) {
165 Stream = function () {}
166 }
167
168 var streamWraps = sax.EVENTS.filter(function (ev) {
169 return ev !== 'error' && ev !== 'end'
170 })
171
172 function createStream (strict, opt) {
173 return new SAXStream(strict, opt)
174 }
175
176 function SAXStream (strict, opt) {
177 if (!(this instanceof SAXStream)) {
178 return new SAXStream(strict, opt)
179 }
180
181 Stream.apply(this)
182
183 this._parser = new SAXParser(strict, opt)
184 this.writable = true
185 this.readable = true
186
187 var me = this
188
189 this._parser.onend = function () {
190 me.emit('end')
191 }
192
193 this._parser.onerror = function (er) {
194 me.emit('error', er)
195
196 // if didn't throw, then means error was handled.
197 // go ahead and clear error, so we can write again.
198 me._parser.error = null
199 }
200
201 this._decoder = null
202
203 streamWraps.forEach(function (ev) {
204 Object.defineProperty(me, 'on' + ev, {
205 get: function () {
206 return me._parser['on' + ev]
207 },
208 set: function (h) {
209 if (!h) {
210 me.removeAllListeners(ev)
211 me._parser['on' + ev] = h
212 return h
213 }
214 me.on(ev, h)
215 },
216 enumerable: true,
217 configurable: false
218 })
219 })
220 }
221
222 SAXStream.prototype = Object.create(Stream.prototype, {
223 constructor: {
224 value: SAXStream
225 }
226 })
227
228 SAXStream.prototype.write = function (data) {
229 if (typeof Buffer === 'function' &&
230 typeof Buffer.isBuffer === 'function' &&
231 Buffer.isBuffer(data)) {
232 if (!this._decoder) {
233 var SD = require('string_decoder').StringDecoder
234 this._decoder = new SD('utf8')
235 }
236 data = this._decoder.write(data)
237 }
238
239 this._parser.write(data.toString())
240 this.emit('data', data)
241 return true
242 }
243
244 SAXStream.prototype.end = function (chunk) {
245 if (chunk && chunk.length) {
246 this.write(chunk)
247 }
248 this._parser.end()
249 return true
250 }
251
252 SAXStream.prototype.on = function (ev, handler) {
253 var me = this
254 if (!me._parser['on' + ev] && streamWraps.indexOf(ev) !== -1) {
255 me._parser['on' + ev] = function () {
256 var args = arguments.length === 1 ? [arguments[0]] : Array.apply(null, arguments)
257 args.splice(0, 0, ev)
258 me.emit.apply(me, args)
259 }
260 }
261
262 return Stream.prototype.on.call(me, ev, handler)
263 }
264
265 // character classes and tokens
266 var whitespace = '\r\n\t '
267
268 // this really needs to be replaced with character classes.
269 // XML allows all manner of ridiculous numbers and digits.
270
271 // (Letter | "_" | ":")
272 var quote = '\'"'
273 var attribEnd = whitespace + '>'
274 var CDATA = '[CDATA['
275 var DOCTYPE = 'DOCTYPE'
276 var XML_NAMESPACE = 'http://www.w3.org/XML/1998/namespace'
277 var XMLNS_NAMESPACE = 'http://www.w3.org/2000/xmlns/'
278 var rootNS = { xml: XML_NAMESPACE, xmlns: XMLNS_NAMESPACE }
279
280 // turn all the string character sets into character class objects.
281 whitespace = charClass(whitespace)
282
283 // http://www.w3.org/TR/REC-xml/#NT-NameStartChar
284 // This implementation works on strings, a single character at a time
285 // as such, it cannot ever support astral-plane characters (10000-EFFFF)
286 // without a significant breaking change to either this parser, or the
287 // JavaScript language. Implementation of an emoji-capable xml parser
288 // is left as an exercise for the reader.
289 var nameStart = /[:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/
290
291 var nameBody = /[:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u00B7\u0300-\u036F\u203F-\u2040.\d-]/
292
293 var entityStart = /[#:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD]/
294 var entityBody = /[#:_A-Za-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\u00B7\u0300-\u036F\u203F-\u2040.\d-]/
295
296 quote = charClass(quote)
297 attribEnd = charClass(attribEnd)
298
299 function charClass (str) {
300 return str.split('').reduce(function (s, c) {
301 s[c] = true
302 return s
303 }, {})
304 }
305
306 function isMatch (regex, c) {
307 return regex.test(c)
308 }
309
310 function is (charclass, c) {
311 return charclass[c]
312 }
313
314 function notMatch (regex, c) {
315 return !isMatch(regex, c)
316 }
317
318 function not (charclass, c) {
319 return !is(charclass, c)
320 }
321
322 var S = 0
323 sax.STATE = {
324 BEGIN: S++, // leading byte order mark or whitespace
325 BEGIN_WHITESPACE: S++, // leading whitespace
326 TEXT: S++, // general stuff
327 TEXT_ENTITY: S++, // &amp and such.
328 OPEN_WAKA: S++, // <
329 SGML_DECL: S++, // <!BLARG
330 SGML_DECL_QUOTED: S++, // <!BLARG foo "bar
331 DOCTYPE: S++, // <!DOCTYPE
332 DOCTYPE_QUOTED: S++, // <!DOCTYPE "//blah
333 DOCTYPE_DTD: S++, // <!DOCTYPE "//blah" [ ...
334 DOCTYPE_DTD_QUOTED: S++, // <!DOCTYPE "//blah" [ "foo
335 COMMENT_STARTING: S++, // <!-
336 COMMENT: S++, // <!--
337 COMMENT_ENDING: S++, // <!-- blah -
338 COMMENT_ENDED: S++, // <!-- blah --
339 CDATA: S++, // <![CDATA[ something
340 CDATA_ENDING: S++, // ]
341 CDATA_ENDING_2: S++, // ]]
342 PROC_INST: S++, // <?hi
343 PROC_INST_BODY: S++, // <?hi there
344 PROC_INST_ENDING: S++, // <?hi "there" ?
345 OPEN_TAG: S++, // <strong
346 OPEN_TAG_SLASH: S++, // <strong /
347 ATTRIB: S++, // <a
348 ATTRIB_NAME: S++, // <a foo
349 ATTRIB_NAME_SAW_WHITE: S++, // <a foo _
350 ATTRIB_VALUE: S++, // <a foo=
351 ATTRIB_VALUE_QUOTED: S++, // <a foo="bar
352 ATTRIB_VALUE_CLOSED: S++, // <a foo="bar"
353 ATTRIB_VALUE_UNQUOTED: S++, // <a foo=bar
354 ATTRIB_VALUE_ENTITY_Q: S++, // <foo bar="&quot;"
355 ATTRIB_VALUE_ENTITY_U: S++, // <foo bar=&quot
356 CLOSE_TAG: S++, // </a
357 CLOSE_TAG_SAW_WHITE: S++, // </a >
358 SCRIPT: S++, // <script> ...
359 SCRIPT_ENDING: S++ // <script> ... <
360 }
361
362 sax.XML_ENTITIES = {
363 'amp': '&',
364 'gt': '>',
365 'lt': '<',
366 'quot': '"',
367 'apos': "'"
368 }
369
370 sax.ENTITIES = {
371 'amp': '&',
372 'gt': '>',
373 'lt': '<',
374 'quot': '"',
375 'apos': "'",
376 'AElig': 198,
377 'Aacute': 193,
378 'Acirc': 194,
379 'Agrave': 192,
380 'Aring': 197,
381 'Atilde': 195,
382 'Auml': 196,
383 'Ccedil': 199,
384 'ETH': 208,
385 'Eacute': 201,
386 'Ecirc': 202,
387 'Egrave': 200,
388 'Euml': 203,
389 'Iacute': 205,
390 'Icirc': 206,
391 'Igrave': 204,
392 'Iuml': 207,
393 'Ntilde': 209,
394 'Oacute': 211,
395 'Ocirc': 212,
396 'Ograve': 210,
397 'Oslash': 216,
398 'Otilde': 213,
399 'Ouml': 214,
400 'THORN': 222,
401 'Uacute': 218,
402 'Ucirc': 219,
403 'Ugrave': 217,
404 'Uuml': 220,
405 'Yacute': 221,
406 'aacute': 225,
407 'acirc': 226,
408 'aelig': 230,
409 'agrave': 224,
410 'aring': 229,
411 'atilde': 227,
412 'auml': 228,
413 'ccedil': 231,
414 'eacute': 233,
415 'ecirc': 234,
416 'egrave': 232,
417 'eth': 240,
418 'euml': 235,
419 'iacute': 237,
420 'icirc': 238,
421 'igrave': 236,
422 'iuml': 239,
423 'ntilde': 241,
424 'oacute': 243,
425 'ocirc': 244,
426 'ograve': 242,
427 'oslash': 248,
428 'otilde': 245,
429 'ouml': 246,
430 'szlig': 223,
431 'thorn': 254,
432 'uacute': 250,
433 'ucirc': 251,
434 'ugrave': 249,
435 'uuml': 252,
436 'yacute': 253,
437 'yuml': 255,
438 'copy': 169,
439 'reg': 174,
440 'nbsp': 160,
441 'iexcl': 161,
442 'cent': 162,
443 'pound': 163,
444 'curren': 164,
445 'yen': 165,
446 'brvbar': 166,
447 'sect': 167,
448 'uml': 168,
449 'ordf': 170,
450 'laquo': 171,
451 'not': 172,
452 'shy': 173,
453 'macr': 175,
454 'deg': 176,
455 'plusmn': 177,
456 'sup1': 185,
457 'sup2': 178,
458 'sup3': 179,
459 'acute': 180,
460 'micro': 181,
461 'para': 182,
462 'middot': 183,
463 'cedil': 184,
464 'ordm': 186,
465 'raquo': 187,
466 'frac14': 188,
467 'frac12': 189,
468 'frac34': 190,
469 'iquest': 191,
470 'times': 215,
471 'divide': 247,
472 'OElig': 338,
473 'oelig': 339,
474 'Scaron': 352,
475 'scaron': 353,
476 'Yuml': 376,
477 'fnof': 402,
478 'circ': 710,
479 'tilde': 732,
480 'Alpha': 913,
481 'Beta': 914,
482 'Gamma': 915,
483 'Delta': 916,
484 'Epsilon': 917,
485 'Zeta': 918,
486 'Eta': 919,
487 'Theta': 920,
488 'Iota': 921,
489 'Kappa': 922,
490 'Lambda': 923,
491 'Mu': 924,
492 'Nu': 925,
493 'Xi': 926,
494 'Omicron': 927,
495 'Pi': 928,
496 'Rho': 929,
497 'Sigma': 931,
498 'Tau': 932,
499 'Upsilon': 933,
500 'Phi': 934,
501 'Chi': 935,
502 'Psi': 936,
503 'Omega': 937,
504 'alpha': 945,
505 'beta': 946,
506 'gamma': 947,
507 'delta': 948,
508 'epsilon': 949,
509 'zeta': 950,
510 'eta': 951,
511 'theta': 952,
512 'iota': 953,
513 'kappa': 954,
514 'lambda': 955,
515 'mu': 956,
516 'nu': 957,
517 'xi': 958,
518 'omicron': 959,
519 'pi': 960,
520 'rho': 961,
521 'sigmaf': 962,
522 'sigma': 963,
523 'tau': 964,
524 'upsilon': 965,
525 'phi': 966,
526 'chi': 967,
527 'psi': 968,
528 'omega': 969,
529 'thetasym': 977,
530 'upsih': 978,
531 'piv': 982,
532 'ensp': 8194,
533 'emsp': 8195,
534 'thinsp': 8201,
535 'zwnj': 8204,
536 'zwj': 8205,
537 'lrm': 8206,
538 'rlm': 8207,
539 'ndash': 8211,
540 'mdash': 8212,
541 'lsquo': 8216,
542 'rsquo': 8217,
543 'sbquo': 8218,
544 'ldquo': 8220,
545 'rdquo': 8221,
546 'bdquo': 8222,
547 'dagger': 8224,
548 'Dagger': 8225,
549 'bull': 8226,
550 'hellip': 8230,
551 'permil': 8240,
552 'prime': 8242,
553 'Prime': 8243,
554 'lsaquo': 8249,
555 'rsaquo': 8250,
556 'oline': 8254,
557 'frasl': 8260,
558 'euro': 8364,
559 'image': 8465,
560 'weierp': 8472,
561 'real': 8476,
562 'trade': 8482,
563 'alefsym': 8501,
564 'larr': 8592,
565 'uarr': 8593,
566 'rarr': 8594,
567 'darr': 8595,
568 'harr': 8596,
569 'crarr': 8629,
570 'lArr': 8656,
571 'uArr': 8657,
572 'rArr': 8658,
573 'dArr': 8659,
574 'hArr': 8660,
575 'forall': 8704,
576 'part': 8706,
577 'exist': 8707,
578 'empty': 8709,
579 'nabla': 8711,
580 'isin': 8712,
581 'notin': 8713,
582 'ni': 8715,
583 'prod': 8719,
584 'sum': 8721,
585 'minus': 8722,
586 'lowast': 8727,
587 'radic': 8730,
588 'prop': 8733,
589 'infin': 8734,
590 'ang': 8736,
591 'and': 8743,
592 'or': 8744,
593 'cap': 8745,
594 'cup': 8746,
595 'int': 8747,
596 'there4': 8756,
597 'sim': 8764,
598 'cong': 8773,
599 'asymp': 8776,
600 'ne': 8800,
601 'equiv': 8801,
602 'le': 8804,
603 'ge': 8805,
604 'sub': 8834,
605 'sup': 8835,
606 'nsub': 8836,
607 'sube': 8838,
608 'supe': 8839,
609 'oplus': 8853,
610 'otimes': 8855,
611 'perp': 8869,
612 'sdot': 8901,
613 'lceil': 8968,
614 'rceil': 8969,
615 'lfloor': 8970,
616 'rfloor': 8971,
617 'lang': 9001,
618 'rang': 9002,
619 'loz': 9674,
620 'spades': 9824,
621 'clubs': 9827,
622 'hearts': 9829,
623 'diams': 9830
624 }
625
626 Object.keys(sax.ENTITIES).forEach(function (key) {
627 var e = sax.ENTITIES[key]
628 var s = typeof e === 'number' ? String.fromCharCode(e) : e
629 sax.ENTITIES[key] = s
630 })
631
632 for (var s in sax.STATE) {
633 sax.STATE[sax.STATE[s]] = s
634 }
635
636 // shorthand
637 S = sax.STATE
638
639 function emit (parser, event, data) {
640 parser[event] && parser[event](data)
641 }
642
643 function emitNode (parser, nodeType, data) {
644 if (parser.textNode) closeText(parser)
645 emit(parser, nodeType, data)
646 }
647
648 function closeText (parser) {
649 parser.textNode = textopts(parser.opt, parser.textNode)
650 if (parser.textNode) emit(parser, 'ontext', parser.textNode)
651 parser.textNode = ''
652 }
653
654 function textopts (opt, text) {
655 if (opt.trim) text = text.trim()
656 if (opt.normalize) text = text.replace(/\s+/g, ' ')
657 return text
658 }
659
660 function error (parser, er) {
661 closeText(parser)
662 if (parser.trackPosition) {
663 er += '\nLine: ' + parser.line +
664 '\nColumn: ' + parser.column +
665 '\nChar: ' + parser.c
666 }
667 er = new Error(er)
668 parser.error = er
669 emit(parser, 'onerror', er)
670 return parser
671 }
672
673 function end (parser) {
674 if (parser.sawRoot && !parser.closedRoot) strictFail(parser, 'Unclosed root tag')
675 if ((parser.state !== S.BEGIN) &&
676 (parser.state !== S.BEGIN_WHITESPACE) &&
677 (parser.state !== S.TEXT)) {
678 error(parser, 'Unexpected end')
679 }
680 closeText(parser)
681 parser.c = ''
682 parser.closed = true
683 emit(parser, 'onend')
684 SAXParser.call(parser, parser.strict, parser.opt)
685 return parser
686 }
687
688 function strictFail (parser, message) {
689 if (typeof parser !== 'object' || !(parser instanceof SAXParser)) {
690 throw new Error('bad call to strictFail')
691 }
692 if (parser.strict) {
693 error(parser, message)
694 }
695 }
696
697 function newTag (parser) {
698 if (!parser.strict) parser.tagName = parser.tagName[parser.looseCase]()
699 var parent = parser.tags[parser.tags.length - 1] || parser
700 var tag = parser.tag = { name: parser.tagName, attributes: {} }
701
702 // will be overridden if tag contails an xmlns="foo" or xmlns:foo="bar"
703 if (parser.opt.xmlns) {
704 tag.ns = parent.ns
705 }
706 parser.attribList.length = 0
707 emitNode(parser, 'onopentagstart', tag)
708 }
709
710 function qname (name, attribute) {
711 var i = name.indexOf(':')
712 var qualName = i < 0 ? [ '', name ] : name.split(':')
713 var prefix = qualName[0]
714 var local = qualName[1]
715
716 // <x "xmlns"="http://foo">
717 if (attribute && name === 'xmlns') {
718 prefix = 'xmlns'
719 local = ''
720 }
721
722 return { prefix: prefix, local: local }
723 }
724
725 function attrib (parser) {
726 if (!parser.strict) {
727 parser.attribName = parser.attribName[parser.looseCase]()
728 }
729
730 if (parser.attribList.indexOf(parser.attribName) !== -1 ||
731 parser.tag.attributes.hasOwnProperty(parser.attribName)) {
732 parser.attribName = parser.attribValue = ''
733 return
734 }
735
736 if (parser.opt.xmlns) {
737 var qn = qname(parser.attribName, true)
738 var prefix = qn.prefix
739 var local = qn.local
740
741 if (prefix === 'xmlns') {
742 // namespace binding attribute. push the binding into scope
743 if (local === 'xml' && parser.attribValue !== XML_NAMESPACE) {
744 strictFail(parser,
745 'xml: prefix must be bound to ' + XML_NAMESPACE + '\n' +
746 'Actual: ' + parser.attribValue)
747 } else if (local === 'xmlns' && parser.attribValue !== XMLNS_NAMESPACE) {
748 strictFail(parser,
749 'xmlns: prefix must be bound to ' + XMLNS_NAMESPACE + '\n' +
750 'Actual: ' + parser.attribValue)
751 } else {
752 var tag = parser.tag
753 var parent = parser.tags[parser.tags.length - 1] || parser
754 if (tag.ns === parent.ns) {
755 tag.ns = Object.create(parent.ns)
756 }
757 tag.ns[local] = parser.attribValue
758 }
759 }
760
761 // defer onattribute events until all attributes have been seen
762 // so any new bindings can take effect. preserve attribute order
763 // so deferred events can be emitted in document order
764 parser.attribList.push([parser.attribName, parser.attribValue])
765 } else {
766 // in non-xmlns mode, we can emit the event right away
767 parser.tag.attributes[parser.attribName] = parser.attribValue
768 emitNode(parser, 'onattribute', {
769 name: parser.attribName,
770 value: parser.attribValue
771 })
772 }
773
774 parser.attribName = parser.attribValue = ''
775 }
776
777 function openTag (parser, selfClosing) {
778 if (parser.opt.xmlns) {
779 // emit namespace binding events
780 var tag = parser.tag
781
782 // add namespace info to tag
783 var qn = qname(parser.tagName)
784 tag.prefix = qn.prefix
785 tag.local = qn.local
786 tag.uri = tag.ns[qn.prefix] || ''
787
788 if (tag.prefix && !tag.uri) {
789 strictFail(parser, 'Unbound namespace prefix: ' +
790 JSON.stringify(parser.tagName))
791 tag.uri = qn.prefix
792 }
793
794 var parent = parser.tags[parser.tags.length - 1] || parser
795 if (tag.ns && parent.ns !== tag.ns) {
796 Object.keys(tag.ns).forEach(function (p) {
797 emitNode(parser, 'onopennamespace', {
798 prefix: p,
799 uri: tag.ns[p]
800 })
801 })
802 }
803
804 // handle deferred onattribute events
805 // Note: do not apply default ns to attributes:
806 // http://www.w3.org/TR/REC-xml-names/#defaulting
807 for (var i = 0, l = parser.attribList.length; i < l; i++) {
808 var nv = parser.attribList[i]
809 var name = nv[0]
810 var value = nv[1]
811 var qualName = qname(name, true)
812 var prefix = qualName.prefix
813 var local = qualName.local
814 var uri = prefix === '' ? '' : (tag.ns[prefix] || '')
815 var a = {
816 name: name,
817 value: value,
818 prefix: prefix,
819 local: local,
820 uri: uri
821 }
822
823 // if there's any attributes with an undefined namespace,
824 // then fail on them now.
825 if (prefix && prefix !== 'xmlns' && !uri) {
826 strictFail(parser, 'Unbound namespace prefix: ' +
827 JSON.stringify(prefix))
828 a.uri = prefix
829 }
830 parser.tag.attributes[name] = a
831 emitNode(parser, 'onattribute', a)
832 }
833 parser.attribList.length = 0
834 }
835
836 parser.tag.isSelfClosing = !!selfClosing
837
838 // process the tag
839 parser.sawRoot = true
840 parser.tags.push(parser.tag)
841 emitNode(parser, 'onopentag', parser.tag)
842 if (!selfClosing) {
843 // special case for <script> in non-strict mode.
844 if (!parser.noscript && parser.tagName.toLowerCase() === 'script') {
845 parser.state = S.SCRIPT
846 } else {
847 parser.state = S.TEXT
848 }
849 parser.tag = null
850 parser.tagName = ''
851 }
852 parser.attribName = parser.attribValue = ''
853 parser.attribList.length = 0
854 }
855
856 function closeTag (parser) {
857 if (!parser.tagName) {
858 strictFail(parser, 'Weird empty close tag.')
859 parser.textNode += '</>'
860 parser.state = S.TEXT
861 return
862 }
863
864 if (parser.script) {
865 if (parser.tagName !== 'script') {
866 parser.script += '</' + parser.tagName + '>'
867 parser.tagName = ''
868 parser.state = S.SCRIPT
869 return
870 }
871 emitNode(parser, 'onscript', parser.script)
872 parser.script = ''
873 }
874
875 // first make sure that the closing tag actually exists.
876 // <a><b></c></b></a> will close everything, otherwise.
877 var t = parser.tags.length
878 var tagName = parser.tagName
879 if (!parser.strict) {
880 tagName = tagName[parser.looseCase]()
881 }
882 var closeTo = tagName
883 while (t--) {
884 var close = parser.tags[t]
885 if (close.name !== closeTo) {
886 // fail the first time in strict mode
887 strictFail(parser, 'Unexpected close tag')
888 } else {
889 break
890 }
891 }
892
893 // didn't find it. we already failed for strict, so just abort.
894 if (t < 0) {
895 strictFail(parser, 'Unmatched closing tag: ' + parser.tagName)
896 parser.textNode += '</' + parser.tagName + '>'
897 parser.state = S.TEXT
898 return
899 }
900 parser.tagName = tagName
901 var s = parser.tags.length
902 while (s-- > t) {
903 var tag = parser.tag = parser.tags.pop()
904 parser.tagName = parser.tag.name
905 emitNode(parser, 'onclosetag', parser.tagName)
906
907 var x = {}
908 for (var i in tag.ns) {
909 x[i] = tag.ns[i]
910 }
911
912 var parent = parser.tags[parser.tags.length - 1] || parser
913 if (parser.opt.xmlns && tag.ns !== parent.ns) {
914 // remove namespace bindings introduced by tag
915 Object.keys(tag.ns).forEach(function (p) {
916 var n = tag.ns[p]
917 emitNode(parser, 'onclosenamespace', { prefix: p, uri: n })
918 })
919 }
920 }
921 if (t === 0) parser.closedRoot = true
922 parser.tagName = parser.attribValue = parser.attribName = ''
923 parser.attribList.length = 0
924 parser.state = S.TEXT
925 }
926
927 function parseEntity (parser) {
928 var entity = parser.entity
929 var entityLC = entity.toLowerCase()
930 var num
931 var numStr = ''
932
933 if (parser.ENTITIES[entity]) {
934 return parser.ENTITIES[entity]
935 }
936 if (parser.ENTITIES[entityLC]) {
937 return parser.ENTITIES[entityLC]
938 }
939 entity = entityLC
940 if (entity.charAt(0) === '#') {
941 if (entity.charAt(1) === 'x') {
942 entity = entity.slice(2)
943 num = parseInt(entity, 16)
944 numStr = num.toString(16)
945 } else {
946 entity = entity.slice(1)
947 num = parseInt(entity, 10)
948 numStr = num.toString(10)
949 }
950 }
951 entity = entity.replace(/^0+/, '')
952 if (numStr.toLowerCase() !== entity) {
953 strictFail(parser, 'Invalid character entity')
954 return '&' + parser.entity + ';'
955 }
956
957 return String.fromCodePoint(num)
958 }
959
960 function beginWhiteSpace (parser, c) {
961 if (c === '<') {
962 parser.state = S.OPEN_WAKA
963 parser.startTagPosition = parser.position
964 } else if (not(whitespace, c)) {
965 // have to process this as a text node.
966 // weird, but happens.
967 strictFail(parser, 'Non-whitespace before first tag.')
968 parser.textNode = c
969 parser.state = S.TEXT
970 }
971 }
972
973 function charAt (chunk, i) {
974 var result = ''
975 if (i < chunk.length) {
976 result = chunk.charAt(i)
977 }
978 return result
979 }
980
981 function write (chunk) {
982 var parser = this
983 if (this.error) {
984 throw this.error
985 }
986 if (parser.closed) {
987 return error(parser,
988 'Cannot write after close. Assign an onready handler.')
989 }
990 if (chunk === null) {
991 return end(parser)
992 }
993 if (typeof chunk === 'object') {
994 chunk = chunk.toString()
995 }
996 var i = 0
997 var c = ''
998 while (true) {
999 c = charAt(chunk, i++)
1000 parser.c = c
1001
1002 if (!c) {
1003 break
1004 }
1005
1006 if (parser.trackPosition) {
1007 parser.position++
1008 if (c === '\n') {
1009 parser.line++
1010 parser.column = 0
1011 } else {
1012 parser.column++
1013 }
1014 }
1015
1016 switch (parser.state) {
1017 case S.BEGIN:
1018 parser.state = S.BEGIN_WHITESPACE
1019 if (c === '\uFEFF') {
1020 continue
1021 }
1022 beginWhiteSpace(parser, c)
1023 continue
1024
1025 case S.BEGIN_WHITESPACE:
1026 beginWhiteSpace(parser, c)
1027 continue
1028
1029 case S.TEXT:
1030 if (parser.sawRoot && !parser.closedRoot) {
1031 var starti = i - 1
1032 while (c && c !== '<' && c !== '&') {
1033 c = charAt(chunk, i++)
1034 if (c && parser.trackPosition) {
1035 parser.position++
1036 if (c === '\n') {
1037 parser.line++
1038 parser.column = 0
1039 } else {
1040 parser.column++
1041 }
1042 }
1043 }
1044 parser.textNode += chunk.substring(starti, i - 1)
1045 }
1046 if (c === '<' && !(parser.sawRoot && parser.closedRoot && !parser.strict)) {
1047 parser.state = S.OPEN_WAKA
1048 parser.startTagPosition = parser.position
1049 } else {
1050 if (not(whitespace, c) && (!parser.sawRoot || parser.closedRoot)) {
1051 strictFail(parser, 'Text data outside of root node.')
1052 }
1053 if (c === '&') {
1054 parser.state = S.TEXT_ENTITY
1055 } else {
1056 parser.textNode += c
1057 }
1058 }
1059 continue
1060
1061 case S.SCRIPT:
1062 // only non-strict
1063 if (c === '<') {
1064 parser.state = S.SCRIPT_ENDING
1065 } else {
1066 parser.script += c
1067 }
1068 continue
1069
1070 case S.SCRIPT_ENDING:
1071 if (c === '/') {
1072 parser.state = S.CLOSE_TAG
1073 } else {
1074 parser.script += '<' + c
1075 parser.state = S.SCRIPT
1076 }
1077 continue
1078
1079 case S.OPEN_WAKA:
1080 // either a /, ?, !, or text is coming next.
1081 if (c === '!') {
1082 parser.state = S.SGML_DECL
1083 parser.sgmlDecl = ''
1084 } else if (is(whitespace, c)) {
1085 // wait for it...
1086 } else if (isMatch(nameStart, c)) {
1087 parser.state = S.OPEN_TAG
1088 parser.tagName = c
1089 } else if (c === '/') {
1090 parser.state = S.CLOSE_TAG
1091 parser.tagName = ''
1092 } else if (c === '?') {
1093 parser.state = S.PROC_INST
1094 parser.procInstName = parser.procInstBody = ''
1095 } else {
1096 strictFail(parser, 'Unencoded <')
1097 // if there was some whitespace, then add that in.
1098 if (parser.startTagPosition + 1 < parser.position) {
1099 var pad = parser.position - parser.startTagPosition
1100 c = new Array(pad).join(' ') + c
1101 }
1102 parser.textNode += '<' + c
1103 parser.state = S.TEXT
1104 }
1105 continue
1106
1107 case S.SGML_DECL:
1108 if ((parser.sgmlDecl + c).toUpperCase() === CDATA) {
1109 emitNode(parser, 'onopencdata')
1110 parser.state = S.CDATA
1111 parser.sgmlDecl = ''
1112 parser.cdata = ''
1113 } else if (parser.sgmlDecl + c === '--') {
1114 parser.state = S.COMMENT
1115 parser.comment = ''
1116 parser.sgmlDecl = ''
1117 } else if ((parser.sgmlDecl + c).toUpperCase() === DOCTYPE) {
1118 parser.state = S.DOCTYPE
1119 if (parser.doctype || parser.sawRoot) {
1120 strictFail(parser,
1121 'Inappropriately located doctype declaration')
1122 }
1123 parser.doctype = ''
1124 parser.sgmlDecl = ''
1125 } else if (c === '>') {
1126 emitNode(parser, 'onsgmldeclaration', parser.sgmlDecl)
1127 parser.sgmlDecl = ''
1128 parser.state = S.TEXT
1129 } else if (is(quote, c)) {
1130 parser.state = S.SGML_DECL_QUOTED
1131 parser.sgmlDecl += c
1132 } else {
1133 parser.sgmlDecl += c
1134 }
1135 continue
1136
1137 case S.SGML_DECL_QUOTED:
1138 if (c === parser.q) {
1139 parser.state = S.SGML_DECL
1140 parser.q = ''
1141 }
1142 parser.sgmlDecl += c
1143 continue
1144
1145 case S.DOCTYPE:
1146 if (c === '>') {
1147 parser.state = S.TEXT
1148 emitNode(parser, 'ondoctype', parser.doctype)
1149 parser.doctype = true // just remember that we saw it.
1150 } else {
1151 parser.doctype += c
1152 if (c === '[') {
1153 parser.state = S.DOCTYPE_DTD
1154 } else if (is(quote, c)) {
1155 parser.state = S.DOCTYPE_QUOTED
1156 parser.q = c
1157 }
1158 }
1159 continue
1160
1161 case S.DOCTYPE_QUOTED:
1162 parser.doctype += c
1163 if (c === parser.q) {
1164 parser.q = ''
1165 parser.state = S.DOCTYPE
1166 }
1167 continue
1168
1169 case S.DOCTYPE_DTD:
1170 parser.doctype += c
1171 if (c === ']') {
1172 parser.state = S.DOCTYPE
1173 } else if (is(quote, c)) {
1174 parser.state = S.DOCTYPE_DTD_QUOTED
1175 parser.q = c
1176 }
1177 continue
1178
1179 case S.DOCTYPE_DTD_QUOTED:
1180 parser.doctype += c
1181 if (c === parser.q) {
1182 parser.state = S.DOCTYPE_DTD
1183 parser.q = ''
1184 }
1185 continue
1186
1187 case S.COMMENT:
1188 if (c === '-') {
1189 parser.state = S.COMMENT_ENDING
1190 } else {
1191 parser.comment += c
1192 }
1193 continue
1194
1195 case S.COMMENT_ENDING:
1196 if (c === '-') {
1197 parser.state = S.COMMENT_ENDED
1198 parser.comment = textopts(parser.opt, parser.comment)
1199 if (parser.comment) {
1200 emitNode(parser, 'oncomment', parser.comment)
1201 }
1202 parser.comment = ''
1203 } else {
1204 parser.comment += '-' + c
1205 parser.state = S.COMMENT
1206 }
1207 continue
1208
1209 case S.COMMENT_ENDED:
1210 if (c !== '>') {
1211 strictFail(parser, 'Malformed comment')
1212 // allow <!-- blah -- bloo --> in non-strict mode,
1213 // which is a comment of " blah -- bloo "
1214 parser.comment += '--' + c
1215 parser.state = S.COMMENT
1216 } else {
1217 parser.state = S.TEXT
1218 }
1219 continue
1220
1221 case S.CDATA:
1222 if (c === ']') {
1223 parser.state = S.CDATA_ENDING
1224 } else {
1225 parser.cdata += c
1226 }
1227 continue
1228
1229 case S.CDATA_ENDING:
1230 if (c === ']') {
1231 parser.state = S.CDATA_ENDING_2
1232 } else {
1233 parser.cdata += ']' + c
1234 parser.state = S.CDATA
1235 }
1236 continue
1237
1238 case S.CDATA_ENDING_2:
1239 if (c === '>') {
1240 if (parser.cdata) {
1241 emitNode(parser, 'oncdata', parser.cdata)
1242 }
1243 emitNode(parser, 'onclosecdata')
1244 parser.cdata = ''
1245 parser.state = S.TEXT
1246 } else if (c === ']') {
1247 parser.cdata += ']'
1248 } else {
1249 parser.cdata += ']]' + c
1250 parser.state = S.CDATA
1251 }
1252 continue
1253
1254 case S.PROC_INST:
1255 if (c === '?') {
1256 parser.state = S.PROC_INST_ENDING
1257 } else if (is(whitespace, c)) {
1258 parser.state = S.PROC_INST_BODY
1259 } else {
1260 parser.procInstName += c
1261 }
1262 continue
1263
1264 case S.PROC_INST_BODY:
1265 if (!parser.procInstBody && is(whitespace, c)) {
1266 continue
1267 } else if (c === '?') {
1268 parser.state = S.PROC_INST_ENDING
1269 } else {
1270 parser.procInstBody += c
1271 }
1272 continue
1273
1274 case S.PROC_INST_ENDING:
1275 if (c === '>') {
1276 emitNode(parser, 'onprocessinginstruction', {
1277 name: parser.procInstName,
1278 body: parser.procInstBody
1279 })
1280 parser.procInstName = parser.procInstBody = ''
1281 parser.state = S.TEXT
1282 } else {
1283 parser.procInstBody += '?' + c
1284 parser.state = S.PROC_INST_BODY
1285 }
1286 continue
1287
1288 case S.OPEN_TAG:
1289 if (isMatch(nameBody, c)) {
1290 parser.tagName += c
1291 } else {
1292 newTag(parser)
1293 if (c === '>') {
1294 openTag(parser)
1295 } else if (c === '/') {
1296 parser.state = S.OPEN_TAG_SLASH
1297 } else {
1298 if (not(whitespace, c)) {
1299 strictFail(parser, 'Invalid character in tag name')
1300 }
1301 parser.state = S.ATTRIB
1302 }
1303 }
1304 continue
1305
1306 case S.OPEN_TAG_SLASH:
1307 if (c === '>') {
1308 openTag(parser, true)
1309 closeTag(parser)
1310 } else {
1311 strictFail(parser, 'Forward-slash in opening tag not followed by >')
1312 parser.state = S.ATTRIB
1313 }
1314 continue
1315
1316 case S.ATTRIB:
1317 // haven't read the attribute name yet.
1318 if (is(whitespace, c)) {
1319 continue
1320 } else if (c === '>') {
1321 openTag(parser)
1322 } else if (c === '/') {
1323 parser.state = S.OPEN_TAG_SLASH
1324 } else if (isMatch(nameStart, c)) {
1325 parser.attribName = c
1326 parser.attribValue = ''
1327 parser.state = S.ATTRIB_NAME
1328 } else {
1329 strictFail(parser, 'Invalid attribute name')
1330 }
1331 continue
1332
1333 case S.ATTRIB_NAME:
1334 if (c === '=') {
1335 parser.state = S.ATTRIB_VALUE
1336 } else if (c === '>') {
1337 strictFail(parser, 'Attribute without value')
1338 parser.attribValue = parser.attribName
1339 attrib(parser)
1340 openTag(parser)
1341 } else if (is(whitespace, c)) {
1342 parser.state = S.ATTRIB_NAME_SAW_WHITE
1343 } else if (isMatch(nameBody, c)) {
1344 parser.attribName += c
1345 } else {
1346 strictFail(parser, 'Invalid attribute name')
1347 }
1348 continue
1349
1350 case S.ATTRIB_NAME_SAW_WHITE:
1351 if (c === '=') {
1352 parser.state = S.ATTRIB_VALUE
1353 } else if (is(whitespace, c)) {
1354 continue
1355 } else {
1356 strictFail(parser, 'Attribute without value')
1357 parser.tag.attributes[parser.attribName] = ''
1358 parser.attribValue = ''
1359 emitNode(parser, 'onattribute', {
1360 name: parser.attribName,
1361 value: ''
1362 })
1363 parser.attribName = ''
1364 if (c === '>') {
1365 openTag(parser)
1366 } else if (isMatch(nameStart, c)) {
1367 parser.attribName = c
1368 parser.state = S.ATTRIB_NAME
1369 } else {
1370 strictFail(parser, 'Invalid attribute name')
1371 parser.state = S.ATTRIB
1372 }
1373 }
1374 continue
1375
1376 case S.ATTRIB_VALUE:
1377 if (is(whitespace, c)) {
1378 continue
1379 } else if (is(quote, c)) {
1380 parser.q = c
1381 parser.state = S.ATTRIB_VALUE_QUOTED
1382 } else {
1383 strictFail(parser, 'Unquoted attribute value')
1384 parser.state = S.ATTRIB_VALUE_UNQUOTED
1385 parser.attribValue = c
1386 }
1387 continue
1388
1389 case S.ATTRIB_VALUE_QUOTED:
1390 if (c !== parser.q) {
1391 if (c === '&') {
1392 parser.state = S.ATTRIB_VALUE_ENTITY_Q
1393 } else {
1394 parser.attribValue += c
1395 }
1396 continue
1397 }
1398 attrib(parser)
1399 parser.q = ''
1400 parser.state = S.ATTRIB_VALUE_CLOSED
1401 continue
1402
1403 case S.ATTRIB_VALUE_CLOSED:
1404 if (is(whitespace, c)) {
1405 parser.state = S.ATTRIB
1406 } else if (c === '>') {
1407 openTag(parser)
1408 } else if (c === '/') {
1409 parser.state = S.OPEN_TAG_SLASH
1410 } else if (isMatch(nameStart, c)) {
1411 strictFail(parser, 'No whitespace between attributes')
1412 parser.attribName = c
1413 parser.attribValue = ''
1414 parser.state = S.ATTRIB_NAME
1415 } else {
1416 strictFail(parser, 'Invalid attribute name')
1417 }
1418 continue
1419
1420 case S.ATTRIB_VALUE_UNQUOTED:
1421 if (not(attribEnd, c)) {
1422 if (c === '&') {
1423 parser.state = S.ATTRIB_VALUE_ENTITY_U
1424 } else {
1425 parser.attribValue += c
1426 }
1427 continue
1428 }
1429 attrib(parser)
1430 if (c === '>') {
1431 openTag(parser)
1432 } else {
1433 parser.state = S.ATTRIB
1434 }
1435 continue
1436
1437 case S.CLOSE_TAG:
1438 if (!parser.tagName) {
1439 if (is(whitespace, c)) {
1440 continue
1441 } else if (notMatch(nameStart, c)) {
1442 if (parser.script) {
1443 parser.script += '</' + c
1444 parser.state = S.SCRIPT
1445 } else {
1446 strictFail(parser, 'Invalid tagname in closing tag.')
1447 }
1448 } else {
1449 parser.tagName = c
1450 }
1451 } else if (c === '>') {
1452 closeTag(parser)
1453 } else if (isMatch(nameBody, c)) {
1454 parser.tagName += c
1455 } else if (parser.script) {
1456 parser.script += '</' + parser.tagName
1457 parser.tagName = ''
1458 parser.state = S.SCRIPT
1459 } else {
1460 if (not(whitespace, c)) {
1461 strictFail(parser, 'Invalid tagname in closing tag')
1462 }
1463 parser.state = S.CLOSE_TAG_SAW_WHITE
1464 }
1465 continue
1466
1467 case S.CLOSE_TAG_SAW_WHITE:
1468 if (is(whitespace, c)) {
1469 continue
1470 }
1471 if (c === '>') {
1472 closeTag(parser)
1473 } else {
1474 strictFail(parser, 'Invalid characters in closing tag')
1475 }
1476 continue
1477
1478 case S.TEXT_ENTITY:
1479 case S.ATTRIB_VALUE_ENTITY_Q:
1480 case S.ATTRIB_VALUE_ENTITY_U:
1481 var returnState
1482 var buffer
1483 switch (parser.state) {
1484 case S.TEXT_ENTITY:
1485 returnState = S.TEXT
1486 buffer = 'textNode'
1487 break
1488
1489 case S.ATTRIB_VALUE_ENTITY_Q:
1490 returnState = S.ATTRIB_VALUE_QUOTED
1491 buffer = 'attribValue'
1492 break
1493
1494 case S.ATTRIB_VALUE_ENTITY_U:
1495 returnState = S.ATTRIB_VALUE_UNQUOTED
1496 buffer = 'attribValue'
1497 break
1498 }
1499
1500 if (c === ';') {
1501 parser[buffer] += parseEntity(parser)
1502 parser.entity = ''
1503 parser.state = returnState
1504 } else if (isMatch(parser.entity.length ? entityBody : entityStart, c)) {
1505 parser.entity += c
1506 } else {
1507 strictFail(parser, 'Invalid character in entity name')
1508 parser[buffer] += '&' + parser.entity + c
1509 parser.entity = ''
1510 parser.state = returnState
1511 }
1512
1513 continue
1514
1515 default:
1516 throw new Error(parser, 'Unknown state: ' + parser.state)
1517 }
1518 } // while
1519
1520 if (parser.position >= parser.bufferCheckPosition) {
1521 checkBufferLength(parser)
1522 }
1523 return parser
1524 }
1525
1526 /*! http://mths.be/fromcodepoint v0.1.0 by @mathias */
1527 /* istanbul ignore next */
1528 if (!String.fromCodePoint) {
1529 (function () {
1530 var stringFromCharCode = String.fromCharCode
1531 var floor = Math.floor
1532 var fromCodePoint = function () {
1533 var MAX_SIZE = 0x4000
1534 var codeUnits = []
1535 var highSurrogate
1536 var lowSurrogate
1537 var index = -1
1538 var length = arguments.length
1539 if (!length) {
1540 return ''
1541 }
1542 var result = ''
1543 while (++index < length) {
1544 var codePoint = Number(arguments[index])
1545 if (
1546 !isFinite(codePoint) || // `NaN`, `+Infinity`, or `-Infinity`
1547 codePoint < 0 || // not a valid Unicode code point
1548 codePoint > 0x10FFFF || // not a valid Unicode code point
1549 floor(codePoint) !== codePoint // not an integer
1550 ) {
1551 throw RangeError('Invalid code point: ' + codePoint)
1552 }
1553 if (codePoint <= 0xFFFF) { // BMP code point
1554 codeUnits.push(codePoint)
1555 } else { // Astral code point; split in surrogate halves
1556 // http://mathiasbynens.be/notes/javascript-encoding#surrogate-formulae
1557 codePoint -= 0x10000
1558 highSurrogate = (codePoint >> 10) + 0xD800
1559 lowSurrogate = (codePoint % 0x400) + 0xDC00
1560 codeUnits.push(highSurrogate, lowSurrogate)
1561 }
1562 if (index + 1 === length || codeUnits.length > MAX_SIZE) {
1563 result += stringFromCharCode.apply(null, codeUnits)
1564 codeUnits.length = 0
1565 }
1566 }
1567 return result
1568 }
1569 /* istanbul ignore next */
1570 if (Object.defineProperty) {
1571 Object.defineProperty(String, 'fromCodePoint', {
1572 value: fromCodePoint,
1573 configurable: true,
1574 writable: true
1575 })
1576 } else {
1577 String.fromCodePoint = fromCodePoint
1578 }
1579 }())
1580 }
1581})(typeof exports === 'undefined' ? this.sax = {} : exports)