- // ==UserScript==
- // @name ETTHelper-TagEditer
- // @name:zh-CN E绅士标签翻译辅助工具-标签编辑
- // @namespace EhTagTranslation
- // @description Help to edit the gallery's tags.
- // @description:zh-CN 辅助编辑E绅士画廊的标签
- // @include *://exhentai.org/g/*
- // @include *://e-hentai.org/g/*
- // @version 1.4.6
- // @author Mapaler <mapaler@163.com>
- // @copyright 2019+, Mapaler <mapaler@163.com>
- // @homepage https://github.com/EhTagTranslation/UserScripts
- // @supportURL https://github.com/EhTagTranslation/UserScripts/issues
- // @grant GM_registerMenuCommand
- // @grant GM_getValue
- // @grant GM_setValue
- // @grant GM_deleteValue
- // ==/UserScript==
-
- const scriptVersion = (defaultVersion=>typeof GM_info != "undefined" ? GM_info.script.version.replace(/(^\s*)|(\s*$)/g, "") : defaultVersion)("unknown"); //本程序的版本
- const scriptName = (defaultName=>{
- if (typeof(GM_info)!="undefined")
- {
- if (GM_info.script.name_i18n)
- {
- const i18n = navigator.language.replace("-","_"); //获取浏览器语言
- return GM_info.script.name_i18n[i18n]; //支持Tampermonkey
- }
- else
- {
- return GM_info.script.localizedName || //支持Greasemonkey 油猴子 3.x
- GM_info.script.name; //支持Violentmonkey 暴力猴
- }
- }else
- {
- return defaultName;
- }
- })("ETTWikiHelper-TagEditer"); //本程序的名称
-
- //限定数值最大最小值
- function limitMaxAndMin(num,max,min)
- {
- return Math.max(Math.min(num, max), min);
- }
-
- //默认CSS内容
- const ewh_tag_styleText_Default = `
- /* fallback */
- @font-face {
- font-family: 'Material Icons';
- font-style: normal;
- font-weight: 400;
- src: url(https://fonts.gstatic.com/s/materialicons/v90/flUhRq6tzZclQEJ-Vdg-IuiaDsNc.woff2) format('woff2');
- }
-
- .material-icons {
- font-family: 'Material Icons';
- font-weight: normal;
- font-style: normal;
- line-height: 1;
- letter-spacing: normal;
- text-transform: none;
- display: inline-block;
- white-space: nowrap;
- word-wrap: normal;
- direction: ltr;
- -moz-font-feature-settings: 'liga';
- -moz-osx-font-smoothing: grayscale;
- }
-
- #gd4.ewh-float { /*浮动窗体*/
- position: fixed;
- top: 10%;
- left: 10%;
- background-color : inherit;
- margin: 0 !important;
- padding: 0 !important;
- border-style: ridge;
- border-width: 3px;
- border-color: #eee black black #eee;
- opacity: 0.8;
- }
- .ewh-bar-floatcaption { /*标题栏整体*/
- height: 22px;
- position: relative;
- }
- .ewh-cpttext-box { /*标题栏文字框*/
- width: 100%;
- height: 100%;
- float: left;
- color: white;
- line-height: 22px;
- font-size: 14px;
- background-image: linear-gradient(to right,#808080,#B7B5BB);
- }
- .ewh-float .ewh-cpttext-box { /*浮动时的标题颜色*/
- background-image: linear-gradient(to right,#000280,#0F80CD);
- }
- .ewh-cpttext-box::before{ /*标题图标*/
- content: "🏷️";
- }
- .ewh-cpttext-box span { /*标题文字*/
- pointer-events:none;
- user-select: none;
- }
- .ewh-cptbtn-box { /*标题按钮框*/
- height: 100%;
- position: absolute;
- top: 0;
- right: 8px;
- line-height: 22px;
- }
- .ewh-cpt-btn { /*平时的按钮*/
- vertical-align: middle;
- padding: 0;
- font-size: 14px;
- margin-top:-2px;
- height: 18px;
- width: 20px;
- background-color: #c0c0c0;
- border-style: outset;
- border-width: 2px;
- border-color: white black black white;
- }
- .ewh-cpt-rag { /*平时的范围拖动条*/
- vertical-align: middle;
- padding: 0;
- font-size: 14px;
- margin-top:0;
- height: 18px;
- width: 100px;
- }
- .ewh-cpt-btn:active { /*按钮按下时的凹陷*/
- background-color: #d8d8d8;
- padding-left: 1px !important;
- padding-top: 1px !important;
- border-style: inset;
- border-color: black white white black;
- }
- .ewh-cpt-btn:focus { /*激活后的虚线*/
- outline: dotted 1px black;
- }
- .ewh-btn-closefloat,.ewh-rag-opacity { /*平时隐藏关闭浮动的按钮*/
- display: none;
- }
- .ewh-float .ewh-btn-closefloat,.ewh-float .ewh-rag-opacity { /*浮动时显示关闭浮动的按钮*/
- display: unset;
- }
- .ewh-float .ewh-btn-openfloat{ /*浮动时隐藏开启浮动的按钮*/
- display: none;
- }
- .ewh-bar-tagsearch{
- position: relative;
- }
- .ewh-ipt-tagsearch{
- width: 200px;
- box-sizing: border-box;
- }
- .ewh-tagsearchtext,.ewh-tagsearchlink{
- font-size: 10pt;
- }
- .ewh-bar-tagsearch a::before{
- font-size: 10pt;
- font-weight: bold;
- }
- .ewh-bar-tagsearch a::after{
- font-size: 10pt;
- background: #c0c0c0;
- color:black;
- border-style: ridge;
- border-width: 3px;
- border-color: #eee black black #eee;
- position:absolute;
- z-index:999;
- padding:8px;
- min-width:150px;
- max-width:500px;
- white-space:pre-wrap;
- opacity: 0;
- transition: opacity 0.1s;
- top:28px;
- left:45%;
- pointer-events:none;
- font-weight: 400;
- line-height: 20px;
- }
- .ewh-bar-tagsearch a:hover::after{
- opacity: 1;
- }
- `;
- //获取Tag编辑区
- var ewhWindow = document.querySelector("#gd4");
-
- //增加浮动窗标题栏
- var divCaptionBar = ewhWindow.insertBefore(document.createElement("div"),gd4.firstChild);
- divCaptionBar.className = "ewh-bar-floatcaption";
-
- //生成辅助器CSS
- var ewh_tag_style = divCaptionBar.appendChild(document.createElement("style"));
- ewh_tag_style.type = "text/css";
- ewh_tag_style.appendChild(document.createTextNode(ewh_tag_styleText_Default));
-
- //生成标题栏文字
- var divCaption = divCaptionBar.appendChild(document.createElement("div"));
- divCaption.className = "ewh-cpttext-box";
- divCaption.appendChild(document.createElement("span")).appendChild(document.createTextNode(scriptName));
-
- //添加窗体鼠标拖拽移动
- var windowPosition = ewhWindow.position = [0, 0]; //[X,Y] 用以储存窗体开始拖动时的鼠标相对窗口坐标差值。
- divCaption.addEventListener("mousedown", function(e) { //按下鼠标则添加移动事件
- if (!ewhWindow.classList.contains("ewh-float")) return; //如果不是浮动窗体直接结束
- var eX = limitMaxAndMin(e.clientX,document.documentElement.clientWidth,0), eY = limitMaxAndMin(e.clientY,document.documentElement.clientHeight,0); //不允许鼠标超出网页。
- windowPosition[0] = eX - ewhWindow.offsetLeft;
- windowPosition[1] = eY - ewhWindow.offsetTop;
- var handler_mousemove = function(e) { //移动鼠标则修改窗体坐标
- var eX = limitMaxAndMin(e.clientX,document.documentElement.clientWidth,0), eY = limitMaxAndMin(e.clientY,document.documentElement.clientHeight,0); //不允许鼠标超出网页。
- ewhWindow.style.left = (eX - windowPosition[0]) + 'px';
- ewhWindow.style.top = (eY - windowPosition[1]) + 'px';
- };
- var handler_mouseup = function(e) { //抬起鼠标则取消移动事件
- document.removeEventListener("mousemove", handler_mousemove);
- if (ewhWindow.style.left) GM_setValue("floatwindow-left",ewhWindow.style.left); //储存窗体位置
- if (ewhWindow.style.top) GM_setValue("floatwindow-top",ewhWindow.style.top); //储存窗体位置
-
- };
- document.addEventListener("mousemove", handler_mousemove);
- document.addEventListener("mouseup", handler_mouseup, { once: true });
- });
-
- //生成标题栏按钮
- var divButtonBox = divCaptionBar.appendChild(document.createElement("div"));
- divButtonBox.className = "ewh-cptbtn-box";
-
- //生成修改设置的按钮
- var ragOpacity = divButtonBox.appendChild(document.createElement("input"));
- ragOpacity.className = "ewh-cpt-rag ewh-rag-opacity";
- ragOpacity.type = "range";
- ragOpacity.max = 1;
- ragOpacity.min = 0.5;
- ragOpacity.step = 0.01;
- ragOpacity.title = "窗体不透明度";
- ragOpacity.onchange = ragOpacity.oninput = function(){
- ewhWindow.style.opacity = this.value;
- };
- ragOpacity.onchange = function(){
- ragOpacity.oninput();
- if (ewhWindow.style.opacity) GM_setValue("floatwindow-opacity",ewhWindow.style.opacity); //储存窗体透明度
- };
-
- //生成打开浮动状态的按钮
- var btnOpenFloat = divButtonBox.appendChild(document.createElement("button"));
- btnOpenFloat.className = "ewh-cpt-btn material-icons ewh-btn-openfloat";
- btnOpenFloat.title = "浮动标签编辑框";
- btnOpenFloat.appendChild(document.createElement("span").appendChild(document.createTextNode("open_in_new")));
- btnOpenFloat.onclick = function(){
- //ewhWindow.setAttribute("style",ewhWindow.getAttribute("style_bak"));
- //ewhWindow.removeAttribute("style_bak");
- ewhWindow.classList.add("ewh-float");
- ewhWindow.style.left = GM_getValue("floatwindow-left");
- ewhWindow.style.top = GM_getValue("floatwindow-top");
- ewhWindow.style.opacity = ragOpacity.value = GM_getValue("floatwindow-opacity") || 0.8;
- };
- GM_registerMenuCommand("打开浮动标签编辑框", btnOpenFloat.onclick);
-
- //生成关闭浮动状态的按钮
- var btnCloseFloat = divButtonBox.appendChild(document.createElement("button"));
- btnCloseFloat.className = "ewh-cpt-btn material-icons ewh-btn-closefloat";
- btnCloseFloat.title = "关闭浮动窗体";
- btnCloseFloat.appendChild(document.createElement("span").appendChild(document.createTextNode("close")));
- btnCloseFloat.onclick = function(){
- //ewhWindow.setAttribute("style_bak",ewhWindow.getAttribute("style"));
- if (ewhWindow.style.left) GM_setValue("floatwindow-left",ewhWindow.style.left); //储存窗体位置
- if (ewhWindow.style.top) GM_setValue("floatwindow-top",ewhWindow.style.top); //储存窗体位置
- if (ewhWindow.style.opacity) GM_setValue("floatwindow-opacity",ewhWindow.style.opacity); //储存窗体透明度
- ewhWindow.removeAttribute("style");
- ewhWindow.classList.remove("ewh-float");
- };
- GM_registerMenuCommand("重置浮动窗位置与透明度", function(){
- btnCloseFloat.onclick(); //先关掉窗体,然后删除设置
- GM_deleteValue("floatwindow-left");
- GM_deleteValue("floatwindow-top");
- GM_deleteValue("floatwindow-opacity");
- });
-
- var nameSpaceC = {
- artist:"艺术家",
- female:"女性",
- male:"男性",
- parody:"原作",
- character:"角色",
- group:"团队",
- language:"语言",
- reclass:"重新分类",
- misc:"杂项"
- };
- //获取标签数据列表
- var tagdatalist = document.querySelector("#tbs-tags");
- var tagData;
- //获取真实标签输入框
- var newTagText = document.querySelector("#newtagfield");
- if (!tagdatalist) //没有ETS,但有ETS扩展版的处理方式
- {
- var tagDataStr = localStorage.getItem("EhSyringe.tag-list"); //ETS扩展版1.2.1的数据
- if (typeof(tagDataStr) == "string")
- {
- tagData = JSON.parse(tagDataStr);
- tagdatalist = document.createElement("datalist");
- tagdatalist.id = "tbs-tags";
- newTagText.setAttribute("list","tbs-tags");
- tagData.forEach(function(tag){
- tagdatalist.appendChild(new Option(nameSpaceC[tag.namespace]+":"+tag.name,tag.search));
- })
- newTagText.insertAdjacentElement('afterend',tagdatalist);
- }
- }
- if (tagdatalist) //如果存在则生成标签搜索框
- {
- var taglist = tagdatalist.options;
- //增加标签搜索框箱子
- var divSearchBar = ewhWindow.insertBefore(document.createElement("div"),document.querySelector("#tagmenu_act"));
- divSearchBar.className = "ewh-bar-tagsearch";
-
- //增加标签搜索框
- var iptTagSearch = divSearchBar.appendChild(document.createElement("input"));
- iptTagSearch.type = "text";
- iptTagSearch.placeholder = "🔍标签搜索:回车附加到下方▼";
- iptTagSearch.setAttribute("list","tbs-tags");
- iptTagSearch.className="ewh-ipt-tagsearch";
- //增加标签搜索提醒文字
- var spnTagSearchInfo = divSearchBar.appendChild(document.createElement("span"));
- spnTagSearchInfo.className="ewh-tagsearchtext";
- //增加标签搜索提醒标签
- var aTagSearchInfo = divSearchBar.appendChild(document.createElement("a"));
- aTagSearchInfo.className="ewh-tagsearchlink";
-
- iptTagSearch.onkeypress = function(e){
- if(e.key == "Enter"){ //回车,将内容附加到真实Tag框,并清空搜索框
- var _this = this;
- if (_this.value.length == 0)
- { //如果什么都没输入,相当于提交
- spnTagSearchInfo.innerHTML = "";
- aTagSearchInfo.removeAttribute("id");
- aTagSearchInfo.innerHTML = "";
- if (newTagText.value.length > 0)tag_from_field(); //如果输入框有内容点击Tag提交
- return;
- };
-
- var clabel = false, useGuess = false, guess = false;
- var tagC;
-
- if (tagData) //如果有JSON数据,直接使用
- { //扩展版的JSON数据
- var searchRes = tagData.filter(function(tag){ //搜索绝对等于的
- return tag.search == _this.value;
- });
- if (searchRes.length<1)
- { //猜测式搜索
- searchRes = tagData.filter(function(tag){
- return tag.name.indexOf(_this.value)>=0 || tag.key.indexOf(_this.value)>=0; //有非tag字符时才搜索中文,其他时候搜索key
- });
- if (searchRes.length>0)
- {
- guess = true; //标记为猜的
- _this.value = searchRes[0].search; //目前的输入修改为猜的tag
- }
- }
- if (searchRes.length>0)
- {
- tagC = searchRes[0];
- clabel = tagC.name;
- }
- }else
- { //脚本版的数据
- if (_this.value.replace(/[\w\:\"\s\-\.\'\$]/,"").length>0) useGuess = true; //如果存在非tag字符,则尝试搜索中文。
- for (var ti=0;ti<taglist.length;ti++)
- { //循环搜索列表中是否已存在这个Tag
- if (taglist[ti].value == _this.value)
- {
- clabel = taglist[ti].label;
- break;
- }else if(useGuess && taglist[ti].label.indexOf(_this.value)>0)
- {
- clabel = taglist[ti].label;
- guess = true; //标记为猜的
- _this.value = taglist[ti].value; //目前的输入修改为猜的tag
- break;
- }
- }
- }
- if (clabel)
- {
- var shortTag;
- if (tagData)
- { //扩展版的JSON数据
- shortTag = (tagC.namespace=="misc"?"":(tagC.namespace.substr(0,1) + ":")) + tagC.key; //缩减Tag长度,以便一次能多提交一些Tag
- }else
- { //脚本版的数据
- var regArr = /^(\w+):"?([\w+\s\-\'\.]+)\$?"?$/ig.exec(_this.value);
- shortTag = (regArr[1]=="misc"?"":(regArr[1].substr(0,1) + ":")) + regArr[2]; //缩减Tag长度,以便一次能多提交一些Tag
- }
- if ((newTagText.value+","+shortTag).length>200)
- {
- spnTagSearchInfo.innerHTML = "⛔超长(原始标签输入框限定200字符)";
- if (!tagData) aTagSearchInfo.removeAttribute("id");
- aTagSearchInfo.innerHTML = "";
- }else
- {
- newTagText.value = (newTagText.value.length>0)?(newTagText.value+","+shortTag):shortTag;
- spnTagSearchInfo.innerHTML = (guess?"程序猜测你想添加":"你添加了")+" " + (tagData?nameSpaceC[tagC.namespace]:clabel.split(":")[0]) + ":";
- if (!tagData) aTagSearchInfo.id = "ta_" + (regArr[1]=="misc"?"":regArr[1]+":") + regArr[2].replace(/\s/igm,"_");
- aTagSearchInfo.innerHTML = clabel;
- _this.value = "";
- }
- }else
- {
- spnTagSearchInfo.innerHTML = "☹️数据库里没有这个标签";
- if (!tagData) aTagSearchInfo.removeAttribute("id");
- aTagSearchInfo.innerHTML = "";
- }
- }
- };
- }