// ==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 0.2.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 画像検索を強化。正確なサイズ、アスペクト比、ファイル形式、地域/サイト内検索など、拡張されたフィルターとカスタマイズオプションを提供。
// @author StonedKhajiit
// @license MIT
// @include http*://*.google.tld/search*tbm=isch*
// @include http*://*.google.tld/search*udm=2*
// @grant GM_registerMenuCommand
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_info
// ==/UserScript==
(function () {
'useastrict';
// --- Configuration ---
const DEBUG_MODE = false;
if (typeof GM_getValue !== 'function' || typeof GM_setValue !== 'function') {
console.error('[GITE] CRITICAL: GM_getValue or GM_setValue is not available. Settings cannot be loaded or saved. The script may not function correctly.');
}
if (typeof GM_registerMenuCommand !== 'function' && DEBUG_MODE) {
console.warn('[GITE] GM_registerMenuCommand is not available. Greasemonkey menu items will not be registered.');
}
// --- Selectors (Constants for DOM elements) ---
const GITE_MENU_ITEM_CLASS = 'gite-custom-menu-li';
const CUSTOM_SIZER_ATTR = 'data-custom-sizer';
const SIZER_VALUE_ATTR = 'data-sizer-value';
const CUSTOM_FILTER_ITEM_ATTR = 'data-custom-filter-item';
const FILTER_PARAM_ATTR = 'data-filter-param';
const FILTER_VALUE_ATTR = 'data-filter-value';
const TBS_PREFIX_ATTR = 'data-tbs-prefix';
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_CLEAR_BUTTON_ID = 'gite-clear-filters-button';
const CLEAR_BUTTON_TARGET_DISPLAY_STYLE = 'inline-flex';
const GITE_SETTINGS_BUTTON_ID = 'gite-settings-toolbar-button';
const GITE_RESULT_STATS_DISPLAY_ID = 'gite-result-stats-display';
// --- Timing & Limits (Constants for delays, retries, etc.) ---
const MAX_OBSERVER_TRIES = 15;
const OBSERVER_RETRY_DELAY_MS = 500;
const POPSTATE_UPDATE_DELAY_MS = 150;
const FINAL_UI_UPDATE_DELAY_MS = 350;
const RESULT_STATS_RETRY_DELAY_MS = 600;
const MAX_RESULT_STATS_TRIES = 10;
const GITE_SETTINGS_GM_KEY = 'GITE_USER_SETTINGS';
const GITE_SCRIPT_VERSION = typeof GM_info !== 'undefined' && GM_info.script ? GM_info.script.version : 'N/A_GM_info_NA';
const GITE_LANG_KEY_ATTR = 'data-gite-lang-key';
let CURRENT_LANGUAGE = 'en';
// --- 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",
'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_1080x1920': "1080×1920 (FHD Port.)", 'option_text_exact_size_768x1024': "768×1024 (XGA Port.)",
'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_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_3m': "Past 3 months",
'option_text_time_past_6m': "Past 6 months", 'option_text_time_past_9m': "Past 9 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.",
'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 GITE Settings",
'gm_please_reload': "Please reload the page for the changes to be fully applied.", 'gm_settings_updated_no_reload': "GITE settings updated.",
'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 GITE toolbar:",
'settings_label_showresultstats': "Show result stats on GITE toolbar:",
'settings_label_theme': "Theme:", 'settings_theme_auto': "Auto-detect", 'settings_theme_light': "Light", 'settings_theme_dark': "Dark",
'settings_label_toolbar_font_size': "Toolbar Font Size (px):", 'settings_label_toolbar_line_height': "Toolbar Line Height:",
'btn_reset_all_settings': "Reset All to Defaults", 'alert_confirm_reset_all_settings': "Are you sure you want to reset ALL GITE settings to their defaults? This cannot be undone.",
'alert_settings_reset_to_default': "All settings have been reset to default. Please save if you wish to keep these defaults, or reload for full effect if saved from GM menu.",
'alert_settings_saved_reload_required': "Settings saved. Some changes may require a page reload to take full effect.",
'tooltip_gite_settings_button': "GITE Settings",
'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': "\"",
'settings_size_options_note': "Manage visibility of default size options.",
'btn_reset_options_for_category_prefix': "Reset \"", 'btn_reset_options_for_category_suffix': "\" Options to Defaults",
'btn_reset_general_options': "Reset General Options to Defaults",
'btn_reset_exact_size_options': "Reset Exact Size Options to Defaults",
'btn_reset_site_search_options': "Reset Site Search Options to Defaults",
'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_region_options_note': "Manage visibility of region options. All listed regions will be available in the filter menu if this category is enabled.",
'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., Official Website)",
'settings_placeholder_site_domain': "Domain (e.g., example.com or .gov)",
'settings_no_saved_items_placeholder': "No saved items yet."
},
'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': "新增網站",
'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_1080x1920': "1080×1920 (FHD 縱向)", 'option_text_exact_size_768x1024': "768×1024 (XGA 縱向)",
'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_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_3m': "過去 3 個月",
'option_text_time_past_6m': "過去 6 個月", 'option_text_time_past_9m': "過去 9 個月", '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 或 .domain 格式。", 'alert_site_already_saved': "此網站/網域已儲存 (請檢查標籤或網域)。",
'alert_site_added': "新網站已新增: ", 'alert_site_deleted': "已儲存的網站已刪除。", 'alert_site_label_updated': "網站標籤已更新。", 'alert_site_value_updated': "網站網域已更新。",
'text_larger_than_prefix': "大於 ", 'text_site_search_active_prefix': "搜尋網站: ",
'gm_menu_gite_settings': "GITE 設定", 'gm_menu_reset_all_gite_settings': "重設所有 GITE 設定",
'gm_please_reload': "請重新載入頁面以套用變更。", 'gm_settings_updated_no_reload': "GITE 設定已更新。",
'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': "工具列行高:",
'btn_reset_all_settings': "全部重設為預設", 'alert_confirm_reset_all_settings': "您確定要將所有 GITE 設定重設為預設值嗎?此操作無法復原。",
'alert_settings_reset_to_default': "所有設定已重設為預設值。如果您希望保留這些預設值,請儲存設定,或於 GM 選單重設後重新載入頁面。",
'alert_settings_saved_reload_required': "設定已儲存。部分變更可能需要重新載入頁面才能完全生效。",
'tooltip_gite_settings_button': "GITE 設定",
'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': "」的選項",
'settings_size_options_note': "管理預設尺寸選項的可見性。",
'btn_reset_options_for_category_prefix': "重設「", 'btn_reset_options_for_category_suffix': "」選項為預設值",
'btn_reset_general_options': "重設「一般」選項為預設值",
'btn_reset_exact_size_options': "重設「精確尺寸」選項為預設值",
'btn_reset_site_search_options': "重設「站內搜尋」選項為預設值",
'settings_section_predefined_options': "預定義選項:",
'settings_section_your_saved_sizes': "您儲存的尺寸:",
'settings_label_add_new_exact_size': "新增精確尺寸:",
'settings_placeholder_label_optional': "標籤 (選填)",
'settings_region_options_note': "管理地區選項的可見性。所有列出的地區選項,若此分類已啟用,即可在篩選器選單中使用。",
'settings_label_enable_site_search_filter': "啟用「站內搜尋」篩選器",
'settings_section_your_saved_sites': "您儲存的網站:",
'settings_label_add_new_site': "新增網站:",
'settings_placeholder_site_label': "標籤 (例如:官方網站)",
'settings_placeholder_site_domain': "網域 (例如:example.com 或 .gov)",
'settings_no_saved_items_placeholder': "尚無儲存的項目。"
},
'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': "サイトを追加",
'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': "200 万画素以上", 'option_text_size_4mp': "400 万画素以上",
'option_text_size_6mp': "600 万画素以上", 'option_text_size_8mp': "800 万画素以上",
'option_text_size_10mp': "1000 万画素以上", 'option_text_size_12mp': "1200 万画素以上",
'option_text_size_15mp': "1500 万画素以上", 'option_text_size_20mp': "2000 万画素以上",
'option_text_size_40mp': "4000 万画素以上", 'option_text_size_70mp': "7000 万画素以上",
'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_1080x1920': "1080×1920 (FHD 縦長)",
'option_text_exact_size_768x1024': "768×1024 (XGA 縦長)",
'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_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_3m': "過去3か月以内",
'option_text_time_past_6m': "過去6か月以内", 'option_text_time_past_9m': "過去9か月以内", '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': "無効なドメイン形式です。example.com または .gov の形式を使用してください。",
'alert_site_already_saved': "このサイト/ドメインは既に保存されています(ラベルまたはドメインを確認してください)。",
'alert_site_added': "新しいサイトが追加されました: ",
'alert_site_deleted': "保存されたサイトが削除されました。",
'alert_site_label_updated': "サイトラベルが更新されました。",
'alert_site_value_updated': "サイトドメインが更新されました。",
'text_larger_than_prefix': "次より大きい: ",
'text_site_search_active_prefix': "サイト: ",
'gm_menu_gite_settings': "GITE 設定",
'gm_menu_reset_all_gite_settings': "すべての GITE 設定をリセット",
'gm_please_reload': "変更を完全に適用するには、ページをリロードしてください。",
'gm_settings_updated_no_reload': "GITE 設定が更新されました。",
'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': "日本語 (Japanese)",
'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': "ツールバーの行の高さ:",
'btn_reset_all_settings': "すべてデフォルトにリセット",
'alert_confirm_reset_all_settings': "すべてのGITE設定をデフォルトにリセットしてもよろしいですか?この操作は元に戻せません。",
'alert_settings_reset_to_default': "すべての設定がデフォルトにリセットされました。これらのデフォルトを保持する場合は保存してください。GMメニューからリセットした場合は、完全に適用するためにページをリロードしてください。",
'alert_settings_saved_reload_required': "設定が保存されました。一部の変更は、完全に有効にするためにページのリロードが必要な場合があります。",
'tooltip_gite_settings_button': "GITE 設定",
'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': "」のオプション",
'settings_size_options_note': "デフォルトのサイズオプションの表示を管理します。",
'btn_reset_options_for_category_prefix': "「",
'btn_reset_options_for_category_suffix': "」オプションをデフォルトにリセット",
'btn_reset_general_options': "「一般」オプションをデフォルトにリセット",
'btn_reset_exact_size_options': "「正確なサイズ」オプションをデフォルトにリセット",
'btn_reset_site_search_options': "「サイト内検索」オプションをデフォルトにリセット",
'settings_section_predefined_options': "事前定義オプション:",
'settings_section_your_saved_sizes': "保存したサイズ:",
'settings_label_add_new_exact_size': "新しい正確なサイズを追加:",
'settings_placeholder_label_optional': "ラベル (オプション)",
'settings_region_options_note': "地域オプションの表示を管理します。このカテゴリが有効な場合、リストされているすべての地域オプションがフィルターメニューで利用可能になります。",
'settings_label_enable_site_search_filter': "「サイト内検索」フィルターを有効にする",
'settings_section_your_saved_sites': "保存したサイト:",
'settings_label_add_new_site': "新しいサイトを追加:",
'settings_placeholder_site_label': "ラベル (例: 公式サイト)",
'settings_placeholder_site_domain': "ドメイン (例: example.com または .gov)",
'settings_no_saved_items_placeholder': "まだ保存されたアイテムはありません。"
}
};
// --- Option Definitions (Language Agnostic Base for Filters) ---
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_col1", type: "separator", textKey: null, defaultEnabled: true },
{ id: "gite_size_qsvga", value: "qsvga", type: "pixel", textKey: "option_text_size_qsvga", defaultEnabled: true },
{ id: "gite_size_vga", value: "vga", type: "pixel", textKey: "option_text_size_vga", defaultEnabled: true },
{ id: "gite_size_svga", value: "svga", type: "pixel", textKey: "option_text_size_svga", defaultEnabled: true },
{ id: "gite_size_xga", value: "xga", type: "pixel", textKey: "option_text_size_xga", defaultEnabled: true },
{ id: "gite_size_2mp", value: "2mp", type: "megapixel", textKey: "option_text_size_2mp", defaultEnabled: true },
{ id: "gite_size_4mp", value: "4mp", type: "megapixel", textKey: "option_text_size_4mp", defaultEnabled: true },
{ id: "gite_size_6mp", value: "6mp", type: "megapixel", textKey: "option_text_size_6mp", defaultEnabled: true },
{ id: "gite_size_8mp", value: "8mp", type: "megapixel", textKey: "option_text_size_8mp", defaultEnabled: true },
{ id: "gite_size_10mp", value: "10mp", type: "megapixel", textKey: "option_text_size_10mp", defaultEnabled: true },
{ id: "gite_size_12mp", value: "12mp", type: "megapixel", textKey: "option_text_size_12mp", defaultEnabled: true },
{ id: "gite_size_15mp", value: "15mp", type: "megapixel", textKey: "option_text_size_15mp", defaultEnabled: true },
{ id: "gite_size_20mp", value: "20mp", type: "megapixel", textKey: "option_text_size_20mp", defaultEnabled: true },
{ id: "gite_size_40mp", value: "40mp", type: "megapixel", textKey: "option_text_size_40mp", defaultEnabled: true },
{ id: "gite_size_70mp", value: "70mp", type: "megapixel", textKey: "option_text_size_70mp", defaultEnabled: true },
],
exactSize: [ // showInputsInMenu setting will control if the input part is rendered
{ 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_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_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_1080x1920", value: "1080x1920", type: "imagesize", textKey: "option_text_exact_size_1080x1920", defaultEnabled: true },
{ 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_tall", value: "t", textKey: "option_text_ar_tall" },
{ id: "ar_square", value: "s", textKey: "option_text_ar_square" }, { id: "ar_wide", value: "w", textKey: "option_text_ar_wide" },
{ id: "ar_panoramic", value: "xw", textKey: "option_text_ar_panoramic" }
],
color: {
prefixOptions: [
{ id: "color_prefix_any", value: "", paramNameOverride: "tbs", tbsValue: "", textKey: "option_text_color_any" },
{ id: "color_prefix_full", value: "color", paramNameOverride: "tbs", tbsValue: "ic:color", textKey: "option_text_color_full" },
{ id: "color_prefix_bw", value: "gray", paramNameOverride: "tbs", tbsValue: "ic:gray", textKey: "option_text_color_bw" },
{ id: "color_prefix_transparent", value: "trans", paramNameOverride: "tbs", tbsValue: "ic:trans", textKey: "option_text_color_transparent" },
{ id: "color_prefix_sep1", type: "separator", textKey: null},
],
paletteColors: [
{ id: "palette_red", nameKey: "option_text_color_palette_red", hex: "#c00", tbsValue: "ic:specific,isc:red", type: "palette" },
{ id: "palette_orange", nameKey: "option_text_color_palette_orange", hex: "#fb940b", tbsValue: "ic:specific,isc:orange", type: "palette" },
{ id: "palette_yellow", nameKey: "option_text_color_palette_yellow", hex: "#ff0", tbsValue: "ic:specific,isc:yellow", type: "palette" },
{ id: "palette_green", nameKey: "option_text_color_palette_green", hex: "#0c0", tbsValue: "ic:specific,isc:green", type: "palette" },
{ id: "palette_teal", nameKey: "option_text_color_palette_teal", hex: "#03c0c6", tbsValue: "ic:specific,isc:teal", type: "palette" },
{ id: "palette_blue", nameKey: "option_text_color_palette_blue", hex: "#00f", tbsValue: "ic:specific,isc:blue", type: "palette" },
{ id: "palette_purple", nameKey: "option_text_color_palette_purple", hex: "#762ca7", tbsValue: "ic:specific,isc:purple", type: "palette" },
{ id: "palette_pink", nameKey: "option_text_color_palette_pink", hex: "#ff98bf", tbsValue: "ic:specific,isc:pink", type: "palette" },
{ id: "palette_white", nameKey: "option_text_color_palette_white", hex: "#fff", tbsValue: "ic:specific,isc:white", type: "palette" },
{ id: "palette_gray", nameKey: "option_text_color_palette_gray", hex: "#999", tbsValue: "ic:specific,isc:gray", type: "palette" },
{ id: "palette_black", nameKey: "option_text_color_palette_black", hex: "#000", tbsValue: "ic:specific,isc:black", type: "palette" },
{ id: "palette_brown", nameKey: "option_text_color_palette_brown", hex: "#885418", tbsValue: "ic:specific,isc:brown", type: "palette" }
]
},
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: "gite_time_any", tbsValue: "", textKey: "option_text_time_any", defaultEnabled: true },
{ id: "gite_time_past_15m", tbsValue: "qdr:n15", textKey: "option_text_time_past_15m", defaultEnabled: false },
{ id: "gite_time_past_30m", tbsValue: "qdr:n30", textKey: "option_text_time_past_30m", defaultEnabled: false },
{ id: "gite_time_past_hour", tbsValue: "qdr:h", textKey: "option_text_time_past_hour", defaultEnabled: true },
{ id: "gite_time_past_24h", tbsValue: "qdr:d", textKey: "option_text_time_past_24h", defaultEnabled: true },
{ id: "gite_time_past_2d", tbsValue: "qdr:d2", textKey: "option_text_time_past_2d", defaultEnabled: false },
{ id: "gite_time_past_3d", tbsValue: "qdr:d3", textKey: "option_text_time_past_3d", defaultEnabled: false },
{ id: "gite_time_past_4d", tbsValue: "qdr:d4", textKey: "option_text_time_past_4d", defaultEnabled: false },
{ id: "gite_time_past_5d", tbsValue: "qdr:d5", textKey: "option_text_time_past_5d", defaultEnabled: false },
{ id: "gite_time_past_6d", tbsValue: "qdr:d6", textKey: "option_text_time_past_6d", defaultEnabled: false },
{ id: "gite_time_past_week", tbsValue: "qdr:w", textKey: "option_text_time_past_week", defaultEnabled: true },
{ id: "gite_time_past_2w", tbsValue: "qdr:w2", textKey: "option_text_time_past_2w", defaultEnabled: false },
{ id: "gite_time_past_3w", tbsValue: "qdr:w3", textKey: "option_text_time_past_3w", defaultEnabled: false },
{ id: "gite_time_past_month", tbsValue: "qdr:m", textKey: "option_text_time_past_month", defaultEnabled: true },
{ id: "gite_time_past_3m", tbsValue: "qdr:m3", textKey: "option_text_time_past_3m", defaultEnabled: true },
{ id: "gite_time_past_6m", tbsValue: "qdr:m6", textKey: "option_text_time_past_6m", defaultEnabled: true },
{ id: "gite_time_past_9m", tbsValue: "qdr:m9", textKey: "option_text_time_past_9m", defaultEnabled: true },
{ id: "gite_time_past_year", tbsValue: "qdr:y", textKey: "option_text_time_past_year", defaultEnabled: true },
{ id: "gite_time_past_2y", tbsValue: "qdr:y2", textKey: "option_text_time_past_2y", defaultEnabled: true },
{ id: "gite_time_past_3y", tbsValue: "qdr:y3", textKey: "option_text_time_past_3y", defaultEnabled: true },
{ id: "gite_time_past_4y", tbsValue: "qdr:y4", textKey: "option_text_time_past_4y", defaultEnabled: false },
{ id: "gite_time_past_5y", tbsValue: "qdr:y5", textKey: "option_text_time_past_5y", defaultEnabled: false },
{ id: "gite_time_past_6y", tbsValue: "qdr:y6", textKey: "option_text_time_past_6y", defaultEnabled: false },
{ id: "gite_time_past_7y", tbsValue: "qdr:y7", textKey: "option_text_time_past_7y", defaultEnabled: false },
{ id: "gite_time_past_8y", tbsValue: "qdr:y8", textKey: "option_text_time_past_8y", defaultEnabled: false },
{ id: "gite_time_past_9y", tbsValue: "qdr:y9", textKey: "option_text_time_past_9y", defaultEnabled: false },
{ id: "gite_time_past_10y", tbsValue: "qdr:y10", textKey: "option_text_time_past_10y", defaultEnabled: false },
{ id: "gite_time_sep1", type: "separator", textKey: null, defaultEnabled: true },
{ id: "gite_time_custom_range", type: "custom_date_trigger", tbsValue: null, 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: "filetype_any", value: "", textKey: "option_text_filetype_any" }, { id: "filetype_jpg", value: "jpg", textKey: "option_text_filetype_jpg" },
{ id: "filetype_gif", value: "gif", textKey: "option_text_filetype_gif" }, { id: "filetype_png", value: "png", textKey: "option_text_filetype_png" },
{ id: "filetype_bmp", value: "bmp", textKey: "option_text_filetype_bmp" }, { id: "filetype_svg", value: "svg", textKey: "option_text_filetype_svg" },
{ id: "filetype_webp", value: "webp", textKey: "option_text_filetype_webp" }, { id: "filetype_avif", value: "avif", textKey: "option_text_filetype_avif" },
{ id: "filetype_ico", value: "ico", textKey: "option_text_filetype_ico" }, { id: "filetype_raw", value: "craw", textKey: "option_text_filetype_raw" }
],
region: [
{ id: "region_ca", value: "countryCA", paramName: "cr", textKey: "option_text_region_ca", defaultEnabled: true },
{ id: "region_us", value: "countryUS", paramName: "cr", textKey: "option_text_region_us", defaultEnabled: true },
{ id: "region_mx", value: "countryMX", paramName: "cr", textKey: "option_text_region_mx", defaultEnabled: false },
{ id: "region_ar", value: "countryAR", paramName: "cr", textKey: "option_text_region_ar", defaultEnabled: false },
{ id: "region_br", value: "countryBR", paramName: "cr", textKey: "option_text_region_br", defaultEnabled: false },
{ id: "region_cl", value: "countryCL", paramName: "cr", textKey: "option_text_region_cl", defaultEnabled: false },
{ id: "region_co", value: "countryCO", paramName: "cr", textKey: "option_text_region_co", defaultEnabled: false },
{ id: "region_pe", value: "countryPE", paramName: "cr", textKey: "option_text_region_pe", defaultEnabled: false },
{ id: "region_al", value: "countryAL", paramName: "cr", textKey: "option_text_region_al", defaultEnabled: false },
{ id: "region_at", value: "countryAT", paramName: "cr", textKey: "option_text_region_at", defaultEnabled: false },
{ id: "region_by", value: "countryBY", paramName: "cr", textKey: "option_text_region_by", defaultEnabled: false },
{ id: "region_be", value: "countryBE", paramName: "cr", textKey: "option_text_region_be", defaultEnabled: false },
{ id: "region_ba", value: "countryBA", paramName: "cr", textKey: "option_text_region_ba", defaultEnabled: false },
{ id: "region_bg", value: "countryBG", paramName: "cr", textKey: "option_text_region_bg", defaultEnabled: false },
{ id: "region_hr", value: "countryHR", paramName: "cr", textKey: "option_text_region_hr", defaultEnabled: false },
{ id: "region_cz", value: "countryCZ", paramName: "cr", textKey: "option_text_region_cz", defaultEnabled: false },
{ id: "region_dk", value: "countryDK", paramName: "cr", textKey: "option_text_region_dk", defaultEnabled: false },
{ id: "region_ee", value: "countryEE", paramName: "cr", textKey: "option_text_region_ee", defaultEnabled: false },
{ id: "region_fi", value: "countryFI", paramName: "cr", textKey: "option_text_region_fi", defaultEnabled: false },
{ id: "region_fr", value: "countryFR", paramName: "cr", textKey: "option_text_region_fr", defaultEnabled: true },
{ id: "region_de", value: "countryDE", paramName: "cr", textKey: "option_text_region_de", defaultEnabled: true },
{ id: "region_gr", value: "countryGR", paramName: "cr", textKey: "option_text_region_gr", defaultEnabled: false },
{ id: "region_hu", value: "countryHU", paramName: "cr", textKey: "option_text_region_hu", defaultEnabled: false },
{ id: "region_is", value: "countryIS", paramName: "cr", textKey: "option_text_region_is", defaultEnabled: false },
{ id: "region_it", value: "countryIT", paramName: "cr", textKey: "option_text_region_it", defaultEnabled: true },
{ id: "region_lv", value: "countryLV", paramName: "cr", textKey: "option_text_region_lv", defaultEnabled: false },
{ id: "region_lt", value: "countryLT", paramName: "cr", textKey: "option_text_region_lt", defaultEnabled: false },
{ id: "region_lu", value: "countryLU", paramName: "cr", textKey: "option_text_region_lu", defaultEnabled: false },
{ id: "region_nl", value: "countryNL", paramName: "cr", textKey: "option_text_region_nl", defaultEnabled: false },
{ id: "region_no", value: "countryNO", paramName: "cr", textKey: "option_text_region_no", defaultEnabled: false },
{ id: "region_pl", value: "countryPL", paramName: "cr", textKey: "option_text_region_pl", defaultEnabled: false },
{ id: "region_pt", value: "countryPT", paramName: "cr", textKey: "option_text_region_pt", defaultEnabled: false },
{ id: "region_ro", value: "countryRO", paramName: "cr", textKey: "option_text_region_ro", defaultEnabled: false },
{ id: "region_ru", value: "countryRU", paramName: "cr", textKey: "option_text_region_ru", defaultEnabled: false },
{ id: "region_rs", value: "countryRS", paramName: "cr", textKey: "option_text_region_rs", defaultEnabled: false },
{ id: "region_sk", value: "countrySK", paramName: "cr", textKey: "option_text_region_sk", defaultEnabled: false },
{ id: "region_si", value: "countrySI", paramName: "cr", textKey: "option_text_region_si", defaultEnabled: false },
{ id: "region_es", value: "countryES", paramName: "cr", textKey: "option_text_region_es", defaultEnabled: true },
{ id: "region_se", value: "countrySE", paramName: "cr", textKey: "option_text_region_se", defaultEnabled: false },
{ id: "region_ch", value: "countryCH", paramName: "cr", textKey: "option_text_region_ch", defaultEnabled: false },
{ id: "region_tr", value: "countryTR", paramName: "cr", textKey: "option_text_region_tr", defaultEnabled: false },
{ id: "region_ua", value: "countryUA", paramName: "cr", textKey: "option_text_region_ua", defaultEnabled: false },
{ id: "region_gb", value: "countryGB", paramName: "cr", textKey: "option_text_region_gb", defaultEnabled: true },
{ id: "region_ie", value: "countryIE", paramName: "cr", textKey: "option_text_region_ie", defaultEnabled: false },
{ id: "region_cn", value: "countryCN", paramName: "cr", textKey: "option_text_region_cn", defaultEnabled: false },
{ id: "region_hk", value: "countryHK", paramName: "cr", textKey: "option_text_region_hk", defaultEnabled: false },
{ id: "region_in", value: "countryIN", paramName: "cr", textKey: "option_text_region_in", defaultEnabled: false },
{ id: "region_id", value: "countryID", paramName: "cr", textKey: "option_text_region_id", defaultEnabled: false },
{ id: "region_il", value: "countryIL", paramName: "cr", textKey: "option_text_region_il", defaultEnabled: false },
{ id: "region_jp", value: "countryJP", paramName: "cr", textKey: "option_text_region_jp", defaultEnabled: true },
{ id: "region_my", value: "countryMY", paramName: "cr", textKey: "option_text_region_my", defaultEnabled: false },
{ id: "region_ph", value: "countryPH", paramName: "cr", textKey: "option_text_region_ph", defaultEnabled: false },
{ id: "region_sa", value: "countrySA", paramName: "cr", textKey: "option_text_region_sa", defaultEnabled: false },
{ id: "region_sg", value: "countrySG", paramName: "cr", textKey: "option_text_region_sg", defaultEnabled: false },
{ id: "region_kr", value: "countryKR", paramName: "cr", textKey: "option_text_region_kr", defaultEnabled: true },
{ id: "region_tw", value: "countryTW", paramName: "cr", textKey: "option_text_region_tw", defaultEnabled: true },
{ id: "region_th", value: "countryTH", paramName: "cr", textKey: "option_text_region_th", defaultEnabled: false },
{ id: "region_ae", value: "countryAE", paramName: "cr", textKey: "option_text_region_ae", defaultEnabled: false },
{ id: "region_vn", value: "countryVN", paramName: "cr", textKey: "option_text_region_vn", defaultEnabled: false },
{ id: "region_au", value: "countryAU", paramName: "cr", textKey: "option_text_region_au", defaultEnabled: true },
{ id: "region_nz", value: "countryNZ", paramName: "cr", textKey: "option_text_region_nz", defaultEnabled: true },
{ id: "region_eg", value: "countryEG", paramName: "cr", textKey: "option_text_region_eg", defaultEnabled: false },
{ id: "region_ng", value: "countryNG", paramName: "cr", textKey: "option_text_region_ng", defaultEnabled: false },
{ id: "region_za", value: "countryZA", paramName: "cr", textKey: "option_text_region_za", defaultEnabled: false },
],
site: [
{ id: "gite_site_any", value: "", type: "site_clear", textKey: "option_text_site_any", defaultEnabled: true, isCustom: false }
]
};
// --- Default Settings Structure ---
function _generateDefaultOptionsFromDefinitions(categoryKey) {
const definitions = GITE_OPTION_DEFINITIONS[categoryKey];
if (!definitions) {
if (DEBUG_MODE) warn(`_generateDefaultOptionsFromDefinitions: No definitions found for categoryKey "${categoryKey}". Returning empty array.`);
return [];
}
if (categoryKey === 'site') {
return definitions.map(def => ({
id: def.id, ...(def.type && { type: def.type }), value: def.value, textKey: def.textKey,
customText: null, isCustom: def.isCustom !== undefined ? def.isCustom : false,
isEnabled: def.defaultEnabled !== undefined ? def.defaultEnabled : true,
}));
}
return definitions.map(def => ({
id: def.id, ...(def.type && { type: def.type }), value: def.value, tbsValue: def.tbsValue,
textKey: def.textKey, customText: null, isCustom: false,
isEnabled: def.defaultEnabled !== undefined ? def.defaultEnabled : true,
...(def.paramName && {paramName: def.paramName})
}));
}
const GITE_DEFAULT_SETTINGS = {
general: {
selectedLanguage: "auto",
showSettingsButtonOnToolbar: true,
showResultStats: true,
themePreference: "auto",
toolbarFontSize: "14px", // Default font size
toolbarLineHeight: "1.0" // Default line height
},
filters: {
size: { enabled: true, options: _generateDefaultOptionsFromDefinitions('size') },
exactSize: {
enabled: true,
showInputsInMenu: true, // New setting for showing exact size inputs in menu
predefinedOptions: _generateDefaultOptionsFromDefinitions('exactSize'),
userDefinedOptions: []
},
time: { enabled: true, options: _generateDefaultOptionsFromDefinitions('time') }, // Now includes all new time options and their defaultEnabled states
region: { enabled: true, options: _generateDefaultOptionsFromDefinitions('region') },
site: {
enabled: true,
userDefinedOptions: [
{ id: "gite_defaultsite_wikipedia", label: "Wikipedia", value: "wikipedia.org", isEnabled: true, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_flickr", label: "Flickr", value: "flickr.com", isEnabled: true, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_artstation", label: "ArtStation", value: "artstation.com", isEnabled: true, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_behance", label: "Behance", value: "behance.net", isEnabled: true, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_unsplash", label: "Unsplash", value: "unsplash.com", isEnabled: true, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_pexels", label: "Pexels", value: "pexels.com", isEnabled: true, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_reddit", label: "Reddit", value: "reddit.com", isEnabled: true, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_twitter", label: "X (Twitter)", value: "twitter.com", isEnabled: true, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_commons_wikimedia", label: "Wikimedia Commons", value: "commons.wikimedia.org", isEnabled: true, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_500px", label: "500px", value: "500px.com", isEnabled: true, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_archive", label: "Internet Archive", value: "archive.org", isEnabled: true, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_cara", label: "Cara", value: "cara.app", isEnabled: true, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_deviantart", label: "DeviantArt", value: "deviantart.com", isEnabled: false, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_nasa", label: "NASA", value: "nasa.gov", isEnabled: false, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_dribbble", label: "Dribbble", value: "dribbble.com", isEnabled: false, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_bluesky", label: "Bluesky", value: "bsky.app", isEnabled: false, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_mastodon_social", label: "Mastodon (social)", value: "mastodon.social", isEnabled: false, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_lemmy_world", label: "Lemmy (world)", value: "lemmy.world", isEnabled: false, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_pixabay", label: "Pixabay", value: "pixabay.com", isEnabled: false, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_tumblr", label: "Tumblr", value: "tumblr.com", isEnabled: false, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_steamcommunity", label: "Steam Community", value: "steamcommunity.com", isEnabled: false, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_natgeo", label: "National Geographic", value: "nationalgeographic.com", isEnabled: false, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_magnumphotos", label: "Magnum Photos", value: "magnumphotos.com", isEnabled: false, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_gettyimages", label: "Getty Images", value: "gettyimages.com", isEnabled: false, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_ign", label: "IGN", value: "ign.com", isEnabled: false, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_vogue", label: "Vogue", value: "vogue.com", isEnabled: false, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_archdigest", label: "Architectural Digest", value: "architecturaldigest.com", isEnabled: false, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_nature", label: "Nature Portfolio", value: "nature.com", isEnabled: false, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_cgmeetup", label: "CGMeetup", value: "cgmeetup.com", isEnabled: false, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_artfol", label: "Artfol", value: "artfol.me", isEnabled: false, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_facebook", label: "Facebook", value: "facebook.com", isEnabled: false, isCustom: true, type: 'site_filter' },
{ id: "gite_defaultsite_metmuseum", label: "The Metropolitan Museum of Art", value: "metmuseum.org", isEnabled: false, isCustom: true, type: 'site_filter' },
]
},
aspectRatio: { enabled: true }, color: { enabled: true }, type: { enabled: true },
usageRights: { enabled: true }, fileType: { enabled: true },
}
};
// --- Global State Variables for UI interaction ---
let giteSettings = {};
let giteSettingsPanelElement = null;
let currentGiteSettingsForPanel = {};
let isSettingsPanelOpen = false;
let currentlyOpenGiteMenu = null;
let enhancedFiltersInitializedThisInstance = false;
let customFilterMenus = [];
let activeDatePicker = null;
let datePickerOverlayElement = null;
let resultStatsObserver = null;
let resultStatsRetryTimeout = null;
// --- Logging Utilities ---
function log(...args) { if (DEBUG_MODE) console.log('[GITE]', ...args); }
function error(...args) { console.error('[GITE]', ...args); }
function warn(...args) { console.warn('[GITE]', ...args); }
// --- Internationalization (I18N) Utilities ---
function getLocalizedString(key, lang = CURRENT_LANGUAGE, fallbackText = null) {
const effectiveLang = GITE_I18N_STRINGS[lang] ? lang : 'en';
const primaryText = GITE_I18N_STRINGS[effectiveLang]?.[key];
if (primaryText !== undefined) return primaryText;
let returnedText = fallbackText !== null ? fallbackText : key;
if (effectiveLang !== 'en') {
const enText = GITE_I18N_STRINGS['en']?.[key];
if (enText !== undefined) {
returnedText = enText;
if (DEBUG_MODE) warn(`[i18n] Key "${key}" not found for lang "${lang}". Fell back to "en": "${enText}".`);
} else if (DEBUG_MODE) {
warn(`[i18n] Key "${key}" not found for lang "${lang}" or "en". Using ${fallbackText !== null ? 'provided fallback' : 'key as text'}: "${returnedText}".`);
}
} else if (DEBUG_MODE) {
warn(`[i18n] Key "${key}" not found for lang "en". Using ${fallbackText !== null ? 'provided fallback' : 'key as text'}: "${returnedText}".`);
}
return returnedText;
}
function getLocalizedTextByPath(pathString, lang = CURRENT_LANGUAGE, fallbackText = '') {
if (!pathString || typeof pathString !== 'string') {
if (DEBUG_MODE && pathString !== null) warn(`getLocalizedTextByPath: non-string/null path: ${pathString}`);
return fallbackText;
}
const keys = pathString.split('.');
if (keys.length === 1) return getLocalizedString(keys[0], lang, fallbackText || pathString);
let primaryLangTranslations = GITE_I18N_STRINGS[lang] || GITE_I18N_STRINGS['en'];
let current = primaryLangTranslations;
for (let i = 0; i < keys.length; i++) {
if (current && typeof current === 'object' && keys[i] in current) {
current = current[keys[i]];
} else {
if (lang !== 'en' && GITE_I18N_STRINGS['en']) {
let enCurrent = GITE_I18N_STRINGS['en'];
for (let j = 0; j < keys.length; j++) {
if (enCurrent && typeof enCurrent === 'object' && keys[j] in enCurrent) enCurrent = enCurrent[keys[j]];
else return fallbackText || pathString;
}
return typeof enCurrent === 'string' ? enCurrent : (fallbackText || pathString);
}
return fallbackText || pathString;
}
}
return typeof current === 'string' ? current : (fallbackText || pathString);
}
function initializeCurrentLanguage() {
let preferredLang = 'auto';
if (giteSettings && giteSettings.general && giteSettings.general.selectedLanguage) {
preferredLang = giteSettings.general.selectedLanguage;
}
if (preferredLang !== 'auto' && GITE_I18N_STRINGS[preferredLang]) {
CURRENT_LANGUAGE = preferredLang;
if (DEBUG_MODE) log(`Using preferred lang from settings: ${CURRENT_LANGUAGE}`);
} else {
const browserLangInit = (navigator.language || navigator.userLanguage || 'en').toLowerCase();
if (browserLangInit.startsWith('zh-tw') || browserLangInit.startsWith('zh-hk') || browserLangInit.startsWith('zh-hant')) CURRENT_LANGUAGE = 'zh-TW';
else if (browserLangInit.startsWith('ja')) CURRENT_LANGUAGE = 'ja';
else CURRENT_LANGUAGE = 'en';
if (DEBUG_MODE) log(`Auto-detected browser lang: ${browserLangInit}, Using: ${CURRENT_LANGUAGE}`);
if (preferredLang !== 'auto' && !GITE_I18N_STRINGS[preferredLang] && DEBUG_MODE) {
warn(`Preferred lang "${preferredLang}" was specified but is not supported. Using auto-detected: ${CURRENT_LANGUAGE}.`);
}
}
}
function updateAllLocalizableElements(newLang, parentElement = document) {
const langToUse = newLang || CURRENT_LANGUAGE;
if (DEBUG_MODE) {
const parentId = parentElement === document ? "document" : (parentElement.id || parentElement.tagName || "UnnamedElement");
log(`Updating localizable elements to lang: ${langToUse} within parent: ${parentId}`);
}
if (!parentElement || typeof parentElement.querySelectorAll !== 'function') {
warn(`updateAllLocalizableElements: Invalid parentElement provided.`); return;
}
parentElement.querySelectorAll(`[${GITE_LANG_KEY_ATTR}]`).forEach(element => {
const key = element.getAttribute(GITE_LANG_KEY_ATTR);
const attributeTarget = element.getAttribute('data-gite-lang-target-attr');
if (key) {
const localizedText = getLocalizedString(key, langToUse, key);
if (attributeTarget && element.hasAttribute(attributeTarget)) element.setAttribute(attributeTarget, localizedText);
else if (element.tagName === 'INPUT' && (element.type === 'button' || element.type === 'submit' || element.type === 'reset')) element.value = localizedText;
else if (element.tagName === 'INPUT' && attributeTarget === 'placeholder') element.setAttribute('placeholder', localizedText);
else element.textContent = localizedText;
}
});
}
// --- Settings Panel State Accessors (operate on currentGiteSettingsForPanel) ---
function getSettingFromPanel(path, defaultValue = undefined) {
let obj = currentGiteSettingsForPanel; const parts = path.split('.');
for (let i = 0; i < parts.length; i++) {
if (!obj || typeof obj !== 'object' || !obj.hasOwnProperty(parts[i])) return defaultValue;
obj = obj[parts[i]];
}
return obj;
}
function setSettingInPanel(path, value) {
let obj = currentGiteSettingsForPanel; const parts = path.split('.');
for (let i = 0; i < parts.length - 1; i++) {
if (!obj[parts[i]] || typeof obj[parts[i]] !== 'object') obj[parts[i]] = {};
obj = obj[parts[i]];
}
obj[parts[parts.length - 1]] = value;
}
// --- Date Picker Specific UI Function ---
function closeDatePicker() {
if (activeDatePicker && activeDatePicker.parentNode) {
activeDatePicker.parentNode.removeChild(activeDatePicker);
if (DEBUG_MODE) log("Custom date picker UI removed.");
}
activeDatePicker = null;
if (datePickerOverlayElement && datePickerOverlayElement.parentNode) {
datePickerOverlayElement.parentNode.removeChild(datePickerOverlayElement);
if (DEBUG_MODE) log("Date picker overlay removed.");
}
if (currentlyOpenGiteMenu && currentlyOpenGiteMenu.menuContainer) {
currentlyOpenGiteMenu.menuContainer.style.display = 'none';
if (currentlyOpenGiteMenu.triggerButton) {
currentlyOpenGiteMenu.triggerButton.setAttribute('aria-expanded', 'false');
}
currentlyOpenGiteMenu = null;
}
}
function detectAndApplyThemeClass() {
const htmlElement = document.documentElement;
let effectiveTheme = giteSettings?.general?.themePreference || 'auto';
let isDarkMode = false;
if (effectiveTheme === 'auto') {
const googleThemeDark = document.querySelector('html[data-theme="dark"], body.dark, body.dark-mode, html.dark');
const googleThemeLight = document.querySelector('html[data-theme="light"], body.light, body.light-mode, html.light');
if (googleThemeDark) {
isDarkMode = true;
if (DEBUG_MODE) log(`Theme detection (auto): Google's page is dark.`);
} else if (googleThemeLight) {
isDarkMode = false;
if (DEBUG_MODE) log(`Theme detection (auto): Google's page is light.`);
} else if (window.matchMedia) {
isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (DEBUG_MODE) log(`Theme detection (auto): System prefers dark: ${isDarkMode}`);
} else {
if (DEBUG_MODE) log("Theme detection (auto): window.matchMedia not supported. Defaulting GITE to light theme for auto mode.");
}
} else if (effectiveTheme === 'dark') {
isDarkMode = true;
if (DEBUG_MODE) log(`Theme forced to dark by user setting.`);
} else {
isDarkMode = false;
if (DEBUG_MODE) log(`Theme forced to light by user setting or fallback.`);
}
htmlElement.classList.toggle('gite-detected-dark-theme', isDarkMode);
htmlElement.classList.toggle('gite-detected-light-theme', !isDarkMode);
if (DEBUG_MODE) log(`Applied GITE CSS theme class: .gite-detected-${isDarkMode ? 'dark' : 'light'}-theme.`);
}
// --- CSS Injection ---
function injectCustomHoverStyles() {
const styleId = 'gite-custom-hover-styles';
let styleElement = document.getElementById(styleId);
if (styleElement && !DEBUG_MODE) return;
const GOOGLE_DARK_THEME_SELECTOR = 'html[data-theme="dark"], body.dark, body.dark-mode, html.dark';
const GOOGLE_LIGHT_THEME_SELECTOR = 'html[data-theme="light"], body.light, body.light-mode, html.light';
const cssBaseVariables = `
:root {
--gite-hover-bg-light: #ebebeb; --gite-hover-text-light: var(--uv-styles-color-on-tertiary, #202124);
--gite-separator-light: var(--uv-styles-color-outline, #dadce0); --gite-hover-bg-dark: var(--uv-styles-color-outline, #3c4043);
--gite-hover-text-dark: var(--YLNNHc, #e8eaed); --gite-separator-dark: var(--uv-styles-color-outline, #3c4043);
--gite-hover-bg: var(--gite-hover-bg-light); --gite-hover-text: var(--gite-hover-text-light); --gite-separator-color: var(--gite-separator-light);
--gite-color-swatch-border-light: #ccc; --gite-color-swatch-border-dark: #5f6368; --gite-color-swatch-border: var(--gite-color-swatch-border-light);
--gite-datepicker-bg: var(--uv-styles-color-surface, #fff); --gite-datepicker-text: var(--uv-styles-color-on-surface, #202124);
--gite-datepicker-border: var(--uv-styles-color-outline, #ccc); --gite-datepicker-button-bg: var(--uv-styles-color-primary, #1a73e8);
--gite-datepicker-button-text: var(--uv-styles-color-on-primary, #fff); --gite-datepicker-button-cancel-bg: var(--uv-styles-color-tertiary, #f1f3f4);
--gite-datepicker-button-cancel-text: var(--uv-styles-color-on-tertiary, #202124);
--gite-custom-size-delete-bg-light: #f8d7da; --gite-custom-size-delete-text-light: #721c24; --gite-custom-size-delete-border-light: #f5c6cb;
--gite-custom-size-delete-bg-dark: #5d383b; --gite-custom-size-delete-text-dark: #f5c6cb; --gite-custom-size-delete-border-dark: #8c3a41;
--gite-custom-size-delete-bg: var(--gite-custom-size-delete-bg-light); --gite-custom-size-delete-text: var(--gite-custom-size-delete-text-light); --gite-custom-size-delete-border: var(--gite-custom-size-delete-border-light);
--gite-modal-max-width: 620px;
--gite-modal-max-height: 90vh;
--gite-tab-nav-width: 130px;
--gite-focus-ring-color: #4285f4;
--gite-input-bg: var(--gite-datepicker-bg); --gite-input-text: var(--gite-datepicker-text); --gite-input-border: var(--gite-datepicker-border);
--gite-button-text-color: var(--gite-datepicker-text);
--gite-button-active-text-color: var(--gite-datepicker-button-bg);
--gite-menu-bg: var(--gite-datepicker-bg);
--gite-menu-text-color: var(--gite-datepicker-text);
--gite-menu-border-color: var(--gite-datepicker-border);
--gite-menu-shadow: 0 2px 6px rgba(0,0,0,0.2);
--gite-menu-item-hover-bg: var(--gite-hover-bg);
--gite-menu-item-hover-text: var(--gite-hover-text);
--gite-menu-item-selected-bg: #e8f0fe;
--gite-menu-item-selected-text: #1967d2;
--gite-menu-item-selected-dark-bg: #37404f;
--gite-menu-item-selected-dark-text: #d1e2fc;
--gite-clear-button-text-light: #b3261e;
--gite-clear-button-text-dark: #f2b8b5;
--gite-clear-button-hover-bg-light: #fcede8;
--gite-clear-button-hover-bg-dark: #4f322e;
--gite-panel-font-size: 14px;
--gite-toolbar-font-size: ${giteSettings?.general?.toolbarFontSize || '14px'};
--gite-toolbar-line-height: ${giteSettings?.general?.toolbarLineHeight || '1.5'};
}`;
const cssThemeOverrides = `
html.gite-detected-light-theme, ${GOOGLE_LIGHT_THEME_SELECTOR} {
--gite-hover-bg: var(--gite-hover-bg-light) !important; --gite-hover-text: var(--gite-hover-text-light) !important; --gite-separator-color: var(--gite-separator-light) !important;
--gite-color-swatch-border: var(--gite-color-swatch-border-light) !important; --gite-datepicker-bg: var(--uv-styles-color-surface, #fff) !important;
--gite-datepicker-text: var(--uv-styles-color-on-surface, #202124) !important; --gite-datepicker-border: var(--uv-styles-color-outline, #ccc) !important;
--gite-datepicker-button-bg: var(--uv-styles-color-primary, #1a73e8) !important; --gite-datepicker-button-text: var(--uv-styles-color-on-primary, #fff) !important;
--gite-datepicker-button-cancel-bg: var(--uv-styles-color-tertiary, #f1f3f4) !important; --gite-datepicker-button-cancel-text: var(--uv-styles-color-on-tertiary, #202124) !important;
--gite-custom-size-delete-bg: var(--gite-custom-size-delete-bg-light) !important; --gite-custom-size-delete-text: var(--gite-custom-size-delete-text-light) !important;
--gite-custom-size-delete-border: var(--gite-custom-size-delete-border-light) !important; --gite-button-text-color: var(--uv-styles-color-on-tertiary, #202124) !important;
--gite-button-active-text-color: var(--uv-styles-color-primary, #1a73e8) !important; --gite-menu-bg: var(--uv-styles-color-surface, #fff) !important;
--gite-menu-text-color: var(--uv-styles-color-on-surface, #202124) !important; --gite-menu-border-color: var(--uv-styles-color-outline, #ccc) !important;
--gite-menu-item-selected-bg: #e8f0fe !important; --gite-menu-item-selected-text: #1967d2 !important;
--gite-clear-button-text-color: var(--gite-clear-button-text-light) !important; --gite-clear-button-hover-background: var(--gite-clear-button-hover-bg-light) !important;
}
html.gite-detected-dark-theme, ${GOOGLE_DARK_THEME_SELECTOR} {
--gite-hover-bg: var(--gite-hover-bg-dark) !important; --gite-hover-text: var(--gite-hover-text-dark) !important; --gite-separator-color: var(--gite-separator-dark) !important;
--gite-color-swatch-border: var(--gite-color-swatch-border-dark) !important; --gite-datepicker-bg: #202124 !important;
--gite-datepicker-text: #e8eaed !important; --gite-datepicker-border: #5f6368 !important;
--gite-datepicker-button-bg: #8ab4f8 !important; --gite-datepicker-button-text: #202124 !important;
--gite-datepicker-button-cancel-bg: #303134 !important; --gite-datepicker-button-cancel-text: #bdc1c6 !important;
--gite-custom-size-delete-bg: var(--gite-custom-size-delete-bg-dark) !important; --gite-custom-size-delete-text: var(--gite-custom-size-delete-text-dark) !important;
--gite-custom-size-delete-border: var(--gite-custom-size-delete-border-dark) !important; --gite-button-text-color: var(--YLNNHc, #e8eaed) !important;
--gite-button-active-text-color: #8ab4f8 !important; --gite-menu-bg: #292a2d !important;
--gite-menu-text-color: #e8eaed !important; --gite-menu-border-color: #5f6368 !important;
--gite-menu-item-selected-bg: var(--gite-menu-item-selected-dark-bg) !important; --gite-menu-item-selected-text: var(--gite-menu-item-selected-dark-text) !important;
--gite-clear-button-text-color: var(--gite-clear-button-text-dark) !important; --gite-clear-button-hover-background: var(--gite-clear-button-hover-bg-dark) !important;
}
@media (prefers-color-scheme: light) {
:root:not(.gite-detected-dark-theme):not(${GOOGLE_DARK_THEME_SELECTOR}) {
--gite-hover-bg: var(--gite-hover-bg-light); --gite-hover-text: var(--gite-hover-text-light); --gite-separator-color: var(--gite-separator-light);
--gite-color-swatch-border: var(--gite-color-swatch-border-light); --gite-datepicker-bg: var(--uv-styles-color-surface, #fff);
--gite-datepicker-text: var(--uv-styles-color-on-surface, #202124); --gite-datepicker-border: var(--uv-styles-color-outline, #ccc);
--gite-datepicker-button-bg: var(--uv-styles-color-primary, #1a73e8); --gite-datepicker-button-text: var(--uv-styles-color-on-primary, #fff);
--gite-datepicker-button-cancel-bg: var(--uv-styles-color-tertiary, #f1f3f4); --gite-datepicker-button-cancel-text: var(--uv-styles-color-on-tertiary, #202124);
--gite-custom-size-delete-bg: var(--gite-custom-size-delete-bg-light); --gite-custom-size-delete-text: var(--gite-custom-size-delete-text-light);
--gite-custom-size-delete-border: var(--gite-custom-size-delete-border-light); --gite-button-text-color: var(--uv-styles-color-on-tertiary, #202124);
--gite-button-active-text-color: var(--uv-styles-color-primary, #1a73e8); --gite-menu-bg: var(--uv-styles-color-surface, #fff);
--gite-menu-text-color: var(--uv-styles-color-on-surface, #202124); --gite-menu-border-color: var(--uv-styles-color-outline, #ccc);
--gite-menu-item-selected-bg: #e8f0fe; --gite-menu-item-selected-text: #1967d2;
--gite-clear-button-text-color: var(--gite-clear-button-text-light); --gite-clear-button-hover-background: var(--gite-clear-button-hover-bg-light);
}
}
@media (prefers-color-scheme: dark) {
:root:not(.gite-detected-light-theme):not(${GOOGLE_LIGHT_THEME_SELECTOR}) {
--gite-hover-bg: var(--gite-hover-bg-dark); --gite-hover-text: var(--gite-hover-text-dark); --gite-separator-color: var(--gite-separator-dark);
--gite-color-swatch-border: var(--gite-color-swatch-border-dark); --gite-datepicker-bg: #202124;
--gite-datepicker-text: #e8eaed; --gite-datepicker-border: #5f6368;
--gite-datepicker-button-bg: #8ab4f8; --gite-datepicker-button-text: #202124;
--gite-datepicker-button-cancel-bg: #303134; --gite-datepicker-button-cancel-text: #bdc1c6;
--gite-custom-size-delete-bg: var(--gite-custom-size-delete-bg-dark); --gite-custom-size-delete-text: var(--gite-custom-size-delete-text-dark);
--gite-custom-size-delete-border: var(--gite-custom-size-delete-border-dark); --gite-button-text-color: var(--YLNNHc, #e8eaed);
--gite-button-active-text-color: #8ab4f8; --gite-menu-bg: #292a2d;
--gite-menu-text-color: #e8eaed; --gite-menu-border-color: #5f6368;
--gite-menu-item-selected-bg: var(--gite-menu-item-selected-dark-bg); --gite-menu-item-selected-text: var(--gite-menu-item-selected-dark-text);
--gite-clear-button-text-color: var(--gite-clear-button-text-dark); --gite-clear-button-hover-background: var(--gite-clear-button-hover-bg-dark);
}
}
`;
const cssComponentStyles = `
${NATIVE_TOOLBAR_SELECTOR_TO_HIDE} { display: none !important; }
#${GITE_TOOLBAR_CONTAINER_ID} {
display: flex; align-items: center; padding: 4px 16px;
background-color: var(--gite-datepicker-bg); border-bottom: 1px solid var(--gite-separator-color);
box-sizing: border-box; flex-wrap: wrap;
font-size: var(--gite-toolbar-font-size);
line-height: var(--gite-toolbar-line-height);
}
.gite-filter-buttons-group {
display: flex;
flex-wrap: wrap;
gap: 8px;
align-items: center;
flex-grow: 1;
}
#${GITE_TOOLBAR_CONTAINER_ID} .gite-filter-trigger-button,
#${GITE_TOOLBAR_CONTAINER_ID} .gite-advanced-search-button,
#${GITE_TOOLBAR_CONTAINER_ID} .gite-clear-button,
#${GITE_TOOLBAR_CONTAINER_ID} #${GITE_SETTINGS_BUTTON_ID} {
display: inline-flex;
align-items: center;
font-size: inherit;
line-height: inherit;
}
.gite-filter-button-wrapper {
position: relative; display: inline-flex;
}
.gite-filter-trigger-button {
padding: 7px 10px; border-radius: 4px; cursor: pointer;
font-family: inherit;
color: var(--gite-button-text-color);
background-color: transparent;
border: 1px solid transparent;
white-space: nowrap;
}
.gite-filter-trigger-button:hover {
background-color: var(--gite-hover-bg);
}
.gite-filter-trigger-button:focus-visible {
outline: 1px solid var(--gite-focus-ring-color) !important;
outline-offset: -1px !important;
box-shadow: none !important;
}
.gite-filter-trigger-button.gite-filter-active .gite-filter-button-text {
color: var(--gite-button-active-text-color);
font-weight: 500;
}
.gite-filter-trigger-button.gite-filter-active .gite-dropdown-arrow {
border-top-color: var(--gite-button-active-text-color);
}
.gite-filter-button-text {
margin-right: 6px;
display: inline-flex;
align-items: center;
}
.gite-dropdown-arrow {
width: 0; height: 0;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid var(--gite-button-text-color);
align-self: center; margin-top: 1px;
}
.gite-custom-menu-container {
display: none; position: absolute;
top: calc(100% + 2px); left: 0;
background-color: var(--gite-menu-bg);
border: 1px solid var(--gite-menu-border-color);
border-radius: 4px;
box-shadow: var(--gite-menu-shadow);
z-index: 2000;
min-width: 220px; max-height: 450px;
overflow-y: auto; padding: 6px 0;
font-size: var(--gite-toolbar-font-size);
}
.gite-custom-menu-container ul {
list-style: none; padding: 0; margin: 0;
}
li.${GITE_MENU_ITEM_CLASS} {
padding: 0; margin: 0;
}
li.${GITE_MENU_ITEM_CLASS}:focus {
outline: none;
}
li.${GITE_MENU_ITEM_CLASS} > a {
display: flex;
align-items: center;
padding: 8px 16px;
color: var(--gite-menu-text-color);
text-decoration: none; cursor: pointer;
white-space: nowrap;
font-size: inherit;
outline: none;
}
li.${GITE_MENU_ITEM_CLASS} > a:hover,
li.${GITE_MENU_ITEM_CLASS}.gite-menu-item-focused > a {
background-color: var(--gite-menu-item-hover-bg);
color: var(--gite-menu-item-hover-text);
}
li.${GITE_MENU_ITEM_CLASS}.gite-menu-item-selected > a {
background-color: var(--gite-menu-item-selected-bg);
color: var(--gite-menu-item-selected-text);
font-weight: 500;
}
html.gite-detected-dark-theme li.${GITE_MENU_ITEM_CLASS}.gite-menu-item-selected > a {
background-color: var(--gite-menu-item-selected-dark-bg);
color: var(--gite-menu-item-selected-dark-text);
}
li.${GITE_MENU_ITEM_CLASS}[role="separator"] > div {
margin-top: 5px !important; margin-bottom: 5px !important;
border-top: 1px solid var(--gite-separator-color) !important;
}
li.${GITE_MENU_ITEM_CLASS} .gite-color-swatch-item {
display: flex; align-items: center; justify-content: center;
padding: 2px 0 !important;
}
.gite-color-palette-container { display: grid; grid-template-columns: repeat(6, 1fr); gap: 4px; padding: 6px 16px; align-items: center; justify-items: center;}
.gite-color-swatch { display: block; width: 20px; height: 20px; border: 1px solid var(--gite-color-swatch-border); border-radius: 4px; cursor: pointer; box-sizing: border-box; }
.gite-color-swatch:hover { border-color: var(--gite-hover-text); }
html.gite-detected-light-theme .gite-color-swatch[style*="background:#fff"], html.gite-detected-light-theme .gite-color-swatch[style*="background:white"] { border-color: #e0e0e0; }
.gite-datepicker-overlay { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background-color: rgba(0, 0, 0, 0.65); z-index: 20000; }
.gite-custom-date-picker { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: var(--gite-datepicker-bg); color: var(--gite-datepicker-text); border: 1px solid var(--gite-datepicker-border); box-shadow: 0 4px 8px rgba(0,0,0,0.2); padding: 20px; z-index: 20001; border-radius: 8px; }
.gite-custom-date-picker div { margin-bottom: 10px; } .gite-custom-date-picker label { margin-right: 5px; }
.gite-custom-date-picker input[type="date"] { padding: 5px; border: 1px solid var(--gite-datepicker-border); border-radius: 4px; background-color: var(--gite-datepicker-bg); color: var(--gite-datepicker-text); }
html.gite-detected-dark-theme .gite-custom-date-picker input[type="date"] { color-scheme: dark; } html.gite-detected-light-theme .gite-custom-date-picker input[type="date"] { color-scheme: light; }
.gite-custom-date-picker button { padding: 8px 15px; border-radius: 4px; cursor: pointer; margin-right: 10px; border: 1px solid var(--gite-datepicker-button-bg); background-color: var(--gite-datepicker-button-bg); color: var(--gite-datepicker-button-text); }
.gite-custom-date-picker button:hover { opacity: 0.9; }
.gite-custom-date-picker button.gite-date-cancel { background-color: var(--gite-datepicker-button-cancel-bg); color: var(--gite-datepicker-button-cancel-text); border-color: var(--gite-datepicker-border); }
.gite-active-color-indicator {
display: inline-block;
width: 16px; height: 16px;
border: 1px solid var(--gite-color-swatch-border);
border-radius: 3px;
vertical-align: middle;
margin-right: 0.3em;
}
html.gite-detected-light-theme .gite-active-color-indicator[style*="background-color: rgb(255, 255, 255)"], html.gite-detected-light-theme .gite-active-color-indicator[style*="background-color:#fff"], html.gite-detected-light-theme .gite-active-color-indicator[style*="background-color:white"] { border-color: #bbb; }
.gite-custom-imagesize-input-area { padding: 8px 16px; margin-bottom: 8px; border-bottom: 1px solid var(--gite-separator-color); }
.gite-imagesize-input-controls { display: flex; align-items: center; } .gite-imagesize-input-controls > span { color: var(--gite-datepicker-text); margin: 0 5px; }
.gite-custom-imagesize-input-area .gite-imagesize-input-controls input[type="number"] { width: 55px; padding: 6px; border: 1px solid var(--gite-datepicker-border); border-radius: 4px; background-color: var(--gite-datepicker-bg); color: var(--gite-datepicker-text); text-align: center; -moz-appearance: textfield; }
.gite-custom-imagesize-input-area input[type="number"]::-webkit-outer-spin-button, .gite-custom-imagesize-input-area input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.gite-custom-imagesize-input-area button { padding: 6px 0px; border-radius: 4px; cursor: pointer; border-width: 1px; border-style: solid; font-size: 16px; font-weight: bold; line-height: 1; min-width: 40px; text-align: center; }
#gite-imagesize-apply-menu { margin-left: 8px; border-color: var(--gite-datepicker-button-bg); background-color: var(--gite-datepicker-button-bg); color: var(--gite-datepicker-button-text); padding: 6px 10px !important; font-size: 1em !important; font-weight: normal !important; min-width: auto !important; }
#gite-imagesize-apply-menu:hover { opacity: 0.9; }
html.gite-detected-dark-theme .gite-custom-imagesize-input-area .gite-imagesize-input-controls input[type="number"] { color-scheme: dark; }
html.gite-detected-light-theme .gite-custom-imagesize-input-area .gite-imagesize-input-controls input[type="number"] { color-scheme: light; }
#${GITE_TOOLBAR_CONTAINER_ID} #${GITE_SETTINGS_BUTTON_ID}.BaegVc {
margin-left: auto;
padding: 2px 8px !important;
cursor: pointer;
display: inline-flex !important;
align-items: center !important;
font-size: 1.2em;
line-height: normal !important;
height: auto !important;
color: var(--gite-button-text-color);
}
#${GITE_TOOLBAR_CONTAINER_ID} #${GITE_SETTINGS_BUTTON_ID}.BaegVc:hover { background-color: var(--gite-hover-bg) !important; }
#${GITE_TOOLBAR_CONTAINER_ID} #${GITE_SETTINGS_BUTTON_ID}.BaegVc:focus-visible { outline: 1px solid var(--gite-focus-ring-color, #4285f4) !important; outline-offset: -1px !important; box-shadow: none !important; }
#${GITE_TOOLBAR_CONTAINER_ID} .gite-clear-button {
padding: 7px 10px;
color: var(--gite-clear-button-text-color);
border-radius: 4px;
}
#${GITE_TOOLBAR_CONTAINER_ID} .gite-clear-button:hover {
background-color: var(--gite-clear-button-hover-background); text-decoration: none;
}
#${GITE_TOOLBAR_CONTAINER_ID} .gite-advanced-search-button {
padding: 7px 10px;
color: var(--gite-button-text-color);
border-radius: 4px; text-decoration: none; cursor: pointer;
}
#${GITE_TOOLBAR_CONTAINER_ID} .gite-advanced-search-button:hover {
background-color: var(--gite-hover-bg);
}
#${GITE_RESULT_STATS_DISPLAY_ID} {
font-size: 0.9em;
color: var(--gite-button-text-color);
margin-left: 8px;
margin-right: 12px;
white-space: nowrap;
align-self: center;
}
`;
const cssSettingsPanelStyles = `
.gite-modal { display: none; position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; z-index: 20000 !important; font-family: Arial, sans-serif; font-size: var(--gite-panel-font-size); align-items: center; justify-content: center; }
.gite-modal-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; background-color: rgba(0, 0, 0, 0.65); }
.gite-modal-content { position: relative; background-color: var(--gite-datepicker-bg); color: var(--gite-datepicker-text); border: 1px solid var(--gite-datepicker-border); border-radius: 8px; box-shadow: 0 5px 15px rgba(0,0,0,0.3); width: 90%; max-width: var(--gite-modal-max-width); max-height: var(--gite-modal-max-height); display: flex; flex-direction: column; overflow: hidden; }
.gite-modal-header { padding: 12px 15px; border-bottom: 1px solid var(--gite-separator-color); display: flex; justify-content: space-between; align-items: center; }
.gite-modal-header h2 { margin: 0; font-size: 1.2em; font-weight: normal; }
.gite-modal-close-btn { background: none; border: none; font-size: 1.6em; line-height: 1; padding: 0 4px; color: var(--gite-datepicker-text); cursor: pointer; opacity: 0.7; } .gite-modal-close-btn:hover { opacity: 1; }
.gite-modal-body { padding: 0; flex-grow: 1; overflow: hidden; display: flex; }
.gite-tabs-navigation { flex: 0 0 var(--gite-tab-nav-width); margin-right: 0px; border-right: 1px solid var(--gite-separator-color); overflow-y: auto; padding: 8px; box-sizing: border-box; }
.gite-tabs-navigation ul { list-style: none; padding: 0; margin: 0; }
.gite-tabs-navigation button[role="tab"] { display: block; width: 100%; padding: 9px 12px; text-align: left; background: none; border: 1px solid transparent; border-radius: 4px; margin-bottom: 5px; cursor: pointer; color: var(--gite-datepicker-text); font-size: 1em; box-sizing: border-box; word-break: break-word; line-height: 1.4; }
.gite-tabs-navigation button[role="tab"]:hover:not([disabled]) { background-color: var(--gite-hover-bg); }
.gite-tabs-navigation button[role="tab"][aria-selected="true"] { background-color: var(--gite-datepicker-button-bg); color: var(--gite-datepicker-button-text); font-weight: bold; }
.gite-tabs-navigation button[role="tab"]:focus-visible:not([disabled]) { border-color: var(--gite-focus-ring-color); outline: none; box-shadow: 0 0 0 1px var(--gite-focus-ring-color) inset; }
.gite-tabs-navigation button[role="tab"][disabled] { opacity: 0.5; cursor: not-allowed; }
.gite-tabs-content { flex-grow: 1; overflow-y: auto; padding: 15px; box-sizing: border-box; line-height: 1.5; }
.gite-tab-panel { display: none; animation: giteFadeIn 0.3s ease-out; } .gite-tab-panel.gite-active-panel { display: block; }
@keyframes giteFadeIn { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: translateY(0); } }
.gite-setting-group { margin-bottom: 12px; display: flex; flex-direction: column; align-items: flex-start; gap: 5px; }
.gite-setting-group label { font-weight: 500; font-size: 1em; }
.gite-setting-group select, .gite-setting-group input[type="text"], .gite-setting-group input[type="number"] { width: 100%; padding: 7px 9px; border: 1px solid var(--gite-input-border); border-radius: 4px; background-color: var(--gite-input-bg); color: var(--gite-input-text); box-sizing: border-box; height: auto; font-size: 0.95em; line-height: 1.4; }
.gite-setting-group input[type="checkbox"] { width: auto; margin-top: 3px; align-self: flex-start; transform: scale(1.1); }
html.gite-detected-dark-theme .gite-setting-group select { color-scheme: dark; }
.gite-modal-footer { padding: 10px 15px; border-top: 1px solid var(--gite-separator-color); display: flex; justify-content: space-between; align-items: center; background-color: rgba(0,0,0,0.03); } html.gite-detected-dark-theme .gite-modal-footer { background-color: rgba(255,255,255,0.03); }
.gite-modal-footer button { padding: 8px 14px; border-radius: 4px; cursor: pointer; margin-left: 7px; border: 1px solid var(--gite-datepicker-border); background-color: var(--gite-datepicker-button-cancel-bg); color: var(--gite-datepicker-button-cancel-text); font-size: 0.9em; line-height: 1.4; }
.gite-modal-footer button:hover { opacity: 0.9; } .gite-modal-footer .gite-primary-btn { background-color: var(--gite-datepicker-button-bg); color: var(--gite-datepicker-button-text); border-color: var(--gite-datepicker-button-bg); font-weight: bold; }
.gite-exactsize-panel-section, .gite-options-panel-section { margin-bottom: 18px; padding-bottom:12px; border-bottom: 1px dashed var(--gite-separator-color); } .gite-exactsize-panel-section:last-child, .gite-options-panel-section:last-child { border-bottom: none; margin-bottom: 0; }
.gite-exactsize-panel-section h3, .gite-options-panel-section h3 { font-size: 1.05em; margin-top: 0; margin-bottom: 10px; color: var(--gite-datepicker-text); opacity: 0.9; font-weight:500; }
.gite-exactsize-options-list, .gite-options-list { list-style: none; padding: 0; margin: 0 0 12px 0; max-height: 150px; overflow-y:auto; border: 1px solid var(--gite-input-border); border-radius: 4px; }
.gite-exactsize-options-list li, .gite-options-list li { display: flex; align-items: center; padding: 7px 10px; border-bottom: 1px solid var(--gite-separator-light); gap: 8px; font-size:0.95em; line-height:1.4; }
.gite-exactsize-options-list li:last-child, .gite-options-list li:last-child { border-bottom: none; }
.gite-exactsize-options-list li .gite-option-text, .gite-options-list li .gite-option-text { flex-grow: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gite-exactsize-options-list li .gite-option-value-hint, .gite-options-list li .gite-option-value-hint { font-size: 0.85em; color: #777; margin-left: auto; flex-shrink: 0; padding-left: 5px; }
html.gite-detected-dark-theme .gite-exactsize-options-list li .gite-option-value-hint, html.gite-detected-dark-theme .gite-options-list li .gite-option-value-hint { color: #aaa; }
.gite-exactsize-options-list li input[type="checkbox"], .gite-options-list li input[type="checkbox"] { margin-right: 6px; flex-shrink:0; transform: scale(1.1); }
.gite-exactsize-options-list li .gite-edit-label-input { flex-grow: 1; padding: 4px 6px; font-size: 0.95em; border: 1px solid var(--gite-input-border); border-radius: 3px; background-color: var(--gite-input-bg); color: var(--gite-input-text); }
.gite-exactsize-options-list li .gite-actions { margin-left:auto; display: flex; gap: 5px; flex-shrink:0; }
.gite-exactsize-options-list li .gite-actions button { background: none; border: none; cursor: pointer; opacity: 0.7; padding: 3px 5px; font-size: 1em; color: var(--gite-datepicker-text); }
.gite-exactsize-options-list li .gite-actions button:hover { opacity: 1; }
.gite-add-exact-size-form { display: flex; flex-direction: column; gap: 10px; margin-top: 12px; }
.gite-add-exact-size-form .gite-form-row { display: flex; gap: 10px; align-items: flex-end; }
.gite-add-exact-size-form div:not(.gite-form-row) { display: flex; flex-direction: column; gap: 3px; }
.gite-add-exact-size-form label { font-size: 0.9em; margin-bottom: 0px; opacity: 0.85; }
.gite-add-exact-size-form input[type="number"], .gite-add-exact-size-form input[type="text"] { width:100%; padding: 7px 9px; border: 1px solid var(--gite-input-border); border-radius: 4px; background-color: var(--gite-input-bg); color: var(--gite-input-text); box-sizing: border-box; height: auto; font-size:0.95em; line-height: 1.4; }
.gite-add-exact-size-form .gite-form-row input[type="number"] { width: 70px; text-align: center; -moz-appearance: textfield; }
.gite-add-exact-size-form input[type="number"]::-webkit-outer-spin-button, .gite-add-exact-size-form input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.gite-add-exact-size-form button[type="submit"] { width:100%; padding: 8px 12px; font-size: 0.95em; height: auto; background-color: var(--gite-datepicker-button-bg); color: var(--gite-datepicker-button-text); border: 1px solid var(--gite-datepicker-button-bg); margin-top: 10px; line-height:1.4; }
html.gite-detected-dark-theme .gite-add-exact-size-form input { color-scheme: dark; }
.gite-panel-category-footer { margin-top:15px; padding-top:10px; border-top: 1px dashed var(--gite-separator-color); text-align: right;}
.gite-reset-category-btn { font-size: 0.9em !important; padding: 6px 12px !important; line-height:1.4; }
#gite-panel-region .gite-options-list { max-height: 220px; }
#gite-panel-sitesearch .gite-exactsize-options-list { max-height: 180px; }
#gite-panel-sitesearch .gite-add-new-site-form .gite-form-row { display: flex; gap: 10px; align-items: flex-start; }
#gite-panel-sitesearch .gite-add-new-site-form .gite-form-row > div { flex: 1; }
`;
const finalCss = `${cssBaseVariables}\n${cssThemeOverrides}\n${cssComponentStyles}\n${cssSettingsPanelStyles}`;
if (!styleElement) {
styleElement = document.createElement('style'); styleElement.id = styleId; styleElement.type = 'text/css';
if (document.head) document.head.appendChild(styleElement);
else { error("Document head not found. Cannot inject GITE CSS."); return; }
}
if (styleElement.textContent !== finalCss || DEBUG_MODE) {
styleElement.textContent = finalCss;
if (DEBUG_MODE) log("Injected/Updated GITE CSS styles.");
}
}
function _getRawDefinitionsForCategory(categoryKey) {
if (categoryKey === 'color') {
const prefixes = GITE_OPTION_DEFINITIONS.color.prefixOptions || [];
const palettes = GITE_OPTION_DEFINITIONS.color.paletteColors || [];
return [...prefixes, ...palettes];
}
return GITE_OPTION_DEFINITIONS[categoryKey] || [];
}
function getFilterOptionsFromSettings(categoryKey) {
const managedOptionCategories = ['size', 'exactSize', 'time', 'region', 'site'];
const categoryConfigFromSettings = giteSettings.filters ? giteSettings.filters[categoryKey] : undefined;
if (categoryConfigFromSettings && categoryConfigFromSettings.enabled === false) {
if (DEBUG_MODE) log(`Filter category '${categoryKey}' is disabled by top-level setting. No options will be shown.`);
return [];
}
if (!categoryConfigFromSettings && !managedOptionCategories.includes(categoryKey) && DEBUG_MODE) {
log(`Category '${categoryKey}' (non-managed) not found in giteSettings.filters. Assuming enabled and using GITE_OPTION_DEFINITIONS.`);
}
let optionsToProcess = [];
if (managedOptionCategories.includes(categoryKey)) {
const fallbackDefaultCatConf = GITE_DEFAULT_SETTINGS.filters[categoryKey];
if (!categoryConfigFromSettings) {
if (DEBUG_MODE) warn(`Managed category '${categoryKey}' missing from giteSettings.filters. Using GITE_DEFAULT_SETTINGS for its structure.`);
if (categoryKey === 'site') {
const predefinedSiteOptions = GITE_OPTION_DEFINITIONS.site ? JSON.parse(JSON.stringify(GITE_OPTION_DEFINITIONS.site)) : [];
const userDefinedSiteOptions = (fallbackDefaultCatConf && fallbackDefaultCatConf.userDefinedOptions) ? fallbackDefaultCatConf.userDefinedOptions : [];
optionsToProcess = [...predefinedSiteOptions, ...userDefinedSiteOptions];
} else if (categoryKey === 'exactSize') {
optionsToProcess = fallbackDefaultCatConf.predefinedOptions || [];
optionsToProcess = [...optionsToProcess, ...( (fallbackDefaultCatConf && fallbackDefaultCatConf.userDefinedOptions) || [])];
} else { optionsToProcess = fallbackDefaultCatConf.options || []; }
} else {
if (categoryKey === 'exactSize') {
const predefined = categoryConfigFromSettings.predefinedOptions || (fallbackDefaultCatConf? fallbackDefaultCatConf.predefinedOptions : []) || [];
const userDefined = categoryConfigFromSettings.userDefinedOptions || [];
optionsToProcess = [...predefined, ...userDefined];
} else if (categoryKey === 'site') {
const predefinedSiteOptions = GITE_OPTION_DEFINITIONS.site ? JSON.parse(JSON.stringify(GITE_OPTION_DEFINITIONS.site)) : [];
const anySiteOption = predefinedSiteOptions.find(opt => opt.id === "gite_site_any" && opt.defaultEnabled === true);
const userDefinedSiteOptions = categoryConfigFromSettings.userDefinedOptions || [];
optionsToProcess = [];
if (anySiteOption) { optionsToProcess.push({ ...anySiteOption, isEnabled: anySiteOption.defaultEnabled !== undefined ? anySiteOption.defaultEnabled : true }); }
optionsToProcess = [...optionsToProcess, ...userDefinedSiteOptions];
if(DEBUG_MODE && categoryKey === 'site') log(`[GITE DEBUG] Site optionsToProcess (merged with explicit Any site):`, JSON.parse(JSON.stringify(optionsToProcess)));
} else { optionsToProcess = categoryConfigFromSettings.options || (fallbackDefaultCatConf ? fallbackDefaultCatConf.options : []) || []; }
}
} else { optionsToProcess = _getRawDefinitionsForCategory(categoryKey); }
const processedOptions = []; let filterTitleKey;
switch (categoryKey) {
case 'exactSize': filterTitleKey = 'filter_title_exact_size'; break;
case 'aspectRatio': filterTitleKey = 'filter_title_aspect_ratio'; break;
case 'usageRights': filterTitleKey = 'filter_title_usage_rights'; break;
case 'fileType': filterTitleKey = 'filter_title_file_type'; break;
case 'site': filterTitleKey = 'filter_title_site_search'; break;
default: filterTitleKey = `filter_title_${categoryKey.toLowerCase()}`;
}
const originalTitleEng = getLocalizedString(filterTitleKey, 'en', categoryKey);
optionsToProcess.forEach(optDef => {
let finalOptionIsEnabled;
if (optDef.isCustom) finalOptionIsEnabled = optDef.isEnabled;
else if (categoryKey === 'site' && optDef.id === 'gite_site_any') finalOptionIsEnabled = optDef.defaultEnabled !== undefined ? optDef.defaultEnabled : true;
else if (managedOptionCategories.includes(categoryKey) && !optDef.isCustom && categoryConfigFromSettings) finalOptionIsEnabled = optDef.isEnabled;
else if (optDef.type === 'separator') finalOptionIsEnabled = optDef.isEnabled !== undefined ? optDef.isEnabled : true;
else finalOptionIsEnabled = optDef.defaultEnabled !== undefined ? optDef.defaultEnabled : true;
if (finalOptionIsEnabled) {
let displayText; let textSourceKey = optDef.textKey || optDef.nameKey;
if ((categoryKey === 'exactSize' || categoryKey === 'site') && optDef.isCustom && optDef.label) displayText = optDef.label;
else if (categoryConfigFromSettings && optDef.customText !== null && optDef.customText !== undefined) displayText = optDef.customText;
else if (textSourceKey) displayText = getLocalizedString(textSourceKey, CURRENT_LANGUAGE, optDef.id);
else if (optDef.type === 'separator') displayText = '---';
else {
displayText = optDef.value || optDef.id || (optDef.type === 'palette' ? optDef.hex : `[${optDef.id || 'Option'}]`);
if (DEBUG_MODE && optDef.type !== 'separator') warn(`Option id "${optDef.id || optDef.value || optDef.hex}" in category "${categoryKey}" is missing a textKey or custom label. Fallback: ${displayText}`);
}
const processedOpt = { ...optDef, text: displayText, isEnabled: finalOptionIsEnabled, originalTitleEn: originalTitleEng, categoryKey: categoryKey, paramName: optDef.paramName || (categoryKey === 'site' || categoryKey === 'exactSize' ? 'q' : null) };
processedOptions.push(processedOpt);
}
});
if (DEBUG_MODE) {
if (processedOptions.length > 0) log(`Processed ${processedOptions.length} displayable options for filter category '${categoryKey}'.`);
else if (categoryConfigFromSettings && categoryConfigFromSettings.enabled === true && optionsToProcess.length > 0) {
if (categoryKey === 'site' && optionsToProcess.some(opt => opt.id === 'gite_site_any')) log(`Filter category '${categoryKey}' is enabled. User-defined site options might all be disabled, or an issue prevented processing options other than 'Any site'.`);
else log(`Filter category '${categoryKey}' is enabled, but all its defined sub-options appear to be disabled or were filtered out in settings. No options will be shown in its menu.`);
}
if (categoryKey === 'site') log(`[GITE DEBUG] Final processedOptions for site:`, JSON.parse(JSON.stringify(processedOptions)));
}
return processedOptions;
}
function getCustomExactSizes() {
if (giteSettings && giteSettings.filters && giteSettings.filters.exactSize && giteSettings.filters.exactSize.userDefinedOptions) {
return giteSettings.filters.exactSize.userDefinedOptions.map(opt => ({
id: opt.id, text: opt.label || `${opt.width}x${opt.height}`, value: opt.value,
width: opt.width, height: opt.height, isEnabled: opt.isEnabled,
isCustom: true, type: 'imagesize'
}));
}
return [];
}
function saveCustomExactSizes(sizes) {
if (!giteSettings || !giteSettings.filters || !giteSettings.filters.exactSize) {
warn("giteSettings not properly initialized for saving exact sizes. Aborting saveCustomExactSizes."); return;
}
giteSettings.filters.exactSize.userDefinedOptions = sizes.map(s => ({
id: s.id || `user_exact_${s.width}x${s.height}_${Date.now()}`, width: parseInt(s.width, 10),
height: parseInt(s.height, 10), label: s.label || s.text || `${s.width}x${s.height}`,
value: s.value || `${s.width}x${s.height}`, type: "imagesize", isCustom: true,
isEnabled: s.isEnabled !== undefined ? s.isEnabled : true,
}));
if (typeof GM_setValue === 'function') {
try {
GM_setValue(GITE_SETTINGS_GM_KEY, JSON.stringify(giteSettings));
if (DEBUG_MODE) log("Updated exact sizes in giteSettings via saveCustomExactSizes and persisted to GM storage.");
} catch (e) { error("Error persisting giteSettings (during saveCustomExactSizes):", e); alert(getLocalizedString('alert_generic_error_saving')); }
} else { warn("GM_setValue is not available. Custom exact sizes cannot be saved persistently."); }
}
// --- URL Construction Logic ---
function _updateSingleTbsParam(currentTbs, keyPrefix, newValue, isColorSpecificPalette = false) {
let tbsParts = currentTbs ? currentTbs.split(',') : [];
const fullPrefixToMatch = keyPrefix + ":";
tbsParts = tbsParts.filter(part => !part.startsWith(fullPrefixToMatch));
if (isColorSpecificPalette) {
tbsParts = tbsParts.filter(part => part !== "ic:specific" && !part.startsWith("isc:"));
if (newValue && newValue !== "") { tbsParts.push("ic:specific"); tbsParts.push(keyPrefix + ":" + newValue); }
} else { if (newValue && newValue !== "") tbsParts.push(keyPrefix + ":" + newValue); }
return tbsParts.filter(part => part && part.trim() !== "").join(',');
}
function _clearTbsCategory(currentTbs, prefixesToClear) {
if (!currentTbs) return ""; let tbsParts = currentTbs.split(',');
prefixesToClear.forEach(prefix => { tbsParts = tbsParts.filter(part => !part.startsWith(prefix + ":")); });
if (prefixesToClear.some(p => p === 'ic' || p === 'isc')) tbsParts = tbsParts.filter(part => part !== "ic:specific");
return tbsParts.filter(part => part && part.trim() !== "").join(',');
}
function _buildTbsUrlPart(targetUrl, option) {
let newTbsString = targetUrl.searchParams.get('tbs') || "";
const tbsValToProcess = option.tbsValue; const categoryKeyFromOption = option.categoryKey;
if (tbsValToProcess === "" || tbsValToProcess === undefined || tbsValToProcess === null) {
let prefixesToClear = [];
if (categoryKeyFromOption === 'type') prefixesToClear = ["itp"];
else if (categoryKeyFromOption === 'time') prefixesToClear = ["qdr", "cdr"];
else if (categoryKeyFromOption === 'usageRights') prefixesToClear = ["sur"];
else if (categoryKeyFromOption === 'color') { prefixesToClear = ["ic", "isc"]; targetUrl.searchParams.delete('imgc'); }
else if (categoryKeyFromOption === 'size' && option.value === "") {
prefixesToClear = ["isz", "islt", "isilu"];
targetUrl.searchParams.delete('imgsz');
}
newTbsString = _clearTbsCategory(newTbsString, prefixesToClear);
} else {
if (categoryKeyFromOption === 'color') {
targetUrl.searchParams.delete('imgc');
if (tbsValToProcess.startsWith("ic:specific,isc:")) {
const colorName = tbsValToProcess.split(',isc:')[1];
newTbsString = _updateSingleTbsParam(newTbsString, "isc", colorName, true);
} else {
const [tbsKey, ...tbsValueParts] = tbsValToProcess.split(':');
newTbsString = _updateSingleTbsParam(newTbsString, tbsKey, tbsValueParts.join(':'));
}
} else if (categoryKeyFromOption === 'size' && tbsValToProcess && (tbsValToProcess.startsWith('isz:') || tbsValToProcess.startsWith('islt:') || tbsValToProcess.startsWith('isilu:'))) {
const [tbsKey, ...tbsValueParts] = tbsValToProcess.split(':');
newTbsString = _updateSingleTbsParam(newTbsString, tbsKey, tbsValueParts.join(':'));
targetUrl.searchParams.delete('imgsz');
let q = targetUrl.searchParams.get('q') || ""; q = q.replace(/imagesize:[\d]+x[\d]+/g, '').trim().replace(/\s\s+/g, ' ');
if (q) targetUrl.searchParams.set('q', q); else targetUrl.searchParams.delete('q');
} else {
if (typeof tbsValToProcess === 'string' && tbsValToProcess.includes(':')) {
const [tbsKey, ...tbsValueParts] = tbsValToProcess.split(':');
let isValidTbsCat = (categoryKeyFromOption === 'type' && tbsKey === 'itp') ||
(categoryKeyFromOption === 'time' && (tbsKey === 'qdr' || tbsKey === 'cdr')) ||
(categoryKeyFromOption === 'usageRights' && tbsKey === 'sur');
if (isValidTbsCat) newTbsString = _updateSingleTbsParam(newTbsString, tbsKey, tbsValueParts.join(':'));
else if (DEBUG_MODE) warn(`_buildTbsUrlPart: tbsVal '${tbsValToProcess}' for category '${categoryKeyFromOption}' has an unexpected tbsKey '${tbsKey}'.`);
} else if (DEBUG_MODE && tbsValToProcess) warn(`_buildTbsUrlPart: tbsVal '${tbsValToProcess}' for category '${categoryKeyFromOption}' is not a processable standard tbs string.`);
}
}
newTbsString = newTbsString.replace(/^,|,$/g, '').replace(/,,+/g, ',');
if (newTbsString) targetUrl.searchParams.set('tbs', newTbsString);
else targetUrl.searchParams.delete('tbs');
}
function _buildQueryUrlPart(targetUrl, paramValueFromOption, option) {
let currentQ = targetUrl.searchParams.get('q') || ""; let baseQ = currentQ;
if (option.categoryKey === 'exactSize' || (option.type === 'imagesize' || option.type === 'imagesize_clear')) {
baseQ = baseQ.replace(/imagesize:[\d]+x[\d]+/g, '').trim();
if (paramValueFromOption && paramValueFromOption !== "") targetUrl.searchParams.set('q', `${baseQ} imagesize:${paramValueFromOption}`.trim().replace(/\s\s+/g, ' '));
else { if (baseQ) targetUrl.searchParams.set('q', baseQ.trim()); else targetUrl.searchParams.delete('q'); }
targetUrl.searchParams.delete('imgsz'); let tbs = targetUrl.searchParams.get('tbs') || "";
tbs = _clearTbsCategory(tbs, ['isz', 'islt', 'isilu']);
if (tbs) targetUrl.searchParams.set('tbs', tbs); else targetUrl.searchParams.delete('tbs');
} else if (option.categoryKey === 'site') {
baseQ = baseQ.replace(/site:[^\s]+/g, '').trim();
if (paramValueFromOption && paramValueFromOption !== "") {
const cleanDomain = String(paramValueFromOption).replace(/\s/g, '');
if (cleanDomain) targetUrl.searchParams.set('q', `${baseQ} site:${cleanDomain}`.trim().replace(/\s\s+/g, ' '));
else { if (baseQ) targetUrl.searchParams.set('q', baseQ.trim()); else targetUrl.searchParams.delete('q');}
} else { if (baseQ) targetUrl.searchParams.set('q', baseQ.trim()); else targetUrl.searchParams.delete('q');}
}
}
function _buildSimpleParamUrlPart(targetUrl, paramName, paramValueFromOption) {
if (paramValueFromOption === "" || paramValueFromOption === undefined || paramValueFromOption === null) targetUrl.searchParams.delete(paramName);
else {
targetUrl.searchParams.set(paramName, String(paramValueFromOption));
if (paramName === 'imgsz') {
let q = targetUrl.searchParams.get('q') || ""; q = q.replace(/imagesize:[\d]+x[\d]+/g, '').trim().replace(/\s\s+/g, ' ');
if (q) targetUrl.searchParams.set('q', q); else targetUrl.searchParams.delete('q');
let tbs = targetUrl.searchParams.get('tbs') || ""; tbs = _clearTbsCategory(tbs, ['isz', 'islt', 'isilu']);
if (tbs) targetUrl.searchParams.set('tbs', tbs); else targetUrl.searchParams.delete('tbs');
}
}
}
function buildNewUrl(paramNameFromMenuDefinition, paramValueFromOption, optionContext, clearAllGiteFilters = false) {
const targetUrl = new URL(window.location.href); let currentQ = targetUrl.searchParams.get('q') || "";
if (clearAllGiteFilters) {
targetUrl.searchParams.delete('imgsz'); targetUrl.searchParams.delete('imgar'); targetUrl.searchParams.delete('as_filetype');
targetUrl.searchParams.delete('imgc'); targetUrl.searchParams.delete('cr');
currentQ = currentQ.replace(/imagesize:[\d]+x[\d]+/g, '').replace(/site:[^\s]+/g, '').trim().replace(/\s\s+/g, ' ');
if (currentQ) targetUrl.searchParams.set('q', currentQ); else targetUrl.searchParams.delete('q');
let tbs = targetUrl.searchParams.get('tbs') || "";
const tbsKeysToClear = ['itp', 'ic', 'isc', 'sur', 'qdr', 'cdr', 'isz', 'islt', 'isilu'];
tbs = _clearTbsCategory(tbs, tbsKeysToClear);
if (tbs) targetUrl.searchParams.set('tbs', tbs); else targetUrl.searchParams.delete('tbs');
return targetUrl.toString();
}
const currentOption = (optionContext && typeof optionContext === 'object') ? optionContext : { categoryKey: null, type: null, value: paramValueFromOption, tbsValue: null, paramNameOverride: null, originalTitleEn: '', paramName: null };
let actualParamName = paramNameFromMenuDefinition;
if (currentOption.paramName) actualParamName = currentOption.paramName;
else if (currentOption.paramNameOverride) actualParamName = currentOption.paramNameOverride;
else if (currentOption.categoryKey === 'exactSize' || currentOption.categoryKey === 'site') actualParamName = 'q';
else if (currentOption.tbsValue !== undefined && currentOption.tbsValue !== null) actualParamName = 'tbs';
if (actualParamName === 'q') _buildQueryUrlPart(targetUrl, paramValueFromOption, currentOption);
else if (actualParamName === 'tbs') _buildTbsUrlPart(targetUrl, currentOption);
else {
if (actualParamName) _buildSimpleParamUrlPart(targetUrl, actualParamName, paramValueFromOption);
else if (DEBUG_MODE) warn(`buildNewUrl: Could not determine actualParamName. Initial: '${paramNameFromMenuDefinition}', Option:`, currentOption);
}
return targetUrl.toString();
}
// --- UI Element Creation ---
function createMenuItem(optionWithContext, paramNameForLinkGeneration, isCustomSizer = false) {
const listItem = document.createElement('li');
listItem.className = GITE_MENU_ITEM_CLASS;
listItem.setAttribute('role', 'menuitemradio');
listItem.tabIndex = -1;
listItem.setAttribute(FILTER_PARAM_ATTR, paramNameForLinkGeneration);
let valForAttr = (optionWithContext.categoryKey === 'color' && optionWithContext.type === 'palette') ? optionWithContext.tbsValue :
(optionWithContext.tbsValue !== undefined && optionWithContext.tbsValue !== null) ? optionWithContext.tbsValue :
optionWithContext.value;
listItem.setAttribute(FILTER_VALUE_ATTR, String(valForAttr || ''));
if (paramNameForLinkGeneration === 'tbs' && optionWithContext.tbsValue && typeof optionWithContext.tbsValue === 'string' && optionWithContext.tbsValue.includes(':')) {
listItem.setAttribute(TBS_PREFIX_ATTR, optionWithContext.tbsValue.split(':')[0] + ':');
}
if (optionWithContext.id) {
listItem.dataset.optionId = optionWithContext.id;
listItem.id = `gite-menuitem-${optionWithContext.id.replace(/[^a-zA-Z0-9-_]/g, '_')}`;
}
listItem.dataset.giteCategory = optionWithContext.categoryKey;
if (optionWithContext.type === "separator") {
listItem.setAttribute('role', 'separator');
const sepDiv = document.createElement('div');
listItem.appendChild(sepDiv);
return listItem;
}
const anchor = document.createElement('a');
anchor.href = "javascript:void(0);";
const textSpan = document.createElement('span');
let itemDisplayTextFromKey = optionWithContext.text || (optionWithContext.textKey ? getLocalizedString(optionWithContext.textKey) : "ErrText");
textSpan.textContent = itemDisplayTextFromKey;
if (optionWithContext.type === 'palette') {
anchor.classList.add('gite-color-swatch');
anchor.style.backgroundColor = optionWithContext.hex;
anchor.title = itemDisplayTextFromKey;
anchor.setAttribute('aria-label', itemDisplayTextFromKey);
if (optionWithContext.nameKey && !optionWithContext.textKey) {
anchor.setAttribute(GITE_LANG_KEY_ATTR, optionWithContext.nameKey);
anchor.setAttribute('data-gite-lang-target-attr','title');
}
const swatchWrapper = document.createElement('div');
swatchWrapper.className = 'gite-color-swatch-item';
swatchWrapper.appendChild(anchor);
listItem.appendChild(swatchWrapper);
listItem.setAttribute('role', 'menuitem');
} else {
anchor.appendChild(textSpan);
listItem.appendChild(anchor);
}
if (optionWithContext.type === "custom_date_trigger") {
listItem.setAttribute('role', 'menuitem');
listItem.addEventListener('click', (event) => {
event.preventDefault(); event.stopPropagation();
closeCurrentlyOpenGiteMenu(true);
closeDatePicker();
datePickerOverlayElement = document.createElement('div');
datePickerOverlayElement.className = 'gite-datepicker-overlay';
datePickerOverlayElement.addEventListener('click', closeDatePicker);
document.body.appendChild(datePickerOverlayElement);
const pickerContainer = document.createElement('div');
pickerContainer.className = 'gite-custom-date-picker';
pickerContainer.innerHTML = `<div><label for="gite-start-date" ${GITE_LANG_KEY_ATTR}="datepicker_label_from">${getLocalizedString('datepicker_label_from')}</label><input type="date" id="gite-start-date"></div><div><label for="gite-end-date" ${GITE_LANG_KEY_ATTR}="datepicker_label_to">${getLocalizedString('datepicker_label_to')}</label><input type="date" id="gite-end-date"></div><div><button id="gite-date-apply" ${GITE_LANG_KEY_ATTR}="btn_apply">${getLocalizedString('btn_apply')}</button><button id="gite-date-cancel" class="gite-date-cancel" ${GITE_LANG_KEY_ATTR}="btn_cancel">${getLocalizedString('btn_cancel')}</button></div>`;
document.body.appendChild(pickerContainer);
activeDatePicker = pickerContainer;
const applyDateBtn = pickerContainer.querySelector('#gite-date-apply');
if(applyDateBtn) applyDateBtn.addEventListener('click', () => {
const startInput = pickerContainer.querySelector('#gite-start-date'); const endInput = pickerContainer.querySelector('#gite-end-date');
if(!startInput || !endInput) { warn("Date picker inputs not found."); return; }
const start = startInput.value; const end = endInput.value;
if (!start || !end) { alert(getLocalizedString('alert_datepicker_select_dates')); return; }
if (new Date(end) < new Date(start)) { alert(getLocalizedString('alert_datepicker_end_before_start')); return; }
const formatDate = (d) => { if (!d) return ""; const o = new Date(d + 'T00:00:00'); if (isNaN(o.getTime())) return ""; return `${o.getMonth()+1}/${o.getDate()}/${o.getFullYear()}`; };
const fS = formatDate(start); const fE = formatDate(end);
if (!fS || !fE) { alert(getLocalizedString('alert_datepicker_invalid_date')); return; }
const tbsVal = `cdr:1,cd_min:${fS},cd_max:${fE}`;
const dateOpt = { ...optionWithContext, tbsValue: tbsVal, categoryKey: 'time' };
const url = buildNewUrl('tbs', tbsVal, dateOpt);
closeDatePicker(); window.location.href = url;
});
const cancelDateBtn = pickerContainer.querySelector('#gite-date-cancel');
if(cancelDateBtn) cancelDateBtn.addEventListener('click', closeDatePicker);
});
} else {
const targetUrl = buildNewUrl(paramNameForLinkGeneration, valForAttr, optionWithContext);
anchor.href = targetUrl;
listItem.addEventListener('click', function(event) {
if (event.target.tagName === 'A' && event.target.href && event.target.href !== "javascript:void(0);") {
closeCurrentlyOpenGiteMenu(false);
return;
}
if (anchor.href && anchor.href !== "javascript:void(0);") {
closeCurrentlyOpenGiteMenu(false);
window.location.href = anchor.href;
}
});
}
return listItem;
}
// --- Filter Menu Population Functions ---
function closeCurrentlyOpenGiteMenu(refocusTrigger = true) {
if (currentlyOpenGiteMenu) {
if (currentlyOpenGiteMenu.menuContainer) {
currentlyOpenGiteMenu.menuContainer.style.display = 'none';
}
if (currentlyOpenGiteMenu.triggerButton) {
currentlyOpenGiteMenu.triggerButton.setAttribute('aria-expanded', 'false');
if (refocusTrigger &&
(currentlyOpenGiteMenu.triggerButton === document.activeElement ||
(currentlyOpenGiteMenu.menuContainer.contains(document.activeElement) && document.activeElement !== currentlyOpenGiteMenu.triggerButton)
)) {
if (document.body.contains(currentlyOpenGiteMenu.triggerButton) && currentlyOpenGiteMenu.triggerButton.offsetParent !== null) {
currentlyOpenGiteMenu.triggerButton.focus();
}
}
}
if (DEBUG_MODE) log(`Closed menu: ${currentlyOpenGiteMenu.idPrefix}`);
currentlyOpenGiteMenu = null;
}
}
function addFilterCategoryMenu(giteToolbarOrGroupContainer, categoryKey, categoryTitleKey, paramNameForUrl, idPrefix) {
const categoryTitle = getLocalizedString(categoryTitleKey);
const optionsForCategory = getFilterOptionsFromSettings(categoryKey);
const categoryConfig = giteSettings.filters[categoryKey];
const isCategoryEnabledInSettings = categoryConfig ? categoryConfig.enabled : true;
if (!isCategoryEnabledInSettings) {
if (DEBUG_MODE) log(`Skipping filter category '${categoryTitle}' (key: ${categoryKey}), as it's disabled in settings.`);
return null;
}
const managedWithOptions = ['size', 'exactSize', 'time', 'region', 'site'];
const isOnlyAnyOptionForManaged = managedWithOptions.includes(categoryKey) &&
optionsForCategory.length === 1 &&
(optionsForCategory[0].id === 'gite_site_any' || optionsForCategory[0].id === 'gite_exact_any' || optionsForCategory[0].id === `${idPrefix}_region_any_explicit`);
if (optionsForCategory.length === 0 && !isOnlyAnyOptionForManaged && !managedWithOptions.includes(categoryKey)) {
if(DEBUG_MODE) warn(`No options defined or enabled for non-managed category '${categoryTitle}' (key: ${categoryKey}). Skipping menu creation.`); return null;
} else if (optionsForCategory.length === 0 && managedWithOptions.includes(categoryKey) && !isOnlyAnyOptionForManaged){
if (DEBUG_MODE) log(`Managed category '${categoryTitle}' (key: ${categoryKey}) has no displayable options (excluding special 'Any' only cases). Menu will not be created.`);
return null;
}
if (DEBUG_MODE) log(`Adding GITE filter category to GITE Toolbar: ${categoryTitle} (key: ${categoryKey})`);
const buttonOuterWrapper = document.createElement('div');
buttonOuterWrapper.className = 'gite-filter-button-wrapper';
buttonOuterWrapper.id = `${idPrefix}-wrapper`;
const triggerButtonDiv = document.createElement('div');
triggerButtonDiv.className = 'gite-filter-trigger-button';
triggerButtonDiv.id = `${idPrefix}-trigger`;
triggerButtonDiv.setAttribute('role', 'button');
triggerButtonDiv.setAttribute('tabindex', '0');
triggerButtonDiv.setAttribute('aria-haspopup', 'true');
triggerButtonDiv.setAttribute('aria-expanded', 'false');
triggerButtonDiv.setAttribute('aria-controls', `${idPrefix}-menu-container`);
const triggerButtonTextElement = document.createElement('span');
triggerButtonTextElement.className = 'gite-filter-button-text';
triggerButtonTextElement.textContent = categoryTitle;
triggerButtonTextElement.setAttribute(GITE_LANG_KEY_ATTR, categoryTitleKey);
triggerButtonDiv.appendChild(triggerButtonTextElement);
const dropdownArrow = document.createElement('span');
dropdownArrow.className = 'gite-dropdown-arrow';
triggerButtonDiv.appendChild(dropdownArrow);
buttonOuterWrapper.appendChild(triggerButtonDiv);
const menuContainer = document.createElement('div');
menuContainer.id = `${idPrefix}-menu-container`;
menuContainer.className = 'gite-custom-menu-container';
menuContainer.setAttribute('role', 'menu');
menuContainer.setAttribute('aria-labelledby', triggerButtonDiv.id);
menuContainer.tabIndex = -1;
buttonOuterWrapper.appendChild(menuContainer);
const menuUl = document.createElement('ul');
menuContainer.appendChild(menuUl);
if (categoryKey === 'exactSize') {
// Check setting if exact size inputs should be shown in menu
const showInputs = giteSettings.filters.exactSize?.showInputsInMenu !== false; // Default to true if undefined
if (showInputs) {
const inputAreaLi = document.createElement('li');
inputAreaLi.className = GITE_MENU_ITEM_CLASS;
inputAreaLi.setAttribute('role', 'presentation');
const inputAreaDiv = document.createElement('div');
inputAreaDiv.className = 'gite-custom-imagesize-input-area';
inputAreaLi.appendChild(inputAreaDiv);
menuUl.appendChild(inputAreaLi);
const topRowControlsDiv = document.createElement('div');
topRowControlsDiv.className = 'gite-imagesize-input-controls';
inputAreaDiv.appendChild(topRowControlsDiv);
const widthInput = document.createElement('input');
widthInput.type = 'number'; widthInput.id = `gite-imagesize-width-${idPrefix}`; widthInput.min = "1";
widthInput.setAttribute(GITE_LANG_KEY_ATTR, "exact_size_placeholder_width");
widthInput.setAttribute('data-gite-lang-target-attr', 'placeholder');
widthInput.placeholder = getLocalizedString('exact_size_placeholder_width');
topRowControlsDiv.appendChild(widthInput);
const crossSpan = document.createElement('span'); crossSpan.textContent = '×';
topRowControlsDiv.appendChild(crossSpan);
const heightInput = document.createElement('input');
heightInput.type = 'number'; heightInput.id = `gite-imagesize-height-${idPrefix}`; heightInput.min = "1";
heightInput.setAttribute(GITE_LANG_KEY_ATTR, "exact_size_placeholder_height");
heightInput.setAttribute('data-gite-lang-target-attr', 'placeholder');
heightInput.placeholder = getLocalizedString('exact_size_placeholder_height');
topRowControlsDiv.appendChild(heightInput);
const applyButton = document.createElement('button');
applyButton.id = `gite-imagesize-apply-menu`;
applyButton.textContent = '✓';
applyButton.setAttribute(GITE_LANG_KEY_ATTR, "btn_apply");
applyButton.setAttribute('data-gite-lang-target-attr', 'title');
applyButton.title = getLocalizedString('btn_apply');
applyButton.style.cssText = "margin-left: 8px; padding: 6px 10px; background-color: var(--gite-datepicker-button-bg); color: var(--gite-datepicker-button-text); border: 1px solid var(--gite-datepicker-button-bg); border-radius: 4px; cursor: pointer;";
topRowControlsDiv.appendChild(applyButton);
const applySizeAction = () => {
const widthVal = parseInt(widthInput.value, 10);
const heightVal = parseInt(heightInput.value, 10);
if (isNaN(widthVal) || isNaN(heightVal) || widthVal <= 0 || heightVal <= 0) {
alert(getLocalizedString('alert_exact_size_invalid_input')); return;
}
const imagesizeValue = `${widthVal}x${heightVal}`;
const exactSizeOptionForUrl = { categoryKey: 'exactSize', type: 'imagesize', value: imagesizeValue, originalTitleEn: getLocalizedString('filter_title_exact_size', 'en') };
const newUrl = buildNewUrl('q', imagesizeValue, exactSizeOptionForUrl);
closeCurrentlyOpenGiteMenu(false);
window.location.href = newUrl;
};
applyButton.addEventListener('click', applySizeAction);
const handleEnterKey = (event) => { if (event.key === "Enter" || event.keyCode === 13) { event.preventDefault(); applySizeAction(); }};
widthInput.addEventListener('keypress', handleEnterKey);
heightInput.addEventListener('keypress', handleEnterKey);
}
// The separator logic here was tied to a specific definition which was removed.
// If input is shown and there are other options, a separator might be implicitly needed for visual clarity,
// but the explicit separator that was removed earlier (`gite_exact_sep1`) is no longer added here.
// A visual break is already provided by the `gite-custom-imagesize-input-area`'s bottom border.
optionsForCategory.forEach(opt => {
menuUl.appendChild(createMenuItem(opt, 'q', false));
});
} else if (categoryKey === 'color') {
let textOptionCount = 0;
const colorPrefixOptions = optionsForCategory.filter(opt => opt.type !== 'palette' && opt.type !== 'separator');
const paletteOptionDefs = optionsForCategory.filter(opt => opt.type === 'palette');
const separatorOption = optionsForCategory.find(opt => opt.type === 'separator');
colorPrefixOptions.forEach(opt => {
menuUl.appendChild(createMenuItem(opt, opt.paramNameOverride || 'tbs', false)); textOptionCount++;
});
if (textOptionCount > 0 && paletteOptionDefs.length > 0 && separatorOption && separatorOption.isEnabled) {
menuUl.appendChild(createMenuItem(separatorOption, 'tbs', false));
}
if (paletteOptionDefs.length > 0) {
const paletteLi = document.createElement('li');
paletteLi.setAttribute('role', 'presentation');
const paletteContainerDiv = document.createElement('div');
paletteContainerDiv.className = 'gite-color-palette-container';
paletteOptionDefs.forEach(paletteOpt => {
const swatchListItem = createMenuItem(paletteOpt, 'tbs', false);
const swatchAnchor = swatchListItem.querySelector('.gite-color-swatch');
if (swatchAnchor) {
swatchAnchor.setAttribute(FILTER_PARAM_ATTR, swatchListItem.getAttribute(FILTER_PARAM_ATTR));
swatchAnchor.setAttribute(FILTER_VALUE_ATTR, swatchListItem.getAttribute(FILTER_VALUE_ATTR));
if (swatchListItem.hasAttribute(TBS_PREFIX_ATTR)) swatchAnchor.setAttribute(TBS_PREFIX_ATTR, swatchListItem.getAttribute(TBS_PREFIX_ATTR));
if (swatchListItem.dataset.optionId) swatchAnchor.dataset.optionId = swatchListItem.dataset.optionId;
swatchAnchor.dataset.giteCategory = categoryKey;
swatchAnchor.href = buildNewUrl('tbs', paletteOpt.tbsValue, paletteOpt);
swatchAnchor.addEventListener('click', function(e){
closeCurrentlyOpenGiteMenu(false);
});
paletteContainerDiv.appendChild(swatchAnchor);
}
});
paletteLi.appendChild(paletteContainerDiv);
menuUl.appendChild(paletteLi);
}
} else {
if (categoryKey === 'region') {
const anyRegionTextKey = "option_text_region_any";
const anyRegionLocalizedText = getLocalizedString(anyRegionTextKey);
const anyRegionOptDef = {
id: `${idPrefix}_region_any_explicit`,
value: "", paramName: "cr", textKey: anyRegionTextKey,
text: anyRegionLocalizedText,
categoryKey: 'region', type: 'region_clear', isEnabled: true
};
menuUl.appendChild(createMenuItem(anyRegionOptDef, anyRegionOptDef.paramName, false));
const regionSpecificOptions = optionsForCategory.filter(opt => !(opt.textKey === "option_text_region_any" && opt.value === ""));
if (regionSpecificOptions.length > 0) {
const explicitSeparator = optionsForCategory.find(opt => opt.type === "separator" && opt.textKey === null && opt.id && opt.id.includes("sep_after_any"));
if (explicitSeparator && explicitSeparator.isEnabled) {
menuUl.appendChild(createMenuItem(explicitSeparator, ''));
} else {
menuUl.appendChild(createMenuItem({ type: "separator", textKey: null, isEnabled: true, id: `${idPrefix}_sep_after_any_fallback` }, ''));
}
}
}
optionsForCategory.forEach(opt => {
if (categoryKey === 'region' && (opt.id === `${idPrefix}_region_any_explicit` || (opt.textKey === "option_text_region_any" && opt.value === ""))) return;
let actualParamNameForLink = opt.paramName || paramNameForUrl;
if (opt.tbsValue !== undefined && opt.tbsValue !== null) actualParamNameForLink = 'tbs';
else if (opt.paramNameOverride) actualParamNameForLink = opt.paramNameOverride;
menuUl.appendChild(createMenuItem(opt, actualParamNameForLink, false));
});
}
triggerButtonDiv.addEventListener('click', (event) => {
event.stopPropagation();
const isCurrentlyOpen = menuContainer.style.display === 'block';
if (currentlyOpenGiteMenu && currentlyOpenGiteMenu.menuContainer !== menuContainer) {
closeCurrentlyOpenGiteMenu(false);
}
if (!isCurrentlyOpen) {
menuContainer.style.display = 'block';
triggerButtonDiv.setAttribute('aria-expanded', 'true');
currentlyOpenGiteMenu = { triggerButton: triggerButtonDiv, menuContainer: menuContainer, menuUl: menuUl, idPrefix: idPrefix };
if (DEBUG_MODE) log(`Menu ${idPrefix} opened.`);
} else {
closeCurrentlyOpenGiteMenu(false);
}
});
triggerButtonDiv.addEventListener('keydown', (event) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
triggerButtonDiv.click();
}
});
giteToolbarOrGroupContainer.appendChild(buttonOuterWrapper);
return {
filterButtonOuterWrapper: buttonOuterWrapper,
triggerButtonDiv: triggerButtonDiv,
triggerButtonTextElement: triggerButtonTextElement,
menuContainer: menuContainer,
menuElement: menuUl,
paramName: paramNameForUrl,
categoryKey: categoryKey,
originalTitleKey: categoryTitleKey
};
}
function tryAppendResultStats(giteToolbarContainer, retriesLeft = MAX_RESULT_STATS_TRIES) {
if (!giteSettings.general.showResultStats) {
const existingGiteStatsSpan = giteToolbarContainer.querySelector(`#${GITE_RESULT_STATS_DISPLAY_ID}`);
if (existingGiteStatsSpan) existingGiteStatsSpan.remove();
if (DEBUG_MODE) log("Result stats display disabled by user setting.");
if (resultStatsObserver) { resultStatsObserver.disconnect(); resultStatsObserver = null; }
if (resultStatsRetryTimeout) { clearTimeout(resultStatsRetryTimeout); resultStatsRetryTimeout = null; }
return;
}
if (DEBUG_MODE) log(`Attempting to append result stats. Retries left: ${retriesLeft}`);
const resultStatsElement = document.getElementById('result-stats');
if (resultStatsElement) {
const statsText = resultStatsElement.textContent || resultStatsElement.innerText;
if (statsText && statsText.trim()) {
const existingGiteStatsSpan = giteToolbarContainer.querySelector(`#${GITE_RESULT_STATS_DISPLAY_ID}`);
if (existingGiteStatsSpan) existingGiteStatsSpan.remove();
const giteStatsSpan = document.createElement('span');
giteStatsSpan.id = GITE_RESULT_STATS_DISPLAY_ID;
giteStatsSpan.textContent = statsText.trim();
const settingsButton = giteToolbarContainer.querySelector(`#${GITE_SETTINGS_BUTTON_ID}`);
if (settingsButton) {
giteToolbarContainer.insertBefore(giteStatsSpan, settingsButton);
} else {
giteToolbarContainer.appendChild(giteStatsSpan);
}
if (DEBUG_MODE) log("Result stats content (potentially from hidden #result-stats) copied to GITE toolbar.");
if (resultStatsObserver) { resultStatsObserver.disconnect(); resultStatsObserver = null; }
if (resultStatsRetryTimeout) { clearTimeout(resultStatsRetryTimeout); resultStatsRetryTimeout = null; }
return;
} else {
if (DEBUG_MODE) log("Google's result-stats element found, but its text content is empty. Will retry or wait for observer.");
}
} else {
if (DEBUG_MODE) log("Google's result-stats element (id='result-stats') not found or not visible. Will retry or wait for observer.");
}
if (retriesLeft > 0) {
if (resultStatsRetryTimeout) clearTimeout(resultStatsRetryTimeout);
resultStatsRetryTimeout = setTimeout(() => {
const currentToolbarContainer = document.getElementById(GITE_TOOLBAR_CONTAINER_ID);
if (currentToolbarContainer) {
tryAppendResultStats(currentToolbarContainer, retriesLeft - 1);
} else {
if (DEBUG_MODE) warn("GITE toolbar container disappeared during result stats retry.");
}
}, RESULT_STATS_RETRY_DELAY_MS);
} else {
if (DEBUG_MODE) warn("Failed to append result stats after multiple retries. Giving up timed retries, relying on observer if active.");
}
}
function observeResultStats(giteToolbarContainer) {
if (!giteSettings.general.showResultStats) {
if (resultStatsObserver) { resultStatsObserver.disconnect(); resultStatsObserver = null; }
return;
}
if (resultStatsObserver) resultStatsObserver.disconnect();
const targetNode = document.body;
if (!targetNode) {
warn("Cannot observe result stats: document.body not found.");
return;
}
if (DEBUG_MODE) log("Starting MutationObserver for #result-stats.");
resultStatsObserver = new MutationObserver((mutationsList, obs) => {
const resultStatsElement = document.getElementById('result-stats');
if (resultStatsElement) {
if (DEBUG_MODE) log("Result stats observer: #result-stats found in DOM. Attempting to update GITE stats.");
tryAppendResultStats(giteToolbarContainer, 0);
}
});
resultStatsObserver.observe(targetNode, { childList: true, subtree: true });
tryAppendResultStats(giteToolbarContainer, MAX_RESULT_STATS_TRIES);
}
function cleanDomainInput(domainStr) {
if (!domainStr || typeof domainStr !== 'string') return "";
let cleaned = domainStr.trim().toLowerCase();
cleaned = cleaned.replace(/^https?:\/\//, '').replace(/^www\./, '');
const slashIndex = cleaned.indexOf('/'); if (slashIndex !== -1) cleaned = cleaned.substring(0, slashIndex);
if (cleaned === "" || cleaned.includes(" ") || (cleaned.startsWith('.') && cleaned.length === 1) ) {
if(DEBUG_MODE) warn(`Cleaned domain input "${domainStr}" resulted in invalid/empty: "${cleaned}"`);
return "";
}
return cleaned;
}
function ensureSettingsPanelDOM() {
if (giteSettingsPanelElement && document.body.contains(giteSettingsPanelElement)) return;
const panelHTML = `
<div id="gite-settings-panel" class="gite-modal">
<div class="gite-modal-overlay"></div>
<div class="gite-modal-content">
<div class="gite-modal-header">
<h2 id="gite-settings-title" ${GITE_LANG_KEY_ATTR}="settings_panel_title">${getLocalizedString('settings_panel_title')}</h2>
<button id="gite-settings-close-btn" class="gite-modal-close-btn" ${GITE_LANG_KEY_ATTR}="btn_close" data-gite-lang-target-attr="title" title="${getLocalizedString('btn_close')}">×</button>
</div>
<div class="gite-modal-body">
<div class="gite-tabs-navigation">
<ul role="tablist">
<li role="presentation"><button role="tab" id="gite-tab-general" aria-controls="gite-panel-general" aria-selected="true" ${GITE_LANG_KEY_ATTR}="settings_tab_general">${getLocalizedString('settings_tab_general')}</button></li>
<li role="presentation"><button role="tab" id="gite-tab-size" aria-controls="gite-panel-size" ${GITE_LANG_KEY_ATTR}="settings_tab_size">${getLocalizedString('settings_tab_size')}</button></li>
<li role="presentation"><button role="tab" id="gite-tab-exactsize" aria-controls="gite-panel-exactsize" ${GITE_LANG_KEY_ATTR}="settings_tab_exact_size">${getLocalizedString('settings_tab_exact_size')}</button></li>
<li role="presentation"><button role="tab" id="gite-tab-time" aria-controls="gite-panel-time" ${GITE_LANG_KEY_ATTR}="settings_tab_time">${getLocalizedString('settings_tab_time')}</button></li>
<li role="presentation"><button role="tab" id="gite-tab-region" aria-controls="gite-panel-region" ${GITE_LANG_KEY_ATTR}="settings_tab_region">${getLocalizedString('settings_tab_region')}</button></li>
<li role="presentation"><button role="tab" id="gite-tab-sitesearch" aria-controls="gite-panel-sitesearch" ${GITE_LANG_KEY_ATTR}="settings_tab_site_search">${getLocalizedString('settings_tab_site_search')}</button></li>
</ul>
</div>
<div class="gite-tabs-content">
<div role="tabpanel" id="gite-panel-general" aria-labelledby="gite-tab-general" class="gite-tab-panel gite-active-panel">
<div class="gite-setting-group"><label for="gite-setting-language" ${GITE_LANG_KEY_ATTR}="settings_label_language">${getLocalizedString('settings_label_language')}</label><select id="gite-setting-language"><option value="auto" ${GITE_LANG_KEY_ATTR}="settings_lang_auto">${getLocalizedString('settings_lang_auto')}</option><option value="en" ${GITE_LANG_KEY_ATTR}="settings_lang_en">${getLocalizedString('settings_lang_en')}</option><option value="zh-TW" ${GITE_LANG_KEY_ATTR}="settings_lang_zh_TW">${getLocalizedString('settings_lang_zh_TW')}</option><option value="ja" ${GITE_LANG_KEY_ATTR}="settings_lang_ja">${getLocalizedString('settings_lang_ja')}</option></select></div>
<div class="gite-setting-group"><label for="gite-setting-theme" ${GITE_LANG_KEY_ATTR}="settings_label_theme">${getLocalizedString('settings_label_theme')}</label><select id="gite-setting-theme"><option value="auto" ${GITE_LANG_KEY_ATTR}="settings_theme_auto">${getLocalizedString('settings_theme_auto')}</option><option value="light" ${GITE_LANG_KEY_ATTR}="settings_theme_light">${getLocalizedString('settings_theme_light')}</option><option value="dark" ${GITE_LANG_KEY_ATTR}="settings_theme_dark">${getLocalizedString('settings_theme_dark')}</option></select></div>
<div class="gite-setting-group"><label for="gite-setting-toolbarfontsize" ${GITE_LANG_KEY_ATTR}="settings_label_toolbar_font_size">${getLocalizedString('settings_label_toolbar_font_size')}</label><input type="number" id="gite-setting-toolbarfontsize" min="8" max="24" step="1"></div>
<div class="gite-setting-group"><label for="gite-setting-toolbarlineheight" ${GITE_LANG_KEY_ATTR}="settings_label_toolbar_line_height">${getLocalizedString('settings_label_toolbar_line_height')}</label><input type="number" id="gite-setting-toolbarlineheight" min="1" max="3" step="0.1"></div>
<div class="gite-setting-group"><label for="gite-setting-showtoolbarbutton" ${GITE_LANG_KEY_ATTR}="settings_label_showtoolbarbutton">${getLocalizedString('settings_label_showtoolbarbutton')}</label><input type="checkbox" id="gite-setting-showtoolbarbutton"></div>
<div class="gite-setting-group"><label for="gite-setting-showresultstats" ${GITE_LANG_KEY_ATTR}="settings_label_showresultstats">${getLocalizedString('settings_label_showresultstats')}</label><input type="checkbox" id="gite-setting-showresultstats"></div>
<div class="gite-panel-category-footer"><button id="gite-general-reset-cat-btn" class="gite-reset-category-btn" ${GITE_LANG_KEY_ATTR}="btn_reset_general_options">${getLocalizedString('btn_reset_general_options')}</button></div>
</div>
<div role="tabpanel" id="gite-panel-size" aria-labelledby="gite-tab-size" class="gite-tab-panel">
<h3 style="margin-top:0;"><span ${GITE_LANG_KEY_ATTR}="settings_options_for_category_prefix">${getLocalizedString('settings_options_for_category_prefix')}</span><span ${GITE_LANG_KEY_ATTR}="filter_title_size">${getLocalizedString('filter_title_size')}</span><span ${GITE_LANG_KEY_ATTR}="settings_options_for_category_suffix">${getLocalizedString('settings_options_for_category_suffix')}</span></h3>
<p style="font-size:0.9em; opacity:0.8; margin-top:-5px; margin-bottom:10px;" ${GITE_LANG_KEY_ATTR}="settings_size_options_note">${getLocalizedString('settings_size_options_note')}</p>
<ul class="gite-options-list" id="gite-size-options-list"></ul>
<div class="gite-panel-category-footer"><button id="gite-size-reset-cat-btn" class="gite-reset-category-btn"><span ${GITE_LANG_KEY_ATTR}="btn_reset_options_for_category_prefix">${getLocalizedString('btn_reset_options_for_category_prefix')}</span><span ${GITE_LANG_KEY_ATTR}="filter_title_size">${getLocalizedString('filter_title_size')}</span><span ${GITE_LANG_KEY_ATTR}="btn_reset_options_for_category_suffix">${getLocalizedString('btn_reset_options_for_category_suffix')}</span></button></div>
</div>
<div role="tabpanel" id="gite-panel-exactsize" aria-labelledby="gite-tab-exactsize" class="gite-tab-panel">
<div class="gite-setting-group gite-exactsize-panel-section"><label for="gite-filter-exactsize-cat-enabled"><span ${GITE_LANG_KEY_ATTR}="settings_enable_filter_category_prefix">${getLocalizedString('settings_enable_filter_category_prefix')}</span><span ${GITE_LANG_KEY_ATTR}="filter_title_exact_size">${getLocalizedString('filter_title_exact_size')}</span><span ${GITE_LANG_KEY_ATTR}="settings_enable_filter_category_suffix">${getLocalizedString('settings_enable_filter_category_suffix')}</span></label><input type="checkbox" id="gite-filter-exactsize-cat-enabled"></div>
<div class="gite-setting-group gite-exactsize-panel-section"><label for="gite-exactsize-showinputs-toggle" ${GITE_LANG_KEY_ATTR}="settings_label_show_exact_size_inputs_in_menu">${getLocalizedString('settings_label_show_exact_size_inputs_in_menu')}</label><input type="checkbox" id="gite-exactsize-showinputs-toggle"></div>
<div class="gite-exactsize-panel-section"><h3 ${GITE_LANG_KEY_ATTR}="settings_section_predefined_options">${getLocalizedString('settings_section_predefined_options')}</h3><ul class="gite-exactsize-options-list" id="gite-exactsize-predefined-list"></ul></div>
<div class="gite-exactsize-panel-section"><h3 ${GITE_LANG_KEY_ATTR}="settings_section_your_saved_sizes">${getLocalizedString('settings_section_your_saved_sizes')}</h3><ul class="gite-exactsize-options-list" id="gite-exactsize-userdefined-list"></ul></div>
<div class="gite-exactsize-panel-section">
<h3 ${GITE_LANG_KEY_ATTR}="settings_label_add_new_exact_size">${getLocalizedString('settings_label_add_new_exact_size')}</h3>
<form class="gite-add-exact-size-form" id="gite-add-exact-size-form">
<div class="gite-form-row">
<div><label for="gite-new-exact-width">W:</label><input type="number" id="gite-new-exact-width" placeholder="${getLocalizedString('exact_size_placeholder_width')}" min="1" required ${GITE_LANG_KEY_ATTR}="exact_size_placeholder_width" data-gite-lang-target-attr="placeholder"></div>
<div><label for="gite-new-exact-height">H:</label><input type="number" id="gite-new-exact-height" placeholder="${getLocalizedString('exact_size_placeholder_height')}" min="1" required ${GITE_LANG_KEY_ATTR}="exact_size_placeholder_height" data-gite-lang-target-attr="placeholder"></div>
</div>
<div><label for="gite-new-exact-label">${getLocalizedString('settings_placeholder_label_optional')}:</label><input type="text" id="gite-new-exact-label" placeholder="${getLocalizedString('settings_placeholder_label_optional')}" ${GITE_LANG_KEY_ATTR}="settings_placeholder_label_optional" data-gite-lang-target-attr="placeholder"></div>
<button type="submit" class="gite-primary-btn" ${GITE_LANG_KEY_ATTR}="btn_add_new_exact_size">${getLocalizedString('btn_add_new_exact_size')}</button>
</form>
</div>
<div class="gite-panel-category-footer"><button id="gite-exact_size-reset-cat-btn" class="gite-reset-category-btn" ${GITE_LANG_KEY_ATTR}="btn_reset_exact_size_options">${getLocalizedString('btn_reset_exact_size_options')}</button></div>
</div>
<div role="tabpanel" id="gite-panel-time" aria-labelledby="gite-tab-time" class="gite-tab-panel">
<h3 style="margin-top:0;"><span ${GITE_LANG_KEY_ATTR}="settings_options_for_category_prefix">${getLocalizedString('settings_options_for_category_prefix')}</span><span ${GITE_LANG_KEY_ATTR}="filter_title_time">${getLocalizedString('filter_title_time')}</span><span ${GITE_LANG_KEY_ATTR}="settings_options_for_category_suffix">${getLocalizedString('settings_options_for_category_suffix')}</span></h3>
<ul class="gite-options-list" id="gite-time-options-list" style="max-height: 250px;"></ul>
<div class="gite-panel-category-footer"><button id="gite-time-reset-cat-btn" class="gite-reset-category-btn"><span ${GITE_LANG_KEY_ATTR}="btn_reset_options_for_category_prefix">${getLocalizedString('btn_reset_options_for_category_prefix')}</span><span ${GITE_LANG_KEY_ATTR}="filter_title_time">${getLocalizedString('filter_title_time')}</span><span ${GITE_LANG_KEY_ATTR}="btn_reset_options_for_category_suffix">${getLocalizedString('btn_reset_options_for_category_suffix')}</span></button></div>
</div>
<div role="tabpanel" id="gite-panel-region" aria-labelledby="gite-tab-region" class="gite-tab-panel">
<div class="gite-setting-group" style="margin-bottom: 5px;"> <label for="gite-filter-region-cat-enabled"><span ${GITE_LANG_KEY_ATTR}="settings_enable_filter_category_prefix">${getLocalizedString('settings_enable_filter_category_prefix')}</span><span ${GITE_LANG_KEY_ATTR}="filter_title_region">${getLocalizedString('filter_title_region')}</span><span ${GITE_LANG_KEY_ATTR}="settings_enable_filter_category_suffix">${getLocalizedString('settings_enable_filter_category_suffix')}</span></label><input type="checkbox" id="gite-filter-region-cat-enabled"></div>
<h3 style="margin-top:10px;"> <span ${GITE_LANG_KEY_ATTR}="settings_options_for_category_prefix">${getLocalizedString('settings_options_for_category_prefix')}</span><span ${GITE_LANG_KEY_ATTR}="filter_title_region">${getLocalizedString('filter_title_region')}</span><span ${GITE_LANG_KEY_ATTR}="settings_options_for_category_suffix">${getLocalizedString('settings_options_for_category_suffix')}</span></h3>
<p style="font-size:0.9em; opacity:0.8; margin-top:-5px; margin-bottom:10px;" ${GITE_LANG_KEY_ATTR}="settings_region_options_note">${getLocalizedString('settings_region_options_note')}</p>
<ul class="gite-options-list" id="gite-region-options-list"></ul>
<div class="gite-panel-category-footer"><button id="gite-region-reset-cat-btn" class="gite-reset-category-btn"><span ${GITE_LANG_KEY_ATTR}="btn_reset_options_for_category_prefix">${getLocalizedString('btn_reset_options_for_category_prefix')}</span><span ${GITE_LANG_KEY_ATTR}="filter_title_region">${getLocalizedString('filter_title_region')}</span><span ${GITE_LANG_KEY_ATTR}="btn_reset_options_for_category_suffix">${getLocalizedString('btn_reset_options_for_category_suffix')}</span></button></div>
</div>
<div role="tabpanel" id="gite-panel-sitesearch" aria-labelledby="gite-tab-sitesearch" class="gite-tab-panel">
<div class="gite-setting-group" style="margin-bottom: 10px;"><label for="gite-filter-sitesearch-cat-enabled"><span ${GITE_LANG_KEY_ATTR}="settings_label_enable_site_search_filter">${getLocalizedString('settings_label_enable_site_search_filter')}</span></label><input type="checkbox" id="gite-filter-sitesearch-cat-enabled"></div>
<div class="gite-options-panel-section"><h3 ${GITE_LANG_KEY_ATTR}="settings_section_your_saved_sites">${getLocalizedString('settings_section_your_saved_sites')}</h3><ul class="gite-exactsize-options-list" id="gite-sitesearch-userdefined-list"></ul></div>
<div class="gite-options-panel-section">
<h3 ${GITE_LANG_KEY_ATTR}="settings_label_add_new_site">${getLocalizedString('settings_label_add_new_site')}</h3>
<form class="gite-add-exact-size-form gite-add-new-site-form" id="gite-add-new-site-form">
<div class="gite-form-row">
<div><label for="gite-new-site-domain">${getLocalizedString('settings_placeholder_site_domain')}:</label><input type="text" id="gite-new-site-domain" placeholder="${getLocalizedString('settings_placeholder_site_domain')}" required ${GITE_LANG_KEY_ATTR}="settings_placeholder_site_domain" data-gite-lang-target-attr="placeholder"></div>
<div><label for="gite-new-site-label">${getLocalizedString('settings_placeholder_site_label')}:</label><input type="text" id="gite-new-site-label" placeholder="${getLocalizedString('settings_placeholder_site_label')}" required ${GITE_LANG_KEY_ATTR}="settings_placeholder_site_label" data-gite-lang-target-attr="placeholder"></div>
</div>
<button type="submit" class="gite-primary-btn" ${GITE_LANG_KEY_ATTR}="btn_add_new_site" style="margin-top: 10px;">${getLocalizedString('btn_add_new_site')}</button>
</form>
</div>
<div class="gite-panel-category-footer"><button id="gite-site_search-reset-cat-btn" class="gite-reset-category-btn" ${GITE_LANG_KEY_ATTR}="btn_reset_site_search_options">${getLocalizedString('btn_reset_site_search_options')}</button></div>
</div>
</div>
</div>
<div class="gite-modal-footer">
<button id="gite-settings-reset-all-btn" ${GITE_LANG_KEY_ATTR}="btn_reset_all_settings">${getLocalizedString('btn_reset_all_settings')}</button>
<div><button id="gite-settings-cancel-btn" ${GITE_LANG_KEY_ATTR}="btn_cancel">${getLocalizedString('btn_cancel')}</button><button id="gite-settings-apply-btn" ${GITE_LANG_KEY_ATTR}="btn_apply">${getLocalizedString('btn_apply')}</button><button id="gite-settings-save-btn" class="gite-primary-btn" ${GITE_LANG_KEY_ATTR}="btn_save_and_close">${getLocalizedString('btn_save_and_close')}</button></div>
</div>
</div>
</div>`;
const tempDiv = document.createElement('div'); tempDiv.innerHTML = panelHTML.trim();
giteSettingsPanelElement = tempDiv.firstChild;
if (document.body) { document.body.appendChild(giteSettingsPanelElement); setupPanelEventListeners(); }
else error("document.body not found. Cannot append GITE settings panel.");
}
function openSettingsPanel() {
ensureSettingsPanelDOM(); if (!giteSettingsPanelElement) { error("Settings panel element could not be ensured/created. Aborting openSettingsPanel."); return; }
currentGiteSettingsForPanel = JSON.parse(JSON.stringify(giteSettings));
updateAllLocalizableElements(CURRENT_LANGUAGE, giteSettingsPanelElement);
loadGeneralSettingsToPanel();
loadSizeOptionsSettingsList();
loadExactSizeSettingsToPanel();
loadTimeOptionsSettingsList();
loadRegionSettingsToPanel(); loadRegionOptionsSettingsList();
loadSiteSearchSettingsToPanel();
const generalTabButton = giteSettingsPanelElement.querySelector('#gite-tab-general');
const generalPanel = giteSettingsPanelElement.querySelector('#gite-panel-general');
giteSettingsPanelElement.querySelectorAll('.gite-tabs-navigation button[role="tab"]').forEach(tb => tb.setAttribute('aria-selected', 'false'));
giteSettingsPanelElement.querySelectorAll('.gite-tabs-content .gite-tab-panel').forEach(tp => tp.classList.remove('gite-active-panel'));
if(generalTabButton) generalTabButton.setAttribute('aria-selected', 'true');
if(generalPanel) generalPanel.classList.add('gite-active-panel');
giteSettingsPanelElement.style.display = 'flex'; isSettingsPanelOpen = true;
if (generalTabButton) generalTabButton.focus();
}
function closeSettingsPanel(forceClose = false) {
if (!isSettingsPanelOpen && !forceClose) return;
if (giteSettingsPanelElement) giteSettingsPanelElement.style.display = 'none';
isSettingsPanelOpen = false;
}
function switchTabInPanel(event) {
const newTabButton = event.target.closest('button[role="tab"]');
if (!newTabButton || newTabButton.disabled || newTabButton.getAttribute('aria-selected') === 'true') return;
const nav = newTabButton.closest('.gite-tabs-navigation');
const contentContainer = giteSettingsPanelElement.querySelector('.gite-tabs-content');
if (!nav || !contentContainer) { warn("Tab navigation or content container not found in settings panel during tab switch."); return; }
const oldActiveTab = nav.querySelector('[role="tab"][aria-selected="true"]');
if (oldActiveTab) {
oldActiveTab.setAttribute('aria-selected', 'false'); const oldActivePanelId = oldActiveTab.getAttribute('aria-controls');
if (oldActivePanelId) { const oldActivePanel = contentContainer.querySelector(`#${oldActivePanelId}`); if (oldActivePanel) oldActivePanel.classList.remove('gite-active-panel');}
}
newTabButton.setAttribute('aria-selected', 'true'); const newPanelId = newTabButton.getAttribute('aria-controls');
const newPanel = contentContainer.querySelector(`#${newPanelId}`);
if (newPanel) {
newPanel.classList.add('gite-active-panel');
if (newPanelId === 'gite-panel-exactsize') loadExactSizeSettingsToPanel();
else if (newPanelId === 'gite-panel-size') loadSizeOptionsSettingsList();
else if (newPanelId === 'gite-panel-time') loadTimeOptionsSettingsList();
else if (newPanelId === 'gite-panel-region') { loadRegionSettingsToPanel(); loadRegionOptionsSettingsList(); }
else if (newPanelId === 'gite-panel-sitesearch') { loadSiteSearchSettingsToPanel(); }
} else warn(`Tab panel with ID "${newPanelId}" not found during tab switch.`);
newTabButton.focus();
}
function loadGeneralSettingsToPanel() {
if (!giteSettingsPanelElement || !currentGiteSettingsForPanel.general) {
warn("Cannot load general settings to panel: Prerequisites not met."); return;
}
const qS = (sel) => giteSettingsPanelElement.querySelector(sel);
qS('#gite-setting-language').value = getSettingFromPanel('general.selectedLanguage', 'auto');
qS('#gite-setting-theme').value = getSettingFromPanel('general.themePreference', 'auto');
qS('#gite-setting-toolbarfontsize').value = parseFloat(getSettingFromPanel('general.toolbarFontSize', '14px'));
qS('#gite-setting-toolbarlineheight').value = parseFloat(getSettingFromPanel('general.toolbarLineHeight', '1.5'));
qS('#gite-setting-showtoolbarbutton').checked = getSettingFromPanel('general.showSettingsButtonOnToolbar', true);
qS('#gite-setting-showresultstats').checked = getSettingFromPanel('general.showResultStats', true);
}
function loadRegionSettingsToPanel() {
if (!giteSettingsPanelElement || !currentGiteSettingsForPanel.filters || !currentGiteSettingsForPanel.filters.region) {
warn("Cannot load region settings to panel: Prerequisites not met."); return;
}
const catEnabledCheckbox = giteSettingsPanelElement.querySelector('#gite-filter-region-cat-enabled');
if (catEnabledCheckbox) catEnabledCheckbox.checked = getSettingFromPanel('filters.region.enabled', true);
else warn("Region category enable checkbox not found in settings panel.");
}
function loadRegionOptionsSettingsList() {
renderSimpleOptionsListForCategory('region', 'gite-region-options-list', true);
}
function loadExactSizeSettingsToPanel() {
if (!giteSettingsPanelElement || !currentGiteSettingsForPanel.filters || !currentGiteSettingsForPanel.filters.exactSize) {
warn("Cannot load exact size settings to panel: Prerequisites not met."); return;
}
const esConfigInPanel = getSettingFromPanel('filters.exactSize', GITE_DEFAULT_SETTINGS.filters.exactSize);
const catEnabledCheckbox = giteSettingsPanelElement.querySelector('#gite-filter-exactsize-cat-enabled');
if (catEnabledCheckbox) catEnabledCheckbox.checked = esConfigInPanel.enabled;
else warn("Exact size category enable checkbox not found in settings panel.");
const showInputsToggle = giteSettingsPanelElement.querySelector('#gite-exactsize-showinputs-toggle');
if(showInputsToggle) showInputsToggle.checked = esConfigInPanel.showInputsInMenu;
else warn("Exact size show inputs toggle not found in settings panel.");
renderExactSizeList('#gite-exactsize-predefined-list', esConfigInPanel.predefinedOptions || []);
renderExactSizeList('#gite-exactsize-userdefined-list', esConfigInPanel.userDefinedOptions || [], true);
}
function renderExactSizeList(listSelector, optionsFromPanelSettings, isUserDefined = false) {
const listElement = giteSettingsPanelElement.querySelector(listSelector);
if (!listElement) {
if (DEBUG_MODE) warn(`Exact size list container ${listSelector} not found in settings panel.`);
return;
}
listElement.innerHTML = '';
if (!optionsFromPanelSettings || optionsFromPanelSettings.length === 0) {
const emptyMsgLi = document.createElement('li');
emptyMsgLi.textContent = getLocalizedString('settings_no_saved_items_placeholder');
emptyMsgLi.style.padding = "5px 8px"; emptyMsgLi.style.opacity = "0.7";
listElement.appendChild(emptyMsgLi);
return;
}
optionsFromPanelSettings.forEach(opt => {
if (opt.type === 'separator') {
if (opt.isEnabled) {
const sepLi = document.createElement('li');
sepLi.innerHTML = '<hr style="width:100%; margin: 3px 0; border-color: var(--gite-separator-color); opacity: 0.5;">';
listElement.appendChild(sepLi);
} return;
}
const listItem = document.createElement('li'); listItem.dataset.id = opt.id; listItem.className = 'gite-exactsize-option-item';
let text = opt.customText || (isUserDefined && opt.label) || (opt.textKey ? getLocalizedString(opt.textKey, CURRENT_LANGUAGE, opt.id) : (isUserDefined ? `${opt.width}x${opt.height}` : opt.id));
const valueHint = isUserDefined ? `(${opt.width}x${opt.height})` : (opt.value ? `(${opt.value})` : '');
const deleteBtnTitle = getLocalizedString('btn_delete'); const editBtnTitle = getLocalizedString('btn_edit_label');
listItem.innerHTML = `<input type="checkbox" class="gite-option-enable-toggle" ${opt.isEnabled ? 'checked' : ''} data-opttype="${isUserDefined ? 'user' : 'predefined'}"> <span class="gite-option-text" title="${text}">${text}</span> <span class="gite-option-value-hint">${valueHint}</span> <div class="gite-actions"> ${isUserDefined ? `<button class="gite-edit-label-btn" ${GITE_LANG_KEY_ATTR}="btn_edit_label" data-gite-lang-target-attr="title" title="${editBtnTitle}">✏️</button>` : ''} ${isUserDefined ? `<button class="gite-delete-user-exact-btn" ${GITE_LANG_KEY_ATTR}="btn_delete" data-gite-lang-target-attr="title" title="${deleteBtnTitle}">🗑️</button>` : ''} </div>`;
listElement.appendChild(listItem);
});
}
function handleExactSizePanelActions(event) {
const target = event.target; const listItem = target.closest('li.gite-exactsize-option-item');
if (!listItem) return; const optionId = listItem.dataset.id;
const optTypeCheckbox = listItem.querySelector('.gite-option-enable-toggle');
const optType = optTypeCheckbox ? optTypeCheckbox.dataset.opttype : null;
if (target.classList.contains('gite-option-enable-toggle')) {
const isEnabled = target.checked; const optionListKey = optType === 'user' ? 'userDefinedOptions' : 'predefinedOptions';
const optionsListInPanel = getSettingFromPanel(`filters.exactSize.${optionListKey}`, []);
const optionInPanel = optionsListInPanel.find(opt => opt.id === optionId);
if (optionInPanel) optionInPanel.isEnabled = isEnabled;
} else if (target.classList.contains('gite-edit-label-btn') && optType === 'user') {
const userOptionsInPanel = getSettingFromPanel('filters.exactSize.userDefinedOptions', []);
const optionInPanel = userOptionsInPanel.find(opt => opt.id === optionId);
if (optionInPanel) {
const currentLabel = optionInPanel.label || `${optionInPanel.width}x${optionInPanel.height}`;
const newLabel = prompt(getLocalizedString('btn_edit_label') + ` for ${currentLabel}:`, currentLabel);
if (newLabel !== null) { optionInPanel.label = newLabel.trim(); loadExactSizeSettingsToPanel(); alert(getLocalizedString('alert_label_updated'));}
}
} else if (target.classList.contains('gite-delete-user-exact-btn') && optType === 'user') {
const optionTextElement = listItem.querySelector('.gite-option-text');
const optionDisplayText = optionTextElement ? optionTextElement.textContent : optionId;
if (confirm(`${getLocalizedString('alert_confirm_delete_option_prefix')}${optionDisplayText}"?`)) {
let userOptionsInPanel = getSettingFromPanel('filters.exactSize.userDefinedOptions', []);
setSettingInPanel('filters.exactSize.userDefinedOptions', userOptionsInPanel.filter(opt => opt.id !== optionId));
loadExactSizeSettingsToPanel();
}
}
}
function handleAddNewExactSizeFromPanel(event) {
event.preventDefault(); const form = event.target;
const widthInput = form.querySelector('#gite-new-exact-width'); const heightInput = form.querySelector('#gite-new-exact-height');
const labelInput = form.querySelector('#gite-new-exact-label');
if(!widthInput || !heightInput || !labelInput) { warn("Add new exact size form inputs not found in settings panel."); return; }
const width = parseInt(widthInput.value, 10); const height = parseInt(heightInput.value, 10);
const label = labelInput.value.trim();
if (isNaN(width) || isNaN(height) || width <= 0 || height <= 0) { alert(getLocalizedString('alert_exact_size_invalid_input')); return; }
const userOptionsInPanel = getSettingFromPanel('filters.exactSize.userDefinedOptions', []);
const newValue = `${width}x${height}`;
if (userOptionsInPanel.some(opt => opt.value === newValue)) { alert(getLocalizedString('alert_size_already_saved')); return; }
const newId = `user_exact_${width}x${height}_${Date.now()}`;
userOptionsInPanel.push({ id: newId, width, height, label: label || newValue, value: newValue, type: "imagesize", isCustom: true, isEnabled: true });
loadExactSizeSettingsToPanel();
form.reset(); widthInput.focus();
}
function loadSiteSearchSettingsToPanel() {
if (!giteSettingsPanelElement || !currentGiteSettingsForPanel.filters || !currentGiteSettingsForPanel.filters.site) {
if (DEBUG_MODE) warn("Site search panel load prerequisites not met. Skipping loadSiteSearchSettingsToPanel."); return;
}
const siteConfigInPanel = getSettingFromPanel('filters.site', GITE_DEFAULT_SETTINGS.filters.site);
const catEnabledCheckbox = giteSettingsPanelElement.querySelector('#gite-filter-sitesearch-cat-enabled');
const panelContentSections = giteSettingsPanelElement.querySelectorAll('#gite-panel-sitesearch .gite-options-panel-section, #gite-panel-sitesearch .gite-panel-category-footer');
if (catEnabledCheckbox) {
catEnabledCheckbox.checked = siteConfigInPanel.enabled;
panelContentSections.forEach(s => s.style.display = catEnabledCheckbox.checked ? '' : 'none');
} else if (DEBUG_MODE) warn("Site search category enable checkbox ('#gite-filter-sitesearch-cat-enabled') not found in panel.");
renderSiteSearchList('#gite-sitesearch-userdefined-list', siteConfigInPanel.userDefinedOptions || []);
}
function renderSiteSearchList(listSelector, optionsFromPanelSettings) {
const listElement = giteSettingsPanelElement.querySelector(listSelector);
if (!listElement) { if (DEBUG_MODE) warn(`Site search list container ${listSelector} not found in settings panel.`); return; }
listElement.innerHTML = '';
if (!optionsFromPanelSettings || optionsFromPanelSettings.length === 0) {
const emptyMsgLi = document.createElement('li'); emptyMsgLi.textContent = getLocalizedString('settings_no_saved_items_placeholder');
emptyMsgLi.style.padding = "5px 8px"; emptyMsgLi.style.opacity = "0.7"; listElement.appendChild(emptyMsgLi); return;
}
optionsFromPanelSettings.forEach(opt => {
const listItem = document.createElement('li'); listItem.dataset.id = opt.id; listItem.className = 'gite-exactsize-option-item gite-site-option-item';
const text = opt.label || opt.id; const valueHint = opt.value ? `(${opt.value})` : '';
const deleteBtnTitle = getLocalizedString('btn_delete'); const editBtnTitle = getLocalizedString('btn_edit_label');
listItem.innerHTML = `<input type="checkbox" class="gite-option-enable-toggle" ${opt.isEnabled ? 'checked' : ''} data-opttype="user-site" data-optionid="${opt.id}"><span class="gite-option-text" title="${text}">${text}</span><span class="gite-option-value-hint">${valueHint}</span><div class="gite-actions"><button class="gite-edit-site-btn" data-optionid="${opt.id}" ${GITE_LANG_KEY_ATTR}="btn_edit_label" data-gite-lang-target-attr="title" title="${editBtnTitle}">✏️</button><button class="gite-delete-user-site-btn" data-optionid="${opt.id}" ${GITE_LANG_KEY_ATTR}="btn_delete" data-gite-lang-target-attr="title" title="${deleteBtnTitle}">🗑️</button></div>`;
listElement.appendChild(listItem);
});
}
function handleAddNewSiteFromPanel(event) {
event.preventDefault(); const form = event.target;
const labelInput = form.querySelector('#gite-new-site-label'); const domainInput = form.querySelector('#gite-new-site-domain');
if(!labelInput || !domainInput) { warn("Cannot add new site: Label or domain input element not found in the form."); return; }
const label = labelInput.value.trim(); const rawDomain = domainInput.value.trim();
if (!label) { alert(getLocalizedString('alert_site_label_empty')); labelInput.focus(); return; }
if (!rawDomain) { alert(getLocalizedString('alert_site_domain_empty')); domainInput.focus(); return; }
const domain = cleanDomainInput(rawDomain); if (!domain) { alert(getLocalizedString('alert_site_domain_invalid')); domainInput.focus(); return; }
const userOptionsInPanel = getSettingFromPanel('filters.site.userDefinedOptions', []);
if (userOptionsInPanel.some(opt => opt.label.toLowerCase() === label.toLowerCase() || opt.value.toLowerCase() === domain.toLowerCase())) {
alert(getLocalizedString('alert_site_already_saved')); return;
}
const newId = `user_site_${domain.replace(/[^a-z0-9]/gi, '')}_${Date.now()}`;
const newSiteOption = { id: newId, label, value: domain, isEnabled: true, isCustom: true, type: 'site_filter' };
userOptionsInPanel.push(newSiteOption);
renderSiteSearchList('#gite-sitesearch-userdefined-list', userOptionsInPanel);
form.reset(); domainInput.focus();
}
function handleSiteSearchPanelActions(event) {
const target = event.target; const optionId = target.dataset.optionid; const listItem = target.closest('li.gite-site-option-item');
if (!optionId || !listItem) return;
const userOptionsInPanel = getSettingFromPanel('filters.site.userDefinedOptions', []);
const optionIndex = userOptionsInPanel.findIndex(opt => opt.id === optionId);
if (optionIndex === -1) { if (DEBUG_MODE) warn(`Site option with ID ${optionId} not found in panel settings during action.`); return; }
const optionInPanel = userOptionsInPanel[optionIndex];
if (target.classList.contains('gite-option-enable-toggle')) {
optionInPanel.isEnabled = target.checked;
if (DEBUG_MODE) log(`Site option "${optionInPanel.label}" isEnabled set to ${target.checked} in panel settings.`);
} else if (target.classList.contains('gite-edit-site-btn')) {
const currentLabel = optionInPanel.label; const currentValue = optionInPanel.value;
const newLabel = prompt(getLocalizedString('btn_edit_label') + ` for "${currentLabel}" (Domain: ${currentValue}):`, currentLabel);
if (newLabel === null) return; const trimmedNewLabel = newLabel.trim();
if (!trimmedNewLabel) { alert(getLocalizedString('alert_site_label_empty')); return; }
const newValue = prompt(`Edit domain for "${trimmedNewLabel}" (Current: ${currentValue}):`, currentValue);
if (newValue === null) return; const trimmedNewValue = newValue.trim();
if (!trimmedNewValue) { alert(getLocalizedString('alert_site_domain_empty')); return; }
const cleanedDomain = cleanDomainInput(trimmedNewValue); if (!cleanedDomain) { alert(getLocalizedString('alert_site_domain_invalid')); return; }
if (userOptionsInPanel.some(opt => opt.id !== optionId && (opt.label.toLowerCase() === trimmedNewLabel.toLowerCase() || opt.value.toLowerCase() === cleanedDomain.toLowerCase()))) {
alert(getLocalizedString('alert_site_already_saved')); return;
}
optionInPanel.label = trimmedNewLabel; optionInPanel.value = cleanedDomain;
renderSiteSearchList('#gite-sitesearch-userdefined-list', userOptionsInPanel);
alert(getLocalizedString('alert_site_label_updated'));
} else if (target.classList.contains('gite-delete-user-site-btn')) {
if (confirm(`${getLocalizedString('alert_confirm_delete_option_prefix')}"${optionInPanel.label}"?`)) {
userOptionsInPanel.splice(optionIndex, 1);
renderSiteSearchList('#gite-sitesearch-userdefined-list', userOptionsInPanel);
}
}
}
function renderSimpleOptionsListForCategory(categoryKey, listElementId, includeValueHint = false) {
if (!giteSettingsPanelElement || !currentGiteSettingsForPanel.filters || !currentGiteSettingsForPanel.filters[categoryKey]) {
if (DEBUG_MODE) warn(`Cannot render simple options list for "${categoryKey}": Prerequisites not met.`); return;
}
const listElement = giteSettingsPanelElement.querySelector(`#${listElementId}`);
if (!listElement) { if (DEBUG_MODE) warn(`Options list container #${listElementId} for category "${categoryKey}" not found.`); return; }
listElement.innerHTML = '';
const optionsFromPanelSettings = getSettingFromPanel(`filters.${categoryKey}.options`, []);
if (!optionsFromPanelSettings || optionsFromPanelSettings.length === 0) {
const emptyMsgLi = document.createElement('li'); emptyMsgLi.textContent = getLocalizedString('settings_no_saved_items_placeholder');
emptyMsgLi.style.padding = "5px 8px"; emptyMsgLi.style.opacity = "0.7"; listElement.appendChild(emptyMsgLi); return;
}
optionsFromPanelSettings.forEach(opt => {
if (opt.type === 'separator') {
if (opt.isEnabled) {
const sepLi = document.createElement('li');
sepLi.innerHTML = '<hr style="width:100%; margin: 3px 0; border-color: var(--gite-separator-color); opacity: 0.5;">';
listElement.appendChild(sepLi);
} return;
}
const listItem = document.createElement('li'); listItem.dataset.id = opt.id; listItem.className = 'gite-option-item';
const text = opt.customText || getLocalizedString(opt.textKey, CURRENT_LANGUAGE, opt.id);
let valueHintHtml = '';
if (includeValueHint) {
const hintValue = opt.tbsValue || opt.value || (opt.paramName === 'cr' ? opt.value : '');
if (hintValue) valueHintHtml = `<span class="gite-option-value-hint">(${hintValue})</span>`;
}
listItem.innerHTML = `<input type="checkbox" class="gite-option-enable-toggle" ${opt.isEnabled ? 'checked' : ''}><span class="gite-option-text" title="${text}">${text}</span>${valueHintHtml}`;
listElement.appendChild(listItem);
});
}
function loadTimeOptionsSettingsList() { renderSimpleOptionsListForCategory('time', 'gite-time-options-list', true); }
function loadSizeOptionsSettingsList() { renderSimpleOptionsListForCategory('size', 'gite-size-options-list', true); }
function handleGenericOptionEnableToggle(event, categoryKey) {
const target = event.target; if (!target.classList.contains('gite-option-enable-toggle')) return;
const listItem = target.closest('li.gite-option-item'); if (!listItem) return;
const optionId = listItem.dataset.id; const isEnabled = target.checked;
const optionsListInPanel = getSettingFromPanel(`filters.${categoryKey}.options`, []);
const optionInPanel = optionsListInPanel.find(opt => opt.id === optionId);
if (optionInPanel) { optionInPanel.isEnabled = isEnabled; if(DEBUG_MODE) log(`Option "${optionId}" in category "${categoryKey}" set to isEnabled: ${isEnabled} in panel settings.`); }
else if (DEBUG_MODE) warn(`Option with ID "${optionId}" not found in panel settings for category "${categoryKey}" during toggle action.`);
}
function handleResetCategoryOptions(categoryKey, isGeneralCategory = false) {
let defaultOptionsOrSettings;
let localizedCategoryTitleKey;
let panelUpdateFunction;
if (isGeneralCategory && categoryKey === 'general') {
defaultOptionsOrSettings = GITE_DEFAULT_SETTINGS.general;
localizedCategoryTitleKey = 'settings_tab_general';
panelUpdateFunction = loadGeneralSettingsToPanel;
} else if (categoryKey === 'exactSize') {
defaultOptionsOrSettings = {
predefinedOptions: GITE_DEFAULT_SETTINGS.filters.exactSize.predefinedOptions,
userDefinedOptions: GITE_DEFAULT_SETTINGS.filters.exactSize.userDefinedOptions,
showInputsInMenu: GITE_DEFAULT_SETTINGS.filters.exactSize.showInputsInMenu
};
localizedCategoryTitleKey = 'filter_title_exact_size';
panelUpdateFunction = loadExactSizeSettingsToPanel;
} else if (categoryKey === 'site') {
defaultOptionsOrSettings = { userDefinedOptions: GITE_DEFAULT_SETTINGS.filters.site.userDefinedOptions };
localizedCategoryTitleKey = 'filter_title_site_search';
panelUpdateFunction = loadSiteSearchSettingsToPanel;
} else {
const defaultCategoryConfig = GITE_DEFAULT_SETTINGS.filters[categoryKey];
if (!defaultCategoryConfig || !defaultCategoryConfig.options) {
warn(`No default options defined in GITE_DEFAULT_SETTINGS for category "${categoryKey}". Cannot reset.`); return;
}
defaultOptionsOrSettings = defaultCategoryConfig.options;
switch (categoryKey) {
case 'time': localizedCategoryTitleKey = 'filter_title_time'; panelUpdateFunction = loadTimeOptionsSettingsList; break;
case 'size': localizedCategoryTitleKey = 'filter_title_size'; panelUpdateFunction = loadSizeOptionsSettingsList; break;
case 'region': localizedCategoryTitleKey = 'filter_title_region'; panelUpdateFunction = loadRegionOptionsSettingsList; break;
default: localizedCategoryTitleKey = `filter_title_${categoryKey.toLowerCase()}`; panelUpdateFunction = () => renderSimpleOptionsListForCategory(categoryKey, `gite-${categoryKey}-options-list`, true); break;
}
}
const localizedCategoryTitle = getLocalizedString(localizedCategoryTitleKey);
if (confirm(`${getLocalizedString('btn_reset_options_for_category_prefix')}${localizedCategoryTitle}${getLocalizedString('btn_reset_options_for_category_suffix')}?`)) {
if (isGeneralCategory && categoryKey === 'general') {
setSettingInPanel('general', JSON.parse(JSON.stringify(defaultOptionsOrSettings)));
} else if (categoryKey === 'exactSize') {
setSettingInPanel('filters.exactSize.predefinedOptions', JSON.parse(JSON.stringify(defaultOptionsOrSettings.predefinedOptions)));
setSettingInPanel('filters.exactSize.userDefinedOptions', JSON.parse(JSON.stringify(defaultOptionsOrSettings.userDefinedOptions)));
setSettingInPanel('filters.exactSize.showInputsInMenu', defaultOptionsOrSettings.showInputsInMenu);
} else if (categoryKey === 'site') {
setSettingInPanel('filters.site.userDefinedOptions', JSON.parse(JSON.stringify(defaultOptionsOrSettings.userDefinedOptions)));
} else {
setSettingInPanel(`filters.${categoryKey}.options`, JSON.parse(JSON.stringify(defaultOptionsOrSettings)));
}
if (panelUpdateFunction) panelUpdateFunction();
if(DEBUG_MODE) log(`Options/settings for category/section "${categoryKey}" reset to defaults in panel settings.`);
}
}
function applyToolbarCustomStyles() {
const toolbarContainer = document.getElementById(GITE_TOOLBAR_CONTAINER_ID);
if (!toolbarContainer) return;
const fontSize = giteSettings?.general?.toolbarFontSize || GITE_DEFAULT_SETTINGS.general.toolbarFontSize;
const lineHeight = giteSettings?.general?.toolbarLineHeight || GITE_DEFAULT_SETTINGS.general.toolbarLineHeight;
toolbarContainer.style.setProperty('--gite-toolbar-font-size', fontSize.includes('px') ? fontSize : `${fontSize}px`);
toolbarContainer.style.setProperty('--gite-toolbar-line-height', String(lineHeight));
if (DEBUG_MODE) log(`Applied toolbar styles: Font Size - ${fontSize}, Line Height - ${lineHeight}`);
}
function applyLiveSettingChangesFromPanel() {
if (DEBUG_MODE) log("Applying live setting changes from panel to main settings and UI...");
let languageActuallyChanged = false;
let themeActuallyChanged = false;
let toolbarStyleChanged = false;
const panelSelectedLang = getSettingFromPanel('general.selectedLanguage', 'auto');
const panelEffectiveLang = panelSelectedLang === 'auto' ? detectBrowserLanguageSafe() : panelSelectedLang;
if (CURRENT_LANGUAGE !== panelEffectiveLang) { languageActuallyChanged = true; CURRENT_LANGUAGE = panelEffectiveLang; if(DEBUG_MODE) log(`Language changed to ${CURRENT_LANGUAGE} via panel "Apply" action.`); }
const panelSelectedTheme = getSettingFromPanel('general.themePreference', 'auto');
if ((giteSettings.general.themePreference || 'auto') !== panelSelectedTheme) { themeActuallyChanged = true; }
const panelToolbarFontSize = getSettingFromPanel('general.toolbarFontSize', GITE_DEFAULT_SETTINGS.general.toolbarFontSize);
const panelToolbarLineHeight = getSettingFromPanel('general.toolbarLineHeight', GITE_DEFAULT_SETTINGS.general.toolbarLineHeight);
if (giteSettings.general.toolbarFontSize !== panelToolbarFontSize || giteSettings.general.toolbarLineHeight !== panelToolbarLineHeight) {
toolbarStyleChanged = true;
}
const categoryEnableCheckboxes = { exactSize: '#gite-filter-exactsize-cat-enabled', region: '#gite-filter-region-cat-enabled', site: '#gite-filter-sitesearch-cat-enabled' };
for (const catKey in categoryEnableCheckboxes) {
if(giteSettingsPanelElement) {
const checkbox = giteSettingsPanelElement.querySelector(categoryEnableCheckboxes[catKey]);
if (checkbox && currentGiteSettingsForPanel.filters && currentGiteSettingsForPanel.filters[catKey]) {
setSettingInPanel(`filters.${catKey}.enabled`, checkbox.checked);
}
}
}
// Capture showInputsInMenu for exactSize
const showInputsToggle = giteSettingsPanelElement.querySelector('#gite-exactsize-showinputs-toggle');
if(showInputsToggle && currentGiteSettingsForPanel.filters?.exactSize) {
setSettingInPanel('filters.exactSize.showInputsInMenu', showInputsToggle.checked);
}
giteSettings = JSON.parse(JSON.stringify(currentGiteSettingsForPanel));
if(themeActuallyChanged) detectAndApplyThemeClass();
if(toolbarStyleChanged) applyToolbarCustomStyles();
addToolbarSettingsButtonIfNeeded(document.getElementById(GITE_TOOLBAR_CONTAINER_ID));
updateResultStatsVisibility();
if(languageActuallyChanged) { updateAllLocalizableElements(); setupGMMenus(); }
initializeEnhancedFilters(true);
if(DEBUG_MODE) log("Live changes applied. Filters re-initialized.");
}
function detectBrowserLanguageSafe() {
const browserLangInit = (navigator.language || navigator.userLanguage || 'en').toLowerCase();
if (browserLangInit.startsWith('zh-tw') || browserLangInit.startsWith('zh-hk') || browserLangInit.startsWith('zh-hant')) return 'zh-TW';
if (browserLangInit.startsWith('ja')) return 'ja';
return 'en';
}
function handleSaveSettingsFromPanel(andClose = true) {
if (!giteSettingsPanelElement || !currentGiteSettingsForPanel.general) { warn("Cannot save settings: Panel element or general settings in panel's copy are missing."); return; }
let languageActuallyChanged = false;
let themeActuallyChanged = false;
let showResultStatsChanged = false;
let toolbarStyleChanged = false;
const panelSelectedLang = getSettingFromPanel('general.selectedLanguage', 'auto');
const panelEffectiveLang = panelSelectedLang === 'auto' ? detectBrowserLanguageSafe() : panelSelectedLang;
if (CURRENT_LANGUAGE !== panelEffectiveLang) languageActuallyChanged = true;
const panelSelectedTheme = getSettingFromPanel('general.themePreference', 'auto');
if ((giteSettings.general.themePreference || 'auto') !== panelSelectedTheme) themeActuallyChanged = true;
const panelShowResultStats = getSettingFromPanel('general.showResultStats', true);
if ((giteSettings.general.showResultStats !== undefined ? giteSettings.general.showResultStats : true) !== panelShowResultStats) {
showResultStatsChanged = true;
}
const panelToolbarFontSize = getSettingFromPanel('general.toolbarFontSize', GITE_DEFAULT_SETTINGS.general.toolbarFontSize);
const panelToolbarLineHeight = getSettingFromPanel('general.toolbarLineHeight', GITE_DEFAULT_SETTINGS.general.toolbarLineHeight);
if (giteSettings.general.toolbarFontSize !== panelToolbarFontSize || giteSettings.general.toolbarLineHeight !== panelToolbarLineHeight) {
toolbarStyleChanged = true;
}
const categoryEnableCheckboxes = { exactSize: '#gite-filter-exactsize-cat-enabled', region: '#gite-filter-region-cat-enabled', site: '#gite-filter-sitesearch-cat-enabled' };
for (const catKey in categoryEnableCheckboxes) {
if(giteSettingsPanelElement) {
const checkbox = giteSettingsPanelElement.querySelector(categoryEnableCheckboxes[catKey]);
if (checkbox && currentGiteSettingsForPanel.filters && currentGiteSettingsForPanel.filters[catKey]) setSettingInPanel(`filters.${catKey}.enabled`, checkbox.checked);
}
}
const showInputsToggle = giteSettingsPanelElement.querySelector('#gite-exactsize-showinputs-toggle');
if(showInputsToggle && currentGiteSettingsForPanel.filters?.exactSize) {
setSettingInPanel('filters.exactSize.showInputsInMenu', showInputsToggle.checked);
}
if (languageActuallyChanged) { CURRENT_LANGUAGE = panelEffectiveLang; if(DEBUG_MODE) log(`Language set to ${CURRENT_LANGUAGE} and will be saved.`); }
giteSettings = JSON.parse(JSON.stringify(currentGiteSettingsForPanel));
if (typeof GM_setValue === 'function') {
try {
GM_setValue(GITE_SETTINGS_GM_KEY, JSON.stringify(giteSettings)); if (DEBUG_MODE) log("GITE settings saved to GM storage.");
let alertMsgKey = 'gm_settings_updated_no_reload';
if (themeActuallyChanged) detectAndApplyThemeClass();
if (toolbarStyleChanged) applyToolbarCustomStyles();
if (languageActuallyChanged) { updateAllLocalizableElements(CURRENT_LANGUAGE); setupGMMenus(); }
const needsReload = languageActuallyChanged;
if (!needsReload) {
addToolbarSettingsButtonIfNeeded(document.getElementById(GITE_TOOLBAR_CONTAINER_ID));
updateResultStatsVisibility();
initializeEnhancedFilters(true);
} else {
alertMsgKey = 'alert_settings_saved_reload_required';
}
alert(getLocalizedString(alertMsgKey));
if (needsReload) { if (DEBUG_MODE) log("Reloading page due to a GITE setting change (language)."); window.location.reload(); }
} catch (e) { error("Error saving GITE settings via GM_setValue:", e); alert(getLocalizedString('alert_generic_error_saving')); }
} else { warn("GM_setValue is not available. Settings cannot be saved."); alert(getLocalizedString('alert_gm_setvalue_unavailable')); }
if (andClose && !languageActuallyChanged) closeSettingsPanel(true);
}
function handleResetAllSettingsInPanel() {
if (confirm(getLocalizedString('alert_confirm_reset_all_settings'))) {
currentGiteSettingsForPanel = JSON.parse(JSON.stringify(GITE_DEFAULT_SETTINGS));
const newLangForPanel = currentGiteSettingsForPanel.general.selectedLanguage === 'auto' ? detectBrowserLanguageSafe() : currentGiteSettingsForPanel.general.selectedLanguage;
updateAllLocalizableElements(newLangForPanel, giteSettingsPanelElement);
loadGeneralSettingsToPanel();
loadSizeOptionsSettingsList();
loadExactSizeSettingsToPanel();
loadTimeOptionsSettingsList();
loadRegionSettingsToPanel(); loadRegionOptionsSettingsList();
loadSiteSearchSettingsToPanel();
alert(getLocalizedString('alert_settings_reset_to_default'));
if (DEBUG_MODE) log("All settings in panel reset to defaults. Save to persist.");
}
}
function setupPanelEventListeners() {
if (!giteSettingsPanelElement) { warn("Cannot setup panel event listeners: giteSettingsPanelElement is null."); return; }
const qS = (sel) => giteSettingsPanelElement.querySelector(sel);
qS('#gite-settings-close-btn')?.addEventListener('click', () => closeSettingsPanel());
qS('.gite-modal-overlay')?.addEventListener('click', () => closeSettingsPanel());
const tabNav = qS('.gite-tabs-navigation ul');
if (tabNav) {
tabNav.addEventListener('click', switchTabInPanel);
tabNav.addEventListener('keydown', (e) => { if ((e.key === 'Enter' || e.key === ' ') && e.target.role === 'tab' && !e.target.disabled) { e.preventDefault(); switchTabInPanel(e); }});
}
// General Tab Listeners
qS('#gite-setting-language')?.addEventListener('change', (e) => { setSettingInPanel('general.selectedLanguage', e.target.value); const nl = e.target.value === 'auto' ? detectBrowserLanguageSafe() : e.target.value; updateAllLocalizableElements(nl, giteSettingsPanelElement);});
qS('#gite-setting-theme')?.addEventListener('change', (e) => setSettingInPanel('general.themePreference', e.target.value));
qS('#gite-setting-toolbarfontsize')?.addEventListener('input', (e) => setSettingInPanel('general.toolbarFontSize', e.target.value + 'px'));
qS('#gite-setting-toolbarlineheight')?.addEventListener('input', (e) => setSettingInPanel('general.toolbarLineHeight', e.target.value));
qS('#gite-setting-showtoolbarbutton')?.addEventListener('change', (e) => setSettingInPanel('general.showSettingsButtonOnToolbar', e.target.checked));
qS('#gite-setting-showresultstats')?.addEventListener('change', (e) => setSettingInPanel('general.showResultStats', e.target.checked));
qS('#gite-general-reset-cat-btn')?.addEventListener('click', () => handleResetCategoryOptions('general', true));
// Footer Buttons
qS('#gite-settings-save-btn')?.addEventListener('click', () => handleSaveSettingsFromPanel(true));
qS('#gite-settings-apply-btn')?.addEventListener('click', () => { applyLiveSettingChangesFromPanel(); handleSaveSettingsFromPanel(false); });
qS('#gite-settings-cancel-btn')?.addEventListener('click', () => { closeSettingsPanel(); });
qS('#gite-settings-reset-all-btn')?.addEventListener('click', handleResetAllSettingsInPanel);
// Size Tab
const sizePanel = qS('#gite-panel-size');
if (sizePanel) {
sizePanel.querySelector('#gite-size-options-list')?.addEventListener('change', (e) => handleGenericOptionEnableToggle(e, 'size'));
sizePanel.querySelector('#gite-size-reset-cat-btn')?.addEventListener('click', () => handleResetCategoryOptions('size'));
}
// Exact Size Tab
const esPanel = qS('#gite-panel-exactsize');
if (esPanel) {
esPanel.querySelector('#gite-exactsize-predefined-list')?.addEventListener('change', handleExactSizePanelActions);
const uList = esPanel.querySelector('#gite-exactsize-userdefined-list');
if(uList) { uList.addEventListener('click', handleExactSizePanelActions); uList.addEventListener('change', handleExactSizePanelActions); }
esPanel.querySelector('#gite-add-exact-size-form')?.addEventListener('submit', handleAddNewExactSizeFromPanel);
esPanel.querySelector('#gite-filter-exactsize-cat-enabled')?.addEventListener('change', (e) => {
setSettingInPanel('filters.exactSize.enabled', e.target.checked);
esPanel.querySelectorAll('.gite-exactsize-panel-section > *:not(.gite-setting-group):not(label), .gite-exactsize-panel-section > h3, .gite-exactsize-panel-section > ul, .gite-exactsize-panel-section > form, .gite-panel-category-footer, #gite-exactsize-showinputs-toggle, label[for="gite-exactsize-showinputs-toggle"]')
.forEach(s => {
s.style.opacity = e.target.checked ? '1' : '0.5';
if(s.tagName === 'INPUT' || s.tagName === 'BUTTON' || s.tagName === 'SELECT' || s.tagName === 'UL') {
s.disabled = !e.target.checked;
if (s.tagName === 'UL') s.style.pointerEvents = e.target.checked ? '' : 'none';
}
});
});
esPanel.querySelector('#gite-exactsize-showinputs-toggle')?.addEventListener('change', (e) => setSettingInPanel('filters.exactSize.showInputsInMenu', e.target.checked));
esPanel.querySelector('#gite-exact_size-reset-cat-btn')?.addEventListener('click', () => handleResetCategoryOptions('exactSize'));
}
// Time Tab
const timePanel = qS('#gite-panel-time');
if (timePanel) {
timePanel.querySelector('#gite-time-options-list')?.addEventListener('change', (e) => handleGenericOptionEnableToggle(e, 'time'));
timePanel.querySelector('#gite-time-reset-cat-btn')?.addEventListener('click', () => handleResetCategoryOptions('time'));
}
// Region Tab
const regionPanel = qS('#gite-panel-region');
if (regionPanel) {
regionPanel.querySelector('#gite-filter-region-cat-enabled')?.addEventListener('change', (e) => {
setSettingInPanel('filters.region.enabled', e.target.checked);
regionPanel.querySelectorAll('.gite-options-list, .gite-panel-category-footer, p, h3:not(:first-of-type)')
.forEach(el => {
el.style.opacity = e.target.checked ? '1' : '0.5';
if(el.tagName === 'UL' || el.tagName === 'BUTTON') el.style.pointerEvents = e.target.checked ? '' : 'none';
});
});
regionPanel.querySelector('#gite-region-options-list')?.addEventListener('change', (e) => handleGenericOptionEnableToggle(e, 'region'));
regionPanel.querySelector('#gite-region-reset-cat-btn')?.addEventListener('click', () => handleResetCategoryOptions('region'));
}
// Site Search Tab
const siteSearchPanel = qS('#gite-panel-sitesearch');
if (siteSearchPanel) {
siteSearchPanel.querySelector('#gite-filter-sitesearch-cat-enabled')?.addEventListener('change', (e) => {
setSettingInPanel('filters.site.enabled', e.target.checked);
siteSearchPanel.querySelectorAll('.gite-options-panel-section, .gite-panel-category-footer')
.forEach(s => { s.style.display = e.target.checked ? '' : 'none'; });
});
siteSearchPanel.querySelector('#gite-add-new-site-form')?.addEventListener('submit', handleAddNewSiteFromPanel);
const userSiteList = siteSearchPanel.querySelector('#gite-sitesearch-userdefined-list');
if (userSiteList) { userSiteList.addEventListener('click', handleSiteSearchPanelActions); userSiteList.addEventListener('change', handleSiteSearchPanelActions); }
siteSearchPanel.querySelector('#gite-site_search-reset-cat-btn')?.addEventListener('click', () => handleResetCategoryOptions('site'));
}
}
function createGiteToolbarSettingsButton() {
const button = document.createElement('div'); button.id = GITE_SETTINGS_BUTTON_ID; button.className = 'BaegVc YmvwI';
button.setAttribute('role', 'button'); button.setAttribute('tabindex', '0'); button.innerHTML = '⚙︎';
button.setAttribute(GITE_LANG_KEY_ATTR, 'tooltip_gite_settings_button'); button.setAttribute('data-gite-lang-target-attr', 'title');
button.title = getLocalizedString('tooltip_gite_settings_button');
button.addEventListener('click', openSettingsPanel);
button.addEventListener('keydown', (e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); openSettingsPanel(); } });
return button;
}
function addToolbarSettingsButtonIfNeeded(toolbarContainer) {
if (!toolbarContainer) {
if (DEBUG_MODE) warn("GITE toolbar container not provided for settings button.");
return;
}
let existingButton = toolbarContainer.querySelector(`#${GITE_SETTINGS_BUTTON_ID}`);
const showButtonSetting = giteSettings && giteSettings.general ? giteSettings.general.showSettingsButtonOnToolbar : true;
if (showButtonSetting) {
if (!existingButton) {
const giteSettingsBtn = createGiteToolbarSettingsButton();
toolbarContainer.appendChild(giteSettingsBtn);
if (DEBUG_MODE) log("GITE Toolbar Settings button added to GITE toolbar.");
} else {
existingButton.style.display = 'inline-flex';
}
} else {
if (existingButton) {
existingButton.remove();
if (DEBUG_MODE) log("GITE Toolbar Settings button removed from GITE toolbar as per settings.");
}
}
}
function updateResultStatsVisibility() {
const giteToolbarContainer = document.getElementById(GITE_TOOLBAR_CONTAINER_ID);
if (!giteToolbarContainer) return;
const existingGiteStatsSpan = giteToolbarContainer.querySelector(`#${GITE_RESULT_STATS_DISPLAY_ID}`);
if (giteSettings.general.showResultStats) {
if (!existingGiteStatsSpan) {
observeResultStats(giteToolbarContainer);
} else {
existingGiteStatsSpan.style.display = '';
}
} else {
if (existingGiteStatsSpan) {
existingGiteStatsSpan.remove();
}
if (resultStatsObserver) { resultStatsObserver.disconnect(); resultStatsObserver = null; }
if (resultStatsRetryTimeout) { clearTimeout(resultStatsRetryTimeout); resultStatsRetryTimeout = null; }
}
}
function _getActiveColorFilterValues(currentUrl) {
const imgcParam = currentUrl.searchParams.get('imgc'); const tbsParam = currentUrl.searchParams.get('tbs') || "";
let effectiveValue = "";
if (tbsParam.includes("ic:gray")) effectiveValue = "ic:gray";
else if (tbsParam.includes("ic:trans")) effectiveValue = "ic:trans";
else if (tbsParam.includes("ic:specific,isc:")) { const match = tbsParam.match(/ic:specific,isc:[^,]+/); if (match) effectiveValue = match[0]; }
else if (tbsParam.includes("ic:color")) effectiveValue = "ic:color";
else if (imgcParam === "color" && !tbsParam.includes("ic:")) effectiveValue = "ic:color";
else if (imgcParam === "gray" && !tbsParam.includes("ic:")) effectiveValue = "ic:gray";
else if (imgcParam === "trans" && !tbsParam.includes("ic:")) effectiveValue = "ic:trans";
return { valueForComparison: effectiveValue, tbs: tbsParam, imgc: imgcParam };
}
function areGiteFiltersActive() {
const params = new URLSearchParams(window.location.search);
if (params.get('imgsz') || params.get('imgar') || params.get('as_filetype') || params.has('cr')) return true;
const qParam = params.get('q') || ""; if (qParam.includes("imagesize:") || qParam.includes("site:")) return true;
const tbs = params.get('tbs');
if (tbs) { const giteTbsKeys = ['itp:', 'ic:', 'isc:', 'sur:', 'qdr:', 'cdr:', 'isz:', 'islt:', 'isilu:']; if (giteTbsKeys.some(key => tbs.includes(key))) return true; }
if (params.get('imgc') === 'color' && !(tbs && tbs.includes("ic:"))) return true;
if (params.get('imgc') === 'gray' && !(tbs && tbs.includes("ic:"))) return true;
if (params.get('imgc') === 'trans' && !(tbs && tbs.includes("ic:"))) return true;
return false;
}
function updateGiteClearButtonVisibility(containerElement) {
if (!containerElement) {
if (DEBUG_MODE) warn("Container for Clear button not provided for updateGiteClearButtonVisibility.");
containerElement = document.querySelector(`#${GITE_TOOLBAR_CONTAINER_ID} .gite-filter-buttons-group`);
if (!containerElement) {
if (DEBUG_MODE && enhancedFiltersInitializedThisInstance) warn("GITE filter buttons group not found for Clear button.");
return;
}
}
let giteClearButton = containerElement.querySelector(`#${GITE_CLEAR_BUTTON_ID}`);
if (areGiteFiltersActive()) {
if (!giteClearButton) {
giteClearButton = document.createElement('a');
giteClearButton.id = GITE_CLEAR_BUTTON_ID;
giteClearButton.className = 'gite-clear-button';
giteClearButton.style.textDecoration = 'none';
giteClearButton.style.cursor = 'pointer';
giteClearButton.setAttribute(GITE_LANG_KEY_ATTR, 'btn_clear');
giteClearButton.textContent = getLocalizedString('btn_clear');
giteClearButton.href = "javascript:void(0);";
giteClearButton.addEventListener('click', (e) => {
e.preventDefault();
if (DEBUG_MODE) log("GITE Clear button clicked.");
const clearUrl = buildNewUrl('', '', null, true);
window.location.href = clearUrl;
});
const advancedSearchBtn = containerElement.querySelector('.gite-advanced-search-button');
if (advancedSearchBtn && advancedSearchBtn.nextSibling) {
containerElement.insertBefore(giteClearButton, advancedSearchBtn.nextSibling);
} else {
containerElement.appendChild(giteClearButton);
}
if (DEBUG_MODE) log(`GITE Clear button added to specified container.`);
}
giteClearButton.style.display = CLEAR_BUTTON_TARGET_DISPLAY_STYLE;
} else {
if (giteClearButton) {
giteClearButton.style.display = 'none';
}
}
}
function isImageUrl() { const href = window.location.href; return href.includes('tbm=isch') || href.includes('udm=2'); }
function _getActiveFilterValueForComparison(currentUrl, categoryKey, paramNameForSimpleParams) {
let valueForComparison = ""; let isEffectivelyActive = false;
if (categoryKey === 'color') { const colorVals = _getActiveColorFilterValues(currentUrl); valueForComparison = colorVals.valueForComparison; }
else if (categoryKey === 'exactSize') { const qParam = currentUrl.searchParams.get('q') || ""; valueForComparison = qParam.match(/imagesize:(\d+x\d+)/)?.[1] || ""; }
else if (categoryKey === 'site') { const qParam = currentUrl.searchParams.get('q') || ""; valueForComparison = qParam.match(/site:([^\s]+)/)?.[1] || ""; }
else if (categoryKey === 'time') { const tbs = currentUrl.searchParams.get('tbs') || ""; valueForComparison = tbs.match(/(cdr:1,cd_min:[^,]+,cd_max:[^,]+)/)?.[0] || tbs.match(/qdr:[^,]+/)?.[0] || ""; }
else if (categoryKey === 'region') valueForComparison = currentUrl.searchParams.get(paramNameForSimpleParams) || "";
else if (paramNameForSimpleParams === 'tbs') {
const tbs = currentUrl.searchParams.get('tbs') || "";
const firstOptionWithTbs = (GITE_OPTION_DEFINITIONS[categoryKey] || []).find(opt => opt.tbsValue && opt.tbsValue.includes(':'));
let tbsPrefixForCategory = "";
if (firstOptionWithTbs && firstOptionWithTbs.tbsValue) {
tbsPrefixForCategory = firstOptionWithTbs.tbsValue.split(':')[0] + ':';
} else if (DEBUG_MODE && GITE_OPTION_DEFINITIONS[categoryKey]) {
warn(`_getActiveFilterValueForComparison: No tbsValue with colon found for category '${categoryKey}' to determine its tbsPrefix.`);
}
if (tbsPrefixForCategory) {
valueForComparison = tbs.split(',').find(p => p.startsWith(tbsPrefixForCategory)) || "";
}
} else {
valueForComparison = currentUrl.searchParams.get(paramNameForSimpleParams) || "";
}
isEffectivelyActive = !!valueForComparison;
return { valueForComparison, isEffectivelyActive };
}
function _determineActiveOptionAndDisplayValue(optionsArray, categoryKey, valueForComparison, originalTitleLocalized) {
let activeOptionFound = null;
let activeFilterDisplayValue = originalTitleLocalized;
const nonSeparatorOptions = optionsArray.filter(opt => opt.type !== "separator");
if (categoryKey === 'time' && typeof valueForComparison === 'string' && valueForComparison.startsWith("cdr:1")) {
activeOptionFound = nonSeparatorOptions.find(opt => opt.type === "custom_date_trigger");
} else {
activeOptionFound = nonSeparatorOptions.find(opt => {
let valueFromOptionDef = (opt.categoryKey === 'color' && opt.type === 'palette') ? opt.tbsValue :
(opt.tbsValue !== undefined && opt.tbsValue !== null) ? opt.tbsValue :
opt.value;
return typeof valueFromOptionDef === 'string' && typeof valueForComparison === 'string' &&
valueFromOptionDef.toLowerCase() === valueForComparison.toLowerCase();
});
}
if (activeOptionFound) {
const isAnyOption = (
(activeOptionFound.value === "" && (!activeOptionFound.tbsValue || activeOptionFound.tbsValue === "")) ||
activeOptionFound.type === 'imagesize_clear' ||
activeOptionFound.type === 'site_clear' ||
(categoryKey === 'region' && activeOptionFound.type === 'region_clear') ||
(activeOptionFound.textKey && [
'option_text_size_any', 'option_text_ar_any', 'option_text_color_any',
'option_text_type_any', 'option_text_time_any', 'option_text_rights_any',
'option_text_filetype_any', 'option_text_region_any', 'option_text_site_any'
].includes(activeOptionFound.textKey))
);
if (isAnyOption) {
activeFilterDisplayValue = originalTitleLocalized;
} else {
activeFilterDisplayValue = activeOptionFound.text;
if (categoryKey === 'site' && activeOptionFound.value !== "") {
activeFilterDisplayValue = getLocalizedString('text_site_search_active_prefix') + activeOptionFound.text;
} else if (activeOptionFound.type === "custom_date_trigger" && typeof valueForComparison === 'string' && valueForComparison.startsWith("cdr:1,cd_min:")) {
const match = valueForComparison.match(/cd_min:([^,]+),cd_max:([^,]+)/);
if (match) {
const parseDate = (d) => { if (!d) return null; const p = d.split('/'); if (p.length === 3) return new Date(parseInt(p[2]), parseInt(p[0]) - 1, parseInt(p[1])); return null; };
const sD = parseDate(match[1]); const eD = parseDate(match[2]);
if (sD && eD && !isNaN(sD.getTime()) && !isNaN(eD.getTime())) {
const isZh = CURRENT_LANGUAGE.startsWith('zh'); const dateOpts = { year: 'numeric', month: isZh ? 'long' : 'short', day: 'numeric' };
const locale = isZh ? 'zh-TW' : (CURRENT_LANGUAGE === 'ja' ? 'ja-JP' : (GITE_I18N_STRINGS[CURRENT_LANGUAGE] ? CURRENT_LANGUAGE : 'en-US'));
try { activeFilterDisplayValue = `${sD.toLocaleDateString(locale, dateOpts)} – ${eD.toLocaleDateString(locale, dateOpts)}`; }
catch (e) { activeFilterDisplayValue = `${match[1]} – ${match[2]}`; warn("Error formatting date range for display:", e); }
} else activeFilterDisplayValue = activeOptionFound.text;
} else activeFilterDisplayValue = activeOptionFound.text;
} else if (categoryKey === 'exactSize' && valueForComparison && activeOptionFound.value !== valueForComparison) {
activeFilterDisplayValue = valueForComparison;
} else if (categoryKey === 'color' && activeOptionFound.type === 'palette' && activeOptionFound.hex && valueForComparison) {
activeFilterDisplayValue = activeOptionFound.text;
}
}
} else if (valueForComparison) {
if (categoryKey === 'size' && (valueForComparison.startsWith('isz:gt') || valueForComparison.startsWith('islt:')) ) {
const m = valueForComparison.match(/(?:isz:gt|islt:)(.*)/i);
if (m && m[1]) activeFilterDisplayValue = `${getLocalizedString('text_larger_than_prefix')}${m[1].toUpperCase()}`;
} else if (categoryKey === 'exactSize') activeFilterDisplayValue = valueForComparison;
else if (categoryKey === 'site') activeFilterDisplayValue = getLocalizedString('text_site_search_active_prefix') + valueForComparison;
}
return { activeOptionFound, activeFilterDisplayValue };
}
function _updateFilterButtonAppearance(triggerButtonTextElement, triggerButtonDiv, categoryKey, activeOptionFound, activeFilterDisplayValue, isFilterEffectivelyActive) {
triggerButtonTextElement.innerHTML = '';
if (categoryKey === 'color' && activeOptionFound && activeOptionFound.type === 'palette' && activeOptionFound.hex && isFilterEffectivelyActive) {
const indicator = document.createElement('span'); indicator.className = 'gite-active-color-indicator'; indicator.style.backgroundColor = activeOptionFound.hex;
triggerButtonTextElement.appendChild(indicator);
triggerButtonTextElement.appendChild(document.createTextNode(" " + activeFilterDisplayValue));
triggerButtonTextElement.title = activeFilterDisplayValue;
} else {
triggerButtonTextElement.textContent = activeFilterDisplayValue;
triggerButtonTextElement.removeAttribute('title');
}
if (triggerButtonDiv) {
triggerButtonDiv.classList.toggle("gite-filter-active", isFilterEffectivelyActive);
}
}
function _updateMenuItemsSelectionState(menuElement, nonSeparatorOptions, valueForComparisonFromUrl) {
const allMenuItems = menuElement.querySelectorAll(`li.${GITE_MENU_ITEM_CLASS}`);
allMenuItems.forEach(itemLi => {
itemLi.classList.remove('gite-menu-item-selected');
itemLi.classList.remove('gite-menu-item-focused');
const anchor = itemLi.querySelector('a');
const optionId = itemLi.dataset.optionId;
const optionDef = nonSeparatorOptions.find(opt => opt.id === optionId);
let thisItemShouldBeSelected = false;
const itemFilterValueAttr = itemLi.getAttribute(FILTER_VALUE_ATTR);
if (itemFilterValueAttr !== null && itemFilterValueAttr !== undefined) {
if (optionDef && optionDef.type === "custom_date_trigger") {
thisItemShouldBeSelected = typeof valueForComparisonFromUrl === 'string' && valueForComparisonFromUrl.startsWith("cdr:1");
} else if (typeof itemFilterValueAttr === 'string' && typeof valueForComparisonFromUrl === 'string' &&
itemFilterValueAttr.toLowerCase() === valueForComparisonFromUrl.toLowerCase()) {
thisItemShouldBeSelected = true;
}
}
if (anchor) {
if (optionDef && optionDef.type === "custom_date_trigger") {
anchor.removeAttribute('aria-checked');
if (thisItemShouldBeSelected) {
itemLi.classList.add('gite-menu-item-selected');
}
} else if (itemLi.getAttribute('role') === 'menuitemradio') {
anchor.setAttribute('aria-checked', thisItemShouldBeSelected.toString());
if (thisItemShouldBeSelected) {
itemLi.classList.add('gite-menu-item-selected');
}
} else {
anchor.removeAttribute('aria-checked');
if (thisItemShouldBeSelected) {
itemLi.classList.add('gite-menu-item-selected');
}
}
} else if (itemLi.querySelector('.gite-color-swatch')) {
const swatchAnchor = itemLi.querySelector('.gite-color-swatch');
if (swatchAnchor && swatchAnchor.getAttribute(FILTER_VALUE_ATTR) === valueForComparisonFromUrl) {
itemLi.classList.add('gite-menu-item-selected');
}
}
});
}
function _updateExactSizeInputsInMenu(menuElement, valueForComparisonFromUrl, activeOptionFound) {
const inputAreaLi = menuElement.querySelector('.gite-custom-imagesize-input-area')?.parentElement;
if (!inputAreaLi) return;
const idPrefix = menuElement.parentElement.id.replace('-menu-container', '');
const widthInput = inputAreaLi.querySelector(`#gite-imagesize-width-${idPrefix}`);
const heightInput = inputAreaLi.querySelector(`#gite-imagesize-height-${idPrefix}`);
if (widthInput && heightInput) {
if (valueForComparisonFromUrl && valueForComparisonFromUrl.includes('x') && (!activeOptionFound || activeOptionFound.value !== valueForComparisonFromUrl)) {
const [w, h] = valueForComparisonFromUrl.split('x'); widthInput.value = w; heightInput.value = h;
} else if (!activeOptionFound || activeOptionFound.type === 'imagesize_clear' || valueForComparisonFromUrl === "") { widthInput.value = ''; heightInput.value = ''; }
else if (activeOptionFound && activeOptionFound.type === 'imagesize' && activeOptionFound.value.includes('x')) {
widthInput.value = ''; heightInput.value = '';
}
}
}
function updateFilterStates(filterInfo) {
if (!filterInfo || !filterInfo.menuElement || !filterInfo.triggerButtonTextElement || !filterInfo.triggerButtonDiv) {
warn(`updateFilterStates: Missing essential info for category '${filterInfo.categoryKey}'. FilterInfo:`, filterInfo);
return;
}
const { menuElement, paramName, categoryKey, originalTitleKey, triggerButtonTextElement, triggerButtonDiv } = filterInfo;
const originalTitleLocalized = getLocalizedString(originalTitleKey);
const optionsArrayForCategory = getFilterOptionsFromSettings(categoryKey);
const currentUrl = new URL(window.location.href);
let effectiveParamName = paramName;
const activeFilterData = _getActiveFilterValueForComparison(currentUrl, categoryKey, effectiveParamName);
let valueForComparisonToUse;
let isFilterCurrentlyActive;
if (typeof activeFilterData === 'object' && activeFilterData !== null &&
activeFilterData.hasOwnProperty('valueForComparison') && activeFilterData.hasOwnProperty('isEffectivelyActive')) {
valueForComparisonToUse = activeFilterData.valueForComparison;
isFilterCurrentlyActive = activeFilterData.isEffectivelyActive;
} else {
error(`_getActiveFilterValueForComparison did not return expected object for category '${categoryKey}'. Got:`, activeFilterData);
valueForComparisonToUse = "";
isFilterCurrentlyActive = false;
}
const { activeOptionFound, activeFilterDisplayValue } = _determineActiveOptionAndDisplayValue(
optionsArrayForCategory, categoryKey, valueForComparisonToUse, originalTitleLocalized
);
_updateFilterButtonAppearance(
triggerButtonTextElement, triggerButtonDiv, categoryKey, activeOptionFound, activeFilterDisplayValue, isFilterCurrentlyActive
);
_updateMenuItemsSelectionState(
menuElement, optionsArrayForCategory.filter(opt => opt.type !== "separator"), valueForComparisonToUse
);
if (categoryKey === 'exactSize' && menuElement && giteSettings.filters.exactSize?.showInputsInMenu !== false) {
_updateExactSizeInputsInMenu( menuElement, valueForComparisonToUse, activeOptionFound );
}
}
let isInitializingFilters = false;
function initializeEnhancedFilters(isReInit = false) {
if (isInitializingFilters && !isReInit) {
if (DEBUG_MODE) log("Filter initialization already in progress. Skipping redundant call.");
return;
}
isInitializingFilters = true;
const isFirstRunAttemptOnThisPageInstance = !enhancedFiltersInitializedThisInstance;
if (DEBUG_MODE) log(`Initializing GITE Filters (v${GITE_SCRIPT_VERSION}). Lang: ${CURRENT_LANGUAGE}. FirstRun: ${isFirstRunAttemptOnThisPageInstance}. Re-init call: ${isReInit}`);
let giteToolbarContainer = document.getElementById(GITE_TOOLBAR_CONTAINER_ID);
if (giteToolbarContainer) {
if (DEBUG_MODE) log("Removing existing GITE toolbar for re-initialization.");
customFilterMenus = [];
giteToolbarContainer.remove();
}
giteToolbarContainer = document.createElement('div');
giteToolbarContainer.id = GITE_TOOLBAR_CONTAINER_ID;
applyToolbarCustomStyles(); // Apply font size/line height from settings
const beforeAppBarElement = document.querySelector(BEFORE_APPBAR_SELECTOR);
if (!beforeAppBarElement) {
error("CRITICAL: #before-appbar element not found. Cannot insert GITE toolbar.");
isInitializingFilters = false; return;
}
beforeAppBarElement.parentNode.insertBefore(giteToolbarContainer, beforeAppBarElement);
if (DEBUG_MODE) log("GITE Toolbar container inserted above #before-appbar.");
customFilterMenus = [];
const filterButtonsGroup = document.createElement('div');
filterButtonsGroup.className = 'gite-filter-buttons-group';
giteToolbarContainer.appendChild(filterButtonsGroup);
const filterCategoriesToAdd = [
{ key: 'size', titleKey: 'filter_title_size', param: 'imgsz', idPrefix: 'gite-size' },
{ key: 'exactSize', titleKey: 'filter_title_exact_size', param: 'q', idPrefix: 'gite-exactsize' },
{ key: 'aspectRatio', titleKey: 'filter_title_aspect_ratio', param: 'imgar', idPrefix: 'gite-ar' },
{ key: 'color', titleKey: 'filter_title_color', param: 'tbs', idPrefix: 'gite-color' },
{ key: 'type', titleKey: 'filter_title_type', param: 'tbs', idPrefix: 'gite-type' },
{ key: 'time', titleKey: 'filter_title_time', param: 'tbs', idPrefix: 'gite-time' },
{ key: 'usageRights', titleKey: 'filter_title_usage_rights', param: 'tbs', idPrefix: 'gite-rights' },
{ key: 'fileType', titleKey: 'filter_title_file_type', param: 'as_filetype', idPrefix: 'gite-filetype' },
{ key: 'region', titleKey: 'filter_title_region', param: 'cr', idPrefix: 'gite-region' },
{ key: 'site', titleKey: 'filter_title_site_search', param: 'q', idPrefix: 'gite-site' }
];
filterCategoriesToAdd.forEach(catConfig => {
const categorySettings = giteSettings.filters[catConfig.key];
let isEnabledForDisplay = categorySettings && categorySettings.hasOwnProperty('enabled') ? categorySettings.enabled : true;
if (isEnabledForDisplay) {
const filterMenuInfo = addFilterCategoryMenu(filterButtonsGroup, catConfig.key, catConfig.titleKey, catConfig.param, catConfig.idPrefix);
if (filterMenuInfo) {
customFilterMenus.push(filterMenuInfo);
}
} else if (DEBUG_MODE) {
log(`GITE filter '${getLocalizedString(catConfig.titleKey)}' (key: ${catConfig.key}) disabled by settings. Not adding to UI.`);
}
});
let currentKeywords = "";
const searchInputFields = [
document.querySelector('input[name="q"][role="combobox"]'), document.querySelector('input[name="q"][title*="Search"]'),
document.querySelector('input[name="q"][aria-label*="Search"]'), document.querySelector('textarea[name="q"][role="combobox"]')
];
const searchInput = searchInputFields.find(input => input !== null);
if (searchInput && searchInput.value) { currentKeywords = searchInput.value; }
else { const urlParams = new URLSearchParams(window.location.search); currentKeywords = urlParams.get('q') || ""; }
let cleanKeywordsForAdvancedSearch = currentKeywords.replace(/imagesize:[\d]+x[\d]+/gi, '').replace(/site:[^\s]+/gi, '').replace(/\s\s+/g, ' ').trim();
const advancedSearchButton = document.createElement('a');
let advancedSearchUrl = 'https://www.google.com/advanced_image_search';
if (cleanKeywordsForAdvancedSearch) { advancedSearchUrl += `?as_q=${encodeURIComponent(cleanKeywordsForAdvancedSearch)}`; }
advancedSearchButton.href = advancedSearchUrl;
advancedSearchButton.className = 'gite-advanced-search-button';
advancedSearchButton.textContent = getLocalizedString('filter_title_advanced_search');
advancedSearchButton.setAttribute(GITE_LANG_KEY_ATTR, 'filter_title_advanced_search');
advancedSearchButton.style.textDecoration = 'none';
filterButtonsGroup.appendChild(advancedSearchButton);
if (DEBUG_MODE) log("Advanced Search button added with URL: ", advancedSearchUrl);
updateGiteClearButtonVisibility(filterButtonsGroup);
if (giteSettings.general.showResultStats) {
observeResultStats(giteToolbarContainer);
} else {
const existingStats = giteToolbarContainer.querySelector(`#${GITE_RESULT_STATS_DISPLAY_ID}`);
if (existingStats) existingStats.remove();
if (resultStatsObserver) { resultStatsObserver.disconnect(); resultStatsObserver = null; }
if (resultStatsRetryTimeout) { clearTimeout(resultStatsRetryTimeout); resultStatsRetryTimeout = null; }
}
addToolbarSettingsButtonIfNeeded(giteToolbarContainer);
customFilterMenus.forEach(filterInfo => updateFilterStates(filterInfo));
enhancedFiltersInitializedThisInstance = true;
isInitializingFilters = false;
if (!document.body.hasAttribute('data-gite-global-click-listener')) {
document.body.setAttribute('data-gite-global-click-listener', 'true');
document.addEventListener('click', (event) => {
if (currentlyOpenGiteMenu && currentlyOpenGiteMenu.menuContainer) {
const wrapper = currentlyOpenGiteMenu.triggerButton.closest('.gite-filter-button-wrapper');
if (wrapper && !wrapper.contains(event.target)) {
closeCurrentlyOpenGiteMenu(false);
}
}
}, true);
}
if (!document.body.hasAttribute('data-gite-global-keydown-listener')) {
document.body.setAttribute('data-gite-global-keydown-listener', 'true');
document.addEventListener('keydown', (event) => {
if (event.key === 'Escape' && currentlyOpenGiteMenu) {
closeCurrentlyOpenGiteMenu(true);
event.preventDefault(); event.stopPropagation();
}
if (currentlyOpenGiteMenu && currentlyOpenGiteMenu.menuUl) {
const items = Array.from(currentlyOpenGiteMenu.menuUl.querySelectorAll(`li.${GITE_MENU_ITEM_CLASS}:not([role="separator"])`));
if (!items.length) return;
let currentIndex = items.findIndex(item => item.classList.contains('gite-menu-item-focused'));
let nextIndex = -1;
if (event.key === 'ArrowDown') {
event.preventDefault(); nextIndex = (currentIndex + 1) % items.length;
} else if (event.key === 'ArrowUp') {
event.preventDefault(); nextIndex = (currentIndex - 1 + items.length) % items.length;
} else if (event.key === 'Enter' || event.key === ' ') {
if (currentIndex >= 0 && items[currentIndex].classList.contains('gite-menu-item-focused')) {
event.preventDefault();
const clickable = items[currentIndex].querySelector('a') || items[currentIndex].querySelector('.gite-color-swatch') || items[currentIndex];
clickable.click();
}
} else if (event.key === 'Tab') {
closeCurrentlyOpenGiteMenu(false); return;
}
if (nextIndex !== -1) {
if (currentIndex >= 0) items[currentIndex].classList.remove('gite-menu-item-focused');
items[nextIndex].classList.add('gite-menu-item-focused');
items[nextIndex].focus();
if(currentlyOpenGiteMenu.menuContainer) currentlyOpenGiteMenu.menuContainer.setAttribute('aria-activedescendant', items[nextIndex].id || items[nextIndex].dataset.optionId || '');
}
}
});
}
setTimeout(() => updateGiteClearButtonVisibility(document.querySelector(`#${GITE_TOOLBAR_CONTAINER_ID} .gite-filter-buttons-group`)), FINAL_UI_UPDATE_DELAY_MS);
}
let menuObserver = null; let menuObserverRetries = MAX_OBSERVER_TRIES;
function initializeMainScript() {
enhancedFiltersInitializedThisInstance = false;
menuObserverRetries = MAX_OBSERVER_TRIES;
const onUiReadyForFilters = () => {
if (DEBUG_MODE) log(`UI ready for GITE filters (based on #before-appbar).`);
if (menuObserver) {
menuObserver.disconnect();
menuObserver = null;
if (DEBUG_MODE) log("Main menu observer stopped.");
}
initializeEnhancedFilters(false);
};
setupMenuObserver(onUiReadyForFilters);
}
function setupMenuObserver(callbackOnUiReady) {
if (menuObserver) { menuObserver.disconnect(); menuObserver = null; }
menuObserverRetries = MAX_OBSERVER_TRIES;
const beforeAppBar = document.querySelector(BEFORE_APPBAR_SELECTOR);
if (beforeAppBar) {
if (DEBUG_MODE) log("Element #before-appbar found immediately. Calling UI ready callback.");
callbackOnUiReady();
return;
}
if (DEBUG_MODE) log("#before-appbar not immediately available. Starting MutationObserver for GITE Toolbar.");
const observerTargetNode = document.body || document.documentElement;
menuObserver = new MutationObserver((mutationsList, obs) => {
const currentBeforeAppBar = document.querySelector(BEFORE_APPBAR_SELECTOR);
if (currentBeforeAppBar) {
if (DEBUG_MODE) log("GITE Toolbar Observer: #before-appbar now available.");
obs.disconnect(); menuObserver = null;
callbackOnUiReady();
} else {
menuObserverRetries--;
if (menuObserverRetries <= 0) {
obs.disconnect(); menuObserver = null;
warn("GITE menu observer timed out waiting for #before-appbar.");
}
}
});
menuObserver.observe(observerTargetNode, { childList: true, subtree: true });
}
window.addEventListener('popstate', function(event) {
if (isImageUrl()) {
if (DEBUG_MODE) log("Popstate event on an image URL. Re-evaluating GITE UI.");
closeDatePicker();
closeCurrentlyOpenGiteMenu(false);
setTimeout(() => {
detectAndApplyThemeClass();
enhancedFiltersInitializedThisInstance = false;
if(menuObserver) { menuObserver.disconnect(); menuObserver = null; }
if (resultStatsObserver) { resultStatsObserver.disconnect(); resultStatsObserver = null; }
if (resultStatsRetryTimeout) { clearTimeout(resultStatsRetryTimeout); resultStatsRetryTimeout = null; }
const oldGiteToolbar = document.getElementById(GITE_TOOLBAR_CONTAINER_ID);
if (oldGiteToolbar) {
customFilterMenus = [];
oldGiteToolbar.remove();
}
if (DEBUG_MODE) log("Popstate: Re-initializing GITE main script for new toolbar.");
initializeMainScript();
}, POPSTATE_UPDATE_DELAY_MS);
} else {
if (DEBUG_MODE) log("Popstate event: Navigated away from image URL. Cleaning up GITE UI.");
closeDatePicker();
closeCurrentlyOpenGiteMenu(false);
enhancedFiltersInitializedThisInstance = false;
if(menuObserver) { menuObserver.disconnect(); menuObserver = null; }
if (resultStatsObserver) { resultStatsObserver.disconnect(); resultStatsObserver = null; }
if (resultStatsRetryTimeout) { clearTimeout(resultStatsRetryTimeout); resultStatsRetryTimeout = null; }
customFilterMenus = [];
const giteToolbar = document.getElementById(GITE_TOOLBAR_CONTAINER_ID); if(giteToolbar) giteToolbar.remove();
}
});
function handleResetAllSettingsConfirmed() {
if (confirm(getLocalizedString('alert_confirm_reset_all_settings'))) {
giteSettings = JSON.parse(JSON.stringify(GITE_DEFAULT_SETTINGS));
if (typeof GM_setValue === 'function') {
try {
GM_setValue(GITE_SETTINGS_GM_KEY, JSON.stringify(giteSettings));
alert(getLocalizedString('alert_settings_reset_to_default') + "\n" + getLocalizedString('gm_please_reload'));
window.location.reload();
} catch (e) {
error("Error saving reset GITE settings:", e);
alert(getLocalizedString('alert_generic_error_saving'));
}
} else {
alert(getLocalizedString('alert_gm_setvalue_unavailable'));
}
}
}
function setupGMMenus() {
if (typeof GM_registerMenuCommand !== 'function') return;
try {
GM_registerMenuCommand(getLocalizedString('gm_menu_gite_settings'), openSettingsPanel, "g");
GM_registerMenuCommand(getLocalizedString('gm_menu_reset_all_gite_settings'), handleResetAllSettingsConfirmed, "r");
if (DEBUG_MODE) log("Greasemonkey menu commands registered/updated.");
} catch (e) {
warn("Error registering Greasemonkey menu commands:", e);
}
}
(() => {
let storedSettingsJson = null; let parsedSettings = null;
if (typeof GM_getValue === 'function') {
try {
storedSettingsJson = GM_getValue(GITE_SETTINGS_GM_KEY);
if (storedSettingsJson) parsedSettings = JSON.parse(storedSettingsJson);
} catch (e) {
error("Error parsing stored GITE settings JSON:", e);
parsedSettings = null;
}
}
if (parsedSettings && parsedSettings.general && parsedSettings.filters) {
giteSettings = parsedSettings;
const defaultGeneralKeys = Object.keys(GITE_DEFAULT_SETTINGS.general);
for (const key of defaultGeneralKeys) {
if (!giteSettings.general.hasOwnProperty(key)) {
giteSettings.general[key] = JSON.parse(JSON.stringify(GITE_DEFAULT_SETTINGS.general[key]));
if (DEBUG_MODE) log(`Settings Load: Added missing general setting default: general.${key}`);
}
}
for (const key in giteSettings.general) {
if (!defaultGeneralKeys.includes(key)) {
delete giteSettings.general[key];
if (DEBUG_MODE) log(`Settings Load: Removed obsolete general setting: general.${key}`);
}
}
for (const key in GITE_DEFAULT_SETTINGS.filters) {
if (!giteSettings.filters.hasOwnProperty(key)) {
giteSettings.filters[key] = JSON.parse(JSON.stringify(GITE_DEFAULT_SETTINGS.filters[key]));
if (DEBUG_MODE) log(`Settings Load: Added missing filter category default: filters.${key}`);
} else {
const defaultCat = GITE_DEFAULT_SETTINGS.filters[key];
const storedCat = giteSettings.filters[key];
if (defaultCat.hasOwnProperty('enabled') && !storedCat.hasOwnProperty('enabled')) {
storedCat.enabled = defaultCat.enabled;
if (DEBUG_MODE) log(`Settings Load: Added missing 'enabled' field to filters.${key}`);
}
if (key === 'exactSize' && defaultCat.hasOwnProperty('showInputsInMenu') && !storedCat.hasOwnProperty('showInputsInMenu')) {
storedCat.showInputsInMenu = defaultCat.showInputsInMenu;
if (DEBUG_MODE) log(`Settings Load: Added missing 'showInputsInMenu' field to filters.exactSize`);
}
if (defaultCat.options && (!storedCat.options || !Array.isArray(storedCat.options))) {
storedCat.options = JSON.parse(JSON.stringify(defaultCat.options));
if (DEBUG_MODE) log(`Settings Load: Replaced/Added missing 'options' array to filters.${key}`);
} else if (defaultCat.options && storedCat.options) {
storedCat.options.forEach(opt => {
const defOpt = defaultCat.options.find(d => d.id === opt.id);
if (defOpt && defOpt.textKey && !opt.textKey) {
opt.textKey = defOpt.textKey;
if (DEBUG_MODE) log(`Settings Load: Added missing 'textKey' to option '${opt.id}' in filters.${key}`);
}
if (defOpt && defOpt.paramName && !opt.paramName && key === 'region') {
opt.paramName = defOpt.paramName;
if (DEBUG_MODE) log(`Settings Load: Added missing 'paramName' to option '${opt.id}' in filters.${key}`);
}
});
// Add new options from default to stored if they don't exist
defaultCat.options.forEach(defOpt => {
if (!storedCat.options.some(storedOpt => storedOpt.id === defOpt.id)) {
storedCat.options.push(JSON.parse(JSON.stringify(defOpt)));
if (DEBUG_MODE) log(`Settings Load: Added new default option '${defOpt.id}' to filters.${key}`);
}
});
}
if (key === 'exactSize') {
if (defaultCat.predefinedOptions && (!storedCat.predefinedOptions || !Array.isArray(storedCat.predefinedOptions))) {
storedCat.predefinedOptions = JSON.parse(JSON.stringify(defaultCat.predefinedOptions));
if (DEBUG_MODE) log(`Settings Load: Replaced/Added missing 'predefinedOptions' to filters.exactSize`);
} else if (defaultCat.predefinedOptions && storedCat.predefinedOptions){
storedCat.predefinedOptions.forEach(opt => {
const defOpt = defaultCat.predefinedOptions.find(d => d.id === opt.id);
if (defOpt && defOpt.textKey && !opt.textKey) {
opt.textKey = defOpt.textKey;
}
});
}
if (defaultCat.userDefinedOptions && !storedCat.userDefinedOptions) {
storedCat.userDefinedOptions = JSON.parse(JSON.stringify(defaultCat.userDefinedOptions));
if (DEBUG_MODE) log(`Settings Load: Added missing 'userDefinedOptions' to filters.exactSize`);
}
}
if (key === 'site' && defaultCat.userDefinedOptions && !storedCat.userDefinedOptions) {
storedCat.userDefinedOptions = JSON.parse(JSON.stringify(defaultCat.userDefinedOptions));
if (DEBUG_MODE) log(`Settings Load: Added missing 'userDefinedOptions' to filters.site`);
}
}
}
if (DEBUG_MODE) log("Loaded GITE settings from storage and ensured completeness/cleared obsolete settings.");
} else {
if (DEBUG_MODE) {
if (storedSettingsJson && !parsedSettings) log("Stored settings were present but failed to parse. Initializing with defaults.");
else if (storedSettingsJson && parsedSettings && (!parsedSettings.general || !parsedSettings.filters)) log("Stored settings format was invalid. Initializing with defaults.");
else log("No GITE settings found in storage. Initializing with defaults.");
}
giteSettings = JSON.parse(JSON.stringify(GITE_DEFAULT_SETTINGS));
if (typeof GM_setValue === 'function') {
try { GM_setValue(GITE_SETTINGS_GM_KEY, JSON.stringify(giteSettings)); if (DEBUG_MODE) log("Initialized and saved GITE settings with defaults."); }
catch (e) { error("Error saving initial default GITE settings:", e); }
}
}
initializeCurrentLanguage();
detectAndApplyThemeClass();
injectCustomHoverStyles();
setupGMMenus();
if (isImageUrl()) {
if (DEBUG_MODE) log(`GITE Initializing main logic (v${GITE_SCRIPT_VERSION}). Language: ${CURRENT_LANGUAGE}.`);
initializeMainScript();
} else {
if (DEBUG_MODE) log(`Not a Google Images page. GITE (v${GITE_SCRIPT_VERSION}) not initializing main filter logic. Language: ${CURRENT_LANGUAGE}`);
}
})();
})();