@angular/platform-server
Version:
Angular - library for using Angular in Node.js
1,507 lines (1,321 loc) • 568 kB
JavaScript
/**
* @license Angular v20.1.6
* (c) 2010-2025 Google LLC. https://angular.io/
* License: MIT
*/
import { ɵsetRootDomAdapter as _setRootDomAdapter, DOCUMENT, XhrFactory, PlatformLocation, ɵgetDOM as _getDOM, ɵPLATFORM_SERVER_ID as _PLATFORM_SERVER_ID, ViewportScroller, ɵNullViewportScroller as _NullViewportScroller } from '@angular/common';
import * as i0 from '@angular/core';
import { InjectionToken, inject, Injector, ɵstartMeasuring as _startMeasuring, ɵstopMeasuring as _stopMeasuring, Injectable, Inject, Optional, APP_ID, TransferState, PLATFORM_ID, PLATFORM_INITIALIZER, ɵALLOW_MULTIPLE_PLATFORMS as _ALLOW_MULTIPLE_PLATFORMS, NgModule, ɵsetDocument as _setDocument, createPlatformFactory, platformCore, Testability, ɵTESTABILITY as _TESTABILITY } from '@angular/core';
import { ɵBrowserDomAdapter as _BrowserDomAdapter, EventManagerPlugin, EVENT_MANAGER_PLUGINS, BrowserModule } from '@angular/platform-browser';
import { ɵHTTP_ROOT_INTERCEPTOR_FNS as _HTTP_ROOT_INTERCEPTOR_FNS } from '@angular/common/http';
import { Subject } from 'rxjs';
function getDefaultExportFromCjs (x) {
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
}
var lib = {};
var Event_1;
var hasRequiredEvent;
function requireEvent () {
if (hasRequiredEvent) return Event_1;
hasRequiredEvent = 1;
Event_1 = Event;
Event.CAPTURING_PHASE = 1;
Event.AT_TARGET = 2;
Event.BUBBLING_PHASE = 3;
function Event(type, dictionary) {
// Initialize basic event properties
this.type = '';
this.target = null;
this.currentTarget = null;
this.eventPhase = Event.AT_TARGET;
this.bubbles = false;
this.cancelable = false;
this.isTrusted = false;
this.defaultPrevented = false;
this.timeStamp = Date.now();
// Initialize internal flags
// XXX: Would it be better to inherit these defaults from the prototype?
this._propagationStopped = false;
this._immediatePropagationStopped = false;
this._initialized = true;
this._dispatching = false;
// Now initialize based on the constructor arguments (if any)
if (type) this.type = type;
if (dictionary) {
for(var p in dictionary) {
this[p] = dictionary[p];
}
}
}
Event.prototype = Object.create(Object.prototype, {
constructor: { value: Event },
stopPropagation: { value: function stopPropagation() {
this._propagationStopped = true;
}},
stopImmediatePropagation: { value: function stopImmediatePropagation() {
this._propagationStopped = true;
this._immediatePropagationStopped = true;
}},
preventDefault: { value: function preventDefault() {
if (this.cancelable) this.defaultPrevented = true;
}},
initEvent: { value: function initEvent(type, bubbles, cancelable) {
this._initialized = true;
if (this._dispatching) return;
this._propagationStopped = false;
this._immediatePropagationStopped = false;
this.defaultPrevented = false;
this.isTrusted = false;
this.target = null;
this.type = type;
this.bubbles = bubbles;
this.cancelable = cancelable;
}},
});
return Event_1;
}
var UIEvent_1;
var hasRequiredUIEvent;
function requireUIEvent () {
if (hasRequiredUIEvent) return UIEvent_1;
hasRequiredUIEvent = 1;
var Event = requireEvent();
UIEvent_1 = UIEvent;
function UIEvent() {
// Just use the superclass constructor to initialize
Event.call(this);
this.view = null; // FF uses the current window
this.detail = 0;
}
UIEvent.prototype = Object.create(Event.prototype, {
constructor: { value: UIEvent },
initUIEvent: { value: function(type, bubbles, cancelable, view, detail) {
this.initEvent(type, bubbles, cancelable);
this.view = view;
this.detail = detail;
}}
});
return UIEvent_1;
}
var MouseEvent_1;
var hasRequiredMouseEvent;
function requireMouseEvent () {
if (hasRequiredMouseEvent) return MouseEvent_1;
hasRequiredMouseEvent = 1;
var UIEvent = requireUIEvent();
MouseEvent_1 = MouseEvent;
function MouseEvent() {
// Just use the superclass constructor to initialize
UIEvent.call(this);
this.screenX = this.screenY = this.clientX = this.clientY = 0;
this.ctrlKey = this.altKey = this.shiftKey = this.metaKey = false;
this.button = 0;
this.buttons = 1;
this.relatedTarget = null;
}
MouseEvent.prototype = Object.create(UIEvent.prototype, {
constructor: { value: MouseEvent },
initMouseEvent: { value: function(type, bubbles, cancelable,
view, detail,
screenX, screenY, clientX, clientY,
ctrlKey, altKey, shiftKey, metaKey,
button, relatedTarget) {
this.initEvent(type, bubbles, cancelable, view, detail);
this.screenX = screenX;
this.screenY = screenY;
this.clientX = clientX;
this.clientY = clientY;
this.ctrlKey = ctrlKey;
this.altKey = altKey;
this.shiftKey = shiftKey;
this.metaKey = metaKey;
this.button = button;
switch(button) {
case 0: this.buttons = 1; break;
case 1: this.buttons = 4; break;
case 2: this.buttons = 2; break;
default: this.buttons = 0; break;
}
this.relatedTarget = relatedTarget;
}},
getModifierState: { value: function(key) {
switch(key) {
case "Alt": return this.altKey;
case "Control": return this.ctrlKey;
case "Shift": return this.shiftKey;
case "Meta": return this.metaKey;
default: return false;
}
}}
});
return MouseEvent_1;
}
var utils = {};
var config = {};
/*
* This file defines Domino behaviour that can be externally configured.
* To change these settings, set the relevant global property *before*
* you call `require("domino")`.
*/
var hasRequiredConfig;
function requireConfig () {
if (hasRequiredConfig) return config;
hasRequiredConfig = 1;
config.isApiWritable = !globalThis.__domino_frozen__;
return config;
}
var hasRequiredUtils;
function requireUtils () {
if (hasRequiredUtils) return utils;
hasRequiredUtils = 1;
var isApiWritable = requireConfig().isApiWritable;
utils.NAMESPACE = {
HTML: 'http://www.w3.org/1999/xhtml',
XML: 'http://www.w3.org/XML/1998/namespace',
XMLNS: 'http://www.w3.org/2000/xmlns/',
MATHML: 'http://www.w3.org/1998/Math/MathML',
SVG: 'http://www.w3.org/2000/svg',
XLINK: 'http://www.w3.org/1999/xlink',
};
//
// Shortcut functions for throwing errors of various types.
//
utils.IndexSizeError = () => {
throw new DOMException('The index is not in the allowed range', 'IndexSizeError');
};
utils.HierarchyRequestError = () => {
throw new DOMException('The node tree hierarchy is not correct', 'HierarchyRequestError');
};
utils.WrongDocumentError = () => {
throw new DOMException('The object is in the wrong Document', 'WrongDocumentError');
};
utils.InvalidCharacterError = () => {
throw new DOMException('The string contains invalid characters', 'InvalidCharacterError');
};
utils.NoModificationAllowedError = () => {
throw new DOMException('The object cannot be modified', 'NoModificationAllowedError');
};
utils.NotFoundError = () => {
throw new DOMException('The object can not be found here', 'NotFoundError');
};
utils.NotSupportedError = () => {
throw new DOMException('The operation is not supported', 'NotSupportedError');
};
utils.InvalidStateError = () => {
throw new DOMException('The object is in an invalid state', 'InvalidStateError');
};
utils.SyntaxError = () => {
throw new DOMException('The string did not match the expected pattern', 'SyntaxError');
};
utils.InvalidModificationError = () => {
throw new DOMException('The object can not be modified in this way', 'InvalidModificationError');
};
utils.NamespaceError = () => {
throw new DOMException('The operation is not allowed by Namespaces in XML', 'NamespaceError');
};
utils.InvalidAccessError = () => {
throw new DOMException(
'The object does not support the operation or argument',
'InvalidAccessError'
);
};
utils.TypeMismatchError = () => {
throw new DOMException(
'The type of the object does not match the expected type',
'TypeMismatchError'
);
};
utils.SecurityError = () => {
throw new DOMException('The operation is insecure', 'SecurityError');
};
utils.NetworkError = () => {
throw new DOMException('A network error occurred', 'NetworkError');
};
utils.AbortError = () => {
throw new DOMException('The operation was aborted', 'AbortError');
};
utils.UrlMismatchError = () => {
throw new DOMException('The given URL does not match another URL', 'URLMismatchError');
};
utils.QuotaExceededError = () => {
throw new DOMException('The quota has been exceeded', 'QuotaExceededError');
};
utils.TimeoutError = () => {
throw new DOMException('The operation timed out', 'TimeoutError');
};
utils.InvalidNodeTypeError = () => {
throw new DOMException('The node is of an invalid type', 'InvalidNodeTypeError');
};
utils.DataCloneError = () => {
throw new DOMException('The object can not be cloned', 'DataCloneError');
};
utils.InUseAttributeError = () => {
throw new DOMException('The attribute is already in use', 'InUseAttributeError');
};
utils.nyi = function () {
throw new Error('NotYetImplemented');
};
utils.shouldOverride = function () {
throw new Error('Abstract function; should be overriding in subclass.');
};
utils.assert = function (expr, msg) {
if (!expr) {
throw new Error('Assertion failed: ' + (msg || '') + '\n' + new Error().stack);
}
};
utils.expose = function (src, c) {
for (var n in src) {
Object.defineProperty(c.prototype, n, {
value: src[n],
writable: isApiWritable,
});
}
};
utils.merge = function (a, b) {
for (var n in b) {
a[n] = b[n];
}
};
// Compare two nodes based on their document order. This function is intended
// to be passed to sort(). Assumes that the array being sorted does not
// contain duplicates. And that all nodes are connected and comparable.
// Clever code by ppk via jeresig.
utils.documentOrder = function (n, m) {
/* jshint bitwise: false */
return 3 - (n.compareDocumentPosition(m) & 6);
};
utils.toASCIILowerCase = function (s) {
return s.replace(/[A-Z]+/g, function (c) {
return c.toLowerCase();
});
};
utils.toASCIIUpperCase = function (s) {
return s.replace(/[a-z]+/g, function (c) {
return c.toUpperCase();
});
};
return utils;
}
var EventTarget_1;
var hasRequiredEventTarget;
function requireEventTarget () {
if (hasRequiredEventTarget) return EventTarget_1;
hasRequiredEventTarget = 1;
var Event = requireEvent();
var MouseEvent = requireMouseEvent();
var utils = requireUtils();
EventTarget_1 = EventTarget;
function EventTarget() {}
EventTarget.prototype = {
// XXX
// See WebIDL §4.8 for details on object event handlers
// and how they should behave. We actually have to accept
// any object to addEventListener... Can't type check it.
// on registration.
// XXX:
// Capturing event listeners are sort of rare. I think I can optimize
// them so that dispatchEvent can skip the capturing phase (or much of
// it). Each time a capturing listener is added, increment a flag on
// the target node and each of its ancestors. Decrement when removed.
// And update the counter when nodes are added and removed from the
// tree as well. Then, in dispatch event, the capturing phase can
// abort if it sees any node with a zero count.
addEventListener: function addEventListener(type, listener, capture) {
if (!listener) return;
if (capture === undefined) capture = false;
if (!this._listeners) this._listeners = Object.create(null);
if (!this._listeners[type]) this._listeners[type] = [];
var list = this._listeners[type];
// If this listener has already been registered, just return
for(var i = 0, n = list.length; i < n; i++) {
var l = list[i];
if (l.listener === listener && l.capture === capture)
return;
}
// Add an object to the list of listeners
var obj = { listener: listener, capture: capture };
if (typeof listener === 'function') obj.f = listener;
list.push(obj);
},
removeEventListener: function removeEventListener(type,
listener,
capture) {
if (capture === undefined) capture = false;
if (this._listeners) {
var list = this._listeners[type];
if (list) {
// Find the listener in the list and remove it
for(var i = 0, n = list.length; i < n; i++) {
var l = list[i];
if (l.listener === listener && l.capture === capture) {
if (list.length === 1) {
this._listeners[type] = undefined;
}
else {
list.splice(i, 1);
}
return;
}
}
}
}
},
// This is the public API for dispatching untrusted public events.
// See _dispatchEvent for the implementation
dispatchEvent: function dispatchEvent(event) {
// Dispatch an untrusted event
return this._dispatchEvent(event, false);
},
//
// See DOMCore §4.4
// XXX: I'll probably need another version of this method for
// internal use, one that does not set isTrusted to false.
// XXX: see Document._dispatchEvent: perhaps that and this could
// call a common internal function with different settings of
// a trusted boolean argument
//
// XXX:
// The spec has changed in how to deal with handlers registered
// on idl or content attributes rather than with addEventListener.
// Used to say that they always ran first. That's how webkit does it
// Spec now says that they run in a position determined by
// when they were first set. FF does it that way. See:
// http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#event-handlers
//
_dispatchEvent: function _dispatchEvent(event, trusted) {
if (typeof trusted !== 'boolean') trusted = false;
function invoke(target, event) {
var type = event.type, phase = event.eventPhase;
event.currentTarget = target;
// If there was an individual handler defined, invoke it first
// XXX: see comment above: this shouldn't always be first.
if (phase !== Event.CAPTURING_PHASE &&
target._handlers && target._handlers[type])
{
var handler = target._handlers[type];
var rv;
if (typeof handler === 'function') {
rv=handler.call(event.currentTarget, event);
}
else {
var f = handler.handleEvent;
if (typeof f !== 'function')
throw new TypeError('handleEvent property of ' +
'event handler object is' +
'not a function.');
rv=f.call(handler, event);
}
switch(event.type) {
case 'mouseover':
if (rv === true) // Historical baggage
event.preventDefault();
break;
case 'beforeunload':
// XXX: eventually we need a special case here
/* falls through */
default:
if (rv === false)
event.preventDefault();
break;
}
}
// Now invoke list list of listeners for this target and type
var list = target._listeners && target._listeners[type];
if (!list) return;
list = list.slice();
for(var i = 0, n = list.length; i < n; i++) {
if (event._immediatePropagationStopped) return;
var l = list[i];
if ((phase === Event.CAPTURING_PHASE && !l.capture) ||
(phase === Event.BUBBLING_PHASE && l.capture))
continue;
if (l.f) {
l.f.call(event.currentTarget, event);
}
else {
var fn = l.listener.handleEvent;
if (typeof fn !== 'function')
throw new TypeError('handleEvent property of event listener object is not a function.');
fn.call(l.listener, event);
}
}
}
if (!event._initialized || event._dispatching) utils.InvalidStateError();
event.isTrusted = trusted;
// Begin dispatching the event now
event._dispatching = true;
event.target = this;
// Build the list of targets for the capturing and bubbling phases
// XXX: we'll eventually have to add Window to this list.
var ancestors = [];
for(var n = this.parentNode; n; n = n.parentNode)
ancestors.push(n);
// Capturing phase
event.eventPhase = Event.CAPTURING_PHASE;
for(var i = ancestors.length-1; i >= 0; i--) {
invoke(ancestors[i], event);
if (event._propagationStopped) break;
}
// At target phase
if (!event._propagationStopped) {
event.eventPhase = Event.AT_TARGET;
invoke(this, event);
}
// Bubbling phase
if (event.bubbles && !event._propagationStopped) {
event.eventPhase = Event.BUBBLING_PHASE;
for(var ii = 0, nn = ancestors.length; ii < nn; ii++) {
invoke(ancestors[ii], event);
if (event._propagationStopped) break;
}
}
event._dispatching = false;
event.eventPhase = Event.AT_TARGET;
event.currentTarget = null;
// Deal with mouse events and figure out when
// a click has happened
if (trusted && !event.defaultPrevented && event instanceof MouseEvent) {
switch(event.type) {
case 'mousedown':
this._armed = {
x: event.clientX,
y: event.clientY,
t: event.timeStamp
};
break;
case 'mouseout':
case 'mouseover':
this._armed = null;
break;
case 'mouseup':
if (this._isClick(event)) this._doClick(event);
this._armed = null;
break;
}
}
return !event.defaultPrevented;
},
// Determine whether a click occurred
// XXX We don't support double clicks for now
_isClick: function(event) {
return (this._armed !== null &&
event.type === 'mouseup' &&
event.isTrusted &&
event.button === 0 &&
event.timeStamp - this._armed.t < 1000 &&
Math.abs(event.clientX - this._armed.x) < 10 &&
Math.abs(event.clientY - this._armed.Y) < 10);
},
// Clicks are handled like this:
// http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#interactive-content-0
//
// Note that this method is similar to the HTMLElement.click() method
// The event argument must be the trusted mouseup event
_doClick: function(event) {
if (this._click_in_progress) return;
this._click_in_progress = true;
// Find the nearest enclosing element that is activatable
// An element is activatable if it has a
// _post_click_activation_steps hook
var activated = this;
while(activated && !activated._post_click_activation_steps)
activated = activated.parentNode;
if (activated && activated._pre_click_activation_steps) {
activated._pre_click_activation_steps();
}
var click = this.ownerDocument.createEvent('MouseEvent');
click.initMouseEvent('click', true, true,
this.ownerDocument.defaultView, 1,
event.screenX, event.screenY,
event.clientX, event.clientY,
event.ctrlKey, event.altKey,
event.shiftKey, event.metaKey,
event.button, null);
var result = this._dispatchEvent(click, true);
if (activated) {
if (result) {
// This is where hyperlinks get followed, for example.
if (activated._post_click_activation_steps)
activated._post_click_activation_steps(click);
}
else {
if (activated._cancelled_activation_steps)
activated._cancelled_activation_steps();
}
}
},
//
// An event handler is like an event listener, but it registered
// by setting an IDL or content attribute like onload or onclick.
// There can only be one of these at a time for any event type.
// This is an internal method for the attribute accessors and
// content attribute handlers that need to register events handlers.
// The type argument is the same as in addEventListener().
// The handler argument is the same as listeners in addEventListener:
// it can be a function or an object. Pass null to remove any existing
// handler. Handlers are always invoked before any listeners of
// the same type. They are not invoked during the capturing phase
// of event dispatch.
//
_setEventHandler: function _setEventHandler(type, handler) {
if (!this._handlers) this._handlers = Object.create(null);
this._handlers[type] = handler;
},
_getEventHandler: function _getEventHandler(type) {
return (this._handlers && this._handlers[type]) || null;
}
};
return EventTarget_1;
}
var LinkedList = {exports: {}};
var hasRequiredLinkedList;
function requireLinkedList () {
if (hasRequiredLinkedList) return LinkedList.exports;
hasRequiredLinkedList = 1;
var utils = requireUtils();
var LinkedList$1 = LinkedList.exports = {
// basic validity tests on a circular linked list a
valid: function(a) {
utils.assert(a, "list falsy");
utils.assert(a._previousSibling, "previous falsy");
utils.assert(a._nextSibling, "next falsy");
// xxx check that list is actually circular
return true;
},
// insert a before b
insertBefore: function(a, b) {
utils.assert(LinkedList$1.valid(a) && LinkedList$1.valid(b));
var a_first = a, a_last = a._previousSibling;
var b_first = b, b_last = b._previousSibling;
a_first._previousSibling = b_last;
a_last._nextSibling = b_first;
b_last._nextSibling = a_first;
b_first._previousSibling = a_last;
utils.assert(LinkedList$1.valid(a) && LinkedList$1.valid(b));
},
// replace a single node a with a list b (which could be null)
replace: function(a, b) {
utils.assert(LinkedList$1.valid(a) && (b===null || LinkedList$1.valid(b)));
if (b!==null) {
LinkedList$1.insertBefore(b, a);
}
LinkedList$1.remove(a);
utils.assert(LinkedList$1.valid(a) && (b===null || LinkedList$1.valid(b)));
},
// remove single node a from its list
remove: function(a) {
utils.assert(LinkedList$1.valid(a));
var prev = a._previousSibling;
if (prev === a) { return; }
var next = a._nextSibling;
prev._nextSibling = next;
next._previousSibling = prev;
a._previousSibling = a._nextSibling = a;
utils.assert(LinkedList$1.valid(a));
}
};
return LinkedList.exports;
}
var NodeUtils;
var hasRequiredNodeUtils;
function requireNodeUtils () {
if (hasRequiredNodeUtils) return NodeUtils;
hasRequiredNodeUtils = 1;
NodeUtils = {
// NOTE: The `serializeOne()` function used to live on the `Node.prototype`
// as a private method `Node#_serializeOne(child)`, however that requires
// a megamorphic property access `this._serializeOne` just to get to the
// method, and this is being done on lots of different `Node` subclasses,
// which puts a lot of pressure on V8's megamorphic stub cache. So by
// moving the helper off of the `Node.prototype` and into a separate
// function in this helper module, we get a monomorphic property access
// `NodeUtils.serializeOne` to get to the function and reduce pressure
// on the megamorphic stub cache.
// See https://github.com/fgnass/domino/pull/142 for more information.
serializeOne: serializeOne,
// Export util functions so that we can run extra test for them.
// Note: we prefix function names with `ɵ`, similar to what we do
// with internal functions in Angular packages.
ɵescapeMatchingClosingTag: escapeMatchingClosingTag,
ɵescapeClosingCommentTag: escapeClosingCommentTag,
ɵescapeProcessingInstructionContent: escapeProcessingInstructionContent
};
var utils = requireUtils();
var NAMESPACE = utils.NAMESPACE;
var hasRawContent = {
STYLE: true,
SCRIPT: true,
XMP: true,
IFRAME: true,
NOEMBED: true,
NOFRAMES: true,
PLAINTEXT: true
};
var emptyElements = {
area: true,
base: true,
basefont: true,
bgsound: true,
br: true,
col: true,
embed: true,
frame: true,
hr: true,
img: true,
input: true,
keygen: true,
link: true,
meta: true,
param: true,
source: true,
track: true,
wbr: true
};
var extraNewLine = {
/* Removed in https://github.com/whatwg/html/issues/944
pre: true,
textarea: true,
listing: true
*/
};
const ESCAPE_REGEXP = /[&<>\u00A0]/g;
const ESCAPE_ATTR_REGEXP = /[&"<>\u00A0]/g;
function escape(s) {
if (!ESCAPE_REGEXP.test(s)) {
// nothing to do, fast path
return s;
}
return s.replace(ESCAPE_REGEXP, (c) => {
switch (c) {
case "&":
return "&";
case "<":
return "<";
case ">":
return ">";
case "\u00A0":
return " ";
}
});
}
function escapeAttr(s) {
if (!ESCAPE_ATTR_REGEXP.test(s)) {
// nothing to do, fast path
return s;
}
return s.replace(ESCAPE_ATTR_REGEXP, (c) => {
switch (c) {
case "<":
return "<";
case ">":
return ">";
case "&":
return "&";
case '"':
return """;
case "\u00A0":
return " ";
}
});
}
function attrname(a) {
var ns = a.namespaceURI;
if (!ns)
return a.localName;
if (ns === NAMESPACE.XML)
return 'xml:' + a.localName;
if (ns === NAMESPACE.XLINK)
return 'xlink:' + a.localName;
if (ns === NAMESPACE.XMLNS) {
if (a.localName === 'xmlns') return 'xmlns';
else return 'xmlns:' + a.localName;
}
return a.name;
}
/**
* Escapes matching closing tag in a raw text.
*
* For example, given `<style>#text(</style><script></script>)</style>`,
* the parent tag would by "style" and the raw text is
* "</style><script></script>". If we come across a matching closing tag
* (in out case `</style>`) - replace `<` with `<` to avoid unexpected
* and unsafe behavior after de-serialization.
*/
function escapeMatchingClosingTag(rawText, parentTag) {
const parentClosingTag = '</' + parentTag;
if (!rawText.toLowerCase().includes(parentClosingTag)) {
return rawText; // fast path
}
const result = [...rawText];
const matches = rawText.matchAll(new RegExp(parentClosingTag, 'ig'));
for (const match of matches) {
result[match.index] = '<';
}
return result.join('');
}
const CLOSING_COMMENT_REGEXP = /--!?>/;
/**
* Escapes closing comment tag in a comment content.
*
* For example, given `#comment('-->')`, the content of a comment would be
* updated to `-->` to avoid unexpected and unsafe behavior after
* de-serialization.
*/
function escapeClosingCommentTag(rawContent) {
if (!CLOSING_COMMENT_REGEXP.test(rawContent)) {
return rawContent; // fast path
}
return rawContent.replace(/(--\!?)>/g, '$1>');
}
/**
* Escapes processing instruction content by replacing `>` with `>`.
*/
function escapeProcessingInstructionContent(rawContent) {
return rawContent.includes('>')
? rawContent.replaceAll('>', '>')
: rawContent;
}
function serializeOne(kid, parent) {
var s = '';
switch(kid.nodeType) {
case 1: //ELEMENT_NODE
var ns = kid.namespaceURI;
var html = ns === NAMESPACE.HTML;
var tagname = (html || ns === NAMESPACE.SVG || ns === NAMESPACE.MATHML) ? kid.localName : kid.tagName;
s += '<' + tagname;
for(var j = 0, k = kid._numattrs; j < k; j++) {
var a = kid._attr(j);
s += ' ' + attrname(a);
if (a.value !== undefined) s += '="' + escapeAttr(a.value) + '"';
}
s += '>';
if (!(html && emptyElements[tagname])) {
var ss = kid.serialize();
// If an element can have raw content, this content may
// potentially require escaping to avoid XSS.
if (hasRawContent[tagname.toUpperCase()]) {
ss = escapeMatchingClosingTag(ss, tagname);
}
if (html && extraNewLine[tagname] && ss.charAt(0)==='\n') s += '\n';
// Serialize children and add end tag for all others
s += ss;
s += '</' + tagname + '>';
}
break;
case 3: //TEXT_NODE
case 4: //CDATA_SECTION_NODE
var parenttag;
if (parent.nodeType === 1 /*ELEMENT_NODE*/ &&
parent.namespaceURI === NAMESPACE.HTML)
parenttag = parent.tagName;
else
parenttag = '';
if (hasRawContent[parenttag] ||
(parenttag==='NOSCRIPT' && parent.ownerDocument._scripting_enabled)) {
s += kid.data;
} else {
s += escape(kid.data);
}
break;
case 8: //COMMENT_NODE
s += '<!--' + escapeClosingCommentTag(kid.data) + '-->';
break;
case 7: //PROCESSING_INSTRUCTION_NODE
const content = escapeProcessingInstructionContent(kid.data);
s += '<?' + kid.target + ' ' + content + '?>';
break;
case 10: //DOCUMENT_TYPE_NODE
s += '<!DOCTYPE ' + kid.name;
s += '>';
break;
default:
utils.InvalidStateError();
}
return s;
}
return NodeUtils;
}
var Node_1;
var hasRequiredNode;
function requireNode () {
if (hasRequiredNode) return Node_1;
hasRequiredNode = 1;
Node_1 = Node;
var EventTarget = requireEventTarget();
var LinkedList = requireLinkedList();
var NodeUtils = requireNodeUtils();
var utils = requireUtils();
// All nodes have a nodeType and an ownerDocument.
// Once inserted, they also have a parentNode.
// This is an abstract class; all nodes in a document are instances
// of a subtype, so all the properties are defined by more specific
// constructors.
function Node() {
EventTarget.call(this);
this.parentNode = null;
this._nextSibling = this._previousSibling = this;
this._index = undefined;
}
var ELEMENT_NODE = Node.ELEMENT_NODE = 1;
var ATTRIBUTE_NODE = Node.ATTRIBUTE_NODE = 2;
var TEXT_NODE = Node.TEXT_NODE = 3;
var CDATA_SECTION_NODE = Node.CDATA_SECTION_NODE = 4;
var ENTITY_REFERENCE_NODE = Node.ENTITY_REFERENCE_NODE = 5;
var ENTITY_NODE = Node.ENTITY_NODE = 6;
var PROCESSING_INSTRUCTION_NODE = Node.PROCESSING_INSTRUCTION_NODE = 7;
var COMMENT_NODE = Node.COMMENT_NODE = 8;
var DOCUMENT_NODE = Node.DOCUMENT_NODE = 9;
var DOCUMENT_TYPE_NODE = Node.DOCUMENT_TYPE_NODE = 10;
var DOCUMENT_FRAGMENT_NODE = Node.DOCUMENT_FRAGMENT_NODE = 11;
var NOTATION_NODE = Node.NOTATION_NODE = 12;
var DOCUMENT_POSITION_DISCONNECTED = Node.DOCUMENT_POSITION_DISCONNECTED = 0x01;
var DOCUMENT_POSITION_PRECEDING = Node.DOCUMENT_POSITION_PRECEDING = 0x02;
var DOCUMENT_POSITION_FOLLOWING = Node.DOCUMENT_POSITION_FOLLOWING = 0x04;
var DOCUMENT_POSITION_CONTAINS = Node.DOCUMENT_POSITION_CONTAINS = 0x08;
var DOCUMENT_POSITION_CONTAINED_BY = Node.DOCUMENT_POSITION_CONTAINED_BY = 0x10;
var DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC = 0x20;
Node.prototype = Object.create(EventTarget.prototype, {
// Node that are not inserted into the tree inherit a null parent
// XXX: the baseURI attribute is defined by dom core, but
// a correct implementation of it requires HTML features, so
// we'll come back to this later.
baseURI: { get: utils.nyi },
parentElement: { get: function() {
return (this.parentNode && this.parentNode.nodeType===ELEMENT_NODE) ? this.parentNode : null;
}},
hasChildNodes: { value: utils.shouldOverride },
firstChild: { get: utils.shouldOverride },
lastChild: { get: utils.shouldOverride },
isConnected: {
get: function () {
let node = this;
while (node != null) {
if (node.nodeType === Node.DOCUMENT_NODE) {
return true;
}
node = node.parentNode;
if (node != null && node.nodeType === Node.DOCUMENT_FRAGMENT_NODE) {
node = node.host;
}
}
return false;
},
},
previousSibling: { get: function() {
var parent = this.parentNode;
if (!parent) return null;
if (this === parent.firstChild) return null;
return this._previousSibling;
}},
nextSibling: { get: function() {
var parent = this.parentNode, next = this._nextSibling;
if (!parent) return null;
if (next === parent.firstChild) return null;
return next;
}},
textContent: {
// Should override for DocumentFragment/Element/Attr/Text/PI/Comment
get: function() { return null; },
set: function(v) { /* do nothing */ },
},
innerText: {
// Should override for DocumentFragment/Element/Attr/Text/PI/Comment
get: function() { return null; },
set: function(v) { /* do nothing */ },
},
_countChildrenOfType: { value: function(type) {
var sum = 0;
for (var kid = this.firstChild; kid !== null; kid = kid.nextSibling) {
if (kid.nodeType === type) sum++;
}
return sum;
}},
_ensureInsertValid: { value: function _ensureInsertValid(node, child, isPreinsert) {
var parent = this, i, kid;
if (!node.nodeType) throw new TypeError('not a node');
// 1. If parent is not a Document, DocumentFragment, or Element
// node, throw a HierarchyRequestError.
switch (parent.nodeType) {
case DOCUMENT_NODE:
case DOCUMENT_FRAGMENT_NODE:
case ELEMENT_NODE:
break;
default: utils.HierarchyRequestError();
}
// 2. If node is a host-including inclusive ancestor of parent,
// throw a HierarchyRequestError.
if (node.isAncestor(parent)) utils.HierarchyRequestError();
// 3. If child is not null and its parent is not parent, then
// throw a NotFoundError. (replaceChild omits the 'child is not null'
// and throws a TypeError here if child is null.)
if (child !== null || !isPreinsert) {
if (child.parentNode !== parent) utils.NotFoundError();
}
// 4. If node is not a DocumentFragment, DocumentType, Element,
// Text, ProcessingInstruction, or Comment node, throw a
// HierarchyRequestError.
switch (node.nodeType) {
case DOCUMENT_FRAGMENT_NODE:
case DOCUMENT_TYPE_NODE:
case ELEMENT_NODE:
case TEXT_NODE:
case PROCESSING_INSTRUCTION_NODE:
case COMMENT_NODE:
break;
default: utils.HierarchyRequestError();
}
// 5. If either node is a Text node and parent is a document, or
// node is a doctype and parent is not a document, throw a
// HierarchyRequestError.
// 6. If parent is a document, and any of the statements below, switched
// on node, are true, throw a HierarchyRequestError.
if (parent.nodeType === DOCUMENT_NODE) {
switch (node.nodeType) {
case TEXT_NODE:
utils.HierarchyRequestError();
break;
case DOCUMENT_FRAGMENT_NODE:
// 6a1. If node has more than one element child or has a Text
// node child.
if (node._countChildrenOfType(TEXT_NODE) > 0)
utils.HierarchyRequestError();
switch (node._countChildrenOfType(ELEMENT_NODE)) {
case 0:
break;
case 1:
// 6a2. Otherwise, if node has one element child and either
// parent has an element child, child is a doctype, or child
// is not null and a doctype is following child. [preinsert]
// 6a2. Otherwise, if node has one element child and either
// parent has an element child that is not child or a
// doctype is following child. [replaceWith]
if (child !== null /* always true here for replaceWith */) {
if (isPreinsert && child.nodeType === DOCUMENT_TYPE_NODE)
utils.HierarchyRequestError();
for (kid = child.nextSibling; kid !== null; kid = kid.nextSibling) {
if (kid.nodeType === DOCUMENT_TYPE_NODE)
utils.HierarchyRequestError();
}
}
i = parent._countChildrenOfType(ELEMENT_NODE);
if (isPreinsert) {
// "parent has an element child"
if (i > 0)
utils.HierarchyRequestError();
} else {
// "parent has an element child that is not child"
if (i > 1 || (i === 1 && child.nodeType !== ELEMENT_NODE))
utils.HierarchyRequestError();
}
break;
default: // 6a1, continued. (more than one Element child)
utils.HierarchyRequestError();
}
break;
case ELEMENT_NODE:
// 6b. parent has an element child, child is a doctype, or
// child is not null and a doctype is following child. [preinsert]
// 6b. parent has an element child that is not child or a
// doctype is following child. [replaceWith]
if (child !== null /* always true here for replaceWith */) {
if (isPreinsert && child.nodeType === DOCUMENT_TYPE_NODE)
utils.HierarchyRequestError();
for (kid = child.nextSibling; kid !== null; kid = kid.nextSibling) {
if (kid.nodeType === DOCUMENT_TYPE_NODE)
utils.HierarchyRequestError();
}
}
i = parent._countChildrenOfType(ELEMENT_NODE);
if (isPreinsert) {
// "parent has an element child"
if (i > 0)
utils.HierarchyRequestError();
} else {
// "parent has an element child that is not child"
if (i > 1 || (i === 1 && child.nodeType !== ELEMENT_NODE))
utils.HierarchyRequestError();
}
break;
case DOCUMENT_TYPE_NODE:
// 6c. parent has a doctype child, child is non-null and an
// element is preceding child, or child is null and parent has
// an element child. [preinsert]
// 6c. parent has a doctype child that is not child, or an
// element is preceding child. [replaceWith]
if (child === null) {
if (parent._countChildrenOfType(ELEMENT_NODE))
utils.HierarchyRequestError();
} else {
// child is always non-null for [replaceWith] case
for (kid = parent.firstChild; kid !== null; kid = kid.nextSibling) {
if (kid === child) break;
if (kid.nodeType === ELEMENT_NODE)
utils.HierarchyRequestError();
}
}
i = parent._countChildrenOfType(DOCUMENT_TYPE_NODE);
if (isPreinsert) {
// "parent has an doctype child"
if (i > 0)
utils.HierarchyRequestError();
} else {
// "parent has an doctype child that is not child"
if (i > 1 || (i === 1 && child.nodeType !== DOCUMENT_TYPE_NODE))
utils.HierarchyRequestError();
}
break;
}
} else {
// 5, continued: (parent is not a document)
if (node.nodeType === DOCUMENT_TYPE_NODE) utils.HierarchyRequestError();
}
}},
insertBefore: { value: function insertBefore(node, child) {
var parent = this;
// 1. Ensure pre-insertion validity
parent._ensureInsertValid(node, child, true);
// 2. Let reference child be child.
var refChild = child;
// 3. If reference child is node, set it to node's next sibling
if (refChild === node) { refChild = node.nextSibling; }
// 4. Adopt node into parent's node document.
parent.doc.adoptNode(node);
// 5. Insert node into parent before reference child.
node._insertOrReplace(parent, refChild, false);
// 6. Return node
return node;
}},
appendChild: { value: function(child) {
// This invokes _appendChild after doing validity checks.
return this.insertBefore(child, null);
}},
_appendChild: { value: function(child) {
child._insertOrReplace(this, null, false);
}},
removeChild: { value: function removeChild(child) {
var parent = this;
if (!child.nodeType) throw new TypeError('not a node');
if (child.parentNode !== parent) utils.NotFoundError();
child.remove();
return child;
}},
// To replace a `child` with `node` within a `parent` (this)
replaceChild: { value: function replaceChild(node, child) {
var parent = this;
// Ensure validity (slight differences from pre-insertion check)
parent._ensureInsertValid(node, child, false);
// Adopt node into parent's node document.
if (node.doc !== parent.doc) {
// XXX adoptNode has side-effect of removing node from its parent
// and generating a mutation event, thus causing the _insertOrReplace
// to generate two deletes and an insert instead of a 'move'
// event. It looks like the new MutationObserver stuff avoids
// this problem, but for now let's only adopt (ie, remove `node`
// from its parent) here if we need to.
parent.doc.adoptNode(node);
}
// Do the replace.
node._insertOrReplace(parent, child, true);
return child;
}},
// See: http://ejohn.org/blog/comparing-document-position/
contains: { value: function contains(node) {
if (node === null) { return false; }
if (this === node) { return true; /* inclusive descendant */ }
/* jshint bitwise: false */
return (this.compareDocumentPosition(node) &
DOCUMENT_POSITION_CONTAINED_BY) !== 0;
}},
compareDocumentPosition: { value: function compareDocumentPosition(that){
// Basic algorithm for finding the relative position of two nodes.
// Make a list the ancestors of each node, starting with the
// document element and proceeding down to the nodes themselves.
// Then, loop through the lists, looking for the first element
// that differs. The order of those two elements give the
// order of their descendant nodes. Or, if one list is a prefix
// of the other one, then that node contains the other.
if (this === that) return 0;
// If they're not owned by the same document or if one is rooted
// and one is not, then they're disconnected.
if (this.doc !== that.doc ||
this.rooted !== that.rooted)
return (DOCUMENT_POSITION_DISCONNECTED +
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC);
// Get arrays of ancestors for this and that
var these = [], those = [];
for(var n = this; n !== null; n = n.parentNode) these.push(n);
for(n = that; n !== null; n = n.parentNode) those.push(n);
these.reverse(); // So we start with the outermost
those.reverse();
if (these[0] !== those[0]) // No common ancestor
return (DOCUMENT_POSITION_DISCONNECTED +
DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC);
n = Math.min(these.length, those.length);
for(var i = 1; i < n; i++) {
if (these[i] !== those[i]) {
// We found two different ancestors, so compare
// their positions
if (these[i].index < those[i].index)
return DOCUMENT_POSITION_FOLLOWING;
else
return DOCUMENT_POSITION_PRECEDING;
}
}
// If we get to here, then one of the nodes (the one with the
// shorter list of ancestors) contains the other one.
if (these.length < those.length)
return (DOCUMENT_POSITION_FOLLOWING +
DOCUMENT_POSITION_CONTAINED_BY);
else
return (DOCUMENT_POSITION_PRECEDING +
DOCUMENT_POSITION_CONTAINS);
}},
isSameNode: {value : function isSameNode(node) {
return this === node;
}},
// This method implements the generic parts of node equality testing
// and defers to the (non-recursive) type-specific isEqual() method
// defined by subclasses
isEqualNode: { value: function isEqualNode(node) {
if (!node) return false;
if (node.nodeType !== this.nodeType) return false;
// Check type-specific properties for equality
if (!this.isEqual(node)) return false;
// Now check children for number and equality
for (var c1 = this.firstChild, c2 = node.firstChild;
c1 && c2;
c1 = c1.nextSibling, c2 = c2.nextSibling) {
if (!c1.isEqualNode(c2)) return false;
}
return c1 === null && c2 === null;
}},
// This method delegates shallow cloning to a clone() method
// that each concrete subclass must implement
cloneNode: { value: function(deep) {
// Clone this node
var clone = this.clone();
// Handle the recursive case if necessary
if (deep) {
for (var kid = this.firstChild; kid !== null; kid = kid.nextSibling) {
clone._appendChild(kid.cloneNode(true));
}
}
return clone;
}},
lookupPrefix: { value: function lookupPrefix(ns) {
var e;
if (ns === '' || ns === null || ns === undefined) return null;
switch(this.nodeType) {
case ELEMENT_NODE:
return this._lookupNamespacePrefix(ns, this);
case DOCUMENT_NODE:
e = this.documentElement;
return e ? e.lookupPrefix(ns) : null;
case ENTITY_NODE:
case NOTATION_NODE:
case DOCUMENT_FRAGMENT_NODE:
case DOCUMENT_TYPE_NODE:
return null;
case ATTRIBUTE_NODE:
e = this.ownerElement;
return e ? e.lookupPrefix(ns) : null;
default:
e = this.parentElement;
return e ? e.lookupPrefix(ns) : null;
}
}},
lookupNamespaceURI: {value: function lookupNamespaceURI(prefix) {
if (prefix === '' || prefix === undefined) { prefix = null; }
var e;
switch(this.nodeType) {
case ELEMENT_NODE:
return utils.shouldOverride();
case DOCUMENT_NODE:
e = this.documentElement;
return e ? e.lookupNamespaceURI(prefix) : null;
case ENTITY_NODE:
case NOTATION_NODE:
case DOCUMENT_TYPE_NODE:
case DOCUMENT_FRAGMENT_NODE:
return null;
case ATTRIBUTE_NODE:
e = this.ownerElement;
return e ? e.lookupNamespaceURI(prefix) : null;
default:
e = this.parentElement;
return e ? e.lookupNamespaceURI(prefix) : null;
}
}},
isDefaultNamespace: { value: function isDefaultNamespace(ns) {
if (ns === '' || ns === undefined) { ns = null; }
var defaultNamespace = this.lookupNamespaceURI(null);
return (defaultNamespace === ns);
}},
// Utility methods for nodes. Not part of the DOM
// Return the index of this node in its parent.
// Throw if no parent, or if this node is not a child of its parent
index: { get: function() {
var parent = this.parentNode;
if (this === parent.firstChild) return 0; // fast case
var kids = parent.childNodes;
if (this._index === undefined || kids[this._index] !== this) {
// Ensure that we don't have an O(N^2) blowup if none of the
// kids have defined indices yet and we're traversing via
// nextSibling or previousSibling
for (var i=0; i<kids.length; i++) {
kids[i]._index = i;
}
utils.assert(kids[this._index] === this);
}
return this._index;
}},
// Return true if this node is equal to or is an ancestor of that node
// Note that nodes are considered to be ancestors of themselves
isAncestor: { value: function(that) {
// If they belong to different documents, then they're unrelated.
if (this.doc !== that.doc) return false;
// If one is rooted and one isn't then they're not related
if (this.rooted !== that.rooted) return false;
// Otherwise check by traversing the parentNode chain
for(var e = that; e; e = e.parentNode) {
if (e === this) return true;
}
return false;
}},
// DOMINO Changed the behavior to conform with the specs. See:
// https://groups.google.com/d/topic/mozilla.dev.platform/77sIYcpdDmc/discussion
ensureSameDoc: { value: function(that) {
if (that.ownerDocument === null) {
that.ownerDocument = this.doc;
}
else if(that.ownerDocument !== this.doc) {
utils.WrongDocumentError();
}
}},
removeChildren: { value: utils.shouldOverride },
// Insert this node as a child of parent before the specified child,
// or insert as the last child of parent if specified child is null,
// or replace the specified child with this node, firing mutation events as
// necessary
_insertOrReplace: { value: function _insertOrReplace(parent, before, isReplace) {
var child = this, before_index, i;
if (child.nodeType === DOCUMENT_FRAGMENT_NODE && child.rooted) {
utils.HierarchyRequestError();
}
/* Ensure index of `before` is cached before we (possibly) remove it. */
if (parent._childNodes) {
before_index = (before === null) ? parent._childNodes.length :
before.index; /* ensure _index is cached */
// If we are already a child of the specified parent, then
// the index may have to be adjusted.
if (child.parentNode === parent) {
var child_index = child.index;
// If the child is before the spot it is to be inserted at,
// then when it is removed, the index of that spot will be
// reduced.
if (child_index < before_index) {
before_index--;
}
}
}
// Delete the old c