|
|
@@ -29,7 +29,7 @@
|
|
|
|
|
|
/* global Node, NodeFilter */
|
|
|
|
|
|
-this.htmlnano = (() => {
|
|
|
+this.htmlnano = this.htmlnano || (() => {
|
|
|
|
|
|
// Source: https://github.com/kangax/html-minifier/issues/63
|
|
|
const booleanAttributes = [
|
|
|
@@ -76,6 +76,74 @@ this.htmlnano = (() => {
|
|
|
"visible"
|
|
|
];
|
|
|
|
|
|
+ const noWhitespaceCollapseElements = ["script", "style", "pre", "textarea"];
|
|
|
+
|
|
|
+ // Source: https://www.w3.org/TR/html4/sgml/dtd.html#events (Generic Attributes)
|
|
|
+ const safeToRemoveAttrs = [
|
|
|
+ "id",
|
|
|
+ "class",
|
|
|
+ "style",
|
|
|
+ "title",
|
|
|
+ "lang",
|
|
|
+ "dir",
|
|
|
+ "onclick",
|
|
|
+ "ondblclick",
|
|
|
+ "onmousedown",
|
|
|
+ "onmouseup",
|
|
|
+ "onmouseover",
|
|
|
+ "onmousemove",
|
|
|
+ "onmouseout",
|
|
|
+ "onkeypress",
|
|
|
+ "onkeydown",
|
|
|
+ "onkeyup"
|
|
|
+ ];
|
|
|
+
|
|
|
+ const redundantAttributes = {
|
|
|
+ "form": {
|
|
|
+ "method": "get"
|
|
|
+ },
|
|
|
+ "input": {
|
|
|
+ "type": "text"
|
|
|
+ },
|
|
|
+ "button": {
|
|
|
+ "type": "submit"
|
|
|
+ },
|
|
|
+ "script": {
|
|
|
+ "language": "javascript",
|
|
|
+ "type": "text/javascript",
|
|
|
+ // Remove attribute if the function returns false
|
|
|
+ "charset": node => {
|
|
|
+ // The charset attribute only really makes sense on “external” SCRIPT elements:
|
|
|
+ // http://perfectionkills.com/optimizing-html/#8_script_charset
|
|
|
+ return !node.getAttribute("src");
|
|
|
+ }
|
|
|
+ },
|
|
|
+ "style": {
|
|
|
+ "media": "all",
|
|
|
+ "type": "text/css"
|
|
|
+ },
|
|
|
+ "link": {
|
|
|
+ "media": "all"
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ const modules = [collapseBooleanAttributes, collapseWhitespace, removeComments, removeEmptyAttributes, removeRedundantAttributes];
|
|
|
+
|
|
|
+ return {
|
|
|
+ process: doc => {
|
|
|
+ const nodesWalker = doc.createTreeWalker(doc.documentElement, NodeFilter.SHOW_ALL, null, false);
|
|
|
+ let node = nodesWalker.nextNode();
|
|
|
+ while (node) {
|
|
|
+ const deletedNode = modules.find(module => module(node));
|
|
|
+ const previousNode = node;
|
|
|
+ node = nodesWalker.nextNode();
|
|
|
+ if (deletedNode) {
|
|
|
+ previousNode.remove();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
function collapseBooleanAttributes(node) {
|
|
|
if (node.nodeType == Node.ELEMENT_NODE) {
|
|
|
node.getAttributeNames().forEach(attributeName => {
|
|
|
@@ -86,8 +154,6 @@ this.htmlnano = (() => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- const noWhitespaceCollapseElements = ["script", "style", "pre", "textarea"];
|
|
|
-
|
|
|
/** Collapses redundant whitespaces */
|
|
|
function collapseWhitespace(node) {
|
|
|
if (node.nodeType == Node.TEXT_NODE) {
|
|
|
@@ -122,26 +188,6 @@ this.htmlnano = (() => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- // Source: https://www.w3.org/TR/html4/sgml/dtd.html#events (Generic Attributes)
|
|
|
- const safeToRemoveAttrs = [
|
|
|
- "id",
|
|
|
- "class",
|
|
|
- "style",
|
|
|
- "title",
|
|
|
- "lang",
|
|
|
- "dir",
|
|
|
- "onclick",
|
|
|
- "ondblclick",
|
|
|
- "onmousedown",
|
|
|
- "onmouseup",
|
|
|
- "onmouseover",
|
|
|
- "onmousemove",
|
|
|
- "onmouseout",
|
|
|
- "onkeypress",
|
|
|
- "onkeydown",
|
|
|
- "onkeyup"
|
|
|
- ];
|
|
|
-
|
|
|
/** Removes empty attributes */
|
|
|
function removeEmptyAttributes(node) {
|
|
|
if (node.nodeType == Node.ELEMENT_NODE) {
|
|
|
@@ -156,35 +202,6 @@ this.htmlnano = (() => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- const redundantAttributes = {
|
|
|
- "form": {
|
|
|
- "method": "get"
|
|
|
- },
|
|
|
- "input": {
|
|
|
- "type": "text"
|
|
|
- },
|
|
|
- "button": {
|
|
|
- "type": "submit"
|
|
|
- },
|
|
|
- "script": {
|
|
|
- "language": "javascript",
|
|
|
- "type": "text/javascript",
|
|
|
- // Remove attribute if the function returns false
|
|
|
- "charset": node => {
|
|
|
- // The charset attribute only really makes sense on “external” SCRIPT elements:
|
|
|
- // http://perfectionkills.com/optimizing-html/#8_script_charset
|
|
|
- return node.attrs && !node.attrs.src;
|
|
|
- }
|
|
|
- },
|
|
|
- "style": {
|
|
|
- "media": "all",
|
|
|
- "type": "text/css"
|
|
|
- },
|
|
|
- "link": {
|
|
|
- "media": "all"
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
/** Removes redundant attributes */
|
|
|
function removeRedundantAttributes(node) {
|
|
|
if (node.nodeType == Node.ELEMENT_NODE) {
|
|
|
@@ -200,19 +217,4 @@ this.htmlnano = (() => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- const modules = [collapseBooleanAttributes, collapseWhitespace, removeComments, removeEmptyAttributes, removeRedundantAttributes];
|
|
|
-
|
|
|
- return doc => {
|
|
|
- const nodesWalker = doc.createTreeWalker(doc.documentElement, NodeFilter.SHOW_ALL, null, false);
|
|
|
- let node = nodesWalker.nextNode();
|
|
|
- while (node) {
|
|
|
- const deletedNode = modules.find(module => module(node));
|
|
|
- const previousNode = node;
|
|
|
- node = nodesWalker.nextNode();
|
|
|
- if (deletedNode) {
|
|
|
- previousNode.remove();
|
|
|
- }
|
|
|
- }
|
|
|
- };
|
|
|
-
|
|
|
})();
|