spm_Track_Block_Tool

移除链接中的spm跟踪参数

当前为 2022-05-08 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name spm_Track_Block_Tool
  3. // @namespace _s7util__
  4. // @version 0.5.7
  5. // @description:en Remove [spm] track paramter in URL
  6. // @description 移除链接中的spm跟踪参数
  7. // @author shc0743
  8. // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
  9. // @grant none
  10. // @license GPL-3.0
  11. // @supportURL https://github.com/shc0743/MyUtility/issues/new
  12. // @run-at document-start
  13. // @match http*://*.bilibili.com/*
  14. // @match http*://*.baidu.com/*
  15. // @match http*://*.cctv.com/*
  16. // @match http*://*.taobao.com/*
  17. // @match http*://*.alibaba.com/*
  18. // @exclude http*://*.paypal.com/*
  19. // @exclude http*://*.alipay.com/*
  20. // ==/UserScript==
  21.  
  22. /*
  23. Description:
  24. 说明:
  25. This user script removes the spm paramter in <a href> elements.
  26. 此脚本移除 <a href> 元素中的spm参数。
  27. If it doesn't work, try refreshing it a few times or wait a while.
  28. 若无法生效,请尝试刷新几次或等一会。
  29. Examples:
  30. 示例:
  31. https://www.bilibili.com/video/av170001?spm_id_from=114514
  32. -> https://www.bilibili.com/video/av170001
  33. https://www.bilibili.com/video/av170001?query1=arg2&spm_id_from=114514&query2=data3
  34. -> https://www.bilibili.com/video/av170001?query1=arg2&query2=data3
  35. https://www.bilibili.com/video/av170001?spm=114514.1919810#hash
  36. -> https://www.bilibili.com/video/av170001#hash
  37. https://www.bilibili.com/video/av170001?spm=114514.1919810&query2=data3#hash1
  38. -> https://www.bilibili.com/video/av170001?query2=data3#hash1
  39. */
  40.  
  41. (function () {
  42. 'use strict';
  43.  
  44. // Your code here...
  45.  
  46. var track_args_list = [
  47. { 'domain': '*', 'keyword': 'spm' },
  48. { 'domain': '*', 'keyword': 'spm_id_from' },
  49. { 'domain': '*', 'keyword': 'from_source' },
  50. { 'domain': 'bilibili.com', 'keyword': 'from' },
  51. { 'domain': 'bilibili.com', 'keyword': 'seid' },
  52. { 'domain': 'baike.baidu.com', 'keyword': 'fr' },
  53. { 'domain': 'alibaba.com', 'keyword': 'tracelog' },
  54. ];
  55. var unwritable_list = [
  56. // https://greasyfork.org/zh-CN/scripts/443049/discussions/132536
  57. { object: window, key: 'goldlog' },
  58. ];
  59. try {
  60. for (let i of unwritable_list) {
  61. Object.defineProperty(i.object, i.key, {
  62. get() { return undefined },
  63. set(value) { void (value) },
  64. enumerable: false,
  65. configurable: true
  66. });
  67. }
  68. }
  69. catch (error) {
  70. console.warn(error);
  71. }
  72.  
  73. return (function (global) {
  74.  
  75. //var expr = /\?[\s\S]*spm/i;
  76.  
  77. /**
  78. * 去除字符串中的spm参数
  79. * @param {String} str URL to remove spm
  80. * @returns 去除spm后的结果
  81. */
  82. var remove_spm = function (str) {
  83. if (typeof (str) != 'string') return str;
  84. var newstr = '';
  85. var len = str.length;
  86. // 只去除查询参数部分,避免正常url被替换而导致404
  87. var hash_part_begin = str.indexOf('#');
  88. var query_part_begin = str.indexOf('?');
  89. if (query_part_begin == -1 ||
  90. (hash_part_begin != -1 && query_part_begin > hash_part_begin))
  91. { return str; } // 没有查询参数或?在#后面,直接返回
  92. newstr = str.substring(0, query_part_begin);
  93. var domain = '';
  94. {
  95. let index = str.indexOf('://');
  96. if (index + 1) {
  97. index = str.indexOf('/', index + 3);
  98. if (index + 1) {
  99. domain = str.substring(0, index);
  100. }
  101. }
  102. }
  103.  
  104. for (let i = query_part_begin, need_break; i < len; ++i) {
  105. for (let j = 0; j < track_args_list.length; ++j) {
  106. if (!(track_args_list[j].domain == '*' ||
  107. domain.indexOf(track_args_list[j].domain) != -1)) {
  108. need_break = false;
  109. break;
  110. }
  111. need_break = true;
  112. if (track_args_list[j].keyword == str.substring(i,
  113. i + track_args_list[j].keyword.length - 0)) {
  114. // 检测到
  115. while ((++i) < len) {
  116. if (str[i] == '&') { // 不能单独保留一个 & 号
  117. i++;
  118. break; // 去掉
  119. }
  120. if (str[i] == '#') break; // 保留hash部分
  121. }
  122. if (i == len) break; // 越界,直接break,以免url出现undefined
  123. }
  124. need_break = false;
  125. }
  126. if (need_break) break;
  127. newstr += str[i];
  128. }
  129.  
  130. var _lastchar;
  131. for (let i = 0; i < newstr.length; ++i) {
  132. _lastchar = newstr[newstr.length - 1];
  133. if (_lastchar == '?' || _lastchar == '&') { // 如果移除后只剩下 ? 或 &
  134. newstr = newstr.substring(0, newstr.length - 1); // 去掉
  135. } else break;
  136. }
  137. // Bug-Fix:
  138. // https://example.com/example?q1=arg&spm=123#hash1
  139. // -> https://example.com/example?q1=arg&#hash1
  140. // Invalid URL syntax at ^^
  141. newstr = newstr.replace(/\&\#/igm, '#');
  142. newstr = newstr.replace(/\?\#/igm, '#');
  143. return newstr;
  144. }
  145. var test_spm = function (str) {
  146. for (let tracker of track_args_list)
  147. if (new RegExp(tracker, 'i').test(str))
  148. return true;
  149. return false;
  150. };
  151. var _realwindowopen = window.open;
  152. var _realhistorypushState = window.history.pushState;
  153. var _realhistoryreplaceState = window.history.replaceState;
  154. var _realaconstructor = window.HTMLAnchorElement.prototype.constructor;
  155.  
  156. var _link_click_test = function (val) {
  157. if (/\#/.test(val)) return true;
  158. if (/javascript\:/i.test(val)) return true;
  159. return false;
  160. };
  161. var _link_click = function (event) {
  162. if (_link_click_test(this.href)) return;
  163. event.preventDefault();
  164. // 防止被再次加入spm
  165. this.href = remove_spm(this.href);
  166. _realwindowopen(this.href, this.target || '_self');
  167. return false;
  168. };
  169. var _link_mouseover = function () {
  170. if (test_spm(this.href))
  171. this.href = remove_spm(this.href);
  172. };
  173. var linkclickhandlerinit = function () {
  174. var el = document.querySelectorAll('a[href]');
  175. for (let i = el.length - 1; i >= 0; --i) {
  176. if (test_spm(el[i].href)) {
  177. // 链接已经被加入spm , 需要移除
  178. el[i].href = remove_spm(el[i].href);
  179. }
  180. el[i].removeEventListener('click', _link_click);
  181. el[i].addEventListener('click', _link_click, true);
  182. el[i].removeEventListener('mouseover', _link_mouseover);
  183. el[i].addEventListener('mouseover', _link_mouseover, false);
  184. }
  185. };
  186. {
  187. try {
  188. let wopen = function (url, target, features) {
  189. return _realwindowopen.call(window,
  190. remove_spm(url),
  191. target,
  192. features);
  193. };
  194. let hp = function (data, title, url) {
  195. return _realhistorypushState.call(
  196. window.history, data, title,
  197. remove_spm(url));
  198. };
  199. let hr = function (data, title, url) {
  200. return _realhistoryreplaceState.call(
  201. window.history, data, title,
  202. remove_spm(url));
  203. };
  204. let a_constructor = function () {
  205. var href = "";
  206. let obj = _realaconstructor.apply(this, arguments);
  207. try {
  208. Object.defineProperty(obj, 'href', {
  209. get() { return href },
  210. set(val) { href = remove_spm(val) },
  211. enumerable: true,
  212. configurable: true
  213. });
  214. }
  215. catch (err) {
  216. console.warn('Error creating element:', err);
  217. }
  218. return obj;
  219. };
  220. wopen.toString =
  221. hp.toString =
  222. hr.toString =
  223. new Function("return 'function () {\n [native code]\n}'");
  224. // 必须定义成 writable 否则一些网站(例如B站收藏夹页面)会出错
  225. Object.defineProperty(window, 'open', {
  226. value: wopen,
  227. writable: true,
  228. enumerable: true,
  229. configurable: true
  230. }); // 重定义window.open 以阻止弹出窗口中的spm
  231. Object.defineProperty(window.history, 'pushState', {
  232. value: hp,
  233. writable: true,
  234. enumerable: true,
  235. configurable: true
  236. }); // 重定义history.pushState
  237. Object.defineProperty(window.history, 'replaceState', {
  238. value: hr,
  239. writable: true,
  240. enumerable: true,
  241. configurable: true
  242. }); // 重定义history.replaceState
  243. Object.defineProperty(window.HTMLAnchorElement.prototype,
  244. 'constructor', {
  245. value: a_constructor,
  246. writable: true,
  247. enumerable: true,
  248. configurable: true
  249. }); // 替代setInterval
  250.  
  251. }
  252. catch (error) {
  253. console.warn("This browser doesn't support redefining" +
  254. " window.open , so [SpmBlockTool] cannot remove" +
  255. " spm in popup window.\nError:", error);
  256. }
  257. }
  258. window.addEventListener('load', function () {
  259. // window.setInterval(linkclickhandlerinit, 5000);
  260. window.setTimeout(linkclickhandlerinit, 1000);
  261. window.setTimeout(linkclickhandlerinit, 500);
  262. window.setTimeout(linkclickhandlerinit, 1);
  263. });
  264.  
  265. // 移除当前页面的spm
  266. // 当然,实际上spm已经在userscript加载前被发送到服务器,
  267. // 所以该功能仅美化url.
  268. // 如果要禁用该功能,删除下面一行开头的斜杠。
  269. //if(0)
  270. // Remove spm from current page
  271. // Of course, in fact, spm has been sent to the server
  272. // before userscript is loaded, so this function only beautifies the URL.
  273. // If you want to disable this feature, remove the slash
  274. // at the beginning of the following line:
  275. //if(0)
  276. if (test_spm(location.href)) {
  277. _realhistoryreplaceState.call(window.history,
  278. {}, document.title,
  279. remove_spm(location.href));
  280. }
  281.  
  282. /*
  283. // 测试代码
  284. var test_urls = [
  285. 'https://www.bilibili.com/video/BV18X4y1N7Yh',
  286. 'https://www.bilibili.com/video/BV18X4y1N7Yh?spm_id_from=114514',
  287. 'https://www.bilibili.com/video/BV18X4y1N7Yh?spm=114514.1919810',
  288. 'https://www.bilibili.com/video/BV18X4y1N7Yh?spm_id_from=114514.123',
  289.  
  290. 'https://www.bilibili.com/video/av170001',
  291. 'https://www.bilibili.com/video/av170001?spm_id_from=114514',
  292. 'https://www.bilibili.com/video/av170001?spm=114514.1919810',
  293. 'https://www.bilibili.com/video/av170001?spm_id_from=114514.123',
  294.  
  295. 'https://www.bilibili.com/video/av170001?query1=arg2&spm_id_from=114514',
  296. 'https://www.bilibili.com/video/av170001?query1=arg2&spm=114514.1919810',
  297. 'https://www.bilibili.com/video/av170001?query1=arg2&spm_id_from=114514.123',
  298. 'https://www.bilibili.com/video/av170001?query1=arg2&spm_id_from=114514',
  299. 'https://www.bilibili.com/video/av170001?query1=arg2&spm=114514.1919810',
  300. 'https://www.bilibili.com/video/av170001?query1=arg2&spm_id_from=114514.123',
  301.  
  302. 'https://www.bilibili.com/video/av170001?query1=arg2&spm_id_from=114514&query2=data3',
  303. 'https://www.bilibili.com/video/av170001?query1=arg2&spm=114514.1919810&query2=data3',
  304.  
  305. 'https://www.bilibili.com/video/av170001?spm_id_from=114514#hash',
  306. 'https://www.bilibili.com/video/av170001?query1=arg2&spm_id_from=114514#hash1',
  307. 'https://www.bilibili.com/video/av170001?query1=arg2&spm=114514.1919810#hash1',
  308.  
  309. 'https://www.bilibili.com/video/av170001?spm_id_from=114514&query2=data3#hash1',
  310. 'https://www.bilibili.com/video/av170001?spm=114514.1919810&query2=data3#hash1',
  311. ];
  312. for(let i=0;i<test_urls.length;++i){
  313. let el=document.createElement('a');
  314. el.href=test_urls[i];
  315. el.innerHTML=i+1 + '';
  316. document.documentElement.appendChild(el);
  317. }
  318. for(let i=0;i<test_urls.length;++i){
  319. let el=document.createElement('a');
  320. el.href=test_urls[i];
  321. el.innerHTML=i+1 + ' blank';
  322. el.target='_blank';
  323. document.documentElement.appendChild(el);
  324. }
  325. */
  326.  
  327. })(window);
  328.  
  329. })();