changeset 19:13df5ac9b34b

more OO
author Franklin Schmidt <fschmidt@gmail.com>
date Mon, 08 Aug 2022 16:50:22 -0600
parents 1334920263a2
children cf42d9b17c25
files src/sceditor.js
diffstat 1 files changed, 764 insertions(+), 758 deletions(-) [+]
line wrap: on
line diff
diff -r 1334920263a2 -r 13df5ac9b34b src/sceditor.js
--- a/src/sceditor.js	Mon Aug 08 16:20:23 2022 -0600
+++ b/src/sceditor.js	Mon Aug 08 16:50:22 2022 -0600
@@ -4101,39 +4101,39 @@
 	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); } }
 
 	var hasOwnProperty = Object.hasOwnProperty,
-	    setPrototypeOf = Object.setPrototypeOf,
-	    isFrozen = Object.isFrozen,
-	    getPrototypeOf = Object.getPrototypeOf,
-	    getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
+		setPrototypeOf = Object.setPrototypeOf,
+		isFrozen = Object.isFrozen,
+		getPrototypeOf = Object.getPrototypeOf,
+		getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
 	var freeze = Object.freeze,
-	    seal = Object.seal,
-	    create = Object.create; // eslint-disable-line import/no-mutable-exports
+		seal = Object.seal,
+		create = Object.create; // eslint-disable-line import/no-mutable-exports
 
 	var _ref = typeof Reflect !== 'undefined' && Reflect,
-	    apply = _ref.apply,
-	    construct = _ref.construct;
+		apply = _ref.apply,
+		construct = _ref.construct;
 
 	if (!apply) {
 	  apply = function apply(fun, thisValue, args) {
-	    return fun.apply(thisValue, args);
+		return fun.apply(thisValue, args);
 	  };
 	}
 
 	if (!freeze) {
 	  freeze = function freeze(x) {
-	    return x;
+		return x;
 	  };
 	}
 
 	if (!seal) {
 	  seal = function seal(x) {
-	    return x;
+		return x;
 	  };
 	}
 
 	if (!construct) {
 	  construct = function construct(Func, args) {
-	    return new (Function.prototype.bind.apply(Func, [null].concat(_toConsumableArray(args))))();
+		return new (Function.prototype.bind.apply(Func, [null].concat(_toConsumableArray(args))))();
 	  };
 	}
 
