common.js 3.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. /*
  2. * Copyright 2018 Gildas Lormeau
  3. * contact : gildas.lormeau <at> gmail.com
  4. *
  5. * This file is part of SingleFile.
  6. *
  7. * SingleFile is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU Lesser General Public License as published by
  9. * the Free Software Foundation, either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * SingleFile is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public License
  18. * along with SingleFile. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. this.common = this.common || (() => {
  21. const REMOVED_CONTENT_ATTRIBUTE_NAME = "data-single-file-removed-content";
  22. const PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME = "data-single-file-preserved-space-element";
  23. return {
  24. preProcessDoc,
  25. postProcessDoc
  26. };
  27. function preProcessDoc(doc, win, options) {
  28. doc.querySelectorAll("script").forEach(element => element.textContent = element.textContent.replace(/<\/script>/gi, "<\\/script>"));
  29. doc.head.querySelectorAll("noscript").forEach(element => {
  30. const disabledNoscriptElement = doc.createElement("disabled-noscript");
  31. Array.from(element.childNodes).forEach(node => disabledNoscriptElement.appendChild(node));
  32. disabledNoscriptElement.hidden = true;
  33. element.parentElement.replaceChild(disabledNoscriptElement, element);
  34. });
  35. doc.head.querySelectorAll("*:not(base):not(link):not(meta):not(noscript):not(script):not(style):not(template):not(title)").forEach(element => element.hidden = true);
  36. if (options.removeHiddenElements) {
  37. doc.querySelectorAll("html > body *:not(style):not(script):not(link):not(frame):not(iframe):not(object)").forEach(element => {
  38. const style = win.getComputedStyle(element);
  39. if (element instanceof win.HTMLElement && (element.hidden || style.display == "none" || ((style.opacity === 0 || style.visibility == "hidden") && !element.clientWidth && !element.clientHeight)) && !element.querySelector("iframe, frame, object[type=\"text/html\"][data]")) {
  40. element.setAttribute(REMOVED_CONTENT_ATTRIBUTE_NAME, "");
  41. }
  42. });
  43. }
  44. if (options.compressHTML) {
  45. doc.querySelectorAll("*").forEach(element => {
  46. const style = win.getComputedStyle(element);
  47. if (style.whiteSpace.startsWith("pre")) {
  48. element.setAttribute(PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME, "");
  49. }
  50. });
  51. }
  52. }
  53. function postProcessDoc(doc, options) {
  54. doc.head.querySelectorAll("disabled-noscript").forEach(element => {
  55. const noscriptElement = doc.createElement("noscript");
  56. Array.from(element.childNodes).forEach(node => noscriptElement.appendChild(node));
  57. element.parentElement.replaceChild(noscriptElement, element);
  58. });
  59. doc.head.querySelectorAll("*:not(base):not(link):not(meta):not(noscript):not(script):not(style):not(template):not(title)").forEach(element => element.removeAttribute("hidden"));
  60. if (options.removeHiddenElements) {
  61. doc.querySelectorAll("[" + REMOVED_CONTENT_ATTRIBUTE_NAME + "]").forEach(element => element.removeAttribute(REMOVED_CONTENT_ATTRIBUTE_NAME));
  62. }
  63. if (options.compressHTML) {
  64. doc.querySelectorAll("[" + PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME + "]").forEach(element => element.removeAttribute(PRESERVED_SPACE_ELEMENT_ATTRIBUTE_NAME));
  65. }
  66. }
  67. })();