comparison src/sceditor.js @ 19:13df5ac9b34b

more OO
author Franklin Schmidt <fschmidt@gmail.com>
date Mon, 08 Aug 2022 16:50:22 -0600
parents 1334920263a2
children cf42d9b17c25
comparison
equal deleted inserted replaced
18:1334920263a2 19:13df5ac9b34b
4099 /*! @license DOMPurify | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.2.2/LICENSE */ 4099 /*! @license DOMPurify | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.2.2/LICENSE */
4100 4100
4101 function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } } 4101 function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }
4102 4102
4103 var hasOwnProperty = Object.hasOwnProperty, 4103 var hasOwnProperty = Object.hasOwnProperty,
4104 setPrototypeOf = Object.setPrototypeOf, 4104 setPrototypeOf = Object.setPrototypeOf,
4105 isFrozen = Object.isFrozen, 4105 isFrozen = Object.isFrozen,
4106 getPrototypeOf = Object.getPrototypeOf, 4106 getPrototypeOf = Object.getPrototypeOf,
4107 getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor; 4107 getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
4108 var freeze = Object.freeze, 4108 var freeze = Object.freeze,
4109 seal = Object.seal, 4109 seal = Object.seal,
4110 create = Object.create; // eslint-disable-line import/no-mutable-exports 4110 create = Object.create; // eslint-disable-line import/no-mutable-exports
4111 4111
4112 var _ref = typeof Reflect !== 'undefined' && Reflect, 4112 var _ref = typeof Reflect !== 'undefined' && Reflect,
4113 apply = _ref.apply, 4113 apply = _ref.apply,
4114 construct = _ref.construct; 4114 construct = _ref.construct;
4115 4115
4116 if (!apply) { 4116 if (!apply) {
4117 apply = function apply(fun, thisValue, args) { 4117 apply = function apply(fun, thisValue, args) {
4118 return fun.apply(thisValue, args); 4118 return fun.apply(thisValue, args);
4119 }; 4119 };
4120 } 4120 }
4121 4121
4122 if (!freeze) { 4122 if (!freeze) {
4123 freeze = function freeze(x) { 4123 freeze = function freeze(x) {
4124 return x; 4124 return x;
4125 }; 4125 };
4126 } 4126 }
4127 4127
4128 if (!seal) { 4128 if (!seal) {
4129 seal = function seal(x) { 4129 seal = function seal(x) {
4130 return x; 4130 return x;
4131 }; 4131 };
4132 } 4132 }
4133 4133
4134 if (!construct) { 4134 if (!construct) {
4135 construct = function construct(Func, args) { 4135 construct = function construct(Func, args) {
4136 return new (Function.prototype.bind.apply(Func, [null].concat(_toConsumableArray(args))))(); 4136 return new (Function.prototype.bind.apply(Func, [null].concat(_toConsumableArray(args))))();
4137 }; 4137 };
4138 } 4138 }
4139 4139
4140 var arrayForEach = unapply(Array.prototype.forEach); 4140 var arrayForEach = unapply(Array.prototype.forEach);
4141 var arrayPop = unapply(Array.prototype.pop); 4141 var arrayPop = unapply(Array.prototype.pop);
4151 4151
4152 var typeErrorCreate = unconstruct(TypeError); 4152 var typeErrorCreate = unconstruct(TypeError);
4153 4153
4154 function unapply(func) { 4154 function unapply(func) {
4155 return function (thisArg) { 4155 return function (thisArg) {
4156 for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 4156 for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
4157 args[_key - 1] = arguments[_key]; 4157 args[_key - 1] = arguments[_key];
4158 } 4158 }
4159 4159
4160 return apply(func, thisArg, args); 4160 return apply(func, thisArg, args);
4161 }; 4161 };
4162 } 4162 }
4163 4163
4164 function unconstruct(func) { 4164 function unconstruct(func) {
4165 return function () { 4165 return function () {
4166 for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { 4166 for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
4167 args[_key2] = arguments[_key2]; 4167 args[_key2] = arguments[_key2];
4168 } 4168 }
4169 4169
4170 return construct(func, args); 4170 return construct(func, args);
4171 }; 4171 };
4172 } 4172 }
4173 4173
4174 /* Add properties to a lookup table */ 4174 /* Add properties to a lookup table */
4175 function addToSet(set, array) { 4175 function addToSet(set, array) {
4176 if (setPrototypeOf) { 4176 if (setPrototypeOf) {
4177 // Make 'in' and truthy checks like Boolean(set.constructor) 4177 // Make 'in' and truthy checks like Boolean(set.constructor)
4178 // independent of any properties defined on Object.prototype. 4178 // independent of any properties defined on Object.prototype.
4179 // Prevent prototype setters from intercepting set as a this value. 4179 // Prevent prototype setters from intercepting set as a this value.
4180 setPrototypeOf(set, null); 4180 setPrototypeOf(set, null);
4181 } 4181 }
4182 4182
4183 var l = array.length; 4183 var l = array.length;
4184 while (l--) { 4184 while (l--) {
4185 var element = array[l]; 4185 var element = array[l];
4186 if (typeof element === 'string') { 4186 if (typeof element === 'string') {
4187 var lcElement = stringToLowerCase(element); 4187 var lcElement = stringToLowerCase(element);
4188 if (lcElement !== element) { 4188 if (lcElement !== element) {
4189 // Config presets (e.g. tags.js, attrs.js) are immutable. 4189 // Config presets (e.g. tags.js, attrs.js) are immutable.
4190 if (!isFrozen(array)) { 4190 if (!isFrozen(array)) {
4191 array[l] = lcElement; 4191 array[l] = lcElement;
4192 } 4192 }
4193 4193
4194 element = lcElement; 4194 element = lcElement;
4195 } 4195 }
4196 } 4196 }
4197 4197
4198 set[element] = true; 4198 set[element] = true;
4199 } 4199 }
4200 4200
4201 return set; 4201 return set;
4202 } 4202 }
4203 4203
4205 function clone(object) { 4205 function clone(object) {
4206 var newObject = create(null); 4206 var newObject = create(null);
4207 4207
4208 var property = void 0; 4208 var property = void 0;
4209 for (property in object) { 4209 for (property in object) {
4210 if (apply(hasOwnProperty, object, [property])) { 4210 if (apply(hasOwnProperty, object, [property])) {
4211 newObject[property] = object[property]; 4211 newObject[property] = object[property];
4212 } 4212 }
4213 } 4213 }
4214 4214
4215 return newObject; 4215 return newObject;
4216 } 4216 }
4217 4217
4219 * simulate it. It also automatically checks 4219 * simulate it. It also automatically checks
4220 * if the prop is function or getter and behaves 4220 * if the prop is function or getter and behaves
4221 * accordingly. */ 4221 * accordingly. */
4222 function lookupGetter(object, prop) { 4222 function lookupGetter(object, prop) {
4223 while (object !== null) { 4223 while (object !== null) {
4224 var desc = getOwnPropertyDescriptor(object, prop); 4224 var desc = getOwnPropertyDescriptor(object, prop);
4225 if (desc) { 4225 if (desc) {
4226 if (desc.get) { 4226 if (desc.get) {
4227 return unapply(desc.get); 4227 return unapply(desc.get);
4228 } 4228 }
4229 4229
4230 if (typeof desc.value === 'function') { 4230 if (typeof desc.value === 'function') {
4231 return unapply(desc.value); 4231 return unapply(desc.value);
4232 } 4232 }
4233 } 4233 }
4234 4234
4235 object = getPrototypeOf(object); 4235 object = getPrototypeOf(object);
4236 } 4236 }
4237 4237
4238 return null; 4238 return null;
4239 } 4239 }
4240 4240
4294 * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types 4294 * @return {?TrustedTypePolicy} The policy created (or null, if Trusted Types
4295 * are not supported). 4295 * are not supported).
4296 */ 4296 */
4297 var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) { 4297 var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {
4298 if ((typeof trustedTypes === 'undefined' ? 'undefined' : _typeof(trustedTypes)) !== 'object' || typeof trustedTypes.createPolicy !== 'function') { 4298 if ((typeof trustedTypes === 'undefined' ? 'undefined' : _typeof(trustedTypes)) !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
4299 return null; 4299 return null;
4300 } 4300 }
4301 4301
4302 // Allow the callers to control the unique policy name 4302 // Allow the callers to control the unique policy name
4303 // by adding a data-tt-policy-suffix to the script element with the DOMPurify. 4303 // by adding a data-tt-policy-suffix to the script element with the DOMPurify.
4304 // Policy creation with duplicate names throws in Trusted Types. 4304 // Policy creation with duplicate names throws in Trusted Types.
4305 var suffix = null; 4305 var suffix = null;
4306 var ATTR_NAME = 'data-tt-policy-suffix'; 4306 var ATTR_NAME = 'data-tt-policy-suffix';
4307 if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) { 4307 if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {
4308 suffix = document.currentScript.getAttribute(ATTR_NAME); 4308 suffix = document.currentScript.getAttribute(ATTR_NAME);
4309 } 4309 }
4310 4310
4311 var policyName = 'dompurify' + (suffix ? '#' + suffix : ''); 4311 var policyName = 'dompurify' + (suffix ? '#' + suffix : '');
4312 4312
4313 try { 4313 try {
4314 return trustedTypes.createPolicy(policyName, { 4314 return trustedTypes.createPolicy(policyName, {
4315 createHTML: function createHTML(html$$1) { 4315 createHTML: function createHTML(html$$1) {
4316 return html$$1; 4316 return html$$1;
4317 } 4317 }
4318 }); 4318 });
4319 } catch (_) { 4319 } catch (_) {
4320 // Policy creation failed (most likely another DOMPurify script has 4320 // Policy creation failed (most likely another DOMPurify script has
4321 // already run). Skip creating the policy, as this will only cause errors 4321 // already run). Skip creating the policy, as this will only cause errors
4322 // if TT are enforced. 4322 // if TT are enforced.
4323 console.warn('TrustedTypes policy ' + policyName + ' could not be created.'); 4323 console.warn('TrustedTypes policy ' + policyName + ' could not be created.');
4324 return null; 4324 return null;
4325 } 4325 }
4326 }; 4326 };
4327 4327
4328 function createDOMPurify() { 4328 function createDOMPurify() {
4329 var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal(); 4329 var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
4330 4330
4331 var DOMPurify = function DOMPurify(root) { 4331 var DOMPurify = function DOMPurify(root) {
4332 return createDOMPurify(root); 4332 return createDOMPurify(root);
4333 }; 4333 };
4334 4334
4335 /** 4335 /**
4336 * Version label, exposed for easier checks 4336 * Version label, exposed for easier checks
4337 * if DOMPurify is up to date or not 4337 * if DOMPurify is up to date or not
4343 * Empty if nothing was removed. 4343 * Empty if nothing was removed.
4344 */ 4344 */
4345 DOMPurify.removed = []; 4345 DOMPurify.removed = [];
4346 4346
4347 if (!window || !window.document || window.document.nodeType !== 9) { 4347 if (!window || !window.document || window.document.nodeType !== 9) {
4348 // Not running in a browser, provide a factory function 4348 // Not running in a browser, provide a factory function
4349 // so that you can pass your own Window 4349 // so that you can pass your own Window
4350 DOMPurify.isSupported = false; 4350 DOMPurify.isSupported = false;
4351 4351
4352 return DOMPurify; 4352 return DOMPurify;
4353 } 4353 }
4354 4354
4355 var originalDocument = window.document; 4355 var originalDocument = window.document;
4356 4356
4357 var document = window.document; 4357 var document = window.document;
4358 var DocumentFragment = window.DocumentFragment, 4358 var DocumentFragment = window.DocumentFragment,
4359 HTMLTemplateElement = window.HTMLTemplateElement, 4359 HTMLTemplateElement = window.HTMLTemplateElement,
4360 Node = window.Node, 4360 Node = window.Node,
4361 Element = window.Element, 4361 Element = window.Element,
4362 NodeFilter = window.NodeFilter, 4362 NodeFilter = window.NodeFilter,
4363 _window$NamedNodeMap = window.NamedNodeMap, 4363 _window$NamedNodeMap = window.NamedNodeMap,
4364 NamedNodeMap = _window$NamedNodeMap === undefined ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap, 4364 NamedNodeMap = _window$NamedNodeMap === undefined ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
4365 Text = window.Text, 4365 Text = window.Text,
4366 Comment = window.Comment, 4366 Comment = window.Comment,
4367 DOMParser = window.DOMParser, 4367 DOMParser = window.DOMParser,
4368 trustedTypes = window.trustedTypes; 4368 trustedTypes = window.trustedTypes;
4369 4369
4370 4370
4371 var ElementPrototype = Element.prototype; 4371 var ElementPrototype = Element.prototype;
4372 4372
4373 var cloneNode = lookupGetter(ElementPrototype, 'cloneNode'); 4373 var cloneNode = lookupGetter(ElementPrototype, 'cloneNode');
4380 // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries) 4380 // (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
4381 // a new empty registry is used when creating a template contents owner 4381 // a new empty registry is used when creating a template contents owner
4382 // document, so we use that as our parent document to ensure nothing 4382 // document, so we use that as our parent document to ensure nothing
4383 // is inherited. 4383 // is inherited.
4384 if (typeof HTMLTemplateElement === 'function') { 4384 if (typeof HTMLTemplateElement === 'function') {
4385 var template = document.createElement('template'); 4385 var template = document.createElement('template');
4386 if (template.content && template.content.ownerDocument) { 4386 if (template.content && template.content.ownerDocument) {
4387 document = template.content.ownerDocument; 4387 document = template.content.ownerDocument;
4388 } 4388 }
4389 } 4389 }
4390 4390
4391 var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument); 4391 var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);
4392 var emptyHTML = trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML('') : ''; 4392 var emptyHTML = trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML('') : '';
4393 4393
4394 var _document = document, 4394 var implementation = document.implementation,
4395 implementation = _document.implementation, 4395 //var importNode = originalDocument.importNode;
4396 createNodeIterator = _document.createNodeIterator,
4397 getElementsByTagName = _document.getElementsByTagName,
4398 createDocumentFragment = _document.createDocumentFragment;
4399 var importNode = originalDocument.importNode;
4400 4396
4401 4397
4402 var documentMode = {}; 4398 var documentMode = {};
4403 try { 4399 try {
4404 documentMode = clone(document).documentMode ? document.documentMode : {}; 4400 documentMode = clone(document).documentMode ? document.documentMode : {};
4405 } catch (_) {} 4401 } catch (_) {}
4406 4402
4407 var hooks = {}; 4403 var hooks = {};
4408 4404
4409 /** 4405 /**
4410 * Expose whether this browser supports running the full DOMPurify. 4406 * Expose whether this browser supports running the full DOMPurify.
4411 */ 4407 */
4412 DOMPurify.isSupported = implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9; 4408 DOMPurify.isSupported = implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9;
4413 4409
4414 var MUSTACHE_EXPR$$1 = MUSTACHE_EXPR, 4410 var MUSTACHE_EXPR$$1 = MUSTACHE_EXPR,
4415 ERB_EXPR$$1 = ERB_EXPR, 4411 ERB_EXPR$$1 = ERB_EXPR,
4416 DATA_ATTR$$1 = DATA_ATTR, 4412 DATA_ATTR$$1 = DATA_ATTR,
4417 ARIA_ATTR$$1 = ARIA_ATTR, 4413 ARIA_ATTR$$1 = ARIA_ATTR,
4418 IS_SCRIPT_OR_DATA$$1 = IS_SCRIPT_OR_DATA, 4414 IS_SCRIPT_OR_DATA$$1 = IS_SCRIPT_OR_DATA,
4419 ATTR_WHITESPACE$$1 = ATTR_WHITESPACE; 4415 ATTR_WHITESPACE$$1 = ATTR_WHITESPACE;
4420 var IS_ALLOWED_URI$$1 = IS_ALLOWED_URI; 4416 var IS_ALLOWED_URI$$1 = IS_ALLOWED_URI;
4421 4417
4422 /** 4418 /**
4423 * We consider the elements and attributes below to be safe. Ideally 4419 * We consider the elements and attributes below to be safe. Ideally
4424 * don't add any new ones but feel free to remove unwanted ones. 4420 * don't add any new ones but feel free to remove unwanted ones.
4525 * 4521 *
4526 * @param {Object} cfg optional config literal 4522 * @param {Object} cfg optional config literal
4527 */ 4523 */
4528 // eslint-disable-next-line complexity 4524 // eslint-disable-next-line complexity
4529 var _parseConfig = function _parseConfig(cfg) { 4525 var _parseConfig = function _parseConfig(cfg) {
4530 if (CONFIG && CONFIG === cfg) { 4526 if (CONFIG && CONFIG === cfg) {
4531 return; 4527 return;
4532 } 4528 }
4533 4529
4534 /* Shield configuration object from tampering */ 4530 /* Shield configuration object from tampering */
4535 if (!cfg || (typeof cfg === 'undefined' ? 'undefined' : _typeof(cfg)) !== 'object') { 4531 if (!cfg || (typeof cfg === 'undefined' ? 'undefined' : _typeof(cfg)) !== 'object') {
4536 cfg = {}; 4532 cfg = {};
4537 } 4533 }
4538 4534
4539 /* Shield configuration object from prototype pollution */ 4535 /* Shield configuration object from prototype pollution */
4540 cfg = clone(cfg); 4536 cfg = clone(cfg);
4541 4537
4542 /* Set configuration parameters */ 4538 /* Set configuration parameters */
4543 ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS; 4539 ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS;
4544 ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR; 4540 ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR;
4545 URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES; 4541 URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES;
4546 DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS) : DEFAULT_DATA_URI_TAGS; 4542 DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS) : DEFAULT_DATA_URI_TAGS;
4547 FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {}; 4543 FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {};
4548 FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {}; 4544 FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {};
4549 USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false; 4545 USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
4550 ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true 4546 ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
4551 ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true 4547 ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
4552 ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false 4548 ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
4553 SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false 4549 SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
4554 WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false 4550 WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
4555 RETURN_DOM = cfg.RETURN_DOM || false; // Default false 4551 RETURN_DOM = cfg.RETURN_DOM || false; // Default false
4556 RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false 4552 RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
4557 RETURN_DOM_IMPORT = cfg.RETURN_DOM_IMPORT !== false; // Default true 4553 RETURN_DOM_IMPORT = cfg.RETURN_DOM_IMPORT !== false; // Default true
4558 RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false 4554 RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
4559 FORCE_BODY = cfg.FORCE_BODY || false; // Default false 4555 FORCE_BODY = cfg.FORCE_BODY || false; // Default false
4560 SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true 4556 SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
4561 KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true 4557 KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
4562 IN_PLACE = cfg.IN_PLACE || false; // Default false 4558 IN_PLACE = cfg.IN_PLACE || false; // Default false
4563 IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1; 4559 IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1;
4564 if (SAFE_FOR_TEMPLATES) { 4560 if (SAFE_FOR_TEMPLATES) {
4565 ALLOW_DATA_ATTR = false; 4561 ALLOW_DATA_ATTR = false;
4566 } 4562 }
4567 4563
4568 if (RETURN_DOM_FRAGMENT) { 4564 if (RETURN_DOM_FRAGMENT) {
4569 RETURN_DOM = true; 4565 RETURN_DOM = true;
4570 } 4566 }
4571 4567
4572 /* Parse profile info */ 4568 /* Parse profile info */
4573 if (USE_PROFILES) { 4569 if (USE_PROFILES) {
4574 ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(text))); 4570 ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(text)));
4575 ALLOWED_ATTR = []; 4571 ALLOWED_ATTR = [];
4576 if (USE_PROFILES.html === true) { 4572 if (USE_PROFILES.html === true) {
4577 addToSet(ALLOWED_TAGS, html); 4573 addToSet(ALLOWED_TAGS, html);
4578 addToSet(ALLOWED_ATTR, html$1); 4574 addToSet(ALLOWED_ATTR, html$1);
4579 } 4575 }
4580 4576
4581 if (USE_PROFILES.svg === true) { 4577 if (USE_PROFILES.svg === true) {
4582 addToSet(ALLOWED_TAGS, svg); 4578 addToSet(ALLOWED_TAGS, svg);
4583 addToSet(ALLOWED_ATTR, svg$1); 4579 addToSet(ALLOWED_ATTR, svg$1);
4584 addToSet(ALLOWED_ATTR, xml); 4580 addToSet(ALLOWED_ATTR, xml);
4585 } 4581 }
4586 4582
4587 if (USE_PROFILES.svgFilters === true) { 4583 if (USE_PROFILES.svgFilters === true) {
4588 addToSet(ALLOWED_TAGS, svgFilters); 4584 addToSet(ALLOWED_TAGS, svgFilters);
4589 addToSet(ALLOWED_ATTR, svg$1); 4585 addToSet(ALLOWED_ATTR, svg$1);
4590 addToSet(ALLOWED_ATTR, xml); 4586 addToSet(ALLOWED_ATTR, xml);
4591 } 4587 }
4592 4588
4593 if (USE_PROFILES.mathMl === true) { 4589 if (USE_PROFILES.mathMl === true) {
4594 addToSet(ALLOWED_TAGS, mathMl); 4590 addToSet(ALLOWED_TAGS, mathMl);
4595 addToSet(ALLOWED_ATTR, mathMl$1); 4591 addToSet(ALLOWED_ATTR, mathMl$1);
4596 addToSet(ALLOWED_ATTR, xml); 4592 addToSet(ALLOWED_ATTR, xml);
4597 } 4593 }
4598 } 4594 }
4599 4595
4600 /* Merge configuration parameters */ 4596 /* Merge configuration parameters */
4601 if (cfg.ADD_TAGS) { 4597 if (cfg.ADD_TAGS) {
4602 if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) { 4598 if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
4603 ALLOWED_TAGS = clone(ALLOWED_TAGS); 4599 ALLOWED_TAGS = clone(ALLOWED_TAGS);
4604 } 4600 }
4605 4601
4606 addToSet(ALLOWED_TAGS, cfg.ADD_TAGS); 4602 addToSet(ALLOWED_TAGS, cfg.ADD_TAGS);
4607 } 4603 }
4608 4604
4609 if (cfg.ADD_ATTR) { 4605 if (cfg.ADD_ATTR) {
4610 if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) { 4606 if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
4611 ALLOWED_ATTR = clone(ALLOWED_ATTR); 4607 ALLOWED_ATTR = clone(ALLOWED_ATTR);
4612 } 4608 }
4613 4609
4614 addToSet(ALLOWED_ATTR, cfg.ADD_ATTR); 4610 addToSet(ALLOWED_ATTR, cfg.ADD_ATTR);
4615 } 4611 }
4616 4612
4617 if (cfg.ADD_URI_SAFE_ATTR) { 4613 if (cfg.ADD_URI_SAFE_ATTR) {
4618 addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR); 4614 addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR);
4619 } 4615 }
4620 4616
4621 /* Add #text in case KEEP_CONTENT is set to true */ 4617 /* Add #text in case KEEP_CONTENT is set to true */
4622 if (KEEP_CONTENT) { 4618 if (KEEP_CONTENT) {
4623 ALLOWED_TAGS['#text'] = true; 4619 ALLOWED_TAGS['#text'] = true;
4624 } 4620 }
4625 4621
4626 /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */ 4622 /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
4627 if (WHOLE_DOCUMENT) { 4623 if (WHOLE_DOCUMENT) {
4628 addToSet(ALLOWED_TAGS, ['html', 'head', 'body']); 4624 addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
4629 } 4625 }
4630 4626
4631 /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */ 4627 /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
4632 if (ALLOWED_TAGS.table) { 4628 if (ALLOWED_TAGS.table) {
4633 addToSet(ALLOWED_TAGS, ['tbody']); 4629 addToSet(ALLOWED_TAGS, ['tbody']);
4634 delete FORBID_TAGS.tbody; 4630 delete FORBID_TAGS.tbody;
4635 } 4631 }
4636 4632
4637 // Prevent further manipulation of configuration. 4633 // Prevent further manipulation of configuration.
4638 // Not available in IE8, Safari 5, etc. 4634 // Not available in IE8, Safari 5, etc.
4639 if (freeze) { 4635 if (freeze) {
4640 freeze(cfg); 4636 freeze(cfg);
4641 } 4637 }
4642 4638
4643 CONFIG = cfg; 4639 CONFIG = cfg;
4644 }; 4640 };
4645 4641
4646 var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']); 4642 var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
4647 4643
4648 var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']); 4644 var HTML_INTEGRATION_POINTS = addToSet({}, ['foreignobject', 'desc', 'title', 'annotation-xml']);
4668 * @returns {boolean} Return false if the element has a 4664 * @returns {boolean} Return false if the element has a
4669 * namespace that a spec-compliant parser would never 4665 * namespace that a spec-compliant parser would never
4670 * return. Return true otherwise. 4666 * return. Return true otherwise.
4671 */ 4667 */
4672 var _checkValidNamespace = function _checkValidNamespace(element) { 4668 var _checkValidNamespace = function _checkValidNamespace(element) {
4673 var parent = getParentNode(element); 4669 var parent = getParentNode(element);
4674 4670
4675 // In JSDOM, if we're inside shadow DOM, then parentNode 4671 // In JSDOM, if we're inside shadow DOM, then parentNode
4676 // can be null. We just simulate parent in this case. 4672 // can be null. We just simulate parent in this case.
4677 if (!parent || !parent.tagName) { 4673 if (!parent || !parent.tagName) {
4678 parent = { 4674 parent = {
4679 namespaceURI: HTML_NAMESPACE, 4675 namespaceURI: HTML_NAMESPACE,
4680 tagName: 'template' 4676 tagName: 'template'
4681 }; 4677 };
4682 } 4678 }
4683 4679
4684 var tagName = stringToLowerCase(element.tagName); 4680 var tagName = stringToLowerCase(element.tagName);
4685 var parentTagName = stringToLowerCase(parent.tagName); 4681 var parentTagName = stringToLowerCase(parent.tagName);
4686 4682
4687 if (element.namespaceURI === SVG_NAMESPACE) { 4683 if (element.namespaceURI === SVG_NAMESPACE) {
4688 // The only way to switch from HTML namespace to SVG 4684 // The only way to switch from HTML namespace to SVG
4689 // is via <svg>. If it happens via any other tag, then 4685 // is via <svg>. If it happens via any other tag, then
4690 // it should be killed. 4686 // it should be killed.
4691 if (parent.namespaceURI === HTML_NAMESPACE) { 4687 if (parent.namespaceURI === HTML_NAMESPACE) {
4692 return tagName === 'svg'; 4688 return tagName === 'svg';
4693 } 4689 }
4694 4690
4695 // The only way to switch from MathML to SVG is via 4691 // The only way to switch from MathML to SVG is via
4696 // svg if parent is either <annotation-xml> or MathML 4692 // svg if parent is either <annotation-xml> or MathML
4697 // text integration points. 4693 // text integration points.
4698 if (parent.namespaceURI === MATHML_NAMESPACE) { 4694 if (parent.namespaceURI === MATHML_NAMESPACE) {
4699 return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]); 4695 return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
4700 } 4696 }
4701 4697
4702 // We only allow elements that are defined in SVG 4698 // We only allow elements that are defined in SVG
4703 // spec. All others are disallowed in SVG namespace. 4699 // spec. All others are disallowed in SVG namespace.
4704 return Boolean(ALL_SVG_TAGS[tagName]); 4700 return Boolean(ALL_SVG_TAGS[tagName]);
4705 } 4701 }
4706 4702
4707 if (element.namespaceURI === MATHML_NAMESPACE) { 4703 if (element.namespaceURI === MATHML_NAMESPACE) {
4708 // The only way to switch from HTML namespace to MathML 4704 // The only way to switch from HTML namespace to MathML
4709 // is via <math>. If it happens via any other tag, then 4705 // is via <math>. If it happens via any other tag, then
4710 // it should be killed. 4706 // it should be killed.
4711 if (parent.namespaceURI === HTML_NAMESPACE) { 4707 if (parent.namespaceURI === HTML_NAMESPACE) {
4712 return tagName === 'math'; 4708 return tagName === 'math';
4713 } 4709 }
4714 4710
4715 // The only way to switch from SVG to MathML is via 4711 // The only way to switch from SVG to MathML is via
4716 // <math> and HTML integration points 4712 // <math> and HTML integration points
4717 if (parent.namespaceURI === SVG_NAMESPACE) { 4713 if (parent.namespaceURI === SVG_NAMESPACE) {
4718 return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName]; 4714 return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
4719 } 4715 }
4720 4716
4721 // We only allow elements that are defined in MathML 4717 // We only allow elements that are defined in MathML
4722 // spec. All others are disallowed in MathML namespace. 4718 // spec. All others are disallowed in MathML namespace.
4723 return Boolean(ALL_MATHML_TAGS[tagName]); 4719 return Boolean(ALL_MATHML_TAGS[tagName]);
4724 } 4720 }
4725 4721
4726 if (element.namespaceURI === HTML_NAMESPACE) { 4722 if (element.namespaceURI === HTML_NAMESPACE) {
4727 // The only way to switch from SVG to HTML is via 4723 // The only way to switch from SVG to HTML is via
4728 // HTML integration points, and from MathML to HTML 4724 // HTML integration points, and from MathML to HTML
4729 // is via MathML text integration points 4725 // is via MathML text integration points
4730 if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) { 4726 if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
4731 return false; 4727 return false;
4732 } 4728 }
4733 4729
4734 if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) { 4730 if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
4735 return false; 4731 return false;
4736 } 4732 }
4737 4733
4738 // Certain elements are allowed in both SVG and HTML 4734 // Certain elements are allowed in both SVG and HTML
4739 // namespace. We need to specify them explicitly 4735 // namespace. We need to specify them explicitly
4740 // so that they don't get erronously deleted from 4736 // so that they don't get erronously deleted from
4741 // HTML namespace. 4737 // HTML namespace.
4742 var commonSvgAndHTMLElements = addToSet({}, ['title', 'style', 'font', 'a', 'script']); 4738 var commonSvgAndHTMLElements = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
4743 4739
4744 // We disallow tags that are specific for MathML 4740 // We disallow tags that are specific for MathML
4745 // or SVG and should never appear in HTML namespace 4741 // or SVG and should never appear in HTML namespace
4746 return !ALL_MATHML_TAGS[tagName] && (commonSvgAndHTMLElements[tagName] || !ALL_SVG_TAGS[tagName]); 4742 return !ALL_MATHML_TAGS[tagName] && (commonSvgAndHTMLElements[tagName] || !ALL_SVG_TAGS[tagName]);
4747 } 4743 }
4748 4744
4749 // The code should never reach this place (this means 4745 // The code should never reach this place (this means
4750 // that the element somehow got namespace that is not 4746 // that the element somehow got namespace that is not
4751 // HTML, SVG or MathML). Return false just in case. 4747 // HTML, SVG or MathML). Return false just in case.
4752 return false; 4748 return false;
4753 }; 4749 };
4754 4750
4755 /** 4751 /**
4756 * _forceRemove 4752 * _forceRemove
4757 * 4753 *
4758 * @param {Node} node a DOM node 4754 * @param {Node} node a DOM node
4759 */ 4755 */
4760 var _forceRemove = function _forceRemove(node) { 4756 var _forceRemove = function _forceRemove(node) {
4761 arrayPush(DOMPurify.removed, { element: node }); 4757 arrayPush(DOMPurify.removed, { element: node });
4762 try { 4758 try {
4763 node.parentNode.removeChild(node); 4759 node.parentNode.removeChild(node);
4764 } catch (_) { 4760 } catch (_) {
4765 try { 4761 try {
4766 node.outerHTML = emptyHTML; 4762 node.outerHTML = emptyHTML;
4767 } catch (_) { 4763 } catch (_) {
4768 node.remove(); 4764 node.remove();
4769 } 4765 }
4770 } 4766 }
4771 }; 4767 };
4772 4768
4773 /** 4769 /**
4774 * _removeAttribute 4770 * _removeAttribute
4775 * 4771 *
4776 * @param {String} name an Attribute name 4772 * @param {String} name an Attribute name
4777 * @param {Node} node a DOM node 4773 * @param {Node} node a DOM node
4778 */ 4774 */
4779 var _removeAttribute = function _removeAttribute(name, node) { 4775 var _removeAttribute = function _removeAttribute(name, node) {
4780 try { 4776 try {
4781 arrayPush(DOMPurify.removed, { 4777 arrayPush(DOMPurify.removed, {
4782 attribute: node.getAttributeNode(name), 4778 attribute: node.getAttributeNode(name),
4783 from: node 4779 from: node
4784 }); 4780 });
4785 } catch (_) { 4781 } catch (_) {
4786 arrayPush(DOMPurify.removed, { 4782 arrayPush(DOMPurify.removed, {
4787 attribute: null, 4783 attribute: null,
4788 from: node 4784 from: node
4789 }); 4785 });
4790 } 4786 }
4791 4787
4792 node.removeAttribute(name); 4788 node.removeAttribute(name);
4793 }; 4789 };
4794 4790
4795 /** 4791 /**
4796 * _initDocument 4792 * _initDocument
4797 * 4793 *
4798 * @param {String} dirty a string of dirty markup 4794 * @param {String} dirty a string of dirty markup
4799 * @return {Document} a DOM, filled with the dirty markup 4795 * @return {Document} a DOM, filled with the dirty markup
4800 */ 4796 */
4801 var _initDocument = function _initDocument(dirty) { 4797 var _initDocument = function _initDocument(dirty) {
4802 /* Create a HTML document */ 4798 /* Create a HTML document */
4803 var doc = void 0; 4799 var doc = void 0;
4804 var leadingWhitespace = void 0; 4800 var leadingWhitespace = void 0;
4805 4801
4806 if (FORCE_BODY) { 4802 if (FORCE_BODY) {
4807 dirty = '<remove></remove>' + dirty; 4803 dirty = '<remove></remove>' + dirty;
4808 } else { 4804 } else {
4809 /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */ 4805 /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
4810 var matches = stringMatch(dirty, /^[\r\n\t ]+/); 4806 var matches = stringMatch(dirty, /^[\r\n\t ]+/);
4811 leadingWhitespace = matches && matches[0]; 4807 leadingWhitespace = matches && matches[0];
4812 } 4808 }
4813 4809
4814 var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty; 4810 var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
4815 /* Use the DOMParser API by default, fallback later if needs be */ 4811 /* Use the DOMParser API by default, fallback later if needs be */
4816 try { 4812 try {
4817 doc = new DOMParser().parseFromString(dirtyPayload, 'text/html'); 4813 doc = new DOMParser().parseFromString(dirtyPayload, 'text/html');
4818 } catch (_) {} 4814 } catch (_) {}
4819 4815
4820 /* Use createHTMLDocument in case DOMParser is not available */ 4816 /* Use createHTMLDocument in case DOMParser is not available */
4821 if (!doc || !doc.documentElement) { 4817 if (!doc || !doc.documentElement) {
4822 doc = implementation.createHTMLDocument(''); 4818 doc = implementation.createHTMLDocument('');
4823 var _doc = doc, 4819 var _doc = doc,
4824 body = _doc.body; 4820 body = _doc.body;
4825 4821
4826 body.parentNode.removeChild(body.parentNode.firstElementChild); 4822 body.parentNode.removeChild(body.parentNode.firstElementChild);
4827 body.outerHTML = dirtyPayload; 4823 body.outerHTML = dirtyPayload;
4828 } 4824 }
4829 4825
4830 if (dirty && leadingWhitespace) { 4826 if (dirty && leadingWhitespace) {
4831 doc.body.insertBefore(document.createTextNode(leadingWhitespace), doc.body.childNodes[0] || null); 4827 doc.body.insertBefore(document.createTextNode(leadingWhitespace), doc.body.childNodes[0] || null);
4832 } 4828 }
4833 4829
4834 /* Work on whole document or just its body */ 4830 /* Work on whole document or just its body */
4835 return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0]; 4831 return doc.getElementsByTagName(WHOLE_DOCUMENT ? 'html' : 'body')[0];
4836 }; 4832 };
4837 4833
4838 /** 4834 /**
4839 * _createIterator 4835 * _createIterator
4840 * 4836 *
4841 * @param {Document} root document/fragment to create iterator for 4837 * @param {Document} root document/fragment to create iterator for
4842 * @return {Iterator} iterator instance 4838 * @return {Iterator} iterator instance
4843 */ 4839 */
4844 var _createIterator = function _createIterator(root) { 4840 var _createIterator = function _createIterator(root) {
4845 return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, function () { 4841 let whatToShow = NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT;
4846 return NodeFilter.FILTER_ACCEPT; 4842 let filter = function () {
4847 }, false); 4843 return NodeFilter.FILTER_ACCEPT;
4844 };
4845 if(root.ownerDocument) {
4846 return root.ownerDocument.createNodeIterator(root,whatToShow,filter,false);
4847 } else {
4848 return root.createNodeIterator(root,whatToShow,filter,false);
4849 }
4848 }; 4850 };
4849 4851
4850 /** 4852 /**
4851 * _isClobbered 4853 * _isClobbered
4852 * 4854 *
4853 * @param {Node} elm element to check for clobbering attacks 4855 * @param {Node} elm element to check for clobbering attacks
4854 * @return {Boolean} true if clobbered, false if safe 4856 * @return {Boolean} true if clobbered, false if safe
4855 */ 4857 */
4856 var _isClobbered = function _isClobbered(elm) { 4858 var _isClobbered = function _isClobbered(elm) {
4857 if (elm instanceof Text || elm instanceof Comment) { 4859 if (elm instanceof Text || elm instanceof Comment) {
4858 return false; 4860 return false;
4859 } 4861 }
4860 4862
4861 if (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function') { 4863 if (typeof elm.nodeName !== 'string' || typeof elm.textContent !== 'string' || typeof elm.removeChild !== 'function' || !(elm.attributes instanceof NamedNodeMap) || typeof elm.removeAttribute !== 'function' || typeof elm.setAttribute !== 'function' || typeof elm.namespaceURI !== 'string' || typeof elm.insertBefore !== 'function') {
4862 return true; 4864 return true;
4863 } 4865 }
4864 4866
4865 return false; 4867 return false;
4866 }; 4868 };
4867 4869
4868 /** 4870 /**
4869 * _isNode 4871 * _isNode
4870 * 4872 *
4871 * @param {Node} obj object to check whether it's a DOM node 4873 * @param {Node} obj object to check whether it's a DOM node
4872 * @return {Boolean} true is object is a DOM node 4874 * @return {Boolean} true is object is a DOM node
4873 */ 4875 */
4874 var _isNode = function _isNode(object) { 4876 var _isNode = function _isNode(object) {
4875 return (typeof Node === 'undefined' ? 'undefined' : _typeof(Node)) === 'object' ? object instanceof Node : object && (typeof object === 'undefined' ? 'undefined' : _typeof(object)) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string'; 4877 return (typeof Node === 'undefined' ? 'undefined' : _typeof(Node)) === 'object' ? object instanceof Node : object && (typeof object === 'undefined' ? 'undefined' : _typeof(object)) === 'object' && typeof object.nodeType === 'number' && typeof object.nodeName === 'string';
4876 }; 4878 };
4877 4879
4878 /** 4880 /**
4879 * _executeHook 4881 * _executeHook
4880 * Execute user configurable hooks 4882 * Execute user configurable hooks
4882 * @param {String} entryPoint Name of the hook's entry point 4884 * @param {String} entryPoint Name of the hook's entry point
4883 * @param {Node} currentNode node to work on with the hook 4885 * @param {Node} currentNode node to work on with the hook
4884 * @param {Object} data additional hook parameters 4886 * @param {Object} data additional hook parameters
4885 */ 4887 */
4886 var _executeHook = function _executeHook(entryPoint, currentNode, data) { 4888 var _executeHook = function _executeHook(entryPoint, currentNode, data) {
4887 if (!hooks[entryPoint]) { 4889 if (!hooks[entryPoint]) {
4888 return; 4890 return;
4889 } 4891 }
4890 4892
4891 arrayForEach(hooks[entryPoint], function (hook) { 4893 arrayForEach(hooks[entryPoint], function (hook) {
4892 hook.call(DOMPurify, currentNode, data, CONFIG); 4894 hook.call(DOMPurify, currentNode, data, CONFIG);
4893 }); 4895 });
4894 }; 4896 };
4895 4897
4896 /** 4898 /**
4897 * _sanitizeElements 4899 * _sanitizeElements
4898 * 4900 *
4902 * 4904 *
4903 * @param {Node} currentNode to check for permission to exist 4905 * @param {Node} currentNode to check for permission to exist
4904 * @return {Boolean} true if node was killed, false if left alive 4906 * @return {Boolean} true if node was killed, false if left alive
4905 */ 4907 */
4906 var _sanitizeElements = function _sanitizeElements(currentNode) { 4908 var _sanitizeElements = function _sanitizeElements(currentNode) {
4907 var content = void 0; 4909 var content = void 0;
4908 4910
4909 /* Execute a hook if present */ 4911 /* Execute a hook if present */
4910 _executeHook('beforeSanitizeElements', currentNode, null); 4912 _executeHook('beforeSanitizeElements', currentNode, null);
4911 4913
4912 /* Check if element is clobbered or can clobber */ 4914 /* Check if element is clobbered or can clobber */
4913 if (_isClobbered(currentNode)) { 4915 if (_isClobbered(currentNode)) {
4914 _forceRemove(currentNode); 4916 _forceRemove(currentNode);
4915 return true; 4917 return true;
4916 } 4918 }
4917 4919
4918 /* Check if tagname contains Unicode */ 4920 /* Check if tagname contains Unicode */
4919 if (stringMatch(currentNode.nodeName, /[\u0080-\uFFFF]/)) { 4921 if (stringMatch(currentNode.nodeName, /[\u0080-\uFFFF]/)) {
4920 _forceRemove(currentNode); 4922 _forceRemove(currentNode);
4921 return true; 4923 return true;
4922 } 4924 }
4923 4925
4924 /* Now let's check the element's type and name */ 4926 /* Now let's check the element's type and name */
4925 var tagName = stringToLowerCase(currentNode.nodeName); 4927 var tagName = stringToLowerCase(currentNode.nodeName);
4926 4928
4927 /* Execute a hook if present */ 4929 /* Execute a hook if present */
4928 _executeHook('uponSanitizeElement', currentNode, { 4930 _executeHook('uponSanitizeElement', currentNode, {
4929 tagName: tagName, 4931 tagName: tagName,
4930 allowedTags: ALLOWED_TAGS 4932 allowedTags: ALLOWED_TAGS
4931 }); 4933 });
4932 4934
4933 /* Detect mXSS attempts abusing namespace confusion */ 4935 /* Detect mXSS attempts abusing namespace confusion */
4934 if (!_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) { 4936 if (!_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
4935 _forceRemove(currentNode); 4937 _forceRemove(currentNode);
4936 return true; 4938 return true;
4937 } 4939 }
4938 4940
4939 /* Remove element if anything forbids its presence */ 4941 /* Remove element if anything forbids its presence */
4940 if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) { 4942 if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
4941 /* Keep content except for bad-listed elements */ 4943 /* Keep content except for bad-listed elements */
4942 if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) { 4944 if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
4943 var parentNode = getParentNode(currentNode); 4945 var parentNode = getParentNode(currentNode);
4944 var childNodes = getChildNodes(currentNode); 4946 var childNodes = getChildNodes(currentNode);
4945 var childCount = childNodes.length; 4947 var childCount = childNodes.length;
4946 for (var i = childCount - 1; i >= 0; --i) { 4948 for (var i = childCount - 1; i >= 0; --i) {
4947 parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode)); 4949 parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));
4948 } 4950 }
4949 } 4951 }
4950 4952
4951 _forceRemove(currentNode); 4953 _forceRemove(currentNode);
4952 return true; 4954 return true;
4953 } 4955 }
4954 4956
4955 /* Check whether element has a valid namespace */ 4957 /* Check whether element has a valid namespace */
4956 if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) { 4958 if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
4957 _forceRemove(currentNode); 4959 _forceRemove(currentNode);
4958 return true; 4960 return true;
4959 } 4961 }
4960 4962
4961 if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)) { 4963 if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)) {
4962 _forceRemove(currentNode); 4964 _forceRemove(currentNode);
4963 return true; 4965 return true;
4964 } 4966 }
4965 4967
4966 /* Sanitize element content to be template-safe */ 4968 /* Sanitize element content to be template-safe */
4967 if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) { 4969 if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
4968 /* Get the element's text content */ 4970 /* Get the element's text content */
4969 content = currentNode.textContent; 4971 content = currentNode.textContent;
4970 content = stringReplace(content, MUSTACHE_EXPR$$1, ' '); 4972 content = stringReplace(content, MUSTACHE_EXPR$$1, ' ');
4971 content = stringReplace(content, ERB_EXPR$$1, ' '); 4973 content = stringReplace(content, ERB_EXPR$$1, ' ');
4972 if (currentNode.textContent !== content) { 4974 if (currentNode.textContent !== content) {
4973 arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() }); 4975 arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() });
4974 currentNode.textContent = content; 4976 currentNode.textContent = content;
4975 } 4977 }
4976 } 4978 }
4977 4979
4978 /* Execute a hook if present */ 4980 /* Execute a hook if present */
4979 _executeHook('afterSanitizeElements', currentNode, null); 4981 _executeHook('afterSanitizeElements', currentNode, null);
4980 4982
4981 return false; 4983 return false;
4982 }; 4984 };
4983 4985
4984 /** 4986 /**
4985 * _isValidAttribute 4987 * _isValidAttribute
4986 * 4988 *
4989 * @param {string} value Attribute value. 4991 * @param {string} value Attribute value.
4990 * @return {Boolean} Returns true if `value` is valid, otherwise false. 4992 * @return {Boolean} Returns true if `value` is valid, otherwise false.
4991 */ 4993 */
4992 // eslint-disable-next-line complexity 4994 // eslint-disable-next-line complexity
4993 var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) { 4995 var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
4994 /* Make sure attribute cannot clobber */ 4996 /* Make sure attribute cannot clobber */
4995 if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) { 4997 if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
4996 return false; 4998 return false;
4997 } 4999 }
4998 5000
4999 /* Allow valid data-* attributes: At least one character after "-" 5001 /* Allow valid data-* attributes: At least one character after "-"
5000 (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes) 5002 (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
5001 XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804) 5003 XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
5002 We don't need to check the value; it's always URI safe. */ 5004 We don't need to check the value; it's always URI safe. */
5003 if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$$1, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) { 5005 if (ALLOW_DATA_ATTR && regExpTest(DATA_ATTR$$1, lcName)) ; else if (ALLOW_ARIA_ATTR && regExpTest(ARIA_ATTR$$1, lcName)) ; else if (!ALLOWED_ATTR[lcName] || FORBID_ATTR[lcName]) {
5004 return false; 5006 return false;
5005 5007
5006 /* Check value is safe. First, is attr inert? If so, is safe */ 5008 /* Check value is safe. First, is attr inert? If so, is safe */
5007 } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$$1, stringReplace(value, ATTR_WHITESPACE$$1, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$$1, stringReplace(value, ATTR_WHITESPACE$$1, ''))) ; else if (!value) ; else { 5009 } else if (URI_SAFE_ATTRIBUTES[lcName]) ; else if (regExpTest(IS_ALLOWED_URI$$1, stringReplace(value, ATTR_WHITESPACE$$1, ''))) ; else if ((lcName === 'src' || lcName === 'xlink:href' || lcName === 'href') && lcTag !== 'script' && stringIndexOf(value, 'data:') === 0 && DATA_URI_TAGS[lcTag]) ; else if (ALLOW_UNKNOWN_PROTOCOLS && !regExpTest(IS_SCRIPT_OR_DATA$$1, stringReplace(value, ATTR_WHITESPACE$$1, ''))) ; else if (!value) ; else {
5008 return false; 5010 return false;
5009 } 5011 }
5010 5012
5011 return true; 5013 return true;
5012 }; 5014 };
5013 5015
5014 /** 5016 /**
5015 * _sanitizeAttributes 5017 * _sanitizeAttributes
5016 * 5018 *
5020 * @protect setAttribute 5022 * @protect setAttribute
5021 * 5023 *
5022 * @param {Node} currentNode to sanitize 5024 * @param {Node} currentNode to sanitize
5023 */ 5025 */
5024 var _sanitizeAttributes = function _sanitizeAttributes(currentNode) { 5026 var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
5025 var attr = void 0; 5027 var attr = void 0;
5026 var value = void 0; 5028 var value = void 0;
5027 var lcName = void 0; 5029 var lcName = void 0;
5028 var l = void 0; 5030 var l = void 0;
5029 /* Execute a hook if present */ 5031 /* Execute a hook if present */
5030 _executeHook('beforeSanitizeAttributes', currentNode, null); 5032 _executeHook('beforeSanitizeAttributes', currentNode, null);
5031 5033
5032 var attributes = currentNode.attributes; 5034 var attributes = currentNode.attributes;
5033 5035
5034 /* Check if we have attributes; if not we might have a text node */ 5036 /* Check if we have attributes; if not we might have a text node */
5035 5037
5036 if (!attributes) { 5038 if (!attributes) {
5037 return; 5039 return;
5038 } 5040 }
5039 5041
5040 var hookEvent = { 5042 var hookEvent = {
5041 attrName: '', 5043 attrName: '',
5042 attrValue: '', 5044 attrValue: '',
5043 keepAttr: true, 5045 keepAttr: true,
5044 allowedAttributes: ALLOWED_ATTR 5046 allowedAttributes: ALLOWED_ATTR
5045 }; 5047 };
5046 l = attributes.length; 5048 l = attributes.length;
5047 5049
5048 /* Go backwards over all attributes; safely remove bad ones */ 5050 /* Go backwards over all attributes; safely remove bad ones */
5049 while (l--) { 5051 while (l--) {
5050 attr = attributes[l]; 5052 attr = attributes[l];
5051 var _attr = attr, 5053 var _attr = attr,
5052 name = _attr.name, 5054 name = _attr.name,
5053 namespaceURI = _attr.namespaceURI; 5055 namespaceURI = _attr.namespaceURI;
5054 5056
5055 value = stringTrim(attr.value); 5057 value = stringTrim(attr.value);
5056 lcName = stringToLowerCase(name); 5058 lcName = stringToLowerCase(name);
5057 5059
5058 /* Execute a hook if present */ 5060 /* Execute a hook if present */
5059 hookEvent.attrName = lcName; 5061 hookEvent.attrName = lcName;
5060 hookEvent.attrValue = value; 5062 hookEvent.attrValue = value;
5061 hookEvent.keepAttr = true; 5063 hookEvent.keepAttr = true;
5062 hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set 5064 hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
5063 _executeHook('uponSanitizeAttribute', currentNode, hookEvent); 5065 _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
5064 value = hookEvent.attrValue; 5066 value = hookEvent.attrValue;
5065 /* Did the hooks approve of the attribute? */ 5067 /* Did the hooks approve of the attribute? */
5066 if (hookEvent.forceKeepAttr) { 5068 if (hookEvent.forceKeepAttr) {
5067 continue; 5069 continue;
5068 } 5070 }
5069 5071
5070 /* Remove attribute */ 5072 /* Remove attribute */
5071 _removeAttribute(name, currentNode); 5073 _removeAttribute(name, currentNode);
5072 5074
5073 /* Did the hooks approve of the attribute? */ 5075 /* Did the hooks approve of the attribute? */
5074 if (!hookEvent.keepAttr) { 5076 if (!hookEvent.keepAttr) {
5075 continue; 5077 continue;
5076 } 5078 }
5077 5079
5078 /* Work around a security issue in jQuery 3.0 */ 5080 /* Work around a security issue in jQuery 3.0 */
5079 if (regExpTest(/\/>/i, value)) { 5081 if (regExpTest(/\/>/i, value)) {
5080 _removeAttribute(name, currentNode); 5082 _removeAttribute(name, currentNode);
5081 continue; 5083 continue;
5082 } 5084 }
5083 5085
5084 /* Sanitize attribute content to be template-safe */ 5086 /* Sanitize attribute content to be template-safe */
5085 if (SAFE_FOR_TEMPLATES) { 5087 if (SAFE_FOR_TEMPLATES) {
5086 value = stringReplace(value, MUSTACHE_EXPR$$1, ' '); 5088 value = stringReplace(value, MUSTACHE_EXPR$$1, ' ');
5087 value = stringReplace(value, ERB_EXPR$$1, ' '); 5089 value = stringReplace(value, ERB_EXPR$$1, ' ');
5088 } 5090 }
5089 5091
5090 /* Is `value` valid for this attribute? */ 5092 /* Is `value` valid for this attribute? */
5091 var lcTag = currentNode.nodeName.toLowerCase(); 5093 var lcTag = currentNode.nodeName.toLowerCase();
5092 if (!_isValidAttribute(lcTag, lcName, value)) { 5094 if (!_isValidAttribute(lcTag, lcName, value)) {
5093 continue; 5095 continue;
5094 } 5096 }
5095 5097
5096 /* Handle invalid data-* attribute set by try-catching it */ 5098 /* Handle invalid data-* attribute set by try-catching it */
5097 try { 5099 try {
5098 if (namespaceURI) { 5100 if (namespaceURI) {
5099 currentNode.setAttributeNS(namespaceURI, name, value); 5101 currentNode.setAttributeNS(namespaceURI, name, value);
5100 } else { 5102 } else {
5101 /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */ 5103 /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
5102 currentNode.setAttribute(name, value); 5104 currentNode.setAttribute(name, value);
5103 } 5105 }
5104 5106
5105 arrayPop(DOMPurify.removed); 5107 arrayPop(DOMPurify.removed);
5106 } catch (_) {} 5108 } catch (_) {}
5107 } 5109 }
5108 5110
5109 /* Execute a hook if present */ 5111 /* Execute a hook if present */
5110 _executeHook('afterSanitizeAttributes', currentNode, null); 5112 _executeHook('afterSanitizeAttributes', currentNode, null);
5111 }; 5113 };
5112 5114
5113 /** 5115 /**
5114 * _sanitizeShadowDOM 5116 * _sanitizeShadowDOM
5115 * 5117 *
5116 * @param {DocumentFragment} fragment to iterate over recursively 5118 * @param {DocumentFragment} fragment to iterate over recursively
5117 */ 5119 */
5118 var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) { 5120 var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
5119 var shadowNode = void 0; 5121 var shadowNode = void 0;
5120 var shadowIterator = _createIterator(fragment); 5122 var shadowIterator = _createIterator(fragment);
5121 5123
5122 /* Execute a hook if present */ 5124 /* Execute a hook if present */
5123 _executeHook('beforeSanitizeShadowDOM', fragment, null); 5125 _executeHook('beforeSanitizeShadowDOM', fragment, null);
5124 5126
5125 while (shadowNode = shadowIterator.nextNode()) { 5127 while (shadowNode = shadowIterator.nextNode()) {
5126 /* Execute a hook if present */ 5128 /* Execute a hook if present */
5127 _executeHook('uponSanitizeShadowNode', shadowNode, null); 5129 _executeHook('uponSanitizeShadowNode', shadowNode, null);
5128 5130
5129 /* Sanitize tags and elements */ 5131 /* Sanitize tags and elements */
5130 if (_sanitizeElements(shadowNode)) { 5132 if (_sanitizeElements(shadowNode)) {
5131 continue; 5133 continue;
5132 } 5134 }
5133 5135
5134 /* Deep shadow DOM detected */ 5136 /* Deep shadow DOM detected */
5135 if (shadowNode.content instanceof DocumentFragment) { 5137 if (shadowNode.content instanceof DocumentFragment) {
5136 _sanitizeShadowDOM(shadowNode.content); 5138 _sanitizeShadowDOM(shadowNode.content);
5137 } 5139 }
5138 5140
5139 /* Check attributes, sanitize if necessary */ 5141 /* Check attributes, sanitize if necessary */
5140 _sanitizeAttributes(shadowNode); 5142 _sanitizeAttributes(shadowNode);
5141 } 5143 }
5142 5144
5143 /* Execute a hook if present */ 5145 /* Execute a hook if present */
5144 _executeHook('afterSanitizeShadowDOM', fragment, null); 5146 _executeHook('afterSanitizeShadowDOM', fragment, null);
5145 }; 5147 };
5146 5148
5147 /** 5149 /**
5148 * Sanitize 5150 * Sanitize
5149 * Public method providing core sanitation functionality 5151 * Public method providing core sanitation functionality
5151 * @param {String|Node} dirty string or DOM node 5153 * @param {String|Node} dirty string or DOM node
5152 * @param {Object} configuration object 5154 * @param {Object} configuration object
5153 */ 5155 */
5154 // eslint-disable-next-line complexity 5156 // eslint-disable-next-line complexity
5155 DOMPurify.sanitize = function (dirty, cfg) { 5157 DOMPurify.sanitize = function (dirty, cfg) {
5156 var body = void 0; 5158 var body = void 0;
5157 var importedNode = void 0; 5159 var importedNode = void 0;
5158 var currentNode = void 0; 5160 var currentNode = void 0;
5159 var oldNode = void 0; 5161 var oldNode = void 0;
5160 var returnNode = void 0; 5162 var returnNode = void 0;
5161 /* Make sure we have a string to sanitize. 5163 /* Make sure we have a string to sanitize.
5162 DO NOT return early, as this will return the wrong type if 5164 DO NOT return early, as this will return the wrong type if
5163 the user has requested a DOM object rather than a string */ 5165 the user has requested a DOM object rather than a string */
5164 if (!dirty) { 5166 if (!dirty) {
5165 dirty = '<!-->'; 5167 dirty = '<!-->';
5166 } 5168 }
5167 5169
5168 /* Stringify, in case dirty is an object */ 5170 /* Stringify, in case dirty is an object */
5169 if (typeof dirty !== 'string' && !_isNode(dirty)) { 5171 if (typeof dirty !== 'string' && !_isNode(dirty)) {
5170 // eslint-disable-next-line no-negated-condition 5172 // eslint-disable-next-line no-negated-condition
5171 if (typeof dirty.toString !== 'function') { 5173 if (typeof dirty.toString !== 'function') {
5172 throw typeErrorCreate('toString is not a function'); 5174 throw typeErrorCreate('toString is not a function');
5173 } else { 5175 } else {
5174 dirty = dirty.toString(); 5176 dirty = dirty.toString();
5175 if (typeof dirty !== 'string') { 5177 if (typeof dirty !== 'string') {
5176 throw typeErrorCreate('dirty is not a string, aborting'); 5178 throw typeErrorCreate('dirty is not a string, aborting');
5177 } 5179 }
5178 } 5180 }
5179 } 5181 }
5180 5182
5181 /* Check we can run. Otherwise fall back or ignore */ 5183 /* Check we can run. Otherwise fall back or ignore */
5182 if (!DOMPurify.isSupported) { 5184 if (!DOMPurify.isSupported) {
5183 if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') { 5185 if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') {
5184 if (typeof dirty === 'string') { 5186 if (typeof dirty === 'string') {
5185 return window.toStaticHTML(dirty); 5187 return window.toStaticHTML(dirty);
5186 } 5188 }
5187 5189
5188 if (_isNode(dirty)) { 5190 if (_isNode(dirty)) {
5189 return window.toStaticHTML(dirty.outerHTML); 5191 return window.toStaticHTML(dirty.outerHTML);
5190 } 5192 }
5191 } 5193 }
5192 5194
5193 return dirty; 5195 return dirty;
5194 } 5196 }
5195 5197
5196 /* Assign config vars */ 5198 /* Assign config vars */
5197 if (!SET_CONFIG) { 5199 if (!SET_CONFIG) {
5198 _parseConfig(cfg); 5200 _parseConfig(cfg);
5199 } 5201 }
5200 5202
5201 /* Clean up removed elements */ 5203 /* Clean up removed elements */
5202 DOMPurify.removed = []; 5204 DOMPurify.removed = [];
5203 5205
5204 /* Check if dirty is correctly typed for IN_PLACE */ 5206 /* Check if dirty is correctly typed for IN_PLACE */
5205 if (typeof dirty === 'string') { 5207 if (typeof dirty === 'string') {
5206 IN_PLACE = false; 5208 IN_PLACE = false;
5207 } 5209 }
5208 5210
5209 if (IN_PLACE) ; else if (dirty instanceof Node) { 5211 if (IN_PLACE) ; else if (dirty instanceof Node) {
5210 /* If dirty is a DOM element, append to an empty document to avoid 5212 /* If dirty is a DOM element, append to an empty document to avoid
5211 elements being stripped by the parser */ 5213 elements being stripped by the parser */
5212 body = _initDocument('<!---->'); 5214 body = _initDocument('<!---->');
5213 importedNode = body.ownerDocument.importNode(dirty, true); 5215 importedNode = body.ownerDocument.importNode(dirty, true);
5214 if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') { 5216 if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
5215 /* Node is already a body, use as is */ 5217 /* Node is already a body, use as is */
5216 body = importedNode; 5218 body = importedNode;
5217 } else if (importedNode.nodeName === 'HTML') { 5219 } else if (importedNode.nodeName === 'HTML') {
5218 body = importedNode; 5220 body = importedNode;
5219 } else { 5221 } else {
5220 // eslint-disable-next-line unicorn/prefer-node-append 5222 // eslint-disable-next-line unicorn/prefer-node-append
5221 body.appendChild(importedNode); 5223 body.appendChild(importedNode);
5222 } 5224 }
5223 } else { 5225 } else {
5224 /* Exit directly if we have nothing to do */ 5226 /* Exit directly if we have nothing to do */
5225 if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT && 5227 if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
5226 // eslint-disable-next-line unicorn/prefer-includes 5228 // eslint-disable-next-line unicorn/prefer-includes
5227 dirty.indexOf('<') === -1) { 5229 dirty.indexOf('<') === -1) {
5228 return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty; 5230 return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
5229 } 5231 }
5230 5232
5231 /* Initialize the document to work on */ 5233 /* Initialize the document to work on */
5232 body = _initDocument(dirty); 5234 body = _initDocument(dirty);
5233 5235
5234 /* Check we have a DOM node from the data */ 5236 /* Check we have a DOM node from the data */
5235 if (!body) { 5237 if (!body) {
5236 return RETURN_DOM ? null : emptyHTML; 5238 return RETURN_DOM ? null : emptyHTML;
5237 } 5239 }
5238 } 5240 }
5239 5241
5240 /* Remove first element node (ours) if FORCE_BODY is set */ 5242 /* Remove first element node (ours) if FORCE_BODY is set */
5241 if (body && FORCE_BODY) { 5243 if (body && FORCE_BODY) {
5242 _forceRemove(body.firstChild); 5244 _forceRemove(body.firstChild);
5243 } 5245 }
5244 5246
5245 /* Get node iterator */ 5247 /* Get node iterator */
5246 var nodeIterator = _createIterator(IN_PLACE ? dirty : body); 5248 var nodeIterator = _createIterator(IN_PLACE ? dirty : body);
5247 5249
5248 /* Now start iterating over the created document */ 5250 /* Now start iterating over the created document */
5249 while (currentNode = nodeIterator.nextNode()) { 5251 while (currentNode = nodeIterator.nextNode()) {
5250 /* Fix IE's strange behavior with manipulated textNodes #89 */ 5252 /* Fix IE's strange behavior with manipulated textNodes #89 */
5251 if (currentNode.nodeType === 3 && currentNode === oldNode) { 5253 if (currentNode.nodeType === 3 && currentNode === oldNode) {
5252 continue; 5254 continue;
5253 } 5255 }
5254 5256
5255 /* Sanitize tags and elements */ 5257 /* Sanitize tags and elements */
5256 if (_sanitizeElements(currentNode)) { 5258 if (_sanitizeElements(currentNode)) {
5257 continue; 5259 continue;
5258 } 5260 }
5259 5261
5260 /* Shadow DOM detected, sanitize it */ 5262 /* Shadow DOM detected, sanitize it */
5261 if (currentNode.content instanceof DocumentFragment) { 5263 if (currentNode.content instanceof DocumentFragment) {
5262 _sanitizeShadowDOM(currentNode.content); 5264 _sanitizeShadowDOM(currentNode.content);
5263 } 5265 }
5264 5266
5265 /* Check attributes, sanitize if necessary */ 5267 /* Check attributes, sanitize if necessary */
5266 _sanitizeAttributes(currentNode); 5268 _sanitizeAttributes(currentNode);
5267 5269
5268 oldNode = currentNode; 5270 oldNode = currentNode;
5269 } 5271 }
5270 5272
5271 oldNode = null; 5273 oldNode = null;
5272 5274
5273 /* If we sanitized `dirty` in-place, return it. */ 5275 /* If we sanitized `dirty` in-place, return it. */
5274 if (IN_PLACE) { 5276 if (IN_PLACE) {
5275 return dirty; 5277 return dirty;
5276 } 5278 }
5277 5279
5278 /* Return sanitized string or DOM */ 5280 /* Return sanitized string or DOM */
5279 if (RETURN_DOM) { 5281 if (RETURN_DOM) {
5280 if (RETURN_DOM_FRAGMENT) { 5282 if (RETURN_DOM_FRAGMENT) {
5281 returnNode = createDocumentFragment.call(body.ownerDocument); 5283 //returnNode = createDocumentFragment.call(body.ownerDocument);
5282 5284 // untested
5283 while (body.firstChild) { 5285 returnNode = body.ownerDocument.createDocumentFragment.call();
5284 // eslint-disable-next-line unicorn/prefer-node-append 5286
5285 returnNode.appendChild(body.firstChild); 5287 while (body.firstChild) {
5286 } 5288 // eslint-disable-next-line unicorn/prefer-node-append
5287 } else { 5289 returnNode.appendChild(body.firstChild);
5288 returnNode = body; 5290 }
5289 } 5291 } else {
5290 5292 returnNode = body;
5291 if (RETURN_DOM_IMPORT) { 5293 }
5292 /* 5294
5293 AdoptNode() is not used because internal state is not reset 5295 if (RETURN_DOM_IMPORT) {
5294 (e.g. the past names map of a HTMLFormElement), this is safe 5296 /*
5295 in theory but we would rather not risk another attack vector. 5297 AdoptNode() is not used because internal state is not reset
5296 The state that is cloned by importNode() is explicitly defined 5298 (e.g. the past names map of a HTMLFormElement), this is safe
5297 by the specs. 5299 in theory but we would rather not risk another attack vector.
5298 */ 5300 The state that is cloned by importNode() is explicitly defined
5299 returnNode = importNode.call(originalDocument, returnNode, true); 5301 by the specs.
5300 } 5302 */
5301 5303 //returnNode = importNode.call(originalDocument, returnNode, true);
5302 return returnNode; 5304 // untested
5303 } 5305 returnNode = originalDocument.importNode(returnNode, true);
5304 5306 }
5305 var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML; 5307
5306 5308 return returnNode;
5307 /* Sanitize final string template-safe */ 5309 }
5308 if (SAFE_FOR_TEMPLATES) { 5310
5309 serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$$1, ' '); 5311 var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
5310 serializedHTML = stringReplace(serializedHTML, ERB_EXPR$$1, ' '); 5312
5311 } 5313 /* Sanitize final string template-safe */
5312 5314 if (SAFE_FOR_TEMPLATES) {
5313 return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML; 5315 serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$$1, ' ');
5316 serializedHTML = stringReplace(serializedHTML, ERB_EXPR$$1, ' ');
5317 }
5318
5319 return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
5314 }; 5320 };
5315 5321
5316 /** 5322 /**
5317 * Public method to set the configuration once 5323 * Public method to set the configuration once
5318 * setConfig 5324 * setConfig
5319 * 5325 *
5320 * @param {Object} cfg configuration object 5326 * @param {Object} cfg configuration object
5321 */ 5327 */
5322 DOMPurify.setConfig = function (cfg) { 5328 DOMPurify.setConfig = function (cfg) {
5323 _parseConfig(cfg); 5329 _parseConfig(cfg);
5324 SET_CONFIG = true; 5330 SET_CONFIG = true;
5325 }; 5331 };
5326 5332
5327 /** 5333 /**
5328 * Public method to remove the configuration 5334 * Public method to remove the configuration
5329 * clearConfig 5335 * clearConfig
5330 * 5336 *
5331 */ 5337 */
5332 DOMPurify.clearConfig = function () { 5338 DOMPurify.clearConfig = function () {
5333 CONFIG = null; 5339 CONFIG = null;
5334 SET_CONFIG = false; 5340 SET_CONFIG = false;
5335 }; 5341 };
5336 5342
5337 /** 5343 /**
5338 * Public method to check if an attribute value is valid. 5344 * Public method to check if an attribute value is valid.
5339 * Uses last set config, if any. Otherwise, uses config defaults. 5345 * Uses last set config, if any. Otherwise, uses config defaults.
5343 * @param {string} attr Attribute name. 5349 * @param {string} attr Attribute name.
5344 * @param {string} value Attribute value. 5350 * @param {string} value Attribute value.
5345 * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false. 5351 * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
5346 */ 5352 */
5347 DOMPurify.isValidAttribute = function (tag, attr, value) { 5353 DOMPurify.isValidAttribute = function (tag, attr, value) {
5348 /* Initialize shared config vars if necessary. */ 5354 /* Initialize shared config vars if necessary. */
5349 if (!CONFIG) { 5355 if (!CONFIG) {
5350 _parseConfig({}); 5356 _parseConfig({});
5351 } 5357 }
5352 5358
5353 var lcTag = stringToLowerCase(tag); 5359 var lcTag = stringToLowerCase(tag);
5354 var lcName = stringToLowerCase(attr); 5360 var lcName = stringToLowerCase(attr);
5355 return _isValidAttribute(lcTag, lcName, value); 5361 return _isValidAttribute(lcTag, lcName, value);
5356 }; 5362 };
5357 5363
5358 /** 5364 /**
5359 * AddHook 5365 * AddHook
5360 * Public method to add DOMPurify hooks 5366 * Public method to add DOMPurify hooks
5361 * 5367 *
5362 * @param {String} entryPoint entry point for the hook to add 5368 * @param {String} entryPoint entry point for the hook to add
5363 * @param {Function} hookFunction function to execute 5369 * @param {Function} hookFunction function to execute
5364 */ 5370 */
5365 DOMPurify.addHook = function (entryPoint, hookFunction) { 5371 DOMPurify.addHook = function (entryPoint, hookFunction) {
5366 if (typeof hookFunction !== 'function') { 5372 if (typeof hookFunction !== 'function') {
5367 return; 5373 return;
5368 } 5374 }
5369 5375
5370 hooks[entryPoint] = hooks[entryPoint] || []; 5376 hooks[entryPoint] = hooks[entryPoint] || [];
5371 arrayPush(hooks[entryPoint], hookFunction); 5377 arrayPush(hooks[entryPoint], hookFunction);
5372 }; 5378 };
5373 5379
5374 /** 5380 /**
5375 * RemoveHook 5381 * RemoveHook
5376 * Public method to remove a DOMPurify hook at a given entryPoint 5382 * Public method to remove a DOMPurify hook at a given entryPoint
5377 * (pops it from the stack of hooks if more are present) 5383 * (pops it from the stack of hooks if more are present)
5378 * 5384 *
5379 * @param {String} entryPoint entry point for the hook to remove 5385 * @param {String} entryPoint entry point for the hook to remove
5380 */ 5386 */
5381 DOMPurify.removeHook = function (entryPoint) { 5387 DOMPurify.removeHook = function (entryPoint) {
5382 if (hooks[entryPoint]) { 5388 if (hooks[entryPoint]) {
5383 arrayPop(hooks[entryPoint]); 5389 arrayPop(hooks[entryPoint]);
5384 } 5390 }
5385 }; 5391 };
5386 5392
5387 /** 5393 /**
5388 * RemoveHooks 5394 * RemoveHooks
5389 * Public method to remove all DOMPurify hooks at a given entryPoint 5395 * Public method to remove all DOMPurify hooks at a given entryPoint
5390 * 5396 *
5391 * @param {String} entryPoint entry point for the hooks to remove 5397 * @param {String} entryPoint entry point for the hooks to remove
5392 */ 5398 */
5393 DOMPurify.removeHooks = function (entryPoint) { 5399 DOMPurify.removeHooks = function (entryPoint) {
5394 if (hooks[entryPoint]) { 5400 if (hooks[entryPoint]) {
5395 hooks[entryPoint] = []; 5401 hooks[entryPoint] = [];
5396 } 5402 }
5397 }; 5403 };
5398 5404
5399 /** 5405 /**
5400 * RemoveAllHooks 5406 * RemoveAllHooks
5401 * Public method to remove all DOMPurify hooks 5407 * Public method to remove all DOMPurify hooks
5402 * 5408 *
5403 */ 5409 */
5404 DOMPurify.removeAllHooks = function () { 5410 DOMPurify.removeAllHooks = function () {
5405 hooks = {}; 5411 hooks = {};
5406 }; 5412 };
5407 5413
5408 return DOMPurify; 5414 return DOMPurify;
5409 } 5415 }
5410 5416