EhTagSyringe

Build EhTagTranslater from Wiki.

As of 2017-09-12. See the latest version.

  1. // ==UserScript==
  2. // @name EhTagSyringe
  3. // @name:zh-CN E绅士翻译注射器?
  4. // @namespace http://www.mapaler.com/
  5. // @description Build EhTagTranslater from Wiki.
  6. // @description:zh-CN 从Wiki获取EhTagTranslater数据库,将E绅士TAG翻译为中文,并注射到E站
  7. // @include *://github.com/Mapaler/EhTagTranslator*
  8. // @include *://exhentai.org/*
  9. // @include *://e-hentai.org/*
  10. // @connect raw.githubusercontent.com
  11. // @connect github.com
  12. // @icon http://exhentai.org/favicon.ico
  13. // @require https://cdn.bootcss.com/angular.js/1.4.6/angular.min.js
  14. // @resource template https://raw.githubusercontent.com/Mapaler/EhTagTranslator/master/template/ets-builder-menu.html?v=10
  15. // @resource ets-prompt https://raw.githubusercontent.com/Mapaler/EhTagTranslator/master/template/ets-prompt.html?v=16
  16. // @version 1.0.0
  17. // @run-at document-start
  18. // @grant unsafeWindow
  19. // @grant GM_xmlhttpRequest
  20. // @grant GM_getValue
  21. // @grant GM_setValue
  22. // @grant GM_addStyle
  23. // @grant GM_deleteValue
  24. // @grant GM_listValues
  25. // @grant GM_info
  26. // @grant GM_getResourceText
  27. // @grant GM_addValueChangeListener
  28. // @grant GM_setClipboard
  29. // @copyright 2017+, Mapaler <mapaler@163.com> , xioxin <i@xioxin.com>
  30. // ==/UserScript==
  31.  
  32.  
  33.  
  34. (function() {
  35. 'use strict';
  36.  
  37. window.requestAnimationFrame = unsafeWindow.requestAnimationFrame;
  38. unsafeWindow.wikiUpdate = autoUpdate;
  39.  
  40. var wiki_URL="https://github.com/Mapaler/EhTagTranslator/wiki"; //GitHub wiki 的地址
  41. var wiki_raw_URL="https://raw.githubusercontent.com/wiki/Mapaler/EhTagTranslator"; //GitHub wiki 的地址
  42. var rows_title="rows"; //行名的地址
  43. var pluginVersion = typeof(GM_info)!="undefined" ? GM_info.script.version.replace(/(^\s*)|(\s*$)/g, "") : "未获取到版本"; //本程序的版本
  44. var pluginName = typeof(GM_info)!="undefined" ? (GM_info.script.localizedName ? GM_info.script.localizedName : GM_info.script.name) : "EhTagSyringe"; //本程序的名称
  45. var rootScope = null;
  46.  
  47.  
  48. const headLoaded = new Promise(function (resolve, reject) {
  49. if(unsafeWindow.document.head && unsafeWindow.document.head.nodeName == "HEAD"){
  50. resolve(unsafeWindow.document.head);
  51. }else{
  52. //监听DOM变化
  53. MutationObserver = window.MutationObserver;
  54. var observer = new MutationObserver(function(mutations) {
  55. for(let i in mutations){
  56. let mutation = mutations[i];
  57. //监听到HEAD 结束
  58. if(mutation.target.nodeName == "HEAD"){
  59. observer.disconnect();
  60. resolve(mutation.target);
  61. break;
  62. }
  63. }
  64. });
  65. observer.observe(document, {childList: true, subtree: true, attributes: true});
  66. }
  67. });
  68.  
  69. function AddGlobalStyle(css) {
  70. //等待head加载完毕
  71. headLoaded.then(function (head) {
  72. GM_addStyle(css);
  73. })
  74. }
  75.  
  76. AddGlobalStyle(`<style type="text/css">@charset "UTF-8";[ng\\:cloak],[ng-cloak],[data-ng-cloak],[x-ng-cloak],.ng-cloak,.x-ng-cloak,.ng-hide:not(.ng-hide-animate){display:none !important;}ng\\:form{display:block;}.ng-animate-shim{visibility:hidden;}.ng-anchor{position:absolute;}</style>`);
  77.  
  78. var defaultConfig = {
  79. 'showDescription':true,
  80. 'imageLimit':3,
  81. 'showIcon':true,
  82. 'syringe':true,
  83. 'style':{
  84. 'public':`div#taglist {
  85. overflow: visible;
  86. min-height: 295px;
  87. height: auto;
  88. }
  89. div#gmid {
  90. min-height: 330px;
  91. height:auto;
  92. position: static;
  93. }
  94. #taglist a{
  95. background:inherit;
  96. }
  97. #taglist a::before{
  98. font-size:12px;
  99. overflow: hidden;
  100. line-height: 20px;
  101. height: 20px;
  102. }
  103. #taglist a::after{
  104. display: block;
  105. color:#FF8E8E;
  106. font-size:14px;
  107. background: inherit;
  108. border: 1px solid #000;
  109. border-radius:5px;
  110. position:absolute;
  111. float: left;
  112. z-index:999;
  113. padding:8px;
  114. box-shadow: 3px 3px 10px #000;
  115. min-width:150px;
  116. max-width:500px;
  117. white-space:pre-wrap;
  118. opacity: 0;
  119. transition: opacity 0.2s;
  120. transform: translate(-50%,20px);
  121. top:0;
  122. left: 50%;
  123. pointer-events:none;
  124. padding-top: 8px;
  125. font-weight: 400;
  126. line-height: 20px;
  127. }
  128. #taglist a:hover::after,#taglist a:focus::after{
  129. opacity: 1;
  130. pointer-events:auto;
  131. }
  132. #taglist a:focus::before,
  133. #taglist a:hover::before {
  134. font-size: 12px;
  135. position: relative;
  136. background-color: inherit;
  137. border: 1px solid #000;
  138. border-width: 1px 1px 0 1px;
  139. margin: -4px -5px;
  140. padding: 3px 4px;
  141. color:inherit;
  142. border-radius: 5px 5px 0 0;
  143. }
  144. div.gt,
  145. div.gtw,
  146. div.gtl{
  147. line-height: 20px;
  148. height: 20px;
  149. }
  150.  
  151. #taglist a:hover::after{ z-index: 9999998; }
  152. #taglist a:focus::after { z-index: 9999996; }
  153. #taglist a:hover::before{ z-index: 9999999; }
  154. #taglist a:focus::before { z-index: 9999997; }`,
  155. 'ex':`#taglist a::after{ color:#fff; }`,
  156. 'eh':`#taglist a::after{ color:#000; }`,
  157. }
  158. };
  159.  
  160. var etbConfig = GM_getValue('config');
  161.  
  162. if(!etbConfig){
  163. /*默认配置 json转换是用来深拷贝 切断关联 */
  164. etbConfig = JSON.parse(JSON.stringify(defaultConfig));
  165. // 不用存储 反正是默认的
  166. // GM_setValue('config',etbConfig);
  167. }
  168.  
  169. // 配置自动升级
  170. for(var i in defaultConfig){
  171. if(typeof etbConfig[i] === "undefined"){
  172. etbConfig[i] = JSON.parse(JSON.stringify(defaultConfig[i]));
  173. }
  174. }
  175.  
  176.  
  177. console.log('ets config:',etbConfig);
  178.  
  179. //UI控制方法等等
  180. function EhTagBuilder(){
  181. console.log('EhTagBuilder');
  182. console.log('angular',window.angular,unsafeWindow.angular);
  183.  
  184. var buttonInserPlace = document.querySelector(".pagehead-actions");//按钮插入位置
  185. var li = document.createElement("li");
  186. li.id = 'etb';
  187. li.setAttribute('ng-csp','ng-csp');
  188. li.innerHTML = GM_getResourceText('template');
  189. var app = angular.module("etb",[]);
  190. app.controller("etb",function($rootScope,$scope,$location,$anchorScroll){
  191. // console.log();
  192. $scope.pluginVersion = pluginVersion;
  193. $scope.pluginName = pluginName;
  194.  
  195. $scope.config = etbConfig;
  196.  
  197. $scope.nowPage = "menu";
  198. $scope.menuShow = false;
  199. rootScope = $rootScope;
  200. $scope.dataset = false;
  201. $scope.wikiVersion = false;
  202.  
  203. var backdrop = document.querySelector(".modal-backdrop");
  204. if(backdrop)backdrop.addEventListener('click',function(){
  205. $scope.closeMenu();
  206. $scope.$apply();
  207. });
  208.  
  209.  
  210. //xx时间前转换方法
  211. $scope.timetime = function (time) {
  212. if(!time){
  213. return '';
  214. }
  215. var now = (new Date).valueOf();
  216. now = Math.floor(now/1000);
  217. time = Math.floor(time/1000);
  218. var t = now-time;
  219.  
  220. if(!t){
  221. return '刚刚';
  222. }
  223. var f = [
  224. [31536000,'年'],
  225. [2592000,'个月'],
  226. [604800,'星期'],
  227. [86400,'天'],
  228. [3600,'小时'],
  229. [60,'分钟'],
  230. [1,'秒']
  231. ];
  232. var c = 0;
  233. for(var i in f){
  234. var k = f[i][0];
  235. var v = f[i][1];
  236. c = Math.floor(t/k);
  237. if (0 != c) {
  238. return c+v+'前';
  239. }
  240. }
  241. };
  242. //打开菜单按钮
  243. $scope.openMenu = function () {
  244. $scope.nowPage = "menu";
  245. $scope.menuShow = true;
  246. };
  247. //关闭菜单按钮
  248. $scope.closeMenu = function () {
  249. $scope.menuShow = false;
  250. };
  251. //开始获取
  252. $scope.startProgram = async function () {
  253. $scope.nowPage = "getData";
  254. await startProgram($scope);
  255. //增加一个延迟 因为处理css时候会卡住 导致加载完毕的ui无法显示
  256. setTimeout(function(){
  257. var css = buildCSS($scope.dataset,$scope.wikiVersion);
  258. // 存储
  259. $scope.css = css;
  260. $scope.cssStylish = buildStylishCSS(css,$scope.config);
  261. $scope.nowPage = 'css';
  262. $scope.$apply();
  263. },0);
  264. };
  265. //存储css样式
  266. $scope.saveCss = function () {
  267. GM_setValue('tags',{
  268. css:$scope.css,
  269. data:$scope.dataset,
  270. version:$scope.wikiVersion,
  271. update_time:new Date().getTime()
  272. });
  273. myNotification('保存完毕');
  274. };
  275.  
  276. $scope.copyStylishCss = function () {
  277. GM_setClipboard($scope.cssStylish)
  278. myNotification('复制完毕');
  279. };
  280. $scope.copyCss = function () {
  281. GM_setClipboard($scope.css)
  282. myNotification('复制完毕');
  283. };
  284.  
  285. //打开设置界面
  286. $scope.openOption = function () {
  287. $scope.nowPage = "option";
  288. };
  289. //保存设置
  290. $scope.optionSave = function () {
  291. GM_setValue('config',etbConfig);
  292. myNotification('保存成功');
  293.  
  294. };
  295. //重置设置
  296. $scope.optionReset = function () {
  297. if(confirm('确定要重置配置吗?')){
  298. $scope.config = etbConfig = JSON.parse(JSON.stringify(defaultConfig));
  299. GM_setValue('config',etbConfig);
  300. myNotification('已重置');
  301. }
  302. };
  303.  
  304. $rootScope.$on('$locationChangeSuccess', function(event){
  305. if( $location.path() == "/ets-open-option" ){
  306. $scope.openMenu();
  307. $scope.openOption();
  308. $anchorScroll('etb')
  309. $location.path("/");
  310. }
  311. if( $location.path() == "/ets-open-menu" ){
  312. $scope.openMenu();
  313. $anchorScroll('etb')
  314. $location.path("/");
  315. }
  316. if( $location.path() == "/ets-auto-update" ){
  317. $scope.openMenu();
  318. $scope.startProgram().then(function () {
  319. $scope.saveCss();
  320. })
  321. $anchorScroll('etb');
  322. $location.path("/");
  323. }
  324. if( $location.path() == "/ets-set-config" ){
  325. let s = $location.search();
  326. for(var i in s){
  327. var v = s[i];
  328. if(v === 'true'){
  329. v = true;
  330. }
  331. if(v === 'false'){
  332. v = false;
  333. }
  334. etbConfig[i] = v;
  335. }
  336. GM_setValue('config',etbConfig);
  337. myNotification('配置已修改',{body:JSON.stringify(s)});
  338. $location.path("/").search({});
  339. }
  340.  
  341. if( $location.path() == "/ets-reset-config" ){
  342. $scope.optionReset();
  343. $location.path("/");
  344. }
  345. });
  346.  
  347. });
  348. angular.bootstrap(li,['etb']);
  349. // unsafeWindow.etbApp = app;
  350. buttonInserPlace.insertBefore(li,buttonInserPlace.querySelector("li"));
  351. console.log('EhTagBuilder loaded')
  352. }
  353.  
  354. //样式写入方法 enema syringe
  355. function EhTagSyringe(){
  356. console.time('EhTagSyringe Load Enema');
  357. let tags = GM_getValue('tags');
  358. console.timeEnd('EhTagSyringe Load Enema');
  359. if(!tags)return;
  360.  
  361. console.time('EhTagSyringe Infusion');
  362. unsafeWindow.tags = tags;
  363. AddGlobalStyle(tags.css);
  364. AddGlobalStyle(etbConfig.style.public);
  365.  
  366. if((/(exhentai\.org)/).test(unsafeWindow.location.href)){
  367. AddGlobalStyle(etbConfig.style.ex);
  368. }
  369. if((/(e-hentai\.org)/).test(unsafeWindow.location.href)){
  370. AddGlobalStyle(etbConfig.style.eh);
  371. }
  372.  
  373. //临时隐藏翻译用的样式
  374. AddGlobalStyle(`
  375. .hideTranslate #taglist a{font-size:12px !important;}
  376. .hideTranslate #taglist a::before{display:none !important;}
  377. .hideTranslate #taglist a::after{display:none !important;}
  378. `);
  379. console.timeEnd('EhTagSyringe Infusion');
  380. }
  381.  
  382. //EH站更新提示
  383. function EhTagVersion() {
  384. console.log('EhTagVersion');
  385. var buttonInserPlace = document.querySelector("#nb"); //按钮插入位置
  386.  
  387. var span = document.createElement("span");
  388. var iconImg = "https://exhentai.org/img/mr.gif";
  389.  
  390.  
  391. if((/(exhentai\.org)/).test(unsafeWindow.location.href)){
  392. iconImg="https://ehgt.org/g/mr.gif";
  393. span.className=span.className+" isEX";
  394. }
  395. var etsPrompt = GM_getResourceText('ets-prompt');
  396. // etsPrompt = ``;
  397.  
  398. span.innerHTML = `${etsPrompt}`;
  399.  
  400. var app = angular.module("etb",[]);
  401. app.controller("etb",function($rootScope,$scope){
  402. $scope.pluginVersion = pluginVersion;
  403. $scope.pluginName = pluginName;
  404. $scope.iconImg = iconImg;
  405. $scope.config = etbConfig;
  406. $scope.noData = false;
  407. let tags = GM_getValue('tags');
  408. if(!tags){
  409. $scope.noData =true;
  410. }
  411. $scope.update_time = tags.update_time;
  412. $scope.nowPage = "";
  413. $scope.menuShow = false;
  414. rootScope = $rootScope;
  415. $scope.dataset = false;
  416. $scope.wikiVersion = {};
  417. if(tags){
  418. $scope.wikiVersion = tags.version;
  419. }
  420.  
  421. $scope.hide = false;
  422. //xx时间前转换方法
  423. $scope.timetime = function (time) {
  424. if(!time){
  425. return '';
  426. }
  427. var now = (new Date).valueOf();
  428. now = Math.floor(now/1000);
  429. time = Math.floor(time/1000);
  430. var t = now-time;
  431.  
  432. if(!t){
  433. return '刚刚';
  434. }
  435. var f = [
  436. [31536000,'年'],
  437. [2592000,'个月'],
  438. [604800,'星期'],
  439. [86400,'天'],
  440. [3600,'小时'],
  441. [60,'分钟'],
  442. [1,'秒']
  443. ];
  444. var c = 0;
  445. for(var i in f){
  446. var k = f[i][0];
  447. var v = f[i][1];
  448. c = Math.floor(t/k);
  449. if (0 != c) {
  450. return c+v+'前';
  451. }
  452. }
  453. };
  454. //打开菜单按钮
  455. $scope.openMenu = function () {
  456. console.log('openMenu');
  457. $scope.nowPage = "menu";
  458. $scope.menuShow = !$scope.menuShow;
  459. };
  460. $scope.showRow = {};
  461. $scope.showRow.value = false;
  462. $scope.showRow.change = function(value){
  463. if(value){
  464. document.body.className = "hideTranslate"
  465. }else{
  466. document.body.className = "";
  467. }
  468. };
  469.  
  470. $scope.VersionCheck = function () {
  471. getWikiVersion().then(function (Version) {
  472. $scope.lastVersionCheck = {
  473. time:new Date().getTime(),
  474. version:Version,
  475. };
  476. GM_setValue('lastVersionCheck',$scope.lastVersionCheck);
  477. $scope.newVersion = Version;
  478. $scope.$apply();
  479.  
  480. //这是个秘密
  481. if(etbConfig.autoUpdate){
  482. if($scope.newVersion.code != $scope.wikiVersion.code){
  483. autoUpdate().then(function () {
  484. myNotification('更新完毕,刷新页面生效');
  485. });
  486. }
  487. }
  488. console.log(Version);
  489. });
  490. };
  491.  
  492. let lastVersionCheck = GM_getValue('lastVersionCheck');
  493. $scope.lastVersionCheck = lastVersionCheck;
  494. if(!lastVersionCheck){
  495. console.log('auto VersionCheck1');
  496. $scope.VersionCheck();
  497. }else{
  498. $scope.newVersion = lastVersionCheck.version;
  499. //限制20分钟检查一次版本
  500. if(new Date().getTime() - lastVersionCheck.time > 20*60*1000 ){
  501. console.log('auto VersionCheck');
  502. $scope.VersionCheck();
  503. }
  504. }
  505. unsafeWindow.r = function () {
  506. $scope.$apply();
  507. };
  508. });
  509. angular.bootstrap(span,['etb']);
  510. unsafeWindow.etsApp = app;
  511.  
  512. buttonInserPlace.appendChild(span);
  513. }
  514. //搜索输入框助手
  515. function EhTagInputHelper() {
  516. let tags = GM_getValue('tags');
  517. console.log(tags);
  518. if(!tags)return;
  519.  
  520. console.time('add datalist');
  521. let stdinput = document.querySelector('.stdinput');
  522. if(!stdinput){return}
  523. stdinput.setAttribute("list", "tbs-tags");
  524.  
  525. var datalist = document.createElement("datalist");
  526. datalist.setAttribute("id", "tbs-tags");
  527. stdinput.parentNode.insertBefore(datalist,stdinput.nextSibling);
  528.  
  529.  
  530. //调整加载顺序 作家在前面影响搜索
  531. let loadOrder = [
  532. 'female',
  533. 'male',
  534. 'language',
  535. 'character',
  536. 'reclass',
  537. 'misc',
  538. 'parody',
  539. 'artist'
  540. ];
  541. var tagsk = {};
  542. tags.data.forEach(function (row) {
  543. tagsk[row.name] = row;
  544. });
  545. loadOrder.forEach(function (key) {
  546. let row = tagsk[key];
  547. let type = row.name;
  548. let typeName = row.cname;
  549. row.tags.forEach(function (tag) {
  550. if(tag.name){
  551. let z = document.createElement("OPTION");
  552. z.setAttribute("value", `${type}:"${tag.name}$"`);
  553. z.setAttribute("label", `${typeName}:${mdImg2cssImg(tag.cname,0)}`);
  554. datalist.appendChild(z);
  555. }
  556. });
  557. })
  558.  
  559.  
  560. console.timeEnd('add datalist');
  561.  
  562.  
  563. }
  564.  
  565.  
  566.  
  567. //获取数据
  568. async function startProgram($scope) {
  569. console.log('startProgram');
  570.  
  571. //存放承诺
  572. var pp = {
  573. wikiVersion:getWikiVersion(),
  574. rows:getRows(),
  575. tags:[]
  576. };
  577.  
  578. //获取 版本与row
  579. var [wikiVersion,rows] = await Promise.all([pp.wikiVersion,pp.rows]);
  580.  
  581. $scope.dataset = rows;
  582. $scope.wikiVersion = wikiVersion;
  583. $scope.$apply();
  584.  
  585. //构建获取tag任务 并执行
  586.  
  587. rows.forEach(function (row) {
  588. var temp = getTags(row.name);
  589. temp.then(function (mdText) {
  590. row.tags = parseTable(mdText,row.name);
  591. $scope.$apply();
  592. });
  593. pp.tags.push(temp);
  594. });
  595.  
  596. //等待获取完毕
  597. await Promise.all(pp.tags);
  598. console.log(rows);
  599.  
  600. return rows;
  601. }
  602.  
  603. //构建css
  604. function buildCSS(dataset,wikiVersion) {
  605. console.time('生成css样式');
  606. var css = "";
  607.  
  608. css+=`
  609. /* update_time:${wikiVersion.update_time} */
  610. /* hash:${wikiVersion.code} */
  611. `;
  612.  
  613. dataset.forEach(function (row) {
  614. css+= `\n/* ${row.name} ${row.cname} */\n`;
  615. // console.log(row.tags);
  616. row.tags.forEach(function (tag) {
  617. if(tag.name){
  618. var tagid = (row.name=="misc"?"":row.name + ":") + tag.name.replace(/\s/ig,"_");
  619. var cname = mdImg2cssImg(specialCharToCss(tag.cname),etbConfig.imageLimit<0?Infinity:etbConfig.imageLimit);
  620. if(!tag.info)tag.info="";
  621. var content = mdImg2cssImg(htmlBr2cssBr(specialCharToCss(tag.info)),etbConfig.imageLimit<0?Infinity:etbConfig.imageLimit);
  622. css += `
  623. a[id="ta_${tagid}"]{
  624. font-size:0px;
  625. }
  626. a[id="ta_${tagid}"]::before{
  627. content:"${cname}";
  628. }
  629. `;
  630. if(content)css+=`a[id="ta_${tagid}"]::after{
  631. content:"${content}";
  632. }`;
  633.  
  634. }else{
  635. css += `\n/* ${row.cname} */\n`;
  636. }
  637. });
  638. });
  639. console.timeEnd('生成css样式');
  640. return css;
  641.  
  642. }
  643.  
  644. //Stylish css
  645. function buildStylishCSS(css,config) {
  646. var cssStylish = "@namespace url(http://www.w3.org/1999/xhtml);\n";
  647.  
  648. cssStylish+=`@-moz-document
  649. domain('exhentai.org'),
  650. domain('e-hentai.org')
  651. {
  652. /* 通用样式 */
  653. ${config.style.public}
  654. }
  655. `;
  656. cssStylish+=`@-moz-document
  657. domain('e-hentai.org')
  658. {
  659. /* 表站样式 */
  660. ${config.style.eh}
  661. }
  662. `;
  663. cssStylish+=`@-moz-document
  664. domain('exhentai.org')
  665. {
  666. /* 里站样式 */
  667. ${config.style.ex}
  668. }
  669. `;
  670.  
  671. cssStylish+=`@-moz-document
  672. domain('exhentai.org'),
  673. domain('e-hentai.org')
  674. {
  675. body{ }
  676. /* 翻译样式 */
  677. ${css}
  678. }`;
  679. return cssStylish;
  680. }
  681.  
  682.  
  683. //转换换行
  684. function htmlBr2cssBr(mdText){
  685. return mdText.replace(/<br[ \t]*(\/)?>/igm,"\\A ");
  686. }
  687.  
  688. //转换图片
  689. function mdImg2cssImg(mdText,max=Infinity){
  690. var n = 0;
  691. return mdText.replace(/\!\[(.*?)\]\((.*?)\)/igm,function (text,alt,href,index) {
  692. n++;
  693. if( max >= n){
  694. var h = trim(href);
  695. if(h.slice(0,1) == "#"){
  696. h = h.replace(/# +\\?['"](.*?)\\?['"]/igm,"$1");
  697. }else if(h.slice(h.length-1,h.length).toLowerCase() == 'h'){
  698. h = h.slice(0,-1);
  699. }
  700. return `"url("${h}")"`;
  701. }else{
  702. return "";
  703. }
  704. });
  705. }
  706.  
  707. function specialCharToCss(str)
  708. {
  709. var strn = str;
  710. strn = strn.replace("\\","\\\\");
  711. strn = strn.replace("\"","\\\"");
  712. strn = strn.replace("\r","");
  713. strn = strn.replace("\n","\\A ");
  714. return strn;
  715. }
  716.  
  717. //获取版本
  718. function getWikiVersion(){
  719. return new Promise(function (resolve, reject) {
  720.  
  721. PromiseRequest.get(wiki_URL+'/_history?t='+new Date().getTime()).then(function (response) {
  722. var parser = new DOMParser();
  723. var PageDOM = parser.parseFromString(response, "text/html");
  724. var lastDOM = PageDOM.querySelector('#version-form table tr:nth-child(1)');
  725. if(!lastDOM){
  726. reject();
  727. return;
  728. }
  729. var code = "";
  730. var time = 0;
  731. var commit = "";
  732.  
  733. var timeDOM = lastDOM.querySelector(".date relative-time");
  734. if(timeDOM)time = Date.parse(new Date(timeDOM.getAttribute('datetime')));
  735.  
  736. var codeDOM = lastDOM.querySelector(".commit-meta code");
  737. if(codeDOM)code = codeDOM.innerText.replace(/(^\s*)|(\s*$)/g, "");
  738.  
  739. var commitDOM = lastDOM.querySelector(".commit code");
  740. if(commitDOM)commit = commitDOM.innerText.replace(/(^\s*)|(\s*$)/g, "");
  741.  
  742. var v = {
  743. update_time:time,
  744. code:code,
  745. commit:commit
  746. };
  747. resolve(v);
  748. },function () {
  749. reject();
  750. });
  751. });
  752. }
  753.  
  754. //去除两端空白
  755. function trim(s){
  756. if(typeof s == 'string'){
  757. return s.replace(/(^\s*)|(\s*$)/g, "");
  758. }else{
  759. return s;
  760. }
  761. }
  762.  
  763. //获取行 并解析
  764. function getRows() {
  765. return new Promise(async function (resolve, reject) {
  766. var url = `${wiki_raw_URL}/${rows_title}.md`+"?t="+new Date().getTime();
  767. console.log(url);
  768. var data = await PromiseRequest.get(url);
  769. /*剔除表格以外的内容*/
  770. var re = (/^\|.*\|$/gm);
  771. var table = "";
  772. resolve( parseTable(data) );
  773. });
  774. }
  775.  
  776. //获取标签 并解析
  777. function getTags(row) {
  778. return new Promise(async function (resolve, reject) {
  779.  
  780. var url = `${wiki_raw_URL}/tags/${row}.md`+"?t="+new Date().getTime();
  781. console.log(url);
  782. console.time(`加载 ${row}`);
  783. var data = await PromiseRequest.get(url);
  784. console.timeEnd(`加载 ${row}`);
  785. resolve(data);
  786. });
  787. }
  788.  
  789. function parseTable(data,name) {
  790. /*剔除表格以外的内容*/
  791. var re = (/^\s*(\|.*\|)\s*$/gm);
  792. var table = "";
  793. var temp = "";
  794. while( temp = re.exec(data) ){
  795. if(table)table+="\n";
  796. table+=temp[1];
  797. }
  798. table = table.replace(/\\\|/igm,"{~Line~}");
  799. let tableArr = table.split("\n").map(
  800. (row)=>row.split("|").map(
  801. (t)=>t.replace("{~Line~}","|")
  802. )
  803. );
  804. let tags = [];
  805. var count = [];
  806. tableArr.forEach(function (tr,index) {
  807. if(index>1){
  808. let t = {
  809. name : trim(tr[1]||""),
  810. cname : trim(tr[2]||""),
  811. info : trim(tr[3]||"")
  812. };
  813. tags.push(t);
  814. if(t.name){count++};
  815. }
  816. });
  817. console.log(name,count);
  818. return tags;
  819. }
  820.  
  821. async function autoUpdate() {
  822. var $scope = {};
  823. $scope.$apply = function(){};
  824. await startProgram($scope);
  825. var css = buildCSS($scope.dataset,$scope.wikiVersion);
  826. GM_setValue('tags',{
  827. css:css,
  828. data:$scope.dataset,
  829. version:$scope.wikiVersion,
  830. update_time:new Date().getTime()
  831. });
  832. return true;
  833. }
  834.  
  835.  
  836.  
  837. async function myNotification(title,options)
  838. {
  839. let permission = await Notification.requestPermission();
  840. if(permission == 'granted'){
  841. return new Notification(title, options);
  842. }else{
  843. return false;
  844. }
  845. }
  846.  
  847. //承诺封装的异步请求
  848. function PromiseRequest(option) {
  849. return new Promise(function (resolve, reject) {
  850. option.onload = function (response) {
  851. resolve(response.responseText);
  852. };
  853. option.onerror = function (response) {
  854. reject(response);
  855. };
  856. // if(rootScope && rootScope.$broadcast){
  857. //
  858. // }
  859. // option.onprogress = function (response,response2) {
  860. // // var info = {
  861. // // loaded:response.loaded,
  862. // // position:response.position,
  863. // // total:response.total,
  864. // // totalSize:response.totalSize,
  865. // // };
  866. // // console.info('onprogress',info,response,response2);
  867. // };
  868. GM_xmlhttpRequest(option);
  869. });
  870. }
  871. //助手 快速get post
  872. PromiseRequest.get = function (url) {
  873. return PromiseRequest({
  874. method: "GET",
  875. url: url,
  876. });
  877. };
  878. PromiseRequest.post = function (url,data) {
  879. var post = "";
  880. for(var i in data){
  881. if(post)post+="&";
  882. post+= encodeURIComponent(i)+"="+encodeURIComponent(data[i]);
  883. }
  884. return PromiseRequest({
  885. method: "POST",
  886. url: url,
  887. data: post
  888. });
  889. };
  890.  
  891. var bootstrap = function(){
  892. //在github页面下添加生成工具
  893. if((/github\.com/).test(unsafeWindow.location.href)){
  894. EhTagBuilder();
  895. }
  896. if(etbConfig.syringe) {
  897. //在EH站点下添加版本提示功能
  898. if ((/(exhentai\.org|e-hentai\.org)/).test(unsafeWindow.location.href)) {
  899. EhTagVersion();
  900. EhTagInputHelper();
  901. }
  902. }
  903. };
  904.  
  905. if (/loaded|complete/.test(document.readyState)){
  906. bootstrap();
  907. }else{
  908. document.addEventListener('DOMContentLoaded',bootstrap,false);
  909. }
  910.  
  911. //注射器总开关
  912. if(etbConfig.syringe){
  913. //注入css 不需要等待页面
  914. if((/(exhentai\.org|e-hentai\.org)/).test(unsafeWindow.location.href)){
  915. EhTagSyringe();
  916. }
  917. }
  918.  
  919. })();