添加自定义css和js(广告屏蔽等)

可自定义css选择器屏蔽页面广告,添加js脚本

当前为 2021-05-07 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         添加自定义css和js(广告屏蔽等)
// @description  可自定义css选择器屏蔽页面广告,添加js脚本
// @namespace    _cus_ad_sp
// @version      2.3.7
// @author       vizo
// updateUrl     http://dwz.win/ac4h
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.min.js
// @require      https://cdn.jsdelivr.net/npm/[email protected]/dist/tiny-oss.min.js
// @require      https://cdn.jsdelivr.net/npm/vio-utils/index.min.js
// @include      /https?\:\/\/(?!greasyfork).*/
// @run-at       document-start
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_listValues
// @grant        GM_registerMenuCommand
// @noframes

// ==/UserScript==
'use strict'

;(function() {
  if (vio.isMobile()) return
  let k = GM_getValue(`_cfg_${location.host}`) || {}
  k = typeof k === 'string' ? JSON.parse(k) : k
  if (k.css && !k.disCSS) {
    GM_addStyle(`sty9z1p52{}\n${k.css}\n`)
  }
})();

GM_addStyle(`
  .GM_dn85b {
    display: none !important;
  }
  .GM_vh85b {
    visibility: hidden !important;
  }
  .GM-yisi-Asd,
  .GM-certain-Asd {
    background: #fff !important;
  }
  .GM-yisi-Asd::before,
  .GM-certain-Asd::before {
    content: '疑似广告';
    width: 100%;
    height: 100%;
    font-size: 16px;
    color: #ddd;
    display: flex;
    justify-content: center;
    align-items: center;
    font-weight: normal;
    font-style: normal;
    font-family: Arial sans-serif;
    position: absolute;
    top: 0;
    left: 0;
    z-index: 1;
  }
  .GM-certain-Asd::before {
    content: '已屏蔽广告';
  }
  .GM-yisi-Asd > *,
  .GM-certain-Asd > * {
    visibility: hidden !important;
    opacity: 0 !important;
  }
  .GM-yisi-Asd:hover > *,
  .GM-certain-Asd:hover > * {
    visibility: visible !important;
    opacity: 0.8 !important;
    animation: anim5z 1.4s both;
  }
  @keyframes anim5z {
    0% {
      opacity: 0;
    }
    30% {
      opacity: 0;
    }
    100% {
      opacity: 0.8;
    }
  }
  @keyframes anim8z {
    80% {
      visibility: visible;
    }
    100% {
      opacity: 0;
      visibility: hidden;
    }
  }
  
  .GM-yisi-Asd:hover::before,
  .GM-certain-Asd:hover::before {
    animation: anim8z 1.2s both;
  }
  #wp5sn [hidden] {
    display: none !important;
  }
  #wp5sn, #wp5sn * {
    margin: 0;
    padding: 0;
    box-sizing: border-box !important;
  }
  #wp5sn {
    width: 28vw;
    height: 68vh;
    min-width: 400px;
    padding: 30px;
    background: #fff;
    border-radius: 3px;
    font-family: sans-serif,"HelveticaNeue",Helvetica,"PingFangSC","MicrosoftYaHei","HiraginoSansGB",Arial;
    line-height: 1.5;
    font-size: 12px;
    resize: both;
    box-shadow: 0 0 5px #ccc;
    position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    z-index: 50050;
    margin: auto;
  }
  #wp5sn .mwp_5c {
    height: 100%;
    display: flex;
    flex-direction: column;
  }
  #wp5sn .tit1v {
    color: #555;
    font-size: 18px;
    text-align: center;
    margin-bottom: 15px;
  }
  #wp5sn .c7d-item {
    margin-bottom: 10px;
    display: flex;
    flex-direction: column;
    flex-wrap: wrap;
  }
  #wp5sn .allset-item {
    flex: 1;
  }
  #wp5sn .css-item-xh {
    flex: 3;
    transition: flex .3s;
  }
  #wp5sn .js-item-xh {
    flex: 1;
    transition: flex .3s;
  }
  #wp5sn .mwp_5c.expandJS .css-item-xh {
    flex: 1;
  }
  #wp5sn .mwp_5c.expandJS .js-item-xh {
    flex: 3;
  }
  #wp5sn .stiz {
    font-size: 14px;
    color: #555;
    margin-bottom: 3px;
    position: relative;
    text-align: left;
    display: flex;
  }
  #wp5sn .tamp-cfg-modal {
    width: 250px;
    height: 170px;
    padding: 10px;
    border-radius: 2px;
    background: #f1f1f1;
    position: absolute;
    top: -50px;
    right: 0;
    bottom: 0;
    left: 0;
    margin: auto;
    z-index: 2;
  }
  #wp5sn .tamp-cfg-modal .txa-cfg {
    width: 100%;
    height: 100%;
    resize: none;
    color: #777 !important;
    font-family: Consolas;
    font-size: 12px;
    padding: 6px;
    overflow-y: auto;
    border: 1px solid #e6e6e6;
    background: #fafafa;
  }
  #wp5sn .tamp-cfg-modal .txa-cfg::-webkit-input-placeholder {
    color: #ccc !important;
  }
  #wp5sn .stiz .s0l {
    flex: 1;
  }
  #wp5sn .stiz .s0r {
    color: #09e;
    cursor: pointer;
    user-select: none;
    margin-left: 10px;
  }
  #wp5sn .stiz .s0r.on {
    color: #9a9a9a;
  }
  #wp5sn .oss-zbtn {
    color: #c7c7c7;
    cursor: pointer;
    user-select: none;
    margin-right: 10px;
  }
  #wp5sn .view-all-set {
    color: #09e;
    cursor: pointer;
    user-select: none;
  }
  #wp5sn .st1k {
    display: flex;
  }
  #wp5sn .st1k .s1p {
    flex: 1;
  }
  #wp5sn .st1k .s2p {
    width: 65px;
    color: #09e;
    cursor: pointer;
    text-align: right;
    white-space: nowrap;
    overflow: hidden;
  }
  #wp5sn .inpy {
    flex: 0 0 auto;
    height: 32px;
    border: 1px solid #ddd;
    color: #555;
    background: #fff;
    border-radius: 2px;
    padding: 0 10px;
    outline: none;
  }
  #wp5sn .inpy:focus {
    border: 1px solid #c1c1c1;
  }
  #wp5sn .txtr1z {
    width: 100%;
    flex: 1;
    color: #555;
    padding: 6px;
    line-height: 1.4;
    overflow-x: hidden;
    overflow-y: auto;
    border-radius: 2px;
    border: 1px solid #ddd;
    background: #fff;
    font-size: 12px;
    resize: none;
    white-space: pre-line;
    outline: none;
    font-family: Consolas,sans-serif,"Helvetica Neue",Helvetica,"PingFang SC","Microsoft YaHei";
  }
  #wp5sn .txtr1z::-webkit-input-placeholder {
    color: #c5c5c5;
  }
  #wp5sn .txtr1z:focus {
    border: 1px solid #39e;
  }
  #wp5sn .txtr1z.disabled {
    color: #999;
    background: #f5f5f5;
  }
  #wp5sn .txtr1z::-webkit-scrollbar {
    width: 4px;
  }
  #wp5sn .txtr1z::-webkit-scrollbar-corner,
  #wp5sn .txtr1z::-webkit-scrollbar-track {
    background-color: #fff;
  }
  #wp5sn .txtr1z::-webkit-scrollbar-thumb {
    background: #fff;
  }
  #wp5sn .txtr1z:hover::-webkit-scrollbar-thumb {
    background: #e1e1e1;
  }
  #wp5sn .txtr1z:hover::-webkit-scrollbar-corner,
  #wp5sn .txtr1z:hover::-webkit-scrollbar-track {
    background-color: #f7f7f7;
  }
  #wp5sn .btn-w {
    margin-top: 5px;
    display: flex;
    flex-direction: row-reverse;
  }
  #wp5sn .btn-w .c5kbtn {
    width: 90px;
    height: 32px;
    border-radius: 2px;
    margin-right: 10px;
    font-family: sans-serif,"Helvetica Neue",Helvetica,"PingFang SC","Microsoft YaHei" !important;
    cursor: pointer;
    outline: none;
    border: 0;
  }
  #wp5sn .btn-w .c5kbtn.b1 {
    color: #fff !important;
    background: #09e !important;
  }
  #wp5sn .btn-w .c5kbtn.b2 {
    color: #555 !important;
    background: #f1f1f1 !important;
  }
  #wp5sn .btn-w .c5kbtn:first-child {
    margin-right: 0;
  }
  #wp5sn .btn-w .c5kbtn:focus {
    border: 0;
  }
  #wp5sn .btn-w .c5kbtn:hover {
    opacity: 0.9;
  }
`)

