dom.js 101 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135
  1. 'use strict';
  2. var conventions = require('./conventions');
  3. var find = conventions.find;
  4. var hasDefaultHTMLNamespace = conventions.hasDefaultHTMLNamespace;
  5. var hasOwn = conventions.hasOwn;
  6. var isHTMLMimeType = conventions.isHTMLMimeType;
  7. var isHTMLRawTextElement = conventions.isHTMLRawTextElement;
  8. var isHTMLVoidElement = conventions.isHTMLVoidElement;
  9. var MIME_TYPE = conventions.MIME_TYPE;
  10. var NAMESPACE = conventions.NAMESPACE;
  11. /**
  12. * Private DOM Constructor symbol
  13. *
  14. * Internal symbol used for construction of all classes whose constructors should be private.
  15. * Currently used for checks in `Node`, `Document`, `Element`, `Attr`, `CharacterData`, `Text`, `Comment`,
  16. * `CDATASection`, `DocumentType`, `Notation`, `Entity`, `EntityReference`, `DocumentFragment`, `ProcessingInstruction`
  17. * so the constructor can't be used from outside the module.
  18. */
  19. var PDC = Symbol();
  20. var errors = require('./errors');
  21. var DOMException = errors.DOMException;
  22. var DOMExceptionName = errors.DOMExceptionName;
  23. var g = require('./grammar');
  24. /**
  25. * Checks if the given symbol equals the Private DOM Constructor symbol (PDC)
  26. * and throws an Illegal constructor exception when the symbols don't match.
  27. * This ensures that the constructor remains private and can't be used outside this module.
  28. */
  29. function checkSymbol(symbol) {
  30. if (symbol !== PDC) {
  31. throw new TypeError('Illegal constructor');
  32. }
  33. }
  34. /**
  35. * A prerequisite for `[].filter`, to drop elements that are empty.
  36. *
  37. * @param {string} input
  38. * The string to be checked.
  39. * @returns {boolean}
  40. * Returns `true` if the input string is not empty, `false` otherwise.
  41. */
  42. function notEmptyString(input) {
  43. return input !== '';
  44. }
  45. /**
  46. * Splits a string on ASCII whitespace characters (U+0009 TAB, U+000A LF, U+000C FF, U+000D CR,
  47. * U+0020 SPACE).
  48. * It follows the definition from the infra specification from WHATWG.
  49. *
  50. * @param {string} input
  51. * The string to be split.
  52. * @returns {string[]}
  53. * An array of the split strings. The array can be empty if the input string is empty or only
  54. * contains whitespace characters.
  55. * @see {@link https://infra.spec.whatwg.org/#split-on-ascii-whitespace}
  56. * @see {@link https://infra.spec.whatwg.org/#ascii-whitespace}
  57. */
  58. function splitOnASCIIWhitespace(input) {
  59. // U+0009 TAB, U+000A LF, U+000C FF, U+000D CR, U+0020 SPACE
  60. return input ? input.split(/[\t\n\f\r ]+/).filter(notEmptyString) : [];
  61. }
  62. /**
  63. * Adds element as a key to current if it is not already present.
  64. *
  65. * @param {Record<string, boolean | undefined>} current
  66. * The current record object to which the element will be added as a key.
  67. * The object's keys are string types and values are either boolean or undefined.
  68. * @param {string} element
  69. * The string to be added as a key to the current record.
  70. * @returns {Record<string, boolean | undefined>}
  71. * The updated record object after the addition of the new element.
  72. */
  73. function orderedSetReducer(current, element) {
  74. if (!hasOwn(current, element)) {
  75. current[element] = true;
  76. }
  77. return current;
  78. }
  79. /**
  80. * Converts a string into an ordered set by splitting the input on ASCII whitespace and
  81. * ensuring uniqueness of elements.
  82. * This follows the definition of an ordered set from the infra specification by WHATWG.
  83. *
  84. * @param {string} input
  85. * The input string to be transformed into an ordered set.
  86. * @returns {string[]}
  87. * An array of unique strings obtained from the input, preserving the original order.
  88. * The array can be empty if the input string is empty or only contains whitespace characters.
  89. * @see {@link https://infra.spec.whatwg.org/#ordered-set}
  90. */
  91. function toOrderedSet(input) {
  92. if (!input) return [];
  93. var list = splitOnASCIIWhitespace(input);
  94. return Object.keys(list.reduce(orderedSetReducer, {}));
  95. }
  96. /**
  97. * Uses `list.indexOf` to implement a function that behaves like `Array.prototype.includes`.
  98. * This function is used in environments where `Array.prototype.includes` may not be available.
  99. *
  100. * @param {any[]} list
  101. * The array in which to search for the element.
  102. * @returns {function(any): boolean}
  103. * A function that accepts an element and returns a boolean indicating whether the element is
  104. * included in the provided list.
  105. */
  106. function arrayIncludes(list) {
  107. return function (element) {
  108. return list && list.indexOf(element) !== -1;
  109. };
  110. }
  111. /**
  112. * Validates a qualified name based on the criteria provided in the DOM specification by
  113. * WHATWG.
  114. *
  115. * @param {string} qualifiedName
  116. * The qualified name to be validated.
  117. * @throws {DOMException}
  118. * With code {@link DOMException.INVALID_CHARACTER_ERR} if the qualified name contains an
  119. * invalid character.
  120. * @see {@link https://dom.spec.whatwg.org/#validate}
  121. */
  122. function validateQualifiedName(qualifiedName) {
  123. if (!g.QName_exact.test(qualifiedName)) {
  124. throw new DOMException(DOMException.INVALID_CHARACTER_ERR, 'invalid character in qualified name "' + qualifiedName + '"');
  125. }
  126. }
  127. /**
  128. * Validates a qualified name and the namespace associated with it,
  129. * based on the criteria provided in the DOM specification by WHATWG.
  130. *
  131. * @param {string | null} namespace
  132. * The namespace to be validated. It can be a string or null.
  133. * @param {string} qualifiedName
  134. * The qualified name to be validated.
  135. * @returns {[namespace: string | null, prefix: string | null, localName: string]}
  136. * Returns a tuple with the namespace,
  137. * prefix and local name of the qualified name.
  138. * @throws {DOMException}
  139. * Throws a DOMException if the qualified name or the namespace is not valid.
  140. * @see {@link https://dom.spec.whatwg.org/#validate-and-extract}
  141. */
  142. function validateAndExtract(namespace, qualifiedName) {
  143. validateQualifiedName(qualifiedName);
  144. namespace = namespace || null;
  145. /**
  146. * @type {string | null}
  147. */
  148. var prefix = null;
  149. var localName = qualifiedName;
  150. if (qualifiedName.indexOf(':') >= 0) {
  151. var splitResult = qualifiedName.split(':');
  152. prefix = splitResult[0];
  153. localName = splitResult[1];
  154. }
  155. if (prefix !== null && namespace === null) {
  156. throw new DOMException(DOMException.NAMESPACE_ERR, 'prefix is non-null and namespace is null');
  157. }
  158. if (prefix === 'xml' && namespace !== conventions.NAMESPACE.XML) {
  159. throw new DOMException(DOMException.NAMESPACE_ERR, 'prefix is "xml" and namespace is not the XML namespace');
  160. }
  161. if ((prefix === 'xmlns' || qualifiedName === 'xmlns') && namespace !== conventions.NAMESPACE.XMLNS) {
  162. throw new DOMException(
  163. DOMException.NAMESPACE_ERR,
  164. 'either qualifiedName or prefix is "xmlns" and namespace is not the XMLNS namespace'
  165. );
  166. }
  167. if (namespace === conventions.NAMESPACE.XMLNS && prefix !== 'xmlns' && qualifiedName !== 'xmlns') {
  168. throw new DOMException(
  169. DOMException.NAMESPACE_ERR,
  170. 'namespace is the XMLNS namespace and neither qualifiedName nor prefix is "xmlns"'
  171. );
  172. }
  173. return [namespace, prefix, localName];
  174. }
  175. /**
  176. * Copies properties from one object to another.
  177. * It only copies the object's own (not inherited) properties.
  178. *
  179. * @param {Object} src
  180. * The source object from which properties are copied.
  181. * @param {Object} dest
  182. * The destination object to which properties are copied.
  183. */
  184. function copy(src, dest) {
  185. for (var p in src) {
  186. if (hasOwn(src, p)) {
  187. dest[p] = src[p];
  188. }
  189. }
  190. }
  191. /**
  192. * Extends a class with the properties and methods of a super class.
  193. * It uses a form of prototypal inheritance, and establishes the `constructor` property
  194. * correctly(?).
  195. *
  196. * It is not clear to the current maintainers if this implementation is making sense,
  197. * since it creates an intermediate prototype function,
  198. * which all properties of `Super` are copied onto using `_copy`.
  199. *
  200. * @param {Object} Class
  201. * The class that is to be extended.
  202. * @param {Object} Super
  203. * The super class from which properties and methods are inherited.
  204. * @private
  205. */
  206. function _extends(Class, Super) {
  207. var pt = Class.prototype;
  208. if (!(pt instanceof Super)) {
  209. function t() {}
  210. t.prototype = Super.prototype;
  211. t = new t();
  212. copy(pt, t);
  213. Class.prototype = pt = t;
  214. }
  215. if (pt.constructor != Class) {
  216. if (typeof Class != 'function') {
  217. console.error('unknown Class:' + Class);
  218. }
  219. pt.constructor = Class;
  220. }
  221. }
  222. var NodeType = {};
  223. var ELEMENT_NODE = (NodeType.ELEMENT_NODE = 1);
  224. var ATTRIBUTE_NODE = (NodeType.ATTRIBUTE_NODE = 2);
  225. var TEXT_NODE = (NodeType.TEXT_NODE = 3);
  226. var CDATA_SECTION_NODE = (NodeType.CDATA_SECTION_NODE = 4);
  227. var ENTITY_REFERENCE_NODE = (NodeType.ENTITY_REFERENCE_NODE = 5);
  228. var ENTITY_NODE = (NodeType.ENTITY_NODE = 6);
  229. var PROCESSING_INSTRUCTION_NODE = (NodeType.PROCESSING_INSTRUCTION_NODE = 7);
  230. var COMMENT_NODE = (NodeType.COMMENT_NODE = 8);
  231. var DOCUMENT_NODE = (NodeType.DOCUMENT_NODE = 9);
  232. var DOCUMENT_TYPE_NODE = (NodeType.DOCUMENT_TYPE_NODE = 10);
  233. var DOCUMENT_FRAGMENT_NODE = (NodeType.DOCUMENT_FRAGMENT_NODE = 11);
  234. var NOTATION_NODE = (NodeType.NOTATION_NODE = 12);
  235. var DocumentPosition = conventions.freeze({
  236. DOCUMENT_POSITION_DISCONNECTED: 1,
  237. DOCUMENT_POSITION_PRECEDING: 2,
  238. DOCUMENT_POSITION_FOLLOWING: 4,
  239. DOCUMENT_POSITION_CONTAINS: 8,
  240. DOCUMENT_POSITION_CONTAINED_BY: 16,
  241. DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC: 32,
  242. });
  243. //helper functions for compareDocumentPosition
  244. /**
  245. * Finds the common ancestor in two parent chains.
  246. *
  247. * @param {Node[]} a
  248. * The first parent chain.
  249. * @param {Node[]} b
  250. * The second parent chain.
  251. * @returns {Node}
  252. * The common ancestor node if it exists. If there is no common ancestor, the function will
  253. * return `null`.
  254. */
  255. function commonAncestor(a, b) {
  256. if (b.length < a.length) return commonAncestor(b, a);
  257. var c = null;
  258. for (var n in a) {
  259. if (a[n] !== b[n]) return c;
  260. c = a[n];
  261. }
  262. return c;
  263. }
  264. /**
  265. * Assigns a unique identifier to a document to ensure consistency while comparing unrelated
  266. * nodes.
  267. *
  268. * @param {Document} doc
  269. * The document to which a unique identifier is to be assigned.
  270. * @returns {string}
  271. * The unique identifier of the document. If the document already had a unique identifier, the
  272. * function will return the existing one.
  273. */
  274. function docGUID(doc) {
  275. if (!doc.guid) doc.guid = Math.random();
  276. return doc.guid;
  277. }
  278. //-- end of helper functions
  279. /**
  280. * The NodeList interface provides the abstraction of an ordered collection of nodes,
  281. * without defining or constraining how this collection is implemented.
  282. * NodeList objects in the DOM are live.
  283. * The items in the NodeList are accessible via an integral index, starting from 0.
  284. * You can also access the items of the NodeList with a `for...of` loop.
  285. *
  286. * @class NodeList
  287. * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-536297177
  288. * @constructs NodeList
  289. */
  290. function NodeList() {}
  291. NodeList.prototype = {
  292. /**
  293. * The number of nodes in the list. The range of valid child node indices is 0 to length-1
  294. * inclusive.
  295. *
  296. * @type {number}
  297. */
  298. length: 0,
  299. /**
  300. * Returns the item at `index`. If index is greater than or equal to the number of nodes in
  301. * the list, this returns null.
  302. *
  303. * @param index
  304. * Unsigned long Index into the collection.
  305. * @returns {Node | null}
  306. * The node at position `index` in the NodeList,
  307. * or null if that is not a valid index.
  308. */
  309. item: function (index) {
  310. return index >= 0 && index < this.length ? this[index] : null;
  311. },
  312. /**
  313. * Returns a string representation of the NodeList.
  314. *
  315. * @param {unknown} nodeFilter
  316. * __A filter function? Not implemented according to the spec?__.
  317. * @returns {string}
  318. * A string representation of the NodeList.
  319. */
  320. toString: function (nodeFilter) {
  321. for (var buf = [], i = 0; i < this.length; i++) {
  322. serializeToString(this[i], buf, nodeFilter);
  323. }
  324. return buf.join('');
  325. },
  326. /**
  327. * Filters the NodeList based on a predicate.
  328. *
  329. * @param {function(Node): boolean} predicate
  330. * - A predicate function to filter the NodeList.
  331. * @returns {Node[]}
  332. * An array of nodes that satisfy the predicate.
  333. * @private
  334. */
  335. filter: function (predicate) {
  336. return Array.prototype.filter.call(this, predicate);
  337. },
  338. /**
  339. * Returns the first index at which a given node can be found in the NodeList, or -1 if it is
  340. * not present.
  341. *
  342. * @param {Node} item
  343. * - The Node item to locate in the NodeList.
  344. * @returns {number}
  345. * The first index of the node in the NodeList; -1 if not found.
  346. * @private
  347. */
  348. indexOf: function (item) {
  349. return Array.prototype.indexOf.call(this, item);
  350. },
  351. };
  352. NodeList.prototype[Symbol.iterator] = function () {
  353. var me = this;
  354. var index = 0;
  355. return {
  356. next: function () {
  357. if (index < me.length) {
  358. return {
  359. value: me[index++],
  360. done: false,
  361. };
  362. } else {
  363. return {
  364. done: true,
  365. };
  366. }
  367. },
  368. return: function () {
  369. return {
  370. done: true,
  371. };
  372. },
  373. };
  374. };
  375. /**
  376. * Represents a live collection of nodes that is automatically updated when its associated
  377. * document changes.
  378. *
  379. * @class LiveNodeList
  380. * @param {Node} node
  381. * The associated node.
  382. * @param {function} refresh
  383. * The function to refresh the live node list.
  384. * @augments NodeList
  385. * @constructs LiveNodeList
  386. */
  387. function LiveNodeList(node, refresh) {
  388. this._node = node;
  389. this._refresh = refresh;
  390. _updateLiveList(this);
  391. }
  392. /**
  393. * Updates the live node list.
  394. *
  395. * @param {LiveNodeList} list
  396. * The live node list to update.
  397. * @private
  398. */
  399. function _updateLiveList(list) {
  400. var inc = list._node._inc || list._node.ownerDocument._inc;
  401. if (list._inc !== inc) {
  402. var ls = list._refresh(list._node);
  403. __set__(list, 'length', ls.length);
  404. if (!list.$$length || ls.length < list.$$length) {
  405. for (var i = ls.length; i in list; i++) {
  406. if (hasOwn(list, i)) {
  407. delete list[i];
  408. }
  409. }
  410. }
  411. copy(ls, list);
  412. list._inc = inc;
  413. }
  414. }
  415. /**
  416. * Returns the node at position `index` in the LiveNodeList, or null if that is not a valid
  417. * index.
  418. *
  419. * @param {number} i
  420. * Index into the collection.
  421. * @returns {Node | null}
  422. * The node at position `index` in the LiveNodeList, or null if that is not a valid index.
  423. */
  424. LiveNodeList.prototype.item = function (i) {
  425. _updateLiveList(this);
  426. return this[i] || null;
  427. };
  428. _extends(LiveNodeList, NodeList);
  429. /**
  430. * Objects implementing the NamedNodeMap interface are used to represent collections of nodes
  431. * that can be accessed by name.
  432. * Note that NamedNodeMap does not inherit from NodeList;
  433. * NamedNodeMaps are not maintained in any particular order.
  434. * Objects contained in an object implementing NamedNodeMap may also be accessed by an ordinal
  435. * index,
  436. * but this is simply to allow convenient enumeration of the contents of a NamedNodeMap,
  437. * and does not imply that the DOM specifies an order to these Nodes.
  438. * NamedNodeMap objects in the DOM are live.
  439. * used for attributes or DocumentType entities
  440. *
  441. * This implementation only supports property indices, but does not support named properties,
  442. * as specified in the living standard.
  443. *
  444. * @class NamedNodeMap
  445. * @see https://dom.spec.whatwg.org/#interface-namednodemap
  446. * @see https://webidl.spec.whatwg.org/#dfn-supported-property-names
  447. * @constructs NamedNodeMap
  448. */
  449. function NamedNodeMap() {}
  450. /**
  451. * Returns the index of a node within the list.
  452. *
  453. * @param {Array} list
  454. * The list of nodes.
  455. * @param {Node} node
  456. * The node to find.
  457. * @returns {number}
  458. * The index of the node within the list, or -1 if not found.
  459. * @private
  460. */
  461. function _findNodeIndex(list, node) {
  462. var i = 0;
  463. while (i < list.length) {
  464. if (list[i] === node) {
  465. return i;
  466. }
  467. i++;
  468. }
  469. }
  470. /**
  471. * Adds a new attribute to the list and updates the owner element of the attribute.
  472. *
  473. * @param {Element} el
  474. * The element which will become the owner of the new attribute.
  475. * @param {NamedNodeMap} list
  476. * The list to which the new attribute will be added.
  477. * @param {Attr} newAttr
  478. * The new attribute to be added.
  479. * @param {Attr} oldAttr
  480. * The old attribute to be replaced, or null if no attribute is to be replaced.
  481. * @returns {void}
  482. * @private
  483. */
  484. function _addNamedNode(el, list, newAttr, oldAttr) {
  485. if (oldAttr) {
  486. list[_findNodeIndex(list, oldAttr)] = newAttr;
  487. } else {
  488. list[list.length] = newAttr;
  489. list.length++;
  490. }
  491. if (el) {
  492. newAttr.ownerElement = el;
  493. var doc = el.ownerDocument;
  494. if (doc) {
  495. oldAttr && _onRemoveAttribute(doc, el, oldAttr);
  496. _onAddAttribute(doc, el, newAttr);
  497. }
  498. }
  499. }
  500. /**
  501. * Removes an attribute from the list and updates the owner element of the attribute.
  502. *
  503. * @param {Element} el
  504. * The element which is the current owner of the attribute.
  505. * @param {NamedNodeMap} list
  506. * The list from which the attribute will be removed.
  507. * @param {Attr} attr
  508. * The attribute to be removed.
  509. * @returns {void}
  510. * @private
  511. */
  512. function _removeNamedNode(el, list, attr) {
  513. //console.log('remove attr:'+attr)
  514. var i = _findNodeIndex(list, attr);
  515. if (i >= 0) {
  516. var lastIndex = list.length - 1;
  517. while (i <= lastIndex) {
  518. list[i] = list[++i];
  519. }
  520. list.length = lastIndex;
  521. if (el) {
  522. var doc = el.ownerDocument;
  523. if (doc) {
  524. _onRemoveAttribute(doc, el, attr);
  525. }
  526. attr.ownerElement = null;
  527. }
  528. }
  529. }
  530. NamedNodeMap.prototype = {
  531. length: 0,
  532. item: NodeList.prototype.item,
  533. /**
  534. * Get an attribute by name. Note: Name is in lower case in case of HTML namespace and
  535. * document.
  536. *
  537. * @param {string} localName
  538. * The local name of the attribute.
  539. * @returns {Attr | null}
  540. * The attribute with the given local name, or null if no such attribute exists.
  541. * @see https://dom.spec.whatwg.org/#concept-element-attributes-get-by-name
  542. */
  543. getNamedItem: function (localName) {
  544. if (this._ownerElement && this._ownerElement._isInHTMLDocumentAndNamespace()) {
  545. localName = localName.toLowerCase();
  546. }
  547. var i = 0;
  548. while (i < this.length) {
  549. var attr = this[i];
  550. if (attr.nodeName === localName) {
  551. return attr;
  552. }
  553. i++;
  554. }
  555. return null;
  556. },
  557. /**
  558. * Set an attribute.
  559. *
  560. * @param {Attr} attr
  561. * The attribute to set.
  562. * @returns {Attr | null}
  563. * The old attribute with the same local name and namespace URI as the new one, or null if no
  564. * such attribute exists.
  565. * @throws {DOMException}
  566. * With code:
  567. * - {@link INUSE_ATTRIBUTE_ERR} - If the attribute is already an attribute of another
  568. * element.
  569. * @see https://dom.spec.whatwg.org/#concept-element-attributes-set
  570. */
  571. setNamedItem: function (attr) {
  572. var el = attr.ownerElement;
  573. if (el && el !== this._ownerElement) {
  574. throw new DOMException(DOMException.INUSE_ATTRIBUTE_ERR);
  575. }
  576. var oldAttr = this.getNamedItemNS(attr.namespaceURI, attr.localName);
  577. if (oldAttr === attr) {
  578. return attr;
  579. }
  580. _addNamedNode(this._ownerElement, this, attr, oldAttr);
  581. return oldAttr;
  582. },
  583. /**
  584. * Set an attribute, replacing an existing attribute with the same local name and namespace
  585. * URI if one exists.
  586. *
  587. * @param {Attr} attr
  588. * The attribute to set.
  589. * @returns {Attr | null}
  590. * The old attribute with the same local name and namespace URI as the new one, or null if no
  591. * such attribute exists.
  592. * @throws {DOMException}
  593. * Throws a DOMException with the name "InUseAttributeError" if the attribute is already an
  594. * attribute of another element.
  595. * @see https://dom.spec.whatwg.org/#concept-element-attributes-set
  596. */
  597. setNamedItemNS: function (attr) {
  598. return this.setNamedItem(attr);
  599. },
  600. /**
  601. * Removes an attribute specified by the local name.
  602. *
  603. * @param {string} localName
  604. * The local name of the attribute to be removed.
  605. * @returns {Attr}
  606. * The attribute node that was removed.
  607. * @throws {DOMException}
  608. * With code:
  609. * - {@link DOMException.NOT_FOUND_ERR} if no attribute with the given name is found.
  610. * @see https://dom.spec.whatwg.org/#dom-namednodemap-removenameditem
  611. * @see https://dom.spec.whatwg.org/#concept-element-attributes-remove-by-name
  612. */
  613. removeNamedItem: function (localName) {
  614. var attr = this.getNamedItem(localName);
  615. if (!attr) {
  616. throw new DOMException(DOMException.NOT_FOUND_ERR, localName);
  617. }
  618. _removeNamedNode(this._ownerElement, this, attr);
  619. return attr;
  620. },
  621. /**
  622. * Removes an attribute specified by the namespace and local name.
  623. *
  624. * @param {string | null} namespaceURI
  625. * The namespace URI of the attribute to be removed.
  626. * @param {string} localName
  627. * The local name of the attribute to be removed.
  628. * @returns {Attr}
  629. * The attribute node that was removed.
  630. * @throws {DOMException}
  631. * With code:
  632. * - {@link DOMException.NOT_FOUND_ERR} if no attribute with the given namespace URI and local
  633. * name is found.
  634. * @see https://dom.spec.whatwg.org/#dom-namednodemap-removenameditemns
  635. * @see https://dom.spec.whatwg.org/#concept-element-attributes-remove-by-namespace
  636. */
  637. removeNamedItemNS: function (namespaceURI, localName) {
  638. var attr = this.getNamedItemNS(namespaceURI, localName);
  639. if (!attr) {
  640. throw new DOMException(DOMException.NOT_FOUND_ERR, namespaceURI ? namespaceURI + ' : ' + localName : localName);
  641. }
  642. _removeNamedNode(this._ownerElement, this, attr);
  643. return attr;
  644. },
  645. /**
  646. * Get an attribute by namespace and local name.
  647. *
  648. * @param {string | null} namespaceURI
  649. * The namespace URI of the attribute.
  650. * @param {string} localName
  651. * The local name of the attribute.
  652. * @returns {Attr | null}
  653. * The attribute with the given namespace URI and local name, or null if no such attribute
  654. * exists.
  655. * @see https://dom.spec.whatwg.org/#concept-element-attributes-get-by-namespace
  656. */
  657. getNamedItemNS: function (namespaceURI, localName) {
  658. if (!namespaceURI) {
  659. namespaceURI = null;
  660. }
  661. var i = 0;
  662. while (i < this.length) {
  663. var node = this[i];
  664. if (node.localName === localName && node.namespaceURI === namespaceURI) {
  665. return node;
  666. }
  667. i++;
  668. }
  669. return null;
  670. },
  671. };
  672. NamedNodeMap.prototype[Symbol.iterator] = function () {
  673. var me = this;
  674. var index = 0;
  675. return {
  676. next: function () {
  677. if (index < me.length) {
  678. return {
  679. value: me[index++],
  680. done: false,
  681. };
  682. } else {
  683. return {
  684. done: true,
  685. };
  686. }
  687. },
  688. return: function () {
  689. return {
  690. done: true,
  691. };
  692. },
  693. };
  694. };
  695. /**
  696. * The DOMImplementation interface provides a number of methods for performing operations that
  697. * are independent of any particular instance of the document object model.
  698. *
  699. * The DOMImplementation interface represents an object providing methods which are not
  700. * dependent on any particular document.
  701. * Such an object is returned by the `Document.implementation` property.
  702. *
  703. * **The individual methods describe the differences compared to the specs**.
  704. *
  705. * @class DOMImplementation
  706. * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation MDN
  707. * @see https://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-102161490 DOM Level 1 Core
  708. * (Initial)
  709. * @see https://www.w3.org/TR/DOM-Level-2-Core/core.html#ID-102161490 DOM Level 2 Core
  710. * @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-102161490 DOM Level 3 Core
  711. * @see https://dom.spec.whatwg.org/#domimplementation DOM Living Standard
  712. * @constructs DOMImplementation
  713. */
  714. function DOMImplementation() {}
  715. DOMImplementation.prototype = {
  716. /**
  717. * Test if the DOM implementation implements a specific feature and version, as specified in
  718. * {@link https://www.w3.org/TR/DOM-Level-3-Core/core.html#DOMFeatures DOM Features}.
  719. *
  720. * The DOMImplementation.hasFeature() method returns a Boolean flag indicating if a given
  721. * feature is supported. The different implementations fairly diverged in what kind of
  722. * features were reported. The latest version of the spec settled to force this method to
  723. * always return true, where the functionality was accurate and in use.
  724. *
  725. * @deprecated
  726. * It is deprecated and modern browsers return true in all cases.
  727. * @function DOMImplementation#hasFeature
  728. * @param {string} feature
  729. * The name of the feature to test.
  730. * @param {string} [version]
  731. * This is the version number of the feature to test.
  732. * @returns {boolean}
  733. * Always returns true.
  734. * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/hasFeature MDN
  735. * @see https://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-5CED94D7 DOM Level 1 Core
  736. * @see https://dom.spec.whatwg.org/#dom-domimplementation-hasfeature DOM Living Standard
  737. * @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-5CED94D7 DOM Level 3 Core
  738. */
  739. hasFeature: function (feature, version) {
  740. return true;
  741. },
  742. /**
  743. * Creates a DOM Document object of the specified type with its document element. Note that
  744. * based on the {@link DocumentType}
  745. * given to create the document, the implementation may instantiate specialized
  746. * {@link Document} objects that support additional features than the "Core", such as "HTML"
  747. * {@link https://www.w3.org/TR/DOM-Level-3-Core/references.html#DOM2HTML DOM Level 2 HTML}.
  748. * On the other hand, setting the {@link DocumentType} after the document was created makes
  749. * this very unlikely to happen. Alternatively, specialized {@link Document} creation methods,
  750. * such as createHTMLDocument
  751. * {@link https://www.w3.org/TR/DOM-Level-3-Core/references.html#DOM2HTML DOM Level 2 HTML},
  752. * can be used to obtain specific types of {@link Document} objects.
  753. *
  754. * __It behaves slightly different from the description in the living standard__:
  755. * - There is no interface/class `XMLDocument`, it returns a `Document`
  756. * instance (with it's `type` set to `'xml'`).
  757. * - `encoding`, `mode`, `origin`, `url` fields are currently not declared.
  758. *
  759. * @function DOMImplementation.createDocument
  760. * @param {string | null} namespaceURI
  761. * The
  762. * {@link https://www.w3.org/TR/DOM-Level-3-Core/glossary.html#dt-namespaceURI namespace URI}
  763. * of the document element to create or null.
  764. * @param {string | null} qualifiedName
  765. * The
  766. * {@link https://www.w3.org/TR/DOM-Level-3-Core/glossary.html#dt-qualifiedname qualified name}
  767. * of the document element to be created or null.
  768. * @param {DocumentType | null} [doctype=null]
  769. * The type of document to be created or null. When doctype is not null, its
  770. * {@link Node#ownerDocument} attribute is set to the document being created. Default is
  771. * `null`
  772. * @returns {Document}
  773. * A new {@link Document} object with its document element. If the NamespaceURI,
  774. * qualifiedName, and doctype are null, the returned {@link Document} is empty with no
  775. * document element.
  776. * @throws {DOMException}
  777. * With code:
  778. *
  779. * - `INVALID_CHARACTER_ERR`: Raised if the specified qualified name is not an XML name
  780. * according to {@link https://www.w3.org/TR/DOM-Level-3-Core/references.html#XML XML 1.0}.
  781. * - `NAMESPACE_ERR`: Raised if the qualifiedName is malformed, if the qualifiedName has a
  782. * prefix and the namespaceURI is null, or if the qualifiedName is null and the namespaceURI
  783. * is different from null, or if the qualifiedName has a prefix that is "xml" and the
  784. * namespaceURI is different from "{@link http://www.w3.org/XML/1998/namespace}"
  785. * {@link https://www.w3.org/TR/DOM-Level-3-Core/references.html#Namespaces XML Namespaces},
  786. * or if the DOM implementation does not support the "XML" feature but a non-null namespace
  787. * URI was provided, since namespaces were defined by XML.
  788. * - `WRONG_DOCUMENT_ERR`: Raised if doctype has already been used with a different document
  789. * or was created from a different implementation.
  790. * - `NOT_SUPPORTED_ERR`: May be raised if the implementation does not support the feature
  791. * "XML" and the language exposed through the Document does not support XML Namespaces (such
  792. * as {@link https://www.w3.org/TR/DOM-Level-3-Core/references.html#HTML40 HTML 4.01}).
  793. * @since DOM Level 2.
  794. * @see {@link #createHTMLDocument}
  795. * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/createDocument MDN
  796. * @see https://dom.spec.whatwg.org/#dom-domimplementation-createdocument DOM Living Standard
  797. * @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#Level-2-Core-DOM-createDocument DOM
  798. * Level 3 Core
  799. * @see https://www.w3.org/TR/DOM-Level-2-Core/core.html#Level-2-Core-DOM-createDocument DOM
  800. * Level 2 Core (initial)
  801. */
  802. createDocument: function (namespaceURI, qualifiedName, doctype) {
  803. var contentType = MIME_TYPE.XML_APPLICATION;
  804. if (namespaceURI === NAMESPACE.HTML) {
  805. contentType = MIME_TYPE.XML_XHTML_APPLICATION;
  806. } else if (namespaceURI === NAMESPACE.SVG) {
  807. contentType = MIME_TYPE.XML_SVG_IMAGE;
  808. }
  809. var doc = new Document(PDC, { contentType: contentType });
  810. doc.implementation = this;
  811. doc.childNodes = new NodeList();
  812. doc.doctype = doctype || null;
  813. if (doctype) {
  814. doc.appendChild(doctype);
  815. }
  816. if (qualifiedName) {
  817. var root = doc.createElementNS(namespaceURI, qualifiedName);
  818. doc.appendChild(root);
  819. }
  820. return doc;
  821. },
  822. /**
  823. * Creates an empty DocumentType node. Entity declarations and notations are not made
  824. * available. Entity reference expansions and default attribute additions do not occur.
  825. *
  826. * **This behavior is slightly different from the one in the specs**:
  827. * - `encoding`, `mode`, `origin`, `url` fields are currently not declared.
  828. * - `publicId` and `systemId` contain the raw data including any possible quotes,
  829. * so they can always be serialized back to the original value
  830. * - `internalSubset` contains the raw string between `[` and `]` if present,
  831. * but is not parsed or validated in any form.
  832. *
  833. * @function DOMImplementation#createDocumentType
  834. * @param {string} qualifiedName
  835. * The {@link https://www.w3.org/TR/DOM-Level-3-Core/glossary.html#dt-qualifiedname qualified
  836. * name} of the document type to be created.
  837. * @param {string} [publicId]
  838. * The external subset public identifier.
  839. * @param {string} [systemId]
  840. * The external subset system identifier.
  841. * @param {string} [internalSubset]
  842. * the internal subset or an empty string if it is not present
  843. * @returns {DocumentType}
  844. * A new {@link DocumentType} node with {@link Node#ownerDocument} set to null.
  845. * @throws {DOMException}
  846. * With code:
  847. *
  848. * - `INVALID_CHARACTER_ERR`: Raised if the specified qualified name is not an XML name
  849. * according to {@link https://www.w3.org/TR/DOM-Level-3-Core/references.html#XML XML 1.0}.
  850. * - `NAMESPACE_ERR`: Raised if the qualifiedName is malformed.
  851. * - `NOT_SUPPORTED_ERR`: May be raised if the implementation does not support the feature
  852. * "XML" and the language exposed through the Document does not support XML Namespaces (such
  853. * as {@link https://www.w3.org/TR/DOM-Level-3-Core/references.html#HTML40 HTML 4.01}).
  854. * @since DOM Level 2.
  855. * @see https://developer.mozilla.org/en-US/docs/Web/API/DOMImplementation/createDocumentType
  856. * MDN
  857. * @see https://dom.spec.whatwg.org/#dom-domimplementation-createdocumenttype DOM Living
  858. * Standard
  859. * @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#Level-3-Core-DOM-createDocType DOM
  860. * Level 3 Core
  861. * @see https://www.w3.org/TR/DOM-Level-2-Core/core.html#Level-2-Core-DOM-createDocType DOM
  862. * Level 2 Core
  863. * @see https://github.com/xmldom/xmldom/blob/master/CHANGELOG.md#050
  864. * @see https://www.w3.org/TR/DOM-Level-2-Core/#core-ID-Core-DocType-internalSubset
  865. * @prettierignore
  866. */
  867. createDocumentType: function (qualifiedName, publicId, systemId, internalSubset) {
  868. validateQualifiedName(qualifiedName);
  869. var node = new DocumentType(PDC);
  870. node.name = qualifiedName;
  871. node.nodeName = qualifiedName;
  872. node.publicId = publicId || '';
  873. node.systemId = systemId || '';
  874. node.internalSubset = internalSubset || '';
  875. node.childNodes = new NodeList();
  876. return node;
  877. },
  878. /**
  879. * Returns an HTML document, that might already have a basic DOM structure.
  880. *
  881. * __It behaves slightly different from the description in the living standard__:
  882. * - If the first argument is `false` no initial nodes are added (steps 3-7 in the specs are
  883. * omitted)
  884. * - `encoding`, `mode`, `origin`, `url` fields are currently not declared.
  885. *
  886. * @param {string | false} [title]
  887. * A string containing the title to give the new HTML document.
  888. * @returns {Document}
  889. * The HTML document.
  890. * @since WHATWG Living Standard.
  891. * @see {@link #createDocument}
  892. * @see https://dom.spec.whatwg.org/#dom-domimplementation-createhtmldocument
  893. * @see https://dom.spec.whatwg.org/#html-document
  894. */
  895. createHTMLDocument: function (title) {
  896. var doc = new Document(PDC, { contentType: MIME_TYPE.HTML });
  897. doc.implementation = this;
  898. doc.childNodes = new NodeList();
  899. if (title !== false) {
  900. doc.doctype = this.createDocumentType('html');
  901. doc.doctype.ownerDocument = doc;
  902. doc.appendChild(doc.doctype);
  903. var htmlNode = doc.createElement('html');
  904. doc.appendChild(htmlNode);
  905. var headNode = doc.createElement('head');
  906. htmlNode.appendChild(headNode);
  907. if (typeof title === 'string') {
  908. var titleNode = doc.createElement('title');
  909. titleNode.appendChild(doc.createTextNode(title));
  910. headNode.appendChild(titleNode);
  911. }
  912. htmlNode.appendChild(doc.createElement('body'));
  913. }
  914. return doc;
  915. },
  916. };
  917. /**
  918. * The DOM Node interface is an abstract base class upon which many other DOM API objects are
  919. * based, thus letting those object types to be used similarly and often interchangeably. As an
  920. * abstract class, there is no such thing as a plain Node object. All objects that implement
  921. * Node functionality are based on one of its subclasses. Most notable are Document, Element,
  922. * and DocumentFragment.
  923. *
  924. * In addition, every kind of DOM node is represented by an interface based on Node. These
  925. * include Attr, CharacterData (which Text, Comment, CDATASection and ProcessingInstruction are
  926. * all based on), and DocumentType.
  927. *
  928. * In some cases, a particular feature of the base Node interface may not apply to one of its
  929. * child interfaces; in that case, the inheriting node may return null or throw an exception,
  930. * depending on circumstances. For example, attempting to add children to a node type that
  931. * cannot have children will throw an exception.
  932. *
  933. * **This behavior is slightly different from the in the specs**:
  934. * - unimplemented interfaces: `EventTarget`
  935. *
  936. * @class
  937. * @abstract
  938. * @param {Symbol} symbol
  939. * @see http://www.w3.org/TR/2000/REC-DOM-Level-2-Core-20001113/core.html#ID-1950641247
  940. * @see https://dom.spec.whatwg.org/#node
  941. * @prettierignore
  942. */
  943. function Node(symbol) {
  944. checkSymbol(symbol);
  945. }
  946. Node.prototype = {
  947. /**
  948. * The first child of this node.
  949. *
  950. * @type {Node | null}
  951. */
  952. firstChild: null,
  953. /**
  954. * The last child of this node.
  955. *
  956. * @type {Node | null}
  957. */
  958. lastChild: null,
  959. /**
  960. * The previous sibling of this node.
  961. *
  962. * @type {Node | null}
  963. */
  964. previousSibling: null,
  965. /**
  966. * The next sibling of this node.
  967. *
  968. * @type {Node | null}
  969. */
  970. nextSibling: null,
  971. /**
  972. * The parent node of this node.
  973. *
  974. * @type {Node | null}
  975. */
  976. parentNode: null,
  977. /**
  978. * The parent element of this node.
  979. *
  980. * @type {Element | null}
  981. */
  982. get parentElement() {
  983. return this.parentNode && this.parentNode.nodeType === this.ELEMENT_NODE ? this.parentNode : null;
  984. },
  985. /**
  986. * The child nodes of this node.
  987. *
  988. * @type {NodeList}
  989. */
  990. childNodes: null,
  991. /**
  992. * The document object associated with this node.
  993. *
  994. * @type {Document | null}
  995. */
  996. ownerDocument: null,
  997. /**
  998. * The value of this node.
  999. *
  1000. * @type {string | null}
  1001. */
  1002. nodeValue: null,
  1003. /**
  1004. * The namespace URI of this node.
  1005. *
  1006. * @type {string | null}
  1007. */
  1008. namespaceURI: null,
  1009. /**
  1010. * The prefix of the namespace for this node.
  1011. *
  1012. * @type {string | null}
  1013. */
  1014. prefix: null,
  1015. /**
  1016. * The local part of the qualified name of this node.
  1017. *
  1018. * @type {string | null}
  1019. */
  1020. localName: null,
  1021. /**
  1022. * The baseURI is currently always `about:blank`,
  1023. * since that's what happens when you create a document from scratch.
  1024. *
  1025. * @type {'about:blank'}
  1026. */
  1027. baseURI: 'about:blank',
  1028. /**
  1029. * Is true if this node is part of a document.
  1030. *
  1031. * @type {boolean}
  1032. */
  1033. get isConnected() {
  1034. var rootNode = this.getRootNode();
  1035. return rootNode && rootNode.nodeType === rootNode.DOCUMENT_NODE;
  1036. },
  1037. /**
  1038. * Checks whether `other` is an inclusive descendant of this node.
  1039. *
  1040. * @param {Node | null | undefined} other
  1041. * The node to check.
  1042. * @returns {boolean}
  1043. * True if `other` is an inclusive descendant of this node; false otherwise.
  1044. * @see https://dom.spec.whatwg.org/#dom-node-contains
  1045. */
  1046. contains: function (other) {
  1047. if (!other) return false;
  1048. var parent = other;
  1049. do {
  1050. if (this === parent) return true;
  1051. parent = other.parentNode;
  1052. } while (parent);
  1053. return false;
  1054. },
  1055. /**
  1056. * @typedef GetRootNodeOptions
  1057. * @property {boolean} [composed=false]
  1058. */
  1059. /**
  1060. * Searches for the root node of this node.
  1061. *
  1062. * **This behavior is slightly different from the in the specs**:
  1063. * - ignores `options.composed`, since `ShadowRoot`s are unsupported, always returns root.
  1064. *
  1065. * @param {GetRootNodeOptions} [options]
  1066. * @returns {Node}
  1067. * Root node.
  1068. * @see https://dom.spec.whatwg.org/#dom-node-getrootnode
  1069. * @see https://dom.spec.whatwg.org/#concept-shadow-including-root
  1070. */
  1071. getRootNode: function (options) {
  1072. var parent = this;
  1073. do {
  1074. if (!parent.parentNode) {
  1075. return parent;
  1076. }
  1077. parent = parent.parentNode;
  1078. } while (parent);
  1079. },
  1080. /**
  1081. * Checks whether the given node is equal to this node.
  1082. *
  1083. * @param {Node} [otherNode]
  1084. * @see https://dom.spec.whatwg.org/#concept-node-equals
  1085. */
  1086. isEqualNode: function (otherNode) {
  1087. if (!otherNode) return false;
  1088. if (this.nodeType !== otherNode.nodeType) return false;
  1089. switch (this.nodeType) {
  1090. case this.DOCUMENT_TYPE_NODE:
  1091. if (this.name !== otherNode.name) return false;
  1092. if (this.publicId !== otherNode.publicId) return false;
  1093. if (this.systemId !== otherNode.systemId) return false;
  1094. break;
  1095. case this.ELEMENT_NODE:
  1096. if (this.namespaceURI !== otherNode.namespaceURI) return false;
  1097. if (this.prefix !== otherNode.prefix) return false;
  1098. if (this.localName !== otherNode.localName) return false;
  1099. if (this.attributes.length !== otherNode.attributes.length) return false;
  1100. for (var i = 0; i < this.attributes.length; i++) {
  1101. var attr = this.attributes.item(i);
  1102. if (!attr.isEqualNode(otherNode.getAttributeNodeNS(attr.namespaceURI, attr.localName))) {
  1103. return false;
  1104. }
  1105. }
  1106. break;
  1107. case this.ATTRIBUTE_NODE:
  1108. if (this.namespaceURI !== otherNode.namespaceURI) return false;
  1109. if (this.localName !== otherNode.localName) return false;
  1110. if (this.value !== otherNode.value) return false;
  1111. break;
  1112. case this.PROCESSING_INSTRUCTION_NODE:
  1113. if (this.target !== otherNode.target || this.data !== otherNode.data) {
  1114. return false;
  1115. }
  1116. break;
  1117. case this.TEXT_NODE:
  1118. case this.COMMENT_NODE:
  1119. if (this.data !== otherNode.data) return false;
  1120. break;
  1121. }
  1122. if (this.childNodes.length !== otherNode.childNodes.length) {
  1123. return false;
  1124. }
  1125. for (var i = 0; i < this.childNodes.length; i++) {
  1126. if (!this.childNodes[i].isEqualNode(otherNode.childNodes[i])) {
  1127. return false;
  1128. }
  1129. }
  1130. return true;
  1131. },
  1132. /**
  1133. * Checks whether or not the given node is this node.
  1134. *
  1135. * @param {Node} [otherNode]
  1136. */
  1137. isSameNode: function (otherNode) {
  1138. return this === otherNode;
  1139. },
  1140. /**
  1141. * Inserts a node before a reference node as a child of this node.
  1142. *
  1143. * @param {Node} newChild
  1144. * The new child node to be inserted.
  1145. * @param {Node | null} refChild
  1146. * The reference node before which newChild will be inserted.
  1147. * @returns {Node}
  1148. * The new child node successfully inserted.
  1149. * @throws {DOMException}
  1150. * Throws a DOMException if inserting the node would result in a DOM tree that is not
  1151. * well-formed, or if `child` is provided but is not a child of `parent`.
  1152. * See {@link _insertBefore} for more details.
  1153. * @since Modified in DOM L2
  1154. */
  1155. insertBefore: function (newChild, refChild) {
  1156. return _insertBefore(this, newChild, refChild);
  1157. },
  1158. /**
  1159. * Replaces an old child node with a new child node within this node.
  1160. *
  1161. * @param {Node} newChild
  1162. * The new node that is to replace the old node.
  1163. * If it already exists in the DOM, it is removed from its original position.
  1164. * @param {Node} oldChild
  1165. * The existing child node to be replaced.
  1166. * @returns {Node}
  1167. * Returns the replaced child node.
  1168. * @throws {DOMException}
  1169. * Throws a DOMException if replacing the node would result in a DOM tree that is not
  1170. * well-formed, or if `oldChild` is not a child of `this`.
  1171. * This can also occur if the pre-replacement validity assertion fails.
  1172. * See {@link _insertBefore}, {@link Node.removeChild}, and
  1173. * {@link assertPreReplacementValidityInDocument} for more details.
  1174. * @see https://dom.spec.whatwg.org/#concept-node-replace
  1175. */
  1176. replaceChild: function (newChild, oldChild) {
  1177. _insertBefore(this, newChild, oldChild, assertPreReplacementValidityInDocument);
  1178. if (oldChild) {
  1179. this.removeChild(oldChild);
  1180. }
  1181. },
  1182. /**
  1183. * Removes an existing child node from this node.
  1184. *
  1185. * @param {Node} oldChild
  1186. * The child node to be removed.
  1187. * @returns {Node}
  1188. * Returns the removed child node.
  1189. * @throws {DOMException}
  1190. * Throws a DOMException if `oldChild` is not a child of `this`.
  1191. * See {@link _removeChild} for more details.
  1192. */
  1193. removeChild: function (oldChild) {
  1194. return _removeChild(this, oldChild);
  1195. },
  1196. /**
  1197. * Appends a child node to this node.
  1198. *
  1199. * @param {Node} newChild
  1200. * The child node to be appended to this node.
  1201. * If it already exists in the DOM, it is removed from its original position.
  1202. * @returns {Node}
  1203. * Returns the appended child node.
  1204. * @throws {DOMException}
  1205. * Throws a DOMException if appending the node would result in a DOM tree that is not
  1206. * well-formed, or if `newChild` is not a valid Node.
  1207. * See {@link insertBefore} for more details.
  1208. */
  1209. appendChild: function (newChild) {
  1210. return this.insertBefore(newChild, null);
  1211. },
  1212. /**
  1213. * Determines whether this node has any child nodes.
  1214. *
  1215. * @returns {boolean}
  1216. * Returns true if this node has any child nodes, and false otherwise.
  1217. */
  1218. hasChildNodes: function () {
  1219. return this.firstChild != null;
  1220. },
  1221. /**
  1222. * Creates a copy of the calling node.
  1223. *
  1224. * @param {boolean} deep
  1225. * If true, the contents of the node are recursively copied.
  1226. * If false, only the node itself (and its attributes, if it is an element) are copied.
  1227. * @returns {Node}
  1228. * Returns the newly created copy of the node.
  1229. * @throws {DOMException}
  1230. * May throw a DOMException if operations within {@link Element#setAttributeNode} or
  1231. * {@link Node#appendChild} (which are potentially invoked in this method) do not meet their
  1232. * specific constraints.
  1233. * @see {@link cloneNode}
  1234. */
  1235. cloneNode: function (deep) {
  1236. return cloneNode(this.ownerDocument || this, this, deep);
  1237. },
  1238. /**
  1239. * Puts the specified node and all of its subtree into a "normalized" form. In a normalized
  1240. * subtree, no text nodes in the subtree are empty and there are no adjacent text nodes.
  1241. *
  1242. * Specifically, this method merges any adjacent text nodes (i.e., nodes for which `nodeType`
  1243. * is `TEXT_NODE`) into a single node with the combined data. It also removes any empty text
  1244. * nodes.
  1245. *
  1246. * This method operates recursively, so it also normalizes any and all descendent nodes within
  1247. * the subtree.
  1248. *
  1249. * @throws {DOMException}
  1250. * May throw a DOMException if operations within removeChild or appendData (which are
  1251. * potentially invoked in this method) do not meet their specific constraints.
  1252. * @since Modified in DOM Level 2
  1253. * @see {@link Node.removeChild}
  1254. * @see {@link CharacterData.appendData}
  1255. */
  1256. normalize: function () {
  1257. var child = this.firstChild;
  1258. while (child) {
  1259. var next = child.nextSibling;
  1260. if (next && next.nodeType == TEXT_NODE && child.nodeType == TEXT_NODE) {
  1261. this.removeChild(next);
  1262. child.appendData(next.data);
  1263. } else {
  1264. child.normalize();
  1265. child = next;
  1266. }
  1267. }
  1268. },
  1269. /**
  1270. * Checks whether the DOM implementation implements a specific feature and its version.
  1271. *
  1272. * @deprecated
  1273. * Since `DOMImplementation.hasFeature` is deprecated and always returns true.
  1274. * @param {string} feature
  1275. * The package name of the feature to test. This is the same name that can be passed to the
  1276. * method `hasFeature` on `DOMImplementation`.
  1277. * @param {string} version
  1278. * This is the version number of the package name to test.
  1279. * @returns {boolean}
  1280. * Returns true in all cases in the current implementation.
  1281. * @since Introduced in DOM Level 2
  1282. * @see {@link DOMImplementation.hasFeature}
  1283. */
  1284. isSupported: function (feature, version) {
  1285. return this.ownerDocument.implementation.hasFeature(feature, version);
  1286. },
  1287. /**
  1288. * Look up the prefix associated to the given namespace URI, starting from this node.
  1289. * **The default namespace declarations are ignored by this method.**
  1290. * See Namespace Prefix Lookup for details on the algorithm used by this method.
  1291. *
  1292. * **This behavior is different from the in the specs**:
  1293. * - no node type specific handling
  1294. * - uses the internal attribute _nsMap for resolving namespaces that is updated when changing attributes
  1295. *
  1296. * @param {string | null} namespaceURI
  1297. * The namespace URI for which to find the associated prefix.
  1298. * @returns {string | null}
  1299. * The associated prefix, if found; otherwise, null.
  1300. * @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespacePrefix
  1301. * @see https://www.w3.org/TR/DOM-Level-3-Core/namespaces-algorithms.html#lookupNamespacePrefixAlgo
  1302. * @see https://dom.spec.whatwg.org/#dom-node-lookupprefix
  1303. * @see https://github.com/xmldom/xmldom/issues/322
  1304. * @prettierignore
  1305. */
  1306. lookupPrefix: function (namespaceURI) {
  1307. var el = this;
  1308. while (el) {
  1309. var map = el._nsMap;
  1310. //console.dir(map)
  1311. if (map) {
  1312. for (var n in map) {
  1313. if (hasOwn(map, n) && map[n] === namespaceURI) {
  1314. return n;
  1315. }
  1316. }
  1317. }
  1318. el = el.nodeType == ATTRIBUTE_NODE ? el.ownerDocument : el.parentNode;
  1319. }
  1320. return null;
  1321. },
  1322. /**
  1323. * This function is used to look up the namespace URI associated with the given prefix,
  1324. * starting from this node.
  1325. *
  1326. * **This behavior is different from the in the specs**:
  1327. * - no node type specific handling
  1328. * - uses the internal attribute _nsMap for resolving namespaces that is updated when changing attributes
  1329. *
  1330. * @param {string | null} prefix
  1331. * The prefix for which to find the associated namespace URI.
  1332. * @returns {string | null}
  1333. * The associated namespace URI, if found; otherwise, null.
  1334. * @since DOM Level 3
  1335. * @see https://dom.spec.whatwg.org/#dom-node-lookupnamespaceuri
  1336. * @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-lookupNamespaceURI
  1337. * @prettierignore
  1338. */
  1339. lookupNamespaceURI: function (prefix) {
  1340. var el = this;
  1341. while (el) {
  1342. var map = el._nsMap;
  1343. //console.dir(map)
  1344. if (map) {
  1345. if (hasOwn(map, prefix)) {
  1346. return map[prefix];
  1347. }
  1348. }
  1349. el = el.nodeType == ATTRIBUTE_NODE ? el.ownerDocument : el.parentNode;
  1350. }
  1351. return null;
  1352. },
  1353. /**
  1354. * Determines whether the given namespace URI is the default namespace.
  1355. *
  1356. * The function works by looking up the prefix associated with the given namespace URI. If no
  1357. * prefix is found (i.e., the namespace URI is not registered in the namespace map of this
  1358. * node or any of its ancestors), it returns `true`, implying the namespace URI is considered
  1359. * the default.
  1360. *
  1361. * **This behavior is different from the in the specs**:
  1362. * - no node type specific handling
  1363. * - uses the internal attribute _nsMap for resolving namespaces that is updated when changing attributes
  1364. *
  1365. * @param {string | null} namespaceURI
  1366. * The namespace URI to be checked.
  1367. * @returns {boolean}
  1368. * Returns true if the given namespace URI is the default namespace, false otherwise.
  1369. * @since DOM Level 3
  1370. * @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#Node3-isDefaultNamespace
  1371. * @see https://dom.spec.whatwg.org/#dom-node-isdefaultnamespace
  1372. * @prettierignore
  1373. */
  1374. isDefaultNamespace: function (namespaceURI) {
  1375. var prefix = this.lookupPrefix(namespaceURI);
  1376. return prefix == null;
  1377. },
  1378. /**
  1379. * Compares the reference node with a node with regard to their position in the document and
  1380. * according to the document order.
  1381. *
  1382. * @param {Node} other
  1383. * The node to compare the reference node to.
  1384. * @returns {number}
  1385. * Returns how the node is positioned relatively to the reference node according to the
  1386. * bitmask. 0 if reference node and given node are the same.
  1387. * @since DOM Level 3
  1388. * @see https://www.w3.org/TR/2004/REC-DOM-Level-3-Core-20040407/core.html#Node3-compare
  1389. * @see https://dom.spec.whatwg.org/#dom-node-comparedocumentposition
  1390. */
  1391. compareDocumentPosition: function (other) {
  1392. if (this === other) return 0;
  1393. var node1 = other;
  1394. var node2 = this;
  1395. var attr1 = null;
  1396. var attr2 = null;
  1397. if (node1 instanceof Attr) {
  1398. attr1 = node1;
  1399. node1 = attr1.ownerElement;
  1400. }
  1401. if (node2 instanceof Attr) {
  1402. attr2 = node2;
  1403. node2 = attr2.ownerElement;
  1404. if (attr1 && node1 && node2 === node1) {
  1405. for (var i = 0, attr; (attr = node2.attributes[i]); i++) {
  1406. if (attr === attr1)
  1407. return DocumentPosition.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC + DocumentPosition.DOCUMENT_POSITION_PRECEDING;
  1408. if (attr === attr2)
  1409. return DocumentPosition.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC + DocumentPosition.DOCUMENT_POSITION_FOLLOWING;
  1410. }
  1411. }
  1412. }
  1413. if (!node1 || !node2 || node2.ownerDocument !== node1.ownerDocument) {
  1414. return (
  1415. DocumentPosition.DOCUMENT_POSITION_DISCONNECTED +
  1416. DocumentPosition.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC +
  1417. (docGUID(node2.ownerDocument) > docGUID(node1.ownerDocument)
  1418. ? DocumentPosition.DOCUMENT_POSITION_FOLLOWING
  1419. : DocumentPosition.DOCUMENT_POSITION_PRECEDING)
  1420. );
  1421. }
  1422. if (attr2 && node1 === node2) {
  1423. return DocumentPosition.DOCUMENT_POSITION_CONTAINS + DocumentPosition.DOCUMENT_POSITION_PRECEDING;
  1424. }
  1425. if (attr1 && node1 === node2) {
  1426. return DocumentPosition.DOCUMENT_POSITION_CONTAINED_BY + DocumentPosition.DOCUMENT_POSITION_FOLLOWING;
  1427. }
  1428. var chain1 = [];
  1429. var ancestor1 = node1.parentNode;
  1430. while (ancestor1) {
  1431. if (!attr2 && ancestor1 === node2) {
  1432. return DocumentPosition.DOCUMENT_POSITION_CONTAINED_BY + DocumentPosition.DOCUMENT_POSITION_FOLLOWING;
  1433. }
  1434. chain1.push(ancestor1);
  1435. ancestor1 = ancestor1.parentNode;
  1436. }
  1437. chain1.reverse();
  1438. var chain2 = [];
  1439. var ancestor2 = node2.parentNode;
  1440. while (ancestor2) {
  1441. if (!attr1 && ancestor2 === node1) {
  1442. return DocumentPosition.DOCUMENT_POSITION_CONTAINS + DocumentPosition.DOCUMENT_POSITION_PRECEDING;
  1443. }
  1444. chain2.push(ancestor2);
  1445. ancestor2 = ancestor2.parentNode;
  1446. }
  1447. chain2.reverse();
  1448. var ca = commonAncestor(chain1, chain2);
  1449. for (var n in ca.childNodes) {
  1450. var child = ca.childNodes[n];
  1451. if (child === node2) return DocumentPosition.DOCUMENT_POSITION_FOLLOWING;
  1452. if (child === node1) return DocumentPosition.DOCUMENT_POSITION_PRECEDING;
  1453. if (chain2.indexOf(child) >= 0) return DocumentPosition.DOCUMENT_POSITION_FOLLOWING;
  1454. if (chain1.indexOf(child) >= 0) return DocumentPosition.DOCUMENT_POSITION_PRECEDING;
  1455. }
  1456. return 0;
  1457. },
  1458. };
  1459. /**
  1460. * Encodes special XML characters to their corresponding entities.
  1461. *
  1462. * @param {string} c
  1463. * The character to be encoded.
  1464. * @returns {string}
  1465. * The encoded character.
  1466. * @private
  1467. */
  1468. function _xmlEncoder(c) {
  1469. return (
  1470. (c == '<' && '&lt;') || (c == '>' && '&gt;') || (c == '&' && '&amp;') || (c == '"' && '&quot;') || '&#' + c.charCodeAt() + ';'
  1471. );
  1472. }
  1473. copy(NodeType, Node);
  1474. copy(NodeType, Node.prototype);
  1475. copy(DocumentPosition, Node);
  1476. copy(DocumentPosition, Node.prototype);
  1477. /**
  1478. * @param callback
  1479. * Return true for continue,false for break.
  1480. * @returns
  1481. * boolean true: break visit;
  1482. */
  1483. function _visitNode(node, callback) {
  1484. if (callback(node)) {
  1485. return true;
  1486. }
  1487. if ((node = node.firstChild)) {
  1488. do {
  1489. if (_visitNode(node, callback)) {
  1490. return true;
  1491. }
  1492. } while ((node = node.nextSibling));
  1493. }
  1494. }
  1495. /**
  1496. * @typedef DocumentOptions
  1497. * @property {string} [contentType=MIME_TYPE.XML_APPLICATION]
  1498. */
  1499. /**
  1500. * The Document interface describes the common properties and methods for any kind of document.
  1501. *
  1502. * It should usually be created using `new DOMImplementation().createDocument(...)`
  1503. * or `new DOMImplementation().createHTMLDocument(...)`.
  1504. *
  1505. * The constructor is considered a private API and offers to initially set the `contentType`
  1506. * property via it's options parameter.
  1507. *
  1508. * @class
  1509. * @param {Symbol} symbol
  1510. * @param {DocumentOptions} [options]
  1511. * @augments Node
  1512. * @private
  1513. * @see https://developer.mozilla.org/en-US/docs/Web/API/Document
  1514. * @see https://dom.spec.whatwg.org/#interface-document
  1515. */
  1516. function Document(symbol, options) {
  1517. checkSymbol(symbol);
  1518. var opt = options || {};
  1519. this.ownerDocument = this;
  1520. /**
  1521. * The mime type of the document is determined at creation time and can not be modified.
  1522. *
  1523. * @type {string}
  1524. * @see https://dom.spec.whatwg.org/#concept-document-content-type
  1525. * @see {@link DOMImplementation}
  1526. * @see {@link MIME_TYPE}
  1527. * @readonly
  1528. */
  1529. this.contentType = opt.contentType || MIME_TYPE.XML_APPLICATION;
  1530. /**
  1531. * @type {'html' | 'xml'}
  1532. * @see https://dom.spec.whatwg.org/#concept-document-type
  1533. * @see {@link DOMImplementation}
  1534. * @readonly
  1535. */
  1536. this.type = isHTMLMimeType(this.contentType) ? 'html' : 'xml';
  1537. }
  1538. /**
  1539. * Updates the namespace mapping of an element when a new attribute is added.
  1540. *
  1541. * @param {Document} doc
  1542. * The document that the element belongs to.
  1543. * @param {Element} el
  1544. * The element to which the attribute is being added.
  1545. * @param {Attr} newAttr
  1546. * The new attribute being added.
  1547. * @private
  1548. */
  1549. function _onAddAttribute(doc, el, newAttr) {
  1550. doc && doc._inc++;
  1551. var ns = newAttr.namespaceURI;
  1552. if (ns === NAMESPACE.XMLNS) {
  1553. //update namespace
  1554. el._nsMap[newAttr.prefix ? newAttr.localName : ''] = newAttr.value;
  1555. }
  1556. }
  1557. /**
  1558. * Updates the namespace mapping of an element when an attribute is removed.
  1559. *
  1560. * @param {Document} doc
  1561. * The document that the element belongs to.
  1562. * @param {Element} el
  1563. * The element from which the attribute is being removed.
  1564. * @param {Attr} newAttr
  1565. * The attribute being removed.
  1566. * @param {boolean} remove
  1567. * Indicates whether the attribute is to be removed.
  1568. * @private
  1569. */
  1570. function _onRemoveAttribute(doc, el, newAttr, remove) {
  1571. doc && doc._inc++;
  1572. var ns = newAttr.namespaceURI;
  1573. if (ns === NAMESPACE.XMLNS) {
  1574. //update namespace
  1575. delete el._nsMap[newAttr.prefix ? newAttr.localName : ''];
  1576. }
  1577. }
  1578. /**
  1579. * Updates `parent.childNodes`, adjusting the indexed items and its `length`.
  1580. * If `newChild` is provided and has no nextSibling, it will be appended.
  1581. * Otherwise, it's assumed that an item has been removed or inserted,
  1582. * and `parent.firstNode` and its `.nextSibling` to re-indexing all child nodes of `parent`.
  1583. *
  1584. * @param {Document} doc
  1585. * The parent document of `el`.
  1586. * @param {Node} parent
  1587. * The parent node whose childNodes list needs to be updated.
  1588. * @param {Node} [newChild]
  1589. * The new child node to be appended. If not provided, the function assumes a node has been
  1590. * removed.
  1591. * @private
  1592. */
  1593. function _onUpdateChild(doc, parent, newChild) {
  1594. if (doc && doc._inc) {
  1595. doc._inc++;
  1596. var childNodes = parent.childNodes;
  1597. // assumes nextSibling and previousSibling were already configured upfront
  1598. if (newChild && !newChild.nextSibling) {
  1599. // if an item has been appended, we only need to update the last index and the length
  1600. childNodes[childNodes.length++] = newChild;
  1601. } else {
  1602. // otherwise we need to reindex all items,
  1603. // which can take a while when processing nodes with a lot of children
  1604. var child = parent.firstChild;
  1605. var i = 0;
  1606. while (child) {
  1607. childNodes[i++] = child;
  1608. child = child.nextSibling;
  1609. }
  1610. childNodes.length = i;
  1611. delete childNodes[childNodes.length];
  1612. }
  1613. }
  1614. }
  1615. /**
  1616. * Removes the connections between `parentNode` and `child`
  1617. * and any existing `child.previousSibling` or `child.nextSibling`.
  1618. *
  1619. * @param {Node} parentNode
  1620. * The parent node from which the child node is to be removed.
  1621. * @param {Node} child
  1622. * The child node to be removed from the parentNode.
  1623. * @returns {Node}
  1624. * Returns the child node that was removed.
  1625. * @throws {DOMException}
  1626. * With code:
  1627. * - {@link DOMException.NOT_FOUND_ERR} If the parentNode is not the parent of the child node.
  1628. * @private
  1629. * @see https://github.com/xmldom/xmldom/issues/135
  1630. * @see https://github.com/xmldom/xmldom/issues/145
  1631. */
  1632. function _removeChild(parentNode, child) {
  1633. if (parentNode !== child.parentNode) {
  1634. throw new DOMException(DOMException.NOT_FOUND_ERR, "child's parent is not parent");
  1635. }
  1636. var oldPreviousSibling = child.previousSibling;
  1637. var oldNextSibling = child.nextSibling;
  1638. if (oldPreviousSibling) {
  1639. oldPreviousSibling.nextSibling = oldNextSibling;
  1640. } else {
  1641. parentNode.firstChild = oldNextSibling;
  1642. }
  1643. if (oldNextSibling) {
  1644. oldNextSibling.previousSibling = oldPreviousSibling;
  1645. } else {
  1646. parentNode.lastChild = oldPreviousSibling;
  1647. }
  1648. _onUpdateChild(parentNode.ownerDocument, parentNode);
  1649. child.parentNode = null;
  1650. child.previousSibling = null;
  1651. child.nextSibling = null;
  1652. return child;
  1653. }
  1654. /**
  1655. * Returns `true` if `node` can be a parent for insertion.
  1656. *
  1657. * @param {Node} node
  1658. * @returns {boolean}
  1659. */
  1660. function hasValidParentNodeType(node) {
  1661. return (
  1662. node &&
  1663. (node.nodeType === Node.DOCUMENT_NODE || node.nodeType === Node.DOCUMENT_FRAGMENT_NODE || node.nodeType === Node.ELEMENT_NODE)
  1664. );
  1665. }
  1666. /**
  1667. * Returns `true` if `node` can be inserted according to it's `nodeType`.
  1668. *
  1669. * @param {Node} node
  1670. * @returns {boolean}
  1671. */
  1672. function hasInsertableNodeType(node) {
  1673. return (
  1674. node &&
  1675. (node.nodeType === Node.CDATA_SECTION_NODE ||
  1676. node.nodeType === Node.COMMENT_NODE ||
  1677. node.nodeType === Node.DOCUMENT_FRAGMENT_NODE ||
  1678. node.nodeType === Node.DOCUMENT_TYPE_NODE ||
  1679. node.nodeType === Node.ELEMENT_NODE ||
  1680. node.nodeType === Node.PROCESSING_INSTRUCTION_NODE ||
  1681. node.nodeType === Node.TEXT_NODE)
  1682. );
  1683. }
  1684. /**
  1685. * Returns true if `node` is a DOCTYPE node.
  1686. *
  1687. * @param {Node} node
  1688. * @returns {boolean}
  1689. */
  1690. function isDocTypeNode(node) {
  1691. return node && node.nodeType === Node.DOCUMENT_TYPE_NODE;
  1692. }
  1693. /**
  1694. * Returns true if the node is an element.
  1695. *
  1696. * @param {Node} node
  1697. * @returns {boolean}
  1698. */
  1699. function isElementNode(node) {
  1700. return node && node.nodeType === Node.ELEMENT_NODE;
  1701. }
  1702. /**
  1703. * Returns true if `node` is a text node.
  1704. *
  1705. * @param {Node} node
  1706. * @returns {boolean}
  1707. */
  1708. function isTextNode(node) {
  1709. return node && node.nodeType === Node.TEXT_NODE;
  1710. }
  1711. /**
  1712. * Check if en element node can be inserted before `child`, or at the end if child is falsy,
  1713. * according to the presence and position of a doctype node on the same level.
  1714. *
  1715. * @param {Document} doc
  1716. * The document node.
  1717. * @param {Node} child
  1718. * The node that would become the nextSibling if the element would be inserted.
  1719. * @returns {boolean}
  1720. * `true` if an element can be inserted before child.
  1721. * @private
  1722. */
  1723. function isElementInsertionPossible(doc, child) {
  1724. var parentChildNodes = doc.childNodes || [];
  1725. if (find(parentChildNodes, isElementNode) || isDocTypeNode(child)) {
  1726. return false;
  1727. }
  1728. var docTypeNode = find(parentChildNodes, isDocTypeNode);
  1729. return !(child && docTypeNode && parentChildNodes.indexOf(docTypeNode) > parentChildNodes.indexOf(child));
  1730. }
  1731. /**
  1732. * Check if en element node can be inserted before `child`, or at the end if child is falsy,
  1733. * according to the presence and position of a doctype node on the same level.
  1734. *
  1735. * @param {Node} doc
  1736. * The document node.
  1737. * @param {Node} child
  1738. * The node that would become the nextSibling if the element would be inserted.
  1739. * @returns {boolean}
  1740. * `true` if an element can be inserted before child.
  1741. * @private
  1742. */
  1743. function isElementReplacementPossible(doc, child) {
  1744. var parentChildNodes = doc.childNodes || [];
  1745. function hasElementChildThatIsNotChild(node) {
  1746. return isElementNode(node) && node !== child;
  1747. }
  1748. if (find(parentChildNodes, hasElementChildThatIsNotChild)) {
  1749. return false;
  1750. }
  1751. var docTypeNode = find(parentChildNodes, isDocTypeNode);
  1752. return !(child && docTypeNode && parentChildNodes.indexOf(docTypeNode) > parentChildNodes.indexOf(child));
  1753. }
  1754. /**
  1755. * Asserts pre-insertion validity of a node into a parent before a child.
  1756. * Throws errors for invalid node combinations that would result in an ill-formed DOM.
  1757. *
  1758. * @param {Node} parent
  1759. * The parent node to insert `node` into.
  1760. * @param {Node} node
  1761. * The node to insert.
  1762. * @param {Node | null} child
  1763. * The node that should become the `nextSibling` of `node`. If null, no sibling is considered.
  1764. * @throws {DOMException}
  1765. * With code:
  1766. * - {@link DOMException.HIERARCHY_REQUEST_ERR} If `parent` is not a Document,
  1767. * DocumentFragment, or Element node.
  1768. * - {@link DOMException.HIERARCHY_REQUEST_ERR} If `node` is a host-including inclusive
  1769. * ancestor of `parent`. (Currently not implemented)
  1770. * - {@link DOMException.NOT_FOUND_ERR} If `child` is non-null and its `parent` is not
  1771. * `parent`.
  1772. * - {@link DOMException.HIERARCHY_REQUEST_ERR} If `node` is not a DocumentFragment,
  1773. * DocumentType, Element, or CharacterData node.
  1774. * - {@link DOMException.HIERARCHY_REQUEST_ERR} If either `node` is a Text node and `parent` is
  1775. * a document, or if `node` is a doctype and `parent` is not a document.
  1776. * @private
  1777. * @see https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
  1778. * @see https://dom.spec.whatwg.org/#concept-node-replace
  1779. */
  1780. function assertPreInsertionValidity1to5(parent, node, child) {
  1781. // 1. If `parent` is not a Document, DocumentFragment, or Element node, then throw a "HierarchyRequestError" DOMException.
  1782. if (!hasValidParentNodeType(parent)) {
  1783. throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Unexpected parent node type ' + parent.nodeType);
  1784. }
  1785. // 2. If `node` is a host-including inclusive ancestor of `parent`, then throw a "HierarchyRequestError" DOMException.
  1786. // not implemented!
  1787. // 3. If `child` is non-null and its parent is not `parent`, then throw a "NotFoundError" DOMException.
  1788. if (child && child.parentNode !== parent) {
  1789. throw new DOMException(DOMException.NOT_FOUND_ERR, 'child not in parent');
  1790. }
  1791. if (
  1792. // 4. If `node` is not a DocumentFragment, DocumentType, Element, or CharacterData node, then throw a "HierarchyRequestError" DOMException.
  1793. !hasInsertableNodeType(node) ||
  1794. // 5. If either `node` is a Text node and `parent` is a document,
  1795. // the sax parser currently adds top level text nodes, this will be fixed in 0.9.0
  1796. // || (node.nodeType === Node.TEXT_NODE && parent.nodeType === Node.DOCUMENT_NODE)
  1797. // or `node` is a doctype and `parent` is not a document, then throw a "HierarchyRequestError" DOMException.
  1798. (isDocTypeNode(node) && parent.nodeType !== Node.DOCUMENT_NODE)
  1799. ) {
  1800. throw new DOMException(
  1801. DOMException.HIERARCHY_REQUEST_ERR,
  1802. 'Unexpected node type ' + node.nodeType + ' for parent node type ' + parent.nodeType
  1803. );
  1804. }
  1805. }
  1806. /**
  1807. * Asserts pre-insertion validity of a node into a document before a child.
  1808. * Throws errors for invalid node combinations that would result in an ill-formed DOM.
  1809. *
  1810. * @param {Document} parent
  1811. * The parent node to insert `node` into.
  1812. * @param {Node} node
  1813. * The node to insert.
  1814. * @param {Node | undefined} child
  1815. * The node that should become the `nextSibling` of `node`. If undefined, no sibling is
  1816. * considered.
  1817. * @returns {Node}
  1818. * @throws {DOMException}
  1819. * With code:
  1820. * - {@link DOMException.HIERARCHY_REQUEST_ERR} If `node` is a DocumentFragment with more than
  1821. * one element child or has a Text node child.
  1822. * - {@link DOMException.HIERARCHY_REQUEST_ERR} If `node` is a DocumentFragment with one
  1823. * element child and either `parent` has an element child, `child` is a doctype, or `child` is
  1824. * non-null and a doctype is following `child`.
  1825. * - {@link DOMException.HIERARCHY_REQUEST_ERR} If `node` is an Element and `parent` has an
  1826. * element child, `child` is a doctype, or `child` is non-null and a doctype is following
  1827. * `child`.
  1828. * - {@link DOMException.HIERARCHY_REQUEST_ERR} If `node` is a DocumentType and `parent` has a
  1829. * doctype child, `child` is non-null and an element is preceding `child`, or `child` is null
  1830. * and `parent` has an element child.
  1831. * @private
  1832. * @see https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
  1833. * @see https://dom.spec.whatwg.org/#concept-node-replace
  1834. */
  1835. function assertPreInsertionValidityInDocument(parent, node, child) {
  1836. var parentChildNodes = parent.childNodes || [];
  1837. var nodeChildNodes = node.childNodes || [];
  1838. // DocumentFragment
  1839. if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
  1840. var nodeChildElements = nodeChildNodes.filter(isElementNode);
  1841. // If node has more than one element child or has a Text node child.
  1842. if (nodeChildElements.length > 1 || find(nodeChildNodes, isTextNode)) {
  1843. throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'More than one element or text in fragment');
  1844. }
  1845. // Otherwise, if `node` has one element child and either `parent` has an element child,
  1846. // `child` is a doctype, or `child` is non-null and a doctype is following `child`.
  1847. if (nodeChildElements.length === 1 && !isElementInsertionPossible(parent, child)) {
  1848. throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Element in fragment can not be inserted before doctype');
  1849. }
  1850. }
  1851. // Element
  1852. if (isElementNode(node)) {
  1853. // `parent` has an element child, `child` is a doctype,
  1854. // or `child` is non-null and a doctype is following `child`.
  1855. if (!isElementInsertionPossible(parent, child)) {
  1856. throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Only one element can be added and only after doctype');
  1857. }
  1858. }
  1859. // DocumentType
  1860. if (isDocTypeNode(node)) {
  1861. // `parent` has a doctype child,
  1862. if (find(parentChildNodes, isDocTypeNode)) {
  1863. throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Only one doctype is allowed');
  1864. }
  1865. var parentElementChild = find(parentChildNodes, isElementNode);
  1866. // `child` is non-null and an element is preceding `child`,
  1867. if (child && parentChildNodes.indexOf(parentElementChild) < parentChildNodes.indexOf(child)) {
  1868. throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Doctype can only be inserted before an element');
  1869. }
  1870. // or `child` is null and `parent` has an element child.
  1871. if (!child && parentElementChild) {
  1872. throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Doctype can not be appended since element is present');
  1873. }
  1874. }
  1875. }
  1876. /**
  1877. * @param {Document} parent
  1878. * The parent node to insert `node` into.
  1879. * @param {Node} node
  1880. * The node to insert.
  1881. * @param {Node | undefined} child
  1882. * the node that should become the `nextSibling` of `node`
  1883. * @returns {Node}
  1884. * @throws {DOMException}
  1885. * For several node combinations that would create a DOM that is not well-formed.
  1886. * @throws {DOMException}
  1887. * If `child` is provided but is not a child of `parent`.
  1888. * @private
  1889. * @see https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
  1890. * @see https://dom.spec.whatwg.org/#concept-node-replace
  1891. */
  1892. function assertPreReplacementValidityInDocument(parent, node, child) {
  1893. var parentChildNodes = parent.childNodes || [];
  1894. var nodeChildNodes = node.childNodes || [];
  1895. // DocumentFragment
  1896. if (node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
  1897. var nodeChildElements = nodeChildNodes.filter(isElementNode);
  1898. // If `node` has more than one element child or has a Text node child.
  1899. if (nodeChildElements.length > 1 || find(nodeChildNodes, isTextNode)) {
  1900. throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'More than one element or text in fragment');
  1901. }
  1902. // Otherwise, if `node` has one element child and either `parent` has an element child that is not `child` or a doctype is following `child`.
  1903. if (nodeChildElements.length === 1 && !isElementReplacementPossible(parent, child)) {
  1904. throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Element in fragment can not be inserted before doctype');
  1905. }
  1906. }
  1907. // Element
  1908. if (isElementNode(node)) {
  1909. // `parent` has an element child that is not `child` or a doctype is following `child`.
  1910. if (!isElementReplacementPossible(parent, child)) {
  1911. throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Only one element can be added and only after doctype');
  1912. }
  1913. }
  1914. // DocumentType
  1915. if (isDocTypeNode(node)) {
  1916. function hasDoctypeChildThatIsNotChild(node) {
  1917. return isDocTypeNode(node) && node !== child;
  1918. }
  1919. // `parent` has a doctype child that is not `child`,
  1920. if (find(parentChildNodes, hasDoctypeChildThatIsNotChild)) {
  1921. throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Only one doctype is allowed');
  1922. }
  1923. var parentElementChild = find(parentChildNodes, isElementNode);
  1924. // or an element is preceding `child`.
  1925. if (child && parentChildNodes.indexOf(parentElementChild) < parentChildNodes.indexOf(child)) {
  1926. throw new DOMException(DOMException.HIERARCHY_REQUEST_ERR, 'Doctype can only be inserted before an element');
  1927. }
  1928. }
  1929. }
  1930. /**
  1931. * Inserts a node into a parent node before a child node.
  1932. *
  1933. * @param {Node} parent
  1934. * The parent node to insert the node into.
  1935. * @param {Node} node
  1936. * The node to insert into the parent.
  1937. * @param {Node | null} child
  1938. * The node that should become the next sibling of the node.
  1939. * If null, the function inserts the node at the end of the children of the parent node.
  1940. * @param {Function} [_inDocumentAssertion]
  1941. * An optional function to check pre-insertion validity if parent is a document node.
  1942. * Defaults to {@link assertPreInsertionValidityInDocument}
  1943. * @returns {Node}
  1944. * Returns the inserted node.
  1945. * @throws {DOMException}
  1946. * Throws a DOMException if inserting the node would result in a DOM tree that is not
  1947. * well-formed. See {@link assertPreInsertionValidity1to5},
  1948. * {@link assertPreInsertionValidityInDocument}.
  1949. * @throws {DOMException}
  1950. * Throws a DOMException if child is provided but is not a child of the parent. See
  1951. * {@link Node.removeChild}
  1952. * @private
  1953. * @see https://dom.spec.whatwg.org/#concept-node-ensure-pre-insertion-validity
  1954. */
  1955. function _insertBefore(parent, node, child, _inDocumentAssertion) {
  1956. // To ensure pre-insertion validity of a node into a parent before a child, run these steps:
  1957. assertPreInsertionValidity1to5(parent, node, child);
  1958. // If parent is a document, and any of the statements below, switched on the interface node implements,
  1959. // are true, then throw a "HierarchyRequestError" DOMException.
  1960. if (parent.nodeType === Node.DOCUMENT_NODE) {
  1961. (_inDocumentAssertion || assertPreInsertionValidityInDocument)(parent, node, child);
  1962. }
  1963. var cp = node.parentNode;
  1964. if (cp) {
  1965. cp.removeChild(node); //remove and update
  1966. }
  1967. if (node.nodeType === DOCUMENT_FRAGMENT_NODE) {
  1968. var newFirst = node.firstChild;
  1969. if (newFirst == null) {
  1970. return node;
  1971. }
  1972. var newLast = node.lastChild;
  1973. } else {
  1974. newFirst = newLast = node;
  1975. }
  1976. var pre = child ? child.previousSibling : parent.lastChild;
  1977. newFirst.previousSibling = pre;
  1978. newLast.nextSibling = child;
  1979. if (pre) {
  1980. pre.nextSibling = newFirst;
  1981. } else {
  1982. parent.firstChild = newFirst;
  1983. }
  1984. if (child == null) {
  1985. parent.lastChild = newLast;
  1986. } else {
  1987. child.previousSibling = newLast;
  1988. }
  1989. do {
  1990. newFirst.parentNode = parent;
  1991. } while (newFirst !== newLast && (newFirst = newFirst.nextSibling));
  1992. _onUpdateChild(parent.ownerDocument || parent, parent, node);
  1993. if (node.nodeType == DOCUMENT_FRAGMENT_NODE) {
  1994. node.firstChild = node.lastChild = null;
  1995. }
  1996. return node;
  1997. }
  1998. Document.prototype = {
  1999. /**
  2000. * The implementation that created this document.
  2001. *
  2002. * @type DOMImplementation
  2003. * @readonly
  2004. */
  2005. implementation: null,
  2006. nodeName: '#document',
  2007. nodeType: DOCUMENT_NODE,
  2008. /**
  2009. * The DocumentType node of the document.
  2010. *
  2011. * @type DocumentType
  2012. * @readonly
  2013. */
  2014. doctype: null,
  2015. documentElement: null,
  2016. _inc: 1,
  2017. insertBefore: function (newChild, refChild) {
  2018. //raises
  2019. if (newChild.nodeType === DOCUMENT_FRAGMENT_NODE) {
  2020. var child = newChild.firstChild;
  2021. while (child) {
  2022. var next = child.nextSibling;
  2023. this.insertBefore(child, refChild);
  2024. child = next;
  2025. }
  2026. return newChild;
  2027. }
  2028. _insertBefore(this, newChild, refChild);
  2029. newChild.ownerDocument = this;
  2030. if (this.documentElement === null && newChild.nodeType === ELEMENT_NODE) {
  2031. this.documentElement = newChild;
  2032. }
  2033. return newChild;
  2034. },
  2035. removeChild: function (oldChild) {
  2036. var removed = _removeChild(this, oldChild);
  2037. if (removed === this.documentElement) {
  2038. this.documentElement = null;
  2039. }
  2040. return removed;
  2041. },
  2042. replaceChild: function (newChild, oldChild) {
  2043. //raises
  2044. _insertBefore(this, newChild, oldChild, assertPreReplacementValidityInDocument);
  2045. newChild.ownerDocument = this;
  2046. if (oldChild) {
  2047. this.removeChild(oldChild);
  2048. }
  2049. if (isElementNode(newChild)) {
  2050. this.documentElement = newChild;
  2051. }
  2052. },
  2053. // Introduced in DOM Level 2:
  2054. importNode: function (importedNode, deep) {
  2055. return importNode(this, importedNode, deep);
  2056. },
  2057. // Introduced in DOM Level 2:
  2058. getElementById: function (id) {
  2059. var rtv = null;
  2060. _visitNode(this.documentElement, function (node) {
  2061. if (node.nodeType == ELEMENT_NODE) {
  2062. if (node.getAttribute('id') == id) {
  2063. rtv = node;
  2064. return true;
  2065. }
  2066. }
  2067. });
  2068. return rtv;
  2069. },
  2070. /**
  2071. * Creates a new `Element` that is owned by this `Document`.
  2072. * In HTML Documents `localName` is the lower cased `tagName`,
  2073. * otherwise no transformation is being applied.
  2074. * When `contentType` implies the HTML namespace, it will be set as `namespaceURI`.
  2075. *
  2076. * __This implementation differs from the specification:__ - The provided name is not checked
  2077. * against the `Name` production,
  2078. * so no related error will be thrown.
  2079. * - There is no interface `HTMLElement`, it is always an `Element`.
  2080. * - There is no support for a second argument to indicate using custom elements.
  2081. *
  2082. * @param {string} tagName
  2083. * @returns {Element}
  2084. * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement
  2085. * @see https://dom.spec.whatwg.org/#dom-document-createelement
  2086. * @see https://dom.spec.whatwg.org/#concept-create-element
  2087. */
  2088. createElement: function (tagName) {
  2089. var node = new Element(PDC);
  2090. node.ownerDocument = this;
  2091. if (this.type === 'html') {
  2092. tagName = tagName.toLowerCase();
  2093. }
  2094. if (hasDefaultHTMLNamespace(this.contentType)) {
  2095. node.namespaceURI = NAMESPACE.HTML;
  2096. }
  2097. node.nodeName = tagName;
  2098. node.tagName = tagName;
  2099. node.localName = tagName;
  2100. node.childNodes = new NodeList();
  2101. var attrs = (node.attributes = new NamedNodeMap());
  2102. attrs._ownerElement = node;
  2103. return node;
  2104. },
  2105. /**
  2106. * @returns {DocumentFragment}
  2107. */
  2108. createDocumentFragment: function () {
  2109. var node = new DocumentFragment(PDC);
  2110. node.ownerDocument = this;
  2111. node.childNodes = new NodeList();
  2112. return node;
  2113. },
  2114. /**
  2115. * @param {string} data
  2116. * @returns {Text}
  2117. */
  2118. createTextNode: function (data) {
  2119. var node = new Text(PDC);
  2120. node.ownerDocument = this;
  2121. node.childNodes = new NodeList();
  2122. node.appendData(data);
  2123. return node;
  2124. },
  2125. /**
  2126. * @param {string} data
  2127. * @returns {Comment}
  2128. */
  2129. createComment: function (data) {
  2130. var node = new Comment(PDC);
  2131. node.ownerDocument = this;
  2132. node.childNodes = new NodeList();
  2133. node.appendData(data);
  2134. return node;
  2135. },
  2136. /**
  2137. * @param {string} data
  2138. * @returns {CDATASection}
  2139. */
  2140. createCDATASection: function (data) {
  2141. var node = new CDATASection(PDC);
  2142. node.ownerDocument = this;
  2143. node.childNodes = new NodeList();
  2144. node.appendData(data);
  2145. return node;
  2146. },
  2147. /**
  2148. * @param {string} target
  2149. * @param {string} data
  2150. * @returns {ProcessingInstruction}
  2151. */
  2152. createProcessingInstruction: function (target, data) {
  2153. var node = new ProcessingInstruction(PDC);
  2154. node.ownerDocument = this;
  2155. node.childNodes = new NodeList();
  2156. node.nodeName = node.target = target;
  2157. node.nodeValue = node.data = data;
  2158. return node;
  2159. },
  2160. /**
  2161. * Creates an `Attr` node that is owned by this document.
  2162. * In HTML Documents `localName` is the lower cased `name`,
  2163. * otherwise no transformation is being applied.
  2164. *
  2165. * __This implementation differs from the specification:__ - The provided name is not checked
  2166. * against the `Name` production,
  2167. * so no related error will be thrown.
  2168. *
  2169. * @param {string} name
  2170. * @returns {Attr}
  2171. * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createAttribute
  2172. * @see https://dom.spec.whatwg.org/#dom-document-createattribute
  2173. */
  2174. createAttribute: function (name) {
  2175. if (!g.QName_exact.test(name)) {
  2176. throw new DOMException(DOMException.INVALID_CHARACTER_ERR, 'invalid character in name "' + name + '"');
  2177. }
  2178. if (this.type === 'html') {
  2179. name = name.toLowerCase();
  2180. }
  2181. return this._createAttribute(name);
  2182. },
  2183. _createAttribute: function (name) {
  2184. var node = new Attr(PDC);
  2185. node.ownerDocument = this;
  2186. node.childNodes = new NodeList();
  2187. node.name = name;
  2188. node.nodeName = name;
  2189. node.localName = name;
  2190. node.specified = true;
  2191. return node;
  2192. },
  2193. /**
  2194. * Creates an EntityReference object.
  2195. * The current implementation does not fill the `childNodes` with those of the corresponding
  2196. * `Entity`
  2197. *
  2198. * @deprecated
  2199. * In DOM Level 4.
  2200. * @param {string} name
  2201. * The name of the entity to reference. No namespace well-formedness checks are performed.
  2202. * @returns {EntityReference}
  2203. * @throws {DOMException}
  2204. * With code `INVALID_CHARACTER_ERR` when `name` is not valid.
  2205. * @throws {DOMException}
  2206. * with code `NOT_SUPPORTED_ERR` when the document is of type `html`
  2207. * @see https://www.w3.org/TR/DOM-Level-3-Core/core.html#ID-392B75AE
  2208. */
  2209. createEntityReference: function (name) {
  2210. if (!g.Name.test(name)) {
  2211. throw new DOMException(DOMException.INVALID_CHARACTER_ERR, 'not a valid xml name "' + name + '"');
  2212. }
  2213. if (this.type === 'html') {
  2214. throw new DOMException('document is an html document', DOMExceptionName.NotSupportedError);
  2215. }
  2216. var node = new EntityReference(PDC);
  2217. node.ownerDocument = this;
  2218. node.childNodes = new NodeList();
  2219. node.nodeName = name;
  2220. return node;
  2221. },
  2222. // Introduced in DOM Level 2:
  2223. /**
  2224. * @param {string} namespaceURI
  2225. * @param {string} qualifiedName
  2226. * @returns {Element}
  2227. */
  2228. createElementNS: function (namespaceURI, qualifiedName) {
  2229. var validated = validateAndExtract(namespaceURI, qualifiedName);
  2230. var node = new Element(PDC);
  2231. var attrs = (node.attributes = new NamedNodeMap());
  2232. node.childNodes = new NodeList();
  2233. node.ownerDocument = this;
  2234. node.nodeName = qualifiedName;
  2235. node.tagName = qualifiedName;
  2236. node.namespaceURI = validated[0];
  2237. node.prefix = validated[1];
  2238. node.localName = validated[2];
  2239. attrs._ownerElement = node;
  2240. return node;
  2241. },
  2242. // Introduced in DOM Level 2:
  2243. /**
  2244. * @param {string} namespaceURI
  2245. * @param {string} qualifiedName
  2246. * @returns {Attr}
  2247. */
  2248. createAttributeNS: function (namespaceURI, qualifiedName) {
  2249. var validated = validateAndExtract(namespaceURI, qualifiedName);
  2250. var node = new Attr(PDC);
  2251. node.ownerDocument = this;
  2252. node.childNodes = new NodeList();
  2253. node.nodeName = qualifiedName;
  2254. node.name = qualifiedName;
  2255. node.specified = true;
  2256. node.namespaceURI = validated[0];
  2257. node.prefix = validated[1];
  2258. node.localName = validated[2];
  2259. return node;
  2260. },
  2261. };
  2262. _extends(Document, Node);
  2263. function Element(symbol) {
  2264. checkSymbol(symbol);
  2265. this._nsMap = Object.create(null);
  2266. }
  2267. Element.prototype = {
  2268. nodeType: ELEMENT_NODE,
  2269. /**
  2270. * The attributes of this element.
  2271. *
  2272. * @type {NamedNodeMap | null}
  2273. */
  2274. attributes: null,
  2275. getQualifiedName: function () {
  2276. return this.prefix ? this.prefix + ':' + this.localName : this.localName;
  2277. },
  2278. _isInHTMLDocumentAndNamespace: function () {
  2279. return this.ownerDocument.type === 'html' && this.namespaceURI === NAMESPACE.HTML;
  2280. },
  2281. /**
  2282. * Implementaton of Level2 Core function hasAttributes.
  2283. *
  2284. * @returns {boolean}
  2285. * True if attribute list is not empty.
  2286. * @see https://www.w3.org/TR/DOM-Level-2-Core/#core-ID-NodeHasAttrs
  2287. */
  2288. hasAttributes: function () {
  2289. return !!(this.attributes && this.attributes.length);
  2290. },
  2291. hasAttribute: function (name) {
  2292. return !!this.getAttributeNode(name);
  2293. },
  2294. /**
  2295. * Returns element’s first attribute whose qualified name is `name`, and `null`
  2296. * if there is no such attribute.
  2297. *
  2298. * @param {string} name
  2299. * @returns {string | null}
  2300. */
  2301. getAttribute: function (name) {
  2302. var attr = this.getAttributeNode(name);
  2303. return attr ? attr.value : null;
  2304. },
  2305. getAttributeNode: function (name) {
  2306. if (this._isInHTMLDocumentAndNamespace()) {
  2307. name = name.toLowerCase();
  2308. }
  2309. return this.attributes.getNamedItem(name);
  2310. },
  2311. /**
  2312. * Sets the value of element’s first attribute whose qualified name is qualifiedName to value.
  2313. *
  2314. * @param {string} name
  2315. * @param {string} value
  2316. */
  2317. setAttribute: function (name, value) {
  2318. if (this._isInHTMLDocumentAndNamespace()) {
  2319. name = name.toLowerCase();
  2320. }
  2321. var attr = this.getAttributeNode(name);
  2322. if (attr) {
  2323. attr.value = attr.nodeValue = '' + value;
  2324. } else {
  2325. attr = this.ownerDocument._createAttribute(name);
  2326. attr.value = attr.nodeValue = '' + value;
  2327. this.setAttributeNode(attr);
  2328. }
  2329. },
  2330. removeAttribute: function (name) {
  2331. var attr = this.getAttributeNode(name);
  2332. attr && this.removeAttributeNode(attr);
  2333. },
  2334. setAttributeNode: function (newAttr) {
  2335. return this.attributes.setNamedItem(newAttr);
  2336. },
  2337. setAttributeNodeNS: function (newAttr) {
  2338. return this.attributes.setNamedItemNS(newAttr);
  2339. },
  2340. removeAttributeNode: function (oldAttr) {
  2341. //console.log(this == oldAttr.ownerElement)
  2342. return this.attributes.removeNamedItem(oldAttr.nodeName);
  2343. },
  2344. //get real attribute name,and remove it by removeAttributeNode
  2345. removeAttributeNS: function (namespaceURI, localName) {
  2346. var old = this.getAttributeNodeNS(namespaceURI, localName);
  2347. old && this.removeAttributeNode(old);
  2348. },
  2349. hasAttributeNS: function (namespaceURI, localName) {
  2350. return this.getAttributeNodeNS(namespaceURI, localName) != null;
  2351. },
  2352. /**
  2353. * Returns element’s attribute whose namespace is `namespaceURI` and local name is
  2354. * `localName`,
  2355. * or `null` if there is no such attribute.
  2356. *
  2357. * @param {string} namespaceURI
  2358. * @param {string} localName
  2359. * @returns {string | null}
  2360. */
  2361. getAttributeNS: function (namespaceURI, localName) {
  2362. var attr = this.getAttributeNodeNS(namespaceURI, localName);
  2363. return attr ? attr.value : null;
  2364. },
  2365. /**
  2366. * Sets the value of element’s attribute whose namespace is `namespaceURI` and local name is
  2367. * `localName` to value.
  2368. *
  2369. * @param {string} namespaceURI
  2370. * @param {string} qualifiedName
  2371. * @param {string} value
  2372. * @see https://dom.spec.whatwg.org/#dom-element-setattributens
  2373. */
  2374. setAttributeNS: function (namespaceURI, qualifiedName, value) {
  2375. var validated = validateAndExtract(namespaceURI, qualifiedName);
  2376. var localName = validated[2];
  2377. var attr = this.getAttributeNodeNS(namespaceURI, localName);
  2378. if (attr) {
  2379. attr.value = attr.nodeValue = '' + value;
  2380. } else {
  2381. attr = this.ownerDocument.createAttributeNS(namespaceURI, qualifiedName);
  2382. attr.value = attr.nodeValue = '' + value;
  2383. this.setAttributeNode(attr);
  2384. }
  2385. },
  2386. getAttributeNodeNS: function (namespaceURI, localName) {
  2387. return this.attributes.getNamedItemNS(namespaceURI, localName);
  2388. },
  2389. /**
  2390. * Returns a LiveNodeList of all child elements which have **all** of the given class name(s).
  2391. *
  2392. * Returns an empty list if `classNames` is an empty string or only contains HTML white space
  2393. * characters.
  2394. *
  2395. * Warning: This returns a live LiveNodeList.
  2396. * Changes in the DOM will reflect in the array as the changes occur.
  2397. * If an element selected by this array no longer qualifies for the selector,
  2398. * it will automatically be removed. Be aware of this for iteration purposes.
  2399. *
  2400. * @param {string} classNames
  2401. * Is a string representing the class name(s) to match; multiple class names are separated by
  2402. * (ASCII-)whitespace.
  2403. * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/getElementsByClassName
  2404. * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/getElementsByClassName
  2405. * @see https://dom.spec.whatwg.org/#concept-getelementsbyclassname
  2406. */
  2407. getElementsByClassName: function (classNames) {
  2408. var classNamesSet = toOrderedSet(classNames);
  2409. return new LiveNodeList(this, function (base) {
  2410. var ls = [];
  2411. if (classNamesSet.length > 0) {
  2412. _visitNode(base, function (node) {
  2413. if (node !== base && node.nodeType === ELEMENT_NODE) {
  2414. var nodeClassNames = node.getAttribute('class');
  2415. // can be null if the attribute does not exist
  2416. if (nodeClassNames) {
  2417. // before splitting and iterating just compare them for the most common case
  2418. var matches = classNames === nodeClassNames;
  2419. if (!matches) {
  2420. var nodeClassNamesSet = toOrderedSet(nodeClassNames);
  2421. matches = classNamesSet.every(arrayIncludes(nodeClassNamesSet));
  2422. }
  2423. if (matches) {
  2424. ls.push(node);
  2425. }
  2426. }
  2427. }
  2428. });
  2429. }
  2430. return ls;
  2431. });
  2432. },
  2433. /**
  2434. * Returns a LiveNodeList of elements with the given qualifiedName.
  2435. * Searching for all descendants can be done by passing `*` as `qualifiedName`.
  2436. *
  2437. * All descendants of the specified element are searched, but not the element itself.
  2438. * The returned list is live, which means it updates itself with the DOM tree automatically.
  2439. * Therefore, there is no need to call `Element.getElementsByTagName()`
  2440. * with the same element and arguments repeatedly if the DOM changes in between calls.
  2441. *
  2442. * When called on an HTML element in an HTML document,
  2443. * `getElementsByTagName` lower-cases the argument before searching for it.
  2444. * This is undesirable when trying to match camel-cased SVG elements (such as
  2445. * `<linearGradient>`) in an HTML document.
  2446. * Instead, use `Element.getElementsByTagNameNS()`,
  2447. * which preserves the capitalization of the tag name.
  2448. *
  2449. * `Element.getElementsByTagName` is similar to `Document.getElementsByTagName()`,
  2450. * except that it only searches for elements that are descendants of the specified element.
  2451. *
  2452. * @param {string} qualifiedName
  2453. * @returns {LiveNodeList}
  2454. * @see https://developer.mozilla.org/en-US/docs/Web/API/Element/getElementsByTagName
  2455. * @see https://dom.spec.whatwg.org/#concept-getelementsbytagname
  2456. */
  2457. getElementsByTagName: function (qualifiedName) {
  2458. var isHTMLDocument = (this.nodeType === DOCUMENT_NODE ? this : this.ownerDocument).type === 'html';
  2459. var lowerQualifiedName = qualifiedName.toLowerCase();
  2460. return new LiveNodeList(this, function (base) {
  2461. var ls = [];
  2462. _visitNode(base, function (node) {
  2463. if (node === base || node.nodeType !== ELEMENT_NODE) {
  2464. return;
  2465. }
  2466. if (qualifiedName === '*') {
  2467. ls.push(node);
  2468. } else {
  2469. var nodeQualifiedName = node.getQualifiedName();
  2470. var matchingQName = isHTMLDocument && node.namespaceURI === NAMESPACE.HTML ? lowerQualifiedName : qualifiedName;
  2471. if (nodeQualifiedName === matchingQName) {
  2472. ls.push(node);
  2473. }
  2474. }
  2475. });
  2476. return ls;
  2477. });
  2478. },
  2479. getElementsByTagNameNS: function (namespaceURI, localName) {
  2480. return new LiveNodeList(this, function (base) {
  2481. var ls = [];
  2482. _visitNode(base, function (node) {
  2483. if (
  2484. node !== base &&
  2485. node.nodeType === ELEMENT_NODE &&
  2486. (namespaceURI === '*' || node.namespaceURI === namespaceURI) &&
  2487. (localName === '*' || node.localName == localName)
  2488. ) {
  2489. ls.push(node);
  2490. }
  2491. });
  2492. return ls;
  2493. });
  2494. },
  2495. };
  2496. Document.prototype.getElementsByClassName = Element.prototype.getElementsByClassName;
  2497. Document.prototype.getElementsByTagName = Element.prototype.getElementsByTagName;
  2498. Document.prototype.getElementsByTagNameNS = Element.prototype.getElementsByTagNameNS;
  2499. _extends(Element, Node);
  2500. function Attr(symbol) {
  2501. checkSymbol(symbol);
  2502. this.namespaceURI = null;
  2503. this.prefix = null;
  2504. this.ownerElement = null;
  2505. }
  2506. Attr.prototype.nodeType = ATTRIBUTE_NODE;
  2507. _extends(Attr, Node);
  2508. function CharacterData(symbol) {
  2509. checkSymbol(symbol);
  2510. }
  2511. CharacterData.prototype = {
  2512. data: '',
  2513. substringData: function (offset, count) {
  2514. return this.data.substring(offset, offset + count);
  2515. },
  2516. appendData: function (text) {
  2517. text = this.data + text;
  2518. this.nodeValue = this.data = text;
  2519. this.length = text.length;
  2520. },
  2521. insertData: function (offset, text) {
  2522. this.replaceData(offset, 0, text);
  2523. },
  2524. deleteData: function (offset, count) {
  2525. this.replaceData(offset, count, '');
  2526. },
  2527. replaceData: function (offset, count, text) {
  2528. var start = this.data.substring(0, offset);
  2529. var end = this.data.substring(offset + count);
  2530. text = start + text + end;
  2531. this.nodeValue = this.data = text;
  2532. this.length = text.length;
  2533. },
  2534. };
  2535. _extends(CharacterData, Node);
  2536. function Text(symbol) {
  2537. checkSymbol(symbol);
  2538. }
  2539. Text.prototype = {
  2540. nodeName: '#text',
  2541. nodeType: TEXT_NODE,
  2542. splitText: function (offset) {
  2543. var text = this.data;
  2544. var newText = text.substring(offset);
  2545. text = text.substring(0, offset);
  2546. this.data = this.nodeValue = text;
  2547. this.length = text.length;
  2548. var newNode = this.ownerDocument.createTextNode(newText);
  2549. if (this.parentNode) {
  2550. this.parentNode.insertBefore(newNode, this.nextSibling);
  2551. }
  2552. return newNode;
  2553. },
  2554. };
  2555. _extends(Text, CharacterData);
  2556. function Comment(symbol) {
  2557. checkSymbol(symbol);
  2558. }
  2559. Comment.prototype = {
  2560. nodeName: '#comment',
  2561. nodeType: COMMENT_NODE,
  2562. };
  2563. _extends(Comment, CharacterData);
  2564. function CDATASection(symbol) {
  2565. checkSymbol(symbol);
  2566. }
  2567. CDATASection.prototype = {
  2568. nodeName: '#cdata-section',
  2569. nodeType: CDATA_SECTION_NODE,
  2570. };
  2571. _extends(CDATASection, Text);
  2572. function DocumentType(symbol) {
  2573. checkSymbol(symbol);
  2574. }
  2575. DocumentType.prototype.nodeType = DOCUMENT_TYPE_NODE;
  2576. _extends(DocumentType, Node);
  2577. function Notation(symbol) {
  2578. checkSymbol(symbol);
  2579. }
  2580. Notation.prototype.nodeType = NOTATION_NODE;
  2581. _extends(Notation, Node);
  2582. function Entity(symbol) {
  2583. checkSymbol(symbol);
  2584. }
  2585. Entity.prototype.nodeType = ENTITY_NODE;
  2586. _extends(Entity, Node);
  2587. function EntityReference(symbol) {
  2588. checkSymbol(symbol);
  2589. }
  2590. EntityReference.prototype.nodeType = ENTITY_REFERENCE_NODE;
  2591. _extends(EntityReference, Node);
  2592. function DocumentFragment(symbol) {
  2593. checkSymbol(symbol);
  2594. }
  2595. DocumentFragment.prototype.nodeName = '#document-fragment';
  2596. DocumentFragment.prototype.nodeType = DOCUMENT_FRAGMENT_NODE;
  2597. _extends(DocumentFragment, Node);
  2598. function ProcessingInstruction(symbol) {
  2599. checkSymbol(symbol);
  2600. }
  2601. ProcessingInstruction.prototype.nodeType = PROCESSING_INSTRUCTION_NODE;
  2602. _extends(ProcessingInstruction, CharacterData);
  2603. function XMLSerializer() {}
  2604. XMLSerializer.prototype.serializeToString = function (node, nodeFilter) {
  2605. return nodeSerializeToString.call(node, nodeFilter);
  2606. };
  2607. Node.prototype.toString = nodeSerializeToString;
  2608. function nodeSerializeToString(nodeFilter) {
  2609. var buf = [];
  2610. var refNode = (this.nodeType === DOCUMENT_NODE && this.documentElement) || this;
  2611. var prefix = refNode.prefix;
  2612. var uri = refNode.namespaceURI;
  2613. if (uri && prefix == null) {
  2614. var prefix = refNode.lookupPrefix(uri);
  2615. if (prefix == null) {
  2616. var visibleNamespaces = [
  2617. { namespace: uri, prefix: null },
  2618. //{namespace:uri,prefix:''}
  2619. ];
  2620. }
  2621. }
  2622. serializeToString(this, buf, nodeFilter, visibleNamespaces);
  2623. return buf.join('');
  2624. }
  2625. function needNamespaceDefine(node, isHTML, visibleNamespaces) {
  2626. var prefix = node.prefix || '';
  2627. var uri = node.namespaceURI;
  2628. // According to [Namespaces in XML 1.0](https://www.w3.org/TR/REC-xml-names/#ns-using) ,
  2629. // and more specifically https://www.w3.org/TR/REC-xml-names/#nsc-NoPrefixUndecl :
  2630. // > In a namespace declaration for a prefix [...], the attribute value MUST NOT be empty.
  2631. // in a similar manner [Namespaces in XML 1.1](https://www.w3.org/TR/xml-names11/#ns-using)
  2632. // and more specifically https://www.w3.org/TR/xml-names11/#nsc-NSDeclared :
  2633. // > [...] Furthermore, the attribute value [...] must not be an empty string.
  2634. // so serializing empty namespace value like xmlns:ds="" would produce an invalid XML document.
  2635. if (!uri) {
  2636. return false;
  2637. }
  2638. if ((prefix === 'xml' && uri === NAMESPACE.XML) || uri === NAMESPACE.XMLNS) {
  2639. return false;
  2640. }
  2641. var i = visibleNamespaces.length;
  2642. while (i--) {
  2643. var ns = visibleNamespaces[i];
  2644. // get namespace prefix
  2645. if (ns.prefix === prefix) {
  2646. return ns.namespace !== uri;
  2647. }
  2648. }
  2649. return true;
  2650. }
  2651. /**
  2652. * Literal whitespace other than space that appear in attribute values are serialized as
  2653. * their entity references, so they will be preserved.
  2654. * (In contrast to whitespace literals in the input which are normalized to spaces).
  2655. *
  2656. * Well-formed constraint: No < in Attribute Values:
  2657. * > The replacement text of any entity referred to directly or indirectly
  2658. * > in an attribute value must not contain a <.
  2659. *
  2660. * @see https://www.w3.org/TR/xml11/#CleanAttrVals
  2661. * @see https://www.w3.org/TR/xml11/#NT-AttValue
  2662. * @see https://www.w3.org/TR/xml11/#AVNormalize
  2663. * @see https://w3c.github.io/DOM-Parsing/#serializing-an-element-s-attributes
  2664. * @prettierignore
  2665. */
  2666. function addSerializedAttribute(buf, qualifiedName, value) {
  2667. buf.push(' ', qualifiedName, '="', value.replace(/[<>&"\t\n\r]/g, _xmlEncoder), '"');
  2668. }
  2669. function serializeToString(node, buf, nodeFilter, visibleNamespaces) {
  2670. if (!visibleNamespaces) {
  2671. visibleNamespaces = [];
  2672. }
  2673. var doc = node.nodeType === DOCUMENT_NODE ? node : node.ownerDocument;
  2674. var isHTML = doc.type === 'html';
  2675. if (nodeFilter) {
  2676. node = nodeFilter(node);
  2677. if (node) {
  2678. if (typeof node == 'string') {
  2679. buf.push(node);
  2680. return;
  2681. }
  2682. } else {
  2683. return;
  2684. }
  2685. //buf.sort.apply(attrs, attributeSorter);
  2686. }
  2687. switch (node.nodeType) {
  2688. case ELEMENT_NODE:
  2689. var attrs = node.attributes;
  2690. var len = attrs.length;
  2691. var child = node.firstChild;
  2692. var nodeName = node.tagName;
  2693. var prefixedNodeName = nodeName;
  2694. if (!isHTML && !node.prefix && node.namespaceURI) {
  2695. var defaultNS;
  2696. // lookup current default ns from `xmlns` attribute
  2697. for (var ai = 0; ai < attrs.length; ai++) {
  2698. if (attrs.item(ai).name === 'xmlns') {
  2699. defaultNS = attrs.item(ai).value;
  2700. break;
  2701. }
  2702. }
  2703. if (!defaultNS) {
  2704. // lookup current default ns in visibleNamespaces
  2705. for (var nsi = visibleNamespaces.length - 1; nsi >= 0; nsi--) {
  2706. var namespace = visibleNamespaces[nsi];
  2707. if (namespace.prefix === '' && namespace.namespace === node.namespaceURI) {
  2708. defaultNS = namespace.namespace;
  2709. break;
  2710. }
  2711. }
  2712. }
  2713. if (defaultNS !== node.namespaceURI) {
  2714. for (var nsi = visibleNamespaces.length - 1; nsi >= 0; nsi--) {
  2715. var namespace = visibleNamespaces[nsi];
  2716. if (namespace.namespace === node.namespaceURI) {
  2717. if (namespace.prefix) {
  2718. prefixedNodeName = namespace.prefix + ':' + nodeName;
  2719. }
  2720. break;
  2721. }
  2722. }
  2723. }
  2724. }
  2725. buf.push('<', prefixedNodeName);
  2726. for (var i = 0; i < len; i++) {
  2727. // add namespaces for attributes
  2728. var attr = attrs.item(i);
  2729. if (attr.prefix == 'xmlns') {
  2730. visibleNamespaces.push({
  2731. prefix: attr.localName,
  2732. namespace: attr.value,
  2733. });
  2734. } else if (attr.nodeName == 'xmlns') {
  2735. visibleNamespaces.push({ prefix: '', namespace: attr.value });
  2736. }
  2737. }
  2738. for (var i = 0; i < len; i++) {
  2739. var attr = attrs.item(i);
  2740. if (needNamespaceDefine(attr, isHTML, visibleNamespaces)) {
  2741. var prefix = attr.prefix || '';
  2742. var uri = attr.namespaceURI;
  2743. addSerializedAttribute(buf, prefix ? 'xmlns:' + prefix : 'xmlns', uri);
  2744. visibleNamespaces.push({ prefix: prefix, namespace: uri });
  2745. }
  2746. serializeToString(attr, buf, nodeFilter, visibleNamespaces);
  2747. }
  2748. // add namespace for current node
  2749. if (nodeName === prefixedNodeName && needNamespaceDefine(node, isHTML, visibleNamespaces)) {
  2750. var prefix = node.prefix || '';
  2751. var uri = node.namespaceURI;
  2752. addSerializedAttribute(buf, prefix ? 'xmlns:' + prefix : 'xmlns', uri);
  2753. visibleNamespaces.push({ prefix: prefix, namespace: uri });
  2754. }
  2755. // in XML elements can be closed when they have no children
  2756. var canCloseTag = !child;
  2757. if (canCloseTag && (isHTML || node.namespaceURI === NAMESPACE.HTML)) {
  2758. // in HTML (doc or ns) only void elements can be closed right away
  2759. canCloseTag = isHTMLVoidElement(nodeName);
  2760. }
  2761. if (canCloseTag) {
  2762. buf.push('/>');
  2763. } else {
  2764. buf.push('>');
  2765. //if is cdata child node
  2766. if (isHTML && isHTMLRawTextElement(nodeName)) {
  2767. while (child) {
  2768. if (child.data) {
  2769. buf.push(child.data);
  2770. } else {
  2771. serializeToString(child, buf, nodeFilter, visibleNamespaces.slice());
  2772. }
  2773. child = child.nextSibling;
  2774. }
  2775. } else {
  2776. while (child) {
  2777. serializeToString(child, buf, nodeFilter, visibleNamespaces.slice());
  2778. child = child.nextSibling;
  2779. }
  2780. }
  2781. buf.push('</', prefixedNodeName, '>');
  2782. }
  2783. // remove added visible namespaces
  2784. //visibleNamespaces.length = startVisibleNamespaces;
  2785. return;
  2786. case DOCUMENT_NODE:
  2787. case DOCUMENT_FRAGMENT_NODE:
  2788. var child = node.firstChild;
  2789. while (child) {
  2790. serializeToString(child, buf, nodeFilter, visibleNamespaces.slice());
  2791. child = child.nextSibling;
  2792. }
  2793. return;
  2794. case ATTRIBUTE_NODE:
  2795. return addSerializedAttribute(buf, node.name, node.value);
  2796. case TEXT_NODE:
  2797. /*
  2798. * The ampersand character (&) and the left angle bracket (<) must not appear in their literal form,
  2799. * except when used as markup delimiters, or within a comment, a processing instruction,
  2800. * or a CDATA section.
  2801. * If they are needed elsewhere, they must be escaped using either numeric character
  2802. * references or the strings `&amp;` and `&lt;` respectively.
  2803. * The right angle bracket (>) may be represented using the string " &gt; ",
  2804. * and must, for compatibility, be escaped using either `&gt;`,
  2805. * or a character reference when it appears in the string `]]>` in content,
  2806. * when that string is not marking the end of a CDATA section.
  2807. *
  2808. * In the content of elements, character data is any string of characters which does not
  2809. * contain the start-delimiter of any markup and does not include the CDATA-section-close
  2810. * delimiter, `]]>`.
  2811. *
  2812. * @see https://www.w3.org/TR/xml/#NT-CharData
  2813. * @see https://w3c.github.io/DOM-Parsing/#xml-serializing-a-text-node
  2814. */
  2815. return buf.push(node.data.replace(/[<&>]/g, _xmlEncoder));
  2816. case CDATA_SECTION_NODE:
  2817. return buf.push(g.CDATA_START, node.data, g.CDATA_END);
  2818. case COMMENT_NODE:
  2819. return buf.push(g.COMMENT_START, node.data, g.COMMENT_END);
  2820. case DOCUMENT_TYPE_NODE:
  2821. var pubid = node.publicId;
  2822. var sysid = node.systemId;
  2823. buf.push(g.DOCTYPE_DECL_START, ' ', node.name);
  2824. if (pubid) {
  2825. buf.push(' ', g.PUBLIC, ' ', pubid);
  2826. if (sysid && sysid !== '.') {
  2827. buf.push(' ', sysid);
  2828. }
  2829. } else if (sysid && sysid !== '.') {
  2830. buf.push(' ', g.SYSTEM, ' ', sysid);
  2831. }
  2832. if (node.internalSubset) {
  2833. buf.push(' [', node.internalSubset, ']');
  2834. }
  2835. buf.push('>');
  2836. return;
  2837. case PROCESSING_INSTRUCTION_NODE:
  2838. return buf.push('<?', node.target, ' ', node.data, '?>');
  2839. case ENTITY_REFERENCE_NODE:
  2840. return buf.push('&', node.nodeName, ';');
  2841. //case ENTITY_NODE:
  2842. //case NOTATION_NODE:
  2843. default:
  2844. buf.push('??', node.nodeName);
  2845. }
  2846. }
  2847. function importNode(doc, node, deep) {
  2848. var node2;
  2849. switch (node.nodeType) {
  2850. case ELEMENT_NODE:
  2851. node2 = node.cloneNode(false);
  2852. node2.ownerDocument = doc;
  2853. //var attrs = node2.attributes;
  2854. //var len = attrs.length;
  2855. //for(var i=0;i<len;i++){
  2856. //node2.setAttributeNodeNS(importNode(doc,attrs.item(i),deep));
  2857. //}
  2858. case DOCUMENT_FRAGMENT_NODE:
  2859. break;
  2860. case ATTRIBUTE_NODE:
  2861. deep = true;
  2862. break;
  2863. //case ENTITY_REFERENCE_NODE:
  2864. //case PROCESSING_INSTRUCTION_NODE:
  2865. ////case TEXT_NODE:
  2866. //case CDATA_SECTION_NODE:
  2867. //case COMMENT_NODE:
  2868. // deep = false;
  2869. // break;
  2870. //case DOCUMENT_NODE:
  2871. //case DOCUMENT_TYPE_NODE:
  2872. //cannot be imported.
  2873. //case ENTITY_NODE:
  2874. //case NOTATION_NODE:
  2875. //can not hit in level3
  2876. //default:throw e;
  2877. }
  2878. if (!node2) {
  2879. node2 = node.cloneNode(false); //false
  2880. }
  2881. node2.ownerDocument = doc;
  2882. node2.parentNode = null;
  2883. if (deep) {
  2884. var child = node.firstChild;
  2885. while (child) {
  2886. node2.appendChild(importNode(doc, child, deep));
  2887. child = child.nextSibling;
  2888. }
  2889. }
  2890. return node2;
  2891. }
  2892. /**
  2893. * Creates a copy of a node from an existing one.
  2894. *
  2895. * @param {Document} doc
  2896. * The Document object representing the document that the new node will belong to.
  2897. * @param {Node} node
  2898. * The node to clone.
  2899. * @param {boolean} deep
  2900. * If true, the contents of the node are recursively copied.
  2901. * If false, only the node itself (and its attributes, if it is an element) are copied.
  2902. * @returns {Node}
  2903. * Returns the newly created copy of the node.
  2904. * @throws {DOMException}
  2905. * May throw a DOMException if operations within setAttributeNode or appendChild (which are
  2906. * potentially invoked in this function) do not meet their specific constraints.
  2907. */
  2908. function cloneNode(doc, node, deep) {
  2909. var node2 = new node.constructor(PDC);
  2910. for (var n in node) {
  2911. if (hasOwn(node, n)) {
  2912. var v = node[n];
  2913. if (typeof v != 'object') {
  2914. if (v != node2[n]) {
  2915. node2[n] = v;
  2916. }
  2917. }
  2918. }
  2919. }
  2920. if (node.childNodes) {
  2921. node2.childNodes = new NodeList();
  2922. }
  2923. node2.ownerDocument = doc;
  2924. switch (node2.nodeType) {
  2925. case ELEMENT_NODE:
  2926. var attrs = node.attributes;
  2927. var attrs2 = (node2.attributes = new NamedNodeMap());
  2928. var len = attrs.length;
  2929. attrs2._ownerElement = node2;
  2930. for (var i = 0; i < len; i++) {
  2931. node2.setAttributeNode(cloneNode(doc, attrs.item(i), true));
  2932. }
  2933. break;
  2934. case ATTRIBUTE_NODE:
  2935. deep = true;
  2936. }
  2937. if (deep) {
  2938. var child = node.firstChild;
  2939. while (child) {
  2940. node2.appendChild(cloneNode(doc, child, deep));
  2941. child = child.nextSibling;
  2942. }
  2943. }
  2944. return node2;
  2945. }
  2946. function __set__(object, key, value) {
  2947. object[key] = value;
  2948. }
  2949. //do dynamic
  2950. try {
  2951. if (Object.defineProperty) {
  2952. Object.defineProperty(LiveNodeList.prototype, 'length', {
  2953. get: function () {
  2954. _updateLiveList(this);
  2955. return this.$$length;
  2956. },
  2957. });
  2958. Object.defineProperty(Node.prototype, 'textContent', {
  2959. get: function () {
  2960. return getTextContent(this);
  2961. },
  2962. set: function (data) {
  2963. switch (this.nodeType) {
  2964. case ELEMENT_NODE:
  2965. case DOCUMENT_FRAGMENT_NODE:
  2966. while (this.firstChild) {
  2967. this.removeChild(this.firstChild);
  2968. }
  2969. if (data || String(data)) {
  2970. this.appendChild(this.ownerDocument.createTextNode(data));
  2971. }
  2972. break;
  2973. default:
  2974. this.data = data;
  2975. this.value = data;
  2976. this.nodeValue = data;
  2977. }
  2978. },
  2979. });
  2980. function getTextContent(node) {
  2981. switch (node.nodeType) {
  2982. case ELEMENT_NODE:
  2983. case DOCUMENT_FRAGMENT_NODE:
  2984. var buf = [];
  2985. node = node.firstChild;
  2986. while (node) {
  2987. if (node.nodeType !== 7 && node.nodeType !== 8) {
  2988. buf.push(getTextContent(node));
  2989. }
  2990. node = node.nextSibling;
  2991. }
  2992. return buf.join('');
  2993. default:
  2994. return node.nodeValue;
  2995. }
  2996. }
  2997. __set__ = function (object, key, value) {
  2998. //console.log(value)
  2999. object['$$' + key] = value;
  3000. };
  3001. }
  3002. } catch (e) {
  3003. //ie8
  3004. }
  3005. exports._updateLiveList = _updateLiveList;
  3006. exports.Attr = Attr;
  3007. exports.CDATASection = CDATASection;
  3008. exports.CharacterData = CharacterData;
  3009. exports.Comment = Comment;
  3010. exports.Document = Document;
  3011. exports.DocumentFragment = DocumentFragment;
  3012. exports.DocumentType = DocumentType;
  3013. exports.DOMImplementation = DOMImplementation;
  3014. exports.Element = Element;
  3015. exports.Entity = Entity;
  3016. exports.EntityReference = EntityReference;
  3017. exports.LiveNodeList = LiveNodeList;
  3018. exports.NamedNodeMap = NamedNodeMap;
  3019. exports.Node = Node;
  3020. exports.NodeList = NodeList;
  3021. exports.Notation = Notation;
  3022. exports.Text = Text;
  3023. exports.ProcessingInstruction = ProcessingInstruction;
  3024. exports.XMLSerializer = XMLSerializer;