- // ==UserScript==
- // @name VoiceLinks
- // @namespace Sanya
- // @description Makes RJ codes more useful.(8-bit RJCode supported.)
- // @match *://*/*
- // @match file:///*
- // @version 4.8.10
- // @connect dlsite.com
- // @connect media.ci-en.jp
- // @grant GM_setClipboard
- // @grant GM_openInTab
- // @grant GM_registerMenuCommand
- // @grant GM_setValue
- // @grant GM_getValue
- // @grant GM_addElement
- // @grant GM.xmlHttpRequest
- // @grant GM_xmlhttpRequest
- // @run-at document-start
- // @homepage https://sleazyfork.org/zh-CN/scripts/456775-voicelinks
- // ==/UserScript==
-
- (function () {
- 'use strict';
-
- const IS_PREVIEW = false;
-
- //------持久化设置项------
- let settings = {
- //语言设置
- _s_lang: "zh_CN",
- _s_popup_lang: "zh_CN",
-
- //常规设置
- _s_parse_url: true,
- _s_parse_url_in_dl: false,
- _s_show_translated_title_in_dl: true,
- _s_copy_as_filename_btn: true,
- _s_show_compatibility_warning: true,
- _s_url_insert_mode: "before_rj",
- _s_url_insert_text: "🔗",
- _s_sfw_mode: false,
- _s_sfw_blur_level: "medium",
- _s_sfw_remove_when_hover: true,
- _s_sfw_blur_transition: true,
-
- //信息显示设置
- _s_category_preset: "voice",
- _s_voice__info_display_order: [
- "voice__dl_count",
- "voice__circle_name",
- "voice__translator_name",
- "voice__release_date",
- "voice__update_date",
- "voice__age_rating",
- "voice__scenario",
- "voice__illustration",
- "voice__voice_actor",
- "voice__music",
- "voice__genre",
- "voice__file_size"
- ],
- _s_voice__dl_count: false,
- _s_voice__circle_name: true,
- _s_voice__translator_name: true,
- _s_voice__release_date: true,
- _s_voice__update_date: true,
- _s_voice__age_rating: true,
- _s_voice__scenario: false,
- _s_voice__illustration: false,
- _s_voice__voice_actor: true,
- _s_voice__music: true,
- _s_voice__genre: true,
- _s_voice__file_size: true,
- _s_game__info_display_order: [
- "game__dl_count",
- "game__circle_name",
- "game__translator_name",
- "game__release_date",
- "game__update_date",
- "game__age_rating",
- "game__scenario",
- "game__illustration",
- "game__voice_actor",
- "game__music",
- "game__genre",
- "game__file_size"
- ],
- _s_game__dl_count: false,
- _s_game__circle_name: true,
- _s_game__translator_name: true,
- _s_game__release_date: true,
- _s_game__update_date: true,
- _s_game__age_rating: true,
- _s_game__scenario: true,
- _s_game__illustration: true,
- _s_game__voice_actor: true,
- _s_game__music: true,
- _s_game__genre: true,
- _s_game__file_size: true,
- _s_manga__info_display_order:[
- "manga__dl_count",
- "manga__circle_name",
- "manga__translator_name",
- "manga__release_date",
- "manga__update_date",
- "manga__age_rating",
- "manga__scenario",
- "manga__illustration",
- "manga__voice_actor",
- "manga__music",
- "manga__genre",
- "manga__file_size"
- ],
- _s_manga__dl_count: false,
- _s_manga__circle_name: true,
- _s_manga__translator_name: true,
- _s_manga__release_date: true,
- _s_manga__update_date: true,
- _s_manga__age_rating: true,
- _s_manga__scenario: true,
- _s_manga__illustration: true,
- _s_manga__voice_actor: true, //音声漫画
- _s_manga__music: true,
- _s_manga__genre: true,
- _s_manga__file_size: true,
- _s_video__info_display_order: [
- "video__dl_count",
- "video__circle_name",
- "video__translator_name",
- "video__release_date",
- "video__update_date",
- "video__age_rating",
- "video__scenario",
- "video__illustration",
- "video__voice_actor",
- "video__music",
- "video__genre",
- "video__file_size"
- ],
- _s_video__dl_count: false,
- _s_video__circle_name: true,
- _s_video__translator_name: true,
- _s_video__release_date: true,
- _s_video__update_date: true,
- _s_video__age_rating: true,
- _s_video__scenario: true,
- _s_video__illustration: true,
- _s_video__voice_actor: true,
- _s_video__music: true,
- _s_video__genre: true,
- _s_video__file_size: true,
- _s_novel__info_display_order: [
- "novel__dl_count",
- "novel__circle_name",
- "novel__translator_name",
- "novel__release_date",
- "novel__update_date",
- "novel__age_rating",
- "novel__scenario",
- "novel__illustration",
- "novel__voice_actor",
- "novel__music",
- "novel__genre",
- "novel__file_size"
- ],
- _s_novel__dl_count: false,
- _s_novel__circle_name: true,
- _s_novel__translator_name: true,
- _s_novel__release_date: true,
- _s_novel__update_date: true,
- _s_novel__age_rating: true,
- _s_novel__scenario: true,
- _s_novel__illustration: true,
- _s_novel__voice_actor: false,
- _s_novel__music: false,
- _s_novel__genre: true,
- _s_novel__file_size: true,
- _s_other__info_display_order: [
- "other__dl_count",
- "other__circle_name",
- "other__translator_name",
- "other__release_date",
- "other__update_date",
- "other__age_rating",
- "other__scenario",
- "other__illustration",
- "other__voice_actor",
- "other__music",
- "other__genre",
- "other__file_size"
- ],
- _s_other__dl_count: false,
- _s_other__circle_name: true,
- _s_other__translator_name: true,
- _s_other__release_date: true,
- _s_other__update_date: true,
- _s_other__age_rating: true,
- _s_other__scenario: true,
- _s_other__illustration: true,
- _s_other__voice_actor: true,
- _s_other__music: true,
- _s_other__genre: true,
- _s_other__file_size: true,
-
- //标签显示设置
- _s_tag_main_switch: true,
- _s_tag_display_order: [
- "tag_no_longer_available",
- "tag_rate",
- "tag_work_type",
- "tag_translatable",
- "tag_not_translatable",
- "tag_translated",
- "tag_bonus_work",
- "tag_has_bonus",
- "tag_language_support",
- "tag_file_format",
- "tag_ai",
- ],
- _s_tag_rate: true,
- _s_tag_work_type: true,
- _s_tag_translatable: true,
- _s_tag_not_translatable: true,
- _s_tag_translated: true,
- _s_tag_language_support: true,
- _s_tag_bonus_work: true,
- _s_tag_has_bonus: true,
- _s_tag_file_format: false,
- _s_tag_no_longer_available: true,
- _s_tag_ai: true,
-
- _s_show_rate_count: false,
-
- _s_tag_translation_request: true,
- _s_tag_translation_request_display_order: [
- "tag_translation_request_simplified_chinese",
- "tag_translation_request_traditional_chinese",
- "tag_translation_request_english",
- "tag_translation_request_korean",
- "tag_translation_request_spanish",
- "tag_translation_request_german",
- "tag_translation_request_french",
- "tag_translation_request_indonesian",
- "tag_translation_request_italian",
- "tag_translation_request_portuguese",
- "tag_translation_request_swedish",
- "tag_translation_request_thai",
- "tag_translation_request_vietnamese",
- ],
- _s_tag_translation_request_english: false,
- _s_tag_translation_request_simplified_chinese: true,
- _s_tag_translation_request_traditional_chinese: true,
- _s_tag_translation_request_korean: false,
- _s_tag_translation_request_spanish: false,
- _s_tag_translation_request_german: false,
- _s_tag_translation_request_french: false,
- _s_tag_translation_request_indonesian: false,
- _s_tag_translation_request_italian: false,
- _s_tag_translation_request_portuguese: false,
- _s_tag_translation_request_swedish: false,
- _s_tag_translation_request_thai: false,
- _s_tag_translation_request_vietnamese: false,
-
- backup: function() {
- let backup = {};
- for (let key in this) {
- if(!key.startsWith("_s_")) continue;
- //如果类型为列表,则需要将其拷贝出来
- if(this[key] && Array.isArray(this[key])){
- backup[key] = [...this[key]];
- }else{
- backup[key] = this[key];
- }
- }
- this.default_backup = backup;
- },
-
- //备份默认值
- default_backup: {},
-
- //暂存已修改值,不更新到设置
- temp_edited: {},
-
- load: function(){
- let need_reorder = false;
- for(let key in this){
- if(!key.startsWith("_s_")) continue;
- let val = GM_getValue(key.substring(3), this[key]);
- if(typeof val !== typeof this[key]){
- val = this[key];
- }
- if(Array.isArray(val) && val.length !== this[key].length){
- val = this[key];
- need_reorder = true;
- }
- this[key] = val !== undefined ? val : this[key];
- }
-
- if(need_reorder) {
- window.addEventListener("load", () => {
- window.alert("VoiceLinks: " + localize(localizationMap.need_reorder));
- });
- this.save();
- }
- },
-
- save: function () {
- //将暂存修改应用至Settings
- for (let key in this.temp_edited) {
- if(!key.startsWith("_s_")) continue;
- if(this[key] === undefined || this.temp_edited[key] === undefined) continue;
- this[key] = this.temp_edited[key];
- this.temp_edited[key] = undefined;
- }
-
- //将修改保存至GM
- for(let key in this){
- if(!key.startsWith("_s_")) continue;
- GM_setValue(key.substring(3), this[key]);
- }
- },
-
- //保存临时修改
- saveTemp: function (key, value){
- if(!key.startsWith("_s_")) key = "_s_" + key;
- this.temp_edited[key] = value;
- },
-
- clearTemp: function (){
- this.temp_edited = {};
- },
-
- reset: function () {
- if(!this.default_backup) return;
- for(let key in this.default_backup){
- if(!key.startsWith("_s_")) continue;
- GM_setValue(key.substring(3), this.default_backup[key]);
- }
- },
-
- hasEdited: function (key) {
- if(!key.startsWith("_s_")) key = "_s_" + key;
- if(this[key] === undefined) return false;
- return this[key] !== this.default_backup[key];
- },
-
- getDefaultValue: function (key) {
- if(!key.startsWith("_s_")) key = "_s_" + key;
- return this.default_backup[key];
- }
- }
- settings.backup();
- settings.load();
- //----------------------
-
- //------本地化-----------
- const localizationMap = {
- notice_update: {
- zh_CN: "VoiceLinks公告更新,可能包含重要的新功能说明,是否跳转至说明页面?",
- zh_TW: "VoiceLinks公告更新,可能包含重要的新功能説明,是否跳轉至説明頁面?",
- en_US: "VoiceLinks Notice Update, may contain important new features, do you want to jump to the notice page?"
- },
-
- title_settings: {
- zh_CN: "VoiceLinks 设置",
- zh_TW: "VoiceLinks 設定",
- en_US: "VoiceLinks Settings"
- },
-
- title_language_settings: {
- zh_CN: "语言设置",
- zh_TW: "語言設定",
- en_US: "Language Settings"
- },
-
- display_language: {
- zh_CN: "显示语言",
- zh_TW: "顯示語言",
- en_US: "Language"
- },
-
- popup_language: {
- zh_CN: "弹窗语言",
- zh_TW: "彈窗語言",
- en_US: "Popup Language"
- },
-
- popup_language_tooltip: {
- zh_CN: "仅修改标题和标签显示语言,信息本身的语言以DLSite网页设置的语言为准。",
- zh_TW: "只修改標題和標籤顯示語言,資訊本身的語言以DLSite網頁設定的語言為準。",
- en_US: "Only modify the title and tag display language, the language of the information itself is determined by the language of the DLSite page settings."
- },
-
- title_general_settings: {
- zh_CN: "常规",
- zh_TW: "常規",
- en_US: "General"
- },
-
- parse_url: {
- zh_CN: "解析URL",
- zh_TW: "解析URL",
- en_US: "Parse URL"
- },
-
- parse_url_tooltip: {
- zh_CN: "鼠标悬停导指向DLSite作品页面的URL时,同样显示作品信息",
- zh_TW: "鼠標懸停導向DLSite作品頁面的URL時,同樣顯示作品資訊",
- en_US: "Show work info when hovering over DLSite work URL"
- },
-
- parse_url_in_dl: {
- zh_CN: "在DLSite上解析URL",
- zh_TW: "在DLSite上解析URL",
- en_US: "Parse URL in DLSite"
- },
-
- parse_url_in_dl_tooltip: {
- zh_CN: "URL较多可能影响正常阅读",
- zh_TW: "URL較多可能影響正常閱讀",
- en_US: "URL is more likely to affect normal reading"
- },
-
- show_translated_title_in_dl: {
- zh_CN: "在DLSite显示对应语言的翻译标题",
- zh_TW: "在DLSite顯示對應語言的翻譯標題",
- en_US: "Show translated title in DLSite"
- },
-
- show_translated_title_in_dl_tooltip: {
- zh_CN: "作品信息页面的标题将会被修改为与翻译语言对应的标题,避免简中看繁中作品标题为日文的问题",
- zh_TW: "作品資訊頁面的標題將會被修改為與翻譯語言對應的標題,避免繁中看簡中作品標題為日文的問題",
- en_US: "The title of the work info page will be modified to match the corresponding translation language, to avoid viewing the title as Japanese when viewing a work in non-English language."
- },
-
- copy_as_filename_btn: {
- zh_CN: "“复制为有效文件名”按钮",
- zh_TW: "“複製為有效檔案名”按鈕",
- en_US: '"Copy as filename" button'
- },
-
- copy_as_filename_btn_tooltip: {
- zh_CN: "鼠标悬停至DLSite作品标题部分将会出现该按钮,点击即可将标题复制为有效文件名,有效文件名指的是会将标题中的非法部分用相似的符号代替",
- zh_TW: "鼠標懸停至DLSite作品標題部分將會出現按鈕,點擊即可將標題複製為有效檔案名,有效檔案名指的是會將標題中的非法部分用相似的符號代替",
- en_US: "Show button when hovering over DLSite work title. Clicking it will copy the title to a valid filename, which will replace the illegal part of the title with similar symbols."
- },
-
- show_compatibility_warning: {
- zh_CN: "显示兼容性警告",
- zh_TW: "顯示兼容性警告",
- en_US: "Show compatibility warning"
- },
-
- show_compatibility_warning_tooltip: {
- zh_CN: "如果脚本中,修改DLSite页面元素的功能覆盖了其它脚本的修改,则会触发该弹窗警告",
- zh_TW: "如果腳本中,修改DLSite頁面元素的功能覆蓋了其它腳本的修改,则會觸發該彈窗警告",
- en_US: "If the script modifies the functionality of DLSite elements that are covered by other scripts, the warning will be triggered"
- },
-
- url_insert_mode: {
- zh_CN: "导向文本的插入方式",
- zh_TW: "導向文本的插入方式",
- en_US: "Type of the insertion"
- },
-
- url_insert_mode_tooltip: {
- zh_CN: "如果某段链接中的RJ号被解析成功,为了保证原链接不被完全覆盖,会根据需要,在URL的文本前/后插入特定导向文本",
- zh_TW: "如果某段連結中的RJ號被解析成功,為了保證原連結不被完全覆蓋,會根據需要,在URL的文本前/後插入特定導向文本",
- en_US: "If the RJ number in a link is parsed successfully, it is necessary to insert a specific text in the URL before/after the link when the link is almost completely covered by the script"
- },
-
- url_insert_mode_none: {
- zh_CN: "不插入",
- zh_TW: "不插入",
- en_US: "None"
- },
-
- url_insert_mode_prefix: {
- zh_CN: "前缀插入代替原链接",
- zh_TW: "前綴插入代替原連結",
- en_US: "Insert before the link as original link."
- },
-
- url_insert_mode_before_rj: {
- zh_CN: "插入到RJ号前代替RJ链接",
- zh_TW: "插入到RJ號前代替RJ連結",
- en_US: "Insert before the RJ link as the RJ link."
- },
-
- url_insert_text: {
- zh_CN: "导向文本",
- zh_TW: "導向文本",
- en_US: "Text to insert"
- },
-
- sfw_mode: {
- zh_CN: "SFW 模式",
- zh_TW: "SFW 模式",
- en_US: "SFW Mode"
- },
-
- sfw_mode_tooltip: {
- zh_CN: "启用后,所有作品封面均会模糊处理(固定窗口时将鼠标移动到图片上可临时去除模糊)",
- zh_TW: "啟用後,所有作品封面均會模糊處理(固定視窗時將滑鼠移動到圖片上可暫時去除模糊)",
- en_US: "Turn on to blur the cover of all works (temporarily remove the blur by moving the mouse over the image when the window is fixed)."
- },
-
- sfw_blur_level: {
- zh_CN: "模糊程度",
- zh_TW: "模糊程度",
- en_US: "Blur level"
- },
-
- sfw_remove_when_hover: {
- zh_CN: "鼠标移到图片上时移除模糊",
- zh_TW: "滑鼠移到圖片上時移除模糊",
- en_US: "Remove the blur when the mouse moves over the image"
- },
-
- sfw_blur_transition: {
- zh_CN: "模糊动画(卡顿请关闭)",
- zh_TW: "模糊動畫(卡頓請關閉)",
- en_US: "Blur animation"
- },
-
- low: {
- zh_CN: "低 - 仅模糊细节",
- zh_TW: "低 - 僅模糊細節",
- en_US: "Low - Only blur details"
- },
-
- medium: {
- zh_CN: "中 - 依稀可见",
- zh_TW: "中 - 依稀可見",
- en_US: "Medium - Hard to see"
- },
-
- high: {
- zh_CN: "高 - 完全无法辨认",
- zh_TW: "高 - 完全無法辨識",
- en_US: "High - Unrecognizable"
- },
-
- title_info_settings: {
- zh_CN: "信息显示",
- zh_TW: "信息顯示",
- en_US: "Info Display"
- },
-
- category_preset: {
- zh_CN: "类别预设",
- zh_TW: "類別預設",
- en_US: "Category Preset"
- },
-
- category_preset_tooltip: {
- zh_CN: "使不同类别的作品根据需要显示不同的信息<br/><br/>注意:即使勾选了显示,若作品中不存在该信息则也会隐藏。",
- zh_TW: "使不同類別的作品根據需要顯示不同的信息<br/><br/>注意:即使勾選了顯示,若作品中不存在該信息則也會隱藏。",
- en_US: "Show the information of different categories of works. <br/><br/>Note: even if checked, the information of a work that does not exist will be hidden."
- },
-
- rate: {
- zh_CN: "评分",
- zh_TW: "評分",
- en_US: "Rate"
- },
-
- rate_tooltip: {
- zh_CN: "星数★ (评分人数 (设置开启))",
- zh_TW: "星數★ (評分人數 (設置開啟))",
- en_US: "Star★ (number of ratings (enable in settings))"
- },
-
- dl_count: {
- zh_CN: "销量",
- zh_TW: "銷量",
- en_US: "Sales"
- },
-
- circle_name: {
- zh_CN: "社团名",
- zh_TW: "社團名",
- en_US: "Circle Name"
- },
-
- translator_name: {
- zh_CN: "翻译者",
- zh_TW: "翻譯者",
- en_US: "Translator"
- },
-
- release_date: {
- zh_CN: "发售日",
- zh_TW: "發售日",
- en_US: "Release Date"
- },
-
- update_date: {
- zh_CN: "更新日",
- zh_TW: "更新日",
- en_US: "Update Date"
- },
-
- age_rating: {
- zh_CN: "年龄指定",
- zh_TW: "年齡指定",
- en_US: "Age Rating"
- },
-
- scenario: {
- zh_CN: "剧情",
- zh_TW: "劇情",
- en_US: "Scenario"
- },
-
- illustration: {
- zh_CN: "插画",
- zh_TW: "插圖",
- en_US: "Illustration"
- },
-
- voice_actor: {
- zh_CN: "声优",
- zh_TW: "聲優",
- en_US: "Voice Actor"
- },
-
- music: {
- zh_CN: "音乐",
- zh_TW: "音樂",
- en_US: "Music"
- },
-
- genre: {
- zh_CN: "分类",
- zh_TW: "分類",
- en_US: "Genre"
- },
-
- file_size: {
- zh_CN: "文件容量",
- zh_TW: "檔案容量",
- en_US: "File Size"
- },
-
- title_tag_settings: {
- zh_CN: "标签显示",
- zh_TW: "標籤顯示",
- en_US: "Tag Display"
- },
-
- tag_main_switch: {
- zh_CN: "标签总开关",
- zh_TW: "標籤總開關",
- en_US: "Tag Main Switch"
- },
-
- tag_main_switch_tooltip: {
- zh_CN: "关闭则所有标签均不显示",
- zh_TW: "關閉則所有標籤都不顯示",
- en_US: "If turned off, all tags will not be displayed"
- },
-
- tag_work_type: {
- zh_CN: "作品类型",
- zh_TW: "作品類型",
- en_US: "Work Type"
- },
-
- work_type_game: {
- zh_CN: "游戏",
- zh_TW: "遊戲",
- en_US: "Game"
- },
-
- work_type_comic: {
- zh_CN: "漫画",
- zh_TW: "漫畫",
- en_US: "Manga"
- },
-
- work_type_illustration: {
- zh_CN: "CG・插画",
- zh_TW: "CG・插畫",
- en_US: "CG + Illustrations"
- },
-
- work_type_novel: {
- zh_CN: "小说",
- zh_TW: "小說",
- en_US: "Novel"
- },
-
- work_type_video: {
- zh_CN: "视频",
- zh_TW: "影片",
- en_US: "Video"
- },
-
- work_type_voice: {
- zh_CN: "音声・ASMR",
- zh_TW: "聲音作品・ASMR",
- en_US: "Voice / ASMR"
- },
-
- work_type_music: {
- zh_CN: "音乐",
- zh_TW: "音樂",
- en_US: "Music"
- },
-
- work_type_tool: {
- zh_CN: "工具/装饰",
- zh_TW: "工具/配件",
- en_US: "Tools / Accessories"
- },
-
- work_type_voice_comic: {
- zh_CN: "音声漫画",
- zh_TW: "有聲漫畫",
- en_US: "Voiced Comics"
- },
-
- work_type_other: {
- zh_CN: "其他",
- zh_TW: "其他",
- en_US: "Miscellaneous"
- },
-
- tag_translatable: {
- zh_CN: "可翻译",
- zh_TW: "可翻譯",
- en_US: "Translatable"
- },
-
- tag_translatable_tooltip: {
- zh_CN: "大家一起来翻译 授权作品",
- zh_TW: "大家一起翻譯 授权作品",
- en_US: "Translators Unite translation permitted work"
- },
-
- tag_not_translatable: {
- zh_CN: "不可翻译",
- zh_TW: "不可翻譯",
- en_US: "Not Translatable"
- },
-
- tag_not_translatable_tooltip: {
- zh_CN: "未授权 大家一起来翻译",
- zh_TW: "未授權 大家一起來翻譯",
- en_US: "Not Translators Unite translation permitted work"
- },
-
- tag_translated: {
- zh_CN: "翻译作品",
- zh_TW: "翻譯作品",
- en_US: "Translated"
- },
-
- tag_translated_tooltip: {
- zh_CN: "当前作品为 大家一起来翻译 作品",
- zh_TW: "當前作品為 大家一起來翻譯 作品",
- en_US: "Current work is Translators Unite translation work"
- },
-
- tag_language_support: {
- zh_CN: "语言支持",
- zh_TW: "語言支援",
- en_US: "Language Support"
- },
-
- language_japanese: {
- zh_CN: "日文",
- zh_TW: "日文",
- en_US: "Japanese"
- },
-
- language_english: {
- zh_CN: "英文",
- zh_TW: "英文",
- en_US: "English"
- },
-
- language_korean: {
- zh_CN: "韩语",
- zh_TW: "韓語",
- en_US: "Korean"
- },
-
- language_simplified_chinese: {
- zh_CN: "简体中文",
- zh_TW: "簡體中文",
- en_US: "Simplified Chinese"
- },
-
- language_traditional_chinese: {
- zh_CN: "繁体中文",
- zh_TW: "繁體中文",
- en_US: "Traditional Chinese"
- },
-
- language_german: {
- zh_CN: "德语",
- zh_TW: "德語",
- en_US: "German"
- },
-
- language_french: {
- zh_CN: "法语",
- zh_TW: "法語",
- en_US: "French"
- },
-
- language_russian: {
- zh_CN: "俄语",
- zh_TW: "俄語",
- en_US: "Russian"
- },
-
- language_spanish: {
- zh_CN: "西班牙语",
- zh_TW: "西班牙語",
- en_US: "Spanish"
- },
-
- language_indonesian: {
- zh_CN: "印尼文",
- zh_TW: "印尼文",
- en_US: "Indonesian"
- },
-
- language_italian: {
- zh_CN: "意大利语",
- zh_TW: "義大利語",
- en_US: "Italian"
- },
-
- language_arabic: {
- zh_CN: "阿拉伯语",
- zh_TW: "阿拉伯語",
- en_US: "Arabic"
- },
-
- language_portuguese: {
- zh_CN: "葡萄牙语",
- zh_TW: "葡萄牙語",
- en_US: "Portuguese"
- },
-
- language_finnish: {
- zh_CN: "芬兰语",
- zh_TW: "芬蘭語",
- en_US: "Finnish"
- },
-
- language_polish: {
- zh_CN: "波兰语",
- zh_TW: "波蘭語",
- en_US: "Polish"
- },
-
- language_swedish: {
- zh_CN: "瑞典文",
- zh_TW: "瑞典文",
- en_US: "Swedish"
- },
-
- language_thai: {
- zh_CN: "泰语",
- zh_TW: "泰語",
- en_US: "Thai"
- },
-
- language_vietnamese: {
- zh_CN: "越南语",
- zh_TW: "越南語",
- en_US: "Vietnamese"
- },
-
- language_japanese_abbr: {
- zh_CN: "日",
- zh_TW: "日",
- en_US: "JP"
- },
-
- language_english_abbr: {
- zh_CN: "英",
- zh_TW: "英",
- en_US: "EN"
- },
-
- language_korean_abbr: {
- zh_CN: "韩",
- zh_TW: "韩",
- en_US: "KO"
- },
-
- language_simplified_chinese_abbr: {
- zh_CN: "简中",
- zh_TW: "簡中",
- en_US: "ZH"
- },
-
- language_traditional_chinese_abbr: {
- zh_CN: "繁中",
- zh_TW: "繁中",
- en_US: "TW"
- },
-
- language_german_abbr: {
- zh_CN: "德",
- zh_TW: "德",
- en_US: "DE"
- },
-
- language_french_abbr: {
- zh_CN: "法",
- zh_TW: "法",
- en_US: "FR"
- },
-
- language_spanish_abbr: {
- zh_CN: "西",
- zh_TW: "西",
- en_US: "ES"
- },
-
- language_indonesian_abbr: {
- zh_CN: "印",
- zh_TW: "印",
- en_US: "ID"
- },
-
- language_italian_abbr: {
- zh_CN: "意",
- zh_TW: "意",
- en_US: "IT"
- },
-
- language_portuguese_abbr: {
- zh_CN: "葡",
- zh_TW: "葡",
- en_US: "PT"
- },
-
- language_swedish_abbr: {
- zh_CN: "瑞典",
- zh_TW: "瑞典",
- en_US: "SV"
- },
-
- language_thai_abbr: {
- zh_CN: "泰",
- zh_TW: "泰",
- en_US: "TH"
- },
-
- language_vietnamese_abbr: {
- zh_CN: "越",
- zh_TW: "越",
- en_US: "VN"
- },
-
- show_rate_count: {
- zh_CN: "显示评分人数",
- zh_TW: "顯示評分人數",
- en_US: "Show Rate Count"
- },
-
- tag_translation_request: {
- zh_CN: "翻译申请情况",
- zh_TW: "翻譯申請情况",
- en_US: "Translation Request"
- },
-
- tag_translation_request_tooltip: {
- zh_CN: "当前作品目前的翻译申请情况,格式为:语言简写 申请数-发售数",
- zh_TW: "當前作品目前的翻譯申請情況,格式為:语言簡稱 申請數-發售數",
- en_US: "Current work's translation request. Format: Language_Abbr Number_of_Requests - Number_of_Sales"
- },
-
- tag_bonus_work: {
- zh_CN: "特典",
- zh_TW: "特典",
- en_US: "Bonus"
- },
-
- tag_bonus_work_tooltip: {
- zh_CN: "当前作品是某部作品的特典",
- zh_TW: "當前作品是某部作品的特典",
- en_US: "Current work is a bonus work"
- },
-
- tag_has_bonus: {
- zh_CN: "有特典",
- zh_TW: "有特典",
- en_US: "Has Bonus"
- },
-
- tag_has_bonus_tooltip: {
- zh_CN: "当前作品目前附赠特典,若特典已下架则不会显示该标签",
- zh_TW: "當前作品目前附赠特典,若特典已下架則不會顯示該標籤",
- en_US: "Current work has bonus. If bonus is not available, the tag will not be displayed."
- },
-
- tag_file_format: {
- zh_CN: "文件格式",
- zh_TW: "檔案形式",
- en_US: "File Format"
- },
-
- tag_file_format_tooltip: {
- zh_CN: "WAV、EXE、MP3等",
- zh_TW: "WAV、EXE、MP3等",
- en_US: "WAV, EXE, MP3, etc."
- },
-
- tag_no_longer_available: {
- zh_CN: "已下架",
- zh_TW: "已下架",
- en_US: "Unavailable"
- },
-
- tag_announce: {
- zh_CN: "预告",
- zh_TW: "預告",
- en_US: "Announce"
- },
-
- tag_ai: {
- zh_CN: "AI & 部分AI",
- zh_TW: "AI & 部分AI",
- en_US: "AI & Partial AI"
- },
-
- tag_aig: {
- zh_CN: "AI生成",
- zh_TW: "AI生成",
- en_US: "AI Gen",
- },
-
- tag_aip: {
- zh_CN: "AI部分使用",
- zh_TW: "AI部分使用",
- en_US: "AI Partial",
- },
-
- tag_ai_tooltip: {
- zh_CN: "全部或部分使用AI的作品",
- zh_TW: "全部或部分使用AI的作品",
- en_US: "Full or partial use of AI",
- },
-
- button_save: {
- zh_CN: "保存设置",
- zh_TW: "保存設置",
- en_US: "Save",
- },
-
- button_cancel: {
- zh_CN: "取消设置",
- zh_TW: "取消設置",
- en_US: "Cancel",
- },
-
- button_reset: {
- zh_CN: "重置设置",
- zh_TW: "重置設置",
- en_US: "Reset",
- },
-
- need_reorder: {
- zh_CN: "检测到设置更新,可能添加了新的信息位,请重新设置对应设置项的排列",
- zh_TW: "檢查到設置更新,可能添加了新的信息位,请重新設置對應設置項的排列",
- en_US: "There is a new setting item added. Please reorder the corresponding setting item",
- },
-
- save_complete: {
- zh_CN: "设置已保存,部分设置需要刷新对应页面以生效",
- zh_TW: "設置已保存,部分設置需要刷新對應頁面以生效",
- en_US: "Settings saved, some settings need to refresh the corresponding page to take effect",
- },
-
- save_failed: {
- zh_CN: "设置保存失败",
- zh_TW: "設置保存失敗",
- en_US: "Settings save failed",
- },
-
- reset_confirm: {
- zh_CN: "确定要将设置重置到最初始的状态吗?(重置后,需要再点击保存才会生效)",
- zh_TW: "確定要將設置重置到最初始的狀態嗎?(重置後,需要再點擊保存才會生效)",
- en_US: "Are you sure you want to reset the settings to the initial state? (After resetting, you need to click Save to take effect)",
- },
-
- reset_complete: {
- zh_CN: "设置已重置",
- zh_TW: "設置已重置",
- en_US: "Settings reset",
- },
-
- reset_failed: {
- zh_CN: "设置重置失败",
- zh_TW: "設置重置失敗",
- en_US: "Settings reset failed",
- },
-
- reset_order: {
- zh_CN: "重置顺序",
- zh_TW: "重置順序",
- en_US: "Reset Order",
- },
-
- reset_order_confirm: {
- zh_CN: "确定要将元素顺序重置到最初始的状态吗?",
- zh_TW: "確定要將元素順序重置到最初始的狀態嗎?",
- en_US: "Are you sure you want to reset the element order to the initial state?",
- },
-
- reset_order_and_setting: {
- zh_CN: "重置元素顺序和各自的设置值",
- zh_TW: "重置元素順序和各自的設置值",
- en_US: "Reset element order and their settings",
- },
-
- hint_pin: {
- zh_CN: "按住CTRL以固定弹框,固定时可复制信息",
- zh_TW: "按住CTRL以固定彈窗,固定時可複製資訊",
- en_US: "Hold CTRL to pin the popup, info can be copied.",
- },
-
- hint_unpin: {
- zh_CN: "抬起CTRL以关闭弹框 & 查看其它作品RJ信息",
- zh_TW: "抬起CTRL以關閉彈窗 & 查看其它作品RJ信息",
- en_US: "Release CTRL to close the popup & view other works.",
- },
-
- hint_copy: {
- zh_CN: "左键单击以复制信息",
- zh_TW: "左鍵單擊以複製資訊",
- en_US: "Left click to copy info.",
- },
-
- hint_copy_all: {
- zh_CN: "左键单击以复制内部所有信息",
- zh_TW: "左鍵單擊以複製內部所有資訊",
- en_US: "Left click to copy all contained info.",
- },
-
- hint_copy_work_title: {
- zh_CN: "单击复制标题,Alt+单击复制为有效文件名",
- zh_TW: "單擊複製標題,Alt+單擊複製為有效檔名",
- en_US: "Click to copy title, Alt+click to copy as valid filename.",
- },
-
- get: function (key, langKey = "_s_lang") {
- return typeof key === "string" ? localizationMap[key][settings[langKey]] : key[settings[langKey]];
- }
- }
-
- function localize(key) {
- return localizationMap.get(key);
- }
-
- function localizePopup(key) {
- return localizationMap.get(key, "_s_popup_lang");
- }
- //----------------------
-
-
- const RJ_REGEX = new RegExp("(R[JE][0-9]{8})|(R[JE][0-9]{6})|([VB]J[0-9]{8})|([VB]J[0-9]{6})", "gi");
- const URL_REGEX = new RegExp("dlsite.com/.*/product_id/((R[JE][0-9]{8})|(R[JE][0-9]{6})|([VB]J[0-9]{8})|([VB]J[0-9]{6}))", "g");
- const VOICELINK_CLASS = 'voicelink-' + Math.random().toString(36).slice(2);
- const VOICELINK_IGNORED_CLASS = `${VOICELINK_CLASS}_ignored`;
- const RJCODE_ATTRIBUTE = 'rjcode';
- const POPUP_CSS = `
- .${VOICELINK_CLASS}_voicepopup {
- min-width: 630px !important;
- z-index: 2147483646 !important;
- max-width: 80% !important;
- position: fixed !important;
- line-height: normal !important; /*原1.4em !important;*/
- font-size:1.1em!important;
- margin-bottom: 10px !important;
- box-shadow: 0 0 .125em 0 rgba(0,0,0,.5) !important;
- border-radius: 0.5em !important;
- background-color:#8080C0 !important;
- color:#F6F6F6 !important;
- text-align: left !important;
- padding: 10px !important;
- pointer-events: none !important;
- }
-
- .${VOICELINK_CLASS}_voicepopup[pin][mouse-in] *[copy-text] {
- text-decoration: underline !important;
- cursor: pointer !important;
- }
- .${VOICELINK_CLASS}_voicepopup[pin][mouse-in] *[copy-text]:active {
- opacity: 0.5 !important;
- }
-
- #${VOICELINK_CLASS}_info-container {
- font-size: 1em !important;
- }
- #${VOICELINK_CLASS}_info-container > div {
- margin-bottom: 3px !important;
- font-size: 1em !important;
- }
- #${VOICELINK_CLASS}_info-container > div > a {
- display: inline;
- }
- #${VOICELINK_CLASS}_info-container > div > .info-title {
- margin-right: 5px !important;
- display: inline-block;
- }
- #${VOICELINK_CLASS}_info-container > div > .info-title::after {
- content: ":" !important;
- text-decoration: none !important;
- display: inline-block !important;
- }
- #${VOICELINK_CLASS}_info-container .${VOICELINK_CLASS}_tags {
- margin-top: 12px !important;
- margin-bottom: 0 !important;
- font-size: 0.909091em !important;
- }
-
- .${VOICELINK_CLASS}_loader {
- display: flex !important;
- justify-content: center !important;
- align-items: center !important;
- position: absolute !important;
- top: 50% !important;
- left: 50% !important;
- transform: translate(-50%, -50%) !important;
- width: 100% !important;
- height: 100% !important;
- min-width: 300px !important;
- min-height: 30px !important;
- z-index: -1 !important;
- }
- .${VOICELINK_CLASS}_dot {
- width: 20px !important;
- height: 20px !important;
- margin: 0 8px !important;
- background-color: #fbfbfb !important;
- border-radius: 50% !important;
- animation: ${VOICELINK_CLASS}_scale 1s infinite !important;
- }
- .${VOICELINK_CLASS}_dot:nth-child(1) {
- animation-delay: 0s !important;
- }
- .${VOICELINK_CLASS}_dot:nth-child(2) {
- animation-delay: 0.2s !important;
- }
- .${VOICELINK_CLASS}_dot:nth-child(3) {
- animation-delay: 0.4s !important;
- }
- @keyframes ${VOICELINK_CLASS}_scale {
- 0%, 100% {
- transform: scale(1);
- }
- 50% {
- transform: scale(1.5);
- }
- }
-
- .${VOICELINK_CLASS}_voicepopup-maniax{
- background-color:#8080C0 !important;
- }
-
- .${VOICELINK_CLASS}_voicepopup-girls{
- background-color:#B33761 !important;
- }
-
- .${VOICELINK_CLASS}_voicepopup .${VOICELINK_CLASS}_left_panel{
- display: flex !important;
- flex-direction: column !important;
- justify-content: space-between !important;
- margin: 0 16px 0 0 !important;
- width: 310px !important;
- flex-shrink: 0 !important;
- }
-
- .${VOICELINK_CLASS}_voicepopup .${VOICELINK_CLASS}_img_container{
- width: 100% !important;
- padding: 3px !important;
- position: relative;
- }
-
- .${VOICELINK_CLASS}_img_container img {
- width: 100% !important;
- height: auto !important;
- }
-
- #${VOICELINK_CLASS}_hint {
- font-size: 0.8em !important;
- opacity: 0.5 !important;
- max-width: 300px !important;
- margin-top: 5px !important;
- }
-
- .${VOICELINK_CLASS}_voicepopup a {
- text-decoration: none !important;
- color: pink !important;
- }
-
- .${VOICELINK_CLASS}_voicepopup .${VOICELINK_CLASS}_age-18{
- color: hsl(300deg 76% 77%) !important;
- }
-
- .${VOICELINK_CLASS}_voicepopup .${VOICELINK_CLASS}_age-all{
- color: hsl(157deg 82% 52%) !important;
- }
-
- .${VOICELINK_CLASS}_voice-title {
- font-size: 1.363636em !important; /*原1.4em*/
- font-weight: bold !important;
- text-align: center !important;
- margin: 5px 10px 0 0 !important;
- display: block !important;
- }
-
- .${VOICELINK_CLASS}_rjcode {
- text-align: center !important;
- margin: 5px 0 !important;
- font-size: 1.2012987em !important; /*原1.2em !important;*/
- font-style: italic !important;
- opacity: 0.3 !important;
- }
-
- .${VOICELINK_CLASS}_error {
- height: 210px !important;
- line-height: 210px !important;
- text-align: center !important;
- }
-
- .${VOICELINK_CLASS}_discord-dark {
- background-color: #36393f !important;
- color: #dcddde !important;
- font-size: 0.9375rem !important;
- }
-
- .${VOICELINK_CLASS}_work_title:hover #${VOICELINK_CLASS}_copy_btn {
- opacity: 1 !important;
- }
-
- #${VOICELINK_CLASS}_copy_btn {
- background: transparent !important;
- border-color: transparent !important;
- cursor: pointer !important;
- transition: all 0.3s !important;
- opacity: 0 !important;
- font-size: 0.75em !important;
- user-select: none !important;
- position: absolute !important;
- }
-
- #${VOICELINK_CLASS}_copy_btn:hover {
- scale: 1.2 !important;
- }
-
- #${VOICELINK_CLASS}_copy_btn:active {
- scale: 1.1 !important;
- }
-
- `
- const SETTINGS_CSS = `
- #${VOICELINK_CLASS}_settings-container {
- font-family: Arial, sans-serif !important;
- background-color: #f4f4f9 !important;
- margin: auto !important;
- padding: 20px 30px !important;
- line-height: unset !important;
-
- position: fixed !important;
- overflow-y: auto !important;
- overflow-x: hidden !important;
- top: 20px !important;
- bottom: 20px !important;
- left: 50% !important;
- transform: translateX(-50%) !important;
- box-sizing: border-box !important;
- max-width: 800px !important;
- width: 100% !important;
- height: calc(100% - 40px) !important;
- z-index: 2147483647 !important;
- border-radius: 20px !important;
- box-shadow: darkgray 0px 0px 17px 2px !important;
-
- /*scrollbar-width: none;*/
- /*-ms-overflow-style: none;*/
- }
- #${VOICELINK_CLASS}_settings-container::-webkit-scrollbar {
- width: 5px !important;
- height: 5px !important;
- }
- #${VOICELINK_CLASS}_settings-container::-webkit-scrollbar-track {
- background-color: #f4f4f9 !important;
- border-radius: 5px !important;
- }
- #${VOICELINK_CLASS}_settings-container::-webkit-scrollbar-thumb {
- background-color: #888 !important;
- border-radius: 5px !important;
- }
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_container {
- max-width: 800px !important;
- margin: auto !important;
- background: #fff !important;
- padding: 20px !important;
- border-radius: 10px !important;
- box-shadow: 0 0 10px rgba(0, 0, 0, 0.1) !important;
- }
- #${VOICELINK_CLASS}_settings-container h1 {
- display: block !important;
- text-align: center !important;
- color: #333 !important;
- font-size: 32px !important;
- margin: 21.44px 0 !important;
- font-weight: bold !important;
- line-height: normal !important;
- }
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_section-container {
- margin: 20px 0 !important;
- }
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_section-container h2 {
- display: block !important;
- color: #007bff !important;
- font-size: 24px !important;
- margin: 22px 0 14px 0 !important;
- font-weight: bold !important;
- line-height: normal !important;
- }
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_setting {
- /*display: flex;*/
- /*align-items: center;*/
- /*justify-content: space-between;*/
- margin: 10px 0 !important;
- }
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_setting .${VOICELINK_CLASS}_row-title {
- margin: 0 0 0 10px !important;
- color: #555 !important;
- font-size: 18px !important;
- font-weight: normal !important;
- /*flex-grow: 1;*/
- }
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_setting input[type="text"],
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_setting input[type="password"],
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_setting input[type="number"],
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_setting input[type="email"],
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_setting select {
- width: 100% !important;
- padding: 10px !important;
- border: 1px solid #ddd !important;
- border-radius: 5px !important;
- background: #fafafa !important;
- box-sizing: border-box !important;
- color: #666666FF !important;
- font-size: 13.3333px !important;
- height: unset !important;
- max-height: unset !important;
- max-width: unset !important;
- /*margin-bottom: 10px;*/
- }
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_setting input[type="checkbox"] {
- display: none !important;
- }
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_toggle-container {
- display: flex !important;
- flex-direction: row !important;
- align-items: center !important;
- justify-content: flex-end !important;
- }
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_setting .${VOICELINK_CLASS}_toggle {
- display: inline-block !important;
- margin: 0 !important;
- width: 60px !important;
- height: 30px !important;
- padding: 0 !important;
- background: #ccc !important;
- border-radius: 15px !important;
- position: relative !important;
- cursor: pointer !important;
- transition: background 0.3s !important;
- }
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_toggle:before {
- content: "" !important;
- display: block !important;
- width: 24px !important;
- height: 24px !important;
- background: #fff !important;
- border-radius: 50% !important;
- position: absolute !important;
- top: 3px !important;
- left: 3px !important;
- transition: transform 0.3s !important;
- }
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_setting input[type="checkbox"]:checked + label {
- background: #007bff !important;
- }
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_setting input[type="checkbox"]:checked + label:before {
- transform: translateX(30px) !important;
- }
- #${VOICELINK_CLASS}_button-close{
- position: absolute !important;
- top: 20px !important;
- right: 20px !important;
- font-size: 24px !important;
- cursor: pointer !important;
- background: rgba(0, 0, 0, 0.05) !important;
- border: none !important;
- width: 42px !important;
- height: 42px !important;
- border-radius: 50% !important;
- }
- #${VOICELINK_CLASS}_button-save,
- #${VOICELINK_CLASS}_button-cancel,
- #${VOICELINK_CLASS}_button-reset{
- display: block !important;
- width: 100% !important;
- padding: 10px !important;
- border: none !important;
- border-radius: 5px !important;
- background: #007bff !important;
- color: #fff !important;
- font-size: 16px !important;
- cursor: pointer !important;
- margin-top: 10px !important;
-
- transition: background 0.3s, filter 0.3s !important;
- }
- #${VOICELINK_CLASS}_button-reset{
- background: #999 !important;
- }
- #${VOICELINK_CLASS}_button-save:hover,
- #${VOICELINK_CLASS}_button-cancel:hover,
- #${VOICELINK_CLASS}_button-reset:hover{
- filter: brightness(1.3) !important;
- }
- #${VOICELINK_CLASS}_button-save:active,
- #${VOICELINK_CLASS}_button-cancel:active,
- #${VOICELINK_CLASS}_button-reset:active{
- filter: brightness(0.9) !important;
- }
-
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_tooltip {
- position: relative !important;
- }
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_tooltip .${VOICELINK_CLASS}_tooltip-text {
- visibility: hidden !important;
- min-width: 200px !important;
- max-width: 100% !important;
- background-color: #555 !important;
- color: #fff !important;
- font-size: 14px !important;
- text-align: center !important;
- border-radius: 5px !important;
- padding: 8px 10px !important;
- position: absolute !important;
- z-index: 1 !important;
- bottom: 125% !important;
- left: 0 !important;
- /*margin-left: -100px;*/
- opacity: 0 !important;
- filter: brightness(1.0) !important;
- transition: opacity 0.3s !important;
- }
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_tooltip:hover .${VOICELINK_CLASS}_tooltip-text {
- visibility: visible !important;
- opacity: 1 !important;
- }
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_sortable {
- cursor: move !important;
- }
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_sortable span{
- cursor: default !important;
- }
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_dragging{
- background-color: #1e82ff38 !important;
- user-select: none !important;
- transition: background-color 0.3s !important;
- }
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_sortable .${VOICELINK_CLASS}_setting {
- cursor: move !important;
- }
- #${VOICELINK_CLASS}_settings-container table {
- width: 100% !important;
- margin-bottom: 20px !important;
- border-collapse: collapse !important;
- font-size: unset !important;
- }
- #${VOICELINK_CLASS}_settings-container table,
- #${VOICELINK_CLASS}_settings-container th,
- #${VOICELINK_CLASS}_settings-container td {
- border: 0 solid #ddd !important;
- }
- #${VOICELINK_CLASS}_settings-container th,
- #${VOICELINK_CLASS}_settings-container td {
- border-bottom: 1px dashed rgba(221, 221, 221, 0.64) !important;
- /*border-top: 1px solid #ddd;*/
- padding: 8px 10px !important;
- text-align: left !important;
- vertical-align: middle !important;
- }
-
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_hidden{
- display: none !important;
- }
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_input-cell{
- text-align: right !important;
- padding-right: 20px !important;
- }
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_indent-1 > td {
- padding: 8px 24px !important;
- }
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_indent-1 .${VOICELINK_CLASS}_input-cell {
- padding: 8px 20px !important;
- }
-
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_tags {
- font-size: 14px;
- }
- .${VOICELINK_CLASS}_tags {
- display: flex !important;
- flex-wrap: wrap !important;
- justify-content: left !important;
- align-items: stretch !important;
- }
- .${VOICELINK_CLASS}_tags > label,
- .${VOICELINK_CLASS}_tags > span {
- border-radius: 5px !important;
- font-size: 1em !important;
- margin-right: 8px !important;
- margin-bottom: 8px !important;
- padding: 5px 8px !important;
-
- display: flex !important;
- justify-content: center !important;
- align-items: center !important;
-
- transition: color 0.3s, background-color 0.3s !important;
- }
- .${VOICELINK_CLASS}_tags > label.${VOICELINK_CLASS}_tag_tight,
- .${VOICELINK_CLASS}_tags > span.${VOICELINK_CLASS}_tag_tight{
- padding: 2px 7px !important;
- }
- .${VOICELINK_CLASS}_tags > label.${VOICELINK_CLASS}_tag_small,
- .${VOICELINK_CLASS}_tags > span.${VOICELINK_CLASS}_tag_small{
- padding: 2px 7px !important;
- font-size: 0.857143em !important;
- }
-
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_tag-off{
- background-color: #ffffff !important;
- color: #aaaaaa !important;
- }
-
- .${VOICELINK_CLASS}_tag-purple{
- background-color: #EED9F2 !important;
- color: #7B1FA2 !important;
- }
-
- .${VOICELINK_CLASS}_tag-blue{
- background-color: #d9eefc !important;
- color: #4285F4 !important;
- }
-
- .${VOICELINK_CLASS}_tag-red{
- background-color: #ffd6da !important;
- color: #EA4335 !important;
- }
-
- .${VOICELINK_CLASS}_tag-yellow{
- background-color: #FFF8E1 !important;
- color: #F57F17 !important;
- }
-
- .${VOICELINK_CLASS}_tag-green{
- background-color: #dcf5e4 !important;
- color: #34A853 !important;
- }
-
- .${VOICELINK_CLASS}_tag-teal{
- background-color: #d8eced !important;
- color: #0097A7 !important;
- }
-
- .${VOICELINK_CLASS}_tag-gray{
- background-color: #E0E0E0 !important;
- color: #424242 !important;
- }
-
- .${VOICELINK_CLASS}_tag-pink{
- background-color: #ffd9e7 !important;
- color: #f032a7 !important;
- }
-
- .${VOICELINK_CLASS}_tag-orange{
- background-color: #ffebcc !important;
- color: #f04000 !important;
- }
-
- .${VOICELINK_CLASS}_tag-darkblue{
- background-color: #d2e7fa !important;
- color: #0D47A1 !important;
- }
-
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_reset-btn-small {
- position: relative !important;
- display: inline-block !important;
- width: 16px !important;
- height: 16px !important;
- margin-right: 4px !important;
- padding: 0 !important;
- color: transparent !important;
- background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAA6xJREFUeF7tmlFy2jAQhn+ZHIScJOEixYQ++BYJt/BDSU0vUnKSuvcoVkeq3RpXsndlScYAMzwwyLL20+rXarUCN/4RN24/7gDuHnDjBO5LYEoHSF+ytHjPiynHMJkHrLfZVwApgOKwzzdTQZgEQMv4xu7JIEQHYDB+UghRAfQYDyGwKr7kx9hLIRqASzRewY4C4FKNjwKgz3gAZesLIfGhBhVzawzqAQPG9y13DUZIHFDhWBS5+h3kEwxA+jl7lhLfPYxaGX8UJ+xCgAgHIM2WVYK1EHjzAEF1UUqJ4tt7vvPUn+4mGADVeeofggYhTlj58oagAAgQSiGwwS+UeMASFZZS4KkOkXs1wpc3BAdAgmCYUeU9coHXPhhS4m3skogCwBVC85xcQB2cnk0uMRZCNAAECMfDPl+ZjBzQEr2MXMPoqAB6IJSHff44pO6fXrJXy67iLIzRARggkIxv4KgkihR6SXQ/rH6ahycB0IbgImI2TxASG24YPRmAIXcf+t8Cge0FswVQb5Mq1F62YXG9YLYA9DIy6wHLC+YN4E+w9F+MwMkuzRqA8gKTFnCCo9kDqLXgR0c0rUFVV1xnD0AZtN5mSgzPQuXDPifZRmo0tCVN/b8JgDjhkXJkvhYAzS3T37mgCuFVADAJoVcASmiQdI6jgZOVnGVligeoARHJA0wJTs5WwzHGpa3RA4jngqsAYEq/exXBsXuty6xyngkOoN5rZWdQrJibYxC37XqbqUDo7FDkPQ4w7rUT3eh2Aa23WXdy4B3A2JibO6vU9pYTIbnggiSCTQZHLuAcc1MN4rYbEwWqd5EB2GJuasDBNYzS3pYfpLo/G4CPBATFMGobk/hx4xOWB1i2Q3BfSjWwr50tMcqZfbYHaC2wpKGExI6bkXUFYbt6p4a/7feyPKAlhqarqlE3NFQYNi9Ut8aUy5Xue9gANAR78YPzDQ0FQF/RhasYOwFQg+27pvJ1dd2G0ms88eBjguwMYACCFsakwoGSlembfUKRBTn/5x0AYXDOZS2EvpU9o4x32gW6FIkD/Vf1laC0XWW3+lIJTmM9QPN+X1vvqCXQhtGjCTYPb5e+nZ3khgTRVfC8LwGTNwyVtQwZN/C/KpfbjNWVUXEAxYB6r7aWtVD66LQJFmN4WwImo+qokVL1ZXpcC2gC/AwZYQYF0Fils8oPWFYVnoTQmZv2t9ECLZRSokwSfLjW/HC9KwoA7qBitr8DiEn7Et9194BLnJWYY7p5D/gNXP0HX03p5E0AAAAASUVORK5CYII=") !important;
- background-position: center !important;
- background-size: contain !important;
- background-color: transparent !important;
- border-radius: 3px !important;
- border: none !important;
- opacity: 0.5 !important;
- }
- #${VOICELINK_CLASS}_settings-container button.${VOICELINK_CLASS}_reset-btn-small:hover {
- opacity: 1 !important;
- }
-
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_button-flat {
- background-color: transparent !important;
- border: none !important;
- color: #aaa !important;
- cursor: pointer !important;
- border-radius: 5px !important;
- padding: 5px 5px !important;
- margin-bottom: 6px !important;
- margin-right: 6px !important;
-
- display: inline-flex !important;
- align-items: center !important;
- justify-content: center !important;
-
- transition: background-color 0.3s !important;
- }
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_button-flat:hover {
- background-color: rgba(0, 0, 0, 0.1) !important;
- }
- #${VOICELINK_CLASS}_settings-container .${VOICELINK_CLASS}_button-flat span{
- display: inline-block !important;
- }
- `
-
- /**
- * Work promise cache
- * @type {{info:{}, api:{}, api2: {}, circle: {}}}
- */
- const work_promise = {};
-
- function getAdditionalPopupClasses() {
- const hostname = document.location.hostname;
- switch (hostname) {
- case "boards.4chan.org": return "post reply";
- case "discordapp.com": return `${VOICELINK_CLASS}_discord-dark`;
- default: return null;
- }
- }
-
- function getOS() {
- const userAgent = navigator.userAgent;
- if (userAgent.indexOf("Windows NT 10.0") !== -1) return "Windows 10";
- if (userAgent.indexOf("Windows NT 6.2") !== -1) return "Windows 8";
- if (userAgent.indexOf("Windows NT 6.1") !== -1) return "Windows 7";
- if (userAgent.indexOf("Windows NT 6.0") !== -1) return "Windows Vista";
- if (userAgent.indexOf("Windows NT 5.1") !== -1) return "Windows XP";
- if (userAgent.indexOf("Windows NT 5.0") !== -1) return "Windows 2000";
- if (userAgent.indexOf("Mac") !== -1) return "Mac";
- if (userAgent.indexOf("X11") !== -1) return "UNIX";
- if (userAgent.indexOf("Linux") !== -1) return "Linux";
- return "Other";
- }
-
- function getVoiceLinkTarget(target){
- while (target && !target.classList.contains(VOICELINK_CLASS)){
- target = target.parentElement;
- }
- return target;
- }
-
- function isInDLSite(){
- return document.location.hostname.endsWith("dlsite.com");
- }
-
- /**
- * Convert to valid file name.
- * @param {String} original
- */
- function convertToValidFileName(original){
- const charMapRegs = {
- "\\/": "/",
- "\\\\": "\",
- "\\:": ":",
- "\\*": "*",
- "\\?": "?",
- "\"": """,
- "\\<": "<",
- "\\>": ">",
- "\\|": "|"
- }
-
- let fileName = original;
- for (let key in charMapRegs){
- fileName = fileName.replaceAll(new RegExp(key, "g"), charMapRegs[key]);
- }
- return fileName;
- }
-
- function setUserSelectTitle(){
- // Make title selectable
- const hostname = document.location.hostname;
- if(!hostname.endsWith("dlsite.com")){
- return;
- }
- const rjList = document.URL.match(RJ_REGEX)
- const rj = rjList[rjList.length - 1]
-
- const title = document.getElementById("work_name");
- if(!title){
- return;
- }
- let titleStr = title.innerText;
- let titleHtml = title.innerHTML;
-
- const button = document.createElement("button");
- button.id = `${VOICELINK_CLASS}_copy_btn`;
- button.innerText = "📃";
- button.addEventListener("mouseenter", function(){
- button.innerText = "📃 复制为有效文件名";
- });
- button.addEventListener("mouseleave", function(){
- button.innerText = "📃";
- });
- button.addEventListener("click", function(){
- const fileName = convertToValidFileName(titleStr);
- // const promise = navigator.clipboard.writeText(fileName);
- const promise = GM_setClipboard(fileName, "text");
- promise?.then(() => {
- button.innerText = "✔ 复制成功";
- });
- promise?.catch(e => {
- window.prompt("复制失败,请手动复制", fileName);
- button.innerText = "📃";
- });
- });
-
- title.style.setProperty("user-select", "text", "important"); //userSelect = "text !important";
- title.classList.add(`${VOICELINK_CLASS}_work_title`);
-
- if(settings._s_show_translated_title_in_dl){
- //将Title替换成大家翻对应的语言翻译版本
- WorkPromise.getTranslationInfo(rj).then(info => {
- if(info.is_original) {
- return null;
- }
- else{
- return WorkPromise.getWorkTitle(rj);
- }
- }).then(t => {
- if(!t){
- if(settings._s_copy_as_filename_btn) title.appendChild(button);
- return;
- }
- compatibilityCheck(title, titleHtml);
- titleStr = t
- title.innerText = t
- if(settings._s_copy_as_filename_btn) title.appendChild(button);
- })
- }else{
- if(settings._s_copy_as_filename_btn) title.appendChild(button);
- }
- }
-
- function compatibilityCheck(titleElement, titleHtml){
- if(!settings._s_show_compatibility_warning) return;
-
- if(titleElement.innerHTML.trim() === titleHtml.trim()){
- return;
- }
-
- //其它脚本修改了标题内部,进行警告
- window.alert("警告:\n" +
- "VoiceLinks检测到DL作品标题元素发生变化,该变化可能是脚本与其它插件冲突导致的。\n" +
- "可以关闭本脚本中的 “在DLSite显示对应语言的翻译标题” 设置项,以尝试解决冲突。(也可根据情况酌情关闭 “在DL作品标题旁添加复制为文件名按钮” 选项)\n\n" +
- "本脚本的设置方法:点击Tampermonkey等扩展程序的按钮,在弹出的脚本列表中找到当前脚本,点击下方的Settings按钮即可打开设置页面。\n\n" +
- "注意:如果不想看到该警告,可以同时关闭“显示兼容性警告”设置项。")
- }
-
- function getXmlHttpRequest() {
- return (typeof GM !== "undefined" && GM !== null ? GM.xmlHttpRequest : GM_xmlhttpRequest);
- }
-
- const Parser = {
- walkNodes: function (elem) {
- const rjNodeTreeWalker = document.createTreeWalker(
- elem,
- NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_TEXT,
- {
- acceptNode: function (node) {
- if(node.nodeName === "SCRIPT" || node.parentElement && node.parentElement.nodeName === "SCRIPT"){
- return NodeFilter.FILTER_REJECT;
- }
-
- if(node.parentElement.isContentEditable){
- return NodeFilter.FILTER_SKIP;
- }
-
- if(settings._s_parse_url && node.nodeName === "A"){
- if(!settings._s_parse_url_in_dl && document.location.hostname.endsWith("dlsite.com")){
- return NodeFilter.FILTER_SKIP;
- }
-
- let href = node.href;
- if(href.match(URL_REGEX) && !node.classList.contains(VOICELINK_IGNORED_CLASS)){
- return NodeFilter.FILTER_ACCEPT;
- }
- }
-
- if (node.nodeName !== "#text") return NodeFilter.FILTER_SKIP;
- if(node.parentElement.classList.contains(VOICELINK_IGNORED_CLASS)
- || node.parentElement.hasAttribute(RJCODE_ATTRIBUTE)){
- return NodeFilter.FILTER_SKIP;
- }
-
- if (node.parentElement.classList.contains(VOICELINK_CLASS))
- return NodeFilter.FILTER_ACCEPT;
- if (node.nodeValue.match(RJ_REGEX))
- return NodeFilter.FILTER_ACCEPT;
-
- return NodeFilter.FILTER_SKIP;
- }
- },
- false,
- );
- while (rjNodeTreeWalker.nextNode()) {
- const node = rjNodeTreeWalker.currentNode;
-
- //Ignore Element which let user input (textarea), input can be ignored because it's not a text node.
- if(node.parentElement.nodeName === "TEXTAREA"){
- continue;
- }
-
- if (node.parentElement.classList.contains(VOICELINK_CLASS)) {
- Parser.rebindEvents(node.parentElement);
- }else if(node.nodeName === "A") {
- // alert("准备解析链接:" + node.nodeValue)
- Parser.linkifyURL(node);
- }else{
- // alert("准备解析文本:" + node.nodeValue)
- Parser.linkify(node);
- }
- }
- },
-
- wrapPlaceholder: function (content) {
- let e;
- e = document.createElement("span");
- e.classList = VOICELINK_CLASS;
- e.innerText = content;
- e.classList.add(VOICELINK_IGNORED_CLASS);
- e.setAttribute(RJCODE_ATTRIBUTE, "");
- return e;
- },
-
- wrapRJCode: function (rjCode) {
- let e;
- e = document.createElement("a");
- e.classList = VOICELINK_CLASS;
- e.href = `https://www.dlsite.com/maniax/work/=/product_id/${rjCode.toUpperCase()}.html`
- e.innerText = rjCode;
- e.target = "_blank";
- e.rel = "noreferrer";
- e.classList.add(VOICELINK_IGNORED_CLASS);
- e.style.setProperty("display", "inline", "important"); //display = "inline !important";
-
- e.setAttribute(RJCODE_ATTRIBUTE, rjCode.toUpperCase());
- e.setAttribute("voicelink-linkified", "true");
- e.addEventListener("mouseover", Popup.over);
- e.addEventListener("mouseout", Popup.out);
- e.addEventListener("mousemove", Popup.move);
- e.addEventListener("keydown", Popup.keydown);
- //e.addEventListener("keyup", Popup.keyup);
- return e;
- },
-
- calculateCoverage: function(text){
- const matches = text.match(RJ_REGEX);
- if (!matches) return 0;
- //覆盖大小 = 所有匹配项的长度总和
- const coverSize = matches.reduce((total, current) => total + current.length, 0);
- return (coverSize / text.length) * 100;
- },
-
- /***
- * 处理直链
- * @param {Node} node
- ***/
- linkifyURL: function(node) {
- const e = node;
- const href = e.href;
- const rjs = href.match(RJ_REGEX);
- const rj = rjs[rjs.length - 1];
- if(!rj) return;
-
- // alert(`解析链接:${e.nodeValue}`)
-
- e.classList.add(VOICELINK_CLASS);
- e.setAttribute(RJCODE_ATTRIBUTE, rj.toUpperCase());
- e.addEventListener("mouseover", Popup.over);
- e.addEventListener("mouseout", Popup.out);
- e.addEventListener("mousemove", Popup.move);
- e.addEventListener("keydown", Popup.keydown);
- //e.addEventListener("keyup", Popup.keyup);
- },
-
- linkify: function (textNode) {
- const nodeOriginalText = textNode.nodeValue;
- const matches = [];
-
- let insert = settings._s_url_insert_mode;
- let tagA = textNode.parentElement.closest("a");
- let tagB = textNode.parentElement.closest("button");
- let tag = tagA ? tagA : tagB;
- if((!tagA && !tagB) || insert.trim() !== "none" && this.calculateCoverage(tag.innerText) < 71){
- insert = "none";
- }
-
- let match;
- while (match = RJ_REGEX.exec(nodeOriginalText)) {
- matches.push({
- index: match.index,
- value: match[0],
- });
- }
- if(matches.length === 0) return;
-
- // alert(`解析文本:${textNode.nodeValue}`)
-
- // Keep text in text node until first RJ code
- textNode.nodeValue = nodeOriginalText.substring(0, matches[0].index);
- if(insert.startsWith("prefix")){
- //加前缀
- textNode.nodeValue = `${settings._s_url_insert_text}${textNode.nodeValue}`
- }
-
- // Insert rest of text while linkifying RJ codes
- let prevNode = null;
- for (let i = 0; i < matches.length; ++i) {
- // Insert linkified RJ code
- let code = matches[i].value
- let rjLinkNode = Parser.wrapRJCode(code);
- //保证后续游走时忽略当前节点
- if(insert.startsWith("before_rj")){
- //用导向文本替代RJ号链接,RJ号保留到后面的文本里不变
- rjLinkNode.innerText = settings._s_url_insert_text;
- textNode.parentNode.insertBefore(
- rjLinkNode,
- prevNode ? prevNode.nextSibling : textNode.nextSibling,
- );
- prevNode = rjLinkNode;
- rjLinkNode = Parser.wrapPlaceholder(code);
- }
- textNode.parentNode.insertBefore(
- rjLinkNode,
- prevNode ? prevNode.nextSibling : textNode.nextSibling,
- );
-
- // Insert text after if there is any
- //找到当前RJ和下一个RJ之间的字符串
- let nextRJ = undefined;
- if (i < matches.length - 1) {
- nextRJ = matches[i + 1].index;
- }
- let substring = nodeOriginalText.substring(matches[i].index + matches[i].value.length, nextRJ);
-
- if (substring) {
- const subtextNode = document.createTextNode(substring);
- textNode.parentNode.insertBefore(
- subtextNode,
- rjLinkNode.nextElementSibling,
- );
- prevNode = subtextNode;
- }
- else {
- prevNode = rjLinkNode;
- }
- }
- },
-
- rebindEvents: function (elem) {
- if (elem.nodeName === "A") {
- elem.addEventListener("mouseover", Popup.over);
- elem.addEventListener("mouseout", Popup.out);
- elem.addEventListener("mousemove", Popup.move);
- elem.addEventListener("keydown", Popup.keydown);
- //elem.addEventListener("keyup", Popup.keyup);
- }
- else {
- const voicelinks = elem.querySelectorAll("." + VOICELINK_CLASS);
- for (let i = 0, j = voicelinks.length; i < j; i++) {
- const voicelink = voicelinks[i];
- voicelink.addEventListener("mouseover", Popup.over);
- voicelink.addEventListener("mouseout", Popup.out);
- voicelink.addEventListener("mousemove", Popup.move);
- voicelink.addEventListener("keydown", Popup.keydown);
- //voicelink.addEventListener("keyup", Popup.keyup);
- }
- }
- },
-
- }
-
- const DateParser = {
- parseDateStr: function(dateStr, lang){
- dateStr = dateStr.trim().replace(/ /g, "");
- lang = lang.trim().toLowerCase().replace(/_/g, "-");
- let nums = this.parseNumbers(dateStr);
- if(!nums || nums.length < 3 && lang !== "en-us" || nums.length < 2 && lang === "en-us"){
- //数字不够,无法解析
- return null;
- }
-
- let parsers = [
- this.parseAsiaDateStr,
- this.parseEnglishDateStr,
- this.parseEuropeanDateStr,
- this.parseSpanishDateStr
- ]
- let date = null;
- for (let i = 0; i < parsers.length; i++){
- date = parsers[i](dateStr, nums, lang);
- if(date){
- break;
- }
- }
-
- return date;
- },
- parseNumbers: function (dateStr){
- let nums = dateStr.match(/\d+/g);
- if(!nums) return null;
-
- for (let i = 0; i < nums.length; i++) {
- nums[i] = Number(nums[i]);
- }
- return nums;
- },
- parseAsiaDateStr: function(dateStr, nums, lang){
- //2024年10月05日
- //2024년 10월 05일(已去除空格)
- if (!dateStr.match(/\d{4}年\d{1,2}月\d{1,2}日/)
- && !dateStr.match(/\d{4}년\d{1,2}월\d{1,2}일/)) {
- return null;
- }
- return new Date(nums[0], nums[1] - 1, nums[2]);
- },
- parseEnglishDateStr: function(dateStr, nums, lang){
- //Oct/05/2024
- if(!dateStr.match(/[a-zA-Z]{3}\/\d{1,2}\/\d{4}/)){
- return null;
- }
- const monthMap = {
- "Jan": 0, "Feb": 1, "Mar": 2,
- "Apr": 3, "May": 4, "Jun": 5,
- "Jul": 6, "Aug": 7, "Sep": 8,
- "Oct": 9, "Nov": 10, "Dec": 11
- }
- let monthStr = dateStr.substring(0, dateStr.indexOf("/")).toLowerCase();
- monthStr = monthStr[0].toUpperCase() + monthStr.substring(1);
- return new Date(nums[1], monthMap[monthStr], nums[0])
- },
- parseSpanishDateStr: function (dateStr, nums, lang) {
- //10/05/2024
- if(lang !== "es-es" || !dateStr.match(/\d{1,2}\/\d{1,2}\/\d{4}/)){
- return null;
- }
- return new Date(nums[2], nums[0] - 1, nums[1]);
- },
- parseEuropeanDateStr: function (dateStr, nums, lang) {
- //05/10/2024
- if(lang === "es-es" || !dateStr.match(/\d{1,2}\/\d{1,2}\/\d{4}/)){
- return null;
- }
- return new Date(nums[2], nums[1] - 1, nums[0]);
- },
- /***
- 获得带倒计时的文本HTML
- @param date {Date}
- ***/
- getCountDownDateElement: function(date){
- if(!date) return "";
-
- const today = new Date();
- today.setHours(0);
- today.setMinutes(0);
- today.setSeconds(0);
- today.setMilliseconds(0);
- date.setHours(0);
- date.setMinutes(0);
- date.setSeconds(0);
- date.setMilliseconds(0);
-
- if(date.getTime() < today.getTime()) return "";
- let days = (date.getTime() - today.getTime()) / (1000 * 60 * 60 * 24);
- let element = document.createElement("span");
- element.innerText = `(Coming in ${days} day${(days > 1 ? "s" : "")})`;
- element.style.setProperty("color", "#ffeb3b", "important");
- element.style.setProperty("font-style", "italic", "important");
- return element;
- //return `<span style="color:#ffeb3b !important; font-size: 16px !important; font-style: italic !important; margin-left: 16px !important"></span>`
- },
- }
-
- const Popup = {
- popupElement: {
- popup: null,
- not_found: null,
- left_panel: null,
- img: {container: null},
- right_panel: null,
- title: null,
- rj_code: null,
- info_container: null,
- loader: null,
- flag: null,
- tags: null,
- dl_count: null,
- circle_name: null,
- debug: null,
- translator_name: null,
- release_date: null,
- update_date: null,
- age_rating: null,
- scenario: null,
- illustration: null,
- voice_actor: null,
- music: null,
- genre: null,
- file_size: null,
-
- _state: {
- mouseX: 0,
- mouseY: 0
- }
- },
-
- makePopup: function (display) {
- const popup = document.createElement("div");
- const ele = Popup.popupElement;
- ele.popup = popup;
-
- popup.className = `${VOICELINK_CLASS}_voicepopup ${VOICELINK_CLASS}_voicepopup-maniax ` + (getAdditionalPopupClasses() || '');
- popup.id = `${VOICELINK_CLASS}-voice-popup`; // + rjCode;
- popup.style.setProperty("display", display === false ? "none" : "flex", "important"); //display = display === false ? "none" : "flex";
- document.body.appendChild(popup);
-
- popup.addEventListener("mouseenter", () => {
- popup.setAttribute("mouse-in", "");
- });
- popup.addEventListener("mouseleave", () => {
- popup.removeAttribute("mouse-in");
- })
-
- const notFoundElement = document.createElement("div");
- ele.not_found = notFoundElement;
- //占满整个popup
- //"display: none; width: 100%; height: 100%";
- notFoundElement.style.setProperty("display", "none", "important");
- notFoundElement.style.setProperty("width", "100%", "important");
- notFoundElement.style.setProperty("height", "100%", "important");
- notFoundElement.innerText = "Work Not Found.";
- popup.appendChild(notFoundElement);
-
- const leftPanel = document.createElement("div");
- leftPanel.classList.add(`${VOICELINK_CLASS}_left_panel`);
- popup.appendChild(leftPanel);
- ele.left_panel = leftPanel;
-
- const imgContainer = document.createElement("div")
- imgContainer.classList.add(`${VOICELINK_CLASS}_img_container`);
- ele.img.container = imgContainer;
- leftPanel.appendChild(imgContainer);
-
- //左下角提示状态栏
- ele.hint = document.createElement("div");
- leftPanel.appendChild(ele.hint);
- ele.hint.id = `${VOICELINK_CLASS}_hint`;
-
- const rightPanel = document.createElement("div");
- ele.right_panel = rightPanel;
-
- const titleElement = Popup.createCopyTag("div", "", false, localizePopup(localizationMap.hint_copy_work_title));
- ele.title = titleElement;
- titleElement.classList.add(`${VOICELINK_CLASS}_voice-title`);
- rightPanel.appendChild(titleElement);
-
- const rjCodeElement = document.createElement("div");
- ele.rj_code = rjCodeElement;
- rjCodeElement.classList.add(`${VOICELINK_CLASS}_rjcode`);
- rightPanel.appendChild(rjCodeElement);
-
- const infoContainer = document.createElement("div");
- ele.info_container = infoContainer;
- infoContainer.id = `${VOICELINK_CLASS}_info-container`;
- infoContainer.style.setProperty("position", "relative", "important"); //position = "relative !important";
- infoContainer.style.setProperty("min-height", "70px", "important"); //minHeight = "70px !important";
- rightPanel.appendChild(infoContainer);
-
- const loader = document.createElement("div");
- loader.className = `${VOICELINK_CLASS}_loader`;
- loader.innerHTML = Csp.createHTML(`
- <div class="${VOICELINK_CLASS}_dot"></div>
- <div class="${VOICELINK_CLASS}_dot"></div>
- <div class="${VOICELINK_CLASS}_dot"></div>
- `);
- ele.loader = loader;
- infoContainer.appendChild(loader);
-
- ele.tags = document.createElement("div");
- infoContainer.appendChild(ele.tags);
-
- ele.dl_count = document.createElement("div");
- ele.circle_name = document.createElement("div");
- ele.debug = document.createElement("div");
- ele.translator_name = document.createElement("div");
- ele.release_date = document.createElement("div");
- ele.update_date = document.createElement("div");
- ele.age_rating = document.createElement("div");
- ele.scenario = document.createElement("div");
- ele.illustration = document.createElement("div");
- ele.voice_actor = document.createElement("div");
- ele.music = document.createElement("div");
- ele.genre = document.createElement("div");
- ele.file_size = document.createElement("div");
-
- rightPanel.style.setProperty("padding-bottom", "3px", "important"); //paddingBottom = "3px !important";
- rightPanel.style.setProperty("flex-grow", "1", "important"); //flexGrow = "1 !important";
- popup.appendChild(rightPanel);
- popup.insertBefore(leftPanel, popup.childNodes[0]);
- },
-
- updatePopup: function(e, rjCode, isParent=false) {
- const ele = Popup.popupElement;
- const popup = ele.popup;
- popup.className = `${VOICELINK_CLASS}_voicepopup ${VOICELINK_CLASS}_voicepopup-maniax ` + (getAdditionalPopupClasses() || '');
- // popup.id = "voice-" + rjCode;
- popup.style.setProperty("display", "flex", "important"); //= "display: flex";
- popup.setAttribute(RJCODE_ATTRIBUTE, rjCode);
-
- //------检查作品存在情况------
- let workFound = true;
- Popup.setFoundState(true);
- WorkPromise.getFound(rjCode).then(async found => {
- if(rjCode !== popup.getAttribute(RJCODE_ATTRIBUTE)) return;
-
- if(found){
- //找到则直接返回交给下一级处理
- return {found: true, parentRJ: rjCode};
- }
-
- //没找到则尝试找到父作品的RJ号,填补子作品信息的缺失
- let parentRJ = await WorkPromise.getParentRJ(rjCode);
- if(parentRJ === rjCode || !parentRJ) {
- return {found: false, parentRJ: rjCode};
- }
- found = await WorkPromise.getFound(parentRJ);
- return {found: found, parentRJ: parentRJ};
-
- }).then((state) => {
- if(rjCode !== popup.getAttribute(RJCODE_ATTRIBUTE)) return;
-
- const found = state.found;
- const rj = state.parentRJ;
- if(found && rj !== rjCode){
- //如果找到了父作品的信息但子作品找不到,就重新update
- Popup.updatePopup(e, rj, true);
- return;
- }
-
- ele.not_found.style.setProperty("display", found ? "none" : "block", "important"); //display = found ? "none" : "block";
- Popup.setFoundState(found);
- workFound = found;
- });
-
- //------检查是否为女性向------
- WorkPromise.getGirls(rjCode).then(isGirls => {
- if(rjCode !== popup.getAttribute(RJCODE_ATTRIBUTE)) return;
- if(isGirls) popup.className += (` ${VOICELINK_CLASS}_voicepopup-girls`)
- }).catch(e => {});
-
- //------获取作品封面------
- const imgContainer = ele.img.container;
-
- //NSFW模糊等级
- const blur_map = {
- low: "6px",
- medium: "12px",
- high: "24px"
- };
-
- //先对Container内的所有img进行隐藏
- for (let i = 0; i < imgContainer.childNodes.length; ++i) {
- imgContainer.childNodes[i].style.setProperty("display", "none", "important"); //display = "none !important";
- }
-
- //NOTE: 注意这里可能因为快速的多次获取导致同时加载两个图片,可使用占位符来预先占用图片位置
- new Promise((resolve, reject) => {
- let img = ele.img[rjCode];
- if(img) resolve(img);
- else throw Error("首次加载图片");
- }).catch(_ => {
- //首次加载图片,对图片添加占位
- ele.img[rjCode] = 1;
- return WorkPromise.getImgLink(rjCode);
- }).then(link => {
- if(typeof link !== "string"){
- //图片已经加载过,传递的是img,不通过当前then
- return link; //实际上是img
- }
-
- if(rjCode !== popup.getAttribute(RJCODE_ATTRIBUTE)) {
- //清除占位
- ele.img[rjCode] = null;
- return null;
- }
- let img;
- try{
- img = GM_addElement("img", {
- src: link,
- });
- if(!img) { // noinspection ExceptionCaughtLocallyJS
- throw new Error("API调用生成失败");
- }
- }catch (e) {
- img = document.createElement("img");
- img.src = link;
- }
-
- imgContainer.appendChild(img);
- console.warn("添加封面!")
- ele.img[rjCode] = img;
-
- //开启动画
- if(settings._s_sfw_blur_transition){
- img.style.setProperty("transition", "all 0.3s", "important");
- }
-
- //鼠标移动上去解除模糊
- img.addEventListener("mouseenter", e => {
- if(!settings._s_sfw_remove_when_hover){
- return;
- }
- img.style.setProperty("filter", "inherit", "important");
- });
- img.addEventListener("mouseleave", e => {
- if(settings._s_sfw_mode){
- img.style.setProperty("filter", `blur(${blur_map[settings._s_sfw_blur_level]})`, "important");
- }else{
- img.style.setProperty("filter", "inherit", "important");
- }
- });
-
- return img;
- }).then(img => {
- if(!(img instanceof HTMLElement)) return;
- img.style.setProperty("display", "block", "important"); //display = "block"
-
- //设置NSFW模糊
- if(settings._s_sfw_mode){
- img.style.setProperty("filter", `blur(${blur_map[settings._s_sfw_blur_level]})`, "important");
- }else{
- img.style.setProperty("filter", "inherit", "important");
- }
- }).catch(e => {
- //清理并在下次重试
- if(ele.img[rjCode] instanceof HTMLElement) img.remove();
- ele.img[rjCode] = null;
- console.error(e)
- });
-
- //------设置hint可见------
- ele.hint.style.setProperty("display", "block", "important");
-
- //------设置标题------
- const titleElement = ele.title;
- titleElement.innerText = "Loading...";
- titleElement.setHint(localizePopup(localizationMap.hint_copy_work_title));
- titleElement.setCopyText(null);
- titleElement.setSecondaryCopyText(null);
- WorkPromise.getWorkTitle(rjCode).then(title => {
- if(rjCode !== popup.getAttribute(RJCODE_ATTRIBUTE)) return;
- titleElement.innerText = title;
- titleElement.setCopyText(title);
- titleElement.setSecondaryCopyText(convertToValidFileName(title));
- }).catch(_ => {
- if(rjCode !== popup.getAttribute(RJCODE_ATTRIBUTE)) return;
- titleElement.innerHTML = Csp.createHTML("");
- })
-
- //------设置RJ号------
- const rjCodeElement = ele.rj_code;
- rjCodeElement.innerHTML = Csp.createHTML(`[ ${isParent ? " ↑ " : ""}<span class="${VOICELINK_IGNORED_CLASS}" style="font-weight: bold !important;text-decoration-line: underline !important;">${rjCode}</span> ]`);
- WorkPromise.getRJChain(rjCode).then(chain => {
- if(rjCode !== popup.getAttribute(RJCODE_ATTRIBUTE)) return;
- rjCodeElement.innerText = "[ ";
- //构造chain
- for (let i = 0; i < chain.length; i++) {
- const rj = chain[i];
- let e = Popup.createCopyTag("span", rj);
- e.innerText = rj;
- e.classList.add(VOICELINK_IGNORED_CLASS);
- if(i === 0) {
- //第一个元素,也就是当前RJ号
- e.style.setProperty("font-weight", "bold", "important");
- e.style.setProperty("text-decoration", "underline", "important");
- }else{
- rjCodeElement.appendChild(document.createTextNode(" → "));
- }
- rjCodeElement.appendChild(e);
- }
- rjCodeElement.appendChild(document.createTextNode(" ]"));
- });
-
- //清除原有信息并展示加载界面
- for(let child of [...this.popupElement.info_container.children]){
- if(child === this.popupElement.loader) continue;
- child.remove();
- }
- ele.loader.style.setProperty("display", "flex", "important"); //display = "flex !important";
- WorkPromise.getWorkCategory(rjCode).then(category => {
- if(rjCode !== popup.getAttribute(RJCODE_ATTRIBUTE)) return;
- this.set_info_container(rjCode, category);
- }).catch(e => {
- if (rjCode !== popup.getAttribute(RJCODE_ATTRIBUTE)) return;
- //默认other
- this.set_info_container(rjCode, "other");
- });
-
- Popup.move(e);
- },
-
- setFoundState(found){
- const ele = Popup.popupElement;
-
- ele.not_found.style.setProperty("display", found ? "none" : "block", "important");
- //ele.img.container.style.setProperty("display", found && !Popup.hideImg ? "block" : "none", "important");
- ele.left_panel.style.setProperty("display", found ? "flex" : "none", "important");
- ele.right_panel.style.setProperty("display", found ? "block" : "none", "important");
- ele.title.style.setProperty("display", found ? "block" : "none", "important");
- ele.rj_code.style.setProperty("display", found ? "block" : "none", "important");
- ele.info_container.style.setProperty("display", found ? "block" : "none", "important");
- ele.hint.style.setProperty("display", found ? "block" : "none", "important");
- /*ele.dl_count.style.setProperty("display", found ? "block" : "none", "important");
- ele.circle_name.style.setProperty("display", found ? "block" : "none", "important");
- ele.debug.style.setProperty("display", found ? "block" : "none", "important");
- ele.translator_name.style.setProperty("display", found ? "block" : "none", "important");
- ele.release_date.style.setProperty("display", found ? "block" : "none", "important");
- ele.update_date.style.setProperty("display", found ? "block" : "none", "important");
- ele.age_rating.style.setProperty("display", found ? "block" : "none", "important");
- ele.voice_actor.style.setProperty("display", found ? "block" : "none", "important");
- ele.music.style.setProperty("display", found ? "block" : "none", "important");
- ele.genre.style.setProperty("display", found ? "block" : "none", "important");
- ele.file_size.style.setProperty("display", found ? "block" : "none", "important");*/
- },
-
- /**
- * 创建可复制标签
- * @param tag {string|HTMLElement} 标签名或标签对象(如果为标签对象,则会将对象原地转化成可复制标签)
- * @param copyText {string} 需要复制的文本
- * @param isTitle {boolean} 是否为标题元素(使用特殊class)
- * @param hint {string} 提示栏显示的提示文本
- * @returns {HTMLElement} 创建/转换后的标签
- */
- createCopyTag: function (tag, copyText, isTitle = false, hint = isTitle ? localizePopup(localizationMap.hint_copy_all) : localizePopup(localizationMap.hint_copy)) {
- tag = (typeof tag === "string") ? document.createElement(tag) : tag;
- if(isTitle) tag.classList.add("info-title");
-
- //添加自定义方法
- tag.getCopyText = () => tag.getAttribute("copy-text");
- tag.setCopyText = text => {
- if(!text){
- tag.removeAttribute("copy-text");
- return;
- }
- tag.setAttribute("copy-text", text);
- };
-
- tag.getSecondaryCopyText = () => tag.getAttribute("sec-copy-text");
- tag.setSecondaryCopyText = text => {
- if(!text){
- tag.removeAttribute("sec-copy-text");
- return;
- }
- tag.setAttribute("sec-copy-text", text);
- };
-
- tag.getHint = () => tag.getAttribute("hint");
- tag.setHint = hint => {
- tag.setAttribute("hint", hint);
- };
-
- tag.setCopyText(copyText);
- tag.setHint(hint);
- tag.addEventListener("click", e => {
- const attr = e.altKey ? "sec-copy-text" : "copy-text";
- if(!tag.hasAttribute(attr)) return;
- GM_setClipboard(tag.getAttribute(attr), "text")?.finally();
- // navigator.clipboard.writeText(tag.getAttribute(attr)).finally();
- });
- tag.addEventListener("mouseenter", e => {
- let hint = tag.getHint();
- Popup.popupElement.hint.innerText = hint ? hint : Popup.popupElement.hint.innerText;
- })
- return tag;
- },
-
- /**
- * 显示某行的信息
- * @param rjCode {string} 信息对应的RJ号
- * @param id {string} 信息对应的ID
- * @param rowElement {HTMLElement} 行对应的Element元素
- * @param title {string} 行信息标题
- * @param contentProvider {Promise<any|Array|HTMLElement>} 无参内容Provider,返回字符串/字符串列表等用于生成文本
- * @param suffixProvider {Promise<HTMLElement>} 无参后缀Provider,返回一个Element用于放在Content后面
- * @param contentSeperator {string, HTMLElement} Content如果是列表,则该内容为作为分隔符
- * @param contentSeperatorText {string} 如果采用HTMLElement的分隔符,则在复制的时候需要有一个文本表示
- */
- set_info_row: function (rjCode, id, rowElement, title, contentProvider, suffixProvider, contentSeperator = " ", contentSeperatorText = undefined ){
- const settingId = `_s_${id}`;
- //如果设置了不展示信息,或信息没在设置中定义,则不显示
- if(!settings[settingId]) return;
-
- const ele = Popup.popupElement;
- const popup = ele.popup;
- const titleElement = Popup.createCopyTag("span", "", true);
- titleElement.innerText = title;
- const contentElement = Popup.createCopyTag("span", null);
- contentElement.innerText = "Loading...";
-
- rowElement.innerHTML = Csp.createHTML("");
- rowElement.appendChild(titleElement);
- rowElement.appendChild(contentElement);
-
- contentProvider.then(contents => {
- if(rjCode !== popup.getAttribute(RJCODE_ATTRIBUTE)) return;
- if(!Array.isArray(contents)){
- //单个结果转化成列表
- contents = [contents];
- }
-
- //处理结果列表
- contentElement.setCopyText(null);
- contentElement.innerText = "";
- //以指定的分隔符文本为准,不指定分隔符文本再使用分隔符内的文本
- let sepText = contentSeperatorText ? contentSeperatorText : contentSeperator.toString();
- let sep;
- if(typeof contentSeperator === "string"){
- sep = document.createElement("span");
- sep.innerText = contentSeperator;
- }else{
- sep = contentSeperator;
- }
-
- let contentsText = [];
- for (let i = 0; i < contents.length; i++) {
- let c = contents[i];
- if(i > 0) {
- //如果Seperator是Element,则直接复制一个出来添加,否则创建一个span然后把内容转换成文本放进去。
- contentElement.appendChild(sep.cloneNode(true));
- }
-
- let element;
- if(c instanceof HTMLElement){
- element = c;
- }else{
- element = Popup.createCopyTag("a", c);
- element.innerText = c;
- }
-
- //将复制文本加入复制列表
- const copyText = element.getAttribute("copy-text");
- if(copyText) contentsText.push(copyText);
-
- contentElement.appendChild(element);
- }
-
- //为标题添加复制文本
- titleElement.setCopyText(contentsText.join(sepText));
- }).catch(e => {
- if(rjCode !== popup.getAttribute(RJCODE_ATTRIBUTE)) return;
- rowElement.innerHTML = Csp.createHTML("");
- //console.error(e);
- }).finally(() => {
- Popup.adjustPopup(ele._state.mouseX, ele._state.mouseY, true);
- });
-
- if(suffixProvider){
- suffixProvider.then((element) => {
- if(rjCode !== popup.getAttribute(RJCODE_ATTRIBUTE)) return;
- rowElement.appendChild(element);
- }).catch(_ => {});
- }
-
- return rowElement;
- },
- set_dl_count: function (rjCode, category){
- const id = `${category}__dl_count`;
- const element = Popup.set_info_row(rjCode, id, Popup.popupElement.dl_count, localizePopup(localizationMap.dl_count),
- WorkPromise.getDLCount(rjCode));
- if(element) Popup.popupElement.info_container.appendChild(element);
- },
- set_circle_name: function (rjCode, category){
- const id = `${category}__circle_name`;
- const element = Popup.set_info_row(rjCode, id, Popup.popupElement.circle_name, localizePopup(localizationMap.circle_name),
- WorkPromise.getCircle(rjCode), null);
- if(element) Popup.popupElement.info_container.appendChild(element);
- },
- set_translator_name: function (rjCode, category){
- const id = `${category}__translator_name`;
- const element = Popup.set_info_row(rjCode, id, Popup.popupElement.translator_name,
- localizePopup(localizationMap.translator_name), WorkPromise.getTranslatorName(rjCode), null);
- if(element) Popup.popupElement.info_container.appendChild(element);
- },
- set_release_date: function (rjCode, category){
- const id = `${category}__release_date`;
- const element = Popup.set_info_row(rjCode, id, Popup.popupElement.release_date, localizePopup(localizationMap.release_date),
- WorkPromise.getReleaseDate(rjCode).then(async (date) => {
- const [dateStr, isAnnounce] = date;
- const e = Popup.createCopyTag("a", dateStr);
- e.innerText = dateStr;
- if(isAnnounce) e.style.setProperty("color", "gold", "important");
- return e;
- }), WorkPromise.getReleaseCountDownElement(rjCode).then(element => {
- element.style.setProperty("margin-left", "16px", "important");
- return element;
- }));
-
- if(element) Popup.popupElement.info_container.appendChild(element);
- },
- set_update_date: function (rjCode, category){
- const id = `${category}__update_date`;
- const element = Popup.set_info_row(rjCode, id, Popup.popupElement.update_date, localizePopup(localizationMap.update_date),
- WorkPromise.getUpdateDate(rjCode));
- if(element) Popup.popupElement.info_container.appendChild(element);
- },
- set_age_rating: function (rjCode, category){
- const id = `${category}__age_rating`;
- const element = Popup.set_info_row(rjCode, id, Popup.popupElement.age_rating, localizePopup(localizationMap.age_rating),
- WorkPromise.getAgeRating(rjCode).then(rating => {
- let ratingClass = `${VOICELINK_CLASS}_age-all`;
- if(rating.includes("18")){
- ratingClass = `${VOICELINK_CLASS}_age-18`;
- }
- let e = Popup.createCopyTag("a", rating);
- e.innerText = rating;
- e.classList.add(ratingClass);
- return e;
- }));
- if(element) Popup.popupElement.info_container.appendChild(element);
- },
- set_scenario: function (rjCode, category){
- const id = `${category}__scenario`;
- const element = Popup.set_info_row(rjCode, id, Popup.popupElement.scenario, localizePopup(localizationMap.scenario),
- WorkPromise.getScenario(rjCode), null, " / ");
- if(element) Popup.popupElement.info_container.appendChild(element);
- },
- set_illustration: function (rjCode, category){
- const id = `${category}__illustration`;
- const element = Popup.set_info_row(rjCode, id, Popup.popupElement.illustration, localizePopup(localizationMap.illustration),
- WorkPromise.getIllustrator(rjCode), null, " / ");
- if(element) Popup.popupElement.info_container.appendChild(element);
- },
- set_voice_actor: function (rjCode, category){
- const id = `${category}__voice_actor`;
- const element = Popup.set_info_row(rjCode, id, Popup.popupElement.voice_actor, localizePopup(localizationMap.voice_actor),
- WorkPromise.getCV(rjCode), null, " / ");
- if(element) Popup.popupElement.info_container.appendChild(element);
- },
- set_music: function (rjCode, category) {
- const id = `${category}__music`;
- const element = Popup.set_info_row(rjCode, id, Popup.popupElement.music, localizePopup(localizationMap.music),
- WorkPromise.getMusic(rjCode), null, " / ");
- if(element) Popup.popupElement.info_container.appendChild(element);
- },
- set_genre: function (rjCode, category){
- const id = `${category}__genre`;
- const element = Popup.set_info_row(rjCode, id, Popup.popupElement.genre, localizePopup(localizationMap.genre),
- WorkPromise.getTags(rjCode), null, "\u3000");
- if(element) Popup.popupElement.info_container.appendChild(element);
- },
- set_file_size: function (rjCode, category){
- const id = `${category}__file_size`;
- const element = Popup.set_info_row(rjCode, id, Popup.popupElement.file_size, localizePopup(localizationMap.file_size),
- WorkPromise.getFileSize(rjCode));
- if(element) Popup.popupElement.info_container.appendChild(element);
- },
-
- get_tag: function (text, tagClass) {
- if(!tagClass.startsWith(`${VOICELINK_CLASS}_`)){
- tagClass = `${VOICELINK_CLASS}_${tagClass}`
- }
- const tag = document.createElement("span");
- tag.classList.add(`${VOICELINK_CLASS}_tag_tight`);
- tag.classList.add(tagClass);
- tag.innerText = text;
- return tag;
- },
- get_tag_rate: async function (rjCode) {
- let rate = await WorkPromise.getRateAvg(rjCode);
- let cot = await WorkPromise.getRateCount(rjCode);
- return Popup.get_tag(`${rate.toFixed(2)}★` + (settings._s_show_rate_count ? ` (${cot})` : ""), "tag-yellow");
- },
- get_tag_no_longer_available: async function (rjCode) {
- let sale = await WorkPromise.getSale(rjCode);
- if(sale) return;
- return Popup.get_tag(localizePopup(localizationMap.tag_no_longer_available),
- "tag-gray");
- },
- get_tag_work_type: async function (rjCode) {
- let type = await WorkPromise.getWorkTypeText(rjCode);
- let tagClass = "tag-gray";
- switch (type) {
- case localizePopup(localizationMap.work_type_game):
- tagClass = "tag-purple";
- break;
- case localizePopup(localizationMap.work_type_comic):
- tagClass = "tag-green";
- break;
- case localizePopup(localizationMap.work_type_illustration):
- tagClass = "tag-teal";
- break;
- case localizePopup(localizationMap.work_type_novel):
- tagClass = "tag-gray";
- break;
- case localizePopup(localizationMap.work_type_video):
- tagClass = "tag-darkblue";
- break;
- case localizePopup(localizationMap.work_type_voice):
- tagClass = "tag-orange";
- break;
- case localizePopup(localizationMap.work_type_music):
- tagClass = "tag-yellow";
- break;
- case localizePopup(localizationMap.work_type_tool):
- tagClass = "tag-gray";
- break;
- case localizePopup(localizationMap.work_type_voice_comic):
- tagClass = "tag-blue";
- break;
- case localizePopup(localizationMap.work_type_other):
- tagClass = "tag-gray";
- break;
- default:
- tagClass = "tag-gray";
- break;
- }
- return Popup.get_tag(type, tagClass);
- },
- get_tag_translatable: async function (rjCode) {
- let able = await WorkPromise.getTranslatable(rjCode);
- if(!able) return;
- return Popup.get_tag(localizePopup(localizationMap.tag_translatable),
- "tag-green");
- },
- get_tag_not_translatable: async function (rjCode) {
- let able = await WorkPromise.getTranslatable(rjCode);
- let translated = await WorkPromise.getTranslated(rjCode);
- if(able || translated) return;
- return Popup.get_tag(localizePopup(localizationMap.tag_not_translatable),
- "tag-red");
- },
- get_tag_translated: async function (rjCode) {
- let translated = await WorkPromise.getTranslated(rjCode);
- if(!translated) return;
- return Popup.get_tag(localizePopup(localizationMap.tag_translated), "tag-teal");
- },
- get_tag_bonus_work: async function (rjCode) {
- let bonus = await WorkPromise.getBonus(rjCode);
- if(!bonus) return;
- return Popup.get_tag(localizePopup(localizationMap.tag_bonus_work),
- "tag-yellow");
- },
- get_tag_has_bonus: async function (rjCode) {
- let has = await WorkPromise.getHasBonus(rjCode);
- if(!has) return;
- return Popup.get_tag(localizePopup(localizationMap.tag_has_bonus),
- "tag-orange");
- },
- get_tag_language_support: async function (rjCode) {
- const lang = await WorkPromise.getLanguages(rjCode);
- if(!lang || lang.length <= 0){
- return;
- }
- let txt = "";
- lang.forEach(l => {
- txt += ` | ${l}`;
- });
- txt = txt.substring(3);
- return Popup.get_tag(txt, "tag-pink");
- },
- get_tag_file_format: async function (rjCode) {
- const format = await WorkPromise.getFileFormats(rjCode);
- if(!format || format.length <= 0){
- return;
- }
- let txt = "";
- format.forEach(f => {
- txt += ` | ${f}`;
- });
- txt = txt.substring(3);
- return Popup.get_tag(txt, "tag-darkblue");
- },
- get_tag_ai: async function (rjCode) {
- const ai = await WorkPromise.getAIUsedText(rjCode);
- if(!ai) return;
- return Popup.get_tag(ai, "tag-purple");
- },
- get_translatable_tag: async function (rjCode, tag_id) {
- if(settings[`_s_${tag_id}`] !== true) return;
-
- if(tag_id.startsWith("tag_")) tag_id = tag_id.substring(4);
- const t = await WorkPromise.getWorkPromise(rjCode).translatable;
- const stat = t[tag_id];
-
- const hasRequest = stat.request > 0;
- const hasSale = stat.sale > 0;
- const displayCount = stat.agree || hasRequest || hasSale;
- const lang = tag_id.substring("translation_request_".length);
- const tag = Popup.get_tag(`${localizePopup(localizationMap[`language_${lang}_abbr`])}${stat.agree ? "" : (stat.agree === false ? " ✘" : " ?")} ${displayCount ? ` ${stat.request}-${stat.sale}` : ""}`,
- hasSale ? "tag-green" : (hasRequest ? "tag-orange" : "tag-gray"));
- tag.classList.add(`${VOICELINK_CLASS}_tag_small`);
- return tag;
- },
-
- get_tag_container: function (rjCode, tag_list) {
- const container = document.createElement("div");
- container.classList.add(`${VOICELINK_CLASS}_tags`);
- for (const tag_id of tag_list) {
- if(settings[`_s_${tag_id}`] !== true) continue;
-
- let shadowTag = document.createElement("span");
- shadowTag.style.setProperty("display", "none", "important"); //display = "none !important";
- shadowTag.setAttribute("data-id", tag_id);
- container.appendChild(shadowTag);
-
- let tag_get = this[`get_${tag_id}`];
- tag_get(rjCode).then(tag => {
- if(tag){
- container.insertBefore(tag, shadowTag);
- shadowTag.remove();
- }
- });
- }
- return container;
- },
- get_translatable_tag_container: function (rjCode, tag_list) {
- const container = document.createElement("div");
- container.classList.add(`${VOICELINK_CLASS}_tags`);
- container.style.setProperty("margin-top", "0", "important"); //marginTop = "0 !important";
- for (const tag_id of tag_list) {
- let shadowTag = document.createElement("span");
- shadowTag.style.setProperty("display", "none", "important"); //display = "none !important";
- shadowTag.setAttribute("data-id", tag_id);
- container.appendChild(shadowTag);
-
- Popup.get_translatable_tag(rjCode, tag_id).then(tag => {
- if(tag){
- container.insertBefore(tag, shadowTag);
- shadowTag.remove();
- }
- }).catch(e => {});
- }
- return container;
- },
-
- //整合顺序
- set_info_container: function (rjCode, category) {
- //清除上次的信息
- for(let child of [...this.popupElement.info_container.children]){
- if(child === this.popupElement.loader) {
- child.style.setProperty("display", "none", "important"); //display = "none !important";
- continue;
- }
- child.remove();
- }
-
- //TAG部分
- const infoContainer = this.popupElement.info_container;
- let tagContainer = null;
- if(settings._s_tag_main_switch === true){
- const container = this.get_tag_container(rjCode,
- settings[`_s_tag_display_order`]);
- tagContainer = container;
- infoContainer.appendChild(container);
- }
-
- //翻译申请情况
- const shadowContainer = document.createElement("div");
- shadowContainer.style.setProperty("display", "none", "important"); //display = "none !important";
- infoContainer.appendChild(shadowContainer);
- WorkPromise.getTranslatable(rjCode).then(able => {
- if(rjCode !== Popup.popupElement.popup.getAttribute(RJCODE_ATTRIBUTE)) return;
- if(able && settings._s_tag_translation_request === true){
- const translatableContainer = this.get_translatable_tag_container(rjCode,
- settings._s_tag_translation_request_display_order);
- infoContainer.insertBefore(translatableContainer, shadowContainer);
- shadowContainer.remove();
- }
- }).catch(e => {});
-
- //信息部分
- const order = settings[`_s_${category}__info_display_order`];
- order.forEach(id => {
- try{
- id = id.substring(id.indexOf("__") + 2);
- this["set_" + id](rjCode, category);
- }catch (e) {
- console.error(e);
- }
- });
-
- const debugElement = document.createElement("div");
- this.popupElement.info_container.appendChild(debugElement);
- WorkPromise.getDebug(rjCode).then(t => {
- debugElement.innerHTML = Csp.createHTML(t);
- });
- },
-
- //调整弹框位置
- adjustPopup: function (mouseX, mouseY, force = false){
- // console.log("定位修正")
-
- //定位修正
- const popup = Popup.popupElement.popup;
- const ele = Popup.popupElement;
- if(!Popup.pinRJ || force){
- if (popup.offsetWidth + mouseX + 10 < window.innerWidth - 10) {
- popup.style.setProperty("left", (mouseX + 10) + "px", "important");
- }
- else {
- popup.style.setProperty("left", (window.innerWidth - popup.offsetWidth - 10) + "px", "important");
- }
- }
-
- let rect = popup.getBoundingClientRect();
- if(!Popup.pinRJ || force || rect.top < 0 || rect.bottom > window.innerHeight){
- if (mouseY > window.innerHeight / 2) {
- let top = Math.max(mouseY - popup.offsetHeight - 8, 0);
- popup.style.setProperty("top", top + "px", "important");
- }
- else {
- let top = Math.min(mouseY + 20, window.innerHeight - popup.offsetHeight);
- popup.style.setProperty("top", top + "px", "important");
- }
- }
-
- //大小修正
- let currentFontSize = popup.computedStyleMap().get("font-size").toString();
- currentFontSize = parseFloat(currentFontSize.substring(0, Math.max(currentFontSize.indexOf("px"), 1)));
- const sizeLevel = [15, 14.5, 14, 13.5, 13, 12.5, 12];
- let size = sizeLevel[sizeLevel.length - 1];
- if(popup.offsetHeight > window.innerHeight){
- //计算popup的高度与window高度的比值,找到离它最相近且更大的当前字体大小和sizeLevel的比值
- for (const s of sizeLevel) {
- if(popup.offsetHeight / window.innerHeight < currentFontSize / s){
- size = s;
- break;
- }
- }
- popup.style.setProperty("font-size", size + "px", "important");
- }
-
- //封面图位置修正
- ele.img.container.style.top = `${Math.max(0, -popup.offsetTop)}px`;
- },
-
- pinRJ: undefined,
- setPinState: function (rjCode, pin, close = true){
- const ele = Popup.popupElement;
- const popup = ele.popup;
- if(!pin){
- //关闭弹框
- popup.style.setProperty("pointer-events", "none", "important");
- Popup.pinRJ = undefined;
- popup.removeAttribute("pin");
-
- if(close) popup.style.setProperty("display", "none", "important");
-
- //取消注册自动关闭监听
- document.removeEventListener("keyup", Popup.keyup);
- document.removeEventListener("mousemove", Popup.domMove);
- return
- }
-
- popup.style.setProperty("pointer-events", "auto", "important");
- Popup.pinRJ = rjCode;
- popup.setAttribute("pin", "");
-
- //添加监听器
- document.addEventListener("keyup", Popup.keyup);
- document.addEventListener("mousemove", Popup.domMove);
- },
- hasPinned: function (){
- return Popup.popupElement.popup.hasAttribute("pin");
- },
- /**
- * @param e {KeyboardEvent}
- */
- isHoldPinKey: function(e){
- if(getOS() === "Mac"){
- return e.metaKey;
- }
- return e.ctrlKey;
- },
- /**
- * @param e {KeyboardEvent}
- */
- isPinKeyDown: function (e) {
- if(getOS() === "Mac"){
- return e.key === "Meta";
- }
- return e.key === "Control";
- },
-
- /**
- * 鼠标离开固定弹窗时,如果没有按住pin键则消失
- * @param e {MouseEvent}
- */
- /*pinLeave: function (e) {
- if(Popup.isHoldPinKey(e)){
- return;
- }
- Popup.setPinState(null, false, true);
- },*/
- /**
- * 监听网页内的鼠标移动事件,来保证弹框正常移除
- * @param e {MouseEvent}
- */
- domMove: function (e) {
- if(!Popup.hasPinned() || Popup.isHoldPinKey(e)){
- return;
- }
- Popup.setPinState(null, false);
- },
- /**
- * 鼠标移动到链接上触发
- * @param e {MouseEvent}
- */
- over: function (e) {
- const target = isInDLSite() ? e.target : getVoiceLinkTarget(e.target);
- if(!target || !target.classList.contains(VOICELINK_CLASS)) return;
-
- const rjCode = target.getAttribute(RJCODE_ATTRIBUTE);
- if(rjCode === null) return;
-
- //记录鼠标位置
- let ele = Popup.popupElement;
- ele._state.mouseX = e.clientX;
- ele._state.mouseY = e.clientY;
-
- //如果用户固定了弹框,则提示用户必须ctrl关闭弹框才能解析
- if(Popup.isHoldPinKey(e) && Popup.pinRJ){
- ele.hint.innerText = localizePopup(localizationMap.hint_unpin);
- return;
- }else{
- //没有固定弹框的话清理pinRJ,因为有时候pinRJ没办法被keyup清理(如keyup未触发)
- Popup.pinRJ = undefined
- ele.hint.innerText = localizePopup(localizationMap.hint_pin);
- }
-
- //修正链接
- if(target.hasAttribute("voicelink-linkified")){
- WorkPromise.getWorkPromise(rjCode).info.then(info => {
- if(info.is_announce === true){
- target.href = `https://www.dlsite.com/maniax/announce/=/product_id/${rjCode}.html`;
- }
- });
- }
-
- let popup = document.querySelector(`div#${VOICELINK_CLASS}-voice-popup`); // + rjCode);
- if (popup) {
- popup.style.setProperty("display", "flex", "important"); //display = "flex !important";
- //先将字体大小变回原样
- popup.style.setProperty("font-size", "15.4px", "important");
- }
- else {
- Popup.makePopup();
- popup = ele.popup;
- }
- Popup.updatePopup(e, rjCode);
-
- //如果按住了CTRL,则将popup可被点击,否则设置穿透
- //并设置Copy显示情况
- if(Popup.isHoldPinKey(e)){
- Popup.setPinState(rjCode, true)
- ele.hint.innerText = localizePopup(localizationMap.hint_unpin);
- }else{
- Popup.setPinState(rjCode, false, false)
- }
-
- //设置焦点至链接上
- target.focus();
- target.style.setProperty("outline", "none", "important");
-
- },
-
- /**
- * 鼠标离开时触发
- * @param e {MouseEvent}
- */
- out: function (e) {
- //如果固定则禁止关闭
- if(Popup.isHoldPinKey(e)) {
- return
- }
-
- const target = isInDLSite() ? e.target : getVoiceLinkTarget(e.target);
- if(!target || !target.classList.contains(VOICELINK_CLASS)) return;
-
- const rjCode = target.getAttribute(RJCODE_ATTRIBUTE);
- if(rjCode === null) return;
-
- //取消固定并关闭
- Popup.setPinState(rjCode, false)
-
- //取消focus
- target.blur();
- target.style.setProperty("outline", null);
- },
-
- /**
- * 鼠标移动时触发
- * @param e {MouseEvent}
- */
- move: function (e) {
- const target = isInDLSite() ? e.target : getVoiceLinkTarget(e.target);
- if(!target || !target.classList.contains(VOICELINK_CLASS)) return;
-
- const popup = document.querySelector(`div#${VOICELINK_CLASS}-voice-popup`); // + rjCode);
- if(!popup) return;
-
- const rjCode = e.target.getAttribute(RJCODE_ATTRIBUTE);
- if(rjCode === null) return;
-
- let ele = Popup.popupElement;
- ele._state.mouseX = e.clientX;
- ele._state.mouseY = e.clientY;
-
- //焦点不在浏览器上的时候无法触发keydown,因此需要用move来辅助激活pin
- if(Popup.isHoldPinKey(e) && !Popup.pinRJ){
- //按下pin键但没激活pin模式的时候手动激活
- Popup.setPinState(rjCode, true);
- }
-
- //如果弹框已固定且固定的并非当前所选链接RJ号,则不进行定位修正
- if(Popup.pinRJ && rjCode !== Popup.pinRJ){
- return;
- }
-
- Popup.adjustPopup(e.clientX, e.clientY);
-
- },
-
- /**
- * 按键按下时触发
- * @param e {KeyboardEvent}
- */
- keydown: function (e) {
- const target = isInDLSite() ? e.target : getVoiceLinkTarget(e.target);
- if(!target || !target.classList.contains(VOICELINK_CLASS)) return;
-
- const rjCode = target.getAttribute(RJCODE_ATTRIBUTE);
- if(rjCode === null) return;
-
- let popup = Popup.popupElement.popup;
- if(popup.style.display !== "none" && Popup.isPinKeyDown(e)){
- //按住CTRL以固定显示弹框
- Popup.setPinState(rjCode, true);
- }
- },
-
- /**
- * 按键抬起时触发
- * @param e {KeyboardEvent}
- */
- keyup: function (e) {
- let popup = Popup.popupElement.popup;
- if(popup && Popup.isPinKeyDown(e)){
- Popup.setPinState(null, false);
- }
- }
- }
-
- const WorkPromise = {
- /**
- * 标题、社团、发行日期、更新日期、年龄指定
- * CV、标签、文件大小、封面地址
- */
-
- checkNotNull: function (obj){
- if(obj === null || obj === undefined) throw new Error();
- return obj;
- },
-
- getWorkPromise: function (rjCode){
- if(work_promise[rjCode]){
- return work_promise[rjCode];
- }
- work_promise[rjCode] = DLsite.getWorkRequestPromise(rjCode);
- return work_promise[rjCode];
- },
-
- getFound: async function(rjCode){
- try{
- const data = await WorkPromise.getWorkPromise(rjCode).api2;
- if(data && data.product_id !== undefined) return true;
-
- //否则再次检查api1
- const api = await WorkPromise.getWorkPromise(rjCode).api;
- return api && api.is_sale !== undefined;
- }catch (e){
- //说明是网络问题,删除缓存并返回true
- delete work_promise[rjCode];
- return true;
- }
- },
-
- getTranslationInfo: async function(rjCode){
- const p = WorkPromise.getWorkPromise(rjCode);
- let data = await p.api2;
- if(data.translation_info) return data.translation_info;
-
- data = await p.api;
- return data.translation_info ? data.translation_info : {};
- },
-
- getRJChain: async function(rjCode) {
- //RJxxx → RJxxx → RJxxx,这样从子级指向父级
- const trans = await WorkPromise.getTranslationInfo(rjCode);
- let chain = [rjCode];
- if(trans.is_child){
- chain.push(trans.parent_workno, trans.original_workno);
- }else if(trans.is_parent){
- chain.push(trans.original_workno);
- }
- return chain;
- },
-
- getParentRJ: async function(rjCode){
- try{
- const p = WorkPromise.getWorkPromise(rjCode);
- let trans = await WorkPromise.getTranslationInfo(rjCode);
- if(trans.is_original || trans.is_parent) return rjCode;
- if(trans.parent_workno) return trans.parent_workno;
-
- let data = await p.info;
- return data.parentWork;
- }catch (e){
- return null;
- }
- },
-
- getGirls: async function(rjCode){
- const p = WorkPromise.getWorkPromise(rjCode);
- let data = await p.api2;
- if(data.sex_category && data.sex_category === 2) return true;
- if(data.site_id === "girls") return true;
-
- //否则再次检查api1
- data = await WorkPromise.getWorkPromise(rjCode).api;
- WorkPromise.checkNotNull(data.is_girls)
- return data.is_girls;
- },
-
- getAnnounce: async function(rjCode) {
- const p = WorkPromise.getWorkPromise(rjCode);
- const info = await p.info;
- return info.is_announce;
- },
-
- getSale: async function(rjCode, checkAnnounce = true){
- const p = WorkPromise.getWorkPromise(rjCode);
- let data = await p.api;
- if(!checkAnnounce){
- return data.is_sale;
- }
- return data.is_sale || await WorkPromise.getAnnounce(rjCode);
- },
-
- getDLCount: async function (rjCode) {
- const p = WorkPromise.getWorkPromise(rjCode);
- let data = await p.api;
- WorkPromise.checkNotNull(data.dl_count);
- return data.dl_count;
- },
-
- getRateAvg: async function (rjCode) {
- const p = WorkPromise.getWorkPromise(rjCode);
- let data = await p.api;
- if(data.rate_average_2dp) return data.rate_average_2dp;
-
- //还可以累加api2的结果获得
- data = await p.api2;
- this.checkNotNull(data.rate_count_detail);
- let sum = 0;
- let count = 0;
- for (const key in data.rate_count_detail) {
- let rate = parseInt(key);
- let cot = parseInt(data.rate_count_detail[key]);
- count += cot
- sum += rate * cot;
- }
- return sum / count;
- },
-
- getRateCount: async function (rjCode) {
- const p = WorkPromise.getWorkPromise(rjCode);
- let data = await p.api;
- if(data.rate_count) return data.rate_count;
-
- //还可以累加api2的结果获得
- data = await p.api2;
- this.checkNotNull(data.rate_count_detail);
- let count = 0;
- for (const key in data.rate_count_detail) {
- count += parseInt(data.rate_count_detail[key]);
- }
- return count;
- },
-
- getWishlistCount: async function (rjCode) {
- const p = WorkPromise.getWorkPromise(rjCode);
- let data = await p.api;
- this.checkNotNull(data.wishlist_count);
- return data.wishlist_count;
- },
-
- getPriceText: async function (rjCode) {
- const p = WorkPromise.getWorkPromise(rjCode);
- //TODO: 价格以后再加,还要考虑汇率和添加设置项
- },
-
- getBonus: async function(rjCode) {
- const p = WorkPromise.getWorkPromise(rjCode);
- let data = await p.api;
- return !data.is_sale && data.is_free && data.is_oly && data.wishlist_count === 0;
- // return data.is_bonus;
- },
-
- getHasBonus: async function(rjCode) {
- const p = WorkPromise.getWorkPromise(rjCode);
- let data = await p.api;
- return data.bonuses && data.bonuses.length > 0;
- },
-
- getTranslatable: async function(rjCode) {
- const trans = await WorkPromise.getTranslationInfo(rjCode);
- return trans.is_translation_agree === true;
- },
-
- getTranslated: async function(rjCode) {
- const trans = await WorkPromise.getTranslationInfo(rjCode);
- return trans.is_parent === true || trans.is_child === true;
- },
-
- getLanguages: async function(rjCode){
- //返回字符串数组,根据popup设置的语言返回支持的语言列表
- const map = {
- JPN: localizePopup(localizationMap.language_japanese),
- ENG: localizePopup(localizationMap.language_english),
- CHI_HANS: localizePopup(localizationMap.language_simplified_chinese),
- CHI_HANT: localizePopup(localizationMap.language_traditional_chinese),
- KO_KR: localizePopup(localizationMap.language_korean),
- SPA: localizePopup(localizationMap.language_spanish),
- FRE: localizePopup(localizationMap.language_french),
- RUS: localizePopup(localizationMap.language_russian),
- THA: localizePopup(localizationMap.language_thai),
- GER: localizePopup(localizationMap.language_german),
- FIN: localizePopup(localizationMap.language_finnish),
- POR: localizePopup(localizationMap.language_portuguese),
- VIE: localizePopup(localizationMap.language_vietnamese),
- ITA: localizePopup(localizationMap.language_italian),
- ARA: localizePopup(localizationMap.language_arabic),
- POL: localizePopup(localizationMap.language_polish),
- }
- const p = WorkPromise.getWorkPromise(rjCode);
- let api = await p.api2;
- api = api.options ? api : await p.api;
- const options = api.options?.split("#");
- const result = [];
- for (const key in map) {
- const lang = map[key];
- if(options?.includes(key)) result.push(lang);
- }
- return result;
- },
-
- getFileFormats: async function(rjCode){
- //返回字符串数组,返回文件格式列表
- const result = [];
- const p = WorkPromise.getWorkPromise(rjCode);
- let api = await p.api2;
- if(api.file_type === "EXE"){
- result.push("EXE");
- }else if(api.file_type_string){
- result.push(api.file_type_string);
- }
- if(api.file_type_special) result.push(api.file_type_special);
-
- if(!api.options) api = await p.api;
- if(api.options && api.options.includes("WPD")){
- result.push("PDF");
- }
- if(api.options && api.options.includes("WAP")){
- result.push("APK");
- }
-
- return result;
- },
-
- getAIUsedText: async function(rjCode) {
- //返回是否使用或部分使用AI,根据popup语言返回字符串。
- const p = WorkPromise.getWorkPromise(rjCode);
- let api = await p.api2;
- api = api.options ? api : await p.api;
- const options = api.options ? api.options : "";
- if(options.includes("AIG")){
- return localizePopup(localizationMap.tag_aig);
- }else if(options.includes("AIP")){
- return localizePopup(localizationMap.tag_aip);
- }
- return null;
- },
-
- getDebug: async function(rjCode){
- return "";
- const work = WorkPromise.getWorkPromise(rjCode);
- const api2 = await work.api2;
- const api = await work.api;
- const info = await work.info;
- const circle = work.circle;
-
- return `is_ana_api2: ${api2.is_ana}<br/>
- is_ana_api: ${api.is_ana}`;
- },
-
- getWorkCategory: async function(rjCode){
- const type = await WorkPromise.getWorkType(rjCode);
- /* voice: 音声
- * game: 游戏
- * manga: 漫画/插画/音声漫画
- * video: 视频
- * novel: 小说
- * other: 其它
- */
- switch (type) {
- case 0:
- return "voice";
- case 1:
- return "game";
- case 2 || 3 || 8:
- return "manga";
- case 5:
- return "video";
- case 4:
- return "novel";
- default:
- return "other";
- }
- },
-
- getWorkTypeText: async function(rjCode) {
- const mapping = [
- localizePopup(localizationMap.work_type_voice),
- localizePopup(localizationMap.work_type_game),
- localizePopup(localizationMap.work_type_comic),
- localizePopup(localizationMap.work_type_illustration),
- localizePopup(localizationMap.work_type_novel),
- localizePopup(localizationMap.work_type_video),
- localizePopup(localizationMap.work_type_music),
- localizePopup(localizationMap.work_type_tool),
- localizePopup(localizationMap.work_type_voice_comic),
- localizePopup(localizationMap.work_type_other),
- ];
- return mapping[await WorkPromise.getWorkType(rjCode)];
- },
-
- getWorkType: async function(rjCode) {
- const p = WorkPromise.getWorkPromise(rjCode);
- const api2 = await p.api2;
- let workType = api2.work_type;
- if(!workType) workType = (await p.api).work_type;
-
- switch (workType) {
- case "SOU":
- return 0;
- case (["ACN", "QIZ", "ADV", "RPG", "TBL", "DNV", "SLN", "TYP", "STG", "PZL", "ETC"]
- .includes(workType) ? workType : "ERR"):
- return 1;
- case (["MNG", "SCM", "WBT"]
- .includes(workType) ? workType : "ERR"):
- return 2;
- case "ICG":
- return 3;
- case (["NRE", "KSV"].includes(workType) ? workType : "ERR"):
- return 4;
- case "MOV":
- return 5;
- case "MUS":
- return 6;
- case (["TOL", "IMT", "AMT"]
- .includes(workType) ? workType : "ERR"):
- return 7;
- case "VCM":
- return 8;
- case "ET3":
- return 9;
- default:
- throw new Error("无法获取作品类型/未知作品类型:" + workType);
- }
- },
-
- getImgLink: async function(rjCode){
- let link = undefined;
- const p = WorkPromise.getWorkPromise(rjCode);
-
- try {
- let data = await p.api2;
- if (data.image_main && data.image_main.url) link = "https:" + data.image_main.url;
- } catch (e) {}
-
- if(link && !link.includes("no_img_main.gif")){
- return link;
- }
-
- try{
- const info = await p.info;
- WorkPromise.checkNotNull(info.img);
- return info.img;
- }catch (e) {
- }
-
- try{
- const apiData = await WorkPromise.getWorkPromise(rjCode).api;
- if(apiData.work_image) return "https:" + apiData.work_image;
- }catch (e){}
-
- throw new Error("无法获取图片链接");
- },
-
- getWorkTitle: async function(rjCode){
- return await WorkPromise.getWorkPromise(rjCode).translated_title;
- },
-
- getAgeRating: async function(rjCode){
- let p = WorkPromise.getWorkPromise(rjCode);
- let api = await p.api2;
- if(!api.age_category) api = await p.api;
- switch (api.age_category){
- case 1:
- return "All";
- case 2:
- return "R15";
- case 3:
- return "R18";
- }
-
- const info = await p.info;
- WorkPromise.checkNotNull(info.rating);
- return info.rating;
- },
-
- getCircle: async function(rjCode, findOriginal = true){
- let trans = await WorkPromise.getTranslationInfo(rjCode);
- if(!trans.is_original && findOriginal){
- //使用原作RJ号开始寻找,如果找不到翻译信息就没办法了
- rjCode = trans.original_workno ? trans.original_workno : rjCode;
- }
-
- let work = WorkPromise.getWorkPromise(rjCode);
- let api2 = await work.api2;
- if(api2.maker_name) return api2.maker_name;
-
- /**
- * 接下来有两种搜索方式:
- * 1. api1 + circle接口
- * 2. info搜索
- * 前者成功率更高(下架后还能获取到api1,社团没解散就能获得社团信息),两个加载速度不确定谁快谁慢,所以把1放在前面
- */
-
- const circleInfo = await work.circle;
- if(circleInfo && circleInfo.name) return circleInfo.name;
-
- let info = await work.info;
- if(info.circle) return info.circle.trim();
-
- throw new Error("无法获取社团信息");
- },
-
- getTranslatorName: async function(rjCode){
- let trans = await WorkPromise.getTranslationInfo(rjCode);
- if(!trans.is_child) throw new Error("非翻译作品RJ号");
- return await WorkPromise.getCircle(rjCode, false);
- },
-
- getReleaseDate: async function(rjCode){
- const p = WorkPromise.getWorkPromise(rjCode);
- const info = await p.info;
- if(info && !info.is_announce && info.date) return [info.date.trim(), false];
- if(info && info.is_announce && info.dateAnnounce) return [info.dateAnnounce.trim(), true];
-
- //从api中查找发售时间
- let api = await p.api2;
- api = api.regist_date ? api : await p.api;
- WorkPromise.checkNotNull(api.regist_date)
-
- return [api.regist_date, api.is_announce];
- },
-
- getReleaseCountDownElement: async function(rjCode) {
- const p = WorkPromise.getWorkPromise(rjCode);
- const info = await p.info;
- if(info && info.is_announce && info.dateAnnounce) {
- return DateParser.getCountDownDateElement(DateParser.parseDateStr(info.dateAnnounce, info.lang));
- }
- return null;
- },
-
- getUpdateDate: async function(rjCode) {
- const p = WorkPromise.getWorkPromise(rjCode);
- const info = await p.info;
- if(info["update"]) return info["update"].trim();
-
- throw new Error();
- },
-
- getScenario: async function(rjCode) {
- const p = WorkPromise.getWorkPromise(rjCode);
- const api2 = await p.api2;
- if(api2.creaters && api2.creaters.scenario_by && api2.creaters.scenario_by.length > 0){
- return api2.creaters.scenario_by.map(v => v.name);
- }
-
- //无法获取api2则直接通过html获取
- const info = await WorkPromise.getWorkPromise(rjCode).info;
- WorkPromise.checkNotNull(info.scenario);
- return info.scenario;
- },
-
- getIllustrator: async function(rjCode) {
- const p = WorkPromise.getWorkPromise(rjCode);
- const api2 = await p.api2;
- if(api2.creaters && api2.creaters.illust_by && api2.creaters.illust_by.length > 0){
- return api2.creaters.illust_by.map(v => v.name);
- }
-
- //无法获取api2则直接通过html获取
- const info = await WorkPromise.getWorkPromise(rjCode).info;
- WorkPromise.checkNotNull(info.illustration);
- return info.illustration;
- },
-
- getCV: async function(rjCode){
- const p = WorkPromise.getWorkPromise(rjCode);
- const api2 = await p.api2;
- if(api2.creaters && api2.creaters.voice_by && api2.creaters.voice_by.length > 0){
- return api2.creaters.voice_by.map(v => v.name);
- }
-
- //无法获取api2则直接通过html获取
- const info = await WorkPromise.getWorkPromise(rjCode).info;
- WorkPromise.checkNotNull(info.cv);
- return info.cv;
- },
-
- getMusic: async function(rjCode) {
- const p = WorkPromise.getWorkPromise(rjCode);
- const api2 = await p.api2;
- if(api2.creaters && api2.creaters.music_by && api2.creaters.music_by.length > 0){
- return api2.creaters.music_by.map(v => v.name);
- }
-
- //无法获取api2则直接通过html获取
- const info = await WorkPromise.getWorkPromise(rjCode).info;
- WorkPromise.checkNotNull(info.music);
- return info.music;
- },
-
- getTags: async function(rjCode) {
- //注意该方法返回字符串数组而不是纯字符串
- const p = WorkPromise.getWorkPromise(rjCode);
- const api2 = await p.api2;
- if(api2.genres && api2.genres.length > 0){
- return api2.genres.map(genre => genre.name);
- }
-
- //无法获取api2时通过html获取
- const info = await p.info;
- WorkPromise.checkNotNull(info.tags);
- return info.tags;
- },
-
- getFileSizeStr: function(byteCount = 0){
- const units = ["B", "KB", "MB", "GB", "TB"];
- let unit = "B";
- for (let i = 1; byteCount >= 1024; i++){
- byteCount /= 1024;
- unit = units[i];
- }
- return `${Math.round(byteCount * 100) / 100}${unit}`;
- },
-
- getFileSize: async function(rjCode) {
- const trans = await WorkPromise.getTranslationInfo(rjCode);
- if(trans.is_parent){
- //翻译版本的父级没有内容信息,自然无法显示文件大小,所以需要获得原作品的大小信息
- //Child和Original都有各自的大小信息,正常获取计算即可
- rjCode = trans.original_workno ? trans.original_workno : rjCode;
- }
-
- const p = WorkPromise.getWorkPromise(rjCode);
- let api2 = await p.api2;
- if(api2.contents_file_size && api2.contents_file_size > 0){
- return WorkPromise.getFileSizeStr(api2.contents_file_size);
- }
-
- //通过html获取
- let info = trans.is_child && trans.original_workno ? await WorkPromise.getWorkPromise(trans.original_workno).info : await p.info;
- if(info.filesize) return info.filesize;
-
- throw new Error("无法获取文件大小信息");
- },
- }
-
- const DLsite = {
- parseWorkDOM: function (dom, rj) {
- // workInfo: {
- // rj: any;
- // img: string;
- // title: any;
- // circle: any;
- // date: any;
- // rating: any;
- // tags: any[];
- // cv: any;
- // filesize: any;
- // dateAnnounce: any;
- // }
- const workInfo = {};
- workInfo.rj = rj;
-
- let metaList = dom.getElementsByTagName("meta")
- for (let i = 0; i < metaList.length; i++){
- let meta = metaList[i];
- if(meta.getAttribute("property") === 'og:image'){
- workInfo.img = meta.content;
- break;
- }
- }
-
- workInfo.lang = dom.querySelector("html").getAttribute("lang");
- workInfo.title = dom.getElementById("work_name").innerText;
- workInfo.circle = dom.querySelector("span.maker_name").innerText;
- workInfo.circleId = dom.querySelector("#work_maker a").href;
- workInfo.circleId = workInfo.circleId.substring(workInfo.circleId.lastIndexOf("/") + 1, workInfo.circleId.lastIndexOf(".")).trim();
-
- const table_outline = dom.querySelector("table#work_outline");
- for (let i = 0, ii = table_outline.rows.length; i < ii; i++) {
- const row = table_outline.rows[i];
- const row_header = row.cells[0].innerText.trim();
- const row_data = row.cells[1];
- const lambda = text => row_header === text;
- switch (true) {
- case (["販売日", "贩卖日", "販賣日", "Release date", "판매일", "Lanzamiento", "Veröffentlicht",
- "Date de sortie", "Tanggal rilis", "Data di rilascio", "Lançamento", "Utgivningsdatum",
- "วันที่ขาย", "Ngày phát hành"].some(lambda)):
- workInfo.date = row_data.innerText.trim();
- break;
- case (["更新情報", "更新信息", "更新資訊", "Update information", "갱신 정보", "Actualizar información",
- "Aktualisierungen", "Mise à jour des informations", "Perbarui informasi", "Aggiorna informazioni",
- "Atualizar informações", "Uppdatera information", "ข้อมูลอัปเดต", "Thông tin cập nhật"].some(lambda)):
- workInfo.update = row_data.firstChild.data.trim();
- break;
- case (["年齢指定", "年龄指定", "年齡指定", "Age", "연령 지정", "Edad", "Altersfreigabe", "Âge", "Batas usia",
- "Età", "Idade", "Ålder", "การกำหนดอายุ", "Độ tuổi chỉ định"].some(lambda)):
- workInfo.rating = row_data.innerText.trim();
- break;
- case (["ジャンル", "分类", "分類", "Genre", "장르", "Género", "Genre", "Genre", "Genre", "Genere", "Gênero",
- "Genre", "ประเภท", "Thể loại"].some(lambda)):
- const tag_nodes = row_data.querySelectorAll("a");
- workInfo.tags = [...tag_nodes].map(a => { return a.innerText.trim() });
- break;
- case (["シナリオ", "Scenario", "剧情", "劇本", "시나리오", "Guión", "Szenario", "Scénario", "Skenario",
- "Scenario", "Cenário", "Scenario", "บทละคร", "Kịch bản"].some(lambda)):
- workInfo.scenario = row_data.innerText.trim();
- break;
- case (["イラスト", "Illustration", "插画", "插畫", "일러스트", "Ilustración", "AbbilDung", "Illustration",
- "Ilustrasi", "Illustrazione", "Ilustração", "Illustration", "ภาพประกอบ", "Tranh minh họa"].some(lambda)):
- workInfo.illustration = row_data.innerText.trim();
- break;
- case (["声優", "声优", "聲優", "Voice Actor", "성우", "Doblador", "Synchronsprecher", "Doubleur",
- "Pengisi suara", "Doppiatore/Doppiatrice", "Ator de voz", "Röstskådespelare", "นักพากย์",
- "Diễn viên lồng tiếng"].some(lambda)):
- workInfo.cv = row_data.innerText.trim();
- break;
- case (["音楽", "Music", "音乐", "音樂", "음악", "Música", "Musik", "Musique", "Musik", "Musica.",
- "Música", "musik", "ดนตรี", "Âm nhạc"].some(lambda)):
- workInfo.music = row_data.innerText.trim();
- break;
- case (["ファイル容量", "文件容量", "檔案容量", "File size", "파일 용량", "Tamaño del Archivo", "Dateigröße",
- "Taille du fichier", "Ukuran file", "Dimensione del file", "Tamanho do arquivo", "Filstorlek",
- "ขนาดไฟล์", "Dung lượng tệp"].some(lambda)):
- workInfo.filesize = row_data.innerText.trim();
- break;
- default:
- break;
- }
- }
-
- //获取发售预告时间
- const work_date_ana = dom.querySelector("strong.work_date_ana");
- if (work_date_ana) {
- workInfo.dateAnnounce = work_date_ana.innerText;
- //workInfo.img = "https://img.dlsite.jp/modpub/images2/ana/doujin/" + rj_group + "/" + rj + "_ana_img_main.jpg"
- }
-
- return workInfo;
- },
-
- // Get language code for DLSite API
- getLangCode: function (lang) {
- if(!lang) return "ja-JP";
-
- switch (lang.toUpperCase()) {
- case "JPN":
- return "ja-JP";
- case "ENG":
- return "en-US";
- case "KO_KR":
- return "ko-KR";
- case "CHI_HANS":
- return "zh-CN";
- case "CHI_HANT":
- return "zh-TW";
- default:
- return "ja-JP"
- }
- },
-
- parseApiData: function (rjCode, data){
- if(!data) data = {};
- let apiData = data;
- apiData.is_bonus = !data.is_sale && data.is_free && data.is_oly && data.wishlist_count === false;
- apiData.is_girls = (data.options && data.options.indexOf("OTM") >= 0) || (data.site_id === "girls");
-
- if(data.regist_date){
- let reg_date = data.regist_date.replace(/-/g, '/');
- let releaseDate = new Date(reg_date);
- apiData.regist_timestamp = releaseDate.getTime();
- apiData.regist_date = `${releaseDate.getFullYear()} / ${releaseDate.getMonth() + 1} / ${releaseDate.getDate()}`;
- if(apiData.regist_timestamp > Date.now()){
- apiData.is_announce = true;
- }
- }
- return apiData;
- },
-
- parseApi2Data: function (rjCode, data) {
- const translation_info = data.translation_info ? data.translation_info : {};
- data.lang = DLsite.getLangCode(translation_info.lang);
-
- if(data.regist_date){
- let reg_date = data.regist_date.replace(/-/g, '/');
- let releaseDate = new Date(reg_date);
- data.regist_timestamp = releaseDate.getTime();
- data.regist_date = `${releaseDate.getFullYear()} / ${releaseDate.getMonth() + 1} / ${releaseDate.getDate()}`;
- if(data.regist_timestamp > Date.now()){
- data.is_announce = true;
- }
- }
-
- return data;
- },
-
- getHttpAsync: async function (url, anonymous = false){
- return new Promise((resolve, reject) => {
- getXmlHttpRequest()({
- method: "GET",
- url,
- headers: {
- "Accept": "text/xml",
- "User-Agent": "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:67.0)",
- "Cache-Control": "no-cache"
- },
- onload: resolve,
- onerror: reject,
- anonymous: anonymous
- });
- })
- },
-
- getAnnouncePromise: async function (rjCode, parentRJ) {
- const url = `https://www.dlsite.com/maniax/announce/=/product_id/${rjCode}.html`;
- let resp = await DLsite.getHttpAsync(url);
- if (resp.readyState === 4 && resp.status === 200) {
- const dom = new DOMParser().parseFromString(Csp.createHTML(resp.responseText), "text/html");
- const workInfo = DLsite.parseWorkDOM(dom, rjCode);
- workInfo.parentWork = parentRJ === rjCode ? null : parentRJ;
- workInfo.is_announce = true;
- return workInfo;
- }
- else if (resp.readyState === 4 && resp.status === 404) {
- return {
- parentWork: parentRJ === rjCode ? null : parentRJ,
- is_announce: false
- };
- }
-
- },
-
- getHtmlPromise: async function (rjCode) {
- const url = `https://www.dlsite.com/maniax/work/=/product_id/${rjCode}.html`;
- let resp = await DLsite.getHttpAsync(url);
- if (resp.readyState === 4 && resp.status === 200) {
- const dom = new DOMParser().parseFromString(Csp.createHTML(resp.responseText), "text/html");
- const workInfo = DLsite.parseWorkDOM(dom, rjCode);
- workInfo.parentWork = DLsite.getParentWorkRjCode(resp.finalUrl);
- workInfo.parentWork = workInfo.parentWork === rjCode ? null : workInfo.parentWork;
- workInfo.is_announce = false;
- return workInfo;
- }
- else if (resp.readyState === 4 && resp.status === 404) {
- return await DLsite.getAnnouncePromise(rjCode, DLsite.getParentWorkRjCode(resp.finalUrl));
- }
- },
-
- getApi2Promise: async function (rjCode, locale = undefined) {
- let url = `https://www.dlsite.com/maniax/api/=/product.json?workno=${rjCode}` + (locale ? `&locale=${locale}` : "");
- let resp = await DLsite.getHttpAsync(url);
- let data;
- if (resp.readyState === 4 && resp.status === 200) {
- data = JSON.parse(resp.responseText);
- data = data ? data[0] : {};
- data = data ? data : {}
- }
- else if (resp.readyState === 4 && resp.status === 404) {
- return {};
- }
- else {
- throw new Error(`无法通过API2获取${rjCode}的信息:${resp.status} ${resp.statusText}`);
- }
-
- return DLsite.parseApi2Data(rjCode, data);
- },
-
- getApiPromise: async function (rjCode, locale = undefined) {
- //获取对应语言下的实际信息
- let url = `https://www.dlsite.com/maniax/product/info/ajax?product_id=${rjCode}&cdn_cache_min=1` + (locale ? `&locale=${locale}` : "");
- let resp = await DLsite.getHttpAsync(url);
- let data;
- if (resp.readyState === 4 && resp.status === 200) {
- data = JSON.parse(resp.responseText);
- data = data ? data[rjCode] : {};
- data = data ? data : {};
- }
- else if(resp.readyState === 4 && resp.status === 404){
- return {};
- }
- else {
- throw new Error(`无法通过API获取${rjCode}的信息:${resp.status} ${resp.statusText}`);
- }
-
- const translation_info = data.translation_info ? data.translation_info : {};
- data.lang = DLsite.getLangCode(translation_info.lang);
-
- return DLsite.parseApiData(rjCode, data);
- },
-
- getCirclePromise: async function (rjCode, apiPromise){
- let apiData = await apiPromise;
- if(!apiData.maker_id) return null;
- const maker_id = apiData.maker_id;
-
- let url;
- let resp;
- let data;
- try {
- url = `https://media.ci-en.jp/dlsite/lookup/${maker_id}.json`;
- resp = await DLsite.getHttpAsync(url);
- data = undefined;
- if (resp.readyState === 4 && resp.status === 200) {
- data = JSON.parse(resp.responseText);
- data = data ? data[0] : {};
- data = data ? data : {};
- data.maker_id = maker_id;
- }
- }catch (e){}
-
- if(!data || !data.name){
- //未获取到社团名称则使用html解析获取
- url = `https://www.dlsite.com/maniax/circle/profile/=/maker_id/${maker_id}.html`;
- resp = await DLsite.getHttpAsync(url);
- data = data ? data : {};
- if(resp.readyState === 4 && resp.status === 200){
- let doc = new DOMParser().parseFromString(Csp.createHTML(resp.responseText), "text/html");
- let name = doc.querySelector("strong.prof_maker_name");
- name = name ? name.innerText : null;
- data.name = name;
- }
- }
-
- return data;
- },
-
- getTranslatablePromise: async function (rjCode, site = "maniax") {
- rjCode = rjCode.toUpperCase();
- const result = {
- translation_request_english: {
- agree: undefined,
- request: undefined,
- sale: undefined
- },
- translation_request_simplified_chinese:{
- agree: undefined,
- request: undefined,
- sale: undefined
- },
- translation_request_traditional_chinese:{
- agree: undefined,
- request: undefined,
- sale: undefined
- },
- translation_request_korean: {
- agree: undefined,
- request: undefined,
- sale: undefined
- },
- translation_request_spanish: {
- agree: undefined,
- request: undefined,
- sale: undefined
- },
- translation_request_german: {
- agree: undefined,
- request: undefined,
- sale: undefined
- },
- translation_request_french: {
- agree: undefined,
- request: undefined,
- sale: undefined
- },
- translation_request_indonesian: {
- agree: undefined,
- request: undefined,
- sale: undefined
- },
- translation_request_italian: {
- agree: undefined,
- request: undefined,
- sale: undefined
- },
- translation_request_portuguese: {
- agree: undefined,
- request: undefined,
- sale: undefined
- },
- translation_request_swedish: {
- agree: undefined,
- request: undefined,
- sale: undefined
- },
- translation_request_thai: {
- agree: undefined,
- request: undefined,
- sale: undefined
- },
- translation_request_vietnamese: {
- agree: undefined,
- request: undefined,
- sale: undefined
- },
- };
- const data = await DLsite.getTranslatableApiPromise(rjCode, site);
- if(!data.translationStatusForTranslator){
- return result;
- }
-
- const map = {
- translation_request_english: "ENG",
- translation_request_simplified_chinese: "CHI_HANS",
- translation_request_traditional_chinese: "CHI_HANT",
- translation_request_korean: "KO_KR",
- translation_request_spanish: "SPA",
- translation_request_german: "GER",
- translation_request_french: "FRE",
- translation_request_indonesian: "IND",
- translation_request_italian: "ITA",
- translation_request_portuguese: "POR",
- translation_request_swedish: "SWE",
- translation_request_thai: "THA",
- translation_request_vietnamese: "VIE",
- };
- for (let key in map) {
- let lang = map[key];
- let status = data.translationStatusForTranslator[lang];
- if(!status){
- //状况未知
- continue;
- }
- result[key].agree = status.available;
- result[key].request = status.count;
- result[key].sale = status.on_sale_count;
- }
-
- return result;
- },
-
- getTranslatableApiPromise: async function (rjCode, site = "maniax") {
- //新的可用api,用于搜索作品翻译情况,但也可以获得其它信息。
- rjCode = rjCode.toUpperCase();
- let url = `https://www.dlsite.com/${site}/api/=/translatableProducts.json?keyword=${rjCode}`; //可以使用locale参数指定语言,但这里不需要
- let resp = await DLsite.getHttpAsync(url, true);
- let data;
- if (resp.readyState === 4 && resp.status === 200) {
- data = JSON.parse(resp.responseText);
- }
- else {
- throw new Error(`无法通过API获取${rjCode}的翻译信息:${resp.status} ${resp.statusText}`);
- }
-
- //从结果中找到对应RJ号,由于关键字是RJ号的话结果一般都在第一页,所以就放弃翻页寻找了
- if(data.meta && data.meta.code !== 200){
- throw new Error(`无法通过API查询${rjCode}的翻译信息:${data.meta.code} - ${data.meta.errorType} - ${data.meta.errorMessage}`);
- }
- if(!data.data || !Array.isArray(data.data.products)){
- throw new Error(`无法通过API查询${rjCode}的翻译信息:未预料到的响应格式。`);
- }
-
- for (const work of data.data.products) {
- if(work.id === rjCode){
- return work;
- }
- }
-
- //未找到则返回空对象
- return {};
-
- },
-
- getWorkRequestPromise: function (rjCode) {
- return {
- _info: undefined,
- _api: undefined,
- _api2: undefined,
- _circle: undefined,
- _translatable: undefined,
- _translated_title: undefined,
- get info(){
- return this._info ? this._info : this._info = DLsite.getHtmlPromise(rjCode);
- },
- get api() {
- return this._api ? this._api : this._api = DLsite.getApiPromise(rjCode);
- },
- get api2() {
- return this._api2 ? this._api2 : this._api2 = DLsite.getApi2Promise(rjCode);
- },
- get circle(){
- return this._circle ? this._circle : this._circle = DLsite.getCirclePromise(rjCode, this.api);
- },
- get translatable() {
- async function getter(t){
- let api = await t.api2;
- if(!api.site_id) api = await t.api;
-
- return t._translatable ? t._translatable : t._translatable = DLsite.getTranslatablePromise(rjCode,
- api.site_id ? api.site_id : "maniax");
- }
- return getter(this);
- },
- get translated_title(){
- async function getter(t){
- if(t._translated_title) return t._translated_title;
-
- let api = await t.api2;
- if(api.translation_info){
- //api2有效
- if(!api.translation_info.is_original) {
- //通过再次查询获得翻译标题
- api = await DLsite.getApi2Promise(rjCode, api.lang);
- }
- t._translated_title = api.work_name;
- return t._translated_title;
- }
-
- //api2无效,通过api查询
- api = await t.api;
- if(!api.translation_info){
- //api无效则无法获取标题(网页获取希望渺茫)
- t._translated_title = null;
- return null;
- }
-
- if(!api.translation_info.is_original) {
- //非原作则再次查询
- api = await DLsite.getApiPromise(rjCode, api.lang);
- }
- t._translated_title = api.work_name;
- return t._translated_title;
- }
-
- return getter(this);
- }
- }
- },
-
- getParentWorkRjCode: function (redirectUrl){
- const reg = new RegExp("(?<=product_id/)((R[JE][0-9]{8})|(R[JE][0-9]{6})|([VB]J[0-9]{8})|([VB]J[0-9]{6}))")
- return redirectUrl.match(reg)[0];
- }
- }
-
- function getSettingsUi() {
- return {
- //这一层是设置界面最顶层,编辑大标题信息
- title: localize(localizationMap.title_settings),
- items: [
- {
- //这一层是设置界面的大分类
- title: localize(localizationMap.title_language_settings),
- items: [
- {
- //这一层是设置项列表集合(使用表格呈现设置项)"
- items: [
- {
- //这一层是设置项
- type: "dropdown",
- title: localize(localizationMap.display_language),
- id: "lang",
- ignore_reset: true,
- options: [
- {
- title: "简体中文",
- value: "zh_CN"
- },
- {
- title: "繁體中文",
- value: "zh_TW"
- },
- {
- title: "English",
- value: "en_US"
- }
- ]
- },
- {
- //这一层是设置项
- type: "dropdown",
- title: localize(localizationMap.popup_language),
- id: "popup_lang",
- ignore_reset: true,
- tooltip: localize(localizationMap.popup_language_tooltip),
- options: [
- {
- title: "简体中文",
- value: "zh_CN"
- },
- {
- title: "繁體中文",
- value: "zh_TW"
- },
- {
- title: "English",
- value: "en_US"
- }
- ]
- }
- ]
- }
- ]
- },
-
- {
- //这一层是设置界面的大分类
- title: localize(localizationMap.title_general_settings),
- items: [
- {
- //这一层是设置项列表集合(使用表格呈现设置项)
- items: [
- {
- //解析URL
- type: "checkbox",
- title: localize(localizationMap.parse_url),
- id: "parse_url",
- tooltip: localize(localizationMap.parse_url_tooltip)
- },
- {
- //DL上解析URL
- binding: {
- target: "parse_url",
- value: true
- },
-
- type: "checkbox",
- title: localize(localizationMap.parse_url_in_dl),
- id: "parse_url_in_dl",
- indent: 1, //设置项缩进
- tooltip: localize(localizationMap.parse_url_in_dl_tooltip)
- },
- {
- //DL显示翻译标题
- type: "checkbox",
- title: localize(localizationMap.show_translated_title_in_dl),
- id: "show_translated_title_in_dl",
- tooltip: localize(localizationMap.show_translated_title_in_dl_tooltip)
- },
- {
- //“复制为有效文件名”按钮
- type: "checkbox",
- title: localize(localizationMap.copy_as_filename_btn),
- id: "copy_as_filename_btn",
- tooltip: localize(localizationMap.copy_as_filename_btn_tooltip)
- },
- {
- //**显示兼容性警告**
- type: "checkbox",
- title: `<strong>**${localize(localizationMap.show_compatibility_warning)}**</strong>`,
- id: "show_compatibility_warning",
- tooltip: localize(localizationMap.show_compatibility_warning_tooltip)
- },
- {
- //导向文本插入方式
- type: "dropdown",
- title: localize(localizationMap.url_insert_mode),
- id: "url_insert_mode",
- tooltip: localize(localizationMap.url_insert_mode_tooltip),
- options: [
- {
- title: localize(localizationMap.url_insert_mode_none),
- value: "none"
- },
- {
- title: localize(localizationMap.url_insert_mode_prefix),
- value: "prefix"
- },
- {
- title: localize(localizationMap.url_insert_mode_before_rj),
- value: "before_rj"
- }
- ]
- },
- {
- //导向文本
- type: "input",
- title: localize(localizationMap.url_insert_text),
- id: "url_insert_text",
- indent: 1
- },
-
- {
- //NSFW模式
- type: "checkbox",
- title: localize(localizationMap.sfw_mode),
- id: "sfw_mode",
- tooltip: localize(localizationMap.sfw_mode_tooltip)
- },
- {
- //模糊程度
- binding: {
- target: "sfw_mode",
- value: true
- },
-
- type: "dropdown",
- title: localize(localizationMap.sfw_blur_level),
- id: "sfw_blur_level",
- indent: 1,
- options: [
- {
- title: localize(localizationMap.low),
- value: "low"
- },
- {
- title: localize(localizationMap.medium),
- value: "medium"
- },
- {
- title: localize(localizationMap.high),
- value: "high"
- }
- ]
- },
- {
- //鼠标移至图片上方移除模糊
- binding: {
- target: "sfw_mode",
- value: true
- },
-
- type: "checkbox",
- title: localize(localizationMap.sfw_remove_when_hover),
- id: "sfw_remove_when_hover",
- indent: 1,
- },
- {
- //是否开启模糊动画
- binding: {
- target: "sfw_mode",
- value: true
- },
-
- type: "checkbox",
- title: localize(localizationMap.sfw_blur_transition),
- id: "sfw_blur_transition",
- indent: 1,
- }
- ]
- }
- ]
- },
- {
- //分类:信息显示
- title: localize(localizationMap.title_info_settings),
- items: [
- {
- //预设表格
- items: [
- {
- type: "dropdown",
- title: localize(localizationMap.category_preset),
- id: "category_preset",
- tooltip: localize(localizationMap.category_preset_tooltip),
- ignore_reset: true, //不显示重置按钮
- options: [
- {
- title: localize(localizationMap.work_type_voice),
- value: "voice"
- },
- {
- title: localize(localizationMap.work_type_game),
- value: "game"
- },
- {
- title: `${localize(localizationMap.work_type_comic)} / ${localize(localizationMap.work_type_illustration)} / ${localize(localizationMap.work_type_voice_comic)}`,
- value: "manga"
- },
- {
- title: localize(localizationMap.work_type_video),
- value: "video"
- },
- {
- title: localize(localizationMap.work_type_novel),
- value: "novel"
- },
- {
- title: localize(localizationMap.work_type_other),
- value: "other"
- }
- ]
- }
- ]
- },
- {
- //音声Preset对应的表格,注意使用Binding来决定表格是否显示
- binding: {
- target: "category_preset",
- value: "voice"
- },
- sortable: true, //为true则代表该表格内的行可以排序
- sort_id: "voice__info_display_order", //若排序则一定要指定id,该id将会存储在设置项中,作为列表记录每个元素的顺序
- items: [
- {
- //销量
- type: "checkbox",
- title: localize(localizationMap.dl_count),
- id: "voice__dl_count", //注意这里不同的preset要改成不同的值
- },
- {
- //社团名
- type: "checkbox",
- title: localize(localizationMap.circle_name),
- id: "voice__circle_name", //注意这里不同的preset要改成不同的值
- },
- {
- //翻译者
- type: "checkbox",
- title: localize(localizationMap.translator_name),
- id: "voice__translator_name",
- },
- {
- //发售日
- type: "checkbox",
- title: localize(localizationMap.release_date),
- id: "voice__release_date",
- },
- {
- //更新日
- type: "checkbox",
- title: localize(localizationMap.update_date),
- id: "voice__update_date",
- },
- {
- //年龄指定
- type: "checkbox",
- title: localize(localizationMap.age_rating),
- id: "voice__age_rating",
- },
- {
- //剧情作者
- type: "checkbox",
- title: localize(localizationMap.scenario),
- id: "voice__scenario",
- },
- {
- //插画作者
- type: "checkbox",
- title: localize(localizationMap.illustration),
- id: "voice__illustration",
- },
- {
- //配音者
- type: "checkbox",
- title: localize(localizationMap.voice_actor),
- id: "voice__voice_actor",
- },
- {
- //音乐作者
- type: "checkbox",
- title: localize(localizationMap.music),
- id: "voice__music",
- },
- {
- //作品标签/分类
- type: "checkbox",
- title: localize(localizationMap.genre),
- id: "voice__genre",
- },
- {
- //文件大小
- type: "checkbox",
- title: localize(localizationMap.file_size),
- id: "voice__file_size",
- }
- ]
- },
- {
- //游戏Preset对应的表格
- binding: {
- target: "category_preset",
- value: "game"
- },
- sortable: true, //为true则代表该表格内的行可以排序
- sort_id: "game__info_display_order",
- items: [
- {
- //销量
- type: "checkbox",
- title: localize(localizationMap.dl_count),
- id: "game__dl_count",
- },
- {
- //社团名
- type: "checkbox",
- title: localize(localizationMap.circle_name),
- id: "game__circle_name",
- },
- {
- //翻译者
- type: "checkbox",
- title: localize(localizationMap.translator_name),
- id: "game__translator_name",
- },
- {
- //发售日
- type: "checkbox",
- title: localize(localizationMap.release_date),
- id: "game__release_date",
- },
- {
- //更新日
- type: "checkbox",
- title: localize(localizationMap.update_date),
- id: "game__update_date",
- },
- {
- //年龄指定
- type: "checkbox",
- title: localize(localizationMap.age_rating),
- id: "game__age_rating",
- },
- {
- //剧情作者
- type: "checkbox",
- title: localize(localizationMap.scenario),
- id: "game__scenario",
- },
- {
- //插画作者
- type: "checkbox",
- title: localize(localizationMap.illustration),
- id: "game__illustration",
- },
- {
- //配音者
- type: "checkbox",
- title: localize(localizationMap.voice_actor),
- id: "game__voice_actor",
- },
- {
- //音乐作者
- type: "checkbox",
- title: localize(localizationMap.music),
- id: "game__music",
- },
- {
- //作品标签/分类
- type: "checkbox",
- title: localize(localizationMap.genre),
- id: "game__genre",
- },
- {
- //文件大小
- type: "checkbox",
- title: localize(localizationMap.file_size),
- id: "game__file_size",
- }
- ]
- },
- {
- //漫画对应preset
- binding: {
- target: "category_preset",
- value: "manga"
- },
- sortable: true, //为true则代表该表格内的行可以排序
- sort_id: "manga__info_display_order",
- items: [
- {
- //销量
- type: "checkbox",
- title: localize(localizationMap.dl_count),
- id: "manga__dl_count",
- },
- {
- //社团名
- type: "checkbox",
- title: localize(localizationMap.circle_name),
- id: "manga__circle_name",
- },
- {
- //翻译者
- type: "checkbox",
- title: localize(localizationMap.translator_name),
- id: "manga__translator_name",
- },
- {
- //发售日
- type: "checkbox",
- title: localize(localizationMap.release_date),
- id: "manga__release_date",
- },
- {
- //更新日
- type: "checkbox",
- title: localize(localizationMap.update_date),
- id: "manga__update_date",
- },
- {
- //年龄指定
- type: "checkbox",
- title: localize(localizationMap.age_rating),
- id: "manga__age_rating",
- },
- {
- //剧情作者
- type: "checkbox",
- title: localize(localizationMap.scenario),
- id: "manga__scenario",
- },
- {
- //插画作者
- type: "checkbox",
- title: localize(localizationMap.illustration),
- id: "manga__illustration",
- },
- {
- //配音者
- type: "checkbox",
- title: localize(localizationMap.voice_actor),
- id: "manga__voice_actor",
- tooltip: localize(localizationMap.work_type_voice_comic)
- },
- {
- //音乐作者
- type: "checkbox",
- title: localize(localizationMap.music),
- id: "manga__music",
- },
- {
- //作品标签/分类
- type: "checkbox",
- title: localize(localizationMap.genre),
- id: "manga__genre",
- },
- {
- //文件大小
- type: "checkbox",
- title: localize(localizationMap.file_size),
- id: "manga__file_size",
- }
- ]
- },
- {
- //视频对应preset
- binding: {
- target: "category_preset",
- value: "video"
- },
- sortable: true, //为true则代表该表格内的行可以排序
- sort_id: "video__info_display_order",
- items: [
- {
- //销量
- type: "checkbox",
- title: localize(localizationMap.dl_count),
- id: "video__dl_count",
- },
- {
- //社团名
- type: "checkbox",
- title: localize(localizationMap.circle_name),
- id: "video__circle_name",
- },
- {
- //翻译者
- type: "checkbox",
- title: localize(localizationMap.translator_name),
- id: "video__translator_name",
- },
- {
- //发售日
- type: "checkbox",
- title: localize(localizationMap.release_date),
- id: "video__release_date",
- },
- {
- //更新日
- type: "checkbox",
- title: localize(localizationMap.update_date),
- id: "video__update_date",
- },
- {
- //年龄指定
- type: "checkbox",
- title: localize(localizationMap.age_rating),
- id: "video__age_rating",
- },
- {
- //剧情作者
- type: "checkbox",
- title: localize(localizationMap.scenario),
- id: "video__scenario",
- },
- {
- //插画作者
- type: "checkbox",
- title: localize(localizationMap.illustration),
- id: "video__illustration",
- },
- {
- //配音者
- type: "checkbox",
- title: localize(localizationMap.voice_actor),
- id: "video__voice_actor",
- },
- {
- //音乐作者
- type: "checkbox",
- title: localize(localizationMap.music),
- id: "video__music",
- },
- {
- //作品标签/分类
- type: "checkbox",
- title: localize(localizationMap.genre),
- id: "video__genre",
- },
- {
- //文件大小
- type: "checkbox",
- title: localize(localizationMap.file_size),
- id: "video__file_size",
- }
- ]
- },
- {
- //小说对应Preset
- binding: {
- target: "category_preset",
- value: "novel"
- },
- sortable: true, //为true则代表该表格内的行可以排序
- sort_id: "novel__info_display_order",
- items: [
- {
- //销量
- type: "checkbox",
- title: localize(localizationMap.dl_count),
- id: "novel__dl_count",
- },
- {
- //社团名
- type: "checkbox",
- title: localize(localizationMap.circle_name),
- id: "novel__circle_name",
- },
- {
- //翻译者
- type: "checkbox",
- title: localize(localizationMap.translator_name),
- id: "novel__translator_name",
- },
- {
- //发售日
- type: "checkbox",
- title: localize(localizationMap.release_date),
- id: "novel__release_date",
- },
- {
- //更新日
- type: "checkbox",
- title: localize(localizationMap.update_date),
- id: "novel__update_date",
- },
- {
- //年龄指定
- type: "checkbox",
- title: localize(localizationMap.age_rating),
- id: "novel__age_rating",
- },
- {
- //剧情作者
- type: "checkbox",
- title: localize(localizationMap.scenario),
- id: "novel__scenario",
- },
- {
- //插画作者
- type: "checkbox",
- title: localize(localizationMap.illustration),
- id: "novel__illustration",
- },
- {
- //配音者
- type: "checkbox",
- title: localize(localizationMap.voice_actor),
- id: "novel__voice_actor",
- },
- {
- //音乐作者
- type: "checkbox",
- title: localize(localizationMap.music),
- id: "novel__music",
- },
- {
- //作品标签/分类
- type: "checkbox",
- title: localize(localizationMap.genre),
- id: "novel__genre",
- },
- {
- //文件大小
- type: "checkbox",
- title: localize(localizationMap.file_size),
- id: "novel__file_size",
- }
- ]
- },
- {
- //其他对应Preset
- binding: {
- target: "category_preset",
- value: "other"
- },
- sortable: true, //为true则代表该表格内的行可以排序
- sort_id: "other__info_display_order",
- items: [
- {
- //销量
- type: "checkbox",
- title: localize(localizationMap.dl_count),
- id: "other__dl_count",
- },
- {
- //社团名
- type: "checkbox",
- title: localize(localizationMap.circle_name),
- id: "other__circle_name",
- },
- {
- //翻译者
- type: "checkbox",
- title: localize(localizationMap.translator_name),
- id: "other__translator_name",
- },
- {
- //发售日
- type: "checkbox",
- title: localize(localizationMap.release_date),
- id: "other__release_date",
- },
- {
- //更新日
- type: "checkbox",
- title: localize(localizationMap.update_date),
- id: "other__update_date",
- },
- {
- //年龄指定
- type: "checkbox",
- title: localize(localizationMap.age_rating),
- id: "other__age_rating",
- },
- {
- //剧情作者
- type: "checkbox",
- title: localize(localizationMap.scenario),
- id: "other__scenario",
- },
- {
- //插画作者
- type: "checkbox",
- title: localize(localizationMap.illustration),
- id: "other__illustration",
- },
- {
- //配音者
- type: "checkbox",
- title: localize(localizationMap.voice_actor),
- id: "other__voice_actor",
- },
- {
- //音乐作者
- type: "checkbox",
- title: localize(localizationMap.music),
- id: "other__music",
- },
- {
- //作品标签/分类
- type: "checkbox",
- title: localize(localizationMap.genre),
- id: "other__genre",
- },
- {
- //文件大小
- type: "checkbox",
- title: localize(localizationMap.file_size),
- id: "other__file_size",
- }
- ]
- }
- ]
- },
- {
- //分类:标签显示
- title: localize(localizationMap.title_tag_settings),
- items: [
- {
- //总开关表格
- items: [
- {
- type: "checkbox",
- title: localize(localizationMap.tag_main_switch),
- tooltip: localize(localizationMap.tag_main_switch_tooltip),
- id: "tag_main_switch"
- },
- {
- //显示评分人数
- type: "checkbox",
- title: localize(localizationMap.show_rate_count),
- id: "show_rate_count",
- indent: 1
- }
- ]
- },
- {
- //标签开关表格
- binding: {
- target: "tag_main_switch",
- value: true
- },
- items: [
- {
- //所有的标签开关集合
- type: "tag_switch",
- //标签之间可以排序
- sortable: true,
- sort_id: "tag_display_order",
- items: [
- {
- //销量
- title: localize(localizationMap.rate),
- id: "tag_rate",
- class: "tag-yellow",
- tooltip: localize(localizationMap.rate_tooltip)
- },
- {
- //作品类型
- title: localize(localizationMap.tag_work_type),
- id: "tag_work_type",
- class: "tag-darkblue",
- tooltip: `
- <div class="${VOICELINK_CLASS}_tags">
- <span class="${VOICELINK_CLASS}_tag-purple">${localize(localizationMap.work_type_game)}</span>
- <span class="${VOICELINK_CLASS}_tag-green">${localize(localizationMap.work_type_comic)}</span>
- <span class="${VOICELINK_CLASS}_tag-teal">${localize(localizationMap.work_type_illustration)}</span>
- <span class="${VOICELINK_CLASS}_tag-gray">${localize(localizationMap.work_type_novel)}</span>
- <span class="${VOICELINK_CLASS}_tag-darkblue">${localize(localizationMap.work_type_video)}</span>
- <span class="${VOICELINK_CLASS}_tag-orange">${localize(localizationMap.work_type_voice)}</span>
- <span class="${VOICELINK_CLASS}_tag-yellow">${localize(localizationMap.work_type_music)}</span>
- <span class="${VOICELINK_CLASS}_tag-gray">${localize(localizationMap.work_type_tool)}</span>
- <span class="${VOICELINK_CLASS}_tag-blue">${localize(localizationMap.work_type_voice_comic)}</span>
- <span class="${VOICELINK_CLASS}_tag-gray">${localize(localizationMap.work_type_other)}</span>
- </div>`,
- },
- {
- //可翻译
- title: localize(localizationMap.tag_translatable),
- id: "tag_translatable",
- class: "tag-green",
- tooltip: localize(localizationMap.tag_translatable_tooltip)
- },
- {
- //不可翻译
- title: localize(localizationMap.tag_not_translatable),
- id: "tag_not_translatable",
- class: "tag-red",
- tooltip: localize(localizationMap.tag_not_translatable_tooltip)
- },
- {
- //翻译作品
- title: localize(localizationMap.tag_translated),
- id: "tag_translated",
- class: "tag-teal",
- tooltip: localize(localizationMap.tag_translated_tooltip)
- },
- {
- //支持语言
- title: localize(localizationMap.tag_language_support),
- id: "tag_language_support",
- class: "tag-pink",
- tooltip: `
- <div class="${VOICELINK_CLASS}_tags">
- <span class="${VOICELINK_CLASS}_tag-pink">${localize(localizationMap.language_japanese)}</span>
- <span class="${VOICELINK_CLASS}_tag-pink">${localize(localizationMap.language_simplified_chinese)}</span>
- <span class="${VOICELINK_CLASS}_tag-pink">${localize(localizationMap.language_traditional_chinese)}</span>
- <span class="${VOICELINK_CLASS}_tag-pink">${localize(localizationMap.language_english)}</span>
- <span class="${VOICELINK_CLASS}_tag-pink">${localize(localizationMap.language_korean)}</span>
- <span class="${VOICELINK_CLASS}_tag-pink">${localize(localizationMap.language_german)}</span>
- <span class="${VOICELINK_CLASS}_tag-pink">${localize(localizationMap.language_french)}</span>
- <span class="${VOICELINK_CLASS}_tag-pink">${localize(localizationMap.language_indonesian)}</span>
- <span class="${VOICELINK_CLASS}_tag-pink">${localize(localizationMap.language_italian)}</span>
- <span class="${VOICELINK_CLASS}_tag-pink">${localize(localizationMap.language_portuguese)}</span>
- <span class="${VOICELINK_CLASS}_tag-pink">${localize(localizationMap.language_swedish)}</span>
- <span class="${VOICELINK_CLASS}_tag-pink">${localize(localizationMap.language_thai)}</span>
- <span class="${VOICELINK_CLASS}_tag-pink">${localize(localizationMap.language_vietnamese)}</span>
- </div>
- `
- },
- {
- //特典作品
- title: localize(localizationMap.tag_bonus_work),
- id: "tag_bonus_work",
- class: "tag-yellow",
- tooltip: localize(localizationMap.tag_bonus_work_tooltip)
- },
- {
- //含特典
- title: localize(localizationMap.tag_has_bonus),
- id: "tag_has_bonus",
- class: "tag-orange",
- tooltip: localize(localizationMap.tag_has_bonus_tooltip)
- },
- {
- //文件格式
- title: localize(localizationMap.tag_file_format),
- id: "tag_file_format",
- class: "tag-darkblue",
- tooltip: localize(localizationMap.tag_file_format_tooltip)
- },
- {
- //已下架
- title: localize(localizationMap.tag_no_longer_available),
- id: "tag_no_longer_available",
- class: "tag-gray",
- },
- {
- //AI & 部分AI
- title: localize(localizationMap.tag_ai),
- id: "tag_ai",
- class: "tag-purple",
- tooltip: localize(localizationMap.tag_ai_tooltip)
- }
- ]
- },
- ]
- },
- {
- //翻译情况显示表格
- items: [
- {
- //翻译情况显示开关
- type: "checkbox",
- title: localize(localizationMap.tag_translation_request),
- id: "tag_translation_request",
- tooltip: localize(localizationMap.tag_translation_request_tooltip)
- },
- ]
- },
- {
- //翻译情况标签显示表格
- binding: {
- target: "tag_translation_request",
- value: true
- },
- items: [
- {
- //各种翻译情况显示
- type: "tag_switch",
- sortable: true,
- sort_id: "tag_translation_request_display_order",
- items: [
- {
- //英语
- title: `${localize(localizationMap.language_english_abbr)} 1-1`,
- id: "tag_translation_request_english",
- class: "tag-orange",
- tooltip: localize(localizationMap.language_english)
- },
- {
- //简体中文
- title: `${localize(localizationMap.language_simplified_chinese_abbr)} 1-1`,
- id: "tag_translation_request_simplified_chinese",
- class: "tag-orange",
- tooltip: localize(localizationMap.language_simplified_chinese)
- },
- {
- //繁体中文
- title: `${localize(localizationMap.language_traditional_chinese_abbr)} 1-1`,
- id: "tag_translation_request_traditional_chinese",
- class: "tag-orange",
- tooltip: localize(localizationMap.language_traditional_chinese)
- },
- {
- //韩语
- title: `${localize(localizationMap.language_korean_abbr)} 1-1`,
- id: "tag_translation_request_korean",
- class: "tag-orange",
- tooltip: localize(localizationMap.language_korean)
- },
- {
- //西班牙语
- title: `${localize(localizationMap.language_spanish_abbr)} 1-1`,
- id: "tag_translation_request_spanish",
- class: "tag-orange",
- tooltip: localize(localizationMap.language_spanish)
- },
- {
- //德语
- title: `${localize(localizationMap.language_german_abbr)} 1-1`,
- id: "tag_translation_request_german",
- class: "tag-orange",
- tooltip: localize(localizationMap.language_german)
- },
- {
- //法语
- title: `${localize(localizationMap.language_french_abbr)} 1-1`,
- id: "tag_translation_request_french",
- class: "tag-orange",
- tooltip: localize(localizationMap.language_french)
- },
- {
- //印尼语
- title: `${localize(localizationMap.language_indonesian_abbr)} 1-1`,
- id: "tag_translation_request_indonesian",
- class: "tag-orange",
- tooltip: localize(localizationMap.language_indonesian)
- },
- {
- //意大利语
- title: `${localize(localizationMap.language_italian_abbr)} 1-1`,
- id: "tag_translation_request_italian",
- class: "tag-orange",
- tooltip: localize(localizationMap.language_italian)
- },
- {
- //葡萄牙语
- title: `${localize(localizationMap.language_portuguese_abbr)} 1-1`,
- id: "tag_translation_request_portuguese",
- class: "tag-orange",
- tooltip: localize(localizationMap.language_portuguese)
- },
- {
- //瑞典语
- title: `${localize(localizationMap.language_swedish_abbr)} 1-1`,
- id: "tag_translation_request_swedish",
- class: "tag-orange",
- tooltip: localize(localizationMap.language_swedish)
- },
- {
- //泰语
- title: `${localize(localizationMap.language_thai_abbr)} 1-1`,
- id: "tag_translation_request_thai",
- class: "tag-orange",
- tooltip: localize(localizationMap.language_thai)
- },
- {
- //越南语
- title: `${localize(localizationMap.language_vietnamese_abbr)} 1-1`,
- id: "tag_translation_request_vietnamese",
- class: "tag-orange",
- tooltip: localize(localizationMap.language_vietnamese)
- }
- ]
- }
- ]
- }
- ]
- }
- ]
- };
- }
- class SettingPageBuilder {
- constructor(structure, settings) {
- this.structure = structure;
- this.settings = settings;
- this.container = null;
- }
-
- getClass(name) {
- if(!VOICELINK_CLASS || VOICELINK_CLASS === "") return name;
- return `${VOICELINK_CLASS}_${name}`;
- }
-
- build(useTemp = false) {
- const f = this.structure;
- let tempSettings = this.settings;
-
- //若未要求则清空设置暂存
- if(useTemp){
- tempSettings = {};
- for(let key in this.settings.temp_edited){
- if(!key.startsWith("_s_")) continue;
- tempSettings[key] = this.settings.temp_edited[key];
- }
- }else{
- this.settings.clearTemp();
- }
-
- //创建container
- const container = document.createElement("div");
- container.className = this.getClass("container");
- container.id = this.getClass("settings-container");
- this.container = container;
-
- //创建关闭按钮
- const closeButton = document.createElement("button");
- closeButton.id = this.getClass("button-close");
- closeButton.innerText = "X";
- closeButton.onclick = () => {
- container.remove();
- };
- container.appendChild(closeButton);
-
- //创建标题
- const title = document.createElement("h1");
- title.innerText = f.title;
- container.appendChild(title);
-
- //遍历构建Section
- for(const section of f.items){
- container.appendChild(this.buildSection(section));
- }
-
- //添加保存、重置按钮
- const buttonContainer = document.createElement("div");
- buttonContainer.className = this.getClass("button-container");
- const saveButton = document.createElement("button");
- saveButton.id = this.getClass("button-save");
- saveButton.innerText = localize(localizationMap.button_save);
- saveButton.onclick = () => {
- try{
- this.settings.save();
- window.alert(localize(localizationMap.save_complete))
- container.remove();
- }catch (e){
- window.alert(e);
- }
- };
- const resetButton = document.createElement("button");
- resetButton.id = this.getClass("button-reset");
- resetButton.innerText = localize(localizationMap.button_reset);
- resetButton.onclick = () => {
- if(!window.confirm(localize(localizationMap.reset_confirm))){
- return;
- }
- try{
- // this.settings.reset();
- window.alert(localize(localizationMap.reset_complete))
- }catch (e) {
- window.alert(e);
- }
- this.refreshSettings(this.settings.default_backup);
- };
- buttonContainer.appendChild(saveButton);
- buttonContainer.appendChild(resetButton);
- container.appendChild(buttonContainer);
-
- this.initSortableElement();
- this.refreshSettings(tempSettings);
- return container;
- };
-
- buildSection(section){
- //创建容器
- const container = document.createElement("div");
- container.className = this.getClass("section-container");
-
- //创建标题
- const title = document.createElement("h2");
- title.innerText = section.title;
- container.appendChild(title);
-
- //遍历构建Table
- for(const item of section.items){
- container.appendChild(this.buildTable(item, container));
- }
-
- return container;
- };
-
- buildTable(table, section){
- //创建table
- const tableElement = document.createElement("table");
-
- //创建可能存在的preset绑定
- this.createBinding(table, tableElement, section);
-
- //遍历构建Row(先把row缓存在列表里,经过排序后构建)
- let rowList = [];
- const nodesCache = document.createElement("div");
- for(const row of table.items){
- let rowElement = this.buildRow(row, nodesCache);
- if(table.sortable){
- this.setSortable(rowElement);
- }
- rowList.push(rowElement);
-
- //存入缓存用于绑定
- nodesCache.appendChild(rowElement);
- }
-
- //重置按钮行
- let resetRow;
- if(table.sortable){
- tableElement.setAttribute("data-sort-id", table.sort_id);
- this.sortSortable(rowList, table.sort_id);
-
- //若可排序则还要添加重置按钮
- if(table.ignore_reset !== true){
- resetRow = document.createElement("tr");
- const resetCell = document.createElement("td");
- resetCell.classList.add(this.getClass("input-cell"))
- resetCell.colSpan = 2;
- resetCell.style.setProperty("text-align", "right", "important"); //textAlign = "right !important";
- const resetButton = document.createElement("button");
- resetButton.classList.add(this.getClass("button-flat"));
- resetButton.title = localize(localizationMap.reset_order);
- resetButton.style.setProperty("margin-right", "0", "important"); //marginRight = "0 !important";
- resetButton.style.setProperty("margin-bottom", "0", "important"); //marginBottom = "0 !important";
- const icon = document.createElement("span");
- icon.classList.add(this.getClass("reset-btn-small"));
- resetButton.appendChild(icon);
- const title = document.createElement("span");
- title.innerText = localize(localizationMap.reset_order);
- resetButton.appendChild(title);
- resetButton.onclick = () => {
- if(!window.confirm(localize(localizationMap.reset_order_confirm))){
- return;
- }
- this.reorderSortable(table.sort_id, this.settings.getDefaultValue(table.sort_id));
- // tableElement.insertBefore(resetRow, tableElement.firstChild);
- };
- resetCell.appendChild(resetButton);
- resetRow.appendChild(resetCell);
- // tableElement.appendChild(resetRow);
- }
- }
-
- rowList.forEach((row) => {
- tableElement.appendChild(row);
- });
- if(resetRow){
- tableElement.appendChild(resetRow);
- }
-
- return tableElement;
- };
-
- buildRow(row, table){
- //创建row
- let rowElement = document.createElement("tr");
-
- //根据类型创建内容
- switch (row.type) {
- case "checkbox":
- rowElement = this.createToggleRow(row);
- break;
- case "dropdown":
- rowElement = this.createDropdownRow(row);
- break;
- case "input":
- rowElement = this.createInputRow(row);
- break;
- case "tag_switch":
- rowElement = this.createTagSwitchRow(row);
- break;
- default:
- console.error(`Unknown row type: ${row.type}`);
- break;
- }
-
- //设置Binding
- if(row.binding){
- this.createBinding(row, rowElement, table)
- }
-
- rowElement.classList.add(this.getClass("setting"));
- if(row.id){
- rowElement.setAttribute("data-id", row.id);
- }
- return rowElement;
- };
-
- createToggleRow(row){
- //创建Row
- const rowElement = document.createElement("tr");
- if(row.indent) {
- rowElement.classList.add(this.getClass(`indent-${row.indent}`));
- }
-
- //创建设置项标题
- const titleCell = document.createElement("td");
- titleCell.className = this.getClass("tooltip");
- const title = document.createElement("span");
- title.className = this.getClass("row-title") + " " + this.getClass("ignore-drag");
- title.innerHTML = Csp.createHTML(row.title);
- titleCell.appendChild(title);
-
- if(row.tooltip) {
- const tooltip = document.createElement("span");
- tooltip.className = this.getClass("tooltip-text");
- tooltip.innerHTML = Csp.createHTML(row.tooltip);
- titleCell.appendChild(tooltip);
- }
-
- //创建开关和重置按钮
- const settingId = `_s_${row.id}`;
- const inputCell = document.createElement("td");
- inputCell.classList.add(this.getClass("input-cell"));
- const inputContainer = document.createElement("div");
- inputContainer.classList.add(this.getClass("toggle-container"));
- inputCell.appendChild(inputContainer);
-
- //创建开关
- const input = document.createElement("input");
- input.type = "checkbox";
- input.id = this.getClass(row.id);
- input.name = input.id;
- // inputCell.appendChild(input);
- inputContainer.appendChild(input);
-
- const label = document.createElement("label");
- label.classList.add(this.getClass("toggle"));
- label.setAttribute("for", input.id);
- const hidden = document.createElement("span");
- hidden.classList.add(this.getClass("hidden"));
- hidden.innerHTML = Csp.createHTML(row.title);
- label.appendChild(hidden);
- // inputCell.appendChild(label);
- inputContainer.appendChild(label);
-
- //创建重置按钮
- if(row.ignore_reset !== true){
- const defaultValue = this.settings.getDefaultValue(settingId);
- const resetButton = document.createElement("button");
- resetButton.className = this.getClass("reset-btn-small") + " " + this.getClass("ignore-drag");
- resetButton.title = localize(localizationMap.button_reset);
- resetButton.onclick = () => {
- input.checked = defaultValue === true;
- input.dispatchEvent(new Event("change"));
- };
- // inputCell.insertBefore(resetButton, inputCell.firstChild);
- inputContainer.insertBefore(resetButton, inputContainer.firstChild);
-
- input.addEventListener("change", () => {
- //决定是否显示重置按钮
- resetButton.style.setProperty("display", input.checked === defaultValue ? "none" : "inline-block", "important"); //display = input.checked === defaultValue ? "none" : "inline-block";
- });
- }
-
- //监听器都创建好了再设置初始值
- input.addEventListener("change", () => {
- //更新到暂存设置
- this.settings.saveTemp(settingId, input.checked);
- })
- input.checked = this.settings[settingId] === true;
- input.dispatchEvent(new Event("change"));
-
- rowElement.appendChild(titleCell);
- rowElement.appendChild(inputCell);
- return rowElement;
- };
-
- createDropdownRow(row){
- const rowElement = document.createElement("tr");
- if(row.indent) {
- rowElement.classList.add(this.getClass(`indent-${row.indent}`));
- }
-
- const titleCell = document.createElement("td");
- titleCell.className = this.getClass("tooltip");
- const label = document.createElement("label");
- label.className = this.getClass("row-title") + " " + this.getClass("ignore-drag");
- label.setAttribute("for", this.getClass(row.id));
- label.innerHTML = Csp.createHTML(row.title);
- titleCell.appendChild(label);
-
- if(row.tooltip) {
- const tooltip = document.createElement("span");
- tooltip.className = this.getClass("tooltip-text");
- tooltip.innerHTML = Csp.createHTML(row.tooltip);
- titleCell.appendChild(tooltip);
- }
-
- const inputCell = document.createElement("td");
- inputCell.classList.add(this.getClass("input-cell"));
- const select = document.createElement("select");
- select.className = this.getClass("ignore-drag");
- select.id = this.getClass(row.id);
- select.name = select.id;
- for(const item of row.options){
- const option = document.createElement("option");
- option.value = item.value;
- option.innerText = item.title;
- select.appendChild(option);
- }
- inputCell.appendChild(select);
-
- //创建重置按钮
- const settingId = `_s_${row.id}`;
- if(row.ignore_reset !== true){
- const defaultValue = this.settings.getDefaultValue(settingId);
- const resetButton = document.createElement("button");
- resetButton.className = this.getClass("reset-btn-small") + " " + this.getClass("ignore-drag");
- resetButton.title = localize(localizationMap.button_reset);
- resetButton.onclick = () => {
- select.value = defaultValue;
- select.dispatchEvent(new Event("change"));
- };
- inputCell.insertBefore(resetButton, inputCell.firstChild);
-
- select.addEventListener("change", () => {
- //决定是否显示重置按钮
- resetButton.style.setProperty("display", select.value === defaultValue ? "none" : "inline-block", "important"); //display = select.value === defaultValue ? "none" : "inline-block";
- });
- }
-
- //监听器都创建好了再设置初始值
- select.addEventListener("change", () => {
- //更新到暂存设置
- this.settings.saveTemp(settingId, select.value);
- })
- select.value = this.settings[settingId];
- select.dispatchEvent(new Event("change"));
-
- rowElement.appendChild(titleCell);
- rowElement.appendChild(inputCell);
- return rowElement;
- };
-
- createInputRow(row){
- const rowElement = document.createElement("tr");
- if(row.indent) {
- rowElement.classList.add(this.getClass(`indent-${row.indent}`));
- }
-
- const titleCell = document.createElement("td");
- titleCell.className = this.getClass("tooltip");
- const label = document.createElement("label");
- label.className = this.getClass("row-title") + " " + this.getClass("ignore-drag");
- label.setAttribute("for", this.getClass(row.id));
- label.innerHTML = Csp.createHTML(row.title);
- titleCell.appendChild(label);
-
- if(row.tooltip) {
- const tooltip = document.createElement("span");
- tooltip.className = this.getClass("tooltip-text");
- tooltip.innerHTML = Csp.createHTML(row.tooltip);
- titleCell.appendChild(tooltip);
- }
-
- const inputCell = document.createElement("td");
- inputCell.classList.add(this.getClass("input-cell"));
- const input = document.createElement("input");
- input.type = "text";
- input.id = this.getClass(row.id);
- input.name = input.id;
- inputCell.appendChild(input);
-
- //创建重置按钮
- const settingId = `_s_${row.id}`;
- if(row.ignore_reset !== true){
- const defaultValue = this.settings.getDefaultValue(settingId);
- const resetButton = document.createElement("button");
- resetButton.className = this.getClass("reset-btn-small") + " " + this.getClass("ignore-drag");
- resetButton.title = localize(localizationMap.button_reset);
- resetButton.onclick = () => {
- input.value = defaultValue;
- input.dispatchEvent(new Event("change"));
- };
- inputCell.insertBefore(resetButton, inputCell.firstChild);
-
- input.addEventListener("change", () => {
- //决定是否显示重置按钮
- resetButton.style.setProperty("display", input.value === defaultValue ? "none" : "inline-block", "important"); //display = input.value === defaultValue ? "none" : "inline-block";
- });
- }
-
- //监听器都创建好了再设置初始值
- input.addEventListener("change", () => {
- //更新到暂存设置
- this.settings.saveTemp(settingId, input.value);
- })
- input.value = this.settings[settingId];
- input.dispatchEvent(new Event("change"));
-
- rowElement.appendChild(titleCell);
- rowElement.appendChild(inputCell);
- return rowElement;
- };
-
- createTagSwitchRow(row){
- const rowElement = document.createElement("tr");
- if(row.indent) {
- rowElement.classList.add(this.getClass(`indent-${row.indent}`));
- }
-
- const tagCell = document.createElement("td");
- tagCell.colSpan = 2;
- //用内部容器再次包裹标签,保证重置按钮的位置
- const tagContainer = document.createElement("div");
- tagContainer.className = this.getClass("tags");
- tagCell.appendChild(tagContainer);
-
- const tagList = [];
- for(const tag of row.items){
- const tagSpan = document.createElement("label");
- tagSpan.classList.add(this.getClass(tag["class"]));
- tagSpan.innerText = tag.title;
- tagSpan.setAttribute("for", this.getClass(tag.id));
- tagSpan.setAttribute("data-id", tag.id);
- this.setSortable(tagSpan);
-
- //添加switch
- const settingId = `_s_${tag.id}`;
- const switchInput = document.createElement("input");
- switchInput.style.setProperty("display", "none", "important"); //display = "none !important";
- switchInput.type = "checkbox";
- switchInput.id = this.getClass(tag.id);
- switchInput.name = switchInput.id;
- switchInput.addEventListener("change", (event) => {
- if(event.target.checked) {
- tagSpan.classList.remove(this.getClass("tag-off"));
- }else if(!tagSpan.classList.contains(this.getClass("tag-off"))) {
- tagSpan.classList.add(this.getClass("tag-off"));
- }
-
- //更新到暂存设置
- this.settings.saveTemp(settingId, switchInput.checked);
- })
- switchInput.checked = this.settings[`_s_${tag.id}`] === true;
- switchInput.dispatchEvent(new Event("change"));
-
- tagSpan.classList.toggle(this.getClass("tag-off"), !switchInput.checked);
- tagSpan.appendChild(switchInput);
-
- if(tag.tooltip) {
- tagSpan.classList.add(this.getClass("tooltip"));
- const tooltip = document.createElement("span");
- tooltip.className = this.getClass("tooltip-text");
- tooltip.innerHTML = Csp.createHTML(tag.tooltip);
- tagSpan.appendChild(tooltip);
- }
-
- tagList.push(tagSpan);
- }
-
- if(row.sortable){
- tagContainer.setAttribute("data-sort-id", row.sort_id);
- this.sortSortable(tagList, row.sort_id);
-
- //添加排列重置按钮
- if(row.ignore_reset !== true){
- const resetOrderButton = document.createElement("button");
- resetOrderButton.classList.add(this.getClass("button-flat"));
- resetOrderButton.title = localize(localizationMap.button_reset);
- resetOrderButton.onclick = () => {
- if(!window.confirm(localize(localizationMap.reset_order_confirm))){
- return;
- }
- this.reorderSortable(row.sort_id, this.settings.getDefaultValue(row.sort_id));
- };
-
- const icon = document.createElement("span");
- icon.classList.add(this.getClass("reset-btn-small"));
- resetOrderButton.appendChild(icon);
-
- const title = document.createElement("span");
- title.innerText = localize(localizationMap.reset_order);
- resetOrderButton.appendChild(title);
-
- tagCell.appendChild(resetOrderButton);
- // tagCell.insertBefore(resetOrderButton, tagCell.firstChild);
- }
- }
-
- //添加设置重置按钮
- if(row.ignore_reset !== true){
- const resetSettingsButton = document.createElement("button");
- resetSettingsButton.classList.add(this.getClass("button-flat"));
- resetSettingsButton.title = localize(localizationMap.button_reset);
- resetSettingsButton.onclick = () => {
- if(!window.confirm(localize(localizationMap.reset_confirm))){
- return;
- }
- for (const item of row.items) {
- let tag = tagContainer.querySelector("#" + this.getClass(item.id));
- if(!tag) continue;
- tag.checked = this.settings.getDefaultValue(item.id);
- tag.dispatchEvent(new Event("change"));
- }
- };
-
- const icon = document.createElement("span");
- icon.classList.add(this.getClass("reset-btn-small"));
- resetSettingsButton.appendChild(icon);
-
- const title = document.createElement("span");
- title.innerText = localize(localizationMap.button_reset);
- resetSettingsButton.appendChild(title);
-
- tagCell.appendChild(resetSettingsButton);
- // tagCell.insertBefore(resetOrderButton, tagCell.firstChild);
- }
-
-
- tagList.forEach((tag) => {
- tagContainer.appendChild(tag);
- });
- rowElement.appendChild(tagCell);
- return rowElement;
- };
-
- createBinding(data, element, section){
- if(!data.binding || !this.container) return;
-
- const binding = data.binding;
- const target = section.querySelector("#" + this.getClass(binding.target));
- if(!target) return;
-
- let showState = "unset";
- switch (element.nodeName){
- case "TABLE":
- showState = "table";
- break;
- case "TR":
- showState = "table-row";
- break;
- }
-
- target.addEventListener("change", (event) => {
- const value = event.target.value;
- const checked = event.target.checked;
- element.style.setProperty("display", value === binding.value || checked === binding.value ? showState : "none", "important"); //display = value === binding.value || checked === binding.value ? showState : "none";
- });
-
- element.style.setProperty("display", target.value === binding.value || target.checked === binding.value ? showState : "none", "important"); //display = target.value === binding.value || target.checked === binding.value ? showState : "none";
- };
-
- sortSortable(sortList, sortId, orderList){
- orderList = orderList ? orderList : this.settings[`_s_${sortId}`];
- if(!orderList) return;
-
- for (let i = 0; i < orderList.length; ++i) {
- for (let j = 0; j < sortList.length; ++j){
- if(orderList[i] === sortList[j].getAttribute("data-id")) {
- let tmp = sortList[i];
- sortList[i] = sortList[j];
- sortList[j] = tmp;
- break;
- }
- }
- }
- };
-
- setSortable(ele){
- ele.classList.add(this.getClass("sortable"));
- };
-
- refreshSettings(settings) {
- //刷新设置项值的展示情况(保持input值和设置中的实际值一致)
- //清空设置暂存
- this.settings.clearTemp();
- settings = settings ? settings : this.settings;
-
- //刷新设置项
- for(const key in settings){
- if(!key.startsWith("_s_")) continue;
- const targetId = this.getClass(key.substring(3));
-
- if(settings[key] && Array.isArray(settings[key])){
- //可能是排序对象,先搜索排序目标,搜不到再按默认值处理
- if(this.reorderSortable(key.substring(3), settings[key])) continue;
- }
-
- //非排序对象
- const target = this.container.querySelector("#" + targetId);
- if(!target) continue;
-
- if(target.tagName === "INPUT") {
- if(target.type === "checkbox") {
- target.checked = settings[key] === true;
- }else{
- target.value = settings[key];
- }
- }else if(target.tagName === "SELECT") {
- target.value = settings[key];
- }
-
- if(key === "_s_lang") continue;
- target.dispatchEvent(new Event("change"));
- }
- };
-
- reorderSortable(sort_id, orderList) {
- const target = this.container.querySelector(`*[data-sort-id="${sort_id}"]`);
- if(target) {
- let list = [...target.children];
- this.sortSortable(list, sort_id, orderList);
-
- for(let i = 0; i < list.length; ++i) {
- target.removeChild(list[i]);
- }
-
- for(let i = 0; i < list.length; ++i) {
- target.appendChild(list[i]);
- }
-
- this.settings.saveTemp(sort_id, list.map(ele => ele.getAttribute("data-id")).filter(val => val && val !== ""));
- return true;
- }
- return false;
- }
-
- initSortableElement() {
- //初始化排序组件
- const sortableGroups = this.container.querySelectorAll(`.${this.getClass('sortable')}`);
- sortableGroups.forEach(group => {
- new Sortable(group, group.parentElement.getAttribute("data-sort-id"), this.settings);
- });
- };
-
- }
- class Sortable {
- constructor(element, sort_id, settings, options) {
- this.element = element;
- this.options = options;
- this.sortId = sort_id;
- this.settings = settings;
- this.dragging = null;
-
- if(!sort_id) {
- throw new Error("sort_id is required");
- }
-
- this.init();
- }
-
- getClass(name) {
- if(!VOICELINK_CLASS || VOICELINK_CLASS === "") return name;
- return `${VOICELINK_CLASS}_${name}`;
- }
-
- init() {
- this.element.addEventListener('mousedown', this.onMouseDown.bind(this));
- document.addEventListener('mousemove', this.onMouseMove.bind(this));
- document.addEventListener('mouseup', this.onMouseUp.bind(this));
- }
-
- onMouseDown(event) {
- if(event.target.nodeName === "INPUT" || event.target.classList.contains(this.getClass("toggle")) || event.target.classList.contains(this.getClass("ignore-drag"))) return;
- if (event.target.closest(`.${this.getClass('sortable')}`)) {
- this.dragging = event.target.closest(`.${this.getClass('sortable')}`);
- this.dragging.classList.add(this.getClass('dragging'));
- }
- }
-
- onMouseMove(event) {
- if (this.dragging) {
- event.preventDefault();
-
- try{
- let sibling = document.elementFromPoint(event.clientX, event.clientY).closest(`.${this.getClass('sortable')}`);
- if (sibling && sibling !== this.dragging) {
- this.element.parentNode.insertBefore(this.dragging, sibling.nextSibling === this.dragging ? sibling : sibling.nextSibling);
- //暂存当前顺序
- const order = [...this.element.parentNode.children].map(item => item.getAttribute("data-id")).filter(item => item || item === "");
- this.settings.saveTemp(this.sortId, order);
- }
- }catch (e) {
- console.error(e)
- }
-
- }
- }
-
- onMouseUp(event) {
- if (this.dragging) {
- this.dragging.classList.remove(this.getClass('dragging'));
- this.dragging = null;
- }
- }
- }
- const SettingsPopup = {
- showPopup(useTemp = false) {
- let uiBuilder = new SettingPageBuilder(getSettingsUi(), settings);
- let ui = uiBuilder.build(useTemp);
- const displayLangElement = ui.querySelector(`#${VOICELINK_CLASS}_lang`);
- displayLangElement.addEventListener("change", e => {
- settings._s_lang = displayLangElement.value;
- GM_setValue("lang", settings._s_lang);
- ui.remove();
- SettingsPopup.showPopup(true);
- });
- document.body.appendChild(ui);
- }
- };
-
- let isInit = false;
- let observing = false;
- function init () {
- if(!isInit) {
- const style = document.createElement("style");
- style.innerHTML = Csp.createHTML(POPUP_CSS + SETTINGS_CSS);
- document.head.appendChild(style);
- // SettingsPopup.getPopup()
- GM_registerMenuCommand("Settings - 设置", () => SettingsPopup.showPopup())
- GM_registerMenuCommand("Notice - 公告", () => showUpdateNotice(true));
- GM_registerMenuCommand("Home / Update - 主页 / 更新", () =>
- GM_openInTab(IS_PREVIEW ? "https://sleazyfork.org/zh-CN/scripts/521128-voicelinks-preview" : "https://sleazyfork.org/zh-CN/scripts/456775-voicelinks", {active: true}));
-
- document.addEventListener("securitypolicyviolation", function (e) {
- if (e.blockedURI.includes("img.dlsite.jp")) {
- const img = document.querySelector(`img[src="${e.blockedURI}"]`);
- img.remove();
-
- const imgContainer = Popup.popupElement.img.container;
- if(imgContainer){
- imgContainer.style.setProperty("display", "none", "important"); //display = "none !important";
- Popup.hideImg = true;
- }
- }
- });
-
- isInit = true;
- }
-
- if(!document.body || observing){
- return;
- }
-
- Parser.walkNodes(document.body);
- if(!document.getElementById(`${VOICELINK_CLASS}-voice-popup`)) Popup.makePopup(false);
-
- const observer = new MutationObserver(function (m) {
- for (let i = 0; i < m.length; ++i) {
- let addedNodes = m[i].addedNodes;
- let removedNodes = m[i].removedNodes;
-
- for (let j = 0; j < removedNodes.length; ++j){
- let node = removedNodes[j];
- }
-
- for (let j = 0; j < addedNodes.length; ++j) {
- Parser.walkNodes(addedNodes[j]);
- }
- }
- });
-
- observer.observe(document.body, { childList: true, subtree: true})
- setUserSelectTitle();
-
- //显示重要通知
- showUpdateNotice();
-
- observing = true;
- }
-
- document.addEventListener("DOMContentLoaded", init);
-
- function showUpdateNotice(force = false) {
- const firstTimeToken = 105;
- if(GM_getValue("first_token", undefined) === firstTimeToken && !force){
- return;
- }
- GM_setValue("first_token", firstTimeToken);
-
- if(!force && window.confirm(localize(localizationMap.notice_update)) === false) {
- return;
- }
-
- GM_openInTab(
- IS_PREVIEW ? `https://github.com/IceFoxy062/VoiceLinks-Extend/blob/dev/docs/major_updates/v4.8.6/v4.8.6-${settings._s_lang}.md` : `https://github.com/IceFoxy062/VoiceLinks-Extend/blob/dev/docs/major_updates/v4.8.6/v4.8.6-${settings._s_lang}.md`,
- {active: true});
- }
-
- //Deal with Trusted Types
- let Csp = {
- createHTML: (str) => str
- };
- if(window.isSecureContext === true && trustedTypes){
- Csp = trustedTypes.createPolicy(
- trustedTypes.defaultPolicy ? "VoiceLinkTrustedTypes" : "VoiceLinkTrustedTypes",
- Csp);
- }
-
- init();
- })();