Greasy Fork is available in English.

highlight-my-interest

highlight keywords in my favorites

  1. // ==UserScript==
  2. // @name highlight-my-interest
  3. // @name:zh-CN 高亮关键词
  4. // @description highlight keywords in my favorites
  5. // @description:zh-CN 高亮特定网页中感兴趣的关键词
  6. // @version 0.3.2
  7. // @author jferroal
  8. // @license GPL-3.0
  9. // @grant GM_xmlhttpRequest
  10. // @require https://greasyfork.org/scripts/31793-jmul/code/JMUL.js?version=209567
  11. // @include http://*
  12. // @include https://*
  13. // @run-at document-end
  14. // @namespace https://greasyfork.org/users/34556-jferroal
  15. // ==/UserScript==
  16.  
  17. (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
  18. let JMUL = window.JMUL || {}
  19.  
  20. const Map = (list, fn) => {
  21. let result = []
  22. if (list && list.length) {
  23. for (let i = 0; i < list.length; i += 1) {
  24. result.push(fn(list[i]))
  25. }
  26. }
  27. return result
  28. }
  29.  
  30. class TextElement {
  31. constructor(element) {
  32. this.element = new JMUL.Element(element)
  33. this.innerText = this.element.innerText
  34. this.shouldHighlight = false
  35. }
  36.  
  37. highlight() {
  38. for (const keyword of TextElement.keywords) {
  39. const keywordPattern = new RegExp(keyword.str, 'gi')
  40. if (keywordPattern.test(this.innerText)) {
  41. this.shouldHighlight = true
  42. this.element.setCss(TextElement.highlightStyle[keyword.type || 'default'])
  43. this.element.setAttribute('title', keyword.title)
  44. }
  45. }
  46. }
  47.  
  48. static init(setting) {
  49. TextElement.highlightStyle = {}
  50. Object.keys(setting.color).forEach((type) => {
  51. TextElement.highlightStyle[type] = {
  52. background: setting.color[type].bg,
  53. color: setting.color[type].text,
  54. }
  55. })
  56. }
  57.  
  58. static setKeywords(keywords) {
  59. TextElement.keywords = keywords
  60. }
  61.  
  62. static findAll() {
  63. return TextElement.targetTagNames.reduce((res, tagName) => {
  64. const tags = document.getElementsByTagName(tagName)
  65. return res.concat(Map(tags, (e) => new TextElement(e)))
  66. }, [])
  67. }
  68. }
  69.  
  70. TextElement.targetTagNames = ['h1', 'h2', 'h3', 'h4', 'h5', 'p', 'a', 'pre', 'blockquote', 'summary']
  71. module.exports = TextElement
  72.  
  73. },{}],2:[function(require,module,exports){
  74. const KeywordService = require('./keyword.service');
  75. const SettingService = require('./setting.service');
  76. const TextElement = require('./element');
  77.  
  78. const Config = {};
  79.  
  80. (function () {
  81. let highlightedCount = 0;
  82. const href = window.location.href;
  83. loadSetting().then((setting) => {
  84. KeywordService.init(setting, href);
  85. TextElement.init(setting);
  86. highlight()
  87. });
  88. window.addEventListener('scroll', highlight);
  89.  
  90. function loadSetting () {
  91. SettingService.init(Config);
  92. return SettingService.load();
  93. }
  94.  
  95. function highlight () {
  96. const elements = TextElement.findAll();
  97. if (elements.length === highlightedCount) return;
  98. KeywordService.list().then((keywords) => {
  99. TextElement.setKeywords(keywords);
  100. elements.map((e) => e.highlight());
  101. highlightedCount = elements.length;
  102. });
  103. }
  104. })();
  105.  
  106. },{"./element":1,"./keyword.service":3,"./setting.service":5}],3:[function(require,module,exports){
  107. class KeywordService {
  108. static init (setting, href) {
  109. KeywordService.Setting = setting;
  110. KeywordService.keywords = [];
  111. const sites = Object.keys(KeywordService.Setting.sites);
  112. if (!sites || !sites.length) return;
  113. sites.forEach((site) => {
  114. const sitePattern = new RegExp(site, 'gi');
  115. if (sitePattern.test(href)) {
  116. KeywordService.keywords.push(...KeywordService.Setting.sites[ site ]);
  117. }
  118. });
  119. }
  120.  
  121. static list () {
  122. return Promise.resolve(KeywordService.keywords);
  123. }
  124. }
  125.  
  126. module.exports = KeywordService;
  127.  
  128. },{}],4:[function(require,module,exports){
  129. class Setting {
  130. constructor (jsonBody) {
  131. Object.assign(this, jsonBody);
  132. }
  133. }
  134.  
  135. module.exports = { Setting };
  136.  
  137. },{}],5:[function(require,module,exports){
  138. const Setting = require('./setting').Setting
  139. const {Request} = window.JMUL || {JMUL: {}}
  140.  
  141. const DefaultKeywords = [
  142. {
  143. "str": "react",
  144. "title": "react",
  145. "type": "programming"
  146. },
  147. {
  148. "str": "爬虫",
  149. "title": "爬虫",
  150. "type": "programming"
  151. },
  152. {
  153. "str": "数据",
  154. "title": "数据"
  155. },
  156. {
  157. 'str': 'android',
  158. 'title': 'android',
  159. 'type': 'programming'
  160. },
  161. {
  162. 'str': 'javascript',
  163. 'title': 'javascript',
  164. 'type': 'programming'
  165. },
  166. {
  167. 'str': 'flask',
  168. 'title': 'flask',
  169. 'type': 'programming'
  170. },
  171. {
  172. 'str': '框架',
  173. 'title': '框架'
  174. },
  175. {
  176. 'str': '限免',
  177. 'title': '限免',
  178. 'type': 'software'
  179. },
  180. {
  181. 'str': '智能家居',
  182. 'title': '智能家居'
  183. },
  184. {
  185. 'str': '笔记',
  186. 'title': '笔记'
  187. },
  188. {
  189. 'str': '手账',
  190. 'title': '手账'
  191. },
  192. {
  193. 'str': 'google',
  194. 'title': 'google'
  195. },
  196. {
  197. 'str': '谷歌',
  198. 'title': '谷歌'
  199. },
  200. {
  201. 'str': '书籍',
  202. 'title': '书籍'
  203. }
  204. ]
  205.  
  206. const DefaultResponseHandler = (_response) => {
  207. let response = _response
  208. if (typeof _response === 'object' && _response.responseText) {
  209. response = _response.responseText
  210. }
  211. return new Setting(JSON.parse(response))
  212. }
  213.  
  214. class SettingService {
  215. static init(config) {
  216. SettingService.loadUrl = config.loadUrl
  217. SettingService.method = config.method || 'GET'
  218. SettingService.contentType = config.contentType || 'application/json'
  219. SettingService.data = config.data || {}
  220. SettingService.resHandler = config.resHandler || DefaultResponseHandler
  221. }
  222.  
  223. static load() {
  224. if (!SettingService.loadUrl) return Promise.resolve(SettingService.DefaultSetting)
  225. const request = new Request({headers: {'content-type': SettingService.contentType}})
  226. request.setUrl(SettingService.loadUrl)
  227. request.setMethod(SettingService.method)
  228. request.setData(SettingService.data)
  229. return request.send().then((response) => {
  230. return SettingService.resHandler(response.responseText)
  231. })
  232. }
  233. }
  234.  
  235. SettingService.DefaultSetting = {
  236. color: {
  237. default: {
  238. bg: '#FFDA5E',
  239. text: 'black'
  240. }
  241. },
  242. sites: {
  243. 'https://sspai.com/*': DefaultKeywords,
  244. 'https://toutiao.io/*': DefaultKeywords,
  245. 'http://www.inoreader.com/*': DefaultKeywords,
  246. },
  247. }
  248.  
  249. module.exports = SettingService
  250.  
  251. },{"./setting":4}]},{},[2]);