您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Idle Infinity
// ==UserScript== // @name Idle Infinity - Filter // @namespace http://dazzyd.org/ // @version 0.4.7 // @description Idle Infinity // @author Dazzy Ding // @license MIT // @grant GM_addStyle // @match https://www.idleinfinity.cn/Config/Query?* // @icon https://www.google.com/s2/favicons?sz=64&domain=idleinfinity.cn // ==/UserScript== const store = { load() { const saved = JSON.parse(localStorage.getItem(`dd_ui_filter`) || "{}") for (const [key, val] of Object.entries(saved)) { this[key] = val } }, save() { localStorage.setItem(`dd_ui_filter`, JSON.stringify(this)) }, } const validOptions = {} class Condition { constructor(name, value) { this.name = name this.value = value } static fromString(string) { const matches = string.match(/^【(.+?)】\s*(>=|=|包含)\s*【?(\d+)】?$/) if (matches == null) { return null } const cond = new Condition(matches[1], matches[3]) if (validOptions.prefix.includes(cond.name) || validOptions.skill.includes(cond.name)) { return cond } return null } toString() { if (this.name === "序号") { return `【${this.name}】 = ${this.value}` } if (this.name === "名称") { return `【${this.name}】 包含 【${this.value}】` } return `【${this.name}】 >= ${this.value}` } compare(other) { // 【技能】排在前面,其他按选项顺序 const indexOf = name => validOptions.prefixAll.indexOf(name) - (name.includes("技能") ? validOptions.prefixAll.length : 0) if (this.name !== other.name) { return indexOf(this.name) - indexOf(other.name) } else { return this.value - other.value } } isSame(other) { return this.compare(other) === 0 } isSkill() { return validOptions.skill.includes(this.name) } } class Rule { constructor(power, type, conditions, dom) { this.power = power this.type = type this.conditions = conditions == null ? [] : conditions.sort((a, b) => a.compare(b)) this.dom = dom } static fromString(string) { const parts = string.split('|') const conds = parts.slice(2).map(s => Condition.fromString(s)) if (conds.indexOf(null) !== -1) { return null } const rule = new Rule(parts[0], parts[1], conds) if (validOptions.power.includes(rule.power) && validOptions.type.includes(rule.type)) { return rule } return null } toString() { const parts = [] parts.push(this.power) parts.push(this.type) for (const cond of this.conditions) { parts.push(cond.toString()) } return parts.join('|') } compare(other) { if (this.power !== other.power) { return validOptions.power.indexOf(this.power) - validOptions.power.indexOf(other.power) } if (this.type !== other.type) { return validOptions.type.indexOf(this.type) - validOptions.type.indexOf(other.type) } if (this.conditions.length !== other.conditions.length) { return this.conditions.length - other.conditions.length } for (const [i, cond] of this.conditions.entries()) { const ret = cond.compare(other.conditions[i]) if (ret !== 0) return ret } return 0 } isSame(other) { return this.compare(other) === 0 } } function createElementByHTML(html) { const template = document.createElement('template') template.innerHTML = html.trim() return template.content.firstChild } function addRule(rule) { function select_value(dom, value) { for (const [index, option] of Object.entries(dom.options)) { if (option.text === value) { dom.selectedIndex = index return } } console.error(`无法在DOM${dom}中找到选项${value}`) } const modal = document.querySelector("#modalConfig") setTimeout(step1, 0) function step1() { const modalOpen = document.querySelector("a[data-target='#modalConfig']") modalOpen.click() select_value(modal.querySelector("select#power"), rule.power) select_value(modal.querySelector("select#type"), rule.type) const condDivList = modal.querySelectorAll("div.condition") const condAdd = modal.querySelector("button.config-magic-add") for (let i = condDivList.length; i < rule.conditions.length; i++) { condAdd.click() } setTimeout(step2, 250) } function step2() { const condDivList = modal.querySelectorAll("div.condition") for (const [index, cond] of rule.conditions.entries()) { const div = condDivList[index] if (cond.isSkill()) { select_value(div.querySelector("select.prefix"), "+ 职业指定技能") select_value(div.querySelector("select.sk"), cond.name) } else { select_value(div.querySelector("select.prefix"), cond.name) } div.querySelector("input.min").value = cond.value } setTimeout(step3, 250) } function step3() { modal.querySelector("button.config-apply").click() } } function loadCurrentRules() { return Array.from(document.querySelectorAll('tbody tr')) .map(row => { const tds = row.querySelectorAll('td') return new Rule( tds[1].innerText, tds[2].innerText, Array.from(tds[3].querySelectorAll('div.col-sm-6')).map( div => Condition.fromString(div.innerText)), row, ) }) .sort((a, b) => a.compare(b)) } function parseRulesByText(text) { const rules = [] const errors = [] const lines = text.split('\n').map(s => s.trim()).filter(s => s.length > 0) for (const [index, line] of lines.entries()) { const rule = Rule.fromString(line) if (rule != null) { rules.push(rule) } else { errors.push([index, line]) } } return [rules, errors] } function updateTable() { if (store.rules == null) { return } const currentRules = loadCurrentRules() const [requireRules, _] = parseRulesByText(store.rules) let isExactMatch = true // 高亮不在配置中的多余规则 for (const rule of currentRules) { if (requireRules.find(other => rule.isSame(other)) != null) { continue } console.log(`多余规则:${rule}`) isExactMatch = false rule.dom.style.backgroundColor = "#900" } // 缺少规则 let firstCurrent = currentRules[0].dom for (const rule of requireRules) { if (currentRules.find(other => rule.isSame(other)) != null) { continue } console.log(`缺少规则:${rule}`) isExactMatch = false const row = createElementByHTML(` <tr style="background-color: #009"> <td class="text-center">无效</td> <td class="text-center">${rule.power}</td> <td class="text-center">${rule.type}</td> <td> <div class="container-fluid"> ${rule.conditions.map(cond => `<div class="col-sm-6 col-md-6"><span>${cond.toString()}</span></div>`).join('')} </div> </td> <td> <span class="label label-primary rule-add">添加</span> </td> </tr> `) firstCurrent.before(row) row.getElementsByClassName('rule-add')[0] .addEventListener("click", () => { addRule(rule) }) } // 如果完全匹配,则清空保存的规则 if (isExactMatch) { store.rules = null store.save() } } function updateUI() { document.querySelector(".panel-heading > .pull-right").prepend(createElementByHTML(` <a class="btn btn-xs btn-danger" id="helper-open" role="button" data-toggle="modal" data-target="#modalHelper">助手</a> `)) document.getElementById("modalImport").after(createElementByHTML(` <div class="modal fade" id="modalHelper" tabindex="-1" role="dialog"> <div class="modal-dialog modal-md" role="document"> <div class="modal-content model-inverse"> <div class="modal-header"> <span class="modal-title">批量管理助手</span> </div> <div class="modal-body"> <div class="form-group"> <label class="control-label">编辑规则文本并提交</label> <textarea class="form-control" rows="20" id="helper-rules"></textarea> </div> <div class="form-group error" id="helper-error"></div> <div class="form-group"> <label>使用说明</label> <p> 蓝底为需要手动增加,红底为需要手动删除。<br> 清空规则文本可以停用助手。<br> </p> </div> </div> <div class="modal-footer"> <button type="button" class="btn btn-primary btn-xs" id="helper-submit">提交</button> <button type="button" class="btn btn-default btn-xs" data-dismiss="modal">关闭</button> </div> </div> </div> </div> `)) document.getElementById("helper-open") .addEventListener("click", () => { const output = store.rules != null ? store.rules : loadCurrentRules().map(rule => rule.toString()).join('\n') document.getElementById("helper-rules").value = output }) document.getElementById("helper-submit") .addEventListener("click", () => { const input = document.getElementById("helper-rules").value const [_, errors] = parseRulesByText(input) if (errors.length > 0) { document.getElementById("helper-error").innerHTML = errors.map(([index, line]) => `第${index + 1}行:${line}`).join('<br>') } else { store.rules = input.trim() === "" ? null : input store.save() location.reload() } }) } setTimeout(() => { store.load() for (const [key, selector] of Object.entries({ power: "#modalConfig #power option", type: "#modalConfig #type option", prefix: ".condition-template .prefix option", skill: ".condition-template .sk option", })) { validOptions[key] = Array.from(document.querySelectorAll(selector)).map( e => e.innerText.trim()) } validOptions.prefixAll = [].concat(validOptions.prefix, validOptions.skill) updateTable() updateUI() }, 0)