No puede seleccionar más de 25 temas Los temas deben comenzar con una letra o número, pueden incluir guiones ('-') y pueden tener hasta 35 caracteres de largo.

tokenizer.js 71 KiB

hace 3 años
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317
  1. 'use strict';
  2. var Preprocessor = require('./preprocessor'),
  3. LocationInfoMixin = require('./location_info_mixin'),
  4. UNICODE = require('../common/unicode'),
  5. NAMED_ENTITY_TRIE = require('./named_entity_trie');
  6. //Aliases
  7. var $ = UNICODE.CODE_POINTS,
  8. $$ = UNICODE.CODE_POINT_SEQUENCES;
  9. //Replacement code points for numeric entities
  10. var NUMERIC_ENTITY_REPLACEMENTS = {
  11. 0x00: 0xFFFD, 0x0D: 0x000D, 0x80: 0x20AC, 0x81: 0x0081, 0x82: 0x201A, 0x83: 0x0192, 0x84: 0x201E,
  12. 0x85: 0x2026, 0x86: 0x2020, 0x87: 0x2021, 0x88: 0x02C6, 0x89: 0x2030, 0x8A: 0x0160, 0x8B: 0x2039,
  13. 0x8C: 0x0152, 0x8D: 0x008D, 0x8E: 0x017D, 0x8F: 0x008F, 0x90: 0x0090, 0x91: 0x2018, 0x92: 0x2019,
  14. 0x93: 0x201C, 0x94: 0x201D, 0x95: 0x2022, 0x96: 0x2013, 0x97: 0x2014, 0x98: 0x02DC, 0x99: 0x2122,
  15. 0x9A: 0x0161, 0x9B: 0x203A, 0x9C: 0x0153, 0x9D: 0x009D, 0x9E: 0x017E, 0x9F: 0x0178
  16. };
  17. //States
  18. var DATA_STATE = 'DATA_STATE',
  19. CHARACTER_REFERENCE_IN_DATA_STATE = 'CHARACTER_REFERENCE_IN_DATA_STATE',
  20. RCDATA_STATE = 'RCDATA_STATE',
  21. CHARACTER_REFERENCE_IN_RCDATA_STATE = 'CHARACTER_REFERENCE_IN_RCDATA_STATE',
  22. RAWTEXT_STATE = 'RAWTEXT_STATE',
  23. SCRIPT_DATA_STATE = 'SCRIPT_DATA_STATE',
  24. PLAINTEXT_STATE = 'PLAINTEXT_STATE',
  25. TAG_OPEN_STATE = 'TAG_OPEN_STATE',
  26. END_TAG_OPEN_STATE = 'END_TAG_OPEN_STATE',
  27. TAG_NAME_STATE = 'TAG_NAME_STATE',
  28. RCDATA_LESS_THAN_SIGN_STATE = 'RCDATA_LESS_THAN_SIGN_STATE',
  29. RCDATA_END_TAG_OPEN_STATE = 'RCDATA_END_TAG_OPEN_STATE',
  30. RCDATA_END_TAG_NAME_STATE = 'RCDATA_END_TAG_NAME_STATE',
  31. RAWTEXT_LESS_THAN_SIGN_STATE = 'RAWTEXT_LESS_THAN_SIGN_STATE',
  32. RAWTEXT_END_TAG_OPEN_STATE = 'RAWTEXT_END_TAG_OPEN_STATE',
  33. RAWTEXT_END_TAG_NAME_STATE = 'RAWTEXT_END_TAG_NAME_STATE',
  34. SCRIPT_DATA_LESS_THAN_SIGN_STATE = 'SCRIPT_DATA_LESS_THAN_SIGN_STATE',
  35. SCRIPT_DATA_END_TAG_OPEN_STATE = 'SCRIPT_DATA_END_TAG_OPEN_STATE',
  36. SCRIPT_DATA_END_TAG_NAME_STATE = 'SCRIPT_DATA_END_TAG_NAME_STATE',
  37. SCRIPT_DATA_ESCAPE_START_STATE = 'SCRIPT_DATA_ESCAPE_START_STATE',
  38. SCRIPT_DATA_ESCAPE_START_DASH_STATE = 'SCRIPT_DATA_ESCAPE_START_DASH_STATE',
  39. SCRIPT_DATA_ESCAPED_STATE = 'SCRIPT_DATA_ESCAPED_STATE',
  40. SCRIPT_DATA_ESCAPED_DASH_STATE = 'SCRIPT_DATA_ESCAPED_DASH_STATE',
  41. SCRIPT_DATA_ESCAPED_DASH_DASH_STATE = 'SCRIPT_DATA_ESCAPED_DASH_DASH_STATE',
  42. SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN_STATE = 'SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN_STATE',
  43. SCRIPT_DATA_ESCAPED_END_TAG_OPEN_STATE = 'SCRIPT_DATA_ESCAPED_END_TAG_OPEN_STATE',
  44. SCRIPT_DATA_ESCAPED_END_TAG_NAME_STATE = 'SCRIPT_DATA_ESCAPED_END_TAG_NAME_STATE',
  45. SCRIPT_DATA_DOUBLE_ESCAPE_START_STATE = 'SCRIPT_DATA_DOUBLE_ESCAPE_START_STATE',
  46. SCRIPT_DATA_DOUBLE_ESCAPED_STATE = 'SCRIPT_DATA_DOUBLE_ESCAPED_STATE',
  47. SCRIPT_DATA_DOUBLE_ESCAPED_DASH_STATE = 'SCRIPT_DATA_DOUBLE_ESCAPED_DASH_STATE',
  48. SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH_STATE = 'SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH_STATE',
  49. SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN_STATE = 'SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN_STATE',
  50. SCRIPT_DATA_DOUBLE_ESCAPE_END_STATE = 'SCRIPT_DATA_DOUBLE_ESCAPE_END_STATE',
  51. BEFORE_ATTRIBUTE_NAME_STATE = 'BEFORE_ATTRIBUTE_NAME_STATE',
  52. ATTRIBUTE_NAME_STATE = 'ATTRIBUTE_NAME_STATE',
  53. AFTER_ATTRIBUTE_NAME_STATE = 'AFTER_ATTRIBUTE_NAME_STATE',
  54. BEFORE_ATTRIBUTE_VALUE_STATE = 'BEFORE_ATTRIBUTE_VALUE_STATE',
  55. ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE = 'ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE',
  56. ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE = 'ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE',
  57. ATTRIBUTE_VALUE_UNQUOTED_STATE = 'ATTRIBUTE_VALUE_UNQUOTED_STATE',
  58. CHARACTER_REFERENCE_IN_ATTRIBUTE_VALUE_STATE = 'CHARACTER_REFERENCE_IN_ATTRIBUTE_VALUE_STATE',
  59. AFTER_ATTRIBUTE_VALUE_QUOTED_STATE = 'AFTER_ATTRIBUTE_VALUE_QUOTED_STATE',
  60. SELF_CLOSING_START_TAG_STATE = 'SELF_CLOSING_START_TAG_STATE',
  61. BOGUS_COMMENT_STATE = 'BOGUS_COMMENT_STATE',
  62. MARKUP_DECLARATION_OPEN_STATE = 'MARKUP_DECLARATION_OPEN_STATE',
  63. COMMENT_START_STATE = 'COMMENT_START_STATE',
  64. COMMENT_START_DASH_STATE = 'COMMENT_START_DASH_STATE',
  65. COMMENT_STATE = 'COMMENT_STATE',
  66. COMMENT_END_DASH_STATE = 'COMMENT_END_DASH_STATE',
  67. COMMENT_END_STATE = 'COMMENT_END_STATE',
  68. COMMENT_END_BANG_STATE = 'COMMENT_END_BANG_STATE',
  69. DOCTYPE_STATE = 'DOCTYPE_STATE',
  70. BEFORE_DOCTYPE_NAME_STATE = 'BEFORE_DOCTYPE_NAME_STATE',
  71. DOCTYPE_NAME_STATE = 'DOCTYPE_NAME_STATE',
  72. AFTER_DOCTYPE_NAME_STATE = 'AFTER_DOCTYPE_NAME_STATE',
  73. AFTER_DOCTYPE_PUBLIC_KEYWORD_STATE = 'AFTER_DOCTYPE_PUBLIC_KEYWORD_STATE',
  74. BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE = 'BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE',
  75. DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE = 'DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE',
  76. DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE = 'DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE',
  77. AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE = 'AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE',
  78. BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS_STATE = 'BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS_STATE',
  79. AFTER_DOCTYPE_SYSTEM_KEYWORD_STATE = 'AFTER_DOCTYPE_SYSTEM_KEYWORD_STATE',
  80. BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE = 'BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE',
  81. DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE = 'DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE',
  82. DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE = 'DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE',
  83. AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE = 'AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE',
  84. BOGUS_DOCTYPE_STATE = 'BOGUS_DOCTYPE_STATE',
  85. CDATA_SECTION_STATE = 'CDATA_SECTION_STATE';
  86. //Utils
  87. //OPTIMIZATION: these utility functions should not be moved out of this module. V8 Crankshaft will not inline
  88. //this functions if they will be situated in another module due to context switch.
  89. //Always perform inlining check before modifying this functions ('node --trace-inlining').
  90. function isWhitespace(cp) {
  91. return cp === $.SPACE || cp === $.LINE_FEED || cp === $.TABULATION || cp === $.FORM_FEED;
  92. }
  93. function isAsciiDigit(cp) {
  94. return cp >= $.DIGIT_0 && cp <= $.DIGIT_9;
  95. }
  96. function isAsciiUpper(cp) {
  97. return cp >= $.LATIN_CAPITAL_A && cp <= $.LATIN_CAPITAL_Z;
  98. }
  99. function isAsciiLower(cp) {
  100. return cp >= $.LATIN_SMALL_A && cp <= $.LATIN_SMALL_Z;
  101. }
  102. function isAsciiAlphaNumeric(cp) {
  103. return isAsciiDigit(cp) || isAsciiUpper(cp) || isAsciiLower(cp);
  104. }
  105. function isDigit(cp, isHex) {
  106. return isAsciiDigit(cp) || (isHex && ((cp >= $.LATIN_CAPITAL_A && cp <= $.LATIN_CAPITAL_F) ||
  107. (cp >= $.LATIN_SMALL_A && cp <= $.LATIN_SMALL_F)));
  108. }
  109. function isReservedCodePoint(cp) {
  110. return cp >= 0xD800 && cp <= 0xDFFF || cp > 0x10FFFF;
  111. }
  112. function toAsciiLowerCodePoint(cp) {
  113. return cp + 0x0020;
  114. }
  115. //NOTE: String.fromCharCode() function can handle only characters from BMP subset.
  116. //So, we need to workaround this manually.
  117. //(see: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/String/fromCharCode#Getting_it_to_work_with_higher_values)
  118. function toChar(cp) {
  119. if (cp <= 0xFFFF)
  120. return String.fromCharCode(cp);
  121. cp -= 0x10000;
  122. return String.fromCharCode(cp >>> 10 & 0x3FF | 0xD800) + String.fromCharCode(0xDC00 | cp & 0x3FF);
  123. }
  124. function toAsciiLowerChar(cp) {
  125. return String.fromCharCode(toAsciiLowerCodePoint(cp));
  126. }
  127. //Tokenizer
  128. var Tokenizer = module.exports = function (html, options) {
  129. this.disableEntitiesDecoding = false;
  130. this.preprocessor = new Preprocessor(html);
  131. this.tokenQueue = [];
  132. this.allowCDATA = false;
  133. this.state = DATA_STATE;
  134. this.returnState = '';
  135. this.consumptionPos = 0;
  136. this.tempBuff = [];
  137. this.additionalAllowedCp = void 0;
  138. this.lastStartTagName = '';
  139. this.currentCharacterToken = null;
  140. this.currentToken = null;
  141. this.currentAttr = null;
  142. if (options) {
  143. this.disableEntitiesDecoding = !options.decodeHtmlEntities;
  144. if (options.locationInfo)
  145. LocationInfoMixin.assign(this);
  146. }
  147. };
  148. //Token types
  149. Tokenizer.CHARACTER_TOKEN = 'CHARACTER_TOKEN';
  150. Tokenizer.NULL_CHARACTER_TOKEN = 'NULL_CHARACTER_TOKEN';
  151. Tokenizer.WHITESPACE_CHARACTER_TOKEN = 'WHITESPACE_CHARACTER_TOKEN';
  152. Tokenizer.START_TAG_TOKEN = 'START_TAG_TOKEN';
  153. Tokenizer.END_TAG_TOKEN = 'END_TAG_TOKEN';
  154. Tokenizer.COMMENT_TOKEN = 'COMMENT_TOKEN';
  155. Tokenizer.DOCTYPE_TOKEN = 'DOCTYPE_TOKEN';
  156. Tokenizer.EOF_TOKEN = 'EOF_TOKEN';
  157. //Tokenizer initial states for different modes
  158. Tokenizer.MODE = Tokenizer.prototype.MODE = {
  159. DATA: DATA_STATE,
  160. RCDATA: RCDATA_STATE,
  161. RAWTEXT: RAWTEXT_STATE,
  162. SCRIPT_DATA: SCRIPT_DATA_STATE,
  163. PLAINTEXT: PLAINTEXT_STATE
  164. };
  165. //Static
  166. Tokenizer.getTokenAttr = function (token, attrName) {
  167. for (var i = token.attrs.length - 1; i >= 0; i--) {
  168. if (token.attrs[i].name === attrName)
  169. return token.attrs[i].value;
  170. }
  171. return null;
  172. };
  173. //Get token
  174. Tokenizer.prototype.getNextToken = function () {
  175. while (!this.tokenQueue.length)
  176. this[this.state](this._consume());
  177. return this.tokenQueue.shift();
  178. };
  179. //Consumption
  180. Tokenizer.prototype._consume = function () {
  181. this.consumptionPos++;
  182. return this.preprocessor.advanceAndPeekCodePoint();
  183. };
  184. Tokenizer.prototype._unconsume = function () {
  185. this.consumptionPos--;
  186. this.preprocessor.retreat();
  187. };
  188. Tokenizer.prototype._unconsumeSeveral = function (count) {
  189. while (count--)
  190. this._unconsume();
  191. };
  192. Tokenizer.prototype._reconsumeInState = function (state) {
  193. this.state = state;
  194. this._unconsume();
  195. };
  196. Tokenizer.prototype._consumeSubsequentIfMatch = function (pattern, startCp, caseSensitive) {
  197. var rollbackPos = this.consumptionPos,
  198. isMatch = true,
  199. patternLength = pattern.length,
  200. patternPos = 0,
  201. cp = startCp,
  202. patternCp = void 0;
  203. for (; patternPos < patternLength; patternPos++) {
  204. if (patternPos > 0)
  205. cp = this._consume();
  206. if (cp === $.EOF) {
  207. isMatch = false;
  208. break;
  209. }
  210. patternCp = pattern[patternPos];
  211. if (cp !== patternCp && (caseSensitive || cp !== toAsciiLowerCodePoint(patternCp))) {
  212. isMatch = false;
  213. break;
  214. }
  215. }
  216. if (!isMatch)
  217. this._unconsumeSeveral(this.consumptionPos - rollbackPos);
  218. return isMatch;
  219. };
  220. //Lookahead
  221. Tokenizer.prototype._lookahead = function () {
  222. var cp = this.preprocessor.advanceAndPeekCodePoint();
  223. this.preprocessor.retreat();
  224. return cp;
  225. };
  226. //Temp buffer
  227. Tokenizer.prototype.isTempBufferEqualToScriptString = function () {
  228. if (this.tempBuff.length !== $$.SCRIPT_STRING.length)
  229. return false;
  230. for (var i = 0; i < this.tempBuff.length; i++) {
  231. if (this.tempBuff[i] !== $$.SCRIPT_STRING[i])
  232. return false;
  233. }
  234. return true;
  235. };
  236. //Token creation
  237. Tokenizer.prototype.buildStartTagToken = function (tagName) {
  238. return {
  239. type: Tokenizer.START_TAG_TOKEN,
  240. tagName: tagName,
  241. selfClosing: false,
  242. attrs: []
  243. };
  244. };
  245. Tokenizer.prototype.buildEndTagToken = function (tagName) {
  246. return {
  247. type: Tokenizer.END_TAG_TOKEN,
  248. tagName: tagName,
  249. ignored: false,
  250. attrs: []
  251. };
  252. };
  253. Tokenizer.prototype._createStartTagToken = function (tagNameFirstCh) {
  254. this.currentToken = this.buildStartTagToken(tagNameFirstCh);
  255. };
  256. Tokenizer.prototype._createEndTagToken = function (tagNameFirstCh) {
  257. this.currentToken = this.buildEndTagToken(tagNameFirstCh);
  258. };
  259. Tokenizer.prototype._createCommentToken = function () {
  260. this.currentToken = {
  261. type: Tokenizer.COMMENT_TOKEN,
  262. data: ''
  263. };
  264. };
  265. Tokenizer.prototype._createDoctypeToken = function (doctypeNameFirstCh) {
  266. this.currentToken = {
  267. type: Tokenizer.DOCTYPE_TOKEN,
  268. name: doctypeNameFirstCh || '',
  269. forceQuirks: false,
  270. publicId: null,
  271. systemId: null
  272. };
  273. };
  274. Tokenizer.prototype._createCharacterToken = function (type, ch) {
  275. this.currentCharacterToken = {
  276. type: type,
  277. chars: ch
  278. };
  279. };
  280. //Tag attributes
  281. Tokenizer.prototype._createAttr = function (attrNameFirstCh) {
  282. this.currentAttr = {
  283. name: attrNameFirstCh,
  284. value: ''
  285. };
  286. };
  287. Tokenizer.prototype._isDuplicateAttr = function () {
  288. return Tokenizer.getTokenAttr(this.currentToken, this.currentAttr.name) !== null;
  289. };
  290. Tokenizer.prototype._leaveAttrName = function (toState) {
  291. this.state = toState;
  292. if (!this._isDuplicateAttr())
  293. this.currentToken.attrs.push(this.currentAttr);
  294. };
  295. //Appropriate end tag token
  296. //(see: http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#appropriate-end-tag-token)
  297. Tokenizer.prototype._isAppropriateEndTagToken = function () {
  298. return this.lastStartTagName === this.currentToken.tagName;
  299. };
  300. //Token emission
  301. Tokenizer.prototype._emitCurrentToken = function () {
  302. this._emitCurrentCharacterToken();
  303. //NOTE: store emited start tag's tagName to determine is the following end tag token is appropriate.
  304. if (this.currentToken.type === Tokenizer.START_TAG_TOKEN)
  305. this.lastStartTagName = this.currentToken.tagName;
  306. this.tokenQueue.push(this.currentToken);
  307. this.currentToken = null;
  308. };
  309. Tokenizer.prototype._emitCurrentCharacterToken = function () {
  310. if (this.currentCharacterToken) {
  311. this.tokenQueue.push(this.currentCharacterToken);
  312. this.currentCharacterToken = null;
  313. }
  314. };
  315. Tokenizer.prototype._emitEOFToken = function () {
  316. this._emitCurrentCharacterToken();
  317. this.tokenQueue.push({type: Tokenizer.EOF_TOKEN});
  318. };
  319. //Characters emission
  320. //OPTIMIZATION: specification uses only one type of character tokens (one token per character).
  321. //This causes a huge memory overhead and a lot of unnecessary parser loops. parse5 uses 3 groups of characters.
  322. //If we have a sequence of characters that belong to the same group, parser can process it
  323. //as a single solid character token.
  324. //So, there are 3 types of character tokens in parse5:
  325. //1)NULL_CHARACTER_TOKEN - \u0000-character sequences (e.g. '\u0000\u0000\u0000')
  326. //2)WHITESPACE_CHARACTER_TOKEN - any whitespace/new-line character sequences (e.g. '\n \r\t \f')
  327. //3)CHARACTER_TOKEN - any character sequence which don't belong to groups 1 and 2 (e.g. 'abcdef1234@@#$%^')
  328. Tokenizer.prototype._appendCharToCurrentCharacterToken = function (type, ch) {
  329. if (this.currentCharacterToken && this.currentCharacterToken.type !== type)
  330. this._emitCurrentCharacterToken();
  331. if (this.currentCharacterToken)
  332. this.currentCharacterToken.chars += ch;
  333. else
  334. this._createCharacterToken(type, ch);
  335. };
  336. Tokenizer.prototype._emitCodePoint = function (cp) {
  337. var type = Tokenizer.CHARACTER_TOKEN;
  338. if (isWhitespace(cp))
  339. type = Tokenizer.WHITESPACE_CHARACTER_TOKEN;
  340. else if (cp === $.NULL)
  341. type = Tokenizer.NULL_CHARACTER_TOKEN;
  342. this._appendCharToCurrentCharacterToken(type, toChar(cp));
  343. };
  344. Tokenizer.prototype._emitSeveralCodePoints = function (codePoints) {
  345. for (var i = 0; i < codePoints.length; i++)
  346. this._emitCodePoint(codePoints[i]);
  347. };
  348. //NOTE: used then we emit character explicitly. This is always a non-whitespace and a non-null character.
  349. //So we can avoid additional checks here.
  350. Tokenizer.prototype._emitChar = function (ch) {
  351. this._appendCharToCurrentCharacterToken(Tokenizer.CHARACTER_TOKEN, ch);
  352. };
  353. //Character reference tokenization
  354. Tokenizer.prototype._consumeNumericEntity = function (isHex) {
  355. var digits = '',
  356. nextCp = void 0;
  357. do {
  358. digits += toChar(this._consume());
  359. nextCp = this._lookahead();
  360. } while (nextCp !== $.EOF && isDigit(nextCp, isHex));
  361. if (this._lookahead() === $.SEMICOLON)
  362. this._consume();
  363. var referencedCp = parseInt(digits, isHex ? 16 : 10),
  364. replacement = NUMERIC_ENTITY_REPLACEMENTS[referencedCp];
  365. if (replacement)
  366. return replacement;
  367. if (isReservedCodePoint(referencedCp))
  368. return $.REPLACEMENT_CHARACTER;
  369. return referencedCp;
  370. };
  371. Tokenizer.prototype._consumeNamedEntity = function (startCp, inAttr) {
  372. var referencedCodePoints = null,
  373. entityCodePointsCount = 0,
  374. cp = startCp,
  375. leaf = NAMED_ENTITY_TRIE[cp],
  376. consumedCount = 1,
  377. semicolonTerminated = false;
  378. for (; leaf && cp !== $.EOF; cp = this._consume(), consumedCount++, leaf = leaf.l && leaf.l[cp]) {
  379. if (leaf.c) {
  380. //NOTE: we have at least one named reference match. But we don't stop lookup at this point,
  381. //because longer matches still can be found (e.g. '&not' and '&notin;') except the case
  382. //then found match is terminated by semicolon.
  383. referencedCodePoints = leaf.c;
  384. entityCodePointsCount = consumedCount;
  385. if (cp === $.SEMICOLON) {
  386. semicolonTerminated = true;
  387. break;
  388. }
  389. }
  390. }
  391. if (referencedCodePoints) {
  392. if (!semicolonTerminated) {
  393. //NOTE: unconsume excess (e.g. 'it' in '&notit')
  394. this._unconsumeSeveral(consumedCount - entityCodePointsCount);
  395. //NOTE: If the character reference is being consumed as part of an attribute and the next character
  396. //is either a U+003D EQUALS SIGN character (=) or an alphanumeric ASCII character, then, for historical
  397. //reasons, all the characters that were matched after the U+0026 AMPERSAND character (&) must be
  398. //unconsumed, and nothing is returned.
  399. //However, if this next character is in fact a U+003D EQUALS SIGN character (=), then this is a
  400. //parse error, because some legacy user agents will misinterpret the markup in those cases.
  401. //(see: http://www.whatwg.org/specs/web-apps/current-work/multipage/tokenization.html#tokenizing-character-references)
  402. if (inAttr) {
  403. var nextCp = this._lookahead();
  404. if (nextCp === $.EQUALS_SIGN || isAsciiAlphaNumeric(nextCp)) {
  405. this._unconsumeSeveral(entityCodePointsCount);
  406. return null;
  407. }
  408. }
  409. }
  410. return referencedCodePoints;
  411. }
  412. this._unconsumeSeveral(consumedCount);
  413. return null;
  414. };
  415. Tokenizer.prototype._consumeCharacterReference = function (startCp, inAttr) {
  416. if (this.disableEntitiesDecoding || isWhitespace(startCp) || startCp === $.GREATER_THAN_SIGN ||
  417. startCp === $.AMPERSAND || startCp === this.additionalAllowedCp || startCp === $.EOF) {
  418. //NOTE: not a character reference. No characters are consumed, and nothing is returned.
  419. this._unconsume();
  420. return null;
  421. }
  422. else if (startCp === $.NUMBER_SIGN) {
  423. //NOTE: we have a numeric entity candidate, now we should determine if it's hex or decimal
  424. var isHex = false,
  425. nextCp = this._lookahead();
  426. if (nextCp === $.LATIN_SMALL_X || nextCp === $.LATIN_CAPITAL_X) {
  427. this._consume();
  428. isHex = true;
  429. }
  430. nextCp = this._lookahead();
  431. //NOTE: if we have at least one digit this is a numeric entity for sure, so we consume it
  432. if (nextCp !== $.EOF && isDigit(nextCp, isHex))
  433. return [this._consumeNumericEntity(isHex)];
  434. else {
  435. //NOTE: otherwise this is a bogus number entity and a parse error. Unconsume the number sign
  436. //and the 'x'-character if appropriate.
  437. this._unconsumeSeveral(isHex ? 2 : 1);
  438. return null;
  439. }
  440. }
  441. else
  442. return this._consumeNamedEntity(startCp, inAttr);
  443. };
  444. //State machine
  445. var _ = Tokenizer.prototype;
  446. //12.2.4.1 Data state
  447. //------------------------------------------------------------------
  448. _[DATA_STATE] = function dataState(cp) {
  449. if (cp === $.AMPERSAND)
  450. this.state = CHARACTER_REFERENCE_IN_DATA_STATE;
  451. else if (cp === $.LESS_THAN_SIGN)
  452. this.state = TAG_OPEN_STATE;
  453. else if (cp === $.NULL)
  454. this._emitCodePoint(cp);
  455. else if (cp === $.EOF)
  456. this._emitEOFToken();
  457. else
  458. this._emitCodePoint(cp);
  459. };
  460. //12.2.4.2 Character reference in data state
  461. //------------------------------------------------------------------
  462. _[CHARACTER_REFERENCE_IN_DATA_STATE] = function characterReferenceInDataState(cp) {
  463. this.state = DATA_STATE;
  464. this.additionalAllowedCp = void 0;
  465. var referencedCodePoints = this._consumeCharacterReference(cp, false);
  466. if (referencedCodePoints)
  467. this._emitSeveralCodePoints(referencedCodePoints);
  468. else
  469. this._emitChar('&');
  470. };
  471. //12.2.4.3 RCDATA state
  472. //------------------------------------------------------------------
  473. _[RCDATA_STATE] = function rcdataState(cp) {
  474. if (cp === $.AMPERSAND)
  475. this.state = CHARACTER_REFERENCE_IN_RCDATA_STATE;
  476. else if (cp === $.LESS_THAN_SIGN)
  477. this.state = RCDATA_LESS_THAN_SIGN_STATE;
  478. else if (cp === $.NULL)
  479. this._emitChar(UNICODE.REPLACEMENT_CHARACTER);
  480. else if (cp === $.EOF)
  481. this._emitEOFToken();
  482. else
  483. this._emitCodePoint(cp);
  484. };
  485. //12.2.4.4 Character reference in RCDATA state
  486. //------------------------------------------------------------------
  487. _[CHARACTER_REFERENCE_IN_RCDATA_STATE] = function characterReferenceInRcdataState(cp) {
  488. this.state = RCDATA_STATE;
  489. this.additionalAllowedCp = void 0;
  490. var referencedCodePoints = this._consumeCharacterReference(cp, false);
  491. if (referencedCodePoints)
  492. this._emitSeveralCodePoints(referencedCodePoints);
  493. else
  494. this._emitChar('&');
  495. };
  496. //12.2.4.5 RAWTEXT state
  497. //------------------------------------------------------------------
  498. _[RAWTEXT_STATE] = function rawtextState(cp) {
  499. if (cp === $.LESS_THAN_SIGN)
  500. this.state = RAWTEXT_LESS_THAN_SIGN_STATE;
  501. else if (cp === $.NULL)
  502. this._emitChar(UNICODE.REPLACEMENT_CHARACTER);
  503. else if (cp === $.EOF)
  504. this._emitEOFToken();
  505. else
  506. this._emitCodePoint(cp);
  507. };
  508. //12.2.4.6 Script data state
  509. //------------------------------------------------------------------
  510. _[SCRIPT_DATA_STATE] = function scriptDataState(cp) {
  511. if (cp === $.LESS_THAN_SIGN)
  512. this.state = SCRIPT_DATA_LESS_THAN_SIGN_STATE;
  513. else if (cp === $.NULL)
  514. this._emitChar(UNICODE.REPLACEMENT_CHARACTER);
  515. else if (cp === $.EOF)
  516. this._emitEOFToken();
  517. else
  518. this._emitCodePoint(cp);
  519. };
  520. //12.2.4.7 PLAINTEXT state
  521. //------------------------------------------------------------------
  522. _[PLAINTEXT_STATE] = function plaintextState(cp) {
  523. if (cp === $.NULL)
  524. this._emitChar(UNICODE.REPLACEMENT_CHARACTER);
  525. else if (cp === $.EOF)
  526. this._emitEOFToken();
  527. else
  528. this._emitCodePoint(cp);
  529. };
  530. //12.2.4.8 Tag open state
  531. //------------------------------------------------------------------
  532. _[TAG_OPEN_STATE] = function tagOpenState(cp) {
  533. if (cp === $.EXCLAMATION_MARK)
  534. this.state = MARKUP_DECLARATION_OPEN_STATE;
  535. else if (cp === $.SOLIDUS)
  536. this.state = END_TAG_OPEN_STATE;
  537. else if (isAsciiUpper(cp)) {
  538. this._createStartTagToken(toAsciiLowerChar(cp));
  539. this.state = TAG_NAME_STATE;
  540. }
  541. else if (isAsciiLower(cp)) {
  542. this._createStartTagToken(toChar(cp));
  543. this.state = TAG_NAME_STATE;
  544. }
  545. else if (cp === $.QUESTION_MARK) {
  546. //NOTE: call bogus comment state directly with current consumed character to avoid unnecessary reconsumption.
  547. this[BOGUS_COMMENT_STATE](cp);
  548. }
  549. else {
  550. this._emitChar('<');
  551. this._reconsumeInState(DATA_STATE);
  552. }
  553. };
  554. //12.2.4.9 End tag open state
  555. //------------------------------------------------------------------
  556. _[END_TAG_OPEN_STATE] = function endTagOpenState(cp) {
  557. if (isAsciiUpper(cp)) {
  558. this._createEndTagToken(toAsciiLowerChar(cp));
  559. this.state = TAG_NAME_STATE;
  560. }
  561. else if (isAsciiLower(cp)) {
  562. this._createEndTagToken(toChar(cp));
  563. this.state = TAG_NAME_STATE;
  564. }
  565. else if (cp === $.GREATER_THAN_SIGN)
  566. this.state = DATA_STATE;
  567. else if (cp === $.EOF) {
  568. this._reconsumeInState(DATA_STATE);
  569. this._emitChar('<');
  570. this._emitChar('/');
  571. }
  572. else {
  573. //NOTE: call bogus comment state directly with current consumed character to avoid unnecessary reconsumption.
  574. this[BOGUS_COMMENT_STATE](cp);
  575. }
  576. };
  577. //12.2.4.10 Tag name state
  578. //------------------------------------------------------------------
  579. _[TAG_NAME_STATE] = function tagNameState(cp) {
  580. if (isWhitespace(cp))
  581. this.state = BEFORE_ATTRIBUTE_NAME_STATE;
  582. else if (cp === $.SOLIDUS)
  583. this.state = SELF_CLOSING_START_TAG_STATE;
  584. else if (cp === $.GREATER_THAN_SIGN) {
  585. this.state = DATA_STATE;
  586. this._emitCurrentToken();
  587. }
  588. else if (isAsciiUpper(cp))
  589. this.currentToken.tagName += toAsciiLowerChar(cp);
  590. else if (cp === $.NULL)
  591. this.currentToken.tagName += UNICODE.REPLACEMENT_CHARACTER;
  592. else if (cp === $.EOF)
  593. this._reconsumeInState(DATA_STATE);
  594. else
  595. this.currentToken.tagName += toChar(cp);
  596. };
  597. //12.2.4.11 RCDATA less-than sign state
  598. //------------------------------------------------------------------
  599. _[RCDATA_LESS_THAN_SIGN_STATE] = function rcdataLessThanSignState(cp) {
  600. if (cp === $.SOLIDUS) {
  601. this.tempBuff = [];
  602. this.state = RCDATA_END_TAG_OPEN_STATE;
  603. }
  604. else {
  605. this._emitChar('<');
  606. this._reconsumeInState(RCDATA_STATE);
  607. }
  608. };
  609. //12.2.4.12 RCDATA end tag open state
  610. //------------------------------------------------------------------
  611. _[RCDATA_END_TAG_OPEN_STATE] = function rcdataEndTagOpenState(cp) {
  612. if (isAsciiUpper(cp)) {
  613. this._createEndTagToken(toAsciiLowerChar(cp));
  614. this.tempBuff.push(cp);
  615. this.state = RCDATA_END_TAG_NAME_STATE;
  616. }
  617. else if (isAsciiLower(cp)) {
  618. this._createEndTagToken(toChar(cp));
  619. this.tempBuff.push(cp);
  620. this.state = RCDATA_END_TAG_NAME_STATE;
  621. }
  622. else {
  623. this._emitChar('<');
  624. this._emitChar('/');
  625. this._reconsumeInState(RCDATA_STATE);
  626. }
  627. };
  628. //12.2.4.13 RCDATA end tag name state
  629. //------------------------------------------------------------------
  630. _[RCDATA_END_TAG_NAME_STATE] = function rcdataEndTagNameState(cp) {
  631. if (isAsciiUpper(cp)) {
  632. this.currentToken.tagName += toAsciiLowerChar(cp);
  633. this.tempBuff.push(cp);
  634. }
  635. else if (isAsciiLower(cp)) {
  636. this.currentToken.tagName += toChar(cp);
  637. this.tempBuff.push(cp);
  638. }
  639. else {
  640. if (this._isAppropriateEndTagToken()) {
  641. if (isWhitespace(cp)) {
  642. this.state = BEFORE_ATTRIBUTE_NAME_STATE;
  643. return;
  644. }
  645. if (cp === $.SOLIDUS) {
  646. this.state = SELF_CLOSING_START_TAG_STATE;
  647. return;
  648. }
  649. if (cp === $.GREATER_THAN_SIGN) {
  650. this.state = DATA_STATE;
  651. this._emitCurrentToken();
  652. return;
  653. }
  654. }
  655. this._emitChar('<');
  656. this._emitChar('/');
  657. this._emitSeveralCodePoints(this.tempBuff);
  658. this._reconsumeInState(RCDATA_STATE);
  659. }
  660. };
  661. //12.2.4.14 RAWTEXT less-than sign state
  662. //------------------------------------------------------------------
  663. _[RAWTEXT_LESS_THAN_SIGN_STATE] = function rawtextLessThanSignState(cp) {
  664. if (cp === $.SOLIDUS) {
  665. this.tempBuff = [];
  666. this.state = RAWTEXT_END_TAG_OPEN_STATE;
  667. }
  668. else {
  669. this._emitChar('<');
  670. this._reconsumeInState(RAWTEXT_STATE);
  671. }
  672. };
  673. //12.2.4.15 RAWTEXT end tag open state
  674. //------------------------------------------------------------------
  675. _[RAWTEXT_END_TAG_OPEN_STATE] = function rawtextEndTagOpenState(cp) {
  676. if (isAsciiUpper(cp)) {
  677. this._createEndTagToken(toAsciiLowerChar(cp));
  678. this.tempBuff.push(cp);
  679. this.state = RAWTEXT_END_TAG_NAME_STATE;
  680. }
  681. else if (isAsciiLower(cp)) {
  682. this._createEndTagToken(toChar(cp));
  683. this.tempBuff.push(cp);
  684. this.state = RAWTEXT_END_TAG_NAME_STATE;
  685. }
  686. else {
  687. this._emitChar('<');
  688. this._emitChar('/');
  689. this._reconsumeInState(RAWTEXT_STATE);
  690. }
  691. };
  692. //12.2.4.16 RAWTEXT end tag name state
  693. //------------------------------------------------------------------
  694. _[RAWTEXT_END_TAG_NAME_STATE] = function rawtextEndTagNameState(cp) {
  695. if (isAsciiUpper(cp)) {
  696. this.currentToken.tagName += toAsciiLowerChar(cp);
  697. this.tempBuff.push(cp);
  698. }
  699. else if (isAsciiLower(cp)) {
  700. this.currentToken.tagName += toChar(cp);
  701. this.tempBuff.push(cp);
  702. }
  703. else {
  704. if (this._isAppropriateEndTagToken()) {
  705. if (isWhitespace(cp)) {
  706. this.state = BEFORE_ATTRIBUTE_NAME_STATE;
  707. return;
  708. }
  709. if (cp === $.SOLIDUS) {
  710. this.state = SELF_CLOSING_START_TAG_STATE;
  711. return;
  712. }
  713. if (cp === $.GREATER_THAN_SIGN) {
  714. this._emitCurrentToken();
  715. this.state = DATA_STATE;
  716. return;
  717. }
  718. }
  719. this._emitChar('<');
  720. this._emitChar('/');
  721. this._emitSeveralCodePoints(this.tempBuff);
  722. this._reconsumeInState(RAWTEXT_STATE);
  723. }
  724. };
  725. //12.2.4.17 Script data less-than sign state
  726. //------------------------------------------------------------------
  727. _[SCRIPT_DATA_LESS_THAN_SIGN_STATE] = function scriptDataLessThanSignState(cp) {
  728. if (cp === $.SOLIDUS) {
  729. this.tempBuff = [];
  730. this.state = SCRIPT_DATA_END_TAG_OPEN_STATE;
  731. }
  732. else if (cp === $.EXCLAMATION_MARK) {
  733. this.state = SCRIPT_DATA_ESCAPE_START_STATE;
  734. this._emitChar('<');
  735. this._emitChar('!');
  736. }
  737. else {
  738. this._emitChar('<');
  739. this._reconsumeInState(SCRIPT_DATA_STATE);
  740. }
  741. };
  742. //12.2.4.18 Script data end tag open state
  743. //------------------------------------------------------------------
  744. _[SCRIPT_DATA_END_TAG_OPEN_STATE] = function scriptDataEndTagOpenState(cp) {
  745. if (isAsciiUpper(cp)) {
  746. this._createEndTagToken(toAsciiLowerChar(cp));
  747. this.tempBuff.push(cp);
  748. this.state = SCRIPT_DATA_END_TAG_NAME_STATE;
  749. }
  750. else if (isAsciiLower(cp)) {
  751. this._createEndTagToken(toChar(cp));
  752. this.tempBuff.push(cp);
  753. this.state = SCRIPT_DATA_END_TAG_NAME_STATE;
  754. }
  755. else {
  756. this._emitChar('<');
  757. this._emitChar('/');
  758. this._reconsumeInState(SCRIPT_DATA_STATE);
  759. }
  760. };
  761. //12.2.4.19 Script data end tag name state
  762. //------------------------------------------------------------------
  763. _[SCRIPT_DATA_END_TAG_NAME_STATE] = function scriptDataEndTagNameState(cp) {
  764. if (isAsciiUpper(cp)) {
  765. this.currentToken.tagName += toAsciiLowerChar(cp);
  766. this.tempBuff.push(cp);
  767. }
  768. else if (isAsciiLower(cp)) {
  769. this.currentToken.tagName += toChar(cp);
  770. this.tempBuff.push(cp);
  771. }
  772. else {
  773. if (this._isAppropriateEndTagToken()) {
  774. if (isWhitespace(cp)) {
  775. this.state = BEFORE_ATTRIBUTE_NAME_STATE;
  776. return;
  777. }
  778. else if (cp === $.SOLIDUS) {
  779. this.state = SELF_CLOSING_START_TAG_STATE;
  780. return;
  781. }
  782. else if (cp === $.GREATER_THAN_SIGN) {
  783. this._emitCurrentToken();
  784. this.state = DATA_STATE;
  785. return;
  786. }
  787. }
  788. this._emitChar('<');
  789. this._emitChar('/');
  790. this._emitSeveralCodePoints(this.tempBuff);
  791. this._reconsumeInState(SCRIPT_DATA_STATE);
  792. }
  793. };
  794. //12.2.4.20 Script data escape start state
  795. //------------------------------------------------------------------
  796. _[SCRIPT_DATA_ESCAPE_START_STATE] = function scriptDataEscapeStartState(cp) {
  797. if (cp === $.HYPHEN_MINUS) {
  798. this.state = SCRIPT_DATA_ESCAPE_START_DASH_STATE;
  799. this._emitChar('-');
  800. }
  801. else
  802. this._reconsumeInState(SCRIPT_DATA_STATE);
  803. };
  804. //12.2.4.21 Script data escape start dash state
  805. //------------------------------------------------------------------
  806. _[SCRIPT_DATA_ESCAPE_START_DASH_STATE] = function scriptDataEscapeStartDashState(cp) {
  807. if (cp === $.HYPHEN_MINUS) {
  808. this.state = SCRIPT_DATA_ESCAPED_DASH_DASH_STATE;
  809. this._emitChar('-');
  810. }
  811. else
  812. this._reconsumeInState(SCRIPT_DATA_STATE);
  813. };
  814. //12.2.4.22 Script data escaped state
  815. //------------------------------------------------------------------
  816. _[SCRIPT_DATA_ESCAPED_STATE] = function scriptDataEscapedState(cp) {
  817. if (cp === $.HYPHEN_MINUS) {
  818. this.state = SCRIPT_DATA_ESCAPED_DASH_STATE;
  819. this._emitChar('-');
  820. }
  821. else if (cp === $.LESS_THAN_SIGN)
  822. this.state = SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN_STATE;
  823. else if (cp === $.NULL)
  824. this._emitChar(UNICODE.REPLACEMENT_CHARACTER);
  825. else if (cp === $.EOF)
  826. this._reconsumeInState(DATA_STATE);
  827. else
  828. this._emitCodePoint(cp);
  829. };
  830. //12.2.4.23 Script data escaped dash state
  831. //------------------------------------------------------------------
  832. _[SCRIPT_DATA_ESCAPED_DASH_STATE] = function scriptDataEscapedDashState(cp) {
  833. if (cp === $.HYPHEN_MINUS) {
  834. this.state = SCRIPT_DATA_ESCAPED_DASH_DASH_STATE;
  835. this._emitChar('-');
  836. }
  837. else if (cp === $.LESS_THAN_SIGN)
  838. this.state = SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN_STATE;
  839. else if (cp === $.NULL) {
  840. this.state = SCRIPT_DATA_ESCAPED_STATE;
  841. this._emitChar(UNICODE.REPLACEMENT_CHARACTER);
  842. }
  843. else if (cp === $.EOF)
  844. this._reconsumeInState(DATA_STATE);
  845. else {
  846. this.state = SCRIPT_DATA_ESCAPED_STATE;
  847. this._emitCodePoint(cp);
  848. }
  849. };
  850. //12.2.4.24 Script data escaped dash dash state
  851. //------------------------------------------------------------------
  852. _[SCRIPT_DATA_ESCAPED_DASH_DASH_STATE] = function scriptDataEscapedDashDashState(cp) {
  853. if (cp === $.HYPHEN_MINUS)
  854. this._emitChar('-');
  855. else if (cp === $.LESS_THAN_SIGN)
  856. this.state = SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN_STATE;
  857. else if (cp === $.GREATER_THAN_SIGN) {
  858. this.state = SCRIPT_DATA_STATE;
  859. this._emitChar('>');
  860. }
  861. else if (cp === $.NULL) {
  862. this.state = SCRIPT_DATA_ESCAPED_STATE;
  863. this._emitChar(UNICODE.REPLACEMENT_CHARACTER);
  864. }
  865. else if (cp === $.EOF)
  866. this._reconsumeInState(DATA_STATE);
  867. else {
  868. this.state = SCRIPT_DATA_ESCAPED_STATE;
  869. this._emitCodePoint(cp);
  870. }
  871. };
  872. //12.2.4.25 Script data escaped less-than sign state
  873. //------------------------------------------------------------------
  874. _[SCRIPT_DATA_ESCAPED_LESS_THAN_SIGN_STATE] = function scriptDataEscapedLessThanSignState(cp) {
  875. if (cp === $.SOLIDUS) {
  876. this.tempBuff = [];
  877. this.state = SCRIPT_DATA_ESCAPED_END_TAG_OPEN_STATE;
  878. }
  879. else if (isAsciiUpper(cp)) {
  880. this.tempBuff = [];
  881. this.tempBuff.push(toAsciiLowerCodePoint(cp));
  882. this.state = SCRIPT_DATA_DOUBLE_ESCAPE_START_STATE;
  883. this._emitChar('<');
  884. this._emitCodePoint(cp);
  885. }
  886. else if (isAsciiLower(cp)) {
  887. this.tempBuff = [];
  888. this.tempBuff.push(cp);
  889. this.state = SCRIPT_DATA_DOUBLE_ESCAPE_START_STATE;
  890. this._emitChar('<');
  891. this._emitCodePoint(cp);
  892. }
  893. else {
  894. this._emitChar('<');
  895. this._reconsumeInState(SCRIPT_DATA_ESCAPED_STATE);
  896. }
  897. };
  898. //12.2.4.26 Script data escaped end tag open state
  899. //------------------------------------------------------------------
  900. _[SCRIPT_DATA_ESCAPED_END_TAG_OPEN_STATE] = function scriptDataEscapedEndTagOpenState(cp) {
  901. if (isAsciiUpper(cp)) {
  902. this._createEndTagToken(toAsciiLowerChar(cp));
  903. this.tempBuff.push(cp);
  904. this.state = SCRIPT_DATA_ESCAPED_END_TAG_NAME_STATE;
  905. }
  906. else if (isAsciiLower(cp)) {
  907. this._createEndTagToken(toChar(cp));
  908. this.tempBuff.push(cp);
  909. this.state = SCRIPT_DATA_ESCAPED_END_TAG_NAME_STATE;
  910. }
  911. else {
  912. this._emitChar('<');
  913. this._emitChar('/');
  914. this._reconsumeInState(SCRIPT_DATA_ESCAPED_STATE);
  915. }
  916. };
  917. //12.2.4.27 Script data escaped end tag name state
  918. //------------------------------------------------------------------
  919. _[SCRIPT_DATA_ESCAPED_END_TAG_NAME_STATE] = function scriptDataEscapedEndTagNameState(cp) {
  920. if (isAsciiUpper(cp)) {
  921. this.currentToken.tagName += toAsciiLowerChar(cp);
  922. this.tempBuff.push(cp);
  923. }
  924. else if (isAsciiLower(cp)) {
  925. this.currentToken.tagName += toChar(cp);
  926. this.tempBuff.push(cp);
  927. }
  928. else {
  929. if (this._isAppropriateEndTagToken()) {
  930. if (isWhitespace(cp)) {
  931. this.state = BEFORE_ATTRIBUTE_NAME_STATE;
  932. return;
  933. }
  934. if (cp === $.SOLIDUS) {
  935. this.state = SELF_CLOSING_START_TAG_STATE;
  936. return;
  937. }
  938. if (cp === $.GREATER_THAN_SIGN) {
  939. this._emitCurrentToken();
  940. this.state = DATA_STATE;
  941. return;
  942. }
  943. }
  944. this._emitChar('<');
  945. this._emitChar('/');
  946. this._emitSeveralCodePoints(this.tempBuff);
  947. this._reconsumeInState(SCRIPT_DATA_ESCAPED_STATE);
  948. }
  949. };
  950. //12.2.4.28 Script data double escape start state
  951. //------------------------------------------------------------------
  952. _[SCRIPT_DATA_DOUBLE_ESCAPE_START_STATE] = function scriptDataDoubleEscapeStartState(cp) {
  953. if (isWhitespace(cp) || cp === $.SOLIDUS || cp === $.GREATER_THAN_SIGN) {
  954. this.state = this.isTempBufferEqualToScriptString() ? SCRIPT_DATA_DOUBLE_ESCAPED_STATE : SCRIPT_DATA_ESCAPED_STATE;
  955. this._emitCodePoint(cp);
  956. }
  957. else if (isAsciiUpper(cp)) {
  958. this.tempBuff.push(toAsciiLowerCodePoint(cp));
  959. this._emitCodePoint(cp);
  960. }
  961. else if (isAsciiLower(cp)) {
  962. this.tempBuff.push(cp);
  963. this._emitCodePoint(cp);
  964. }
  965. else
  966. this._reconsumeInState(SCRIPT_DATA_ESCAPED_STATE);
  967. };
  968. //12.2.4.29 Script data double escaped state
  969. //------------------------------------------------------------------
  970. _[SCRIPT_DATA_DOUBLE_ESCAPED_STATE] = function scriptDataDoubleEscapedState(cp) {
  971. if (cp === $.HYPHEN_MINUS) {
  972. this.state = SCRIPT_DATA_DOUBLE_ESCAPED_DASH_STATE;
  973. this._emitChar('-');
  974. }
  975. else if (cp === $.LESS_THAN_SIGN) {
  976. this.state = SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN_STATE;
  977. this._emitChar('<');
  978. }
  979. else if (cp === $.NULL)
  980. this._emitChar(UNICODE.REPLACEMENT_CHARACTER);
  981. else if (cp === $.EOF)
  982. this._reconsumeInState(DATA_STATE);
  983. else
  984. this._emitCodePoint(cp);
  985. };
  986. //12.2.4.30 Script data double escaped dash state
  987. //------------------------------------------------------------------
  988. _[SCRIPT_DATA_DOUBLE_ESCAPED_DASH_STATE] = function scriptDataDoubleEscapedDashState(cp) {
  989. if (cp === $.HYPHEN_MINUS) {
  990. this.state = SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH_STATE;
  991. this._emitChar('-');
  992. }
  993. else if (cp === $.LESS_THAN_SIGN) {
  994. this.state = SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN_STATE;
  995. this._emitChar('<');
  996. }
  997. else if (cp === $.NULL) {
  998. this.state = SCRIPT_DATA_DOUBLE_ESCAPED_STATE;
  999. this._emitChar(UNICODE.REPLACEMENT_CHARACTER);
  1000. }
  1001. else if (cp === $.EOF)
  1002. this._reconsumeInState(DATA_STATE);
  1003. else {
  1004. this.state = SCRIPT_DATA_DOUBLE_ESCAPED_STATE;
  1005. this._emitCodePoint(cp);
  1006. }
  1007. };
  1008. //12.2.4.31 Script data double escaped dash dash state
  1009. //------------------------------------------------------------------
  1010. _[SCRIPT_DATA_DOUBLE_ESCAPED_DASH_DASH_STATE] = function scriptDataDoubleEscapedDashDashState(cp) {
  1011. if (cp === $.HYPHEN_MINUS)
  1012. this._emitChar('-');
  1013. else if (cp === $.LESS_THAN_SIGN) {
  1014. this.state = SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN_STATE;
  1015. this._emitChar('<');
  1016. }
  1017. else if (cp === $.GREATER_THAN_SIGN) {
  1018. this.state = SCRIPT_DATA_STATE;
  1019. this._emitChar('>');
  1020. }
  1021. else if (cp === $.NULL) {
  1022. this.state = SCRIPT_DATA_DOUBLE_ESCAPED_STATE;
  1023. this._emitChar(UNICODE.REPLACEMENT_CHARACTER);
  1024. }
  1025. else if (cp === $.EOF)
  1026. this._reconsumeInState(DATA_STATE);
  1027. else {
  1028. this.state = SCRIPT_DATA_DOUBLE_ESCAPED_STATE;
  1029. this._emitCodePoint(cp);
  1030. }
  1031. };
  1032. //12.2.4.32 Script data double escaped less-than sign state
  1033. //------------------------------------------------------------------
  1034. _[SCRIPT_DATA_DOUBLE_ESCAPED_LESS_THAN_SIGN_STATE] = function scriptDataDoubleEscapedLessThanSignState(cp) {
  1035. if (cp === $.SOLIDUS) {
  1036. this.tempBuff = [];
  1037. this.state = SCRIPT_DATA_DOUBLE_ESCAPE_END_STATE;
  1038. this._emitChar('/');
  1039. }
  1040. else
  1041. this._reconsumeInState(SCRIPT_DATA_DOUBLE_ESCAPED_STATE);
  1042. };
  1043. //12.2.4.33 Script data double escape end state
  1044. //------------------------------------------------------------------
  1045. _[SCRIPT_DATA_DOUBLE_ESCAPE_END_STATE] = function scriptDataDoubleEscapeEndState(cp) {
  1046. if (isWhitespace(cp) || cp === $.SOLIDUS || cp === $.GREATER_THAN_SIGN) {
  1047. this.state = this.isTempBufferEqualToScriptString() ? SCRIPT_DATA_ESCAPED_STATE : SCRIPT_DATA_DOUBLE_ESCAPED_STATE;
  1048. this._emitCodePoint(cp);
  1049. }
  1050. else if (isAsciiUpper(cp)) {
  1051. this.tempBuff.push(toAsciiLowerCodePoint(cp));
  1052. this._emitCodePoint(cp);
  1053. }
  1054. else if (isAsciiLower(cp)) {
  1055. this.tempBuff.push(cp);
  1056. this._emitCodePoint(cp);
  1057. }
  1058. else
  1059. this._reconsumeInState(SCRIPT_DATA_DOUBLE_ESCAPED_STATE);
  1060. };
  1061. //12.2.4.34 Before attribute name state
  1062. //------------------------------------------------------------------
  1063. _[BEFORE_ATTRIBUTE_NAME_STATE] = function beforeAttributeNameState(cp) {
  1064. if (isWhitespace(cp))
  1065. return;
  1066. if (cp === $.SOLIDUS)
  1067. this.state = SELF_CLOSING_START_TAG_STATE;
  1068. else if (cp === $.GREATER_THAN_SIGN) {
  1069. this.state = DATA_STATE;
  1070. this._emitCurrentToken();
  1071. }
  1072. else if (isAsciiUpper(cp)) {
  1073. this._createAttr(toAsciiLowerChar(cp));
  1074. this.state = ATTRIBUTE_NAME_STATE;
  1075. }
  1076. else if (cp === $.NULL) {
  1077. this._createAttr(UNICODE.REPLACEMENT_CHARACTER);
  1078. this.state = ATTRIBUTE_NAME_STATE;
  1079. }
  1080. else if (cp === $.QUOTATION_MARK || cp === $.APOSTROPHE || cp === $.LESS_THAN_SIGN || cp === $.EQUALS_SIGN) {
  1081. this._createAttr(toChar(cp));
  1082. this.state = ATTRIBUTE_NAME_STATE;
  1083. }
  1084. else if (cp === $.EOF)
  1085. this._reconsumeInState(DATA_STATE);
  1086. else {
  1087. this._createAttr(toChar(cp));
  1088. this.state = ATTRIBUTE_NAME_STATE;
  1089. }
  1090. };
  1091. //12.2.4.35 Attribute name state
  1092. //------------------------------------------------------------------
  1093. _[ATTRIBUTE_NAME_STATE] = function attributeNameState(cp) {
  1094. if (isWhitespace(cp))
  1095. this._leaveAttrName(AFTER_ATTRIBUTE_NAME_STATE);
  1096. else if (cp === $.SOLIDUS)
  1097. this._leaveAttrName(SELF_CLOSING_START_TAG_STATE);
  1098. else if (cp === $.EQUALS_SIGN)
  1099. this._leaveAttrName(BEFORE_ATTRIBUTE_VALUE_STATE);
  1100. else if (cp === $.GREATER_THAN_SIGN) {
  1101. this._leaveAttrName(DATA_STATE);
  1102. this._emitCurrentToken();
  1103. }
  1104. else if (isAsciiUpper(cp))
  1105. this.currentAttr.name += toAsciiLowerChar(cp);
  1106. else if (cp === $.QUOTATION_MARK || cp === $.APOSTROPHE || cp === $.LESS_THAN_SIGN)
  1107. this.currentAttr.name += toChar(cp);
  1108. else if (cp === $.NULL)
  1109. this.currentAttr.name += UNICODE.REPLACEMENT_CHARACTER;
  1110. else if (cp === $.EOF)
  1111. this._reconsumeInState(DATA_STATE);
  1112. else
  1113. this.currentAttr.name += toChar(cp);
  1114. };
  1115. //12.2.4.36 After attribute name state
  1116. //------------------------------------------------------------------
  1117. _[AFTER_ATTRIBUTE_NAME_STATE] = function afterAttributeNameState(cp) {
  1118. if (isWhitespace(cp))
  1119. return;
  1120. if (cp === $.SOLIDUS)
  1121. this.state = SELF_CLOSING_START_TAG_STATE;
  1122. else if (cp === $.EQUALS_SIGN)
  1123. this.state = BEFORE_ATTRIBUTE_VALUE_STATE;
  1124. else if (cp === $.GREATER_THAN_SIGN) {
  1125. this.state = DATA_STATE;
  1126. this._emitCurrentToken();
  1127. }
  1128. else if (isAsciiUpper(cp)) {
  1129. this._createAttr(toAsciiLowerChar(cp));
  1130. this.state = ATTRIBUTE_NAME_STATE;
  1131. }
  1132. else if (cp === $.NULL) {
  1133. this._createAttr(UNICODE.REPLACEMENT_CHARACTER);
  1134. this.state = ATTRIBUTE_NAME_STATE;
  1135. }
  1136. else if (cp === $.QUOTATION_MARK || cp === $.APOSTROPHE || cp === $.LESS_THAN_SIGN) {
  1137. this._createAttr(toChar(cp));
  1138. this.state = ATTRIBUTE_NAME_STATE;
  1139. }
  1140. else if (cp === $.EOF)
  1141. this._reconsumeInState(DATA_STATE);
  1142. else {
  1143. this._createAttr(toChar(cp));
  1144. this.state = ATTRIBUTE_NAME_STATE;
  1145. }
  1146. };
  1147. //12.2.4.37 Before attribute value state
  1148. //------------------------------------------------------------------
  1149. _[BEFORE_ATTRIBUTE_VALUE_STATE] = function beforeAttributeValueState(cp) {
  1150. if (isWhitespace(cp))
  1151. return;
  1152. if (cp === $.QUOTATION_MARK)
  1153. this.state = ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE;
  1154. else if (cp === $.AMPERSAND)
  1155. this._reconsumeInState(ATTRIBUTE_VALUE_UNQUOTED_STATE);
  1156. else if (cp === $.APOSTROPHE)
  1157. this.state = ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE;
  1158. else if (cp === $.NULL) {
  1159. this.currentAttr.value += UNICODE.REPLACEMENT_CHARACTER;
  1160. this.state = ATTRIBUTE_VALUE_UNQUOTED_STATE;
  1161. }
  1162. else if (cp === $.GREATER_THAN_SIGN) {
  1163. this.state = DATA_STATE;
  1164. this._emitCurrentToken();
  1165. }
  1166. else if (cp === $.LESS_THAN_SIGN || cp === $.EQUALS_SIGN || cp === $.GRAVE_ACCENT) {
  1167. this.currentAttr.value += toChar(cp);
  1168. this.state = ATTRIBUTE_VALUE_UNQUOTED_STATE;
  1169. }
  1170. else if (cp === $.EOF)
  1171. this._reconsumeInState(DATA_STATE);
  1172. else {
  1173. this.currentAttr.value += toChar(cp);
  1174. this.state = ATTRIBUTE_VALUE_UNQUOTED_STATE;
  1175. }
  1176. };
  1177. //12.2.4.38 Attribute value (double-quoted) state
  1178. //------------------------------------------------------------------
  1179. _[ATTRIBUTE_VALUE_DOUBLE_QUOTED_STATE] = function attributeValueDoubleQuotedState(cp) {
  1180. if (cp === $.QUOTATION_MARK)
  1181. this.state = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
  1182. else if (cp === $.AMPERSAND) {
  1183. this.additionalAllowedCp = $.QUOTATION_MARK;
  1184. this.returnState = this.state;
  1185. this.state = CHARACTER_REFERENCE_IN_ATTRIBUTE_VALUE_STATE;
  1186. }
  1187. else if (cp === $.NULL)
  1188. this.currentAttr.value += UNICODE.REPLACEMENT_CHARACTER;
  1189. else if (cp === $.EOF)
  1190. this._reconsumeInState(DATA_STATE);
  1191. else
  1192. this.currentAttr.value += toChar(cp);
  1193. };
  1194. //12.2.4.39 Attribute value (single-quoted) state
  1195. //------------------------------------------------------------------
  1196. _[ATTRIBUTE_VALUE_SINGLE_QUOTED_STATE] = function attributeValueSingleQuotedState(cp) {
  1197. if (cp === $.APOSTROPHE)
  1198. this.state = AFTER_ATTRIBUTE_VALUE_QUOTED_STATE;
  1199. else if (cp === $.AMPERSAND) {
  1200. this.additionalAllowedCp = $.APOSTROPHE;
  1201. this.returnState = this.state;
  1202. this.state = CHARACTER_REFERENCE_IN_ATTRIBUTE_VALUE_STATE;
  1203. }
  1204. else if (cp === $.NULL)
  1205. this.currentAttr.value += UNICODE.REPLACEMENT_CHARACTER;
  1206. else if (cp === $.EOF)
  1207. this._reconsumeInState(DATA_STATE);
  1208. else
  1209. this.currentAttr.value += toChar(cp);
  1210. };
  1211. //12.2.4.40 Attribute value (unquoted) state
  1212. //------------------------------------------------------------------
  1213. _[ATTRIBUTE_VALUE_UNQUOTED_STATE] = function attributeValueUnquotedState(cp) {
  1214. if (isWhitespace(cp))
  1215. this.state = BEFORE_ATTRIBUTE_NAME_STATE;
  1216. else if (cp === $.AMPERSAND) {
  1217. this.additionalAllowedCp = $.GREATER_THAN_SIGN;
  1218. this.returnState = this.state;
  1219. this.state = CHARACTER_REFERENCE_IN_ATTRIBUTE_VALUE_STATE;
  1220. }
  1221. else if (cp === $.GREATER_THAN_SIGN) {
  1222. this.state = DATA_STATE;
  1223. this._emitCurrentToken();
  1224. }
  1225. else if (cp === $.NULL)
  1226. this.currentAttr.value += UNICODE.REPLACEMENT_CHARACTER;
  1227. else if (cp === $.QUOTATION_MARK || cp === $.APOSTROPHE || cp === $.LESS_THAN_SIGN ||
  1228. cp === $.EQUALS_SIGN || cp === $.GRAVE_ACCENT) {
  1229. this.currentAttr.value += toChar(cp);
  1230. }
  1231. else if (cp === $.EOF)
  1232. this._reconsumeInState(DATA_STATE);
  1233. else
  1234. this.currentAttr.value += toChar(cp);
  1235. };
  1236. //12.2.4.41 Character reference in attribute value state
  1237. //------------------------------------------------------------------
  1238. _[CHARACTER_REFERENCE_IN_ATTRIBUTE_VALUE_STATE] = function characterReferenceInAttributeValueState(cp) {
  1239. var referencedCodePoints = this._consumeCharacterReference(cp, true);
  1240. if (referencedCodePoints) {
  1241. for (var i = 0; i < referencedCodePoints.length; i++)
  1242. this.currentAttr.value += toChar(referencedCodePoints[i]);
  1243. } else
  1244. this.currentAttr.value += '&';
  1245. this.state = this.returnState;
  1246. };
  1247. //12.2.4.42 After attribute value (quoted) state
  1248. //------------------------------------------------------------------
  1249. _[AFTER_ATTRIBUTE_VALUE_QUOTED_STATE] = function afterAttributeValueQuotedState(cp) {
  1250. if (isWhitespace(cp))
  1251. this.state = BEFORE_ATTRIBUTE_NAME_STATE;
  1252. else if (cp === $.SOLIDUS)
  1253. this.state = SELF_CLOSING_START_TAG_STATE;
  1254. else if (cp === $.GREATER_THAN_SIGN) {
  1255. this.state = DATA_STATE;
  1256. this._emitCurrentToken();
  1257. }
  1258. else if (cp === $.EOF)
  1259. this._reconsumeInState(DATA_STATE);
  1260. else
  1261. this._reconsumeInState(BEFORE_ATTRIBUTE_NAME_STATE);
  1262. };
  1263. //12.2.4.43 Self-closing start tag state
  1264. //------------------------------------------------------------------
  1265. _[SELF_CLOSING_START_TAG_STATE] = function selfClosingStartTagState(cp) {
  1266. if (cp === $.GREATER_THAN_SIGN) {
  1267. this.currentToken.selfClosing = true;
  1268. this.state = DATA_STATE;
  1269. this._emitCurrentToken();
  1270. }
  1271. else if (cp === $.EOF)
  1272. this._reconsumeInState(DATA_STATE);
  1273. else
  1274. this._reconsumeInState(BEFORE_ATTRIBUTE_NAME_STATE);
  1275. };
  1276. //12.2.4.44 Bogus comment state
  1277. //------------------------------------------------------------------
  1278. _[BOGUS_COMMENT_STATE] = function bogusCommentState(cp) {
  1279. this._createCommentToken();
  1280. while (true) {
  1281. if (cp === $.GREATER_THAN_SIGN) {
  1282. this.state = DATA_STATE;
  1283. break;
  1284. }
  1285. else if (cp === $.EOF) {
  1286. this._reconsumeInState(DATA_STATE);
  1287. break;
  1288. }
  1289. else {
  1290. this.currentToken.data += cp === $.NULL ? UNICODE.REPLACEMENT_CHARACTER : toChar(cp);
  1291. cp = this._consume();
  1292. }
  1293. }
  1294. this._emitCurrentToken();
  1295. };
  1296. //12.2.4.45 Markup declaration open state
  1297. //------------------------------------------------------------------
  1298. _[MARKUP_DECLARATION_OPEN_STATE] = function markupDeclarationOpenState(cp) {
  1299. if (this._consumeSubsequentIfMatch($$.DASH_DASH_STRING, cp, true)) {
  1300. this._createCommentToken();
  1301. this.state = COMMENT_START_STATE;
  1302. }
  1303. else if (this._consumeSubsequentIfMatch($$.DOCTYPE_STRING, cp, false))
  1304. this.state = DOCTYPE_STATE;
  1305. else if (this.allowCDATA && this._consumeSubsequentIfMatch($$.CDATA_START_STRING, cp, true))
  1306. this.state = CDATA_SECTION_STATE;
  1307. else {
  1308. //NOTE: call bogus comment state directly with current consumed character to avoid unnecessary reconsumption.
  1309. this[BOGUS_COMMENT_STATE](cp);
  1310. }
  1311. };
  1312. //12.2.4.46 Comment start state
  1313. //------------------------------------------------------------------
  1314. _[COMMENT_START_STATE] = function commentStartState(cp) {
  1315. if (cp === $.HYPHEN_MINUS)
  1316. this.state = COMMENT_START_DASH_STATE;
  1317. else if (cp === $.NULL) {
  1318. this.currentToken.data += UNICODE.REPLACEMENT_CHARACTER;
  1319. this.state = COMMENT_STATE;
  1320. }
  1321. else if (cp === $.GREATER_THAN_SIGN) {
  1322. this.state = DATA_STATE;
  1323. this._emitCurrentToken();
  1324. }
  1325. else if (cp === $.EOF) {
  1326. this._emitCurrentToken();
  1327. this._reconsumeInState(DATA_STATE);
  1328. }
  1329. else {
  1330. this.currentToken.data += toChar(cp);
  1331. this.state = COMMENT_STATE;
  1332. }
  1333. };
  1334. //12.2.4.47 Comment start dash state
  1335. //------------------------------------------------------------------
  1336. _[COMMENT_START_DASH_STATE] = function commentStartDashState(cp) {
  1337. if (cp === $.HYPHEN_MINUS)
  1338. this.state = COMMENT_END_STATE;
  1339. else if (cp === $.NULL) {
  1340. this.currentToken.data += '-';
  1341. this.currentToken.data += UNICODE.REPLACEMENT_CHARACTER;
  1342. this.state = COMMENT_STATE;
  1343. }
  1344. else if (cp === $.GREATER_THAN_SIGN) {
  1345. this.state = DATA_STATE;
  1346. this._emitCurrentToken();
  1347. }
  1348. else if (cp === $.EOF) {
  1349. this._emitCurrentToken();
  1350. this._reconsumeInState(DATA_STATE);
  1351. }
  1352. else {
  1353. this.currentToken.data += '-';
  1354. this.currentToken.data += toChar(cp);
  1355. this.state = COMMENT_STATE;
  1356. }
  1357. };
  1358. //12.2.4.48 Comment state
  1359. //------------------------------------------------------------------
  1360. _[COMMENT_STATE] = function commentState(cp) {
  1361. if (cp === $.HYPHEN_MINUS)
  1362. this.state = COMMENT_END_DASH_STATE;
  1363. else if (cp === $.NULL)
  1364. this.currentToken.data += UNICODE.REPLACEMENT_CHARACTER;
  1365. else if (cp === $.EOF) {
  1366. this._emitCurrentToken();
  1367. this._reconsumeInState(DATA_STATE);
  1368. }
  1369. else
  1370. this.currentToken.data += toChar(cp);
  1371. };
  1372. //12.2.4.49 Comment end dash state
  1373. //------------------------------------------------------------------
  1374. _[COMMENT_END_DASH_STATE] = function commentEndDashState(cp) {
  1375. if (cp === $.HYPHEN_MINUS)
  1376. this.state = COMMENT_END_STATE;
  1377. else if (cp === $.NULL) {
  1378. this.currentToken.data += '-';
  1379. this.currentToken.data += UNICODE.REPLACEMENT_CHARACTER;
  1380. this.state = COMMENT_STATE;
  1381. }
  1382. else if (cp === $.EOF) {
  1383. this._emitCurrentToken();
  1384. this._reconsumeInState(DATA_STATE);
  1385. }
  1386. else {
  1387. this.currentToken.data += '-';
  1388. this.currentToken.data += toChar(cp);
  1389. this.state = COMMENT_STATE;
  1390. }
  1391. };
  1392. //12.2.4.50 Comment end state
  1393. //------------------------------------------------------------------
  1394. _[COMMENT_END_STATE] = function commentEndState(cp) {
  1395. if (cp === $.GREATER_THAN_SIGN) {
  1396. this.state = DATA_STATE;
  1397. this._emitCurrentToken();
  1398. }
  1399. else if (cp === $.EXCLAMATION_MARK)
  1400. this.state = COMMENT_END_BANG_STATE;
  1401. else if (cp === $.HYPHEN_MINUS)
  1402. this.currentToken.data += '-';
  1403. else if (cp === $.NULL) {
  1404. this.currentToken.data += '--';
  1405. this.currentToken.data += UNICODE.REPLACEMENT_CHARACTER;
  1406. this.state = COMMENT_STATE;
  1407. }
  1408. else if (cp === $.EOF) {
  1409. this._reconsumeInState(DATA_STATE);
  1410. this._emitCurrentToken();
  1411. }
  1412. else {
  1413. this.currentToken.data += '--';
  1414. this.currentToken.data += toChar(cp);
  1415. this.state = COMMENT_STATE;
  1416. }
  1417. };
  1418. //12.2.4.51 Comment end bang state
  1419. //------------------------------------------------------------------
  1420. _[COMMENT_END_BANG_STATE] = function commentEndBangState(cp) {
  1421. if (cp === $.HYPHEN_MINUS) {
  1422. this.currentToken.data += '--!';
  1423. this.state = COMMENT_END_DASH_STATE;
  1424. }
  1425. else if (cp === $.GREATER_THAN_SIGN) {
  1426. this.state = DATA_STATE;
  1427. this._emitCurrentToken();
  1428. }
  1429. else if (cp === $.NULL) {
  1430. this.currentToken.data += '--!';
  1431. this.currentToken.data += UNICODE.REPLACEMENT_CHARACTER;
  1432. this.state = COMMENT_STATE;
  1433. }
  1434. else if (cp === $.EOF) {
  1435. this._emitCurrentToken();
  1436. this._reconsumeInState(DATA_STATE);
  1437. }
  1438. else {
  1439. this.currentToken.data += '--!';
  1440. this.currentToken.data += toChar(cp);
  1441. this.state = COMMENT_STATE;
  1442. }
  1443. };
  1444. //12.2.4.52 DOCTYPE state
  1445. //------------------------------------------------------------------
  1446. _[DOCTYPE_STATE] = function doctypeState(cp) {
  1447. if (isWhitespace(cp))
  1448. this.state = BEFORE_DOCTYPE_NAME_STATE;
  1449. else if (cp === $.EOF) {
  1450. this._createDoctypeToken();
  1451. this.currentToken.forceQuirks = true;
  1452. this._emitCurrentToken();
  1453. this._reconsumeInState(DATA_STATE);
  1454. }
  1455. else
  1456. this._reconsumeInState(BEFORE_DOCTYPE_NAME_STATE);
  1457. };
  1458. //12.2.4.53 Before DOCTYPE name state
  1459. //------------------------------------------------------------------
  1460. _[BEFORE_DOCTYPE_NAME_STATE] = function beforeDoctypeNameState(cp) {
  1461. if (isWhitespace(cp))
  1462. return;
  1463. if (isAsciiUpper(cp)) {
  1464. this._createDoctypeToken(toAsciiLowerChar(cp));
  1465. this.state = DOCTYPE_NAME_STATE;
  1466. }
  1467. else if (cp === $.GREATER_THAN_SIGN) {
  1468. this._createDoctypeToken();
  1469. this.currentToken.forceQuirks = true;
  1470. this._emitCurrentToken();
  1471. this.state = DATA_STATE;
  1472. }
  1473. else if (cp === $.EOF) {
  1474. this._createDoctypeToken();
  1475. this.currentToken.forceQuirks = true;
  1476. this._emitCurrentToken();
  1477. this._reconsumeInState(DATA_STATE);
  1478. }
  1479. else if (cp === $.NULL) {
  1480. this._createDoctypeToken(UNICODE.REPLACEMENT_CHARACTER);
  1481. this.state = DOCTYPE_NAME_STATE;
  1482. }
  1483. else {
  1484. this._createDoctypeToken(toChar(cp));
  1485. this.state = DOCTYPE_NAME_STATE;
  1486. }
  1487. };
  1488. //12.2.4.54 DOCTYPE name state
  1489. //------------------------------------------------------------------
  1490. _[DOCTYPE_NAME_STATE] = function doctypeNameState(cp) {
  1491. if (isWhitespace(cp))
  1492. this.state = AFTER_DOCTYPE_NAME_STATE;
  1493. else if (cp === $.GREATER_THAN_SIGN) {
  1494. this._emitCurrentToken();
  1495. this.state = DATA_STATE;
  1496. }
  1497. else if (isAsciiUpper(cp))
  1498. this.currentToken.name += toAsciiLowerChar(cp);
  1499. else if (cp === $.NULL)
  1500. this.currentToken.name += UNICODE.REPLACEMENT_CHARACTER;
  1501. else if (cp === $.EOF) {
  1502. this.currentToken.forceQuirks = true;
  1503. this._emitCurrentToken();
  1504. this._reconsumeInState(DATA_STATE);
  1505. }
  1506. else
  1507. this.currentToken.name += toChar(cp);
  1508. };
  1509. //12.2.4.55 After DOCTYPE name state
  1510. //------------------------------------------------------------------
  1511. _[AFTER_DOCTYPE_NAME_STATE] = function afterDoctypeNameState(cp) {
  1512. if (isWhitespace(cp))
  1513. return;
  1514. if (cp === $.GREATER_THAN_SIGN) {
  1515. this.state = DATA_STATE;
  1516. this._emitCurrentToken();
  1517. }
  1518. else if (cp === $.EOF) {
  1519. this.currentToken.forceQuirks = true;
  1520. this._emitCurrentToken();
  1521. this._reconsumeInState(DATA_STATE);
  1522. }
  1523. else if (this._consumeSubsequentIfMatch($$.PUBLIC_STRING, cp, false))
  1524. this.state = AFTER_DOCTYPE_PUBLIC_KEYWORD_STATE;
  1525. else if (this._consumeSubsequentIfMatch($$.SYSTEM_STRING, cp, false))
  1526. this.state = AFTER_DOCTYPE_SYSTEM_KEYWORD_STATE;
  1527. else {
  1528. this.currentToken.forceQuirks = true;
  1529. this.state = BOGUS_DOCTYPE_STATE;
  1530. }
  1531. };
  1532. //12.2.4.56 After DOCTYPE public keyword state
  1533. //------------------------------------------------------------------
  1534. _[AFTER_DOCTYPE_PUBLIC_KEYWORD_STATE] = function afterDoctypePublicKeywordState(cp) {
  1535. if (isWhitespace(cp))
  1536. this.state = BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
  1537. else if (cp === $.QUOTATION_MARK) {
  1538. this.currentToken.publicId = '';
  1539. this.state = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
  1540. }
  1541. else if (cp === $.APOSTROPHE) {
  1542. this.currentToken.publicId = '';
  1543. this.state = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
  1544. }
  1545. else if (cp === $.GREATER_THAN_SIGN) {
  1546. this.currentToken.forceQuirks = true;
  1547. this._emitCurrentToken();
  1548. this.state = DATA_STATE;
  1549. }
  1550. else if (cp === $.EOF) {
  1551. this.currentToken.forceQuirks = true;
  1552. this._emitCurrentToken();
  1553. this._reconsumeInState(DATA_STATE);
  1554. }
  1555. else {
  1556. this.currentToken.forceQuirks = true;
  1557. this.state = BOGUS_DOCTYPE_STATE;
  1558. }
  1559. };
  1560. //12.2.4.57 Before DOCTYPE public identifier state
  1561. //------------------------------------------------------------------
  1562. _[BEFORE_DOCTYPE_PUBLIC_IDENTIFIER_STATE] = function beforeDoctypePublicIdentifierState(cp) {
  1563. if (isWhitespace(cp))
  1564. return;
  1565. if (cp === $.QUOTATION_MARK) {
  1566. this.currentToken.publicId = '';
  1567. this.state = DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE;
  1568. }
  1569. else if (cp === $.APOSTROPHE) {
  1570. this.currentToken.publicId = '';
  1571. this.state = DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE;
  1572. }
  1573. else if (cp === $.GREATER_THAN_SIGN) {
  1574. this.currentToken.forceQuirks = true;
  1575. this._emitCurrentToken();
  1576. this.state = DATA_STATE;
  1577. }
  1578. else if (cp === $.EOF) {
  1579. this.currentToken.forceQuirks = true;
  1580. this._emitCurrentToken();
  1581. this._reconsumeInState(DATA_STATE);
  1582. }
  1583. else {
  1584. this.currentToken.forceQuirks = true;
  1585. this.state = BOGUS_DOCTYPE_STATE;
  1586. }
  1587. };
  1588. //12.2.4.58 DOCTYPE public identifier (double-quoted) state
  1589. //------------------------------------------------------------------
  1590. _[DOCTYPE_PUBLIC_IDENTIFIER_DOUBLE_QUOTED_STATE] = function doctypePublicIdentifierDoubleQuotedState(cp) {
  1591. if (cp === $.QUOTATION_MARK)
  1592. this.state = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
  1593. else if (cp === $.NULL)
  1594. this.currentToken.publicId += UNICODE.REPLACEMENT_CHARACTER;
  1595. else if (cp === $.GREATER_THAN_SIGN) {
  1596. this.currentToken.forceQuirks = true;
  1597. this._emitCurrentToken();
  1598. this.state = DATA_STATE;
  1599. }
  1600. else if (cp === $.EOF) {
  1601. this.currentToken.forceQuirks = true;
  1602. this._emitCurrentToken();
  1603. this._reconsumeInState(DATA_STATE);
  1604. }
  1605. else
  1606. this.currentToken.publicId += toChar(cp);
  1607. };
  1608. //12.2.4.59 DOCTYPE public identifier (single-quoted) state
  1609. //------------------------------------------------------------------
  1610. _[DOCTYPE_PUBLIC_IDENTIFIER_SINGLE_QUOTED_STATE] = function doctypePublicIdentifierSingleQuotedState(cp) {
  1611. if (cp === $.APOSTROPHE)
  1612. this.state = AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE;
  1613. else if (cp === $.NULL)
  1614. this.currentToken.publicId += UNICODE.REPLACEMENT_CHARACTER;
  1615. else if (cp === $.GREATER_THAN_SIGN) {
  1616. this.currentToken.forceQuirks = true;
  1617. this._emitCurrentToken();
  1618. this.state = DATA_STATE;
  1619. }
  1620. else if (cp === $.EOF) {
  1621. this.currentToken.forceQuirks = true;
  1622. this._emitCurrentToken();
  1623. this._reconsumeInState(DATA_STATE);
  1624. }
  1625. else
  1626. this.currentToken.publicId += toChar(cp);
  1627. };
  1628. //12.2.4.60 After DOCTYPE public identifier state
  1629. //------------------------------------------------------------------
  1630. _[AFTER_DOCTYPE_PUBLIC_IDENTIFIER_STATE] = function afterDoctypePublicIdentifierState(cp) {
  1631. if (isWhitespace(cp))
  1632. this.state = BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS_STATE;
  1633. else if (cp === $.GREATER_THAN_SIGN) {
  1634. this._emitCurrentToken();
  1635. this.state = DATA_STATE;
  1636. }
  1637. else if (cp === $.QUOTATION_MARK) {
  1638. this.currentToken.systemId = '';
  1639. this.state = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
  1640. }
  1641. else if (cp === $.APOSTROPHE) {
  1642. this.currentToken.systemId = '';
  1643. this.state = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
  1644. }
  1645. else if (cp === $.EOF) {
  1646. this.currentToken.forceQuirks = true;
  1647. this._emitCurrentToken();
  1648. this._reconsumeInState(DATA_STATE);
  1649. }
  1650. else {
  1651. this.currentToken.forceQuirks = true;
  1652. this.state = BOGUS_DOCTYPE_STATE;
  1653. }
  1654. };
  1655. //12.2.4.61 Between DOCTYPE public and system identifiers state
  1656. //------------------------------------------------------------------
  1657. _[BETWEEN_DOCTYPE_PUBLIC_AND_SYSTEM_IDENTIFIERS_STATE] = function betweenDoctypePublicAndSystemIdentifiersState(cp) {
  1658. if (isWhitespace(cp))
  1659. return;
  1660. if (cp === $.GREATER_THAN_SIGN) {
  1661. this._emitCurrentToken();
  1662. this.state = DATA_STATE;
  1663. }
  1664. else if (cp === $.QUOTATION_MARK) {
  1665. this.currentToken.systemId = '';
  1666. this.state = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
  1667. }
  1668. else if (cp === $.APOSTROPHE) {
  1669. this.currentToken.systemId = '';
  1670. this.state = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
  1671. }
  1672. else if (cp === $.EOF) {
  1673. this.currentToken.forceQuirks = true;
  1674. this._emitCurrentToken();
  1675. this._reconsumeInState(DATA_STATE);
  1676. }
  1677. else {
  1678. this.currentToken.forceQuirks = true;
  1679. this.state = BOGUS_DOCTYPE_STATE;
  1680. }
  1681. };
  1682. //12.2.4.62 After DOCTYPE system keyword state
  1683. //------------------------------------------------------------------
  1684. _[AFTER_DOCTYPE_SYSTEM_KEYWORD_STATE] = function afterDoctypeSystemKeywordState(cp) {
  1685. if (isWhitespace(cp))
  1686. this.state = BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
  1687. else if (cp === $.QUOTATION_MARK) {
  1688. this.currentToken.systemId = '';
  1689. this.state = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
  1690. }
  1691. else if (cp === $.APOSTROPHE) {
  1692. this.currentToken.systemId = '';
  1693. this.state = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
  1694. }
  1695. else if (cp === $.GREATER_THAN_SIGN) {
  1696. this.currentToken.forceQuirks = true;
  1697. this._emitCurrentToken();
  1698. this.state = DATA_STATE;
  1699. }
  1700. else if (cp === $.EOF) {
  1701. this.currentToken.forceQuirks = true;
  1702. this._emitCurrentToken();
  1703. this._reconsumeInState(DATA_STATE);
  1704. }
  1705. else {
  1706. this.currentToken.forceQuirks = true;
  1707. this.state = BOGUS_DOCTYPE_STATE;
  1708. }
  1709. };
  1710. //12.2.4.63 Before DOCTYPE system identifier state
  1711. //------------------------------------------------------------------
  1712. _[BEFORE_DOCTYPE_SYSTEM_IDENTIFIER_STATE] = function beforeDoctypeSystemIdentifierState(cp) {
  1713. if (isWhitespace(cp))
  1714. return;
  1715. if (cp === $.QUOTATION_MARK) {
  1716. this.currentToken.systemId = '';
  1717. this.state = DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE;
  1718. }
  1719. else if (cp === $.APOSTROPHE) {
  1720. this.currentToken.systemId = '';
  1721. this.state = DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE;
  1722. }
  1723. else if (cp === $.GREATER_THAN_SIGN) {
  1724. this.currentToken.forceQuirks = true;
  1725. this._emitCurrentToken();
  1726. this.state = DATA_STATE;
  1727. }
  1728. else if (cp === $.EOF) {
  1729. this.currentToken.forceQuirks = true;
  1730. this._emitCurrentToken();
  1731. this._reconsumeInState(DATA_STATE);
  1732. }
  1733. else {
  1734. this.currentToken.forceQuirks = true;
  1735. this.state = BOGUS_DOCTYPE_STATE;
  1736. }
  1737. };
  1738. //12.2.4.64 DOCTYPE system identifier (double-quoted) state
  1739. //------------------------------------------------------------------
  1740. _[DOCTYPE_SYSTEM_IDENTIFIER_DOUBLE_QUOTED_STATE] = function doctypeSystemIdentifierDoubleQuotedState(cp) {
  1741. if (cp === $.QUOTATION_MARK)
  1742. this.state = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
  1743. else if (cp === $.GREATER_THAN_SIGN) {
  1744. this.currentToken.forceQuirks = true;
  1745. this._emitCurrentToken();
  1746. this.state = DATA_STATE;
  1747. }
  1748. else if (cp === $.NULL)
  1749. this.currentToken.systemId += UNICODE.REPLACEMENT_CHARACTER;
  1750. else if (cp === $.EOF) {
  1751. this.currentToken.forceQuirks = true;
  1752. this._emitCurrentToken();
  1753. this._reconsumeInState(DATA_STATE);
  1754. }
  1755. else
  1756. this.currentToken.systemId += toChar(cp);
  1757. };
  1758. //12.2.4.65 DOCTYPE system identifier (single-quoted) state
  1759. //------------------------------------------------------------------
  1760. _[DOCTYPE_SYSTEM_IDENTIFIER_SINGLE_QUOTED_STATE] = function doctypeSystemIdentifierSingleQuotedState(cp) {
  1761. if (cp === $.APOSTROPHE)
  1762. this.state = AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE;
  1763. else if (cp === $.GREATER_THAN_SIGN) {
  1764. this.currentToken.forceQuirks = true;
  1765. this._emitCurrentToken();
  1766. this.state = DATA_STATE;
  1767. }
  1768. else if (cp === $.NULL)
  1769. this.currentToken.systemId += UNICODE.REPLACEMENT_CHARACTER;
  1770. else if (cp === $.EOF) {
  1771. this.currentToken.forceQuirks = true;
  1772. this._emitCurrentToken();
  1773. this._reconsumeInState(DATA_STATE);
  1774. }
  1775. else
  1776. this.currentToken.systemId += toChar(cp);
  1777. };
  1778. //12.2.4.66 After DOCTYPE system identifier state
  1779. //------------------------------------------------------------------
  1780. _[AFTER_DOCTYPE_SYSTEM_IDENTIFIER_STATE] = function afterDoctypeSystemIdentifierState(cp) {
  1781. if (isWhitespace(cp))
  1782. return;
  1783. if (cp === $.GREATER_THAN_SIGN) {
  1784. this._emitCurrentToken();
  1785. this.state = DATA_STATE;
  1786. }
  1787. else if (cp === $.EOF) {
  1788. this.currentToken.forceQuirks = true;
  1789. this._emitCurrentToken();
  1790. this._reconsumeInState(DATA_STATE);
  1791. }
  1792. else
  1793. this.state = BOGUS_DOCTYPE_STATE;
  1794. };
  1795. //12.2.4.67 Bogus DOCTYPE state
  1796. //------------------------------------------------------------------
  1797. _[BOGUS_DOCTYPE_STATE] = function bogusDoctypeState(cp) {
  1798. if (cp === $.GREATER_THAN_SIGN) {
  1799. this._emitCurrentToken();
  1800. this.state = DATA_STATE;
  1801. }
  1802. else if (cp === $.EOF) {
  1803. this._emitCurrentToken();
  1804. this._reconsumeInState(DATA_STATE);
  1805. }
  1806. };
  1807. //12.2.4.68 CDATA section state
  1808. //------------------------------------------------------------------
  1809. _[CDATA_SECTION_STATE] = function cdataSectionState(cp) {
  1810. while (true) {
  1811. if (cp === $.EOF) {
  1812. this._reconsumeInState(DATA_STATE);
  1813. break;
  1814. }
  1815. else if (this._consumeSubsequentIfMatch($$.CDATA_END_STRING, cp, true)) {
  1816. this.state = DATA_STATE;
  1817. break;
  1818. }
  1819. else {
  1820. this._emitCodePoint(cp);
  1821. cp = this._consume();
  1822. }
  1823. }
  1824. };