· 6 years ago · Apr 17, 2019, 09:16 AM
1// ==UserScript==
2// @name RU AdList JS Fixes
3// @namespace ruadlist_js_fixes
4// @version 20190317.7
5// @description try to take over the world!
6// @author lainverse & dimisa
7// @supportURL https://greasyfork.org/en/scripts/19993-ru-adlist-js-fixes/feedback
8// @match *://*/*
9// @exclude *://auth.wi-fi.ru/*
10// @exclude *://*.alfabank.ru/*
11// @exclude *://alfabank.ru/*
12// @exclude *://*.unicreditbanking.net/*
13// @exclude *://unicreditbanking.net/*
14// @exclude *://*.telegram.org/*
15// @exclude *://telegram.org/*
16// @grant unsafeWindow
17// @grant window.close
18// @run-at document-start
19// ==/UserScript==
20
21(function() {
22 'use strict';
23
24 let win = (unsafeWindow || window);
25
26 // MooTools are crazy enough to replace standard browser object window.Document: https://mootools.net/core
27 // Occasionally their code runs before my script on some domains and causes all kinds of havoc.
28 let _Document = Object.getPrototypeOf(HTMLDocument.prototype);
29 let _Element = Object.getPrototypeOf(HTMLElement.prototype);
30 // dTree 2.05 in some cases replaces Node object
31 let _Node = Object.getPrototypeOf(_Element);
32 let _console = {};
33 for (let name in win.console) _console[name] = console[name];
34 Object.defineProperty(win.console, 'clear', { value: () => null });
35
36 // http://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser
37 let isOpera = (!!window.opr && !!window.opr.addons) || !!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0,
38 isChrome = !!window.chrome && !!window.chrome.webstore,
39 isSafari =
40 Object.prototype.toString.call(window.HTMLElement).indexOf('Constructor') > 0 ||
41 (function (p) { return p.toString() === "[object SafariRemoteNotification]"; })(!window.safari || window.safari.pushNotification);
42 let isFirefox = 'InstallTrigger' in win;
43 let inIFrame = (win.self !== win.top);
44 let _getAttribute = Function.prototype.call.bind(_Element.getAttribute),
45 _setAttribute = Function.prototype.call.bind(_Element.setAttribute),
46 _removeAttribute = Function.prototype.call.bind(_Element.removeAttribute);
47 let _document = win.document,
48 _de = _document.documentElement,
49 _appendChild = _Document.appendChild.bind(_de),
50 _removeChild = _Document.removeChild.bind(_de),
51 _createElement = _Document.createElement.bind(_document),
52 _querySelector = _Document.querySelector.bind(_document),
53 _querySelectorAll = _Document.querySelectorAll.bind(_document);
54
55 if (isFirefox && // Exit on image pages in Fx
56 _document.constructor.prototype.toString() === '[object ImageDocumentPrototype]')
57 return;
58
59 // NodeList and HTMLCollection iterator polyfill
60 // required for old versions of Safari and Chrome 49 (last available for WinXP)
61 // https://jakearchibald.com/2014/iterators-gonna-iterate/
62 if (!NodeList.prototype[Symbol.iterator])
63 NodeList.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
64 if (!HTMLCollection.prototype[Symbol.iterator])
65 HTMLCollection.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
66
67 // Wrapper to run scripts designed to override objects available to other scripts
68 // Required in old versions of Firefox (<58) or when running with Greasemonkey
69 let skipLander = true;
70 try {
71 skipLander = !(isFirefox && ('StopIteration' in win || GM.info.scriptHandler === 'Greasemonkey'));
72 } catch(ignore){}
73 let batchLand = [];
74 let batchPrepend = [];
75 let _APIString = 'let win = window, _console = {}; for (let name in win.console) _console[name] = console[name]; '+
76 'let _document = win.document, _Document = Object.getPrototypeOf(HTMLDocument.prototype),'+
77 ' _Element = Object.getPrototypeOf(HTMLElement.prototype), _Node = Object.getPrototypeOf(_Element);';
78 let landScript = (f, pre) => {
79 let script = _createElement('script');
80 script.textContent = `(()=>{${_APIString}${(
81 (pre.length > 0 ? pre.join(';') : '')
82 )};(${f.join(')();(')})();})();`;
83 _appendChild(script);
84 _removeChild(script);
85 };
86 let scriptLander = f => f();
87 if (!skipLander) {
88 scriptLander = (func, ...prepend) => {
89 prepend.forEach(
90 x => batchPrepend.includes(x) ? null : batchPrepend.push(x)
91 );
92 batchLand.push(func);
93 };
94 _document.addEventListener(
95 'DOMContentLoaded', () => void (scriptLander = (f, ...prep) => landScript([f], prep)), false
96 );
97 }
98
99 function nullTools(opts) {
100 let nt = this;
101 opts = opts || {};
102 let log = (...args) => opts.log && _console.log(...args);
103 let warn = (...args) => _console.warn(...args);
104 let trace = (...args) => (opts.log || opts.trace) && warn(...args);
105
106 nt.destroy = function(o, destroy) {
107 if (!opts.destroy && !destroy && o instanceof Object)
108 return;
109 log('cleaning', o);
110 try {
111 for (let item in o) {
112 if (item instanceof Object)
113 nt.destroy(item);
114 delete o[item];
115 }
116 } catch (e) {
117 log('Error in object destructor', e);
118 }
119 };
120
121 nt.define = function(obj, prop, val, enumerable = true) {
122 try {
123 Object.defineProperty(
124 obj, prop, {
125 get: () => val,
126 set: v => {
127 if (v !== val) {
128 log(`set ${prop} of`, obj, 'to', v);
129 nt.destroy(v);
130 }
131 },
132 enumerable: enumerable
133 }
134 );
135 } catch (err) {
136 _console.log(`Unable to redefine "${prop}" in `, obj, err);
137 }
138 };
139 nt.proxy = function(obj, missingFuncParentName, missingFuncValue) {
140 return new Proxy(
141 obj, {
142 get: (t, p) => {
143 if (p in t)
144 return t[p];
145 if (typeof p === 'symbol') {
146 if (p === Symbol.toPrimitive)
147 t[p] = function(hint) {
148 if (hint === 'string')
149 return Object.prototype.toString.call(this);
150 return `[missing toPrimitive] ${name} ${hint}`;
151 };
152 else {
153 t[p] = void 0;
154 _console.warn('Missing', p, missingFuncParentName ? `in ${missingFuncParentName}` : '', '>>', t[p]);
155 }
156 return t[p];
157 }
158 if (missingFuncParentName) {
159 t[p] = nt.func(missingFuncValue, `${missingFuncParentName}.${p}`);
160 return t[p];
161 }
162 _console.warn(`Missing ${p} in`, t);
163 },
164 set: (t, p, v) => {
165 if (v !== t[p]) {
166 log(`set ${p} of`, t, 'to', v);
167 nt.destroy(v);
168 }
169 return true;
170 }
171 }
172 );
173 };
174 nt.func = (val, name = '', force_log = false) => nt.proxy((...args) => {
175 (force_log ? warn : trace)(`call ${name}(`, ...args,`) return`, val);
176 return val;
177 });
178 }
179
180 // Debug function, lists all unusual window properties
181 function getStrangeObjectsList() {
182 _console.warn('Strangers list start');
183 let _toString = Function.prototype.call.bind(Function.prototype.toString);
184 let _skip = 'frames/self/window/webkitStorageInfo'.split('/');
185 for (let n in win) {
186 let val = win[n];
187 if (val && !_skip.includes(n) && (win !== window && val !== window[n] || win === window) &&
188 (!(val instanceof Function) ||
189 val instanceof Function &&
190 !(new RegExp (`^function\\s(${n})?\\(\\)[\\s\\r\\n]*\\{[\\s\\r\\n]*\\[native\\scode\\][\\s\\r\\n]*\\}$`)).test(_toString(val))))
191 _console.log(`${n} =`, val);
192 }
193 _console.warn('Strangers list end');
194 }
195
196 // Creates and return protected style (unless protection is manually disabled).
197 // Protected style will re-add itself on removal and remaind enabled on attempt to disable it.
198 function createStyle(rules, props, skip_protect) {
199 props = props || {};
200 props.type = 'text/css';
201
202 function _protect(style) {
203 if (skip_protect)
204 return;
205
206 Object.defineProperty(style, 'sheet', {
207 value: null,
208 enumerable: true
209 });
210 Object.defineProperty(style, 'disabled', {
211 get: () => true, //pretend to be disabled
212 set: () => undefined,
213 enumerable: true
214 });
215 (new MutationObserver(
216 (ms) => _removeChild(ms[0].target)
217 )).observe(style, { childList: true });
218 }
219
220
221 function _create() {
222 let style = _appendChild(_createElement('style'));
223 Object.assign(style, props);
224
225 function insertRules(rule) {
226 if (rule.forEach)
227 rule.forEach(insertRules);
228 else try {
229 style.sheet.insertRule(rule, 0);
230 } catch (e) {
231 _console.error(e);
232 }
233 }
234
235 insertRules(rules);
236 _protect(style);
237
238 return style;
239 }
240
241 let style = _create();
242 if (skip_protect)
243 return style;
244
245 (new MutationObserver(
246 function(ms) {
247 let m, node;
248 let createStyleInANewThread = resolve => setTimeout(
249 resolve => resolve(_create()),
250 0, resolve
251 );
252 let setStyle = st => void(style = st);
253 for (m of ms) for (node of m.removedNodes)
254 if (node === style)
255 (new Promise(createStyleInANewThread))
256 .then(setStyle);
257 }
258 )).observe(_de, { childList: true });
259
260 return style;
261 }
262
263 // Fake objects of advertisement networks to break their workflow
264 // Popular adblock detector
265 function deployFABStub(root) {
266 let nt = new nullTools();
267 if (!('fuckAdBlock' in root)) {
268 let FuckAdBlock = function(options) {
269 let self = this;
270 self._options = {
271 checkOnLoad: false,
272 resetOnEnd: false,
273 checking: false
274 };
275 self.setOption = function(opt, val) {
276 if (val)
277 self._options[opt] = val;
278 else
279 Object.assign(self._options, opt);
280 };
281 if (options)
282 self.setOption(options);
283
284 self._var = { event: {} };
285 self.clearEvent = function() {
286 self._var.event.detected = [];
287 self._var.event.notDetected = [];
288 };
289 self.clearEvent();
290
291 self.on = function(detected, fun) {
292 self._var.event[detected?'detected':'notDetected'].push(fun);
293 return self;
294 };
295 self.onDetected = function(cb) {
296 return self.on(true, cb);
297 };
298 self.onNotDetected = function(cb) {
299 return self.on(false, cb);
300 };
301 self.emitEvent = function() {
302 for (let fun of self._var.event.notDetected)
303 fun();
304 if (self._options.resetOnEnd)
305 self.clearEvent();
306 return self;
307 };
308 self._creatBait = () => null;
309 self._destroyBait = () => null;
310 self._checkBait = function() {
311 setTimeout((() => self.emitEvent()), 1);
312 };
313 self.check = function() {
314 self._checkBait();
315 return true;
316 };
317
318 let callback = function() {
319 if (self._options.checkOnLoad)
320 setTimeout(self.check, 1);
321 };
322 root.addEventListener('load', callback, false);
323 };
324 nt.define(root, 'FuckAdBlock', FuckAdBlock);
325 nt.define(root, 'fuckAdBlock', new FuckAdBlock({
326 checkOnLoad: true,
327 resetOnEnd: true
328 }));
329 }
330 }
331 // new version of fAB adapting to fake API
332 // scriptLander(() => deployFABStub(win), nullTools); // so it's disabled by default for now
333
334 scriptLander(() => {
335 let nt = new nullTools();
336
337 // CoinHive miner stub. Continuous 100% CPU load can easily kill some CPU with overheat.
338 if (!('CoinHive' in win))
339 if (location.hostname !== 'cnhv.co') {
340 // CoinHive stub for cases when it doesn't affect site functionality
341 let CoinHiveConstructor = function() {
342 _console.warn('Fake CoinHive miner created.');
343 this.setThrottle = nt.func(null);
344 this.start = nt.func(null);
345 this.on = nt.func(null);
346 this.getHashesPerSecond = nt.func(Infinity);
347 this.getTotalHashes = nt.func(Infinity);
348 this.getAcceptedHashes = nt.func(Infinity);
349 };
350 let CoinHiveStub = nt.proxy({
351 Anonymous: CoinHiveConstructor,
352 User: CoinHiveConstructor,
353 Token: CoinHiveConstructor,
354 JobThread: nt.func(null),
355 Res: nt.func(null),
356 IF_EXCLUSIVE_TAB: false,
357 CONFIG: nt.proxy({})
358 });
359 nt.define(win, 'CoinHive', CoinHiveStub);
360 } else {
361 // CoinHive wrapper to fool sites which expect it to actually work and return results
362 let CoinHiveObject;
363 Object.defineProperty(win, 'CoinHive', {
364 set: function(obj) {
365 if ('Token' in obj) {
366 _console.log('[CoinHive] Token wrapper applied.');
367 let _Token = obj.Token.bind(obj);
368 obj.Token = function(siteKey, goal, params) {
369 let _goal = goal;
370 goal = goal > 256 ? 256 : goal;
371 _console.log(`[CoinHive] Original goal: ${_goal}, new smaller goal ${goal}.`);
372 _console.log(`With smaller goals server may return 'invalid_goal' error and stop working.`);
373 let miner = _Token(siteKey, goal, params);
374 miner.setThrottle(0.99);
375 miner.setThrottle = () => null;
376 let _start = miner.start.bind(miner);
377 miner.start = function() {
378 let res = _start(window.CoinHive.FORCE_EXCLUSIVE_TAB);
379 return res;
380 };
381 let _getTotalHashes = miner.getTotalHashes;
382 miner.getTotalHashes = function() {
383 return Math.trunc(_getTotalHashes.call(this) / goal * _goal);
384 };
385 let __emit = miner._emit;
386 miner._emit = function(state, props) {
387 let _self = this;
388 _console.log('[CoinHive] state:', state, props);
389 if (state === 'job')
390 setTimeout(() => {
391 _self.stop();
392 _self._emit('accepted', { hashes: goal });
393 }, 1000);
394 return __emit.apply(_self, arguments);
395 };
396 let _on = miner.on.bind(miner);
397 miner.on = function(type, callback) {
398 if (type === 'accepted') {
399 _console.log('[CoinHive] "accepted" callback wrapper applied.');
400 let _callback = callback;
401 callback = function(params) {
402 _console.log('[CoinHive] "accepted" callback is called, imitating original goal being reached.');
403 params.hashes = _goal;
404 return _callback.apply(this, arguments);
405 };
406 miner.stop();
407 }
408 return _on(type, callback);
409 };
410 return miner;
411 };
412 }
413 CoinHiveObject = obj;
414 },
415 get: () => CoinHiveObject
416 });
417 }
418
419 // VideoJS player wrapper
420 VideoJS: {
421 let _videojs = win.videojs || void 0;
422 Object.defineProperty(win, 'videojs', {
423 get: () => _videojs,
424 set: f => {
425 if (f === _videojs)
426 return true;
427 _console.log('videojs =', f);
428 _videojs = new Proxy(f, {
429 apply: (tgt, ths, args) => {
430 _console.log('videojs(', ...args, ')');
431 let params = args[1];
432 if (params) {
433 if (params.hasAd)
434 params.hasAd = false;
435 if (params.autoplay)
436 params.autoplay = false;
437 if (params.plugins && params.plugins.vastClient)
438 delete params.plugins.vastClient;
439 }
440 let res = tgt.apply(ths, args);
441 if (res && res.seed)
442 res.seed = () => null;
443 _console.log('player = ', res);
444 return res;
445 }
446 });
447 }
448 });
449 }
450
451 // Set a little trap for BodyClick ads
452 Object.defineProperty(win, '__BC_domain', {
453 set: () => { throw 'BodyClick trap' }
454 });
455
456 // Yandex API (ADBTools, Metrika)
457 let hostname = location.hostname;
458 if (// Thank you, Greasemonkey, now I have to check for this. -_-
459 location.protocol === 'about:' ||
460 // Google likes to define odd global variables like Ya
461 hostname.startsWith('google.') || hostname.includes('.google.') ||
462 // Also, Yandex uses their Ya object for a lot of things on their pages and
463 // wrapping it may cause problems. It's better to skip it in some cases.
464 ((hostname.startsWith('yandex.') || hostname.includes('.yandex.')) &&
465 /^\/((yand)?search|images)/i.test(location.pathname) && !hostname.startsWith('news.')) ||
466 // Also skip on these following sites since they use
467 // code minification which generated global Ya variable.
468 hostname.endsWith('chatango.com') || hostname.endsWith('github.io') ||
469 hostname.endsWith('grimtools.com') || hostname.endsWith('poeplanner.com'))
470 return;
471
472 let YaProps = new Set();
473 function setObfuscatedProperty(Ya, rootProp, obj, name) {
474 if (YaProps.has(rootProp))
475 return;
476 _console.warn(`Ya.${rootProp} = Ya.${name}`);
477 nt.define(Ya, rootProp, Ya[name]);
478 YaProps.add(rootProp);
479 for (let prop in obj)
480 delete obj[prop];
481 for (let prop in Ya[name])
482 obj[prop] = Ya[name][prop];
483 }
484 function onObfuscatedProperty (Ya, rootProp, obj) {
485 if ('AdvManager' in obj || 'isAllowedRepeatAds' in obj) {
486 setObfuscatedProperty(Ya, rootProp, obj, 'Context');
487 return Ya.Context;
488 }
489 if ('create' in obj && 'createAdaptive' in obj && 'createScroll' in obj) {
490 setObfuscatedProperty(Ya, rootProp, obj, 'adfoxCode');
491 return Ya.adfoxCode;
492 }
493 return new Proxy(obj, {
494 set: (tgt, prop, val) => {
495 if (prop === 'AdvManager' || prop === 'isAllowedRepeatAds') {
496 setObfuscatedProperty(Ya, rootProp, obj, 'Context');
497 return true;
498 }
499 if (prop === 'create' && 'createAdaptive' in obj && 'createScroll' in obj ||
500 prop === 'createScroll' && 'create' in obj && 'createAdaptive' in obj ||
501 prop === 'createAdaptive' && 'create' in obj && 'createScroll' in obj) {
502 setObfuscatedProperty(Ya, rootProp, obj, 'adfoxCode');
503 return true;
504 }
505 tgt[prop] = val;
506 return true;
507 },
508 get: (tgt, prop) => {
509 if (prop === 'AdvManager' && !(prop in tgt)) {
510 _console.warn(`Injected missing ${prop} in Ya.${rootProp}.`);
511 tgt[prop] = Ya.Context[prop];
512 }
513 return tgt[prop];
514 }
515 });
516 }
517 let Rum = {};
518 [
519 '__timeMarks', '_timeMarks', '__deltaMarks', '_deltaMarks',
520 '__defRes', '_defRes', '__defTimes', '_defTimes', '_vars',
521 'commonVars'
522 ].forEach(name => void(Rum[name] = []));
523 [
524 'getSettings', 'getVarsList'
525 ].forEach(name => void(Rum[name] = nt.func([], `Ya.Rum.${name}`)));
526 [
527 ['ajaxStart', 0], ['ajaxComplete', 0],
528 ['enabled', true], ['_tti', null],
529 ['vsChanged', false], ['vsStart', 'visible']
530 ].forEach(([prop, val]) => void(Rum[prop] = val));
531 Rum = nt.proxy(Rum, 'Ya.Rum', null);
532 let Ya = new Proxy({}, {
533 set: function(tgt, prop, val) {
534 if (val === tgt[prop])
535 return true;
536 if (prop === 'Rum') {
537 nt.define(tgt, prop, Rum);
538 YaProps.add(prop);
539 Object.assign(val, Rum);
540 }
541 if (YaProps.has(prop)) {
542 _console.log(`Ya.${prop} \u2260`, val);
543 return true;
544 }
545 if (val instanceof Object && prop !== '__inline_params__')
546 val = onObfuscatedProperty(Ya, prop, val);
547 tgt[prop] = val;
548 _console.log(`Ya.${prop} =`, val);
549 return true;
550 },
551 get: (tgt, prop) => tgt[prop]
552 });
553 let callWithParams = function(f) {
554 f.call(this, Ya.__inline_params__ || {});
555 Ya.__inline_params__ = null;
556 };
557 nt.define(Ya, 'callWithParams', callWithParams);
558 nt.define(Ya, 'PerfCounters', nt.proxy({
559 __cacheEvents: []
560 }, 'Ya.PerfCounters', null));
561 nt.define(Ya, '__isSent', true);
562 nt.define(Ya, 'confirmUrl', '');
563 nt.define(Ya, 'Direct', nt.proxy({}, 'Ya.Direct', null));
564 nt.define(Ya, 'mediaCode', nt.proxy({
565 create: function() {
566 if (inIFrame) {
567 _console.log('Removed body of ad-frame.');
568 _document.documentElement.removeChild(_document.body);
569 }
570 }
571 }, 'Ya.mediaCode', null));
572 let extra = nt.proxy({
573 extra: nt.proxy({ match: 0, confirm: '', src: '' }),
574 id: 0, percent: 100, threshold: 1
575 });
576 nt.define(Ya, '_exp', nt.proxy({
577 id: 0, coin: 0,
578 choose: nt.func(extra),
579 get: (prop) => extra.hasOwnProperty(prop) ? extra[prop] : null,
580 getId: nt.func(0),
581 defaultVersion: extra,
582 getExtra: nt.func(extra.extra),
583 getDefaultExtra: nt.func(extra.extra),
584 versions: [extra]
585 }));
586 nt.define(Ya, 'c', nt.func(null));
587 nt.define(Ya, 'ADBTools', function(){
588 this.getCurrentState = nt.func(true);
589 return nt.proxy(this, 'Ya.ADBTools', null);
590 });
591 nt.define(Ya, 'AdDetector', nt.proxy({}, 'Ya.AdDetector', null));
592 let definePr = o => {
593 Object.defineProperty(o, 'pr', {
594 get: () => Math.floor(Math.random() * 1e6) + 1,
595 set: () => true
596 });
597 };
598 let adfoxCode = {
599 forcedDirectLoadingExp: nt.proxy({ isLoadingTurnedOn: false, isExp: false }),
600 isLoadingTurnedOn: false,
601 xhrExperiment: nt.proxy({ isXhr: true, isControl: true }),
602 _: []
603 };
604 definePr(adfoxCode);
605 [
606 'clearSession', 'create', 'createAdaptive', 'createScroll',
607 'destroy', 'moduleLoad', 'reload', 'setModule'
608 ].forEach(name => void(adfoxCode[name] = nt.func(null, `Ya.adfoxCode.${name}`)));
609 nt.define(Ya, 'adfoxCode', nt.proxy(adfoxCode, 'Ya.adfoxCode', null));
610 let managerForAdfox = {
611 loaderVersion: 1,
612 isCurrrencyExp: true,
613 isReady: nt.func(true, 'Ya.headerBidding.managerForAdfox.isReady'),
614 getRequestTimeout: nt.func(300 + Math.floor(Math.random()*100), 'Ya.headerBidding.managerForAdfox.getRequestTimeout')
615 };
616 let headerBidding = nt.proxy({
617 setSettings: opts => {
618 if (!(opts && opts.adUnits))
619 return null;
620 let ids = [];
621 for (let unit of opts.adUnits)
622 ids.push(unit.code);
623 createStyle(`#${ids.join(', #')} { display: none !important }`);
624 },
625 pushAdUnits: nt.func(null, 'Ya.headerBidding.pushAdUnits'),
626 managerForAdfox: nt.proxy(managerForAdfox, 'Ya.headerBidding.managerForAdfox', null)
627 });
628 definePr(headerBidding);
629 nt.define(Ya, 'headerBidding', headerBidding);
630
631 let AdvManager = function() {
632 this.render = function(o) {
633 if (!o.renderTo)
634 return;
635 let placeholder = _document.getElementById(o.renderTo);
636 if (!placeholder)
637 return _console.warn('Ya.AdvManager.render call w/o placeholder', o);
638 let parent = placeholder.parentNode;
639 placeholder.style = 'display:none!important';
640 parent.style = (parent.getAttribute('style')||'') + 'height:auto!important';
641 // fix for Yandex TV pages
642 if (location.hostname.startsWith('tv.yandex.')) {
643 let sibling = placeholder.previousSibling;
644 if (sibling && sibling.classList && sibling.classList.contains('tv-spin'))
645 sibling.style.display = 'none';
646 }
647 };
648 this.constructor = Object;
649 return nt.proxy(this, 'Ya.AdvManager', null);
650 };
651 let _Ya_Context_undefined_count = {};
652 nt.define(Ya, 'Context', new Proxy({
653 __longExperiment: null,
654 _callbacks: nt.proxy([]),
655 _asyncModeOn: true,
656 _init: nt.func(null),
657 _load_callbacks: nt.proxy([]),
658 processCallbacks: nt.func(null),
659 isAllowedRepeatAds: nt.func(null),
660 isNewLoader: nt.func(false),
661 AdvManager: new AdvManager(),
662 AdvManagerStatic: nt.func({})
663 }, {
664 get: (ctx, prop) => {
665 if (prop in ctx)
666 return ctx[prop];
667 if (prop in _Ya_Context_undefined_count)
668 _Ya_Context_undefined_count[prop]++;
669 else {
670 _Ya_Context_undefined_count[prop] = 1;
671 _console.warn(`Mising '${prop}' in Ya.Context`);
672 }
673 if (_Ya_Context_undefined_count[prop] >= 5) {
674 _console.warn(`Ya.Context.${prop} = Ya.Context.AdvManager`);
675 ctx[prop] = ctx.AdvManager;
676 }
677 },
678 set: () => true
679 }));
680 let Metrika = function Metrika(x) {
681 this._ecommerce = '';
682 if (x && 'id' in x)
683 this.id = x.id;
684 else
685 this.id = 0;
686 return nt.proxy(this, 'Ya.Metrika', null);
687 };
688 Metrika.counters = () => Ya._metrika.counters;
689 nt.define(Ya, 'Metrika', Metrika);
690 nt.define(Ya, 'Metrika2', Metrika);
691 let counter = new Ya.Metrika();
692 nt.define(Ya, '_metrika', nt.proxy({
693 counter: counter,
694 counters: [counter],
695 hitParam: {},
696 counterNum: 0,
697 hitId: 0,
698 v: 1,
699 i: 0,
700 _globalMetrikaHitId: 0,
701 getCounters: null,
702 dataLayer: null,
703 f1: null
704 }));
705 nt.define(Ya, '_globalMetrikaHitId', 0);
706 counter = {};
707 [
708 'stringifyParams','_getVars',
709 'getUid','getUrl','getHash'
710 ].forEach(name => void(counter[name] = nt.func('', `Ya.counter.${name}`)));
711 nt.define(Ya, 'counter', nt.proxy(counter, 'Ya.counter', null));
712 nt.define(Ya, 'jserrors', []);
713 nt.define(Ya, 'onerror', nt.func(null, 'Ya.onerror'));
714 let error_on_access = false;
715 if ('Ya' in win)
716 try {
717 _console.log('Found existing Ya object:', win.Ya);
718 for (let prop in win.Ya)
719 Ya[prop] = win.Ya[prop];
720 } catch(ignore) {
721 error_on_access = true;
722 }
723 if (!error_on_access && // some people don't know how to export only necessary stuff into global context
724 location.hostname !== 'material.io') { // so, here is an exception for one of such cases
725 for (let prop in Ya)
726 if (prop !== '__inline_params__')
727 YaProps.add(prop);
728 nt.define(win, 'Ya', Ya);
729 } else
730 _console.log('Looks like window.Ya blocked with error-on-access scriptlet.');
731 // Yandex.Metrika callbacks
732 let yandex_metrika_callbacks = [];
733 _document.addEventListener(
734 'DOMContentLoaded', () => {
735 yandex_metrika_callbacks.forEach((f) => f && f.call(window));
736 yandex_metrika_callbacks.length = 0;
737 yandex_metrika_callbacks.push = (f) => setTimeout(f, 0);
738 }, false
739 );
740 nt.define(win, 'yandex_metrika_callbacks', yandex_metrika_callbacks);
741 }, nullTools, createStyle);
742
743 if (!isFirefox) {
744 // scripts for non-Firefox browsers
745 // https://greasyfork.org/scripts/14720-it-s-not-important
746 unimptt: {
747 // BigInt were implemented in Chrome 67 which also support
748 // proper user styles and doesn't need this fix anymore.
749 if ((isChrome || isOpera) && 'BigInt' in win)
750 break unimptt;
751
752 let imptt = /((display|(margin|padding)(-top|-bottom)?)\s*:[^;!]*)!\s*important/ig,
753 ret_b = (a,b) => b,
754 _toLowerCase = String.prototype.toLowerCase,
755 protectedNodes = new WeakSet(),
756 log = false;
757
758 let logger = function() {
759 if (log)
760 _console.log('Some page elements became a bit less important.');
761 log = false;
762 };
763
764 let unimportanter = function(node) {
765 let style = (node.nodeType === _Node.ELEMENT_NODE) ?
766 _getAttribute(node, 'style') : null;
767
768 if (!style || !imptt.test(style) || node.style.display === 'none' ||
769 (node.src && node.src.startsWith('chrome-extension:'))) // Web of Trust IFRAME and similar
770 return false; // get out if we have nothing to do here
771
772 protectedNodes.add(node);
773 _setAttribute(node, 'style', style.replace(imptt, ret_b));
774 log = true;
775 };
776
777 (new MutationObserver(
778 function(mutations) {
779 setTimeout(
780 function(ms) {
781 let m, node;
782 for (m of ms) for (node of m.addedNodes)
783 unimportanter(node);
784 logger();
785 }, 0, mutations
786 );
787 }
788 )).observe(_document, {
789 childList : true,
790 subtree : true
791 });
792
793 _Element.setAttribute = function setAttribute(name, value) {
794 '[native code]';
795 let replaced = value;
796 if (name && _toLowerCase.call(name) === 'style' && protectedNodes.has(this))
797 replaced = value.replace(imptt, ret_b);
798 log = (replaced !== value);
799 logger();
800 return _setAttribute(this, ...arguments);
801 };
802
803 win.addEventListener (
804 'load', () => {
805 for (let imp of _document.querySelectorAll('[style*="!"]'))
806 unimportanter(imp);
807 logger();
808 }, false
809 );
810 }
811
812 // Naive ABP Style protector
813 if ('ShadowRoot' in win) {
814 let _removeChild = Function.prototype.call.bind(_Node.removeChild);
815 let _appendChild = Function.prototype.call.bind(_Node.appendChild);
816 let createShadow = () => _createElement('shadow');
817 // Prevent adding fake content entry point
818 _Node.appendChild = function appendChild(child) {
819 if (this instanceof ShadowRoot &&
820 child instanceof HTMLContentElement)
821 return _appendChild(this, createShadow());
822 return _appendChild(this, ...arguments);
823 };
824 {
825 let _shadowSelector = Function.prototype.call.bind(ShadowRoot.prototype.querySelector);
826 let _innerHTML = Object.getOwnPropertyDescriptor(ShadowRoot.prototype, 'innerHTML');
827 let _parentNode = Object.getOwnPropertyDescriptor(_Node, 'parentNode');
828 if (_innerHTML && _parentNode) {
829 let _set = Function.prototype.call.bind(_innerHTML.set);
830 let _getParent = Function.prototype.call.bind(_parentNode.get);
831 _innerHTML.configurable = false;
832 _innerHTML.set = function() {
833 _set(this, ...arguments);
834 let content = _shadowSelector(this, 'content');
835 if (content) {
836 let parent = _getParent(content);
837 _removeChild(parent, content);
838 _appendChild(parent, createShadow());
839 }
840 };
841 }
842 Object.defineProperty(ShadowRoot.prototype, 'innerHTML', _innerHTML);
843 }
844 // Locate and apply extra protection to a style on top of what ABP does
845 let style;
846 (new Promise(
847 function(resolve, reject) {
848 let getStyle = () => _querySelector('::shadow style');
849 style = getStyle();
850 if (style)
851 return resolve(style);
852 let intv = setInterval(
853 function() {
854 style = getStyle();
855 if (!style)
856 return;
857 intv = clearInterval(intv);
858 return resolve(style);
859 }, 0
860 );
861 _document.addEventListener(
862 'DOMContentLoaded', () => {
863 if (intv)
864 clearInterval(intv);
865 style = getStyle();
866 return style ? resolve(style) : reject();
867 }, false
868 );
869 }
870 )).then(
871 function(style) {
872 let emptyArr = [],
873 nullStr = {
874 get: () => '',
875 set: () => undefined
876 };
877 let shadow = style.parentNode;
878 Object.defineProperties(shadow, {
879 childElementCount: { value: 0 },
880 styleSheets: { value: emptyArr },
881 firstChild: { value: null },
882 firstElementChild: { value: null },
883 lastChild: { value: null },
884 lastElementChild: { value: null },
885 childNodes: { value: emptyArr },
886 children: { value: emptyArr },
887 innerHTML: { value: nullStr },
888 });
889 Object.defineProperties(style, {
890 innerHTML: { value: nullStr },
891 textContent: { value: nullStr },
892 ownerDocument: { value: null },
893 parentNode: {value: null },
894 previousElementSibling: { value: null },
895 previousSibling: { value: null },
896 disabled: { get: () => true, set: () => null }
897 });
898 Object.defineProperties(style.sheet, {
899 deleteRule: { value: () => null },
900 disabled: { get: () => true, set: () => null },
901 cssRules: { value: emptyArr },
902 rules: { value: emptyArr }
903 });
904 }
905 ).catch(()=>null);
906 _Node.removeChild = function removeChild(child) {
907 if (child === style)
908 return;
909 return _removeChild(this, ...arguments);
910 };
911 }
912 }
913
914 if (/^https?:\/\/(mail\.yandex\.|music\.yandex\.|news\.yandex\.|(www\.)?yandex\.[^/]+\/(yand)?search[/?])/i.test(win.location.href) ||
915 /^https?:\/\/tv\.yandex\./i.test(win.location.href)) {
916 // https://greasyfork.org/en/scripts/809-no-yandex-ads
917 let yadWord = /ЯндекÑ.Директ/i,
918 adWords = /Реклама|Ad/i;
919 let _querySelector = _document.querySelector.bind(_document),
920 _querySelectorAll = _document.querySelectorAll.bind(_document),
921 _getAttribute = Function.prototype.call.bind(_Element.getAttribute),
922 _setAttribute = Function.prototype.call.bind(_Element.setAttribute);
923 // Function to attach an observer to monitor dynamic changes on the page
924 let pageUpdateObserver = (func, obj, params) => {
925 if (obj)
926 (new MutationObserver(func))
927 .observe(obj, (params || { childList:true, subtree:true }));
928 };
929 // Short name for parentNode.removeChild and setAttribute style to display:none
930 let remove = (node) => {
931 if (!node || !node.parentNode)
932 return false;
933 _console.log('Removed node.');
934 node.parentNode.removeChild(node);
935 };
936 let hide = (node) => {
937 if (!node)
938 return false;
939 _console.log('Hid node.');
940 _setAttribute(node, 'style', 'display:none!important');
941 };
942 // Yandex search ads in Google Chrome
943 if ('attachShadow' in _Element) {
944 let _attachShadow = _Element.attachShadow;
945 _Element.attachShadow = function() {
946 let node = this,
947 root = _attachShadow.apply(node, arguments);
948 pageUpdateObserver(
949 (ms) => {
950 for (let m of ms) if (m.addedNodes.length)
951 if (adWords.test(root.textContent))
952 remove(node.closest('.serp-item'));
953 }, root
954 );
955 return root;
956 };
957 }
958 // Yandex Mail ads
959 if (location.hostname.startsWith('mail.')) {
960 let nt = new nullTools();
961 let wrap = vl => {
962 if (!vl)
963 return vl;
964 _console.log('Daria =', vl);
965 nt.define(vl, 'AdBlock', nt.proxy({
966 detect: nt.func(new Promise(() => null), 'Daria.AdBlock.detect'),
967 enabled: false
968 }));
969 nt.define(vl, 'AdvPresenter', nt.proxy({
970 _config: nt.proxy({
971 banner: false,
972 done: false,
973 line: true
974 })
975 }));
976 if (vl.Config) {
977 delete vl.Config.adBlockDetector;
978 delete vl.Config['adv-url'];
979 delete vl.Config.cryprox;
980 if (vl.Config.features) {
981 delete vl.Config.features.web_adloader_with_cookie_cache;
982 delete vl.Config.features.web_ads;
983 delete vl.Config.features.web_ads_mute;
984 }
985 vl.Config.mayHaveAdv = false;
986 }
987 return vl;
988 };
989 let _Daria = wrap(win.Daria);
990 if (_Daria)
991 _console.log('Wrapped already existing object "Daria".');
992 Object.defineProperty(win, 'Daria', {
993 get: () => _Daria,
994 set: vl => {
995 if (vl === _Daria)
996 return;
997 _Daria = wrap(vl);
998 }
999 });
1000 }
1001 // prevent/defuse adblock detector
1002 setInterval(() => {
1003 localStorage.ic = '';
1004 localStorage._mt__data = '';
1005 }, 100);
1006 let yp_keepCookieParts = /\.(sp|ygo|ygu)\./; // ygo = city id; ygu = detect city automatically
1007 let _doc_proto = ('cookie' in _Document) ? _Document : Object.getPrototypeOf(_document);
1008 let _cookie = Object.getOwnPropertyDescriptor(_doc_proto, 'cookie');
1009 if (_cookie) {
1010 let _set_cookie = Function.prototype.call.bind(_cookie.set);
1011 _cookie.set = function(value) {
1012 if (/^(mda=|yp=|ys=|yabs-|__|bltsr=)/.test(value))
1013 // remove value, set expired
1014 if (!value.startsWith('yp=')) {
1015 value = value.replace(/^([^=]+=)[^;]+/,'$1').replace(/(expires=)[\w\s\d,]+/,'$1Thu, 01 Jan 1970 00');
1016 _console.log('expire cookie', value.match(/^[^=]+/)[0]);
1017 } else {
1018 let parts = value.split(';');
1019 let values = parts[0].split('#').filter(part => yp_keepCookieParts.test(part));
1020 if (values.length)
1021 values[0] = values[0].replace(/^yp=/, '');
1022 let res = `yp=${values.join('#')}`;
1023 _console.log(`set cookie ${res}, dropped ${parts[0].replace(res,'')}`);
1024 parts[0] = res;
1025 value = parts.join(';');
1026 }
1027 return _set_cookie(this, value);
1028 };
1029 Object.defineProperty(_doc_proto, 'cookie', _cookie);
1030 }
1031 // other ads
1032 _document.addEventListener(
1033 'DOMContentLoaded', () => {
1034
1035 {
1036 // Generic ads removal and fixes
1037 let node = _querySelector('.serp-header');
1038 if (node)
1039 node.style.marginTop = '0';
1040 for (node of _querySelectorAll(
1041 '.serp-adv__head + .serp-item,'+
1042 '#adbanner,'+
1043 '.serp-adv,'+
1044 '.b-spec-adv,'+
1045 'div[class*="serp-adv__"]:not(.serp-adv__found):not(.serp-adv__displayed)'
1046 )) remove(node);
1047 }
1048 // Search ads
1049 function removeSearchAds() {
1050 for (let node of _querySelectorAll('.serp-item'))
1051 if (_getAttribute(node, 'role') === 'complementary' ||
1052 adWords.test((node.querySelector('.label')||{}).textContent))
1053 hide(node);
1054 }
1055 // News ads
1056 function removeNewsAds() {
1057 let node, block, items, mask, classes,
1058 masks = [
1059 { class: '.ads__wrapper', regex: /[^,]*?,[^,]*?\.ads__wrapper/ },
1060 { class: '.ads__pool', regex: /[^,]*?,[^,]*?\.ads__pool/ }
1061 ];
1062 for (node of _querySelectorAll('style[nonce]')) {
1063 classes = node.innerText.replace(/\{[^}]+\}+/ig, '|').split('|');
1064 for (block of classes) for (mask of masks)
1065 if (block.includes(mask.class)) {
1066 block = block.match(mask.regex)[0];
1067 items = _querySelectorAll(block);
1068 for (item of items)
1069 remove(items[0]);
1070 }
1071 }
1072 }
1073 // Music ads
1074 function removeMusicAds() {
1075 for (let node of _querySelectorAll('.ads-block'))
1076 remove(node);
1077 }
1078 // News fixes
1079 function removePageAdsClass() {
1080 if (_document.body.classList.contains("b-page_ads_yes")) {
1081 _document.body.classList.remove("b-page_ads_yes");
1082 _console.log('Page ads class removed.');
1083 }
1084 }
1085 // TV fixes
1086 function removeTVAds() {
1087 for (let node of _querySelectorAll('div[class^="_"][data-reactid] > div'))
1088 if (yadWord.test(node.textContent) || node.querySelector('iframe:not([src])')) {
1089 if (node.offsetWidth) {
1090 let pad = _document.createElement('div');
1091 _setAttribute(pad, 'style', `width:${node.offsetWidth}px`);
1092 node.parentNode.appendChild(pad);
1093 }
1094 remove(node);
1095 }
1096 }
1097
1098 if (location.hostname.startsWith('music.')) {
1099 pageUpdateObserver(removeMusicAds, _querySelector('.sidebar'));
1100 removeMusicAds();
1101 } else if (location.hostname.startsWith('news.')) {
1102 pageUpdateObserver(removeNewsAds, _document.body);
1103 pageUpdateObserver(removePageAdsClass, _document.body, { attributes:true, attributesFilter:['class'] });
1104 removeNewsAds();
1105 removePageAdsClass();
1106 } else if (location.hostname.startsWith('tv.')) {
1107 pageUpdateObserver(removeTVAds, _document.body);
1108 removeTVAds();
1109 } else if (!location.hostname.startsWith('mail.')) {
1110 pageUpdateObserver(removeSearchAds, _querySelector('.main__content'));
1111 removeSearchAds();
1112 }
1113 }
1114 );
1115 }
1116
1117 // Yandex Raven stub (some monitoring sub-system)
1118 function yandexRavenStub() {
1119 let nt = new nullTools();
1120 nt.define(win, 'Raven', nt.proxy({
1121 context: f => f(),
1122 config: nt.func(nt.proxy({
1123 install: nt.func(null, 'Raven.config().install()')
1124 }, 'Raven.config()', null), 'Raven.config')
1125 }, 'Raven', null));
1126 }
1127
1128 // Generic Yandex Scripts
1129 if (/^https?:\/\/([^.]+\.)*yandex\.[^/]+/i.test(win.location.href)) {
1130 // remove banner on the start page
1131 selectiveCookies();
1132 scriptLander(() => {
1133 let nt = new nullTools({log: false, trace: true});
1134 let AwapsJsonAPI_Json = function(...args) {
1135 _console.log('>> new AwapsJsonAPI.Json(', ...args, ')');
1136 };
1137 [
1138 'setID', 'addImageContent', 'sendCounts',
1139 'drawBanner', 'bannerIsInvisible', 'expand', 'refreshAd'
1140 ].forEach(name => void(AwapsJsonAPI_Json.prototype[name] = nt.func(null, `AwapsJsonAPI.Json.${name}`)));
1141 AwapsJsonAPI_Json.prototype.checkBannerVisibility = nt.func(true, 'AwapsJsonAPI.Json.checkBannerVisibility');
1142 AwapsJsonAPI_Json.prototype.addIframeContent = nt.proxy(function(...args) {
1143 try {
1144 let frame = args[1][0].parentNode;
1145 frame.parentNode.removeChild(frame);
1146 _console.log(`Removed banner placeholder.`);
1147 } catch(ignore) {
1148 _console.log(`Can't locate frame object to remove.`);
1149 }
1150 });
1151 AwapsJsonAPI_Json.prototype.getHTML = nt.func('', 'AwapsJsonAPI.Json.getHTML');
1152 AwapsJsonAPI_Json.prototype = nt.proxy(AwapsJsonAPI_Json.prototype);
1153 AwapsJsonAPI_Json = nt.proxy(AwapsJsonAPI_Json);
1154 if ('AwapsJsonAPI' in win) {
1155 _console.log('Oops! AwapsJsonAPI already defined.');
1156 let f = win.AwapsJsonAPI.Json;
1157 win.AwapsJsonAPI.Json = AwapsJsonAPI_Json;
1158 if (f && f.prototype)
1159 f.prototype = AwapsJsonAPI_Json.prototype;
1160 } else
1161 nt.define(win, 'AwapsJsonAPI', nt.proxy({
1162 Json: AwapsJsonAPI_Json
1163 }));
1164
1165 let parseExport = x => {
1166 if (!x)
1167 return x;
1168 // remove banner placeholder
1169 if (x.banner && x.banner.cls) {
1170 let _parent = `.${x.banner.cls.banner__parent}`;
1171 _document.addEventListener('DOMContentLoaded', () => {
1172 for (let banner of _document.querySelectorAll(_parent)) {
1173 _setAttribute(banner, 'style', 'display:none!important');
1174 _console.log('Hid banner placeholder.');
1175 }
1176 }, false);
1177 }
1178
1179 // remove banner data and some other stuff
1180 delete x.banner;
1181 delete x.consistency;
1182 delete x['i-bannerid'];
1183 delete x['i-counter'];
1184 delete x['promo-curtain'];
1185
1186 // remove parts of ga-counter (complete removal break "ТВ Онлайн")
1187 if (x['ga-counter'] && x['ga-counter'].data) {
1188 x['ga-counter'].data.id = 0;
1189 delete x['ga-counter'].data.ether;
1190 delete x['ga-counter'].data.iframeSrc;
1191 delete x['ga-counter'].data.iframeSrcEx;
1192 }
1193
1194 return x;
1195 };
1196 // Yandex banner on main page and some other things
1197 let _home = win.home,
1198 _home_set = !!_home;
1199 Object.defineProperty(win, 'home', {
1200 get: () => _home,
1201 set: vl => {
1202 if (!_home_set && vl === _home)
1203 return;
1204 _home_set = false;
1205 _console.log('home =', vl);
1206 let _home_export = parseExport(vl.export);
1207 Object.defineProperty(vl, 'export', {
1208 get: () => _home_export,
1209 set: vl => {
1210 _home_export = parseExport(vl);
1211 }
1212 });
1213 _home = vl;
1214 }
1215 });
1216 // adblock circumvention on some Yandex domains (weather in particular)
1217 yandexRavenStub();
1218 }, nullTools, selectiveCookies, yandexRavenStub, 'let _setAttribute = Function.prototype.call.bind(_Element.setAttribute)');
1219
1220 if ('attachShadow' in _Element) {
1221 let fakeRoot = () => ({
1222 firstChild: null,
1223 appendChild: () => null,
1224 querySelector: () => null,
1225 querySelectorAll: () => null
1226 });
1227 _Element.createShadowRoot = fakeRoot;
1228 let shadows = new WeakMap();
1229 let _attachShadow = Object.getOwnPropertyDescriptor(_Element, 'attachShadow');
1230 _attachShadow.value = function() {
1231 return shadows.set(this, fakeRoot()).get(this);
1232 };
1233 Object.defineProperty(_Element, 'attachShadow', _attachShadow);
1234 let _shadowRoot = Object.getOwnPropertyDescriptor(_Element, 'shadowRoot');
1235 _shadowRoot.set = () => null;
1236 _shadowRoot.get = function() {
1237 return shadows.has(this) ? shadows.get(this) : void 0;
1238 };
1239 Object.defineProperty(_Element, 'shadowRoot', _shadowRoot);
1240 }
1241
1242 // Disable banner styleSheet (on main page)
1243 document.addEventListener('DOMContentLoaded', () => {
1244 for (let sheet of document.styleSheets)
1245 try {
1246 for (let rule of sheet.cssRules)
1247 if (rule.cssText.includes(' 728px 90px')) {
1248 rule.parentStyleSheet.disabled = true;
1249 _console.log('Disabled banner styleSheet:', rule.parentStyleSheet);
1250 }
1251 } catch(ignore) {}
1252 }, false);
1253
1254 // Partially based on https://greasyfork.org/en/scripts/22737-remove-yandex-redirect
1255 let selectors = (
1256 'A[onmousedown*="/jsredir"],'+
1257 'A[data-vdir-href],'+
1258 'A[data-counter]'
1259 );
1260 let removeTrackingAttributes = function(link) {
1261 link.removeAttribute('onmousedown');
1262 if (link.hasAttribute('data-vdir-href')) {
1263 link.removeAttribute('data-vdir-href');
1264 link.removeAttribute('data-orig-href');
1265 }
1266 if (link.hasAttribute('data-counter')) {
1267 link.removeAttribute('data-counter');
1268 link.removeAttribute('data-bem');
1269 }
1270 };
1271 let removeTracking = function(scope) {
1272 if (scope instanceof Element)
1273 for (let link of scope.querySelectorAll(selectors))
1274 removeTrackingAttributes(link);
1275 };
1276 _document.addEventListener('DOMContentLoaded', (e) => removeTracking(e.target));
1277 (new MutationObserver(
1278 function(ms) {
1279 let m, node;
1280 for (m of ms) for (node of m.addedNodes)
1281 if (node instanceof HTMLAnchorElement && node.matches(selectors))
1282 removeTrackingAttributes(node);
1283 else
1284 removeTracking(node);
1285 }
1286 )).observe(_de, { childList: true, subtree: true });
1287 }
1288
1289 // https://greasyfork.org/en/scripts/21937-moonwalk-hdgo-kodik-fix v0.8 (adapted)
1290 _document.addEventListener(
1291 'DOMContentLoaded', function() {
1292 let log = name => _console.log(`Player FIX: Detected ${name} player in ${location.href}`);
1293 function removeVast (data) {
1294 if (data && typeof data === 'object') {
1295 let keys = Object.getOwnPropertyNames(data);
1296 let isVast = name => /vast|clickunder/.test(name);
1297 if (!keys.some(isVast))
1298 return data;
1299 for (let key of keys)
1300 if (typeof data[key] === 'object' && key !== 'links') {
1301 _console.log(`Removed data.${key}`, data[key]);
1302 delete data[key];
1303 }
1304 if (data.chain) {
1305 let need = [],
1306 drop = [],
1307 links = data.chain.split('.');
1308 for (let link of links)
1309 if (!isVast(link))
1310 need.push(link);
1311 else
1312 drop.push(link);
1313 _console.log('Dropped from the chain:', ...drop);
1314 data.chain = need.join('.');
1315 }
1316 }
1317 return data;
1318 }
1319
1320 if ('video_balancer_options' in win && 'event_callback' in win) {
1321 log('Moonwalk');
1322 if (video_balancer_options.adv)
1323 removeVast(video_balancer_options.adv);
1324 if ('_mw_adb' in win)
1325 Object.defineProperty(win, '_mw_adb', {
1326 get: () => false,
1327 set: () => true
1328 });
1329 } else if (win.startKodikPlayer !== void 0) {
1330 log('Kodik');
1331 // skip attempt to block access to HD resolutions
1332 let chainCall = new Proxy({}, { get: () => () => chainCall });
1333 if ($ && $.prototype && $.prototype.addClass) {
1334 let $addClass = $.prototype.addClass;
1335 $.prototype.addClass = function (className) {
1336 if (className === 'blocked')
1337 return chainCall;
1338 return $addClass.apply(this, arguments);
1339 };
1340 }
1341 // remove ad links from the metadata
1342 let _ajax = win.$.ajax;
1343 win.$.ajax = (params, ...args) => {
1344 if (params.success) {
1345 let _s = params.success;
1346 params.success = (data, ...args) => _s(removeVast(data), ...args);
1347 }
1348 return _ajax(params, ...args);
1349 }
1350 } else if (win.getnextepisode && win.uppodEvent) {
1351 log('Share-Serials.net');
1352 scriptLander(
1353 function() {
1354 let _setInterval = win.setInterval,
1355 _setTimeout = win.setTimeout,
1356 _toString = Function.prototype.call.bind(Function.prototype.toString);
1357 win.setInterval = function(func) {
1358 if (func instanceof Function && _toString(func).includes('_delay')) {
1359 let intv = _setInterval.call(
1360 this, function() {
1361 _setTimeout.call(
1362 this, function(intv) {
1363 clearInterval(intv);
1364 let timer = _document.querySelector('#timer');
1365 if (timer)
1366 timer.click();
1367 }, 100, intv);
1368 func.call(this);
1369 }, 5
1370 );
1371
1372 return intv;
1373 }
1374 return _setInterval.apply(this, arguments);
1375 };
1376 win.setTimeout = function(func) {
1377 if (func instanceof Function && _toString(func).includes('adv_showed'))
1378 return _setTimeout.call(this, func, 0);
1379 return _setTimeout.apply(this, arguments);
1380 };
1381 }
1382 );
1383 } else if ('ADC' in win) {
1384 log('vjs-creatives plugin in');
1385 let replacer = (obj) => {
1386 for (let name in obj)
1387 if (obj[name] instanceof Function)
1388 obj[name] = () => null;
1389 };
1390 replacer(win.ADC);
1391 replacer(win.currentAdSlot);
1392 }
1393 UberVK: {
1394 if (!inIFrame)
1395 break UberVK;
1396 let oddNames = 'HD' in win &&
1397 !Object.getOwnPropertyNames(win).every(n => !n.startsWith('_0x'));
1398 if (!oddNames)
1399 break UberVK;
1400 log('UberVK');
1401 XMLHttpRequest.prototype.open = () => {
1402 throw 404;
1403 };
1404 }
1405 }, false
1406 );
1407
1408 // Applies wrapper function on the current page and all newly created same-origin iframes
1409 // This is used to prevent trick which allows to get fresh page API through newly created same-origin iframes
1410 function deepWrapAPI(wrapper) {
1411 let wrapped = new WeakSet(),
1412 _get_contentWindow = () => null,
1413 log = (...args) => false && _console.log(...args);
1414 let wrapAPI = root => {
1415 if (!root || wrapped.has(root))
1416 return;
1417 wrapped.add(root);
1418 try {
1419 wrapper(root instanceof HTMLIFrameElement ? _get_contentWindow(root) : root);
1420 log('Wrapped API in', (root === win) ? "main window." : root);
1421 } catch(e) {
1422 log('Failed to wrap API in', (root === win) ? "main window." : root, '\n', e);
1423 }
1424 };
1425
1426 // wrap API on contentWindow access
1427 let _apply = Function.prototype.apply;
1428 let _contentWindow = Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype, 'contentWindow');
1429 _get_contentWindow = _apply.bind(_contentWindow.get);
1430 _contentWindow.get = function() {
1431 wrapAPI(this);
1432 return _get_contentWindow(this);;
1433 };
1434 Object.defineProperty(HTMLIFrameElement.prototype, 'contentWindow', _contentWindow);
1435
1436 // wrap API on contentDocument access
1437 let _contentDocument = Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype, 'contentDocument');
1438 let _get_contentDocument = _apply.bind(_contentDocument.get);
1439 _contentDocument.get = function() {
1440 wrapAPI(this);
1441 return _get_contentDocument(this);
1442 };
1443 Object.defineProperty(HTMLIFrameElement.prototype, 'contentDocument', _contentDocument);
1444
1445 // manual children objects traverser to avoid issues
1446 // with calling querySelectorAll on wrong types of objects
1447 let _nodeType = _apply.bind(Object.getOwnPropertyDescriptor(_Node, 'nodeType').get);
1448 let _childNodes = _apply.bind(Object.getOwnPropertyDescriptor(_Node, 'childNodes').get);
1449 let _ELEMENT_NODE = _Node.ELEMENT_NODE;
1450 let _DOCUMENT_FRAGMENT_NODE = _Node.DOCUMENT_FRAGMENT_NODE
1451 let wrapFrames = root => {
1452 if (_nodeType(root) !== _ELEMENT_NODE && _nodeType(root) !== _DOCUMENT_FRAGMENT_NODE)
1453 return; // only process nodes which may contain an IFRAME or be one
1454 if (root instanceof HTMLIFrameElement) {
1455 wrapAPI(root);
1456 return;
1457 }
1458 for (let child of _childNodes(root))
1459 wrapFrames(child);
1460 };
1461
1462 // wrap API in a newly appended iframe objects
1463 let _appendChild = _apply.bind(Node.prototype.appendChild);
1464 Node.prototype.appendChild = function appendChild() {
1465 '[native code]';
1466 let res = _appendChild(this, arguments);
1467 wrapFrames(arguments[0]);
1468 return res;
1469 };
1470
1471 // wrap API in iframe objects created with innerHTML of element on page
1472 let _innerHTML = Object.getOwnPropertyDescriptor(_Element, 'innerHTML');
1473 let _set_innerHTML = _apply.bind(_innerHTML.set);
1474 _innerHTML.set = function() {
1475 _set_innerHTML(this, arguments);
1476 if (_document.contains(this))
1477 wrapFrames(this);
1478 };
1479 Object.defineProperty(_Element, 'innerHTML', _innerHTML);
1480
1481 wrapAPI(win);
1482 }
1483
1484 // piguiqproxy.com / zmctrack.net circumvention and onerror callback prevention
1485 scriptLander(
1486 () => {
1487 // onerror callback blacklist
1488 let masks = [],
1489 //blockAll = /(^|\.)(rutracker-org\.appspot\.com)$/,
1490 isBlocked = url => masks.some(mask => mask.test(url));// || blockAll.test(location.hostname);
1491 for (let filter of [// blacklist
1492 // global
1493 '/adv/www/',
1494 // adservers
1495 '||185.87.50.147^',
1496 '||10root25.website^', '||24video.xxx^',
1497 '||adlabs.ru^', '||adspayformymortgage.win^', '||amgload.net^', '||aviabay.ru^',
1498 '||bgrndi.com^', '||brokeloy.com^',
1499 '||cdnjs-aws.ru^','||cnamerutor.ru^',
1500 '||directadvert.ru^', '||dsn-fishki.ru^', '||docfilms.info^', '||dreadfula.ru^',
1501 '||et-cod.com^', '||et-code.ru^', '||etcodes.com^',
1502 '||franecki.net^', '||film-doma.ru^',
1503 '||free-torrent.org^', '||free-torrent.pw^',
1504 '||free-torrents.org^', '||free-torrents.pw^',
1505 '||game-torrent.info^', '||gocdn.ru^',
1506 '||hdkinoshka.com^', '||hghit.com^', '||hindcine.net^',
1507 '||kinotochka.net^', '||kinott.com^', '||kinott.ru^',
1508 '||klcheck.com^', '||kuveres.com^',
1509 '||lepubs.com^', '||luxadv.com^', '||luxup.ru^', '||luxupcdna.com^',
1510 '||marketgid.com^', '||mebablo.com^', '||mixadvert.com^', '||mxtads.com^',
1511 '||nickhel.com^',
1512 '||oconner.biz^', '||oconner.link^', '||octoclick.net^', '||octozoon.org^',
1513 '||pigiuqproxy.com^', '||piguiqproxy.com^', '||pkpojhc.com^',
1514 '||psma01.com^', '||psma02.com^', '||psma03.com^',
1515 '||rcdn.pro^', '||recreativ.ru^', '||redtram.com^', '||regpole.com^',
1516 '||rootmedia.ws^', '||ruttwind.com^', '||rutvind.com^',
1517 '||skidl.ru^', '||smi2.net^', '||smcheck.org^',
1518 '||torvind.com^', '||traffic-media.co^', '||trafmag.com^', '||trustjs.net^', '||ttarget.ru^',
1519 '||u-dot-id-adtool.appspot.com^', '||utarget.ru^',
1520 '||webadvert-gid.ru^', '||webadvertgid.ru^',
1521 '||xxuhter.ru^',
1522 '||yuiout.online^',
1523 '||zmctrack.net^', '||zoom-film.ru^'])
1524 masks.push(new RegExp(
1525 filter.replace(/([\\/[\].+?(){}$])/g, '\\$1')
1526 .replace(/\*/g, '.*?')
1527 .replace(/\^(?!$)/g,'\\.?[^\\w%._-]')
1528 .replace(/\^$/,'\\.?([^\\w%._-]|$)')
1529 .replace(/^\|\|/,'^(ws|http)s?:\\/+([^/.]+\\.)*?'),
1530 'i'));
1531 // main script
1532 deepWrapAPI(root => {
1533 let _call = root.Function.prototype.call,
1534 _defineProperty = root.Object.defineProperty,
1535 _getOwnPropertyDescriptor = root.Object.getOwnPropertyDescriptor;
1536 onerror: {
1537 // 'onerror' handler for scripts from blacklisted sources
1538 let scriptMap = new WeakMap();
1539 let _Reflect_apply = root.Reflect.apply,
1540 _HTMLScriptElement = root.HTMLScriptElement,
1541 _HTMLImageElement = root.HTMLImageElement;
1542 let _get_tagName = _call.bind(_getOwnPropertyDescriptor(root.Element.prototype, 'tagName').get),
1543 _get_scr_src = _call.bind(_getOwnPropertyDescriptor(_HTMLScriptElement.prototype, 'src').get),
1544 _get_img_src = _call.bind(_getOwnPropertyDescriptor(_HTMLImageElement.prototype, 'src').get);
1545 let _get_src = node => {
1546 if (node instanceof _HTMLScriptElement)
1547 return _get_scr_src(node);
1548 if (node instanceof _HTMLImageElement)
1549 return _get_img_src(node);
1550 return void 0
1551 };
1552 let _onerror = _getOwnPropertyDescriptor(root.HTMLElement.prototype, 'onerror'),
1553 _set_onerror = _call.bind(_onerror.set);
1554 _onerror.get = function() {
1555 return scriptMap.get(this) || null;
1556 };
1557 _onerror.set = function(callback) {
1558 if (typeof callback !== 'function') {
1559 scriptMap.delete(this);
1560 _set_onerror(this, callback);
1561 return;
1562 }
1563 scriptMap.set(this, callback);
1564 _set_onerror(this, function() {
1565 let src = _get_src(this);
1566 if (isBlocked(src)) {
1567 _console.warn(`Blocked "onerror" callback from ${_get_tagName(this)}: ${src}`);
1568 return;
1569 }
1570 _Reflect_apply(scriptMap.get(this), this, arguments);
1571 });
1572 };
1573 _defineProperty(root.HTMLElement.prototype, 'onerror', _onerror);
1574 }
1575 // Simplistic WebSocket wrapper for Maxthon and Firefox before v58
1576 WSWrap: { // once again seems required in Google Chrome and similar browsers due to zmctrack.net -_-
1577 if (true /*/Maxthon/.test(navigator.appVersion) ||
1578 'InstallTrigger' in win && 'StopIteration' in win*/) {
1579 let _ws = _getOwnPropertyDescriptor(root, 'WebSocket');
1580 if (!_ws)
1581 break WSWrap;
1582 _ws.value = new Proxy(_ws.value, {
1583 construct: (ws, args) => {
1584 if (isBlocked(args[0])) {
1585 _console.log('Blocked WS connection:', args[0]);
1586 return {};
1587 }
1588 return new ws(...args);
1589 }
1590 });
1591 _defineProperty(root, 'WebSocket', _ws);
1592 }
1593 }
1594 untrustedClick: {
1595 // Block popular method to open a new window in Google Chrome by dispatching a custom click
1596 // event on a newly created anchor with _blank target. Untrusted events must not open new windows.
1597 let _dispatchEvent = _call.bind(root.EventTarget.prototype.dispatchEvent);
1598 root.EventTarget.prototype.dispatchEvent = function dispatchEvent(e) {
1599 if (!e.isTrusted && e.type === 'click' && e.constructor.name === 'MouseEvent' &&
1600 !this.parentNode && this.tagName === 'A' && this.target[0] === '_') {
1601 _console.log('Blocked dispatching a click event on a parentless anchor:', this);
1602 return;
1603 }
1604 return _dispatchEvent(this, ...arguments);
1605 };
1606 }
1607 // XHR Wrapper
1608 let _proto = void 0;
1609 try {
1610 _proto = root.XMLHttpRequest.prototype;
1611 } catch(ignore) {
1612 return;
1613 };
1614 // blacklist of domains where all third-party requests are ignored
1615 let ondomains = /(^|[/.@])oane\.ws($|[:/])/i;
1616 // highly suspicious URLs
1617 let suspicious = /^(https?:)?\/\/(?!rutube\.ru[:/])(csp-)?([a-z0-9]{6}){1,2}\.ru\//i;
1618 let on_get_ban = /^(https?:)?\/\/(?!rutube\.ru[:/])(csp-)?([a-z0-9]{6}){1,2}\.ru\/([a-z0-9/]{40,}|[a-z0-9]{8,}|ad\/banner\/.+|show\/\?\d+=\d+&.+)$/i;
1619 let on_post_ban = /^(https?:)?\/\/(?!rutube\.ru[:/])(csp-)?([a-z0-9]{6}){1,2}\.ru\/([a-z0-9]{6,})$/i;
1620 let yandex_direct = /^(https?:)?\/\/([^.]+\.)??yandex(\.[a-z]{2,3}){1,2}\/((images|weather)\/[a-z0-9/_-]{40,}|jstracer?|j?clck\/.*|set\/s\/rsya-tag-users\/data(\?.*)?|static\/main\.js(\?.*)?)$/i;
1621 let more_y_direct = /^(https?:)?\/\/([^.]+\.)??(24smi\.org|(echo\.msk|kakprosto|liveinternet|razlozhi)\.ru)\/(.{290,}|[a-z0-9/_-]{100,})$/i;
1622 let whitelist = /^(https?:)?\/\/yandex\.ru\/yobject$/;
1623 let fabPatterns = /\/fuckadblock/i;
1624
1625 let blockedUrls = new Set();
1626 function checkRequest(fname, method, url) {
1627 if ((isBlocked(url) ||
1628 ondomains.test(location.hostname) && !ondomains.test(url) ||
1629 method !== 'POST' && on_get_ban.test(url) ||
1630 method === 'POST' && on_post_ban.test(url) ||
1631 yandex_direct.test(url) || more_y_direct.test(url)) && !whitelist.test(url)) {
1632 if (!blockedUrls.has(url)) // don't repeat log if the same URL were blocked more than once
1633 _console.warn(`Blocked ${fname} ${method} request:`, url);
1634 blockedUrls.add(url);
1635 return true;
1636 }
1637 if (suspicious.test(url))
1638 _console.warn(`Suspicious ${fname} ${method} request:`, url);
1639 return false;
1640 }
1641
1642 // workaround for a broken weather mini-map on Yandex
1643 let skip_xhr_check = false;
1644 if (root.location.hostname.startsWith('yandex.') &&
1645 root.location.pathname.startsWith('/pogoda/') ||
1646 root.location.hostname.endsWith('.kakprosto.ru'))
1647 skip_xhr_check = true;
1648
1649 let xhrStopList = new WeakSet();
1650 let _open = root.Function.prototype.apply.bind(_proto.open);
1651 _proto.open = function open() {
1652 '[native code]';
1653 return !skip_xhr_check && checkRequest('xhr', ...arguments) ?
1654 (xhrStopList.add(this), void 0) : _open(this, arguments);
1655 };
1656 ['send', 'setRequestHeader', 'getAllResponseHeaders'].forEach(
1657 name => {
1658 let func = _proto[name];
1659 _proto[name] = function(...args) {
1660 return xhrStopList.has(this) ? null : func.apply(this, args);
1661 };
1662 }
1663 );
1664 // simulate readyState === 1 for blocked requests
1665 let _readyState = Object.getOwnPropertyDescriptor(_proto, 'readyState');
1666 let _get_readyState = root.Function.prototype.apply.bind(_readyState.get);
1667 _readyState.get = function() {
1668 return xhrStopList.has(this) ? 1 : _get_readyState(this, arguments);
1669 }
1670 Object.defineProperty(_proto, 'readyState', _readyState);
1671
1672 let _fetch = root.Function.prototype.apply.bind(root.fetch);
1673 root.fetch = function fetch() {
1674 '[native code]';
1675 let url = arguments[0];
1676 let method = arguments[1] ? arguments[1].method : void 0;
1677 if (arguments[0] instanceof Request) {
1678 method = url.method;
1679 url = url.url;
1680 }
1681 if (checkRequest('fetch', method, url))
1682 return new Promise(() => null);
1683 return _fetch(root, arguments);
1684 };
1685
1686 let _script_src = Object.getOwnPropertyDescriptor(root.HTMLScriptElement.prototype, 'src');
1687 let _script_src_set = root.Function.prototype.apply.bind(_script_src.set);
1688 let _dispatchEvent = root.Function.prototype.call.bind(root.EventTarget.prototype.dispatchEvent);
1689 _script_src.set = function(src) {
1690 if (fabPatterns.test(src)) {
1691 _console.warn(`Blocked set script.src request:`, src);
1692 deployFABStub(root);
1693 setTimeout(() => {
1694 let e = root.document.createEvent('Event');
1695 e.initEvent('load', false, false);
1696 _dispatchEvent(this, e);
1697 }, 0);
1698 return;
1699 }
1700 return checkRequest('set', 'script.src', src) || _script_src_set(this, arguments);
1701 };
1702 Object.defineProperty(root.HTMLScriptElement.prototype, 'src', _script_src);
1703
1704 let adregain_pattern = /ggg==" alt="advertisement"/;
1705 if (root.self !== root.top) { // in IFrame
1706 let _write = Function.prototype.call.bind(root.document.write);
1707 root.document.write = function write(text, ...args) {
1708 "[native code]";
1709 if (adregain_pattern.test(text)) {
1710 _console.log('Skipped AdRegain frame.');
1711 return _write(this, '');
1712 }
1713 return _write(this, text, ...args);
1714 };
1715 }
1716 });
1717
1718 win.stop = () => {
1719 _console.warn('window.stop() ...y tho?');
1720 for (let sheet of _document.styleSheets)
1721 if (sheet.disabled) {
1722 sheet.disabled = false;
1723 _console.log('Re-enabled:', sheet);
1724 }
1725 }
1726 }, deepWrapAPI
1727 );
1728
1729 // === Helper functions ===
1730
1731 // function to search and remove nodes by content
1732 // selector - standard CSS selector to define set of nodes to check
1733 // words - regular expression to check content of the suspicious nodes
1734 // params - object with multiple extra parameters:
1735 // .log - display log in the console
1736 // .hide - set display to none instead of removing from the page
1737 // .parent - parent node to remove if content is found in the child node
1738 // .siblings - number of simling nodes to remove (excluding text nodes)
1739 let scRemove = (node) => node.parentNode.removeChild(node);
1740 let scHide = function(node) {
1741 let style = _getAttribute(node, 'style') || '',
1742 hide = ';display:none!important;';
1743 if (style.indexOf(hide) < 0)
1744 _setAttribute(node, 'style', style + hide);
1745 };
1746
1747 function scissors (selector, words, scope, params) {
1748 let logger = (...args) => { if (params.log) _console.log(...args) };
1749 if (!scope.contains(_document.body))
1750 logger('[s] scope', scope);
1751 let remFunc = (params.hide ? scHide : scRemove),
1752 iterFunc = (params.siblings > 0 ? 'nextElementSibling' : 'previousElementSibling'),
1753 toRemove = [],
1754 siblings;
1755 for (let node of scope.querySelectorAll(selector)) {
1756 // drill up to a parent node if specified, break if not found
1757 if (params.parent) {
1758 let old = node;
1759 node = node.closest(params.parent);
1760 if (node === null || node.contains(scope)) {
1761 logger('[s] went out of scope with', old);
1762 continue;
1763 }
1764 }
1765 logger('[s] processing', node);
1766 if (toRemove.includes(node))
1767 continue;
1768 if (words.test(node.innerHTML)) {
1769 // skip node if already marked for removal
1770 logger('[s] marked for removal');
1771 toRemove.push(node);
1772 // add multiple nodes if defined more than one sibling
1773 siblings = Math.abs(params.siblings) || 0;
1774 while (siblings) {
1775 node = node[iterFunc];
1776 if (!node) break; // can't go any further - exit
1777 logger('[s] adding sibling node', node);
1778 toRemove.push(node);
1779 siblings -= 1;
1780 }
1781 }
1782 }
1783 let toSkip = [];
1784 for (let node of toRemove)
1785 if (!toRemove.every(other => other === node || !node.contains(other)))
1786 toSkip.push(node);
1787 if (toRemove.length)
1788 logger(`[s] proceeding with ${params.hide?'hide':'removal'} of`, toRemove, `skip`, toSkip);
1789 for (let node of toRemove) if (!toSkip.includes(node))
1790 remFunc(node);
1791 }
1792
1793 // function to perform multiple checks if ads inserted with a delay
1794 // by default does 30 checks withing a 3 seconds unless nonstop mode specified
1795 // also does 1 extra check when a page completely loads
1796 // selector and words - passed dow to scissors
1797 // params - object with multiple extra parameters:
1798 // .log - display log in the console
1799 // .root - selector to narrow down scope to scan;
1800 // .observe - if true then check will be performed continuously;
1801 // Other parameters passed down to scissors.
1802 function gardener(selector, words, params) {
1803 let logger = (...args) => { if (params.log) _console.log(...args) };
1804 params = params || {};
1805 logger(`[gardener] selector: '${selector}' detector: ${words} options: ${JSON.stringify(params)}`);
1806 let scope;
1807 let globalScope = [_de];
1808 let domLoaded = false;
1809 let getScope = root => root ? _de.querySelectorAll(root) : globalScope;
1810 let onevent = e => {
1811 logger(`[gardener] cleanup on ${Object.getPrototypeOf(e)} "${e.type}"`);
1812 for (let node of scope)
1813 scissors(selector, words, node, params);
1814 };
1815 let repeater = n => {
1816 if (!domLoaded && n) {
1817 setTimeout(repeater, 500, n - 1);
1818 scope = getScope(params.root);
1819 if (!scope) // exit if the root element is not present on the page
1820 return 0;
1821 onevent({type: 'Repeater'});
1822 }
1823 };
1824 repeater(20);
1825 _document.addEventListener(
1826 'DOMContentLoaded', (e) => {
1827 domLoaded = true;
1828 // narrow down scope to a specific element
1829 scope = getScope(params.root);
1830 if (!scope) // exit if the root element is not present on the page
1831 return 0;
1832 logger('[g] scope', scope);
1833 // add observe mode if required
1834 if (params.observe) {
1835 let params = { childList:true, subtree: true };
1836 let observer = new MutationObserver(
1837 function(ms) {
1838 for (let m of ms)
1839 if (m.addedNodes.length)
1840 onevent(m);
1841 }
1842 );
1843 for (let node of scope)
1844 observer.observe(node, params);
1845 logger('[g] observer enabled');
1846 }
1847 onevent(e);
1848 }, false);
1849 // wait for a full page load to do one extra cut
1850 win.addEventListener('load', onevent, false);
1851 }
1852
1853 // wrap popular methods to open a new tab to catch specific behaviours
1854 function createWindowOpenWrapper(openFunc) {
1855 let _createElement = _Document.createElement,
1856 _appendChild = _Element.appendChild,
1857 fakeNative = (f) => (f.toString = () => `function ${f.name}() { [native code] }`);
1858
1859 let nt = new nullTools();
1860 fakeNative(openFunc);
1861
1862 let parser = _createElement.call(_document, 'a');
1863 let openWhitelist = (url, parent) => {
1864 parser.href = url;
1865 return parser.hostname === 'www.imdb.com' || parser.hostname === 'www.kinopoisk.ru' ||
1866 parent.hostname === 'radikal.ru' && url === void 0;
1867 };
1868
1869 let redefineOpen = (root) => {
1870 if ('open' in root) {
1871 let _open = root.open.bind(root);
1872 nt.define(root, 'open', (...args) => {
1873 if (openWhitelist(args[0], location)) {
1874 _console.log('Whitelisted popup:', ...args);
1875 return _open(...args);
1876 }
1877 return openFunc(...args);
1878 });
1879 }
1880 };
1881 redefineOpen(win);
1882
1883 function createElement() {
1884 '[native code]';
1885 let el = _createElement.apply(this, arguments);
1886 // redefine window.open in first-party frames
1887 if (el instanceof HTMLIFrameElement || el instanceof HTMLObjectElement)
1888 el.addEventListener('load', (e) => {
1889 try {
1890 redefineOpen(e.target.contentWindow);
1891 } catch(ignore) {}
1892 }, false);
1893 return el;
1894 }
1895 fakeNative(createElement);
1896
1897 let redefineCreateElement = (obj) => {
1898 for (let root of [obj.document, _Document]) if ('createElement' in root)
1899 nt.define(root, 'createElement', createElement);
1900 };
1901 redefineCreateElement(win);
1902
1903 // wrap window.open in newly added first-party frames
1904 _Element.appendChild = function appendChild() {
1905 '[native code]';
1906 let el = _appendChild.apply(this, arguments);
1907 if (el instanceof HTMLIFrameElement)
1908 try {
1909 redefineOpen(el.contentWindow);
1910 redefineCreateElement(el.contentWindow);
1911 } catch(ignore) {}
1912 return el;
1913 };
1914 fakeNative(_Element.appendChild);
1915 }
1916
1917 // Function to catch and block various methods to open a new window with 3rd-party content.
1918 // Some advertisement networks went way past simple window.open call to circumvent default popup protection.
1919 // This funciton blocks window.open, ability to restore original window.open from an IFRAME object,
1920 // ability to perform an untrusted (not initiated by user) click on a link, click on a link without a parent
1921 // node or simply a link with piece of javascript code in the HREF attribute.
1922 function preventPopups() {
1923 // call sandbox-me if in iframe and not whitelisted
1924 if (inIFrame) {
1925 win.top.postMessage({ name: 'sandbox-me', href: win.location.href }, '*');
1926 return;
1927 }
1928
1929 scriptLander(() => {
1930 let nt = new nullTools({log:true});
1931 let open = (...args) => {
1932 '[native code]';
1933 _console.warn('Site attempted to open a new window', ...args);
1934 return {
1935 document: nt.proxy({
1936 write: nt.func({}, 'write'),
1937 writeln: nt.func({}, 'writeln')
1938 }),
1939 location: nt.proxy({})
1940 };
1941 };
1942
1943 createWindowOpenWrapper(open);
1944
1945 _console.log('Popup prevention enabled.');
1946 }, nullTools, createWindowOpenWrapper);
1947 }
1948
1949 // Helper function to close background tab if site opens itself in a new tab and then
1950 // loads a 3rd-party page in the background one (thus performing background redirect).
1951 function preventPopunders() {
1952 // create "close_me" event to call high-level window.close()
1953 let eventName = `close_me_${Math.random().toString(36).substr(2)}`;
1954 let callClose = () => {
1955 _console.log('close call');
1956 window.close();
1957 };
1958 window.addEventListener(eventName, callClose, true);
1959
1960 scriptLander(() => {
1961 // get host of a provided URL with help of an anchor object
1962 // unfortunately new URL(url, window.location) generates wrong URL in some cases
1963 let parseURL = _document.createElement('A');
1964 let getHost = url => {
1965 parseURL.href = url;
1966 return parseURL.hostname
1967 };
1968 // site went to a new tab and attempts to unload
1969 // call for high-level close through event
1970 let closeWindow = () => window.dispatchEvent(new CustomEvent(eventName, {}));
1971 // check is URL local or goes to different site
1972 let isLocal = (url) => {
1973 if (url === location.pathname || url === location.href)
1974 return true; // URL points to current pathname or full address
1975 let host = getHost(url);
1976 let site = location.hostname;
1977 return host !== '' && // URLs with unusual protocol may have empty 'host'
1978 (site === host || site.endsWith(`.${host}`) || host.endsWith(`.${site}`));
1979 };
1980
1981 let _open = window.open.bind(window);
1982 let open = (...args) => {
1983 '[native code]';
1984 let url = args[0];
1985 if (url && isLocal(url))
1986 window.addEventListener('beforeunload', closeWindow, true);
1987 return _open(...args);
1988 };
1989
1990 createWindowOpenWrapper(open);
1991
1992 _console.log("Background redirect prevention enabled.");
1993 }, `let eventName="${eventName}"`, nullTools, createWindowOpenWrapper);
1994 }
1995
1996 // Mix between check for popups and popunders
1997 // Significantly more agressive than both and can't be used as universal solution
1998 function preventPopMix() {
1999 if (inIFrame) {
2000 win.top.postMessage({ name: 'sandbox-me', href: win.location.href }, '*');
2001 return;
2002 }
2003
2004 // create "close_me" event to call high-level window.close()
2005 let eventName = `close_me_${Math.random().toString(36).substr(2)}`;
2006 let callClose = () => {
2007 _console.log('close call');
2008 window.close();
2009 };
2010 window.addEventListener(eventName, callClose, true);
2011
2012 scriptLander(() => {
2013 let _open = window.open,
2014 parseURL = _document.createElement('A');
2015 // get host of a provided URL with help of an anchor object
2016 // unfortunately new URL(url, window.location) generates wrong URL in some cases
2017 let getHost = (url) => {
2018 parseURL.href = url;
2019 return parseURL.host;
2020 };
2021 // site went to a new tab and attempts to unload
2022 // call for high-level close through event
2023 let closeWindow = () => {
2024 _open(window.location,'_self');
2025 window.dispatchEvent(new CustomEvent(eventName, {}));
2026 };
2027 // check is URL local or goes to different site
2028 function isLocal(url) {
2029 let loc = window.location;
2030 if (url === loc.pathname || url === loc.href)
2031 return true; // URL points to current pathname or full address
2032 let host = getHost(url),
2033 site = loc.host;
2034 if (host === '')
2035 return false; // URLs with unusual protocol may have empty 'host'
2036 if (host.length > site.length)
2037 [site, host] = [host, site];
2038 return site.includes(host, site.length - host.length);
2039 }
2040
2041 // add check for redirect for 5 seconds, then disable it
2042 function checkRedirect() {
2043 window.addEventListener('beforeunload', closeWindow, true);
2044 setTimeout(closeWindow=>window.removeEventListener('beforeunload', closeWindow, true), 5000, closeWindow);
2045 }
2046
2047 function open(url, name) {
2048 '[native code]';
2049 if (url && isLocal(url) && (!name || name === '_blank')) {
2050 _console.warn('Suspicious local new window', arguments);
2051 checkRedirect();
2052 return _open.apply(this, arguments);
2053 }
2054 _console.warn('Blocked attempt to open a new window', arguments);
2055 return {
2056 document: {
2057 write: () => {},
2058 writeln: () => {}
2059 }
2060 };
2061 }
2062
2063 function clickHandler(e) {
2064 let link = e.target,
2065 url = link.href||'';
2066 if (e.targetParentNode && e.isTrusted || link.target !== '_blank') {
2067 _console.log('Link', link, 'were created dinamically, but looks fine.');
2068 return true;
2069 }
2070 if (isLocal(url) && link.target === '_blank') {
2071 _console.log('Suspicious local link', link);
2072 checkRedirect();
2073 return;
2074 }
2075 _console.log('Blocked suspicious click on a link', link);
2076 e.stopPropagation();
2077 e.preventDefault();
2078 }
2079
2080 createWindowOpenWrapper(open, clickHandler);
2081
2082 _console.log("Mixed popups prevention enabled.");
2083 }, `let eventName="${eventName}"`, createWindowOpenWrapper);
2084 }
2085 // External listener for case when site known to open popups were loaded in iframe
2086 // It will sandbox any iframe which will send message 'forbid.popups' (preventPopups sends it)
2087 // Some sites replace frame's window.location with data-url to run in clean context
2088 if (!inIFrame) window.addEventListener(
2089 'message', function(e) {
2090 if (!e.data || e.data.name !== 'sandbox-me' || !e.data.href)
2091 return;
2092 let src = e.data.href;
2093 for (let frame of _document.querySelectorAll('iframe'))
2094 if (frame.contentWindow === e.source) {
2095 if (frame.hasAttribute('sandbox')) {
2096 if (!frame.sandbox.contains('allow-popups'))
2097 return; // exit frame since it's already sandboxed and popups are blocked
2098 // remove allow-popups if frame already sandboxed
2099 frame.sandbox.remove('allow-popups');
2100 } else
2101 // set sandbox mode for troublesome frame and allow scripts, forms and a few other actions
2102 // technically allowing both scripts and same-origin allows removal of the sandbox attribute,
2103 // but to apply content must be reloaded and this script will re-apply it in the result
2104 frame.setAttribute('sandbox','allow-forms allow-scripts allow-presentation allow-top-navigation allow-same-origin');
2105 _console.log('Disallowed popups from iframe', frame);
2106
2107 // reload frame content to apply restrictions
2108 if (!src) {
2109 src = frame.src;
2110 _console.log('Unable to get current iframe location, reloading from src', src);
2111 } else
2112 _console.log('Reloading iframe with URL', src);
2113 frame.src = 'about:blank';
2114 frame.src = src;
2115 }
2116 }, false
2117 );
2118
2119 let evalPatternYandex = /{exports:{},id:r,loaded:!1}|containerId:(.|\r|\n)+params:/;
2120 let evalPatternGeneric = /_0x|location\s*?=|location.href\s*?=|location.assign\(|open\(/i;
2121 function selectiveEval(...patterns) {
2122 if (patterns.length === 0)
2123 patterns.push(evalPatternGeneric);
2124 scriptLander(() => {
2125 let _eval_def = Object.getOwnPropertyDescriptor(win, 'eval');
2126 if (!_eval_def || !_eval_def.value) {
2127 _console.warn('Unable to wrap window.eval.', _eval_def);
2128 return;
2129 }
2130 let _eval_val = _eval_def.value;
2131 _eval_def.value = function(...args) {
2132 if (patterns.some(pattern => pattern.test(args[0]))) {
2133 _console.warn(`Skipped eval of ${args[0].slice(0, 512)}\u2026`);
2134 return null;
2135 }
2136 try {
2137 return _eval_val.apply(this, args);
2138 } catch(e) {
2139 _console.log('Crash source:', args[0]);
2140 throw e;
2141 }
2142 };
2143 Object.defineProperty(win, 'eval', _eval_def);
2144 }, `let patterns = [${patterns}];`);
2145 }
2146
2147 // hides cookies by pattern and attempts to remove them if they already set
2148 // also prevents setting new versions of such cookies
2149 function selectiveCookies(scPattern = '', scPaths = []) {
2150 scriptLander(() => {
2151 let patterns = scPattern.split('|');
2152 if (patterns[0] !== ';default') {
2153 // Google Analytics cookies
2154 patterns.push('_g(at?|id)|__utm[a-z]');
2155 // Yandex ABP detection cookies
2156 patterns.push('bltsr|blcrm');
2157 } else
2158 patterns.shift();
2159 let blacklist = new RegExp(`(^|;\\s?)(${patterns.join('|')})($|=)`);
2160 if (isFirefox && scPaths.length)
2161 scPaths = scPaths.map(path => `${path}/`);
2162 scPaths.push('/');
2163 let _doc_proto = ('cookie' in _Document) ? _Document : Object.getPrototypeOf(_document);
2164 let _cookie = Object.getOwnPropertyDescriptor(_doc_proto, 'cookie');
2165 if (_cookie) {
2166 let _set_cookie = Function.prototype.call.bind(_cookie.set);
2167 let _get_cookie = Function.prototype.call.bind(_cookie.get);
2168 let expireDate = 'Thu, 01 Jan 1970 00:00:01 UTC';
2169 let expireAge = '-99999999';
2170 let expireBase = `=;expires=${expireDate};Max-Age=${expireAge}`;
2171 let expireAttempted = {};
2172 // expire is called from cookie getter and doesn't know exact parameters used to set cookies present there
2173 // so, it will use path=/ by default if scPaths wasn't set and attempt to set cookies on all parent domains
2174 let expire = (cookie, that) => {
2175 let domain = that.location.hostname.split('.'),
2176 name = cookie.replace(/=.*/,'');
2177 scPaths.forEach(path =>_set_cookie(that, `${name}${expireBase};path=${path}`));
2178 while (domain.length > 1) {
2179 try {
2180 scPaths.forEach(
2181 path => _set_cookie(that, `${name}${expireBase};domain=${domain.join('.')};path=${path}`)
2182 );
2183 } catch(e) { _console.warn(e); }
2184 domain.shift();
2185 }
2186 expireAttempted[name] = true;
2187 _console.log('Removing existing cookie:', cookie);
2188 };
2189 // skip setting unwanted cookies
2190 _cookie.set = function(value) {
2191 if (blacklist.test(value)) {
2192 _console.warn('Ignored cookie:', value);
2193 // try to remove same cookie if it already exists using exact values from the set string
2194 if (blacklist.test(_get_cookie(this))) {
2195 let parts = value.split(/;\s?/),
2196 name = parts[0].replace(/=.*/,''),
2197 newParts = [`${name}=`, `expires=${expireDate}`, `Max-Age=${expireAge}`],
2198 skip = [name, 'expires', 'Max-Age'];
2199 for (let part of parts)
2200 if (!skip.includes(part.replace(/=.*/,'')))
2201 newParts.push(part);
2202 try {
2203 _set_cookie(this, newParts.join(';'));
2204 } catch(e) { _console.warn(e); }
2205 _console.log('Removing existing cookie:', name);
2206 }
2207 return;
2208 }
2209 return _set_cookie(this, value);
2210 };
2211 // hide unwanted cookies from site
2212 _cookie.get = function() {
2213 let res = _get_cookie(this);
2214 if (blacklist.test(res)) {
2215 let stack = [];
2216 for (let cookie of res.split(/;\s?/))
2217 if (!blacklist.test(cookie))
2218 stack.push(cookie);
2219 else {
2220 let name = cookie.replace(/=.*/,'');
2221 if (expireAttempted[name]) {
2222 _console.log('Unable to expire:', cookie);
2223 expireAttempted[name] = false;
2224 }
2225 if (!(name in expireAttempted))
2226 expire(cookie, this);
2227 }
2228 res = stack.join('; ');
2229 }
2230 return res;
2231 };
2232 Object.defineProperty(_doc_proto, 'cookie', _cookie);
2233 _console.log('Active cookies:', win.document.cookie);
2234 }
2235 }, `let scPattern = "${scPattern}", scPaths = ${JSON.stringify(scPaths)}, isFirefox = ${isFirefox};`);
2236 }
2237
2238 /*{ // simple toString wrapper, might be useful to prevent detection
2239 '[native code]';
2240 let _toString = Function.prototype.apply.bind(Function.prototype.toString);
2241 let baseText = Function.prototype.toString.toString();
2242 let protect = new WeakSet();
2243 protect.add(_Document.createElement);
2244 protect.add(_Node.appendChild);
2245 protect.add(_Node.removeChild);
2246 win.Function.prototype.toString = function() {
2247 if (protect.has(this))
2248 return baseText.replace('toString', this.name);
2249 return _toString(this);
2250 };
2251 protect.add(Function.prototype.toString);
2252 }*/
2253
2254 // Locates a node with specific text in Russian
2255 // Uses table of substitutions for similar letters
2256 let selectNodeByTextContent = (()=> {
2257 let subs = {
2258 // english & greek
2259 'Ð': 'AΑ', 'Ð’': 'BÎ’', 'Г':'Γ',
2260 'Е': 'EΕ', 'З': '3', 'К':'KΚ',
2261 'М': 'MΜ', 'Ð': 'HΗ', 'О':'OΟ',
2262 'П': 'Π', 'Р': 'PΡ', 'С':'C',
2263 'Т': 'T', 'Ф': 'Φ', 'Х':'XΧ'
2264 }
2265 let regExpBuilder = text => new RegExp(
2266 text.toUpperCase()
2267 .split('')
2268 .map(function(e){
2269 return `${e in subs ? `[${e}${subs[e]}]` : (e === ' ' ? '\\s+' : e)}[\u200b\u200c\u200d]*`;
2270 })
2271 .join(''),
2272 'i');
2273 let reMap = {};
2274 return (re, opts = { root: _document.body }) => {
2275 if (!re.test) {
2276 if (!reMap[re])
2277 reMap[re] = regExpBuilder(re);
2278 re = reMap[re];
2279 }
2280
2281 for (let child of opts.root.children)
2282 if (re.test(child.textContent)) {
2283 if (opts.shallow)
2284 return child;
2285 opts.root = child;
2286 return selectNodeByTextContent(re, opts) || child;
2287 }
2288 }
2289 })();
2290
2291 // webpackJsonp filter
2292 function webpackJsonpFilter(blacklist) {
2293 let _apply = Reflect.apply;
2294 let _toString = Function.prototype.call.bind(Function.prototype.toString);
2295 function wrapPush(webpack) {
2296 let _push = webpack.push.bind(webpack);
2297 Object.defineProperty(webpack, 'push', {
2298 get: () => _push,
2299 set: vl => {
2300 _push = new Proxy(vl, {
2301 apply: (push, obj, args) => {
2302 wrapper: {
2303 if (!(args[0] instanceof Array))
2304 break wrapper;
2305 let mainName;
2306 if (args[0][2] instanceof Array && args[0][2][0] instanceof Array)
2307 mainName = args[0][2][0][0];
2308 let funs = args[0][1];
2309 if (!(funs instanceof Object && !(funs instanceof Array)))
2310 break wrapper;
2311 for (let name in funs) {
2312 if (typeof funs[name] !== 'function')
2313 continue;
2314 if (blacklist.test(_toString(funs[name])) && name !== mainName)
2315 funs[name] = () => _console.log(`Skip webpack ${name}`);
2316 }
2317 }
2318 _console.log('webpack.push()');
2319 return _apply(push, obj, args);
2320 }
2321 });
2322 return true;
2323 }
2324 });
2325 return webpack
2326 }
2327 let _webpackJsonp = wrapPush([]);
2328 Object.defineProperty(win, 'webpackJsonp', {
2329 get: () => _webpackJsonp,
2330 set: vl => {
2331 if (vl === _webpackJsonp)
2332 return;
2333 _console.log('new webpackJsonp', vl);
2334 _webpackJsonp = wrapPush(vl);
2335 return true;
2336 }
2337 });
2338 }
2339
2340 // === Scripts for specific domains ===
2341
2342 let scripts = {};
2343 // prevent popups and redirects block
2344 // Popups
2345 scripts.preventPopups = {
2346 other: [
2347 'biqle.ru',
2348 'chaturbate.com',
2349 'dfiles.ru',
2350 'eporner.eu',
2351 'hentaiz.org',
2352 'mirrorcreator.com',
2353 'online-multy.ru',
2354 'radikal.ru', 'rumedia.ws',
2355 'tapehub.tech', 'thepiratebay.org',
2356 'unionpeer.com',
2357 'zippyshare.com'
2358 ],
2359 now: preventPopups
2360 };
2361 // Popunders (background redirect)
2362 scripts.preventPopunders = {
2363 other: [
2364 'lostfilm-online.ru',
2365 'mediafire.com', 'megapeer.org', 'megapeer.ru',
2366 'perfectgirls.net'
2367 ],
2368 now: preventPopunders
2369 };
2370 // PopMix (both types of popups encountered on site)
2371 scripts['openload.co'] = {
2372 other: ['oload.tv', 'oload.info'],
2373 now: () => {
2374 let nt = new nullTools();
2375 nt.define(win, 'CNight', win.CoinHive);
2376 if (location.pathname.startsWith('/embed/')) {
2377 nt.define(win, 'BetterJsPop', {
2378 add: ((a, b) => _console.warn('BetterJsPop.add', a, b)),
2379 config: ((o) => _console.warn('BetterJsPop.config', o)),
2380 Browser: { isChrome: true }
2381 });
2382 nt.define(win, 'isSandboxed', nt.func(null));
2383 nt.define(win, 'adblock', false);
2384 nt.define(win, 'adblock2', false);
2385 } else preventPopMix();
2386 }
2387 };
2388 scripts['turbobit.net'] = preventPopMix;
2389
2390 scripts['tapochek.net'] = () => {
2391 // workaround for moradu.com/apu.php load error handler script, not sure which ad network is this
2392 let _appendChild = Object.getOwnPropertyDescriptor(_Node, 'appendChild');
2393 let _appendChild_value = _appendChild.value;
2394 _appendChild.value = function appendChild(node) {
2395 if (this === _document.body)
2396 if ((node instanceof HTMLScriptElement || node instanceof HTMLStyleElement) &&
2397 /^https?:\/\/[0-9a-f]{15}\.com\/\d+(\/|\.css)$/.test(node.src) ||
2398 node instanceof HTMLDivElement && node.style.zIndex > 900000 &&
2399 node.style.backgroundImage.includes('R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7'))
2400 throw '...eenope!';
2401 return _appendChild_value.apply(this, arguments);
2402 };
2403 Object.defineProperty(_Node, 'appendChild', _appendChild);
2404
2405 // disable window focus tricks and changing location
2406 let focusHandlerName = /\WfocusAchieved\(/
2407 let _toString = Function.prototype.call.bind(Function.prototype.toString);
2408 let _setInterval = win.setInterval;
2409 win.setInterval = (...args) => {
2410 if (args.length && focusHandlerName.test(_toString(args[0]))) {
2411 _console.log('skip setInterval for', ...args);
2412 return -1;
2413 }
2414 return _setInterval(...args);
2415 };
2416 let _addEventListener = win.addEventListener;
2417 win.addEventListener = function(...args) {
2418 if (args.length && args[0] === 'focus' && focusHandlerName.test(_toString(args[1]))) {
2419 _console.log('skip addEventListener for', ...args);
2420 return void 0;
2421 }
2422 return _addEventListener.apply(this, args);
2423 };
2424
2425 // generic popup prevention
2426 preventPopups();
2427 };
2428
2429 scripts['rustorka.com'] = {
2430 other: ['rustorka.club', 'rustorka.lib', 'rustorka.net'],
2431 now: () => {
2432 selectiveEval(evalPatternGeneric, /antiadblock/);
2433 selectiveCookies('adblock|u_count|gophp|st2|st3', ['/forum']);
2434 scriptLander(() => {
2435 // wrap window.open to catch a popup if it triggers
2436 win.open = (...args) => {
2437 _console.warn(`Site attempted to open "${args[0]}" in a new window.`);
2438 location.replace(location.href);
2439 return null;
2440 };
2441 window.addEventListener('DOMContentLoaded', () => {
2442 let link = void 0;
2443 _document.body.addEventListener('mousedown', e => {
2444 link = e.target.closest('a, select, #fancybox-title-wrap');
2445 }, false);
2446 let _open = window.open.bind(window);
2447 let _getAttribute = Function.prototype.call.bind(_Element.getAttribute);
2448 win.open = (...args) => {
2449 let url = args[0];
2450 if (link instanceof HTMLAnchorElement) {
2451 // third-party post links
2452 let href = _getAttribute(link, 'href');
2453 if (link.classList.contains('postLink') &&
2454 !link.matches(`a[href*="${location.hostname}"]`) &&
2455 (href === url || link.href === url))
2456 return _open(...args);
2457 // onclick # links
2458 if (href === '#' && /window\.open/.test(_getAttribute(link, 'onclick')))
2459 return _open(...args);
2460 // force local links to load in the current window
2461 if (href[0] === '/' || href.startsWith('./') || href.includes(`//${location.hostname}/`))
2462 location.assign(href);
2463 }
2464 // list of image hostings under upload picture button (new comment)
2465 if (link instanceof HTMLSelectElement &&
2466 !url.includes(location.hostname) &&
2467 link.value === url)
2468 return _open(...args);
2469 // open screenshot in a new window
2470 if (link instanceof HTMLSpanElement &&
2471 link.id === 'fancybox-title-wrap')
2472 return _open(...args);
2473 // looks like tabunder
2474 if (link === null && url === location.href)
2475 location.replace(url); // reload current page
2476 // other cases
2477 _console.warn(`Site attempted to open "${url}" in a new window. Source: `, link);
2478 return {};
2479 };
2480 }, true);
2481 }, nullTools)
2482 }
2483 };
2484
2485 // = other ======================================================================================
2486 scripts['1tv.ru'] = {
2487 other: ['mediavitrina.ru'],
2488 now: () => scriptLander(() => {
2489 let nt = new nullTools();
2490 nt.define(win, 'EUMPAntiblockConfig', nt.proxy({url: '//www.1tv.ru/favicon.ico'}));
2491 let disablePlugins = {
2492 'antiblock': false,
2493 'stat1tv': false
2494 };
2495 let _EUMPConfig = void 0;
2496 let _EUMPConfig_set = x => {
2497 if (x.plugins) {
2498 x.plugins = x.plugins.filter(plugin => (plugin in disablePlugins) ? !(disablePlugins[plugin] = true) : true);
2499 _console.warn(`Player plugins: active [${x.plugins}], disabled [${Object.keys(disablePlugins).filter(x => disablePlugins[x])}]`);
2500 }
2501 _EUMPConfig = x;
2502 };
2503 if ('EUMPConfig' in win)
2504 _EUMPConfig_set(win.EUMPConfig);
2505 Object.defineProperty(win, 'EUMPConfig', {
2506 enumerable: true,
2507 get: () => _EUMPConfig,
2508 set: _EUMPConfig_set
2509 });
2510 }, nullTools)
2511 };
2512
2513 scripts['24smi.org'] = () => selectiveCookies('has_adblock');
2514
2515 scripts['2picsun.ru'] = {
2516 other: [
2517 'pics2sun.ru', '3pics-img.ru'
2518 ],
2519 now: () => {
2520 Object.defineProperty(navigator, 'userAgent', {value: 'googlebot'});
2521 }
2522 };
2523
2524 scripts['4pda.ru'] = {
2525 now: () => {
2526 // https://greasyfork.org/en/scripts/14470-4pda-unbrender
2527 let isForum = location.pathname.startsWith('/forum/'),
2528 remove = node => (node && node.parentNode.removeChild(node)),
2529 hide = node => (node && (node.style.display = 'none'));
2530
2531 // clean a page
2532 window.addEventListener(
2533 'DOMContentLoaded', function() {
2534 let width = () => window.innerWidth || _de.clientWidth || _document.body.clientWidth || 0;
2535 let height = () => window.innerHeight || _de.clientHeight || _document.body.clientHeight || 0;
2536
2537 HeaderAds: {
2538 // hide ads above HEADER
2539 let header = _document.querySelector('.drop-search');
2540 if (!header) {
2541 _console.warn('Unable to locate header element');
2542 break HeaderAds;
2543 }
2544 header = header.parentNode.parentNode;
2545 for (let itm of header.parentNode.children)
2546 if (itm !== header)
2547 hide(itm);
2548 else break;
2549 }
2550
2551 if (isForum) {
2552 let itm = _document.querySelector('#logostrip');
2553 if (itm)
2554 remove(itm.parentNode.nextSibling);
2555 // clear background in the download frame
2556 if (location.pathname.startsWith('/forum/dl/')) {
2557 let setBackground = node => _setAttribute(
2558 node,
2559 'style', (_getAttribute(node, 'style') || '') +
2560 ';background-color:#4ebaf6!important'
2561 );
2562 setBackground(_document.body);
2563 for (let itm of _document.querySelectorAll('body > div'))
2564 if (!itm.querySelector('.dw-fdwlink, .content') && !itm.classList.contains('footer'))
2565 remove(itm);
2566 else
2567 setBackground(itm);
2568 }
2569 // exist from DOMContentLoaded since the rest is not for forum
2570 return;
2571 }
2572
2573 FixNavMenu: {
2574 // restore DevDB link in the navigation
2575 let itm = _document.querySelector('#nav li a[href$="/devdb/"]')
2576 if (!itm) {
2577 _console.warn('Unable to locate navigation menu');
2578 break FixNavMenu;
2579 }
2580 itm.closest('li').style.display = 'block';
2581 // hide ad link from the navigation
2582 hide(_document.querySelector('#nav li a[data-dotrack]'));
2583 }
2584 SidebarAds: {
2585 // remove ads from sidebar
2586 let aside = _document.querySelectorAll('[class]:not([id]) > [id]:not([class]) > :first-child + :last-child');
2587 if (!aside.length) {
2588 _console.warn('Unable to locate sidebar');
2589 break SidebarAds;
2590 }
2591 let post;
2592 for (let side of aside) {
2593 _console.log('Processing potential sidebar:', side);
2594 for (let itm of Array.from(side.children)) {
2595 post = itm.classList.contains('post');
2596 if (itm.querySelector('iframe') && !post)
2597 remove(itm);
2598 if (itm.querySelector('script, a[target="_blank"] > img') && !post || !itm.children.length)
2599 hide(itm);
2600 }
2601 }
2602 }
2603
2604 _document.body.setAttribute('style', (_document.body.getAttribute('style')||'')+';background-color:#E6E7E9!important');
2605
2606 let extra = 'background-image:none!important;background-color:transparent!important',
2607 fakeStyles = new WeakMap(),
2608 styleProxy = {
2609 get: (target, prop) => fakeStyles.get(target)[prop] || target[prop],
2610 set: function(target, prop, value) {
2611 let fakeStyle = fakeStyles.get(target);
2612 ((prop in fakeStyle) ? fakeStyle : target)[prop] = value;
2613 return true;
2614 }
2615 };
2616 for (let itm of _document.querySelectorAll('[id]:not(A), A')) {
2617 if (!(itm.offsetWidth > 0.95 * width() &&
2618 itm.offsetHeight > 0.85 * height()))
2619 continue;
2620 if (itm.tagName !== 'A') {
2621 fakeStyles.set(itm.style, {
2622 'backgroundImage': itm.style.backgroundImage,
2623 'backgroundColor': itm.style.backgroundColor
2624 });
2625
2626 try {
2627 Object.defineProperty(itm, 'style', {
2628 value: new Proxy(itm.style, styleProxy),
2629 enumerable: true
2630 });
2631 } catch (e) {
2632 _console.log('Unable to protect style property.', e);
2633 }
2634
2635 _setAttribute(itm, 'style', `${(_getAttribute(itm, 'style') || '')};${extra}`);
2636 }
2637 if (itm.tagName === 'A')
2638 _setAttribute(itm, 'style', 'display:none!important');
2639 }
2640 }
2641 );
2642 }
2643 };
2644
2645 scripts['adhands.ru'] = () => scriptLander(() => {
2646 let nt = new nullTools();
2647 try {
2648 let _adv;
2649 Object.defineProperty(win, 'adv', {
2650 get: () => _adv,
2651 set: (v) => {
2652 _console.log('Blocked advert on adhands.ru.');
2653 nt.define(v, 'advert', '');
2654 _adv = v;
2655 }
2656 });
2657 } catch (ignore) {
2658 if (!win.adv)
2659 _console.log('Unable to locate advert on adhands.ru.');
2660 else {
2661 _console.log('Blocked advert on adhands.ru.');
2662 nt.define(win.adv, 'advert', '');
2663 }
2664 }
2665 }, nullTools);
2666
2667 scripts['all-episodes.tv'] = () => {
2668 let nt = new nullTools();
2669 nt.define(win, 'perX1', 2);
2670 createStyle('#advtss, #ad3, a[href*="/ad.admitad.com/"] { display:none!important }');
2671 };
2672
2673 scripts['allhentai.ru'] = () => {
2674 selectiveEval();
2675 preventPopups();
2676 scriptLander(() => {
2677 let _onerror = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'onerror');
2678 if (!_onerror)
2679 return;
2680 _onerror.set = (...args) => _console.log(args[0].toString());
2681 Object.defineProperty(HTMLElement.prototype, 'onerror', _onerror);
2682 });
2683 };
2684
2685 scripts['allmovie.pro'] = {
2686 other: ['rufilmtv.org'],
2687 dom: function() {
2688 // pretend to be Android to make site use different played for ads
2689 if (isSafari)
2690 return;
2691 Object.defineProperty(navigator, 'userAgent', {
2692 get: function(){
2693 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';
2694 },
2695 enumerable: true
2696 });
2697 }
2698 };
2699
2700 scripts['anidub-online.ru'] = {
2701 other: ['anime.anidub.com', 'online.anidub.com'],
2702 dom: function() {
2703 if (win.ogonekstart1)
2704 win.ogonekstart1 = () => _console.log("Fire in the hole!");
2705 },
2706 now: () => createStyle([
2707 '.background {background: none!important;}',
2708 '.background > script + div,'+
2709 '.background > script ~ div:not([id]):not([class]) + div[id][class]'+
2710 '{display:none!important}'
2711 ])
2712 };
2713
2714 scripts['audioportal.su'] = {
2715 now: () => createStyle('#blink2 { display: none !important }'),
2716 dom: () => {
2717 let links = _document.querySelectorAll('a[onclick*="clickme("]');
2718 if (!links) return;
2719 for (let link of links)
2720 clickme(link);
2721 }
2722 };
2723
2724 scripts['avito.ru'] = () => selectiveCookies('abp|cmtchd|crookie|is_adblock');
2725
2726 scripts['di.fm'] = () => scriptLander(() => {
2727 let log = false;
2728 // wrap global app object to catch registration of specific modules
2729 let _di = void 0;
2730 Object.defineProperty(win, 'di', {
2731 get: () => _di,
2732 set: vl => {
2733 if (vl === _di)
2734 return;
2735 log && _console.log('di =', vl);
2736 _di = new Proxy(vl, {
2737 set: (di, name, vl) => {
2738 if (vl === di[name])
2739 return true;
2740 if (name === 'app') {
2741 log && _console.log('di.app =', vl);
2742 if ('module' in vl)
2743 vl.module = new Proxy(vl.module, {
2744 apply: (module, that, args) => {
2745 if (/Wall|Banner|Detect|WebplayerApp\.Ads/.test(args[0])) {
2746 let name = args[0];
2747 log && _console.warn('wrap', name, 'module');
2748 if (typeof args[1] === 'function')
2749 args[1] = new Proxy(args[1], {
2750 apply: (fun, that, args) => {
2751 if (args[0]) // module object
2752 args[0].start = () => _console.log('Skipped start of', name);
2753 return Reflect.apply(fun, that, args);
2754 }
2755 });
2756 }// else log && _console.log('loading module', args[0]);
2757 if (args[0] === 'Modals') {
2758 log && _console.warn('wrap', name, 'module');
2759 if (typeof args[1] === 'function')
2760 args[1] = new Proxy(args[1], {
2761 apply: (fun, that, args) => {
2762 if ('commands' in args[1] && 'setHandlers' in args[1].commands &&
2763 !Object.hasOwnProperty.call(args[1].commands, 'setHandlers')) {
2764 let _commands = args[1].commands;
2765 _commands.setHandlers = new Proxy(_commands.setHandlers, {
2766 apply: (fun, that, args) => {
2767 for (let name in args[0])
2768 if (name === 'modal:streaminterrupt' ||
2769 name === 'modal:midroll')
2770 args[0][name] = () => _console.log('Skipped', name, 'window');
2771 delete _commands.setHandlers;
2772 return Reflect.apply(fun, that, args);
2773 }
2774 });
2775 }
2776 return Reflect.apply(fun, that, args);
2777 }
2778 });
2779 }
2780 return Reflect.apply(module, that, args);
2781 }
2782 });
2783 }
2784 di[name] = vl;
2785 return true;
2786 }
2787 });
2788 }
2789 });
2790 // don't send errorception logs
2791 Object.defineProperty(win, 'onerror', {
2792 set: vl => log && _console.warn('Skipped global onerror callback', vl)
2793 });
2794 });
2795
2796 scripts['draug.ru'] = {
2797 other: ['vargr.ru'],
2798 now: () => scriptLander(() => {
2799 if (location.pathname === '/pop.html')
2800 win.close();
2801 createStyle([
2802 '#timer_1 { display: none !important }',
2803 '#timer_2 { display: block !important }'
2804 ]);
2805 let _contentWindow = Object.getOwnPropertyDescriptor(HTMLIFrameElement.prototype, 'contentWindow');
2806 let _get_contentWindow = Function.prototype.apply.bind(_contentWindow.get);
2807 _contentWindow.get = function() {
2808 let res = _get_contentWindow(this);
2809 if (res.location.href === 'about:blank')
2810 res.document.write = (...args) => _console.log('Skipped iframe.write(', ...args, ')');
2811 return res;
2812 };
2813 Object.defineProperty(HTMLIFrameElement.prototype, 'contentWindow', _contentWindow);
2814 }),
2815 dom: () => {
2816 let list = _querySelectorAll('div[id^="yandex_rtb_"], .adsbygoogle');
2817 list.forEach(node => _console.log('Removed:', node.parentNode.parentNode.removeChild(node.parentNode)));
2818 }
2819 };
2820
2821 scripts['drive2.ru'] = () => {
2822 selectiveCookies();
2823 gardener('.c-block:not([data-metrika="recomm"]),.o-grid__item', />Реклама<\//i);
2824 scriptLander(() => {
2825 let _d2 = void 0;
2826 Object.defineProperty(win, 'd2', {
2827 get: () => _d2,
2828 set: o => {
2829 if (o === _d2)
2830 return true;
2831 _d2 = new Proxy(o, {
2832 set: (tgt, prop, val) => {
2833 if (['brandingRender', 'dvReveal', '__dv'].includes(prop))
2834 val = () => null;
2835 tgt[prop] = val;
2836 return true;
2837 }
2838 });
2839 }
2840 });
2841 });
2842 };
2843
2844 scripts['echo.msk.ru'] = () => {
2845 selectiveCookies();
2846 selectiveEval(evalPatternYandex, /^document\.write/, /callAdblock/);
2847 }
2848
2849 scripts['fastpic.ru'] = () => {
2850 let nt = new nullTools();
2851 // Had to obfuscate property name to avoid triggering anti-obfuscation on greasyfork.org -_- (Exception 403012)
2852 nt.define(win, `_0x${'4955'}`, []);
2853 };
2854
2855 scripts['fishki.net'] = () => {
2856 scriptLander(() => {
2857 let nt = new nullTools();
2858 let fishki = {};
2859 nt.define(fishki, 'adv', nt.proxy({
2860 afterAdblockCheck: nt.func(null),
2861 refreshFloat: nt.func(null)
2862 }));
2863 nt.define(fishki, 'is_adblock', false);
2864 nt.define(win, 'fishki', fishki);
2865 }, nullTools);
2866 gardener('.drag_list > .drag_element, .list-view > .paddingtop15, .post-wrap', /543769|ÐовоÑти\sпартнеров|ПолезнаÑ\sреклама/);
2867 };
2868
2869 scripts['friends.in.ua'] = () => scriptLander(() => {
2870 Object.defineProperty(win, 'need_warning', {
2871 get: () => 0, set: () => null
2872 });
2873 });
2874
2875 scripts['gidonline.club'] = () => createStyle('.tray > div[style] {display: none!important}');
2876
2877 scripts['hdgo.cc'] = {
2878 other: ['46.30.43.38', 'couber.be'],
2879 now: () => (new MutationObserver(
2880 (ms) => {
2881 let m, node;
2882 for (m of ms) for (node of m.addedNodes)
2883 if (node.tagName instanceof HTMLScriptElement && _getAttribute(node, 'onerror') !== null)
2884 node.removeAttribute('onerror');
2885 }
2886 )).observe(_document.documentElement, { childList:true, subtree: true })
2887 };
2888
2889 scripts['gismeteo.ru'] = {
2890 other: ['gismeteo.by', 'gismeteo.kz', 'gismeteo.ua'],
2891 now: () => {
2892 selectiveCookies('ab_[^=]*|redirect|_gab');
2893 gardener('div > script', /AdvManager/i, { observe: true, parent: 'div' })
2894 }
2895 };
2896
2897 scripts['hdrezka.ag'] = () => {
2898 Object.defineProperty(win, 'ab', { value: false, enumerable: true });
2899 gardener('div[id][onclick][onmouseup][onmousedown]', /onmouseout/i);
2900 };
2901
2902 scripts['hqq.tv'] = () => scriptLander(() => {
2903 // disable anti-debugging in hqq.tv player
2904 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);
2905 deepWrapAPI(root => {
2906 // skip obfuscated stuff and a few other calls
2907 let _setInterval = root.setInterval,
2908 _setTimeout = root.setTimeout,
2909 _toString = root.Function.prototype.call.bind(root.Function.prototype.toString);
2910 root.setInterval = (...args) => {
2911 let fun = args[0];
2912 if (fun instanceof Function) {
2913 let text = _toString(fun),
2914 skip = text.includes('check();') || isObfuscated(text);
2915 _console.warn('setInterval', text, 'skip', skip);
2916 if (skip) return -1;
2917 }
2918 return _setInterval.apply(this, args);
2919 };
2920 let wrappedST = new WeakSet();
2921 root.setTimeout = (...args) => {
2922 let fun = args[0];
2923 if (fun instanceof Function) {
2924 let text = _toString(fun),
2925 skip = fun.name === 'check' || isObfuscated(text);
2926 if (!wrappedST.has(fun)) {
2927 _console.warn('setTimeout', text, 'skip', skip);
2928 wrappedST.add(fun);
2929 }
2930 if (skip) return;
2931 }
2932 return _setTimeout.apply(this, args);
2933 };
2934 // skip 'debugger' call
2935 let _eval = root.eval;
2936 root.eval = text => {
2937 if (typeof text === 'string' && text.includes('debugger;')) {
2938 _console.warn('skip eval', text);
2939 return;
2940 }
2941 _eval(text);
2942 };
2943 // Prevent RegExpt + toString trick
2944 let _proto = void 0;
2945 try {
2946 _proto = root.RegExp.prototype;
2947 } catch(ignore) {
2948 return;
2949 }
2950 let _RE_tS = Object.getOwnPropertyDescriptor(_proto, 'toString');
2951 let _RE_tSV = _RE_tS.value || _RE_tS.get();
2952 Object.defineProperty(_proto, 'toString', {
2953 enumerable: _RE_tS.enumerable,
2954 configurable: _RE_tS.configurable,
2955 get: () => _RE_tSV,
2956 set: val => _console.warn('Attempt to change toString for', this, 'with', _toString(val))
2957 });
2958 });
2959 }, deepWrapAPI);
2960
2961 scripts['hideip.me'] = {
2962 now: () => scriptLander(() => {
2963 let _innerHTML = Object.getOwnPropertyDescriptor(_Element, 'innerHTML');
2964 let _set_innerHTML = _innerHTML.set;
2965 let _innerText = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'innerText');
2966 let _get_innerText = _innerText.get;
2967 let div = _document.createElement('div');
2968 _innerHTML.set = function(...args) {
2969 _set_innerHTML.call(div, args[0].replace('i','a'));
2970 if (args[0] && /[рp][еe]кл/.test(_get_innerText.call(div))||
2971 /(\d\d\d?\.){3}\d\d\d?:\d/.test(_get_innerText.call(this)) ) {
2972 _console.log('Anti-Adblock killed.');
2973 return true;
2974 }
2975 _set_innerHTML.apply(this, args);
2976 };
2977 Object.defineProperty(_Element, 'innerHTML', _innerHTML);
2978 Object.defineProperty(win, 'adblock', {
2979 get: () => false,
2980 set: () => null,
2981 enumerable: true
2982 });
2983 let _$ = {};
2984 let _$_map = new WeakMap();
2985 let _gOPD = Object.getOwnPropertyDescriptor(Object, 'getOwnPropertyDescriptor');
2986 let _val_gOPD = _gOPD.value;
2987 _gOPD.value = function(...args) {
2988 let _res = _val_gOPD.apply(this, args);
2989 if (args[0] instanceof Window && (args[1] === '$' || args[1] === 'jQuery')) {
2990 delete _res.get;
2991 delete _res.set;
2992 _res.value = win[args[1]];
2993 }
2994 return _res;
2995 };
2996 Object.defineProperty(Object, 'getOwnPropertyDescriptor', _gOPD);
2997 let getJQWrap = (n) => {
2998 let name = n;
2999 return {
3000 enumerable: true,
3001 get: () => _$[name],
3002 set: x => {
3003 if (_$_map.has(x)) {
3004 _$[name] = _$_map.get(x);
3005 return true;
3006 }
3007 if (x === _$.$ || x === _$.jQuery) {
3008 _$[name] = x;
3009 return true;
3010 }
3011 _$[name] = new Proxy(x, {
3012 apply: (t, o, args) => {
3013 let _res = t.apply(o, args);
3014 if (_$_map.has(_res.is))
3015 _res.is = _$_map.get(_res.is);
3016 else {
3017 let _is = _res.is;
3018 _res.is = function(...args) {
3019 if (args[0] === ':hidden')
3020 return false;
3021 return _is.apply(this, args);
3022 };
3023 _$_map.set(_is, _res.is);
3024 }
3025 return _res;
3026 }
3027 });
3028 _$_map.set(x, _$[name]);
3029 return true;
3030 }
3031 };
3032 };
3033 Object.defineProperty(win, '$', getJQWrap('$'));
3034 Object.defineProperty(win, 'jQuery', getJQWrap('jQuery'));
3035 let _dP = Object.defineProperty;
3036 Object.defineProperty = function(...args) {
3037 if (args[0] instanceof Window && (args[1] === '$' || args[1] === 'jQuery'))
3038 return void 0;
3039 return _dP.apply(this, args);
3040 };
3041 })
3042 };
3043
3044 scripts['igra-prestoloff.cx'] = () => scriptLander(() => {
3045 let nt = new nullTools();
3046 /*jslint evil: true */ // yes, evil, I know
3047 let _write = _document.write.bind(_document);
3048 /*jslint evil: false */
3049 nt.define(_document, 'write', t => {
3050 let id = t.match(/jwplayer\("(\w+)"\)/i);
3051 if (id && id[1])
3052 return _write(`<div id="${id[1]}"></div>${t}`);
3053 return _write('');
3054 });
3055 });
3056
3057 scripts['imageban.ru'] = () => { Object.defineProperty(win, 'V7x1J', { get: () => null }); };
3058
3059 scripts['inoreader.com'] = () => scriptLander(() => {
3060 let i = setInterval(() => {
3061 if ('adb_detected' in win) {
3062 win.adb_detected = () => adb_not_detected();
3063 clearInterval(i);
3064 }
3065 }, 10);
3066 _document.addEventListener('DOMContentLoaded', () => clearInterval(i), false);
3067 });
3068
3069 scripts['ivi.ru'] = () => {
3070 let _xhr_open = win.XMLHttpRequest.prototype.open;
3071 win.XMLHttpRequest.prototype.open = function(method, url, ...args) {
3072 if (typeof url === 'string')
3073 if (url.endsWith('/track'))
3074 return;
3075 return _xhr_open.call(this, method, url, ...args);
3076 };
3077 let _responseText = Object.getOwnPropertyDescriptor(XMLHttpRequest.prototype, 'responseText');
3078 let _responseText_get = _responseText.get;
3079 _responseText.get = function() {
3080 if (this.__responseText__)
3081 return this.__responseText__;
3082 let res = _responseText_get.apply(this, arguments);
3083 let o;
3084 try {
3085 if (res)
3086 o = JSON.parse(res);
3087 } catch(ignore) {};
3088 let changed = false;
3089 if (o && o.result) {
3090 if (o.result instanceof Array &&
3091 'adv_network_logo_url' in o.result[0]) {
3092 o.result = [];
3093 changed = true;
3094 }
3095 if (o.result.show_adv) {
3096 o.result.show_adv = false;
3097 changed = true;
3098 }
3099 }
3100 if (changed) {
3101 _console.log('changed response >>', o);
3102 res = JSON.stringify(o);
3103 }
3104 this.__responseText__ = res;
3105 return res;
3106 };
3107 Object.defineProperty(XMLHttpRequest.prototype, 'responseText', _responseText);
3108 };
3109
3110 scripts['kinopoisk.ru'] = () => {
3111 selectiveCookies('cmtchd|crookie|kpunk');
3112 // set no-branding body style and adjust other blocks on the page
3113 let style = [
3114 '.app__header.app__header_margin-bottom_brand, #top { margin-bottom: 20px !important }',
3115 '.app__branding { display: none !important}'
3116 ];
3117 if (location.hostname === 'www.kinopoisk.ru' && !location.pathname.startsWith('/games/'))
3118 style.push('html:not(#id), body:not(#id), .app-container { background: #d5d5d5 url(/images/noBrandBg.jpg) 50% 0 no-repeat !important }');
3119 createStyle(style);
3120 // catch branding and other things
3121 let _KP = void 0;
3122 Object.defineProperty(win, 'KP', {
3123 get: () => _KP,
3124 set: val => {
3125 if (_KP === val)
3126 return true;
3127 _KP = new Proxy(val, {
3128 set: (kp, name, val) => {
3129 if (name === 'branding') {
3130 kp[name] = new Proxy({ weborama: {} }, {
3131 get: (kp, name) => name in kp ? kp[name] : '',
3132 set: () => true
3133 });
3134 return true;
3135 }
3136 if (name === 'config')
3137 val = new Proxy(val, {
3138 set: (cfg, name, val) => {
3139 if (name === 'anContextUrl')
3140 return true;
3141 if (name === 'adfoxEnabled' || name === 'hasBranding')
3142 val = false;
3143 if (name === 'adfoxVideoAdUrls')
3144 val = {flash:{}, html:{}};
3145 cfg[name] = val;
3146 return true;
3147 }
3148 });
3149 kp[name] = val;
3150 return true;
3151 }
3152 });
3153 _console.log('KP =', val);
3154 }
3155 });
3156 // skip branding and some other junk
3157 if (!('advBlock' in win))
3158 Object.defineProperty(win, 'advBlock', {
3159 get: () => () => null,
3160 set: () => true
3161 });
3162 // skip timeout check for blocked requests
3163 let _setTimeout = Function.prototype.apply.bind(win.setTimeout);
3164 let _toString = Function.prototype.apply.bind(Function.prototype.toString);
3165 win.setTimeout = function(...args) {
3166 if (args[1] === 100) {
3167 let str = _toString(args[0]);
3168 if (str.endsWith('{a()}') || str.endsWith('{n()}'))
3169 return;
3170 }
3171 return _setTimeout(this, args);
3172 };
3173 };
3174
3175 scripts['korrespondent.net'] = {
3176 now: () => scriptLander(() => {
3177 let nt = new nullTools();
3178 nt.define(win, 'holder', function(id) {
3179 let div = _document.getElementById(id);
3180 if (!div)
3181 return;
3182 if (div.parentNode.classList.contains('col__sidebar')) {
3183 div.parentNode.appendChild(div);
3184 div.style.height = '300px';
3185 }
3186 });
3187 }, nullTools),
3188 dom: () => {
3189 for (let frame of _document.querySelectorAll('.unit-side-informer > iframe'))
3190 frame.parentNode.style.width = '1px';
3191 }
3192 };
3193
3194 scripts['liveinternet.ru'] = () => selectiveEval(evalPatternYandex);
3195
3196 scripts['mail.ru'] = {
3197 other: ['ok.ru', 'sportmail.ru'],
3198 now: () => {
3199 selectiveCookies('act|testcookie');
3200 scriptLander(() => {
3201 // setTimeout filter
3202 let pattern = /advBlock/i;
3203 let _toString = Function.prototype.call.bind(Function.prototype.toString);
3204 let _setTimeout = Function.prototype.apply.bind(win.setTimeout);
3205 win.setTimeout = function setTimeout(...args) {
3206 let text = _toString(args[0]);
3207 if (pattern.test(text)) {
3208 _console.warn('Skipped setTimeout:', text);
3209 return;
3210 }// else if (!text.includes('checkLoaded()'))
3211 // _console.warn(text, args[1]);
3212 return _setTimeout(this, args);
3213 };
3214
3215 let nt = new nullTools();
3216 // Trick to prevent mail.ru from removing 3rd-party styles
3217 nt.define(Object.prototype, 'restoreVisibility', nt.func(null), false);
3218 // _mimic check
3219 nt.define(win, '_mimic', false);
3220 // ads on okminigames.mail.ru (opened in iframe)
3221 nt.define(win, 'MrgContext', nt.proxy({
3222 version: null
3223 }, 'MrgContext'));
3224 // Disable some of their counters
3225 nt.define(win, 'rb_counter', nt.func(null, 'rb_counter'));
3226 if (location.hostname === 'e.mail.ru')
3227 nt.define(win, 'aRadar', nt.func(null, 'aRadar'));
3228 else
3229 nt.define(win, 'createRadar', nt.func(nt.func(null, 'aRadar'), 'createRadar'));
3230
3231 // Disable page scrambler on mail.ru to let extensions easily block ads there
3232 let logger = {
3233 apply: (target, thisArg, args) => {
3234 let res = target.apply(thisArg, args);
3235 _console.log(`${target._name}(`, ...args, `)\n>>`, res);
3236 return res;
3237 }
3238 };
3239
3240 function wrapLocator(locator) {
3241 if ('setup' in locator) {
3242 let _setup = locator.setup;
3243 locator.setup = function(o) {
3244 if ('enable' in o) {
3245 o.enable = false;
3246 _console.log('Disable mimic mode.');
3247 }
3248 if ('links' in o) {
3249 o.links = [];
3250 _console.log('Call with empty list of sheets.');
3251 }
3252 return _setup.call(this, o);
3253 };
3254 locator.insertSheet = () => false;
3255 locator.wrap = () => false;
3256 }
3257 try {
3258 let names = [];
3259 for (let name in locator)
3260 if (locator[name] instanceof Function && name !== 'transform') {
3261 locator[name]._name = "locator." + name;
3262 locator[name] = new Proxy(locator[name], logger);
3263 names.push(name);
3264 }
3265 _console.log(`[locator] wrapped properties: ${names.length ? names.join(', ') : '[empty]'}`);
3266 } catch(e) {
3267 _console.log(e);
3268 }
3269 return locator;
3270 }
3271
3272 function defineLocator(root) {
3273 let _locator = root.locator;
3274 let wrapLocatorSetter = vl => _locator = wrapLocator(vl);
3275 let loc_desc = Object.getOwnPropertyDescriptor(root, 'locator');
3276 if (!loc_desc || loc_desc.set !== wrapLocatorSetter)
3277 try {
3278 Object.defineProperty(root, 'locator', {
3279 set: wrapLocatorSetter,
3280 get: () => _locator
3281 });
3282 } catch (err) {
3283 _console.log('Unable to redefine "locator" object!!!', err);
3284 }
3285 if (loc_desc.value)
3286 _locator = wrapLocator(loc_desc.value);
3287 }
3288
3289 {
3290 let missingCheck = {
3291 get: (obj, name) => {
3292 if (!(name in obj))
3293 _console.warn(obj, 'missing:', name);
3294 return obj[name];
3295 }
3296 };
3297 // wow, Mail.ru can't just keep base Array functionality alone >_<
3298 let _reduce = Function.prototype.call.bind(Array.prototype.reduce);
3299 let skipLog = (name, ret) => (...args) => (_console.log(`Skip ${name}(`, ...args, ')'), ret);
3300 let createSkipLogObject = (baseName, list) => _reduce(
3301 list,
3302 (acc, cur) => (acc[cur] = skipLog(`${baseName}.${cur}`), acc),
3303 {}
3304 );
3305 let _apply = Reflect.apply;
3306 let redefiner = {
3307 apply: (target, thisArg, args) => {
3308 let res = void 0;
3309 let warn = false;
3310 let name = target._name;
3311 if (name === 'mrg-smokescreen/Welter')
3312 res = {
3313 isWelter: () => true,
3314 wrap: skipLog(`${name}.wrap`)
3315 };
3316 if (name === 'mrg-smokescreen/StyleSheets')
3317 res = createSkipLogObject(name, ['update', 'remove', 'insert', 'setup']);
3318 if (name === 'mrg-honeypot/main' ||
3319 name === 'mrg-smokescreen/Honeypot')
3320 res = { check: skipLog(`${name}.check`, false) };
3321 if (name.startsWith('advert/rb/slot')) {
3322 res = createSkipLogObject(name, ['get', 'getHTML', 'createBlock', 'onRedirect']);
3323 res.slot = '0';
3324 }
3325 if (name.startsWith('OK/banners/'))
3326 res = createSkipLogObject(name, ['activate', 'deactivate', 'onHooksReady', 'subscribeToEvents']);
3327 if (name === 'advert/adman/adman') {
3328 res = createSkipLogObject(name, ['deleteRBParams', 'getBlock', 'init', 'refresh', 'setForceMimic', 'setRBParams']);
3329 let features = { siteZones: {}, slots: {} };
3330 [
3331 'expId', 'siteId', 'mimicEndpoint', 'mimicPartnerId', 'immediateFetchTimeout', 'delayedFetchTimeout'
3332 ].forEach(name => void (features[name] = null));
3333 res.getFeatures = skipLog('advert/adman/adman.getFeatures', features);
3334 }
3335 if (name.startsWith('adv/'))
3336 res = createSkipLogObject(name, ['create', 'logError', 'rb', 'sendCounter', 'top']);
3337 if (name === 'app/banners')
3338 res = createSkipLogObject(name, ['create', 'createRunTime', 'startLoad', 'load']);
3339 if (name === 'right-banners')
3340 res = createSkipLogObject(name, ['create']);
3341 if (name === 'mimic/mimic') {
3342 res = createSkipLogObject(name, ['createRunTime']);
3343 res.prototype = Function.prototype;
3344 }
3345 if (name === 'mimic/right-column')
3346 res = createSkipLogObject(name, []);
3347 // mimic/css/mimic must load or https://my.mail.ru/apps/ breaks
3348 if (name.startsWith('mimic/css/') && name !== 'mimic/css/mimic')
3349 res = {};
3350 if (res) {
3351 Object.defineProperty(res, Symbol.toStringTag, {
3352 get: () => `Skiplog object for ${name}`
3353 });
3354 Object.defineProperty(res, Symbol.toPrimitive, {
3355 value: function(hint) {
3356 if (hint === 'string')
3357 return Object.prototype.toString.call(this);
3358 return `[missing toPrimitive] ${name} ${hint}`;
3359 }
3360 });
3361 res = new Proxy(res, missingCheck);
3362 } else {
3363 res = _apply(target, thisArg, args);
3364 warn = true;
3365 }
3366 if (name === 'mimic')
3367 res = new Proxy(res, {
3368 construct: (...args) => {
3369 function wrap(o) {
3370 if ('locator' in o)
3371 defineLocator(o);
3372 for (let name in o)
3373 if (typeof o[name] === 'object' && name !== 'locator')
3374 wrap(o[name]);
3375 return o;
3376 }
3377 let res = wrap(Reflect.construct(...args));
3378 _console.warn('new mimic', res);
3379 return res;
3380 }
3381 });
3382 if (name === 'mrg-smokescreen/Utils')
3383 res.extend = function(...args) {
3384 let res = {
3385 enable: false,
3386 match: [],
3387 links: []
3388 };
3389 _console.log(`${name}.extend(`, ...args, ') >>', res );
3390 return res;
3391 };
3392 if (name === 'advert/RB') {
3393 res.getSlots = () => [];
3394 res.load._name = name + '.load';
3395 res.load = new Proxy(res.load, redefiner);
3396 }
3397 _console[warn?'warn':'log'](name, '(',...args,')\n>>', res);
3398 return res;
3399 }
3400 };
3401
3402 let advModuleNamesStartWith = /^(mrg-(context|honeypot)|adv\/)/;
3403 let advModuleNamesGeneric = /advert|banner|mimic|smoke/i;
3404 let otherModuleNames = [];
3405 let printCollectedLog = () => {
3406 if (otherModuleNames.length) {
3407 // sort list and remove duplicates
3408 otherModuleNames = [...new Set(otherModuleNames)].sort();
3409 _console.log(`Define:\n${otherModuleNames.join('\n')}`);
3410 otherModuleNames = [];
3411 }
3412 };
3413 win.addEventListener('load', () => {
3414 printCollectedLog();
3415 let cnt = 0;
3416 let i = setInterval(() => {
3417 otherModuleNames.length ? cnt = 0 : cnt++;
3418 cnt >= 5 && clearInterval(i);
3419 printCollectedLog();
3420 }, 1000);
3421 }, false);
3422 let wrapAdFuncs = {
3423 apply: (target, thisArg, args) => {
3424 let module = args[0];
3425 if (typeof module === 'string')
3426 if ((advModuleNamesStartWith.test(module) ||
3427 advModuleNamesGeneric.test(module)) &&
3428 // fix for e.mail.ru in Fx56 and below, looks like Proxy is quirky there
3429 !module.startsWith('patron.v2.')) {
3430 let fun = args[args.length-1];
3431 fun._name = module;
3432 args[args.length-1] = new Proxy(fun, redefiner);
3433 } else otherModuleNames.push(module);
3434 return _apply(target, thisArg, args);
3435 }
3436 };
3437 let wrapDefine = def => {
3438 if (!def)
3439 return;
3440 _console.log('define =', def);
3441 def = new Proxy(def, wrapAdFuncs);
3442 def._name = 'define';
3443 return def;
3444 };
3445 let _define = wrapDefine(win.define);
3446 Object.defineProperty(win, 'define', {
3447 get: () => _define,
3448 set: x => {
3449 if (_define === x)
3450 return true;
3451 _define = wrapDefine(x);
3452 return true;
3453 }
3454 });
3455 }
3456
3457 let _honeyPot;
3458 function defineDetector(mr) {
3459 let __ = mr._ || {};
3460 let setHoneyPot = o => {
3461 if (!o || o === _honeyPot) return;
3462 _console.log('[honeyPot]', o);
3463 _honeyPot = function() {
3464 this.check = new Proxy(() => {
3465 __.STUCK_IN_POT = false;
3466 return false;
3467 }, logger);
3468 this.check._name = 'honeyPot.check';
3469 this.destroy = () => null;
3470 };
3471 };
3472 if ('honeyPot' in mr)
3473 setHoneyPot(mr.honeyPot);
3474 else
3475 Object.defineProperty(mr, 'honeyPot', {
3476 get: () => _honeyPot,
3477 set: setHoneyPot
3478 });
3479
3480 __ = new Proxy(__, {
3481 get: (t, p) => t[p],
3482 set: (t, p, v) => {
3483 _console.log(`mr._.${p} =`, v);
3484 t[p] = v;
3485 return true;
3486 }
3487 });
3488 mr._ = __;
3489 }
3490
3491 function defineAdd(mr) {
3492 let _add;
3493 let addWrapper = {
3494 apply: (tgt, that, args) => {
3495 let module = args[0];
3496 if (typeof module === 'string' && module.startsWith('ad')) {
3497 _console.log('Skip module:', module);
3498 return;
3499 }
3500 if (typeof module === 'object' && module.name.startsWith('ad'))
3501 _console.log('Loaded module:', module);
3502 return logger.apply(tgt, that, args);
3503 }
3504 };
3505 let setMrAdd = v => {
3506 if (!v) return;
3507 v._name = 'mr.add';
3508 v = new Proxy(v, addWrapper);
3509 _add = v;
3510 };
3511 if ('add' in mr)
3512 setMrAdd(mr.add);
3513 Object.defineProperty(mr, 'add', {
3514 get: () => _add,
3515 set: setMrAdd
3516 });
3517
3518 }
3519
3520 let _mr_wrapper = vl => {
3521 defineLocator(vl.mimic ? vl.mimic : vl);
3522 defineDetector(vl);
3523 defineAdd(vl);
3524 return vl;
3525 };
3526 if ('mr' in win) {
3527 _console.log('Found existing "mr" object.');
3528 win.mr = _mr_wrapper(win.mr);
3529 } else {
3530 let _mr = void 0;
3531 Object.defineProperty(win, 'mr', {
3532 get: () => _mr,
3533 set: vl => { _mr = _mr_wrapper(vl) },
3534 configurable: true
3535 });
3536 let _defineProperty = Function.prototype.apply.bind(Object.defineProperty);
3537 Object.defineProperty = function defineProperty(o, name, conf) {
3538 if (name === 'mr' && o instanceof Window) {
3539 _console.warn('Object.defineProperty(', ...arguments, ')');
3540 conf.set(_mr_wrapper(conf.get()));
3541 }
3542 if ((name === 'honeyPot' || name === 'add') && _mr === o && conf.set)
3543 return;
3544 return _defineProperty(this, arguments);
3545 };
3546 }
3547
3548 // smokyTools wrapper for news.mail.ru
3549 nt.define(win, 'smokyTools', nt.proxy({
3550 getDict: nt.func(nt.proxy({}, 'smokyTools.getDict', null), 'smokyTools.getDict'),
3551 CSS: nt.func(nt.proxy({}, 'smokyTools.CSS', null), 'smokyTools.CSS')
3552 }, 'smokyTools', null));
3553 nt.define(win, 'smoky', nt.func(null, 'smoky'));
3554 nt.define(win, 'smokySingleElement', nt.func(null, 'smokySingleElement'));
3555 nt.define(win, 'smokyByClass', nt.func(null, 'smokyByClass'));
3556 // other things
3557 nt.define(win, 'getAdvTargetParam', nt.func(null, 'getAdvTargetParam'));
3558 nt.define(win, 'rb_banner', nt.func(null, 'rb_banner'));
3559 nt.define(win, 'rb_tadq', nt.func(null, 'rb_tadq'));
3560 nt.define(win, 'rb_bannerClick', nt.func(null, 'rb_bannerClick'));
3561 }, nullTools);
3562 }
3563 };
3564
3565 scripts['oms.matchat.online'] = () => scriptLander(() => {
3566 let _rmpGlobals = void 0;
3567 Object.defineProperty(win, 'rmpGlobals', {
3568 get: () => _rmpGlobals,
3569 set: x => {
3570 if (x === _rmpGlobals)
3571 return true;
3572 _rmpGlobals = new Proxy(x, {
3573 get: (obj, name) => {
3574 if (name === 'adBlockerDetected')
3575 return false;
3576 return obj[name];
3577 },
3578 set: (obj, name, val) => {
3579 if (name === 'adBlockerDetected')
3580 _console.warn('rmpGlobals.adBlockerDetected =', val)
3581 else
3582 obj[name] = val;
3583 return true;
3584 }
3585 });
3586 }
3587 });
3588 });
3589
3590 scripts['megogo.net'] = {
3591 now: () => {
3592 let nt = new nullTools();
3593 nt.define(win, 'adBlock', false);
3594 nt.define(win, 'showAdBlockMessage', nt.func(null));
3595 }
3596 };
3597
3598 scripts['n-torrents.org'] = () => scriptLander(() => {
3599 let _$ = void 0;
3600 Object.defineProperty(win, '$', {
3601 get: () => _$,
3602 set: vl => {
3603 _$ = vl;
3604 if (!vl.fn)
3605 return true;
3606 let _videoPopup = vl.fn.videoPopup;
3607 Object.defineProperty(vl.fn, 'videoPopup', {
3608 get: () => _videoPopup,
3609 set: vl => {
3610 if (vl === _videoPopup)
3611 return true;
3612 _videoPopup = new Proxy(vl, {
3613 apply: (fun, obj, args) => {
3614 let opts = args[0];
3615 if (opts) {
3616 opts.adv = '';
3617 opts.duration = 0;
3618 }
3619 return Reflect.apply(fun, obj, args);
3620 }
3621 });
3622 return true;
3623 }
3624 });
3625 return true
3626 }
3627 });
3628 });
3629
3630 scripts['naruto-base.su'] = () => gardener('div[id^="entryID"],.block', /href="http.*?target="_blank"/i);
3631
3632 scripts['newdeaf-online.net'] = {
3633 dom: () => {
3634 let adNodes = _document.querySelectorAll('.ads');
3635 if (!adNodes)
3636 return;
3637 let getter = x => {
3638 let val = x;
3639 return () => (_console.warn('read .ads', name, val), val);
3640 };
3641 let setter = x => _console.warn('skip write .ads', name, x);
3642 for (let adNode of adNodes)
3643 for (let name of ['innerHTML'])
3644 Object.defineProperty(adNode, name, {
3645 get: getter(ads[name]),
3646 set: setter
3647 });
3648 }
3649 };
3650
3651 scripts['overclockers.ru'] = {
3652 dom: () => scriptLander(() => {
3653 let killed = () => _console.warn('Anti-Adblock killed.');
3654 if ('$' in win)
3655 win.$ = new Proxy($, {
3656 apply: (tgt, that, args) => {
3657 let res = tgt.apply(that, args);
3658 if (res[0] && res[0] === _document.body) {
3659 res.html = killed;
3660 res.empty = killed;
3661 }
3662 return res;
3663 }
3664 });
3665 })
3666 };
3667 scripts['forums.overclockers.ru'] = {
3668 now: () => {
3669 createStyle('.needblock {position: fixed; left: -10000px}');
3670 Object.defineProperty(win, 'adblck', {
3671 get: () => 'no',
3672 set: () => undefined,
3673 enumerable: true
3674 });
3675 }
3676 };
3677
3678 scripts['pb.wtf'] = {
3679 other: ['piratbit.org', 'piratbit.ru'],
3680 dom: () => {
3681 // line above topic content and images in the slider in the header
3682 let remove = node => (_console.log('removed', node), node.parentNode.removeChild(node));
3683 for (let el of _document.querySelectorAll('.release-block-img a, #page_content a')) {
3684 if (location.hostname === el.hostname &&
3685 /^\/(\w{3}|exit)\/[\w=/]{20,}$/.test(el.pathname)) {
3686 remove(el.closest('div, tr'));
3687 continue;
3688 }
3689 // ads in the topic header in case filter above wasn't enough
3690 let parent = el.closest('tr');
3691 if (parent) {
3692 let span = (parent.querySelector('span') || {}).textContent;
3693 span && span.startsWith('YO!') && remove(parent);
3694 }
3695 }
3696 // casino ad button in random places
3697 for (let el of _document.querySelectorAll('.btn-group')) {
3698 el = el.parentNode;
3699 if (el.tagName === 'CENTER')
3700 remove(el.parentNode);
3701 }
3702 // ads in comments
3703 let el = _document.querySelector('thead + tbody[id^="post_"] + tbody[class*=" "]');
3704 if (el && el.parentNode.children[2] == el)
3705 remove(el);
3706 }
3707 };
3708
3709 scripts['pikabu.ru'] = () => gardener('.story', /story__author[^>]+>ads</i, {root: '.inner_wrap', observe: true});
3710
3711 scripts['peka2.tv'] = () => {
3712 let bodyClass = 'body--branding';
3713 let checkNode = node => {
3714 for (let className of node.classList)
3715 if (className.includes('banner') || className === bodyClass) {
3716 _removeAttribute(node, 'style');
3717 node.classList.remove(className);
3718 for (let attr of Array.from(node.attributes))
3719 if (attr.name.startsWith('advert'))
3720 _removeAttribute(node, attr.name);
3721 }
3722 };
3723 (new MutationObserver(ms => {
3724 let m, node;
3725 for (m of ms) for (node of m.addedNodes)
3726 if (node instanceof HTMLElement)
3727 checkNode(node);
3728 })).observe(_de, {childList: true, subtree: true});
3729 (new MutationObserver(ms => {
3730 for (let m of ms)
3731 checkNode(m.target);
3732 })).observe(_de, {attributes: true, subtree: true, attributeFilter: ['class']});
3733 };
3734
3735 scripts['qrz.ru'] = {
3736 now: () => {
3737 let nt = new nullTools();
3738 nt.define(win, 'ab', false);
3739 nt.define(win, 'tryMessage', nt.func(null));
3740 }
3741 };
3742
3743 scripts['razlozhi.ru'] = {
3744 now: () => {
3745 let nt = new nullTools();
3746 nt.define(win, 'cadb', false);
3747 for (let func of ['createShadowRoot', 'attachShadow'])
3748 if (func in _Element)
3749 _Element[func] = function(){
3750 return this.cloneNode();
3751 };
3752 }
3753 };
3754
3755 scripts['rbc.ru'] = {
3756 other: ['autonews.ru', 'rbcplus.ru', 'sportrbc.ru'],
3757 now: () => {
3758 selectiveCookies('adb_on');
3759 let _RA = void 0;
3760 let setArgs = {
3761 'showBanners': true,
3762 'showAds': true,
3763 'banners.staticPath': '',
3764 'paywall.staticPath': '',
3765 'banners.dfp.config': [],
3766 'banners.dfp.pageTargeting': () => null,
3767 };
3768 Object.defineProperty(win, 'RA', {
3769 get: () => _RA,
3770 set: vl => {
3771 _console.log('RA =', vl);
3772 if ('repo' in vl) {
3773 _console.log('RA.repo =', vl.repo);
3774 vl.repo = new Proxy(vl.repo, {
3775 set: (o, name, val) => {
3776 if (name === 'banner') {
3777 _console.log(`RA.repo.${name} =`, val);
3778 val = new Proxy(val, {
3779 get: (o, name) => {
3780 let res = o[name];
3781 if (typeof o[name] === 'function') {
3782 res = () => null;
3783 res.toString = o[name].toString.bind(o[name]);
3784 }
3785 if (name === 'isInited')
3786 res = true;
3787 _console.warn(`get RA.repo.banner.${name}`, res);
3788 return res;
3789 }
3790 });
3791 }
3792 o[name] = val;
3793 return true;
3794 }
3795 });
3796 } else
3797 _console.log('Unable to locate RA.repo');
3798 _RA = new Proxy(vl, {
3799 set: (o, name, val) => {
3800 if (name === 'config') {
3801 _console.log('RA.config =', val);
3802 if ('set' in val) {
3803 val.set = new Proxy(val.set, {
3804 apply: (set, that, args) => {
3805 let name = args[0];
3806 if (name in setArgs)
3807 args[1] = setArgs[name];
3808 if (name in setArgs || name === 'checkad')
3809 _console.log('RA.config.set(', ...args, ')');
3810 return Reflect.apply(set, that, args);
3811 }
3812 });
3813 val.set('showAds', true); // pretend ads already were shown
3814 }
3815 }
3816 o[name] = val;
3817 return true;
3818 }
3819 });
3820 }
3821 });
3822 Object.defineProperty(win, 'bannersConfig', {
3823 get: () => [], set: () => null
3824 });
3825 // pretend there is a paywall landing on screen already
3826 let pwl = _document.createElement('div');
3827 pwl.style.display = 'none';
3828 pwl.className = 'js-paywall-landing';
3829 _document.documentElement.appendChild(pwl);
3830 // detect and skip execution of one of the ABP detectors
3831 let _setTimeout = Function.prototype.apply.bind(win.setTimeout);
3832 let _toString = Function.prototype.call.bind(Function.prototype.toString);
3833 win.setTimeout = function setTimeout() {
3834 if (typeof arguments[0] === 'function') {
3835 let fts = _toString(arguments[0]);
3836 if (/\.length\s*>\s*0\s*&&/.test(fts) && /:hidden/.test(fts)) {
3837 _console.log('Skipped setTimout(', fts, arguments[1], ')');
3838 return;
3839 }
3840 }
3841 return _setTimeout(this, arguments);
3842 };
3843 // hide banner placeholders
3844 createStyle('[data-banner-id], .banner__container, .banners__yandex__article { display: none !important }');
3845 },
3846 dom: () => {
3847 // hide sticky banner place at the top of the page
3848 for (let itm of _document.querySelectorAll('.l-sticky'))
3849 if (itm.querySelector('.banner__container__link'))
3850 itm.style.display = 'none';
3851 }
3852 };
3853
3854 scripts['rp5.ru'] = {
3855 other: ['rp5.by', 'rp5.kz', 'rp5.ua'],
3856 now: () => {
3857 Object.defineProperty(win, 'sContentBottom', {
3858 get: () => '',
3859 set: () => true
3860 });
3861 },
3862 dom: () => {
3863 let node = selectNodeByTextContent('РазмеÑтить текÑтовое объÑвление', { root: _de.querySelector('#content-wrapper'), shallow: true });
3864 if (node)
3865 node.style.display = 'none';
3866 }
3867 };
3868
3869 scripts['rutube.ru'] = () => scriptLander(() => {
3870 let _parse = JSON.parse;
3871 let _skip_enabled = false;
3872 JSON.parse = (...args) => {
3873 let res = _parse(...args),
3874 log = false;
3875 if (!res)
3876 return res;
3877 // parse player configuration
3878 if ('appearance' in res || 'video_balancer' in res) {
3879 log = true;
3880 if (res.appearance) {
3881 if ('forbid_seek' in res.appearance && res.appearance.forbid_seek)
3882 res.appearance.forbid_seek = false;
3883 if ('forbid_timeline_preview' in res.appearance && res.appearance.forbid_timeline_preview)
3884 res.appearance.forbid_timeline_preview = false;
3885 }
3886 _skip_enabled = !!res.remove_unseekable_blocks;
3887 //res.advert = [];
3888 delete res.advert;
3889 //for (let limit of res.limits)
3890 // limit.limit = 0;
3891 delete res.limits;
3892 //res.yast = null;
3893 //res.yast_live_online = null;
3894 delete res.yast;
3895 delete res.yast_live_online;
3896 Object.defineProperty(res, 'stat', {
3897 get: () => [],
3898 set: () => true,
3899 enumerable: true
3900 });
3901 }
3902
3903 // parse video configuration
3904 if ('video_url' in res) {
3905 log = true;
3906 if (res.cuepoints && !_skip_enabled)
3907 for (let point of res.cuepoints) {
3908 point.is_pause = false;
3909 point.show_navigation = true;
3910 point.forbid_seek = false;
3911 }
3912 }
3913
3914 if (log)
3915 _console.log('[rutube]', res);
3916 return res;
3917 };
3918 });
3919
3920 scripts['simpsonsua.com.ua'] = {
3921 other: ['simpsonsua.tv'],
3922 now: () => scriptLander(() => {
3923 let _addEventListener = _Document.addEventListener;
3924 _document.addEventListener = function(event, callback) {
3925 if (event === 'DOMContentLoaded' && callback.toString().includes('show_warning'))
3926 return;
3927 return _addEventListener.apply(this, arguments);
3928 };
3929 let nt = new nullTools();
3930 nt.define(win, 'need_warning', 0);
3931 }, nullTools)
3932 };
3933
3934 scripts['smotretanime.ru'] = () => scriptLander(() => {
3935 deepWrapAPI(root => {
3936 let _pause = root.Function.prototype.call.bind(root.Audio.prototype.pause);
3937 let _addEventListener = root.Function.prototype.call.bind(root.Element.prototype.addEventListener);
3938 let stopper = e => _pause(e.target);
3939 root.Audio = new Proxy(root.Audio, {
3940 construct: (audio, args) => {
3941 let res = new audio(...args);
3942 _addEventListener(res, 'play', stopper, true);
3943 return res;
3944 }
3945 });
3946 _createElement = root.Document.prototype.createElement;
3947 root.Document.prototype.createElement = function createElement() {
3948 let res = _createElement.apply(this, arguments);
3949 if (res instanceof HTMLAudioElement)
3950 _addEventListener(res, 'play', stopper, true);
3951 return res;
3952 };
3953 });
3954 }, deepWrapAPI);
3955
3956 scripts['spaces.ru'] = () => {
3957 gardener('div:not(.f-c_fll) > a[href*="spaces.ru/?Cl="]', /./, { parent: 'div' });
3958 gardener('.js-banner_rotator', /./, { parent: '.widgets-group' });
3959 };
3960
3961 scripts['spam-club.blogspot.co.uk'] = () => {
3962 let _clientHeight = Object.getOwnPropertyDescriptor(_Element, 'clientHeight'),
3963 _clientWidth = Object.getOwnPropertyDescriptor(_Element, 'clientWidth');
3964 let wrapGetter = (getter) => {
3965 let _getter = getter;
3966 return function() {
3967 let _size = _getter.apply(this, arguments);
3968 return _size ? _size : 1;
3969 };
3970 };
3971 _clientHeight.get = wrapGetter(_clientHeight.get);
3972 _clientWidth.get = wrapGetter(_clientWidth.get);
3973 Object.defineProperty(_Element, 'clientHeight', _clientHeight);
3974 Object.defineProperty(_Element, 'clientWidth', _clientWidth);
3975 let _onload = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'onload'),
3976 _set_onload = _onload.set;
3977 _onload.set = function() {
3978 if (this instanceof HTMLImageElement)
3979 return true;
3980 _set_onload.apply(this, arguments);
3981 };
3982 Object.defineProperty(HTMLElement.prototype, 'onload', _onload);
3983 };
3984
3985 scripts['sport-express.ru'] = () => gardener('.js-relap__item',/>Реклама\s+<\//, {root:'.container', observe: true});
3986
3987 scripts['sports.ru'] = {
3988 other: ['tribuna.com'],
3989 now: () => {
3990 // extra functionality: shows/hides panel at the top depending on scroll direction
3991 createStyle([
3992 '.user-panel__fixed { transition: top 0.2s ease-in-out!important; }',
3993 '.user-panel-up { top: -40px!important }'
3994 ], {id: 'userPanelSlide'}, false);
3995 scriptLander(() => {
3996 yandexRavenStub();
3997 webpackJsonpFilter(/AdBlockDetector|addBranding|_desktop\/banners\/|loadPlista/);
3998 }, nullTools, yandexRavenStub);
3999 },
4000 dom: () => {
4001 (function lookForPanel() {
4002 let panel = _document.querySelector('.user-panel__fixed');
4003 if (!panel)
4004 setTimeout(lookForPanel, 100);
4005 else
4006 window.addEventListener(
4007 'wheel', function(e) {
4008 if (e.deltaY > 0 && !panel.classList.contains('user-panel-up'))
4009 panel.classList.add('user-panel-up');
4010 else if (e.deltaY < 0 && panel.classList.contains('user-panel-up'))
4011 panel.classList.remove('user-panel-up');
4012 }, false
4013 );
4014 })();
4015 }
4016 };
4017 scripts['stealthz.ru'] = {
4018 dom: () => {
4019 // skip timeout
4020 let $ = _document.querySelector.bind(_document);
4021 let [timer_1, timer_2] = [$('#timer_1'), $('#timer_2')];
4022 if (!timer_1 || !timer_2)
4023 return;
4024 timer_1.style.display = 'none';
4025 timer_2.style.display = 'block';
4026 }
4027 };
4028
4029 scripts['xatab-repack.net'] = {
4030 other: ['rg-mechanics.org'],
4031 now: () => scriptLander(() => {
4032 Object.defineProperty(win, 'blocked', {
4033 set: () => { throw 'and unlooked for.'; }
4034 });
4035 }, nullTools)
4036 };
4037
4038 scripts['xittv.net'] = () => scriptLander(() => {
4039 let logNames = ['setup', 'trigger', 'on', 'off', 'onReady', 'onError', 'getConfig', 'addPlugin', 'getAdBlock'];
4040 let skipEvents = ['adComplete', 'adSkipped', 'adBlock', 'adRequest', 'adMeta', 'adImpression', 'adError', 'adTime', 'adStarted', 'adClick'];
4041 let _jwplayer = void 0;
4042 Object.defineProperty(win, 'jwplayer', {
4043 get: () => _jwplayer,
4044 set: x => {
4045 _jwplayer = new Proxy(x, {
4046 apply: (fun, that, args) => {
4047 let res = fun.apply(that, args);
4048 res = new Proxy(res, {
4049 get: (obj, name) => {
4050 if (logNames.includes(name) && obj[name] instanceof Function)
4051 return new Proxy(obj[name], {
4052 apply: (fun, that, args) => {
4053 if (name === 'setup') {
4054 let o = args[0];
4055 if (o)
4056 delete o.advertising;
4057 }
4058 if (name === 'on' || name === 'trigger') {
4059 let events = typeof args[0] === 'string' ? args[0].split(" ") : null;
4060 if (events.length === 1 && skipEvents.includes(events[0]))
4061 return res;
4062 if (events.length > 1) {
4063 let names = [];
4064 for (let event of events)
4065 if (!skipEvents.includes(event))
4066 names.push(event);
4067 if (names.length > 0)
4068 args[0] = names.join(" ");
4069 else
4070 return res;
4071 }
4072 }
4073 let subres = fun.apply(that, args);
4074 _console.warn(`jwplayer().${name}(`, ...args, `) >>`, res);
4075 return subres;
4076 }
4077 });
4078 return obj[name];
4079 }
4080 });
4081 return res;
4082 }
4083 });
4084 _console.log('jwplayer =', x);
4085 }
4086 });
4087 });
4088
4089 scripts['yap.ru'] = {
4090 other: ['yaplakal.com'],
4091 now: () => {
4092 gardener('form > table[id^="p_row_"]:nth-of-type(2)', /member1438|Administration/);
4093 gardener('.icon-comments', /member1438|Administration|\/go\/\?http/, {parent:'tr', siblings:-2});
4094 }
4095 };
4096
4097 scripts['rambler.ru'] = {
4098 other: ['championat.com', 'gazeta.ru', 'lenta.ru', 'media.eagleplatform.com', 'quto.ru', 'rns.online'],
4099 now: () => {
4100 selectiveCookies('detect_count');
4101 scriptLander(() => {
4102 // Prevent autoplay
4103 if (!('EaglePlayer' in win)) {
4104 let _EaglePlayer = void 0;
4105 Object.defineProperty(win, 'EaglePlayer', {
4106 enumerable: true,
4107 get: () => _EaglePlayer,
4108 set: x => {
4109 if (x === _EaglePlayer)
4110 return true;
4111 _EaglePlayer = new Proxy(x, {
4112 construct: (targ, args) => {
4113 let player = new targ(...args);
4114 if (!player.options) {
4115 _console.log('EaglePlayer: no options', EaglePlayer);
4116 return player;
4117 }
4118 Object.defineProperty(player.options, 'autoplay', {
4119 get: () => false,
4120 set: () => true
4121 });
4122 Object.defineProperty(player.options, 'scroll', {
4123 get: () => false,
4124 set: () => true
4125 });
4126 return player;
4127 }
4128 });
4129 }
4130 });
4131 let _setAttribute = Function.prototype.apply.bind(_Element.setAttribute);
4132 let isAutoplay = /^autoplay$/i;
4133 _Element.setAttribute = function setAttribute(name) {
4134 if (!this._stopped && isAutoplay.test(name)) {
4135 _console.log('Prevented assigning autoplay attribute.');
4136 return null;
4137 }
4138 return _setAttribute(this, arguments);
4139 };
4140 } else {
4141 _console.log('EaglePlayer function already exists.');
4142 if (inIFrame) {
4143 let _setAttribute = Function.prototype.apply.bind(_Element.setAttribute);
4144 let isAutoplay = /^autoplay$/i;
4145 _Element.setAttribute = function setAttribute(name) {
4146 if (!this._stopped && isAutoplay.test(name)) {
4147 _console.log('Prevented assigning autoplay attribute.');
4148 this._stopped = true;
4149 this.play = () => {
4150 _console.log('Prevented attempt to force-start playback.');
4151 delete this.play;
4152 };
4153 return null;
4154 }
4155 return _setAttribute(this, arguments);
4156 };
4157 }
4158 }
4159 if (location.hostname.endsWith('.media.eagleplatform.com'))
4160 return;
4161 let nt = new nullTools();
4162 // Adblock Detector
4163 let _hidden = void 0;
4164 let ABD_user = {};
4165 [
4166 'getOrSetUid', 'getSyncFPruid', 'getRuid',
4167 'updateDetectCount', 'getSplitCookies'
4168 ].forEach(name => void(ABD_user[name] = nt.func(null, `ABD.user.${name}`)));
4169 Object.defineProperty(win, 'QW50aS1BZEJsb2Nr', {
4170 set: vl => {
4171 for (let name in vl) delete vl[name];
4172 nt.define(vl, 'Detector', nt.proxy({
4173 getBlockingStatus: () => new Promise(),
4174 blockingDetectors: [],
4175 baitURLPrefix: '',
4176 commonRules: []
4177 }));
4178 nt.define(vl, 'isAdBlockFlag', '');
4179 nt.define(vl, 'ruid', '');
4180 nt.define(vl, 'user', nt.proxy(ABD_user));
4181 _console.log('Found Adblock Detector.');
4182 _hidden = vl;
4183 },
4184 get: () => _hidden
4185 });
4186 // some logging
4187 yandexRavenStub();
4188 // prevent ads from loading
4189 let blockPatterns = /\[[a-z]{1,4}\("0x[\da-f]+"\)\]|\.(rnet\.plus|24smi\.net|infox\.sg|lentainform\.com)\//i;
4190 let _toString = Function.prototype.call.bind(Function.prototype.toString);
4191 let _setTimeout = Function.prototype.apply.bind(win.setTimeout);
4192 win.setTimeout = function(f) {
4193 //_console.log('setTimeout', ...arguments);
4194 if (blockPatterns.test(_toString(f))) {
4195 _console.warn('Stopped setTimeout for:', _toString(f).slice(0,100), '\u2026');
4196 return null;
4197 };
4198 return _setTimeout(this, arguments);
4199 };
4200 // fake global Adf object
4201 let Adf_banner = {};
4202 let autoresolvePromise = () => new Promise(r => r({status: true}));
4203 [
4204 'reloadssp', 'sspScroll', 'sspRich', 'ssp', 'init'
4205 ].forEach(name => void(Adf_banner[name] = nt.proxy(autoresolvePromise)));
4206 let Adf_util = {
4207 stack: nt.func(null, 'Adf.util.stack')
4208 };
4209 nt.define(win, 'Adf', nt.proxy({
4210 banner: nt.proxy(Adf_banner),
4211 util: nt.proxy(Adf_util),
4212 }));
4213 nt.define(win, 'AdfProxy', nt.proxy(Adf_banner));
4214 // extra script to remove partner news on gazeta.ru
4215 if (!location.hostname.includes('gazeta.ru'))
4216 return;
4217 (new MutationObserver(
4218 (ms) => {
4219 let m, node, header;
4220 for (m of ms) for (node of m.addedNodes)
4221 if (node instanceof HTMLDivElement && node.matches('.sausage')) {
4222 header = node.querySelector('.sausage-header');
4223 if (header && /новоÑти\s+партн[её]ров/i.test(header.textContent))
4224 node.style.display = 'none';
4225 }
4226 }
4227 )).observe(_document.documentElement, { childList:true, subtree: true });
4228 }, `let inIFrame = ${inIFrame}`, nullTools, yandexRavenStub)
4229 }
4230 };
4231
4232 scripts['reactor.cc'] = {
4233 other: ['joyreactor.cc', 'pornreactor.cc'],
4234 now: () => {
4235 selectiveEval();
4236 scriptLander(() => {
4237 let nt = new nullTools();
4238 win.open = function(){
4239 throw new Error('Redirect prevention.');
4240 };
4241 nt.define(win, 'Worker', function(){});
4242 nt.define(win, 'JRCH', win.CoinHive);
4243 }, nullTools);
4244 },
4245 click: function(e) {
4246 let node = e.target;
4247 if (node.nodeType === _Node.ELEMENT_NODE &&
4248 node.style.position === 'absolute' &&
4249 node.style.zIndex > 0)
4250 node.parentNode.removeChild(node);
4251 },
4252 dom: function() {
4253 let tid = void 0;
4254 function probe() {
4255 let node = selectNodeByTextContent('блокировщик рекламы');
4256 if (!node) return;
4257 while (node.parentNode.offsetHeight < 750 && node !== _document.body)
4258 node = node.parentNode;
4259 _setAttribute(node, 'style', 'background:none!important');
4260 // stop observer
4261 if (!tid) tid = setTimeout(() => this.disconnect(), 1000);
4262 }
4263 (new MutationObserver(probe))
4264 .observe(_document, { childList:true, subtree:true });
4265 }
4266 };
4267
4268 scripts['auto.ru'] = () => {
4269 let words = /Реклама|ЯндекÑ.Директ|yandex_ad_/;
4270 let userAdsListAds = (
4271 '.listing-list > .listing-item,'+
4272 '.listing-item_type_fixed.listing-item'
4273 );
4274 let catalogAds = (
4275 'div[class*="layout_catalog-inline"],'+
4276 'div[class$="layout_horizontal"]'
4277 );
4278 let otherAds = (
4279 '.advt_auto,'+
4280 '.sidebar-block,'+
4281 '.pager-listing + div[class],'+
4282 '.card > div[class][style],'+
4283 '.sidebar > div[class],'+
4284 '.main-page__section + div[class],'+
4285 '.listing > tbody'
4286 );
4287 gardener(userAdsListAds, words, {root:'.listing-wrap', observe:true});
4288 gardener(catalogAds, words, {root:'.catalog__page,.content__wrapper', observe:true});
4289 gardener(otherAds, words);
4290 };
4291
4292 scripts['rsload.net'] = {
4293 load: () => {
4294 let dis = _document.querySelector('label[class*="cb-disable"]');
4295 if (dis)
4296 dis.click();
4297 },
4298 click: e => {
4299 let t = e.target;
4300 if (t && t.href && (/:\/\/\d+\.\d+\.\d+\.\d+\//.test(t.href)))
4301 t.href = t.href.replace('://','://rsload.net:rsload.net@');
4302 }
4303 };
4304
4305 let domain;
4306 // add alternative domain names if present and wrap functions into objects
4307 for (let name in scripts) {
4308 if (scripts[name] instanceof Function)
4309 scripts[name] = { now: scripts[name] };
4310 for (domain of (scripts[name].other||[])) {
4311 if (domain in scripts)
4312 _console.log('Error in scripts list. Script for', name, 'replaced script for', domain);
4313 scripts[domain] = scripts[name];
4314 }
4315 delete scripts[name].other;
4316 }
4317 // look for current domain in the list and run appropriate code
4318 domain = _document.domain;
4319 while (domain.includes('.')) {
4320 if (domain in scripts) for (let when in scripts[domain])
4321 switch(when) {
4322 case 'now':
4323 scripts[domain][when]();
4324 break;
4325 case 'dom':
4326 _document.addEventListener('DOMContentLoaded', scripts[domain][when], false);
4327 break;
4328 default:
4329 _document.addEventListener (when, scripts[domain][when], false);
4330 }
4331 domain = domain.slice(domain.indexOf('.') + 1);
4332 }
4333
4334 // Batch script lander
4335 if (!skipLander)
4336 landScript(batchLand, batchPrepend);
4337
4338 { // JS Fixes Tools Menu
4339 let openOptions = function() {
4340 let ovl = _createElement('div'),
4341 inner = _createElement('div');
4342 ovl.style = (
4343 'position: fixed;'+
4344 'top:0; left:0;'+
4345 'bottom: 0; right: 0;'+
4346 'background: rgba(0,0,0,0.85);'+
4347 'z-index: 2147483647;'+
4348 'padding: 5em'
4349 );
4350 inner.style = (
4351 'background: whitesmoke;'+
4352 'font-size: 10pt;'+
4353 'color: black;'+
4354 'padding: 1em'
4355 );
4356 inner.textContent = 'JS Fixes Tools';
4357 inner.appendChild(_createElement('br'));
4358 inner.appendChild(_createElement('br'));
4359 ovl.addEventListener(
4360 'click', function(e) {
4361 if (e.target === ovl) {
4362 ovl.parentNode.removeChild(ovl);
4363 e.preventDefault();
4364 }
4365 e.stopPropagation();
4366 }, false
4367 );
4368
4369 let sObjBtn = _createElement('button');
4370 sObjBtn.onclick = getStrangeObjectsList;
4371 sObjBtn.textContent = 'Print (in console) list of unusual window properties';
4372 inner.appendChild(_createElement('br'));
4373 inner.appendChild(sObjBtn);
4374
4375 _document.body.appendChild(ovl);
4376 ovl.appendChild(inner);
4377 };
4378
4379 // monitor keys pressed for Ctrl+Alt+Shift+J > s > f code
4380 let opPos = 0, opKey = ['KeyJ','KeyS','KeyF'];
4381 _document.addEventListener(
4382 'keydown', function(e) {
4383 if ((e.code === opKey[opPos] || e.location) &&
4384 (!!opPos || e.altKey && e.ctrlKey && e.shiftKey)) {
4385 opPos += e.location ? 0 : 1;
4386 e.stopPropagation();
4387 e.preventDefault();
4388 } else
4389 opPos = 0;
4390 if (opPos === opKey.length) {
4391 opPos = 0;
4392 openOptions();
4393 }
4394 }, false
4395 );
4396 }
4397})();