jcunews

chotot-advanced-search.user.js

Nov 27th, 2022 (edited)
631
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name        chotot.com Advanced Search
  3. // @namespace   https://greasyfork.org/en/users/85671-jcunews
  4. // @version     1.1.3
  5. // @license     MIT
  6. // @author      jcunews
  7. // @description Based on https://openuserjs.org/scripts/icetbr/Shopee_Advanced_Search
  8. // @match       https://www.chotot.com/*
  9. // @grant       none
  10. // ==/UserScript==
  11.  
  12. const
  13.     $  = (selector, parent = document) => parent.querySelector(selector),
  14.  
  15.     $$ = (selector, parent = document) => Array.from(parent.querySelectorAll(selector)),
  16.  
  17.     el = (name, attrs) => Object.assign(document.createElement(name), attrs),
  18.  
  19.     toBase64 = svg => `data:image/svg+xml;base64,${window.btoa(svg)}`,
  20.  
  21.     toSearcheable = string => string
  22.         .trim()
  23.         .toLowerCase()
  24.         .normalize('NFD')
  25.         .replace(/\p{Diacritic}/gu, ''),
  26.  
  27.     isBrazil = () => window.location.hostname.endsWith('.br');
  28.  
  29. const split = value => value ? value.split(' ') : [];
  30.  
  31. const filterIconSvg = `
  32.     <svg width="26px" height="26px" viewBox="0 0 21 21" xmlns="http://www.w3.org/2000/svg">
  33.         <g stroke="currentColor">
  34.             <path d="m4.5 7.5h12"/>
  35.             <path d="m6.5 10.5h8"/>
  36.             <path d="m8.5 13.5h4"/>
  37.         </g>
  38.     </svg>`;
  39.  
  40. const filter = ($searchedWordsInput, $excludedWordsInput) => () => {
  41.     const $products = $$('div[class*="OrderFilter_orderFilter__"]+.list-view div[class*="ListAds_ListAds__"]>ul>div[role="button"],div[class*="OrderFilter_orderFilter__"]+.list-view div[class*="AdItem_item__"]');
  42.  
  43.     const searchedWords = split(toSearcheable($searchedWordsInput.value));
  44.     const excludedWords = split(toSearcheable($excludedWordsInput.value));
  45.     localStorage.excludedWords = $excludedWordsInput.value;
  46.  
  47.     const lacksAllSearchedWords = element => !searchedWords.every(w => element.dataset.searcheableText.includes(w));
  48.     const hasAnyExcludedWords = element => excludedWords.some(w => element.dataset.searcheableText.includes(w));
  49.  
  50.     const withSearcheableText = el => {
  51.         el.dataset.searcheableText = toSearcheable(el.querySelector('h3[class*="commonStyle_adTitle__"],div[class*="commonStyle_adTitleGrid__"]')?.textContent ?? '');
  52.         return el;
  53.     };
  54.  
  55.     const toggleHidden = (count, el) => {
  56.         if (lacksAllSearchedWords(el) || hasAnyExcludedWords(el)) {
  57.             el.style.display = 'none';
  58.             count++;
  59.         } else {
  60.             el.style.display = 'block';
  61.         }
  62.         return count;
  63.     };
  64.  
  65.     const $loadedProducts = $products
  66.         .map(withSearcheableText)
  67.         .filter(p => p.dataset.searcheableText);
  68.  
  69.     const hiddenCount = $loadedProducts.reduce(toggleHidden, 0);
  70.  
  71.     const excludedMsg = excludedWords.length ? ` -'${excludedWords.join(' ')}'` : '';
  72.     console.log(`${$products.length} products, ${$loadedProducts.length} loaded, ${hiddenCount} hidden for '${searchedWords.join(' ')}'${excludedMsg}`);
  73. };
  74.  
  75. let filterProducts;
  76. const enable = () => {
  77.     const $searchedWordsInput = document.querySelector('input#__inputItemProps');
  78.     const $searchBar = $searchedWordsInput?.parentNode;
  79.     if (!$searchBar || $searchBar.querySelector('#excludedWords')) return;
  80.  
  81.     console.log('chotot filter enabled');
  82.  
  83.     const $excludedWordsInput = el('input', {
  84.       id: 'excludedWords',
  85.       placeholder: isBrazil() ? 'excluir palavras' : 'exclude words',
  86.       value: localStorage.excludedWords || "",
  87.       onkeyup: function(e) { if (e.key === 'Enter') filterProducts(); }
  88.     });
  89.     filterProducts = filter($searchedWordsInput, $excludedWordsInput);
  90.  
  91.     const $filterButton = el('button', {
  92.         type: 'button',
  93.         onclick: filterProducts,
  94.         style: `
  95.             background: no-repeat url(${toBase64(filterIconSvg)});
  96.             padding: 13px;
  97.             margin-top: 3px;
  98.             margin-right: 4em;
  99.             border: none;
  100.         `,
  101.     });
  102.  
  103.     $searchBar.appendChild($excludedWordsInput);
  104.     $excludedWordsInput.parentNode.firstElementChild.style.width = "auto";
  105.     $excludedWordsInput.previousElementSibling.style.paddingRight = ".5em";
  106.     $searchBar.appendChild($filterButton);
  107. };
  108.  
  109. const observer = new MutationObserver(() => {
  110.     enable();
  111.     filterProducts && filterProducts();
  112. });
  113. observer.observe(document.body, { childList: true, subtree: true });
Add Comment
Please, Sign In to add comment