原價屋快速搜尋套件-可以快速搜尋商品和儲存歷史搜尋紀錄
// ==UserScript==
// @name Coolpc Search
// @namespace https://github.com/lovebuizel
// @version v1.1.3
// @description 原價屋快速搜尋套件-可以快速搜尋商品和儲存歷史搜尋紀錄
// @author lovebuizel
// @match *://www.coolpc.com.tw/evaluate.php
// @grant none
// ==/UserScript==
(function() {
'use strict';
class Coolpc {
constructor() {
this.keyword = ""
this.keywords = []
this.selecteds = []
this.records = JSON.parse(localStorage.getItem('coolpc')) || [];
this.input = null
this.details = ''
this.isOpenDetails = JSON.parse(localStorage.getItem('coolpc_isOpenDetails')) || false
this.isAutoAddResults = JSON.parse(localStorage.getItem('coolpc_isAutoAddResults')) === null ? true : JSON.parse(localStorage.getItem('coolpc_isAutoAddResults'))
this.searchResultOptions = []
this.minPrice = null
this.maxPrice = null
}
init() {
this.clearAd()
this.addUI()
this.removeOriginalEventListener()
}
addUI() {
const fDiv_table = document.querySelector('#fDiv table')
const div = document.createElement("div")
div.setAttribute('style', `
width:100%;
background:#FFCA62;
`)
div.innerHTML = `
<div class="details" style="max-height:40vh;overflow:auto;height:${this.isOpenDetails ? '100%' : '0'};">
<ul style="list-style:none;padding-left:0px;overflow:hidden;margin:0;"></ul>
</div>
<div style="display:flex;align-items:center;justify-content:flex-start;margin-top:5px;">
<span class="openDetails" style="margin-left:20px;margin-right:20px;display:flex;align-items:center;cursor:pointer;color:blue;user-select:none;">
<span style="font-size:15pt;" class="signOpenDetails">${this.isOpenDetails ? '−' : '+'}</span>
<span style="margin-left:5px;">全部搜尋結果</span>
</span>
<div class="autoAddResults" style="display:flex;align-items:center;cursor:pointer;user-select:none;margin-right:20px;">
<div class="autoAddResults_div" style="
width:15px;
height:15px;
background:white;
border:1px solid black;
display:flex;
justify-content:center;
align-items:center;
"></div>
<span style="margin-left:5px;">搜尋後自動填入</span>
</div>
<input type="text" spellcheck="false" class="minPrice" style="width:70px;height:20px;padding-left:3px;flex-shrink:0;user-select:none;">
<span style="margin-left:5px;margin-right:5px;">~</span>
<input type="text" spellcheck="false" class="maxPrice" style="width:70px;height:20px;padding-left:3px;flex-shrink:0;user-select:none;">
<span style="margin-left:5px;">價格區間內</span>
</div>
`
fDiv_table.appendChild(div)
const divDetails = document.querySelector(".details")
divDetails.addEventListener('click', this.clickDetails.bind(this))
const openDetails = document.querySelector('.openDetails')
const signOpenDetails = document.querySelector('.signOpenDetails')
openDetails.addEventListener('click', () => {
this.isOpenDetails = !this.isOpenDetails
signOpenDetails.innerHTML = `${this.isOpenDetails ? '−' : '+'}`
divDetails.style.height = `${this.isOpenDetails ? '100%' : 0}`
localStorage.setItem('coolpc_isOpenDetails', this.isOpenDetails)
})
// 價格篩選
this.minPrice = document.querySelector('.minPrice')
this.maxPrice = document.querySelector('.maxPrice')
this.minPrice.addEventListener('input', this.checkPrice.bind(this))
this.maxPrice.addEventListener('input', this.checkPrice.bind(this))
const divSearch = document.createElement("div")
divSearch.setAttribute('style', `
width:100%;
height:40px;
background:#FFCA62;
display:flex;
align-items:center;
justify-content:flex-start;
`)
divSearch.innerHTML = `
<img src="https://i.imgur.com/aXfbfwd.png" style="width:15px;height:15px;margin-right:5px;margin-left:20px;user-select:none;"></img>
<input class="input" type="text" placeholder="e.g., R5 3600" spellcheck="false" style="width:250px;height:25px;padding-left:5px;flex-shrink:0;user-select:none;">
<button class="btn" style="
height:25px;
color:black;
user-select:none;
border-width:2px;
border-style:outset;
border-color:buttonface;
border-image:initial;
border-radius:0;
background-color:buttonface;
cursor:pointer;
">GO</button>
<ul class="records" style="display:flex;list-style:none;padding-left:20px;overflow:hidden;align-items:center;"></ul>`
fDiv_table.appendChild(divSearch)
this.input = document.querySelector(".input")
this.input.focus()
const btn = document.querySelector(".btn")
const records = document.querySelector(".records")
btn.addEventListener('click', () => {
this.keyword = this.input.value
this.search()
})
this.input.addEventListener('keydown', (e) => {
if (e.keyCode === 13 || e.KeyCode === 13) {
this.keyword = this.input.value
this.search()
}
})
records.addEventListener('click', this.clickRecords.bind(this))
const autoAddResults_div = document.querySelector('.autoAddResults_div')
autoAddResults_div.innerHTML = this.isAutoAddResults ? '✓' : ''
const autoAddResults = document.querySelector('.autoAddResults')
autoAddResults.addEventListener('click', this.clickAutoAddResults_div.bind(this))
this.updateRecords()
}
search() {
// 重置
this.selecteds.forEach(option => {
this.clearSelected(option)
})
this.selecteds = []
this.details = ''
this.searchResultOptions = []
if (this.keyword.trim() === '') return
this.keywords = []
this.keyword.toLowerCase().split(' ').forEach(keyword => {
if (keyword.trim() !== '') {
this.keywords.push(keyword.trim())
}
})
const totalSelects = document.querySelectorAll('td:nth-child(3) > select')
const ulDetails = document.querySelector('.details ul')
// 重置optionNumber
let optionNumber = 0
totalSelects.forEach(select => {
const totalOptions = select.querySelectorAll('option')
for (let i = 0; i < totalOptions.length; i++) {
if(this.isMatch(totalOptions[i])){
if (this.isAutoAddResults) {
this.addSelected(totalOptions[i])
this.selecteds.push(totalOptions[i])
}
this.details += `<li style="padding-left:20px;background:#fffd92;margin-top:20px;border:none;">${select.parentNode.previousSibling.textContent}</li>`
break
}
}
for (let i = 0; i < totalOptions.length; i++) {
if(this.isMatch(totalOptions[i])){
this.details += `<li style="padding-left:20px;cursor:pointer;border:none;" data-optionnumber="${optionNumber}">${this.addKeywordsStyle(totalOptions[i].text)}</li>`
this.searchResultOptions.push(totalOptions[i])
optionNumber++
}
}
})
ulDetails.innerHTML = this.details
this.updateDetailStyle()
this.checkPrice()
this.addRecords()
this.updateRecords()
}
isMatch(option) {
return this.keywords.every(keyword => option.text.toLowerCase().includes(keyword.toLowerCase()))
}
addKeywordsStyle(text) {
let reg = ''
this.keywords.forEach((keyword, i) => {
i === 0 ? reg = reg + keyword : reg = reg + `|${keyword}`
})
text = text.replace(new RegExp(reg, 'gi'), str => {
return `<span style="color:red;">${str}</span>`
})
return text
}
addSelected(option) {
option.selected = true
option.parentNode.parentNode.dispatchEvent(new Event('change'))
}
clearSelected(option) {
option.selected = false
option.parentNode.parentNode.dispatchEvent(new Event('change'))
}
updateRecords() {
let str = `<span style="flex-shrink:0;">歷史紀錄:</span>`
for (let i = 0; i < this.records.length; i++) {
str += `<li class="record" style="margin-right:5px;cursor:pointer;color:blue;text-decoration:underline;flex-shrink:0;border:none;background:transparent;">${this.records[i]}</li><span class="deleteRecord" style="margin-right:20px;flex-shrink:0;font-size:15pt;cursor:pointer;" data-recordnumber="${i}">×</span>`
}
const records = document.querySelector('.records')
records.innerHTML = str
this.input.value = this.keyword
}
addRecords() {
const isNotRepeat = this.records.every( record => record.toLowerCase() !== this.keyword.toLowerCase() )
if (!isNotRepeat) return
this.records.unshift(this.keyword)
if (this.records.length > 10) {
this.records.splice(10)
}
localStorage.setItem('coolpc', JSON.stringify(this.records))
}
clickRecords(e) {
if (e.target.nodeName == 'LI' && e.target.className == 'record') {
this.keyword = e.target.textContent
this.search()
}
if (e.target.nodeName === 'SPAN' && e.target.className === 'deleteRecord') {
this.records.splice(e.target.dataset.recordnumber,1)
localStorage.setItem('coolpc', JSON.stringify(this.records))
this.updateRecords()
}
}
clearAd() {
const body = document.querySelector('body')
if (body !== null) {
body.style.overflow = 'auto'
}
const Psu = document.querySelector('#Psu')
if (Psu !== null) {
Psu.style.display = 'none'
}
const doc = document.querySelector('#doc tbody')
if (doc !== null) {
doc.style.display = 'none'
}
}
removeOriginalEventListener() {
const script = document.createElement('script')
script.innerHTML = `
document.querySelector('body').onkeyup = null;
document.querySelector('body').onselectstart = null;
document.querySelector('body').oncontextmenu = null;
document.querySelector('#fDiv').ondblclick = null;
`
document.querySelector('body').append(script)
}
clickDetails(e) {
if (e.target.nodeName === 'LI' && e.target.dataset.optionnumber) {
if(!this.searchResultOptions[e.target.dataset.optionnumber].selected){
this.addSelected(this.searchResultOptions[e.target.dataset.optionnumber])
} else {
this.clearSelected(this.searchResultOptions[e.target.dataset.optionnumber])
}
this.updateDetailStyle()
}
}
updateDetailStyle() {
const lists = document.querySelectorAll('.details li')
lists.forEach(li => {
if(li.dataset.optionnumber){
if(this.searchResultOptions[li.dataset.optionnumber].selected === true) {
li.style.background = '#c99932'
} else {
li.style.background = 'transparent'
}
}
})
}
clickAutoAddResults_div() {
this.isAutoAddResults = !this.isAutoAddResults
localStorage.setItem('coolpc_isAutoAddResults', this.isAutoAddResults)
const autoAddResults_div = document.querySelector('.autoAddResults_div')
autoAddResults_div.innerHTML = this.isAutoAddResults ? '✓' : ''
}
checkPrice() {
const details = document.querySelectorAll(".details ul li")
if(this.minPrice.value.trim() === '' || this.maxPrice.value.trim() === '' || isNaN(Number(this.minPrice.value)) || isNaN(Number(this.maxPrice.value))) {
details.forEach(li => {
li.style.display = 'list-item';
})
return
}
details.forEach(li => {
const match = li.textContent.match(/\$[0-9]+/g)
if(match){
const price = match[match.length-1].slice(1)
if(Number(this.minPrice.value) <= Number(price) && Number(price) <= Number(this.maxPrice.value)) {
li.style.display = 'list-item';
} else {
li.style.display = 'none';
}
}
})
}
}
const coolpc = new Coolpc()
window.onload = coolpc.init()
})();