Greasy Fork 支持简体中文。

RU AdList JS Fixes

try to take over the world!

目前為 2021-06-30 提交的版本,檢視 最新版本

  1. // ==UserScript==
  2. // @name RU AdList JS Fixes
  3. // @namespace ruadlist_js_fixes
  4. // @version 20210630.0
  5. // @description try to take over the world!
  6. // @author lainverse & dimisa
  7. // @license CC-BY-SA-4.0
  8. // @supportURL https://greasyfork.org/en/scripts/19993-ru-adlist-js-fixes/feedback
  9. // @match *://*/*
  10. // @exclude /^https?:\/\/([^.]+\.)*?(auth\.wi-fi\.ru|hd\.kinopoisk\.ru|(webntp|brontp(-pr|-beta|-alpha)?|diehard|market|money|trust)\.yandex\.(by|kz|ru))([:/]|$)/
  11. // @exclude /^https?:\/\/([^.]+\.)*?(1cfresh\.com|(alfabank|(cdn-)?tinkoff)\.ru|ingress\.com|lineageos\.org|telegram\.org|unicreditbanking\.net)([:/]|$)/
  12. // @compatible chrome Only with Tampermonkey or Violentmonkey. Только с Tampermonkey или Violentmonkey.
  13. // @compatible edge Only in Edge 79+ with Tampermonkey or Violentmonkey. Только в Edge 79+ с Tampermonkey или Violentmonkey.
  14. // @compatible firefox Only in Firefox 56+ with Tampermonkey. Только Firefox 56+ с Tampermonkey.
  15. // @compatible opera Only with Tampermonkey. Только с Tampermonkey.
  16. // @grant GM_getValue
  17. // @grant GM_setValue
  18. // @grant GM_listValues
  19. // @grant GM_registerMenuCommand
  20. // @grant GM.cookie
  21. // @grant unsafeWindow
  22. // @grant window.close
  23. // @run-at document-start
  24. // ==/UserScript==
  25.  
  26. // jshint esversion: 8
  27. // jshint unused: true
  28. /* eslint no-empty: ["error", { "allowEmptyCatch": true }] */
  29. (function () {
  30. 'use strict';
  31.  
  32. const win = (unsafeWindow || window);
  33.  
  34. // MooTools are crazy enough to replace standard browser object window.Document: https://mootools.net/core
  35. // Occasionally their code runs before my script on some domains and causes all kinds of havoc.
  36. const
  37. _Document = Object.getPrototypeOf(HTMLDocument.prototype),
  38. _Element = Object.getPrototypeOf(HTMLElement.prototype);
  39. // dTree 2.05 in some cases replaces Node object
  40. const
  41. _Node = Object.getPrototypeOf(_Element);
  42.  
  43. // http://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser
  44. const
  45. // isOpera = (!!window.opr && !!window.opr.addons) || !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0,
  46. // isChrome = !!window.chrome && !!window.chrome.webstore,
  47. isSafari =
  48. Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0 ||
  49. (function (p) {
  50. return p.toString() === "[object SafariRemoteNotification]";
  51. })(!window.safari || window.safari.pushNotification),
  52. isFirefox = 'InstallTrigger' in win,
  53. inIFrame = (win.self !== win.top);
  54. const
  55. _bindCall = fun => Function.prototype.call.bind(fun),
  56. _getAttribute = _bindCall(_Element.getAttribute),
  57. _setAttribute = _bindCall(_Element.setAttribute),
  58. _removeAttribute = _bindCall(_Element.removeAttribute),
  59. _hasOwnProperty = _bindCall(Object.prototype.hasOwnProperty),
  60. _toString = _bindCall(Function.prototype.toString),
  61. _document = win.document,
  62. _de = _document.documentElement,
  63. _appendChild = _Document.appendChild.bind(_de),
  64. _removeChild = _Document.removeChild.bind(_de),
  65. _createElement = _Document.createElement.bind(_document),
  66. _querySelector = _Document.querySelector.bind(_document),
  67. _querySelectorAll = _Document.querySelectorAll.bind(_document),
  68. _apply = Reflect.apply,
  69. _construct = Reflect.construct;
  70. const _attachShadow = (() => {
  71. try {
  72. return ('attachShadow' in _Element) ? _bindCall(_Element.attachShadow) : null;
  73. } catch (ignore) {}
  74. })();
  75.  
  76. let skipLander = true;
  77. try {
  78. skipLander = !(isFirefox && 'StopIteration' in win);
  79. } catch (ignore) {}
  80.  
  81. const _console = {};
  82. _console.initConsole = () => {
  83. const keys = new Set();
  84. const _stopImmediatePropagation = _bindCall(Event.prototype.stopImmediatePropagation);
  85. win.addEventListener('message', e => {
  86. if (e.source === win || typeof e.data !== 'string')
  87. return;
  88. if (e.data.startsWith('_console.key')) {
  89. _stopImmediatePropagation(e);
  90. keys.add(e.data.slice(13));
  91. }
  92. if (keys.has(e.data.slice(0, 10))) {
  93. _stopImmediatePropagation(e);
  94. _console.log(`From: ${e.origin}\n${e.data.slice(11)}`);
  95. }
  96. });
  97. if (inIFrame) {
  98. const _postMessage = win.parent.postMessage.bind(win.parent);
  99. const key = Math.random().toString(36).substr(2).padStart(10, '0').slice(0, 10);
  100. _postMessage(`_console.key ${key}`, '*');
  101. const _Object_toString = _bindCall(Object.prototype.toString);
  102. const _Symbol_toString = _bindCall(Symbol.prototype.toString);
  103. const _Array_toString = _bindCall(Array.prototype.toString);
  104. const selfHandled = ['string', 'number', 'boolean', 'undefined'];
  105. const stringify = x => {
  106. if (x === null)
  107. return 'null';
  108. const type = typeof x;
  109. if (selfHandled.includes(type))
  110. return x;
  111. if (type === 'object') {
  112. if (x instanceof Window || x instanceof Document)
  113. return _Object_toString(x);
  114. if (Array.isArray(x))
  115. return _Array_toString(x);
  116. if (x instanceof Element)
  117. return `<${x.tagName} ${Array.from(x.attributes).map(x => x.value ? `${x.name}="${x.value}"`: x.name).join(' ')}>`;
  118. let props = Object.getOwnPropertyNames(x);
  119. if (props.length > 30) {
  120. props.splice(30, props.length - 30);
  121. props.push('\u2026');
  122. }
  123. return `[object {${props.join(', ')}}]`;
  124. }
  125. if (type === 'function') {
  126. let str = _toString(x);
  127. return str.length > 200 ? `${str.slice(0, 200)}\u2026` : str;
  128. }
  129. if (type === 'symbol')
  130. return _Symbol_toString(x);
  131. return `[unhandled ${typeof x}]`;
  132. };
  133. const passIt = (...args) => {
  134. let strs = args.map(stringify);
  135. _postMessage(`${key} ${strs.join(' ')}`, '*', );
  136. };
  137. for (let name in win.console)
  138. _console[name] = passIt;
  139. } else {
  140. for (let name in win.console)
  141. _console[name] = console[name];
  142. _console._trace = _console.trace;
  143. _console.trace = (...args) => {
  144. if (!skipLander)
  145. return _console.warn(...args);
  146. _console.groupCollapsed(...args);
  147. _console._trace('Stack trace.');
  148. _console.groupEnd();
  149. };
  150. }
  151.  
  152. Object.freeze(_console);
  153. Object.defineProperty(win.console, 'clear', {
  154. value() {
  155. return null;
  156. }
  157. });
  158. };
  159. _console.initConsole();
  160.  
  161. const jsf = (function () {
  162. const opts = {};
  163. let getValue = (a, b) => b,
  164. setValue = () => null,
  165. listValues = () => [];
  166. try {
  167. [getValue, setValue, listValues] = [GM_getValue, GM_setValue, GM_listValues];
  168. } catch (ignore) {}
  169. // defaults
  170. opts.Lang = 'rus';
  171. opts.AbortExecutionStatistics = false;
  172. opts.AccessStatistics = false;
  173. opts.LogAttachedCSS = false;
  174. opts.BlockNotificationPermissionRequests = false;
  175. opts.ShowScriptHandlerCompatibilityWarning = true;
  176. // load actual values
  177. for (let name of listValues())
  178. opts[name] = getValue(name, opts[name]);
  179. const checkName = name => {
  180. if (!_hasOwnProperty(opts, name))
  181. throw new Error('Attempt to access missing option value.');
  182. return true;
  183. };
  184. return new Proxy(opts, {
  185. get(opts, name) {
  186. if (name === 'toString')
  187. return () => JSON.stringify(opts);
  188. if (checkName(name))
  189. return opts[name];
  190. },
  191. set(opts, name, value) {
  192. if (checkName(name)) {
  193. opts[name] = value;
  194. setValue(name, value);
  195. }
  196. return true;
  197. }
  198. });
  199. })();
  200.  
  201. if (jsf.BlockNotificationPermissionRequests && win.Notification && win.Notification.permission === 'default') {
  202. win.Notification.requestPermission = () => new Promise(resolve => resolve('denied'));
  203. Object.defineProperty(win.Notification, 'permission', {
  204. set() {},
  205. get() {
  206. return 'denied';
  207. }
  208. });
  209. }
  210.  
  211. if (isFirefox && // Exit on image pages in Fx
  212. _document.constructor.prototype.toString() === '[object ImageDocumentPrototype]')
  213. return;
  214.  
  215. // NodeList and HTMLCollection iterator polyfill
  216. // required for old versions of Safari and Chrome 49 (last available for WinXP)
  217. // https://jakearchibald.com/2014/iterators-gonna-iterate/
  218. if (!NodeList.prototype[Symbol.iterator])
  219. NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
  220. if (!HTMLCollection.prototype[Symbol.iterator])
  221. HTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
  222. // Firefox 60 ESR Fix: calling toString on Proxy object throws Error
  223. function FxProxyToStringFix(root) {
  224. if (!isFirefox || parseInt((navigator.userAgent.match(/Firefox\/(\d+)\./) || [])[1]) > 60)
  225. return;
  226. const wrapper = {
  227. apply(fun, that, args) {
  228. let res;
  229. try {
  230. res = _apply(fun, that, args);
  231. } catch (e) {
  232. if (typeof that === 'function')
  233. res = Function[fun.name || 'toString']();
  234. else
  235. throw e;
  236. if (that.name && res)
  237. res = res.replace('Function', that.name);
  238. }
  239. return res;
  240. }
  241. };
  242. ['toLocaleString', 'toSource', 'toString'].forEach(
  243. toWrap => (root.Function.prototype[toWrap] = new Proxy(root.Function.prototype[toWrap], wrapper))
  244. );
  245. }
  246. FxProxyToStringFix(win);
  247.  
  248. // Stub for missing "GM.cookie.list" in unsupported script managers
  249. if (GM.cookie === undefined)
  250. GM.cookie = {
  251. list: () => ({
  252. then: () => null
  253. })
  254. };
  255.  
  256. // Wrapper to run scripts designed to override objects available to other scripts
  257. // Required in old versions of Firefox (<58) or when running with Greasemonkey
  258. const
  259. batchLand = [],
  260. batchPrepend = new Set(),
  261. _APIString = `const win = window, isFirefox = ${isFirefox}, inIFrame = ${inIFrame}, _document = win.document, _de = _document.documentElement,
  262. _Document = Object.getPrototypeOf(HTMLDocument.prototype), _Element = Object.getPrototypeOf(HTMLElement.prototype), _Node = Object.getPrototypeOf(_Element),
  263. _appendChild = _Document.appendChild.bind(_de), _removeChild = _Document.removeChild.bind(_de), skipLander = ${skipLander},
  264. _createElement = _Document.createElement.bind(_document), _querySelector = _Document.querySelector.bind(_document),
  265. _querySelectorAll = _Document.querySelectorAll.bind(_document), _bindCall = fun => Function.prototype.call.bind(fun),
  266. _getAttribute = _bindCall(_Element.getAttribute), _setAttribute = _bindCall(_Element.setAttribute),
  267. _removeAttribute = _bindCall(_Element.removeAttribute), _hasOwnProperty = _bindCall(Object.prototype.hasOwnProperty),
  268. _toString = _bindCall(Function.prototype.toString), _apply = Reflect.apply, _construct = Reflect.construct;
  269. const GM = { info: { version: '0.0', scriptHandler: null }, cookie: { list: () => ({ then: () => null }) } };
  270. const jsf = ${jsf.toString()}, _console = {}; (${_console.initConsole.toString()})();`,
  271. landScript = (f, pre) => {
  272. const script = _createElement('script');
  273. script.textContent = `(()=>{${_APIString}${[...pre].join(';')};(${f.join(')();(')})();})();`;
  274. _appendChild(script);
  275. _removeChild(script);
  276. };
  277. let scriptLander = f => f();
  278. if (!skipLander) {
  279. scriptLander = (func, ...prepend) => {
  280. prepend.forEach(x => batchPrepend.add(x));
  281. batchLand.push(func);
  282. };
  283. _document.addEventListener(
  284. 'DOMContentLoaded', () => void(scriptLander = (f, ...prep) => landScript([f], prep)), false
  285. );
  286. }
  287.  
  288. function nullTools(opts) {
  289. /* jshint validthis: true */
  290. const nt = this;
  291. opts = opts || {};
  292.  
  293. function Stats() {
  294. const updated = new Map();
  295. const logged = new Map();
  296.  
  297. function printUpdated() {
  298. const prepared = [...updated].map(x => {
  299. const [prop, dir] = x;
  300. logged.set(prop, logged.get(prop) | dir);
  301. return `${prop} (${dir ^ (Stats.GET | Stats.SET) ? (dir & Stats.GET ? 'R' : 'W') : 'R/W'})`;
  302. }).sort();
  303. updated.clear();
  304. _console.log(`Accessed properties:\n * ${prepared.join('\n * ')}`);
  305. }
  306.  
  307. let logLock;
  308. this.log = async (prop, dir, doLog) => {
  309. if (!doLog) return;
  310. if (!(logged.get(prop) & dir))
  311. updated.set(prop, updated.get(prop) | dir);
  312. logLock = (logLock > 0 || !updated.size) ? logLock : setInterval(() => {
  313. printUpdated();
  314. logLock = clearInterval(logLock);
  315. }, 2500);
  316. };
  317. }
  318. Stats.GET = 1;
  319. Stats.SET = 2;
  320. Object.freeze(Stats);
  321. const stats = new Stats();
  322.  
  323. const log = async (...args) => opts.log && _console.log(...args);
  324. const isObjecty = x => (x !== null) && typeof x === 'object' || typeof x === 'function';
  325. const parsePath = path => {
  326. let root = win,
  327. chain = path.split('.'),
  328. link = chain.shift();
  329. for (; chain.length > 0; link = chain.shift()) {
  330. if (!isObjecty(root[link]))
  331. break;
  332. root = root[link];
  333. }
  334. return [root, link, chain];
  335. };
  336.  
  337. const namedObjects = new WeakMap();
  338.  
  339. nt.destroy = function (o, destroy) {
  340. if (!opts.destroy && !destroy && o instanceof Object)
  341. return;
  342. log('cleaning', o);
  343. try {
  344. for (let item in o) {
  345. if (item instanceof Object)
  346. nt.destroy(item);
  347. delete o[item];
  348. }
  349. } catch (e) {
  350. log('Error in object destructor', e);
  351. }
  352. };
  353.  
  354. nt.define = function (path, val, other = {}) {
  355. other.enumerable = other.enumerable || false;
  356. const [obj, prop, remainder] = parsePath(path);
  357. if (remainder.length) {
  358. if (other.dontWait)
  359. _console.warn(`Unable to resolve ${remainder.join('.')} in ${path}`);
  360. else {
  361. let _obj;
  362. Object.defineProperty(obj, prop, {
  363. get() {
  364. return _obj;
  365. },
  366. set(val) {
  367. _obj = val;
  368. nt.define(path, val, other);
  369. },
  370. configurable: true
  371. });
  372. }
  373. return;
  374. }
  375. namedObjects.set(obj, path);
  376. nt.defineOn(obj, prop, val, path, other);
  377. };
  378.  
  379. nt.defineOn = function (obj, prop, val, path, other = {}) {
  380. path = path || ((obj === win) ? prop : `?.${prop}`);
  381. if (path[path.length - 1] === '.')
  382. path = `${path}${prop}`;
  383. const desc = Object.getOwnPropertyDescriptor(obj, prop);
  384. if (desc !== undefined && !desc.configurable) {
  385. _console.warn(`Unable to redefine not configurable ${prop} in`, obj);
  386. return;
  387. }
  388. const doLog = other.trace || jsf.AccessStatistics || opts.trace;
  389. Object.defineProperty(
  390. obj, prop, {
  391. get: exportFunction(() => {
  392. stats.log(path, Stats.GET, doLog);
  393. return val;
  394. }, win),
  395. set: exportFunction(v => {
  396. stats.log(path, Stats.SET, doLog);
  397. if (v !== val) {
  398. log(`set ${prop} of`, obj, 'to', v);
  399. nt.destroy(v);
  400. }
  401. }, win),
  402. enumerable: other.enumerable
  403. }
  404. );
  405. };
  406.  
  407. nt.proxy = function (obj, name, opts = {}) {
  408. if (name) namedObjects.set(obj, name);
  409. return new Proxy(
  410. obj, {
  411. get(that, prop) {
  412. if (prop in that)
  413. return that[prop];
  414. if (typeof prop === 'symbol') {
  415. if (prop === Symbol.toPrimitive)
  416. that[prop] = function (hint) {
  417. if (hint === 'string')
  418. return Object.prototype.toString.call(this);
  419. return `[missing toPrimitive] ${name} ${hint}`;
  420. };
  421. else if (prop === Symbol.toStringTag)
  422. that[prop] = () => 'Object';
  423. else {
  424. that[prop] = undefined;
  425. _console.trace('Missing', prop, 'in', name || '?', '>>', that[prop]);
  426. }
  427. return that[prop];
  428. }
  429. if (name) {
  430. that[prop] = nt.func(opts.val, `${name}.${prop}`, opts.log);
  431. return that[prop];
  432. }
  433. _console.trace('Missing', prop, 'in', namedObjects.get(that) || that);
  434. },
  435. set(that, prop, val) {
  436. if (val !== that[prop]) {
  437. log('skip set', prop, 'of', namedObjects.get(that) || that, 'to', val);
  438. nt.destroy(val);
  439. }
  440. return true;
  441. }
  442. }
  443. );
  444. };
  445.  
  446. nt.func = (val, name, log = opts.log || opts.trace) => nt.proxy((() => {
  447. let f = function () {
  448. if (log) _console.trace(`call ${name || ''}(`, ...arguments, `) return`, val);
  449. return val;
  450. };
  451. if (name) namedObjects.set(f, name);
  452. return f;
  453. })());
  454.  
  455. nt.NULL = {
  456. val: null
  457. };
  458. Object.freeze(nt.NULL);
  459.  
  460. Object.freeze(nt);
  461. }
  462. nullTools.toString = new Proxy(nullTools.toString, {
  463. apply(...args) {
  464. return `${_apply(...args)} let nt = new nullTools();`;
  465. }
  466. });
  467. let nt = new nullTools();
  468.  
  469. // Creates and return protected style (unless protection is manually disabled).
  470. // Protected style will re-add itself on removal and remaind enabled on attempt to disable it.
  471.  
  472. const createStyle = (function createStyleModule() {
  473. function createStyleElement(rules, opts) {
  474. const style = _createElement('style');
  475. Object.assign(style, opts.props);
  476. opts.root.appendChild(style);
  477.  
  478. if (style.sheet) // style.sheet is only available when style attached to DOM
  479. rules.forEach(style.sheet.insertRule.bind(style.sheet));
  480. else
  481. style.textContent = rules.join('\n');
  482.  
  483. if (opts.protect) {
  484. Object.defineProperty(style, 'sheet', {
  485. value: null,
  486. enumerable: true
  487. });
  488. Object.defineProperty(style, 'disabled', { //pretend to be disabled
  489. enumerable: true,
  490. set() {},
  491. get() {
  492. return true;
  493. }
  494. });
  495. (new MutationObserver(
  496. () => opts.root.removeChild(style)
  497. )).observe(style, {
  498. childList: true
  499. });
  500. }
  501.  
  502. return style;
  503. }
  504.  
  505. // functions to parse object-based rulesets
  506. function parseRule(rec) {
  507. /* jshint validthis: true */
  508. return this.concat(rec[0], ' {\n', Object.entries(rec[1]).map(parseProperty, this + '\t').join('\n'), '\n', this, '}');
  509. }
  510.  
  511. function parseProperty(rec) {
  512. /* jshint validthis: true */
  513. return rec[1] instanceof Object ? parseRule.call(this, rec) : `${this}${rec[0].replace(/_/g, '-')}: ${rec[1]};`;
  514. }
  515.  
  516. // main
  517. const createStyle = (rules, opts) => {
  518. // parse options
  519. opts = Object.assign({
  520. protect: true,
  521. root: _de,
  522. type: 'text/css'
  523. }, opts);
  524. // move style properties into separate property
  525. // { a, b, ...rest } construction is not available in Fx 52
  526. opts.props = Object.assign({}, opts);
  527. delete opts.props.protect;
  528. delete opts.props.root;
  529. // store binded methods instead of element
  530. opts.root = {
  531. appendChild: opts.root.appendChild.bind(opts.root),
  532. removeChild: opts.root.removeChild.bind(opts.root)
  533. };
  534.  
  535. // convert rules set into an array if it isn't one already
  536. rules = Array.isArray(rules) ? rules : rules instanceof Object ? Object.entries(rules).map(parseRule, '') : [rules];
  537.  
  538. // could be reassigned when protection triggered
  539. let style = createStyleElement(rules, opts);
  540.  
  541. if (!opts.protect)
  542. return style;
  543.  
  544. const replaceStyle = () => new Promise(
  545. resolve => setTimeout(re => re(createStyleElement(rules, opts)), 0, resolve)
  546. ).then(st => (style = st)); // replace poiner to style object with a new style object
  547.  
  548. (new MutationObserver(ms => {
  549. for (let m of ms)
  550. for (let node of m.removedNodes)
  551. if (node === style) replaceStyle();
  552. })).observe(_de, {
  553. childList: true
  554. });
  555.  
  556. if (jsf.LogAttachedCSS)
  557. _console.log(`Attached CSS:\n${rules.join('\n')}`);
  558.  
  559. return style;
  560. };
  561. createStyle.toString = () => `const createStyle = (${createStyleModule.toString()})();`;
  562. return createStyle;
  563. })();
  564.  
  565. // aborts currently running script with TypeError on specific property access
  566. // in case of inline script also checks if it contains specific pattern in it
  567.  
  568. const removeOwnFootprint = e => (e.stack = e.stack.split('\n').filter(x => !x.includes('-extension://')).join('\n'), e);
  569. const abortExecution = (function abortExecutionModule() {
  570. let map = new Map(),
  571. cnt = new Map(),
  572. stack = 0;
  573. const printCounters = (cnts) => {
  574. let s = '';
  575. for (let cntr of cnts)
  576. s += `\n * ${cntr[0]}: ${cntr[1]}`;
  577. return s;
  578. };
  579. const logger = id => {
  580. if (!jsf.AbortExecutionStatistics)
  581. return;
  582. let {
  583. path,
  584. mode
  585. } = map.get(id);
  586. let prop = `${path} ${mode.toString().replace('Symbol','')}`;
  587. cnt.set(prop, (cnt.get(prop) || 0) + 1);
  588. stack++;
  589. setTimeout(() => {
  590. stack--;
  591. if (!stack)
  592. console.log('Abort execution counters:', printCounters(Array.from(cnt.entries())));
  593. }, 1000);
  594. };
  595. const isObjecty = x => (x !== null) && typeof x === 'object' || typeof x === 'function';
  596. const Mode = {
  597. Get: Symbol('Read'),
  598. Set: Symbol('Write'),
  599. All: Symbol('Access'),
  600. InlineScript: Symbol('InlineScript')
  601. };
  602. Object.freeze(Mode);
  603.  
  604. const abortExecution = function abortExecution(mode, path, conf = {}) {
  605. let root = conf.root || win,
  606. chain = path.split('.'),
  607. postponed = false;
  608. const postpone = (link, chain) => {
  609. postponed = true;
  610. let _val;
  611. try {
  612. Object.defineProperty(root, link, {
  613. get() {
  614. return _val;
  615. },
  616. set(val) {
  617. _val = val;
  618. conf.root = val;
  619. conf.fullPath = conf.fullPath || path;
  620. abortExecution(mode, chain.join('.'), conf);
  621. }
  622. });
  623. } catch (e) {
  624. _console.warn(`Unable to set postpone point at ${link} in ${path}\n`, e);
  625. }
  626. };
  627. while (chain.length > 1) {
  628. let link = chain.shift();
  629. if (!isObjecty(root[link])) {
  630. if (conf.breakOnMissing)
  631. break;
  632. postpone(link, chain);
  633. break;
  634. }
  635. root = root[link];
  636. }
  637. path = conf.fullPath || path;
  638. if (postponed) {
  639. if (conf.breakOnMissing)
  640. _console.log(`Unable to locate "${path}", abort anchor skipped.`);
  641. return;
  642. }
  643. const target = chain[0];
  644. const message = `Cannot read property '${target}' of undefined`;
  645. const des = Object.getOwnPropertyDescriptor(root, target);
  646. if (des && des.get !== undefined)
  647. return;
  648. const id = Math.random().toString(36).substr(2);
  649. map.set(id, {
  650. path: path,
  651. mode: mode
  652. });
  653. win.addEventListener('error', e => {
  654. if (e.error && e.error.message === message)
  655. e.stopImmediatePropagation();
  656. }, false);
  657. const get = Symbol('get');
  658. const set = Symbol('set');
  659.  
  660. const check = (mode === Mode.InlineScript) ?
  661. () => {
  662. const script = _document.currentScript;
  663. if (script && script.src === '' &&
  664. (!conf.pattern || conf.pattern.test(script.textContent))) {
  665. logger(id);
  666. throw removeOwnFootprint(new TypeError(message));
  667. }
  668. } : io => {
  669. if (io === set && mode === Mode.Get ||
  670. io === get && mode === Mode.Set)
  671. return;
  672. logger(id);
  673. throw removeOwnFootprint(new TypeError(message));
  674. };
  675.  
  676. let _val = root[target];
  677. Object.defineProperty(root, target, {
  678. get() {
  679. check(get);
  680. return _val;
  681. },
  682. set(v) {
  683. check(set);
  684. _val = v;
  685. }
  686. });
  687. };
  688. abortExecution.onGet = (path, conf) => abortExecution(Mode.Get, path, conf);
  689. abortExecution.onSet = (path, conf) => abortExecution(Mode.Set, path, conf);
  690. abortExecution.onAll = (path, conf) => abortExecution(Mode.All, path, conf);
  691. abortExecution.inlineScript = (path, conf) => abortExecution(Mode.InlineScript, path, conf);
  692. abortExecution.toString = () => ` const abortExecution = (${abortExecutionModule.toString()})();`;
  693. return abortExecution;
  694. })();
  695.  
  696. // Fake objects of advertisement networks to break their workflow
  697. // Popular adblock detector
  698. function deployFABStub(root) {
  699. if (!('fuckAdBlock' in root)) {
  700. let FuckAdBlock = function (options) {
  701. let self = this;
  702. self._options = {
  703. checkOnLoad: false,
  704. resetOnEnd: false,
  705. checking: false
  706. };
  707. self.setOption = function (opt, val) {
  708. if (val)
  709. self._options[opt] = val;
  710. else
  711. Object.assign(self._options, opt);
  712. };
  713. if (options)
  714. self.setOption(options);
  715.  
  716. self._var = {
  717. event: {}
  718. };
  719. self.clearEvent = function () {
  720. self._var.event.detected = [];
  721. self._var.event.notDetected = [];
  722. };
  723. self.clearEvent();
  724.  
  725. self.on = function (detected, fun) {
  726. self._var.event[detected ? 'detected' : 'notDetected'].push(fun);
  727. return self;
  728. };
  729. self.onDetected = function (cb) {
  730. return self.on(true, cb);
  731. };
  732. self.onNotDetected = function (cb) {
  733. return self.on(false, cb);
  734. };
  735. self.emitEvent = function () {
  736. for (let fun of self._var.event.notDetected)
  737. fun();
  738. if (self._options.resetOnEnd)
  739. self.clearEvent();
  740. return self;
  741. };
  742. self._creatBait = () => null;
  743. self._destroyBait = () => null;
  744. self._checkBait = function () {
  745. setTimeout((() => self.emitEvent()), 1);
  746. };
  747. self.check = function () {
  748. self._checkBait();
  749. return true;
  750. };
  751.  
  752. let callback = function () {
  753. if (self._options.checkOnLoad)
  754. setTimeout(self.check, 1);
  755. };
  756. root.addEventListener('load', callback, false);
  757. };
  758. nt.defineOn(root, 'FuckAdBlock', FuckAdBlock);
  759. nt.defineOn(root, 'fuckAdBlock', new FuckAdBlock({
  760. checkOnLoad: true,
  761. resetOnEnd: true
  762. }));
  763. }
  764. }
  765. // new version of fAB adapting to fake API
  766. // scriptLander(() => deployFABStub(win), nullTools); // so it's disabled by default for now
  767.  
  768. scriptLander(() => {
  769. // VideoJS player wrapper
  770. {
  771. let _videojs = win.videojs;
  772. Object.defineProperty(win, 'videojs', {
  773. get() {
  774. return _videojs;
  775. },
  776. set(f) {
  777. if (f === _videojs)
  778. return true;
  779. _console.log('videojs =', f);
  780. _videojs = new Proxy(f, {
  781. apply(fun, that, args) {
  782. _console.log('videojs(', ...args, ')');
  783. const params = args[1];
  784. if (params) {
  785. if (params.hasAd)
  786. params.hasAd = false;
  787. if (params.plugins && params.plugins.vastClient)
  788. delete params.plugins.vastClient;
  789. // disable unmuted autoplay (muted used for preview purposes)
  790. if (params.autoplay && !params.muted)
  791. params.autoplay = false;
  792. }
  793. const res = _apply(fun, that, args);
  794. if (res.seed)
  795. res.seed = () => null;
  796. if (res.on && res.off) {
  797. const logPlayer = () => {
  798. _console.log('player =', res);
  799. res.off('playing', logPlayer);
  800. };
  801. res.on('playing', logPlayer);
  802. }
  803. return res;
  804. }
  805. });
  806. }
  807. });
  808. }
  809.  
  810. // Set a little trap for BodyClick ads
  811. abortExecution.onSet('__BC_domain');
  812.  
  813. // some developers don't know how to export only necessary stuff into global context
  814. // so, let's skip everything related to Yandex API (window.Ya) on such sites
  815. const skipYa = ['coolors.co', 'material.io', 'haiku-os.org'];
  816. if (skipYa.some(x => location.hostname === x || location.hostname.endsWith(`.${x}`)))
  817. return;
  818.  
  819. // Yandex API (ADBTools, Metrika)
  820. let hostname = location.hostname;
  821. if ( // Thank you, Greasemonkey, now I have to check for this. -_-
  822. location.protocol === 'about:' ||
  823. // Google likes to define odd global variables like Ya
  824. hostname.startsWith('google.') || hostname.includes('.google.') ||
  825. // Also, Yandex uses their Ya object for a lot of things on their pages and
  826. // wrapping it may cause problems. It's better to skip it in some cases.
  827. ((hostname.startsWith('yandex.') || hostname.includes('.yandex.')) &&
  828. /^\/((yand)?search|images|health)/i.test(location.pathname) && !hostname.startsWith('news.')) ||
  829. // Also skip on these following sites since they use
  830. // code minification which generated global Ya variable.
  831. hostname.endsWith('chatango.com') || hostname.endsWith('github.io') ||
  832. hostname.endsWith('grimtools.com') || hostname.endsWith('poeplanner.com'))
  833. return;
  834.  
  835. let YaProps = new Set();
  836.  
  837. function setObfuscatedProperty(Ya, rootProp, obj, name) {
  838. if (YaProps.has(rootProp))
  839. return;
  840. _console.trace(`Ya.${rootProp} = Ya.${name}`);
  841. nt.defineOn(Ya, rootProp, Ya[name], 'Ya.');
  842. YaProps.add(rootProp);
  843. for (let prop in obj)
  844. delete obj[prop];
  845. for (let prop in Ya[name])
  846. obj[prop] = Ya[name][prop];
  847. }
  848.  
  849. function onObfuscatedProperty(Ya, rootProp, obj) {
  850. if ('AdvManager' in obj || 'AdvManagerStatic' in obj || 'isAllowedRepeatAds' in obj) {
  851. setObfuscatedProperty(Ya, rootProp, obj, 'Context');
  852. return Ya.Context;
  853. }
  854. if ('create' in obj && 'createAdaptive' in obj && 'createScroll' in obj) {
  855. setObfuscatedProperty(Ya, rootProp, obj, 'adfoxCode');
  856. return Ya.adfoxCode;
  857. }
  858. return new Proxy(obj, {
  859. set(target, prop, val) {
  860. if (prop === 'AdvManager' || prop === 'isAllowedRepeatAds') {
  861. setObfuscatedProperty(Ya, rootProp, obj, 'Context');
  862. return true;
  863. }
  864. if (prop === 'create' && 'createAdaptive' in obj && 'createScroll' in obj ||
  865. prop === 'createScroll' && 'create' in obj && 'createAdaptive' in obj ||
  866. prop === 'createAdaptive' && 'create' in obj && 'createScroll' in obj) {
  867. setObfuscatedProperty(Ya, rootProp, obj, 'adfoxCode');
  868. return true;
  869. }
  870. target[prop] = val;
  871. return true;
  872. },
  873. get(target, prop) {
  874. if (prop === 'AdvManager' && !(prop in target)) {
  875. _console.trace(`Injected missing ${prop} in Ya.${rootProp}.`);
  876. target[prop] = Ya.Context[prop];
  877. }
  878. return target[prop];
  879. }
  880. });
  881. }
  882. let Rum = {
  883. getSettings: nt.func([], 'Ya.Rum.getSettings'),
  884. getVarsList: nt.func([], 'Ya.Rum.getVarsList'),
  885. ajaxStart: 0,
  886. ajaxComplete: 0,
  887. enabled: true,
  888. vsChanged: false,
  889. vsStart: 'visible',
  890. ERROR_LEVEL: nt.proxy({
  891. ERROR: 0,
  892. WARN: 0,
  893. INFO: 0
  894. }, 'Ya.Rum.ERROR_LEVEL'),
  895. _tti: null,
  896. _markListeners: nt.proxy({}, 'Ya.Rum._markListeners'),
  897. _periodicTasks: nt.proxy({}, 'Ya.Rum._periodicTasks')
  898. };
  899. [
  900. '__timeMarks', '_timeMarks', '__deltaMarks', '_deltaMarks',
  901. '__defRes', '_defRes', '__defTimes', '_defTimes', '_vars',
  902. 'commonVars'
  903. ].forEach(name => void(Rum[name] = []));
  904. Rum = nt.proxy(Rum, 'Ya.Rum', nt.NULL);
  905. let Ya = new Proxy({}, {
  906. set(obj, prop, val) {
  907. if (val === obj[prop])
  908. return true;
  909. if (prop === 'Rum') {
  910. nt.defineOn(obj, prop, Rum, 'Ya.');
  911. YaProps.add(prop);
  912. Object.assign(val, Rum);
  913. }
  914. if (YaProps.has(prop)) {
  915. _console.log(`Ya.${prop} \u2260`, val);
  916. return true;
  917. }
  918. if (typeof val === 'object' && prop !== '__inline_params__' && !('length' in val))
  919. val = onObfuscatedProperty(Ya, prop, val);
  920. obj[prop] = val;
  921. _console.log(`Ya.${prop} =`, val);
  922. return true;
  923. },
  924. get(obj, prop) {
  925. return obj[prop];
  926. }
  927. });
  928. let callWithParams = function (f) {
  929. f.call(this, Ya.__inline_params__ || {});
  930. Ya.__inline_params__ = null;
  931. };
  932. nt.defineOn(Ya, 'callWithParams', callWithParams, 'Ya.');
  933. nt.defineOn(Ya, 'PerfCounters', nt.proxy({
  934. __cacheEvents: []
  935. }, 'Ya.PerfCounters', nt.NULL), 'Ya.');
  936. nt.defineOn(Ya, '__isSent', true, 'Ya.');
  937. nt.defineOn(Ya, 'confirmUrl', '', 'Ya.');
  938. nt.defineOn(Ya, 'Direct', nt.proxy({}, 'Ya.Direct', nt.NULL), 'Ya.');
  939. nt.defineOn(Ya, 'mediaCode', nt.proxy({
  940. create() {
  941. if (inIFrame) {
  942. _console.log('Removed body of ad-frame.');
  943. _document.documentElement.removeChild(_document.body);
  944. }
  945. }
  946. }, 'Ya.mediaCode', nt.NULL), 'Ya.');
  947. let extra = nt.proxy({
  948. extra: nt.proxy({
  949. match: 0,
  950. confirm: '',
  951. src: ''
  952. }),
  953. id: 0,
  954. percent: 100,
  955. threshold: 1
  956. });
  957. nt.defineOn(Ya, '_exp', nt.proxy({
  958. id: 0,
  959. coin: 0,
  960. choose: nt.func(extra, 'Ya._exp.choose'),
  961. get(prop) {
  962. return _hasOwnProperty(extra, prop) ? extra[prop] : null;
  963. },
  964. getId: nt.func(0, 'Ya._exp.getId'),
  965. defaultVersion: extra,
  966. getExtra: nt.func(extra.extra, 'Ya._exp.getExtra'),
  967. getDefaultExtra: nt.func(extra.extra, 'Ya._exp.getDefaultExtra'),
  968. versions: [extra]
  969. }), 'Ya.');
  970. nt.defineOn(Ya, 'c', nt.func(null, 'Ya.c'), 'Ya.');
  971. nt.defineOn(Ya, 'ADBTools', function () {
  972. this.getCurrentState = nt.func(true, 'Ya.ADBTools().getCurrentState');
  973. return nt.proxy(this, 'Ya.ADBTools', nt.NULL);
  974. }, 'Ya.');
  975. nt.defineOn(Ya, 'AdDetector', nt.proxy({}, 'Ya.AdDetector', nt.NULL), 'Ya.');
  976. let definePr = o => {
  977. Object.defineProperty(o, 'pr', {
  978. set() {},
  979. get() {
  980. return Math.floor(Math.random() * 1e6) + 1;
  981. }
  982. });
  983. };
  984. let adfoxCode = {
  985. forcedDirectLoadingExp: nt.proxy({
  986. isLoadingTurnedOn: false,
  987. isExp: false
  988. }),
  989. isLoadingTurnedOn: false,
  990. xhrExperiment: nt.proxy({
  991. isXhr: true,
  992. isControl: true
  993. }),
  994. matchidManager: nt.proxy({}, 'Ya.adfoxCode.matchidManager', nt.NULL),
  995. _: []
  996. };
  997. definePr(adfoxCode);
  998. [
  999. 'clearSession', 'create', 'createAdaptive', 'createScroll',
  1000. 'destroy', 'moduleLoad', 'reload', 'setModule'
  1001. ].forEach(name => void(adfoxCode[name] = nt.func(null, `Ya.adfoxCode.${name}`)));
  1002. nt.defineOn(Ya, 'adfoxCode', nt.proxy(adfoxCode, 'Ya.adfoxCode', nt.NULL), 'Ya.');
  1003. let managerForAdfox = {
  1004. loaderVersion: 1,
  1005. isCurrrencyExp: true,
  1006. isReady: nt.func(true, 'Ya.headerBidding.managerForAdfox.isReady'),
  1007. getRequestTimeout: nt.func(
  1008. 300 + Math.floor(Math.random() * 100),
  1009. 'Ya.headerBidding.managerForAdfox.getRequestTimeout'
  1010. )
  1011. };
  1012. let headerBidding = nt.proxy({
  1013. setSettings(opts) {
  1014. if (!(opts && opts.adUnits))
  1015. return null;
  1016. let ids = [];
  1017. for (let unit of opts.adUnits)
  1018. ids.push(unit.code);
  1019. createStyle(`#${ids.join(', #')} { display: none !important }`);
  1020. },
  1021. pushAdUnits: nt.func(null, 'Ya.headerBidding.pushAdUnits'),
  1022. managerForAdfox: nt.proxy(managerForAdfox, 'Ya.headerBidding.managerForAdfox', nt.NULL)
  1023. });
  1024. definePr(headerBidding);
  1025. nt.defineOn(Ya, 'headerBidding', headerBidding, 'Ya.');
  1026.  
  1027. let AdvManager = function () {
  1028. this.render = function (o) {
  1029. if (!o.renderTo)
  1030. return;
  1031. let placeholder = _document.getElementById(o.renderTo);
  1032. if (!placeholder)
  1033. return _console.warn('Ya.AdvManager.render call w/o placeholder', o);
  1034. let parent = placeholder.parentNode;
  1035. placeholder.style = 'display:none!important';
  1036. parent.style = (parent.getAttribute('style') || '') + 'height:auto!important';
  1037. // fix for Yandex TV pages
  1038. if (location.hostname.startsWith('tv.yandex.')) {
  1039. let sibling = placeholder.previousSibling;
  1040. if (sibling && sibling.classList && sibling.classList.contains('tv-spin'))
  1041. sibling.style.display = 'none';
  1042. }
  1043. };
  1044. this.constructor = Object;
  1045. return nt.proxy(this, 'Ya.AdvManager', nt.NULL);
  1046. };
  1047. nt.defineOn(Ya, 'Context', new Proxy({
  1048. __longExperiment: null,
  1049. _callbacks: nt.proxy([]),
  1050. _asyncModeOn: true,
  1051. _init: nt.func(null, 'Ya.Context._init'),
  1052. _load_callbacks: nt.proxy([]),
  1053. performanceStorage: nt.proxy({}),
  1054. processCallbacks: nt.func(null, 'Ya.Context.processCallbacks'),
  1055. isAllowedRepeatAds: nt.func(null, 'Ya.Context.isAllowedRepeatAds'),
  1056. isNewLoader: nt.func(false, 'Ya.Context.isNewLoader'),
  1057. getItem: nt.func(null, 'Ya.Context.getItem'),
  1058. AdvManager: new AdvManager(),
  1059. AdvManagerStatic: new AdvManager()
  1060. }, {
  1061. get(obj, prop) {
  1062. if (prop in obj)
  1063. return obj[prop];
  1064. _console.trace(`Ya.Context.${prop} = Ya.Context.AdvManager`);
  1065. obj[prop] = obj.AdvManager;
  1066. return obj[prop];
  1067. },
  1068. set() {
  1069. return true;
  1070. }
  1071. }), 'Ya.');
  1072. let Metrika = function Metrika(x) {
  1073. this._ecommerce = '';
  1074. if (x && 'id' in x)
  1075. this.id = x.id;
  1076. else
  1077. this.id = 0;
  1078. return nt.proxy(this, 'Ya.Metrika', nt.NULL);
  1079. };
  1080. Metrika.counters = () => Ya._metrika.counters;
  1081. nt.defineOn(Ya, 'Metrika', Metrika, 'Ya.');
  1082. nt.defineOn(Ya, 'Metrika2', Metrika, 'Ya.');
  1083. let counter = new Metrika();
  1084. nt.defineOn(Ya, '_metrika', nt.proxy({
  1085. counter: counter,
  1086. counters: [counter],
  1087. hitParam: {},
  1088. counterNum: 0,
  1089. hitId: 0,
  1090. v: 1,
  1091. i: 0,
  1092. _globalMetrikaHitId: 0,
  1093. getCounters: null,
  1094. dataLayer: null,
  1095. f1: null
  1096. }), 'Ya.');
  1097. nt.defineOn(Ya, '_globalMetrikaHitId', 0, 'Ya.');
  1098. counter = {};
  1099. [
  1100. 'stringifyParams', '_getVars',
  1101. 'getUid', 'getUrl', 'getHash'
  1102. ].forEach(name => void(counter[name] = nt.func('', `Ya.counter.${name}`)));
  1103. nt.defineOn(Ya, 'counter', nt.proxy(counter, 'Ya.counter', nt.NULL), 'Ya.');
  1104. nt.defineOn(Ya, 'jserrors', [], 'Ya.');
  1105. nt.defineOn(Ya, 'onerror', nt.func(null, 'Ya.onerror'), 'Ya.');
  1106. let error_on_access = false;
  1107. if ('Ya' in win)
  1108. try {
  1109. _console.log('Found existing Ya object:', win.Ya);
  1110. for (let prop in win.Ya)
  1111. Ya[prop] = win.Ya[prop];
  1112. } catch (ignore) {
  1113. error_on_access = true;
  1114. }
  1115. if (!error_on_access) {
  1116. for (let prop in Ya)
  1117. if (prop !== '__inline_params__')
  1118. YaProps.add(prop);
  1119. nt.define('Ya', Ya);
  1120. } else
  1121. _console.warn('Looks like window.Ya blocked with error-on-access scriptlet.');
  1122. // Yandex.Metrika callbacks
  1123. let yandex_metrika_callbacks = [];
  1124. _document.addEventListener(
  1125. 'DOMContentLoaded', () => {
  1126. yandex_metrika_callbacks.forEach((f) => f && f.call(window));
  1127. yandex_metrika_callbacks.length = 0;
  1128. yandex_metrika_callbacks.push = (f) => setTimeout(f, 0);
  1129. }, false
  1130. );
  1131. nt.define('yandex_metrika_callbacks', yandex_metrika_callbacks);
  1132. }, nullTools, createStyle, abortExecution);
  1133.  
  1134. // Yandex Raven stub (some monitoring sub-system)
  1135. function yandexRavenStub() {
  1136. let nt = new nullTools({
  1137. log: true
  1138. });
  1139. nt.define('Raven', nt.proxy({
  1140. context(f) {
  1141. return f();
  1142. },
  1143. config: nt.func(
  1144. nt.proxy({}, 'Raven.config', {
  1145. val: nt.proxy({}, 'Raven.config()..', nt.NULL)
  1146. }), 'Raven.config')
  1147. }, 'Raven', nt.NULL));
  1148. }
  1149.  
  1150. // Based on https://greasyfork.org/en/scripts/21937-moonwalk-hdgo-kodik-fix v0.8
  1151. {
  1152. const log = name => _console.log(`Player FIX: Detected ${name} player in ${location.href}`);
  1153. const removeVast = (data) => {
  1154. if (data && typeof data === 'object') {
  1155. _console.log('Player configuration:', data);
  1156. if (data.advert_script && data.advert_script !== '') {
  1157. _console.log('Set data.advert_script to empty string.');
  1158. data.advert_script = '';
  1159. }
  1160. let keys = Object.getOwnPropertyNames(data);
  1161. let isVast = name => /vast|clickunder/.test(name);
  1162. if (!keys.some(isVast))
  1163. return data;
  1164. for (let key of keys)
  1165. if (typeof data[key] === 'object' && key !== 'links') {
  1166. _console.log(`Removed data.${key}:`, data[key]);
  1167. delete data[key];
  1168. }
  1169. if (data.chain) {
  1170. let need = [],
  1171. drop = [],
  1172. links = data.chain.split('.');
  1173. for (let link of links)
  1174. if (!isVast(link))
  1175. need.push(link);
  1176. else
  1177. drop.push(link);
  1178. _console.log('Dropped from the chain:', ...drop);
  1179. data.chain = need.join('.');
  1180. }
  1181. }
  1182. return data;
  1183. };
  1184.  
  1185. _document.addEventListener(
  1186. 'DOMContentLoaded',
  1187. function () {
  1188. if ('video_balancer_options' in win && 'event_callback' in win) {
  1189. log('Moonwalk');
  1190. if (win.video_balancer_options.adv)
  1191. removeVast(win.video_balancer_options.adv);
  1192. if ('_mw_adb' in win)
  1193. Object.defineProperty(win, '_mw_adb', {
  1194. set() {},
  1195. get() {
  1196. return false;
  1197. }
  1198. });
  1199. } else if (win.startKodikPlayer !== undefined) {
  1200. log('Kodik');
  1201. // skip attempt to block access to HD resolutions
  1202. const chainCall = new Proxy({}, {
  1203. get() {
  1204. return () => chainCall;
  1205. }
  1206. });
  1207. if (win.$ && win.$.prototype && win.$.prototype.addClass) {
  1208. let $addClass = win.$.prototype.addClass;
  1209. win.$.prototype.addClass = function (className) {
  1210. if (className === 'blocked')
  1211. return chainCall;
  1212. return $addClass.apply(this, arguments);
  1213. };
  1214. }
  1215. // remove ad links from the metadata
  1216. let _ajax = win.$.ajax;
  1217. win.$.ajax = (params, ...args) => {
  1218. if (params.success) {
  1219. let _s = params.success;
  1220. params.success = (data, ...args) => _s(removeVast(data), ...args);
  1221. }
  1222. return _ajax(params, ...args);
  1223. };
  1224. } else if (win.getnextepisode && win.uppodEvent) {
  1225. log('Share-Serials.net');
  1226. scriptLander(
  1227. function () {
  1228. let _setInterval = win.setInterval,
  1229. _setTimeout = win.setTimeout;
  1230. win.setInterval = function (func) {
  1231. if (typeof func === 'function' && _toString(func).includes('_delay')) {
  1232. let intv = _setInterval.call(
  1233. this,
  1234. function () {
  1235. _setTimeout.call(
  1236. this,
  1237. function (intv) {
  1238. clearInterval(intv);
  1239. let timer = _document.querySelector('#timer');
  1240. if (timer)
  1241. timer.click();
  1242. }, 100, intv);
  1243. func.call(this);
  1244. }, 5
  1245. );
  1246.  
  1247. return intv;
  1248. }
  1249. return _setInterval.apply(this, arguments);
  1250. };
  1251. win.setTimeout = function (func) {
  1252. if (typeof func === 'function' && _toString(func).includes('adv_showed'))
  1253. return _setTimeout.call(this, func, 0);
  1254. return _setTimeout.apply(this, arguments);
  1255. };
  1256. }
  1257. );
  1258. } else if ('ADC' in win) {
  1259. log('vjs-creatives plugin in');
  1260. let replacer = (obj) => {
  1261. for (let name in obj)
  1262. if (typeof obj[name] === 'function')
  1263. obj[name] = () => null;
  1264. };
  1265. replacer(win.ADC);
  1266. replacer(win.currentAdSlot);
  1267. } else if ('Playerjs' in win) {
  1268. log('Playerjs');
  1269. win.Playerjs = new Proxy(win.Playerjs, {
  1270. construct(fn, args) {
  1271. let params = args[0];
  1272. if (params && typeof params === 'object') {
  1273. delete params.preroll;
  1274. params = removeVast(params);
  1275. Object.defineProperty(params, 'hasOwnProperty', {
  1276. value(...args) {
  1277. let res = _hasOwnProperty(this, ...args);
  1278. if (typeof args[0] === 'string' && args[0].startsWith('vast_') &&
  1279. res && params[args[0]]) {
  1280. _console.log(`Removed params.${args[0]}:`, params[args[0]]);
  1281. delete params[args[0]];
  1282. return false;
  1283. }
  1284. return res;
  1285. },
  1286. enumerable: false,
  1287. configurable: true
  1288. });
  1289. }
  1290. return _construct(fn, args);
  1291. }
  1292. });
  1293. }
  1294.  
  1295. UberVK: {
  1296. if (!inIFrame)
  1297. break UberVK;
  1298. let oddNames = 'HD' in win &&
  1299. !Object.getOwnPropertyNames(win).every(n => !n.startsWith('_0x'));
  1300. if (!oddNames)
  1301. break UberVK;
  1302. log('UberVK');
  1303. XMLHttpRequest.prototype.open = () => {
  1304. throw 404;
  1305. };
  1306. }
  1307. }, false
  1308. );
  1309. }
  1310.  
  1311. // Applies wrapper function on the current page and all newly created same-origin iframes
  1312. // This is used to prevent trick which allows to get fresh page API through newly created same-origin iframes
  1313. function deepWrapAPI(wrapper) {
  1314. let wrapped = new WeakSet();
  1315. const
  1316. log = (...args) => false && _console.log(...args),
  1317. _HTMLIFrameElement = HTMLIFrameElement.prototype,
  1318. isIFrameElement = _HTMLIFrameElement.isPrototypeOf.bind(_HTMLIFrameElement),
  1319. _contentWindow = Object.getOwnPropertyDescriptor(_HTMLIFrameElement, 'contentWindow'),
  1320. _get_contentWindow = _bindCall(_contentWindow.get);
  1321.  
  1322. function wrapAPI(root) {
  1323. if (!root || wrapped.has(root))
  1324. return;
  1325. wrapped.add(root);
  1326. try {
  1327. wrapper(isIFrameElement(root) ? _get_contentWindow(root) : root);
  1328. log('Wrapped API in', (root === win) ? "main window." : root);
  1329. } catch (e) {
  1330. log('Failed to wrap API in', (root === win) ? "main window." : root, '\n', e);
  1331. }
  1332. }
  1333.  
  1334. // wrap API on contentWindow access
  1335. const getter = {
  1336. apply(get, that, args) {
  1337. wrapAPI(that);
  1338. return _apply(get, that, args);
  1339. }
  1340. };
  1341. _contentWindow.get = exportFunction(new Proxy(_contentWindow.get, getter), _HTMLIFrameElement);
  1342. Object.defineProperty(_HTMLIFrameElement, 'contentWindow', _contentWindow);
  1343. // wrap API on contentDocument access
  1344. const _contentDocument = Object.getOwnPropertyDescriptor(_HTMLIFrameElement, 'contentDocument');
  1345. _contentDocument.get = exportFunction(new Proxy(_contentDocument.get, getter), _HTMLIFrameElement);
  1346. Object.defineProperty(_HTMLIFrameElement, 'contentDocument', _contentDocument);
  1347.  
  1348. // manual children objects traverser to avoid issues
  1349. // with calling querySelectorAll on wrong types of objects
  1350. const
  1351. _nodeType = _bindCall(Object.getOwnPropertyDescriptor(_Node, 'nodeType').get),
  1352. _childNodes = _bindCall(Object.getOwnPropertyDescriptor(_Node, 'childNodes').get),
  1353. _ELEMENT_NODE = _Node.ELEMENT_NODE,
  1354. _DOCUMENT_FRAGMENT_NODE = _Node.DOCUMENT_FRAGMENT_NODE;
  1355. const wrapFrames = root => {
  1356. if (_nodeType(root) !== _ELEMENT_NODE && _nodeType(root) !== _DOCUMENT_FRAGMENT_NODE)
  1357. return; // only process nodes which may contain an IFRAME or be one
  1358. if (isIFrameElement(root)) {
  1359. wrapAPI(root);
  1360. return;
  1361. }
  1362. for (let child of _childNodes(root))
  1363. wrapFrames(child);
  1364. };
  1365.  
  1366. // wrap API in a newly appended iframe objects
  1367. const wrappedAppendChild = exportFunction(new Proxy(_Node.appendChild, {
  1368. apply(fun, that, args) {
  1369. let res = _apply(fun, that, args);
  1370. wrapFrames(args[0]);
  1371. return res;
  1372. }
  1373. }), _Node);
  1374. // ABP Freeze Element snippet replaces normal properties with getters without setters
  1375. const _Node_appendChild = Object.getOwnPropertyDescriptor(Node.prototype, 'appendChild');
  1376. if (_Node_appendChild.configurable) {
  1377. if (_Node_appendChild.value)
  1378. _Node_appendChild.value = wrappedAppendChild;
  1379. if (_Node_appendChild.get)
  1380. _Node_appendChild.get = () => wrappedAppendChild;
  1381. Object.defineProperty(_Node, 'appendChild', _Node_appendChild);
  1382. }
  1383.  
  1384. // wrap API in iframe objects created with innerHTML of element on page
  1385. const _innerHTML = Object.getOwnPropertyDescriptor(_Element, 'innerHTML');
  1386. _innerHTML.set = exportFunction(new Proxy(_innerHTML.set, {
  1387. apply(set, that, args) {
  1388. _apply(set, that, args);
  1389. if (_document.contains(that))
  1390. wrapFrames(that);
  1391. }
  1392. }), _Element);
  1393. Object.defineProperty(_Element, 'innerHTML', _innerHTML);
  1394.  
  1395. wrapAPI(win);
  1396. }
  1397.  
  1398. // piguiqproxy.com / zmctrack.net circumvention and onerror callback prevention
  1399. scriptLander(
  1400. () => {
  1401. // onerror callback blacklist
  1402. let masks = [],
  1403. //blockAll = /(^|\.)(rutracker-org\.appspot\.com)$/,
  1404. isBlocked = url => masks.some(mask => mask.test(url)); // || blockAll.test(location.hostname);
  1405. for (let filter of [ // blacklist
  1406. // global
  1407. '/adv/www/',
  1408. // adservers
  1409. '||185.87.50.147^',
  1410. '||10root25.website^', '||24video.xxx^',
  1411. '||adlabs.ru^', '||adspayformymortgage.win^', '||aliru1.ru^', '||amgload.net^', '||aviabay.ru^',
  1412. '||bgrndi.com^', '||brokeloy.com^',
  1413. '||cdnjs-aws.ru^', '||cnamerutor.ru^',
  1414. '||directadvert.ru^', '||docfilms.info^', '||dreadfula.ru^', '||dsn-fishki.ru^',
  1415. '||et-cod.com^', '||et-code.ru^', '||etcodes.com^',
  1416. '||film-doma.ru^',
  1417. '||free-torrent.org^', '||free-torrent.pw^',
  1418. '||free-torrents.org^', '||free-torrents.pw^',
  1419. '||game-torrent.info^', '||gocdn.ru^',
  1420. '||hdkinoshka.com^', '||hghit.com^', '||hindcine.net^',
  1421. '||kinotochka.net^', '||kinott.com^', '||kinott.ru^',
  1422. '||klcheck.com^', '||kuveres.com^',
  1423. '||lepubs.com^', '||luxadv.com^', '||luxup.ru^', '||luxupcdna.com^',
  1424. '||marketgid.com^', '||mebablo.com^', '||mixadvert.com^', '||mxtads.com^',
  1425. '||nickhel.com^',
  1426. '||oconner.biz^', '||oconner.link^', '||octoclick.net^', '||octozoon.org^',
  1427. '||pigiuqproxy.com^', '||piguiqproxy.com^', '||pkpojhc.com^',
  1428. '||psma01.com^', '||psma02.com^', '||psma03.com^',
  1429. '||rcdn.pro^', '||recreativ.ru^', '||redtram.com^', '||regpole.com^',
  1430. '||rootmedia.ws^', '||ruttwind.com^', '||rutvind.com^',
  1431. '||skidl.ru^', '||smi2.net^', '||smcheck.org^',
  1432. '||torvind.com^', '||traffic-media.co^', '||trafmag.com^', '||trustjs.net^', '||ttarget.ru^',
  1433. '||u-dot-id-adtool.appspot.com^', '||utarget.ru^',
  1434. '||webadvert-gid.ru^', '||webadvertgid.ru^',
  1435. '||xxuhter.ru^',
  1436. '||yuiout.online^',
  1437. '||zmctrack.net^', '||zoom-film.ru^'
  1438. ])
  1439. masks.push(new RegExp(
  1440. filter.replace(/([\\/[\].+?(){}$])/g, '\\$1')
  1441. .replace(/\*/g, '.*?')
  1442. .replace(/\^(?!$)/g, '\\.?[^\\w%._-]')
  1443. .replace(/\^$/, '\\.?([^\\w%._-]|$)')
  1444. .replace(/^\|\|/, '^((ws|http)s?:|/)/+([^/.]+\\.)*?'),
  1445. 'i'));
  1446. // main script
  1447. deepWrapAPI(root => {
  1448. FxProxyToStringFix(root);
  1449.  
  1450. const
  1451. _defineProperty = root.Object.defineProperty,
  1452. _getOwnPropertyDescriptor = root.Object.getOwnPropertyDescriptor,
  1453. _dispatchEvent = _bindCall(root.EventTarget.prototype.dispatchEvent);
  1454.  
  1455. const dispatchCustomEvent = (
  1456. target, name, opts = {
  1457. bubble: false,
  1458. cancelable: false
  1459. }
  1460. ) => _dispatchEvent(target, new CustomEvent(name, opts));
  1461.  
  1462. {
  1463. // 'onerror' handler for scripts from blacklisted sources
  1464. const scriptMap = new WeakMap();
  1465. const _HTMLScriptElement = root.HTMLScriptElement,
  1466. _HTMLImageElement = root.HTMLImageElement;
  1467. const _get_tagName = _bindCall(_getOwnPropertyDescriptor(root.Element.prototype, 'tagName').get),
  1468. _get_scr_src = _bindCall(_getOwnPropertyDescriptor(_HTMLScriptElement.prototype, 'src').get),
  1469. _get_img_src = _bindCall(_getOwnPropertyDescriptor(_HTMLImageElement.prototype, 'src').get);
  1470. const _get_src = node => {
  1471. if (node instanceof _HTMLScriptElement)
  1472. return _get_scr_src(node);
  1473. if (node instanceof _HTMLImageElement)
  1474. return _get_img_src(node);
  1475. return undefined;
  1476. };
  1477. const _onerror = _getOwnPropertyDescriptor(root.HTMLElement.prototype, 'onerror');
  1478. _onerror.get = exportFunction(new Proxy(_onerror.get, {
  1479. apply(_fun, that) {
  1480. return scriptMap.get(that) || null;
  1481. }
  1482. }), root.HTMLElement.prototype);
  1483. _onerror.set = exportFunction(new Proxy(_onerror.set, {
  1484. apply(fun, that, args) {
  1485. let [callback] = args;
  1486. if (typeof callback !== 'function') {
  1487. scriptMap.delete(that);
  1488. _apply(fun, that, args);
  1489. return;
  1490. }
  1491. scriptMap.set(that, callback);
  1492. _apply(fun, that, [function () {
  1493. let src = _get_src(this);
  1494. if (isBlocked(src)) {
  1495. _console.trace(`Blocked "onerror" callback from ${_get_tagName(this)}: ${src}`);
  1496. return;
  1497. }
  1498. _apply(scriptMap.get(this), this, arguments);
  1499. }]);
  1500. }
  1501. }), root.HTMLElement.prototype);
  1502. _defineProperty(root.HTMLElement.prototype, 'onerror', _onerror);
  1503. }
  1504. // Simplistic WebSocket wrapper for Maxthon and Firefox before v58
  1505. // once again seems required in Google Chrome and similar browsers due to zmctrack.net -_-
  1506. if (_getOwnPropertyDescriptor(root, 'WebSocket'))
  1507. root.WebSocket = exportFunction(new Proxy(root.WebSocket, {
  1508. construct(ws, args) {
  1509. if (isBlocked(args[0])) {
  1510. _console.log('Blocked WS connection:', args[0]);
  1511. return {};
  1512. }
  1513. return _construct(ws, args);
  1514. }
  1515. }), root);
  1516. // Block popular method to open a new window in Google Chrome by dispatching a custom click
  1517. // event on a newly created anchor with _blank target. Untrusted events must not open new windows.
  1518. const clickWhitelist = /^([^.]\.)*?nakarte\.me$/;
  1519. root.EventTarget.prototype.dispatchEvent = exportFunction(new Proxy(root.EventTarget.prototype.dispatchEvent, {
  1520. apply(fun, that, args) {
  1521. const e = args[0];
  1522. if (!clickWhitelist.test(win.location.hostname) &&
  1523. !e.isTrusted && e.type === 'click' && e.constructor.name === 'MouseEvent' &&
  1524. !that.parentNode && that.tagName === 'A' && that.target[0] === '_') {
  1525. _console.log('Blocked dispatching a click event on a parentless anchor:', that);
  1526. return;
  1527. }
  1528. return _apply(fun, that, args);
  1529. }
  1530. }), root.EventTarget.prototype);
  1531. // blacklist of domains where all third-party requests are ignored
  1532. const ondomains = /(^|[/.@])oane\.ws($|[:/])/i;
  1533. const yandex_direct = /^(https?:)?\/\/([^.]+\.)??yandex(\.[a-z]{2,3}){1,2}\/(images\/[a-z0-9/_-]{40,}|jstracer?|j?clck\/.*|set\/s\/rsya-tag-users\/data(\?.*)?|static\/main\.js(\?.*)?)$/i;
  1534. const more_y_direct = /^(https?:)?\/\/((([^.]+\.)??(drive2|kakprosto|razlozhi)\.ru\/(.{290,}|[a-z0-9/_-]{100,}))|yastatic\.net\/.*?\/chunks\/promo\/.*)$/i;
  1535. const whitelist = /^(https?:)?\/\/yandex\.ru\/yobject$/;
  1536. const fabPatterns = /\/fuckadblock/i;
  1537.  
  1538. const blockedUrls = new Set();
  1539.  
  1540. function checkRequest(fname, method, url) {
  1541. let block = isBlocked(url) ||
  1542. ondomains.test(location.hostname) && !ondomains.test(url) ||
  1543. yandex_direct.test(url) || more_y_direct.test(url);
  1544. let allow = block && whitelist.test(url) ||
  1545. // Fix for infinite load on Yandex Images: find image, open "other sizes and similar images" in a new tab, click on a preview of a similar image
  1546. (block && method === 'script.src' &&
  1547. root.location.pathname === '/images/search' && root.location.hostname.startsWith('yandex.') &&
  1548. url.startsWith('http') && url.includes('/images/')) || // Direct URLs are similar, but don't have protocol for some reason
  1549. (block && root.location.hostname === 'widgets.kinopoisk.ru' && url.includes('/static/main.js?')) ||
  1550. (block && !url.startsWith('http') && // drive2.ru hid a little CSS style in their requests which shows page content like this
  1551. (root.location.hostname === 'drive2.ru' || root.location.hostname.endsWith('.drive2.ru')));
  1552. if (allow) {
  1553. block = false;
  1554. _console.trace(`Allowed ${fname} ${method} request %o from %o`, url, root.location.href);
  1555. }
  1556. if (block) {
  1557. if (!blockedUrls.has(url)) // don't repeat log if the same URL were blocked more than once
  1558. _console.trace(`Blocked ${fname} ${method} request %o from %o`, url, root.location.href);
  1559. blockedUrls.add(url);
  1560. return true;
  1561. }
  1562. return false;
  1563. }
  1564.  
  1565. // workaround for broken searchbar on market.yandex.ru
  1566. const checkOnloadEvent = location.hostname.startsWith('market.yandex.');
  1567. const triggerLoadEvent = /^(https?:)?\/\/([^.]+\.)??yandex(\.[a-z]{2,3}){1,2}\/(j?clck\/.*)$/i;
  1568.  
  1569. // XHR Wrapper
  1570. const _proto = root.XMLHttpRequest && root.XMLHttpRequest.prototype;
  1571. if (_proto) {
  1572. const xhrStopList = new WeakSet();
  1573. const xhrDispatchLoadList = new WeakSet();
  1574. _proto.open = exportFunction(new Proxy(_proto.open, {
  1575. apply(fun, that, args) {
  1576. if (checkOnloadEvent && triggerLoadEvent.test(args[1]))
  1577. xhrDispatchLoadList.add(that);
  1578. if (checkRequest('xhr', ...args)) {
  1579. xhrStopList.add(that);
  1580. return;
  1581. }
  1582. return _apply(fun, that, args);
  1583. }
  1584. }), _proto);
  1585. const _DONE = _proto.DONE; // 4
  1586. const sendWrapper = {
  1587. apply(fun, that, args) {
  1588. if (xhrStopList.has(that)) {
  1589. if (that.readyState !== _DONE && xhrDispatchLoadList.has(that)) {
  1590. that.readyState = _DONE;
  1591. setTimeout(() => dispatchCustomEvent(that, 'load'), 0);
  1592. }
  1593. return null;
  1594. }
  1595. return _apply(fun, that, args);
  1596. }
  1597. };
  1598. ['send', 'setRequestHeader', 'getAllResponseHeaders'].forEach(
  1599. name => _proto[name] = exportFunction(new Proxy(_proto[name], sendWrapper), _proto)
  1600. );
  1601. // simulate readyState === 1 for blocked requests
  1602. const _readyState = Object.getOwnPropertyDescriptor(_proto, 'readyState');
  1603. _readyState.get = exportFunction(new Proxy(_readyState.get, {
  1604. apply(fun, that, args) {
  1605. return xhrStopList.has(that) ? 1 : _apply(fun, that, args);
  1606. }
  1607. }), _proto);
  1608. Object.defineProperty(_proto, 'readyState', _readyState);
  1609. }
  1610.  
  1611. if (root.fetch)
  1612. root.fetch = exportFunction(new Proxy(root.fetch, {
  1613. apply(fun, that, args) {
  1614. let [url, opts] = args;
  1615. let method = opts && opts.method || 'GET';
  1616. if (typeof url === 'object' && 'headers' in url &&
  1617. 'url' in url && 'method' in url) // url instanceof Request
  1618. ({
  1619. url,
  1620. method
  1621. } = url);
  1622. if (checkRequest('fetch', method, url))
  1623. return new Promise(() => null);
  1624. return _apply(fun, that, args);
  1625. }
  1626. }), root);
  1627.  
  1628. const _script_src = Object.getOwnPropertyDescriptor(root.HTMLScriptElement.prototype, 'src');
  1629. _script_src.set = exportFunction(new Proxy(_script_src.set, {
  1630. apply(fun, that, args) {
  1631. if (fabPatterns.test(args[0])) {
  1632. _console.trace('Blocked set script.src request:', args[0]);
  1633. deployFABStub(root);
  1634. setTimeout(() => dispatchCustomEvent(that, 'load'), 0);
  1635. return;
  1636. }
  1637. return checkRequest('set', 'script.src', args[0]) || _apply(fun, that, args);
  1638. }
  1639. }), root.HTMLScriptElement.prototype);
  1640. Object.defineProperty(root.HTMLScriptElement.prototype, 'src', _script_src);
  1641.  
  1642. const adregain_pattern = /ggg==" alt="advertisement"/;
  1643. if (root.self !== root.top) // in IFrame
  1644. root.document.write = exportFunction(new Proxy(root.document.write, {
  1645. apply(fun, that, args) {
  1646. if (adregain_pattern.test(args[0])) {
  1647. _console.log('Skipped AdRegain frame.');
  1648. args[0] = '';
  1649. }
  1650. return _apply(fun, that, args);
  1651. }
  1652. }), root.document);
  1653. });
  1654. }, deepWrapAPI
  1655. );
  1656.  
  1657. // === Helper functions ===
  1658.  
  1659. const gardener = (() => {
  1660. // function to search and remove nodes by content
  1661. // selector - standard CSS selector to define set of nodes to check
  1662. // words - regular expression to check content of the suspicious nodes
  1663. // params - object with multiple extra parameters:
  1664. // .log - display log in the console
  1665. // .hide - set display to none instead of removing from the page
  1666. // .parent - parent node to remove if content is found in the child node
  1667. // .siblings - number of simling nodes to remove (excluding text nodes)
  1668. const scissors = (selector, words, scope, params) => {
  1669. const logger = (...args) => {
  1670. if (params.log) _console.log(...args);
  1671. };
  1672. const hideStyleStr = ';display:none!important;';
  1673. const getStyleAtt = node => _getAttribute(node, 'style') || '';
  1674. const scHide = node => {
  1675. const style = getStyleAtt(node);
  1676. if (!style.includes(hideStyleStr))
  1677. _setAttribute(node, 'style', style + hideStyleStr);
  1678. };
  1679.  
  1680. if (!scope.contains(_document.body))
  1681. logger('[s] scope', scope);
  1682. let remFunc = (params.hide ? scHide : node => node.parentNode.removeChild(node)),
  1683. iterFunc = (params.siblings > 0 ? 'nextElementSibling' : 'previousElementSibling'),
  1684. toRemove = [],
  1685. siblings;
  1686. for (let node of scope.querySelectorAll(selector)) {
  1687. // drill up to a parent node if specified, break if not found
  1688. if (params.parent) {
  1689. let old = node;
  1690. node = node.closest(params.parent);
  1691. if (node === null || node.contains(scope)) {
  1692. logger('[s] went out of scope with', old);
  1693. continue;
  1694. }
  1695. }
  1696. if (getStyleAtt(node).includes(hideStyleStr))
  1697. continue;
  1698. logger('[s] processing', node);
  1699. if (toRemove.includes(node))
  1700. continue;
  1701. if (words.test(node.innerHTML)) {
  1702. // skip node if already marked for removal
  1703. logger('[s] marked for removal');
  1704. toRemove.push(node);
  1705. // add multiple nodes if defined more than one sibling
  1706. siblings = Math.abs(params.siblings) || 0;
  1707. while (siblings) {
  1708. node = node[iterFunc];
  1709. if (!node) break; // can't go any further - exit
  1710. logger('[s] adding sibling node', node);
  1711. toRemove.push(node);
  1712. siblings -= 1;
  1713. }
  1714. }
  1715. }
  1716. const toSkip = [];
  1717. toSkip.checkNode = node => !toRemove.every(other => other === node || !node.contains(other));
  1718. for (let node of toRemove)
  1719. if (toSkip.checkNode(node))
  1720. toSkip.push(node);
  1721. if (toRemove.length)
  1722. logger(`[s] proceeding with ${params.hide?'hide':'removal'} of`, toRemove, `skip`, toSkip);
  1723. for (let node of toRemove)
  1724. if (!toSkip.includes(node))
  1725. remFunc(node);
  1726. };
  1727.  
  1728. // function to perform multiple checks if ads inserted with a delay
  1729. // by default does 30 checks withing a 3 seconds unless nonstop mode specified
  1730. // also does 1 extra check when a page completely loads
  1731. // selector and words - passed dow to scissors
  1732. // params - object with multiple extra parameters:
  1733. // .log - display log in the console
  1734. // .root - selector to narrow down scope to scan;
  1735. // .observe - if true then check will be performed continuously;
  1736. // Other parameters passed down to scissors.
  1737. return (selector, words, params) => {
  1738. let logger = (...args) => {
  1739. if (params.log) _console.log(...args);
  1740. };
  1741. params = params || {};
  1742. logger(`[gardener] selector: '${selector}' detector: ${words} options: ${JSON.stringify(params)}`);
  1743. let scope;
  1744. let globalScope = [_de.parentNode];
  1745. let domLoaded = false;
  1746. let getScope = root => root ? _de.querySelectorAll(root) : globalScope;
  1747. let onevent = e => {
  1748. logger(`[gardener] cleanup on ${Object.getPrototypeOf(e).toString().slice(1, -1).split(/\s/)[1]} "${e.type}"`);
  1749. for (let node of scope)
  1750. scissors(selector, words, node, params);
  1751. };
  1752. let repeater = n => {
  1753. if (!domLoaded && n) {
  1754. setTimeout(repeater, 500, n - 1);
  1755. scope = getScope(params.root);
  1756. if (!scope) // exit if the root element is not present on the page
  1757. return 0;
  1758. onevent({
  1759. type: 'Repeater'
  1760. });
  1761. }
  1762. };
  1763. repeater(20);
  1764. _document.addEventListener(
  1765. 'DOMContentLoaded', (e) => {
  1766. domLoaded = true;
  1767. // narrow down scope to a specific element
  1768. scope = getScope(params.root);
  1769. if (!scope) // exit if the root element is not present on the page
  1770. return 0;
  1771. logger('[g] scope', scope);
  1772. // add observe mode if required
  1773. if (params.observe) {
  1774. let params = {
  1775. childList: true,
  1776. subtree: true
  1777. };
  1778. let observer = new MutationObserver(
  1779. function (ms) {
  1780. for (let m of ms)
  1781. if (m.addedNodes.length)
  1782. onevent(m);
  1783. }
  1784. );
  1785. for (let node of scope)
  1786. observer.observe(node, params);
  1787. logger('[g] observer enabled');
  1788. }
  1789. onevent(e);
  1790. }, false);
  1791. // wait for a full page load to do one extra cut
  1792. win.addEventListener('load', onevent, false);
  1793. };
  1794. })();
  1795.  
  1796. // wrap popular methods to open a new tab to catch specific behaviours
  1797. function createWindowOpenWrapper(openFunc) {
  1798. const parser = _createElement('a');
  1799. const openWhitelist = (url, parent) => {
  1800. parser.href = url;
  1801. return parser.hostname === 'www.imdb.com' || parser.hostname === 'www.kinopoisk.ru' ||
  1802. parent.hostname === 'radikal.ru' && url === undefined;
  1803. };
  1804.  
  1805. function redefineOpen(root) {
  1806. if ('open' in root)
  1807. root.open = new Proxy(root.open, {
  1808. apply(fun, that, args) {
  1809. if (openWhitelist(args[0], location)) {
  1810. _console.log('Whitelisted popup:', ...args);
  1811. return _apply(fun, that, args);
  1812. }
  1813. return openFunc(...args);
  1814. }
  1815. });
  1816. }
  1817. redefineOpen(win);
  1818.  
  1819. const getTagName = _bindCall(Object.getOwnPropertyDescriptor(_Element, 'tagName').get);
  1820. const hasOwnProperty = _bindCall(Object.hasOwnProperty);
  1821. const createElementWrapper = {
  1822. apply(fun, that, args) {
  1823. const el = _apply(fun, that, args);
  1824. // redefine window.open in first-party frames
  1825. if (getTagName(el) === 'IFRAME' || getTagName(el) === 'OBJECT')
  1826. el.addEventListener('load', (e) => {
  1827. try {
  1828. redefineOpen(e.target.contentWindow);
  1829. } catch (ignore) {}
  1830. }, false);
  1831. return el;
  1832. }
  1833. };
  1834.  
  1835. function redefineCreateElement(obj) {
  1836. for (let root of [obj.document, Object.getPrototypeOf(obj.HTMLDocument.prototype)])
  1837. if (hasOwnProperty(root, 'createElement'))
  1838. root.createElement = exportFunction(new Proxy(root.createElement, createElementWrapper), root);
  1839. }
  1840. redefineCreateElement(win);
  1841.  
  1842. // wrap window.open in newly added first-party frames
  1843. const wrappedAppendChild = exportFunction(new Proxy(_Node.appendChild, {
  1844. apply(fun, that, args) {
  1845. let el = _apply(fun, that, args);
  1846. if (el instanceof HTMLIFrameElement)
  1847. try {
  1848. redefineOpen(el.contentWindow);
  1849. redefineCreateElement(el.contentWindow);
  1850. } catch (ignore) {}
  1851. return el;
  1852. }
  1853. }), _Node);
  1854. // ABP Freeze Element snippet replaces normal properties with getters without setters
  1855. const _Node_appendChild = Object.getOwnPropertyDescriptor(Node.prototype, 'appendChild');
  1856. if (_Node_appendChild.configurable) {
  1857. if (_Node_appendChild.value)
  1858. _Node_appendChild.value = wrappedAppendChild;
  1859. if (_Node_appendChild.get)
  1860. _Node_appendChild.get = () => wrappedAppendChild;
  1861. Object.defineProperty(_Node, 'appendChild', _Node_appendChild);
  1862. }
  1863. }
  1864.  
  1865. // Function to catch and block various methods to open a new window with 3rd-party content.
  1866. // Some advertisement networks went way past simple window.open call to circumvent default popup protection.
  1867. // This funciton blocks window.open, ability to restore original window.open from an IFRAME object,
  1868. // ability to perform an untrusted (not initiated by user) click on a link, click on a link without a parent
  1869. // node or simply a link with piece of javascript code in the HREF attribute.
  1870. function preventPopups() {
  1871. // call sandbox-me if in iframe and not whitelisted
  1872. if (inIFrame) {
  1873. win.top.postMessage({
  1874. name: 'sandbox-me',
  1875. href: win.location.href
  1876. }, '*');
  1877. return;
  1878. }
  1879.  
  1880. scriptLander(() => {
  1881. let open = (...args) => {
  1882. '[native code]';
  1883. _console.trace('Site attempted to open a new window', ...args);
  1884. return {
  1885. document: nt.proxy({
  1886. write: nt.func({}, 'write'),
  1887. writeln: nt.func({}, 'writeln')
  1888. }),
  1889. location: nt.proxy({})
  1890. };
  1891. };
  1892.  
  1893. createWindowOpenWrapper(open);
  1894.  
  1895. _console.log('Popup prevention enabled.');
  1896. }, nullTools, createWindowOpenWrapper);
  1897. }
  1898.  
  1899. // Helper function to close background tab if site opens itself in a new tab and then
  1900. // loads a 3rd-party page in the background one (thus performing background redirect).
  1901. function preventPopunders() {
  1902. // create "close_me" event to call high-level window.close()
  1903. let eventName = `close_me_${Math.random().toString(36).substr(2)}`;
  1904. let callClose = () => {
  1905. _console.log('close call');
  1906. window.close();
  1907. };
  1908. window.addEventListener(eventName, callClose, true);
  1909.  
  1910. scriptLander(() => {
  1911. // get host of a provided URL with help of an anchor object
  1912. // unfortunately new URL(url, window.location) generates wrong URL in some cases
  1913. let parseURL = _document.createElement('A');
  1914. let getHost = url => {
  1915. parseURL.href = url;
  1916. return parseURL.hostname;
  1917. };
  1918. // site went to a new tab and attempts to unload
  1919. // call for high-level close through event
  1920. let closeWindow = () => window.dispatchEvent(new CustomEvent(eventName, {}));
  1921. // check is URL local or goes to different site
  1922. let isLocal = (url) => {
  1923. if (url === location.pathname || url === location.href)
  1924. return true; // URL points to current pathname or full address
  1925. let host = getHost(url);
  1926. let site = location.hostname;
  1927. return host !== '' && // URLs with unusual protocol may have empty 'host'
  1928. (site === host || site.endsWith(`.${host}`) || host.endsWith(`.${site}`));
  1929. };
  1930.  
  1931. let _open = window.open.bind(window);
  1932. let open = (...args) => {
  1933. '[native code]';
  1934. let url = args[0];
  1935. if (url && isLocal(url))
  1936. window.addEventListener('beforeunload', closeWindow, true);
  1937. return _open(...args);
  1938. };
  1939.  
  1940. createWindowOpenWrapper(open);
  1941.  
  1942. _console.log("Background redirect prevention enabled.");
  1943. }, `let eventName="${eventName}"`, nullTools, createWindowOpenWrapper);
  1944. }
  1945.  
  1946. // Mix between check for popups and popunders
  1947. // Significantly more agressive than both and can't be used as universal solution
  1948. function preventPopMix() {
  1949. if (inIFrame) {
  1950. win.top.postMessage({
  1951. name: 'sandbox-me',
  1952. href: win.location.href
  1953. }, '*');
  1954. return;
  1955. }
  1956.  
  1957. // create "close_me" event to call high-level window.close()
  1958. let eventName = `close_me_${Math.random().toString(36).substr(2)}`;
  1959. let callClose = () => {
  1960. _console.log('close call');
  1961. window.close();
  1962. };
  1963. window.addEventListener(eventName, callClose, true);
  1964.  
  1965. scriptLander(() => {
  1966. let _open = window.open,
  1967. parseURL = _document.createElement('A');
  1968. // get host of a provided URL with help of an anchor object
  1969. // unfortunately new URL(url, window.location) generates wrong URL in some cases
  1970. let getHost = (url) => {
  1971. parseURL.href = url;
  1972. return parseURL.host;
  1973. };
  1974. // site went to a new tab and attempts to unload
  1975. // call for high-level close through event
  1976. let closeWindow = () => {
  1977. _open(window.location, '_self');
  1978. window.dispatchEvent(new CustomEvent(eventName, {}));
  1979. };
  1980. // check is URL local or goes to different site
  1981. function isLocal(url) {
  1982. let loc = window.location;
  1983. if (url === loc.pathname || url === loc.href)
  1984. return true; // URL points to current pathname or full address
  1985. let host = getHost(url),
  1986. site = loc.host;
  1987. if (host === '')
  1988. return false; // URLs with unusual protocol may have empty 'host'
  1989. if (host.length > site.length)
  1990. [site, host] = [host, site];
  1991. return site.includes(host, site.length - host.length);
  1992. }
  1993.  
  1994. // add check for redirect for 5 seconds, then disable it
  1995. function checkRedirect() {
  1996. window.addEventListener('beforeunload', closeWindow, true);
  1997. setTimeout(closeWindow => window.removeEventListener('beforeunload', closeWindow, true), 5000, closeWindow);
  1998. }
  1999.  
  2000. function open(url, name) {
  2001. '[native code]';
  2002. if (url && isLocal(url) && (!name || name === '_blank')) {
  2003. _console.trace('Suspicious local new window', ...arguments);
  2004. checkRedirect();
  2005. /* jshint validthis: true */
  2006. return _open.apply(this, arguments);
  2007. }
  2008. _console.trace('Blocked attempt to open a new window', ...arguments);
  2009. return {
  2010. document: {
  2011. write() {},
  2012. writeln() {}
  2013. }
  2014. };
  2015. }
  2016.  
  2017. function clickHandler(e) {
  2018. let link = e.target,
  2019. url = link.href || '';
  2020. if (e.targetParentNode && e.isTrusted || link.target !== '_blank') {
  2021. _console.log('Link', link, 'were created dinamically, but looks fine.');
  2022. return true;
  2023. }
  2024. if (isLocal(url) && link.target === '_blank') {
  2025. _console.log('Suspicious local link', link);
  2026. checkRedirect();
  2027. return;
  2028. }
  2029. _console.log('Blocked suspicious click on a link', link);
  2030. e.stopPropagation();
  2031. e.preventDefault();
  2032. }
  2033.  
  2034. createWindowOpenWrapper(open, clickHandler);
  2035.  
  2036. _console.log("Mixed popups prevention enabled.");
  2037. }, `let eventName="${eventName}"`, createWindowOpenWrapper);
  2038. }
  2039. // External listener for case when site known to open popups were loaded in iframe
  2040. // It will sandbox any iframe which will send message 'forbid.popups' (preventPopups sends it)
  2041. // Some sites replace frame's window.location with data-url to run in clean context
  2042. if (!inIFrame) window.addEventListener(
  2043. 'message',
  2044. function (e) {
  2045. if (!e.data || e.data.name !== 'sandbox-me' || !e.data.href)
  2046. return;
  2047. let src = e.data.href;
  2048. for (let frame of _document.querySelectorAll('iframe'))
  2049. if (frame.contentWindow === e.source) {
  2050. if (frame.hasAttribute('sandbox')) {
  2051. if (!frame.sandbox.contains('allow-popups'))
  2052. return; // exit frame since it's already sandboxed and popups are blocked
  2053. // remove allow-popups if frame already sandboxed
  2054. frame.sandbox.remove('allow-popups');
  2055. } else
  2056. // set sandbox mode for troublesome frame and allow scripts, forms and a few other actions
  2057. // technically allowing both scripts and same-origin allows removal of the sandbox attribute,
  2058. // but to apply content must be reloaded and this script will re-apply it in the result
  2059. frame.setAttribute('sandbox', 'allow-forms allow-scripts allow-presentation allow-top-navigation allow-same-origin');
  2060. _console.log('Disallowed popups from iframe', frame);
  2061.  
  2062. // reload frame content to apply restrictions
  2063. if (!src) {
  2064. src = frame.src;
  2065. _console.log('Unable to get current iframe location, reloading from src', src);
  2066. } else
  2067. _console.log('Reloading iframe with URL', src);
  2068. frame.src = 'about:blank';
  2069. frame.src = src;
  2070. }
  2071. }, false
  2072. );
  2073.  
  2074. const evalPatternYandex = /{exports:{},id:r,loaded:!1}|containerId:(.|\r|\n)+params:/,
  2075. evalPatternGeneric = /_0x|location\s*?=|location.href\s*?=|location.assign\(|open\(/i;
  2076.  
  2077. function selectiveEval(...patterns) {
  2078. let fullLog = false;
  2079. if (patterns[patterns.length - 1] === true) {
  2080. fullLog = true;
  2081. patterns.length = patterns.length - 1;
  2082. }
  2083. if (patterns.length === 0)
  2084. patterns.push(evalPatternGeneric);
  2085. win.eval = new Proxy(win.eval, {
  2086. apply(fun, that, args) {
  2087. if (patterns.some(pattern => pattern.test(args[0]))) {
  2088. _console[fullLog ? 'trace' : 'log'](`Skipped eval ${fullLog ? args[0] : args[0].slice(0, 512)}${fullLog ? '' : '\u2026'}`);
  2089. return null;
  2090. }
  2091. try {
  2092. if (fullLog)
  2093. _console.trace(`eval ${args[0]}`);
  2094. return _apply(fun, that, args);
  2095. } catch (e) {
  2096. _console.error('Crash source:', args[0]);
  2097. throw e;
  2098. }
  2099. }
  2100. });
  2101. }
  2102. selectiveEval.toString = new Proxy(selectiveEval.toString, {
  2103. apply(...args) {
  2104. return `${_apply(...args)} const evalPatternYandex = ${evalPatternYandex}, evalPatternGeneric = ${evalPatternGeneric}`;
  2105. }
  2106. });
  2107.  
  2108. // hides cookies by pattern and attempts to remove them if they already set
  2109. // also prevents setting new versions of such cookies
  2110. function selectiveCookies(scPattern = '', opts = {}) {
  2111. let patterns = scPattern.split('|');
  2112. if (patterns[0] !== '~default') {
  2113. // Google Analytics cookies
  2114. patterns.push('_g(at?|id)|__utm[a-z]');
  2115. // Yandex ABP detection cookies
  2116. patterns.push('altrs|bltsr|blcrm');
  2117. } else
  2118. patterns.shift();
  2119. let blacklist = new RegExp(`(^|;\\s?)(${patterns.join('|')})($|=)`);
  2120.  
  2121. const root = opts.root || win;
  2122. const _root_Document = Object.getPrototypeOf(root.HTMLDocument.prototype);
  2123. const _doc_proto = ('cookie' in _root_Document) ? _root_Document : Object.getPrototypeOf(root.document);
  2124. const _cookie = Object.getOwnPropertyDescriptor(_doc_proto, 'cookie');
  2125. const _set_cookie = _bindCall(_cookie.set);
  2126.  
  2127. let removed = new Set();
  2128. const removeLog = (cookie) => {
  2129. let strings = [`${cookie.name}=${cookie.value}`];
  2130. if (cookie.domain)
  2131. strings.push(`domain=${cookie.domain}`);
  2132. if (cookie.path)
  2133. strings.push(`path=${cookie.path}`);
  2134. if (cookie.sameSite !== 'unspecified')
  2135. strings.push(`sameSite=${cookie.sameSite}`);
  2136. for (let name of ['httpOnly', 'hostOnly', 'secure', 'session'])
  2137. if (cookie[name]) strings.push(name);
  2138. let full = strings.join('; ');
  2139. if (!removed.has(full))
  2140. _console.log(`Removed cookie: ${full}`);
  2141. removed.add(full);
  2142. };
  2143.  
  2144. let skipTM = true;
  2145. const asyncCookieCleaner = () => {
  2146. GM.cookie.list({
  2147. url: location.href
  2148. }).then(cookies => {
  2149. if (!cookies) return;
  2150. if (skipTM) {
  2151. cookies = cookies.filter(x => !x.name.startsWith('TM_'));
  2152. skipTM = false;
  2153. }
  2154. for (let cookie of cookies)
  2155. if (blacklist.test(cookie.name)) {
  2156. if (skipTM && cookie.name)
  2157. continue;
  2158. GM.cookie.delete(cookie);
  2159. removeLog(cookie);
  2160. }
  2161. }, () => null);
  2162. };
  2163.  
  2164. const useOldPass = (() => {
  2165. if (GM.info.scriptHandler === 'Tampermonkey' && GM.info.version === undefined)
  2166. return false; // TM Beta doesn't have a version, apparently
  2167. // returns true if GM version <= 4.10
  2168. let v = GM.info.version.split('.').map(x => x - 0);
  2169. return v[0] < 4 || v[0] === 4 && v[1] <= 10 && v[2] === undefined || GM.info.scriptHandler !== 'Tampermonkey';
  2170. })();
  2171.  
  2172. const getName = (cookie) => cookie && cookie.indexOf('=') ? /^(.+?)=/.exec(cookie)[1] : cookie;
  2173.  
  2174. const removeCookie = (cookie, that) => {
  2175. const expireCookie = (name, domain) => {
  2176. domain = domain ? `;domain=${domain.join('.')}` : '';
  2177. _set_cookie(that, `${name}=;Max-Age=0;path=/${domain}`);
  2178. _set_cookie(that, `${name}=;Max-Age=0;path=/${domain.replace('=', '=.')}`);
  2179. };
  2180. const name = getName(cookie);
  2181. const domain = that.location.hostname.split('.');
  2182.  
  2183. expireCookie(name);
  2184. while (domain.length > 1) {
  2185. try {
  2186. expireCookie(name, domain);
  2187. } catch (e) {
  2188. _console.error(e);
  2189. }
  2190. domain.shift();
  2191. }
  2192. _console.log('Removing existing cookie:', cookie);
  2193. };
  2194.  
  2195. if (_cookie) {
  2196. // skip setting unwanted cookies
  2197. _cookie.set = new Proxy(_cookie.set, {
  2198. apply(fun, that, args) {
  2199. if (useOldPass) {
  2200. let cookie = args[0];
  2201. if (blacklist.test(getName(cookie))) {
  2202. _console.log('Ignored cookie: %s', cookie);
  2203. removeCookie(cookie, that);
  2204. return;
  2205. }
  2206. }
  2207. _apply(fun, that, args);
  2208. asyncCookieCleaner();
  2209. return true;
  2210. }
  2211. });
  2212. // hide unwanted cookies from site
  2213. _cookie.get = new Proxy(_cookie.get, {
  2214. apply(fun, that, args) {
  2215. asyncCookieCleaner();
  2216. let res = _apply(fun, that, args);
  2217. if (blacklist.test(res)) {
  2218. let stack = [];
  2219. for (let cookie of res.split(/;\s?/))
  2220. if (!blacklist.test(getName(cookie)))
  2221. stack.push(cookie);
  2222. else if (useOldPass) removeCookie(cookie, that);
  2223. res = stack.join('; ');
  2224. }
  2225. return res;
  2226. }
  2227. });
  2228. Object.defineProperty(_doc_proto, 'cookie', _cookie);
  2229. _console.log('Active cookies:', root.document.cookie);
  2230. }
  2231. }
  2232.  
  2233. // Locates a node with specific text in Russian
  2234. // Uses table of substitutions for similar letters
  2235. let selectNodeByTextContent = (() => {
  2236. let subs = {
  2237. // english & greek
  2238. 'А': 'AΑ',
  2239. 'В': 'BΒ',
  2240. 'Г': 'Γ',
  2241. 'Е': 'EΕ',
  2242. 'З': '3',
  2243. 'К': 'KΚ',
  2244. 'М': 'MΜ',
  2245. 'Н': 'HΗ',
  2246. 'О': 'OΟ',
  2247. 'П': 'Π',
  2248. 'Р': 'PΡ',
  2249. 'С': 'C',
  2250. 'Т': 'T',
  2251. 'Ф': 'Φ',
  2252. 'Х': 'XΧ'
  2253. };
  2254. let regExpBuilder = text => new RegExp(
  2255. text.toUpperCase()
  2256. .split('')
  2257. .map(function (e) {
  2258. return `${e in subs ? `[${e}${subs[e]}]` : (e === ' ' ? '\\s+' : e)}[\u200b\u200c\u200d]*`;
  2259. })
  2260. .join(''),
  2261. 'i');
  2262. let reMap = {};
  2263. return (re, opts = {
  2264. root: _document.body
  2265. }) => {
  2266. if (!re.test) {
  2267. if (!reMap[re])
  2268. reMap[re] = regExpBuilder(re);
  2269. re = reMap[re];
  2270. }
  2271.  
  2272. for (let child of opts.root.children)
  2273. if (re.test(child.textContent)) {
  2274. if (opts.shallow)
  2275. return child;
  2276. opts.root = child;
  2277. return selectNodeByTextContent(re, opts) || child;
  2278. }
  2279. };
  2280. })();
  2281.  
  2282. // webpackJsonp filter
  2283. function webpackJsonpFilter(blacklist, log = false) {
  2284. function wrapPush(webpack) {
  2285. let _push = webpack.push.bind(webpack);
  2286. Object.defineProperty(webpack, 'push', {
  2287. get() {
  2288. return _push;
  2289. },
  2290. set(vl) {
  2291. _push = new Proxy(vl, {
  2292. apply(fun, that, args) {
  2293. wrapper: {
  2294. if (!(args[0] instanceof Array))
  2295. break wrapper;
  2296. let mainName;
  2297. if (args[0][2] instanceof Array && args[0][2][0] instanceof Array)
  2298. mainName = args[0][2][0][0];
  2299. let funs = args[0][1];
  2300. if (!(funs instanceof Object && !(funs instanceof Array)))
  2301. break wrapper;
  2302. const noopFunc = (name, text) => () => _console.log(`Skip webpack ${name}`, text);
  2303. for (let name in funs) {
  2304. if (typeof funs[name] !== 'function')
  2305. continue;
  2306. if (blacklist.test(_toString(funs[name])) && name !== mainName)
  2307. funs[name] = noopFunc(name, log ? _toString(funs[name]) : '');
  2308. }
  2309. }
  2310. _console.log('webpack.push()');
  2311. return _apply(fun, that, args);
  2312. }
  2313. });
  2314. return true;
  2315. }
  2316. });
  2317. return webpack;
  2318. }
  2319. let _webpackJsonp = wrapPush([]);
  2320. Object.defineProperty(win, 'webpackJsonp', {
  2321. get() {
  2322. return _webpackJsonp;
  2323. },
  2324. set(vl) {
  2325. if (vl === _webpackJsonp)
  2326. return;
  2327. _console.log('new webpackJsonp', vl);
  2328. _webpackJsonp = wrapPush(vl);
  2329. }
  2330. });
  2331. }
  2332.  
  2333. // JSON filter
  2334. // removeList - list of paths divided by space to remove
  2335. // checkList - optional list of paths divided by space to check presence of before removal
  2336. const jsonFilter = (function jsonFilterModule() {
  2337. const _log = (() => {
  2338. if (!jsf.AccessStatistics)
  2339. return () => null;
  2340. const counter = {};
  2341. const counterToString = () => Object.entries(counter).map(a => `\n * ${a.join(': ')}`).join('');
  2342. let lock = 0;
  2343. return async function _log(path) {
  2344. counter[path] = (counter[path] || 0) + 1;
  2345. lock++;
  2346. setTimeout(() => {
  2347. lock--;
  2348. if (lock === 0)
  2349. _console.log('JSON filters:', counterToString());
  2350. }, 3333);
  2351. };
  2352. })();
  2353.  
  2354. const isObjecty = o => (typeof o === 'object' || typeof o === 'function') && o !== null;
  2355.  
  2356. function parsePath(root, path) {
  2357. let pos;
  2358. pos = path.indexOf('.');
  2359. for (let name; pos > 0;) {
  2360. name = path.slice(0, pos);
  2361. if (!isObjecty(root[name]))
  2362. break;
  2363. root = root[name];
  2364. path = path.slice(pos + 1);
  2365. pos = path.indexOf('.');
  2366. }
  2367. return [pos < 0 && _hasOwnProperty(root, path), root, path];
  2368. }
  2369.  
  2370. const filterList = [];
  2371.  
  2372. function filter(result) {
  2373. if (!isObjecty(result))
  2374. return result;
  2375.  
  2376. const pathNotInObject = path => !(parsePath(result, path)[0]);
  2377. const removePathInObject = path => {
  2378. let [exist, root, name] = parsePath(result, path);
  2379. if (exist) {
  2380. delete root[name];
  2381. _log(path);
  2382. }
  2383. };
  2384. for (let list of filterList) {
  2385. if (list.check && list.check.some(pathNotInObject))
  2386. return result;
  2387. list.remove.forEach(removePathInObject);
  2388. }
  2389.  
  2390. return result;
  2391. }
  2392.  
  2393.  
  2394. let wrapped = false;
  2395.  
  2396. function jsonFilter(removeList, checkList) {
  2397. filterList.push({
  2398. remove: removeList.split(/\s/),
  2399. check: checkList ? checkList.split(/\s/) : undefined
  2400. });
  2401.  
  2402. if (wrapped) return;
  2403. wrapped = true;
  2404.  
  2405. win.JSON.parse = new Proxy(win.JSON.parse, {
  2406. apply(fun, that, args) {
  2407. return filter(_apply(fun, that, args));
  2408. }
  2409. });
  2410.  
  2411. win.Response.prototype.json = new Proxy(win.Response.prototype.json, {
  2412. apply(fun, that, args) {
  2413. let promise = _apply(fun, that, args);
  2414. promise.then(res => filter(res));
  2415. return promise;
  2416. }
  2417. });
  2418. }
  2419. jsonFilter.toString = () => `const jsonFilter = (${jsonFilterModule.toString()})()`;
  2420. return jsonFilter;
  2421. })();
  2422.  
  2423. function zmcPlug(conf) {
  2424. // enable Emcode debug mode in ZMCTrack code (just to see it in the log)
  2425. const _RegExpToString = _bindCall(RegExp.prototype.toString);
  2426. String.prototype.match = new Proxy(String.prototype.match, {
  2427. apply(fun, that, args) {
  2428. let str = typeof args[0] === 'string' ? args[0] : _RegExpToString(args[0]);
  2429. if (str.includes('argon_debug'))
  2430. return true;
  2431. return _apply(fun, that, args);
  2432. }
  2433. });
  2434. // catch and overwrite API in the clean IFrame created by ZMCTrack
  2435. _Node.appendChild = new Proxy(_Node.appendChild, {
  2436. apply(fun, that, args) {
  2437. const res = _apply(fun, that, args);
  2438. if (res && res.name && res.name.startsWith('_m')) {
  2439. const zmcWin = win[res.name];
  2440. if (!zmcWin) return;
  2441. zmcWin.write = nt.func(null, 'zmc.write', true);
  2442. zmcWin.setTimeout = nt.func(null, 'zmc.setTimeout', true);
  2443. zmcWin.document.addEventListener = nt.func(null, 'zmc.document.addEventListener', true);
  2444. zmcWin.XMLHttpRequest.prototype.open = nt.func(null, 'zmc.XMLHttpRequest.prototype.open', true);
  2445. zmcWin.XMLHttpRequest.prototype.send = nt.func(null, 'zmc.XMLHttpRequest.prototype.send', true);
  2446. }
  2447. return res;
  2448. }
  2449. });
  2450.  
  2451. const define = name => {
  2452. let _win;
  2453. Object.defineProperty(win, name, {
  2454. get() {
  2455. if (!_win) {
  2456. let frame = _document.querySelector(`iframe[name="${name}"`);
  2457. if (frame)
  2458. _win = frame.contentWindow;
  2459. }
  2460. return _win;
  2461. }
  2462. });
  2463. };
  2464. // "predict" names of zmctrack frames on certain domains which use date-based frame names
  2465. // id - some fixed number, zone - server's timezone (hours), step - how often name changes (minutes)
  2466. // range - period in hours to cover from -range/2 to +range/2, offset - fixed number of minutes to add
  2467. if (typeof conf === 'object') {
  2468. let {
  2469. id,
  2470. zone = 2,
  2471. step = 5,
  2472. range = 3,
  2473. offset = 0
  2474. } = conf;
  2475. const pad = n => n.toString().padStart(2, '0');
  2476. const m2ms = x => x * 60 * 1000;
  2477. const d = new Date();
  2478. d.setTime(Math.floor(d.getTime() / m2ms(step)) * m2ms(step) + m2ms(zone * 60) + m2ms(offset));
  2479. const defineByDate = d => {
  2480. define(`n${pad(
  2481. d.getUTCMonth() + 1
  2482. )}${pad(
  2483. d.getUTCDate()
  2484. )}${pad(
  2485. d.getUTCHours()
  2486. )}${pad(
  2487. d.getUTCMinutes()
  2488. )}${(
  2489. id ? `_${id}` : ''
  2490. )}`);
  2491. };
  2492. const time = d.getTime();
  2493. for (let n = -Math.floor(range * 30 / step); n <= Math.floor(range * 30 / step); n += 1) {
  2494. d.setTime(time + n * m2ms(step));
  2495. defineByDate(d);
  2496. }
  2497. }
  2498. if (typeof conf === 'string')
  2499. define(conf);
  2500. }
  2501.  
  2502. function documentRewrite(pattern, substitute) {
  2503. /* jshint -W060 */ // document.write is a form of evil, a necessary evil in this case
  2504. const inject = (pattern, substitute) => {
  2505. let xhr = new XMLHttpRequest();
  2506. xhr.open('GET', location.href);
  2507. xhr.onload = () => {
  2508. document.close();
  2509. //console.log(xhr.responseText.match(pattern));
  2510. document.write(xhr.responseText.replace(pattern, substitute));
  2511. document.close();
  2512. };
  2513. xhr.send();
  2514. };
  2515. /* jshint +W060 */
  2516. const style = [
  2517. '@keyframes spinner { 0% { transform: translate3d(-50%, -50%, 0) rotate(0deg); } 100% { transform: translate3d(-50%, -50%, 0) rotate(360deg); } }',
  2518. '.spinner::before { animation: 1.5s linear infinite spinner; animation-play-state: running;',
  2519. 'content: ""; border: solid 3px #dedede; border-bottom-color: #EF6565; border-radius: 50%;',
  2520. 'height: 10vh; width: 10vh; left: 50%; top: 50%; position: absolute; transform: translate3d(-50%, -50%, 0); };'
  2521. ].join('');
  2522. _document.write(`<html><head><script>(${inject.toString()})(${pattern.toString()},'${substitute}')</script>`);
  2523. _document.write(`<style>${style}</style></head><body><div class="spinner"></div></body></html>`);
  2524. }
  2525.  
  2526. // === Scripts for specific domains ===
  2527.  
  2528. const scripts = {
  2529. // Prevent Popups
  2530. preventPopups: {
  2531. other: 'biqle.ru, chaturbate.com, dfiles.ru, eporner.eu, hentaiz.org, mirrorcreator.com, online-multy.ru' +
  2532. 'radikal.ru, rumedia.ws, tapehub.tech, thepiratebay.org, unionpeer.com, zippyshare.com',
  2533. now: preventPopups
  2534. },
  2535. // Prevent Popunders (background redirect)
  2536. preventPopunders: {
  2537. other: 'lostfilm-online.ru, mediafire.com, megapeer.org, megapeer.ru, perfectgirls.net',
  2538. now: preventPopunders
  2539. },
  2540. // zmctrack remover
  2541. zmcDocumentRewrite: {
  2542. other: 'www.ukr.net', // generic script removal pattern
  2543. now: () => documentRewrite(/<iframe\sname="n\d+(_\d+)?"\sstyle="display:none"><\/iframe><script(\s+[^>]+)?>.*?<\/script>/, '<!-- removed -->')
  2544. },
  2545. zmcPlug: {
  2546. other: [
  2547. '4mama.ua,beauty.ua,eknigi.org,forumodua.com,internetua.com,mama.ua,newsyou.info,okino.ua,orakul.com',
  2548. 'sinoptik.ua,toneto.net,tvgid.ua,tvoymalysh.com.ua,udoktora.net'
  2549. ].join(','),
  2550. now: () => {
  2551. if (GM.info.scriptHandler === 'Violentmonkey')
  2552. documentRewrite(/ /, ' ');
  2553. zmcPlug();
  2554. }
  2555. },
  2556. zmcPlugTime: {
  2557. other: [ // using time-based iframe names
  2558. 'avtovod.com.ua,besplatka.ua,bigmir.net,gismeteo.tld,hvylya.net,inforesist.org,isport.ua',
  2559. 'kolobok.ua,kriminal.tv,mport.ua,nnovosti.info,smak.ua,tochka.net,tv.ua,viva.ua'
  2560. ].join(','),
  2561. now: () => {
  2562. let is = name => location.hostname === name || location.hostname.includes(name);
  2563. if ([
  2564. ['avtovod.com', 'id', 12624],
  2565. ['besplatka.ua', 'step', 1, 'range', 5],
  2566. ['gismeteo', 'id', 11622, 'zone', 0],
  2567. ['hvylya.net', 'step', 1, 'range', 5],
  2568. ['inforesist.org', 'step', 30, 'range', 64],
  2569. ['kriminal.tv', 'id', 12213],
  2570. ['nnovosti.info', 'id', 12252],
  2571. ['tochka.net', 'step', 1, 'range', 2.2],
  2572. ['viva.ua', 'id', 11702]
  2573. ].some(e => is(e[0]) && !zmcPlug( // object from flat key/value array
  2574. e.reduceRight((o, x, i) => (o[i % 2 ? x : 'x'] = i % 2 ? o.x : x, o), {})
  2575. ))) return;
  2576. zmcPlug({});
  2577. }
  2578. },
  2579. // using fixed iframe names
  2580. 'businessua.com': () => zmcPlug('n01132136'),
  2581. 'enovosty.com': () => zmcPlug('n01212138'),
  2582. 'epravda.com.ua': () => zmcPlug('n09221342'),
  2583. 'eurointegration.com.ua': () => zmcPlug('n09221342'),
  2584. 'football24.ua': () => zmcPlug('n04211212'),
  2585. 'glianec.com': () => zmcPlug('n12100938'),
  2586. 'kp.ua': () => zmcPlug('n07310013'),
  2587. 'meteo.ua': () => zmcPlug('n11191753'),
  2588. 'nv.ua': () => zmcPlug('n10300948'),
  2589. 'ostro.org': () => zmcPlug('n10101319'),
  2590. 'pravda.com.ua': () => {
  2591. zmcPlug('n09221555');
  2592. nt.define('AdnetLoadScript');
  2593. },
  2594. 'real-vin.com': () => zmcPlug('n09201149'),
  2595. 'stravy.net': () => zmcPlug('n01132136'),
  2596. 'zdorovia.com.ua': () => zmcPlug('n01132136'),
  2597. // custom zmc-related fixes
  2598. 'kzblow.info': () => documentRewrite(/<script>\(function\(\w\w,.*?['"]n\d+['"]\);<\/script>/, '<!-- removed -->'),
  2599. // disables ads when specific cookies are set
  2600. 'liga.net': () => (_document.cookie = 'isShowAd=false; domain=.liga.net', _document.cookie = 'is_login=true; domain=.liga.net'),
  2601. // disables ads if screen width is below 1200
  2602. 'segodnya.ua': () => {
  2603. nt.define('document.documentElement', new Proxy(_document.documentElement, {
  2604. get(that, prop) {
  2605. if (prop === 'clientWidth' && that[prop] > 1199)
  2606. return 1199;
  2607. return that[prop];
  2608. }
  2609. }));
  2610. },
  2611.  
  2612. // PopMix (both types of popups encountered on site)
  2613. 'openload.co': {
  2614. other: 'oload.tv, oload.info, openload.co.com',
  2615. now() {
  2616. if (inIFrame) {
  2617. nt.define('BetterJsPop', {
  2618. add(a, b) {
  2619. _console.trace('BetterJsPop.add(%o, %o)', a, b);
  2620. },
  2621. config(o) {
  2622. _console.trace('BetterJsPop.config(%o)', o);
  2623. },
  2624. Browser: {
  2625. isChrome: true
  2626. }
  2627. });
  2628. nt.define('isSandboxed', nt.func(null, 'isSandboxed'));
  2629. nt.define('adblock', false);
  2630. nt.define('adblock2', false);
  2631. } else preventPopMix();
  2632. }
  2633. },
  2634.  
  2635. 'turbobit.net': preventPopMix,
  2636.  
  2637. 'tapochek.net': () => {
  2638. // workaround for moradu.com/apu.php load error handler script, not sure which ad network is this
  2639. let _appendChild = Object.getOwnPropertyDescriptor(_Node, 'appendChild');
  2640. let _appendChild_value = _appendChild.value;
  2641. _appendChild.value = function appendChild(node) {
  2642. if (this === _document.body)
  2643. if ((node instanceof HTMLScriptElement || node instanceof HTMLStyleElement) &&
  2644. /^https?:\/\/[0-9a-f]{15}\.com\/\d+(\/|\.css)$/.test(node.src) ||
  2645. node instanceof HTMLDivElement && node.style.zIndex > 900000 &&
  2646. node.style.backgroundImage.includes('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'))
  2647. throw '...eenope!';
  2648. return _appendChild_value.apply(this, arguments);
  2649. };
  2650. Object.defineProperty(_Node, 'appendChild', _appendChild);
  2651.  
  2652. // disable window focus tricks and changing location
  2653. let focusHandlerName = /\WfocusAchieved\(/;
  2654. let _setInterval = win.setInterval;
  2655. win.setInterval = (...args) => {
  2656. if (args.length && focusHandlerName.test(_toString(args[0]))) {
  2657. _console.log('skip setInterval for', ...args);
  2658. return -1;
  2659. }
  2660. return _setInterval(...args);
  2661. };
  2662. let _addEventListener = win.addEventListener;
  2663. win.addEventListener = function (...args) {
  2664. if (args.length && args[0] === 'focus' && focusHandlerName.test(_toString(args[1]))) {
  2665. _console.log('skip addEventListener for', ...args);
  2666. return undefined;
  2667. }
  2668. return _addEventListener.apply(this, args);
  2669. };
  2670.  
  2671. // generic popup prevention
  2672. preventPopups();
  2673. },
  2674.  
  2675. // = other ======================================================================================
  2676.  
  2677. '1plus1.video': () => {
  2678. let noopDefine = ['abMessage', 'BLOCK'];
  2679. win.Object.defineProperty = new Proxy(win.Object.defineProperty, {
  2680. apply(fun, that, args) {
  2681. let [, prop, desc] = args;
  2682. if (noopDefine.includes(prop)) {
  2683. delete desc.get;
  2684. delete desc.set;
  2685. desc.value = () => {};
  2686. }
  2687. return _apply(fun, that, args);
  2688. }
  2689. });
  2690. /* jshint -W001 */ // aka 'hasOwnProperty' is a really bad name, but this is a wrapper
  2691. win.Object.prototype.hasOwnProperty = new Proxy(win.Object.prototype.hasOwnProperty, {
  2692. apply(fun, that, args) {
  2693. if (args[0] === 'dl')
  2694. return false; // when true checks is partner and changes value of 'wa'
  2695. return _apply(fun, that, args);
  2696. }
  2697. });
  2698. },
  2699.  
  2700. '1tv.ru': {
  2701. other: 'mediavitrina.ru',
  2702. now: () => scriptLander(() => {
  2703. nt.define('EUMPAntiblockConfig', nt.proxy({
  2704. url: '//www.1tv.ru/favicon.ico'
  2705. }));
  2706. nt.define('Object.prototype.disableSeek', nt.func(undefined, 'disableSeek'));
  2707. //nt.define('preroll', undefined);
  2708.  
  2709. let _EUMP;
  2710. const _EUMP_set = x => {
  2711. if (x === _EUMP)
  2712. return true;
  2713. let _plugins = x.plugins;
  2714. Object.defineProperty(x, 'plugins', {
  2715. enumerable: true,
  2716. get() {
  2717. return _plugins;
  2718. },
  2719. set(vl) {
  2720. if (vl === _plugins)
  2721. return true;
  2722. nt.defineOn(vl, 'antiblock', function (player, opts) {
  2723. const antiblock = nt.proxy({
  2724. opts: opts,
  2725. readyState: 'ready',
  2726. isEUMPPlugin: true,
  2727. detected: nt.func(false, 'antiblock.detected'),
  2728. currentWeight: nt.func(0, 'antiblock.currentWeight')
  2729. });
  2730. player.antiblock = antiblock;
  2731. return antiblock;
  2732. }, 'EUMP.plugins.');
  2733. _plugins = vl;
  2734. }
  2735. });
  2736. _EUMP = x;
  2737. return true;
  2738. };
  2739. if ('EUMP' in win)
  2740. _EUMP_set(win.EUMP);
  2741. Object.defineProperty(win, 'EUMP', {
  2742. enumerable: true,
  2743. get() {
  2744. return _EUMP;
  2745. },
  2746. set: _EUMP_set
  2747. });
  2748.  
  2749. let _EUMPVGTRK;
  2750. const _EUMPVGTRK_set = x => {
  2751. if (x === _EUMPVGTRK)
  2752. return true;
  2753. if (x && x.prototype) {
  2754. if ('generatePrerollUrls' in x.prototype)
  2755. nt.defineOn(x.prototype, 'generatePrerollUrls', nt.func(null, 'EUMPVGTRK.generatePrerollUrls'), 'EUMPVGTRK.prototype.', {
  2756. enumerable: false
  2757. });
  2758. if ('sendAdsEvent' in x.prototype)
  2759. nt.defineOn(x.prototype, 'sendAdsEvent', nt.func(null, 'EUMPVGTRK.sendAdsEvent'), 'EUMPVGTRK.prototype.', {
  2760. enumerable: false
  2761. });
  2762. }
  2763. _EUMPVGTRK = x;
  2764. return true;
  2765. };
  2766. if ('EUMPVGTRK' in win)
  2767. _EUMPVGTRK_set(win.EUMPVGTRK);
  2768. Object.defineProperty(win, 'EUMPVGTRK', {
  2769. enumerable: true,
  2770. get() {
  2771. return _EUMPVGTRK;
  2772. },
  2773. set: _EUMPVGTRK_set
  2774. });
  2775. }, nullTools)
  2776. },
  2777.  
  2778. '24smi.org': () => {
  2779. selectiveCookies('isab');
  2780. abortExecution.onGet('Object.prototype.getYa');
  2781. abortExecution.onAll('Object.prototype.YaBaseController');
  2782. },
  2783.  
  2784. '2picsun.ru': {
  2785. other: 'pics2sun.ru, 3pics-img.ru',
  2786. now() {
  2787. Object.defineProperty(navigator, 'userAgent', {
  2788. value: 'googlebot'
  2789. });
  2790. }
  2791. },
  2792.  
  2793. '4pda.to': {
  2794. now() {
  2795. // https://greasyfork.org/en/scripts/14470-4pda-unbrender
  2796. const _setAttribute = _bindCall(_Element.setAttribute);
  2797. const _removeChild = _bindCall(_Element.removeChild);
  2798. const isForum = location.pathname.startsWith('/forum/');
  2799. const remove = node => (node && _removeChild(node.parentNode, node));
  2800. const hide = node => node && _setAttribute(node, 'style', 'display:none!important');
  2801.  
  2802. selectiveCookies('viewpref');
  2803. abortExecution.inlineScript('document.querySelector', {
  2804. pattern: /\(document(,window)?\);/
  2805. });
  2806.  
  2807. const log = false;
  2808.  
  2809. function cleaner() {
  2810. HeaderAds: {
  2811. // hide ads above HEADER
  2812. let nav = _querySelector('.menu-main-item');
  2813. while (nav && (nav.parentNode !== _de)) {
  2814. if (!nav.parentNode.querySelector('article, .container[itemtype$="Article"]') && nav.parentNode.clientHeight < 500)
  2815. nav = nav.parentNode;
  2816. else break;
  2817. }
  2818. if (!nav || (nav.parentNode === _de)) {
  2819. if (log) _console.warn('Unable to locate header element');
  2820. break HeaderAds;
  2821. }
  2822. if (log) _console.log('Processing header:', nav);
  2823. for (let itm of nav.parentNode.children)
  2824. if (itm !== nav)
  2825. hide(itm);
  2826. else break;
  2827. }
  2828.  
  2829. FixNavMenu: {
  2830. // hide ad link from the navigation
  2831. let ad = _querySelector('.menu-main-item > a > svg');
  2832. if (ad) {
  2833. ad = ad.parentNode.parentNode;
  2834. hide(ad);
  2835. if (log) _console.log('Hid menu ad item:', ad);
  2836. break FixNavMenu;
  2837. }
  2838. if (log) _console.warn('Unable to locate menu ad item');
  2839. }
  2840.  
  2841. SidebarAds: {
  2842. // remove ads from sidebar
  2843. let aside = _querySelectorAll('[class]:not([id]) > [id]:not([class]) > :first-child + :last-child:not(.v-panel)');
  2844. if (!aside.length) {
  2845. if (log) _console.warn('Unable to locate sidebar');
  2846. break SidebarAds;
  2847. }
  2848. for (let side of aside) {
  2849. if (log) _console.log('Processing potential sidebar:', side);
  2850. for (let itm of Array.from(side.children)) {
  2851. if (itm.classList.contains('post'))
  2852. continue;
  2853. if (itm.querySelector('iframe') || !itm.children.length)
  2854. remove(itm);
  2855. let script = itm.querySelector('script');
  2856. if (itm.querySelector('a[target="_blank"] > img') ||
  2857. script && script.src === '' && (script.type === 'text/javascript' || !script.type) &&
  2858. script.textContent.includes('document')) {
  2859. if (log) _console.log('Hid:', itm);
  2860. hide(itm);
  2861. }
  2862. }
  2863. }
  2864. }
  2865. }
  2866.  
  2867. const cln = setInterval(cleaner, 100);
  2868.  
  2869. // hide banner next to logo and header banner in profiles
  2870. if (isForum)
  2871. createStyle([
  2872. 'div[class]:not([id]) tr[valign="top"] > td:last-child { display: none !important }',
  2873. 'html[style] > body [class]:not([id]):not(div):not([style]) > div:empty + [data-revive-zoneid] { display: none !important }'
  2874. ]);
  2875. // clean page
  2876. window.addEventListener(
  2877. 'DOMContentLoaded',
  2878. function () {
  2879. clearInterval(cln);
  2880. const width = () => win.innerWidth || _de.clientWidth || _document.body.clientWidth || 0,
  2881. height = () => win.innerHeight || _de.clientHeight || _document.body.clientHeight || 0;
  2882.  
  2883. if (isForum) {
  2884. // hide banner next to logo
  2885. //let itm = _document.querySelector('#logostrip');
  2886. //if (itm) hide(itm.parentNode.nextSibling);
  2887. // clear background in the download frame
  2888. if (location.pathname.startsWith('/forum/dl/')) {
  2889. let setBackground = node => _setAttribute(
  2890. node,
  2891. 'style', (_getAttribute(node, 'style') || '') +
  2892. ';background-color:#4ebaf6!important'
  2893. );
  2894. setBackground(_document.body);
  2895. for (let itm of _document.querySelectorAll('body > div'))
  2896. if (!itm.querySelector('.dw-fdwlink, .content') && !itm.classList.contains('footer'))
  2897. remove(itm);
  2898. else
  2899. setBackground(itm);
  2900. }
  2901. // exist from DOMContentLoaded since the rest is not for forum
  2902. return;
  2903. }
  2904.  
  2905. cleaner();
  2906.  
  2907. _document.body.setAttribute('style', (_document.body.getAttribute('style') || '') + ';background-color:#E6E7E9!important');
  2908.  
  2909. let extra = 'background-image:none!important;background-color:transparent!important',
  2910. fakeStyles = new WeakMap(),
  2911. styleProxy = {
  2912. get(target, prop) {
  2913. return fakeStyles.get(target)[prop] || target[prop];
  2914. },
  2915. set(target, prop, value) {
  2916. let fakeStyle = fakeStyles.get(target);
  2917. ((prop in fakeStyle) ? fakeStyle : target)[prop] = value;
  2918. return true;
  2919. }
  2920. };
  2921. for (let itm of _document.querySelectorAll('[id]:not(A), A')) {
  2922. if (!(itm.offsetWidth > 0.95 * width() &&
  2923. itm.offsetHeight > 0.85 * height()))
  2924. continue;
  2925. if (itm.tagName !== 'A') {
  2926. fakeStyles.set(itm.style, {
  2927. 'backgroundImage': itm.style.backgroundImage,
  2928. 'backgroundColor': itm.style.backgroundColor
  2929. });
  2930.  
  2931. try {
  2932. Object.defineProperty(itm, 'style', {
  2933. value: new Proxy(itm.style, styleProxy),
  2934. enumerable: true
  2935. });
  2936. } catch (e) {
  2937. _console.log('Unable to protect style property.', e);
  2938. }
  2939.  
  2940. _setAttribute(itm, 'style', `${(_getAttribute(itm, 'style') || '')};${extra}`);
  2941. }
  2942. if (itm.tagName === 'A')
  2943. _setAttribute(itm, 'style', 'display:none!important');
  2944. }
  2945. }
  2946. );
  2947. }
  2948. },
  2949.  
  2950. 'adhands.ru': () => scriptLander(() => {
  2951. try {
  2952. let _adv;
  2953. Object.defineProperty(win, 'adv', {
  2954. get() {
  2955. return _adv;
  2956. },
  2957. set(val) {
  2958. _console.log('Blocked advert on adhands.ru.');
  2959. nt.defineOn(val, 'advert', '', 'adv.');
  2960. _adv = val;
  2961. }
  2962. });
  2963. } catch (ignore) {
  2964. if (!win.adv)
  2965. _console.log('Unable to locate advert on adhands.ru.');
  2966. else {
  2967. _console.log('Blocked advert on adhands.ru.');
  2968. nt.define('adv.advert', '');
  2969. }
  2970. }
  2971. }, nullTools),
  2972.  
  2973. 'all-episodes.org': () => {
  2974. nt.define('perROS', 0); // blocks access when = 1
  2975. nt.define('idm', -1); // blocks quality when >= 0
  2976. nt.define('advtss', nt.proxy({
  2977. offsetHeight: 200,
  2978. offsetWidth: 200
  2979. }, 'advtss'));
  2980. // wrap player to prevent some events and interactions
  2981. let _playerInstance = win.playerInstance;
  2982. Object.defineProperty(win, 'playerInstance', {
  2983. get() {
  2984. return _playerInstance;
  2985. },
  2986. set(vl) {
  2987. _console.log('player =', vl, vl.on, vl.getAdBlock);
  2988. vl.on = new Proxy(vl.on, {
  2989. apply(fun, that, args) {
  2990. if (/^(ad[A-Z]|before(Play|Complete))/.test(args[0]))
  2991. return;
  2992. //_console.log('on', ...args);
  2993. return _apply(fun, that, args);
  2994. }
  2995. });
  2996. nt.defineOn(vl, 'getAdBlock', nt.func(false, 'playerInstance.getAdBlock'), 'playerInstance.getAdBlock');
  2997. _playerInstance = vl;
  2998. }
  2999. });
  3000. },
  3001.  
  3002. 'allhentai.ru': () => {
  3003. preventPopups();
  3004. scriptLander(() => {
  3005. selectiveEval();
  3006. let _onerror = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'onerror');
  3007. if (!_onerror)
  3008. return;
  3009. _onerror.set = (...args) => _console.log(args[0].toString());
  3010. Object.defineProperty(HTMLElement.prototype, 'onerror', _onerror);
  3011. }, selectiveEval);
  3012. },
  3013.  
  3014. 'allmovie.pro': {
  3015. other: 'rufilmtv.org',
  3016. dom() {
  3017. // pretend to be Android to make site use different played for ads
  3018. if (isSafari)
  3019. return;
  3020. Object.defineProperty(navigator, 'userAgent', {
  3021. get() {
  3022. return 'Mozilla/5.0 (Linux; Android 4.1.1; Nexus 7 Build/JRO03D) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/18.0.1025.166 Safari/535.19';
  3023. },
  3024. enumerable: true
  3025. });
  3026. }
  3027. },
  3028.  
  3029. 'anidub.com': {
  3030. other: 'anidub.life, myanime.online, loveanime.live',
  3031. now() {
  3032. let _AnidubAd = win.AnidubAd;
  3033. Object.defineProperty(win, 'AnidubAd', {
  3034. get() {
  3035. return _AnidubAd;
  3036. },
  3037. set(x) {
  3038. _console.log(x.usePlyr);
  3039. if (x && 'usePlyr' in x)
  3040. x.usePlyr = new Proxy(x.usePlyr, {
  3041. apply(fun, that, args) {
  3042. if (args[1] && 'sources' in args[1])
  3043. args[1].sources = [];
  3044. return new Proxy(_apply(fun, that, args), {
  3045. get(that, prop) {
  3046. if (prop === 'isDone')
  3047. return true;
  3048. return that[prop];
  3049. }
  3050. });
  3051. }
  3052. });
  3053. _AnidubAd = new Proxy(x, {
  3054. construct(that, args) {
  3055. return new Proxy(_construct(that, args), {
  3056. get(that, prop) {
  3057. if (prop === 'done')
  3058. return true;
  3059. if (prop === 'on')
  3060. return () => {};
  3061. return that[prop];
  3062. }
  3063. });
  3064. }
  3065. });
  3066. return true;
  3067. }
  3068. });
  3069. const onmessage = Object.getOwnPropertyDescriptor(win, 'onmessage');
  3070. onmessage.set = new Proxy(onmessage.set, {
  3071. apply(fun, that, args) {
  3072. if (typeof args[0] === 'function')
  3073. args[0] = new Proxy(args[0], {
  3074. apply(fun, that, args) {
  3075. let [event] = args;
  3076. if (event.origin.includes('googleapis'))
  3077. return;
  3078. if (event.data && !event.data.indexOf)
  3079. event.data.indexOf = () => -1;
  3080. return _apply(fun, that, args);
  3081. }
  3082. });
  3083. return _apply(fun, that, args);
  3084. }
  3085. });
  3086. Object.defineProperty(win, 'onmessage', onmessage);
  3087. }
  3088. },
  3089.  
  3090. 'ati.su': () => scriptLander(() => {
  3091. nt.define('Object.prototype.advManager', nt.proxy({}, 'advManager'));
  3092. abortExecution.onGet('Object.prototype.bannersResolved');
  3093. }),
  3094.  
  3095. 'audioportal.su': {
  3096. now() {
  3097. createStyle('#blink2 { display: none !important }');
  3098. },
  3099. dom() {
  3100. let links = _document.querySelectorAll('a[onclick*="clickme("]');
  3101. if (!links) return;
  3102. for (let link of links)
  3103. win.clickme(link);
  3104. }
  3105. },
  3106.  
  3107. 'auto.ru': () => {
  3108. let words = /Реклама|Яндекс.Директ|yandex_ad_/;
  3109. let userAdsListAds = (
  3110. '.listing-list > .listing-item,' +
  3111. '.listing-item_type_fixed.listing-item'
  3112. );
  3113. let catalogAds = (
  3114. 'div[class*="layout_catalog-inline"],' +
  3115. 'div[class$="layout_horizontal"]'
  3116. );
  3117. let otherAds = (
  3118. '.advt_auto,' +
  3119. '.sidebar-block,' +
  3120. '.pager-listing + div[class],' +
  3121. '.card > div[class][style],' +
  3122. '.sidebar > div[class],' +
  3123. '.main-page__section + div[class],' +
  3124. '.listing > tbody'
  3125. );
  3126. gardener(userAdsListAds, words, {
  3127. root: '.listing-wrap',
  3128. observe: true
  3129. });
  3130. gardener(catalogAds, words, {
  3131. root: '.catalog__page,.content__wrapper',
  3132. observe: true
  3133. });
  3134. gardener(otherAds, words);
  3135. nt.define('Object.prototype.yaads', undefined);
  3136. nt.define('Object.prototype.initYaDirect', undefined);
  3137. nt.define('Object.prototype.direct', nt.proxy({}, 'Yandex.direct'));
  3138. },
  3139.  
  3140. 'avito.ru': () => scriptLander(() => selectiveCookies('abp|cmtchd|crookie|is_adblock'), selectiveCookies),
  3141.  
  3142. 'di.fm': () => scriptLander(() => {
  3143. let log = false;
  3144. // wrap global app object to catch registration of specific modules
  3145. let _di = win.di;
  3146. Object.defineProperty(win, 'di', {
  3147. get() {
  3148. return _di;
  3149. },
  3150. set(vl) {
  3151. if (vl === _di)
  3152. return;
  3153. if (log) _console.trace('di =', vl);
  3154. _di = new Proxy(vl, {
  3155. set(di, name, vl) {
  3156. if (vl === di[name])
  3157. return true;
  3158. if (name === 'app') {
  3159. if (log) _console.trace(`di.${name} =`, vl);
  3160. if (!('module' in vl))
  3161. return;
  3162. vl.module = new Proxy(vl.module, {
  3163. apply(module, that, args) {
  3164. if (/Wall|Banner|Detect|WebplayerApp\.Ads/.test(args[0])) {
  3165. let name = args[0];
  3166. if (log) _console.log('wrap', name, 'module');
  3167. if (typeof args[1] === 'function')
  3168. args[1] = new Proxy(args[1], {
  3169. apply(fun, that, args) {
  3170. if (args[0]) // module object
  3171. args[0].start = () => _console.log('Skipped start of', name);
  3172. return Reflect.apply(fun, that, args);
  3173. }
  3174. });
  3175. } // else log && _console.log('loading module', args[0]);
  3176. if (args[0] === 'Modals' && typeof args[1] === 'function') {
  3177. if (log) _console.log('wrap', name, 'module');
  3178. args[1] = new Proxy(args[1], {
  3179. apply(fun, that, args) {
  3180. if ('commands' in args[1] && 'setHandlers' in args[1].commands &&
  3181. !Object.hasOwnProperty.call(args[1].commands, 'setHandlers')) {
  3182. let _commands = args[1].commands;
  3183. _commands.setHandlers = new Proxy(_commands.setHandlers, {
  3184. apply(fun, that, args) {
  3185. const noopFunc = name => () => _console.log('Skipped', name, 'window');
  3186. for (let name in args[0])
  3187. if (name === 'modal:streaminterrupt' ||
  3188. name === 'modal:midroll')
  3189. args[0][name] = noopFunc(name);
  3190. delete _commands.setHandlers;
  3191. return Reflect.apply(fun, that, args);
  3192. }
  3193. });
  3194. }
  3195. return Reflect.apply(fun, that, args);
  3196. }
  3197. });
  3198. }
  3199. return Reflect.apply(module, that, args);
  3200. }
  3201. });
  3202. }
  3203. di[name] = vl;
  3204. }
  3205. });
  3206. }
  3207. });
  3208. // don't send errorception logs
  3209. Object.defineProperty(win, 'onerror', {
  3210. set(vl) {
  3211. if (log) _console.trace('Skipped global onerror callback:', vl);
  3212. }
  3213. });
  3214. }),
  3215.  
  3216. 'draug.ru': {
  3217. other: 'vargr.ru',
  3218. now: () => scriptLander(() => {
  3219. if (location.pathname === '/pop.html')
  3220. win.close();
  3221. createStyle({
  3222. '#timer_1': {
  3223. display: 'none !important'
  3224. },
  3225. '#timer_2': {
  3226. position: 'relative !important',
  3227. display: 'block !important',
  3228. z_index: '42 !important'
  3229. },
  3230. '.clearfix, .clearfix > *': {
  3231. z_index: 'initial !important'
  3232. }
  3233. });
  3234. let _contentWindow = Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype, 'contentWindow');
  3235. let _get_contentWindow = _bindCall(_contentWindow.get);
  3236. _contentWindow.get = function () {
  3237. let res = _get_contentWindow(this);
  3238. if (res.location.href === 'about:blank')
  3239. res.document.write = (...args) => _console.log('Skipped iframe.write(', ...args, ')');
  3240. return res;
  3241. };
  3242. Object.defineProperty(HTMLIFrameElement.prototype, 'contentWindow', _contentWindow);
  3243. }),
  3244. dom() {
  3245. let list = _querySelectorAll('div[id^="yandex_rtb_"], .adsbygoogle');
  3246. list.forEach(node => _console.log('Removed:', node.parentNode.parentNode.removeChild(node.parentNode)));
  3247. }
  3248. },
  3249.  
  3250. 'drive2.ru': () => {
  3251. gardener('.c-block:not([data-metrika="recomm"]),.o-grid__item', />Реклама<\//i);
  3252. scriptLander(() => {
  3253. selectiveCookies();
  3254. let _d2;
  3255. Object.defineProperty(win, 'd2', {
  3256. get() {
  3257. return _d2;
  3258. },
  3259. set(vl) {
  3260. if (vl === _d2)
  3261. return true;
  3262. _d2 = new Proxy(vl, {
  3263. set(target, prop, val) {
  3264. if (['brandingRender', 'dvReveal', '__dv'].includes(prop))
  3265. val = () => null;
  3266. target[prop] = val;
  3267. return true;
  3268. }
  3269. });
  3270. }
  3271. });
  3272. // obfuscated Yandex.Direct
  3273. nt.define('Object.prototype.initYaDirect', undefined);
  3274. }, nullTools, selectiveCookies);
  3275. },
  3276.  
  3277. 'echo.msk.ru': {
  3278. now() {
  3279. scripts.yandexDirect.now();
  3280. selectiveCookies();
  3281. win.localStorage.removeItem('COOKIE_MATCHING_FAIL');
  3282. win.localStorage.removeItem('beerka');
  3283. win.localStorage.removeItem('ludca');
  3284. }
  3285. },
  3286.  
  3287. 'eurogamer.tld': {
  3288. other: 'metabomb.net, usgamer.net',
  3289. now: () => scriptLander(() => {
  3290. abortExecution.inlineScript('_sp_');
  3291. selectiveCookies('sp');
  3292. }, selectiveCookies, abortExecution)
  3293. },
  3294.  
  3295. 'fastpic.ru': () => {
  3296. // Had to obfuscate property name to avoid triggering anti-obfuscation on greasyfork.org -_- (Exception 403012)
  3297. nt.define(`_0x${'4955'}`, []);
  3298. },
  3299.  
  3300. 'fishki.net': () => {
  3301. scriptLander(() => {
  3302. const fishki = {};
  3303. const adv = nt.proxy({
  3304. afterAdblockCheck: nt.func(null, 'fishki.afterAdblockCheck'),
  3305. refreshFloat: nt.func(null, 'fishki.refreshFloat')
  3306. });
  3307. nt.defineOn(fishki, 'adv', adv, 'fishki.');
  3308. nt.defineOn(fishki, 'is_adblock', false, 'fishki.');
  3309. nt.define('fishki', fishki);
  3310. nt.define('Object.prototype.detect', nt.func(undefined, 'detect'));
  3311. win.Object.defineProperty = new Proxy(win.Object.defineProperty, {
  3312. apply(fun, that, args) {
  3313. if (['is_adblock', 'adv'].includes(args[1]) || args[0] === adv)
  3314. return;
  3315. return _apply(fun, that, args);
  3316. }
  3317. });
  3318. }, nullTools);
  3319. gardener('.drag_list > .drag_element, .list-view > .paddingtop15, .post-wrap', /543769|Новости\sпартнеров|Полезная\sреклама/);
  3320. },
  3321.  
  3322. 'forbes.com': () => {
  3323. createStyle(['fbs-ad[ad-id], .top-ad-container, .fbs-ad-wrapper, .footer-ad-labeling, .ad-rail, .ad-unit { display: none !important; }']);
  3324. nt.define('Object.prototype.isAdLight', true);
  3325. nt.define('Object.prototype.initializeAd', nt.func(undefined, '?.initializeAd'));
  3326. win.getComputedStyle = new Proxy(win.getComputedStyle, {
  3327. apply(fun, that, args) {
  3328. let res = _apply(fun, that, args);
  3329. if (res.display === 'none')
  3330. nt.defineOn(res, 'display', 'block', 'getComputedStyle().');
  3331. if (res.visibility === 'hidden')
  3332. nt.defineOn(res, 'visibility', 'visible', 'getComputedStyle().');
  3333. return res;
  3334. }
  3335. });
  3336. win.CSSStyleDeclaration.prototype.getPropertyValue = new Proxy(win.CSSStyleDeclaration.prototype.getPropertyValue, {
  3337. apply(fun, that, args) {
  3338. let res = _apply(fun, that, args);
  3339. if (args[0] === 'display' && res === 'none')
  3340. return 'block';
  3341. if (args[0] === 'visibility' && res === 'hidden')
  3342. return 'visible';
  3343. return res;
  3344. }
  3345. });
  3346. },
  3347.  
  3348. 'freeopenvpn.org': () => {
  3349. const alterEval = Function.constructor;
  3350. win.addEventListener = new Proxy(win.addEventListener, {
  3351. apply(fun, that, args) {
  3352. let [event, callback] = args;
  3353. if (event === 'load') {
  3354. let str = _toString(callback);
  3355. if (/\.clientHeight\s*!=/.test(str))
  3356. args[1] = () => alterEval(
  3357. str.replace(/^function\s*\(\)/, 'function pwd()')
  3358. .replace(/if[^\r\n]*clientHeight[^\r\n]*\)\s*{/, 'if (false) {') + ' pwd();'
  3359. )();
  3360. }
  3361. return _apply(fun, that, args);
  3362. }
  3363. });
  3364. },
  3365.  
  3366. 'friends.in.ua': () => scriptLander(() => {
  3367. Object.defineProperty(win, 'need_warning', {
  3368. get() {
  3369. return 0;
  3370. },
  3371. set() {}
  3372. });
  3373. }),
  3374.  
  3375. 'gamerevolution.com': () => {
  3376. const _clientHeight = Object.getOwnPropertyDescriptor(_Element, 'clientHeight');
  3377. _clientHeight.get = new Proxy(_clientHeight.get, {
  3378. apply(...args) {
  3379. return _apply(...args) || 1;
  3380. }
  3381. });
  3382. Object.defineProperty(_Element, 'clientHeight', _clientHeight);
  3383.  
  3384. const toReplace = [
  3385. 'blockerDetected', 'disableDetected', 'hasAdBlocker',
  3386. 'hasBlockerFlag', 'hasDisabledAdBlocker', 'hasBlocker'
  3387. ];
  3388. win.Object.defineProperty = new Proxy(win.Object.defineProperty, {
  3389. apply(fun, that, args) {
  3390. if (toReplace.includes(args[1])) {
  3391. args[2] = {
  3392. value() {
  3393. return false;
  3394. }
  3395. };
  3396. console.log(args);
  3397. }
  3398. return _apply(fun, that, args);
  3399. }
  3400. });
  3401. },
  3402.  
  3403. 'gamersheroes.com': () => abortExecution.inlineScript('document.createElement', {
  3404. pattern: /window\[\w+\(\[(\d+,?\s?)+\],\s?\w+\)\]/
  3405. }),
  3406.  
  3407. 'gidonline.club': () => createStyle('.tray > div[style] {display: none!important}'),
  3408.  
  3409. 'glav.su': () => scriptLander(() => {
  3410. abortExecution.onSet('abd');
  3411. abortExecution.onSet('script1');
  3412. }, abortExecution),
  3413.  
  3414. 'gorodrabot.ru': () => scriptLander(() => {
  3415. abortExecution.onGet('Object.prototype.yaads');
  3416. abortExecution.onGet('Object.prototype.initYaDirect');
  3417. }, abortExecution),
  3418.  
  3419. 'haes.tech': () => {
  3420. // debugger detection prevention
  3421. win.eval = new Proxy(win.eval, {
  3422. apply(fun, that, args) {
  3423. if (typeof args[0] === 'string' && args[0].includes('debugger;'))
  3424. throw removeOwnFootprint(new ReferenceError('debugger is not defined'));
  3425. return _apply(fun, that, args);
  3426. }
  3427. });
  3428. },
  3429.  
  3430. 'hdgo.cc': {
  3431. other: '46.30.43.38, couber.be',
  3432. now() {
  3433. (new MutationObserver(
  3434. ms => {
  3435. let m, node;
  3436. for (m of ms)
  3437. for (node of m.addedNodes)
  3438. if (node instanceof HTMLScriptElement && _getAttribute(node, 'onerror') !== null)
  3439. node.removeAttribute('onerror');
  3440. }
  3441. )).observe(_document.documentElement, {
  3442. childList: true,
  3443. subtree: true
  3444. });
  3445. }
  3446. },
  3447.  
  3448. 'hentai-share.tv': () => {
  3449. // debugger detection prevention
  3450. win.Object.defineProperty = new Proxy(win.Object.defineProperty, {
  3451. apply(fun, that, args) {
  3452. if (args[1] === 'id' && 'get' in args[2])
  3453. return;
  3454. return _apply(fun, that, args);
  3455. }
  3456. });
  3457. },
  3458.  
  3459. 'gamepur.com': () => {
  3460. nt.define('ga', nt.func(null, 'ga'));
  3461. win.Object.defineProperty = new Proxy(win.Object.defineProperty, {
  3462. apply(fun, that, args) {
  3463. if (typeof args[1] === 'string' &&
  3464. (args[1] === 'hasAdblocker' || args[1] === 'blockerDetected'))
  3465. throw new TypeError(`Cannot read property '${args[1]}' of undefined`);
  3466. return Reflect.apply(fun, that, args);
  3467. }
  3468. });
  3469. },
  3470.  
  3471. 'hdrezka.ag': () => {
  3472. Object.defineProperty(win, 'ab', {
  3473. value: false,
  3474. enumerable: true
  3475. });
  3476. gardener('div[id][onclick][onmouseup][onmousedown]', /onmouseout/i);
  3477. },
  3478.  
  3479. 'htmlweb.ru': () => {
  3480. let _onerror = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'onerror');
  3481. _onerror.set = new Proxy(_onerror.set, {
  3482. apply(fun, that, args) {
  3483. if (that.tagName === 'SCRIPT')
  3484. return _console.log('Skip set onerror for', that);
  3485. return _apply(fun, that, args);
  3486. }
  3487. });
  3488. Object.defineProperty(HTMLElement.prototype, 'onerror', _onerror);
  3489. },
  3490.  
  3491. 'hqq.tv': () => scriptLander(() => {
  3492. // disable anti-debugging in hqq.tv player
  3493. let isObfuscated = text => /[^a-z0-9]([a-z0-9]{1,2}\.[a-z0-9]{1,2}\(|[a-z0-9]{4}\.[a-z]\(\d+\)|[a-z0-9]\[[a-z0-9]{1,2}\]\[[a-z0-9]{1,2}\])/i.test(text);
  3494. deepWrapAPI(root => {
  3495. // skip obfuscated stuff and a few other calls
  3496. let _setInterval = root.setInterval,
  3497. _setTimeout = root.setTimeout;
  3498. root.setInterval = (...args) => {
  3499. let fun = args[0];
  3500. if (typeof fun === 'function') {
  3501. let text = _toString(fun),
  3502. skip = text.includes('check();') || isObfuscated(text);
  3503. _console.trace('setInterval', text, 'skip', skip);
  3504. if (skip) return -1;
  3505. }
  3506. return _setInterval.apply(this, args);
  3507. };
  3508. let wrappedST = new WeakSet();
  3509. root.setTimeout = (...args) => {
  3510. let fun = args[0];
  3511. if (typeof fun === 'function') {
  3512. let text = _toString(fun),
  3513. skip = fun.name === 'check' || isObfuscated(text);
  3514. if (!wrappedST.has(fun)) {
  3515. _console.trace('setTimeout', text, 'skip', skip);
  3516. wrappedST.add(fun);
  3517. }
  3518. if (skip) return;
  3519. }
  3520. return _setTimeout.apply(this, args);
  3521. };
  3522. // skip 'debugger' call
  3523. let _eval = root.eval;
  3524. root.eval = text => {
  3525. if (typeof text === 'string' && text.includes('debugger;')) {
  3526. _console.trace('skip eval', text);
  3527. return;
  3528. }
  3529. _eval(text);
  3530. };
  3531. // Prevent RegExpt + toString trick
  3532. let _proto;
  3533. try {
  3534. _proto = root.RegExp.prototype;
  3535. } catch (ignore) {
  3536. return;
  3537. }
  3538. let _RE_tS = Object.getOwnPropertyDescriptor(_proto, 'toString');
  3539. let _RE_tSV = _RE_tS.value || _RE_tS.get();
  3540. Object.defineProperty(_proto, 'toString', {
  3541. enumerable: _RE_tS.enumerable,
  3542. configurable: _RE_tS.configurable,
  3543. get() {
  3544. return _RE_tSV;
  3545. },
  3546. set(val) {
  3547. _console.trace('Attempt to change toString for', this, 'with', _toString(val));
  3548. }
  3549. });
  3550. });
  3551. }, deepWrapAPI),
  3552.  
  3553. 'hideip.me': {
  3554. now: () => scriptLander(() => {
  3555. let _innerHTML = Object.getOwnPropertyDescriptor(_Element, 'innerHTML');
  3556. let _set_innerHTML = _innerHTML.set;
  3557. let _innerText = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'innerText');
  3558. let _get_innerText = _innerText.get;
  3559. let div = _document.createElement('div');
  3560. _innerHTML.set = function (...args) {
  3561. _set_innerHTML.call(div, args[0].replace('i', 'a'));
  3562. if (args[0] && /[рp][еe]кл/.test(_get_innerText.call(div)) ||
  3563. /(\d\d\d?\.){3}\d\d\d?:\d/.test(_get_innerText.call(this))) {
  3564. _console.log('Anti-Adblock killed.');
  3565. return true;
  3566. }
  3567. _set_innerHTML.apply(this, args);
  3568. };
  3569. Object.defineProperty(_Element, 'innerHTML', _innerHTML);
  3570. Object.defineProperty(win, 'adblock', {
  3571. get() {
  3572. return false;
  3573. },
  3574. set() {},
  3575. enumerable: true
  3576. });
  3577. let _$ = {};
  3578. let _$_map = new WeakMap();
  3579. let _gOPD = Object.getOwnPropertyDescriptor(Object, 'getOwnPropertyDescriptor');
  3580. let _val_gOPD = _gOPD.value;
  3581. _gOPD.value = function (...args) {
  3582. let _res = _val_gOPD.apply(this, args);
  3583. if (args[0] instanceof Window && (args[1] === '$' || args[1] === 'jQuery')) {
  3584. delete _res.get;
  3585. delete _res.set;
  3586. _res.value = win[args[1]];
  3587. }
  3588. return _res;
  3589. };
  3590. Object.defineProperty(Object, 'getOwnPropertyDescriptor', _gOPD);
  3591. let getJQWrap = (n) => {
  3592. let name = n;
  3593. return {
  3594. enumerable: true,
  3595. get() {
  3596. return _$[name];
  3597. },
  3598. set(x) {
  3599. if (_$_map.has(x)) {
  3600. _$[name] = _$_map.get(x);
  3601. return true;
  3602. }
  3603. if (x === _$.$ || x === _$.jQuery) {
  3604. _$[name] = x;
  3605. return true;
  3606. }
  3607. _$[name] = new Proxy(x, {
  3608. apply(t, o, args) {
  3609. let _res = t.apply(o, args);
  3610. if (_$_map.has(_res.is))
  3611. _res.is = _$_map.get(_res.is);
  3612. else {
  3613. let _is = _res.is;
  3614. _res.is = function (...args) {
  3615. if (args[0] === ':hidden')
  3616. return false;
  3617. return _is.apply(this, args);
  3618. };
  3619. _$_map.set(_is, _res.is);
  3620. }
  3621. return _res;
  3622. }
  3623. });
  3624. _$_map.set(x, _$[name]);
  3625. return true;
  3626. }
  3627. };
  3628. };
  3629. Object.defineProperty(win, '$', getJQWrap('$'));
  3630. Object.defineProperty(win, 'jQuery', getJQWrap('jQuery'));
  3631. let _dP = Object.defineProperty;
  3632. Object.defineProperty = function (...args) {
  3633. if (args[0] instanceof Window && (args[1] === '$' || args[1] === 'jQuery'))
  3634. return undefined;
  3635. return _dP.apply(this, args);
  3636. };
  3637. })
  3638. },
  3639.  
  3640. 'igra-prestoloff.cx': () => scriptLander(() => {
  3641. /*jslint evil: true */ // yes, evil, I know
  3642. let _write = _document.write.bind(_document);
  3643. /*jslint evil: false */
  3644. nt.define('document.write', t => {
  3645. let id = t.match(/jwplayer\("(\w+)"\)/i);
  3646. if (id && id[1])
  3647. return _write(`<div id="${id[1]}"></div>${t}`);
  3648. return _write('');
  3649. }, {
  3650. enumerable: true
  3651. });
  3652. }),
  3653.  
  3654. 'imageban.ru': () => {
  3655. Object.defineProperty(win, 'V7x1J', {
  3656. get() {
  3657. return null;
  3658. }
  3659. });
  3660. },
  3661.  
  3662. 'inoreader.com': () => scriptLander(() => {
  3663. createStyle('.block_article_ad { display: none !important }');
  3664. nt.define('gn', true);
  3665. // their own hidden adblock detection skip
  3666. let cookie = Object.getOwnPropertyDescriptor(_Document, 'cookie');
  3667. cookie.get = new Proxy(cookie.get, {
  3668. apply(...args) {
  3669. return 'aguineapigtrickedme=1; ' + _apply(...args);
  3670. }
  3671. });
  3672. Object.defineProperty(_Document, 'cookie', cookie);
  3673. }),
  3674.  
  3675. 'it-actual.ru': () => scriptLander(() => {
  3676. abortExecution.onAll('blocked');
  3677. abortExecution.onGet('nsg');
  3678. }, abortExecution),
  3679.  
  3680. 'ivi.ru': () => {
  3681. let _xhr_open = win.XMLHttpRequest.prototype.open;
  3682. win.XMLHttpRequest.prototype.open = function (method, url, ...args) {
  3683. if (typeof url === 'string')
  3684. if (url.endsWith('/track'))
  3685. return;
  3686. return _xhr_open.call(this, method, url, ...args);
  3687. };
  3688. let _responseText = Object.getOwnPropertyDescriptor(XMLHttpRequest.prototype, 'responseText');
  3689. let _responseText_get = _responseText.get;
  3690. _responseText.get = function () {
  3691. if (this.__responseText__)
  3692. return this.__responseText__;
  3693. let res = _responseText_get.apply(this, arguments);
  3694. let o;
  3695. try {
  3696. if (res)
  3697. o = JSON.parse(res);
  3698. } catch (ignore) {}
  3699. let changed = false;
  3700. if (o && o.result) {
  3701. if (o.result instanceof Array &&
  3702. 'adv_network_logo_url' in o.result[0]) {
  3703. o.result = [];
  3704. changed = true;
  3705. }
  3706. if (o.result.show_adv) {
  3707. o.result.show_adv = false;
  3708. changed = true;
  3709. }
  3710. }
  3711. if (changed) {
  3712. _console.log('changed response >>', o);
  3713. res = JSON.stringify(o);
  3714. }
  3715. this.__responseText__ = res;
  3716. return res;
  3717. };
  3718. Object.defineProperty(XMLHttpRequest.prototype, 'responseText', _responseText);
  3719. },
  3720.  
  3721. 'kakprosto.ru': () => scriptLander(() => {
  3722. selectiveCookies('yadb');
  3723. abortExecution.inlineScript('yaProxy', {
  3724. pattern: /yadb/
  3725. });
  3726. abortExecution.inlineScript('yandexContextAsyncCallbacks');
  3727. abortExecution.inlineScript('adfoxAsyncParams');
  3728. abortExecution.inlineScript('adfoxBackGroundLoaded');
  3729. }, selectiveCookies, abortExecution),
  3730.  
  3731. 'kinonavigator.ru': () => {
  3732. // fix for broken pages specific for this site, no need to make it global
  3733. nt.define('Ya.share2', nt.func(nt.proxy({}, 'Ya.share2'), 'Ya.share2'));
  3734. },
  3735.  
  3736. 'kinopoisk.ru': () => {
  3737. // filter cookies
  3738. // set no-branding body style and adjust other blocks on the page
  3739. const style = {
  3740. '.app__header.app__header_margin-bottom_brand, #top': {
  3741. margin_bottom: '20px !important'
  3742. },
  3743. '.app__branding': {
  3744. display: 'none!important'
  3745. }
  3746. };
  3747. if (location.hostname === 'www.kinopoisk.ru' && !location.pathname.startsWith('/games/'))
  3748. style['html:not(#id), body:not(#id), .app-container'] = {
  3749. background: '#d5d5d5 url(/images/noBrandBg.jpg) 50% 0 no-repeat !important'
  3750. };
  3751. createStyle(style);
  3752. scriptLander(() => {
  3753. selectiveCookies('cmtchd|crookie|kpunk');
  3754. // filter JSON
  3755. win.JSON.parse = new Proxy(win.JSON.parse, {
  3756. apply(fun, that, args) {
  3757. let o = _apply(fun, that, args);
  3758. let name = 'antiAdBlockCookieName';
  3759. if (name in o && typeof o[name] === 'string')
  3760. selectiveCookies(o[name]);
  3761. name = 'branding';
  3762. if (name in o) o[name] = {};
  3763. // tricks against ads in the trailer player
  3764. // if (location.hostname.startsWith('widgets.'))
  3765. if (o.page && o.page.playerParams)
  3766. delete o.page.playerParams.adConfig;
  3767. if (o.common && o.common.bunker && o.common.bunker.adv && o.common.bunker.adv.filmIdWithoutAd)
  3768. o.common.bunker.adv.filmIdWithoutAd.includes = () => true;
  3769. //_console.log('JSON.parse', o);
  3770. return o;
  3771. }
  3772. });
  3773. // skip timeout check for blocked requests
  3774. win.setTimeout = new Proxy(win.setTimeout, {
  3775. apply(fun, that, args) {
  3776. if (args[1] === 100) {
  3777. let str = _toString(args[0]);
  3778. if (str.endsWith('{a()}') || str.endsWith('{n()}'))
  3779. return;
  3780. }
  3781. return _apply(fun, that, args);
  3782. }
  3783. });
  3784. // obfuscated Yandex.Direct
  3785. nt.define('Object.prototype.initYaDirect', undefined);
  3786. nt.define('Object.prototype._resolveDetectResult', () => null);
  3787. nt.define('Object.prototype.detectResultPromise', new Promise(r => r(false)));
  3788. if (location.hostname === 'www.kinopoisk.ru')
  3789. nt.define('Object.prototype.initAd', nt.func(undefined, 'initAd'));
  3790. // catch branding and other things
  3791. let _KP;
  3792. Object.defineProperty(win, 'KP', {
  3793. get() {
  3794. return _KP;
  3795. },
  3796. set(val) {
  3797. if (_KP === val)
  3798. return true;
  3799. _KP = new Proxy(val, {
  3800. set(kp, name, val) {
  3801. if (name === 'branding') {
  3802. kp[name] = new Proxy({
  3803. weborama: {}
  3804. }, {
  3805. get(kp, name) {
  3806. return name in kp ? kp[name] : '';
  3807. },
  3808. set() {}
  3809. });
  3810. return true;
  3811. }
  3812. if (name === 'config')
  3813. val = new Proxy(val, {
  3814. set(cfg, name, val) {
  3815. if (name === 'anContextUrl')
  3816. return true;
  3817. if (name === 'adfoxEnabled' || name === 'hasBranding')
  3818. val = false;
  3819. if (name === 'adfoxVideoAdUrls')
  3820. val = {
  3821. flash: {},
  3822. html: {}
  3823. };
  3824. cfg[name] = val;
  3825. return true;
  3826. }
  3827. });
  3828. kp[name] = val;
  3829. return true;
  3830. }
  3831. });
  3832. _console.log('KP =', val);
  3833. }
  3834. });
  3835. }, selectiveCookies, nullTools);
  3836. },
  3837.  
  3838. 'korrespondent.net': {
  3839. now: () => scriptLander(() => {
  3840. nt.define('holder', function (id) {
  3841. let div = _document.getElementById(id);
  3842. if (!div)
  3843. return;
  3844. if (div.parentNode.classList.contains('col__sidebar')) {
  3845. div.parentNode.appendChild(div);
  3846. div.style.height = '300px';
  3847. }
  3848. });
  3849. }, nullTools),
  3850. dom() {
  3851. for (let frame of _document.querySelectorAll('.unit-side-informer > iframe'))
  3852. frame.parentNode.style.width = '1px';
  3853. }
  3854. },
  3855.  
  3856. 'libertycity.ru': () => scriptLander(() => {
  3857. nt.define('adBlockEnabled', false);
  3858. }, nullTools),
  3859.  
  3860. 'liveinternet.ru': () => scriptLander(() => {
  3861. abortExecution.onGet('Object.prototype.initAd');
  3862. abortExecution.onGet('Object.prototype.yaads');
  3863. }, abortExecution),
  3864.  
  3865. 'livejournal.com': () => scriptLander(() => {
  3866. nt.define('Object.prototype.Adf', undefined);
  3867. nt.define('Object.prototype.Begun', undefined);
  3868. }, nullTools),
  3869.  
  3870. 'mail.ru': {
  3871. other: 'ok.ru, sportmail.ru',
  3872. now: () => scriptLander(() => {
  3873. const _hostparts = location.hostname.split('.');
  3874. const _subdomain = _hostparts.slice(-3).join('.');
  3875. const _hostname = _hostparts.slice(-2).join('.');
  3876. const _emailru = _subdomain === 'e.mail.ru' || _subdomain === 'octavius.mail.ru';
  3877. const _mymailru = _subdomain === 'my.mail.ru';
  3878. const _otvet = _subdomain === 'otvet.mail.ru';
  3879. const _okru = _hostname === 'ok.ru';
  3880. // setTimeout filter
  3881. // advBlock|rbParams - ads
  3882. // document\.title= - blinking title on background news load on main page
  3883. const pattern = /advBlock|rbParams|document\.title=/i;
  3884. const _setTimeout = win.setTimeout;
  3885. win.setTimeout = function setTimeout(...args) {
  3886. let text = _toString(args[0]);
  3887. if (pattern.test(text)) {
  3888. _console.trace('Skipped setTimeout:', text);
  3889. return;
  3890. }
  3891. return _setTimeout(...args);
  3892. };
  3893.  
  3894. // Trick to prevent mail.ru from removing 3rd-party styles
  3895. nt.define('Object.prototype.restoreVisibility', nt.func(null, 'restoreVisibility'));
  3896. // Other Yandex Direct and other ads
  3897. nt.define('Object.prototype.initMimic', undefined);
  3898. nt.define('Object.prototype.hpConfig', undefined);
  3899. if (!_otvet) // used for a different purpose there
  3900. nt.define('Object.prototype.direct', undefined);
  3901. const getAds = () => new Promise(
  3902. r => r(nt.proxy({}, '?.getAds()'))
  3903. );
  3904. nt.define('Object.prototype.getAds', getAds);
  3905. nt.define('rb_counter', nt.func(null, 'rb_counter'));
  3906. if (_subdomain === 'mail.ru') { // main page
  3907. nt.define('Object.prototype.baits', undefined); // detector
  3908. nt.define('Object.prototype.getFeed', nt.func(null, 'pulse.getFeed')); // Pulse feed
  3909. createStyle('body > div > .pulse { display: none !important }');
  3910. }
  3911. if (_emailru)
  3912. nt.define('Object.prototype.show_me_ads', undefined);
  3913. else if (_mymailru)
  3914. nt.define('Object.prototype.runMimic', nt.func(null, 'runMimic'));
  3915. else {
  3916. nt.define('Object.prototype.mimic', undefined);
  3917. const xray = nt.func(undefined, 'xray');
  3918. nt.defineOn(xray, 'send', nt.func(undefined, 'xray.send'), 'xray.');
  3919. nt.defineOn(xray, 'radarPrefix', null, 'xray.');
  3920. nt.defineOn(xray, 'xrayRadarUrl', undefined, 'xray.');
  3921. nt.defineOn(xray, 'defaultParams', nt.proxy({
  3922. i: undefined,
  3923. p: 'media'
  3924. }), 'xray.');
  3925. nt.defineOn(xray, 'getConfig', nt.func(
  3926. nt.proxy({
  3927. radarPrefix: 'dev'
  3928. }, 'xray.getConfig().'),
  3929. 'xray.getConfig'
  3930. ));
  3931. nt.define('Object.prototype.xray', nt.proxy(xray));
  3932. }
  3933. // shenanigans against ok.ru ABP detector
  3934. if (_okru) {
  3935. abortExecution.onGet('OK.hooks');
  3936. // banners on ok.ru and counter
  3937. nt.define('getAdvTargetParam', nt.func(null, 'getAdvTargetParam'));
  3938. // break detection in case detector wasn't wrapped
  3939. abortExecution.onSet('Object.prototype.adBlockDetected');
  3940. }
  3941. // news.mail.ru and sportmail.ru
  3942. abortExecution.onGet('myWidget');
  3943. // cleanup e.mail.ru configs and mimic config on news and sport
  3944. const emptyString = (root, name) => root[name] && (root[name] = '');
  3945. const detectMimic = /direct|240x400|SlotView/;
  3946. win.JSON.parse = new Proxy(win.JSON.parse, {
  3947. apply(fun, that, args) {
  3948. let o = _apply(fun, that, args);
  3949. if (o && typeof o === 'object') {
  3950. if (o.cfg && o.cfg.sotaFeatures) {
  3951. let root = o.cfg.sotaFeatures;
  3952. if (Array.isArray(root.adv)) root.adv = [];
  3953. for (let name in root)
  3954. if (name.startsWith('adv-') || name.startsWith('adman-'))
  3955. delete root[name];
  3956. ['email_logs_to', 'smokescreen-locators'].forEach(name => emptyString(root, name));
  3957. }
  3958. if (o.userConfig) {
  3959. if (Array.isArray(o.userConfig.honeypot))
  3960. o.userConfig.honeypot.forEach((v, id, me) => (me[id] = []));
  3961. const cfg = o.userConfig.config;
  3962. if (cfg && cfg.honeypot)
  3963. emptyString(cfg.honeypot, 'baits');
  3964. }
  3965. if (o.body) {
  3966. const flags = o.body.common_purpose_flags;
  3967. if (flags && 'hide_ad_in_mail_web' in flags)
  3968. flags.hide_ad_in_mail_web = true;
  3969. if (o.body.show_me_ads)
  3970. o.body.show_me_ads = false;
  3971. }
  3972. //_console.log('JSON.parse', o);
  3973. }
  3974. if (Array.isArray(o))
  3975. if (o.some(t => typeof t === 'string' && detectMimic.test(t))) {
  3976. _console.log('Replaced', o);
  3977. o = [];
  3978. } //else _console.log('JSON.parse', o);
  3979. return o;
  3980. }
  3981. });
  3982. // all the rest is only needed on main page and in emails
  3983. if (_subdomain !== 'mail.ru' && !_emailru && !_okru)
  3984. return;
  3985.  
  3986. // Disable page scrambler on mail.ru to let extensions easily block ads there
  3987. let logger = {
  3988. apply(fun, that, args) {
  3989. let res = _apply(fun, that, args);
  3990. _console.log(`${fun._name}(`, ...args, `)\n>>`, res);
  3991. return res;
  3992. }
  3993. };
  3994.  
  3995. function wrapLocator(locator) {
  3996. if ('setup' in locator) {
  3997. let _setup = locator.setup;
  3998. locator.setup = function (o) {
  3999. if ('enable' in o) {
  4000. o.enable = false;
  4001. _console.log('Disable mimic mode.');
  4002. }
  4003. if ('links' in o) {
  4004. o.links = [];
  4005. _console.log('Call with empty list of sheets.');
  4006. }
  4007. return _setup.call(this, o);
  4008. };
  4009. locator.insertSheet = () => false;
  4010. locator.wrap = () => false;
  4011. }
  4012. try {
  4013. let names = [];
  4014. for (let name in locator)
  4015. if (typeof locator[name] === 'function' && name !== 'transform') {
  4016. locator[name]._name = "locator." + name;
  4017. locator[name] = new Proxy(locator[name], logger);
  4018. names.push(name);
  4019. }
  4020. _console.log(`[locator] wrapped properties: ${names.length ? names.join(', ') : '[empty]'}`);
  4021. } catch (e) {
  4022. _console.log(e);
  4023. }
  4024. return locator;
  4025. }
  4026.  
  4027. const same = Symbol('same');
  4028.  
  4029. function defineLocator(root) {
  4030. let _locator = root.locator;
  4031. let wrapLocatorSetter = vl => _locator = wrapLocator(vl);
  4032. wrapLocatorSetter[same] = true;
  4033. let loc_desc = Object.getOwnPropertyDescriptor(root, 'locator');
  4034. if (!loc_desc || !loc_desc.set[same])
  4035. try {
  4036. Object.defineProperty(root, 'locator', {
  4037. set: wrapLocatorSetter,
  4038. get() {
  4039. return _locator;
  4040. }
  4041. });
  4042. } catch (err) {
  4043. _console.log('Unable to redefine "locator" object!!!', err);
  4044. }
  4045. else if (loc_desc.value)
  4046. _locator = wrapLocator(loc_desc.value);
  4047. }
  4048.  
  4049. { // auto-stubs for various ad, detection and obfuscation modules
  4050. const missingCheck = {
  4051. get(obj, name) {
  4052. let res = obj[name];
  4053. if (!(name in obj))
  4054. _console.trace(`Missing "${name}" in`, obj);
  4055. return res;
  4056. }
  4057. };
  4058. const skipLog = (name, ret) => (...args) => (_console.log(`${name}(`, ...args, ')'), ret);
  4059. const createSkipAllObject = (baseName, obj = {
  4060. __esModule: true
  4061. }) => new Proxy(obj, {
  4062. get(obj, name) {
  4063. if (name in obj)
  4064. return obj[name];
  4065. _console.log(`Created stub for "${name}" in ${baseName}.`);
  4066. obj[name] = skipLog(`${baseName}.${name}`);
  4067. return obj[name];
  4068. },
  4069. set() {}
  4070. });
  4071. const redefiner = {
  4072. apply(fun, that, args) {
  4073. let res;
  4074. let warn = false;
  4075. let name = fun._name;
  4076. if (name === 'mrg-smokescreen/Welter')
  4077. res = {
  4078. isWelter() {
  4079. return true;
  4080. },
  4081. wrap: skipLog(`${name}.wrap`)
  4082. };
  4083. if (name === 'mrg-smokescreen/Honeypot')
  4084. res = {
  4085. check(...args) {
  4086. _console.log(`${name}.check(`, ...args, ')');
  4087. return new Promise(() => undefined);
  4088. },
  4089. version: "-1"
  4090. };
  4091. if (name === 'advert/adman/adman') {
  4092. let features = {
  4093. siteZones: {},
  4094. slots: {}
  4095. };
  4096. [
  4097. 'expId', 'siteId', 'mimicEndpoint', 'mimicPartnerId',
  4098. 'immediateFetchTimeout', 'delayedFetchTimeout'
  4099. ].forEach(name => void(features[name] = null));
  4100. res = createSkipAllObject(name, {
  4101. getFeatures: skipLog(`${name}.getFeatures`, features)
  4102. });
  4103. }
  4104. if (name === 'mrg-smokescreen/Utils')
  4105. res = createSkipAllObject(name, {
  4106. extend(...args) {
  4107. let res = {
  4108. enable: false,
  4109. match: [],
  4110. links: []
  4111. };
  4112. _console.log(`${name}.extend(`, ...args, ') >>', res);
  4113. return res;
  4114. }
  4115. });
  4116. if (name.startsWith('OK/banners/') ||
  4117. name.startsWith('mrg-smokescreen/StyleSheets') ||
  4118. name === '@mail/mimic' ||
  4119. name === 'mediator/advert-managers')
  4120. res = createSkipAllObject(name);
  4121. if (res) {
  4122. Object.defineProperty(res, Symbol.toStringTag, {
  4123. get() {
  4124. return `Skiplog object for ${name}`;
  4125. }
  4126. });
  4127. Object.defineProperty(res, Symbol.toPrimitive, {
  4128. value(hint) {
  4129. if (hint === 'string')
  4130. return Object.prototype.toString.call(this);
  4131. return `[missing toPrimitive] ${name} ${hint}`;
  4132. }
  4133. });
  4134. res = new Proxy(res, missingCheck);
  4135. } else {
  4136. res = _apply(fun, that, args);
  4137. warn = true;
  4138. }
  4139. _console[warn ? 'warn' : 'log'](name, '(', ...args, ')\n>>', res);
  4140. return res;
  4141. }
  4142. };
  4143.  
  4144. const advModuleNamesStartWith = /^(mrg-(context|honeypot)|adv\/)/;
  4145. const advModuleNamesGeneric = /advert|banner|mimic|smoke/i;
  4146. const wrapAdFuncs = {
  4147. apply(fun, that, args) {
  4148. let module = args[0];
  4149. if (typeof module === 'string')
  4150. if ((advModuleNamesStartWith.test(module) ||
  4151. advModuleNamesGeneric.test(module)) &&
  4152. // fix for e.mail.ru in Fx56 and below, looks like Proxy is quirky there
  4153. !module.startsWith('patron.v2.')) {
  4154. let main = args[args.length - 1];
  4155. main._name = module;
  4156. args[args.length - 1] = new Proxy(main, redefiner);
  4157. }
  4158. return _apply(fun, that, args);
  4159. }
  4160. };
  4161. const wrapDefine = def => {
  4162. if (!def)
  4163. return;
  4164. _console.log('define =', def);
  4165. def = new Proxy(def, wrapAdFuncs);
  4166. def._name = 'define';
  4167. return def;
  4168. };
  4169. let _define = wrapDefine(win.define);
  4170. Object.defineProperty(win, 'define', {
  4171. get() {
  4172. return _define;
  4173. },
  4174. set(x) {
  4175. if (_define === x)
  4176. return true;
  4177. _define = wrapDefine(x);
  4178. return true;
  4179. }
  4180. });
  4181. }
  4182.  
  4183. let _honeyPot;
  4184.  
  4185. function defineDetector(mr) {
  4186. let __ = mr._ || {};
  4187. let setHoneyPot = o => {
  4188. if (!o || o === _honeyPot) return;
  4189. _console.log('[honeyPot]', o);
  4190. _honeyPot = function () {
  4191. this.check = new Proxy(() => {
  4192. __.STUCK_IN_POT = false;
  4193. return false;
  4194. }, logger);
  4195. this.check._name = 'honeyPot.check';
  4196. this.destroy = () => null;
  4197. };
  4198. };
  4199. if ('honeyPot' in mr)
  4200. setHoneyPot(mr.honeyPot);
  4201. else
  4202. Object.defineProperty(mr, 'honeyPot', {
  4203. get() {
  4204. return _honeyPot;
  4205. },
  4206. set: setHoneyPot
  4207. });
  4208.  
  4209. __ = new Proxy(__, {
  4210. get(target, prop) {
  4211. return target[prop];
  4212. },
  4213. set(target, prop, val) {
  4214. _console.log(`mr._.${prop} =`, val);
  4215. target[prop] = val;
  4216. return true;
  4217. }
  4218. });
  4219. mr._ = __;
  4220. }
  4221.  
  4222. function defineAdd(mr) {
  4223. let _add;
  4224. let addWrapper = {
  4225. apply(fun, that, args) {
  4226. let module = args[0];
  4227. if (typeof module === 'string' && module.startsWith('ad')) {
  4228. _console.log('Skip module:', module);
  4229. return;
  4230. }
  4231. if (typeof module === 'object' && module.name.startsWith('ad'))
  4232. _console.log('Loaded module:', module);
  4233. return logger.apply(fun, that, args);
  4234. }
  4235. };
  4236. let setMrAdd = v => {
  4237. if (!v) return;
  4238. v._name = 'mr.add';
  4239. v = new Proxy(v, addWrapper);
  4240. _add = v;
  4241. };
  4242. if ('add' in mr)
  4243. setMrAdd(mr.add);
  4244. Object.defineProperty(mr, 'add', {
  4245. get() {
  4246. return _add;
  4247. },
  4248. set: setMrAdd
  4249. });
  4250.  
  4251. }
  4252.  
  4253. const _mr_wrapper = vl => {
  4254. defineLocator(vl.mimic ? vl.mimic : vl);
  4255. defineDetector(vl);
  4256. defineAdd(vl);
  4257. return vl;
  4258. };
  4259. if ('mr' in win) {
  4260. _console.log('Found existing "mr" object.');
  4261. win.mr = _mr_wrapper(win.mr);
  4262. } else {
  4263. let _mr;
  4264. Object.defineProperty(win, 'mr', {
  4265. get() {
  4266. return _mr;
  4267. },
  4268. set(vl) {
  4269. _mr = vl ? _mr_wrapper(vl) : vl;
  4270. },
  4271. configurable: true
  4272. });
  4273. let _defineProperty = _bindCall(Object.defineProperty);
  4274. Object.defineProperty = function defineProperty(...args) {
  4275. const [obj, name, conf] = args;
  4276. if (name === 'mr' && obj instanceof Window) {
  4277. _console.trace('Object.defineProperty(', ...args, ')');
  4278. conf.set(_mr_wrapper(conf.get()));
  4279. }
  4280. if ((name === 'honeyPot' || name === 'add') && _mr === obj && conf.set)
  4281. return;
  4282. return _defineProperty(this, ...args);
  4283. };
  4284. }
  4285. }, nullTools, selectiveCookies, abortExecution)
  4286. },
  4287.  
  4288. 'oms.matchat.online': () => scriptLander(() => {
  4289. let _rmpGlobals;
  4290. Object.defineProperty(win, 'rmpGlobals', {
  4291. get() {
  4292. return _rmpGlobals;
  4293. },
  4294. set(val) {
  4295. if (val === _rmpGlobals)
  4296. return true;
  4297. _rmpGlobals = new Proxy(val, {
  4298. get(obj, name) {
  4299. if (name === 'adBlockerDetected')
  4300. return false;
  4301. return obj[name];
  4302. },
  4303. set(obj, name, val) {
  4304. if (name === 'adBlockerDetected')
  4305. _console.trace('rmpGlobals.adBlockerDetected =', val);
  4306. else
  4307. obj[name] = val;
  4308. return true;
  4309. }
  4310. });
  4311. }
  4312. });
  4313. }),
  4314.  
  4315. 'megogo.net': {
  4316. now() {
  4317. nt.define('adBlock', false);
  4318. nt.define('showAdBlockMessage', nt.func(null, 'showAdBlockMessage'));
  4319. }
  4320. },
  4321.  
  4322. 'naruto-base.su': () => gardener('div[id^="entryID"],.block', /href="http.*?target="_blank"/i),
  4323.  
  4324. 'online-fix.me': () => {
  4325. localStorage.setItem('disable-helpus', true);
  4326. let _$ = win.$;
  4327. Object.defineProperty(win, '$', {
  4328. get() {
  4329. return _$;
  4330. },
  4331. set(jQuery) {
  4332. _$ = new Proxy(jQuery, {
  4333. apply(fun, that, args) {
  4334. const res = _apply(fun, that, args);
  4335. if (res.selector === '#helpus')
  4336. res.fadeIn = nt.func(null, '$.fadeIn');
  4337. if (res.selector && res.selector.endsWith('a.btn'))
  4338. res.replaceWith = nt.func(null, '$.replaceWith');
  4339. return res;
  4340. }
  4341. });
  4342. return true;
  4343. }
  4344. });
  4345. },
  4346.  
  4347. 'otzovik.com': () => scriptLander(() => {
  4348. abortExecution.onAll('Object.prototype.getYa');
  4349. abortExecution.onGet('Object.prototype.parseServerDataFunction');
  4350. let _o_math = win.o_math;
  4351. Object.defineProperty(win, 'o_math', {
  4352. get() {
  4353. return _o_math;
  4354. },
  4355. set(val) {
  4356. delete val.ext_uid;
  4357. _o_math = val;
  4358. throw removeOwnFootprint(new ReferenceError('fetch is not defined'));
  4359. }
  4360. });
  4361. }, abortExecution, selectiveCookies),
  4362.  
  4363. 'overclockers.ru': {
  4364. now() {
  4365. abortExecution.onAll('cardinals');
  4366. abortExecution.inlineScript('Document.prototype.createElement', {
  4367. pattern: /mamydirect/
  4368. });
  4369. }
  4370. },
  4371.  
  4372. 'peka2.tv': () => {
  4373. let bodyClass = 'body--branding';
  4374. let checkNode = node => {
  4375. for (let className of node.classList)
  4376. if (className.includes('banner') || className === bodyClass) {
  4377. _removeAttribute(node, 'style');
  4378. node.classList.remove(className);
  4379. for (let attr of Array.from(node.attributes))
  4380. if (attr.name.startsWith('advert'))
  4381. _removeAttribute(node, attr.name);
  4382. }
  4383. };
  4384. (new MutationObserver(ms => {
  4385. let m, node;
  4386. for (m of ms)
  4387. for (node of m.addedNodes)
  4388. if (node instanceof HTMLElement)
  4389. checkNode(node);
  4390. })).observe(_de, {
  4391. childList: true,
  4392. subtree: true
  4393. });
  4394. (new MutationObserver(ms => {
  4395. for (let m of ms)
  4396. checkNode(m.target);
  4397. })).observe(_de, {
  4398. attributes: true,
  4399. subtree: true,
  4400. attributeFilter: ['class']
  4401. });
  4402. },
  4403.  
  4404. 'pikabu.ru': () => gardener('.story', /story__author[^>]+>ads</i, {
  4405. root: '.inner_wrap',
  4406. observe: true
  4407. }),
  4408.  
  4409. 'piratbit.tld': {
  4410. other: 'pb.wtf',
  4411. dom() {
  4412. const remove = node => node && node.parentNode && (_console.log('removed', node), node.parentNode.removeChild(node));
  4413. const isAdLink = el => location.hostname === el.hostname && /^\/(\w{3}|exit|out)\/[\w=/]{20,}$/.test(el.pathname);
  4414. // line above topic content and images in the slider in the header
  4415. for (let el of _document.querySelectorAll('.releas-navbar div a, #page_contents a'))
  4416. if (isAdLink(el))
  4417. remove(el.closest('tr[class]:not(.top_line):not(.active), .row2[id^="post_"]') || el.closest('div[style]:not(.row1):not(.btn-group)'));
  4418. }
  4419. },
  4420.  
  4421. 'pixelexperience.org': () => scriptLander(() => {
  4422. abortExecution.inlineScript('eval', {
  4423. pattern: /blockadblock/
  4424. });
  4425. }, abortExecution),
  4426.  
  4427. 'player.starlight.digital': {
  4428. other: 'teleportal.ua',
  4429. dom() {
  4430. scriptLander(() => {
  4431. let _currVideo = win.currVideo;
  4432. Object.defineProperty(win, 'currVideo', {
  4433. get() {
  4434. return _currVideo;
  4435. },
  4436. set(val) {
  4437. _console.log('currVideo =', val);
  4438. if ('adv' in val)
  4439. val.adv.creatives = [];
  4440. if ('showadv' in val)
  4441. val.showadv = false;
  4442. if ('mediaHls' in val)
  4443. val.mediaHls = val.mediaHls.replace('adv=1', 'adv=0');
  4444. if ('media' in val)
  4445. for (let media of val.media)
  4446. media.url = media.url.replace('adv=1', 'adv=0');
  4447. _currVideo = val;
  4448. }
  4449. });
  4450. nt.define('Object.prototype.isAdBlockEnabled', false);
  4451. nt.define('Object.prototype.AdBlockDynamicConfig', undefined);
  4452. nt.define('ADT_PLAYER_ADBLOCK_CONFIG', '');
  4453. nt.define('ADT_PLAYER_ADBLOCK_CONFIG_DETECT_ON_FAIL', false);
  4454. }, nullTools);
  4455. }
  4456. },
  4457.  
  4458. 'player.vgtrk.com': () => nt.define('Object.prototype.CheckAuth', nt.func(undefined, '?.CheckAuth')),
  4459.  
  4460. 'qrz.ru': {
  4461. now() {
  4462. nt.define('ab', false);
  4463. nt.define('tryMessage', nt.func(null, 'tryMessage'));
  4464. }
  4465. },
  4466.  
  4467. 'rambler.ru': {
  4468. other: [
  4469. 'afisha.ru', 'autorambler.ru', 'championat.com', 'eda.ru', 'gazeta.ru', 'lenta.ru', 'letidor.ru',
  4470. 'media.eagleplatform.com', 'motor.ru', 'passion.ru', 'quto.ru', 'rns.online', 'wmj.ru'
  4471. ].join(','),
  4472. now() {
  4473. scriptLander(() => {
  4474. // Skip login form and frames, and comments frames. Nothing to do here.
  4475. if (['id.rambler.ru', 'comments.rambler.ru'].includes(location.hostname))
  4476. return;
  4477.  
  4478. // prevent autoplay
  4479. if (location.hostname === 'vp.rambler.ru') {
  4480. nt.define('Object.prototype.minPlayingVisibleHeight', Number.MAX_SAFE_INTEGER);
  4481. return;
  4482. }
  4483. if (location.hostname.endsWith('.media.eagleplatform.com')) {
  4484. const _stopImmediatePropagation = _bindCall(Event.prototype.stopImmediatePropagation);
  4485. win.addEventListener('message', e => {
  4486. if (typeof e.data === 'object' && e.data.visible)
  4487. _stopImmediatePropagation(e);
  4488. });
  4489. return;
  4490. }
  4491. /* jshint -W001 */ // aka 'hasOwnProperty' is a really bad name, but this is a wrapper
  4492. const autoList = new Set(['autoplay', 'scrollplay']);
  4493. win.Object.prototype.hasOwnProperty = new Proxy(win.Object.prototype.hasOwnProperty, {
  4494. apply(fun, that, args) {
  4495. if (autoList.has(args[0]))
  4496. return false;
  4497. return _apply(fun, that, args);
  4498. }
  4499. });
  4500. /* jshint +W001 */
  4501.  
  4502. // ABP detection dev override, handy ^_^
  4503. _document.cookie = '_blocker_hidden=1; domain=.rambler.ru; path=/';
  4504.  
  4505. selectiveCookies('detect_count|dv|dvr|lv|lvr');
  4506. // Wrapper for adv loader settings in QW50aS1BZEJsb2Nr['7t7hystz']
  4507. const _contexts = new WeakMap();
  4508. Object.defineProperty(Object.prototype, 'Settings', {
  4509. set(val) {
  4510. if (typeof val === 'object' && 'Transports' in val && 'Urls' in val)
  4511. val.Urls = [];
  4512. _contexts.set(this, val);
  4513. },
  4514. get() {
  4515. return _contexts.get(this);
  4516. }
  4517. });
  4518. // disable video pop-outs in articles on gazeta.ru
  4519. if (location.hostname === 'gazeta.ru' || location.hostname.endsWith('.gazeta.ru'))
  4520. nt.define('creepyVideo', nt.func(null, 'creepyVideo'));
  4521. // disable Alice popup (encountered on horoscopes.rambler.ru)
  4522. nt.define('Object.prototype.needShowAlicePopup', false);
  4523. // disable some logging
  4524. yandexRavenStub();
  4525. // hide "disable ads" button
  4526. createStyle('a[href^="https://prime.rambler.ru/promo/"] { display: none !important }');
  4527. // prevent ads from loading
  4528. abortExecution.onGet('g_GazetaNoExchange');
  4529.  
  4530. //const toBlock = /[[:][a-z]{1,4}\("0x[\da-f]+"\)[\],}]|{[a-z]{1,2}\([a-z]{1,2}\)}|\.(rnet\.plus|24smi\.net|infox\.sg|lentainform\.com)\//i;
  4531. const scriptSkipList = /nrWrapper|\/(desktopVendor|vendorsDesktop)\.|<anonymous>/;
  4532. const isLocalScript = (log) => {
  4533. let e = removeOwnFootprint(new Error()),
  4534. parts = e.stack.split(/\n/),
  4535. row = 0;
  4536. if (!/http/.test(parts[row]))
  4537. row += 1;
  4538. while (scriptSkipList.test(parts[row]))
  4539. row += 1;
  4540. let parse = /(https?:.*):\d+:\d+/.exec(parts[row]);
  4541. if (log)
  4542. _console.log(parse && parse[1] === location.href, parts[row], [parts]);
  4543. return parse && parse[1] === location.href;
  4544. };
  4545. const cutoff = 200;
  4546. const fts = f => _toString(f.__sentry__ && f.__sentry_original__ || f['nr@original'] || f);
  4547. win.setTimeout = new Proxy(win.setTimeout, {
  4548. apply(fun, that, args) {
  4549. if (isLocalScript()) {
  4550. const [callback, delay] = args;
  4551. const str = fts(callback);
  4552. if (!/\n/.test(str)) {
  4553. _console.trace(`Skipped setTimeout(${str.slice(0, cutoff)}${str.length > cutoff ? '\u2026' : ''}, ${delay})`);
  4554. return null;
  4555. }
  4556. }
  4557. return _apply(fun, that, args);
  4558. }
  4559. });
  4560. const _onerror = Object.getOwnPropertyDescriptor(win.HTMLElement.prototype, 'onerror');
  4561. _onerror.set = new Proxy(_onerror.set, {
  4562. apply(fun, that, args) {
  4563. if (typeof args[0] === 'function' && isLocalScript()) {
  4564. const str = fts(args[0]);
  4565. _console.trace(`Skipped onerror = ${str.slice(0, cutoff)}${str.length > cutoff ? '\u2026' : ''}`);
  4566. return;
  4567. }
  4568. return _apply(fun, that, args);
  4569. }
  4570. });
  4571. Object.defineProperty(win.HTMLElement.prototype, 'onerror', _onerror);
  4572. // Skip dev console check
  4573. win.console.debug = new Proxy(win.console.debug, {
  4574. apply(fun, that, args) {
  4575. if (args[0] instanceof HTMLImageElement)
  4576. return;
  4577. return _apply(fun, that, args);
  4578. }
  4579. });
  4580. // anti-abdetector
  4581. let _primeStorage;
  4582. Object.defineProperty(win, 'primeStorage', {
  4583. get() {
  4584. if (isLocalScript())
  4585. throw removeOwnFootprint(new TypeError(`Cannot read property 'primeStorage' of undefined`));
  4586. return _primeStorage;
  4587. },
  4588. set(val) {
  4589. _primeStorage = val;
  4590. }
  4591. });
  4592. // Defense against triggered detector
  4593. _Node.removeChild = new Proxy(_Node.removeChild, {
  4594. apply(fun, that, args) {
  4595. const [el] = args;
  4596. if (el.tagName === 'LINK' && isLocalScript()) {
  4597. _console.log(`Let's not remove ${el.tagName}.`);
  4598. return;
  4599. }
  4600. return _apply(fun, that, args);
  4601. }
  4602. });
  4603. }, nullTools, yandexRavenStub, selectiveCookies, abortExecution);
  4604. },
  4605. dom() {
  4606. // disable video pop-outs in articles on lenta.ru and rambler.ru
  4607. let domain = location.hostname.split('.');
  4608. if (['lenta', 'rambler'].includes(domain[domain.length - 2])) {
  4609. const player = _document.querySelector('.js-video-box__container, .j-mini-player__video');
  4610. if (player) player.removeAttribute('class');
  4611. }
  4612. // remove utm_ form links
  4613. const parser = _document.createElement('a');
  4614. _document.addEventListener('mousedown', (e) => {
  4615. let t = e.target;
  4616. if (!t.href)
  4617. t = t.closest('A');
  4618. if (t && t.href) {
  4619. parser.href = t.href;
  4620. let remove = [];
  4621. let params = parser.search.slice(1).split('&').filter(name => {
  4622. if (name.startsWith('utm_')) {
  4623. remove.push(name);
  4624. return false;
  4625. }
  4626. return true;
  4627. });
  4628. if (remove.length)
  4629. _console.log('Removed parameters from link:', ...remove);
  4630. if (params.length)
  4631. parser.search = `?${params.join('&')}`;
  4632. else
  4633. parser.search = '';
  4634. t.href = parser.href;
  4635. }
  4636. }, false);
  4637. }
  4638. },
  4639.  
  4640. 'razlozhi.ru': {
  4641. now() {
  4642. nt.define('cadb', false);
  4643. for (let func of ['createShadowRoot', 'attachShadow'])
  4644. if (func in _Element)
  4645. _Element[func] = function () {
  4646. return this.cloneNode();
  4647. };
  4648. createStyle([
  4649. 'div[class][style*="width:"][style*="height"][style*="/themes/default/bg.png"] > div > div[style*="height:"][style*="width:"] { width: 100% !important }',
  4650. 'div[class][style*="width:"][style*="height"][style*="/themes/default/bg.png"] > div[class][style^="top: "] ~ div[class]:not([style]) { right: -100% !important }',
  4651. 'div[class][style*="width:"][style*="height"][style*="/themes/default/bg.png"] > div[style*="px;"] { width: 100% !important }',
  4652. 'div[class][style*="width:"][style*="height"][style*="/themes/default/bg.png"] { width: 100% !important }'
  4653. ]);
  4654. }
  4655. },
  4656.  
  4657. 'rbc.ru': {
  4658. other: 'autonews.ru, rbcplus.ru, sportrbc.ru',
  4659. now() {
  4660. scriptLander(() => selectiveCookies('adb_on'), selectiveCookies);
  4661. let _RA;
  4662. let setArgs = {
  4663. 'showBanners': true,
  4664. 'showAds': true,
  4665. 'banners.staticPath': '',
  4666. 'paywall.staticPath': '',
  4667. 'banners.dfp.config': [],
  4668. 'banners.dfp.pageTargeting': () => null,
  4669. };
  4670. Object.defineProperty(win, 'RA', {
  4671. get() {
  4672. return _RA;
  4673. },
  4674. set(vl) {
  4675. _console.log('RA =', vl);
  4676. if ('repo' in vl) {
  4677. _console.log('RA.repo =', vl.repo);
  4678. vl.repo = new Proxy(vl.repo, {
  4679. set(obj, name, val) {
  4680. if (name === 'banner') {
  4681. _console.log(`RA.repo.${name} =`, val);
  4682. val = new Proxy(val, {
  4683. get(obj, name) {
  4684. let res = obj[name];
  4685. if (typeof obj[name] === 'function') {
  4686. res = () => undefined;
  4687. if (name === 'getService')
  4688. res = service => {
  4689. if (service === 'dfp')
  4690. return {
  4691. getPlaces() {
  4692. return;
  4693. },
  4694. createPlaceholder() {
  4695. return;
  4696. }
  4697. };
  4698. return undefined;
  4699. };
  4700. res.toString = obj[name].toString.bind(obj[name]);
  4701. }
  4702. if (name === 'isInited')
  4703. res = true;
  4704. _console.trace(`get RA.repo.banner.${name}`, res);
  4705. return res;
  4706. }
  4707. });
  4708. }
  4709. obj[name] = val;
  4710. return true;
  4711. }
  4712. });
  4713. } else
  4714. _console.log('Unable to locate RA.repo');
  4715. _RA = new Proxy(vl, {
  4716. set(o, name, val) {
  4717. if (name === 'config') {
  4718. _console.log('RA.config =', val);
  4719. if ('set' in val) {
  4720. val.set = new Proxy(val.set, {
  4721. apply(set, that, args) {
  4722. let name = args[0];
  4723. if (name in setArgs)
  4724. args[1] = setArgs[name];
  4725. if (name in setArgs || name === 'checkad')
  4726. _console.log('RA.config.set(', ...args, ')');
  4727. return _apply(set, that, args);
  4728. }
  4729. });
  4730. val.set('showAds', true); // pretend ads already were shown
  4731. }
  4732. }
  4733. o[name] = val;
  4734. return true;
  4735. }
  4736. });
  4737. }
  4738. });
  4739. Object.defineProperty(win, 'bannersConfig', {
  4740. set() {},
  4741. get() {
  4742. return [];
  4743. }
  4744. });
  4745. // pretend there is a paywall landing on screen already
  4746. let pwl = _document.createElement('div');
  4747. pwl.style.display = 'none';
  4748. pwl.className = 'js-paywall-landing';
  4749. _document.documentElement.appendChild(pwl);
  4750. // detect and skip execution of one of the ABP detectors
  4751. win.setTimeout = new Proxy(win.setTimeout, {
  4752. apply(fun, that, args) {
  4753. if (typeof args[0] === 'function') {
  4754. let fts = _toString(args[0]);
  4755. if (/\.length\s*>\s*0\s*&&/.test(fts) && /:hidden/.test(fts)) {
  4756. _console.log('Skipped setTimout(', fts, args[1], ')');
  4757. return;
  4758. }
  4759. }
  4760. return _apply(fun, that, args);
  4761. }
  4762. });
  4763. // hide banner placeholders
  4764. createStyle('[data-banner-id], .banner__container, .banners__yandex__article { display: none !important }');
  4765. },
  4766. dom() {
  4767. // hide sticky banner place at the top of the page
  4768. for (let itm of _document.querySelectorAll('.l-sticky'))
  4769. if (itm.querySelector('.banner__container__link'))
  4770. itm.style.display = 'none';
  4771. }
  4772. },
  4773.  
  4774. 'reactor.cc': {
  4775. other: 'joyreactor.cc, pornreactor.cc',
  4776. now: () => scriptLander(() => {
  4777. selectiveEval();
  4778. win.open = function () {
  4779. throw new ReferenceError('Redirect prevention.');
  4780. };
  4781. nt.define('Worker', nt.func(nt.proxy({}, 'Worker'), 'Worker'));
  4782. let _CTRManager = win.CTRManager;
  4783. Object.defineProperty(win, 'CTRManager', {
  4784. get() {
  4785. return _CTRManager;
  4786. },
  4787. set(vl) {
  4788. if (vl === _CTRManager)
  4789. return true;
  4790. _CTRManager = {};
  4791. for (let name in vl)
  4792. if (typeof vl[name] !== 'function')
  4793. _CTRManager[name] = vl[name];
  4794. _CTRManager = nt.proxy(_CTRManager, 'CTRManager');
  4795. }
  4796. });
  4797. }, nullTools, selectiveEval),
  4798. click(e) {
  4799. let node = e.target;
  4800. if (node.nodeType === _Node.ELEMENT_NODE &&
  4801. node.style.position === 'absolute' &&
  4802. node.style.zIndex > 0)
  4803. node.parentNode.removeChild(node);
  4804. }
  4805. },
  4806.  
  4807. 'rp5.tld': {
  4808. now() {
  4809. Object.defineProperty(win, 'sContentBottom', {
  4810. set() {},
  4811. get() {
  4812. return '';
  4813. }
  4814. });
  4815. // skip timeout check for blocked requests
  4816. let _setTimeout = win.setTimeout;
  4817. win.setTimeout = function setTimeout(...args) {
  4818. let str = (typeof args[0] === 'string' ? args[0] : _toString(args[0]));
  4819. if (str.includes('xvb')) {
  4820. _console.log('Blocked setTimeout for:', str);
  4821. return;
  4822. }
  4823. return _setTimeout(...args);
  4824. };
  4825. },
  4826. dom() {
  4827. let node = selectNodeByTextContent('Разместить текстовое объявление', {
  4828. root: _de.querySelector('#content-wrapper'),
  4829. shallow: true
  4830. });
  4831. if (node)
  4832. node.style.display = 'none';
  4833. }
  4834. },
  4835.  
  4836. 'rsload.net': {
  4837. load() {
  4838. let dis = _document.querySelector('label[class*="cb-disable"]');
  4839. if (dis)
  4840. dis.click();
  4841. },
  4842. click(e) {
  4843. let t = e.target;
  4844. if (t && t.href && (/:\/\/\d+\.\d+\.\d+\.\d+\//.test(t.href)))
  4845. t.href = t.href.replace('://', '://rsload.net:rsload.net@');
  4846. }
  4847. },
  4848.  
  4849. 'rustorka.tld': {
  4850. other: [
  4851. 'rustorka.innal.top, rustorka2.innal.top, rustorka3.innal.top',
  4852. 'rustorka4.innal.top, rustorka5.innal.top, rustorka6.innal.top',
  4853. 'rustorka.naylo.top'
  4854. ].join(', '),
  4855. now: () => scriptLander(() => {
  4856. selectiveCookies('~default|(?!(PHPSESSID|__cfduid|announcements|bb_data|bb_t|id|opt_js|shout)$).*');
  4857. selectiveEval(/antiadblock/);
  4858. abortExecution.onGet('ads_script');
  4859. abortExecution.inlineScript('setTimeout', {
  4860. pattern: /("(\\x[0-9A-F]{2})+",\s?){4}/
  4861. });
  4862.  
  4863. const _doc_proto = ('cookie' in _Document) ? _Document : Object.getPrototypeOf(_document);
  4864. const _cookie = Object.getOwnPropertyDescriptor(_doc_proto, 'cookie');
  4865.  
  4866. if (_cookie && GM.info.scriptHandler) {
  4867. const asyncCookieCleaner = () => {
  4868. GM.cookie.list({
  4869. url: location.href
  4870. }).then(cookies => {
  4871. for (let cookie of (cookies || []))
  4872. if (cookie.name === cookie.value) {
  4873. GM.cookie.delete(cookie);
  4874. _console.log(`Removed cookie: ${cookie.name}=${cookie.value}`);
  4875. }
  4876. });
  4877. };
  4878. _cookie.get = new Proxy(_cookie.get, {
  4879. apply(fun, that, args) {
  4880. asyncCookieCleaner();
  4881. return _apply(fun, that, args);
  4882. }
  4883. });
  4884. _cookie.set = new Proxy(_cookie.set, {
  4885. apply(fun, that, args) {
  4886. _apply(fun, that, args);
  4887. asyncCookieCleaner();
  4888. return true;
  4889. }
  4890. });
  4891. Object.defineProperty(_doc_proto, 'cookie', _cookie);
  4892. }
  4893. }, selectiveCookies, abortExecution),
  4894. dom: () => _document.cookie.slice(0, 0)
  4895. },
  4896.  
  4897. 'rutracker.org': {
  4898. other: 'rutracker.lib, rutracker.net, rutracker.nl',
  4899. now: () => {
  4900. const ext = 'p-ext-link';
  4901. _Element.setAttribute = new Proxy(_Element.setAttribute, {
  4902. apply(fun, that, args) {
  4903. let [prop, classes] = args;
  4904. if (prop === 'class') {
  4905. let list = classes.split(/\s/);
  4906. if (list.includes(ext)) {
  4907. args[1] = list.filter(x => x !== ext).join(' ');
  4908. that.setAttribute('target', '_blank');
  4909. that.setAttribute('rel', 'noreferrer');
  4910. }
  4911. }
  4912. return _apply(fun, that, args);
  4913. }
  4914. });
  4915. }
  4916. },
  4917.  
  4918. 'rutube.ru': () => scriptLander(() => {
  4919. jsonFilter('creative', 'creative.id');
  4920. jsonFilter('interactives', 'interactives.0');
  4921. }, jsonFilter),
  4922.  
  4923. 'sdamgia.ru': () => {
  4924. selectiveCookies('bda|bltsr');
  4925. abortExecution.onGet('Object.prototype.getYa');
  4926. abortExecution.onGet('Object.prototype.initYa');
  4927. abortExecution.onGet('Object.prototype.initYaDirect');
  4928. },
  4929.  
  4930. 'shazoo.ru': () => {
  4931. const wash = node => node.tagName === 'BODY' && !node.removeAttribute('style');
  4932. if (!Array.prototype.some.call(_de.children, wash)) {
  4933. let o = new MutationObserver(() => wash(_de.children[_de.children.length - 1]) && o.disconnect());
  4934. o.observe(_de, {
  4935. childList: true
  4936. });
  4937. }
  4938. },
  4939.  
  4940. 'simpsonsua.tv': () => {
  4941. let _addEventListener = _Document.addEventListener;
  4942. _document.addEventListener = function (event, callback) {
  4943. if (event === 'DOMContentLoaded' && callback.toString().includes('show_warning'))
  4944. return;
  4945. return _addEventListener.apply(this, arguments);
  4946. };
  4947. nt.define('need_warning', 0);
  4948. nt.define('onYouTubeIframeAPIReady', nt.func(null, 'onYouTubeIframeAPIReady'));
  4949. },
  4950.  
  4951. 'smotret-anime-365.ru': () => scriptLander(() => {
  4952. deepWrapAPI(root => {
  4953. const _pause = _bindCall(root.Audio.prototype.pause);
  4954. const _addEventListener = _bindCall(root.Element.prototype.addEventListener);
  4955. let stopper = e => _pause(e.target);
  4956. root.Audio = exportFunction(new Proxy(root.Audio, {
  4957. construct(audio, args) {
  4958. let res = _construct(audio, args);
  4959. _addEventListener(res, 'play', stopper, true);
  4960. return res;
  4961. }
  4962. }), root);
  4963. const getTagName = _bindCall(Object.getOwnPropertyDescriptor(_Element, 'tagName').get);
  4964. root.Document.prototype.createElement = exportFunction(new Proxy(root.Document.prototype.createElement, {
  4965. apply(fun, that, args) {
  4966. let res = _apply(fun, that, args);
  4967. if (getTagName(res) === 'AUDIO')
  4968. _addEventListener(res, 'play', stopper, true);
  4969. return res;
  4970. }
  4971. }), root.Document.prototype);
  4972. });
  4973. }, deepWrapAPI),
  4974.  
  4975. 'smotrim.ru': () => createStyle('.dialog-wrapper { display: none !important }'),
  4976.  
  4977. 'spaces.ru': () => {
  4978. gardener('div:not(.f-c_fll) > a[href*="spaces.ru/?Cl="]', /./, {
  4979. parent: 'div'
  4980. });
  4981. gardener('.js-banner_rotator', /./, {
  4982. parent: '.widgets-group'
  4983. });
  4984. },
  4985.  
  4986. 'spam-club.blogspot.co.uk': () => {
  4987. let _clientHeight = Object.getOwnPropertyDescriptor(_Element, 'clientHeight'),
  4988. _clientWidth = Object.getOwnPropertyDescriptor(_Element, 'clientWidth');
  4989. let wrapGetter = (getter) => {
  4990. let _getter = getter;
  4991. return function () {
  4992. let _size = _getter.apply(this, arguments);
  4993. return _size ? _size : 1;
  4994. };
  4995. };
  4996. _clientHeight.get = wrapGetter(_clientHeight.get);
  4997. _clientWidth.get = wrapGetter(_clientWidth.get);
  4998. Object.defineProperty(_Element, 'clientHeight', _clientHeight);
  4999. Object.defineProperty(_Element, 'clientWidth', _clientWidth);
  5000. let _onload = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'onload'),
  5001. _set_onload = _onload.set;
  5002. _onload.set = function () {
  5003. if (this instanceof HTMLImageElement)
  5004. return true;
  5005. _set_onload.apply(this, arguments);
  5006. };
  5007. Object.defineProperty(HTMLElement.prototype, 'onload', _onload);
  5008. },
  5009.  
  5010. 'sport-express.ru': () => gardener('.js-relap__item', />Реклама\s+<\//, {
  5011. root: '.container',
  5012. observe: true
  5013. }),
  5014.  
  5015. 'sports.ru': {
  5016. other: 'tribuna.com',
  5017. now() {
  5018. // extra functionality: shows/hides panel at the top depending on scroll direction
  5019. createStyle({
  5020. '.user-panel__fixed': {
  5021. transition: 'top 0.2s ease-in-out!important'
  5022. },
  5023. '.popup__overlay.feedback': {
  5024. display: 'none!important'
  5025. },
  5026. '.user-panel-up': {
  5027. top: '-40px!important'
  5028. },
  5029. '#branding-layout': {
  5030. margin_top: '100px!important'
  5031. }
  5032. }, {
  5033. id: 'fixes',
  5034. protect: false
  5035. });
  5036. scriptLander(() => {
  5037. yandexRavenStub();
  5038. webpackJsonpFilter(/AdBlockDetector|addBranding|loadPlista/);
  5039. }, nullTools, yandexRavenStub, webpackJsonpFilter);
  5040. },
  5041. dom() {
  5042. (function lookForPanel() {
  5043. let panel = _document.querySelector('.user-panel__fixed');
  5044. if (!panel)
  5045. setTimeout(lookForPanel, 100);
  5046. else
  5047. window.addEventListener(
  5048. 'wheel',
  5049. function (e) {
  5050. if (e.deltaY > 0 && !panel.classList.contains('user-panel-up'))
  5051. panel.classList.add('user-panel-up');
  5052. else if (e.deltaY < 0 && panel.classList.contains('user-panel-up'))
  5053. panel.classList.remove('user-panel-up');
  5054. }, false
  5055. );
  5056. })();
  5057. }
  5058. },
  5059.  
  5060. 'stealthz.ru': {
  5061. dom() {
  5062. // skip timeout
  5063. let $ = _document.querySelector.bind(_document);
  5064. let [timer_1, timer_2] = [$('#timer_1'), $('#timer_2')];
  5065. if (!timer_1 || !timer_2)
  5066. return;
  5067. timer_1.style.display = 'none';
  5068. timer_2.style.display = 'block';
  5069. }
  5070. },
  5071.  
  5072. 'tortuga.wtf': () => {
  5073. nt.define('Object.prototype.hideab', undefined);
  5074. },
  5075.  
  5076. 'tv.animebest.org': {
  5077. now() {
  5078. let _eval = win.eval;
  5079. win.eval = new win.Proxy(win.eval, {
  5080. apply(evl, ths, args) {
  5081. if (typeof args[0] === 'string' &&
  5082. args[0].includes("'VASTP'")) {
  5083. args[0] = args[0].replace("'VASTP'", "''");
  5084. win.eval = _eval;
  5085. }
  5086. return Reflect.apply(evl, ths, args);
  5087. }
  5088. });
  5089. }
  5090. },
  5091.  
  5092. 'tv-kanali.online': () => {
  5093. win.setTimeout = new Proxy(win.setTimeout, {
  5094. apply(fun, that, args) {
  5095. if (args[0].name && args[0].name.includes('doAd'))
  5096. return;
  5097. if (args[1] === 30000) args[1] = 100;
  5098. return _apply(fun, that, args);
  5099. }
  5100. });
  5101. },
  5102.  
  5103. 'video.khl.ru': () => {
  5104. let props = new Set(['detectBlockers', 'detectBlockersByLink', 'detectBlockersByElement']);
  5105. win.Object.defineProperty = new Proxy(win.Object.defineProperty, {
  5106. apply(def, that, args) {
  5107. if (props.has(args[1])) {
  5108. args[2] = {
  5109. key: args[1],
  5110. value() {
  5111. _console.log(`Skipped ${args[1]} call.`);
  5112. }
  5113. };
  5114. _console.log(`Replaced method ${args[1]}.`);
  5115. }
  5116. return Reflect.apply(def, that, args);
  5117. }
  5118. });
  5119. },
  5120.  
  5121. 'websdr.space': () => {
  5122. // scripts_base.min.js localDetect(): lol. seriously, lol
  5123. win.setTimeout = new Proxy(win.setTimeout, {
  5124. apply(fun, that, args) {
  5125. let [callback, timeout] = args;
  5126. if (timeout > 1000 && typeof callback === 'function' && _toString(callback).includes('5e3')) {
  5127. _console.log('skip', callback);
  5128. return;
  5129. }
  5130. return _apply(fun, that, args);
  5131. }
  5132. });
  5133. },
  5134.  
  5135. 'xatab-repack.net': {
  5136. other: 'rg-mechanics.org',
  5137. now() {
  5138. abortExecution.onSet('blocked');
  5139. }
  5140. },
  5141.  
  5142. 'xittv.net': () => scriptLander(() => {
  5143. let logNames = ['setup', 'trigger', 'on', 'off', 'onReady', 'onError', 'getConfig', 'addPlugin', 'getAdBlock'];
  5144. let skipEvents = ['adComplete', 'adSkipped', 'adBlock', 'adRequest', 'adMeta', 'adImpression', 'adError', 'adTime', 'adStarted', 'adClick'];
  5145. let _jwplayer;
  5146. Object.defineProperty(win, 'jwplayer', {
  5147. get() {
  5148. return _jwplayer;
  5149. },
  5150. set(x) {
  5151. _jwplayer = new Proxy(x, {
  5152. apply(fun, that, args) {
  5153. let res = fun.apply(that, args);
  5154. res = new Proxy(res, {
  5155. get(obj, name) {
  5156. if (logNames.includes(name) && typeof obj[name] === 'function')
  5157. return new Proxy(obj[name], {
  5158. apply(fun, that, args) {
  5159. if (name === 'setup') {
  5160. let o = args[0];
  5161. if (o)
  5162. delete o.advertising;
  5163. }
  5164. if (name === 'on' || name === 'trigger') {
  5165. let events = typeof args[0] === 'string' ? args[0].split(" ") : null;
  5166. if (events.length === 1 && skipEvents.includes(events[0]))
  5167. return res;
  5168. if (events.length > 1) {
  5169. let names = [];
  5170. for (let event of events)
  5171. if (!skipEvents.includes(event))
  5172. names.push(event);
  5173. if (names.length > 0)
  5174. args[0] = names.join(" ");
  5175. else
  5176. return res;
  5177. }
  5178. }
  5179. let subres = fun.apply(that, args);
  5180. _console.trace(`jwplayer().${name}(`, ...args, `) >>`, res);
  5181. return subres;
  5182. }
  5183. });
  5184. return obj[name];
  5185. }
  5186. });
  5187. return res;
  5188. }
  5189. });
  5190. _console.log('jwplayer =', x);
  5191. }
  5192. });
  5193. }),
  5194.  
  5195. 'yandex.tld': {
  5196. other: 'yandexsport.tld',
  5197. now: () => {
  5198. // Generic Yandex Scripts
  5199. const mainScript = () => {
  5200. let nt = new nullTools({
  5201. log: false,
  5202. trace: true
  5203. });
  5204.  
  5205. let cookiefilter = '';
  5206. // ads on afisha.yandex.ru, however it looks like selectiveEval isn't perfect
  5207. // since eval could be called in scope to access properties of that scope and
  5208. // such calls with it active break functionality on metrika.yandex.ru
  5209. if (/(^|\.)afisha\./.test(location.hostname)) {
  5210. selectiveEval(/AdvManagerStatic/);
  5211. nt.define('Object.prototype._adbStyles', null);
  5212. nt.define('Object.prototype._adbClass', null);
  5213. cookiefilter += (cookiefilter.length ? '|' : '') + 'checkcookie';
  5214. }
  5215.  
  5216. selectiveCookies(cookiefilter);
  5217. // remove banner on the start page
  5218. let AwapsJsonAPI_Json = function (...args) {
  5219. _console.log('>> new AwapsJsonAPI.Json(', ...args, ')');
  5220. };
  5221. const cleaner = (_params, nodes) => {
  5222. try {
  5223. for (let i = 0; i < nodes.length; i++)
  5224. nodes[i].parentNode.parentNode.removeChild(nodes[i].parentNode);
  5225. _console.log(`Removed banner placeholder.`);
  5226. } catch (ignore) {
  5227. _console.log(`Can't locate placeholder to remove.`);
  5228. }
  5229. };
  5230. Object.assign(AwapsJsonAPI_Json.prototype, {
  5231. checkBannerVisibility: nt.func(true, 'AwapsJsonAPI.Json.checkBannerVisibility'),
  5232. autorefresh: nt.proxy(cleaner, 'AwapsJsonAPI.Json.prototype.autorefresh'),
  5233. addIframeContent: nt.proxy(cleaner, 'AwapsJsonAPI.Json.prototype.addIframeContent'),
  5234. getHTML: nt.func('', 'AwapsJsonAPI.Json.getHTML')
  5235. });
  5236. AwapsJsonAPI_Json.prototype = nt.proxy(AwapsJsonAPI_Json.prototype, 'AwapsJsonAPI.Json.prototype');
  5237. AwapsJsonAPI_Json = nt.proxy(AwapsJsonAPI_Json);
  5238. if ('AwapsJsonAPI' in win) {
  5239. _console.log('Oops! AwapsJsonAPI already defined.');
  5240. let f = win.AwapsJsonAPI.Json;
  5241. win.AwapsJsonAPI.Json = AwapsJsonAPI_Json;
  5242. if (f && f.prototype)
  5243. f.prototype = AwapsJsonAPI_Json.prototype;
  5244. } else
  5245. nt.define('AwapsJsonAPI', nt.proxy({
  5246. Json: AwapsJsonAPI_Json
  5247. }));
  5248.  
  5249. const parseExport = x => {
  5250. if (!x)
  5251. return x;
  5252. // remove banner placeholder
  5253. if (x.banner && x.banner.cls && x.banner.cls.banner__parent) {
  5254. let hide = pattern => {
  5255. for (let banner of _document.querySelectorAll(pattern)) {
  5256. _setAttribute(banner, 'style', 'display:none!important');
  5257. _console.log('Hid banner placeholder.');
  5258. }
  5259. };
  5260. let _parent = `.${x.banner.cls.banner__parent}`;
  5261. hide(_parent);
  5262. _document.addEventListener('DOMContentLoaded', () => hide(_parent), false);
  5263. }
  5264.  
  5265. // remove banner data and some other stuff
  5266. delete x.banner;
  5267. delete x.consistency;
  5268. delete x['i-bannerid'];
  5269. delete x['i-counter'];
  5270. delete x['promo-curtain'];
  5271.  
  5272. // remove parts of ga-counter (complete removal break "ТВ Онлайн")
  5273. if (x['ga-counter'] && x['ga-counter'].data) {
  5274. x['ga-counter'].data.id = 0;
  5275. delete x['ga-counter'].data.ether;
  5276. delete x['ga-counter'].data.iframeSrc;
  5277. delete x['ga-counter'].data.iframeSrcEx;
  5278. }
  5279.  
  5280. // remove adblock detector parameters and clean up detector cookie
  5281. if ('adb' in x) {
  5282. let cookie = x.adb.data ? x.adb.data.cookie : undefined;
  5283. if (cookie) {
  5284. selectiveCookies(cookie);
  5285. x.adb.data.adb = 0;
  5286. }
  5287. delete x.adb;
  5288. }
  5289.  
  5290. return x;
  5291. };
  5292. // Yandex banner on main page and some other things
  5293. let _home = win.home,
  5294. _home_set = !!_home;
  5295. Object.defineProperty(win, 'home', {
  5296. get() {
  5297. return _home;
  5298. },
  5299. set(vl) {
  5300. if (!_home_set && vl === _home)
  5301. return;
  5302. _home_set = false;
  5303. _console.log('home =', vl);
  5304. let _home_export = parseExport(vl.export);
  5305. Object.defineProperty(vl, 'export', {
  5306. get() {
  5307. return _home_export;
  5308. },
  5309. set(vl) {
  5310. _home_export = parseExport(vl);
  5311. }
  5312. });
  5313. _home = vl;
  5314. }
  5315. });
  5316.  
  5317. // adblock circumvention on some Yandex domains
  5318. yandexRavenStub();
  5319.  
  5320. // news, sport, docviewer in emails and probably other places
  5321. abortExecution.onGet('yaads.adRenderedCount');
  5322. let AdvertPartner = nt.func(false, 'AdvertPartner');
  5323. nt.defineOn(AdvertPartner, 'defaultProps', {}, 'AdvertPartner.');
  5324. nt.defineOn(AdvertPartner, 'contextTypes', [], 'AdvertPartner.');
  5325. nt.define('Object.prototype.AdvertPartner', AdvertPartner);
  5326. // ads in videoplayer
  5327. nt.define('Object.prototype.useAbdBundle', false);
  5328.  
  5329. (path => { // code specific for certain paths on yandex
  5330. const paths = {
  5331. news: () => {
  5332. win.JSON.parse = new Proxy(win.JSON.parse, {
  5333. apply(fun, that, args) {
  5334. const res = _apply(fun, that, args);
  5335. //_console.log(res);
  5336. if ('content' in res) {
  5337. delete res.content.adTags;
  5338. delete res.content.adConfig;
  5339. }
  5340. if ('request_info' in res && res.request_info.with_ad_insertion)
  5341. res.request_info.with_ad_insertion = false;
  5342. delete res.rtb;
  5343. delete res.seatbid;
  5344. return res;
  5345. }
  5346. });
  5347. },
  5348. pogoda: () => createStyle(
  5349. 'div[class^="content "][data-bem] > .content__bottom ~ div[class*="card "],' +
  5350. '[class*="segment__container"] > div > [class^="card "][class*="_"],' +
  5351. '.b-statcounter + div[class] > div[id][class] { display: none !important }'
  5352. ),
  5353. '': () => {
  5354. // banner on the main page under search
  5355. createStyle([
  5356. '.widgets[role="main"] div[class] > div[class]:empty:first-child + div[class]:last-child' +
  5357. '{ border: none !important }',
  5358. 'div.media-grid__row[data-blockname="infinity_zen"] .feed__item > div[class^="card-wrapper"][class*=" "]:not([style])' +
  5359. '{ display: none !important }',
  5360. '.widgets[role="main"] a[href^="https://yastatic.net/www/_/"] ~ div, .widgets[role="main"] a[href^="https://yastatic.net/www/_/"]' +
  5361. '{ display: none !important }'
  5362. ]);
  5363. const getChildren = _bindCall(Object.getOwnPropertyDescriptor(_Element, 'children').get);
  5364. const HTMLElementDescriptors = Object.getOwnPropertyDescriptors(HTMLElement.prototype);
  5365. const [getOffsetParent, getOffsetHeight, getOffsetLeft, getOffsetTop] = [
  5366. 'offsetParent', 'offsetHeight', 'offsetLeft', 'offsetTop'
  5367. ].map(prop => _bindCall(HTMLElementDescriptors[prop].get));
  5368. const isParent = _bindCall(_Node.contains);
  5369. const getComputedStyle = win.getComputedStyle;
  5370. const getElementsFromPoint = _document.elementsFromPoint.bind(_document);
  5371. const getElementPoint = (e, x = 0, y = 0) => {
  5372. while (e) {
  5373. x += getOffsetLeft(e);
  5374. y += getOffsetTop(e);
  5375. e = getOffsetParent(e);
  5376. }
  5377. return {
  5378. x,
  5379. y
  5380. };
  5381. };
  5382. const getRoot = (() => {
  5383. let root;
  5384. return () => root || (root = _querySelector('.widgets[role="main"]'));
  5385. })();
  5386.  
  5387. function hideAtPoint(node) {
  5388. const arrow = _querySelector('.home-arrow, form[role="search"]');
  5389. if (!arrow) {
  5390. _console.log('Unable to locate search field.');
  5391. return;
  5392. }
  5393. const [pt, at] = [getElementPoint(node), getElementPoint(arrow)];
  5394. const stack = getElementsFromPoint(
  5395. Math.max(pt.x, at.x + 16),
  5396. Math.max(pt.y, at.y + getOffsetHeight(arrow) + 16)
  5397. );
  5398. const root = getRoot();
  5399. stack.filter(n => !isParent(n, arrow) && root.contains(n)).forEach(
  5400. e => e.setAttribute('style', 'display:none!important')
  5401. );
  5402. }
  5403.  
  5404. const processed = new WeakSet();
  5405.  
  5406. function reWalker(root) {
  5407. for (let node of getChildren(root)) {
  5408. let bg = getComputedStyle(node).backgroundImage;
  5409. if (bg && bg.includes('/yastatic.net/www/_/') && !processed.has(node)) {
  5410. _setAttribute(node, 'style', 'background-image:none!important');
  5411. hideAtPoint(node);
  5412. processed.add(node);
  5413. _console.log('Hid banner.');
  5414. }
  5415. reWalker(node);
  5416. }
  5417. }
  5418.  
  5419. (new MutationObserver(
  5420. ms => {
  5421. const root = getRoot();
  5422. if (!root) return;
  5423. for (let m of ms)
  5424. for (let node of m.addedNodes) {
  5425. if (!(node instanceof HTMLElement) || !root.contains(node))
  5426. continue;
  5427. reWalker(node);
  5428. }
  5429. }
  5430. )).observe(_de, {
  5431. childList: true,
  5432. subtree: true
  5433. });
  5434.  
  5435. win.addEventListener('DOMContentLoaded', () => reWalker(getRoot()));
  5436. }
  5437. };
  5438. if (paths[path]) paths[path]();
  5439. if (path === 'news' || path === 'sport') {
  5440. createStyle(
  5441. 'div[class*="-header"] ~ div[id*="page"] > div > div[class*="__"] + div[class] > [class*="__row"] > div[class*="__col"]:last-child,' +
  5442. '.news-top-rubric-heading > span:only-child, div[class*="sport-app__advert"], div[class*="_banger"] { display: none !important }'
  5443. );
  5444. gardener('div[class*="__col"] > div[class*="-feed__"][class*="_type_"] > div[class*="loader"]', /./, {
  5445. root: 'div[class*="-header"] ~ div[id*="page"]',
  5446. parent: 'div[class*="-header"] ~ div[id*="page"] div[class*="__col"]',
  5447. observe: true,
  5448. hide: true
  5449. });
  5450. }
  5451. })(location.pathname.slice(1, (x => x < 0 ? undefined : x)(location.pathname.indexOf('/', 1))).toLowerCase());
  5452.  
  5453. // abp detector cookie on yandex pogoda and afisha
  5454. win.Element.prototype.getAttribute = new Proxy(win.Element.prototype.getAttribute, {
  5455. apply(get, el, args) {
  5456. let res = _apply(get, el, args);
  5457. if (res && res.length > 20 && el instanceof HTMLBodyElement)
  5458. try {
  5459. let o = JSON.parse(res),
  5460. found = false,
  5461. check;
  5462. for (let prop in o) {
  5463. check = 'param' in o[prop] || 'aabCookieName' in o[prop];
  5464. if (check || 'banners' in o[prop]) {
  5465. found = true;
  5466. if (check)
  5467. selectiveCookies(o[prop].param || o[prop].aabCookieName);
  5468. _console.log(el.tagName, o, 'removed', o[prop]);
  5469. delete o[prop];
  5470. }
  5471. }
  5472. if (!found) _console.log(el.tagName, o);
  5473. res = JSON.stringify(o);
  5474. } catch (ignore) {}
  5475. return res;
  5476. }
  5477. });
  5478. };
  5479. scriptLander(mainScript, nullTools, yandexRavenStub, abortExecution, selectiveCookies, selectiveEval);
  5480.  
  5481. if ('attachShadow' in _Element) try {
  5482. let fakeRoot = () => ({
  5483. firstChild: null,
  5484. appendChild() {
  5485. return null;
  5486. },
  5487. querySelector() {
  5488. return null;
  5489. },
  5490. querySelectorAll() {
  5491. return null;
  5492. }
  5493. });
  5494. _Element.createShadowRoot = fakeRoot;
  5495. let shadows = new WeakMap();
  5496. let _attachShadow = Object.getOwnPropertyDescriptor(_Element, 'attachShadow');
  5497. _attachShadow.value = function () {
  5498. return shadows.set(this, fakeRoot()).get(this);
  5499. };
  5500. Object.defineProperty(_Element, 'attachShadow', _attachShadow);
  5501. let _shadowRoot = Object.getOwnPropertyDescriptor(_Element, 'shadowRoot');
  5502. _shadowRoot.set = () => null;
  5503. _shadowRoot.get = function () {
  5504. return shadows.has(this) ? shadows.get(this) : undefined;
  5505. };
  5506. Object.defineProperty(_Element, 'shadowRoot', _shadowRoot);
  5507. } catch (e) {
  5508. _console.warn('Unable to wrap Element.prototype.attachShadow\n', e);
  5509. }
  5510.  
  5511. // Disable banner styleSheet (on main page)
  5512. document.addEventListener('DOMContentLoaded', () => {
  5513. for (let sheet of document.styleSheets)
  5514. try {
  5515. for (let rule of sheet.cssRules)
  5516. if (rule.cssText.includes(' 728px 90px')) {
  5517. rule.parentStyleSheet.disabled = true;
  5518. _console.log('Disabled banner styleSheet:', rule.parentStyleSheet);
  5519. }
  5520. } catch (ignore) {}
  5521. }, false);
  5522.  
  5523. // Subdomain-specific Yandex scripts
  5524. const subDomain = location.hostname.slice(0, location.hostname.indexOf('.'));
  5525.  
  5526. // Yandex Mail ads
  5527. if (subDomain === 'mail') {
  5528. let wrap = vl => {
  5529. if (!vl)
  5530. return vl;
  5531. _console.log('Daria =', vl);
  5532. nt.defineOn(vl, 'AdBlock', nt.proxy({
  5533. detect: nt.func(new Promise(() => null), 'Daria.AdBlock.detect'),
  5534. enabled: false
  5535. }), 'Daria.');
  5536. nt.defineOn(vl, 'AdvPresenter', nt.proxy({
  5537. _config: nt.proxy({
  5538. banner: false,
  5539. done: false,
  5540. line: false
  5541. })
  5542. }), 'Daria.');
  5543. if (vl.Config) {
  5544. delete vl.Config.adBlockDetector;
  5545. delete vl.Config['adv-url'];
  5546. delete vl.Config.cryprox;
  5547. if (vl.Config.features) {
  5548. delete vl.Config.features.web_adloader_with_cookie_cache;
  5549. delete vl.Config.features.web_ads;
  5550. delete vl.Config.features.web_ads_mute;
  5551. }
  5552. vl.Config.mayHaveAdv = false;
  5553. }
  5554. return vl;
  5555. };
  5556. let _Daria = wrap(win.Daria);
  5557. if (_Daria)
  5558. _console.log('Wrapped already existing object "Daria".');
  5559. Object.defineProperty(win, 'Daria', {
  5560. get() {
  5561. return _Daria;
  5562. },
  5563. set(vl) {
  5564. if (vl === _Daria)
  5565. return;
  5566. _Daria = wrap(vl);
  5567. }
  5568. });
  5569. // Buttons to pay to disable ads
  5570. createStyle('.ns-view-mail-pro-left-column-button, .PSHeader-Pro { display: none !important }');
  5571. }
  5572.  
  5573. // Detector and ads on Yandex Music
  5574. if (subDomain === 'music') {
  5575. nt.define('tryPay', nt.func(null, 'tryPay'));
  5576. nt.define('Object.prototype.initMegabannerAPI', nt.func(null, 'initMegabannerAPI'));
  5577. nt.define('Object.prototype.mediaAd', undefined);
  5578. nt.define('Object.prototype.detect', () => new Promise(() => null));
  5579. nt.define('Object.prototype.loadContext', () => new Promise(r => r()));
  5580. nt.define('Object.prototype.antiAdbSetup', nt.func(null, 'ya.music.antiAdbSetup'));
  5581. }
  5582.  
  5583. const isSearch = /^\/(yand)?search[/?]/i.test(location.pathname);
  5584. if (['mail', 'music', 'tv', 'yandexsport'].includes(subDomain) || isSearch) {
  5585. // prevent/defuse adblock detector and cleanup localStorage
  5586. for (let name in localStorage)
  5587. if (name.startsWith('videoplayer-ad-session-') || ['ic', 'yu', 'ludca', 'test'].includes(name))
  5588. localStorage.removeItem(name);
  5589.  
  5590. const mapsmb = Symbol('lsOverrideMap');
  5591. let lsOverride = {
  5592. // generic
  5593. '_mt__data': '',
  5594. 'yandexJSPlayerApiSavedSingleVideoSessionWatchedTimeSinceAd': Math.floor(Math.random() * 1000000) / 1000,
  5595. 'yandexJSPlayerApiSavedSingleVideoSessionWatchedTimeSinceAdUtcTimestamp': true,
  5596. // Yandex Music specific
  5597. 'playerBrandingType': null,
  5598. 'paywall-onloaded': true,
  5599. 'gdpr-welcome': true
  5600. };
  5601. lsOverride[mapsmb] = new Map();
  5602. lsOverride = new Proxy(lsOverride, {
  5603. get(that, name) {
  5604. if (name === mapsmb)
  5605. return that[name];
  5606. if (name === 'yandexJSPlayerApiSavedSingleVideoSessionWatchedTimeSinceAdUtcTimestamp')
  5607. return String((new Date()).getTime() / 1000 - that.yandexJSPlayerApiSavedSingleVideoSessionWatchedTimeSinceAd).slice(1, 14);
  5608. if (name === 'paywall-onloaded' || name === 'gdpr-welcome') {
  5609. let val = that[mapsmb].get(name);
  5610. if (!val) return val;
  5611. let obj = JSON.parse(val);
  5612. let dt = Math.floor((new Date()).getTime() + Math.random() * 3 * 86400000 + 2 * 86400000);
  5613. for (let itm in obj)
  5614. obj[itm] = dt;
  5615. return JSON.stringify(obj);
  5616. }
  5617. return String(that[name]);
  5618. },
  5619. set(that, name, val) {
  5620. that[mapsmb].set(name, val);
  5621. return true;
  5622. }
  5623. });
  5624. const _getItem = win.localStorage.getItem;
  5625. const _localStorage = new Proxy(win.localStorage, {
  5626. get(that, name) {
  5627. if (name === 'getItem') {
  5628. return (it) => {
  5629. //console.log('get', it, _getItem.call(that, it));
  5630. if (it in lsOverride) {
  5631. lsOverride[it] = _getItem.call(that, it);
  5632. //console.log('override', lsOverride[it]);
  5633. return lsOverride[it];
  5634. }
  5635. return _getItem.call(that, it);
  5636. };
  5637. }
  5638. if (name === 'removeItem' || name === 'setItem')
  5639. return that[name].bind(that);
  5640. if (name in lsOverride) {
  5641. lsOverride[name] = _localStorage[name];
  5642. _localStorage[name] = lsOverride[name];
  5643. return lsOverride[name];
  5644. }
  5645. return that[name];
  5646. }
  5647. });
  5648. Object.defineProperty(win, 'localStorage', {
  5649. enumerable: true,
  5650. configurable: true,
  5651. get: new Proxy(function localStorage() {
  5652. return _localStorage;
  5653. }, {})
  5654. });
  5655.  
  5656. // cookie cleaner
  5657. let yp_keepCookieParts = /\.(sp|ygo|ygu|fblkv2)\./;
  5658. // ygo = city id; ygu = detect city automatically; fblkv2 = visible sections on the main page under search
  5659. let _doc_proto = ('cookie' in _Document) ? _Document : Object.getPrototypeOf(_document);
  5660. let _cookie = Object.getOwnPropertyDescriptor(_doc_proto, 'cookie');
  5661. if (_cookie) {
  5662. let _set_cookie = _bindCall(_cookie.set);
  5663. _cookie.set = function (value) {
  5664. if (/^(mda=|yp=|ys=|yabs-|__|bltsr=)/.test(value))
  5665. // remove value, set expired
  5666. if (!value.startsWith('yp=')) {
  5667. value = value.replace(/^([^=]+=)[^;]+/, '$1').replace(/(expires=)[\w\s\d,]+/, '$1Thu, 01 Jan 1970 00');
  5668. _console.trace('expire cookie', value.match(/^[^=]+/)[0]);
  5669. } else {
  5670. let parts = value.split(';');
  5671. let values = parts[0].split('#').filter(part => yp_keepCookieParts.test(part));
  5672. if (values.length)
  5673. values[0] = values[0].replace(/^yp=/, '');
  5674. let res = `yp=${values.join('#')}`;
  5675. _console.trace(`set cookie ${res}, dropped ${parts[0].replace(res,'')}`);
  5676. parts[0] = res;
  5677. value = parts.join(';');
  5678. }
  5679. return _set_cookie(this, value);
  5680. };
  5681. Object.defineProperty(_doc_proto, 'cookie', _cookie);
  5682. }
  5683. }
  5684. },
  5685. dom: () => {
  5686. { // Partially based on https://greasyfork.org/en/scripts/22737-remove-yandex-redirect
  5687. let count = 0,
  5688. lock = false;
  5689. const log = () => {
  5690. count++;
  5691. if (lock)
  5692. return;
  5693. setTimeout(() => {
  5694. _console.log('Removed tracking attributes from', count, 'links.');
  5695. count = 0;
  5696. lock = false;
  5697. }, 3333);
  5698. lock = true;
  5699. };
  5700. const selectors = (
  5701. 'A[onmousedown*="/jsredir"],' +
  5702. 'A[data-log-node],' +
  5703. 'A[data-vdir-href],' +
  5704. 'A[data-counter]'
  5705. );
  5706. const removeTrackingAttributes = (link) => {
  5707. _removeAttribute(link, 'onmousedown');
  5708. _removeAttribute(link, 'data-log-node');
  5709. // data-vdir-href
  5710. _removeAttribute(link, 'data-vdir-href');
  5711. _removeAttribute(link, 'data-orig-href');
  5712. // data-counter
  5713. _removeAttribute(link, 'data-counter');
  5714. _removeAttribute(link, 'data-bem');
  5715. log();
  5716. };
  5717. const removeTracking = (scope) => {
  5718. if (scope instanceof Element)
  5719. for (let link of scope.querySelectorAll(selectors))
  5720. removeTrackingAttributes(link);
  5721. };
  5722.  
  5723. removeTracking(_document);
  5724. (new MutationObserver(
  5725. function (ms) {
  5726. let m, node;
  5727. for (m of ms)
  5728. for (node of m.addedNodes)
  5729. if (node instanceof HTMLAnchorElement && node.matches(selectors))
  5730. removeTrackingAttributes(node);
  5731. else
  5732. removeTracking(node);
  5733. }
  5734. )).observe(_de, {
  5735. childList: true,
  5736. subtree: true
  5737. });
  5738. }
  5739.  
  5740. // Subdomain-specific Yandex scripts
  5741. const subDomain = location.hostname.slice(0, location.hostname.indexOf('.'));
  5742.  
  5743. // Function to attach an observer to monitor dynamic changes on the page
  5744. const pageUpdateObserver = (func, obj, params) => {
  5745. if (obj)
  5746. (new MutationObserver(func))
  5747. .observe(obj, (params || {
  5748. childList: true,
  5749. subtree: true
  5750. }));
  5751. };
  5752. // Short name for parentNode.removeChild
  5753. const remove = node => {
  5754. if (!node || !node.parentNode)
  5755. return false;
  5756. _console.log('Removed node.');
  5757. node.parentNode.removeChild(node);
  5758. };
  5759. // Short name for setAttribute style to display:none
  5760. const hide = node => {
  5761. if (!node)
  5762. return false;
  5763. _console.log('Hid node.');
  5764. _setAttribute(node, 'style', 'display:none!important');
  5765. };
  5766.  
  5767. if (subDomain === 'music') {
  5768. const removeMusicAds = () => {
  5769. for (let node of _querySelectorAll('.ads-block'))
  5770. remove(node);
  5771. };
  5772. pageUpdateObserver(removeMusicAds, _querySelector('.sidebar'));
  5773. removeMusicAds();
  5774. }
  5775.  
  5776. if (subDomain === 'tv') {
  5777. const removeTVAds = () => {
  5778. const yadWord = /Яндекс.Директ/i;
  5779. for (let node of _querySelectorAll('div[class^="_"][data-reactid] > div'))
  5780. if (yadWord.test(node.textContent) || node.querySelector('iframe:not([src])')) {
  5781. if (node.offsetWidth) {
  5782. let pad = _document.createElement('div');
  5783. _setAttribute(pad, 'style', `width:${node.offsetWidth}px`);
  5784. node.parentNode.appendChild(pad);
  5785. }
  5786. remove(node);
  5787. }
  5788. };
  5789. pageUpdateObserver(removeTVAds, _document.body);
  5790. removeTVAds();
  5791. }
  5792.  
  5793. const isSearch = /^\/(yand)?search[/?]/i.test(location.pathname);
  5794. if (isSearch) {
  5795. const removeSearchAds = () => {
  5796. const adWords = /Реклама|Ad/i;
  5797. for (let node of _querySelectorAll('.serp-item'))
  5798. if (_getAttribute(node, 'role') === 'complementary' ||
  5799. adWords.test((node.querySelector('.label') || {}).textContent))
  5800. hide(node);
  5801. };
  5802. pageUpdateObserver(removeSearchAds, _querySelector('.main__content'));
  5803. removeSearchAds();
  5804. }
  5805.  
  5806. if (['mail', 'music', 'tv', 'yandexsport'].includes(subDomain) || isSearch) {
  5807. // Generic ads removal and fixes
  5808. for (let node of _querySelectorAll('.serp-header'))
  5809. node.style.marginTop = '0';
  5810. for (let node of _querySelectorAll(
  5811. '.serp-adv__head + .serp-item,' +
  5812. '#adbanner,' +
  5813. '.serp-adv,' +
  5814. '.b-spec-adv,' +
  5815. 'div[class*="serp-adv__"]:not(.serp-adv__found):not(.serp-adv__displayed)'
  5816. )) remove(node);
  5817. }
  5818. }
  5819. },
  5820.  
  5821. 'yap.ru': {
  5822. other: 'yaplakal.com',
  5823. now() {
  5824. gardener('form > table[id^="p_row_"]:nth-of-type(2)', /member1438|Administration/);
  5825. gardener('.icon-comments', /member1438|Administration|\/go\/\?http/, {
  5826. parent: 'tr',
  5827. siblings: -2
  5828. });
  5829. }
  5830. },
  5831.  
  5832. 'yapx.ru': () => scriptLander(() => {
  5833. selectiveCookies('adblock_state|adblock_views');
  5834. nt.define('blockAdBlock', {
  5835. on: nt.func(nt.proxy({}, 'blockAdBlock.on', nt.NULL), 'blockAdBlock.on'),
  5836. check: nt.func(null, 'blockAdBlock.check')
  5837. });
  5838. }, selectiveCookies, nullTools),
  5839.  
  5840. 'youtube.com': () => scriptLander(() => {
  5841. jsonFilter('playerResponse.adPlacements playerResponse.playerAds adPlacements playerAds');
  5842. }, jsonFilter),
  5843.  
  5844. 'znanija.com': () => scriptLander(() => {
  5845. localStorage.clear();
  5846. }, abortExecution)
  5847. };
  5848.  
  5849. // replace '.tld' in domain names, add alternative domain names if present and wrap functions into objects
  5850. {
  5851. const parts = _document.domain.split('.');
  5852. const tld = /\.tld$/;
  5853. const tldSubstitur = (() => {
  5854. // stores TLD of current domain (simplistic TLD implementation)
  5855. const last = parts.length - 1;
  5856. const tld = ['', parts[last]];
  5857. const secondLevel = [
  5858. 'biz', 'com', 'edu', 'gov', 'info', 'int', 'mil', 'net', 'org', 'pro'
  5859. ];
  5860. // add second from the end part of domain name as part of the TLD substitutor
  5861. // when domain name consists of more than 2 parts and it looks like a part of TLD
  5862. if ((parts[0] !== 'www' && parts.length > 2 || parts.length > 3) &&
  5863. (parts[last - 1].length < 3 || secondLevel.includes(parts[last - 1])))
  5864. tld.splice(0, 1, parts[last - 1]);
  5865. return tld.join('.');
  5866. })();
  5867. for (let name in scripts) {
  5868. if (typeof scripts[name] === 'function')
  5869. scripts[name] = {
  5870. now: scripts[name]
  5871. };
  5872. if (name.endsWith('.tld'))
  5873. scripts[name.replace(tld, tldSubstitur)] = scripts[name];
  5874. for (let domain of (scripts[name].other && scripts[name].other.split(/,\s*/) || [])) {
  5875. domain = domain.replace(tld, tldSubstitur);
  5876. if (domain in scripts)
  5877. _console.log('Error in scripts list. Script for', name, 'replaced script for', domain);
  5878. scripts[domain] = scripts[name];
  5879. }
  5880. delete scripts[name].other;
  5881. }
  5882. // scripts lookup
  5883. const windowEvents = ['load', 'unload', 'beforeunload'];
  5884. let domain;
  5885. while (parts.length > 1) {
  5886. domain = parts.join('.');
  5887. if (domain in scripts) {
  5888. for (let when in scripts[domain]) {
  5889. let script = scripts[domain][when];
  5890. if (when === 'now')
  5891. script();
  5892. else if (when === 'dom')
  5893. _document.addEventListener('DOMContentLoaded', script);
  5894. else if (windowEvents.includes(when))
  5895. win.addEventListener(when, scripts[domain][when]);
  5896. else
  5897. _document.addEventListener(when, scripts[domain][when]);
  5898. }
  5899. }
  5900. parts.shift();
  5901. }
  5902. }
  5903.  
  5904. // Batch script lander
  5905. if (!skipLander)
  5906. landScript(batchLand, batchPrepend);
  5907.  
  5908. { // JS Fixes Tools Menu
  5909. const incompatibleScriptHandler = !/^(Tamper|Violent)monkey$/.test(GM.info.scriptHandler) || GM.info.scriptHandler === 'Violentmonkey' && isFirefox;
  5910. // Debug function, lists all unusual window properties
  5911. const isNativeFunction = /^[^{]*\{[\s\r\n]*\[native\scode\][\s\r\n]*\}$/;
  5912. const getStrangeObjectsList = () => {
  5913. _console.group('Window strangers list');
  5914. const _skip = 'frames/self/window/webkitStorageInfo'.split('/');
  5915. for (let n of Object.getOwnPropertyNames(win))
  5916. try {
  5917. let val = win[n];
  5918. if (val && !_skip.includes(n) && (win !== window && val !== window[n] || win === window) &&
  5919. (typeof val !== 'function' || typeof val === 'function' && !isNativeFunction.test(_toString(val))))
  5920. _console.log(`${n} =`, val);
  5921. } catch (e) {
  5922. _console.log(n, 'returns error on read', e);
  5923. }
  5924. _console.groupEnd('Window strangers list');
  5925. };
  5926.  
  5927. const lines = {
  5928. linked: [],
  5929. MenuOptions: {
  5930. eng: 'Options',
  5931. rus: 'Настройки'
  5932. },
  5933. MenuCompatibilityWarning: {
  5934. eng: 'is not supported',
  5935. rus: 'не поддерживается'
  5936. },
  5937. langs: {
  5938. eng: 'English',
  5939. rus: 'Русский'
  5940. },
  5941. sObjBtn: {
  5942. eng: 'List unusual “window” properties in console',
  5943. rus: 'Вывести в консоль нестандартные свойства «window»'
  5944. },
  5945. HeaderTools: {
  5946. eng: 'Tools',
  5947. rus: 'Инструменты'
  5948. },
  5949. HeaderOptions: {
  5950. eng: 'Options',
  5951. rus: 'Настройки'
  5952. },
  5953. AccessStatisticsLabel: {
  5954. eng: 'Display stubs access statistics and JSON filter',
  5955. rus: 'Выводить статистику запросов к заглушкам и JSON фильтра'
  5956. },
  5957. AbortExecutionStatisticsLabel: {
  5958. eng: 'Display abort execution statistics',
  5959. rus: 'Выводить статистику прерывания исполнения скриптов'
  5960. },
  5961. LogAttachedCSSLabel: {
  5962. eng: 'Log CSS attached to a page',
  5963. rus: 'Журналировать CSS добавленные на страницу'
  5964. },
  5965. BlockNotificationPermissionRequestsLabel: {
  5966. eng: 'Block requests to Show Notifications on sites',
  5967. rus: 'Блокировать запросы Показывать Уведомления на сайтах'
  5968. },
  5969. ShowScriptHandlerCompatibilityWarningLabel: {
  5970. eng: 'Show compatibility warning in menu next to Options',
  5971. rus: 'Отображать предупреждение о совместимости в меню рядом с Настройками'
  5972. },
  5973. DisableTMContextMenuLabel: {
  5974. eng: [
  5975. 'To hide “Tampermonkey” in context menu please open “Dashboard” from extension\'s menu,',
  5976. 'select “Settings” tab, switch “Config mode” to “Advanced”, navigate to “Context Menu” section',
  5977. 'and disable “Userscript menu commands” option. Install Tampermonkey BETA if you can\'t locate',
  5978. '“Context Menu” section or wait for the next stable release. Additionally, make sure “Inject mode”',
  5979. 'in “Experimental” section is set to “Instant”.'
  5980. ].join(' '),
  5981. rus: [
  5982. 'Для скрытия "Tampermonkey" в контекстном меню пожалуйста откройте «Панель упарвления» из меню расширения,',
  5983. 'перейдите на вкладку «Настройки», переключите «Режим конфигурации» на «Продвинутый», найдите секцию «Context Menu»',
  5984. 'и выключите опцию «Userscript menu commands». Установите Tampermonkey BETA если не можете найти секцию',
  5985. '«Context Menu» или подождите следующей стабильной версии. Также убедитесь, что «Режим встраивания» в секции',
  5986. '«Экспериментально» установлен на «Мгновенный».',
  5987. ].join(' ')
  5988. },
  5989. reg(el, name) {
  5990. this[name].link = el;
  5991. this.linked.push(name);
  5992. },
  5993. setLang(lang = 'eng') {
  5994. for (let name of this.linked) {
  5995. const el = this[name].link;
  5996. const label = this[name][lang];
  5997. el.textContent = label;
  5998. }
  5999. this.langs.link.value = lang;
  6000. jsf.Lang = lang;
  6001. }
  6002. };
  6003.  
  6004. const _createTextNode = _Document.createTextNode.bind(_document);
  6005. const createOptionsWindow = () => {
  6006. const root = _createElement('div'),
  6007. shadow = _attachShadow ? _attachShadow(root, {
  6008. mode: 'closed'
  6009. }) : root,
  6010. overlay = _createElement('div'),
  6011. inner = _createElement('div');
  6012.  
  6013. overlay.id = 'overlay';
  6014. overlay.appendChild(inner);
  6015. shadow.appendChild(overlay);
  6016.  
  6017. inner.id = 'inner';
  6018. inner.br = function appendBreakLine() {
  6019. return this.appendChild(_createElement('br'));
  6020. };
  6021.  
  6022. createStyle({
  6023. 'h2': {
  6024. margin_top: 0
  6025. },
  6026. 'h2, h3': {
  6027. margin_block_end: '0.5em'
  6028. },
  6029. 'div, button, select, input': {
  6030. font_family: 'Helvetica, Arial, sans-serif',
  6031. font_size: '12pt'
  6032. },
  6033. 'button': {
  6034. background: 'linear-gradient(to bottom, #f0f0f0 5%, #c0c0c0 100%)',
  6035. border_radius: '3px',
  6036. border: '1px solid #a1a1a1',
  6037. color: '#000000',
  6038. text_shadow: '0px 1px 0px #d4d4d4'
  6039. },
  6040. 'button:hover': {
  6041. background: 'linear-gradient(to bottom, #c0c0c0 5%, #f0f0f0 100%)'
  6042. },
  6043. 'button:active': {
  6044. position: 'relative',
  6045. top: '1px'
  6046. },
  6047. 'select': {
  6048. border: '1px solid darkgrey',
  6049. border_radius: '0px 0px 5px 5px',
  6050. border_top: '0px'
  6051. },
  6052. 'button:focus, select:focus': {
  6053. outline: 'none'
  6054. },
  6055. '#overlay': {
  6056. position: 'fixed',
  6057. top: 0,
  6058. left: 0,
  6059. bottom: 0,
  6060. right: 0,
  6061. background: 'rgba(0,0,0,0.65)',
  6062. z_index: 2147483647 // Highest z-index: Math.pow(2, 31) - 1
  6063. },
  6064. '#inner': {
  6065. background: 'whitesmoke',
  6066. color: 'black',
  6067. padding: '1.5em 1em 1.5em 1em',
  6068. max_width: '65ch',
  6069. position: 'absolute',
  6070. top: '50%',
  6071. left: '50%',
  6072. transform: 'translate(-50%, -50%)',
  6073. border: '1px solid darkgrey',
  6074. border_radius: '5px'
  6075. },
  6076. '#closeOptionsButton': {
  6077. float: 'right',
  6078. transform: 'translate(1em, -1.5em)',
  6079. border: 0,
  6080. border_radius: 0,
  6081. background: 'none',
  6082. box_shadow: 'none'
  6083. },
  6084. '#selectLang': {
  6085. float: 'right',
  6086. transform: 'translate(0, -1.5em)'
  6087. },
  6088. '.optionsLabel': {
  6089. padding_left: '1.5em',
  6090. text_indent: '-1em',
  6091. display: 'block'
  6092. },
  6093. '.optionsCheckbox': {
  6094. left: '-0.25em',
  6095. width: '1em',
  6096. height: '1em',
  6097. padding: 0,
  6098. margin: 0,
  6099. position: 'relative',
  6100. vertical_align: 'middle'
  6101. },
  6102. '@media (prefers-color-scheme: dark)': {
  6103. '#inner': {
  6104. background_color: '#292a2d',
  6105. color: 'white',
  6106. border: '1px solid #1a1b1e'
  6107. },
  6108. 'input': {
  6109. filter: 'invert(100%)'
  6110. },
  6111. 'button': {
  6112. background: 'linear-gradient(to bottom, #575757 5%, #303030 100%)',
  6113. border_color: '#575757',
  6114. color: '#f0f0f0',
  6115. text_shadow: '0px 1px 0px #171717'
  6116. },
  6117. 'button:hover': {
  6118. background: 'linear-gradient(to bottom, #303030 5%, #575757 100%)'
  6119. },
  6120. 'select': {
  6121. background_color: '#303030',
  6122. color: '#f0f0f0',
  6123. border: '1px solid #1a1b1e',
  6124. border_radius: '0px 0px 5px 5px',
  6125. border_top: '0px'
  6126. },
  6127. '#overlay': {
  6128. background: 'rgba(0,0,0,.85)',
  6129. }
  6130. }
  6131. }, {
  6132. root: shadow,
  6133. protect: false
  6134. });
  6135.  
  6136. // components
  6137. function createCheckbox(name) {
  6138. const checkbox = _createElement('input'),
  6139. label = _createElement('label');
  6140. checkbox.type = 'checkbox';
  6141. checkbox.classList.add('optionsCheckbox');
  6142. checkbox.checked = jsf[name];
  6143. checkbox.onclick = e => {
  6144. jsf[name] = e.target.checked;
  6145. return true;
  6146. };
  6147. label.classList.add('optionsLabel');
  6148. label.appendChild(checkbox);
  6149. const text = _createTextNode('');
  6150. label.appendChild(text);
  6151. Object.defineProperty(label, 'textContent', {
  6152. set(title) {
  6153. text.textContent = title;
  6154. }
  6155. });
  6156. return label;
  6157. }
  6158.  
  6159. // language & close
  6160. const closeBtn = _createElement('button');
  6161. closeBtn.onclick = () => _removeChild(root);
  6162. closeBtn.textContent = '\u2715';
  6163. closeBtn.id = 'closeOptionsButton';
  6164. inner.appendChild(closeBtn);
  6165.  
  6166. overlay.addEventListener('click', e => {
  6167. if (e.target === overlay) {
  6168. _removeChild(root);
  6169. e.preventDefault();
  6170. }
  6171. e.stopPropagation();
  6172. }, false);
  6173.  
  6174. const selectLang = _createElement('select');
  6175. for (let name in lines.langs) {
  6176. const langOption = _createElement('option');
  6177. langOption.value = name;
  6178. langOption.innerText = lines.langs[name];
  6179. selectLang.appendChild(langOption);
  6180. }
  6181. selectLang.id = 'selectLang';
  6182. lines.langs.link = selectLang;
  6183. inner.appendChild(selectLang);
  6184.  
  6185. selectLang.onchange = e => {
  6186. const lang = e.target.value;
  6187. lines.setLang(lang);
  6188. };
  6189.  
  6190. // fill options form
  6191. const header = _createElement('h2');
  6192. header.textContent = 'RU AdList JS Fixes';
  6193. inner.appendChild(header);
  6194.  
  6195. lines.reg(inner.appendChild(_createElement('h3')), 'HeaderTools');
  6196.  
  6197. const sObjBtn = _createElement('button');
  6198. sObjBtn.onclick = getStrangeObjectsList;
  6199. sObjBtn.textContent = '';
  6200. lines.reg(inner.appendChild(sObjBtn), 'sObjBtn');
  6201.  
  6202. lines.reg(inner.appendChild(_createElement('h3')), 'HeaderOptions');
  6203.  
  6204. lines.reg(inner.appendChild(createCheckbox('AccessStatistics')), 'AccessStatisticsLabel');
  6205. lines.reg(inner.appendChild(createCheckbox('AbortExecutionStatistics')), 'AbortExecutionStatisticsLabel');
  6206. lines.reg(inner.appendChild(createCheckbox('LogAttachedCSS')), 'LogAttachedCSSLabel');
  6207.  
  6208. inner.appendChild(_createElement('br'));
  6209. lines.reg(inner.appendChild(createCheckbox('BlockNotificationPermissionRequests')), 'BlockNotificationPermissionRequestsLabel');
  6210.  
  6211. if (GM.info.scriptHandler === 'Tampermonkey') {
  6212. inner.appendChild(_createElement('br'));
  6213. lines.reg(inner.appendChild(_createElement('label')), 'DisableTMContextMenuLabel');
  6214. }
  6215.  
  6216. if (incompatibleScriptHandler) {
  6217. inner.appendChild(_createElement('br'));
  6218. lines.reg(inner.appendChild(createCheckbox('ShowScriptHandlerCompatibilityWarning')), 'ShowScriptHandlerCompatibilityWarningLabel');
  6219. }
  6220.  
  6221. lines.setLang(jsf.Lang);
  6222.  
  6223. return root;
  6224. };
  6225.  
  6226. let optionsWindow;
  6227. GM_registerMenuCommand(lines.MenuOptions[jsf.Lang], () => _appendChild(optionsWindow = optionsWindow || createOptionsWindow()));
  6228. // add warning to script menu for non-Tampermonkey users
  6229. if (jsf.ShowScriptHandlerCompatibilityWarning && incompatibleScriptHandler)
  6230. GM_registerMenuCommand(`${GM.info.scriptHandler} ${lines.MenuCompatibilityWarning[jsf.Lang]}`, () => {
  6231. win.open(`https://greasyfork.org/${jsf.Lang.slice(0,2)}/scripts/19993-ru-adlist-js-fixes#additional-info`);
  6232. });
  6233. }
  6234. })();