const G = {
  html: `
    <div id="wp5sn" v-show="dialog1s">
      <div class="mwp_5c" :class="{'expandJS': isExpandJS}">
        <div class="tit1v">设置</div>
        <div class="tamp-cfg-modal" v-show="isShowTampModal">
          <textarea class="txa-cfg" @change="changeTampCfg" v-model="tampCfgVal" placeholder="oss配置"></textarea>
        </div>
        <div class="c7d-item">
          <p class="stiz">
            <span class="s0l">打开面板快捷键</span>
            <span class="oss-zbtn" @click="hdlTgOssModal">oss</span>
            <span class="view-all-set" @click="hdlViewAllSet">{{ viewSetText }}</span>
          </p>
          <input type="text" class="inpy" v-model="eKey" placeholder="请输入a-z的任意字母">
        </div>
        <div class="c7d-item css-item-xh" v-show="!showAllSet">
          <p class="stiz">
            <span class="s0l">添加css(不含style标签)</span>
            <span class="s0r" :class="{on: disCSS}" @click="hdlTgDisCss">{{ disCssText }}</span>
          </p>
          <textarea class="txtr1z" :class="{'disabled': disCSS}" v-model="texCssVal" :readonly="disCSS" spellcheck="false" placeholder="请输入css代码" @click="hdlExpandJSJs(1)"></textarea>
        </div>
        <div class="c7d-item js-item-xh" v-show="!showAllSet">
          <p class="stiz">
            <span class="s0l">添加js(不含script标签)</span>
            <span class="s0r" :class="{on: disJS}" @click="hdlTgDisJs">{{ disJsText }}</span>
          </p>
          <textarea class="txtr1z" :class="{'disabled': disJS}" v-model="texJsVal" :readonly="disJS" spellcheck="false" placeholder="请输入js代码" @click="hdlExpandJSJs(2)"></textarea>
        </div>
        <div class="c7d-item allset-item" v-show="showAllSet">
          <p class="stiz st1k">
            <span class="s1p">已添加的网站(可删除) {{ addedNum }} 个 </span>
            <span class="s2p imt-c" @click="hdlImportCfg">导入配置</span>
            <span class="s2p ext-c" @click="hdlExportCfg">导出配置</span>
          </p>
          <input type="file" hidden ref="inp_hide" @change="hdlUpFile">  
          <textarea class="txtr1z" v-model="allAddedStr"></textarea>
        </div>
        <div class="btn-w">
          <button class="c5kbtn b2" @click="hdlCancel">取消</button>
          <button class="c5kbtn b1" @click="hdlSave">保存</button>
        </div>
      </div>
    </div>
  `,
}
const vm = new Vue({
  data() {
    return {
      // 模态框状态
      dialog1s: false,
      // 快捷键名称
      eKey: '',
      showAllSet: false,
      // css代码
      texCssVal: '',
      // js代码
      texJsVal: '',
      // 已添加的网站
      allAddedStr: '',
      disCSS: false,
      disJS: false,
      // 是否展开js
      isExpandJS: false,
      // oss配置modal
      isShowTampModal: false,
      tampCfgVal: '',
    }
  },
  computed: {
    viewSetText() {
      return this.showAllSet ? '查看当前网站' : '查看全部网站'
    },
    addedNum() {
      return this.allAddedStr
        .split('\n')
        .filter(v => !!v)
        .length
    },
    disCssText() {
      return this.disCSS ? '已禁用' : '禁用css'
    },
    disJsText() {
      return this.disJS ? '已禁用' : '禁用js'
    },
  },
  watch: {
    eKey(nVal) {
      this.eKey = /[a-z]/.test(nVal) ? nVal.slice(0, 1) : ''
      GM_setValue('_gus_keyboard', this.eKey)
    },
    dialog1s(nVal) {
      if (!nVal) {
        this.showAllSet = false
        this.isShowTampModal = false
      }
    },
  },
  methods: {
    setGmVal(val) {
      return GM_setValue(`_cfg_${location.host}`, val)
    },
    getGmVal() {
      let gmVal = GM_getValue(`_cfg_${location.host}`) || {}
      return typeof gmVal === 'string' ? JSON.parse(gmVal) : gmVal
    },
    hdlTgOssModal() {
      this.isShowTampModal = !this.isShowTampModal
    },
    hdlExpandJSJs(type) {
      this.isExpandJS = type === 2
    },
    changeTampCfg() {
      GM_setValue('tampOssCfg7n', this.tampCfgVal)
    },
    async hdlSave() {
      if (!this.showAllSet) {
        this.saveCssAndJs()
        this.initAddedWebToTextArea()
      } else {
        this.updateTextAreaValToGm()
        await this.saveJsonToOss('state')
        await this.saveJsonToOss('cfg')
        location.reload()
      }
      this.dialog1s = false
    },
    hdlCancel() {
      this.dialog1s = false
    },
    // 禁用css
    hdlTgDisCss() {
      this.disCSS = !this.disCSS
    },
    // 禁用js
    hdlTgDisJs() {
      this.disJS = !this.disJS
    },
    async saveCssAndJs() {
      let css = `<style class="sty9z1p52">${this.texCssVal}</style>`
      document.head.insertAdjacentHTML('beforeend', css)
      
      Array.from(document.querySelectorAll('.sty9z1p52')).forEach((v, i, y) => {
        if (i !== y.length - 1) {
          v.remove()
        } else {
          v.disabled = this.disCSS
        }
      })
      
      const gm = this.getGmVal()
      this.setGmVal({
        ...gm,
        css: this.texCssVal,
        js: this.texJsVal,
        disCSS: this.disCSS,
        disJS: this.disJS,
      })
      
      // 同步至oss
      if ( !await this.saveJsonToOss('state') ) {
        alert('同步失败, 请导出配置后在其他网站重新上传配置就能同步了')
        return
      }
      await this.saveJsonToOss('cfg')
      
      if (gm.disJS !== this.disJS || gm.js !== this.texJsVal) {
        await vio.timeout(500)
        location.reload()
      }
      
    },
    saveJsonToOss(name) {
      let lsCfg = GM_getValue('tampOssCfg7n')
      if (!lsCfg) return Promise.resolve(true)
      lsCfg = typeof lsCfg === 'string' ? JSON.parse(lsCfg) : lsCfg
      const oss = new TinyOSS(lsCfg.ossParams)
      const lastTime = Date.now()
      const data = name === 'state' ? {lastTime} : this.getAllCfg()
      const blob = new Blob([JSON.stringify(data)], { type: 'text/json' })
      
      GM_setValue('tampCfgUpdateTime', lastTime)
      return new Promise(async (resolve) => {
        try {
          await oss.put(`json/tamp-cfg/${name}.json`, blob, {
            onprogress(e) {
              if (e.total > 0) {
                return resolve(true)
              }
            }
          })
        } catch (err) {
          return resolve(false)
        }
      })
    },
    async updateCfgFromOss() {
      const lsCfg = this.getOssCfg()
      const gmLastTime = GM_getValue('tampCfgUpdateTime')
      if (lsCfg) {
        try {
          const res = (await (await fetch(`${lsCfg.state}&t=${Date.now()}`)).json())
          const { lastTime } = res
          const now = Date.now()
          if (
            !gmLastTime ||
            lastTime > gmLastTime ||
            now - gmLastTime > 30e3
          ) {
            const rCfg = await ((await fetch(`${lsCfg.cfg}&t=${Date.now()}`)).json())
            this.updateJsonToGm(rCfg)
            this.initAddedWebToTextArea()
            GM_setValue('tampCfgUpdateTime', now)
          }
        } catch (err) {}
      }
    },
    
    // 判断是否从远程更新
    async judgeIsUpdateCfgFromOss() {
      const gmTime = GM_getValue('pageRefreshTime')
      const now = Date.now()
      if (!gmTime || now - gmTime > 5000) {
        GM_setValue('pageRefreshTime', now)
        await this.updateCfgFromOss()
      }
    },
    
    updateTextAreaValToGm() {
      const gmArr = GM_listValues()
        .filter(v => v.startsWith('_cfg_'))
        .map(v => v.replace(/^_cfg_/, ''))
      const cArr = this.allAddedStr.split('\n')
      gmArr.forEach(v => {
        if (!cArr.some(c => v === c)) {
          GM_deleteValue(`_cfg_${v}`)
        }
      })
    },
    
    updateJsonToGm(obj) {
      if (!obj) return
      GM_listValues()
        .filter(v => /^_gus_|^_cfg_/.test(v))
        .forEach(v => {
          GM_deleteValue(v)
        })
      
      for (let k in obj) {
        GM_setValue(k, obj[k])
      }
    },
    
    tgCfgDialog() {
      this.dialog1s = !this.dialog1s
    },
    hdlViewAllSet() {
      this.showAllSet = !this.showAllSet
    },
    // 导入配置
    hdlImportCfg() {
      this.$refs.inp_hide.click()
    },
    // 导出配置
    hdlExportCfg() {
      const res = this.getAllCfg()
      vio.downloadText(JSON.stringify(res, null, 2), '1.json')
    },
    
    // 获取oss配置
    getOssCfg() {
      let lsCfg = GM_getValue('tampOssCfg7n')
      if (!lsCfg) return
      try {
        return typeof lsCfg === 'string' ? JSON.parse(lsCfg) : lsCfg
      } catch (err) {}
    },
    
    initOssVal() {
      const lsCfg = this.getOssCfg()
      if (lsCfg) {
        this.tampCfgVal = JSON.stringify(lsCfg)
      }
    },
    
    // 获取所有已配置的网站数据
    getAllCfg() {
      return GM_listValues()
        .filter(v => /^_gus_|^_cfg_/.test(v))
        .reduce((acc, v) => {
          let gmVal = GM_getValue(v)
          gmVal = typeof gmVal === 'string' && gmVal > 1 ? JSON.parse(gmVal) : gmVal
          return {
            ...acc,
            [v]: gmVal
          }
        }, {})
    },
    
    hdlUpFile(e) {
      let file = e.target.files[0]
      if (file) {
        let reader = new FileReader()
        reader.readAsText(file, 'utf-8')
        reader.onload = async (evt) => {
          try {
            oUp = JSON.parse(evt.target.result)
            this.updateJsonToGm(oUp)
            
            if (!await this.saveJsonToOss('state')) {
              alert('上传失败, 请选择其他网站重新上传')
              return
            }
            await this.saveJsonToOss('cfg')
            
            this.initCssJsVal()
            this.initAddedWebToTextArea()
            this.initAddedScript()
            this.initGMStyleClass()
            
            setTimeout(() => {
              location.reload()
            }, 200)
          } catch (e) {
            // 上传失败
          }
        }
      }
    },
    async resetCss() {
      this.texCssVal = ''
      this.disCSS = false
      this.setGmVal({
        ...this.getGmVal(),
        css: '',
        disCSS: false,
      })
      await this.saveJsonToOss('state')
      await this.saveJsonToOss('cfg')
      location.reload()
    },
    async resetJs() {
      this.texJsVal = ''
      this.disJS = false
      this.setGmVal({
        ...this.getGmVal(),
        js: '',
        disJS: false,
      })
      await this.saveJsonToOss('state')
      await this.saveJsonToOss('cfg')
      location.reload()
    },
    initEvt() {
      window.addEventListener('keydown', e => {
        if (
          !(/text|search|number|password|tel|url|email/.test(e.target.type)) &&
          e.target.tagName !== 'TEXTAREA' &&
          !e.altKey && 
          !e.ctrlKey &&
          e.key === this.eKey
        ) {
          this.tgCfgDialog()
        }
        if (/esc/i.test(e.key)) {
          this.dialog1s = false
        }
      })
    },
    initEkey() {
      this.eKey = GM_getValue('_gus_keyboard') || ''
    },
    initCssJsVal() {
      this.texCssVal = this.getGmVal().css || ''
      this.texJsVal = this.getGmVal().js || ''
      this.disCSS = !!this.getGmVal().disCSS
      this.disJS = !!this.getGmVal().disJS
    },
    initAddedWebToTextArea() {
      const gmArr = GM_listValues()
        .filter(v => v.startsWith('_cfg_'))
        .map(v => v.replace(/^_cfg_/, ''))
      this.allAddedStr = gmArr.join('\n')
    },
    initAddedScript() {
      let js = this.getGmVal().js
      let isDisabled = this.getGmVal().disJS
      if (js && !isDisabled) {
        let st = document.createElement('script')
        st.type = 'module'
        st.textContent = js
        document.body.appendChild(st)
      }
    },
    initGMStyleClass() {
      document.querySelectorAll('style').forEach(v => {
        if (!/\w{8}(-\w{4}){3}-\w{12}/.test(v.id)) {
          return
        }
        if (v.textContent.includes('sty9z1p52')) {
          v.classList.add('sty9z1p52')
        }
      })
    },
    
  },
  async mounted() {
    this.initEvt()
    this.initOssVal()
    await this.judgeIsUpdateCfgFromOss()
    this.initEkey()
    this.initCssJsVal()
    this.initAddedWebToTextArea()
    this.initAddedScript()
    this.initGMStyleClass()
  },
})

