No Yandex Ads

Removes ads in Yandex search results.

  1. // ==UserScript==
  2. // @name No Yandex Ads
  3. // @namespace lainverse_no_yandex_ads
  4. // @description Removes ads in Yandex search results.
  5. // @author lainverse
  6. // @contributor James Lewis
  7. // @license CC-BY-SA
  8. // @version 3.6.1
  9. // @grant none
  10. // @match http://*/yandsearch?*
  11. // @match https://*/yandsearch?*
  12. // @match http://yandex.ru/search/*
  13. // @match http://*.yandex.ru/search/*
  14. // @match https://yandex.ru/search/*
  15. // @match https://*.yandex.ru/search/*
  16. // @run-at document-end
  17. // ==/UserScript==
  18.  
  19. /*!
  20. * Shim for MutationObserver interface
  21. * Author: Graeme Yeates (github.com/megawac)
  22. * Repository: https://github.com/megawac/MutationObserver.js
  23. * License: WTFPL V2, 2004 (wtfpl.net).
  24. * Though credit and staring the repo will make me feel pretty, you can modify and redistribute as you please.
  25. * Attempts to follow spec (http:// www.w3.org/TR/dom/#mutation-observers) as closely as possible for native javascript
  26. * See https://github.com/WebKit/webkit/blob/master/Source/WebCore/dom/MutationObserver.cpp for current webkit source c++ implementation
  27. */
  28. /**
  29. * prefix bugs:
  30. - https://bugs.webkit.org/show_bug.cgi?id=85161
  31. - https://bugzilla.mozilla.org/show_bug.cgi?id=749920
  32. * Don't use WebKitMutationObserver as Safari (6.0.5-6.1) use a buggy implementation
  33. */
  34. window.MutationObserver = window.MutationObserver || window.MozMutationObserver || (function (undefined) {
  35. 'use strict';
  36. /**
  37. * @param {function(Array.<MutationRecord>, MutationObserver)} listener
  38. * @constructor
  39. */
  40. function MutationObserver(listener) {
  41. /**
  42. * @type {Array.<Object>}
  43. * @private
  44. */
  45. this._watched = [];
  46. /** @private */
  47. this._listener = listener;
  48. }
  49. /**
  50. * Start a recursive timeout function to check all items being observed for mutations
  51. * @type {MutationObserver} observer
  52. * @private
  53. */
  54. function startMutationChecker(observer) {
  55. (function check() {
  56. var mutations = observer.takeRecords();
  57. if (mutations.length) // fire away
  58. // calling the listener with context is not spec but currently consistent with FF and WebKit
  59. observer._listener(mutations, observer);
  60. /** @private */
  61. observer._timeout = setTimeout(check, MutationObserver._period);
  62. })();
  63. }
  64. /**
  65. * Period to check for mutations (~32 times/sec)
  66. * @type {number}
  67. * @expose
  68. */
  69. MutationObserver._period = 30; /*ms+runtime*/
  70. /**
  71. * Exposed API
  72. * @expose
  73. * @final
  74. */
  75. MutationObserver.prototype = {
  76. /**
  77. * see http:// dom.spec.whatwg.org/#dom-mutationobserver-observe
  78. * not going to throw here but going to follow the current spec config sets
  79. * @param {Node|null} $target
  80. * @param {Object|null} config : MutationObserverInit configuration dictionary
  81. * @expose
  82. * @return undefined
  83. */
  84. observe: function($target, config) {
  85. /**
  86. * Using slightly different names so closure can go ham
  87. * @type {!Object} : A custom mutation config
  88. */
  89. var settings = {
  90. attr: !!(config.attributes || config.attributeFilter || config.attributeOldValue),
  91. // some browsers are strict in their implementation that config.subtree and childList must be set together. We don't care - spec doesn't specify
  92. kids: !!config.childList, descendents: !!config.subtree,
  93. charData: !!(config.characterData || config.characterDataOldValue)
  94. }, watched = this._watched;
  95. // remove already observed target element from pool
  96. for (var i = 0; i < watched.length; i++)
  97. if (watched[i].tar === $target) watched.splice(i, 1);
  98. if (config.attributeFilter)
  99. /**
  100. * converts to a {key: true} dict for faster lookup
  101. * @type {Object.<String,Boolean>}
  102. */
  103. settings.afilter = reduce(config.attributeFilter, function(a, b) {
  104. a[b] = true;
  105. return a;
  106. }, {});
  107. watched.push({
  108. tar: $target,
  109. fn: createMutationSearcher($target, settings)
  110. });
  111. // reconnect if not connected
  112. if (!this._timeout) startMutationChecker(this);
  113. },
  114. /**
  115. * Finds mutations since last check and empties the "record queue" i.e. mutations will only be found once
  116. * @expose
  117. * @return {Array.<MutationRecord>}
  118. */
  119. takeRecords: function() {
  120. var mutations = [], watched = this._watched, wl = watched.length;
  121. for (var i = 0; i < wl; i++) watched[i].fn(mutations);
  122. return mutations;
  123. },
  124. /**
  125. * @expose
  126. * @return undefined
  127. */
  128. disconnect: function() {
  129. this._watched = []; // clear the stuff being observed
  130. clearTimeout(this._timeout); // ready for garbage collection
  131. /** @private */
  132. this._timeout = null;
  133. }
  134. };
  135. /**
  136. * Simple MutationRecord pseudoclass. No longer exposing as its not fully compliant
  137. * @param {Object} data
  138. * @return {Object} a MutationRecord
  139. */
  140. function MutationRecord(data) {
  141. var settings = { // technically these should be on proto so hasOwnProperty will return false for non explicitly set props
  142. type: null, target: null, addedNodes: [], removedNodes: [], previousSibling: null,
  143. nextSibling: null, attributeName: null, attributeNamespace: null, oldValue: null
  144. };
  145. for (var prop in data)
  146. if (has(settings, prop) && data[prop] !== undefined) settings[prop] = data[prop];
  147. return settings;
  148. }
  149. /**
  150. * Creates a func to find all the mutations
  151. *
  152. * @param {Node} $target
  153. * @param {!Object} config : A custom mutation config
  154. */
  155. function createMutationSearcher($target, config) {
  156. /** type {Elestuct} */
  157. var $oldstate = clone($target, config); // create the cloned datastructure
  158. /**
  159. * consumes array of mutations we can push to
  160. *
  161. * @param {Array.<MutationRecord>} mutations
  162. */
  163. return function(mutations) {
  164. var olen = mutations.length, dirty;
  165. // Alright we check base level changes in attributes... easy
  166. if (config.attr && $oldstate.attr)
  167. findAttributeMutations(mutations, $target, $oldstate.attr, config.afilter);
  168. // check childlist or subtree for mutations
  169. if (config.kids || config.descendents)
  170. dirty = searchSubtree(mutations, $target, $oldstate, config);
  171. // reclone data structure if theres changes
  172. if (dirty || mutations.length !== olen)
  173. /** type {Elestuct} */
  174. $oldstate = clone($target, config);
  175. };
  176. }
  177. /* attributes + attributeFilter helpers */
  178. /**
  179. * fast helper to check to see if attributes object of an element has changed
  180. * doesnt handle the textnode case
  181. *
  182. * @param {Array.<MutationRecord>} mutations
  183. * @param {Node} $target
  184. * @param {Object.<string, string>} $oldstate : Custom attribute clone data structure from clone
  185. * @param {Object} filter
  186. */
  187. function findAttributeMutations(mutations, $target, $oldstate, filter) {
  188. var checked = {}, attributes = $target.attributes, i = attributes.length, attr, name;
  189. while (i--) {
  190. attr = attributes[i];
  191. name = attr.name;
  192. if (!filter || has(filter, name)) {
  193. if (attr.value !== $oldstate[name])
  194. // The pushing is redundant but gzips very nicely
  195. mutations.push(MutationRecord({
  196. type: 'attributes', target: $target, attributeName: name, oldValue: $oldstate[name],
  197. attributeNamespace: attr.namespaceURI // in ie<8 it incorrectly will return undefined
  198. }));
  199. checked[name] = true;
  200. }
  201. }
  202. for (name in $oldstate)
  203. if (!(checked[name]) && $oldstate.hasOwnProperty(name))
  204. mutations.push(MutationRecord({target: $target, type: 'attributes', attributeName: name, oldValue: $oldstate[name]}));
  205. }
  206. /**
  207. * searchSubtree: array of mutations so far, element, element clone, bool
  208. * synchronous dfs comparision of two nodes
  209. * This function is applied to any observed element with childList or subtree specified
  210. * Sorry this is kind of confusing as shit, tried to comment it a bit...
  211. * codereview.stackexchange.com/questions/38351 discussion of an earlier version of this func
  212. *
  213. * @param {Array} mutations
  214. * @param {Node} $target
  215. * @param {!Object} $oldstate : A custom cloned node from clone()
  216. * @param {!Object} config : A custom mutation config
  217. */
  218. function searchSubtree(mutations, $target, $oldstate, config) {
  219. // Track if the tree is dirty and has to be recomputed (#14).
  220. var dirty;
  221. /*
  222. * Helper to identify node rearrangment and stuff...
  223. * There is no gaurentee that the same node will be identified for both added and removed nodes
  224. * if the positions have been shuffled.
  225. * conflicts array will be emptied by end of operation
  226. */
  227. function resolveConflicts(conflicts, node, $kids, $oldkids, numAddedNodes) {
  228. // the distance between the first conflicting node and the last
  229. var distance = conflicts.length - 1,
  230. // prevents same conflict being resolved twice consider when two nodes switch places.
  231. // only one should be given a mutation event (note -~ is used as a math.ceil shorthand)
  232. counter = -~((distance - numAddedNodes) / 2), $cur, oldstruct, conflict;
  233. while (conflict = conflicts.pop()) {
  234. $cur = $kids[conflict.i];
  235. oldstruct = $oldkids[conflict.j];
  236. // attempt to determine if there was node rearrangement... won't gaurentee all matches
  237. // also handles case where added/removed nodes cause nodes to be identified as conflicts
  238. if (config.kids && counter && Math.abs(conflict.i - conflict.j) >= distance) {
  239. mutations.push(MutationRecord({
  240. type: 'childList', target: node, addedNodes: [$cur], removedNodes: [$cur],
  241. // haha don't rely on this please
  242. nextSibling: $cur.nextSibling, previousSibling: $cur.previousSibling
  243. }));
  244. counter--; // found conflict
  245. }
  246. // Alright we found the resorted nodes now check for other types of mutations
  247. if (config.attr && oldstruct.attr) findAttributeMutations(mutations, $cur, oldstruct.attr, config.afilter);
  248. if (config.charData && $cur.nodeType === 3 && $cur.nodeValue !== oldstruct.charData)
  249. mutations.push(MutationRecord({type: 'characterData', target: $cur}));
  250. // now look @ subtree
  251. if (config.descendents) findMutations($cur, oldstruct);
  252. }
  253. }
  254. /**
  255. * Main worker. Finds and adds mutations if there are any
  256. * @param {Node} node
  257. * @param {!Object} old : A cloned data structure using internal clone
  258. */
  259. function findMutations(node, old) {
  260. var $kids = node.childNodes, $oldkids = old.kids, klen = $kids.length,
  261. // $oldkids will be undefined for text and comment nodes
  262. olen = $oldkids ? $oldkids.length : 0;
  263. // if (!olen && !klen) return; // both empty; clearly no changes
  264. // we delay the intialization of these for marginal performance in the expected case (actually quite signficant on large subtrees when these would be otherwise unused)
  265. // map of checked element of ids to prevent registering the same conflict twice
  266. var map,
  267. // array of potential conflicts (ie nodes that may have been re arranged)
  268. conflicts, id, // element id from getElementId helper
  269. idx, // index of a moved or inserted element
  270. oldstruct,
  271. // current and old nodes
  272. $cur, $old,
  273. // track the number of added nodes so we can resolve conflicts more accurately
  274. numAddedNodes = 0,
  275. // iterate over both old and current child nodes at the same time
  276. i = 0, j = 0;
  277. // while there is still anything left in $kids or $oldkids (same as i < $kids.length || j < $oldkids.length;)
  278. while (i < klen || j < olen) {
  279. // current and old nodes at the indexs
  280. $cur = $kids[i];
  281. oldstruct = $oldkids[j];
  282. $old = oldstruct && oldstruct.node;
  283. if ($cur === $old) { // expected case - optimized for this case
  284. // check attributes as specified by config
  285. if (config.attr && oldstruct.attr) /* oldstruct.attr instead of textnode check */ findAttributeMutations(mutations, $cur, oldstruct.attr, config.afilter);
  286. // check character data if node is a comment or textNode and it's being observed
  287. if (config.charData && oldstruct.charData !== undefined && $cur.nodeValue !== oldstruct.charData)
  288. mutations.push(MutationRecord({type: 'characterData', target: $cur}));
  289. // resolve conflicts; it will be undefined if there are no conflicts - otherwise an array
  290. if (conflicts) resolveConflicts(conflicts, node, $kids, $oldkids, numAddedNodes);
  291. // recurse on next level of children. Avoids the recursive call when there are no children left to iterate
  292. if (config.descendents && ($cur.childNodes.length || oldstruct.kids && oldstruct.kids.length)) findMutations($cur, oldstruct);
  293. i++;
  294. j++;
  295. } else { // (uncommon case) lookahead until they are the same again or the end of children
  296. dirty = true;
  297. if (!map) { // delayed initalization (big perf benefit)
  298. map = {};
  299. conflicts = [];
  300. }
  301. if ($cur) {
  302. // check id is in the location map otherwise do a indexOf search
  303. if (!(map[id = getElementId($cur)])) { // to prevent double checking
  304. // mark id as found
  305. map[id] = true;
  306. // custom indexOf using comparitor checking oldkids[i].node === $cur
  307. if ((idx = indexOfCustomNode($oldkids, $cur, j)) === -1)
  308. if (config.kids) {
  309. mutations.push(MutationRecord({
  310. type: 'childList', target: node,
  311. addedNodes: [$cur], // $cur is a new node
  312. nextSibling: $cur.nextSibling, previousSibling: $cur.previousSibling
  313. }));
  314. numAddedNodes++;
  315. }
  316. else conflicts.push({i: i, j: idx}); // add conflict
  317. }
  318. i++;
  319. }
  320. if ($old &&
  321. // special case: the changes may have been resolved: i and j appear congurent so we can continue using the expected case
  322. $old !== $kids[i]
  323. ) {
  324. if (!(map[id = getElementId($old)])) {
  325. map[id] = true;
  326. if ((idx = indexOf($kids, $old, i)) === -1)
  327. if (config.kids) {
  328. mutations.push(MutationRecord({
  329. type: 'childList', target: old.node, removedNodes: [$old],
  330. nextSibling: $oldkids[j + 1], // praise no indexoutofbounds exception
  331. previousSibling: $oldkids[j - 1]
  332. }));
  333. numAddedNodes--;
  334. }
  335. else conflicts.push({i: idx, j: j});
  336. }
  337. j++;
  338. }
  339. }// end uncommon case
  340. }// end loop
  341. // resolve any remaining conflicts
  342. if (conflicts) resolveConflicts(conflicts, node, $kids, $oldkids, numAddedNodes);
  343. }
  344. findMutations($target, $oldstate);
  345. return dirty;
  346. }
  347. /**
  348. * Utility
  349. * Clones a element into a custom data structure designed for comparision. https://gist.github.com/megawac/8201012
  350. *
  351. * @param {Node} $target
  352. * @param {!Object} config : A custom mutation config
  353. * @return {!Object} : Cloned data structure
  354. */
  355. function clone($target, config) {
  356. var recurse = true; // set true so childList we'll always check the first level
  357. return (function copy($target) {
  358. var elestruct = {/** @type {Node} */ node: $target};
  359. // Store current character data of target text or comment node if the config requests
  360. // those properties to be observed.
  361. if (config.charData && ($target.nodeType === 3 || $target.nodeType === 8))
  362. elestruct.charData = $target.nodeValue;
  363. // its either a element, comment, doc frag or document node
  364. else {
  365. // Add attr only if subtree is specified or top level and avoid if
  366. // attributes is a document object (#13).
  367. if (config.attr && recurse && $target.nodeType === 1)
  368. /**
  369. * clone live attribute list to an object structure {name: val}
  370. * @type {Object.<string, string>}
  371. */
  372. elestruct.attr = reduce($target.attributes, function(memo, attr) {
  373. if (!config.afilter || config.afilter[attr.name])
  374. memo[attr.name] = attr.value;
  375. return memo;
  376. }, {});
  377. // whether we should iterate the children of $target node
  378. if (recurse && ((config.kids || config.charData) || (config.attr && config.descendents)))
  379. /** @type {Array.<!Object>} : Array of custom clone */
  380. elestruct.kids = map($target.childNodes, copy);
  381. recurse = config.descendents;
  382. }
  383. return elestruct;
  384. }($target));
  385. }
  386. /**
  387. * indexOf an element in a collection of custom nodes
  388. *
  389. * @param {NodeList} set
  390. * @param {!Object} $node : A custom cloned node
  391. * @param {number} idx : index to start the loop
  392. * @return {number}
  393. */
  394. function indexOfCustomNode(set, $node, idx) {
  395. return indexOf(set, $node, idx, JSCompiler_renameProperty('node'));
  396. }
  397. // using a non id (eg outerHTML or nodeValue) is extremely naive and will run into issues with nodes that may appear the same like <li></li>
  398. var counter = 1, // don't use 0 as id (falsy)
  399. /** @const */
  400. expando = 'mo_id';
  401. /**
  402. * Attempt to uniquely id an element for hashing. We could optimize this for legacy browsers but it hopefully wont be called enough to be a concern
  403. *
  404. * @param {Node} $ele
  405. * @return {(string|number)}
  406. */
  407. function getElementId($ele) {
  408. try {
  409. return $ele.id || ($ele[expando] = $ele[expando] || counter++);
  410. } catch (o_O) { // ie <8 will throw if you set an unknown property on a text node
  411. try {
  412. return $ele.nodeValue; // naive
  413. } catch (shitie) { // when text node is removed: https://gist.github.com/megawac/8355978 :(
  414. return counter++;
  415. }
  416. }
  417. }
  418. /**
  419. * **map** Apply a mapping function to each item of a set
  420. * @param {Array|NodeList} set
  421. * @param {Function} iterator
  422. */
  423. function map(set, iterator) {
  424. var results = [], sl = set.length;
  425. for (var index = 0; index < sl; index++)
  426. results[index] = iterator(set[index], index, set);
  427. return results;
  428. }
  429. /**
  430. * **Reduce** builds up a single result from a list of values
  431. * @param {Array|NodeList|NamedNodeMap} set
  432. * @param {Function} iterator
  433. * @param {*} [memo] Initial value of the memo.
  434. */
  435. function reduce(set, iterator, memo) {
  436. var sl = set.length;
  437. for (var index = 0; index < sl; index++)
  438. memo = iterator(memo, set[index], index, set);
  439. return memo;
  440. }
  441. /**
  442. * **indexOf** find index of item in collection.
  443. * @param {Array|NodeList} set
  444. * @param {Object} item
  445. * @param {number} idx
  446. * @param {string} [prop] Property on set item to compare to item
  447. */
  448. function indexOf(set, item, idx, prop) {
  449. var sl = set.length;
  450. for (/*idx = ~~idx*/; idx < sl; idx++) // start idx is always given as this is internal
  451. if ((prop ? set[idx][prop] : set[idx]) === item) return idx;
  452. return -1;
  453. }
  454. /**
  455. * @param {Object} obj
  456. * @param {(string|number)} prop
  457. * @return {boolean}
  458. */
  459. function has(obj, prop) {
  460. return obj[prop] !== undefined; // will be nicely inlined by gcc
  461. }
  462. // GCC hack see http://stackoverflow.com/a/23202438/1517919
  463. function JSCompiler_renameProperty(a) {
  464. return a;
  465. }
  466. return MutationObserver;
  467. }());
  468. (function (window, undefined) {
  469. 'use strict';
  470. (function (s) {
  471. for (var l in s) s[l].parentNode.removeChild(s[l]);
  472. })(document.querySelectorAll('.serp-adv,.b-spec-adv'));
  473. function removeAds() {
  474. var s = document.querySelectorAll('.serp-block');
  475. for (var l in s)
  476. if (s[l].querySelector('.serp-item__label'))
  477. s[l].parentNode.removeChild(s[l]);
  478. }
  479. (function(s){
  480. if (s) (new MutationObserver(removeAds)).observe(s, {childList: true});
  481. })(document.querySelector('.main__content'));
  482. removeAds();
  483. }(window));

QingJ © 2025

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