remove google tracking UWAA

remove google tracking

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

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

QingJ © 2025

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