jcunews

chotot-advanced-search-1.2.4.user.js

Dec 1st, 2022
240
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.2.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. let prevUrl;
  41.  
  42. const filter = ($searchedWordsInput, $excludedWordsInput) => () => {
  43.     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__"]');
  44.  
  45.     if (prevUrl && (location.href !== prevUrl)) {
  46.       const match = location.href.match(/\/mua-ban\?q=([^&]+)/);
  47.       if (match && localStorage.searchedWords && ($searchedWordsInput.value !== localStorage.searchedWords)) {
  48.         setTimeout(() => $searchedWordsInput.value = localStorage.searchedWords, 0);
  49.       }
  50.     }
  51.     prevUrl = location.href;
  52.  
  53.     const searchedWords = split(toSearcheable($searchedWordsInput.value));
  54.     const excludedWords = split(toSearcheable($excludedWordsInput.value));
  55.     localStorage.excludedWords = $excludedWordsInput.value;
  56.  
  57.     const lacksAllSearchedWords = element => !searchedWords.every(w => element.dataset.searcheableText.includes(w));
  58.     const hasAnyExcludedWords = element => excludedWords.some(w => element.dataset.searcheableText.includes(w));
  59.  
  60.     const withSearcheableText = el => {
  61.         el.dataset.searcheableText = toSearcheable(el.querySelector('h3[class*="commonStyle_adTitle__"],div[class*="commonStyle_adTitleGrid__"]')?.textContent ?? '');
  62.         return el;
  63.     };
  64.  
  65.     const toggleHidden = (count, el) => {
  66.         if (lacksAllSearchedWords(el) || hasAnyExcludedWords(el)) {
  67.             el.style.display = 'none';
  68.             count++;
  69.         } else {
  70.             el.style.display = 'block';
  71.         }
  72.         return count;
  73.     };
  74.  
  75.     const $loadedProducts = $products
  76.         .map(withSearcheableText)
  77.         .filter(p => p.dataset.searcheableText);
  78.  
  79.     const hiddenCount = $loadedProducts.reduce(toggleHidden, 0);
  80.  
  81.     const excludedMsg = excludedWords.length ? ` -'${excludedWords.join(' ')}'` : '';
  82.     console.log(`${$products.length} products, ${$loadedProducts.length} loaded, ${hiddenCount} hidden for '${searchedWords.join(' ')}'${excludedMsg}`);
  83.  
  84.     document.querySelectorAll('div[class*="Paging_pagingItem__"]>a').forEach(a => {
  85.       a.href = a.href.replace(/\bq=[^&]+/, 'q=' + encodeURIComponent($searchedWordsInput.value.trim()))
  86.     });
  87. };
  88.  
  89. let filterProducts;
  90. const enable = () => {
  91.     const $searchBar = window?.__inputItemProps?.parentNode;
  92.     if (!$searchBar || $searchBar.querySelector('#excludedWords')) return;
  93.  
  94.     console.log('chotot filter enabled');
  95.  
  96.     const $searchedWordsInput = __inputItemProps;
  97.     $searchedWordsInput.addEventListener("input", () => {
  98.       localStorage.searchedWords = $searchedWordsInput.value;
  99.     });
  100.     localStorage.searchedWords = $searchedWordsInput.value;
  101.     const $excludedWordsInput = el('input', {
  102.       id: 'excludedWords',
  103.       placeholder: isBrazil() ? 'excluir palavras' : 'exclude words',
  104.       value: localStorage.excludedWords || "",
  105.       onkeyup: function(e) { if (e.key === 'Enter') filterProducts(); }
  106.     });
  107.     filterProducts = filter($searchedWordsInput, $excludedWordsInput);
  108.  
  109.     const $filterButton = el('button', {
  110.         type: 'button',
  111.         onclick: filterProducts,
  112.         style: `
  113.             background: no-repeat url(${toBase64(filterIconSvg)});
  114.             padding: 13px;
  115.             margin-top: 3px;
  116.             margin-right: 4em;
  117.             border: none;
  118.         `,
  119.     });
  120.  
  121.     $searchBar.appendChild($excludedWordsInput);
  122.     $excludedWordsInput.parentNode.firstElementChild.style.width = "auto";
  123.     $excludedWordsInput.previousElementSibling.style.paddingRight = ".5em";
  124.     $searchBar.appendChild($filterButton);
  125. };
  126.  
  127. const observer = new MutationObserver(() => {
  128.     enable();
  129.     filterProducts && filterProducts();
  130. });
  131. observer.observe(document.body, { childList: true, subtree: true });
  132.  
Add Comment
Please, Sign In to add comment