@@ -4153,49 +4153,49 @@
 
 	function unapply(func) {
 	  return function (thisArg) {
-	    for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
-	      args[_key - 1] = arguments[_key];
-	    }
-
-	    return apply(func, thisArg, args);
+		for (var _len = arguments.length, args = Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) {
+		  args[_key - 1] = arguments[_key];
+		}
+
+		return apply(func, thisArg, args);
 	  };
 	}
 
 	function unconstruct(func) {
 	  return function () {
-	    for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
-	      args[_key2] = arguments[_key2];
-	    }
-
-	    return construct(func, args);
+		for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
+		  args[_key2] = arguments[_key2];
+		}
+
+		return construct(func, args);
 	  };
 	}
 
 	/* Add properties to a lookup table */
 	function addToSet(set, array) {
 	  if (setPrototypeOf) {
-	    // Make 'in' and truthy checks like Boolean(set.constructor)
-	    // independent of any properties defined on Object.prototype.
-	    // Prevent prototype setters from intercepting set as a this value.
-	    setPrototypeOf(set, null);
+		// Make 'in' and truthy checks like Boolean(set.constructor)
+		// independent of any properties defined on Object.prototype.
+		// Prevent prototype setters from intercepting set as a this value.
+		setPrototypeOf(set, null);
 	  }
 
 	  var l = array.length;
 	  while (l--) {
-	    var element = array[l];
-	    if (typeof element === 'string') {
-	      var lcElement = stringToLowerCase(element);
-	      if (lcElement !== element) {
-	        // Config presets (e.g. tags.js, attrs.js) are immutable.
-	        if (!isFrozen(array)) {
-	          array[l] = lcElement;
-	        }
-
-	        element = lcElement;
-	      }
-	    }
-
-	    set[element] = true;
+		var element = array[l];
+		if (typeof element === 'string') {
+		  var lcElement = stringToLowerCase(element);
+		  if (lcElement !== element) {
+			// Config presets (e.g. tags.js, attrs.js) are immutable.
+			if (!isFrozen(array)) {
+			  array[l] = lcElement;
+			}
+
+			element = lcElement;
+		  }
+		}
+
+		set[element] = true;
 	  }
 
 	  return set;
@@ -4207,9 +4207,9 @@
 
 	  var property = void 0;
 	  for (property in object) {
-	    if (apply(hasOwnProperty, object, [property])) {
-	      newObject[property] = object[property];
-	    }
+		if (apply(hasOwnProperty, object, [property])) {
+		  newObject[property] = object[property];
+		}
 	  }
 
 	  return newObject;
@@ -4221,18 +4221,18 @@
 	 * accordingly. */
 	function lookupGetter(object, prop) {
 	  while (object !== null) {
-	    var desc = getOwnPropertyDescriptor(object, prop);
-	    if (desc) {
-	      if (desc.get) {
-	        return unapply(desc.get);
-	      }
-
-	      if (typeof desc.value === 'function') {
-	        return unapply(desc.value);
-	      }
-	    }
-
-	    object = getPrototypeOf(object);
+		var desc = getOwnPropertyDescriptor(object, prop);
+		if (desc) {
+		  if (desc.get) {
+			return unapply(desc.get);
+		  }
+
+		  if (typeof desc.value === 'function') {
+			return unapply(desc.value);
+		  }
+		}
+
+		object = getPrototypeOf(object);
 	  }
 
 	  return null;
@@ -4296,7 +4296,7 @@
 	 */
 	var _createTrustedTypesPolicy = function _createTrustedTypesPolicy(trustedTypes, document) {
 	  if ((typeof trustedTypes === 'undefined' ? 'undefined' : _typeof(trustedTypes)) !== 'object' || typeof trustedTypes.createPolicy !== 'function') {
-	    return null;
+		return null;
 	  }
 
 	  // Allow the callers to control the unique policy name
@@ -4305,23 +4305,23 @@
 	  var suffix = null;
 	  var ATTR_NAME = 'data-tt-policy-suffix';
 	  if (document.currentScript && document.currentScript.hasAttribute(ATTR_NAME)) {
-	    suffix = document.currentScript.getAttribute(ATTR_NAME);
+		suffix = document.currentScript.getAttribute(ATTR_NAME);
 	  }
 
 	  var policyName = 'dompurify' + (suffix ? '#' + suffix : '');
 
 	  try {
-	    return trustedTypes.createPolicy(policyName, {
-	      createHTML: function createHTML(html$$1) {
-	        return html$$1;
-	      }
-	    });
+		return trustedTypes.createPolicy(policyName, {
+		  createHTML: function createHTML(html$$1) {
+			return html$$1;
+		  }
+		});
 	  } catch (_) {
-	    // Policy creation failed (most likely another DOMPurify script has
-	    // already run). Skip creating the policy, as this will only cause errors
-	    // if TT are enforced.
-	    console.warn('TrustedTypes policy ' + policyName + ' could not be created.');
-	    return null;
+		// Policy creation failed (most likely another DOMPurify script has
+		// already run). Skip creating the policy, as this will only cause errors
+		// if TT are enforced.
+		console.warn('TrustedTypes policy ' + policyName + ' could not be created.');
+		return null;
 	  }
 	};
 
@@ -4329,7 +4329,7 @@
 	  var window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
 
 	  var DOMPurify = function DOMPurify(root) {
-	    return createDOMPurify(root);
+		return createDOMPurify(root);
 	  };
 
 	  /**
@@ -4345,27 +4345,27 @@
 	  DOMPurify.removed = [];
 
 	  if (!window || !window.document || window.document.nodeType !== 9) {
-	    // Not running in a browser, provide a factory function
-	    // so that you can pass your own Window
-	    DOMPurify.isSupported = false;
-
-	    return DOMPurify;
+		// Not running in a browser, provide a factory function
+		// so that you can pass your own Window
+		DOMPurify.isSupported = false;
+
+		return DOMPurify;
 	  }
 
 	  var originalDocument = window.document;
 
 	  var document = window.document;
 	  var DocumentFragment = window.DocumentFragment,
-	      HTMLTemplateElement = window.HTMLTemplateElement,
-	      Node = window.Node,
-	      Element = window.Element,
-	      NodeFilter = window.NodeFilter,
-	      _window$NamedNodeMap = window.NamedNodeMap,
-	      NamedNodeMap = _window$NamedNodeMap === undefined ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
-	      Text = window.Text,
-	      Comment = window.Comment,
-	      DOMParser = window.DOMParser,
-	      trustedTypes = window.trustedTypes;
+		  HTMLTemplateElement = window.HTMLTemplateElement,
+		  Node = window.Node,
+		  Element = window.Element,
+		  NodeFilter = window.NodeFilter,
+		  _window$NamedNodeMap = window.NamedNodeMap,
+		  NamedNodeMap = _window$NamedNodeMap === undefined ? window.NamedNodeMap || window.MozNamedAttrMap : _window$NamedNodeMap,
+		  Text = window.Text,
+		  Comment = window.Comment,
+		  DOMParser = window.DOMParser,
+		  trustedTypes = window.trustedTypes;
 
 
 	  var ElementPrototype = Element.prototype;
@@ -4382,26 +4382,22 @@
 	  // document, so we use that as our parent document to ensure nothing
 	  // is inherited.
 	  if (typeof HTMLTemplateElement === 'function') {
-	    var template = document.createElement('template');
-	    if (template.content && template.content.ownerDocument) {
-	      document = template.content.ownerDocument;
-	    }
+		var template = document.createElement('template');
+		if (template.content && template.content.ownerDocument) {
+		  document = template.content.ownerDocument;
+		}
 	  }
 
 	  var trustedTypesPolicy = _createTrustedTypesPolicy(trustedTypes, originalDocument);
 	  var emptyHTML = trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML('') : '';
 
-	  var _document = document,
-	      implementation = _document.implementation,
-	      createNodeIterator = _document.createNodeIterator,
-	      getElementsByTagName = _document.getElementsByTagName,
-	      createDocumentFragment = _document.createDocumentFragment;
-	  var importNode = originalDocument.importNode;
+		var implementation = document.implementation,
+		//var importNode = originalDocument.importNode;
 
 
 	  var documentMode = {};
 	  try {
-	    documentMode = clone(document).documentMode ? document.documentMode : {};
+		documentMode = clone(document).documentMode ? document.documentMode : {};
 	  } catch (_) {}
 
 	  var hooks = {};
@@ -4412,11 +4408,11 @@
 	  DOMPurify.isSupported = implementation && typeof implementation.createHTMLDocument !== 'undefined' && documentMode !== 9;
 
 	  var MUSTACHE_EXPR$$1 = MUSTACHE_EXPR,
-	      ERB_EXPR$$1 = ERB_EXPR,
-	      DATA_ATTR$$1 = DATA_ATTR,
-	      ARIA_ATTR$$1 = ARIA_ATTR,
-	      IS_SCRIPT_OR_DATA$$1 = IS_SCRIPT_OR_DATA,
-	      ATTR_WHITESPACE$$1 = ATTR_WHITESPACE;
+		  ERB_EXPR$$1 = ERB_EXPR,
+		  DATA_ATTR$$1 = DATA_ATTR,
+		  ARIA_ATTR$$1 = ARIA_ATTR,
+		  IS_SCRIPT_OR_DATA$$1 = IS_SCRIPT_OR_DATA,
+		  ATTR_WHITESPACE$$1 = ATTR_WHITESPACE;
 	  var IS_ALLOWED_URI$$1 = IS_ALLOWED_URI;
 
 	  /**
@@ -4527,120 +4523,120 @@
 	   */
 	  // eslint-disable-next-line complexity
 	  var _parseConfig = function _parseConfig(cfg) {
-	    if (CONFIG && CONFIG === cfg) {
-	      return;
-	    }
-
-	    /* Shield configuration object from tampering */
-	    if (!cfg || (typeof cfg === 'undefined' ? 'undefined' : _typeof(cfg)) !== 'object') {
-	      cfg = {};
-	    }
-
-	    /* Shield configuration object from prototype pollution */
-	    cfg = clone(cfg);
-
-	    /* Set configuration parameters */
-	    ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS;
-	    ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR;
-	    URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES;
-	    DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS) : DEFAULT_DATA_URI_TAGS;
-	    FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {};
-	    FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {};
-	    USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
-	    ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
-	    ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
-	    ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
-	    SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
-	    WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
-	    RETURN_DOM = cfg.RETURN_DOM || false; // Default false
-	    RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
-	    RETURN_DOM_IMPORT = cfg.RETURN_DOM_IMPORT !== false; // Default true
-	    RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
-	    FORCE_BODY = cfg.FORCE_BODY || false; // Default false
-	    SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
-	    KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
-	    IN_PLACE = cfg.IN_PLACE || false; // Default false
-	    IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1;
-	    if (SAFE_FOR_TEMPLATES) {
-	      ALLOW_DATA_ATTR = false;
-	    }
-
-	    if (RETURN_DOM_FRAGMENT) {
-	      RETURN_DOM = true;
-	    }
-
-	    /* Parse profile info */
-	    if (USE_PROFILES) {
-	      ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(text)));
-	      ALLOWED_ATTR = [];
-	      if (USE_PROFILES.html === true) {
-	        addToSet(ALLOWED_TAGS, html);
-	        addToSet(ALLOWED_ATTR, html$1);
-	      }
-
-	      if (USE_PROFILES.svg === true) {
-	        addToSet(ALLOWED_TAGS, svg);
-	        addToSet(ALLOWED_ATTR, svg$1);
-	        addToSet(ALLOWED_ATTR, xml);
-	      }
-
-	      if (USE_PROFILES.svgFilters === true) {
-	        addToSet(ALLOWED_TAGS, svgFilters);
-	        addToSet(ALLOWED_ATTR, svg$1);
-	        addToSet(ALLOWED_ATTR, xml);
-	      }
-
-	      if (USE_PROFILES.mathMl === true) {
-	        addToSet(ALLOWED_TAGS, mathMl);
-	        addToSet(ALLOWED_ATTR, mathMl$1);
-	        addToSet(ALLOWED_ATTR, xml);
-	      }
-	    }
-
-	    /* Merge configuration parameters */
-	    if (cfg.ADD_TAGS) {
-	      if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
-	        ALLOWED_TAGS = clone(ALLOWED_TAGS);
-	      }
-
-	      addToSet(ALLOWED_TAGS, cfg.ADD_TAGS);
-	    }
-
-	    if (cfg.ADD_ATTR) {
-	      if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
-	        ALLOWED_ATTR = clone(ALLOWED_ATTR);
-	      }
-
-	      addToSet(ALLOWED_ATTR, cfg.ADD_ATTR);
-	    }
-
-	    if (cfg.ADD_URI_SAFE_ATTR) {
-	      addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR);
-	    }
-
-	    /* Add #text in case KEEP_CONTENT is set to true */
-	    if (KEEP_CONTENT) {
-	      ALLOWED_TAGS['#text'] = true;
-	    }
-
-	    /* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
-	    if (WHOLE_DOCUMENT) {
-	      addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
-	    }
-
-	    /* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
-	    if (ALLOWED_TAGS.table) {
-	      addToSet(ALLOWED_TAGS, ['tbody']);
-	      delete FORBID_TAGS.tbody;
-	    }
-
-	    // Prevent further manipulation of configuration.
-	    // Not available in IE8, Safari 5, etc.
-	    if (freeze) {
-	      freeze(cfg);
-	    }
-
-	    CONFIG = cfg;
+		if (CONFIG && CONFIG === cfg) {
+		  return;
+		}
+
+		/* Shield configuration object from tampering */
+		if (!cfg || (typeof cfg === 'undefined' ? 'undefined' : _typeof(cfg)) !== 'object') {
+		  cfg = {};
+		}
+
+		/* Shield configuration object from prototype pollution */
+		cfg = clone(cfg);
+
+		/* Set configuration parameters */
+		ALLOWED_TAGS = 'ALLOWED_TAGS' in cfg ? addToSet({}, cfg.ALLOWED_TAGS) : DEFAULT_ALLOWED_TAGS;
+		ALLOWED_ATTR = 'ALLOWED_ATTR' in cfg ? addToSet({}, cfg.ALLOWED_ATTR) : DEFAULT_ALLOWED_ATTR;
+		URI_SAFE_ATTRIBUTES = 'ADD_URI_SAFE_ATTR' in cfg ? addToSet(clone(DEFAULT_URI_SAFE_ATTRIBUTES), cfg.ADD_URI_SAFE_ATTR) : DEFAULT_URI_SAFE_ATTRIBUTES;
+		DATA_URI_TAGS = 'ADD_DATA_URI_TAGS' in cfg ? addToSet(clone(DEFAULT_DATA_URI_TAGS), cfg.ADD_DATA_URI_TAGS) : DEFAULT_DATA_URI_TAGS;
+		FORBID_TAGS = 'FORBID_TAGS' in cfg ? addToSet({}, cfg.FORBID_TAGS) : {};
+		FORBID_ATTR = 'FORBID_ATTR' in cfg ? addToSet({}, cfg.FORBID_ATTR) : {};
+		USE_PROFILES = 'USE_PROFILES' in cfg ? cfg.USE_PROFILES : false;
+		ALLOW_ARIA_ATTR = cfg.ALLOW_ARIA_ATTR !== false; // Default true
+		ALLOW_DATA_ATTR = cfg.ALLOW_DATA_ATTR !== false; // Default true
+		ALLOW_UNKNOWN_PROTOCOLS = cfg.ALLOW_UNKNOWN_PROTOCOLS || false; // Default false
+		SAFE_FOR_TEMPLATES = cfg.SAFE_FOR_TEMPLATES || false; // Default false
+		WHOLE_DOCUMENT = cfg.WHOLE_DOCUMENT || false; // Default false
+		RETURN_DOM = cfg.RETURN_DOM || false; // Default false
+		RETURN_DOM_FRAGMENT = cfg.RETURN_DOM_FRAGMENT || false; // Default false
+		RETURN_DOM_IMPORT = cfg.RETURN_DOM_IMPORT !== false; // Default true
+		RETURN_TRUSTED_TYPE = cfg.RETURN_TRUSTED_TYPE || false; // Default false
+		FORCE_BODY = cfg.FORCE_BODY || false; // Default false
+		SANITIZE_DOM = cfg.SANITIZE_DOM !== false; // Default true
+		KEEP_CONTENT = cfg.KEEP_CONTENT !== false; // Default true
+		IN_PLACE = cfg.IN_PLACE || false; // Default false
+		IS_ALLOWED_URI$$1 = cfg.ALLOWED_URI_REGEXP || IS_ALLOWED_URI$$1;
+		if (SAFE_FOR_TEMPLATES) {
+		  ALLOW_DATA_ATTR = false;
+		}
+
+		if (RETURN_DOM_FRAGMENT) {
+		  RETURN_DOM = true;
+		}
+
+		/* Parse profile info */
+		if (USE_PROFILES) {
+		  ALLOWED_TAGS = addToSet({}, [].concat(_toConsumableArray$1(text)));
+		  ALLOWED_ATTR = [];
+		  if (USE_PROFILES.html === true) {
+			addToSet(ALLOWED_TAGS, html);
+			addToSet(ALLOWED_ATTR, html$1);
+		  }
+
+		  if (USE_PROFILES.svg === true) {
+			addToSet(ALLOWED_TAGS, svg);
+			addToSet(ALLOWED_ATTR, svg$1);
+			addToSet(ALLOWED_ATTR, xml);
+		  }
+
+		  if (USE_PROFILES.svgFilters === true) {
+			addToSet(ALLOWED_TAGS, svgFilters);
+			addToSet(ALLOWED_ATTR, svg$1);
+			addToSet(ALLOWED_ATTR, xml);
+		  }
+
+		  if (USE_PROFILES.mathMl === true) {
+			addToSet(ALLOWED_TAGS, mathMl);
+			addToSet(ALLOWED_ATTR, mathMl$1);
+			addToSet(ALLOWED_ATTR, xml);
+		  }
+		}
+
+		/* Merge configuration parameters */
+		if (cfg.ADD_TAGS) {
+		  if (ALLOWED_TAGS === DEFAULT_ALLOWED_TAGS) {
+			ALLOWED_TAGS = clone(ALLOWED_TAGS);
+		  }
+
+		  addToSet(ALLOWED_TAGS, cfg.ADD_TAGS);
+		}
+
+		if (cfg.ADD_ATTR) {
+		  if (ALLOWED_ATTR === DEFAULT_ALLOWED_ATTR) {
+			ALLOWED_ATTR = clone(ALLOWED_ATTR);
+		  }
+
+		  addToSet(ALLOWED_ATTR, cfg.ADD_ATTR);
+		}
+
+		if (cfg.ADD_URI_SAFE_ATTR) {
+		  addToSet(URI_SAFE_ATTRIBUTES, cfg.ADD_URI_SAFE_ATTR);
+		}
+
+		/* Add #text in case KEEP_CONTENT is set to true */
+		if (KEEP_CONTENT) {
+		  ALLOWED_TAGS['#text'] = true;
+		}
+
+		/* Add html, head and body to ALLOWED_TAGS in case WHOLE_DOCUMENT is true */
+		if (WHOLE_DOCUMENT) {
+		  addToSet(ALLOWED_TAGS, ['html', 'head', 'body']);
+		}
+
+		/* Add tbody to ALLOWED_TAGS in case tables are permitted, see #286, #365 */
+		if (ALLOWED_TAGS.table) {
+		  addToSet(ALLOWED_TAGS, ['tbody']);
+		  delete FORBID_TAGS.tbody;
+		}
+
+		// Prevent further manipulation of configuration.
+		// Not available in IE8, Safari 5, etc.
+		if (freeze) {
+		  freeze(cfg);
+		}
+
+		CONFIG = cfg;
 	  };
 
 	  var MATHML_TEXT_INTEGRATION_POINTS = addToSet({}, ['mi', 'mo', 'mn', 'ms', 'mtext']);