// 屏蔽疑似站外广告(a标签下的图片)
function hideAdSuspected() {
  const fn = hideAdSuspected
  fn.count = fn.count || 0
  fn.count++
  const f_host = location.host.replace(/^([\w-]+\.[\w-]+)$/, 'www.$1')
  const dma1 = (f_host.match(/(?<=[\w-]+\.).+/) || [])[0]
  document.querySelectorAll('a > img').forEach(el => {
    const link = el.parentElement
    const linkUrl = link.href
    const isGif = el.src.endsWith('.gif')
    const dma2 = isUrl(linkUrl) ? new URL(linkUrl).host.replace(/^([\w-]+\.[\w-]+)$/, 'www.$1').match(/(?<=[\w-]+\.).+/)[0] : ''
    const iw = el.offsetWidth
    const ih = el.offsetHeight
    
    if (link.target !== '_blank') return
    if (
        ( (dma2.includes(dma1) && !isGif) || 
        (!isGif && !/^https?|^\/\//i.test(link.getAttribute('href'))) )
      ) {
        return
    }
    if (getSiblings(el).length > 1) return
    if (iw < 150 && ih < 50 || iw < 50 && ih < 150) return
    if (el.matches('.diyd')) return
    
    el.classList.add('diyd')
    if (getComputedStyle(link).display === 'inline') {
      link.style.display = 'inline-block'
    }
    
    const arr = getParentElArr([], el).reverse()
    for (let [i, v] of arr.entries()) {
      if (isLikeImgArea(v, iw, ih)) {
        v.classList.add('GM-yisi-Asd')
        if (getComputedStyle(v).position === 'static') {
          v.style.position = 'relative'
        }
        break
      }
    }
  })
  
  if (fn.count < 50) {
    setTimeout(fn, 100)
  }
}
function isUrl(str) {
  return /^(((ht|f)tps?):\/\/)?[\w-]+(\.[\w-]+)+([\w.,@?^=%&:/~+#-]*[\w@?^=%&/~+#-])?$/.test(str)
}
function isLikeImgArea(el, iw, ih) {
  const w = getComputedStyle(el).width.replace('px', '') - 0
  const h = getComputedStyle(el).height.replace('px', '') - 0
  return 20 >= Math.abs(iw - w) && 20 >= Math.abs(ih - h)
}
 
function getParentElArr(arr, el) {
  if (/body|html/i.test(el.tagName)) {
    return arr
  }
  arr.push(el)
  return getParentElArr(arr, el.parentElement)
}
 
function getSiblings(el) {
  return Array.from(el.parentNode.childNodes)
  .filter(node => node !== el && node.nodeType === 1)
}
// 屏蔽包含广告文字的box
function hideContainAdText() {
  document.querySelectorAll('div[class*="ad" i],div[id*="ad" i],a[class*="ad" i],a[id*="ad" i]').forEach(v => {
    if (![...v.classList].some(c => /\bads?\b/i.test(c))) return
    if (!v.textContent.includes('广告')) return
    v.classList.add('GM-certain-Asd', 'Asd-text')
  })
}
// 屏蔽iframe疑似广告
function hideIframeYiSiAd() {
  const fn = hideIframeYiSiAd
  fn.count = fn.count || 0
  fn.count++
  document.querySelectorAll('iframe').forEach(v => {
    if (!/^https?|^\/\/|^javascript/i.test(v.src)) return
    if (
      v.offsetWidth > 320 &&
      v.offsetHeight > 320 ||
      v.offsetWidth < 40 &&
      v.offsetHeight < 40
    ) {
      return
    }
    const pEl = v.parentElement
    if (!/body|html/i.test(pEl.tagName)) {
      pEl.classList.add('GM-certain-Asd')
    }
    if (getComputedStyle(pEl).position === 'static') {
      pEl.style.position = 'relative'
      pEl.style.backgroundColor = '#fff'
    }
  })
  if (fn.count < 40) {
    setTimeout(fn, 200)
  }
}

const initFunc = () => {
  if (!vio.isMobile()) {
    document.addEventListener('DOMContentLoaded', () => {
      document.body.insertAdjacentHTML('beforeend', G.html)
      vm.$mount('#wp5sn')
      GM_registerMenuCommand('打开设置面板', vm.tgCfgDialog)
      GM_registerMenuCommand('清空当前网站添加的CSS', vm.resetCss)
      GM_registerMenuCommand('清空当前网站添加的JS', vm.resetJs)
      if (!location.host.includes('localhost')) {
        // 屏蔽疑似站外广告
        hideAdSuspected()
        // 屏蔽包含广告文字的box
        hideContainAdText()
        // 屏蔽iframe广告
        hideIframeYiSiAd()
      }
    }, false)
  }
}

initFunc()