Greasy Fork

来自缓存

Greasy Fork is available in English.

查找图片

查找图片哦

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         查找图片
// @namespace    http://tampermonkey.net/
// @version      0.2
// @description  查找图片哦
// @author       Chengguan
// @match        https://*.huaban.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=huaban.com
// @grant        GM_registerMenuCommand
// @run-at       document-body
// @license      MIT
// ==/UserScript==

(function () {
  'use strict';

  const styleEle = document.createElement('style');
  styleEle.innerText = `
      .hb-img {outline: #F007 dashed 5px; outline-offset: -5px;}

      .hb-imgTip { font-size: 11px; position: absolute; z-index: 999999; opacity: 0.8; color: #F00; background-color: #FFF; word-break: break-all; padding: 4px;}
      .hb-imgTip:hover { z-index: 9999999999; opacity: 1;}

      .hb-container { position: fixed; top: 50px; left: 50px; width: 320px; box-shadow: 0px 0px 20px 4px #686868; background-color: #FFF; z-index: 99999999999; padding: 10px; resize: both; overflow: auto; text-align: center; border: 1px solid #CCC; border-radius: 5px; user-select: none; }

      .hb-container h1 { font-size: 22px; cursor: move; background-color: #efefef }
      .hb-container h1:hover { background-color: #d9d9d9 }

      .hb-container h2 { font-size: 18px;  }

      .hb-container label { padding: 8px; display: block; text-align: left;}
      .hb-container label:hover { background-color: #efefef}

      .hb-container label select { margin-left: 5px; padding: 3px; }
  `;
  document.head.appendChild(styleEle);

  let allTips = [];
  let allImages = [];
  let allFileSizePromises = [];

  function mark(
    reg = /./,
    {
      reverseSelect = false,
      showImageSrc = false,
      showImageSize = false,
      domainReplace = '',
      urlReplaceInput = false,
      urlReplaceFrom = '',
      urlReplaceTo = '',
    } = {},
  ) {
    allTips.forEach((tip) => tip.remove());
    allImages.forEach((img) => img.classList.remove('hb-img'));

    allTips = [];
    allImages = [];
    allFileSizePromises = [];

    [...document.querySelectorAll('img')].forEach((img) => {
      console.count('Image');
      const src =
        (img.dataset.bakSrc || '') + (img.dataset.bakSrcset || '') ||
        img.currentSrc;

      let testValue = reg.test(src);
      testValue = reverseSelect ? !testValue : testValue;

      if (testValue) {
        img.classList.add('hb-img');

        // 替换域名 或 替换文件地址
        if (domainReplace || urlReplaceInput) {
          const urlObj = new URL(src);
          const oldHost = urlObj.host;

          // 备份
          if (!img.getAttribute('data-bak-src')) {
            img.src && img.setAttribute('data-bak-src', img.src);
            img.srcset && img.setAttribute('data-bak-srcset', img.srcset);
          }

          let newSrc = img.getAttribute('data-bak-src') || img.src;
          let newSrcset = img.getAttribute('data-bak-srcset') || img.srcset;

          // 替换
          if (domainReplace) {
            newSrc = newSrc.replace(oldHost, domainReplace);
            newSrcset = newSrcset.replace(oldHost, domainReplace);
          }

          if (urlReplaceInput) {
            newSrc = newSrc.replace(urlReplaceFrom, urlReplaceTo);
            newSrcset = newSrcset.replace(urlReplaceFrom, urlReplaceTo);
          }

          const srcAddRandom = (src) => {
            const random = Math.random()
              .toString(36)
              .substring(2, 15)
              .replace('.', '');
            const srcParts = src.split(' ');
            srcParts[0] =
              srcParts[0] + (srcParts[0].includes('?') ? '&' : '?') + random;

            return srcParts.join(' ');
          };

          newSrc && img.setAttribute('src', srcAddRandom(newSrc));
          newSrcset && img.setAttribute('srcset', srcAddRandom(newSrcset));
        } else if (img.getAttribute('data-bak-src')) {
          // 恢复
          img.dataset.bakSrc && (img.src = img.getAttribute('data-bak-src'));
          img.dataset.bakSrcset &&
            (img.srcset = img.getAttribute('data-bak-srcset'));

          // // 清除
          // img.removeAttribute('data-bak-src');
          // img.removeAttribute('data-bak-srcset');
        }

        function imageCompleted() {
          if (showImageSrc || showImageSize) {
            const { tip, fileSize } = createImgTip(img, {
              showImageSrc,
              showImageSize,
            });
            document.scrollingElement.appendChild(tip);
            allTips.push(tip);
            allFileSizePromises.push(fileSize);
          }

          img.removeEventListener('onload', imageCompleted);
        }

        imageCompleted();

        allImages.push(img);
      }
    });

    return allImages;
  }

  function createImgTip(img, { showImageSrc, showImageSize }) {
    let tip = document.createElement('p');
    tip.className = 'hb-imgTip';
    tip.style.top =
      img.getBoundingClientRect().top +
      document.scrollingElement.scrollTop +
      'px';
    tip.style.left =
      img.getBoundingClientRect().left +
      document.scrollingElement.scrollLeft +
      'px';
    tip.style.maxWidth =
      Math.max(img.getBoundingClientRect().width, 200) + 'px';

    let fileSize;

    if (showImageSrc) {
      tip.innerText = img.currentSrc.startsWith('data:image')
        ? 'data:image/ xxxx'
        : img.currentSrc + '\n';
    }

    if (showImageSize) {
      let currentSrc = img.currentSrc.startsWith('data:image')
        ? ''
        : img.currentSrc;

      if (currentSrc) {
        fileSize = getImageSize(currentSrc).then((result) => {
          let size = (result / 1024).toFixed(2);
          tip.innerText += ' 尺寸:' + size + 'KB';
          return size;
        });
      }
    }
    return { tip, fileSize };
  }

  // 拖拽元素
  function dragableElement({ element, trigger }) {
    trigger = trigger || element;

    let sx = 0;
    let sy = 0;

    trigger.addEventListener('mousedown', (e) => {
      let x = parseInt(window.getComputedStyle(element).left, 10);
      let y = parseInt(window.getComputedStyle(element).top, 10);

      sx = e.clientX;
      sy = e.clientY;

      const moveHandler = (e) => {
        console.info(e.clientX, e.clientY);
        console.info(e.clientX - sx, e.clientY - sy);

        element.style.left = x + (e.clientX - sx) + 'px';
        element.style.top = y + (e.clientY - sy) + 'px';
      };

      const upHandler = () => {
        document.body.removeEventListener('mousemove', moveHandler);
        document.body.removeEventListener('mouseup', upHandler);
      };

      document.body.addEventListener('mousemove', moveHandler);
      document.body.addEventListener('mouseup', upHandler);
    });
  }

  // 获取文件大小
  function getImageSize(url) {
    return fetch(url)
      .then((response) => {
        if (!response.ok) {
          throw new Error('Network response was not ok');
        }
        return response.headers.get('content-length');
      })
      .then((contentLength) => {
        // console.log('远程图片大小为: ' + contentLength + ' 字节');
        return contentLength;
      })
      .catch((error) => {
        console.error(
          'There has been a problem with your fetch operation:',
          error,
        );
      });
  }

  GM_registerMenuCommand(
    '查找图片',
    () => {
      // 容器
      const container = document.createElement('div');
      container.className = 'hb-container';
      document.body.appendChild(container);

      // 标题
      const titleEle = document.createElement('h1');
      titleEle.innerText = '查找图片';
      container.appendChild(titleEle);

      dragableElement({ element: container, trigger: titleEle });

      // 匹配图片数量
      const matchCount = document.createElement('h2');
      container.appendChild(matchCount);

      // 匹配正则
      const matchRegLabel = document.createElement('label');
      matchRegLabel.innerText = '匹配图片:';
      container.appendChild(matchRegLabel);

      const input = document.createElement('input');
      matchRegLabel.appendChild(input);

      // 显示图片地址
      const showSrcLabel = document.createElement('label');
      showSrcLabel.innerText = ' 显示图片地址   ';
      container.appendChild(showSrcLabel);

      const showSrcInput = document.createElement('input');
      showSrcInput.setAttribute('type', 'checkbox');
      showSrcLabel.prepend(showSrcInput);

      // 显示图片文件大小
      const showFileSizeLabel = document.createElement('label');
      showFileSizeLabel.innerText = ' 显示图片大小';
      container.appendChild(showFileSizeLabel);

      const showFileSizeInput = document.createElement('input');
      showFileSizeInput.setAttribute('type', 'checkbox');
      // showFileSizeInput.setAttribute('checked', true);
      showFileSizeLabel.prepend(showFileSizeInput);

      // 反选图片
      const reverseLabel = document.createElement('label');
      reverseLabel.innerText = ' 反选';
      container.appendChild(reverseLabel);

      const reverseSelect = document.createElement('input');
      reverseSelect.setAttribute('type', 'checkbox');
      reverseLabel.prepend(reverseSelect);

      // 域名替换
      const domainReplaceLabel = document.createElement('label');
      domainReplaceLabel.innerText = ' 域名替换';
      container.appendChild(domainReplaceLabel);

      const domainReplaceInput = document.createElement('input');
      domainReplaceInput.setAttribute('type', 'checkbox');
      domainReplaceLabel.prepend(domainReplaceInput);

      const domainReplaceSelect = document.createElement('select');
      domainReplaceSelect.innerHTML = `
          <option value=''>默认</option>
          <option value='gd-hbimg.huaban.com'>gd-hbimg.huaban.com</option>
          <option value='gd-hbimg.huabanimg.com'>gd-hbimg.huabanimg.com</option>
      `;
      domainReplaceLabel.appendChild(domainReplaceSelect);

      // 图片URL替换
      const urlReplaceLabel = document.createElement('label');
      urlReplaceLabel.innerText = ' 图片URL替换 ';
      container.appendChild(urlReplaceLabel);

      const urlReplaceInput = document.createElement('input');
      urlReplaceInput.setAttribute('type', 'checkbox');
      urlReplaceLabel.prepend(urlReplaceInput);

      const urlReplaceFromInput = document.createElement('input');
      urlReplaceLabel.appendChild(urlReplaceFromInput);
      const urlReplaceToInput = document.createElement('input');
      urlReplaceToInput.style.width = '100%';
      urlReplaceLabel.appendChild(urlReplaceToInput);

      function render() {
        try {
          const reg = new RegExp(input.value);
          mark(reg, {
            reverseSelect: reverseSelect.checked,
            showImageSrc: showSrcInput.checked,
            showImageSize: showFileSizeInput.checked,
            domainReplace: domainReplaceInput.checked
              ? domainReplaceSelect.value
              : '',
            urlReplaceInput: urlReplaceInput.checked,
            urlReplaceFrom: urlReplaceFromInput.value,
            urlReplaceTo: urlReplaceToInput.value,
          });
          matchCount.innerText = `匹配文件数:${allImages.length}`;

          Promise.all(allFileSizePromises).then((allSizes) => {
            const result = allSizes.reduce((total, size) => {
              return total + Number(size);
            }, 0);
            matchCount.innerText += `,总大小:${result.toFixed(2)}KB`;
          });
        } catch (e) {
          // nothing;
        }
      }

      input.addEventListener('input', render);
      container.addEventListener('click', (e) => {
        if (e.target === container) {
          render();
        }
      });
      showSrcInput.addEventListener('change', render);
      showFileSizeInput.addEventListener('change', render);
      reverseSelect.addEventListener('change', render);

      domainReplaceInput.addEventListener('change', render);
      domainReplaceSelect.addEventListener('change', render);

      urlReplaceInput.addEventListener('change', render);

      render();
    },
    's',
  );
})();