@@ -4670,86 +4666,86 @@
 	   *  return. Return true otherwise.
 	   */
 	  var _checkValidNamespace = function _checkValidNamespace(element) {
-	    var parent = getParentNode(element);
-
-	    // In JSDOM, if we're inside shadow DOM, then parentNode
-	    // can be null. We just simulate parent in this case.
-	    if (!parent || !parent.tagName) {
-	      parent = {
-	        namespaceURI: HTML_NAMESPACE,
-	        tagName: 'template'
-	      };
-	    }
-
-	    var tagName = stringToLowerCase(element.tagName);
-	    var parentTagName = stringToLowerCase(parent.tagName);
-
-	    if (element.namespaceURI === SVG_NAMESPACE) {
-	      // The only way to switch from HTML namespace to SVG
-	      // is via <svg>. If it happens via any other tag, then
-	      // it should be killed.
-	      if (parent.namespaceURI === HTML_NAMESPACE) {
-	        return tagName === 'svg';
-	      }
-
-	      // The only way to switch from MathML to SVG is via
-	      // svg if parent is either <annotation-xml> or MathML
-	      // text integration points.
-	      if (parent.namespaceURI === MATHML_NAMESPACE) {
-	        return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
-	      }
-
-	      // We only allow elements that are defined in SVG
-	      // spec. All others are disallowed in SVG namespace.
-	      return Boolean(ALL_SVG_TAGS[tagName]);
-	    }
-
-	    if (element.namespaceURI === MATHML_NAMESPACE) {
-	      // The only way to switch from HTML namespace to MathML
-	      // is via <math>. If it happens via any other tag, then
-	      // it should be killed.
-	      if (parent.namespaceURI === HTML_NAMESPACE) {
-	        return tagName === 'math';
-	      }
-
-	      // The only way to switch from SVG to MathML is via
-	      // <math> and HTML integration points
-	      if (parent.namespaceURI === SVG_NAMESPACE) {
-	        return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
-	      }
-
-	      // We only allow elements that are defined in MathML
-	      // spec. All others are disallowed in MathML namespace.
-	      return Boolean(ALL_MATHML_TAGS[tagName]);
-	    }
-
-	    if (element.namespaceURI === HTML_NAMESPACE) {
-	      // The only way to switch from SVG to HTML is via
-	      // HTML integration points, and from MathML to HTML
-	      // is via MathML text integration points
-	      if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
-	        return false;
-	      }
-
-	      if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
-	        return false;
-	      }
-
-	      // Certain elements are allowed in both SVG and HTML
-	      // namespace. We need to specify them explicitly
-	      // so that they don't get erronously deleted from
-	      // HTML namespace.
-	      var commonSvgAndHTMLElements = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
-
-	      // We disallow tags that are specific for MathML
-	      // or SVG and should never appear in HTML namespace
-	      return !ALL_MATHML_TAGS[tagName] && (commonSvgAndHTMLElements[tagName] || !ALL_SVG_TAGS[tagName]);
-	    }
-
-	    // The code should never reach this place (this means
-	    // that the element somehow got namespace that is not
-	    // HTML, SVG or MathML). Return false just in case.
-	    return false;
+		var parent = getParentNode(element);
+
+		// In JSDOM, if we're inside shadow DOM, then parentNode
+		// can be null. We just simulate parent in this case.
+		if (!parent || !parent.tagName) {
+		  parent = {
+			namespaceURI: HTML_NAMESPACE,
+			tagName: 'template'
+		  };
+		}
+
+		var tagName = stringToLowerCase(element.tagName);
+		var parentTagName = stringToLowerCase(parent.tagName);
+
+		if (element.namespaceURI === SVG_NAMESPACE) {
+		  // The only way to switch from HTML namespace to SVG
+		  // is via <svg>. If it happens via any other tag, then
+		  // it should be killed.
+		  if (parent.namespaceURI === HTML_NAMESPACE) {
+			return tagName === 'svg';
+		  }
+
+		  // The only way to switch from MathML to SVG is via
+		  // svg if parent is either <annotation-xml> or MathML
+		  // text integration points.
+		  if (parent.namespaceURI === MATHML_NAMESPACE) {
+			return tagName === 'svg' && (parentTagName === 'annotation-xml' || MATHML_TEXT_INTEGRATION_POINTS[parentTagName]);
+		  }
+
+		  // We only allow elements that are defined in SVG
+		  // spec. All others are disallowed in SVG namespace.
+		  return Boolean(ALL_SVG_TAGS[tagName]);
+		}
+
+		if (element.namespaceURI === MATHML_NAMESPACE) {
+		  // The only way to switch from HTML namespace to MathML
+		  // is via <math>. If it happens via any other tag, then
+		  // it should be killed.
+		  if (parent.namespaceURI === HTML_NAMESPACE) {
+			return tagName === 'math';
+		  }
+
+		  // The only way to switch from SVG to MathML is via
+		  // <math> and HTML integration points
+		  if (parent.namespaceURI === SVG_NAMESPACE) {
+			return tagName === 'math' && HTML_INTEGRATION_POINTS[parentTagName];
+		  }
+
+		  // We only allow elements that are defined in MathML
+		  // spec. All others are disallowed in MathML namespace.
+		  return Boolean(ALL_MATHML_TAGS[tagName]);
+		}
+
+		if (element.namespaceURI === HTML_NAMESPACE) {
+		  // The only way to switch from SVG to HTML is via
+		  // HTML integration points, and from MathML to HTML
+		  // is via MathML text integration points
+		  if (parent.namespaceURI === SVG_NAMESPACE && !HTML_INTEGRATION_POINTS[parentTagName]) {
+			return false;
+		  }
+
+		  if (parent.namespaceURI === MATHML_NAMESPACE && !MATHML_TEXT_INTEGRATION_POINTS[parentTagName]) {
+			return false;
+		  }
+
+		  // Certain elements are allowed in both SVG and HTML
+		  // namespace. We need to specify them explicitly
+		  // so that they don't get erronously deleted from
+		  // HTML namespace.
+		  var commonSvgAndHTMLElements = addToSet({}, ['title', 'style', 'font', 'a', 'script']);
+
+		  // We disallow tags that are specific for MathML
+		  // or SVG and should never appear in HTML namespace
+		  return !ALL_MATHML_TAGS[tagName] && (commonSvgAndHTMLElements[tagName] || !ALL_SVG_TAGS[tagName]);
+		}
+
+		// The code should never reach this place (this means
+		// that the element somehow got namespace that is not
+		// HTML, SVG or MathML). Return false just in case.
+		return false;
 	  };
 
 	  /**
@@ -4758,16 +4754,16 @@
 	   * @param  {Node} node a DOM node
 	   */
 	  var _forceRemove = function _forceRemove(node) {
-	    arrayPush(DOMPurify.removed, { element: node });
-	    try {
-	      node.parentNode.removeChild(node);
-	    } catch (_) {
-	      try {
-	        node.outerHTML = emptyHTML;
-	      } catch (_) {
-	        node.remove();
-	      }
-	    }
+		arrayPush(DOMPurify.removed, { element: node });
+		try {
+		  node.parentNode.removeChild(node);
+		} catch (_) {
+		  try {
+			node.outerHTML = emptyHTML;
+		  } catch (_) {
+			node.remove();
+		  }
+		}
 	  };
 
 	  /**
@@ -4777,19 +4773,19 @@
 	   * @param  {Node} node a DOM node
 	   */
 	  var _removeAttribute = function _removeAttribute(name, node) {
-	    try {
-	      arrayPush(DOMPurify.removed, {
-	        attribute: node.getAttributeNode(name),
-	        from: node
-	      });
-	    } catch (_) {
-	      arrayPush(DOMPurify.removed, {
-	        attribute: null,
-	        from: node
-	      });
-	    }
-
-	    node.removeAttribute(name);
+		try {
+		  arrayPush(DOMPurify.removed, {
+			attribute: node.getAttributeNode(name),
+			from: node
+		  });
+		} catch (_) {
+		  arrayPush(DOMPurify.removed, {
+			attribute: null,
+			from: node
+		  });
+		}
+
+		node.removeAttribute(name);
 	  };
 
 	  /**
@@ -4799,40 +4795,40 @@
 	   * @return {Document} a DOM, filled with the dirty markup
 	   */
 	  var _initDocument = function _initDocument(dirty) {
-	    /* Create a HTML document */
-	    var doc = void 0;
-	    var leadingWhitespace = void 0;
-
-	    if (FORCE_BODY) {
-	      dirty = '<remove></remove>' + dirty;
-	    } else {
-	      /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
-	      var matches = stringMatch(dirty, /^[\r\n\t ]+/);
-	      leadingWhitespace = matches && matches[0];
-	    }
-
-	    var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
-	    /* Use the DOMParser API by default, fallback later if needs be */
-	    try {
-	      doc = new DOMParser().parseFromString(dirtyPayload, 'text/html');
-	    } catch (_) {}
-
-	    /* Use createHTMLDocument in case DOMParser is not available */
-	    if (!doc || !doc.documentElement) {
-	      doc = implementation.createHTMLDocument('');
-	      var _doc = doc,
-	          body = _doc.body;
-
-	      body.parentNode.removeChild(body.parentNode.firstElementChild);
-	      body.outerHTML = dirtyPayload;
-	    }
-
-	    if (dirty && leadingWhitespace) {
-	      doc.body.insertBefore(document.createTextNode(leadingWhitespace), doc.body.childNodes[0] || null);
-	    }
-
-	    /* Work on whole document or just its body */
-	    return getElementsByTagName.call(doc, WHOLE_DOCUMENT ? 'html' : 'body')[0];
+		/* Create a HTML document */
+		var doc = void 0;
+		var leadingWhitespace = void 0;
+
+		if (FORCE_BODY) {
+		  dirty = '<remove></remove>' + dirty;
+		} else {
+		  /* If FORCE_BODY isn't used, leading whitespace needs to be preserved manually */
+		  var matches = stringMatch(dirty, /^[\r\n\t ]+/);
+		  leadingWhitespace = matches && matches[0];
+		}
+
+		var dirtyPayload = trustedTypesPolicy ? trustedTypesPolicy.createHTML(dirty) : dirty;
+		/* Use the DOMParser API by default, fallback later if needs be */
+		try {
+		  doc = new DOMParser().parseFromString(dirtyPayload, 'text/html');
+		} catch (_) {}
+
+		/* Use createHTMLDocument in case DOMParser is not available */
+		if (!doc || !doc.documentElement) {
+		  doc = implementation.createHTMLDocument('');
+		  var _doc = doc,
+			  body = _doc.body;
+
+		  body.parentNode.removeChild(body.parentNode.firstElementChild);
+		  body.outerHTML = dirtyPayload;
+		}
+
+		if (dirty && leadingWhitespace) {
+		  doc.body.insertBefore(document.createTextNode(leadingWhitespace), doc.body.childNodes[0] || null);
+		}
+
+		/* Work on whole document or just its body */
+		return doc.getElementsByTagName(WHOLE_DOCUMENT ? 'html' : 'body')[0];
 	  };
 
 	  /**
@@ -4842,9 +4838,15 @@
 	   * @return {Iterator} iterator instance
 	   */
 	  var _createIterator = function _createIterator(root) {
-	    return createNodeIterator.call(root.ownerDocument || root, root, NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT, function () {
-	      return NodeFilter.FILTER_ACCEPT;
-	    }, false);
+		let whatToShow = NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT;
+		let filter = function () {
+			return NodeFilter.FILTER_ACCEPT;
+		};
+		if(root.ownerDocument) {
+			return root.ownerDocument.createNodeIterator(root,whatToShow,filter,false);
+		} else {
+			return root.createNodeIterator(root,whatToShow,filter,false);
+		}
 	  };
 
 	  /**
@@ -4854,15 +4856,15 @@
 	   * @return {Boolean} true if clobbered, false if safe
 	   */
 	  var _isClobbered = function _isClobbered(elm) {
-	    if (elm instanceof Text || elm instanceof Comment) {
-	      return false;
-	    }
-
-	    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') {
-	      return true;
-	    }
-
-	    return false;
+		if (elm instanceof Text || elm instanceof Comment) {
+		  return false;
+		}
+
+		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') {
+		  return true;
+		}
+
+		return false;
 	  };
 
 	  /**
@@ -4872,7 +4874,7 @@
 	   * @return {Boolean} true is object is a DOM node
 	   */
 	  var _isNode = function _isNode(object) {
-	    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';
+		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';
 	  };
 
 	  /**
@@ -4884,13 +4886,13 @@
 	   * @param  {Object} data additional hook parameters
 	   */
 	  var _executeHook = function _executeHook(entryPoint, currentNode, data) {
-	    if (!hooks[entryPoint]) {
-	      return;
-	    }
-
-	    arrayForEach(hooks[entryPoint], function (hook) {
-	      hook.call(DOMPurify, currentNode, data, CONFIG);
-	    });
+		if (!hooks[entryPoint]) {
+		  return;
+		}
+
+		arrayForEach(hooks[entryPoint], function (hook) {
+		  hook.call(DOMPurify, currentNode, data, CONFIG);
+		});
 	  };
 
 	  /**
@@ -4904,81 +4906,81 @@
 	   * @return  {Boolean} true if node was killed, false if left alive
 	   */
 	  var _sanitizeElements = function _sanitizeElements(currentNode) {
-	    var content = void 0;
-
-	    /* Execute a hook if present */
-	    _executeHook('beforeSanitizeElements', currentNode, null);
-
-	    /* Check if element is clobbered or can clobber */
-	    if (_isClobbered(currentNode)) {
-	      _forceRemove(currentNode);
-	      return true;
-	    }
-
-	    /* Check if tagname contains Unicode */
-	    if (stringMatch(currentNode.nodeName, /[\u0080-\uFFFF]/)) {
-	      _forceRemove(currentNode);
-	      return true;
-	    }
-
-	    /* Now let's check the element's type and name */
-	    var tagName = stringToLowerCase(currentNode.nodeName);
-
-	    /* Execute a hook if present */
-	    _executeHook('uponSanitizeElement', currentNode, {
-	      tagName: tagName,
-	      allowedTags: ALLOWED_TAGS
-	    });
-
-	    /* Detect mXSS attempts abusing namespace confusion */
-	    if (!_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
-	      _forceRemove(currentNode);
-	      return true;
-	    }
-
-	    /* Remove element if anything forbids its presence */
-	    if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
-	      /* Keep content except for bad-listed elements */
-	      if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
-	        var parentNode = getParentNode(currentNode);
-	        var childNodes = getChildNodes(currentNode);
-	        var childCount = childNodes.length;
-	        for (var i = childCount - 1; i >= 0; --i) {
-	          parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));
-	        }
-	      }
-
-	      _forceRemove(currentNode);
-	      return true;
-	    }
-
-	    /* Check whether element has a valid namespace */
-	    if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
-	      _forceRemove(currentNode);
-	      return true;
-	    }
-
-	    if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)) {
-	      _forceRemove(currentNode);
-	      return true;
-	    }
-
-	    /* Sanitize element content to be template-safe */
-	    if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
-	      /* Get the element's text content */
-	      content = currentNode.textContent;
-	      content = stringReplace(content, MUSTACHE_EXPR$$1, ' ');
-	      content = stringReplace(content, ERB_EXPR$$1, ' ');
-	      if (currentNode.textContent !== content) {
-	        arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() });
-	        currentNode.textContent = content;
-	      }
-	    }
-
-	    /* Execute a hook if present */
-	    _executeHook('afterSanitizeElements', currentNode, null);
-
-	    return false;
+		var content = void 0;
+
+		/* Execute a hook if present */
+		_executeHook('beforeSanitizeElements', currentNode, null);
+
+		/* Check if element is clobbered or can clobber */
+		if (_isClobbered(currentNode)) {
+		  _forceRemove(currentNode);
+		  return true;
+		}
+
+		/* Check if tagname contains Unicode */
+		if (stringMatch(currentNode.nodeName, /[\u0080-\uFFFF]/)) {
+		  _forceRemove(currentNode);
+		  return true;
+		}
+
+		/* Now let's check the element's type and name */
+		var tagName = stringToLowerCase(currentNode.nodeName);
+
+		/* Execute a hook if present */
+		_executeHook('uponSanitizeElement', currentNode, {
+		  tagName: tagName,
+		  allowedTags: ALLOWED_TAGS
+		});
+
+		/* Detect mXSS attempts abusing namespace confusion */
+		if (!_isNode(currentNode.firstElementChild) && (!_isNode(currentNode.content) || !_isNode(currentNode.content.firstElementChild)) && regExpTest(/<[/\w]/g, currentNode.innerHTML) && regExpTest(/<[/\w]/g, currentNode.textContent)) {
+		  _forceRemove(currentNode);
+		  return true;
+		}
+
+		/* Remove element if anything forbids its presence */
+		if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
+		  /* Keep content except for bad-listed elements */
+		  if (KEEP_CONTENT && !FORBID_CONTENTS[tagName]) {
+			var parentNode = getParentNode(currentNode);
+			var childNodes = getChildNodes(currentNode);
+			var childCount = childNodes.length;
+			for (var i = childCount - 1; i >= 0; --i) {
+			  parentNode.insertBefore(cloneNode(childNodes[i], true), getNextSibling(currentNode));
+			}
+		  }
+
+		  _forceRemove(currentNode);
+		  return true;
+		}
+
+		/* Check whether element has a valid namespace */
+		if (currentNode instanceof Element && !_checkValidNamespace(currentNode)) {
+		  _forceRemove(currentNode);
+		  return true;
+		}
+
+		if ((tagName === 'noscript' || tagName === 'noembed') && regExpTest(/<\/no(script|embed)/i, currentNode.innerHTML)) {
+		  _forceRemove(currentNode);
+		  return true;
+		}
+
+		/* Sanitize element content to be template-safe */
+		if (SAFE_FOR_TEMPLATES && currentNode.nodeType === 3) {
+		  /* Get the element's text content */
+		  content = currentNode.textContent;
+		  content = stringReplace(content, MUSTACHE_EXPR$$1, ' ');
+		  content = stringReplace(content, ERB_EXPR$$1, ' ');
+		  if (currentNode.textContent !== content) {
+			arrayPush(DOMPurify.removed, { element: currentNode.cloneNode() });
+			currentNode.textContent = content;
+		  }
+		}
+
+		/* Execute a hook if present */
+		_executeHook('afterSanitizeElements', currentNode, null);
+
+		return false;
 	  };
 
 	  /**
@@ -4991,24 +4993,24 @@
 	   */
 	  // eslint-disable-next-line complexity
 	  var _isValidAttribute = function _isValidAttribute(lcTag, lcName, value) {
-	    /* Make sure attribute cannot clobber */
-	    if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
-	      return false;
-	    }
-
-	    /* Allow valid data-* attributes: At least one character after "-"
-	        (https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
-	        XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
-	        We don't need to check the value; it's always URI safe. */
-	    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]) {
-	      return false;
-
-	      /* Check value is safe. First, is attr inert? If so, is safe */
-	    } 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 {
-	      return false;
-	    }
-
-	    return true;
+		/* Make sure attribute cannot clobber */
+		if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
+		  return false;
+		}
+
+		/* Allow valid data-* attributes: At least one character after "-"
+			(https://html.spec.whatwg.org/multipage/dom.html#embedding-custom-non-visible-data-with-the-data-*-attributes)
+			XML-compatible (https://html.spec.whatwg.org/multipage/infrastructure.html#xml-compatible and http://www.w3.org/TR/xml/#d0e804)
+			We don't need to check the value; it's always URI safe. */
+		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]) {
+		  return false;
+
+		  /* Check value is safe. First, is attr inert? If so, is safe */
+		} 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 {
+		  return false;
+		}
+
+		return true;
 	  };
 
 	  /**
@@ -5022,92 +5024,92 @@
 	   * @param  {Node} currentNode to sanitize
 	   */
 	  var _sanitizeAttributes = function _sanitizeAttributes(currentNode) {
-	    var attr = void 0;
-	    var value = void 0;
-	    var lcName = void 0;
-	    var l = void 0;
-	    /* Execute a hook if present */
-	    _executeHook('beforeSanitizeAttributes', currentNode, null);
-
-	    var attributes = currentNode.attributes;
-
-	    /* Check if we have attributes; if not we might have a text node */
-
-	    if (!attributes) {
-	      return;
-	    }
-
-	    var hookEvent = {
-	      attrName: '',
-	      attrValue: '',
-	      keepAttr: true,
-	      allowedAttributes: ALLOWED_ATTR
-	    };
-	    l = attributes.length;
-
-	    /* Go backwards over all attributes; safely remove bad ones */
-	    while (l--) {
-	      attr = attributes[l];
-	      var _attr = attr,
-	          name = _attr.name,
-	          namespaceURI = _attr.namespaceURI;
-
-	      value = stringTrim(attr.value);
-	      lcName = stringToLowerCase(name);
-
-	      /* Execute a hook if present */
-	      hookEvent.attrName = lcName;
-	      hookEvent.attrValue = value;
-	      hookEvent.keepAttr = true;
-	      hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
-	      _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
-	      value = hookEvent.attrValue;
-	      /* Did the hooks approve of the attribute? */
-	      if (hookEvent.forceKeepAttr) {
-	        continue;
-	      }
-
-	      /* Remove attribute */
-	      _removeAttribute(name, currentNode);
-
-	      /* Did the hooks approve of the attribute? */
-	      if (!hookEvent.keepAttr) {
-	        continue;
-	      }
-
-	      /* Work around a security issue in jQuery 3.0 */
-	      if (regExpTest(/\/>/i, value)) {
-	        _removeAttribute(name, currentNode);
-	        continue;
-	      }
-
-	      /* Sanitize attribute content to be template-safe */
-	      if (SAFE_FOR_TEMPLATES) {
-	        value = stringReplace(value, MUSTACHE_EXPR$$1, ' ');
-	        value = stringReplace(value, ERB_EXPR$$1, ' ');
-	      }
-
-	      /* Is `value` valid for this attribute? */
-	      var lcTag = currentNode.nodeName.toLowerCase();
-	      if (!_isValidAttribute(lcTag, lcName, value)) {
-	        continue;
-	      }
-
-	      /* Handle invalid data-* attribute set by try-catching it */
-	      try {
-	        if (namespaceURI) {
-	          currentNode.setAttributeNS(namespaceURI, name, value);
-	        } else {
-	          /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
-	          currentNode.setAttribute(name, value);
-	        }
-
-	        arrayPop(DOMPurify.removed);
-	      } catch (_) {}
-	    }
-
-	    /* Execute a hook if present */
-	    _executeHook('afterSanitizeAttributes', currentNode, null);
+		var attr = void 0;
+		var value = void 0;
+		var lcName = void 0;
+		var l = void 0;
+		/* Execute a hook if present */
+		_executeHook('beforeSanitizeAttributes', currentNode, null);
+
+		var attributes = currentNode.attributes;
+
+		/* Check if we have attributes; if not we might have a text node */
+
+		if (!attributes) {
+		  return;
+		}
+
+		var hookEvent = {
+		  attrName: '',
+		  attrValue: '',
+		  keepAttr: true,
+		  allowedAttributes: ALLOWED_ATTR
+		};
+		l = attributes.length;
+
+		/* Go backwards over all attributes; safely remove bad ones */
+		while (l--) {
+		  attr = attributes[l];
+		  var _attr = attr,
+			  name = _attr.name,
+			  namespaceURI = _attr.namespaceURI;
+
+		  value = stringTrim(attr.value);
+		  lcName = stringToLowerCase(name);
+
+		  /* Execute a hook if present */
+		  hookEvent.attrName = lcName;
+		  hookEvent.attrValue = value;
+		  hookEvent.keepAttr = true;
+		  hookEvent.forceKeepAttr = undefined; // Allows developers to see this is a property they can set
+		  _executeHook('uponSanitizeAttribute', currentNode, hookEvent);
+		  value = hookEvent.attrValue;
+		  /* Did the hooks approve of the attribute? */
+		  if (hookEvent.forceKeepAttr) {
+			continue;
+		  }
+
+		  /* Remove attribute */
+		  _removeAttribute(name, currentNode);
+
+		  /* Did the hooks approve of the attribute? */
+		  if (!hookEvent.keepAttr) {
+			continue;
+		  }
+
+		  /* Work around a security issue in jQuery 3.0 */
+		  if (regExpTest(/\/>/i, value)) {
+			_removeAttribute(name, currentNode);
+			continue;
+		  }
+
+		  /* Sanitize attribute content to be template-safe */
+		  if (SAFE_FOR_TEMPLATES) {
+			value = stringReplace(value, MUSTACHE_EXPR$$1, ' ');
+			value = stringReplace(value, ERB_EXPR$$1, ' ');
+		  }
+
+		  /* Is `value` valid for this attribute? */
+		  var lcTag = currentNode.nodeName.toLowerCase();
+		  if (!_isValidAttribute(lcTag, lcName, value)) {
+			continue;
+		  }
+
+		  /* Handle invalid data-* attribute set by try-catching it */
+		  try {
+			if (namespaceURI) {
+			  currentNode.setAttributeNS(namespaceURI, name, value);
+			} else {
+			  /* Fallback to setAttribute() for browser-unrecognized namespaces e.g. "x-schema". */
+			  currentNode.setAttribute(name, value);
+			}
+
+			arrayPop(DOMPurify.removed);
+		  } catch (_) {}
+		}
+
+		/* Execute a hook if present */
+		_executeHook('afterSanitizeAttributes', currentNode, null);
 	  };
 
 	  /**
@@ -5116,32 +5118,32 @@
 	   * @param  {DocumentFragment} fragment to iterate over recursively
 	   */
 	  var _sanitizeShadowDOM = function _sanitizeShadowDOM(fragment) {
-	    var shadowNode = void 0;
-	    var shadowIterator = _createIterator(fragment);
-
-	    /* Execute a hook if present */
-	    _executeHook('beforeSanitizeShadowDOM', fragment, null);
-
-	    while (shadowNode = shadowIterator.nextNode()) {
-	      /* Execute a hook if present */
-	      _executeHook('uponSanitizeShadowNode', shadowNode, null);
-
-	      /* Sanitize tags and elements */
-	      if (_sanitizeElements(shadowNode)) {
-	        continue;
-	      }
-
-	      /* Deep shadow DOM detected */
-	      if (shadowNode.content instanceof DocumentFragment) {
-	        _sanitizeShadowDOM(shadowNode.content);
-	      }
-
-	      /* Check attributes, sanitize if necessary */
-	      _sanitizeAttributes(shadowNode);
-	    }
-
-	    /* Execute a hook if present */
-	    _executeHook('afterSanitizeShadowDOM', fragment, null);
+		var shadowNode = void 0;
+		var shadowIterator = _createIterator(fragment);
+
+		/* Execute a hook if present */
+		_executeHook('beforeSanitizeShadowDOM', fragment, null);
+
+		while (shadowNode = shadowIterator.nextNode()) {
+		  /* Execute a hook if present */
+		  _executeHook('uponSanitizeShadowNode', shadowNode, null);
+
+		  /* Sanitize tags and elements */
+		  if (_sanitizeElements(shadowNode)) {
+			continue;
+		  }
+
+		  /* Deep shadow DOM detected */
+		  if (shadowNode.content instanceof DocumentFragment) {
+			_sanitizeShadowDOM(shadowNode.content);
+		  }
+
+		  /* Check attributes, sanitize if necessary */
+		  _sanitizeAttributes(shadowNode);
+		}
+
+		/* Execute a hook if present */
+		_executeHook('afterSanitizeShadowDOM', fragment, null);
 	  };
 
 	  /**
@@ -5153,164 +5155,168 @@
 	   */
 	  // eslint-disable-next-line complexity
 	  DOMPurify.sanitize = function (dirty, cfg) {
-	    var body = void 0;
-	    var importedNode = void 0;
-	    var currentNode = void 0;
-	    var oldNode = void 0;
-	    var returnNode = void 0;
-	    /* Make sure we have a string to sanitize.
-	      DO NOT return early, as this will return the wrong type if
-	      the user has requested a DOM object rather than a string */
-	    if (!dirty) {
-	      dirty = '<!-->';
-	    }
-
-	    /* Stringify, in case dirty is an object */
-	    if (typeof dirty !== 'string' && !_isNode(dirty)) {
-	      // eslint-disable-next-line no-negated-condition
-	      if (typeof dirty.toString !== 'function') {
-	        throw typeErrorCreate('toString is not a function');
-	      } else {
-	        dirty = dirty.toString();
-	        if (typeof dirty !== 'string') {
-	          throw typeErrorCreate('dirty is not a string, aborting');
-	        }
-	      }
-	    }
-
-	    /* Check we can run. Otherwise fall back or ignore */
-	    if (!DOMPurify.isSupported) {
-	      if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') {
-	        if (typeof dirty === 'string') {
-	          return window.toStaticHTML(dirty);
-	        }
-
-	        if (_isNode(dirty)) {
-	          return window.toStaticHTML(dirty.outerHTML);
-	        }
-	      }
-
-	      return dirty;
-	    }
-
-	    /* Assign config vars */
-	    if (!SET_CONFIG) {
-	      _parseConfig(cfg);
-	    }
-
-	    /* Clean up removed elements */
-	    DOMPurify.removed = [];
-
-	    /* Check if dirty is correctly typed for IN_PLACE */
-	    if (typeof dirty === 'string') {
-	      IN_PLACE = false;
-	    }
-
-	    if (IN_PLACE) ; else if (dirty instanceof Node) {
-	      /* If dirty is a DOM element, append to an empty document to avoid
-	         elements being stripped by the parser */
-	      body = _initDocument('<!---->');
-	      importedNode = body.ownerDocument.importNode(dirty, true);
-	      if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
-	        /* Node is already a body, use as is */
-	        body = importedNode;
-	      } else if (importedNode.nodeName === 'HTML') {
-	        body = importedNode;
-	      } else {
-	        // eslint-disable-next-line unicorn/prefer-node-append
-	        body.appendChild(importedNode);
-	      }
-	    } else {
-	      /* Exit directly if we have nothing to do */
-	      if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
-	      // eslint-disable-next-line unicorn/prefer-includes
-	      dirty.indexOf('<') === -1) {
-	        return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
-	      }
-
-	      /* Initialize the document to work on */
-	      body = _initDocument(dirty);
-
-	      /* Check we have a DOM node from the data */
-	      if (!body) {
-	        return RETURN_DOM ? null : emptyHTML;
-	      }
-	    }
-
-	    /* Remove first element node (ours) if FORCE_BODY is set */
-	    if (body && FORCE_BODY) {
-	      _forceRemove(body.firstChild);
-	    }
-
-	    /* Get node iterator */
-	    var nodeIterator = _createIterator(IN_PLACE ? dirty : body);
-
-	    /* Now start iterating over the created document */
-	    while (currentNode = nodeIterator.nextNode()) {
-	      /* Fix IE's strange behavior with manipulated textNodes #89 */
-	      if (currentNode.nodeType === 3 && currentNode === oldNode) {
-	        continue;
-	      }
-
-	      /* Sanitize tags and elements */
-	      if (_sanitizeElements(currentNode)) {
-	        continue;
-	      }
-
-	      /* Shadow DOM detected, sanitize it */
-	      if (currentNode.content instanceof DocumentFragment) {
-	        _sanitizeShadowDOM(currentNode.content);
-	      }
-
-	      /* Check attributes, sanitize if necessary */
-	      _sanitizeAttributes(currentNode);
-
-	      oldNode = currentNode;
-	    }
-
-	    oldNode = null;
-
-	    /* If we sanitized `dirty` in-place, return it. */
-	    if (IN_PLACE) {
-	      return dirty;
-	    }
-
-	    /* Return sanitized string or DOM */
-	    if (RETURN_DOM) {
-	      if (RETURN_DOM_FRAGMENT) {
-	        returnNode = createDocumentFragment.call(body.ownerDocument);
-
-	        while (body.firstChild) {
-	          // eslint-disable-next-line unicorn/prefer-node-append
-	          returnNode.appendChild(body.firstChild);
-	        }
-	      } else {
-	        returnNode = body;
-	      }
-
-	      if (RETURN_DOM_IMPORT) {
-	        /*
-	          AdoptNode() is not used because internal state is not reset
-	          (e.g. the past names map of a HTMLFormElement), this is safe
-	          in theory but we would rather not risk another attack vector.
-	          The state that is cloned by importNode() is explicitly defined
-	          by the specs.
-	        */
-	        returnNode = importNode.call(originalDocument, returnNode, true);
-	      }
-
-	      return returnNode;
-	    }
-
-	    var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
-
-	    /* Sanitize final string template-safe */
-	    if (SAFE_FOR_TEMPLATES) {
-	      serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$$1, ' ');
-	      serializedHTML = stringReplace(serializedHTML, ERB_EXPR$$1, ' ');
-	    }
-
-	    return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
+		var body = void 0;
+		var importedNode = void 0;
+		var currentNode = void 0;
+		var oldNode = void 0;
+		var returnNode = void 0;
+		/* Make sure we have a string to sanitize.
+		  DO NOT return early, as this will return the wrong type if
+		  the user has requested a DOM object rather than a string */
+		if (!dirty) {
+		  dirty = '<!-->';
+		}
+
+		/* Stringify, in case dirty is an object */
+		if (typeof dirty !== 'string' && !_isNode(dirty)) {
+		  // eslint-disable-next-line no-negated-condition
+		  if (typeof dirty.toString !== 'function') {
+			throw typeErrorCreate('toString is not a function');
+		  } else {
+			dirty = dirty.toString();
+			if (typeof dirty !== 'string') {
+			  throw typeErrorCreate('dirty is not a string, aborting');
+			}
+		  }
+		}
+
+		/* Check we can run. Otherwise fall back or ignore */
+		if (!DOMPurify.isSupported) {
+		  if (_typeof(window.toStaticHTML) === 'object' || typeof window.toStaticHTML === 'function') {
+			if (typeof dirty === 'string') {
+			  return window.toStaticHTML(dirty);
+			}
+
+			if (_isNode(dirty)) {
+			  return window.toStaticHTML(dirty.outerHTML);
+			}
+		  }
+
+		  return dirty;
+		}
+
+		/* Assign config vars */
+		if (!SET_CONFIG) {
+		  _parseConfig(cfg);
+		}
+
+		/* Clean up removed elements */
+		DOMPurify.removed = [];
+
+		/* Check if dirty is correctly typed for IN_PLACE */
+		if (typeof dirty === 'string') {
+		  IN_PLACE = false;
+		}
+
+		if (IN_PLACE) ; else if (dirty instanceof Node) {
+		  /* If dirty is a DOM element, append to an empty document to avoid
+			 elements being stripped by the parser */
+		  body = _initDocument('<!---->');
+		  importedNode = body.ownerDocument.importNode(dirty, true);
+		  if (importedNode.nodeType === 1 && importedNode.nodeName === 'BODY') {
+			/* Node is already a body, use as is */
+			body = importedNode;
+		  } else if (importedNode.nodeName === 'HTML') {
+			body = importedNode;
+		  } else {
+			// eslint-disable-next-line unicorn/prefer-node-append
+			body.appendChild(importedNode);
+		  }
+		} else {
+		  /* Exit directly if we have nothing to do */
+		  if (!RETURN_DOM && !SAFE_FOR_TEMPLATES && !WHOLE_DOCUMENT &&
+		  // eslint-disable-next-line unicorn/prefer-includes
+		  dirty.indexOf('<') === -1) {
+			return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(dirty) : dirty;
+		  }
+
+		  /* Initialize the document to work on */
+		  body = _initDocument(dirty);
+
+		  /* Check we have a DOM node from the data */
+		  if (!body) {
+			return RETURN_DOM ? null : emptyHTML;
+		  }
+		}
+
+		/* Remove first element node (ours) if FORCE_BODY is set */
+		if (body && FORCE_BODY) {
+		  _forceRemove(body.firstChild);
+		}
+
+		/* Get node iterator */
+		var nodeIterator = _createIterator(IN_PLACE ? dirty : body);
+
+		/* Now start iterating over the created document */
+		while (currentNode = nodeIterator.nextNode()) {
+		  /* Fix IE's strange behavior with manipulated textNodes #89 */
+		  if (currentNode.nodeType === 3 && currentNode === oldNode) {
+			continue;
+		  }
+
+		  /* Sanitize tags and elements */
+		  if (_sanitizeElements(currentNode)) {
+			continue;
+		  }
+
+		  /* Shadow DOM detected, sanitize it */
+		  if (currentNode.content instanceof DocumentFragment) {
+			_sanitizeShadowDOM(currentNode.content);
+		  }
+
+		  /* Check attributes, sanitize if necessary */
+		  _sanitizeAttributes(currentNode);
+
+		  oldNode = currentNode;
+		}
+
+		oldNode = null;
+
+		/* If we sanitized `dirty` in-place, return it. */
+		if (IN_PLACE) {
+		  return dirty;
+		}
+
+		/* Return sanitized string or DOM */
+		if (RETURN_DOM) {
+		  if (RETURN_DOM_FRAGMENT) {
+			//returnNode = createDocumentFragment.call(body.ownerDocument);
+			// untested
+			returnNode = body.ownerDocument.createDocumentFragment.call();
+
+			while (body.firstChild) {
+			  // eslint-disable-next-line unicorn/prefer-node-append
+			  returnNode.appendChild(body.firstChild);
+			}
+		  } else {
+			returnNode = body;
+		  }
+
+		  if (RETURN_DOM_IMPORT) {
+			/*
+			  AdoptNode() is not used because internal state is not reset
+			  (e.g. the past names map of a HTMLFormElement), this is safe
+			  in theory but we would rather not risk another attack vector.
+			  The state that is cloned by importNode() is explicitly defined
+			  by the specs.
+			*/
+			//returnNode = importNode.call(originalDocument, returnNode, true);
+			// untested
+			returnNode = originalDocument.importNode(returnNode, true);
+		  }
+
+		  return returnNode;
+		}
+
+		var serializedHTML = WHOLE_DOCUMENT ? body.outerHTML : body.innerHTML;
+
+		/* Sanitize final string template-safe */
+		if (SAFE_FOR_TEMPLATES) {
+		  serializedHTML = stringReplace(serializedHTML, MUSTACHE_EXPR$$1, ' ');
+		  serializedHTML = stringReplace(serializedHTML, ERB_EXPR$$1, ' ');
+		}
+
+		return trustedTypesPolicy && RETURN_TRUSTED_TYPE ? trustedTypesPolicy.createHTML(serializedHTML) : serializedHTML;
 	  };
 
 	  /**
@@ -5320,8 +5326,8 @@
 	   * @param {Object} cfg configuration object
 	   */
 	  DOMPurify.setConfig = function (cfg) {
-	    _parseConfig(cfg);
-	    SET_CONFIG = true;
+		_parseConfig(cfg);
+		SET_CONFIG = true;
 	  };
 
 	  /**
@@ -5330,8 +5336,8 @@
 	   *
 	   */
 	  DOMPurify.clearConfig = function () {
-	    CONFIG = null;
-	    SET_CONFIG = false;
+		CONFIG = null;
+		SET_CONFIG = false;
 	  };
 
 	  /**
@@ -5345,14 +5351,14 @@
 	   * @return {Boolean} Returns true if `value` is valid. Otherwise, returns false.
 	   */
 	  DOMPurify.isValidAttribute = function (tag, attr, value) {
-	    /* Initialize shared config vars if necessary. */
-	    if (!CONFIG) {
-	      _parseConfig({});
-	    }
-
-	    var lcTag = stringToLowerCase(tag);
-	    var lcName = stringToLowerCase(attr);
-	    return _isValidAttribute(lcTag, lcName, value);
+		/* Initialize shared config vars if necessary. */
+		if (!CONFIG) {
+		  _parseConfig({});
+		}
+
+		var lcTag = stringToLowerCase(tag);
+		var lcName = stringToLowerCase(attr);
+		return _isValidAttribute(lcTag, lcName, value);
 	  };
 
 	  /**
@@ -5363,12 +5369,12 @@
 	   * @param {Function} hookFunction function to execute
 	   */
 	  DOMPurify.addHook = function (entryPoint, hookFunction) {
-	    if (typeof hookFunction !== 'function') {
-	      return;
-	    }
-
-	    hooks[entryPoint] = hooks[entryPoint] || [];
-	    arrayPush(hooks[entryPoint], hookFunction);
+		if (typeof hookFunction !== 'function') {
+		  return;
+		}
+
+		hooks[entryPoint] = hooks[entryPoint] || [];
+		arrayPush(hooks[entryPoint], hookFunction);
 	  };
 
 	  /**
@@ -5379,9 +5385,9 @@
 	   * @param {String} entryPoint entry point for the hook to remove
 	   */
 	  DOMPurify.removeHook = function (entryPoint) {
-	    if (hooks[entryPoint]) {
-	      arrayPop(hooks[entryPoint]);
-	    }
+		if (hooks[entryPoint]) {
+		  arrayPop(hooks[entryPoint]);
+		}
 	  };
 
 	  /**
@@ -5391,9 +5397,9 @@
 	   * @param  {String} entryPoint entry point for the hooks to remove
 	   */
 	  DOMPurify.removeHooks = function (entryPoint) {
-	    if (hooks[entryPoint]) {
-	      hooks[entryPoint] = [];
-	    }
+		if (hooks[entryPoint]) {
+		  hooks[entryPoint] = [];
+		}
 	  };
 
 	  /**
@@ -5402,7 +5408,7 @@
 	   *
 	   */
 	  DOMPurify.removeAllHooks = function () {
-	    hooks = {};
+		hooks = {};
 	  };
 
 	  return DOMPurify;