remove google tracking UWAA

remove google tracking

当前为 2017-07-13 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @namespace jp.sceneq.rgtuwaaa
  3.  
  4. // @name remove google tracking UWAA
  5.  
  6. // @description remove google tracking
  7. // @description:ja Google追跡UWAAを取り除きなさい
  8.  
  9. // @homepageURL https://github.com/sceneq/RemoveGoogleTracking
  10.  
  11. // @version 0.6
  12. // @include https://www.google.*/*
  13. // @grant none
  14. // @run-at document-start
  15.  
  16. // @author sceneq
  17. // @license MIT
  18. // ==/UserScript==
  19.  
  20. 'use strict';
  21.  
  22. const yesman = function() {
  23. return true;
  24. };
  25. const tired = function() {};
  26.  
  27. // matching tracking paramaters
  28. const badParametersNames = [
  29. 'biw',
  30. 'bih',
  31. 'ei',
  32. 'sa',
  33. 'ved',
  34. 'source',
  35. 'prmd',
  36. 'bvm',
  37. 'bav',
  38. 'psi',
  39. 'stick',
  40. 'dq',
  41. 'ech',
  42.  
  43. // image search
  44. 'scroll',
  45. 'vet',
  46. 'yv',
  47. 'ijn',
  48. 'iact',
  49. 'forward',
  50. 'ndsp',
  51. 'csi',
  52. 'tbnid',
  53. 'docid',
  54. //'imgdii', // related images
  55.  
  56. // search form
  57. 'pbx',
  58. 'dpr',
  59. 'pf',
  60. 'gs_rn',
  61. 'gs_mss',
  62. 'pq',
  63. 'cp',
  64. 'oq',
  65. 'sclient',
  66. 'gs_l',
  67. 'aqs',
  68. //'gs_ri', // suggestions
  69. //'gs_id', // suggestions
  70. //'xhr', // suggestions at image search
  71. //'tch', // js flag?
  72.  
  73. // mobile
  74. 'gs_gbg',
  75. 'gs_rn',
  76. 'cp'
  77. ];
  78. const badAttrNamesObj = {
  79. default: ['onmousedown', 'ping', 'oncontextmenu'],
  80. search: ['onmousedown', 'ping', 'oncontextmenu'],
  81. vid: ['onmousedown'],
  82. isch: []
  83. };
  84.  
  85. // From the nodes selected here, delete parameters specified by badParametersNames
  86. const dirtyLinkSelectors = [
  87. // menu
  88. 'a.q.qs',
  89.  
  90. // doodle
  91. 'a.doodle'
  92. ];
  93.  
  94. const badPaths = ['imgevent'];
  95.  
  96. /* Compile */
  97. // The first paramater is probably 'q' so '?' does not consider
  98. const regBadParameters = new RegExp(
  99. '&(?:' + badParametersNames.join('|') + ')=.*?(?=(&|$))',
  100. 'g'
  101. );
  102. const regBadPaths = new RegExp('^/(?:' + badPaths.join('|') + ')');
  103. const dirtyLinkSelector = dirtyLinkSelectors
  104. .map(s => s + ":not([href=''])")
  105. .join(',');
  106.  
  107. /*
  108. * Functions
  109. */
  110. /* Return parameter value */
  111. function extractDirectLink(str, param) {
  112. //(?<=q=)(.*)(?=&)/
  113. const res = new RegExp(`[?&]${param}(=([^&#]*))`).exec(str);
  114. if (!res || !res[2]) return '';
  115. return decodeURIComponent(res[2]);
  116. }
  117.  
  118. /* Return the current Google search mode */
  119. function getParam(parameter, name) {
  120. var results = new RegExp('[?&]' + name + '=([^&#]*)').exec(parameter);
  121. if (results === null) {
  122. return null;
  123. } else {
  124. return results.pop() || 0;
  125. }
  126. }
  127.  
  128. /* return search mode */
  129. function getMode() {
  130. const parameter = location.search + location.hash;
  131. return getParam(parameter, 'tbm') || 'search';
  132. }
  133.  
  134. function sleep(ms) {
  135. return new Promise(resolve => setTimeout(resolve, ms));
  136. }
  137.  
  138. /* Return Promise when declared the variable name specified by argument */
  139. async function onDeclare(obj, propertyStr, interval = 80) {
  140. return new Promise(async function(resolve, reject) {
  141. const propertyNames = propertyStr.split('.');
  142. let currObj = obj;
  143. for (const propertyName of propertyNames) {
  144. while (!(propertyName in currObj) || currObj[propertyName] === null) {
  145. await sleep(interval);
  146. }
  147. currObj = currObj[propertyName];
  148. }
  149. resolve(currObj);
  150. });
  151. }
  152.  
  153. function removeDOM(node) {
  154. node.parentNode.removeChild(node);
  155. }
  156.  
  157. function rewriteProperties(prop) {
  158. prop.forEach(table => {
  159. //const targetObject = typeof table[0] === 'function' ? table[0]() : table[0];
  160. Object.defineProperty(table[0] || {}, table[1], {
  161. value: table[2],
  162. writable: false
  163. });
  164. });
  165. }
  166.  
  167. function load() {
  168. console.time('LOAD');
  169.  
  170. /* Overwrite disturbing functions */
  171. rewriteProperties([[window, 'rwt', yesman], [window.gbar_, 'Rm', yesman]]);
  172.  
  173. // do not send referrer
  174. const noreferrerMeta = document.createElement('meta');
  175. noreferrerMeta.setAttribute('name', 'referrer');
  176. noreferrerMeta.setAttribute('content', 'no-referrer');
  177. document.querySelector('head').appendChild(noreferrerMeta);
  178.  
  179. /*
  180. * Variables
  181. */
  182. // Whether to use AJAX
  183. const legacy = document.getElementById('cst') === null;
  184.  
  185. /* Nodes */
  186. const nodeMain = document.getElementById("main");
  187. const nodeCnt = document.getElementById("cnt");
  188. const root = (() => {
  189. if (legacy) {
  190. return nodeCnt || nodeMain || window.document;
  191. } else {
  192. return nodeMain; // || nodeCnt;
  193. }
  194. })();
  195.  
  196. // Flag indicating whether the hard tab is loaded on 'DOMContentLoaded'
  197. const lazy_hdtb = !legacy || root === nodeCnt;
  198.  
  199. // Define selector function
  200. const $ = root.querySelector.bind(root);
  201. const $$ = sel =>
  202. Array.prototype.slice.call(root.querySelectorAll.call(root, [sel]));
  203.  
  204. // Selector pointing to anchors to purify
  205. const dirtySelector = (() => {
  206. if (root === window.document) {
  207. return 'body a';
  208. } else if (legacy) {
  209. return `#${root.id} a`;
  210. } else {
  211. return '#rcnt a';
  212. }
  213. })();
  214.  
  215. /*
  216. * Functions
  217. */
  218. function removeTracking() {
  219. console.time('removeTracking');
  220. const mode = getMode();
  221. const badAttrNames = badAttrNamesObj[mode]
  222. ? badAttrNamesObj[mode]
  223. : badAttrNamesObj['default'];
  224. const directLinkParamName = 'q';
  225.  
  226. // search result
  227. for (const searchResult of $$(dirtySelector)) {
  228. // remove attributes
  229. badAttrNames.map(s => {
  230. searchResult.removeAttribute(s);
  231. });
  232.  
  233. // hide referrer
  234. searchResult.rel = 'noreferrer';
  235.  
  236. // remove google redirect link(legacy)
  237. if (
  238. searchResult.hasAttribute('href') &&
  239. searchResult.getAttribute('href').startsWith('/url?')
  240. ) {
  241. searchResult.href = extractDirectLink(
  242. searchResult.href,
  243. directLinkParamName
  244. );
  245. }
  246. searchResult.href = searchResult.href.replace(regBadParameters, '');
  247. }
  248. for (const dirtyLink of document.querySelectorAll(dirtyLinkSelector)) {
  249. dirtyLink.href = dirtyLink.href.replace(regBadParameters, '');
  250. }
  251.  
  252. switch (mode) {
  253. case 'shop':
  254. // Overwrite links(desktop version only)
  255. //Object.values(google.pmc.smpo.r).map(s=>{return {title:s[14][0],link:s[28][8]}})
  256. if (legacy) break;
  257. onDeclare(google, 'pmc.spop.r').then(shopObj => {
  258. const shopElements = $$('.pstl');
  259. const shopLinks = Object.values(shopObj).map(a => a[34][6]);
  260.  
  261. if (shopElements.length !== shopLinks.length) {
  262. console.warn(
  263. 'length does not match',
  264. shopElements.length,
  265. shopLinks.length
  266. );
  267. return;
  268. }
  269.  
  270. const zip = rows => rows[0].map((_, c) => rows.map(row => row[c]));
  271. for (const detail of zip([shopElements, shopLinks])) {
  272. detail[0].href = detail[1];
  273. }
  274. console.log('Links Rewrited');
  275. });
  276. break;
  277. default:
  278. break;
  279. }
  280. console.timeEnd('removeTracking');
  281. }
  282.  
  283. const ObserveOp = {
  284. LOADED: {
  285. FORM: Symbol(),
  286. IMAGE: Symbol(),
  287. HDTB: Symbol()
  288. },
  289. UPDATE: {
  290. HDTB: Symbol()
  291. },
  292. CHANGE: {
  293. HDTB: Symbol(),
  294. PAGE: Symbol()
  295. }
  296. };
  297. const ObserveDisconnectListAfTriggered = Object.values(ObserveOp.LOADED);
  298.  
  299. function startObserve(targetElement, op, func, conf = { childList: true }) {
  300. //console.log("Operation", op , "Register To", targetElement)
  301. new MutationObserver((mutations, observer) => {
  302. let nodes = Array.prototype.concat
  303. .apply([], mutations.map(s => Array.prototype.slice.call(s.addedNodes)))
  304. .filter(n => n.nodeName !== '#comment');
  305.  
  306. //console.log("Nodes Captured By", op, nodes);
  307.  
  308. switch (op) {
  309. case ObserveOp.LOADED.FORM:
  310. nodes = nodes.filter(n => n.name === 'gs_l');
  311. break;
  312. case ObserveOp.LOADED.IMAGE:
  313. nodes = nodes.filter(n => n.classList.contains('irc_bg'));
  314. break;
  315. case ObserveOp.LOADED.HDTB:
  316. nodes = nodes.filter(n => n.className === 'hdtb-mn-cont');
  317. break;
  318. case ObserveOp.UPDATE.HDTB:
  319. nodes = nodes.filter(n => n.className === 'hdtb-mn-cont');
  320. break;
  321. case ObserveOp.CHANGE.HDTB:
  322. nodes = nodes.filter(n => n.id === 'cnt');
  323. break;
  324. case ObserveOp.CHANGE.PAGE:
  325. nodes = nodes.filter(n => n.dataset && n.dataset.ved !== undefined);
  326. break;
  327. default:
  328. break;
  329. }
  330.  
  331. if (nodes.length >= 1) {
  332. //console.log("Operation", op , "Fired", nodes[0])
  333. func();
  334. if (ObserveDisconnectListAfTriggered.includes(op)) {
  335. observer.disconnect();
  336. }
  337. }
  338. }).observe(targetElement, conf);
  339. }
  340.  
  341. function pageInit() {
  342. removeTracking();
  343. startObserve($('#search'), ObserveOp.CHANGE.PAGE, removeTracking);
  344. }
  345.  
  346. const initMode = getMode();
  347. const confDeepObserve = { childList: true, subtree: true };
  348.  
  349. // Wait for .hdtb-mn-cont appears in the first page access
  350. if (lazy_hdtb && !legacy) {
  351. startObserve(
  352. root,
  353. ObserveOp.LOADED.HDTB,
  354. () => {
  355. switch (initMode) {
  356. case 'isch': // Image Search
  357. removeTracking();
  358. startObserve($('#isr_mc'), ObserveOp.LOADED.IMAGE, () => {
  359. $$(
  360. ".irc_tas, .irc_mil, .irc_hol, .irc_but[jsaction*='mousedown']"
  361. ).forEach(e => {
  362. e.__jsaction = null;
  363. e.removeAttribute('jsaction');
  364. });
  365. });
  366. startObserve(
  367. $('#top_nav'),
  368. ObserveOp.UPDATE.HDTB,
  369. removeTracking,
  370. confDeepObserve
  371. );
  372. break;
  373. default:
  374. pageInit();
  375. // Wait for #cnt inserted. In HDTB switching, since .hdtb-mn-cont does not appear
  376. startObserve(root, ObserveOp.CHANGE.HDTB, pageInit);
  377. break;
  378. }
  379. },
  380. confDeepObserve
  381. );
  382. }
  383.  
  384. if (legacy) {
  385. removeTracking();
  386.  
  387. // Remove unnecessary input
  388. startObserve(document.querySelector('form'), ObserveOp.LOADED.FORM, () => {
  389. document
  390. .querySelectorAll("form input:not([name='q']):not([name='hl'])")
  391. .forEach(s => removeDOM(s));
  392. });
  393.  
  394. // Remove unnecessary parameters from 'option'
  395. for(const option of document.querySelectorAll("#mor > option")){
  396. option.value = option.value.replace(regBadParameters, '');
  397. }
  398.  
  399. console.warn('legacy mode');
  400. console.timeEnd('LOAD');
  401. return;
  402. }
  403.  
  404. console.timeEnd('LOAD');
  405. }
  406.  
  407. function init() {
  408. console.time('init');
  409. onDeclare(window, 'google', 20).then(() => {
  410. rewriteProperties([
  411. [google, 'log', yesman],
  412. [google, 'rll', yesman],
  413. [google, 'logUrl', tired],
  414. [google, 'getEI', yesman],
  415. [google, 'getLEI', yesman],
  416. [google, 'ctpacw', yesman],
  417. [google, 'csiReport', yesman],
  418. [google, 'report', yesman],
  419. [google, 'aft', yesman],
  420. [google, 'kEI', '0']
  421. ]);
  422. });
  423.  
  424. // Reject Request by img tag
  425. const regBadImageSrc = /\/(?:gen(?:erate)?|client)_204/;
  426. Object.defineProperty(window.Image.prototype, 'src', {
  427. set: function(url) {
  428. if (!regBadImageSrc.test(url)) {
  429. this.setAttribute('src', url);
  430. }
  431. }
  432. });
  433.  
  434. // Reject unknown parameters by script tag
  435. Object.defineProperty(window.HTMLScriptElement.prototype, 'src', {
  436. set: function(url) {
  437. this.setAttribute('src', url.replace(regBadParameters, ''));
  438. }
  439. });
  440.  
  441. // hook XHR
  442. const origOpen = XMLHttpRequest.prototype.open;
  443. window.XMLHttpRequest.prototype.open = function(act, path) {
  444. if (regBadPaths.test(path)) {
  445. return;
  446. }
  447. origOpen.apply(this, [act, path.replace(regBadParameters, '')]);
  448. };
  449.  
  450. console.timeEnd('init');
  451. }
  452.  
  453. /* Execute */
  454.  
  455. init();
  456. window.addEventListener('DOMContentLoaded', load);
  457.  
  458. // for older browser
  459. if (document.getElementById('universal') !== null) {
  460. load();
  461. }

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址