infobar.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  1. /*
  2. * Copyright 2010-2019 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. /* global browser, document, Node, window, top, getComputedStyle, location, setTimeout */
  21. this.singlefile.infobar = this.singlefile.infobar || (() => {
  22. const INFOBAR_TAGNAME = "singlefile-infobar";
  23. const LINK_ICON = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAABmJLR0QABQDuAACS38mlAAAACXBIWXMAACfuAAAn7gExzuVDAAAAB3RJTUUH4ggCDDcMnYqGGAAAATtJREFUOMvNk19LwlAYxp+zhOoqpxJ1la3patFVINk/oRDBLuyreiPFMmcj/QQRSOOwpEINDCpwRr7d1HBMc4sufO7Oe877e5/zcA4wbWLDi8urGr2+vXsOFfJZdnPboDtuueoRcQEH6RQDgNBP8bxcpfvmA0QxPHF6u/MMInLVHFDP7kMUwyjks2xU8+ZGkgGAbtSp1e5gRhBc+0KQHHSjTg2TY0tVEItF/wYqV6+pYXKoiox0atvjOuQXYnILqiJj/ztceXUlGEirGGRyC0pCciDDmfm6mlYxiFtNKAkJmb0dV2OxpFGxpNFE0NmFTtxqQpbiHsgojQX1bBuyFMfR4S7zk+PYjE5PcizI0xD+6685jubnZvH41MJwgL+p233B8tKiF7SeXMPnYIB+/8OXg2hERO44wzC1+gJYGGpVbtoqiAAAAABJRU5ErkJggg==";
  24. const IMAGE_ICON = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAACNiR0NAAABIUlEQVQ4y+2TsarCMBSGvxTBRdqiUZAWOrhJB9EXcPKFfCvfQYfulUKHDqXg4CYUJSioYO4mSDX3ttzt3n87fMlHTpIjlsulxpDZbEYYhgghSNOUOI5Ny2mZYBAELBYLer0eAJ7ncTweKYri4x7LJJRS0u12n7XrukgpjSc0CpVSXK/XZ32/31FKNW85z3PW6zXT6RSAJEnIsqy5UGvNZrNhu90CcDqd+C6tT6J+v//2Th+PB2VZ1hN2Oh3G4zGTyQTbtl/YbrdjtVpxu91+Ljyfz0RRhG3bzOfzF+Y4TvNXvlwuaK2pE4tfzr/wzwsty0IIURlL0998KxRCMBqN8H2/wlzXJQxD2u12vVkeDoeUZUkURRU+GAw4HA7s9/sK+wK6CWHasQ/S/wAAAABJRU5ErkJggg==";
  25. const SINGLEFILE_COMMENT = "SingleFile";
  26. if (window == top && location && location.href && location.href.startsWith("file:///")) {
  27. if (document.readyState == "loading") {
  28. document.addEventListener("DOMContentLoaded", displayIcon, false);
  29. } else {
  30. displayIcon();
  31. }
  32. }
  33. return true;
  34. async function displayIcon() {
  35. let singleFileComment = document.documentElement.childNodes[0];
  36. if (!isSingleFileComment(singleFileComment)) {
  37. singleFileComment = findSingleFileComment();
  38. }
  39. if (singleFileComment) {
  40. const info = singleFileComment.textContent.split("\n");
  41. const [, , url, saveDate, ...infoData] = info;
  42. const options = await browser.runtime.sendMessage({ loadFileURI: true, url });
  43. if (options.displayInfobar) {
  44. initInfobar(url, saveDate, infoData);
  45. }
  46. }
  47. }
  48. function findSingleFileComment(node = document.documentElement) {
  49. return node.childNodes && node.childNodes.length ? Array.from(node.childNodes).find(findSingleFileComment) : isSingleFileComment(node);
  50. }
  51. function isSingleFileComment(node) {
  52. return node.nodeType == Node.COMMENT_NODE && node.textContent.includes(SINGLEFILE_COMMENT);
  53. }
  54. function initInfobar(url, saveDate, infoData) {
  55. let infobarElement = document.querySelector(INFOBAR_TAGNAME);
  56. if (!infobarElement) {
  57. url = url.split("url: ")[1];
  58. saveDate = saveDate.split("saved date: ")[1];
  59. if (infoData && infoData.length > 1) {
  60. let content = infoData[0].split("info: ")[1].trim();
  61. for (let indexLine = 1; indexLine < infoData.length - 1; indexLine++) {
  62. content += "\n" + infoData[indexLine].trim();
  63. }
  64. infoData = content.trim();
  65. } else {
  66. infoData = saveDate;
  67. }
  68. infobarElement = createElement(INFOBAR_TAGNAME, document.body);
  69. infobarElement.style.setProperty("background-color", "#f9f9f9", "important");
  70. infobarElement.style.setProperty("display", "block", "important");
  71. infobarElement.style.setProperty("position", "fixed", "important");
  72. infobarElement.style.setProperty("top", "16px", "important");
  73. infobarElement.style.setProperty("right", "16px", "important");
  74. infobarElement.style.setProperty("height", "auto", "important");
  75. infobarElement.style.setProperty("min-height", "24px", "important");
  76. infobarElement.style.setProperty("min-width", "24px", "important");
  77. infobarElement.style.setProperty("background-position", "center", "important");
  78. infobarElement.style.setProperty("background-repeat", "no-repeat", "important");
  79. infobarElement.style.setProperty("border-radius", "16px", "important");
  80. infobarElement.style.setProperty("z-index", 2147483647, "important");
  81. infobarElement.style.setProperty("text-align", "center", "important");
  82. infobarElement.style.setProperty("will-change", "opacity, padding-left, padding-right, width, background-color, color", "important");
  83. infobarElement.style.setProperty("margin", "0 0 0 16px", "important");
  84. const infoElement = createElement("span", infobarElement);
  85. infoElement.style.setProperty("font-family", "Arial", "important");
  86. infoElement.style.setProperty("color", "#9aa0a6", "important");
  87. infoElement.style.setProperty("font-size", "14px", "important");
  88. infoElement.style.setProperty("line-height", "24px", "important");
  89. infoElement.style.setProperty("word-break", "break-word", "important");
  90. infoElement.style.setProperty("white-space", "pre-wrap", "important");
  91. infoElement.textContent = infoData;
  92. const linkElement = createElement("a", infobarElement);
  93. linkElement.style.setProperty("display", "inline-block", "important");
  94. linkElement.style.setProperty("padding-left", "8px", "important");
  95. linkElement.style.setProperty("line-height", "24px", "important");
  96. linkElement.style.setProperty("cursor", "pointer", "important");
  97. linkElement.style.setProperty("user-select", "none", "important");
  98. linkElement.target = "_blank";
  99. linkElement.rel = "noopener noreferrer";
  100. linkElement.title = "Open source URL: " + url;
  101. linkElement.href = url;
  102. const imgElement = createElement("img", linkElement);
  103. imgElement.style.setProperty("vertical-align", "middle", "important");
  104. imgElement.style.setProperty("padding-bottom", "2px", "important");
  105. imgElement.style.setProperty("-webkit-padding-after", "2px", "important");
  106. imgElement.style.setProperty("padding-left", "2px", "important");
  107. imgElement.style.setProperty("-webkit-padding-start", "2px", "important");
  108. imgElement.style.setProperty("cursor", "pointer", "important");
  109. infobarElement.style.setProperty("text-align", "right", "important");
  110. imgElement.src = LINK_ICON;
  111. hideInfobar(infobarElement, linkElement, infoElement);
  112. infobarElement.onmouseover = () => infobarElement.style.setProperty("opacity", 1, "important");
  113. document.addEventListener("click", event => {
  114. if (event.button === 0) {
  115. let element = event.target;
  116. while (element && element != infobarElement) {
  117. element = element.parentElement;
  118. }
  119. if (element != infobarElement) {
  120. hideInfobar(infobarElement, linkElement, infoElement);
  121. }
  122. }
  123. });
  124. setTimeout(() => {
  125. infobarElement.style.setProperty("transition-property", "opacity", "important");
  126. infobarElement.style.setProperty("transition-duration", "250ms", "important");
  127. });
  128. }
  129. }
  130. function displayInfobar(infobarElement, linkElement, infoElement) {
  131. infobarElement.style.setProperty("font-size", "13px", "important");
  132. infobarElement.style.setProperty("opacity", 1, "important");
  133. infobarElement.style.setProperty("width", "auto", "important");
  134. infobarElement.style.setProperty("background-color", "#f9f9f9", "important");
  135. infobarElement.style.setProperty("cursor", "auto", "important");
  136. infobarElement.style.setProperty("color", "#9aa0a6", "important");
  137. infobarElement.style.setProperty("padding-left", "12px", "important");
  138. infobarElement.style.setProperty("padding-right", "12px", "important");
  139. infobarElement.style.setProperty("-webkit-padding-start", "12px", "important");
  140. infobarElement.style.setProperty("-webkit-padding-end", "12px", "important");
  141. infobarElement.style.setProperty("border", "2px solid #555", "important");
  142. infobarElement.style.setProperty("-webkit-border-start", "2px solid #555", "important");
  143. infobarElement.style.setProperty("-webkit-border-before", "2px solid #555", "important");
  144. infobarElement.style.setProperty("-webkit-border-end", "2px solid #555", "important");
  145. infobarElement.style.setProperty("-webkit-border-after", "2px solid #555", "important");
  146. infobarElement.style.setProperty("background-image", "none");
  147. infoElement.style.setProperty("display", "inline-block", "important");
  148. linkElement.style.setProperty("display", "inline-block", "important");
  149. infobarElement.onclick = null;
  150. infobarElement.onmouseout = null;
  151. }
  152. function hideInfobar(infobarElement, linkElement, infoElement) {
  153. infobarElement.style.opacity = .7;
  154. infobarElement.onmouseout = () => infobarElement.style.opacity = .7;
  155. infobarElement.style.setProperty("width", "24px", "important");
  156. infobarElement.style.setProperty("background-color", "#737373", "important");
  157. infobarElement.style.setProperty("cursor", "pointer", "important");
  158. infobarElement.style.setProperty("color", "white", "important");
  159. infobarElement.style.setProperty("padding-left", 0, "important");
  160. infobarElement.style.setProperty("padding-right", 0, "important");
  161. infobarElement.style.setProperty("-webkit-padding-start", 0, "important");
  162. infobarElement.style.setProperty("-webkit-padding-end", 0, "important");
  163. infobarElement.style.setProperty("border", "2px solid #eee", "important");
  164. infobarElement.style.setProperty("-webkit-border-start", "2px solid #eee", "important");
  165. infobarElement.style.setProperty("-webkit-border-before", "2px solid #eee", "important");
  166. infobarElement.style.setProperty("-webkit-border-end", "2px solid #eee", "important");
  167. infobarElement.style.setProperty("-webkit-border-after", "2px solid #eee", "important");
  168. infobarElement.style.setProperty("background-image", "url(" + IMAGE_ICON + ")");
  169. infobarElement.style.setProperty("background-size", "70% 70%", "important");
  170. linkElement.style.setProperty("display", "none", "important");
  171. infoElement.style.setProperty("display", "none", "important");
  172. infobarElement.onclick = event => {
  173. if (event.button === 0) {
  174. displayInfobar(infobarElement, linkElement, infoElement);
  175. return false;
  176. }
  177. };
  178. }
  179. function createElement(tagName, parentElement) {
  180. const element = document.createElement(tagName);
  181. parentElement.appendChild(element);
  182. Array.from(getComputedStyle(element)).forEach(property => element.style.setProperty(property, "initial", "important"));
  183. return element;
  184. }
  185. })();