phtml
Version:
A tool for transforming HTML with JavaScript
1 lines • 121 kB
Source Map (JSON)
{"version":3,"file":"index.mjs","sources":["src/AttributeList.js","src/visit.js","src/Node.js","src/Comment.js","src/Container.js","src/Doctype.js","src/Fragment.js","src/Text.js","src/normalize.js","src/NodeList.js","src/parseHTMLTreeAdapter.js","src/parseHTMLTokenizer.js","src/parseHTML.js","src/Result.js","src/Element.js","src/Plugin.js","src/index.js"],"sourcesContent":["/**\n* @name AttributeList\n* @class\n* @extends Array\n* @classdesc Return a new list of {@link Element} attributes.\n* @param {...Array|AttributeList|Object} attrs - An array or object of attributes.\n* @returns {AttributeList}\n* @example\n* new AttributeList([{ name: 'class', value: 'foo' }, { name: 'id', value: 'bar' }])\n* @example\n* new AttributeList({ class: 'foo', id: 'bar' })\n*/\nclass AttributeList extends Array {\n\tconstructor (attrs) {\n\t\tsuper();\n\n\t\tif (attrs === Object(attrs)) {\n\t\t\tthis.push(...getAttributeListArray(attrs));\n\t\t}\n\t}\n\n\t/**\n\t* Add an attribute or attributes to the current {@link AttributeList}.\n\t* @param {Array|Object|RegExp|String} name - The attribute to remove.\n\t* @param {String} [value] - The value of the attribute being added.\n\t* @returns {Boolean} - Whether the attribute or attributes were added to the current {@link AttributeList}.\n\t* @example <caption>Add an empty \"id\" attribute.</caption>\n\t* attrs.add('id')\n\t* @example <caption>Add an \"id\" attribute with a value of \"bar\".</caption>\n\t* attrs.add({ id: 'bar' })\n\t* @example\n\t* attrs.add([{ name: 'id', value: 'bar' }])\n\t*/\n\tadd (name, ...args) {\n\t\treturn toggle(this, getAttributeListArray(name, ...args), true).attributeAdded;\n\t}\n\n\t/**\n\t* Return a new clone of the current {@link AttributeList} while conditionally applying additional attributes.\n\t* @param {...Array|AttributeList|Object} attrs - Additional attributes to be added to the new {@link AttributeList}.\n\t* @returns {Element} - The cloned Element.\n\t* @example\n\t* attrs.clone()\n\t* @example <caption>Clone the current attribute and add an \"id\" attribute with a value of \"bar\".</caption>\n\t* attrs.clone({ name: 'id', value: 'bar' })\n\t*/\n\tclone (...attrs) {\n\t\treturn new AttributeList(Array.from(this).concat(getAttributeListArray(attrs)));\n\t}\n\n\t/**\n\t* Return whether an attribute or attributes exists in the current {@link AttributeList}.\n\t* @param {String} name - The name or attribute object being accessed.\n\t* @returns {Boolean} - Whether the attribute exists.\n\t* @example <caption>Return whether there is an \"id\" attribute.</caption>\n\t* attrs.contains('id')\n\t* @example\n\t* attrs.contains({ id: 'bar' })\n\t* @example <caption>Return whether there is an \"id\" attribute with a value of \"bar\".</caption>\n\t* attrs.contains([{ name: 'id': value: 'bar' }])\n\t*/\n\tcontains (name) {\n\t\treturn this.indexOf(name) !== -1;\n\t}\n\n\t/**\n\t* Return an attribute value by name from the current {@link AttributeList}.\n\t* @description If the attribute exists with a value then a String is returned. If the attribute exists with no value then `null` is returned. If the attribute does not exist then `false` is returned.\n\t* @param {RegExp|String} name - The name of the attribute being accessed.\n\t* @returns {Boolean|Null|String} - The value of the attribute (a string or null) or false (if the attribute does not exist).\n\t* @example <caption>Return the value of \"id\" or `false`.</caption>\n\t* // <div>this element has no \"id\" attribute</div>\n\t* attrs.get('id') // returns false\n\t* // <div id>this element has an \"id\" attribute with no value</div>\n\t* attrs.get('id') // returns null\n\t* // <div id=\"\">this element has an \"id\" attribute with a value</div>\n\t* attrs.get('id') // returns ''\n\t*/\n\tget (name) {\n\t\tconst index = this.indexOf(name);\n\n\t\treturn index === -1\n\t\t\t? false\n\t\t: this[index].value;\n\t}\n\n\t/**\n\t* Return the position of an attribute by name or attribute object in the current {@link AttributeList}.\n\t* @param {Array|Object|RegExp|String} name - The attribute to locate.\n\t* @returns {Number} - The index of the attribute or -1.\n\t* @example <caption>Return the index of \"id\".</caption>\n\t* attrs.indexOf('id')\n\t* @example <caption>Return the index of /d$/.</caption>\n\t* attrs.indexOf(/d$/i)\n\t* @example <caption>Return the index of \"foo\" with a value of \"bar\".</caption>\n\t* attrs.indexOf({ foo: 'bar' })\n\t* @example <caption>Return the index of \"ariaLabel\" or \"aria-label\" matching /^open/.</caption>\n\t* attrs.indexOf({ ariaLabel: /^open/ })\n\t* @example <caption>Return the index of an attribute whose name matches `/^foo/`.</caption>\n\t* attrs.indexOf([{ name: /^foo/ })\n\t*/\n\tindexOf (name, ...args) {\n\t\treturn this.findIndex(\n\t\t\tArray.isArray(name)\n\t\t\t\t? findIndexByArray\n\t\t\t: isRegExp(name)\n\t\t\t\t? findIndexByRegExp\n\t\t\t: name === Object(name)\n\t\t\t\t? findIndexByObject\n\t\t\t: findIndexByString\n\t\t);\n\n\t\tfunction findIndexByArray (attr) {\n\t\t\treturn name.some(\n\t\t\t\tinnerAttr => (\n\t\t\t\t\t'name' in Object(innerAttr)\n\t\t\t\t\t\t? isRegExp(innerAttr.name)\n\t\t\t\t\t\t\t? innerAttr.name.test(attr.name)\n\t\t\t\t\t\t: String(innerAttr.name) === attr.name\n\t\t\t\t\t: true\n\t\t\t\t) && (\n\t\t\t\t\t'value' in Object(innerAttr)\n\t\t\t\t\t\t? isRegExp(innerAttr.value)\n\t\t\t\t\t\t\t? innerAttr.value.test(attr.value)\n\t\t\t\t\t\t: getAttributeValue(innerAttr.value) === attr.value\n\t\t\t\t\t: true\n\t\t\t\t)\n\t\t\t);\n\t\t}\n\n\t\tfunction findIndexByObject (attr) {\n\t\t\tconst innerAttr = name[attr.name] || name[toCamelCaseString(attr.name)];\n\n\t\t\treturn innerAttr\n\t\t\t\t? isRegExp(innerAttr)\n\t\t\t\t\t? innerAttr.test(attr.value)\n\t\t\t\t: attr.value === innerAttr\n\t\t\t: false;\n\t\t}\n\n\t\tfunction findIndexByRegExp (attr) {\n\t\t\treturn name.test(attr.name) && (\n\t\t\t\targs.length\n\t\t\t\t\t? isRegExp(args[0])\n\t\t\t\t\t\t? args[0].test(attr.value)\n\t\t\t\t\t: attr.value === getAttributeValue(args[0])\n\t\t\t\t: true\n\t\t\t);\n\t\t}\n\n\t\tfunction findIndexByString (attr) {\n\t\t\treturn (\n\t\t\t\tattr.name === String(name) || attr.name === toKebabCaseString(name)\n\t\t\t) && (\n\t\t\t\targs.length\n\t\t\t\t\t? isRegExp(args[0])\n\t\t\t\t\t\t? args[0].test(attr.value)\n\t\t\t\t\t: attr.value === getAttributeValue(args[0])\n\t\t\t\t: true\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t* Remove an attribute or attributes from the current {@link AttributeList}.\n\t* @param {Array|Object|RegExp|String} name - The attribute to remove.\n\t* @param {String} [value] - The value of the attribute being removed.\n\t* @returns {Boolean} - Whether the attribute or attributes were removed from the {@link AttributeList}.\n\t* @example <caption>Remove the \"id\" attribute.</caption>\n\t* attrs.remove('id')\n\t* @example <caption>Remove the \"id\" attribute when it has a value of \"bar\".</caption>\n\t* attrs.remove('id', 'bar')\n\t* @example\n\t* attrs.remove({ id: 'bar' })\n\t* @example\n\t* attrs.remove([{ name: 'id', value: 'bar' }])\n\t* @example <caption>Remove the \"id\" and \"class\" attributes.</caption>\n\t* attrs.remove(['id', 'class'])\n\t*/\n\tremove (name, ...args) {\n\t\treturn toggle(this, getAttributeListArray(name, ...args), false).attributeRemoved;\n\t}\n\n\t/**\n\t* Toggle an attribute or attributes from the current {@link AttributeList}.\n\t* @param {String|Object} name_or_attrs - The name of the attribute being toggled, or an object of attributes being toggled.\n\t* @param {String|Boolean} [value_or_force] - The value of the attribute being toggled when the first argument is not an object, or attributes should be exclusively added (true) or removed (false).\n\t* @param {Boolean} [force] - Whether attributes should be exclusively added (true) or removed (false).\n\t* @returns {Boolean} - Whether any attribute was added to the current {@link AttributeList}.\n\t* @example <caption>Toggle the \"id\" attribute.</caption>\n\t* attrs.toggle('id')\n\t* @example <caption>Toggle the \"id\" attribute with a value of \"bar\".</caption>\n\t* attrs.toggle('id', 'bar')\n\t* @example\n\t* attrs.toggle({ id: 'bar' })\n\t* @example\n\t* attrs.toggle([{ name: 'id', value: 'bar' }])\n\t*/\n\ttoggle (name, ...args) {\n\t\tconst attrs = getAttributeListArray(name, ...args);\n\t\tconst force = (\n\t\t\tname === Object(name)\n\t\t\t\t? args[0] == null ? null : Boolean(args[0])\n\t\t\t: args[1] == null ? null : Boolean(args[1])\n\t\t);\n\n\t\tconst result = toggle(this, attrs, force);\n\n\t\treturn result.attributeAdded || result.atttributeModified;\n\t}\n\n\t/**\n\t* Return the current {@link AttributeList} as a String.\n\t* @returns {String} A string version of the current {@link AttributeList}\n\t* @example\n\t* attrs.toString() // returns 'class=\"foo\" data-foo=\"bar\"'\n\t*/\n\ttoString () {\n\t\treturn this.length\n\t\t\t? `${this.map(\n\t\t\t\tattr => `${Object(attr.source).before || ' '}${attr.name}${attr.value === null ? '' : `=${Object(attr.source).quote || '\"'}${attr.value}${Object(attr.source).quote || '\"'}`}`\n\t\t\t).join('')}`\n\t\t: '';\n\t}\n\n\t/**\n\t* Return the current {@link AttributeList} as an Object.\n\t* @returns {Object} point - An object version of the current {@link AttributeList}\n\t* @example\n\t* attrs.toJSON() // returns { class: 'foo', dataFoo: 'bar' } when <x class=\"foo\" data-foo: \"bar\" />\n\t*/\n\ttoJSON () {\n\t\treturn this.reduce(\n\t\t\t(object, attr) => Object.assign(\n\t\t\t\tobject,\n\t\t\t\t{\n\t\t\t\t\t[toCamelCaseString(attr.name)]: attr.value\n\t\t\t\t}\n\t\t\t),\n\t\t\t{}\n\t\t);\n\t}\n\n\t/**\n\t* Return a new {@link AttributeList} from an array or object.\n\t* @param {Array|AttributeList|Object} nodes - An array or object of attributes.\n\t* @returns {AttributeList} A new {@link AttributeList}\n\t* @example <caption>Return an array of attributes from a regular object.</caption>\n\t* AttributeList.from({ dataFoo: 'bar' }) // returns AttributeList [{ name: 'data-foo', value: 'bar' }]\n\t* @example <caption>Return a normalized array of attributes from an impure array of attributes.</caption>\n\t* AttributeList.from([{ name: 'data-foo', value: true, foo: 'bar' }]) // returns AttributeList [{ name: 'data-foo', value: 'true' }]\n\t*/\n\n\tstatic from (attrs) {\n\t\treturn new AttributeList(getAttributeListArray(attrs));\n\t}\n}\n\n/**\n* Toggle an attribute or attributes from an {@link AttributeList}.\n* @param {AttributeList} attrs - The {@link AttributeList} being modified.\n* @param {String|Object} toggles - The attributes being toggled.\n* @param {Boolean} [force] - Whether attributes should be exclusively added (true) or removed (false)\n* @returns {Object} An object specifying whether any attributes were added, removed, and/or modified.\n* @private\n*/\n\nfunction toggle (attrs, toggles, force) {\n\tlet attributeAdded = false;\n\tlet attributeRemoved = false;\n\tlet atttributeModified= false;\n\n\ttoggles.forEach(toggleAttr => {\n\t\tconst index = attrs.indexOf(toggleAttr.name);\n\n\t\tif (index === -1) {\n\t\t\tif (force !== false) {\n\t\t\t\t// add the attribute (if not exclusively removing attributes)\n\t\t\t\tattrs.push(toggleAttr);\n\n\t\t\t\tattributeAdded = true;\n\t\t\t}\n\t\t} else if (force !== true) {\n\t\t\t// remove the attribute (if not exclusively adding attributes)\n\t\t\tattrs.splice(index, 1);\n\n\t\t\tattributeRemoved = true;\n\t\t} else if (toggleAttr.value !== undefined && attrs[index].value !== toggleAttr.value) {\n\t\t\t// change the value of the attribute (if exclusively adding attributes)\n\t\t\tattrs[index].value = toggleAttr.value;\n\n\t\t\tatttributeModified = true;\n\t\t}\n\t});\n\n\treturn { attributeAdded, attributeRemoved, atttributeModified };\n}\n\n/**\n* Return an AttributeList-compatible array from an array or object.\n* @private\n*/\n\nfunction getAttributeListArray (attrs, value) {\n\treturn attrs === null || attrs === undefined\n\t\t// void values are omitted\n\t\t? []\n\t: Array.isArray(attrs)\n\t\t// arrays are sanitized as a name or value, and then optionally a source\n\t\t? attrs.reduce(\n\t\t\t(attrs, rawattr) => {\n\t\t\t\tconst attr = {};\n\n\t\t\t\tif ('name' in Object(rawattr)) {\n\t\t\t\t\tattr.name = String(rawattr.name);\n\t\t\t\t}\n\n\t\t\t\tif ('value' in Object(rawattr)) {\n\t\t\t\t\tattr.value = getAttributeValue(rawattr.value);\n\t\t\t\t}\n\n\t\t\t\tif ('source' in Object(rawattr)) {\n\t\t\t\t\tattr.source = rawattr.source;\n\t\t\t\t}\n\n\t\t\t\tif ('name' in attr || 'value' in attr) {\n\t\t\t\t\tattrs.push(attr);\n\t\t\t\t}\n\n\t\t\t\treturn attrs;\n\t\t\t},\n\t\t\t[]\n\t\t)\n\t: attrs === Object(attrs)\n\t\t// objects are sanitized as a name and value\n\t\t? Object.keys(attrs).map(\n\t\t\tname => ({\n\t\t\t\tname: toKebabCaseString(name),\n\t\t\t\tvalue: getAttributeValue(attrs[name])\n\t\t\t})\n\t\t)\n\t: 1 in arguments\n\t\t// both name and value arguments are sanitized as a name and value\n\t\t? [{\n\t\t\tname: attrs,\n\t\t\tvalue: getAttributeValue(value)\n\t\t}]\n\t// one name argument is sanitized as a name\n\t: [{\n\t\tname: attrs\n\t}];\n}\n\n/**\n* Return a value transformed into an attribute value.\n* @description Expected values are strings. Unexpected values are null, objects, and undefined. Nulls returns null, Objects with the default toString return their JSON.stringify’d value otherwise toString’d, and Undefineds return an empty string.\n* @example <caption>Expected values.</caption>\n* getAttributeValue('foo') // returns 'foo'\n* getAttributeValue('') // returns ''\n* @example <caption>Unexpected values.</caption>\n* getAttributeValue(null) // returns null\n* getAttributeValue(undefined) // returns ''\n* getAttributeValue(['foo']) // returns '[\"foo\"]'\n* getAttributeValue({ toString() { return 'bar' }}) // returns 'bar'\n* getAttributeValue({ toString: 'bar' }) // returns '{\"toString\":\"bar\"}'\n* @private\n*/\n\nfunction getAttributeValue (value) {\n\treturn value === null\n\t\t? null\n\t: value === undefined\n\t\t? ''\n\t: value === Object(value)\n\t\t? value.toString === Object.prototype.toString\n\t\t\t? JSON.stringify(value)\n\t\t: String(value)\n\t: String(value);\n}\n\n/**\n* Return a string formatted using camelCasing.\n* @param {String} value - The value being formatted.\n* @example\n* toCamelCaseString('hello-world') // returns 'helloWorld'\n* @private\n*/\n\nfunction toCamelCaseString (value) {\n\treturn isKebabCase(value)\n\t\t? String(value).replace(/-[a-z]/g, $0 => $0.slice(1).toUpperCase())\n\t: String(value);\n}\n\n/**\n* Return a string formatted using kebab-casing.\n* @param {String} value - The value being formatted.\n* @description Expected values do not already contain dashes.\n* @example <caption>Expected values.</caption>\n* toKebabCaseString('helloWorld') // returns 'hello-world'\n* toKebabCaseString('helloworld') // returns 'helloworld'\n* @example <caption>Unexpected values.</caption>\n* toKebabCaseString('hello-World') // returns 'hello-World'\n* @private\n*/\n\nfunction toKebabCaseString (value) {\n\treturn isCamelCase(value)\n\t\t? String(value).replace(/[A-Z]/g, $0 => `-${$0.toLowerCase()}`)\n\t: String(value);\n}\n\n/**\n* Return whether a value is formatted camelCase.\n* @example\n* isCamelCase('helloWorld') // returns true\n* isCamelCase('hello-world') // returns false\n* isCamelCase('helloworld') // returns false\n* @private\n*/\n\nfunction isCamelCase (value) {\n\treturn /^\\w+[A-Z]\\w*$/.test(value);\n}\n\n/**\n* Return whether a value is formatted kebab-case.\n* @example\n* isKebabCase('hello-world') // returns true\n* isKebabCase('helloworld') // returns false\n* isKebabCase('helloWorld') // returns false\n* @private\n*/\n\nfunction isKebabCase (value) {\n\treturn /^\\w+[-]\\w+$/.test(value);\n}\n\n/**\n* Return whether a value is a Regular Expression.\n* @example\n* isRegExp(/hello-world/) // returns true\n* isRegExp('/hello-world/') // returns false\n* isRegExp(new RegExp('hello-world')) // returns true\n* @private\n*/\n\nfunction isRegExp (value) {\n\treturn Object.prototype.toString.call(value) === '[object RegExp]';\n}\n\nexport default AttributeList;\n","/**\n* Transform a {@link Node} and any descendants using visitors.\n* @param {Node} node - The {@link Node} to be visited.\n* @param {Result} result - The {@link Result} to be used by visitors.\n* @param {Object} [overrideVisitors] - Alternative visitors to be used in place of {@link Result} visitors.\n* @returns {ResultPromise}\n* @private\n*/\n\nfunction visit (node, result, overrideVisitors) {\n\t// get visitors as an object\n\tconst visitors = Object(overrideVisitors || Object(result).visitors);\n\n\t// get node types\n\tconst beforeType = getTypeFromNode(node);\n\tconst beforeSubType = getSubTypeFromNode(node);\n\tconst beforeNodeType = 'Node';\n\tconst beforeRootType = 'Root';\n\tconst afterType = `after${beforeType}`;\n\tconst afterSubType = `after${beforeSubType}`;\n\tconst afterNodeType = 'afterNode';\n\tconst afterRootType = 'afterRoot';\n\n\tlet promise = Promise.resolve();\n\n\t// fire \"before\" visitors\n\tif (visitors[beforeNodeType]) {\n\t\tpromise = promise.then(\n\t\t\t() => runAll(visitors[beforeNodeType], node, result)\n\t\t);\n\t}\n\n\tif (visitors[beforeType]) {\n\t\tpromise = promise.then(\n\t\t\t() => runAll(visitors[beforeType], node, result)\n\t\t);\n\t}\n\n\tif (beforeSubType !== beforeType && visitors[beforeSubType]) {\n\t\tpromise = promise.then(\n\t\t\t() => runAll(visitors[beforeSubType], node, result)\n\t\t);\n\t}\n\n\t// dispatch before root event\n\tif (visitors[beforeRootType] && node === result.root) {\n\t\tpromise = promise.then(\n\t\t\t() => runAll(visitors[beforeRootType], node, result)\n\t\t);\n\t}\n\n\t// walk children\n\tif (Array.isArray(node.nodes)) {\n\t\tnode.nodes.slice(0).forEach(childNode => {\n\t\t\tpromise = promise.then(\n\t\t\t\t() => (\n\t\t\t\t\tchildNode.parent === node &&\n\t\t\t\t\tvisit(childNode, result, overrideVisitors)\n\t\t\t\t)\n\t\t\t);\n\t\t})\n\t}\n\n\t// fire \"after\" visitors\n\tif (visitors[afterNodeType]) {\n\t\tpromise = promise.then(\n\t\t\t() => runAll(visitors[afterNodeType], node, result)\n\t\t);\n\t}\n\n\tif (visitors[afterType]) {\n\t\tpromise = promise.then(\n\t\t\t() => runAll(visitors[afterType], node, result)\n\t\t);\n\t}\n\n\tif (afterType !== afterSubType && visitors[afterSubType]) {\n\t\tpromise = promise.then(\n\t\t\t() => runAll(visitors[afterSubType], node, result)\n\t\t);\n\t}\n\n\t// dispatch root event\n\tif (visitors[afterRootType] && node === result.root) {\n\t\tpromise = promise.then(\n\t\t\t() => runAll(visitors[afterRootType], node, result)\n\t\t);\n\t}\n\n\treturn promise.then(\n\t\t() => result\n\t);\n}\n\nexport function runAll (plugins, node, result) {\n\tlet promise = Promise.resolve();\n\n\t[].concat(plugins || []).forEach(plugin => {\n\t\t// run the current plugin\n\t\tpromise = promise.then(() => {\n\t\t\t// update the current plugin\n\t\t\tresult.currentPlugin = plugin;\n\n\t\t\treturn plugin(node, result);\n\t\t}).then(() => {\n\t\t\t// clear the current plugin\n\t\t\tresult.currentPlugin = null;\n\t\t});\n\t});\n\n\treturn promise;\n}\n\n// return normalized plugins and visitors\nfunction getVisitors (rawplugins) {\n\tconst visitors = {};\n\n\t// initialize plugins and visitors\n\t[].concat(rawplugins || []).forEach(plugin => {\n\t\tconst initializedPlugin = Object(plugin).type === 'plugin' ? plugin() : plugin;\n\n\t\tif (initializedPlugin instanceof Function) {\n\t\t\tif (!visitors.afterRoot) {\n\t\t\t\tvisitors.afterRoot = [];\n\t\t\t}\n\n\t\t\tvisitors.afterRoot.push(initializedPlugin);\n\t\t} else if (Object(initializedPlugin) === initializedPlugin && Object.keys(initializedPlugin).length) {\n\t\t\tObject.keys(initializedPlugin).forEach(key => {\n\t\t\t\tconst fn = initializedPlugin[key];\n\n\t\t\t\tif (fn instanceof Function) {\n\t\t\t\t\tif (!visitors[key]) {\n\t\t\t\t\t\tvisitors[key] = [];\n\t\t\t\t\t}\n\n\t\t\t\t\tvisitors[key].push(initializedPlugin[key]);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t});\n\n\treturn visitors;\n}\n\nfunction getTypeFromNode (node) {\n\treturn {\n\t\t'comment': 'Comment',\n\t\t'text': 'Text',\n\t\t'doctype': 'Doctype',\n\t\t'fragment': 'Fragment'\n\t}[node.type] || 'Element';\n}\n\nfunction getSubTypeFromNode (node) {\n\treturn {\n\t\t'comment': 'Comment',\n\t\t'text': 'Text',\n\t\t'doctype': 'Doctype',\n\t\t'fragment': 'Fragment'\n\t}[node.type] || (\n\t\t!node.name\n\t\t\t? 'FragmentElement'\n\t\t: `${node.name[0].toUpperCase()}${node.name.slice(1)}Element`\n\t);\n}\n\nexport {\n\tvisit as default,\n\tgetVisitors\n};\n","import visit from './visit';\n\n/**\n* @name Node\n* @class\n* @extends Node\n* @classdesc Create a new {@link Node}.\n* @returns {Node}\n*/\nclass Node {\n\t/**\n\t* The position of the current {@link Node} from its parent.\n\t* @returns {Number}\n\t* @example\n\t* node.index // returns the index of the node or -1\n\t*/\n\tget index () {\n\t\tif (this.parent === Object(this.parent) && this.parent.nodes && this.parent.nodes.length) {\n\t\t\treturn this.parent.nodes.indexOf(this);\n\t\t}\n\n\t\treturn -1;\n\t}\n\n\t/**\n\t* The next {@link Node} after the current {@link Node}, or `null` if there is none.\n\t* @returns {Node|Null} - The next Node or null.\n\t* @example\n\t* node.next // returns null\n\t*/\n\tget next () {\n\t\tconst index = this.index;\n\n\t\tif (index !== -1) {\n\t\t\treturn this.parent.nodes[index + 1] || null;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t* The next {@link Element} after the current {@link Node}, or `null` if there is none.\n\t* @returns {Element|Null}\n\t* @example\n\t* node.nextElement // returns an element or null\n\t*/\n\tget nextElement () {\n\t\tconst index = this.index;\n\n\t\tif (index !== -1) {\n\t\t\treturn this.parent.nodes.slice(index).find(hasNodes);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t* The previous {@link Node} before the current {@link Node}, or `null` if there is none.\n\t* @returns {Node|Null}\n\t* @example\n\t* node.previous // returns a node or null\n\t*/\n\tget previous () {\n\t\tconst index = this.index;\n\n\t\tif (index !== -1) {\n\t\t\treturn this.parent.nodes[index - 1] || null;\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t* The previous {@link Element} before the current {@link Node}, or `null` if there is none.\n\t* @returns {Element|Null}\n\t* @example\n\t* node.previousElement // returns an element or null\n\t*/\n\tget previousElement () {\n\t\tconst index = this.index;\n\n\t\tif (index !== -1) {\n\t\t\treturn this.parent.nodes.slice(0, index).reverse().find(hasNodes);\n\t\t}\n\n\t\treturn null;\n\t}\n\n\t/**\n\t* The top-most ancestor from the current {@link Node}.\n\t* @returns {Node}\n\t* @example\n\t* node.root // returns the top-most node or the current node itself\n\t*/\n\tget root () {\n\t\tlet parent = this;\n\n\t\twhile (parent.parent) {\n\t\t\tparent = parent.parent;\n\t\t}\n\n\t\treturn parent;\n\t}\n\n\t/**\n\t* Insert one ore more {@link Node}s after the current {@link Node}.\n\t* @param {...Node|String} nodes - Any nodes to be inserted after the current {@link Node}.\n\t* @returns {Node} - The current {@link Node}.\n\t* @example\n\t* node.after(new Text({ data: 'Hello World' }))\n\t*/\n\tafter (...nodes) {\n\t\tif (nodes.length) {\n\t\t\tconst index = this.index;\n\n\t\t\tif (index !== -1) {\n\t\t\t\tthis.parent.nodes.splice(index + 1, 0, ...nodes);\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t* Append Nodes or new Text Nodes to the current {@link Node}.\n\t* @param {...Node|String} nodes - Any nodes to be inserted after the last child of the current {@link Node}.\n\t* @returns {Node} - The current {@link Node}.\n\t* @example\n\t* node.append(someOtherNode)\n\t*/\n\tappend (...nodes) {\n\t\tif (this.nodes) {\n\t\t\tthis.nodes.splice(this.nodes.length, 0, ...nodes);\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t* Append the current {@link Node} to another Node.\n\t* @param {Container} parent - The {@link Container} for the current {@link Node}.\n\t* @returns {Node} - The current {@link Node}.\n\t*/\n\tappendTo (parent) {\n\t\tif (parent && parent.nodes) {\n\t\t\tparent.nodes.splice(parent.nodes.length, 0, this);\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t* Insert Nodes or new Text Nodes before the Node if it has a parent.\n\t* @param {...Node|String} nodes - Any nodes to be inserted before the current {@link Node}.\n\t* @returns {Node}\n\t* @example\n\t* node.before(new Text({ data: 'Hello World' })) // returns the current node\n\t*/\n\tbefore (...nodes) {\n\t\tif (nodes.length) {\n\t\t\tconst index = this.index;\n\n\t\t\tif (index !== -1) {\n\t\t\t\tthis.parent.nodes.splice(index, 0, ...nodes);\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t* Prepend Nodes or new Text Nodes to the current {@link Node}.\n\t* @param {...Node|String} nodes - Any nodes inserted before the first child of the current {@link Node}.\n\t* @returns {Node} - The current {@link Node}.\n\t* @example\n\t* node.prepend(someOtherNode)\n\t*/\n\tprepend (...nodes) {\n\t\tif (this.nodes) {\n\t\t\tthis.nodes.splice(0, 0, ...nodes);\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t* Remove the current {@link Node} from its parent.\n\t* @returns {Node}\n\t* @example\n\t* node.remove() // returns the current node\n\t*/\n\tremove () {\n\t\tconst index = this.index;\n\n\t\tif (index !== -1) {\n\t\t\tthis.parent.nodes.splice(index, 1);\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t* Replace the current {@link Node} with another Node or Nodes.\n\t* @param {...Node} nodes - Any nodes replacing the current {@link Node}.\n\t* @returns {Node} - The current {@link Node}\n\t* @example\n\t* node.replaceWith(someOtherNode) // returns the current node\n\t*/\n\treplaceWith (...nodes) {\n\t\tconst index = this.index;\n\n\t\tif (index !== -1) {\n\t\t\tthis.parent.nodes.splice(index, 1, ...nodes);\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t* Transform the current {@link Node} and any descendants using visitors.\n\t* @param {Result} result - The {@link Result} to be used by visitors.\n\t* @param {Object} [overrideVisitors] - Alternative visitors to be used in place of {@link Result} visitors.\n\t* @returns {ResultPromise}\n\t* @example\n\t* await node.visit(result)\n\t* @example\n\t* await node.visit() // visit using the result of the current node\n\t* @example\n\t* await node.visit(result, {\n\t* Element () {\n\t* // do something to an element\n\t* }\n\t* })\n\t*/\n\tvisit (result, overrideVisitors) {\n\t\tconst resultToUse = 0 in arguments ? result : this.result;\n\n\t\treturn visit(this, resultToUse, overrideVisitors);\n\t}\n\n\t/**\n\t* Add a warning from the current {@link Node}.\n\t* @param {Result} result - The {@link Result} the warning is being added to.\n\t* @param {String} text - The message being sent as the warning.\n\t* @param {Object} [opts] - Additional information about the warning.\n\t* @example\n\t* node.warn(result, 'Something went wrong')\n\t* @example\n\t* node.warn(result, 'Something went wrong', {\n\t* node: someOtherNode,\n\t* plugin: someOtherPlugin\n\t* })\n\t*/\n\twarn (result, text, opts) {\n\t\tconst data = Object.assign({ node: this }, opts);\n\n\t\treturn result.warn(text, data);\n\t}\n}\n\nfunction hasNodes (node) {\n\treturn node.nodes;\n}\n\nexport default Node;\n","import Node from './Node';\n\n/**\n* @name Comment\n* @class\n* @extends Node\n* @classdesc Return a new {@link Comment} {@link Node}.\n* @param {Object|String} settings - Custom settings applied to the Comment, or the content of the {@link Comment}.\n* @param {String} settings.comment - Content of the Comment.\n* @param {Object} settings.source - Source mapping of the Comment.\n* @returns {Comment}\n* @example\n* new Comment({ comment: ' Hello World ' })\n*/\nclass Comment extends Node {\n\tconstructor (settings) {\n\t\tsuper();\n\n\t\tif (typeof settings === 'string') {\n\t\t\tsettings = { comment: settings };\n\t\t}\n\n\t\tObject.assign(this, {\n\t\t\ttype: 'comment',\n\t\t\tname: '#comment',\n\t\t\tcomment: String(Object(settings).comment || ''),\n\t\t\tsource: Object(Object(settings).source)\n\t\t});\n\t}\n\n\t/**\n\t* Return the stringified innerHTML of the current {@link Comment}.\n\t* @returns {String}\n\t* @example\n\t* attrs.innerHTML // returns ' Hello World '\n\t*/\n\tget innerHTML () {\n\t\treturn String(this.comment);\n\t}\n\n\t/**\n\t* Return the stringified outerHTML of the current {@link Comment}.\n\t* @returns {String}\n\t* @example\n\t* attrs.outerHTML // returns '<!-- Hello World -->'\n\t*/\n\tget outerHTML () {\n\t\treturn String(this);\n\t}\n\n\t/**\n\t* Return the stringified innerHTML from the source input.\n\t* @returns {String}\n\t* @example\n\t* attrs.sourceInnerHTML // returns ' Hello World '\n\t*/\n\tget sourceInnerHTML () {\n\t\treturn typeof Object(this.source.input).html !== 'string'\n\t\t\t? ''\n\t\t: this.source.input.html.slice(\n\t\t\tthis.source.startOffset + 4,\n\t\t\tthis.source.endOffset - 3\n\t\t);\n\t}\n\n\t/**\n\t* Return the stringified outerHTML from the source input.\n\t* @returns {String}\n\t* @example\n\t* attrs.sourceOuterHTML // returns '<!-- Hello World -->'\n\t*/\n\tget sourceOuterHTML () {\n\t\treturn typeof Object(this.source.input).html !== 'string'\n\t\t\t? ''\n\t\t: this.source.input.html.slice(\n\t\t\tthis.source.startOffset,\n\t\t\tthis.source.endOffset\n\t\t);\n\t}\n\n\t/**\n\t* Return a clone of the current {@link Comment}.\n\t* @param {Object} settings - Custom settings applied to the cloned {@link Comment}.\n\t* @returns {Comment} - The cloned {@link Comment}\n\t* @example\n\t* comment.clone()\n\t* @example <caption>Clone the current text with new source.</caption>\n\t* comment.clone({ source: { input: 'modified source' } })\n\t*/\n\tclone (settings) {\n\t\treturn new this.constructor(Object.assign({}, this, settings, {\n\t\t\tsource: Object.assign({}, this.source, Object(settings).source)\n\t\t}));\n\t}\n\n\t/**\n\t* Return the current {@link Comment} as a String.\n\t* @returns {String} A string version of the current {@link Comment}\n\t* @example\n\t* attrs.toJSON() // returns '<!-- Hello World -->'\n\t*/\n\ttoString () {\n\t\treturn `<!--${this.comment}-->`;\n\t}\n\n\t/**\n\t* Return the current {@link Comment} as a Object.\n\t* @returns {Object} An object version of the current {@link Comment}\n\t* @example\n\t* attrs.toJSON() // returns { comment: ' Hello World ' }\n\t*/\n\ttoJSON () {\n\t\treturn {\n\t\t\tcomment: this.comment\n\t\t};\n\t}\n}\n\nexport default Comment;\n","import Node from './Node';\n\n/**\n* @name Container\n* @class\n* @extends Node\n* @classdesc Return a new {@link Container} {@link Node}.\n* @returns {Container}\n*/\nclass Container extends Node {\n\t/**\n\t* Return the first child {@link Node} of the current {@link Container}, or `null` if there is none.\n\t* @returns {Node|Null}\n\t* @example\n\t* container.first // returns a Node or null\n\t*/\n\tget first () {\n\t\treturn this.nodes[0] || null;\n\t}\n\n\t/**\n\t* Return the first child {@link Element} of the current {@link Container}, or `null` if there is none.\n\t* @returns {Node|Null}\n\t* @example\n\t* container.firstElement // returns an Element or null\n\t*/\n\tget firstElement () {\n\t\treturn this.nodes.find(hasNodes) || null;\n\t}\n\n\t/**\n\t* Return the last child {@link Node} of the current {@link Container}, or `null` if there is none.\n\t* @returns {Node|Null}\n\t* @example\n\t* container.last // returns a Node or null\n\t*/\n\tget last () {\n\t\treturn this.nodes[this.nodes.length - 1] || null;\n\t}\n\n\t/**\n\t* Return the last child {@link Element} of the current {@link Container}, or `null` if there is none.\n\t* @returns {Node|Null}\n\t* @example\n\t* container.lastElement // returns an Element or null\n\t*/\n\tget lastElement () {\n\t\treturn this.nodes.slice().reverse().find(hasNodes) || null;\n\t}\n\n\t/**\n\t* Return a child {@link Element} {@link NodeList} of the current {@link Container}.\n\t* @returns {Array}\n\t* @example\n\t* container.elements // returns an array of Elements\n\t*/\n\tget elements () {\n\t\treturn this.nodes.filter(hasNodes) || [];\n\t}\n\n\t/**\n\t* Return the innerHTML of the current {@link Container} as a String.\n\t* @returns {String}\n\t* @example\n\t* container.innerHTML // returns a string of innerHTML\n\t*/\n\tget innerHTML () {\n\t\treturn this.nodes.innerHTML;\n\t}\n\n\t/**\n\t* Define the nodes of the current {@link Container} from a String.\n\t* @param {String} innerHTML - Source being processed.\n\t* @returns {Void}\n\t* @example\n\t* container.innerHTML = 'Hello <strong>world</strong>';\n\t* container.nodes.length; // 2\n\t*/\n\tset innerHTML (innerHTML) {\n\t\tthis.nodes.innerHTML = innerHTML;\n\t}\n\n\t/**\n\t* Return the outerHTML of the current {@link Container} as a String.\n\t* @returns {String}\n\t* @example\n\t* container.outerHTML // returns a string of outerHTML\n\t*/\n\tget outerHTML () {\n\t\treturn this.nodes.innerHTML;\n\t}\n\n\t/**\n\t* Replace the current {@link Container} from a String.\n\t* @param {String} input - Source being processed.\n\t* @returns {Void}\n\t* @example\n\t* container.outerHTML = 'Hello <strong>world</strong>';\n\t*/\n\tset outerHTML (outerHTML) {\n\t\tconst Result = Object(this.result).constructor;\n\n\t\tif (Result) {\n\t\t\tconst childNodes = new Result(outerHTML).root.nodes;\n\n\t\t\tthis.replaceWith(...childNodes);\n\t\t}\n\t}\n\n\t/**\n\t* Return the stringified innerHTML from the source input.\n\t* @returns {String}\n\t*/\n\tget sourceInnerHTML () {\n\t\treturn this.isSelfClosing || this.isVoid || typeof Object(this.source.input).html !== 'string'\n\t\t\t? ''\n\t\t: 'startInnerOffset' in this.source && 'endInnerOffset' in this.source\n\t\t\t? this.source.input.html.slice(\n\t\t\t\tthis.source.startInnerOffset,\n\t\t\t\tthis.source.endInnerOffset\n\t\t\t)\n\t\t: this.sourceOuterHTML;\n\t}\n\n\t/**\n\t* Return the stringified outerHTML from the source input.\n\t* @returns {String}\n\t*/\n\tget sourceOuterHTML () {\n\t\treturn typeof Object(this.source.input).html !== 'string'\n\t\t\t? ''\n\t\t: this.source.input.html.slice(\n\t\t\tthis.source.startOffset,\n\t\t\tthis.source.endOffset\n\t\t);\n\t}\n\n\t/**\n\t* Return the text content of the current {@link Container} as a String.\n\t* @returns {String}\n\t*/\n\tget textContent () {\n\t\treturn this.nodes.textContent;\n\t}\n\n\t/**\n\t* Define the content of the current {@link Container} as a new {@link Text} {@link Node}.\n\t* @returns {String}\n\t*/\n\tset textContent (textContent) {\n\t\tthis.nodes.textContent = textContent;\n\t}\n\n\t/**\n\t* Return a child {@link Node} of the current {@link Container} by last index, or `null` if there is none.\n\t* @returns {Node|Null}\n\t* @example\n\t* container.lastNth(0) // returns a Node or null\n\t*/\n\tlastNth (index) {\n\t\treturn this.nodes.slice().reverse()[index] || null;\n\t}\n\n\t/**\n\t* Return a child {@link Element} of the current {@link Container} by last index, or `null` if there is none.\n\t* @returns {Element|Null}\n\t* @example\n\t* container.lastNthElement(0) // returns an Element or null\n\t*/\n\tlastNthElement (index) {\n\t\treturn this.elements.reverse()[index] || null;\n\t}\n\n\t/**\n\t* Return a child {@link Node} of the current {@link Container} by index, or `null` if there is none.\n\t* @returns {Node|Null}\n\t* @example\n\t* container.nth(0) // returns a Node or null\n\t*/\n\tnth (index) {\n\t\treturn this.nodes[index] || null;\n\t}\n\n\t/**\n\t* Return an {@link Element} child of the current Container by index, or `null` if there is none.\n\t* @returns {Element|Null}\n\t* @example\n\t* container.nthElement(0) // returns an Element or null\n\t*/\n\tnthElement (index) {\n\t\treturn this.elements[index] || null;\n\t}\n\n\t/**\n\t* Replace all of the children of the current {@link Container}.\n\t* @param {...Node} nodes - Any nodes replacing the current children of the {@link Container}.\n\t* @returns {Container} - The current {@link Container}.\n\t* @example\n\t* container.replaceAll(new Text({ data: 'Hello World' }))\n\t*/\n\treplaceAll (...nodes) {\n\t\tif (this.nodes) {\n\t\t\tthis.nodes.splice(0, this.nodes.length, ...nodes);\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t* Traverse the descendant {@link Node}s of the current {@link Container} with a callback function.\n\t* @param {Function|String|RegExp} callback_or_filter - A callback function, or a filter to reduce {@link Node}s the callback is applied to.\n\t* @param {Function|String|RegExp} callback - A callback function when a filter is also specified.\n\t* @returns {Container} - The current {@link Container}.\n\t* @example\n\t* container.walk(node => {\n\t* console.log(node);\n\t* })\n\t* @example\n\t* container.walk('*', node => {\n\t* console.log(node);\n\t* })\n\t* @example <caption>Walk only \"section\" {@link Element}s.</caption>\n\t* container.walk('section', node => {\n\t* console.log(node); // logs only Section Elements\n\t* })\n\t* @example\n\t* container.walk(/^section$/, node => {\n\t* console.log(node); // logs only Section Elements\n\t* })\n\t* @example\n\t* container.walk(\n\t* node => node.name.toLowerCase() === 'section',\n\t* node => {\n\t* console.log(node); // logs only Section Elements\n\t* })\n\t* @example <caption>Walk only {@link Text}.</caption>\n\t* container.walk('#text', node => {\n\t* console.log(node); // logs only Text Nodes\n\t* })\n\t*/\n\twalk () {\n\t\tconst [ cb, filter ] = getCbAndFilterFromArgs(arguments);\n\n\t\twalk(this, cb, filter);\n\n\t\treturn this;\n\t}\n}\n\nfunction walk (node, cb, filter) {\n\tif (typeof cb === 'function' && node.nodes) {\n\t\tnode.nodes.slice(0).forEach(child => {\n\t\t\tif (Object(child).parent === node) {\n\t\t\t\tif (testWithFilter(child, filter)) {\n\t\t\t\t\tcb(child); // eslint-disable-line callback-return\n\t\t\t\t}\n\n\t\t\t\tif (child.nodes) {\n\t\t\t\t\twalk(child, cb, filter);\n\t\t\t\t}\n\t\t\t}\n\t\t});\n\t}\n}\n\nfunction getCbAndFilterFromArgs (args) {\n\tconst [ cbOrFilter, onlyCb ] = args;\n\tconst cb = onlyCb || cbOrFilter;\n\tconst filter = onlyCb ? cbOrFilter : undefined;\n\n\treturn [cb, filter];\n}\n\nfunction testWithFilter (node, filter) {\n\tif (!filter) {\n\t\treturn true;\n\t} else if (filter === '*') {\n\t\treturn Object(node).constructor.name === 'Element';\n\t} else if (typeof filter === 'string') {\n\t\treturn node.name === filter;\n\t} else if (filter instanceof RegExp) {\n\t\treturn filter.test(node.name);\n\t} else if (filter instanceof Function) {\n\t\treturn filter(node);\n\t} else {\n\t\treturn false;\n\t}\n}\n\nfunction hasNodes (node) {\n\treturn node.nodes;\n}\n\nexport default Container;\n","import Node from './Node';\n\n/**\n* @name Doctype\n* @class\n* @extends Node\n* @classdesc Create a new {@link Doctype} {@link Node}.\n* @param {Object|String} settings - Custom settings applied to the {@link Doctype}, or the name of the {@link Doctype}.\n* @param {String} settings.name - Name of the {@link Doctype}.\n* @param {String} settings.publicId - Public identifier portion of the {@link Doctype}.\n* @param {String} settings.systemId - System identifier portion of the {@link Doctype}.\n* @param {Object} settings.source - Source mapping of the {@link Doctype}.\n* @returns {Doctype}\n* @example\n* new Doctype({ name: 'html' }) // <!doctype html>\n*/\nclass Doctype extends Node {\n\tconstructor (settings) {\n\t\tsuper();\n\n\t\tif (typeof settings === 'string') {\n\t\t\tsettings = { name: settings };\n\t\t}\n\n\t\tObject.assign(this, {\n\t\t\ttype: 'doctype',\n\t\t\tdoctype: String(Object(settings).doctype || 'doctype'),\n\t\t\tname: String(Object(settings).name || 'html'),\n\t\t\tpublicId: Object(settings).publicId || null,\n\t\t\tsystemId: Object(settings).systemId || null,\n\t\t\tsource: Object.assign({\n\t\t\t\tbefore: Object(Object(settings).source).before || ' ',\n\t\t\t\tafter: Object(Object(settings).source).after || '',\n\t\t\t\tbeforePublicId: Object(Object(settings).source).beforePublicId || null,\n\t\t\t\tbeforeSystemId: Object(Object(settings).source).beforeSystemId || null\n\t\t\t}, Object(settings).source)\n\t\t});\n\t}\n\n\t/**\n\t* Return a clone of the current {@link Doctype}.\n\t* @param {Object} settings - Custom settings applied to the cloned {@link Doctype}.\n\t* @returns {Doctype} - The cloned {@link Doctype}\n\t* @example\n\t* doctype.clone()\n\t* @example <caption>Clone the current text with new source.</caption>\n\t* doctype.clone({ source: { input: 'modified source' } })\n\t*/\n\tclone (settings) {\n\t\treturn new this.constructor(Object.assign({}, this, settings, {\n\t\t\tsource: Object.assign({}, this.source, Object(settings).source)\n\t\t}));\n\t}\n\n\t/**\n\t* Return the current {@link Doctype} as a String.\n\t* @returns {String}\n\t*/\n\ttoString () {\n\t\tconst publicId = this.publicId ? `${this.source.beforePublicId || ' '}${this.publicId}` : '';\n\t\tconst systemId = this.systemId ? `${this.source.beforeSystemId || ' '}${this.systemId}` : '';\n\n\t\treturn `<!${this.doctype}${this.source.before}${this.name}${this.source.after}${publicId}${systemId}>`;\n\t}\n\n\t/**\n\t* Return the current {@link Doctype} as an Object.\n\t* @returns {Object}\n\t*/\n\ttoJSON () {\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tpublicId: this.publicId,\n\t\t\tsystemId: this.systemId\n\t\t};\n\t}\n}\n\nexport default Doctype;\n","import Container from './Container';\nimport NodeList from './NodeList';\n\n/**\n* @name Fragment\n* @class\n* @extends Container\n* @classdesc Create a new {@link Fragment} {@Link Node}.\n* @param {Object} settings - Custom settings applied to the {@link Fragment}.\n* @param {Array|NodeList} settings.nodes - Nodes appended to the {@link Fragment}.\n* @param {Object} settings.source - Source mapping of the {@link Fragment}.\n* @returns {Fragment}\n* @example\n* new Fragment() // returns an empty fragment\n*\n* new Fragment({ nodes: [ new Element('span') ] }) // returns a fragment with a <span>\n*/\n\nclass Fragment extends Container {\n\tconstructor (settings) {\n\t\tsuper();\n\n\t\tObject.assign(this, settings, {\n\t\t\ttype: 'fragment',\n\t\t\tname: '#document-fragment',\n\t\t\tnodes: Array.isArray(Object(settings).nodes)\n\t\t\t\t? new NodeList(this, ...Array.from(settings.nodes))\n\t\t\t: Object(settings).nodes !== null && Object(settings).nodes !== undefined\n\t\t\t\t? new NodeList(this, settings.nodes)\n\t\t\t: new NodeList(this),\n\t\t\tsource: Object(Object(settings).source)\n\t\t});\n\t}\n\n\t/**\n\t* Return a clone of the current {@link Fragment}.\n\t* @param {Boolean} isDeep - Whether the descendants of the current Fragment should also be cloned.\n\t* @returns {Fragment} - The cloned Fragment\n\t*/\n\tclone (isDeep) {\n\t\tconst clone = new Fragment({ ...this, nodes: [] });\n\n\t\tif (isDeep) {\n\t\t\tclone.nodes = this.nodes.clone(clone);\n\t\t}\n\n\t\treturn clone;\n\t}\n\n\t/**\n\t* Return the current {@link Fragment} as an Array.\n\t* @returns {Array}\n\t* @example\n\t* fragment.toJSON() // returns []\n\t*/\n\ttoJSON () {\n\t\treturn this.nodes.toJSON();\n\t}\n\n\t/**\n\t* Return the current {@link Fragment} as a String.\n\t* @returns {String}\n\t* @example\n\t* fragment.toJSON() // returns ''\n\t*/\n\ttoString () {\n\t\treturn String(this.nodes);\n\t}\n}\n\nexport default Fragment;\n","import Node from './Node';\n\n/**\n* @name Text\n* @class\n* @extends Node\n* @classdesc Create a new {@link Text} {@link Node}.\n* @param {Object|String} settings - Custom settings applied to the {@link Text}, or the content of the {@link Text}.\n* @param {String} settings.data - Content of the {@link Text}.\n* @param {Object} settings.source - Source mapping of the {@link Text}.\n* @returns {Text}\n* @example\n* new Text({ data: 'Hello World' })\n*/\nclass Text extends Node {\n\tconstructor (settings) {\n\t\tsuper();\n\n\t\tif (typeof settings === 'string') {\n\t\t\tsettings = { data: settings };\n\t\t}\n\n\t\tObject.assign(this, {\n\t\t\ttype: 'text',\n\t\t\tname: '#text',\n\t\t\tdata: String(Object(settings).data || ''),\n\t\t\tsource: Object(Object(settings).source)\n\t\t});\n\t}\n\n\t/**\n\t* Return the stringified innerHTML from the source input.\n\t* @returns {String}\n\t*/\n\tget sourceInnerHTML () {\n\t\treturn typeof Object(this.source.input).html !== 'string'\n\t\t\t? ''\n\t\t: this.source.input.html.slice(\n\t\t\tthis.source.startOffset,\n\t\t\tthis.source.endOffset\n\t\t);\n\t}\n\n\t/**\n\t* Return the stringified outerHTML from the source input.\n\t* @returns {String}\n\t*/\n\tget sourceOuterHTML () {\n\t\treturn typeof Object(this.source.input).html !== 'string'\n\t\t\t? ''\n\t\t: this.source.input.html.slice(\n\t\t\tthis.source.startOffset,\n\t\t\tthis.source.endOffset\n\t\t);\n\t}\n\n\t/**\n\t* Return the current {@link Text} as a String.\n\t* @returns {String}\n\t* @example\n\t* text.textContent // returns ''\n\t*/\n\tget textContent () {\n\t\treturn String(this.data);\n\t}\n\n\t/**\n\t* Define the current {@link Text} from a String.\n\t* @returns {Void}\n\t* @example\n\t* text.textContent = 'Hello World'\n\t* text.textContent // 'Hello World'\n\t*/\n\tset textContent (textContent) {\n\t\tthis.data = String(textContent);\n\t}\n\n\t/**\n\t* Return a clone of the current {@link Text}.\n\t* @param {Object} settings - Custom settings applied to the cloned {@link Text}.\n\t* @returns {Text} - The cloned {@link Text}\n\t* @example\n\t* text.clone()\n\t* @example <caption>Clone the current text with new source.</caption>\n\t* text.clone({ source: { input: 'modified source' } })\n\t*/\n\tclone (settings) {\n\t\treturn new Text(Object.assign({}, this, settings, {\n\t\t\tsource: Object.assign({}, this.source, Object(settings).source)\n\t\t}));\n\t}\n\n\t/**\n\t* Return the current {@link Text} as a String.\n\t* @returns {String}\n\t* @example\n\t* text.toString() // returns ''\n\t*/\n\ttoString () {\n\t\treturn String(this.data);\n\t}\n\n\t/**\n\t* Return the current {@link Text} as a String.\n\t* @returns {String}\n\t* @example\n\t* text.toJSON() // returns ''\n\t*/\n\ttoJSON () {\n\t\treturn String(this.data);\n\t}\n}\n\nexport default Text;\n","import Comment from './Comment';\nimport Doctype from './Doctype';\nimport Element from './Element';\nimport Fragment from './Fragment';\nimport Node from './Node';\nimport Text from './Text';\n\nexport default function normalize (node) {\n\tconst nodeTypes = {\n\t\tcomment: Comment,\n\t\tdoctype: Doctype,\n\t\telement: Element,\n\t\tfragment: Fragment,\n\t\ttext: Text\n\t};\n\n\treturn node instanceof Node\n\t\t// Nodes are unchanged\n\t\t? node\n\t: node.type in nodeTypes\n\t\t// Strings are converted into Text nodes\n\t\t? new nodeTypes[node.type](node)\n\t// Node-like Objects with recognized types are normalized\n\t: new Text({ data: String(node) })\n}\n","import Fragment from './Fragment';\nimport Text from './Text';\nimport normalize from './normalize';\n\n// weak map of the parents of NodeLists\nconst parents = new WeakMap();\n\n/**\n* @name NodeList\n* @class\n* @extends Array\n* @classdesc Create a new {@link NodeList}.\n* @param {Container} parent - Parent containing the current {@link NodeList}.\n* @param {...Node} nodes - {@link Node}s belonging to the current {@link NodeList}.\n* @returns {NodeList}\n*/\nclass NodeList extends Array {\n\tconstructor (parent, ...nodes) {\n\t\tsuper();\n\n\t\tparents.set(this, parent);\n\n\t\tif (nodes.length) {\n\t\t\tthis.push(...nodes);\n\t\t}\n\t}\n\n\t/**\n\t* Return the innerHTML of the current {@link Container} as a String.\n\t* @returns {String}\n\t* @example\n\t* container.innerHTML // returns a string of innerHTML\n\t*/\n\tget innerHTML () {\n\t\treturn this.map(\n\t\t\tnode => node.type === 'text'\n\t\t\t\t? getInnerHtmlEncodedString(node.data)\n\t\t\t: 'outerHTML' in node\n\t\t\t\t? node.outerHTML\n\t\t\t: String(node)\n\t\t).join('');\n\t}\n\n\t/**\n\t* Define the nodes of the current {@link NodeList} from a String.\n\t* @param {String} innerHTML - Source being processed.\n\t* @returns {Void}\n\t* @example\n\t* nodeList.innerHTML = 'Hello <strong>world</strong>';\n\t* nodeList.length; // 2\n\t*/\n\tset innerHTML (innerHTML) {\n\t\tconst parent = this.parent;\n\n\t\tconst Result = Object(parent.result).constructor\n\n\t\tif (Result) {\n\t\t\tconst nodes = new Result(innerHTML).root.nodes;\n\n\t\t\tthis.splice(0, this.length, ...nodes);\n\t\t}\n\t}\n\n\t/**\n\t* Return the parent of the current {@link NodeList}.\n\t* @returns {Container}\n\t*/\n\tget parent () {\n\t\treturn parents.get(this);\n\t}\n\n\t/**\n\t* Return the text content of the current {@link NodeList} as a String.\n\t* @returns {String}\n\t*/\n\tget textContent () {\n\t\treturn this.map(\n\t\t\tnode => Object(node).textContent || ''\n\t\t).join('');\n\t}\n\n\t/**\n\t* Define the content of the current {@link NodeList} as a new {@link Text} {@link Node}.\n\t* @returns {String}\n\t*/\n\tset textContent (textContent) {\n\t\tthis.splice(0, this.length, new Text({ data: textContent }));\n\t}\n\n\t/**\n\t* Return a clone of the current {@link NodeList}.\n\t* @param {Object} parent - New parent containing the cloned {@link NodeList}.\n\t* @returns {NodeList} - The cloned NodeList\n\t*/\n\tclone (parent) {\n\t\treturn new NodeList(parent, ...this.map(node => node.clone({}, true)));\n\t}\n\n\t/**\n\t* Remove and return the last {@link Node} in the {@link NodeList}.\n\t* @returns {Node}\n\t*/\n\tpop () {\n\t\tconst [ remove ] = this.splice(this.length - 1, 1);\n\n\t\treturn remove;\n\t}\n\n\t/**\n\t* Add {@link Node}s to the end of the {@link NodeList} and return the new length of the {@link NodeList}.\n\t* @returns {Number}\n\t*/\n\tpush (...nodes) {\n\t\tconst parent = this.parent;\n\t\tconst inserts = nodes.filter(node => node !== parent);\n\n\t\tthis.splice(this.length, 0, ...inserts);\n\n\t\treturn this.length;\n\t}\n\n\t/**\n\t* Remove and return the first {@link Node} in the {@link NodeList}.\n\t* @returns {Node}\n\t*/\n\tshift () {\n\t\tconst [ remove ] = this.splice(0, 1);\n\n\t\treturn remove;\n\t}\n\n\t/**\n\t* Add and remove {@link Node}s to and from the {@link NodeList}.\n\t* @r