pagemod.js 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  1. /* This Source Code Form is subject to the terms of the Mozilla Public
  2. * License, v. 2.0. If a copy of the MPL was not distributed with this
  3. * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  4. "use strict";
  5. const { Cc, Ci, Cu } = require("chrome");
  6. const l10n = require("gcli/l10n");
  7. exports.items = [
  8. {
  9. name: "pagemod",
  10. description: l10n.lookup("pagemodDesc"),
  11. },
  12. {
  13. item: "command",
  14. runAt: "server",
  15. name: "pagemod replace",
  16. description: l10n.lookup("pagemodReplaceDesc"),
  17. params: [
  18. {
  19. name: "search",
  20. type: "string",
  21. description: l10n.lookup("pagemodReplaceSearchDesc"),
  22. },
  23. {
  24. name: "replace",
  25. type: "string",
  26. description: l10n.lookup("pagemodReplaceReplaceDesc"),
  27. },
  28. {
  29. name: "ignoreCase",
  30. type: "boolean",
  31. description: l10n.lookup("pagemodReplaceIgnoreCaseDesc"),
  32. },
  33. {
  34. name: "selector",
  35. type: "string",
  36. description: l10n.lookup("pagemodReplaceSelectorDesc"),
  37. defaultValue: "*:not(script):not(style):not(embed):not(object):not(frame):not(iframe):not(frameset)",
  38. },
  39. {
  40. name: "root",
  41. type: "node",
  42. description: l10n.lookup("pagemodReplaceRootDesc"),
  43. defaultValue: null,
  44. },
  45. {
  46. name: "attrOnly",
  47. type: "boolean",
  48. description: l10n.lookup("pagemodReplaceAttrOnlyDesc"),
  49. },
  50. {
  51. name: "contentOnly",
  52. type: "boolean",
  53. description: l10n.lookup("pagemodReplaceContentOnlyDesc"),
  54. },
  55. {
  56. name: "attributes",
  57. type: "string",
  58. description: l10n.lookup("pagemodReplaceAttributesDesc"),
  59. defaultValue: null,
  60. },
  61. ],
  62. // Make a given string safe to use in a regular expression.
  63. escapeRegex: function(aString) {
  64. return aString.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&");
  65. },
  66. exec: function(args, context) {
  67. let searchTextNodes = !args.attrOnly;
  68. let searchAttributes = !args.contentOnly;
  69. let regexOptions = args.ignoreCase ? "ig" : "g";
  70. let search = new RegExp(this.escapeRegex(args.search), regexOptions);
  71. let attributeRegex = null;
  72. if (args.attributes) {
  73. attributeRegex = new RegExp(args.attributes, regexOptions);
  74. }
  75. let root = args.root || context.environment.document;
  76. let elements = root.querySelectorAll(args.selector);
  77. elements = Array.prototype.slice.call(elements);
  78. let replacedTextNodes = 0;
  79. let replacedAttributes = 0;
  80. function replaceAttribute() {
  81. replacedAttributes++;
  82. return args.replace;
  83. }
  84. function replaceTextNode() {
  85. replacedTextNodes++;
  86. return args.replace;
  87. }
  88. for (let i = 0; i < elements.length; i++) {
  89. let element = elements[i];
  90. if (searchTextNodes) {
  91. for (let y = 0; y < element.childNodes.length; y++) {
  92. let node = element.childNodes[y];
  93. if (node.nodeType == node.TEXT_NODE) {
  94. node.textContent = node.textContent.replace(search, replaceTextNode);
  95. }
  96. }
  97. }
  98. if (searchAttributes) {
  99. if (!element.attributes) {
  100. continue;
  101. }
  102. for (let y = 0; y < element.attributes.length; y++) {
  103. let attr = element.attributes[y];
  104. if (!attributeRegex || attributeRegex.test(attr.name)) {
  105. attr.value = attr.value.replace(search, replaceAttribute);
  106. }
  107. }
  108. }
  109. }
  110. return l10n.lookupFormat("pagemodReplaceResult",
  111. [elements.length, replacedTextNodes,
  112. replacedAttributes]);
  113. }
  114. },
  115. {
  116. name: "pagemod remove",
  117. description: l10n.lookup("pagemodRemoveDesc"),
  118. },
  119. {
  120. item: "command",
  121. runAt: "server",
  122. name: "pagemod remove element",
  123. description: l10n.lookup("pagemodRemoveElementDesc"),
  124. params: [
  125. {
  126. name: "search",
  127. type: "string",
  128. description: l10n.lookup("pagemodRemoveElementSearchDesc"),
  129. },
  130. {
  131. name: "root",
  132. type: "node",
  133. description: l10n.lookup("pagemodRemoveElementRootDesc"),
  134. defaultValue: null,
  135. },
  136. {
  137. name: "stripOnly",
  138. type: "boolean",
  139. description: l10n.lookup("pagemodRemoveElementStripOnlyDesc"),
  140. },
  141. {
  142. name: "ifEmptyOnly",
  143. type: "boolean",
  144. description: l10n.lookup("pagemodRemoveElementIfEmptyOnlyDesc"),
  145. },
  146. ],
  147. exec: function(args, context) {
  148. let root = args.root || context.environment.document;
  149. let elements = Array.prototype.slice.call(root.querySelectorAll(args.search));
  150. let removed = 0;
  151. for (let i = 0; i < elements.length; i++) {
  152. let element = elements[i];
  153. let parentNode = element.parentNode;
  154. if (!parentNode || !element.removeChild) {
  155. continue;
  156. }
  157. if (args.stripOnly) {
  158. while (element.hasChildNodes()) {
  159. parentNode.insertBefore(element.childNodes[0], element);
  160. }
  161. }
  162. if (!args.ifEmptyOnly || !element.hasChildNodes()) {
  163. element.parentNode.removeChild(element);
  164. removed++;
  165. }
  166. }
  167. return l10n.lookupFormat("pagemodRemoveElementResultMatchedAndRemovedElements",
  168. [elements.length, removed]);
  169. }
  170. },
  171. {
  172. item: "command",
  173. runAt: "server",
  174. name: "pagemod remove attribute",
  175. description: l10n.lookup("pagemodRemoveAttributeDesc"),
  176. params: [
  177. {
  178. name: "searchAttributes",
  179. type: "string",
  180. description: l10n.lookup("pagemodRemoveAttributeSearchAttributesDesc"),
  181. },
  182. {
  183. name: "searchElements",
  184. type: "string",
  185. description: l10n.lookup("pagemodRemoveAttributeSearchElementsDesc"),
  186. },
  187. {
  188. name: "root",
  189. type: "node",
  190. description: l10n.lookup("pagemodRemoveAttributeRootDesc"),
  191. defaultValue: null,
  192. },
  193. {
  194. name: "ignoreCase",
  195. type: "boolean",
  196. description: l10n.lookup("pagemodRemoveAttributeIgnoreCaseDesc"),
  197. },
  198. ],
  199. exec: function(args, context) {
  200. let root = args.root || context.environment.document;
  201. let regexOptions = args.ignoreCase ? "ig" : "g";
  202. let attributeRegex = new RegExp(args.searchAttributes, regexOptions);
  203. let elements = root.querySelectorAll(args.searchElements);
  204. elements = Array.prototype.slice.call(elements);
  205. let removed = 0;
  206. for (let i = 0; i < elements.length; i++) {
  207. let element = elements[i];
  208. if (!element.attributes) {
  209. continue;
  210. }
  211. var attrs = Array.prototype.slice.call(element.attributes);
  212. for (let y = 0; y < attrs.length; y++) {
  213. let attr = attrs[y];
  214. if (attributeRegex.test(attr.name)) {
  215. element.removeAttribute(attr.name);
  216. removed++;
  217. }
  218. }
  219. }
  220. return l10n.lookupFormat("pagemodRemoveAttributeResult",
  221. [elements.length, removed]);
  222. }
  223. },
  224. // This command allows the user to export the page to HTML after DOM changes
  225. {
  226. name: "export",
  227. description: l10n.lookup("exportDesc"),
  228. },
  229. {
  230. item: "command",
  231. runAt: "server",
  232. name: "export html",
  233. description: l10n.lookup("exportHtmlDesc"),
  234. params: [
  235. {
  236. name: "destination",
  237. type: {
  238. name: "selection",
  239. data: [ "window", "stdout", "clipboard" ]
  240. },
  241. defaultValue: "window"
  242. }
  243. ],
  244. exec: function(args, context) {
  245. let html = context.environment.document.documentElement.outerHTML;
  246. if (args.destination === "stdout") {
  247. return html;
  248. }
  249. if (args.desination === "clipboard") {
  250. let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"]
  251. .getService(Ci.nsIClipboardHelper);
  252. clipboard.copyString(url);
  253. return '';
  254. }
  255. let url = "data:text/plain;charset=utf8," + encodeURIComponent(html);
  256. context.environment.window.open(url);
  257. return '';
  258. }
  259. }
  260. ];