// ==UserScript== // @name Google Images Tools Enhanced // @name:en Google Images Tools Enhanced // @name:zh-TW Google 圖片工具強化版 // @name:ja Google 画像検索ツール拡張 // @namespace https://greasyfork.org/en/users/1467948-stonedkhajiit // @version 1.1.0 // @description Enhances Google Images with a custom toolbar and expanded filter options: Exact Size, Aspect Ratio, File Type, Region/Site Search & more. // @description:en Enhances Google Images with a custom toolbar and expanded filter options: Exact Size, Aspect Ratio, File Type, Region/Site Search & more. // @description:zh-TW 以自訂工具列強化 Google 圖片搜尋,提供精確尺寸、長寬比、檔案類型、地區/站內搜尋等更多篩選及自訂選項。 // @description:ja カスタムツールバーで Google 画像検索を強化。正確なサイズ、アスペクト比、ファイル形式、地域/サイト内検索など、拡張されたフィルターとカスタマイズオプションを提供。 // @icon https://www.google.com/s2/favicons?sz=64&domain=google.com // @author StonedKhajiit // @license MIT // @include http*://*.google.tld/search*tbm=isch* // @include http*://*.google.tld/search*udm=2&* // @include http*://*.google.tld/search*udm=2 // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_getValue // @grant GM_info // @downloadURL none // ==/UserScript== (function () { 'use strict'; // --- Configuration --- const DEBUG_MODE = false; const GITE_SETTINGS_GM_KEY = 'GITE_USER_SETTINGS_V1'; // --- Selectors & Constants --- const GITE_TOOLBAR_CONTAINER_ID = 'gite-toolbar-container'; const BEFORE_APPBAR_SELECTOR = '#before-appbar'; const NATIVE_TOOLBAR_SELECTOR_TO_HIDE = '[data-st-u="top_nav"]'; const GITE_SETTINGS_BUTTON_ID = 'gite-settings-toolbar-button'; const GITE_RESULT_STATS_DISPLAY_ID = 'gite-result-stats-display'; const GITE_LANG_KEY_ATTR = 'data-gite-lang-key'; // --- Core Helper: el (DOM Builder) --- function el(tag, attrs = {}, children = []) { const element = document.createElement(tag); for (const [key, val] of Object.entries(attrs)) { if (key === 'className') element.className = val; else if (key === 'style' && typeof val === 'object') Object.assign(element.style, val); else if (key === 'dataset' && typeof val === 'object') Object.assign(element.dataset, val); else if (key.startsWith('on') && typeof val === 'function') { element.addEventListener(key.substring(2).toLowerCase(), val); } else if (['innerHTML', 'textContent', 'value', 'title', 'href', 'target', 'role', 'tabIndex', 'id', 'min', 'max', 'step', 'type', 'placeholder', 'checked', 'selected', 'disabled', 'for'].includes(key)) { element[key] = val; } else if (val !== null && val !== undefined && val !== false) { element.setAttribute(key, val); } } if (!Array.isArray(children)) children = [children]; children.forEach(child => { if (child === null || child === undefined) return; if (child instanceof Node) element.appendChild(child); else element.appendChild(document.createTextNode(String(child))); }); return element; } // --- 1. CSS Styles --- const GITE_STYLES = ` :root { /* Core Colors */ --gite-primary: #1a73e8; --gite-primary-bg: #e8f0fe; --gite-bg: #ffffff; --gite-surface: #f8f9fa; --gite-border: #dadce0; --gite-border-light: #e8eaed; --gite-text: #3c4043; --gite-text-secondary: #70757a; --gite-hover: #ededed; --gite-danger: #d93025; --gite-danger-bg: #fce8e6; /* UI Elements */ --gite-toast-bg: #323232; --gite-toast-text: #ffffff; --gite-shadow: 0 4px 16px rgba(0,0,0,0.2); --gite-menu-shadow: 0 2px 10px 0 rgba(0,0,0,0.2); --gite-font: 'Roboto', 'Arial', sans-serif; /* Toolbar Theme Defaults */ --gite-toolbar-bg: #ffffff; --gite-menu-bg: #ffffff; --gite-btn-active-text: #1a73e8; --gite-btn-active-bg: #e8f0fe; /* Dimensions */ --gite-toolbar-height: 34px; --gite-btn-height: 28px; --gite-input-height: 24px; --gite-sidebar-width: 200px; /* Configurable */ --gite-menu-font-size: 14px; --gite-menu-line-height: 1.5; } html.gite-detected-dark-theme { --gite-primary: #8ab4f8; --gite-primary-bg: #394457; --gite-bg: #202124; --gite-surface: #303134; --gite-border: #5f6368; --gite-border-light: #3c4043; --gite-text: #e8eaed; --gite-text-secondary: #9aa0a6; --gite-hover: #3c4043; --gite-danger: #f28b82; --gite-danger-bg: #5c2b29; --gite-toast-bg: #e8eaed; --gite-toast-text: #202124; --gite-menu-shadow: 0 2px 10px 0 rgba(0,0,0,0.5); /* Dark Mode Toolbar */ --gite-toolbar-bg: #1f1f1f; --gite-menu-bg: #303134; --gite-btn-active-text: #8ab4f8; --gite-btn-active-bg: rgba(138, 180, 248, 0.15); } ${NATIVE_TOOLBAR_SELECTOR_TO_HIDE} { display: none !important; } /* Toolbar Container */ #${GITE_TOOLBAR_CONTAINER_ID} { position: relative; background-color: var(--gite-toolbar-bg); padding: 2px 16px; padding-right: 150px; display: flex; align-items: center; gap: 4px; flex-wrap: wrap; font-family: var(--gite-font); font-size: var(--gite-menu-font-size); box-sizing: border-box; z-index: 100; min-height: var(--gite-toolbar-height); } .gite-filters-group { display: flex; flex-wrap: wrap; gap: 4px; align-items: center; width: 100%; } .gite-utilities-group { position: absolute; right: 16px; top: 0; bottom: 0; display: flex; align-items: center; gap: 4px; background-color: var(--gite-toolbar-bg); padding-left: 10px; } .gite-tb-group { position: relative; flex-shrink: 0; } .gite-tb-btn { display: inline-flex; align-items: center; gap: 4px; padding: 3px 8px; border-radius: 3px; color: var(--gite-text); cursor: pointer; user-select: none; border: 1px solid transparent; transition: background 0.1s, color 0.1s; text-decoration: none !important; height: var(--gite-btn-height); box-sizing: border-box; background-color: transparent; font-size: inherit; } .gite-tb-btn:hover, .gite-tb-btn.open { background-color: var(--gite-hover); color: var(--gite-text); } .gite-tb-btn:focus-visible { outline: 2px solid var(--gite-primary); } .gite-tb-btn.active { background-color: var(--gite-btn-active-bg); color: var(--gite-btn-active-text); font-weight: 500; } .gite-tb-btn.active .gite-tb-arrow { border-top-color: var(--gite-btn-active-text); opacity: 1; } .gite-tb-icon { width: 16px; height: 16px; fill: currentColor; flex-shrink: 0; display: flex; align-items: center; justify-content: center; } .gite-tb-icon svg { width: 100%; height: 100%; } .gite-tb-arrow { width: 0; height: 0; border-left: 3px solid transparent; border-right: 3px solid transparent; border-top: 3px solid currentColor; margin-left: 1px; opacity: 0.6; flex-shrink: 0; } .gite-tb-btn.style-text .gite-tb-icon { display: none; } .gite-tb-btn.style-icon .gite-btn-text { display: none; } .gite-tb-btn.style-icon .gite-tb-arrow { display: none; } .gite-tb-adv-link { padding: 3px; width: 28px; justify-content: center; } .gite-tb-adv-link .gite-btn-text { display: none; } .gite-tb-adv-link:visited { color: var(--gite-text); } .gite-tb-btn.clear-action { color: var(--gite-danger); margin-left: 4px; } .gite-tb-btn.clear-action:hover { background-color: var(--gite-danger-bg); color: var(--gite-danger); } .gite-tb-btn.clear-action .gite-tb-icon { fill: var(--gite-danger); } #${GITE_SETTINGS_BUTTON_ID} { color: var(--gite-text-secondary); padding: 4px; border-radius: 50%; display: inline-flex; cursor: pointer; flex-shrink: 0; } #${GITE_SETTINGS_BUTTON_ID}:hover { background-color: var(--gite-hover); color: var(--gite-text); } #${GITE_SETTINGS_BUTTON_ID} svg { width: 18px; height: 18px; fill: currentColor; } #${GITE_RESULT_STATS_DISPLAY_ID} { font-size: 13px; color: var(--gite-text); margin: 0 4px; white-space: nowrap; cursor: default; } /* --- Menus & Dropdowns --- */ .gite-menu-dropdown { position: absolute; top: 100%; left: 0; margin-top: 1px; background: var(--gite-menu-bg); border: 1px solid var(--gite-border); border-radius: 4px; box-shadow: var(--gite-menu-shadow); min-width: 180px; padding: 4px 0; z-index: 2147483647; display: none; /* Improved Scrollbar Support */ max-height: calc(85vh - 50px); overflow-y: auto; text-align: left; font-size: var(--gite-menu-font-size); line-height: var(--gite-menu-line-height); } .gite-menu-dropdown.show { display: block; } /* Custom Scrollbar for Menu */ .gite-menu-dropdown::-webkit-scrollbar { width: 8px; } .gite-menu-dropdown::-webkit-scrollbar-track { background: transparent; } .gite-menu-dropdown::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.2); border-radius: 4px; } .gite-menu-dropdown::-webkit-scrollbar-thumb:hover { background: rgba(0,0,0,0.3); } html.gite-detected-dark-theme .gite-menu-dropdown::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.2); } html.gite-detected-dark-theme .gite-menu-dropdown::-webkit-scrollbar-thumb:hover { background: rgba(255,255,255,0.3); } a.gite-menu-item, div.gite-menu-item { display: flex; justify-content: space-between; align-items: center; padding: 4px 12px; color: var(--gite-text); text-decoration: none; cursor: pointer; transition: background 0.05s; white-space: nowrap; } a.gite-menu-item:hover, div.gite-menu-item:hover, a.gite-menu-item.focus, div.gite-menu-item.focus, a.gite-menu-item:focus, div.gite-menu-item:focus { background-color: var(--gite-hover); text-decoration: none; color: var(--gite-text); outline: none; } a.gite-menu-item.selected, div.gite-menu-item.selected { font-weight: 500; color: var(--gite-primary); background-color: var(--gite-primary-bg); } html.gite-detected-dark-theme a.gite-menu-item.selected, html.gite-detected-dark-theme div.gite-menu-item.selected { background-color: rgba(138, 180, 248, 0.1); } .gite-check-icon { width: 14px; height: 14px; fill: currentColor; margin-left: 8px; visibility: hidden; } .gite-menu-item.selected .gite-check-icon { visibility: visible; } .gite-menu-separator { margin: 2px 0; border-top: 1px solid var(--gite-border); opacity: 0.5; } /* --- Advanced Layouts (2-Col, 3-Col, Masonry) --- */ .gite-menu-row-container { display: flex; width: max-content; } .gite-menu-col { display: flex; flex-direction: column; min-width: 160px; padding: 2px 0; } .gite-menu-col:not(:last-child) { border-right: 1px solid var(--gite-border-light); } /* Masonry / Multi-Column (Region, Site) */ .gite-menu-masonry { column-count: 2; column-gap: 0; width: max-content; min-width: 320px; max-width: 80vw; } .gite-menu-masonry .gite-menu-item { break-inside: avoid; } .gite-menu-masonry-header { border-bottom: 1px solid var(--gite-border-light); margin-bottom: 2px; } /* Embedded Date Picker Styles */ .gite-embedded-picker { padding: 8px; display: flex; flex-direction: column; gap: 8px; min-width: 150px; border-left: 1px solid var(--gite-border-light); background: var(--gite-surface); } .gite-embedded-header { font-size: 12px; font-weight: 500; color: var(--gite-text-secondary); margin-bottom: 2px; } .gite-embedded-input { width: 100%; box-sizing: border-box; border: 1px solid var(--gite-border); border-radius: 4px; padding: 3px 6px; font-size: 13px; font-family: inherit; background: var(--gite-bg); color: var(--gite-text); } .gite-embedded-input.error { border-color: var(--gite-danger); } .gite-embedded-apply-btn { margin-top: 4px; width: 100%; background: var(--gite-primary); color: #fff; border: none; border-radius: 4px; padding: 4px; cursor: pointer; font-size: 12px; font-weight: 500; } .gite-embedded-apply-btn:hover { filter: brightness(1.1); } /* Menu Inputs */ .gite-menu-input-row { padding: 4px 12px; display: flex; align-items: center; gap: 4px; border-bottom: 1px solid var(--gite-border-light); margin-bottom: 2px; box-sizing: border-box; width: 100%; } .gite-menu-input { width: 72px; padding: 2px 4px; border: 1px solid var(--gite-border); border-radius: 3px; background: var(--gite-bg); color: var(--gite-text); font-size: 13px; text-align: center; height: 24px; -moz-appearance: textfield; } html.gite-detected-dark-theme .gite-menu-input { color-scheme: dark; } .gite-menu-btn-icon { padding: 2px; width: 24px; height: 24px; background: var(--gite-primary); color: #fff; border: none; border-radius: 3px; cursor: pointer; display: flex; align-items: center; justify-content: center; flex-shrink: 0; } .gite-menu-btn-icon svg { width: 14px; height: 14px; fill: currentColor; } /* Color Palette Grid (Updated: 4x3) */ .gite-palette-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; padding: 8px 12px; width: 180px; box-sizing: border-box; } /* Swatch as Anchor for A11y & Interactions (Larger Size) */ a.gite-swatch { display: block; width: 28px; height: 28px; border-radius: 50%; border: 1px solid rgba(0,0,0,0.1); cursor: pointer; box-sizing: border-box; transition: transform 0.1s; margin: 0 auto; } a.gite-swatch:hover, a.gite-swatch:focus { transform: scale(1.15); outline: none; } a.gite-swatch.selected { border-color: transparent; box-shadow: 0 0 0 2px var(--gite-bg), 0 0 0 4px var(--gite-primary); z-index: 1; transform: scale(1.1); } /* Settings Panel */ .gite-modal-overlay { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0,0,0,0.6); z-index: 2147483647; display: flex; align-items: center; justify-content: center; opacity: 0; pointer-events: none; transition: opacity 0.15s; font-family: var(--gite-font); font-size: 13px; } .gite-modal-overlay.open { opacity: 1; pointer-events: auto; } .gite-panel { background: var(--gite-bg); color: var(--gite-text); width: 800px; height: 550px; max-width: 95%; max-height: 95%; border-radius: 8px; box-shadow: var(--gite-shadow); display: flex; overflow: hidden; border: 1px solid var(--gite-border); } .gite-sidebar { width: var(--gite-sidebar-width); background: var(--gite-surface); border-right: 1px solid var(--gite-border); display: flex; flex-direction: column; overflow-y: auto; flex-shrink: 0; } .gite-sidebar-header { padding: 12px 16px; font-weight: bold; color: var(--gite-primary); border-bottom: 1px solid var(--gite-border); display: flex; gap: 8px; font-size: 15px; align-items: center; flex-shrink: 0; height: 48px; box-sizing: border-box;} .gite-sidebar-header svg { width: 20px; height: 20px; fill: currentColor; flex-shrink: 0; } .gite-nav-item { padding: 8px 16px; cursor: pointer; display: flex; align-items: center; gap: 10px; color: var(--gite-text-secondary); border-left: 3px solid transparent; font-size: 13px; user-select: none; } .gite-nav-item:hover { background: var(--gite-hover); color: var(--gite-text); } .gite-nav-item.active { background: rgba(26, 115, 232, 0.08); color: var(--gite-primary); border-left-color: var(--gite-primary); font-weight: 500; } html.gite-detected-dark-theme .gite-nav-item.active { background: rgba(138, 180, 248, 0.1); } .gite-nav-item svg { width: 16px; height: 16px; fill: currentColor; opacity: 0.8; flex-shrink: 0; } .gite-content { flex: 1; display: flex; flex-direction: column; min-width: 0; } .gite-header { padding: 0 20px; height: 48px; border-bottom: 1px solid var(--gite-border); display: flex; justify-content: space-between; align-items: center; flex-shrink: 0; } .gite-header h2 { margin: 0; font-size: 16px; font-weight: 500; } .gite-close-btn { background: none; border: none; font-size: 22px; color: var(--gite-text-secondary); cursor: pointer; padding: 4px; border-radius: 4px; line-height: 1; } .gite-close-btn:hover { background: var(--gite-hover); color: var(--gite-text); } .gite-scroll-area { flex: 1; overflow-y: auto; padding: 16px 20px; } .gite-input-group { margin-bottom: 12px; } .gite-input-group label { display: block; margin-bottom: 4px; font-weight: 500; font-size: 12px; color: var(--gite-text-secondary); } .gite-control-row { display: flex; justify-content: space-between; align-items: center; padding: 4px 0; border-bottom: 1px dashed var(--gite-border-light); min-height: 28px; } .gite-input { width: 100%; height: var(--gite-input-height); padding: 0 8px; border: 1px solid var(--gite-border); border-radius: 4px; background: var(--gite-surface); color: var(--gite-text); font-size: 13px; box-sizing: border-box; } input[type=range].gite-range-input { width: 100%; background: transparent; margin: 4px 0; height: 24px; cursor: pointer; display: block; } .gite-option-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 6px; } .gite-option-check-item { display: flex; align-items: center; gap: 8px; font-size: 13px; padding: 6px; border-radius: 4px; cursor: pointer; user-select: none; } .gite-option-check-item:hover { background: var(--gite-surface); } .gite-toggle { position: relative; width: 34px; height: 18px; display: inline-block; flex-shrink: 0; } .gite-toggle input { opacity: 0; width: 0; height: 0; } .gite-slider { position: absolute; top:0;left:0;right:0;bottom:0; background: #bdc1c6; transition:.2s; border-radius:18px; cursor: pointer; } .gite-slider:before { position: absolute; content:""; height:14px; width:14px; left:2px; bottom:2px; background:white; transition:.2s; border-radius:50%; box-shadow:0 1px 2px rgba(0,0,0,0.2); } input:checked + .gite-slider { background: var(--gite-primary); } input:checked + .gite-slider:before { transform: translateX(16px); } .gite-add-box { background: var(--gite-surface); padding: 8px; border-radius: 6px; margin-bottom: 12px; border: 1px solid var(--gite-border); display: flex; gap: 6px; align-items: center; } .gite-list { list-style: none; padding: 0; margin: 0; border: 1px solid var(--gite-border); border-radius: 6px; max-height: 220px; overflow-y: auto; } .gite-list-item { display: flex; align-items: center; padding: 0 12px; height: 32px; background: var(--gite-bg); border-bottom: 1px solid var(--gite-border-light); font-size: 13px; } .gite-list-item:hover { background: var(--gite-surface); } .gite-item-label { flex: 1; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display:flex; align-items:center; gap:8px;} .gite-item-actions { display: flex; gap: 4px; opacity: 0.6; } .gite-list-item:hover .gite-item-actions { opacity: 1; } .gite-icon-btn { width: 24px; height: 24px; cursor: pointer; color: var(--gite-text-secondary); background: transparent; border: 1px solid transparent; border-radius: 4px; display: flex; align-items: center; justify-content: center; } .gite-icon-btn svg { width: 16px; height: 16px; fill: currentColor; } .gite-icon-btn:hover { background: rgba(0,0,0,0.05); color: var(--gite-text); } .gite-icon-btn.danger:hover { color: var(--gite-danger); background: var(--gite-danger-bg); } .gite-category-footer { margin-top: 15px; padding-top: 10px; border-top: 1px dashed var(--gite-border-light); text-align: right; } .gite-btn-reset-cat { background: var(--gite-surface); border: 1px solid var(--gite-border); color: var(--gite-text-secondary); font-size: 12px; padding: 4px 10px; cursor: pointer; border-radius: 4px; display: inline-flex; align-items: center; gap: 6px; transition: 0.2s; height: 26px; } .gite-btn-reset-cat:hover { color: var(--gite-danger); background: var(--gite-danger-bg); border-color: var(--gite-danger-bg); } .gite-btn-reset-cat svg { width: 14px; height: 14px; fill: currentColor; } .gite-footer { height: 50px; padding: 0 20px; border-top: 1px solid var(--gite-border); display: flex; justify-content: space-between; align-items: center; background: var(--gite-surface); flex-shrink: 0; } .gite-footer-right { display: flex; gap: 8px; } .gite-btn { height: 28px; padding: 0 12px; border: 1px solid var(--gite-border); border-radius: 4px; background: var(--gite-bg); color: var(--gite-text); font-size: 13px; cursor: pointer; font-weight: 500; transition: 0.1s; display: inline-flex; align-items: center; justify-content: center; white-space: nowrap; } .gite-btn:hover { background: var(--gite-hover); } .gite-btn-primary { background: var(--gite-primary); color: #fff; border:none; } .gite-btn-primary:hover { background: #1557b0; } .gite-btn-danger { color: var(--gite-danger); } .gite-toast-container { position: fixed; bottom: 30px; left: 50%; transform: translateX(-50%); z-index: 2147483647; pointer-events: none; display: flex; flex-direction: column; gap: 10px; align-items: center; } .gite-toast { background: var(--gite-toast-bg); color: var(--gite-toast-text); padding: 10px 20px; border-radius: 4px; font-size: 13px; opacity: 0; transform: translateY(10px); transition: 0.2s; display: flex; align-items: center; gap: 8px; white-space: nowrap; box-shadow: 0 4px 12px rgba(0,0,0,0.3); } .gite-toast.show { opacity: 1; transform: translateY(0); } /* Date Picker Popup */ .gite-custom-date-picker { background: var(--gite-bg); color: var(--gite-text); border: 1px solid var(--gite-border); border-radius: 8px; box-shadow: var(--gite-shadow); padding: 24px; display: flex; flex-direction: column; min-width: 180px; max-width: 90vw; box-sizing: border-box; } .gite-datepicker-overlay { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0,0,0,0.5); z-index: 2147483647; display: flex; align-items: center; justify-content: center; } .gite-date-row { display: flex; flex-direction: column; align-items: flex-start; margin-bottom: 16px; width: 100%; } .gite-custom-date-picker input[type="date"] { width: 100%; padding: 8px 12px; border: 1px solid var(--gite-border); border-radius: 4px; background: var(--gite-surface); color: var(--gite-text); outline: none; transition: border-color 0.2s; box-sizing: border-box; } .gite-datepicker-actions { display: flex; justify-content: flex-end; margin-top: 12px; width: 100%; gap: 8px; } /* Snappier Ripple */ .gite-ripple { position: relative; overflow: hidden; transform: translate3d(0, 0, 0); } .gite-ripple::after { content: ""; display: block; position: absolute; width: 100%; height: 100%; top: 0; left: 0; pointer-events: none; background-image: radial-gradient(circle, #000 10%, transparent 10.01%); background-repeat: no-repeat; background-position: 50%; transform: scale(10, 10); opacity: 0; transition: transform .3s, opacity .3s; } .gite-ripple:active::after { transform: scale(0, 0); opacity: 0.1; transition: 0s; } html.gite-detected-dark-theme .gite-ripple::after { background-image: radial-gradient(circle, #fff 10%, transparent 10.01%); } `; // --- 2. ICONS (Kept Same) --- const GITE_ICONS = { gear: '', size: '', exactSize: '', aspectRatio: '', color: '', time: '', region: '', site: '', refresh: '', check: '', clear: '', type: '', usageRights: '', fileType: '', edit: '', delete: '', sliders: '', arrowRight: '', globe: '' }; // --- 3. I18N Strings --- const GITE_I18N_STRINGS = { 'en': { 'filter_title_size': "Size", 'filter_title_exact_size': "Exact size", 'filter_title_aspect_ratio': "Aspect ratio", 'filter_title_color': "Color", 'filter_title_type': "Type", 'filter_title_time': "Time", 'filter_title_usage_rights': "Usage rights", 'filter_title_file_type': "File type", 'filter_title_region': "Region", 'filter_title_site_search': "Site Search", 'filter_title_advanced_search': "Advanced Search", 'btn_clear': "Clear", 'btn_apply': "Apply", 'btn_cancel': "Cancel", 'btn_close': "Close", 'btn_save_and_close': "Save & Close", 'btn_delete': "Delete", 'btn_edit_label': "Edit Label", 'btn_save_changes': "Save", 'btn_add_new_exact_size': "Add Size", 'btn_add_new_site': "Add Site", 'btn_reset_all_settings': "Reset All to Defaults", 'btn_reset_general_settings': "Reset General Settings", 'option_text_size_any': "Any size", 'option_text_size_large': "Large", 'option_text_size_medium': "Medium", 'option_text_size_icon': "Icon", 'option_text_size_qsvga': "Larger than 400×300", 'option_text_size_vga': "Larger than 640×480", 'option_text_size_svga': "Larger than 800×600", 'option_text_size_xga': "Larger than 1024×768", 'option_text_size_2mp': "Larger than 2MP", 'option_text_size_4mp': "Larger than 4MP", 'option_text_size_6mp': "Larger than 6MP", 'option_text_size_8mp': "Larger than 8MP", 'option_text_size_10mp': "Larger than 10MP", 'option_text_size_12mp': "Larger than 12MP", 'option_text_size_15mp': "Larger than 15MP", 'option_text_size_20mp': "Larger than 20MP", 'option_text_size_40mp': "Larger than 40MP", 'option_text_size_70mp': "Larger than 70MP", 'option_text_exact_size_any': "Any exact size", 'option_text_exact_size_1024x768': "1024×768 (XGA)", 'option_text_exact_size_1280x720': "1280×720 (HD)", 'option_text_exact_size_1366x768': "1366×768 (Laptop)", 'option_text_exact_size_1600x900': "1600×900 (HD+)", 'option_text_exact_size_1920x1080': "1920×1080 (FHD)", 'option_text_exact_size_2560x1080': "2560×1080 (Ultrawide FHD)", 'option_text_exact_size_2560x1440': "2560×1440 (QHD)", 'option_text_exact_size_3440x1440': "3440×1440 (Ultrawide QHD)", 'option_text_exact_size_3840x2160': "3840×2160 (4K UHD)", 'option_text_exact_size_7680x4320': "7680×4320 (8K UHD)", 'option_text_exact_size_1080x1920': "1080×1920 (FHD Port.)", 'option_text_exact_size_768x1024': "768×1024 (XGA Port.)", 'option_text_exact_size_1920x1200': "1920×1200 (WUXGA)", 'option_text_exact_size_2560x1600': "2560×1600 (WQXGA)", 'option_text_exact_size_5120x2880': "5120×2880 (5K UHD)", 'option_text_exact_size_2160x3840': "2160×3840 (4K Portrait)", 'exact_size_placeholder_width': "Width", 'exact_size_placeholder_height': "Height", 'option_text_ar_any': "Any aspect ratio", 'option_text_ar_tall': "Tall", 'option_text_ar_square': "Square", 'option_text_ar_wide': "Wide", 'option_text_ar_panoramic': "Panoramic", 'option_text_color_any': "Any color", 'option_text_color_full': "Full color", 'option_text_color_bw': "Black and white", 'option_text_color_transparent': "Transparent", 'option_text_color_palette_red': "Red", 'option_text_color_palette_orange': "Orange", 'option_text_color_palette_yellow': "Yellow", 'option_text_color_palette_green': "Green", 'option_text_color_palette_teal': "Teal", 'option_text_color_palette_blue': "Blue", 'option_text_color_palette_purple': "Purple", 'option_text_color_palette_pink': "Pink", 'option_text_color_palette_white': "White", 'option_text_color_palette_gray': "Gray", 'option_text_color_palette_black': "Black", 'option_text_color_palette_brown': "Brown", 'option_text_type_any': "Any type", 'option_text_type_face': "Face", 'option_text_type_photo': "Photo", 'option_text_type_clipart': "Clip art", 'option_text_type_lineart': "Line drawing", 'option_text_type_gif': "GIF", 'option_text_time_any': "Any time", 'option_text_time_past_15m': "Past 15 minutes", 'option_text_time_past_30m': "Past 30 minutes", 'option_text_time_past_hour': "Past hour", 'option_text_time_past_3h': "Past 3 hours", 'option_text_time_past_6h': "Past 6 hours", 'option_text_time_past_9h': "Past 9 hours", 'option_text_time_past_12h': "Past 12 hours", 'option_text_time_past_15h': "Past 15 hours", 'option_text_time_past_18h': "Past 18 hours", 'option_text_time_past_21h': "Past 21 hours", 'option_text_time_past_24h': "Past 24 hours", 'option_text_time_past_2d': "Past 2 days", 'option_text_time_past_3d': "Past 3 days", 'option_text_time_past_4d': "Past 4 days", 'option_text_time_past_5d': "Past 5 days", 'option_text_time_past_6d': "Past 6 days", 'option_text_time_past_week': "Past week", 'option_text_time_past_2w': "Past 2 weeks", 'option_text_time_past_3w': "Past 3 weeks", 'option_text_time_past_month': "Past month", 'option_text_time_past_2m': "Past 2 months", 'option_text_time_past_3m': "Past 3 months", 'option_text_time_past_4m': "Past 4 months", 'option_text_time_past_5m': "Past 5 months", 'option_text_time_past_6m': "Past 6 months", 'option_text_time_past_7m': "Past 7 months", 'option_text_time_past_8m': "Past 8 months", 'option_text_time_past_9m': "Past 9 months", 'option_text_time_past_10m': "Past 10 months", 'option_text_time_past_11m': "Past 11 months", 'option_text_time_past_year': "Past year", 'option_text_time_past_2y': "Past 2 years", 'option_text_time_past_3y': "Past 3 years", 'option_text_time_past_4y': "Past 4 years", 'option_text_time_past_5y': "Past 5 years", 'option_text_time_past_6y': "Past 6 years", 'option_text_time_past_7y': "Past 7 years", 'option_text_time_past_8y': "Past 8 years", 'option_text_time_past_9y': "Past 9 years", 'option_text_time_past_10y': "Past 10 years", 'option_text_time_custom_range': "Custom range...", 'datepicker_label_from': "From:", 'datepicker_label_to': "To:", 'option_text_rights_any': "Not filtered by license", 'option_text_rights_cc': "Creative Commons licenses", 'option_text_rights_commercial': "Commercial & other licenses", 'option_text_filetype_any': "Any format", 'option_text_filetype_jpg': "JPG files", 'option_text_filetype_gif': "GIF files", 'option_text_filetype_png': "PNG files", 'option_text_filetype_bmp': "BMP files", 'option_text_filetype_svg': "SVG files", 'option_text_filetype_webp': "WEBP files", 'option_text_filetype_avif': "AVIF files", 'option_text_filetype_ico': "ICO files", 'option_text_filetype_raw': "RAW files", 'option_text_region_any': "Any region", 'option_text_region_ca': "Canada", 'option_text_region_us': "United States", 'option_text_region_mx': "Mexico", 'option_text_region_ar': "Argentina", 'option_text_region_br': "Brazil", 'option_text_region_cl': "Chile", 'option_text_region_co': "Colombia", 'option_text_region_pe': "Peru", 'option_text_region_gb': "United Kingdom", 'option_text_region_fr': "France", 'option_text_region_de': "Germany", 'option_text_region_it': "Italy", 'option_text_region_es': "Spain", 'option_text_region_al': "Albania", 'option_text_region_at': "Austria", 'option_text_region_by': "Belarus", 'option_text_region_be': "Belgium", 'option_text_region_ba': "Bosnia and Herzegovina", 'option_text_region_bg': "Bulgaria", 'option_text_region_hr': "Croatia", 'option_text_region_cz': "Czech Republic", 'option_text_region_dk': "Denmark", 'option_text_region_ee': "Estonia", 'option_text_region_fi': "Finland", 'option_text_region_gr': "Greece", 'option_text_region_hu': "Hungary", 'option_text_region_is': "Iceland", 'option_text_region_ie': "Ireland", 'option_text_region_lv': "Latvia", 'option_text_region_lt': "Lithuania", 'option_text_region_lu': "Luxembourg", 'option_text_region_nl': "Netherlands", 'option_text_region_no': "Norway", 'option_text_region_pl': "Poland", 'option_text_region_pt': "Portugal", 'option_text_region_ro': "Romania", 'option_text_region_ru': "Russia", 'option_text_region_rs': "Serbia", 'option_text_region_sk': "Slovakia", 'option_text_region_si': "Slovenia", 'option_text_region_se': "Sweden", 'option_text_region_ch': "Switzerland", 'option_text_region_tr': "Turkey", 'option_text_region_ua': "Ukraine", 'option_text_region_jp': "Japan", 'option_text_region_kr': "South Korea", 'option_text_region_tw': "Taiwan", 'option_text_region_cn': "China", 'option_text_region_hk': "Hong Kong", 'option_text_region_in': "India", 'option_text_region_id': "Indonesia", 'option_text_region_il': "Israel", 'option_text_region_my': "Malaysia", 'option_text_region_ph': "Philippines", 'option_text_region_sa': "Saudi Arabia", 'option_text_region_sg': "Singapore", 'option_text_region_th': "Thailand", 'option_text_region_ae': "United Arab Emirates", 'option_text_region_vn': "Vietnam", 'option_text_region_au': "Australia", 'option_text_region_nz': "New Zealand", 'option_text_region_eg': "Egypt", 'option_text_region_ng': "Nigeria", 'option_text_region_za': "South Africa", 'option_text_site_any': "Any site", 'alert_size_already_saved': "This size is already saved.", 'alert_custom_size_deleted': "Custom size deleted: ", 'alert_custom_size_saved': "Custom size saved: ", 'alert_invalid_domain': "Invalid domain format.", 'alert_datepicker_select_dates': "Please select a start and end date.", 'alert_datepicker_end_before_start': "End date cannot be earlier than start date.", 'alert_datepicker_invalid_date': "Invalid date selected or formatting failed. Please choose again.", 'alert_exact_size_invalid_input': "Please enter valid width and height (positive integers).", 'alert_confirm_delete_option_prefix': "Delete \"", 'alert_label_updated': "Label updated.", 'alert_exact_size_added': "New exact size added.", 'alert_exact_size_deleted': "Saved size deleted.", 'alert_generic_error_saving': "Error saving settings.", 'alert_gm_setvalue_unavailable': "Error: GM_setValue is not available. Settings cannot be saved.", 'alert_site_label_empty': "Site label cannot be empty.", 'alert_site_domain_empty': "Site domain cannot be empty.", 'alert_site_domain_invalid': "Invalid domain format. Use example.com or .domain.", 'alert_site_already_saved': "This site/domain is already saved (check label or domain).", 'alert_site_added': "New site added.", 'alert_site_deleted': "Saved site deleted.", 'alert_site_label_updated': "Site label updated.", 'alert_site_value_updated': "Site domain updated.", 'alert_settings_reset_to_default': "All settings have been reset to default.", 'alert_settings_saved': "Settings saved successfully.", 'text_larger_than_prefix': "Larger than ", 'text_site_search_active_prefix': "Site: ", 'gm_menu_gite_settings': "⚙️ GITE Settings", 'gm_menu_reset_all_gite_settings': "🚨 Reset All Settings", 'gm_please_reload': "Please reload the page for changes to take full effect.", 'settings_panel_title': "GITE Settings", 'settings_tab_general': "General", 'settings_tab_exact_size': "Exact Size", 'settings_tab_size': "Size", 'settings_tab_time': "Time", 'settings_tab_region': "Region", 'settings_tab_site_search': "Site Search", 'settings_label_language': "Script Language:", 'settings_lang_auto': "Auto-detect", 'settings_lang_en': "English", 'settings_lang_zh_TW': "Traditional Chinese (繁體中文)", 'settings_lang_ja': "Japanese (日本語)", 'settings_label_showtoolbarbutton': "Show settings button on toolbar:", 'settings_label_showresultstats': "Show result stats on toolbar:", 'settings_label_theme': "Theme:", 'settings_theme_auto': "Auto-detect", 'settings_theme_light': "Light", 'settings_theme_dark': "Dark", 'settings_label_toolbar_font_size': "Menu Font Size (px):", 'settings_label_toolbar_line_height': "Menu Line Height:", 'settings_label_showregionflags': "Show country flags in Region filter:", 'settings_label_showfavicons': "Show favicons in Site Search filter:", 'settings_label_showadvsearch': "Show Advanced Search button:", 'settings_label_btn_style': "Button Style:", 'settings_btn_style_text': "Text Only", 'settings_btn_style_icon': "Icon Only", 'settings_btn_style_both': "Icon + Text", 'alert_confirm_reset_all_settings': "Are you sure you want to reset ALL GITE settings to their defaults? This cannot be undone.", 'settings_enable_filter_category_prefix': "Enable \"", 'settings_enable_filter_category_suffix': "\" filter", 'settings_label_show_exact_size_inputs_in_menu': "Show manual input in Exact Size menu:", 'settings_options_for_category_prefix': "Options for \"", 'settings_options_for_category_suffix': "\"", 'btn_reset_options_for_category_prefix': "Reset \"", 'btn_reset_options_for_category_suffix': "\" Options", 'settings_section_predefined_options': "Predefined Options:", 'settings_section_your_saved_sizes': "Your Saved Sizes:", 'settings_label_add_new_exact_size': "Add New Exact Size:", 'settings_placeholder_label_optional': "Label (optional)", 'settings_label_enable_site_search_filter': "Enable \"Site Search\" filter", 'settings_section_your_saved_sites': "Your Saved Sites:", 'settings_label_add_new_site': "Add New Site:", 'settings_placeholder_site_label': "Label (e.g., Reddit)", 'settings_placeholder_site_domain': "Domain (e.g., reddit.com)", 'settings_no_saved_items_placeholder': "No saved items yet.", /* New Layout Strings */ 'settings_label_layout_two_col': "Use two-column layout:", 'settings_label_layout_multi_col': "Use multi-column (masonry) layout:", 'settings_label_embedded_date': "Show date picker directly in menu:" }, 'zh-TW': { 'filter_title_size': "大小", 'filter_title_exact_size': "精確尺寸", 'filter_title_aspect_ratio': "長寬比", 'filter_title_color': "顏色", 'filter_title_type': "類型", 'filter_title_time': "時間", 'filter_title_usage_rights': "使用權限", 'filter_title_file_type': "檔案類型", 'filter_title_region': "地區", 'filter_title_site_search': "站內搜尋", 'filter_title_advanced_search': "進階搜尋", 'btn_clear': "清除", 'btn_apply': "套用", 'btn_cancel': "取消", 'btn_close': "關閉", 'btn_save_and_close': "儲存並關閉", 'btn_delete': "刪除", 'btn_edit_label': "編輯標籤", 'btn_save_changes': "儲存", 'btn_add_new_exact_size': "新增", 'btn_add_new_site': "新增", 'btn_reset_all_settings': "重置所有設定", 'btn_reset_general_settings': "重置一般設定", 'option_text_size_any': "任何大小", 'option_text_size_large': "大型", 'option_text_size_medium': "中型", 'option_text_size_icon': "圖示", 'option_text_size_qsvga': "大於 400×300", 'option_text_size_vga': "大於 640×480", 'option_text_size_svga': "大於 800×600", 'option_text_size_xga': "大於 1024×768", 'option_text_size_2mp': "大於 2MP", 'option_text_size_4mp': "大於 4MP", 'option_text_size_6mp': "大於 6MP", 'option_text_size_8mp': "大於 8MP", 'option_text_size_10mp': "大於 10MP", 'option_text_size_12mp': "大於 12MP", 'option_text_size_15mp': "大於 15MP", 'option_text_size_20mp': "大於 20MP", 'option_text_size_40mp': "大於 40MP", 'option_text_size_70mp': "大於 70MP", 'option_text_exact_size_any': "任何精確尺寸", 'option_text_exact_size_1024x768': "1024×768 (XGA)", 'option_text_exact_size_1280x720': "1280×720 (HD)", 'option_text_exact_size_1366x768': "1366×768 (筆電)", 'option_text_exact_size_1600x900': "1600×900 (HD+)", 'option_text_exact_size_1920x1080': "1920×1080 (FHD)", 'option_text_exact_size_2560x1080': "2560×1080 (超寬 FHD)", 'option_text_exact_size_2560x1440': "2560×1440 (QHD)", 'option_text_exact_size_3440x1440': "3440×1440 (超寬 QHD)", 'option_text_exact_size_3840x2160': "3840×2160 (4K UHD)", 'option_text_exact_size_7680x4320': "7680×4320 (8K UHD)", 'option_text_exact_size_1080x1920': "1080×1920 (FHD 縱向)", 'option_text_exact_size_768x1024': "768×1024 (XGA 縱向)", 'option_text_exact_size_1920x1200': "1920×1200 (WUXGA)", 'option_text_exact_size_2560x1600': "2560×1600 (WQXGA)", 'option_text_exact_size_5120x2880': "5120×2880 (5K UHD)", 'option_text_exact_size_2160x3840': "2160×3840 (4K 縱向)", 'exact_size_placeholder_width': "寬", 'exact_size_placeholder_height': "高", 'option_text_ar_any': "任何長寬比", 'option_text_ar_tall': "高", 'option_text_ar_square': "正方形", 'option_text_ar_wide': "寬幅", 'option_text_ar_panoramic': "全景", 'option_text_color_any': "任何色彩", 'option_text_color_full': "全彩", 'option_text_color_bw': "黑白", 'option_text_color_transparent': "透明背景", 'option_text_color_palette_red': "紅色", 'option_text_color_palette_orange': "橘色", 'option_text_color_palette_yellow': "黃色", 'option_text_color_palette_green': "綠色", 'option_text_color_palette_teal': "藍綠色", 'option_text_color_palette_blue': "藍色", 'option_text_color_palette_purple': "紫色", 'option_text_color_palette_pink': "粉紅色", 'option_text_color_palette_white': "白色", 'option_text_color_palette_gray': "灰色", 'option_text_color_palette_black': "黑色", 'option_text_color_palette_brown': "棕色", 'option_text_type_any': "任何類型", 'option_text_type_face': "臉部特寫", 'option_text_type_photo': "相片", 'option_text_type_clipart': "美工圖案", 'option_text_type_lineart': "線條藝術畫", 'option_text_type_gif': "GIF 動畫", 'option_text_time_any': "不限時間", 'option_text_time_past_15m': "過去 15 分鐘", 'option_text_time_past_30m': "過去 30 分鐘", 'option_text_time_past_hour': "過去 1 小時", 'option_text_time_past_3h': "過去 3 小時", 'option_text_time_past_6h': "過去 6 小時", 'option_text_time_past_9h': "過去 9 小時", 'option_text_time_past_12h': "過去 12 小時", 'option_text_time_past_15h': "過去 15 小時", 'option_text_time_past_18h': "過去 18 小時", 'option_text_time_past_21h': "過去 21 小時", 'option_text_time_past_24h': "過去 24 小時", 'option_text_time_past_2d': "過去 2 天", 'option_text_time_past_3d': "過去 3 天", 'option_text_time_past_4d': "過去 4 天", 'option_text_time_past_5d': "過去 5 天", 'option_text_time_past_6d': "過去 6 天", 'option_text_time_past_week': "過去 1 週", 'option_text_time_past_2w': "過去 2 週", 'option_text_time_past_3w': "過去 3 週", 'option_text_time_past_month': "過去 1 個月", 'option_text_time_past_2m': "過去 2 個月", 'option_text_time_past_3m': "過去 3 個月", 'option_text_time_past_4m': "過去 4 個月", 'option_text_time_past_5m': "過去 5 個月", 'option_text_time_past_6m': "過去 6 個月", 'option_text_time_past_7m': "過去 7 個月", 'option_text_time_past_8m': "過去 8 個月", 'option_text_time_past_9m': "過去 9 個月", 'option_text_time_past_10m': "過去 10 個月", 'option_text_time_past_11m': "過去 11 個月", 'option_text_time_past_year': "過去 1 年", 'option_text_time_past_2y': "過去 2 年", 'option_text_time_past_3y': "過去 3 年", 'option_text_time_past_4y': "過去 4 年", 'option_text_time_past_5y': "過去 5 年", 'option_text_time_past_6y': "過去 6 年", 'option_text_time_past_7y': "過去 7 年", 'option_text_time_past_8y': "過去 8 年", 'option_text_time_past_9y': "過去 9 年", 'option_text_time_past_10y': "過去 10 年", 'option_text_time_custom_range': "自訂日期範圍...", 'datepicker_label_from': "開始日期:", 'datepicker_label_to': "結束日期:", 'option_text_rights_any': "不限使用權", 'option_text_rights_cc': "創用 CC 授權", 'option_text_rights_commercial': "商業和其他授權", 'option_text_filetype_any': "不限格式", 'option_text_filetype_jpg': "JPG", 'option_text_filetype_gif': "GIF", 'option_text_filetype_png': "PNG", 'option_text_filetype_bmp': "BMP", 'option_text_filetype_svg': "SVG", 'option_text_filetype_webp': "WEBP", 'option_text_filetype_avif': "AVIF", 'option_text_filetype_ico': "ICO", 'option_text_filetype_raw': "RAW", 'option_text_region_any': "不限地區", 'option_text_region_ca': "加拿大", 'option_text_region_us': "美國", 'option_text_region_mx': "墨西哥", 'option_text_region_ar': "阿根廷", 'option_text_region_br': "巴西", 'option_text_region_cl': "智利", 'option_text_region_co': "哥倫比亞", 'option_text_region_pe': "秘魯", 'option_text_region_gb': "英國", 'option_text_region_fr': "法國", 'option_text_region_de': "德國", 'option_text_region_it': "義大利", 'option_text_region_es': "西班牙", 'option_text_region_al': "阿爾巴尼亞", 'option_text_region_at': "奧地利", 'option_text_region_by': "白俄羅斯", 'option_text_region_be': "比利時", 'option_text_region_ba': "波士尼亞與赫塞哥維納", 'option_text_region_bg': "保加利亞", 'option_text_region_hr': "克羅埃西亞", 'option_text_region_cz': "捷克", 'option_text_region_dk': "丹麥", 'option_text_region_ee': "愛沙尼亞", 'option_text_region_fi': "芬蘭", 'option_text_region_gr': "希臘", 'option_text_region_hu': "匈牙利", 'option_text_region_is': "冰島", 'option_text_region_ie': "愛爾蘭", 'option_text_region_lv': "拉脫維亞", 'option_text_region_lt': "立陶宛", 'option_text_region_lu': "盧森堡", 'option_text_region_nl': "荷蘭", 'option_text_region_no': "挪威", 'option_text_region_pl': "波蘭", 'option_text_region_pt': "葡萄牙", 'option_text_region_ro': "羅馬尼亞", 'option_text_region_ru': "俄羅斯", 'option_text_region_rs': "塞爾維亞", 'option_text_region_sk': "斯洛伐克", 'option_text_region_si': "斯洛維尼亞", 'option_text_region_se': "瑞典", 'option_text_region_ch': "瑞士", 'option_text_region_tr': "土耳其", 'option_text_region_ua': "烏克蘭", 'option_text_region_jp': "日本", 'option_text_region_kr': "韓國", 'option_text_region_tw': "台灣", 'option_text_region_cn': "中國", 'option_text_region_hk': "香港", 'option_text_region_in': "印度", 'option_text_region_id': "印尼", 'option_text_region_il': "以色列", 'option_text_region_my': "馬來西亞", 'option_text_region_ph': "菲律賓", 'option_text_region_sa': "沙烏地阿拉伯", 'option_text_region_sg': "新加坡", 'option_text_region_th': "泰國", 'option_text_region_ae': "阿拉伯聯合大公國", 'option_text_region_vn': "越南", 'option_text_region_au': "澳洲", 'option_text_region_nz': "紐西蘭", 'option_text_region_eg': "埃及", 'option_text_region_ng': "奈及利亞", 'option_text_region_za': "南非", 'option_text_site_any': "不限網站", 'alert_size_already_saved': "此尺寸已儲存。", 'alert_custom_size_deleted': "已刪除自訂尺寸: ", 'alert_custom_size_saved': "已儲存自訂尺寸: ", 'alert_invalid_domain': "網域格式無效。", 'alert_datepicker_select_dates': "請選擇開始和結束日期。", 'alert_datepicker_end_before_start': "結束日期不能早於開始日期。", 'alert_datepicker_invalid_date': "選擇的日期無效,請重新選擇。", 'alert_exact_size_invalid_input': "請輸入有效的寬度和高度 (正整數)。", 'alert_confirm_delete_option_prefix': "確定刪除「", 'alert_label_updated': "標籤已更新。", 'alert_exact_size_added': "新尺寸已新增。", 'alert_exact_size_deleted': "尺寸已刪除。", 'alert_generic_error_saving': "儲存設定時發生錯誤。", 'alert_gm_setvalue_unavailable': "錯誤:GM_setValue 功能不可用,無法儲存設定。", 'alert_site_label_empty': "網站標籤不得為空。", 'alert_site_domain_empty': "網站網域不得為空。", 'alert_site_domain_invalid': "網域格式無效 (例: example.com)。", 'alert_site_already_saved': "此網站/網域已儲存。", 'alert_site_added': "新網站已新增。", 'alert_site_deleted': "網站已刪除。", 'alert_site_label_updated': "標籤已更新。", 'alert_site_value_updated': "網域已更新。", 'alert_settings_reset_to_default': "所有設定已重置為預設值。", 'alert_settings_saved': "設定已儲存。", 'text_larger_than_prefix': "大於 ", 'text_site_search_active_prefix': "搜尋: ", 'gm_menu_gite_settings': "⚙️ GITE 設定", 'gm_menu_reset_all_gite_settings': "🚨 重置所有 GITE 設定", 'gm_please_reload': "請重新載入頁面以套用變更。", 'settings_panel_title': "GITE 設定", 'settings_tab_general': "一般設定", 'settings_tab_exact_size': "精確尺寸", 'settings_tab_size': "大小", 'settings_tab_time': "時間", 'settings_tab_region': "地區", 'settings_tab_site_search': "站內搜尋", 'settings_label_language': "腳本語言:", 'settings_lang_auto': "自動偵測", 'settings_lang_en': "English (英文)", 'settings_lang_zh_TW': "Traditional Chinese (繁體中文)", 'settings_lang_ja': "日文 (日本語)", 'settings_label_showtoolbarbutton': "在 GITE 工具列顯示設定按鈕:", 'settings_label_showresultstats': "在 GITE 工具列顯示搜尋結果數量:", 'settings_label_theme': "主題:", 'settings_theme_auto': "自動偵測", 'settings_theme_light': "淺色", 'settings_theme_dark': "深色", 'settings_label_toolbar_font_size': "選單文字大小 (px):", 'settings_label_toolbar_line_height': "選單行高:", 'settings_label_showregionflags': "在「地區」選單中顯示國旗:", 'settings_label_showfavicons': "在「站內搜尋」選單中顯示網站圖示:", 'settings_label_showadvsearch': "在工具列顯示進階搜尋按鈕:", 'settings_label_btn_style': "按鈕樣式:", 'settings_btn_style_text': "僅文字", 'settings_btn_style_icon': "僅圖示", 'settings_btn_style_both': "圖示 + 文字", 'alert_confirm_reset_all_settings': "確定要將所有設定重置為預設值嗎?此操作無法復原。", 'settings_enable_filter_category_prefix': "啟用「", 'settings_enable_filter_category_suffix': "」篩選器", 'settings_label_show_exact_size_inputs_in_menu': "在選單中顯示手動輸入框:", 'settings_options_for_category_prefix': "「", 'settings_options_for_category_suffix': "」的選項", 'btn_reset_options_for_category_prefix': "重置「", 'btn_reset_options_for_category_suffix': "」選項", 'settings_section_predefined_options': "預定義選項:", 'settings_section_your_saved_sizes': "自訂尺寸:", 'settings_label_add_new_exact_size': "新增精確尺寸:", 'settings_placeholder_label_optional': "標籤 (選填)", 'settings_label_enable_site_search_filter': "啟用「站內搜尋」篩選器", 'settings_section_your_saved_sites': "自訂網站:", 'settings_label_add_new_site': "新增網站:", 'settings_placeholder_site_label': "標籤 (例:Reddit)", 'settings_placeholder_site_domain': "網域 (例:reddit.com)", 'settings_no_saved_items_placeholder': "尚無儲存的項目。", /* New Layout Strings */ 'settings_label_layout_two_col': "啟用雙欄式佈局:", 'settings_label_layout_multi_col': "啟用多欄式(瀑布流)佈局:", 'settings_label_embedded_date': "在選單中顯示自訂日期輸入框:" }, 'ja': { 'filter_title_size': "サイズ", 'filter_title_exact_size': "正確なサイズ", 'filter_title_aspect_ratio': "アスペクト比", 'filter_title_color': "色", 'filter_title_type': "種類", 'filter_title_time': "期間", 'filter_title_usage_rights': "ライセンス", 'filter_title_file_type': "ファイル形式", 'filter_title_region': "地域", 'filter_title_site_search': "サイト内検索", 'filter_title_advanced_search': "詳細検索", 'btn_clear': "クリア", 'btn_apply': "適用", 'btn_cancel': "キャンセル", 'btn_close': "閉じる", 'btn_save_and_close': "保存して閉じる", 'btn_delete': "削除", 'btn_edit_label': "ラベルを編集", 'btn_save_changes': "保存", 'btn_add_new_exact_size': "追加", 'btn_add_new_site': "追加", 'btn_reset_all_settings': "すべてリセット", 'btn_reset_general_settings': "一般設定をリセット", 'option_text_size_any': "すべてのサイズ", 'option_text_size_large': "大", 'option_text_size_medium': "中", 'option_text_size_icon': "アイコン", 'option_text_size_qsvga': "400×300 以上", 'option_text_size_vga': "640×480 以上", 'option_text_size_svga': "800×600 以上", 'option_text_size_xga': "1024×768 以上", 'option_text_size_2mp': "2MP 以上", 'option_text_size_4mp': "4MP 以上", 'option_text_size_6mp': "6MP 以上", 'option_text_size_8mp': "8MP 以上", 'option_text_size_10mp': "10MP 以上", 'option_text_size_12mp': "12MP 以上", 'option_text_size_15mp': "15MP 以上", 'option_text_size_20mp': "20MP 以上", 'option_text_size_40mp': "40MP 以上", 'option_text_size_70mp': "70MP 以上", 'option_text_exact_size_any': "すべての正確なサイズ", 'option_text_exact_size_1024x768': "1024×768 (XGA)", 'option_text_exact_size_1280x720': "1280×720 (HD)", 'option_text_exact_size_1366x768': "1366×768 (Laptop)", 'option_text_exact_size_1600x900': "1600×900 (HD+)", 'option_text_exact_size_1920x1080': "1920×1080 (FHD)", 'option_text_exact_size_2560x1080': "2560×1080 (Ultrawide FHD)", 'option_text_exact_size_2560x1440': "2560×1440 (QHD)", 'option_text_exact_size_3440x1440': "3440×1440 (Ultrawide QHD)", 'option_text_exact_size_3840x2160': "3840×2160 (4K UHD)", 'option_text_exact_size_7680x4320': "7680×4320 (8K UHD)", 'option_text_exact_size_1080x1920': "1080×1920 (FHD 縦)", 'option_text_exact_size_768x1024': "768×1024 (XGA 縦)", 'option_text_exact_size_1920x1200': "1920×1200 (WUXGA)", 'option_text_exact_size_2560x1600': "2560×1600 (WQXGA)", 'option_text_exact_size_5120x2880': "5120×2880 (5K UHD)", 'option_text_exact_size_2160x3840': "2160×3840 (4K 縦)", 'exact_size_placeholder_width': "幅", 'exact_size_placeholder_height': "高さ", 'option_text_ar_any': "全アスペクト比", 'option_text_ar_tall': "縦長", 'option_text_ar_square': "正方形", 'option_text_ar_wide': "横長", 'option_text_ar_panoramic': "パノラマ", 'option_text_color_any': "すべての色", 'option_text_color_full': "フルカラー", 'option_text_color_bw': "白黒", 'option_text_color_transparent': "透明", 'option_text_color_palette_red': "赤", 'option_text_color_palette_orange': "オレンジ", 'option_text_color_palette_yellow': "黄", 'option_text_color_palette_green': "緑", 'option_text_color_palette_teal': "青緑", 'option_text_color_palette_blue': "青", 'option_text_color_palette_purple': "紫", 'option_text_color_palette_pink': "ピンク", 'option_text_color_palette_white': "白", 'option_text_color_palette_gray': "グレー", 'option_text_color_palette_black': "黒", 'option_text_color_palette_brown': "茶", 'option_text_type_any': "すべての種類", 'option_text_type_face': "顔", 'option_text_type_photo': "写真", 'option_text_type_clipart': "クリップアート", 'option_text_type_lineart': "線画", 'option_text_type_gif': "GIF", 'option_text_time_any': "期間指定なし", 'option_text_time_past_15m': "過去15分", 'option_text_time_past_30m': "過去30分", 'option_text_time_past_hour': "過去1時間", 'option_text_time_past_3h': "過去3時間", 'option_text_time_past_6h': "過去6時間", 'option_text_time_past_9h': "過去9時間", 'option_text_time_past_12h': "過去12時間", 'option_text_time_past_15h': "過去15時間", 'option_text_time_past_18h': "過去18時間", 'option_text_time_past_21h': "過去21時間", 'option_text_time_past_24h': "過去24時間", 'option_text_time_past_2d': "過去2日", 'option_text_time_past_3d': "過去3日", 'option_text_time_past_4d': "過去4日", 'option_text_time_past_5d': "過去5日", 'option_text_time_past_6d': "過去6日", 'option_text_time_past_week': "過去1週間", 'option_text_time_past_2w': "過去2週間", 'option_text_time_past_3w': "過去3週間", 'option_text_time_past_month': "過去1か月", 'option_text_time_past_2m': "過去2か月", 'option_text_time_past_3m': "過去3か月", 'option_text_time_past_4m': "過去4か月", 'option_text_time_past_5m': "過去5か月", 'option_text_time_past_6m': "過去6か月", 'option_text_time_past_7m': "過去7か月", 'option_text_time_past_8m': "過去8か月", 'option_text_time_past_9m': "過去9か月", 'option_text_time_past_10m': "過去10か月", 'option_text_time_past_11m': "過去11か月", 'option_text_time_past_year': "過去1年", 'option_text_time_past_2y': "過去2年", 'option_text_time_past_3y': "過去3年", 'option_text_time_past_4y': "過去4年", 'option_text_time_past_5y': "過去5年", 'option_text_time_past_6y': "過去6年", 'option_text_time_past_7y': "過去7年", 'option_text_time_past_8y': "過去8年", 'option_text_time_past_9y': "過去9年", 'option_text_time_past_10y': "過去10年", 'option_text_time_custom_range': "期間を指定...", 'datepicker_label_from': "開始日:", 'datepicker_label_to': "終了日:", 'option_text_rights_any': "すべて", 'option_text_rights_cc': "クリエイティブ・コモンズ", 'option_text_rights_commercial': "商用およびその他のライセンス", 'option_text_filetype_any': "すべての形式", 'option_text_filetype_jpg': "JPG", 'option_text_filetype_gif': "GIF", 'option_text_filetype_png': "PNG", 'option_text_filetype_bmp': "BMP", 'option_text_filetype_svg': "SVG", 'option_text_filetype_webp': "WEBP", 'option_text_filetype_avif': "AVIF", 'option_text_filetype_ico': "ICO", 'option_text_filetype_raw': "RAW", 'option_text_region_any': "すべての地域", 'option_text_region_ca': "カナダ", 'option_text_region_us': "アメリカ", 'option_text_region_mx': "メキシコ", 'option_text_region_ar': "アルゼンチン", 'option_text_region_br': "ブラジル", 'option_text_region_cl': "チリ", 'option_text_region_co': "コロンビア", 'option_text_region_pe': "ペルー", 'option_text_region_gb': "イギリス", 'option_text_region_fr': "フランス", 'option_text_region_de': "ドイツ", 'option_text_region_it': "イタリア", 'option_text_region_es': "スペイン", 'option_text_region_al': "アルバニア", 'option_text_region_at': "オーストリア", 'option_text_region_by': "ベラルーシ", 'option_text_region_be': "ベルギー", 'option_text_region_ba': "ボスニア・ヘルツェゴビナ", 'option_text_region_bg': "ブルガリア", 'option_text_region_hr': "クロアチア", 'option_text_region_cz': "チェコ", 'option_text_region_dk': "デンマーク", 'option_text_region_ee': "エストニア", 'option_text_region_fi': "フィンランド", 'option_text_region_gr': "ギリシャ", 'option_text_region_hu': "ハンガリー", 'option_text_region_is': "アイスランド", 'option_text_region_ie': "アイルランド", 'option_text_region_lv': "ラトビア", 'option_text_region_lt': "リトアニア", 'option_text_region_lu': "ルクセンブルク", 'option_text_region_nl': "オランダ", 'option_text_region_no': "ノルウェー", 'option_text_region_pl': "ポーランド", 'option_text_region_pt': "ポルトガル", 'option_text_region_ro': "ルーマニア", 'option_text_region_ru': "ロシア", 'option_text_region_rs': "セルビア", 'option_text_region_sk': "スロバキア", 'option_text_region_si': "スロベニア", 'option_text_region_se': "スウェーデン", 'option_text_region_ch': "スイス", 'option_text_region_tr': "トルコ", 'option_text_region_ua': "ウクライナ", 'option_text_region_jp': "日本", 'option_text_region_kr': "韓国", 'option_text_region_tw': "台湾", 'option_text_region_cn': "中国", 'option_text_region_hk': "香港", 'option_text_region_in': "インド", 'option_text_region_id': "インドネシア", 'option_text_region_il': "イスラエル", 'option_text_region_my': "マレーシア", 'option_text_region_ph': "フィリピン", 'option_text_region_sa': "サウジアラビア", 'option_text_region_sg': "シンガポール", 'option_text_region_th': "タイ", 'option_text_region_ae': "アラブ首長国連邦", 'option_text_region_vn': "ベトナム", 'option_text_region_au': "オーストラリア", 'option_text_region_nz': "ニュージーランド", 'option_text_region_eg': "エジプト", 'option_text_region_ng': "ナイジェリア", 'option_text_region_za': "南アフリカ", 'option_text_site_any': "すべてのサイト", 'alert_size_already_saved': "このサイズは既に保存されています。", 'alert_custom_size_deleted': "削除しました: ", 'alert_custom_size_saved': "保存しました: ", 'alert_invalid_domain': "無効なドメイン形式です。", 'alert_datepicker_select_dates': "開始日と終了日を選択してください。", 'alert_datepicker_end_before_start': "終了日を開始日より前にすることはできません。", 'alert_datepicker_invalid_date': "無効な日付です。", 'alert_exact_size_invalid_input': "有効な幅と高さを入力してください。", 'alert_confirm_delete_option_prefix': "「", 'alert_label_updated': "ラベルを更新しました。", 'alert_exact_size_added': "サイズを追加しました。", 'alert_exact_size_deleted': "サイズを削除しました。", 'alert_generic_error_saving': "保存中にエラーが発生しました。", 'alert_gm_setvalue_unavailable': "GM_setValue が利用できません。", 'alert_site_label_empty': "ラベルは空にできません。", 'alert_site_domain_empty': "ドメインは空にできません。", 'alert_site_domain_invalid': "無効なドメイン形式です。", 'alert_site_already_saved': "このサイトは既に保存されています。", 'alert_site_added': "サイトを追加しました。", 'alert_site_deleted': "サイトを削除しました。", 'alert_site_label_updated': "ラベルを更新しました。", 'alert_site_value_updated': "ドメインを更新しました。", 'alert_settings_reset_to_default': "設定をリセットしました。", 'alert_settings_saved': "設定を保存しました。", 'text_larger_than_prefix': "次より大きい: ", 'text_site_search_active_prefix': "サイト: ", 'gm_menu_gite_settings': "⚙️ GITE 設定", 'gm_menu_reset_all_gite_settings': "🚨 すべての設定をリセット", 'gm_please_reload': "変更を適用するにはリロードしてください。", 'settings_panel_title': "GITE 設定", 'settings_tab_general': "一般設定", 'settings_tab_exact_size': "正確なサイズ", 'settings_tab_size': "サイズ", 'settings_tab_time': "期間", 'settings_tab_region': "地域", 'settings_tab_site_search': "サイト内検索", 'settings_label_language': "言語:", 'settings_lang_auto': "自動検出", 'settings_lang_en': "English (英語)", 'settings_lang_zh_TW': "繁體中文 (台湾)", 'settings_lang_ja': "日本語 (日本)", 'settings_label_showtoolbarbutton': "ツールバーに設定ボタンを表示:", 'settings_label_showresultstats': "ツールバーに結果数を表示:", 'settings_label_theme': "テーマ:", 'settings_theme_auto': "自動検出", 'settings_theme_light': "ライト", 'settings_theme_dark': "ダーク", 'settings_label_toolbar_font_size': "メニューのフォントサイズ (px):", 'settings_label_toolbar_line_height': "メニューの行の高さ:", 'settings_label_showregionflags': "地域メニューに国旗を表示:", 'settings_label_showfavicons': "サイト内検索メニューにアイコンを表示:", 'settings_label_showadvsearch': "詳細検索ボタンを表示:", 'settings_label_btn_style': "ボタンのスタイル:", 'settings_btn_style_text': "テキストのみ", 'settings_btn_style_icon': "アイコンのみ", 'settings_btn_style_both': "アイコン + テキスト", 'alert_confirm_reset_all_settings': "すべての設定をリセットしてもよろしいですか?", 'settings_enable_filter_category_prefix': "「", 'settings_enable_filter_category_suffix': "」フィルターを有効にする", 'settings_label_show_exact_size_inputs_in_menu': "メニューに手動入力欄を表示:", 'settings_options_for_category_prefix': "「", 'settings_options_for_category_suffix': "」のオプション", 'btn_reset_options_for_category_prefix': "「", 'btn_reset_options_for_category_suffix': "」オプションをリセット", 'settings_section_predefined_options': "事前定義オプション:", 'settings_section_your_saved_sizes': "保存したサイズ:", 'settings_label_add_new_exact_size': "サイズを追加:", 'settings_placeholder_label_optional': "ラベル (オプション)", 'settings_label_enable_site_search_filter': "「サイト内検索」フィルターを有効にする", 'settings_section_your_saved_sites': "保存したサイト:", 'settings_label_add_new_site': "サイトを追加:", 'settings_placeholder_site_label': "ラベル (例: Reddit)", 'settings_placeholder_site_domain': "ドメイン (例: reddit.com)", 'settings_no_saved_items_placeholder': "保存されたアイテムはありません。", /* New Layout Strings */ 'settings_label_layout_two_col': "2列レイアウトを使用:", 'settings_label_layout_multi_col': "マルチカラム(メイソンリー)レイアウトを使用:", 'settings_label_embedded_date': "メニューに日付選択を表示:" } }; // --- 4. Option Definitions --- const GITE_OPTION_DEFINITIONS = { size: [ { id: "gite_size_any", value: "", textKey: "option_text_size_any", defaultEnabled: true }, { id: "gite_size_large", value: "l", textKey: "option_text_size_large", defaultEnabled: true }, { id: "gite_size_medium", value: "m", textKey: "option_text_size_medium", defaultEnabled: true }, { id: "gite_size_icon", value: "i", textKey: "option_text_size_icon", defaultEnabled: true }, { id: "gite_size_sep1", type: "separator", defaultEnabled: true }, { id: "gite_size_qsvga", value: "qsvga", textKey: "option_text_size_qsvga", defaultEnabled: true }, { id: "gite_size_vga", value: "vga", textKey: "option_text_size_vga", defaultEnabled: true }, { id: "gite_size_svga", value: "svga", textKey: "option_text_size_svga", defaultEnabled: true }, { id: "gite_size_xga", value: "xga", textKey: "option_text_size_xga", defaultEnabled: true }, { id: "gite_size_2mp", value: "2mp", textKey: "option_text_size_2mp", defaultEnabled: true }, { id: "gite_size_4mp", value: "4mp", textKey: "option_text_size_4mp", defaultEnabled: true }, { id: "gite_size_6mp", value: "6mp", textKey: "option_text_size_6mp", defaultEnabled: true }, { id: "gite_size_8mp", value: "8mp", textKey: "option_text_size_8mp", defaultEnabled: true }, { id: "gite_size_10mp", value: "10mp", textKey: "option_text_size_10mp", defaultEnabled: true }, { id: "gite_size_12mp", value: "12mp", textKey: "option_text_size_12mp", defaultEnabled: true }, { id: "gite_size_15mp", value: "15mp", textKey: "option_text_size_15mp", defaultEnabled: true }, { id: "gite_size_20mp", value: "20mp", textKey: "option_text_size_20mp", defaultEnabled: true }, { id: "gite_size_40mp", value: "40mp", textKey: "option_text_size_40mp", defaultEnabled: true }, { id: "gite_size_70mp", value: "70mp", textKey: "option_text_size_70mp", defaultEnabled: true }, ], exactSize: [ { id: "gite_exact_any", value: "", type: "imagesize_clear", textKey: "option_text_exact_size_any", defaultEnabled: true }, { id: "gite_exact_1024x768", value: "1024x768", type: "imagesize", textKey: "option_text_exact_size_1024x768", defaultEnabled: true }, { id: "gite_exact_1280x720", value: "1280x720", type: "imagesize", textKey: "option_text_exact_size_1280x720", defaultEnabled: true }, { id: "gite_exact_1366x768", value: "1366x768", type: "imagesize", textKey: "option_text_exact_size_1366x768", defaultEnabled: true }, { id: "gite_exact_1600x900", value: "1600x900", type: "imagesize", textKey: "option_text_exact_size_1600x900", defaultEnabled: true }, { id: "gite_exact_1920x1080", value: "1920x1080", type: "imagesize", textKey: "option_text_exact_size_1920x1080", defaultEnabled: true }, { id: "gite_exact_1920x1200", value: "1920x1200", type: "imagesize", textKey: "option_text_exact_size_1920x1200", defaultEnabled: false }, { id: "gite_exact_2560x1080", value: "2560x1080", type: "imagesize", textKey: "option_text_exact_size_2560x1080", defaultEnabled: true }, { id: "gite_exact_2560x1440", value: "2560x1440", type: "imagesize", textKey: "option_text_exact_size_2560x1440", defaultEnabled: true }, { id: "gite_exact_2560x1600", value: "2560x1600", type: "imagesize", textKey: "option_text_exact_size_2560x1600", defaultEnabled: true }, { id: "gite_exact_3440x1440", value: "3440x1440", type: "imagesize", textKey: "option_text_exact_size_3440x1440", defaultEnabled: true }, { id: "gite_exact_3840x2160", value: "3840x2160", type: "imagesize", textKey: "option_text_exact_size_3840x2160", defaultEnabled: true }, { id: "gite_exact_5120x2880", value: "5120x2880", type: "imagesize", textKey: "option_text_exact_size_5120x2880", defaultEnabled: true }, { id: "gite_exact_7680x4320", value: "7680x4320", type: "imagesize", textKey: "option_text_exact_size_7680x4320", defaultEnabled: true }, { id: "gite_exact_1080x1920", value: "1080x1920", type: "imagesize", textKey: "option_text_exact_size_1080x1920", defaultEnabled: true }, { id: "gite_exact_2160x3840", value: "2160x3840", type: "imagesize", textKey: "option_text_exact_size_2160x3840", defaultEnabled: false }, { id: "gite_exact_768x1024", value: "768x1024", type: "imagesize", textKey: "option_text_exact_size_768x1024", defaultEnabled: true }, ], aspectRatio: [ { id: "ar_any", value: "", textKey: "option_text_ar_any" }, { id: "ar_square", value: "s", textKey: "option_text_ar_square" }, { id: "ar_wide", value: "w", textKey: "option_text_ar_wide" }, { id: "ar_tall", value: "t", textKey: "option_text_ar_tall" }, { id: "ar_panoramic", value: "xw", textKey: "option_text_ar_panoramic" } ], color: { prefixOptions: [ { id: "color_any", value: "", tbsValue: "", textKey: "option_text_color_any" }, { id: "color_full", value: "color", tbsValue: "ic:color", textKey: "option_text_color_full" }, { id: "color_bw", value: "gray", tbsValue: "ic:gray", textKey: "option_text_color_bw" }, { id: "color_trans", value: "trans", tbsValue: "ic:trans", textKey: "option_text_color_transparent" }, { id: "color_sep", type: "separator"}, ], paletteColors: [ { id: "pal_red", hex: "#CC0000", tbsValue: "ic:specific,isc:red", type: "palette", textKey: "option_text_color_palette_red" }, { id: "pal_orange", hex: "#FB940B", tbsValue: "ic:specific,isc:orange", type: "palette", textKey: "option_text_color_palette_orange" }, { id: "pal_yellow", hex: "#FFFF00", tbsValue: "ic:specific,isc:yellow", type: "palette", textKey: "option_text_color_palette_yellow" }, { id: "pal_green", hex: "#00CC00", tbsValue: "ic:specific,isc:green", type: "palette", textKey: "option_text_color_palette_green" }, { id: "pal_teal", hex: "#03C0C6", tbsValue: "ic:specific,isc:teal", type: "palette", textKey: "option_text_color_palette_teal" }, { id: "pal_blue", hex: "#0000FF", tbsValue: "ic:specific,isc:blue", type: "palette", textKey: "option_text_color_palette_blue" }, { id: "pal_purple", hex: "#762CA7", tbsValue: "ic:specific,isc:purple", type: "palette", textKey: "option_text_color_palette_purple" }, { id: "pal_pink", hex: "#FF98BF", tbsValue: "ic:specific,isc:pink", type: "palette", textKey: "option_text_color_palette_pink" }, { id: "pal_white", hex: "#FFFFFF", tbsValue: "ic:specific,isc:white", type: "palette", textKey: "option_text_color_palette_white" }, { id: "pal_gray", hex: "#999999", tbsValue: "ic:specific,isc:gray", type: "palette", textKey: "option_text_color_palette_gray" }, { id: "pal_black", hex: "#000000", tbsValue: "ic:specific,isc:black", type: "palette", textKey: "option_text_color_palette_black" }, { id: "pal_brown", hex: "#885418", tbsValue: "ic:specific,isc:brown", type: "palette", textKey: "option_text_color_palette_brown" } ] }, type: [ { id: "type_any", tbsValue: "", textKey: "option_text_type_any" }, { id: "type_face", tbsValue: "itp:face", textKey: "option_text_type_face" }, { id: "type_photo", tbsValue: "itp:photo", textKey: "option_text_type_photo" }, { id: "type_clipart", tbsValue: "itp:clipart", textKey: "option_text_type_clipart" }, { id: "type_lineart", tbsValue: "itp:lineart", textKey: "option_text_type_lineart" }, { id: "type_gif", tbsValue: "itp:animated", textKey: "option_text_type_gif" } ], time: [ { id: "time_any", tbsValue: "", textKey: "option_text_time_any", defaultEnabled: true }, { id: "time_past_15m", tbsValue: "qdr:n15", textKey: "option_text_time_past_15m", defaultEnabled: false }, { id: "time_past_30m", tbsValue: "qdr:n30", textKey: "option_text_time_past_30m", defaultEnabled: true }, { id: "time_past_hour", tbsValue: "qdr:h", textKey: "option_text_time_past_hour", defaultEnabled: true }, { id: "time_past_3h", tbsValue: "qdr:h3", textKey: "option_text_time_past_3h", defaultEnabled: false }, { id: "time_past_6h", tbsValue: "qdr:h6", textKey: "option_text_time_past_6h", defaultEnabled: true }, { id: "time_past_9h", tbsValue: "qdr:h9", textKey: "option_text_time_past_9h", defaultEnabled: false }, { id: "time_past_12h", tbsValue: "qdr:h12", textKey: "option_text_time_past_12h", defaultEnabled: true }, { id: "time_past_15h", tbsValue: "qdr:h15", textKey: "option_text_time_past_15h", defaultEnabled: false }, { id: "time_past_18h", tbsValue: "qdr:h18", textKey: "option_text_time_past_18h", defaultEnabled: false }, { id: "time_past_21h", tbsValue: "qdr:h21", textKey: "option_text_time_past_21h", defaultEnabled: false }, { id: "time_past_24h", tbsValue: "qdr:d", textKey: "option_text_time_past_24h", defaultEnabled: true }, { id: "time_past_2d", tbsValue: "qdr:d2", textKey: "option_text_time_past_2d", defaultEnabled: false }, { id: "time_past_3d", tbsValue: "qdr:d3", textKey: "option_text_time_past_3d", defaultEnabled: true }, { id: "time_past_4d", tbsValue: "qdr:d4", textKey: "option_text_time_past_4d", defaultEnabled: false }, { id: "time_past_5d", tbsValue: "qdr:d5", textKey: "option_text_time_past_5d", defaultEnabled: true }, { id: "time_past_6d", tbsValue: "qdr:d6", textKey: "option_text_time_past_6d", defaultEnabled: false }, { id: "time_past_week", tbsValue: "qdr:w", textKey: "option_text_time_past_week", defaultEnabled: true }, { id: "time_past_2w", tbsValue: "qdr:w2", textKey: "option_text_time_past_2w", defaultEnabled: true }, { id: "time_past_3w", tbsValue: "qdr:w3", textKey: "option_text_time_past_3w", defaultEnabled: true }, { id: "time_past_month", tbsValue: "qdr:m", textKey: "option_text_time_past_month", defaultEnabled: true }, { id: "time_past_2m", tbsValue: "qdr:m2", textKey: "option_text_time_past_2m", defaultEnabled: false }, { id: "time_past_3m", tbsValue: "qdr:m3", textKey: "option_text_time_past_3m", defaultEnabled: true }, { id: "time_past_4m", tbsValue: "qdr:m4", textKey: "option_text_time_past_4m", defaultEnabled: false }, { id: "time_past_5m", tbsValue: "qdr:m5", textKey: "option_text_time_past_5m", defaultEnabled: false }, { id: "time_past_6m", tbsValue: "qdr:m6", textKey: "option_text_time_past_6m", defaultEnabled: true }, { id: "time_past_7m", tbsValue: "qdr:m7", textKey: "option_text_time_past_7m", defaultEnabled: false }, { id: "time_past_8m", tbsValue: "qdr:m8", textKey: "option_text_time_past_8m", defaultEnabled: false }, { id: "time_past_9m", tbsValue: "qdr:m9", textKey: "option_text_time_past_9m", defaultEnabled: true }, { id: "time_past_10m", tbsValue: "qdr:m10", textKey: "option_text_time_past_10m", defaultEnabled: false }, { id: "time_past_11m", tbsValue: "qdr:m11", textKey: "option_text_time_past_11m", defaultEnabled: false }, { id: "time_past_year", tbsValue: "qdr:y", textKey: "option_text_time_past_year", defaultEnabled: true }, { id: "time_past_2y", tbsValue: "qdr:y2", textKey: "option_text_time_past_2y", defaultEnabled: true }, { id: "time_past_3y", tbsValue: "qdr:y3", textKey: "option_text_time_past_3y", defaultEnabled: true }, { id: "time_past_4y", tbsValue: "qdr:y4", textKey: "option_text_time_past_4y", defaultEnabled: false }, { id: "time_past_5y", tbsValue: "qdr:y5", textKey: "option_text_time_past_5y", defaultEnabled: false }, { id: "time_past_6y", tbsValue: "qdr:y6", textKey: "option_text_time_past_6y", defaultEnabled: false }, { id: "time_past_7y", tbsValue: "qdr:y7", textKey: "option_text_time_past_7y", defaultEnabled: false }, { id: "time_past_8y", tbsValue: "qdr:y8", textKey: "option_text_time_past_8y", defaultEnabled: false }, { id: "time_past_9y", tbsValue: "qdr:y9", textKey: "option_text_time_past_9y", defaultEnabled: false }, { id: "time_past_10y", tbsValue: "qdr:y10", textKey: "option_text_time_past_10y", defaultEnabled: false }, { id: "time_sep", type: "separator", defaultEnabled: true }, { id: "time_custom", type: "custom_date_trigger", textKey: "option_text_time_custom_range", defaultEnabled: true } ], usageRights: [ { id: "rights_any", tbsValue: "", textKey: "option_text_rights_any" }, { id: "rights_cc", tbsValue: "sur:cl", textKey: "option_text_rights_cc" }, { id: "rights_commercial", tbsValue: "sur:ol", textKey: "option_text_rights_commercial" } ], fileType: [ { id: "file_any", value: "", textKey: "option_text_filetype_any" }, { id: "file_jpg", value: "jpg", textKey: "option_text_filetype_jpg" }, { id: "file_png", value: "png", textKey: "option_text_filetype_png" }, { id: "file_gif", value: "gif", textKey: "option_text_filetype_gif" }, { id: "file_bmp", value: "bmp", textKey: "option_text_filetype_bmp" }, { id: "file_svg", value: "svg", textKey: "option_text_filetype_svg" }, { id: "file_webp", value: "webp", textKey: "option_text_filetype_webp" }, { id: "file_avif", value: "avif", textKey: "option_text_filetype_avif" }, { id: "file_ico", value: "ico", textKey: "option_text_filetype_ico" }, { id: "file_raw", value: "craw", textKey: "option_text_filetype_raw" } ], region: [ { id: "reg_any", value: "", paramName: "cr", textKey: "option_text_region_any", defaultEnabled: true }, { id: "reg_ca", value: "countryCA", paramName: "cr", textKey: "option_text_region_ca", defaultEnabled: true }, { id: "reg_us", value: "countryUS", paramName: "cr", textKey: "option_text_region_us", defaultEnabled: true }, { id: "reg_mx", value: "countryMX", paramName: "cr", textKey: "option_text_region_mx", defaultEnabled: false }, { id: "reg_ar", value: "countryAR", paramName: "cr", textKey: "option_text_region_ar", defaultEnabled: false }, { id: "reg_br", value: "countryBR", paramName: "cr", textKey: "option_text_region_br", defaultEnabled: false }, { id: "reg_cl", value: "countryCL", paramName: "cr", textKey: "option_text_region_cl", defaultEnabled: false }, { id: "reg_co", value: "countryCO", paramName: "cr", textKey: "option_text_region_co", defaultEnabled: false }, { id: "reg_pe", value: "countryPE", paramName: "cr", textKey: "option_text_region_pe", defaultEnabled: false }, { id: "reg_gb", value: "countryGB", paramName: "cr", textKey: "option_text_region_gb", defaultEnabled: true }, { id: "reg_fr", value: "countryFR", paramName: "cr", textKey: "option_text_region_fr", defaultEnabled: true }, { id: "reg_de", value: "countryDE", paramName: "cr", textKey: "option_text_region_de", defaultEnabled: true }, { id: "reg_it", value: "countryIT", paramName: "cr", textKey: "option_text_region_it", defaultEnabled: true }, { id: "reg_es", value: "countryES", paramName: "cr", textKey: "option_text_region_es", defaultEnabled: true }, { id: "reg_al", value: "countryAL", paramName: "cr", textKey: "option_text_region_al", defaultEnabled: false }, { id: "reg_at", value: "countryAT", paramName: "cr", textKey: "option_text_region_at", defaultEnabled: false }, { id: "reg_by", value: "countryBY", paramName: "cr", textKey: "option_text_region_by", defaultEnabled: false }, { id: "reg_be", value: "countryBE", paramName: "cr", textKey: "option_text_region_be", defaultEnabled: false }, { id: "reg_ba", value: "countryBA", paramName: "cr", textKey: "option_text_region_ba", defaultEnabled: false }, { id: "reg_bg", value: "countryBG", paramName: "cr", textKey: "option_text_region_bg", defaultEnabled: false }, { id: "reg_hr", value: "countryHR", paramName: "cr", textKey: "option_text_region_hr", defaultEnabled: false }, { id: "reg_cz", value: "countryCZ", paramName: "cr", textKey: "option_text_region_cz", defaultEnabled: false }, { id: "reg_dk", value: "countryDK", paramName: "cr", textKey: "option_text_region_dk", defaultEnabled: false }, { id: "reg_ee", value: "countryEE", paramName: "cr", textKey: "option_text_region_ee", defaultEnabled: false }, { id: "reg_fi", value: "countryFI", paramName: "cr", textKey: "option_text_region_fi", defaultEnabled: false }, { id: "reg_gr", value: "countryGR", paramName: "cr", textKey: "option_text_region_gr", defaultEnabled: false }, { id: "reg_hu", value: "countryHU", paramName: "cr", textKey: "option_text_region_hu", defaultEnabled: false }, { id: "reg_is", value: "countryIS", paramName: "cr", textKey: "option_text_region_is", defaultEnabled: false }, { id: "reg_ie", value: "countryIE", paramName: "cr", textKey: "option_text_region_ie", defaultEnabled: false }, { id: "reg_lv", value: "countryLV", paramName: "cr", textKey: "option_text_region_lv", defaultEnabled: false }, { id: "reg_lt", value: "countryLT", paramName: "cr", textKey: "option_text_region_lt", defaultEnabled: false }, { id: "reg_lu", value: "countryLU", paramName: "cr", textKey: "option_text_region_lu", defaultEnabled: false }, { id: "reg_nl", value: "countryNL", paramName: "cr", textKey: "option_text_region_nl", defaultEnabled: false }, { id: "reg_no", value: "countryNO", paramName: "cr", textKey: "option_text_region_no", defaultEnabled: false }, { id: "reg_pl", value: "countryPL", paramName: "cr", textKey: "option_text_region_pl", defaultEnabled: false }, { id: "reg_pt", value: "countryPT", paramName: "cr", textKey: "option_text_region_pt", defaultEnabled: false }, { id: "reg_ro", value: "countryRO", paramName: "cr", textKey: "option_text_region_ro", defaultEnabled: false }, { id: "reg_ru", value: "countryRU", paramName: "cr", textKey: "option_text_region_ru", defaultEnabled: false }, { id: "reg_rs", value: "countryRS", paramName: "cr", textKey: "option_text_region_rs", defaultEnabled: false }, { id: "reg_sk", value: "countrySK", paramName: "cr", textKey: "option_text_region_sk", defaultEnabled: false }, { id: "reg_si", value: "countrySI", paramName: "cr", textKey: "option_text_region_si", defaultEnabled: false }, { id: "reg_se", value: "countrySE", paramName: "cr", textKey: "option_text_region_se", defaultEnabled: false }, { id: "reg_ch", value: "countryCH", paramName: "cr", textKey: "option_text_region_ch", defaultEnabled: false }, { id: "reg_tr", value: "countryTR", paramName: "cr", textKey: "option_text_region_tr", defaultEnabled: false }, { id: "reg_ua", value: "countryUA", paramName: "cr", textKey: "option_text_region_ua", defaultEnabled: false }, { id: "reg_jp", value: "countryJP", paramName: "cr", textKey: "option_text_region_jp", defaultEnabled: true }, { id: "reg_kr", value: "countryKR", paramName: "cr", textKey: "option_text_region_kr", defaultEnabled: true }, { id: "reg_tw", value: "countryTW", paramName: "cr", textKey: "option_text_region_tw", defaultEnabled: true }, { id: "reg_cn", value: "countryCN", paramName: "cr", textKey: "option_text_region_cn", defaultEnabled: false }, { id: "reg_hk", value: "countryHK", paramName: "cr", textKey: "option_text_region_hk", defaultEnabled: false }, { id: "reg_in", value: "countryIN", paramName: "cr", textKey: "option_text_region_in", defaultEnabled: false }, { id: "reg_id", value: "countryID", paramName: "cr", textKey: "option_text_region_id", defaultEnabled: false }, { id: "reg_il", value: "countryIL", paramName: "cr", textKey: "option_text_region_il", defaultEnabled: false }, { id: "reg_my", value: "countryMY", paramName: "cr", textKey: "option_text_region_my", defaultEnabled: false }, { id: "reg_ph", value: "countryPH", paramName: "cr", textKey: "option_text_region_ph", defaultEnabled: false }, { id: "reg_sa", value: "countrySA", paramName: "cr", textKey: "option_text_region_sa", defaultEnabled: false }, { id: "reg_sg", value: "countrySG", paramName: "cr", textKey: "option_text_region_sg", defaultEnabled: false }, { id: "reg_th", value: "countryTH", paramName: "cr", textKey: "option_text_region_th", defaultEnabled: false }, { id: "reg_ae", value: "countryAE", paramName: "cr", textKey: "option_text_region_ae", defaultEnabled: false }, { id: "reg_vn", value: "countryVN", paramName: "cr", textKey: "option_text_region_vn", defaultEnabled: false }, { id: "reg_au", value: "countryAU", paramName: "cr", textKey: "option_text_region_au", defaultEnabled: true }, { id: "reg_nz", value: "countryNZ", paramName: "cr", textKey: "option_text_region_nz", defaultEnabled: true }, { id: "reg_eg", value: "countryEG", paramName: "cr", textKey: "option_text_region_eg", defaultEnabled: false }, { id: "reg_ng", value: "countryNG", paramName: "cr", textKey: "option_text_region_ng", defaultEnabled: false }, { id: "reg_za", value: "countryZA", paramName: "cr", textKey: "option_text_region_za", defaultEnabled: false } ], site: [ { id: "site_any", value: "", type: "site_clear", textKey: "option_text_site_any", defaultEnabled: true } ] }; // --- 5. Default Settings --- function _generateDefaults(catKey) { const defs = GITE_OPTION_DEFINITIONS[catKey]; if (catKey === 'color') return [...defs.prefixOptions, ...defs.paletteColors].map(d => ({ ...d, isEnabled: true })); return (defs || []).map(d => ({ ...d, isEnabled: d.defaultEnabled !== false })); } const GITE_DEFAULT_SETTINGS = { general: { selectedLanguage: "auto", showSettingsButtonOnToolbar: true, showResultStats: true, showAdvancedSearchButton: true, themePreference: "auto", toolbarFontSize: "14.0", toolbarLineHeight: "1.5", toolbarButtonStyle: "text", showRegionFlags: true }, filters: { size: { enabled: true, useTwoColumnLayout: false, options: _generateDefaults('size') }, exactSize: { enabled: true, useTwoColumnLayout: false, showInputsInMenu: true, predefinedOptions: _generateDefaults('exactSize'), userDefinedOptions: [] }, aspectRatio: { enabled: true, options: _generateDefaults('aspectRatio') }, color: { enabled: true, options: _generateDefaults('color') }, type: { enabled: true, options: _generateDefaults('type') }, time: { enabled: true, useTwoColumnLayout: false, showEmbeddedDatePicker: false, options: _generateDefaults('time') }, usageRights: { enabled: true, options: _generateDefaults('usageRights') }, fileType: { enabled: true, options: _generateDefaults('fileType') }, region: { enabled: true, useMultiColumnLayout: false, options: _generateDefaults('region') }, site: { enabled: true, useMultiColumnLayout: false, showFavicons: true, userDefinedOptions: [ // --- Enabled by Default --- { id: "site_wiki", label: "Wikipedia", value: "wikipedia.org", isEnabled: true, isCustom: true, type: 'site_filter' }, { id: "site_reddit", label: "Reddit", value: "reddit.com", isEnabled: true, isCustom: true, type: 'site_filter' }, { id: "site_commons", label: "Wikimedia Commons", value: "commons.wikimedia.org", isEnabled: true, isCustom: true, type: 'site_filter' }, { id: "site_flickr", label: "Flickr", value: "flickr.com", isEnabled: true, isCustom: true, type: 'site_filter' }, { id: "site_artstation", label: "ArtStation", value: "artstation.com", isEnabled: true, isCustom: true, type: 'site_filter' }, { id: "site_behance", label: "Behance", value: "behance.net", isEnabled: true, isCustom: true, type: 'site_filter' }, { id: "site_unsplash", label: "Unsplash", value: "unsplash.com", isEnabled: true, isCustom: true, type: 'site_filter' }, { id: "site_pexels", label: "Pexels", value: "pexels.com", isEnabled: true, isCustom: true, type: 'site_filter' }, { id: "site_twitter", label: "X (Twitter)", value: "twitter.com OR x.com", isEnabled: true, isCustom: true, type: 'site_filter' }, { id: "site_500px", label: "500px", value: "500px.com", isEnabled: true, isCustom: true, type: 'site_filter' }, { id: "site_archive", label: "Internet Archive", value: "archive.org", isEnabled: true, isCustom: true, type: 'site_filter' }, { id: "site_archive_today", label: "archive.today", value: "archive.ph OR archive.today OR archive.is", isEnabled: true, isCustom: true, type: 'site_filter' }, { id: "site_cara", label: "Cara", value: "cara.app", isEnabled: true, isCustom: true, type: 'site_filter' }, // --- Disabled by Default (Social / Video) --- { id: "site_threads", label: "Threads", value: "threads.net", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_instagram", label: "Instagram", value: "instagram.com", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_facebook", label: "Facebook", value: "facebook.com", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_bluesky", label: "Bluesky", value: "bsky.app", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_mastodon", label: "Mastodon (social)", value: "mastodon.social", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_tumblr", label: "Tumblr", value: "tumblr.com", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_pinterest", label: "Pinterest", value: "pinterest.com", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_pixiv", label: "Pixiv", value: "pixiv.net", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_deviantart", label: "DeviantArt", value: "deviantart.com", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_dribbble", label: "Dribbble", value: "dribbble.com", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_pixabay", label: "Pixabay", value: "pixabay.com", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_giphy", label: "GIPHY", value: "giphy.com", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_youtube", label: "YouTube", value: "youtube.com", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_vimeo", label: "Vimeo", value: "vimeo.com", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_linkedin", label: "LinkedIn", value: "linkedin.com", isEnabled: false, isCustom: true, type: 'site_filter' }, // --- Disabled by Default (Museums / Libraries / Academic) --- { id: "site_europeana", label: "Europeana", value: "europeana.eu", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_dpla", label: "DPLA", value: "dp.la", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_loc", label: "Library of Congress", value: "loc.gov", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_si", label: "Smithsonian", value: "si.edu", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_britishmuseum", label: "British Museum", value: "britishmuseum.org", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_met", label: "The Met", value: "metmuseum.org", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_rijks", label: "Rijksmuseum", value: "rijksmuseum.nl", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_tate", label: "Tate", value: "tate.org.uk", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_nga", label: "National Gallery of Art", value: "nga.gov", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_artic", label: "Art Institute of Chicago", value: "artic.edu", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_cleveland", label: "Cleveland Museum of Art", value: "clevelandart.org", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_getty", label: "Getty", value: "getty.edu", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_nypl", label: "NYPL Digital Collections", value: "digitalcollections.nypl.org", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_wellcome", label: "Wellcome Collection", value: "wellcomecollection.org", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_bodleian", label: "Bodleian Libraries", value: "bodleian.ox.ac.uk", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_biodiversity", label: "Biodiversity Heritage Library", value: "biodiversitylibrary.org", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_ndl", label: "National Diet Library", value: "dl.ndl.go.jp", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_archives_gov", label: "National Archives (US)", value: "archives.gov", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_oldbook", label: "Old Book Illustrations", value: "oldbookillustrations.com", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_rumsey", label: "David Rumsey Map Collection", value: "davidrumsey.com", isEnabled: false, isCustom: true, type: 'site_filter' }, // --- Other --- { id: "site_nasa", label: "NASA", value: "nasa.gov", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_natgeo", label: "National Geographic", value: "nationalgeographic.com", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_magnum", label: "Magnum Photos", value: "magnumphotos.com", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_ign", label: "IGN", value: "ign.com", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_vogue", label: "Vogue", value: "vogue.com", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_archdigest", label: "Architectural Digest", value: "architecturaldigest.com", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_nature", label: "Nature Portfolio", value: "nature.com", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_cgmeetup", label: "CGMeetup", value: "cgmeetup.com", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_artfol", label: "Artfol", value: "artfol.me", isEnabled: false, isCustom: true, type: 'site_filter' }, // --- Stock Photos / Design (Disabled by Default) --- { id: "site_getty", label: "Getty Images", value: "gettyimages.com", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_shutterstock", label: "Shutterstock", value: "shutterstock.com", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_adobe_stock", label: "Adobe Stock", value: "stock.adobe.com", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_alamy", label: "Alamy", value: "alamy.com", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_dreamstime", label: "Dreamstime", value: "dreamstime.com", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_canva", label: "Canva (Content)", value: "canva.com", isEnabled: false, isCustom: true, type: 'site_filter' }, // --- Community / Forums (Disabled by Default) --- { id: "site_lemmy", label: "Lemmy (world)", value: "lemmy.world", isEnabled: false, isCustom: true, type: 'site_filter' }, { id: "site_steam", label: "Steam Community", value: "steamcommunity.com", isEnabled: false, isCustom: true, type: 'site_filter' } ] } } }; // --- 6. Global State & Utilities --- let giteSettings = {}; let giteSettingsPanelElement = null; let currentGiteSettingsForPanel = {}; let isSettingsPanelOpen = false; let currentlyOpenMenuId = null; let activeDatePicker = null; let resultStatsObserver = null; let menuObserver = null; let CURRENT_LANGUAGE = 'en'; function log(...args) { if (DEBUG_MODE) console.log('[GITE]', ...args); } function error(...args) { console.error('[GITE]', ...args); } function warn(...args) { console.warn('[GITE]', ...args); } function getLocalizedString(key, lang = CURRENT_LANGUAGE, fallbackText = null) { const effectiveLang = GITE_I18N_STRINGS[lang] ? lang : 'en'; const text = GITE_I18N_STRINGS[effectiveLang]?.[key] || GITE_I18N_STRINGS['en']?.[key]; return text !== undefined ? text : (fallbackText || key); } function initializeCurrentLanguage() { let preferred = giteSettings?.general?.selectedLanguage || 'auto'; if (preferred === 'auto') { const browserLang = (navigator.language || navigator.userLanguage || 'en').toLowerCase(); if (browserLang.startsWith('zh-tw') || browserLang.startsWith('zh-hk') || browserLang.startsWith('zh-hant')) CURRENT_LANGUAGE = 'zh-TW'; else if (browserLang.startsWith('ja')) CURRENT_LANGUAGE = 'ja'; else CURRENT_LANGUAGE = 'en'; } else { CURRENT_LANGUAGE = GITE_I18N_STRINGS[preferred] ? preferred : 'en'; } } function updateAllLocalizableElements(parent = document) { if (!parent || typeof parent.querySelectorAll !== 'function') return; parent.querySelectorAll(`[${GITE_LANG_KEY_ATTR}]`).forEach(elem => { const key = elem.getAttribute(GITE_LANG_KEY_ATTR); const targetAttr = elem.getAttribute('data-gite-lang-target-attr'); const text = getLocalizedString(key); if (targetAttr) { elem.setAttribute(targetAttr, text); } else if (elem.tagName === 'INPUT' && ['button', 'submit', 'reset'].includes(elem.type)) { elem.value = text; } else if (elem.tagName === 'INPUT' && elem.hasAttribute('placeholder')) { elem.setAttribute('placeholder', text); } else { let hasChildren = elem.children.length > 0; if (!hasChildren) { elem.textContent = text; } else { for (const node of elem.childNodes) { if (node.nodeType === Node.TEXT_NODE && node.nodeValue.trim() !== '') { node.nodeValue = ' ' + text; } } } } }); } function getFlagEmoji(countryCode) { if (!countryCode || countryCode.length !== 2) return ''; const codePoints = countryCode.toUpperCase().split('').map(char => 0x1F1E6 + (char.charCodeAt(0) - 65)); return String.fromCodePoint(...codePoints); } function injectStyles() { if (document.getElementById('gite-styles')) return; const style = document.createElement('style'); style.id = 'gite-styles'; style.type = 'text/css'; style.textContent = GITE_STYLES; if (document.head) document.head.appendChild(style); } function detectAndApplyThemeClass() { const htmlElement = document.documentElement; let pref = giteSettings?.general?.themePreference || 'auto'; let isDark = false; if (pref === 'auto') { const googleTheme = document.documentElement.getAttribute('data-theme'); if (googleTheme === 'dark' || googleTheme === 'light') { isDark = (googleTheme === 'dark'); } else { // Fallback: check computed style const bgColor = window.getComputedStyle(document.body).backgroundColor; const rgb = bgColor.match(/\d+/g); if (rgb) { isDark = (parseInt(rgb[0]) < 128 && parseInt(rgb[1]) < 128 && parseInt(rgb[2]) < 128); } else { isDark = window.matchMedia('(prefers-color-scheme: dark)').matches; } } } else { isDark = (pref === 'dark'); } htmlElement.classList.toggle('gite-detected-dark-theme', isDark); } // Live Theme Listener window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => { if (giteSettings?.general?.themePreference === 'auto') detectAndApplyThemeClass(); }); function showToast(messageKey, isLiteral = false) { let container = document.querySelector('.gite-toast-container'); if (!container) { container = el('div', { className: 'gite-toast-container' }); document.body.appendChild(container); } const msg = isLiteral ? messageKey : getLocalizedString(messageKey); const toast = el('div', { className: 'gite-toast' }); toast.innerHTML = `${GITE_ICONS.check} ${msg}`; container.appendChild(toast); requestAnimationFrame(() => toast.classList.add('show')); setTimeout(() => { toast.classList.remove('show'); setTimeout(() => toast.remove(), 300); }, 2500); } // --- 7. URL Construction Logic (Core) --- function buildNewUrl(paramName, paramValue, optionCtx, clearAll = false) { const url = new URL(window.location.href); const params = url.searchParams; let currentQ = params.get('q') || ""; let currentTbs = params.get('tbs') || ""; // 1. Clear All Logic if (clearAll) { params.delete('imgsz'); params.delete('imgar'); params.delete('as_filetype'); params.delete('cr'); params.delete('imgc'); // Clean Q: Remove syntax but keep keywords currentQ = currentQ.replace(/(?:\s|^)imagesize:\d+x\d+/gi, '') .replace(/(?:\s|^)site:[^\s]+(?:\s+OR\s+site:[^\s]+)*/gi, '') // Remove site:A OR site:B .replace(/\s+/g, ' ').trim(); if (currentQ) params.set('q', currentQ); // Clean TBS: Remove specific keys const tbsKeysToRemove = ['itp', 'ic', 'isc', 'sur', 'qdr', 'cdr', 'cd_min', 'cd_max', 'isz', 'islt', 'isilu']; const parts = currentTbs.split(',') .map(p => p.trim()).filter(p => p !== "") .filter(p => !tbsKeysToRemove.some(k => p.startsWith(k + ':') || p === k)); if (parts.length > 0) params.set('tbs', parts.join(',')); else params.delete('tbs'); return url.toString(); } const cat = optionCtx.categoryKey; // 2. Handle Q modifications (Update-in-Place Strategy) let newQ = currentQ; if (cat === 'exactSize' || (optionCtx.type && optionCtx.type.startsWith('imagesize'))) { newQ = newQ.replace(/(?:\s|^)imagesize:\d+x\d+/gi, '').trim(); if (paramValue) newQ += ` imagesize:${paramValue}`; // [Mutex] Remove Standard Size & Aspect Ratio params.delete('imgsz'); params.delete('imgar'); currentTbs = currentTbs.split(',').filter(p => !p.startsWith('isz:') && !p.startsWith('islt:')).join(','); } else if (cat === 'site' || optionCtx.type === 'site_filter') { // Remove existing site params (single or multi) newQ = newQ.replace(/(?:\s|^)site:[^\s]+(?:\s+OR\s+site:[^\s]+)*/gi, '').trim(); if (paramValue) { // Handle "OR" logic: "twitter.com OR x.com" -> "site:twitter.com OR site:x.com" if (paramValue.includes(' OR ')) { const sites = paramValue.split(' OR ').map(s => s.trim()).filter(s => s); const siteStr = sites.map(s => `site:${s}`).join(' OR '); newQ += ` ${siteStr}`; } else { newQ += ` site:${paramValue}`; } } } else if (cat === 'size') { // [Mutex] Remove Exact Size newQ = newQ.replace(/(?:\s|^)imagesize:\d+x\d+/gi, '').trim(); } else if (cat === 'aspectRatio') { // [Mutex] Remove Exact Size newQ = newQ.replace(/(?:\s|^)imagesize:\d+x\d+/gi, '').trim(); } if (newQ !== currentQ) params.set('q', newQ.replace(/\s+/g, ' ').trim()); // 3. Handle TBS modifications (Token Analysis) let tbsParts = currentTbs.split(',').map(p => p.trim()).filter(p => p !== ""); const updateTbs = (prefix, val) => { tbsParts = tbsParts.filter(p => !p.startsWith(prefix + ':') && p !== prefix); if (val) tbsParts.push(val.includes(':') ? val : `${prefix}:${val}`); }; if (cat === 'color') { params.delete('imgc'); tbsParts = tbsParts.filter(p => !p.startsWith('ic:') && !p.startsWith('isc:')); if (optionCtx.tbsValue) { optionCtx.tbsValue.split(',').forEach(p => tbsParts.push(p)); } } else if (cat === 'time') { tbsParts = tbsParts.filter(p => !p.startsWith('qdr:') && !p.startsWith('cdr:') && !p.startsWith('cd_min:') && !p.startsWith('cd_max:')); if (optionCtx.tbsValue) tbsParts.push(optionCtx.tbsValue); } else if (cat === 'type') { updateTbs('itp', optionCtx.tbsValue); } else if (cat === 'usageRights') { updateTbs('sur', optionCtx.tbsValue); } else if (cat === 'size') { // Hybrid Strategy: L, M, I -> tbs:isz; MP, SVGA -> imgsz params.delete('imgsz'); tbsParts = tbsParts.filter(p => !p.startsWith('isz:') && !p.startsWith('islt:') && !p.startsWith('isilu:')); if (['l', 'm', 'i'].includes(paramValue)) { tbsParts.push(`isz:${paramValue}`); } else if (paramValue) { params.set('imgsz', paramValue); } } const finalTbs = tbsParts.filter(Boolean).join(','); if (finalTbs) params.set('tbs', finalTbs); else params.delete('tbs'); // 4. Simple Params if (cat === 'region') { if (paramValue) params.set('cr', paramValue); else params.delete('cr'); } else if (cat === 'fileType') { if (paramValue) params.set('as_filetype', paramValue); else params.delete('as_filetype'); } else if (cat === 'aspectRatio') { if (paramValue) params.set('imgar', paramValue); else params.delete('imgar'); } // 5. Final Output Decoding let urlString = url.toString(); return urlString.replace(/%3A/g, ':').replace(/%2C/g, ',').replace(/%2F/g, '/'); } // --- 8. Menu Item Creation --- function createMenuItem(opt, catKey) { if (opt.type === 'separator') return el('div', { className: 'gite-menu-separator' }); // Custom Date Trigger (Popup) if (opt.type === 'custom_date_trigger') { const currentTbs = new URL(window.location.href).searchParams.get('tbs') || ""; const isActive = currentTbs.includes('cdr:1'); const item = el('div', { className: `gite-menu-item ${isActive ? 'selected' : ''}`, role: 'button', tabIndex: -1, style: "cursor: pointer;" }); item.append( document.createTextNode(getLocalizedString(opt.textKey)), el('div', { className: 'gite-check-icon', innerHTML: GITE_ICONS.check }) ); item.onclick = (e) => { e.stopPropagation(); openDatePicker(); }; item.addEventListener('keydown', (e) => { if(e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openDatePicker(); }}); return item; } const label = opt.customText || (opt.label) || getLocalizedString(opt.textKey, CURRENT_LANGUAGE, opt.id); let pName = opt.paramName || 'q'; if (['color','type','time','usageRights'].includes(catKey)) pName = 'tbs'; if (catKey === 'fileType') pName = 'as_filetype'; if (catKey === 'region') pName = 'cr'; if (catKey === 'aspectRatio') pName = 'imgar'; if (catKey === 'size') pName = 'imgsz'; const targetUrl = buildNewUrl(pName, opt.value || opt.tbsValue, { ...opt, categoryKey: catKey }); // Active Check Logic let isSelected = false; const url = new URL(window.location.href); const q = url.searchParams.get('q') || ""; const tbs = url.searchParams.get('tbs') || ""; const tbsTokens = tbs.split(','); const val = opt.tbsValue !== undefined ? opt.tbsValue : opt.value; if (catKey === 'exactSize') { if (val && q.match(new RegExp(`imagesize:${val}(?:\\s|$)`))) isSelected = true; if (opt.type === 'imagesize_clear' && !q.includes('imagesize:')) isSelected = true; } else if (catKey === 'site') { if (val) { // Check for multi-site OR if (val.includes(' OR ')) { const sites = val.split(' OR ').map(s => s.trim()).filter(s => s); const allPresent = sites.every(s => q.toLowerCase().includes(`site:${s.toLowerCase()}`)); if (allPresent) isSelected = true; } else { if (q.match(new RegExp(`site:${val}(?:\\s|$)`, 'i'))) isSelected = true; } } if (opt.type === 'site_clear' && !q.toLowerCase().includes('site:')) isSelected = true; } else if (catKey === 'color') { if (opt.type === 'palette') { if (opt.tbsValue.split(',').every(token => tbsTokens.includes(token))) isSelected = true; } else { if (val === "") { if (!tbsTokens.some(t => t.startsWith('ic:') || t.startsWith('isc:'))) isSelected = true; } else { if (tbsTokens.includes(val)) isSelected = true; } } } else if (catKey === 'region') { const cr = url.searchParams.get('cr'); if (opt.value === cr || (opt.value === "" && !cr)) isSelected = true; } else if (catKey === 'size') { const imgsz = url.searchParams.get('imgsz'); const isAny = opt.value === ""; if (isAny) { if (!imgsz && !tbsTokens.some(t => t.startsWith('isz:') || t.startsWith('islt:'))) isSelected = true; } else if (['l', 'm', 'i'].includes(opt.value)) { if (tbsTokens.includes(`isz:${opt.value}`) || imgsz === opt.value) isSelected = true; } else { if (imgsz === opt.value || tbsTokens.includes(`islt:${opt.value}`)) isSelected = true; } } else if (catKey === 'time') { if (opt.tbsValue === "" && !tbsTokens.some(t => t.startsWith('qdr:') || t.startsWith('cdr:'))) isSelected = true; else if (tbsTokens.includes(opt.tbsValue)) isSelected = true; } else { if (val === "" || val === undefined) { const hasParam = url.searchParams.has(pName) || (pName==='tbs' && tbsTokens.some(t => t.startsWith(catKey==='type'?'itp:':'sur:'))); if (!hasParam) isSelected = true; } else { if (pName === 'tbs' && tbsTokens.includes(val)) isSelected = true; if (pName !== 'tbs' && url.searchParams.get(pName) === val) isSelected = true; } } // Palette Item (Link style) if (catKey === 'color' && opt.type === 'palette') { const swatch = el('a', { className: `gite-swatch ${isSelected ? 'selected' : ''}`, style: `background:${opt.hex}`, title: label, href: targetUrl, tabIndex: -1 }); swatch.addEventListener('click', (e) => { if (e.button !== 0 || e.ctrlKey || e.metaKey || e.shiftKey || e.altKey) return; e.preventDefault(); window.location.href = targetUrl; }); return swatch; } // Standard Menu Item const item = el('a', { className: `gite-menu-item ${isSelected ? 'selected' : ''}`, href: targetUrl, tabIndex: -1, dataset: { id: opt.id } }); if (catKey === 'site' && opt.value) item.title = opt.value.replace(/\s+OR\s+/g, ', '); const contentDiv = el('div', { style: 'display:flex; align-items:center;' }); if (catKey === 'region' && giteSettings.general.showRegionFlags && opt.value) { contentDiv.appendChild(document.createTextNode(getFlagEmoji(opt.value.replace('country', '')) + ' ')); } if (catKey === 'site' && giteSettings.filters.site.showFavicons && opt.value) { let isMulti = opt.value.includes(' OR '); if (isMulti) { const iconDiv = el('div', { className: 'gite-tb-icon', innerHTML: GITE_ICONS.globe, style: "margin-right:8px; width:16px; height:16px; color: var(--gite-text-secondary);" }); contentDiv.appendChild(iconDiv); } else { const favicon = el('img', { className: 'gite-favicon', src: `https://www.google.com/s2/favicons?sz=32&domain_url=${opt.value}`, style: "margin-right:8px; width:16px; height:16px; vertical-align:middle; border-radius:2px;" }); favicon.onerror = function() { this.style.display = 'none'; }; contentDiv.appendChild(favicon); } } contentDiv.appendChild(document.createTextNode(label)); item.append(contentDiv, el('div', { className: 'gite-check-icon', innerHTML: GITE_ICONS.check })); item.addEventListener('click', (e) => { if (e.button !== 0 || e.ctrlKey || e.metaKey || e.shiftKey || e.altKey) return; e.preventDefault(); window.location.href = targetUrl; }); return item; } // --- 9. Toolbar UI Building --- function addFilterCategoryMenu(container, catKey, titleKey, paramName, idPrefix) { if (giteSettings.filters[catKey] && giteSettings.filters[catKey].enabled === false) return; let title = getLocalizedString(titleKey); const iconSvg = GITE_ICONS[catKey] || GITE_ICONS.gear; const globalBtnStyle = giteSettings.general.toolbarButtonStyle || 'text'; let options = []; if (catKey === 'exactSize') options = [...(giteSettings.filters.exactSize.predefinedOptions || [])]; else if (catKey === 'site') { const base = GITE_DEFAULT_SETTINGS.filters.site.options || [{ id: "site_any", value: "", type: "site_clear", textKey: "option_text_site_any" }]; options = [...base]; } else options = giteSettings.filters[catKey].options || []; // Check Active State let activeLabel = null; let isActive = false; const url = new URL(window.location.href); const tbsTokens = (url.searchParams.get('tbs')||"").split(','); const q = url.searchParams.get('q') || ""; // Manual Checks for Label Display if (catKey === 'exactSize') { const match = q.match(/(?:\s|^)imagesize:(\d+x\d+)/); if (match) { isActive = true; activeLabel = match[1]; } } if (catKey === 'site') { const siteMatches = q.match(/(?:\s|^)site:([^\s]+)/g); if (siteMatches && siteMatches.length > 0) { isActive = true; activeLabel = getLocalizedString('text_site_search_active_prefix') + (siteMatches.length === 1 ? siteMatches[0].replace('site:', '').trim() : siteMatches.map(s => s.replace('site:', '').trim()).join(', ')); } } let allOptionsToCheck = [...options]; if (catKey === 'exactSize' && giteSettings.filters.exactSize.userDefinedOptions) allOptionsToCheck = [...giteSettings.filters.exactSize.userDefinedOptions, ...options]; if (catKey === 'site' && giteSettings.filters.site.userDefinedOptions) allOptionsToCheck = [...giteSettings.filters.site.userDefinedOptions, ...options]; for (const opt of allOptionsToCheck) { if (opt.type === 'separator') continue; let matched = false; const val = opt.tbsValue !== undefined ? opt.tbsValue : opt.value; if (catKey === 'exactSize' && val && q.includes(`imagesize:${val}`)) matched = true; else if (catKey === 'site' && val) { if (val.includes(' OR ')) { if (val.split(' OR ').every(s => q.toLowerCase().includes(`site:${s.trim().toLowerCase()}`))) matched = true; } else if (q.toLowerCase().includes(`site:${val}`)) matched = true; } else if (catKey === 'size') { const imgsz = url.searchParams.get('imgsz'); if (['l','m','i'].includes(val) && (tbsTokens.includes(`isz:${val}`) || imgsz === val)) matched = true; else if (val && (imgsz === val || tbsTokens.includes(`islt:${val}`))) matched = true; } else if (catKey === 'time') { if (tbsTokens.includes(opt.tbsValue)) matched = true; } else if (catKey === 'region') { if (opt.value && url.searchParams.get('cr') === opt.value) matched = true; } else if (val && val !== "") { if (opt.paramName==='tbs' || ['color','type','usageRights'].includes(catKey)) { if (tbsTokens.includes(val) || (catKey==='color' && opt.type==='palette' && opt.tbsValue.split(',').every(t=>tbsTokens.includes(t)))) matched = true; } else if (url.searchParams.get(opt.paramName || paramName) === val) matched = true; } if(opt.type === 'custom_date_trigger' && tbsTokens.some(t=>t.startsWith('cdr:1'))) { matched = true; const minToken = tbsTokens.find(t=>t.startsWith('cd_min:')); const maxToken = tbsTokens.find(t=>t.startsWith('cd_max:')); if(minToken && maxToken) { const min = minToken.replace('cd_min:', ''); const max = maxToken.replace('cd_max:', ''); activeLabel = `${min} - ${max}`; } } if (matched) { const isAny = (opt.value === "" && !opt.tbsValue) || opt.type?.endsWith('_clear') || opt.textKey?.includes('_any'); if (!isAny) { isActive = true; if (!activeLabel || catKey !== 'time') { activeLabel = opt.customText || opt.label || getLocalizedString(opt.textKey, CURRENT_LANGUAGE, opt.id); } if(catKey === 'site') activeLabel = getLocalizedString('text_site_search_active_prefix') + activeLabel; } else { isActive = false; activeLabel = title; } break; } } const displayTitle = isActive && activeLabel ? activeLabel : title; const btn = el('div', { id: `${idPrefix}-btn`, className: `gite-tb-btn gite-ripple style-${globalBtnStyle} ${isActive ? 'active' : ''}`, tabIndex: 0, role: 'button', 'aria-haspopup': 'true', 'aria-expanded': 'false' }); if (globalBtnStyle === 'icon') btn.title = displayTitle; btn.append( el('div', { className: 'gite-tb-icon', innerHTML: iconSvg }), el('span', { className: 'gite-btn-text', textContent: displayTitle }), el('div', { className: 'gite-tb-arrow' }) ); const menu = el('div', { id: `${idPrefix}-menu`, className: 'gite-menu-dropdown', role: 'menu' }); // --- Layout Building Logic --- const catSettings = giteSettings.filters[catKey]; const useTwoCol = catSettings.useTwoColumnLayout; const useMultiCol = catSettings.useMultiColumnLayout; const showEmbeddedDate = catKey === 'time' && catSettings.showEmbeddedDatePicker; const appendOptionsTo = (list, container) => { list.forEach(opt => { if(opt.isEnabled !== false) container.appendChild(createMenuItem(opt, catKey)); }); }; if (catKey === 'color') { const paletteParams = options.filter(o => o.type === 'palette'); const otherParams = options.filter(o => o.type !== 'palette'); appendOptionsTo(otherParams, menu); if (paletteParams.length > 0) { const grid = el('div', { className: 'gite-palette-grid' }); paletteParams.forEach(p => { if (p.isEnabled !== false) grid.appendChild(createMenuItem(p, catKey)); }); menu.appendChild(grid); } } else if (useMultiCol && (catKey === 'region' || catKey === 'site')) { const masonryContainer = el('div', { className: 'gite-menu-masonry' }); let topItems = []; let masonryItems = []; if (catKey === 'site') { const anyOpt = options.find(o => o.id === 'site_any'); const userOpts = catSettings.userDefinedOptions || []; const otherOpts = options.filter(o => o.id !== 'site_any'); if (anyOpt) topItems.push(anyOpt); masonryItems = [...userOpts, ...otherOpts]; } else { const anyOpt = options.find(o => o.id === 'reg_any'); const otherOpts = options.filter(o => o.id !== 'reg_any'); if (anyOpt) topItems.push(anyOpt); masonryItems = otherOpts; } if (topItems.length > 0) { appendOptionsTo(topItems, menu); menu.appendChild(el('div', { className: 'gite-menu-separator' })); } appendOptionsTo(masonryItems, masonryContainer); menu.appendChild(masonryContainer); } else if (useTwoCol || showEmbeddedDate) { const rowContainer = el('div', { className: 'gite-menu-row-container' }); const leftCol = el('div', { className: 'gite-menu-col' }); const rightCol = el('div', { className: 'gite-menu-col' }); if (catKey === 'size') { const stdOpts = options.filter(o => !o.id.includes('mp') && o.type !== 'separator'); const mpOpts = options.filter(o => o.id.includes('mp')); appendOptionsTo(stdOpts, leftCol); appendOptionsTo(mpOpts, rightCol); rowContainer.append(leftCol, rightCol); } else if (catKey === 'exactSize') { appendOptionsTo(options, leftCol); if (catSettings.showInputsInMenu) { const inputRow = el('div', { className: 'gite-menu-input-row', style: 'border:none;' }); let activeW = '', activeH = ''; if (isActive && activeLabel && activeLabel.match(/^\d+x\d+$/)) { const dims = activeLabel.split('x'); activeW = dims[0]; activeH = dims[1]; } const wInput = el('input', { type: 'number', className: 'gite-menu-input', placeholder: 'W', min: 1, value: activeW, style: 'width:48px' }); const hInput = el('input', { type: 'number', className: 'gite-menu-input', placeholder: 'H', min: 1, value: activeH, style: 'width:48px' }); const goBtn = el('button', { className: 'gite-menu-btn-icon', innerHTML: GITE_ICONS.check, title: getLocalizedString('btn_apply') }); const doCustomSize = () => { if(wInput.value && hInput.value) window.location.href = buildNewUrl('q', `${wInput.value}x${hInput.value}`, { categoryKey: 'exactSize', type: 'imagesize' }); }; goBtn.onclick = (e) => { e.stopPropagation(); doCustomSize(); }; inputRow.append(wInput, document.createTextNode('×'), hInput, goBtn); rightCol.appendChild(inputRow); rightCol.appendChild(el('div', { className: 'gite-menu-separator' })); } const userOpts = catSettings.userDefinedOptions || []; appendOptionsTo(userOpts, rightCol); rowContainer.append(leftCol, rightCol); } else if (catKey === 'time') { // Long: Week, Month, Year const longKeys = ['time_past_week', 'time_past_2w', 'time_past_3w', 'time_past_month', 'time_past_2m', 'time_past_3m', 'time_past_4m', 'time_past_5m', 'time_past_6m', 'time_past_7m', 'time_past_8m', 'time_past_9m', 'time_past_10m', 'time_past_11m', 'time_past_year', 'time_past_2y', 'time_past_3y', 'time_past_4y', 'time_past_5y', 'time_past_6y', 'time_past_7y', 'time_past_8y', 'time_past_9y', 'time_past_10y']; // Left Col: Any, Min, Hour, Days (anything NOT in longKeys, and not separator/custom) const leftOpts = options.filter(o => o.type !== 'separator' && o.type !== 'custom_date_trigger' && !longKeys.includes(o.id)); // Right Col: Weeks, Months, Years const rightOpts = options.filter(o => longKeys.includes(o.id)); const customTrigger = options.find(o => o.type === 'custom_date_trigger'); if (useTwoCol) { appendOptionsTo(leftOpts, leftCol); appendOptionsTo(rightOpts, rightCol); if (!showEmbeddedDate && customTrigger && customTrigger.isEnabled !== false) { rightCol.appendChild(el('div', { className: 'gite-menu-separator' })); rightCol.appendChild(createMenuItem(customTrigger, catKey)); } rowContainer.append(leftCol, rightCol); } else { appendOptionsTo(options.filter(o => o.type !== 'custom_date_trigger'), menu); if (!showEmbeddedDate && customTrigger && customTrigger.isEnabled !== false) { menu.appendChild(createMenuItem(customTrigger, catKey)); } } if (showEmbeddedDate) { const dateCol = el('div', { className: 'gite-embedded-picker' }); dateCol.onclick = (e) => e.stopPropagation(); const header = el('div', { className: 'gite-embedded-header', textContent: getLocalizedString('option_text_time_custom_range') }); let defS = '', defE = ''; const tbs = new URL(window.location.href).searchParams.get('tbs') || ""; if(tbs.includes('cdr:1')) { const min = tbs.match(/cd_min:(\d{1,2})\/(\d{1,2})\/(\d{4})/); const max = tbs.match(/cd_max:(\d{1,2})\/(\d{1,2})\/(\d{4})/); if(min) defS = `${min[3]}-${min[1].padStart(2,'0')}-${min[2].padStart(2,'0')}`; if(max) defE = `${max[3]}-${max[1].padStart(2,'0')}-${max[2].padStart(2,'0')}`; } const startIn = el('input', { type: 'date', className: 'gite-embedded-input', value: defS }); const endIn = el('input', { type: 'date', className: 'gite-embedded-input', value: defE }); const applyBtn = el('button', { className: 'gite-embedded-apply-btn', textContent: getLocalizedString('btn_apply') }); const validate = () => { const s = startIn.value; const e = endIn.value; if(s && e && new Date(s) > new Date(e)) { endIn.classList.add('error'); return false; } endIn.classList.remove('error'); return true; }; startIn.onchange = validate; endIn.onchange = validate; const doApply = () => { if(!validate()) return; if(!startIn.value || !endIn.value) return; const formatDate = d => { const p = d.split('-'); return `${p[1]}/${p[2]}/${p[0]}`; }; const tbsVal = `cdr:1,cd_min:${formatDate(startIn.value)},cd_max:${formatDate(endIn.value)}`; window.location.href = buildNewUrl('tbs', tbsVal, { categoryKey: 'time', tbsValue: tbsVal }); }; applyBtn.onclick = doApply; dateCol.addEventListener('keydown', (e) => { if(e.key === 'Enter') doApply(); }); dateCol.append(header, startIn, endIn, applyBtn); if (useTwoCol) rowContainer.appendChild(dateCol); else { const mixedContainer = el('div', { className: 'gite-menu-row-container' }); const optsCol = el('div', { className: 'gite-menu-col' }); while(menu.firstChild) optsCol.appendChild(menu.firstChild); mixedContainer.append(optsCol, dateCol); menu.appendChild(mixedContainer); } } } if (menu.children.length === 0) menu.appendChild(rowContainer); } else { if (catKey === 'site') { const anyOpt = options.find(o => o.id === 'site_any'); const userOpts = catSettings.userDefinedOptions || []; const otherOpts = options.filter(o => o.id !== 'site_any'); if (anyOpt && anyOpt.isEnabled !== false) menu.appendChild(createMenuItem(anyOpt, catKey)); if (userOpts.length > 0) { if(anyOpt) menu.appendChild(el('div', { className: 'gite-menu-separator' })); appendOptionsTo(userOpts, menu); } if(otherOpts.length > 0) { if(userOpts.length > 0 || anyOpt) menu.appendChild(el('div', { className: 'gite-menu-separator' })); appendOptionsTo(otherOpts, menu); } } else if (catKey === 'exactSize') { const userOpts = catSettings.userDefinedOptions || []; if (catSettings.showInputsInMenu) { const inputRow = el('div', { className: 'gite-menu-input-row' }); let activeW = '', activeH = ''; if (isActive && activeLabel && activeLabel.match(/^\d+x\d+$/)) { const dims = activeLabel.split('x'); activeW = dims[0]; activeH = dims[1]; } const wInput = el('input', { type: 'number', className: 'gite-menu-input', placeholder: 'W', min: 1, value: activeW }); const hInput = el('input', { type: 'number', className: 'gite-menu-input', placeholder: 'H', min: 1, value: activeH }); const goBtn = el('button', { className: 'gite-menu-btn-icon', innerHTML: GITE_ICONS.check, title: getLocalizedString('btn_apply') }); const doCustomSize = () => { if(wInput.value && hInput.value) window.location.href = buildNewUrl('q', `${wInput.value}x${hInput.value}`, { categoryKey: 'exactSize', type: 'imagesize' }); }; goBtn.onclick = (e) => { e.stopPropagation(); doCustomSize(); }; inputRow.append(wInput, document.createTextNode('×'), hInput, goBtn); menu.appendChild(inputRow); } if(userOpts.length > 0) { appendOptionsTo(userOpts, menu); menu.appendChild(el('div', { className: 'gite-menu-separator' })); } appendOptionsTo(options, menu); } else if (catKey === 'region') { const anyOpt = options.find(o => o.id === 'reg_any'); const otherOpts = options.filter(o => o.id !== 'reg_any'); if (anyOpt && anyOpt.isEnabled !== false) menu.appendChild(createMenuItem(anyOpt, catKey)); if (anyOpt && otherOpts.length > 0) menu.appendChild(el('div', { className: 'gite-menu-separator' })); appendOptionsTo(otherOpts, menu); } else { appendOptionsTo(options, menu); } } const toggleMenu = (shouldOpen) => { if (typeof shouldOpen === 'undefined') shouldOpen = !menu.classList.contains('show'); document.querySelectorAll('.gite-menu-dropdown').forEach(m => { if (m !== menu) m.classList.remove('show'); }); document.querySelectorAll('.gite-tb-btn').forEach(b => { if (b !== btn) { b.classList.remove('open'); b.setAttribute('aria-expanded', 'false'); } }); if (shouldOpen) { menu.classList.add('show'); btn.classList.add('open'); btn.setAttribute('aria-expanded', 'true'); currentlyOpenMenuId = menu.id; let focusTarget = menu.querySelector('input'); if (!focusTarget) focusTarget = menu.querySelector('a.gite-menu-item, a.gite-swatch'); if (focusTarget) requestAnimationFrame(() => focusTarget.focus()); } else { menu.classList.remove('show'); btn.classList.remove('open'); btn.setAttribute('aria-expanded', 'false'); currentlyOpenMenuId = null; } }; btn.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ' || e.key === 'ArrowDown') { e.preventDefault(); toggleMenu(true); } else if (['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(e.key)) { e.preventDefault(); handleToolbarNav(e, btn); } }); btn.onclick = (e) => { e.preventDefault(); e.stopPropagation(); toggleMenu(); }; menu.addEventListener('keydown', (e) => { if (e.key === 'Escape') { e.preventDefault(); toggleMenu(false); btn.focus(); } else handleMenuNav(e, menu); }); const wrapper = el('div', { className: 'gite-tb-group' }); wrapper.append(btn, menu); container.appendChild(wrapper); } // --- Keyboard Navigation Helpers --- function handleToolbarNav(e, currentBtn) { const allBtns = Array.from(document.querySelectorAll(`#${GITE_TOOLBAR_CONTAINER_ID} .gite-tb-btn`)); if (!allBtns.length) return; const currIdx = allBtns.indexOf(currentBtn); let nextIdx = currIdx; if (e.key === 'ArrowLeft') nextIdx = currIdx - 1; else if (e.key === 'ArrowRight') nextIdx = currIdx + 1; else if (e.key === 'Home') nextIdx = 0; else if (e.key === 'End') nextIdx = allBtns.length - 1; if (nextIdx >= 0 && nextIdx < allBtns.length) allBtns[nextIdx].focus(); } function handleMenuNav(e, menu) { if (e.target.tagName === 'INPUT' && (e.key === 'ArrowLeft' || e.key === 'ArrowRight')) return; if (e.target.tagName === 'INPUT' && e.target.type === 'number') return; // Select all interactive elements within the menu, regardless of layout depth const items = Array.from(menu.querySelectorAll('a.gite-menu-item, a.gite-swatch, button.gite-menu-btn-icon, button.gite-embedded-apply-btn, input:not([type="hidden"])')); if (!items.length) return; let currIdx = items.indexOf(document.activeElement); if (currIdx === -1) currIdx = 0; let nextIdx = currIdx; if (e.key === 'ArrowDown') { e.preventDefault(); nextIdx = (currIdx + 1) % items.length; } else if (e.key === 'ArrowUp') { e.preventDefault(); nextIdx = (currIdx - 1 + items.length) % items.length; } else if (e.key === 'Home') { e.preventDefault(); nextIdx = 0; } else if (e.key === 'End') { e.preventDefault(); nextIdx = items.length - 1; } if (nextIdx !== currIdx) items[nextIdx].focus(); } function initializeEnhancedFilters() { if (document.getElementById(GITE_TOOLBAR_CONTAINER_ID)) return; const anchor = document.querySelector(BEFORE_APPBAR_SELECTOR); if (!anchor) return; const toolbar = el('div', { id: GITE_TOOLBAR_CONTAINER_ID }); if (giteSettings.general.toolbarFontSize) { let fs = giteSettings.general.toolbarFontSize; if (!fs.includes('px')) fs += 'px'; toolbar.style.setProperty('--gite-menu-font-size', fs); } if (giteSettings.general.toolbarLineHeight) { toolbar.style.setProperty('--gite-menu-line-height', giteSettings.general.toolbarLineHeight); } const filtersGroup = el('div', { className: 'gite-filters-group' }); toolbar.appendChild(filtersGroup); const categories = [ { k: 'size', t: 'filter_title_size', p: 'imgsz', id: 'gite-size' }, { k: 'exactSize', t: 'filter_title_exact_size', p: 'q', id: 'gite-es' }, { k: 'aspectRatio', t: 'filter_title_aspect_ratio', p: 'imgar', id: 'gite-ar' }, { k: 'color', t: 'filter_title_color', p: 'tbs', id: 'gite-col' }, { k: 'type', t: 'filter_title_type', p: 'tbs', id: 'gite-type' }, { k: 'time', t: 'filter_title_time', p: 'tbs', id: 'gite-time' }, { k: 'usageRights', t: 'filter_title_usage_rights', p: 'tbs', id: 'gite-rights' }, { k: 'fileType', t: 'filter_title_file_type', p: 'as_filetype', id: 'gite-file' }, { k: 'region', t: 'filter_title_region', p: 'cr', id: 'gite-reg' }, { k: 'site', t: 'filter_title_site_search', p: 'q', id: 'gite-site' } ]; categories.forEach(c => addFilterCategoryMenu(filtersGroup, c.k, c.t, c.p, c.id)); if (giteSettings.general.showAdvancedSearchButton) { const currentQ = new URL(window.location.href).searchParams.get('q') || ""; const cleanQ = currentQ.replace(/(?:\s|^)imagesize:\d+x\d+/gi, '').replace(/(?:\s|^)site:[^\s]+(?:\s+OR\s+site:[^\s]+)*/gi, '').trim(); const advUrl = `https://www.google.com/advanced_image_search?as_q=${encodeURIComponent(cleanQ)}`; const advBtn = el('a', { className: 'gite-tb-btn gite-tb-adv-link gite-ripple style-icon', href: advUrl, title: getLocalizedString('filter_title_advanced_search'), tabIndex: 0 }); advBtn.append(el('div', { className: 'gite-tb-icon', innerHTML: GITE_ICONS.sliders })); advBtn.addEventListener('keydown', (e) => { if (['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(e.key)) { e.preventDefault(); handleToolbarNav(e, advBtn); } }); filtersGroup.appendChild(advBtn); } const url = new URL(window.location.href); const hasFilter = url.searchParams.get('tbs') || url.searchParams.get('imgsz') || url.searchParams.get('imgar') || url.searchParams.get('as_filetype') || url.searchParams.get('cr') || url.searchParams.get('q')?.match(/imagesize:|site:/); if (hasFilter) { const btnStyle = giteSettings.general.toolbarButtonStyle || 'text'; const clearBtn = el('a', { className: `gite-tb-btn gite-ripple clear-action style-${btnStyle}`, href: "javascript:void(0);", title: btnStyle==='icon' ? getLocalizedString('btn_clear') : '', tabIndex: 0 }); clearBtn.append( el('div', { className: 'gite-tb-icon', innerHTML: GITE_ICONS.clear }), el('span', { className: 'gite-btn-text', textContent: getLocalizedString('btn_clear') }) ); clearBtn.onclick = (e) => { if(e.button===0) { e.preventDefault(); window.location.href = buildNewUrl('', '', {}, true); } }; clearBtn.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); clearBtn.click(); } else if (['ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(e.key)) { e.preventDefault(); handleToolbarNav(e, clearBtn); } }); filtersGroup.appendChild(clearBtn); } const utilGroup = el('div', { className: 'gite-utilities-group' }); toolbar.appendChild(utilGroup); if (giteSettings.general.showResultStats) { utilGroup.appendChild(el('span', { id: GITE_RESULT_STATS_DISPLAY_ID })); updateResultStats(); } if (giteSettings.general.showSettingsButtonOnToolbar) { const setBtn = el('div', { id: GITE_SETTINGS_BUTTON_ID, innerHTML: GITE_ICONS.gear, title: getLocalizedString('gm_menu_gite_settings'), tabIndex: 0, role: 'button' }); setBtn.onclick = openSettingsPanel; setBtn.addEventListener('keydown', (e) => { if(e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openSettingsPanel(); } }); utilGroup.appendChild(setBtn); } anchor.parentNode.insertBefore(toolbar, anchor); } // --- 10. UI Interaction Helpers --- function openDatePicker() { document.querySelectorAll('.gite-menu-dropdown').forEach(m => m.classList.remove('show')); document.querySelectorAll('.gite-tb-btn').forEach(b => { b.classList.remove('open'); b.setAttribute('aria-expanded', 'false'); }); const overlay = el('div', { className: 'gite-datepicker-overlay' }); const picker = el('div', { className: 'gite-custom-date-picker' }); const row1 = el('div', { className: 'gite-date-row' }, [ el('label', { for: 'gite-date-start' }, getLocalizedString('datepicker_label_from')), el('input', { type: 'date', id: 'gite-date-start' }) ]); const row2 = el('div', { className: 'gite-date-row' }, [ el('label', { for: 'gite-date-end' }, getLocalizedString('datepicker_label_to')), el('input', { type: 'date', id: 'gite-date-end' }) ]); const actions = el('div', { className: 'gite-datepicker-actions' }); const applyBtn = el('button', { className: 'gite-btn gite-btn-primary', textContent: getLocalizedString('btn_apply') }); const close = () => { if(overlay.parentNode) overlay.parentNode.removeChild(overlay); }; overlay.onclick = (e) => { if(e.target === overlay) close(); }; const doApply = () => { const s = picker.querySelector('#gite-date-start').value; const e = picker.querySelector('#gite-date-end').value; if(!s || !e) { alert(getLocalizedString('alert_datepicker_select_dates')); return; } if(new Date(e) < new Date(s)) { alert(getLocalizedString('alert_datepicker_end_before_start')); return; } const formatDate = d => { const p = d.split('-'); return `${p[1]}/${p[2]}/${p[0]}`; }; const tbsVal = `cdr:1,cd_min:${formatDate(s)},cd_max:${formatDate(e)}`; window.location.href = buildNewUrl('tbs', tbsVal, { categoryKey: 'time', tbsValue: tbsVal }); close(); }; applyBtn.onclick = doApply; picker.addEventListener('keydown', (e) => { if (e.key === 'Enter') doApply(); if (e.key === 'Escape') close(); }); actions.append(applyBtn); picker.append(row1, row2, actions); overlay.appendChild(picker); document.body.appendChild(overlay); setTimeout(() => { const i = document.getElementById('gite-date-start'); if(i) i.focus(); }, 50); } // --- 11. Settings Panel Logic --- const CAT_TITLE_MAP = { 'general': 'settings_tab_general', 'exactSize': 'filter_title_exact_size', 'aspectRatio': 'filter_title_aspect_ratio', 'usageRights': 'filter_title_usage_rights', 'fileType': 'filter_title_file_type', 'site': 'filter_title_site_search' }; function getCatTitleKey(k) { return CAT_TITLE_MAP[k] || `filter_title_${k}`; } function renderToggle(id, labelKey, checked) { const div = el('div', { className: 'gite-control-row' }); div.innerHTML = `
${getLocalizedString(labelKey)}
`; return div; } function renderRangeSlider(id, labelKey, value, min, max, step, unit = '') { const div = el('div', { className: 'gite-control-row', style: "flex-direction:column; align-items:stretch; border:none; padding: 10px 0;" }); const topRow = el('div', { style: "display:flex; justify-content:space-between; margin-bottom:5px;" }); topRow.innerHTML = `${getLocalizedString(labelKey)}${value}${unit}`; const input = el('input', { type: 'range', id: id, className: 'gite-range-input', min, max, step, value }); input.addEventListener('input', (e) => { document.getElementById(`${id}-display`).textContent = `${e.target.value}${unit}`; const tb = document.getElementById(GITE_TOOLBAR_CONTAINER_ID); if (tb) tb.style.setProperty(id === 'setting-fontsize' ? '--gite-menu-font-size' : '--gite-menu-line-height', id === 'setting-fontsize' ? `${e.target.value}px` : e.target.value); }); div.append(topRow, input); return div; } function renderCustomList(catKey, listId) { const ul = el('ul', { className: 'gite-list', id: listId }); const items = currentGiteSettingsForPanel.filters[catKey].userDefinedOptions || []; const createLi = (opt) => { const li = el('li', { className: 'gite-list-item' }); const checkContainer = el('div', { className: 'gite-item-check' }); const toggle = el('label', { className: 'gite-toggle', style: 'transform:scale(0.7); width:32px;' }); const chk = el('input', { type: 'checkbox', checked: opt.isEnabled }); chk.onchange = (e) => { opt.isEnabled = e.target.checked; }; toggle.append(chk, el('span', { className: 'gite-slider' })); checkContainer.appendChild(toggle); const labelDiv = el('div', { className: 'gite-item-label' }); if (catKey === 'site' && giteSettings.filters.site.showFavicons) { // Check Multi let iconSrc = ''; if(opt.value.includes(' OR ')) { labelDiv.appendChild(el('div', { className: 'gite-tb-icon', innerHTML: GITE_ICONS.globe, style: "width:16px;height:16px;margin-right:8px;vertical-align:middle;display:inline-flex;" })); } else { labelDiv.appendChild(el('img', { src: `https://www.google.com/s2/favicons?sz=32&domain_url=${opt.value}`, style: "width:16px;height:16px;margin-right:8px;vertical-align:middle;border-radius:2px;", onerror: function(){this.style.display='none'} })); } } labelDiv.appendChild(document.createTextNode(opt.label || opt.id)); const displayValue = (catKey === 'site' && opt.value) ? opt.value.replace(/\s+OR\s+/g, ', ') : opt.value; const valDiv = el('div', { className: 'gite-item-value', textContent: displayValue, style: "margin-left:auto; margin-right:10px; color:var(--gite-text-secondary); font-size:0.9em;" }); const actions = el('div', { className: 'gite-item-actions' }); const editBtn = el('button', { className: 'gite-icon-btn gite-ripple', title: getLocalizedString('btn_edit_label'), innerHTML: GITE_ICONS.edit }); editBtn.onclick = () => { const newLabel = prompt(getLocalizedString('btn_edit_label'), opt.label); if (newLabel !== null) { opt.label = newLabel; ul.replaceWith(renderCustomList(catKey, listId)); showToast('alert_label_updated'); } }; const delBtn = el('button', { className: 'gite-icon-btn danger gite-ripple', title: getLocalizedString('btn_delete'), innerHTML: GITE_ICONS.delete }); delBtn.onclick = () => { const idx = currentGiteSettingsForPanel.filters[catKey].userDefinedOptions.indexOf(opt); if (idx > -1) { currentGiteSettingsForPanel.filters[catKey].userDefinedOptions.splice(idx, 1); li.remove(); } }; actions.append(editBtn, delBtn); li.append(checkContainer, labelDiv, valDiv, actions); return li; }; if (items.length === 0) ul.innerHTML = `
  • ${getLocalizedString('settings_no_saved_items_placeholder')}
  • `; else items.forEach(item => ul.appendChild(createLi(item))); return ul; } function switchTab(tabId) { if (!giteSettingsPanelElement) return; giteSettingsPanelElement.querySelectorAll('.gite-nav-item').forEach(n => n.classList.toggle('active', n.dataset.tab === tabId)); const contentArea = document.getElementById('gite-settings-content-area'); const titleArea = document.getElementById('gite-settings-page-title'); contentArea.innerHTML = ''; if (tabId === 'general') { titleArea.setAttribute(GITE_LANG_KEY_ATTR, 'settings_tab_general'); titleArea.textContent = getLocalizedString('settings_tab_general'); const langGroup = el('div', { className: 'gite-input-group' }); langGroup.innerHTML = ``; const langSel = el('select', { className: 'gite-input' }); ['auto', 'en', 'zh-TW', 'ja'].forEach(l => { const opt = el('option', { value: l, textContent: getLocalizedString(`settings_lang_${l.replace('-', '_')}`) }); if(l === currentGiteSettingsForPanel.general.selectedLanguage) opt.selected = true; langSel.appendChild(opt); }); langSel.onchange = (e) => { const val = e.target.value; currentGiteSettingsForPanel.general.selectedLanguage = val; if (val === 'auto') { const browserLang = (navigator.language || navigator.userLanguage || 'en').toLowerCase(); if (browserLang.startsWith('zh-tw') || browserLang.startsWith('zh-hk') || browserLang.startsWith('zh-hant')) CURRENT_LANGUAGE = 'zh-TW'; else if (browserLang.startsWith('ja')) CURRENT_LANGUAGE = 'ja'; else CURRENT_LANGUAGE = 'en'; } else { CURRENT_LANGUAGE = val; } updateAllLocalizableElements(giteSettingsPanelElement); switchTab('general'); }; langGroup.appendChild(langSel); const themeGroup = el('div', { className: 'gite-input-group' }); themeGroup.innerHTML = ``; const themeSel = el('select', { className: 'gite-input' }); ['auto', 'light', 'dark'].forEach(t => { const opt = el('option', { value: t, textContent: getLocalizedString(`settings_theme_${t}`) }); if(t === currentGiteSettingsForPanel.general.themePreference) opt.selected = true; themeSel.appendChild(opt); }); themeSel.onchange = (e) => { currentGiteSettingsForPanel.general.themePreference = e.target.value; const isDark = e.target.value === 'dark' || (e.target.value === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches); document.documentElement.classList.toggle('gite-detected-dark-theme', isDark); }; themeGroup.appendChild(themeSel); const styleGroup = el('div', { className: 'gite-input-group' }); styleGroup.innerHTML = ``; const styleSel = el('select', { className: 'gite-input' }); ['text', 'icon', 'both'].forEach(s => { const opt = el('option', { value: s, textContent: getLocalizedString(`settings_btn_style_${s}`) }); if(s === (currentGiteSettingsForPanel.general.toolbarButtonStyle || 'text')) opt.selected = true; styleSel.appendChild(opt); }); styleSel.onchange = (e) => currentGiteSettingsForPanel.general.toolbarButtonStyle = e.target.value; styleGroup.appendChild(styleSel); const fsSlider = renderRangeSlider('setting-fontsize', 'settings_label_toolbar_font_size', parseFloat(currentGiteSettingsForPanel.general.toolbarFontSize), 8, 24, 0.5); fsSlider.querySelector('input').onchange = (e) => currentGiteSettingsForPanel.general.toolbarFontSize = e.target.value + 'px'; const lhSlider = renderRangeSlider('setting-lineheight', 'settings_label_toolbar_line_height', parseFloat(currentGiteSettingsForPanel.general.toolbarLineHeight), 0.1, 2.5, 0.1); lhSlider.querySelector('input').onchange = (e) => currentGiteSettingsForPanel.general.toolbarLineHeight = e.target.value; const t1 = renderToggle('set-btn-toggle', 'settings_label_showtoolbarbutton', currentGiteSettingsForPanel.general.showSettingsButtonOnToolbar); t1.querySelector('input').onchange = (e) => currentGiteSettingsForPanel.general.showSettingsButtonOnToolbar = e.target.checked; const t2 = renderToggle('res-stats-toggle', 'settings_label_showresultstats', currentGiteSettingsForPanel.general.showResultStats); t2.querySelector('input').onchange = (e) => currentGiteSettingsForPanel.general.showResultStats = e.target.checked; const t3 = renderToggle('adv-search-toggle', 'settings_label_showadvsearch', currentGiteSettingsForPanel.general.showAdvancedSearchButton); t3.querySelector('input').onchange = (e) => currentGiteSettingsForPanel.general.showAdvancedSearchButton = e.target.checked; const t4 = renderToggle('reg-flags-toggle', 'settings_label_showregionflags', currentGiteSettingsForPanel.general.showRegionFlags); t4.querySelector('input').onchange = (e) => currentGiteSettingsForPanel.general.showRegionFlags = e.target.checked; const footer = el('div', { className: 'gite-category-footer' }); const rstBtn = el('button', { className: 'gite-btn-reset-cat gite-ripple', innerHTML: `${GITE_ICONS.refresh} ${getLocalizedString('btn_reset_general_settings')}` }); rstBtn.onclick = () => { if(confirm(getLocalizedString('btn_reset_general_settings') + '?')) { currentGiteSettingsForPanel.general = JSON.parse(JSON.stringify(GITE_DEFAULT_SETTINGS.general)); switchTab('general'); showToast('alert_settings_reset_to_default'); detectAndApplyThemeClass(); }}; footer.appendChild(rstBtn); contentArea.append(langGroup, themeGroup, styleGroup, el('hr', {style:'border:0;border-top:1px dashed var(--gite-border);margin:12px 0'}), fsSlider, lhSlider, el('hr', {style:'border:0;border-top:1px dashed var(--gite-border);margin:12px 0'}), t1, t2, t3, t4, footer); } else { const catKey = tabId; const catSettings = currentGiteSettingsForPanel.filters[catKey]; const titleKey = getCatTitleKey(catKey); let localizedTitle = getLocalizedString(titleKey); titleArea.setAttribute(GITE_LANG_KEY_ATTR, titleKey); titleArea.textContent = localizedTitle; const enToggle = el('div', { className: 'gite-control-row' }); enToggle.innerHTML = `
    ${getLocalizedString('settings_enable_filter_category_prefix')}${localizedTitle}${getLocalizedString('settings_enable_filter_category_suffix')}
    `; enToggle.querySelector('input').onchange = (e) => catSettings.enabled = e.target.checked; contentArea.appendChild(enToggle); // --- Layout Options Toggles --- if (['size', 'exactSize', 'time'].includes(catKey)) { const twoColToggle = renderToggle(`cat-${catKey}-2col`, 'settings_label_layout_two_col', catSettings.useTwoColumnLayout); twoColToggle.querySelector('input').onchange = (e) => catSettings.useTwoColumnLayout = e.target.checked; contentArea.appendChild(twoColToggle); } if (['region', 'site'].includes(catKey)) { const multiColToggle = renderToggle(`cat-${catKey}-multi`, 'settings_label_layout_multi_col', catSettings.useMultiColumnLayout); multiColToggle.querySelector('input').onchange = (e) => catSettings.useMultiColumnLayout = e.target.checked; contentArea.appendChild(multiColToggle); } if (catKey === 'time') { const embedToggle = renderToggle('time-embed-date', 'settings_label_embedded_date', catSettings.showEmbeddedDatePicker); embedToggle.querySelector('input').onchange = (e) => catSettings.showEmbeddedDatePicker = e.target.checked; contentArea.appendChild(embedToggle); } if (catKey === 'exactSize') { const inputToggle = renderToggle('es-input-show', 'settings_label_show_exact_size_inputs_in_menu', catSettings.showInputsInMenu); inputToggle.querySelector('input').onchange = (e) => catSettings.showInputsInMenu = e.target.checked; contentArea.appendChild(inputToggle); } if (catKey === 'site') { const favToggle = renderToggle('site-fav-show', 'settings_label_showfavicons', catSettings.showFavicons); favToggle.querySelector('input').onchange = (e) => catSettings.showFavicons = e.target.checked; contentArea.appendChild(favToggle); } if (catKey === 'exactSize' || catKey === 'site') { const sectionTitleKey = catKey === 'site' ? 'settings_section_your_saved_sites' : 'settings_section_your_saved_sizes'; const sectionTitle = el('h3', { textContent: getLocalizedString(sectionTitleKey) }); sectionTitle.setAttribute(GITE_LANG_KEY_ATTR, sectionTitleKey); const addBox = el('div', { className: 'gite-add-box' }); if (catKey === 'exactSize') { addBox.innerHTML = ` `; } else { addBox.innerHTML = ` `; } contentArea.append(sectionTitle, addBox); setTimeout(() => { const addBtn = addBox.querySelector('button'); if(addBtn) addBtn.onclick = () => { const list = currentGiteSettingsForPanel.filters[catKey].userDefinedOptions; if(catKey === 'exactSize') { const w = document.getElementById('new-es-w').value; const h = document.getElementById('new-es-h').value; const l = document.getElementById('new-es-l').value; if(!w || !h) { alert(getLocalizedString('alert_exact_size_invalid_input')); return; } if(list.some(i => i.value === `${w}x${h}`)) { alert(getLocalizedString('alert_size_already_saved')); return; } list.push({ id: `user_es_${Date.now()}`, width: w, height: h, value: `${w}x${h}`, label: l || `${w}x${h}`, isEnabled: true, type:'imagesize', isCustom: true }); } else { let dInput = document.getElementById('new-site-d').value.trim(); const l = document.getElementById('new-site-l').value.trim(); if(!dInput) { alert(getLocalizedString('alert_site_domain_empty')); return; } if(!l) { alert(getLocalizedString('alert_site_label_empty')); return; } const tokens = dInput.replace(/,/g, ' ').split(/\s+/).filter(t => t && t.toLowerCase() !== 'or'); let d = tokens.join(' OR '); if(list.some(i => i.value === d)) { alert(getLocalizedString('alert_site_already_saved')); return; } list.push({ id: `user_site_${Date.now()}`, value: d, label: l, isEnabled: true, type:'site_filter', isCustom: true }); } showToast(catKey === 'site' ? 'alert_site_added' : 'alert_exact_size_added'); document.getElementById(`list-${catKey}`)?.replaceWith(renderCustomList(catKey, `list-${catKey}`)); }; }, 0); contentArea.appendChild(renderCustomList(catKey, `list-${catKey}`)); } const options = catKey === 'exactSize' ? catSettings.predefinedOptions : catSettings.options; if (options && options.length > 0) { const gridTitle = el('h3', { textContent: getLocalizedString('settings_section_predefined_options'), style: 'margin-top:20px;' }); gridTitle.setAttribute(GITE_LANG_KEY_ATTR, 'settings_section_predefined_options'); const grid = el('div', { className: 'gite-option-grid' }); options.forEach(opt => { if (opt.type === 'separator') return; const label = opt.customText || getLocalizedString(opt.textKey, CURRENT_LANGUAGE, opt.id); const div = el('label', { className: 'gite-option-check-item gite-ripple' }); const chk = el('input', { type: 'checkbox', checked: opt.isEnabled !== false }); chk.onchange = (e) => { opt.isEnabled = e.target.checked; }; div.appendChild(chk); if (catKey === 'region' && currentGiteSettingsForPanel.general.showRegionFlags && opt.paramName === 'cr') div.appendChild(el('span', { className: 'gite-icon-preview', textContent: getFlagEmoji(opt.id.split('_').pop()) })); if (catKey === 'color' && opt.type === 'palette' && opt.hex) div.appendChild(el('span', { className: 'gite-icon-preview', style: `width:12px;height:12px;background:${opt.hex};border-radius:50%;border:1px solid rgba(0,0,0,0.1);` })); if(opt.textKey && !opt.customText) { const span = el('span', { textContent: ' ' + label }); span.setAttribute(GITE_LANG_KEY_ATTR, opt.textKey); div.appendChild(span); } else { div.appendChild(document.createTextNode(' ' + label)); } grid.appendChild(div); }); contentArea.append(gridTitle, grid); } const footer = el('div', { className: 'gite-category-footer' }); const rstBtn = el('button', { className: 'gite-btn-reset-cat gite-ripple' }); rstBtn.innerHTML = `${GITE_ICONS.refresh}${getLocalizedString('btn_reset_options_for_category_prefix')}${localizedTitle}${getLocalizedString('btn_reset_options_for_category_suffix')}`; rstBtn.onclick = () => { const confirmMsg = getLocalizedString('btn_reset_options_for_category_prefix') + localizedTitle + getLocalizedString('btn_reset_options_for_category_suffix') + '?'; if(confirm(confirmMsg)) { const defs = GITE_DEFAULT_SETTINGS.filters[catKey]; if (catKey === 'exactSize') { catSettings.predefinedOptions = JSON.parse(JSON.stringify(defs.predefinedOptions)); catSettings.userDefinedOptions = JSON.parse(JSON.stringify(defs.userDefinedOptions)); } else if (catKey === 'site') { catSettings.userDefinedOptions = JSON.parse(JSON.stringify(defs.userDefinedOptions)); } else { catSettings.options = JSON.parse(JSON.stringify(defs.options)); } switchTab(catKey); showToast('alert_settings_reset_to_default'); } }; footer.appendChild(rstBtn); contentArea.appendChild(footer); } } function ensureSettingsPanelDOM() { if (document.getElementById('gite-settings-panel')) return; const panelOverlay = el('div', { id: 'gite-settings-panel', className: 'gite-modal-overlay' }); const navItems = ['general','size','exactSize','aspectRatio','color','type','time','usageRights','fileType','region','site'].map(k => { let iconName = k; if (['exactSize','site','fileType','usageRights','aspectRatio'].includes(k)) iconName = k; if (k === 'general') iconName = 'gear'; const titleKey = getCatTitleKey(k); return `
    ${GITE_ICONS[iconName]} ${getLocalizedString(titleKey)}
    `; }).join(''); panelOverlay.innerHTML = `
    ${GITE_ICONS.gear} ${getLocalizedString('settings_panel_title')}
    ${navItems}

    `; document.body.appendChild(panelOverlay); panelOverlay.querySelector('#gite-settings-close').onclick = closeSettingsPanel; panelOverlay.querySelector('#gite-settings-cancel').onclick = closeSettingsPanel; panelOverlay.querySelector('#gite-settings-save').onclick = saveSettingsFromPanel; panelOverlay.querySelector('#gite-settings-reset').onclick = resetAllSettings; panelOverlay.querySelectorAll('.gite-nav-item').forEach(item => item.onclick = () => switchTab(item.dataset.tab)); panelOverlay.onclick = (e) => { if (e.target === panelOverlay) closeSettingsPanel(); }; // Settings Panel A11y (Esc to close) document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && isSettingsPanelOpen) closeSettingsPanel(); }); } function openSettingsPanel() { if (!giteSettingsPanelElement) ensureSettingsPanelDOM(); giteSettingsPanelElement = document.getElementById('gite-settings-panel'); currentGiteSettingsForPanel = JSON.parse(JSON.stringify(giteSettings)); giteSettingsPanelElement.classList.add('open'); isSettingsPanelOpen = true; switchTab('general'); } function closeSettingsPanel() { if (giteSettingsPanelElement) giteSettingsPanelElement.classList.remove('open'); isSettingsPanelOpen = false; const tb = document.getElementById(GITE_TOOLBAR_CONTAINER_ID); if(tb) { const fs = giteSettings.general.toolbarFontSize; const lh = giteSettings.general.toolbarLineHeight; tb.style.setProperty('--gite-menu-font-size', fs.includes('px')?fs:`${fs}px`); tb.style.setProperty('--gite-menu-line-height', lh); } detectAndApplyThemeClass(); } function saveSettingsFromPanel() { giteSettings = JSON.parse(JSON.stringify(currentGiteSettingsForPanel)); GM_setValue(GITE_SETTINGS_GM_KEY, JSON.stringify(giteSettings)); initializeCurrentLanguage(); detectAndApplyThemeClass(); const oldTb = document.getElementById(GITE_TOOLBAR_CONTAINER_ID); if(oldTb) oldTb.remove(); initializeEnhancedFilters(); showToast('alert_settings_saved'); closeSettingsPanel(); } function resetAllSettings() { if (confirm(getLocalizedString('alert_confirm_reset_all_settings'))) { giteSettings = JSON.parse(JSON.stringify(GITE_DEFAULT_SETTINGS)); GM_setValue(GITE_SETTINGS_GM_KEY, JSON.stringify(giteSettings)); alert(getLocalizedString('alert_settings_reset_to_default') + '\n' + getLocalizedString('gm_please_reload')); window.location.reload(); } } // --- 12. Initialization --- function updateResultStats() { if (!giteSettings.general.showResultStats) return; const target = document.getElementById(GITE_RESULT_STATS_DISPLAY_ID); const source = document.getElementById('result-stats'); if (target && source) { target.textContent = source.textContent; target.style.display = 'inline'; } } function observeResultStats() { if (resultStatsObserver) return; resultStatsObserver = new MutationObserver(updateResultStats); const source = document.getElementById('result-stats'); if (source) resultStatsObserver.observe(source, { childList: true, subtree: true, characterData: true }); new MutationObserver(() => { if (document.getElementById('result-stats')) updateResultStats(); }).observe(document.body, { childList: true }); } function loadSettings() { const storedNew = GM_getValue(GITE_SETTINGS_GM_KEY); if (storedNew) { try { const parsed = JSON.parse(storedNew); giteSettings = JSON.parse(JSON.stringify(GITE_DEFAULT_SETTINGS)); // Merge General if (parsed.general) Object.assign(giteSettings.general, parsed.general); // Merge Filters for (const key in giteSettings.filters) { if (parsed.filters && parsed.filters[key]) { const target = giteSettings.filters[key]; const source = parsed.filters[key]; if (source.hasOwnProperty('enabled')) target.enabled = source.enabled; // New Layout Props if (source.hasOwnProperty('useTwoColumnLayout')) target.useTwoColumnLayout = source.useTwoColumnLayout; if (source.hasOwnProperty('useMultiColumnLayout')) target.useMultiColumnLayout = source.useMultiColumnLayout; if (source.hasOwnProperty('showEmbeddedDatePicker')) target.showEmbeddedDatePicker = source.showEmbeddedDatePicker; // Merge Options if (source.options) { target.options.forEach(defOpt => { const match = source.options.find(s => s.id === defOpt.id); if (match && match.hasOwnProperty('isEnabled')) defOpt.isEnabled = match.isEnabled; }); } if (key === 'exactSize') { if (source.predefinedOptions) { target.predefinedOptions.forEach(defOpt => { const match = source.predefinedOptions.find(s => s.id === defOpt.id); if (match && match.hasOwnProperty('isEnabled')) defOpt.isEnabled = match.isEnabled; }); } if (source.userDefinedOptions) target.userDefinedOptions = source.userDefinedOptions; if (source.hasOwnProperty('showInputsInMenu')) target.showInputsInMenu = source.showInputsInMenu; } if (key === 'site') { if (source.userDefinedOptions) target.userDefinedOptions = source.userDefinedOptions; if (source.hasOwnProperty('showFavicons')) target.showFavicons = source.showFavicons; } } } } catch (e) { console.error("[GITE] Error loading settings:", e); giteSettings = JSON.parse(JSON.stringify(GITE_DEFAULT_SETTINGS)); } } else { // Migration Logic (V0.3 -> V1) - Simplified for brevity as V1 structure is now dominant const storedOld = GM_getValue('GITE_USER_SETTINGS'); giteSettings = JSON.parse(JSON.stringify(GITE_DEFAULT_SETTINGS)); if (storedOld) { try { const parsedOld = JSON.parse(storedOld); if (parsedOld.general) Object.assign(giteSettings.general, parsedOld.general); if (parsedOld.filters) { // Minimal migration for custom lists if(parsedOld.filters.exactSize?.userDefinedOptions) giteSettings.filters.exactSize.userDefinedOptions = parsedOld.filters.exactSize.userDefinedOptions; if(parsedOld.filters.site?.userDefinedOptions) { // Simple copy, smarter logic handled in previous versions if needed giteSettings.filters.site.userDefinedOptions = parsedOld.filters.site.userDefinedOptions; } } GM_setValue(GITE_SETTINGS_GM_KEY, JSON.stringify(giteSettings)); } catch(e) {} } } } function debounce(func, wait) { let timeout; return function(...args) { clearTimeout(timeout); timeout = setTimeout(() => func.apply(this, args), wait); }; } function main() { loadSettings(); initializeCurrentLanguage(); injectStyles(); detectAndApplyThemeClass(); if (typeof GM_registerMenuCommand === 'function') { GM_registerMenuCommand(getLocalizedString('gm_menu_gite_settings'), openSettingsPanel); GM_registerMenuCommand(getLocalizedString('gm_menu_reset_all_gite_settings'), resetAllSettings); } const runInit = () => { initializeEnhancedFilters(); observeResultStats(); if (!menuObserver) { const debouncedReInit = debounce(() => { const anchor = document.querySelector(BEFORE_APPBAR_SELECTOR); const tb = document.getElementById(GITE_TOOLBAR_CONTAINER_ID); if (anchor && !tb) { initializeEnhancedFilters(); } }, 200); menuObserver = new MutationObserver(debouncedReInit); menuObserver.observe(document.body, { childList: true, subtree: true }); } }; if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', runInit); else runInit(); window.addEventListener('popstate', () => { const tb = document.getElementById(GITE_TOOLBAR_CONTAINER_ID); if(tb) tb.remove(); initializeEnhancedFilters(); }); document.addEventListener('click', (e) => { if (currentlyOpenMenuId) { const menu = document.getElementById(currentlyOpenMenuId); const btnId = currentlyOpenMenuId.replace('-menu', '-btn'); const btn = document.getElementById(btnId); // Important: Don't close if clicking inside embedded picker if (e.target.closest('.gite-embedded-picker')) return; if ((menu && !menu.contains(e.target)) && (btn && !btn.contains(e.target))) { document.querySelectorAll('.gite-menu-dropdown').forEach(m => m.classList.remove('show')); document.querySelectorAll('.gite-tb-btn').forEach(b => { b.classList.remove('open'); b.setAttribute('aria-expanded', 'false'); }); currentlyOpenMenuId = null; } } }); } main(); })();