WoD 人物卡

用来在英雄-》属性页面添加一个按钮,点击后生成文本格式的可复制的人物卡,便于群众交流

// ==UserScript==
// @name           WoD 人物卡
// @icon           http://info.world-of-dungeons.org/wod/css/WOD.gif
// @namespace      WOD_Tools_(fork) by_hildolfr@aino
// @description    用来在英雄-》属性页面添加一个按钮,点击后生成文本格式的可复制的人物卡,便于群众交流
// @include        http*://*.world-of-dungeons.org/wod/spiel/hero/attributes.php*
// @license        MIT License
// @modifier       Christophero
// @version        2023.05.15.1
// ==/UserScript==

var mini=(function(){var b=/(?:[\w\-\\.#]+)+(?:\[\w+?=([\'"])?(?:\\\1|.)+?\1\])?|\*|>/ig,g=/^(?:[\w\-_]+)?\.([\w\-_]+)/,f=/^(?:[\w\-_]+)?#([\w\-_]+)/,j=/^([\w\*\-_]+)/,h=[null,null];function d(o,m){m=m||document;var k=/^[\w\-_#]+$/.test(o);if(!k&&m.querySelectorAll){return c(m.querySelectorAll(o))}if(o.indexOf(",")>-1){var v=o.split(/,/g),t=[],s=0,r=v.length;for(;s<r;++s){t=t.concat(d(v[s],m))}return e(t)}var p=o.match(b),n=p.pop(),l=(n.match(f)||h)[1],u=!l&&(n.match(g)||h)[1],w=!l&&(n.match(j)||h)[1],q;if(u&&!w&&m.getElementsByClassName){q=c(m.getElementsByClassName(u))}else{q=!l&&c(m.getElementsByTagName(w||"*"));if(u){q=i(q,"className",RegExp("(^|\\s)"+u+"(\\s|$)"))}if(l){var x=m.getElementById(l);return x?[x]:[]}}return p[0]&&q[0]?a(p,q):q}function c(o){try{return Array.prototype.slice.call(o)}catch(n){var l=[],m=0,k=o.length;for(;m<k;++m){l[m]=o[m]}return l}}function a(w,p,n){var q=w.pop();if(q===">"){return a(w,p,true)}var s=[],k=-1,l=(q.match(f)||h)[1],t=!l&&(q.match(g)||h)[1],v=!l&&(q.match(j)||h)[1],u=-1,m,x,o;v=v&&v.toLowerCase();while((m=p[++u])){x=m.parentNode;do{o=!v||v==="*"||v===x.nodeName.toLowerCase();o=o&&(!l||x.id===l);o=o&&(!t||RegExp("(^|\\s)"+t+"(\\s|$)").test(x.className));if(n||o){break}}while((x=x.parentNode));if(o){s[++k]=m}}return w[0]&&s[0]?a(w,s):s}var e=(function(){var k=+new Date();var l=(function(){var m=1;return function(p){var o=p[k],n=m++;if(!o){p[k]=n;return true}return false}})();return function(m){var s=m.length,n=[],q=-1,o=0,p;for(;o<s;++o){p=m[o];if(l(p)){n[++q]=p}}k+=1;return n}})();function i(q,k,p){var m=-1,o,n=-1,l=[];while((o=q[++m])){if(p.test(o[k])){l[++n]=o}}return l}return d})();


Function.prototype.method = function ( name, func ) {
	this.prototype[name] = func;
	return this;
};

String.method( 'txt', function () {
	return this.replace( /<.*?>|\s+|\s+|\n|\r|!/g, '' );
});

// 1 need reset point to unequip
// 2 item name
// 3 = 4+5+6+7
// 4 empty space
// 5 uses
// 6 empty space
// 7 group mark
//var rObj = new RegExp( '^(.+?)(!)?([ ])?([\(][0-9]+[\/][0-9]+[\)])?$' );
//var rObj = /^(!! )?(.+?)((!)?([ ])?(\([0-9]+\/[0-9]+\))?)$/;
var rObj = /^(!! )?(.+?)(([ ])?(\([0-9]+\/[0-9]+\))?([ ])?(!)?)$/;

var pattern_graft = /^\/wod\/css\/icons\/WOD\/gems\/gem_(.)\.png$/ ;

// used to separate item name, group mark, and number of uses
String.method( 'item_txt', function () {
	var result = [];
	var item = this.replace( /<.*?>|\n|\r/g, '' );

	var item_list = rObj.exec(item);
	result.push( (item_list[1])?item_list[1]:'' );
	result.push(item_list[2]);
	result.push( (item_list[3])?item_list[3]:'' );
	return result

});

String.method( 'sindexOf', function ( subs ) {
	var i = -1;
	while ( (sub = subs[++i]) ) {
		if ( this.indexOf( sub ) != -1 ) {
			return this.indexOf( sub );
		}
	}
	return ( -1 );
});

var WoDTool = {

	attrs : [],
	skills : [],
	items : [],
	busy : 0,
	root : location.protocol + '//' + location.host + '/',

	done_func : function () {},

	done : function ( func ) {
		WoDTool.done_func = func || WoDTool.done_func;
		if (WoDTool.busy == 0) {
			WoDTool.done_func();
		}

		return this;
	},

	walker : function ( selector, func, context ) {

		var i = -1, node;
		context = context || document;
		var collection = mini( selector, context );

		while ( (node = collection[++i]) ) {
			if (func( node ) ) break;
		}

		return this;
	},

	loader : function ( url, func, i ) {
		WoDTool.busy++;
		if ( url.indexOf( 'http' ) == -1 ) {
			url = WoDTool.root + url;
		}
		var req = new XMLHttpRequest();
		req.onreadystatechange = function () {
			if ( ( req.readyState == 4 ) && ( req.status == 200 ) ) {
				var dummy = document.createElement( 'div' );
				dummy.innerHTML = req.responseText;
				func( dummy, i );
				WoDTool.busy--;
				WoDTool.done();
			}
		};
		req.open( 'GET', url, true);
		req.send();
		return this;
	},

	loadHeroAll : function () {
		var load_attrs = function ( func ) {
			WoDTool.loader( 'wod/spiel/hero/attributes.php?IS_POPUP=1', function ( dummy ) {
				WoDTool.getAttr( dummy );
				func();
			});
		};
		var load_skills = function ( func ) {
			WoDTool.loader( 'wod/spiel/hero/skills.php?IS_POPUP=1', function ( dummy ) {
				WoDTool.getSkillPages( dummy );
				func();
			});
		};
		var load_items = function ( func ) {
			WoDTool.loader( 'wod/spiel/hero/items.php?IS_POPUP=1&view=gear', function ( dummy ) {
				WoDTool.getItem( dummy );
				func();
			});
		};
		load_skills(function () {
			load_items(function () {
				load_attrs(function() { return true } );
			});
		});
		return this;
	},

	getAttr : function ( context ) {
		context = context || document;

		//英雄属性的表
		var attrTable = false, attrs = [], skills =[];

		//遍历所有标签,找出正确标签
		WoDTool.walker( 'th', function ( th ) {
			if ( th.innerHTML == '属性' ) {
				attrTable = th.parentNode.parentNode;
				return true;
			}
		}, context );

		if( !attrTable ) return false;

		WoDTool.walker( 'tr', function ( tr ) {
			if ( tr.className.indexOf( 'row' ) != -1 ) {
				if ( tr.childNodes[1] ) {
					var attr = [];
					if ( tr.childNodes[1].childNodes[1] ) {
						attr[0] = tr.childNodes[1].childNodes[1].innerHTML.txt();
					} else {
						attr[0] = tr.childNodes[1].innerHTML.txt();
					}
					var valueTds = tr.childNodes[3].childNodes[1].getElementsByTagName( 'td' );
					attr[1] = valueTds[1].innerHTML.txt();
					attrs.push( attr );
				}
			}
		}, attrTable );

		// 英雄 强化 / 弱化
		WoDTool.walker( 'form > table:nth-of-type(2) tbody tr > td:nth-of-type(3) table.content_table td', function ( td ) {
			var skill = [];
			if ( td.childNodes[1] && td.childNodes[1].innerHTML) {
				skill[0] = td.childNodes[1].innerHTML.txt();
			} else {
				skill[0] = td.innerHTML.txt();
			}

			skill[1]=''
			skills.push( skill );
		}, context );

		WoDTool.walker( 'form > table:nth-of-type(1) tbody tr > td:nth-of-type(3) table.content_table td', function ( td ) {
			if ( td.innerHTML.sindexOf( ['英雄等级', '体力', '体力恢复', '法力', '法力回复', '每回合行动次数', '先攻附加值'] ) != -1 ) {
				var attr = [];
				if ( td.childNodes[1] ) {
					attr[0] = td.childNodes[1].innerHTML.txt();
				} else {
					attr[0] = td.innerHTML.txt();
				};

				if ( attr[0].indexOf( '体力恢复' ) != -1 ) {
					attr[0] = '体力恢复';
					attr[1] = td.innerHTML.txt().substr( td.innerHTML.txt().indexOf( '体力恢复' ) + 5 );
				} else if ( attr[0].indexOf( '法力回复') != -1 ){
					attr[0] = '法力回复';
					attr[1] = td.innerHTML.txt().substr( td.innerHTML.txt().indexOf( '法力回复' ) + 5 );
				} else {
					attr[1] = td.parentNode.childNodes[3].innerHTML.txt();
				};

				attrs.push( attr );
			}
		}, context );

		WoDTool.attrs = attrs;

		if ( WoDTool.skill_L ){
			WoDTool.skill_L[0] = skills;
		} else {
			WoDTool.skill_L = [skills];
		};

		return attrs;
	},

	getMaxRepPage : function (context) {
		context = context || document;
		WoDTool.nMaxRepPage = 1;

		WoDTool.walker('input', function(myinput){
			var myname = myinput.getAttribute('name');
			var mytest = /^SKILLPAGI_PAGE\[([0-9]+)\]$/;
			var mylist = mytest.exec(myname);
			if (mylist && mylist[1]){
				var nCurrent = parseInt(mylist[1]);
				if (nCurrent > WoDTool.nMaxRepPage){ WoDTool.nMaxRepPage = nCurrent; };
			};
		}, context);

		return WoDTool.nMaxRepPage;
	},

	getSkillPages : function (context) {
		context = context || document;

		if (!WoDTool.skill_L){
			WoDTool.skill_L = [];};

		var nMaxRepPage = WoDTool.getMaxRepPage(context);

		for (var i = 1; i <= WoDTool.nMaxRepPage; ++i){
			var myurl = 'wod/spiel/hero/skills.php?IS_POPUP=1&SKILLPAGI_PAGE='+i;
			WoDTool.loader( myurl, function (dummy, t) {
				WoDTool.skill_L[t] = WoDTool.getSkill(dummy);
			}, i );
		};
	},

	getSkill : function ( context ) {
		context = context || document;

		//英雄技能的表
		var skillTable = false, skills = [];

		WoDTool.walker( 'th', function ( th ) {
			if ( th.innerHTML == '训练费' ) {
				skillTable = th.parentNode.parentNode;
				return true;
			}
		}, context);

		if( !skillTable ) return false;

		WoDTool.walker( 'a.skill_primary, a.skill_secondary, a.skill_foreign', function ( a ) {
			if (a.parentNode.parentNode.innerHTML.indexOf('学习') == -1) {
				var skill = [];
				skill[0] = a.innerHTML.txt();
				WoDTool.walker( 'div', function ( div ) {
					if ( div.id.indexOf( 'skill_rang' ) != -1 ) {
						skill[1] = div.innerHTML.txt();
					}
				}, a.parentNode.parentNode );
				WoDTool.walker( 'span', function ( span ) {
					if ( span.id.indexOf( 'skill_eff_rang' ) != -1 ) {
						skill[1] = skill[1] + span.innerHTML.txt();
					}
				}, a.parentNode.parentNode );
				if (skill[1].indexOf( '-' ) == -1 ){
					skills.push(skill);
				}
			}
		}, skillTable );

		return skills;
	},

	getItem : function ( context ) {


		context = context || document;

		//英雄装备的表
		var itemTable = false, items = [];

		WoDTool.walker( 'td.texttoken',function ( td ) {
			if ( td.innerHTML == '头' ) {
				itemTable = td.parentNode.parentNode.parentNode.parentNode.parentNode;
				return true;
			}
		}, context );

		if( !itemTable ) return false;

		WoDTool.walker('table tbody tr', function ( tr ) {

			var item =[];
			var tds = mini('td',tr)

			// 位置 = item[0]
			item[0] = tds[0].innerHTML.txt();
			var td = tds[1];

			item[1]=[];

			WoDTool.walker('select, img', function ( item_part ) {
				if (item_part.tagName.toLowerCase() == 'select'){
					WoDTool.walker( 'option', function ( option ) {
						if ( option.getAttribute('selected') ) {
							item[1].push(['select',option.innerHTML.item_txt()]);
						}
					}, item_part );
				};
				if (item_part.tagName.toLowerCase() == 'img'){
					// 有可裝備物品的空位
					if ( ! /zustand_leer.gif$/.test(item_part.getAttribute('src'))){
						item[1].push(['img',item_part.getAttribute('src')]);
					};
				};
			}, td);

			if (item[1].length > 0){items.push(item);};
		}, itemTable );

		WoDTool.items = items;

		return items;
	}

};

(function () {

	var btn = document.createElement( 'input' );
	btn.setAttribute( 'class', 'button' );
	btn.setAttribute( 'type', 'button' );
	btn.setAttribute( 'value', '人物卡代码' );
	btn.addEventListener(
    "click",
    function () {
      var newdiv = document.createElement("div");
      newdiv.innerHTML = "加载中";
      newdiv.contentEditable = true;

      WoDTool.loadHeroAll().done(function () {
        WoDTool.skills = [];

        // WoDTool.skill_L[0] = 强化 / 弱化
        // WoDTool.skill_L[k] = 技能第k頁
        for (var i = 0; i < WoDTool.skill_L.length; ++i) {
          WoDTool.skills = WoDTool.skills.concat(WoDTool.skill_L[i]);
        }

        var output = "[table border=1]<br />";
        var rows = Math.ceil(
          (WoDTool.attrs.length + WoDTool.skills.length) / 2
        );
        if (rows < WoDTool.attrs.length) {
          rows = WoDTool.attrs.length;
        }
        for (var i = 0; i < rows; ++i) {
          output = output + "[tr]";
          if (i < WoDTool.attrs.length) {
            output =
              output +
              "[td][color=orange]" +
              WoDTool.attrs[i][0] +
              "[/color][/td][td]" +
              WoDTool.attrs[i][1] +
              "[/td]";
          } else {
            var s1 = i - WoDTool.attrs.length;
            output =
              output +
              "[td][skill:" +
              WoDTool.skills[s1][0] +
              "][/td][td]" +
              WoDTool.skills[s1][1] +
              "[/td]";
          }
          var s2 = i + rows - WoDTool.attrs.length;
          if (WoDTool.skills[s2]) {
            output =
              output +
              "[td][skill:" +
              WoDTool.skills[s2][0] +
              "][/td][td]" +
              WoDTool.skills[s2][1] +
              "[/td]";
          } else {
            output = output + "[td][/td][td][/td]";
          }
          output = output + "[/tr]<br />";
        }
        var i = -1;
        while ((item = WoDTool.items[++i])) {
          output =
            output +
            "[tr][td][color=orange]" +
            item[0] +
            "[/color][/td][td colspan=3]";
          var t = -1;
          while ((item_part = item[1][++t])) {
            if (item_part[0] == "img") {
              //output = output + "[img]" + item_part[1] + "[/img]";
              var graft_result = pattern_graft.exec(item_part[1]);
              if (graft_result != null) {
                output = output + ":g" + graft_result[1] + ":";
              }
            }
            if (item_part[0] == "select") {
              output =
                output +
                item_part[1][0] +
                "[item:" +
                item_part[1][1] +
                "]" +
                item_part[1][2];
            }
          }
          output = output + "[/td][/tr]<br />";
        }
        output = output + "[/table]";
        newdiv.innerHTML = output;
        navigator.clipboard
          .writeText(output.replaceAll("<br />", "\n"))
          .then(() => {
            alert("人物卡已复制到剪贴板");
          })
          .catch((err) => {
            alert("不能复制人物卡到剪贴板,可能未授予剪贴板权限,请手动复制");
            // This can happen if the user denies clipboard permissions:
            console.error("Could not copy text: ", err);
          });
      });

      this.style.display = "none";
      this.parentNode.after(newdiv);
    },
    false
  );

  WoDTool.walker("h1", function (h1) {
    h1.appendChild(btn);
    return true;
  });

})();