Tokenize, merge, and subtract space-separated booru tag queries
Dieses Skript sollte nicht direkt installiert werden. Es handelt sich hier um eine Bibliothek für andere Skripte, welche über folgenden Befehl in den Metadaten eines Skriptes eingebunden wird // @require https://update.greasyfork.org/scripts/583965/1857992/Brazen%20Framework%20-%20Tag%20Query%20Engine.js
// ==UserScript==
// @name Brazen Framework - Tag Query Engine
// @namespace brazenvoid
// @version 1.0.0
// @author brazenvoid
// @license GPL-3.0-only
// @description Tokenize, merge, and subtract space-separated booru tag queries
// @run-at document-end
// ==/UserScript==
/**
* Core tag-query engine: parenthesis-aware tokenize, idempotent merge, subtract for display.
* Site-specific URL/param handling lives in adapters (e.g. GelbooruFamilySearchAdapter).
*/
class BrazenTagQueryEngine
{
/**
* @param {{singleInstanceKeys?: Set<string>, metatagKey?: function(string): (string|null)}} [options]
*/
constructor(options = {})
{
this._singleInstanceKeys = options.singleInstanceKeys ?? new Set()
this._metatagKey = options.metatagKey ?? BrazenTagQueryEngine.defaultMetatagKey
}
/**
* @param {string} token
* @return {string|null}
*/
static defaultMetatagKey(token)
{
let subject = token.startsWith('-') ? token.slice(1) : token
if (subject.startsWith('(')) {
return null
}
let colon = subject.indexOf(':')
return colon > 0 ? subject.slice(0, colon) : null
}
/**
* @param {string} query
* @return {string[]}
*/
tokenize(query)
{
let tokens = []
if (!query) {
return tokens
}
let index = 0
let length = query.length
while (index < length) {
while (index < length && /\s/.test(query[index])) {
index++
}
if (index >= length) {
break
}
let start = index
if (query[index] === '(') {
let depth = 0
while (index < length) {
if (query[index] === '(') {
depth++
} else if (query[index] === ')') {
depth--
if (depth === 0) {
index++
break
}
}
index++
}
} else {
while (index < length && !/\s/.test(query[index])) {
index++
}
}
tokens.push(query.slice(start, index))
}
return tokens
}
/**
* @param {string} token
* @return {string|null}
*/
metatagKey(token)
{
return this._metatagKey(token)
}
/**
* @param {string} userQuery
* @param {string[]} defaultTokens
* @return {string[]}
*/
merge(userQuery, defaultTokens)
{
let userTokens = this.tokenize(userQuery)
let userKeys = new Set()
let present = new Set(userTokens)
for (let token of userTokens) {
let key = this.metatagKey(token)
if (key) {
userKeys.add(key)
}
}
let merged = [...userTokens]
for (let token of defaultTokens) {
let key = this.metatagKey(token)
if (key && this._singleInstanceKeys.has(key) && userKeys.has(key)) {
continue
}
if (present.has(token)) {
continue
}
merged.push(token)
present.add(token)
}
return merged
}
/**
* @param {string[]} tokens
* @param {string[]} defaultTokens
* @return {string[]}
*/
subtract(tokens, defaultTokens)
{
let defaults = new Set(defaultTokens)
return tokens.filter((token) => !defaults.has(token))
}
}