PixivUserBatchDownload

Batch download pixiv user's images in one key.

Ekde 2020/06/22. Vidu La ĝisdata versio.

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name		PixivUserBatchDownload
// @name:zh-CN	P站画师个人作品批量下载工具
// @name:zh-TW	P站畫師個人作品批量下載工具
// @name:zh-HK	P站畫師個人作品批量下載工具
// @version		5.10.95
// @author      Mapaler <[email protected]>
// @copyright	2018+, Mapaler <[email protected]>
// @namespace	http://www.mapaler.com/
// @icon		http://www.pixiv.net/favicon.ico
// @homepage	https://github.com/Mapaler/PixivUserBatchDownload
// @supportURL  https://github.com/Mapaler/PixivUserBatchDownload/issues
// @description	Batch download pixiv user's images in one key.
// @description:zh-CN	配合Aria2,一键批量下载P站画师的全部作品
// @description:zh-TW	配合Aria2,一鍵批量下載P站畫師的全部作品
// @description:zh-HK	配合Aria2,一鍵批量下載P站畫師的全部作品
// @homepage    https://github.com/Mapaler/PixivUserBatchDownload
// @supportURL  https://github.com/Mapaler/PixivUserBatchDownload/issues
//-@downloadURL https://greasyfork.org/scripts/17879/code/PixivUserBatchDownload.user.js
// @include		*://www.pixiv.net/*
// @exclude		*://www.pixiv.net/search.php*
// @exclude		*://www.pixiv.net/upload.php*
// @exclude		*://www.pixiv.net/messages.php*
// @exclude		*://www.pixiv.net/ranking.php*
// @exclude		*://www.pixiv.net/setting*
// @exclude		*://www.pixiv.net/stacc*
// @exclude		*://www.pixiv.net/premium*
// @exclude		*://www.pixiv.net/discovery*
// @exclude		*://www.pixiv.net/howto*
// @exclude		*://www.pixiv.net/idea*
// @exclude		*://www.pixiv.net/novel*
// @exclude		*://www.pixiv.net/cate_r18*
// @resource    pubd-style  https://github.com/Mapaler/PixivUserBatchDownload/raw/master/PixivUserBatchDownload%20ui.css?v=2020年3月23日
// @require		https://cdn.staticfile.org/crypto-js/4.0.0/core.min.js
// @require		https://cdn.staticfile.org/crypto-js/4.0.0/md5.min.js
// @require     https://cdn.staticfile.org/localforage/1.7.3/localforage.min.js
// @grant       unsafeWindow
// @grant       window.close
// @grant       window.focus
// @grant       GM_xmlhttpRequest
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_deleteValue
//-@grant       GM_listValues
// @grant       GM_addStyle
// @grant       GM_getResourceText
//-@grant       GM_getResourceURL
// @grant       GM_addValueChangeListener
//-@grant       GM_notification
// @grant       GM_registerMenuCommand
// @grant       GM_unregisterMenuCommand
// @connect     pixiv.net
// @connect     localhost
// @connect     127.0.0.1
// @noframes
// ==/UserScript==
/*jshint esversion: 6 */

//非顶级页面退出程序
if (
	self.frameElement && self.frameElement.tagName == "IFRAME" || //iframe判断方式1
	window.frames.length != parent.frames.length || //iframe判断方式2
	self != top //iframe判断方式3
){return;}//iframe退出执行
//获取当前是否是本地开发状态
const mdev = Boolean(localStorage.getItem("pubd-dev"));

/*
 * 公共变量区
 */
var scriptVersion = "LocalDebug", //本程序的版本
	scriptName = "PixivUserBatchDownload", //本程序的名称
	scriptIcon = "http://www.pixiv.net/favicon.ico"; //本程序的图标
if (typeof(GM_info)!="undefined")
{
	if (mdev) console.log("GM_info信息:",GM_info); //用来显示meta数据
	scriptVersion = GM_info.script.version.trim();
	scriptIcon = GM_info.script.icon64 || GM_info.script.icon;
	if (GM_info.script.name_i18n)
	{
		var i18n = (navigator.language||navigator.userLanguage).replace("-","_"); //获取浏览器语言
		scriptName = GM_info.script.name_i18n[i18n]; //支持Tampermonkey
	}
	else
	{
		scriptName = GM_info.script.localizedName || //支持Greasemonkey 油猴子 3.x
					GM_info.script.name; //支持Violentmonkey 暴力猴
	}
}

var pubd = { //储存程序设置
	configVersion: 1, //当前设置版本,用于提醒是否需要重置
	touch: false, //是触屏
	loggedIn: false, //登陆了
	start: null, //开始按钮
	menu: null, //菜单
	dialog: { //窗口些个
		config: null, //设置窗口
		login: null, //登陆窗口
		downthis: null, //下载当前窗口
		downillust: null, //下载当前作品窗口
	},
	auth: null, //储存账号密码
	downSchemes: [], //储存下载方案
	downbreak: false, //是否停止发送Aria2的Flag
	fastStarList: [], //储存快速收藏的简单数字
	staruserlists: [], //储存完整的下载列表
};

//匹配P站内容的正则表达式
const illustPattern = '(https?://([^/]+)/.+/\\d{4}/\\d{2}/\\d{2}/\\d{2}/\\d{2}/\\d{2}/(\\d+(?:-([0-9a-zA-Z]+))?(?:_p|_ugoira)))\\d+(?:_\\w+)?\\.([\\w\\d]+)'; //P站图片地址正则匹配式
const limitingPattern = '(https?://([^/]+)/common/images/(limit_(mypixiv|unknown)))_\\d+\\.([\\w\\d]+)'; //P站上锁图片完整地址正则匹配式
const limitingFilenamePattern = 'limit_(mypixiv|unknown)'; //P站上锁图片文件名正则匹配式
//作者页面“主页”按钮的CSS位置(用来获取作者ID)
const userMainPageCssPath = "#root>div:nth-of-type(2)>div>div:nth-of-type(2)>nav>a";
//作品页,收藏按钮的CSS位置(用来获取当前作品ID)
const artWorkStarCssPath = "#root>div:nth-of-type(2)>div>div>main>section>div>div>figcaption>div>div>ul>li:nth-of-type(2)>a";
//作品页,作者头像链接的CSS位置(用来获取作者ID)
const artWorkUserHeadCssPath = "#root>div:nth-of-type(2)>div>div>aside>section a";

//Header使用
const PixivAppVersion = "5.0.169"; //Pixiv APP的版本
const AndroidVersion = "9"; //安卓的版本
const UA = "PixivAndroidApp/" + PixivAppVersion + " (Android " + AndroidVersion + "; Android SDK built for x86)"; //向P站请求数据时的UA
const X_Client_Hash_Salt = "28c1fdd170a5204386cb1313c7077b34f83e4aaf4aa829ce78c231e05b0bae2c"; //X_Client加密的slat
const Referer = "https://app-api.pixiv.net/";
const ContentType = "application/x-www-form-urlencoded; charset=UTF-8"; //重要
//登陆时的固定参数
const authURL = "https://oauth.secure.pixiv.net/auth/token";
const client_id = "MOBrBDS8blbauoSck0ZfDbtuzpyT"; //安卓版固定数据
const client_secret = "lsACyCD94FhDUtGTXi3QzcFE2uU1hqtDaKeqrdwj"; //安卓版固定数据
const device_token = "pixiv"; //每个设备不一样,不过好像随便写也没事

var thisPageUserid = null, //当前页面的画师ID
	thisPageIllustid = null, //当前页面的作品ID
	findInsertPlaceHook = null, //储存插入点循环钩子(循环函数指针)
	observer = null, //储存DOM变动监听钩子
	btnStartInsertPlace = null, //储存开始按钮插入点
	downIllustMenuId = null, //下载当前作品的菜单的ID(Tampermonker菜单内的指针)
	recommendList = null; //推荐作品列表
/*
 * 获取初始状态
 */
//1、获取原网页数据对象
if (typeof(unsafeWindow) != "undefined")
{
	const metaPreloadData = document.querySelector('#meta-preload-data'); //HTML源代码里有,会被前端删掉的数据
	const globalInitData = unsafeWindow.globalInitData; //新版的插画页面信息-已失效
	const pixiv = unsafeWindow.pixiv; //原来的信息-已失效
//2、获取是否为登录状态与当前页面画师ID
	if (metaPreloadData == undefined && pixiv == undefined && globalInitData == undefined)
	{
		console.error("PUBD:当前网页没有找到 preloadData 元数据或 pixiv 对象或 globalInitData 对象");
	}
	else
	{
		if (metaPreloadData != undefined) //更加新的存在于HTML元数据中的页面信息
		{
			pubd.loggedIn = true;
			console.log("PUBD:本页面抢救出 metaPreloadData 对象:",metaPreloadData);
			const preloadData = JSON.parse(metaPreloadData.content);
			console.log("PUBD:metaPreloadData 中的 preloadData 元数据:",preloadData);
			if (preloadData.user) thisPageUserid = parseInt(Object.keys(preloadData.user)[0]);
			if (preloadData.illust) thisPageIllustid = parseInt(Object.keys(preloadData.illust)[0]); //必须判断是否存在,否则会出现can't convert undefined to object错误
		}
		else if (globalInitData != undefined) //新版的插画页面信息
		{
			pubd.loggedIn = true;
			console.log("PUBD:本页面存在 globalInitData 对象:",globalInitData);
			if (globalInitData.preload.user) thisPageUserid = parseInt(Object.keys(globalInitData.preload.user)[0]); //id不是属性值,而是子对象名,所以需要通过这样的方式获取
			if (globalInitData.preload.illust) thisPageIllustid = parseInt(Object.keys(globalInitData.preload.illust)[0]);
		}
		else if (pixiv != undefined) //原来的信息
		{
			console.log("PUBD:本页面存在 pixiv 对象:",pixiv);
			thisPageUserid = parseInt(pixiv.context.userId);
			if (pixiv.user.loggedIn)
			{
				pubd.loggedIn = true; //判断是否已经登陆
			}
		}
	}
}
//3、获取是否为手机版
if (location.host.indexOf("touch") >= 0) //typeof(pixiv.AutoView)!="undefined"
{
	pubd.touch = true;
	console.info("PUBD:当前访问的是P站触屏手机版,我没开发。");
} else {
	//console.info("PUBD:当前访问的是P站桌面版");
}

//仿GM_notification函数v1.2,发送网页通知。
//此函数非Debug用,为了替换选项较少但是兼容其格式的GM_notification插件
if (typeof(GM_notification) == "undefined") {
	var GM_notification = function(text, title, image, onclick) {
		var options = {},rTitle,rText;
		var dataMode = (typeof(text) == "string"); //GM_notification有两种模式,普通4参数模式和option对象模式
		if (dataMode)
		{ //普通模式
			rTitle = title;
			rText = text;
			options.body = text;
			options.icon = image;
		}else
		{ //选项模式
			var details = text, ondone = title, onclose = image;
			rTitle = details.title;
			rText = details.text;
			if (details.text) options.body = details.text;
			if (details.image) options.icon = details.image;
			if (details.timeout) options.timestamp = details.timeout;
			//if (details.highlight) options.highlight = details.highlight; //没找到这个功能
		}

		function sendNotification(general){
			var n = new Notification(rTitle, options);
			if (general)
			{ //普通模式
				if (onclick) n.onclick = onclick;
			}else
			{ //选项模式,这里和TamperMonkey API不一样,区分了关闭和点击。
				if (ondone) n.onclick = ondone;
				if (onclose) n.onclose = onclose;
			}
		}
		// 先检查浏览器是否支持
		if (!("Notification" in window)) {
			alert(rTitle + "\r\n" + rText);
		// 检查用户是否同意接受通知
		} else if (Notification.permission === "granted") {
			Notification.requestPermission(function(permission) {
				sendNotification(dataMode);
			});
		}
		// 否则我们需要向用户获取权限
		else if (Notification.permission !== 'denied') {
			Notification.requestPermission(function(permission) {
				// 如果用户同意,就可以向他们发送通知
				if (permission === "granted") {
					sendNotification(dataMode);
				}
			});
		}
	};
}

/*
 * 自定义对象区
 */

//生成P站需要的时间格式,如 "2019-09-03T18:51:40+08:00"
Date.prototype.toPixivString = function() {
	function pad(num, length=2)
	{ //填充截取法补前导0 
		let ns = num.toString();
		if (ns.length >= length)
			return ns;
		else
		{ //这里用slice和substr均可
			return (Array(length).join('0') + num.toString()).slice(-length);
		}
	}
	const timezoneOffset = this.getTimezoneOffset(); //时区差值
	const str = 
	[
		this.getFullYear(), //年
		this.getMonth()+1, //月
		this.getDate()  //日
	].map(n=>pad(n)).join('-') +
	'T' +
	[
		this.getHours(), //时
		this.getMinutes(), //秒
		this.getSeconds() //分
	].map(n=>pad(n)).join(':') +
	(timezoneOffset<=0?"+":"-") + //时区正负
	[
		timezoneOffset/60, //时区差时
		timezoneOffset%60, //时区差分
	].map(n=>pad(Math.floor(Math.abs(n)))).join(':');
	return str;
};

//一个被收藏的画师
class StarUser{
	constructor(id){
		const user = this;
		user.id = id;
		user.infoDone = false;
		user.downDone = false;
		user.userinfo = null;
		user.illusts = null;
	}
}
//一个画师收藏列表
class UsersStarList{
	constructor(title){
		const list = this;
		list.title=title;
		list.users=[];
	}
	add(userid)
	{
		this.users.push(new StarUser(userid));
	}
	remove(userid)
	{
		this.users = this.users.filter(u=>u.id!=userid);
	}
	toggle(userid)
	{
		if (this.users.some(u=>u.id == userid))
		{
			this.remove(userid);
			return false;
		}else
		{
			this.add(userid);
			return true;
		}
	}
}

//一个本程序使用的headers数据
class HeadersObject{
	constructor(obj){
		const header = this;
		const timeStr = new Date().toPixivString();
		header["App-OS"] = "android";
		header["App-OS-Version"] = AndroidVersion;
		header["App-Version"] = PixivAppVersion;
		header["User-Agent"] = UA;
		header["Content-Type"] = ContentType; //重要
		header["Referer"] = Referer;
		header["X-Client-Hash"] = CryptoJS.MD5(timeStr + X_Client_Hash_Salt).toString();
		header["X-Client-Time"] = timeStr;
		if (typeof(obj) == "object")
		{
			Object.entries(obj).forEach(entry=> header[entry[0]] = entry[1]);
		}
	}
}

//储存一项图片列表分析数据的对象
var Works = function(){
	this.done = false; //是否分析完毕
	this.item = []; //储存图片数据
	this.break = false; //储存停止分析的Flag
	this.runing = false; //是否正在运行的Flasg
	this.next_url = ""; //储存下一页地址(断点续传)
};
//一个认证方案
var Auth = function (username, password, remember) {
	this.response = null;
	this.needlogin = false;
	this.username = username || null;
	this.password = password || null;
	this.save_account = remember || false,
	this.login_date = null;
};
Auth.prototype.newAccount = function(username, password, remember) {
	if (typeof(remember) == "boolean") this.save_account = remember;
	this.username = username;
	this.password = password;
};
Auth.prototype.loadFromAuth = function(auth) {
	if (typeof(auth) == "string")
	{
		try
		{
			auth = JSON.parse(auth);
		}catch(e)
		{
			console.error("读取的Auth数据是字符串,但非JSON。",e);
			return;
		}
	}else if (auth == undefined)
	{
		return;
	}
	var _thisAuth = this;
	Object.keys(_thisAuth).forEach(function(key){
		if (typeof(auth[key]) != "undefined")
			_thisAuth[key] = auth[key];
	});
};
Auth.prototype.save = function() {
	let saveObj = Object.assign({},this);
	if (!saveObj.save_account) {
		saveObj.username = "";
		saveObj.password = "";
	}
	GM_setValue("pubd-auth", saveObj);
};
Auth.prototype.login = function(onload_suceess_Cb, onload_hasError_Cb, onload_notJson_Cb, onerror_Cb) {
	let _thisAuth = this;
	let postObj = new URLSearchParams();
	postObj.set("client_id","MOBrBDS8blbauoSck0ZfDbtuzpyT");//安卓某个版本的数据
	postObj.set("client_secret","lsACyCD94FhDUtGTXi3QzcFE2uU1hqtDaKeqrdwj");//安卓某个版本的数据
	postObj.set("grant_type","password");
	postObj.set("username",_thisAuth.username);
	postObj.set("password",_thisAuth.password);
	postObj.set("device_token","pixiv");
	postObj.set("get_secure_url","true");
	postObj.set("include_policy","true");

	//登陆的Auth API
	GM_xmlhttpRequest({
		url: authURL,
		method: "post",
		responseType: "text",
		headers: new HeadersObject(),
		data: postObj.toString(),
		onload: function(response) {
			var jo;
			try {
				jo = JSON.parse(response.responseText);
			} catch (e) {
				console.error("登录失败,返回可能不是JSON格式,或本程序异常。", e, response);
				onload_notJson_Cb(response);
				return;
			}

			if (jo)
			{
				if (jo.has_error || jo.errors) {
					console.error("登录失败,返回错误消息", jo);
					onload_hasError_Cb(jo);
					return;
				} else { //登陆成功
					_thisAuth.response = jo.response;
					_thisAuth.login_date = new Date().getTime();
					console.info("登陆成功", jo);
					onload_suceess_Cb(jo);
					return;
				}
			}
		},
		onerror: function(response) {
			console.error("登录失败,网络请求发生错误", response);
			onerror_Cb(response);
			return;
		}
	})
}
//一个掩码
var Mask = function(name, logic, content){
	this.name = name;
	this.logic = logic;
	this.content = content;
}
//一个下载方案
var DownScheme = function(name) {
	//默认值
	this.name = name ? name : "默认方案";
	this.rpcurl = "http://localhost:6800/jsonrpc";
	this.https2http = false;
	this.downfilter = "";
	this.savedir = "D:/PixivDownload/";
	this.savepath = "%{illust.user.id}/%{illust.filename}%{page}.%{illust.extention}";
	this.textout = "%{illust.url_without_page}%{page}.%{illust.extention}\n";
	this.masklist = [];
};
DownScheme.prototype.maskAdd = function(name, logic, content) {
	var mask = new Mask(name, logic, content);
	this.masklist.push(mask);
	return mask;
}
DownScheme.prototype.maskRemove = function(index) {
	this.masklist.splice(index, 1);
}
DownScheme.prototype.loadFromJson = function(json) {
	if (typeof(json) == "string") {
		try {
			json = JSON.parse(json);
		} catch (e) {
			console.error("读取的方案数据是字符串,但非JSON。",e);
			return false;
		}
	}
	var _this = this;
	Object.keys(_this).forEach(function(key){
		if (key=="masklist")
		{
			_this.masklist.length = 0; //清空之前的
			json.masklist.forEach(function(mask){
				_this.masklist.push(new Mask(mask.name, mask.logic, mask.content))
			})
		}else
		{
			_this[key] = json[key];
		}
	})
	return true;
};

//创建菜单类
var pubdMenu = function(classname) {
	//生成菜单项
	function buildMenuItem(title, classname, callback, submenu) {
		var item = document.createElement("li");
		if (title == 0) //title为0时,只添加一条菜单分割线
		{
			item.className = "pubd-menu-line" + (classname ? " " + classname : "");
			return item;
		}
		item.className = "pubd-menu-item" + (classname ? " " + classname : "");

		//如果有子菜单则添加子菜单
		if (typeof(submenu) == "object") {
			item.classList.add("pubd-menu-includesub"); //表明该菜单项有子菜单
			submenu.classList.add("pubd-menu-submenu"); //表明该菜单是子菜单
			//a.addEventListener("mouseenter",function(){callback.show()});
			//a.addEventListener("mouseleave",function(){callback.hide()});
			item.appendChild(submenu);
			item.subitem = submenu;
		}else
		{
			item.subitem = null; //子菜单默认为空
		}

		//添加链接
		var a = item.appendChild(document.createElement("a"));
		a.className = "pubd-menu-item-a"
		//添加图标
		var icon = a.appendChild(document.createElement("i"));
		icon.className = "pubd-icon";
		//添加文字
		var span = a.appendChild(document.createElement("span"));
		span.className = "text";
		span.innerHTML = title;

		//添加菜单操作
		if (typeof(callback) == "string") { //为字符串时,当作链接处理
			a.target = "_blank";
			a.href = callback;
		} else if (typeof(callback) == "function") { //为函数时,当作按钮处理
			item.addEventListener("click", callback);
			//a.onclick = callback;
		}
		return item;
	}

	var menu = document.createElement("ul");
	menu.className = "pubd-menu display-none" + (classname ? " " + classname : "");
	menu.item = new Array();
	//显示该菜单
	menu.show = function() {
		menu.classList.remove("display-none");
	}
	menu.hide = function() {
			menu.classList.add("display-none");
		}
		//添加菜单项
	menu.add = function(title, classname, callback, submenu) {
			var itm = buildMenuItem(title, classname, callback, submenu);
			this.appendChild(itm);
			this.item.push(itm)
			return itm;
		}
		//鼠标移出菜单时消失
	menu.addEventListener("mouseleave", function(e) {
		this.hide();
	});
	return menu;
};

//创建通用对话框类
var Dialog = function(caption, classname, id) {
	//构建标题栏按钮
	function buildDlgCptBtn(text, classname, callback) {
		if (!callback) classname = "";
		var btn = document.createElement("a");
		btn.className = "dlg-cpt-btn" + (classname ? " " + classname : "");
		if (typeof(callback) == "string") {
			btn.target = "_blank";
			btn.href = callback;
		} else {
			if (callback)
				btn.addEventListener("click", callback);
		}
		var btnTxt = btn.appendChild(document.createElement("span"));
		btnTxt.className = "dlg-cpt-btn-text";
		btnTxt.innerHTML = text;

		return btn;
	}

	var dlg = document.createElement("div");
	if (id) dlg.id = id;
	dlg.className = "pubd-dialog display-none" + (classname ? " " + classname : "");

	//添加图标与标题
	var cpt = dlg.appendChild(document.createElement("div"));
	cpt.className = "caption";
	dlg.icon = cpt.appendChild(document.createElement("i"));
	dlg.icon.className = "pubd-icon";
	var captionDom = cpt.appendChild(document.createElement("span"));
	Object.defineProperty(dlg , "caption", {
		get() {
			return captionDom.textContent;
		},
		set(str) {
			captionDom.innerHTML = str;
		}
	});
	dlg.caption = caption;

	//添加标题栏右上角按钮 captionButtons
	var cptBtns = dlg.cptBtns = dlg.appendChild(document.createElement("div"));
	cptBtns.className = "dlg-cpt-btns";
	//添加按钮的函数
	cptBtns.add = function(text, classname, callback) {
		var btn = buildDlgCptBtn(text, classname, callback);
		this.insertBefore(btn, this.firstChild);
		return btn;
	}
	//添加关闭按钮
	cptBtns.close = cptBtns.add("X", "dlg-btn-close", (function() {
		dlg.classList.add("display-none");
	}));

	//添加内容区域
	var content = dlg.content = dlg.appendChild(document.createElement("div"));
	content.className = "dlg-content";

	//窗口激活
	dlg.active = function() {
			if (!this.classList.contains("pubd-dlg-active")) { //如果没有激活的话才执行
				var dlgs = document.querySelectorAll(".pubd-dialog"); //获取网页已经载入的所有的窗口
				for (var dlgi = 0; dlgi < dlgs.length; dlgi++) { //循环所有窗口
					if (dlgs[dlgi] != this)
					{
						dlgs[dlgi].classList.remove("pubd-dlg-active"); //取消激活
						dlgs[dlgi].style.zIndex = parseInt(window.getComputedStyle(dlgs[dlgi], null).getPropertyValue("z-index")) - 1; //从当前网页最终样式获取该窗体z级,并-1.
					}
				}
				this.classList.add("pubd-dlg-active"); //添加激活
				this.style.zIndex = ""; //z级归零
			}
		}
	//窗口初始化
	dlg.initialise = function() { //窗口初始化默认情况下什么也不做,具体在每个窗口再设置
			return;
		}
		//窗口显示
	dlg.show = function(posX, posY, arg) {
			if (posX) dlg.style.left = posX + "px"; //更改显示时初始坐标
			if (posY) dlg.style.top = posY + "px";
			dlg.initialise(arg); //对窗体进行初始化(激活为可见前提前修改窗体内容)
			dlg.classList.remove("display-none");
			dlg.active(); //激活窗口
		}
		//窗口隐藏
	dlg.hide = function() { //默认情况下等同于关闭窗口
			dlg.cptBtns.close.click();
		}
	
	//添加鼠标拖拽移动
	var drag = dlg.drag = [0, 0]; //[X,Y] 用以储存窗体开始拖动时的鼠标相对窗口坐标差值。
	//startDrag(cpt, dlg);
	cpt.addEventListener("mousedown", function(e) { //按下鼠标则添加移动事件
		var eX = e.pageX>0?e.pageX:0, eY = e.pageY>0?e.pageY:0; //不允许鼠标坐标向上、左超出网页。
		drag[0] = eX - dlg.offsetLeft;
		drag[1] = eY - dlg.offsetTop;
		var handler_mousemove = function(e) { //移动鼠标则修改窗体坐标
			var eX = e.pageX>0?e.pageX:0, eY = e.pageY>0?e.pageY:0; //不允许鼠标坐标向上、左超出网页。
			dlg.style.left = (eX - drag[0]) + 'px';
			dlg.style.top = (eY - drag[1]) + 'px';
		};
		var handler_mouseup = function(e) { //抬起鼠标则取消移动事件
			document.removeEventListener("mousemove", handler_mousemove);
		};
		document.addEventListener("mousemove", handler_mousemove);
		document.addEventListener("mouseup", handler_mouseup, { once: true });
	});
	//点击窗口任何区域激活窗口
	dlg.addEventListener("mousedown", function(e) {
		dlg.active();
	});
	return dlg;
};

//创建框架类
var Frame = function(title, classname) {
	var frame = document.createElement("div");
	frame.className = "pubd-frame" + (classname ? " " + classname : "");

	var caption = frame.caption = frame.appendChild(document.createElement("div"));
	caption.className = "pubd-frame-caption";
	caption.innerHTML = title;
	
	var content = frame.content = frame.appendChild(document.createElement("div"));
	content.className = "pubd-frame-content";

	frame.name = function() {
		return this.caption.textContent;
	}
	frame.rename = function(newName) {
		if (typeof(newName) == "string" && newName.length > 0) {
			this.caption.innerHTML = newName;
			return true;
		} else
			return false;
	}

	return frame;
};

//创建带Label的Input类
var LabelInput = function(text, classname, name, type, value, beforeText, title) {
	var label = document.createElement("label");
	label.innerHTML = text;
	label.className = classname;
	if (typeof(title) != "undefined")
		label.title = title;

	var ipt = label.input = document.createElement("input");
	ipt.name = name;
	ipt.id = ipt.name;
	ipt.type = type;
	ipt.value = value;

	if (beforeText)
		label.insertBefore(ipt, label.firstChild);
	else
		label.appendChild(ipt);
	return label;
};

//创建进度条类
var Progress = function(classname, align_right) {
	//强制保留pos位小数,如:2,会在2后面补上00.即2.00
	function toDecimal2(num, pos) {
		var f = parseFloat(num);
		if (isNaN(f)) {
			return false;
		}
		f = Math.round(num * Math.pow(10, pos)) / Math.pow(10, pos);
		var s = f.toString();
		var rs = s.indexOf('.');
		if (pos > 0 && rs < 0) {
			rs = s.length;
			s += '.';
		}
		while (s.length <= rs + pos) {
			s += '0';
		}
		return s;
	}

	var progress = document.createElement("div");
	progress.className = "pubd-progress" + (classname ? " " + classname : "");
	if (align_right) progress.classList.add("pubd-progress-right");

	progress.scaleNum = 0;

	var bar = progress.appendChild(document.createElement("div"));
	bar.className = "pubd-progress-bar";

	var txt = progress.appendChild(document.createElement("span"));
	txt.className = "pubd-progress-text";

	progress.set = function(scale, pos, str) {
		if (pos == undefined) pos = 2;
		var percentStr = toDecimal2((scale * 100), pos) + "%";
		scale = scale > 1 ? 1 : (scale < 0 ? 0 : scale);
		this.scaleNum = scale;
		bar.style.width = percentStr;
		if (str)
			txt.innerHTML = str;
		else
			txt.innerHTML = percentStr;
	}
	Object.defineProperty(progress , "scale", {
		get() {
			return this.scaleNum;
		},
		set(num) {
			progress.set(num);
		}
	});

	return progress;
};

//创建 卡片类
function InfoCard(datas) {
	var cardDiv = this.dom = document.createElement("div");
	cardDiv.className = "pubd-infoCard";
	var thumbnailDiv = cardDiv.appendChild(document.createElement("div"));
	thumbnailDiv.className = "pubd-infoCard-thumbnail";
	var thumbnailImgDom = thumbnailDiv.appendChild(document.createElement("img"));
	var infosDlDom = cardDiv.appendChild(document.createElement("dl"));
	infosDlDom.className = "pubd-infoCard-dl";
	Object.defineProperty(this , "thumbnail", {
		get() {
			return thumbnailImgDom.src;
		},
		set(url) {
			thumbnailImgDom.src = url;
		}
	});
	var infoObj;
	this.reload = function() //重构Card文本区域
	{
		for (var ci = infosDlDom.children.length-1;ci >= 0;ci--)
		{ //删掉所有老子元素
			var x = infosDlDom.children[ci];
			x.remove();
			x = null;
		}
		for (var pn in infoObj)
		{
			var dt = infosDlDom.appendChild(document.createElement("dt"));
			var dd = infosDlDom.appendChild(document.createElement("dd"));
			dt.appendChild(document.createTextNode(pn));
			dd.appendChild(document.createTextNode(infoObj[pn]));
		}
	}

	Object.defineProperty(this , "infos", {
		get() {
			return infoObj;
		},
		set(obj) {
			infoObj = obj;
			this.reload();
		}
	});
	this.infos = datas || {}; //使用传入data进行初始设定
}
//创建下拉框类
var Select = function(classname, name) {
	var select = document.createElement("select");
	select.className = "pubd-select" + (classname ? " " + classname : "");
	select.name = name;
	select.id = select.name;

	select.add = function(text, value) {
		var opt = new Option(text, value);
		this.options.add(opt);
	}
	select.remove = function(index) {
		var x = this.options.remove(index);
		x = null;
	}

	return select;
};

//创建Aria2类
var Aria2 = (function() {
	var jsonrpc_version = '2.0';

	function get_auth(url) {
		return url.match(/^(?:(?![^:@]+:[^:@\/]*@)[^:\/?#.]+:)?(?:\/\/)?(?:([^:@]*(?::[^:@]*)?)?@)?/)[1];
	}

	function request(jsonrpc_path, method, params, callback, priority) {
		if (callback == undefined) callback = function() { return; }
		var auth = get_auth(jsonrpc_path);
		jsonrpc_path = jsonrpc_path.replace(/^((?![^:@]+:[^:@\/]*@)[^:\/?#.]+:)?(\/\/)?(?:(?:[^:@]*(?::[^:@]*)?)?@)?(.*)/, '$1$2$3'); // auth string not allowed in url for firefox

		var request_obj = {
			jsonrpc: jsonrpc_version,
			method: method,
			id: priority ? "1" : (new Date()).getTime().toString(),
		};
		if (params) request_obj['params'] = params;
		
		if (auth && auth.indexOf('token:') == 0)
		{
			if (method == "system.multicall")
			{ //多项目操作时单独设置token
				params.forEach(function(param){
					param.forEach(function(method){
						method.params.unshift(auth);
					})
				})
			}else
			{
				params.unshift(auth);
			}
		}

		var headers = { "Content-Type": ContentType };
		if (auth && auth.indexOf('token:') != 0) {
			headers.Authorization = "Basic " + btoa(auth);
		}
		GM_xmlhttpRequest({
			url: jsonrpc_path + "?tm=" + (new Date()).getTime().toString(),
			method: "POST",
			responseType: "text",
			data: JSON.stringify(request_obj),
			headers: headers,
			onload: function(response) {
				try {
					var JSONreq = JSON.parse(response.response);
					callback(JSONreq);
				} catch (e) {
					console.error("Aria2发送信息错误", e, response);
					callback(false);
				}
			},
			onerror: function(response) {
				console.error(response);
				callback(false);
			}
		});
	}

	return function(jsonrpc_path) {
		_this = this;
		_this.jsonrpc_path = jsonrpc_path;
		_this.addUri = function(uri, options, callback) {
			request(_this.jsonrpc_path, 'aria2.addUri', [
				[uri, ], options
			], callback);
		};
		_this.addTorrent = function(base64txt, options, callback) {
			request(_this.jsonrpc_path, 'aria2.addTorrent', [base64txt, [], options], callback);
		};
		_this.getVersion = function(callback) {
			request(_this.jsonrpc_path, 'aria2.getVersion', [], callback, true);
		};
		_this.getGlobalOption = function(callback) {
			request(_this.jsonrpc_path, 'aria2.getGlobalOption', [], callback, true);
		};
		_this.system = {
			multicall:function(params,callback){
				request(_this.jsonrpc_path, 'system.multicall', params, callback);
			},
		};
		return this;
	}
})();

/*
 * 自定义函数区
 */
//有默认值的获取设置
function getValueDefault(name, defaultValue) {
	var value = GM_getValue(name);
	if (value != undefined)
		return value;
	else
		return defaultValue;
}
//加入了Auth的网络请求函数
function xhrGenneral(url, onload_suceess_Cb, onload_hasError_Cb, onload_notJson_Cb, onerror_Cb) {
	var headersObj = new HeadersObject();
	var auth = pubd.auth;
	if (auth.needlogin) {
		var token_type = auth.response.token_type.substring(0, 1).toUpperCase() + auth.response.token_type.substring(1);
		headersObj.Authorization = token_type + " " + auth.response.access_token;
	} else {
		console.info("非登录模式获取信息");
	}
	GM_xmlhttpRequest({
		url: url,
		method: "get",
		responseType: "text",
		headers: headersObj,
		onload: function(response) {
			var jo;
			try {
				jo = JSON.parse(response.responseText);
			} catch (e) {
				console.error("错误:返回可能不是JSON格式,或本程序异常", e, response);
				onload_notJson_Cb(response);
				return;
			}

			if (jo)
			{
				//jo.error.message 是JSON字符串的错误信息,Token错误的时候返回的又是普通字符串
				//jo.error.user_message 是单行文本的错误信息
				if (jo.error) {
					if (jo.error.message.indexOf("Error occurred at the OAuth process.") >= 0) {
						console.warn("Token过期,或其他错误",jo);
						reLogin(
							function(){
								xhrGenneral(url, onload_suceess_Cb, onload_hasError_Cb, onload_notJson_Cb, onerror_Cb);
							},
							onload_hasError_Cb
						);
						return;
					}else
					{
						onload_hasError_Cb(jo);
						return;
					}
				} else { //登陆成功
					//console.info("JSON返回成功",jo);
					onload_suceess_Cb(jo);
					return;
				}
			}
		},
		onerror: function(response) {
			console.error("错误:网络请求发送失败", response);
			onerror_Cb(response);
		}
	})
}
//用id来获取动画帧数据
function getUgoiraMeta(iid, onload_suceess_Cb, onload_hasError_Cb, onload_notJson_Cb, onerror_Cb)
{
	xhrGenneral(
		"https://app-api.pixiv.net/v1/ugoira/metadata?illust_id=" + iid,
		onload_suceess_Cb,
		onload_hasError_Cb,
		onload_notJson_Cb,
		onerror_Cb
	)
}
//为了区分设置窗口和保存的设置,产生一个新的下载方案数组
function NewDownSchemeArrayFromJson(jsonarr) {
	if (typeof(jsonarr) == "string") {
		try {
			var jsonarr = JSON.parse(jsonarr);
		} catch (e) {
			console.error("PUBD:拷贝新下载方案数组时失败(是字符串,但不是JSON)", e);
			return false;
		}
	}
	var sarr = new Array();
	if (jsonarr instanceof Array) {
		for (var si = 0; si < jsonarr.length; si++) {
			var scheme = new DownScheme();
			scheme.loadFromJson(jsonarr[si]);
			sarr.push(scheme);
		}
	}
	return sarr;
}
//获取URL参数
function getQueryString(name,url) {
	if (!!(window.URL && window.URLSearchParams))
	{ //浏览器原生支持的API
		const urlObj = new URL(url || document.location);
		return urlObj.searchParams.get(name);
	}else
	{
		const reg = new RegExp(`(?:^|&)${name}=([^&]*)(?:&|$)`, "i");
		const searchStr = url || location.search.substr(1);
		const r = searchStr.match(reg);
		if (r != null)
		{
			return decodeURIComponent(r[1]);
		}else
		{
			return null;
		}
	}
}
//从URL获取图片ID
function getArtworkIdFromImageUrl(url) {
	var regSrc = new RegExp(illustPattern, "ig");
	var regRes = regSrc.exec(url);
	if (regRes) {
		var idRes = /^(\d+)/.exec(regRes[3]);
		return parseInt(idRes[1]);
	}else
		return null;
}
//获取当前用户ID
function getCurrentUserId()
{
	//从URL获取作者ID
	function getUserIdFromUrl(url) {
		var userid = parseInt(getQueryString("id",url),10);
		if (!userid)
		{
			var regSrc = new RegExp("users/(\\d+)", "ig");
			var regRes = regSrc.exec(url.pathname);
			if (regRes) {
				return parseInt(regRes[1],10);
			}
		}
		return userid;
	}
	var userid = getUserIdFromUrl(document.location);
	if(!userid)
	{
		var userMainPageLink = document.querySelector(userMainPageCssPath); //作者主页的“主页”按钮
		//var artWorkLink = document.querySelector(artWorkStarCssPath);
		var userHeadLink = document.querySelector(artWorkUserHeadCssPath);
		if (userMainPageLink) //如果是作者页面
		{
			userid = getUserIdFromUrl(userMainPageLink);
		}
		if (userHeadLink) //如果是作品页面
		{
			userid = getUserIdFromUrl(userHeadLink);
		}else
		{
			userid = thisPageUserid;
		}
	}
	return userid;
}
//检查画师是否存在的函数
function fastStarIndex(userid)
{
	userid = userid || getCurrentUserId();
	return pubd.fastStarList.indexOf(userid);
}
//检查并快速添加画师收藏的函数
function toggleStar(userid)
{
	userid = userid || getCurrentUserId();
	var starIdx = fastStarIndex(userid)
	if (starIdx>=0)
	{ //存在,则删除
		pubd.fastStarList.splice(starIdx,1);
		pubd.start.star.classList.remove("stars");
	}else
	{ //不存在,则添加
		pubd.fastStarList.push(userid);
		pubd.start.star.classList.add("stars");
	}
	GM_setValue("pubd-faststar-list",pubd.fastStarList);
}
//检查是否有画师并改变星星状态
function checkStar()
{
	var starIdx = fastStarIndex()
	if (starIdx>=0)
	{ //存在,则标记
		pubd.start.star.classList.add("stars");
		return true;
	}else
	{ //不存在,则去掉标记
		pubd.start.star.classList.remove("stars");
		return false;
	}
}

//构建开始按钮
function buildbtnStart() {
	var btnStart = document.createElement("div");
	btnStart.id = "pubd-start";
	btnStart.className = "pubd-start";
	//添加图标
	var star = btnStart.star = btnStart.appendChild(document.createElement("i"));
	star.className = "pubd-icon star";
	star.title = "快速收藏当前画师(开发中功能,目前没用)";
	//添加文字
	var caption = btnStart.caption = btnStart.appendChild(document.createElement("div"));
	caption.className = "text";
	caption.innerHTML = "使用PUBD扒图";
	caption.title = "快速下载当前画师";
	//添加文字
	var menu = btnStart.menu = btnStart.appendChild(document.createElement("i"));
	menu.className = "pubd-icon menu";
	menu.title = "PUBD菜单";

	//鼠标移入和按下都起作用
	//btnStart.addEventListener("mouseenter",function(){pubd.menu.show()});
	star.addEventListener("click", function(){toggleStar(); });
	menu.addEventListener("click", function(){pubd.menu.classList.toggle("display-none");});
	caption.addEventListener("click", function(){pubd.menu.downthis.click();});
	return btnStart;
}

//构建开始菜单
function buildbtnMenu() {
	/*
	var menu2 = new pubdMenu();
	menu2.add("子菜单1","",function(){alert("子菜单1")});
	menu2.add("子菜单2","",function(){alert("子菜单2")});
	var menu1 = new pubdMenu();
	menu1.add("子菜单1","",function(){alert("子菜单1")});
	menu1.add("子菜单2","",null,menu2);
	var menu3 = new pubdMenu();
	menu3.add("子菜单1","",function(){alert("子菜单1")});
	menu3.add("子菜单2","",function(){alert("子菜单2")});
	menu3.add("子菜单2","",function(){alert("子菜单3")});
	menu3.add("子菜单2","",function(){alert("子菜单4")});
	var menu4 = new pubdMenu();
	menu4.add("子菜单1","",null,menu3);
	menu4.add("子菜单2","",function(){alert("子菜单2")});
	menu4.add("子菜单2","",function(){alert("子菜单5")});
	menu4.add("子菜单2","",function(){alert("子菜单6")});
	*/
	var menu = new pubdMenu("pubd-menu-main");
	menu.id = "pubd-menu";
	menu.downillust = menu.add("下载当前作品", "pubd-menu-this-illust", function(e) {
		var artWorkLink = document.querySelector(artWorkStarCssPath);
		pubd.dialog.downillust.show(
			(document.body.clientWidth - 500)/2,
			window.pageYOffset+150,
			{id:getQueryString('illust_id',artWorkLink)}
		);
		menu.hide();
	});
	menu.downthis = menu.add("下载该画师所有作品", "pubd-menu-this-user", function(e) {
		pubd.dialog.downthis.show(
			(document.body.clientWidth - 440)/2,
			window.pageYOffset+100,
			{id:getCurrentUserId()}
		);
		menu.hide();
	});
	/*
	menu.add("占位用","",null,menu1);
	menu.add("没功能","",null,menu4);
	menu.add("多个画师下载",null,function()
			{//做成“声音”的设备样子
				alert("这个功能也没有开发")
			}
		);
	*/
	/*
	if (typeof(pixiv.context.userId) != "undefined")
	{
	menu.add("收藏作者","",function()
			{

				pubd.staruser.push(pixiv.context.userId);
				var starStr = JSON.stringify(pubd.staruser);
				GM_setValue("pubd-staruser",starStr); //下载方案

				menu.hide();
			}
		);
	}
	*/
	menu.add(0);
	if (mdev) menu.downmult = menu.add("多画师下载", "pubd-menu-multiple", function(e) {
		pubd.dialog.multiple.show(
			(document.body.clientWidth - 440)/2,
			window.pageYOffset+100
		);
		menu.hide();
	});
	menu.add("选项", "pubd-menu-setting", function(e) {
		pubd.dialog.config.show(
			(document.body.clientWidth - 400)/2,
			window.pageYOffset+50
		);
		menu.hide();
	});
	return menu;
}

//构建设置对话框
function buildDlgConfig() {
	var dlg = new Dialog("PUBD选项 v" + scriptVersion, "pubd-config", "pubd-config");
	dlg.cptBtns.add("反馈", "dlg-btn-debug", "https://github.com/Mapaler/PixivUserBatchDownload/issues");
	dlg.cptBtns.add("?", "dlg-btn-help", "https://github.com/Mapaler/PixivUserBatchDownload/wiki");
	dlg.token_ani = null; //储存Token进度条动画句柄
	var dlgc = dlg.content;

	var dl = document.createElement("dl");
	dlgc.appendChild(dl);
	var dt = document.createElement("dt");
	dl.appendChild(dt);
	var dd = document.createElement("dd");
	dl.appendChild(dd);

	var frm = new Frame("Pixiv访问权限", "pubd-token");
	dd.appendChild(frm);

	var dl_t = document.createElement("dl");
	frm.content.appendChild(dl_t);

	var dd = document.createElement("dd");
	dl_t.appendChild(dd);
	var checkbox = new LabelInput("开启登陆功能,解除浏览限制", "pubd-needlogin", "pubd-needlogin", "checkbox", "1", true);
	dlg.needlogin = checkbox.input;
	dlg.needlogin.onclick = function() {
		if (dlg.needlogin.checked) {
			dlg.token_info.classList.remove("height-none");
			dlg.start_token_animate();
		} else {
			dlg.token_info.classList.add("height-none");
			dlg.stop_token_animate();
		}
		pubd.dialog.login.cptBtns.close.click();
	}
	dd.appendChild(checkbox);

	var a_setting = document.createElement("a");
	a_setting.className = "pubd-browsing-restriction";
	a_setting.href = "http://www.pixiv.net/setting_user.php#over-18";
	a_setting.target = "_blank";
	a_setting.innerHTML = "设置我的账户浏览限制";
	dd.appendChild(a_setting);
	var dd = document.createElement("dd");
	dl_t.appendChild(dd);
	dd.className = "pubd-token-info height-none";
	dlg.token_info = dd;
	var progress = new Progress("pubd-token-expires", true);
	dlg.token_expires = progress;
	dd.appendChild(progress);
	//开始动画
	dlg.start_token_animate = function() {
			//if (!dlg.classList.contains("display-none"))
			//{
			dlg.stop_token_animate();
			requestAnimationFrame(token_animate);
			dlg.token_ani = setInterval(function() { requestAnimationFrame(token_animate) }, 1000);
			//}
		}
		//停止动画
	dlg.stop_token_animate = function() {
			clearInterval(dlg.token_ani);
		};
		//动画具体实现
	function token_animate() {
		var nowdate = new Date();
		var olddate = new Date(pubd.auth.login_date);
		var expires_in = parseInt(pubd.auth.response.expires_in);
		var differ = expires_in - (nowdate - olddate) / 1000;
		var scale = differ / expires_in;
		if (differ > 0) {
			progress.set(scale, 2, "Token有效剩余" + parseInt(differ) + "秒");
		} else {
			progress.set(0, 2, "Token已失效,请重新登录");
			clearInterval(dlg.token_ani);
		}
		//console.log("Token有效剩余" + differ + "秒"); //检测动画后台是否停止
	}

	var ipt = document.createElement("input");
	ipt.type = "button";
	ipt.className = "pubd-tologin";
	ipt.value = "账户登陆";
	ipt.onclick = function(e) {
		pubd.dialog.login.show(
			(document.body.clientWidth - 370)/2,
			window.pageYOffset+200
		);
	};
	dd.appendChild(ipt);

	//“通用分析选项”窗口选项
	var dt = document.createElement("dt");
	dl.appendChild(dt);
	var dd = document.createElement("dd");

	var frm = new Frame("通用分析选项", "pubd-commonanalyseoptions");
	var chk_getugoiraframe = new LabelInput("获取动图帧数", "pubd-getugoiraframe", "pubd-getugoiraframe", "checkbox", "1", true);
	dlg.getugoiraframe = chk_getugoiraframe.input;

	frm.content.appendChild(chk_getugoiraframe);
	dd.appendChild(frm);
	dl.appendChild(dd);

	//“下载该画师”窗口选项
	var dt = document.createElement("dt");
	dl.appendChild(dt);
	var dd = document.createElement("dd");

	var frm = new Frame("下载窗口", "pubd-frm-downthis");
	var chk_autoanalyse = new LabelInput("打开窗口自动获取数据", "pubd-autoanalyse", "pubd-autoanalyse", "checkbox", "1", true);
	dlg.autoanalyse = chk_autoanalyse.input;
	var chk_autodownload = new LabelInput("获取完成自动发送下载", "pubd-autodownload", "pubd-autodownload", "checkbox", "1", true);
	dlg.autodownload = chk_autodownload.input;

	frm.content.appendChild(chk_autoanalyse);
	frm.content.appendChild(chk_autodownload);
	dd.appendChild(frm);
	dl.appendChild(dd);

	//向Aria2的发送模式
	var dt = dl.appendChild(document.createElement("dt"));
	var dd = dl.appendChild(document.createElement("dd"));

	var frm = dd.appendChild(new Frame("向Aria2逐项发送模式", "pubd-frm-termwisetype"));
	var radio0 = frm.content.appendChild(new LabelInput("完全逐项(按图片)", "pubd-termwisetype", "pubd-termwisetype", "radio", "0", true));
	var radio1 = frm.content.appendChild(new LabelInput("半逐项(按作品)", "pubd-termwisetype", "pubd-termwisetype", "radio", "1", true));
	var radio2 = frm.content.appendChild(new LabelInput("不逐项(按作者)", "pubd-termwisetype", "pubd-termwisetype", "radio", "2", true));
	dlg.termwiseType = [radio0.input, radio1.input, radio2.input];

	//“发送完成后,点击通知”窗口选项
	var dt = dl.appendChild(document.createElement("dt"));
	var dd = dl.appendChild(document.createElement("dd"));

	var frm = dd.appendChild(new Frame("发送完成通知", "pubd-frm-clicknotification"));
	var radio0 = frm.content.appendChild(new LabelInput("点击通知什么也不做", "pubd-clicknotification", "pubd-clicknotification", "radio", "0", true));
	var radio1 = frm.content.appendChild(new LabelInput("点击通知激活该窗口", "pubd-clicknotification", "pubd-clicknotification", "radio", "1", true));
	var radio2 = frm.content.appendChild(new LabelInput("点击通知关闭该窗口", "pubd-clicknotification", "pubd-clicknotification", "radio", "2", true));
	var radio3 = frm.content.appendChild(new LabelInput("通知自动消失关闭该窗口", "pubd-clicknotification", "pubd-clicknotification", "radio", "3", true));
	dlg.noticeType = [radio0.input, radio1.input, radio2.input, radio3.input];

	//配置方案储存
	dlg.schemes = null;
	dlg.reloadSchemes = function() { //重新读取所有下载方案
		if (dlg.schemes.length < 1) {
			alert("目前本程序没有任何下载方案,需要正常使用请先新建方案。");
		}
		dlg.downSchemeDom.options.length = 0;
		dlg.schemes.forEach(function(item, index) {
			dlg.downSchemeDom.add(item.name, index);
		})
		if (dlg.downSchemeDom.options.length > 0)
			dlg.selectScheme(0);
	}
	dlg.loadScheme = function(scheme) { //读取一个下载方案
		if (scheme == undefined) {
			dlg.rpcurl.value = "";
			dlg.https2http.checked = false;
			dlg.downfilter.value = "";
			dlg.savedir.value = "";
			dlg.savepath.value = "";
			dlg.textout.value = "";
			dlg.loadMasklistFromArray([]);
		} else {
			dlg.rpcurl.value = scheme.rpcurl;
			dlg.https2http.checked = scheme.https2http;
			dlg.downfilter.value = scheme.downfilter;
			dlg.savedir.value = scheme.savedir;
			dlg.savepath.value = scheme.savepath;
			dlg.textout.value = scheme.textout;
			dlg.loadMasklistFromArray(scheme.masklist);
		}
	}
	dlg.addMask = function(name, logic, content, value) { //向掩码列表添加一个新的掩码
		if (value == undefined)
			value = dlg.masklist.options.length;
		var text = name + " : " + logic + " : " + content;
		var opt = new Option(text, value);
		dlg.masklist.options.add(opt);
	}
	dlg.loadMask = function(mask) { //读取一个掩码到三个文本框,只是用来查看
		dlg.mask_name.value = mask.name;
		dlg.mask_logic.value = mask.logic;
		dlg.mask_content.value = mask.content;
	}
	dlg.loadMasklistFromArray = function(masklist) { //从掩码数组重置掩码列表
			dlg.masklist.length = 0;
			masklist.forEach(function(item, index) {
				dlg.addMask(item.name, item.logic, item.content, index);
			})
		}
		//选择一个方案,同时读取设置
	dlg.selectScheme = function(index) {
			if (index == undefined) index = 0;
			if (dlg.downSchemeDom.options.length < 1 || dlg.downSchemeDom.selectedOptions.length < 1) { return; }
			var scheme = dlg.schemes[index];
			dlg.loadScheme(scheme);
			dlg.downSchemeDom.selectedIndex = index;
		}
		//选择一个掩码,同时读取设置
	dlg.selectMask = function(index) {
		if (dlg.downSchemeDom.options.length < 1 || dlg.downSchemeDom.selectedOptions.length < 1) { return; }
		if (dlg.masklist.options.length < 1 || dlg.masklist.selectedOptions.length < 1) { return; }
		var scheme = dlg.schemes[dlg.downSchemeDom.selectedIndex];
		var mask = scheme.masklist[index];
		dlg.loadMask(mask);
		dlg.masklist.selectedIndex = index;
	}

	//配置方案选择
	var dt = document.createElement("dt");
	dt.innerHTML = "默认下载方案";
	dl.appendChild(dt);
	var dd = document.createElement("dd");
	var slt = new Select("pubd-downscheme");
	slt.onchange = function() {
		dlg.selectScheme(this.selectedIndex);
	};
	dlg.downSchemeDom = slt;
	dd.appendChild(slt);

	var ipt = document.createElement("input");
	ipt.type = "button";
	ipt.className = "pubd-downscheme-new";
	ipt.value = "新建"
	ipt.onclick = function() {
		var schemName = prompt("请输入方案名", "我的方案");
		if (schemName)
		{
			var scheme = new DownScheme(schemName);
			var length = dlg.schemes.push(scheme);
			dlg.downSchemeDom.add(scheme.name, length - 1);
			dlg.downSchemeDom.selectedIndex = length - 1;
			dlg.loadScheme(scheme);
			//dlg.reloadSchemes();
		}
	}
	dd.appendChild(ipt);

	var ipt = document.createElement("input");
	ipt.type = "button";
	ipt.className = "pubd-downscheme-remove";
	ipt.value = "删除"
	ipt.onclick = function() {
		if (dlg.downSchemeDom.options.length < 1) { alert("已经没有方案了"); return; }
		if (dlg.downSchemeDom.selectedOptions.length < 1) { alert("没有选中方案"); return; }
		var index = dlg.downSchemeDom.selectedIndex;
		var c = confirm("你确定要删除“" + dlg.schemes[index].name + "”方案吗?");
		if (c)
		{
			var x = dlg.schemes.splice(index, 1);
			x = null;
			dlg.downSchemeDom.remove(index);
			var index = dlg.downSchemeDom.selectedIndex;
			if (index < 0) dlg.reloadSchemes(); //没有选中的,重置
			else dlg.loadScheme(dlg.schemes[index]);
		}
	}
	dd.appendChild(ipt);
	dl.appendChild(dd);

	//配置方案详情设置
	var dt = document.createElement("dt");
	dl.appendChild(dt);
	var dd = document.createElement("dd");
	dd.className = "pubd-selectscheme-bar";

	var frm = new Frame("当前方案设置", "pubd-selectscheme");

	var dl_ss = document.createElement("dl");

	frm.content.appendChild(dl_ss);
	dd.appendChild(frm);
	dl.appendChild(dd);

	//Aria2 URL

	var dt = document.createElement("dt");
	dl_ss.appendChild(dt);
	dt.innerHTML = "Aria2 JSON-RPC 路径";
	var rpcchk = document.createElement("span"); //显示检查状态用
	rpcchk.className = "pubd-rpcchk-info";
	dlg.rpcchk = rpcchk;
	dlg.rpcchk.runing = false;
	dt.appendChild(rpcchk);
	var dd = document.createElement("dd");
	var rpcurl = document.createElement("input");
	rpcurl.type = "url";
	rpcurl.className = "pubd-rpcurl";
	rpcurl.name = "pubd-rpcurl";
	rpcurl.id = rpcurl.name;
	rpcurl.placeholder = "Aria2的信息接收路径"
	rpcurl.onchange = function() {
		dlg.rpcchk.innerHTML = "";
		dlg.rpcchk.runing = false;
		if (dlg.downSchemeDom.selectedOptions.length < 1) { return; }
		var schemeIndex = dlg.downSchemeDom.selectedIndex;
		dlg.schemes[schemeIndex].rpcurl = rpcurl.value;
	}
	dlg.rpcurl = rpcurl;
	dd.appendChild(rpcurl);

	var ipt = document.createElement("input");
	ipt.type = "button";
	ipt.className = "pubd-rpcchk";
	ipt.value = "检查路径"
	ipt.onclick = function() {
		if (dlg.rpcchk.runing) return;
		if (dlg.rpcurl.value.length < 1) {
			dlg.rpcchk.innerHTML = "路径为空";
			return;
		}
		dlg.rpcchk.innerHTML = "正在连接...";
		dlg.rpcchk.runing = true;
		var aria2 = new Aria2(dlg.rpcurl.value);
		aria2.getVersion(function(rejo) {
			if (rejo)
				dlg.rpcchk.innerHTML = "发现Aria2 ver" + rejo.result.version;
			else
				dlg.rpcchk.innerHTML = "Aria2连接失败";
			dlg.rpcchk.runing = false;
		});
	}
	dd.appendChild(ipt);
	dl_ss.appendChild(dd);

	//额外设置,https转http
	var dt = document.createElement("dt");
	dl_ss.appendChild(dt);
	var dd = document.createElement("dd");
	var chk_https2http = new LabelInput("图片网址https转http", "pubd-https2http", "pubd-https2http", "checkbox", "1", true, "某些Linux没有正确安装新版OpenSSL,https的图片链接会下载失败。");
	dlg.https2http = chk_https2http.input;
	dlg.https2http.onchange = function() {
		if (dlg.downSchemeDom.selectedOptions.length < 1) { return; }
		var schemeIndex = dlg.downSchemeDom.selectedIndex;
		dlg.schemes[schemeIndex].https2http = this.checked;
	}
	dd.appendChild(chk_https2http);
	dl_ss.appendChild(dd);

	//下载过滤
	var dt = dl_ss.appendChild(document.createElement("dt"));
	dt.innerHTML = "下载过滤器";
	var dta = dt.appendChild(document.createElement("a"));
	dta.className = "pubd-help-link";
	dta.innerHTML = "(?)";
	dta.href = "https://github.com/Mapaler/PixivUserBatchDownload/wiki/%E4%B8%8B%E8%BD%BD%E8%BF%87%E6%BB%A4%E5%99%A8";
	dta.target = "_blank";
	var dd = document.createElement("dd");
	var downfilter = document.createElement("input");
	downfilter.type = "text";
	downfilter.className = "pubd-downfilter";
	downfilter.name = "pubd-downfilter";
	downfilter.id = downfilter.name;
	downfilter.placeholder = "符合条件的图片将不会被发送到Aria2"
	downfilter.onchange = function() {
		if (dlg.downSchemeDom.selectedOptions.length < 1) { return; }
		var schemeIndex = dlg.downSchemeDom.selectedIndex;
		dlg.schemes[schemeIndex].downfilter = downfilter.value;
	}
	dlg.downfilter = downfilter;
	dd.appendChild(downfilter);
	dl_ss.appendChild(dd);

	//下载目录
	var dt = document.createElement("dt");
	dl_ss.appendChild(dt);
	dt.innerHTML = "下载目录";
	var dd = document.createElement("dd");
	var savedir = document.createElement("input");
	savedir.type = "text";
	savedir.className = "pubd-savedir";
	savedir.name = "pubd-savedir";
	savedir.id = savedir.name;
	savedir.placeholder = "文件下载到的目录"
	savedir.onchange = function() {
		if (dlg.downSchemeDom.selectedOptions.length < 1) { return; }
		var schemeIndex = dlg.downSchemeDom.selectedIndex;
		dlg.schemes[schemeIndex].savedir = savedir.value;
	}
	dlg.savedir = savedir;
	dd.appendChild(savedir);
	dl_ss.appendChild(dd);

	//保存路径
	var dt = dl_ss.appendChild(document.createElement("dt"));
	dt.innerHTML = "保存路径";
	var dta = dt.appendChild(document.createElement("a"));
	dta.className = "pubd-help-link";
	dta.innerHTML = "(?)";
	dta.href = "https://github.com/Mapaler/PixivUserBatchDownload/wiki/%E6%8E%A9%E7%A0%81";
	dta.target = "_blank";
	var dd = document.createElement("dd");
	var savepath = document.createElement("input");
	savepath.type = "text";
	savepath.className = "pubd-savepath";
	savepath.name = "pubd-savepath";
	savepath.id = savepath.name;
	savepath.placeholder = "分组保存的文件夹和文件名"
	savepath.onchange = function() {
		if (dlg.downSchemeDom.selectedOptions.length < 1) { return; }
		var schemeIndex = dlg.downSchemeDom.selectedIndex;
		dlg.schemes[schemeIndex].savepath = savepath.value;
	}
	dlg.savepath = savepath;
	dd.appendChild(savepath);
	dl_ss.appendChild(dd);

	//输出文本
	var dt = dl_ss.appendChild(document.createElement("dt"));
	dt.innerHTML = "文本输出模式格式";
	var dta = dt.appendChild(document.createElement("a"));
	dta.className = "pubd-help-link";
	dta.innerHTML = "(?)";
	dta.href = "https://github.com/Mapaler/PixivUserBatchDownload/wiki/%e9%80%89%e9%a1%b9%e7%aa%97%e5%8f%a3#%E6%96%87%E6%9C%AC%E8%BE%93%E5%87%BA%E6%A8%A1%E5%BC%8F%E6%A0%BC%E5%BC%8F";
	dta.target = "_blank";
	var dd = document.createElement("dd");
	dd.className = "pubd-textout-bar";
	var textout = document.createElement("textarea");
	textout.className = "pubd-textout";
	textout.name = "pubd-textout";
	textout.id = textout.name;
	textout.placeholder = "直接输出文本信息时的格式"
	textout.wrap = "off";
	textout.onchange = function() {
		if (dlg.downSchemeDom.selectedOptions.length < 1) { return; }
		var schemeIndex = dlg.downSchemeDom.selectedIndex;
		dlg.schemes[schemeIndex].textout = textout.value;
	}
	dlg.textout = textout;
	dd.appendChild(textout);
	dl_ss.appendChild(dd);


	//自定义掩码
	var dt = dl_ss.appendChild(document.createElement("dt"));
	dt.innerHTML = "自定义掩码";
	var dta = dt.appendChild(document.createElement("a"));
	dta.className = "pubd-help-link";
	dta.innerHTML = "(?)";
	dta.href = "https://github.com/Mapaler/PixivUserBatchDownload/wiki/%E8%87%AA%E5%AE%9A%E4%B9%89%E6%8E%A9%E7%A0%81";
	dta.target = "_blank";
	var dd = document.createElement("dd");
	dl_ss.appendChild(dd);
	//▼掩码名
	var ipt = document.createElement("input");
	ipt.type = "text";
	ipt.className = "pubd-mask-name";
	ipt.name = "pubd-mask-name";
	ipt.id = ipt.name;
	ipt.placeholder = "自定义掩码名";
	dlg.mask_name = ipt;
	dd.appendChild(ipt);
	//▲掩码名
	//▼执行条件
	var ipt = document.createElement("input");
	ipt.type = "text";
	ipt.className = "pubd-mask-logic";
	ipt.name = "pubd-mask-logic";
	ipt.id = ipt.name;
	ipt.placeholder = "执行条件";
	dlg.mask_logic = ipt;
	dd.appendChild(ipt);
	//▲执行条件
	var ipt = document.createElement("input");
	ipt.type = "button";
	ipt.className = "pubd-mask-add";
	ipt.value = "+";
	ipt.onclick = function() { //增加自定义掩码
		if (dlg.downSchemeDom.selectedOptions.length < 1) { alert("没有选中下载方案"); return; }
		if (dlg.mask_name.value.length < 1) { alert("掩码名称为空"); return; }
		if (dlg.mask_logic.value.length < 1) { alert("执行条件为空"); return; }
		if (dlg.mask_content.value.indexOf("%{" + dlg.mask_logic.value + "}")>=0) { alert("该掩码调用自身,会形成死循环。"); return; }
		var schemeIndex = dlg.downSchemeDom.selectedIndex;
		dlg.schemes[schemeIndex].maskAdd(dlg.mask_name.value, dlg.mask_logic.value, dlg.mask_content.value);
		dlg.addMask(dlg.mask_name.value, dlg.mask_logic.value, dlg.mask_content.value);
		dlg.mask_name.value = dlg.mask_logic.value = dlg.mask_content.value = "";
	}
	dd.appendChild(ipt);
	var mask_remove = document.createElement("input");
	mask_remove.type = "button";
	mask_remove.className = "pubd-mask-remove";
	mask_remove.value = "-";
	mask_remove.onclick = function() { //删除自定义掩码
		if (dlg.downSchemeDom.selectedOptions.length < 1) { alert("没有选中下载方案"); return; }
		if (dlg.masklist.options.length < 1) { alert("已经没有掩码了"); return; }
		if (dlg.masklist.selectedOptions.length < 1) { alert("没有选中掩码"); return; }
		var schemeIndex = dlg.downSchemeDom.selectedIndex;
		var maskIndex = dlg.masklist.selectedIndex;
		dlg.schemes[schemeIndex].maskRemove(maskIndex);
		dlg.masklist.remove(maskIndex);
		for (var mi = maskIndex; mi < dlg.masklist.options.length; mi++) {
			dlg.masklist.options[mi].value = mi;
		}
	}
	dd.appendChild(mask_remove);

	//▼掩码内容
	var ipt = document.createElement("input");
	ipt.type = "text";
	ipt.className = "pubd-mask-content";
	ipt.name = "pubd-mask-content";
	ipt.id = ipt.name;
	ipt.placeholder = "掩码内容";
	dlg.mask_content = ipt;
	dd.appendChild(ipt);
	//▲掩码内容
	dl_ss.appendChild(dd);

	//▼掩码列表
	var dd = document.createElement("dd");
	dd.className = "pubd-mask-list-bar";
	var masklist = new Select("pubd-mask-list", "pubd-mask-list")
	masklist.size = 5;
	masklist.onchange = function() { //读取选中的掩码
		dlg.selectMask(this.selectedIndex);
	}
	dlg.masklist = masklist;
	dd.appendChild(masklist);
	//▲掩码列表
	dl_ss.appendChild(dd);

	//保存按钮栏
	var dt = document.createElement("dt");
	dl.appendChild(dt);
	var dd = document.createElement("dd");
	dd.className = "pubd-config-savebar"
	var ipt = document.createElement("input");
	ipt.type = "button";
	ipt.className = "pubd-reset";
	ipt.value = "清空选项"
	ipt.onclick = function() {
		if (confirm("您确定要将PUBD保存的所有设置,以及方案全部删除吗?\n(⚠️不可恢复)")==true){
			dlg.reset();
			return true;
		}else{
			return false;
		}
	}
	dd.appendChild(ipt);
	var ipt = document.createElement("input");
	ipt.type = "button";
	ipt.className = "pubd-save";
	ipt.value = "保存选项"
	ipt.onclick = function() {
		dlg.save();
	}
	dd.appendChild(ipt);
	dl.appendChild(dd);

	//保存设置函数
	dlg.save = function() {
			pubd.auth.needlogin = dlg.needlogin.checked;
			pubd.auth.save();

			//作品发送完成后,如何处理通知
			var noticeType = 0;
			dlg.noticeType.some(function(item){
				if (item.checked) noticeType = parseInt(item.value);
				return item.checked;
			});
			//逐项发送模式
			var termwiseType = 2;
			dlg.termwiseType.some(function(item){
				if (item.checked) termwiseType = parseInt(item.value);
				return item.checked;
			});

			GM_setValue("pubd-getugoiraframe", dlg.getugoiraframe.checked); //获取动图帧数
			GM_setValue("pubd-autoanalyse", dlg.autoanalyse.checked); //自动分析
			GM_setValue("pubd-autodownload", dlg.autodownload.checked); //自动下载
			GM_setValue("pubd-noticeType", noticeType); //处理通知
			GM_setValue("pubd-termwiseType", termwiseType); //逐项发送
			GM_setValue("pubd-downschemes", dlg.schemes); //下载方案
			GM_setValue("pubd-defaultscheme", dlg.downSchemeDom.selectedIndex); //默认方案
			GM_setValue("pubd-configversion", pubd.configVersion); //设置版本

			GM_notification({text:"设置已保存", title:scriptName, image:scriptIcon});
			pubd.downSchemes = NewDownSchemeArrayFromJson(dlg.schemes);
			pubd.dialog.downthis.reloadSchemes();
		}
		//重置设置函数
	dlg.reset = function() {
			GM_deleteValue("pubd-auth"); //登陆相关信息
			GM_deleteValue("pubd-getugoiraframe"); //获取动图帧数
			GM_deleteValue("pubd-autoanalyse"); //自动分析
			GM_deleteValue("pubd-autodownload"); //自动下载
			GM_deleteValue("pubd-noticeType"); //处理通知
			GM_deleteValue("pubd-termwiseType"); //逐项发送
			GM_deleteValue("pubd-downschemes"); //下载方案
			GM_deleteValue("pubd-defaultscheme"); //默认方案
			GM_deleteValue("pubd-configversion"); //设置版本
			GM_notification({text:"已清空重置设置", title:scriptName, image:scriptIcon});
		}
		//窗口关闭
	dlg.close = function() {
		dlg.stop_token_animate();
	};
	//关闭窗口按钮
	dlg.cptBtns.close.addEventListener("click", dlg.close);
	//窗口初始化
	dlg.initialise = function() {
		dlg.needlogin.checked = pubd.auth.needlogin;
		if (pubd.auth.needlogin) //如果要登陆,就显示Token区域,和动画
		{
			dlg.token_info.classList.remove("height-none");
			dlg.start_token_animate();
		} else {
			dlg.token_info.classList.add("height-none");
		}

		dlg.getugoiraframe.checked = getValueDefault("pubd-getugoiraframe", true);
		dlg.autoanalyse.checked = getValueDefault("pubd-autoanalyse", false);
		dlg.autodownload.checked = getValueDefault("pubd-autodownload", false);
		(dlg.noticeType[parseInt(getValueDefault("pubd-noticeType", 0))] || dlg.noticeType[0]).checked = true;
		(dlg.termwiseType[parseInt(getValueDefault("pubd-termwiseType", 2))] || dlg.termwiseType[2]).checked = true;

		dlg.schemes = NewDownSchemeArrayFromJson(pubd.downSchemes);
		dlg.reloadSchemes();
		dlg.selectScheme(getValueDefault("pubd-defaultscheme", 0));
		//ipt_token.value = pubd.auth.response.access_token;
	};
	return dlg;
}

//重新登陆
function reLogin(onload_suceess_Cb,onerror_Cb)
{
	var dlgLogin = pubd.dialog.login;
	dlgLogin.show((document.body.clientWidth - 370)/2, window.pageYOffset+200);
	var defaultError = {error:{message:"自动登录失败"}};
	if (pubd.auth.save_account) {
		dlgLogin.error.replace("正在自动登陆");

		pubd.auth.login(
			function(jore) { //onload_suceess_Cb
				dlgLogin.error.replace("登录成功");
				//pubd.dialog.config.start_token_animate();
				dlgLogin.cptBtns.close.click();

				//如果设置窗口运行着的话还启动动画
				if (!pubd.dialog.config.classList.contains("display-none"))
					pubd.dialog.config.start_token_animate();
				//调用成功后函数
				onload_suceess_Cb(jore);
			},
			function(jore) { //onload_haserror_Cb //返回错误消息
				dlgLogin.error.replace(["错误代码:" + jore.errors.system.code, jore.errors.system.message]);
				onerror_Cb(defaultError);
			},
			function(jore) { //onload_notjson_Cb //返回不是JSON
				dlgLogin.error.replace("返回不是JSON,或本程序异常");
				onerror_Cb(defaultError);
			},
			function(jore) { //onerror_Cb //网络请求发生错误
				dlgLogin.error.replace("网络请求发生错误");
				onerror_Cb(defaultError);
			}
		);
	}else
	{
		dlgLogin.error.replace("请手动登陆后重新执行");
		onerror_Cb(defaultError);
	}
}

//构建登陆对话框
function buildDlgLogin() {
	var dlg = new Dialog("登陆账户", "pubd-login", "pubd-login");

	var dlgc = dlg.content;
	//Logo部分
	var logo_box = document.createElement("div");
	logo_box.className = "logo-box";
	var logo = document.createElement("div");
	logo.className = "logo";
	logo_box.appendChild(logo);
	var catchphrase = document.createElement("div");
	catchphrase.className = "catchphrase";
	catchphrase.innerHTML = "登陆获取你的账户许可,解除年龄限制";
	logo_box.appendChild(catchphrase);
	dlgc.appendChild(logo_box);
	//实际登陆部分
	var container_login = document.createElement("div");
	container_login.className = "container-login";

	var input_field_group = document.createElement("div");
	input_field_group.className = "input-field-group";
	container_login.appendChild(input_field_group);
	var input_field1 = document.createElement("div");
	input_field1.className = "input-field";
	var pid = document.createElement("input");
	pid.type = "text";
	pid.className = "pubd-username";
	pid.name = "pubd-username";
	pid.id = pid.name;
	pid.placeholder = "邮箱地址/pixiv ID";
	dlg.pid = pid;
	input_field1.appendChild(pid);
	input_field_group.appendChild(input_field1);
	var input_field2 = document.createElement("div");
	input_field2.className = "input-field";
	var pass = document.createElement("input");
	pass.type = "password";
	pass.className = "pubd-password";
	pass.name = "pubd-password";
	pass.id = pass.name;
	pass.placeholder = "密码";
	dlg.pass = pass;
	input_field2.appendChild(pass);
	input_field_group.appendChild(input_field2);

	var error_msg_list = document.createElement("ul"); //登陆错误信息
	error_msg_list.className = "error-msg-list";
	container_login.appendChild(error_msg_list);

	var submit = document.createElement("button");
	submit.className = "submit";
	submit.innerHTML = "登陆";
	container_login.appendChild(submit);

	var signup_form_nav = document.createElement("div");
	signup_form_nav.className = "signup-form-nav";
	container_login.appendChild(signup_form_nav);
	var checkbox = new LabelInput("记住账号密码(警告:明文保存于本地)", "pubd-remember", "pubd-remember", "checkbox", "1", true);
	dlg.remember = checkbox.input;
	signup_form_nav.appendChild(checkbox);
	dlgc.appendChild(container_login);

	submit.onclick = function() {
			dlg.error.replace("登陆中···");

			pubd.auth.newAccount(pid.value, pass.value, dlg.remember.checked);

			pubd.auth.login(
				function(jore) { //onload_suceess_Cb
					dlg.error.replace("登陆成功");
					pubd.dialog.config.start_token_animate();
				},
				function(jore) { //onload_haserror_Cb //返回错误消息
					dlg.error.replace(["错误代码:" + jore.errors.system.code, jore.errors.system.message]);
				},
				function(re) { //onload_notjson_Cb //返回不是JSON
					dlg.error.replace("返回不是JSON,或本程序异常");
				},
				function(re) { //onerror_Cb //网络请求发生错误
					dlg.error.replace("网络请求发生错误");
				}
			);
		}
		//添加错误功能
	error_msg_list.clear = function() {
		this.innerHTML = ""; //清空当前信息
	}
	error_msg_list.add = function(text) {
		var error_msg_list_item = document.createElement("li");
		error_msg_list_item.className = "error-msg-list-item";
		error_msg_list_item.innerHTML = text;
		this.appendChild(error_msg_list_item);
	}
	error_msg_list.adds = function(arr) {
		arr.forEach(
			function(item) {
				error_msg_list.add(item);
			}
		)
	}
	error_msg_list.replace = function(text) {
		this.clear();
		if (typeof(text) == "object") //数组
			this.adds(text);
		else //单文本
			this.add(text);
	}
	dlg.error = error_msg_list;
	//窗口关闭
	dlg.close = function() {
		pubd.auth.newAccount(pid.value, pass.value, dlg.remember.checked);
		pubd.auth.save();
	};
	//关闭窗口按钮
	dlg.cptBtns.close.addEventListener("click", dlg.close);
	//窗口初始化
	dlg.initialise = function() {
		dlg.remember.checked = pubd.auth.save_account;
		pid.value = pubd.auth.username || "";
		pass.value = pubd.auth.password || "";
		error_msg_list.clear();
	};
	return dlg;
}

//构建通用下载对话框
function buildDlgDown(caption, classname, id) {
	var dlg = new Dialog(caption, classname, id);

	var dl = dlg.content.appendChild(document.createElement("dl"));

	var dt = document.createElement("dt");
	dl.appendChild(dt);
	dt.innerHTML = ""; //用户头像等信息
	var dd = document.createElement("dd");
	dlg.infoCard = new InfoCard(); //创建信息卡
	dd.appendChild(dlg.infoCard.dom);
	dl.appendChild(dd);

	var dt = document.createElement("dt");
	dl.appendChild(dt);
	dt.innerHTML = "进程日志";

	var dd = document.createElement("dd");
	var ipt = document.createElement("textarea");
	ipt.readOnly = true;
	ipt.className = "pubd-down-log";
	ipt.wrap = "off";
	dlg.logTextarea = ipt;
	dd.appendChild(ipt);
	dl.appendChild(dd);

	//下载方案
	dlg.schemes = null;

	dlg.reloadSchemes = function() { //重新读取所有下载方案
		dlg.schemes = pubd.downSchemes;

		dlg.downSchemeDom.options.length = 0;
		dlg.schemes.forEach(function(item, index) {
			dlg.downSchemeDom.add(item.name, index);
		})
		if (getValueDefault("pubd-defaultscheme",0) >= 0)
			dlg.selectScheme(getValueDefault("pubd-defaultscheme",0));
		else if (dlg.downSchemeDom.options.length > 0)
			dlg.selectScheme(0);
	}

	//选择一个方案,同时读取设置
	dlg.selectScheme = function(index) {
		if (index == undefined) index = 0;
		if (dlg.downSchemeDom.options.length < 1 || dlg.downSchemeDom.selectedOptions.length < 1) { return; }
		dlg.downSchemeDom.selectedIndex = index;
	}

	var dt = document.createElement("dt");
	dl.appendChild(dt);
	dt.innerHTML = "选择下载方案";
	var dd = document.createElement("dd");
	var slt = new Select("pubd-downscheme");
	dlg.downSchemeDom = slt;
	dd.appendChild(slt);
	dl.appendChild(dd);

	//下载按钮栏
	var dt = document.createElement("dt");
	dl.appendChild(dt);
	var dd = document.createElement("dd");
	dd.className = "pubd-downthis-downbar"

	var textdown = document.createElement("input");
	textdown.type = "button";
	textdown.className = "pubd-textdown";
	textdown.value = "输出\n文本";
	textdown.onclick = function() {
		dlg.textdownload();
	}
	textdown.disabled = true;
	dlg.textdown = textdown;
	dd.appendChild(textdown);

	var startdown = document.createElement("input");
	startdown.type = "button";
	startdown.className = "pubd-startdown";
	startdown.value = "发送到Aria2";
	startdown.onclick = function() {
		dlg.startdownload();
	}
	startdown.disabled = true;
	dlg.startdown = startdown;
	dd.appendChild(startdown);
	dl.appendChild(dd);

	//文本输出栏
	var dt = document.createElement("dt");
	dl.appendChild(dt);
	var dd = document.createElement("dd");
	dd.className = "pubd-down-textout-bar"
	dl.appendChild(dd);

	var ipt = document.createElement("textarea");
	ipt.readOnly = true;
	ipt.className = "pubd-down-textout display-none";
	ipt.wrap = "off";
	dlg.textoutTextarea = ipt;
	dd.appendChild(ipt);

	//显示日志相关
	dlg.logArr = []; //用于储存一行一行的日志信息。
	dlg.logClear = function() {
		dlg.logArr.length = 0;
		this.logTextarea.value = "";
	};
	dlg.log = function(text) {
		dlg.logArr.push(text);
		this.logTextarea.value = this.logArr.join("\n");
		this.logTextarea.scrollTop = this.logTextarea.scrollHeight;
	};

	return dlg;
}

//构建当前画师下载对话框
function buildDlgDownThis(userid) {
	//一个用户的信息
	var UserInfo = function() {
		this.done = false; //是否已完成用户信息获取
		this.info = {
			profile: null,
			user: null,
		};
		this.illusts = new Works();
		this.bookmarks = new Works();
	}

	var dlg = new buildDlgDown("下载当前画师", "pubd-down pubd-downthis", "pubd-downthis");
	dlg.infoCard.infos = {"ID":userid};

	dlg.user = new UserInfo();
	dlg.works = null; //当前处理对象

	var dt = document.createElement("dt");
	var dd = document.createElement("dd");
	dlg.infoCard.dom.insertAdjacentElement("afterend",dt);
	dt.insertAdjacentElement("afterend",dd);

	var frm = dd.appendChild(new Frame("下载内容"));
	var radio1 = frm.content.appendChild(new LabelInput("他的作品", "pubd-down-content", "pubd-down-content", "radio", "0", true));
	var radio2 = frm.content.appendChild(new LabelInput("他的收藏", "pubd-down-content", "pubd-down-content", "radio", "1", true));
	dlg.dcType = [radio1.input, radio2.input];
	radio1.input.onclick = function() { reAnalyse(this) };
	radio2.input.onclick = function() { reAnalyse(this) };

	function reAnalyse(radio) {
		if (radio.checked == true) {
			if (radio.value == 0)
				dlg.user.bookmarks.break = true; //radio值为0,使收藏中断
			else
				dlg.user.illusts.break = true; //radio值为1,使作品中断

			dlg.analyse(radio.value, dlg.infoCard.infos["ID"]);
		}
	}

	var dt = document.createElement("dt");
	dd.insertAdjacentElement("afterend",dt);
	dt.innerHTML = "信息获取进度";
	var dd = document.createElement("dd");
	dt.insertAdjacentElement("afterend",dd);
	var progress = new Progress();
	dlg.progress = progress;
	dd.appendChild(progress);

	var btnBreak = document.createElement("input");
	btnBreak.type = "button";
	btnBreak.className = "pubd-breakdown";
	btnBreak.value = "中断操作";
	btnBreak.onclick = function() {
		dlg.user.illusts.break = true; //使作品中断
		dlg.user.bookmarks.break = true; //使收藏中断
		pubd.downbreak = true; //使下载中断
	}
	dlg.logTextarea.parentNode.previousElementSibling.appendChild(btnBreak);

	//分析
	dlg.analyse = function(contentType, userid, callbackAfterAnalyse) {
			if (!userid) {dlg.log("错误:没有用户ID。"); return;}
			contentType = contentType == undefined ? 0 : parseInt(contentType);
			var works = contentType == 0 ? dlg.user.illusts : dlg.user.bookmarks; //将需要分析的数据储存到works里
			dlg.works = works;

			if (works.runing) {
				dlg.log("已经在进行分析操作了");
				return;
			}
			works.break = false; //暂停flag为false
			works.runing = true; //运行状态为true

			dlg.textdown.disabled = true; //禁用下载按钮
			dlg.startdown.disabled = true; //禁用输出文本按钮
			dlg.progress.set(0); //进度条归零
			dlg.logClear(); //清空日志

			//根据用户信息是否存在,决定分析用户还是图像
			if (!dlg.user.done) {
				startAnalyseUser(userid, contentType);
			} else {
				dlg.log("ID:" + userid + " 用户信息已存在");
				startAnalyseWorks(dlg.user, contentType); //开始获取第一页
			}

			function startAnalyseUser(userid, contentType) {

				dlg.log("开始获取ID为 " + userid + " 的用户信息");
				xhrGenneral(
					"https://app-api.pixiv.net/v1/user/detail?user_id=" + userid,
					function(jore) { //onload_suceess_Cb
						works.runing = true;
						dlg.user.done = true;
						dlg.user.info = Object.assign(dlg.user.info, jore);
						dlg.infoCard.thumbnail = jore.user.profile_image_urls.medium.replace(/_170./g, ".");//将170大小的头像换成原本大小
						dlg.infoCard.infos = Object.assign(dlg.infoCard.infos, {
							"昵称": jore.user.name,
							"作品投稿数": jore.profile.total_illusts + jore.profile.total_manga,
							"公开收藏数": jore.profile.total_illust_bookmarks_public,
						});
						startAnalyseWorks(dlg.user, contentType); //分析完成后开始获取第一页
					},
					function(jore) { //onload_haserror_Cb //返回错误消息
						works.runing = false;
						dlg.log("错误信息:" + (jore.error.message || jore.error.user_message));
						dlg.textdown.disabled = false; //错误暂停时,可以操作目前的进度。
						dlg.startdown.disabled = false;
						return;
					},
					function(re) { //onload_notjson_Cb //返回不是JSON
						dlg.log("错误:返回不是JSON,或本程序异常");
						works.runing = false;
						dlg.textdown.disabled = false; //错误暂停时,可以操作目前的进度。
						dlg.startdown.disabled = false;
					},
					function(re) { //onerror_Cb //网络请求发生错误
						dlg.log("错误:网络请求发生错误");
						works.runing = false;
						dlg.textdown.disabled = false; //错误暂停时,可以操作目前的进度。
						dlg.startdown.disabled = false;
					}
				);
			}

			//开始分析作品的前置操作
			function startAnalyseWorks(user, contentType) {
				var uInfo = user.info;
				var works, total, contentName, apiurl;
				//获取作品,contentType == 0,获取收藏,contentType == 1
				if (contentType == 0) {
					works = user.illusts;
					total = uInfo.profile.total_illusts + uInfo.profile.total_manga;
					contentName = "作品";
					apiurl = "https://app-api.pixiv.net/v1/user/illusts?user_id=" + uInfo.user.id;
				} else {
					works = user.bookmarks;
					total = uInfo.profile.total_illust_bookmarks_public;
					contentName = "收藏";
					apiurl = "https://app-api.pixiv.net/v1/user/bookmarks/illust?user_id=" + uInfo.user.id + "&restrict=public";
				}
				if (works.item.length > 0) { //断点续传
					dlg.log(contentName + " 断点续传进度 " + works.item.length + "/" + total);
					dlg.progress.set(works.item.length / total); //设置当前下载进度
					apiurl = works.next_url;
				}
				analyseWorks(user, contentType, apiurl); //开始获取第一页
			}
			//分析作品递归函数
			function analyseWorks(user, contentType, apiurl) {
				var uInfo = user.info;
				var works, total, contentName;
				if (contentType == 0) {
					works = user.illusts;
					total = uInfo.profile.total_illusts + uInfo.profile.total_manga;
					contentName = "作品";
				} else {
					works = user.bookmarks;
					total = uInfo.profile.total_illust_bookmarks_public;
					contentName = "收藏";
				}
				if (works.done) {
					//返回所有动图
					var ugoiras = works.item.filter(function(item) {
						return item.type == "ugoira";
					})
					dlg.log("共存在 " + ugoiras.length + " 件动图");
					if (ugoiras.some(function(item) { //如果有没有帧数据的动图
							return item.ugoira_metadata == undefined;
						})) {
						if (!getValueDefault("pubd-getugoiraframe",true)) {
							dlg.log("由于用户设置,跳过获取动图帧数。");
						} else {
							analyseUgoira(works, ugoiras, function() { //开始分析动图
								analyseWorks(user, contentType, apiurl) //开始获取下一页
							});
							return;
						}
					}//没有动图则继续
					
					if (works.item.length < total)
						dlg.log("可能因为权限原因,无法获取到所有 " + contentName);

					//计算一下总页数
					works.picCount = works.item.reduce(function(pV,cItem){
						var page = cItem.page_count;
						if (cItem.type == "ugoira" && cItem.ugoira_metadata) //动图
						{
							page = cItem.ugoira_metadata.frames.length;
						}
						return pV+=page;
					},0);

					dlg.log(contentName + " 共 " + works.item.length + " 件(约 " + works.picCount + " 张图片)已获取完毕。");
					dlg.progress.set(1);
					works.runing = false;
					works.next_url = "";
					dlg.textdown.disabled = false;
					dlg.startdown.disabled = false;
					
					if (callbackAfterAnalyse) callbackAfterAnalyse();
					return;
				}
				if (works.break) {
					dlg.log("检测到 " + contentName + " 中断进程命令");
					works.break = false;
					works.runing = false;
					dlg.textdown.disabled = false; //启用按钮,中断暂停时,可以操作目前的进度。
					dlg.startdown.disabled = false;
					return;
				}

				xhrGenneral(
					apiurl,
					function(jore) { //onload_suceess_Cb
						works.runing = true;
						var illusts = jore.illusts;
						for (var ii = 0, ii_len = illusts.length; ii < ii_len; ii++) {
							var work = illusts[ii];
							var original;
							if (work.page_count > 1) { /*漫画多图*/
								original = work.meta_pages[0].image_urls.original;
							} else { /*单张图片或动图,含漫画单图*/
								original = work.meta_single_page.original_image_url;
							}
							var regSrc = new RegExp(illustPattern, "ig");
							var regRes = regSrc.exec(original);
							if (regRes) {
								//然后添加扩展名等
								work.url_without_page = regRes[1];
								work.domain = regRes[2];
								work.filename = regRes[3];
								work.token = regRes[4];
								work.extention = regRes[5];
							} else {
								var regSrcL = new RegExp(limitingPattern, "ig");
								var regResL = regSrcL.exec(original);
								if (regResL) {
									dlg.log(contentName + " " + work.id + " 非公开,无权获取下载地址。");
									//console.log(work);
									work.url_without_page = regResL[1];
									work.domain = regResL[2];
									work.filename = regResL[3];
									work.token = regResL[4];
									work.extention = regResL[5];
								} else {
									dlg.log(contentName + " " + work.id + " 原图格式未知。");
								}
							}

							works.item.push(work);
						}
						dlg.log(contentName + " 获取进度 " + works.item.length + "/" + total);
						if (works == dlg.works) dlg.progress.set(works.item.length / total); //如果没有中断则设置当前下载进度
						if (jore.next_url) { //还有下一页
							works.next_url = jore.next_url;
						} else { //没有下一页
							works.done = true;
						}
						analyseWorks(user, contentType, jore.next_url); //开始获取下一页
					},
					function(jore) { //onload_haserror_Cb //返回错误消息
						works.runing = false;
						dlg.log("错误信息:" + (jore.error.message || jore.error.user_message));
						dlg.textdown.disabled = false; //错误暂停时,可以操作目前的进度。
						dlg.startdown.disabled = false;
						return;
					},
					function(re) { //onload_notjson_Cb //返回不是JSON
						dlg.log("错误:返回不是JSON,或本程序异常");
						works.runing = false;
						dlg.textdown.disabled = false; //错误暂停时,可以操作目前的进度。
						dlg.startdown.disabled = false;
					},
					function(re) { //onerror_Cb //网络请求发生错误
						dlg.log("错误:网络请求发生错误");
						works.runing = false;
						dlg.textdown.disabled = false; //错误暂停时,可以操作目前的进度。
						dlg.startdown.disabled = false;
					}
				)
			}

			function analyseUgoira(works, ugoirasItems, callback) {
				var dealItems = ugoirasItems.filter(function(item) {
					return (item.type == "ugoira" && item.ugoira_metadata == undefined);
				})
				if (dealItems.length < 1) {
					dlg.log("动图获取完毕");
					dlg.progress.set(1); //设置当前下载进度
					callback();
					return;
				}
				if (works.break) {
					dlg.log("检测到中断进程命令");
					works.break = false;
					works.runing = false;
					dlg.textdown.disabled = false; //中断暂停时,可以操作目前的进度。
					dlg.startdown.disabled = false;
					return;
				}

				var work = dealItems[0]; //当前处理的图

				getUgoiraMeta(
					work.id,
					function(jore) { //onload_suceess_Cb
						works.runing = true;
						//var illusts = jore.illusts;
						work = Object.assign(work, jore);
						dlg.log("动图信息 获取进度 " + (ugoirasItems.length - dealItems.length + 1) + "/" + ugoirasItems.length);
						dlg.progress.set(1 - dealItems.length / ugoirasItems.length); //设置当前下载进度
						analyseUgoira(works, ugoirasItems, callback); //开始获取下一项
					},
					function(jore) { //onload_haserror_Cb //返回错误消息
						if(work.restrict > 0) //非公共权限
						{ //添加一条空信息
							work.ugoira_metadata = {
								frames: [],
								zip_urls: {
									medium: "",
								},
							};
							dlg.log("无访问权限,跳过本条。");
							analyseUgoira(works, ugoirasItems, callback); //开始获取下一项
						}else
						{
							works.runing = false;
							dlg.log("错误信息:" + (jore.error.message || jore.error.user_message));
							dlg.textdown.disabled = false; //错误暂停时,可以操作目前的进度。
							dlg.startdown.disabled = false;
						}
						return;
					},
					function(re) { //onload_notjson_Cb //返回不是JSON
						dlg.log("错误:返回不是JSON,或本程序异常");
						works.runing = false;
						dlg.textdown.disabled = false; //错误暂停时,可以操作目前的进度。
						dlg.startdown.disabled = false;
					},
					function(re) { //onerror_Cb //网络请求发生错误
						dlg.log("错误:网络请求发生错误");
						works.runing = false;
						dlg.textdown.disabled = false; //错误暂停时,可以操作目前的进度。
						dlg.startdown.disabled = false;
					}
				)
			}
		}
	//输出文本按钮
	dlg.textdownload = function() {
			if (dlg.downSchemeDom.selectedOptions.length < 1) { alert("没有选中方案"); return; }
			var scheme = dlg.schemes[dlg.downSchemeDom.selectedIndex];
			var contentType = dlg.dcType[1].checked ? 1 : 0;
			var userInfo = dlg.user.info;
			var illustsItems = contentType == 0 ? dlg.user.illusts.item : dlg.user.bookmarks.item; //将需要分析的数据储存到works里
			dlg.log("正在生成文本信息");

			try {
				var outTxtArr = illustsItems.map(function(illust) {
					var page_count = illust.page_count;
					if (illust.type == "ugoira" && illust.ugoira_metadata) //动图
					{
						page_count = illust.ugoira_metadata.frames.length;
					}
					var outArr = []; //输出内容
					for (var pi = 0; pi < page_count; pi++) {
						if (returnLogicValue(scheme.downfilter, userInfo, illust, pi) || new RegExp(limitingFilenamePattern, "ig").exec(illust.filename)) {
							//跳过此次输出
							continue;
						}else{
							outArr.push(showMask(scheme.textout, scheme.masklist, userInfo, illust, pi));
						}
					}
					return outArr.join("");
				});
				var outTxt = outTxtArr.join("");
				dlg.textoutTextarea.value = outTxt;
				dlg.textoutTextarea.classList.remove("display-none");
				dlg.log("文本信息输出成功");
			} catch (error) {
				console.log(error)
			}
		}
	//开始下载按钮
	dlg.startdownload = function() {
			dlg.textoutTextarea.classList.add("display-none");
			if (dlg.downSchemeDom.selectedOptions.length < 1) { alert("没有选中方案"); return; }
			var scheme = dlg.schemes[dlg.downSchemeDom.selectedIndex];
			var contentType = dlg.dcType[1].checked ? 1 : 0;
			var userInfo = dlg.user.info;
			var works = (contentType == 0 ? dlg.user.illusts : dlg.user.bookmarks);
			var illustsItems = works.item.concat(); //为了不改变原数组,新建一个数组

			var termwiseType = parseInt(getValueDefault("pubd-termwiseType", 2));
			if (termwiseType == 0)
				dlg.log("开始按图片逐项发送(约 "+works.picCount+" 次请求),⏳请耐心等待。");
			else if (termwiseType == 1)
				dlg.log("开始按作品逐项发送(约 "+illustsItems.length+" 次请求),⏳请耐心等待。");
			else if (termwiseType == 2)
				dlg.log("开始按作者发送,数据量较大时有较高延迟。\n⏳请耐心等待完成通知,勿多次点击。");
			else
			{
				alert("错误:未知的逐项模式" + termwiseType);
				console.error("PUBD:错误:未知的逐项模式:", termwiseType);
				return;
			}
			var downP = { progress: dlg.progress, current: 0, max: 0 };
			downP.max = works.picCount; //获取总需要下载发送的页数
	
			var aria2 = new Aria2(scheme.rpcurl); //生成一个aria2对象
			sendToAria2_illust(aria2, termwiseType, illustsItems, userInfo, scheme, downP, function() {
				aria2 = null;
				dlg.log("😄 " + userInfo.user.name + " 下载信息发送完毕");
				
				var ntype = parseInt(getValueDefault("pubd-noticeType", 0)); //获取结束后如何处理通知
				var bodyText = "" + userInfo.user.name + " 的相关插画已全部发送到指定的Aria2";
				if (ntype == 1)
					bodyText += "\n\n点击此通知 🔙返回 页面。";
				else if (ntype == 2)
					bodyText += "\n\n点击此通知 ❌关闭 页面。";
				else if (ntype == 3)
					bodyText += "\n\n通知结束时页面将 🅰️自动❌关闭。";
				GM_notification(
					{
						text:bodyText,
						title:"下载信息发送完毕",
						image:userInfo.user.profile_image_urls.medium
					},
					function(){ //点击了通知
						var ntype = parseInt(getValueDefault("pubd-noticeType", 0));
						if (ntype == 1)
							window.focus();
						else if (ntype == 2)
							window.close();
					},
					function(){ //关闭了通知
						var ntype = parseInt(getValueDefault("pubd-noticeType", 0));
						if (ntype == 3)
							window.close();
					},
				);
			});
		}
	//启动初始化
	dlg.initialise = function(arg) {
		var dcType = 0;
		if (dlg.user.bookmarks.runing) //如果有程序正在运行,则覆盖设置。
			dcType = 1;
		else if (dlg.user.illusts.runing)
			dcType = 0;
		dlg.dcType[dcType].checked = true;

		if (arg && arg.id>0) //提供了ID
		{
			if (arg.id != dlg.infoCard.infos["ID"])
			{ //更换新的id
				dlg.infoCard.thumbnail = "";
				dlg.infoCard.infos = {"ID":arg.id}; //初始化窗口id
				dlg.user = new UserInfo(); //重置用户数据
			}
		}else if(!dlg.infoCard.infos["ID"]) //没有ID
		{
			dlg.infoCard.infos = {"ID":parseInt(prompt("没有用户ID,请手动输入。", "ID缺失"))}; //初始化窗口id
		}
		if (getValueDefault("pubd-autoanalyse",false)) {
			dlg.analyse(dcType, dlg.infoCard.infos["ID"], function(){
				if (getValueDefault("pubd-autodownload",false)) { //自动开始
					dlg.log("🅰️自动开始发送");
					dlg.startdownload();
				}
			});
		}
		dlg.reloadSchemes();
	};

	return dlg;
}

//构建当前作品下载对话框
function buildDlgDownIllust(illustid) {
	var dlg = new buildDlgDown("下载当前作品", "pubd-down pubd-downillust", "pubd-downillust");
	dlg.infoCard.infos = {"ID":illustid};
	dlg.work = null; //当前处理对象

	//分析
	dlg.analyse = function(illustid,callbackAfterAnalyse) {
		if (!illustid) {dlg.log("错误:没有作品ID。"); return;}

		dlg.textdown.disabled = true; //禁用下载按钮
		dlg.startdown.disabled = true; //禁用输出文本按钮
		dlg.logClear(); //清空日志

		if (dlg.work != undefined)
		{
			dlg.textdown.disabled = false;
			dlg.startdown.disabled = false;
			console.log("当前作品JSON数据:",dlg.work);
			dlg.log("图片信息获取完毕");
			if (callbackAfterAnalyse) callbackAfterAnalyse();
		}else
		{
			dlg.log("开始获取作品信息");
			analyseWork(illustid); //开始获取第一页
		}

		//分析作品递归函数
		function analyseWork(illustid) {
			xhrGenneral(
				"https://app-api.pixiv.net/v1/illust/detail?illust_id=" + illustid,
				function(jore) { //onload_suceess_Cb
					var work = dlg.work = jore.illust;
					var original;
					if (work.page_count > 1) { /*漫画多图*/
						original = work.meta_pages[0].image_urls.original;
					} else { /*单张图片或动图,含漫画单图*/
						original = work.meta_single_page.original_image_url;
					}
					var regSrc = new RegExp(illustPattern, "ig");
					var regRes = regSrc.exec(original);
					if (regRes) {
						//然后添加扩展名等
						work.url_without_page = regRes[1];
						work.domain = regRes[2];
						work.filename = regRes[3];
						work.token = regRes[4];
						work.extention = regRes[5];
					} else {
						var regSrcL = new RegExp(limitingPattern, "ig");
						var regResL = regSrcL.exec(original);
						if (regResL) {
							dlg.log(contentName + " " + work.id + " 非公开,无权获取下载地址。");
							//console.log(work);
							work.url_without_page = regResL[1];
							work.domain = regResL[2];
							work.filename = regResL[3];
							work.token = regResL[4];
							work.extention = regResL[5];
						} else {
							dlg.log(contentName + " " + work.id + " 原图格式未知。");
						}
					}
					dlg.infoCard.thumbnail = work.image_urls.square_medium;
					var iType = "插画";
					if (work.type == "ugoira")
						iType = "动画";
					else if (work.type == "manga")
						iType = "漫画";
					if (work.page_count>1)
						iType += "(多图)";

					dlg.infoCard.infos = Object.assign(dlg.infoCard.infos, {
						"作品名称": work.title,
						"作品类型": iType,
						"作品页数": work.page_count,
					});

					
					if (work.type == "ugoira" && work.ugoira_metadata == undefined && getValueDefault("pubd-getugoiraframe",true))
					{
						analyseUgoira(work, function() { //开始分析动图
							dlg.textdown.disabled = false;
							dlg.startdown.disabled = false;
							dlg.infoCard.infos["作品页数"] = work.ugoira_metadata.frames.length;
							dlg.infoCard.reload(); //必须要reload
							dlg.log("图片信息获取完毕");
							console.log("当前作品JSON数据:",work);
							if (callbackAfterAnalyse) callbackAfterAnalyse();
						});
						return;
					}else
					{
						if (!getValueDefault("pubd-getugoiraframe",true)) {
							dlg.log("由于用户设置,跳过获取动图帧数。");
						}
						dlg.textdown.disabled = false;
						dlg.startdown.disabled = false;
						dlg.log("图片信息获取完毕");
						console.log("当前作品JSON数据:",work);
						if (callbackAfterAnalyse) callbackAfterAnalyse();
					}
				},
				function(jore) { //onload_haserror_Cb //返回错误消息
					dlg.log("错误信息:" + (jore.error.message || jore.error.user_message));
					dlg.textdown.disabled = false; //错误暂停时,可以操作目前的进度。
					dlg.startdown.disabled = false;
					return;
				},
				function(re) { //onload_notjson_Cb //返回不是JSON
					dlg.log("错误:返回不是JSON,或本程序异常");
					dlg.textdown.disabled = false; //错误暂停时,可以操作目前的进度。
					dlg.startdown.disabled = false;
				},
				function(re) { //onerror_Cb //网络请求发生错误
					dlg.log("错误:网络请求发生错误");
					dlg.textdown.disabled = false; //错误暂停时,可以操作目前的进度。
					dlg.startdown.disabled = false;
				}
			)
		}

		function analyseUgoira(work, callback) {
			getUgoiraMeta(
				work.id,
				function(jore) { //onload_suceess_Cb
					work = Object.assign(work, jore);
					dlg.log("动图信息获取完成");
					callback(); //开始获取下一项
				},
				function(jore) { //onload_haserror_Cb //返回错误消息
					if(work.restrict > 0) //非公共权限
					{ //添加一条空信息
						work.ugoira_metadata = {
							frames: [],
							zip_urls: {
								medium: "",
							},
						};
						dlg.log("无访问权限,跳过本条。");
						callback(); //开始获取下一项
					}else
					{
						works.runing = false;
						dlg.log("错误信息:" + (jore.error.message || jore.error.user_message));
						dlg.textdown.disabled = false; //错误暂停时,可以操作目前的进度。
						dlg.startdown.disabled = false;
					}
					return;
				},
				function(re) { //onload_notjson_Cb //返回不是JSON
					dlg.log("错误:返回不是JSON,或本程序异常");
					works.runing = false;
					dlg.textdown.disabled = false; //错误暂停时,可以操作目前的进度。
					dlg.startdown.disabled = false;
				},
				function(re) { //onerror_Cb //网络请求发生错误
					dlg.log("错误:网络请求发生错误");
					works.runing = false;
					dlg.textdown.disabled = false; //错误暂停时,可以操作目前的进度。
					dlg.startdown.disabled = false;
				}
			)
		}
	}
	//输出文本按钮
	dlg.textdownload = function() {
		var illust = dlg.work;
		if (illust == undefined) {dlg.log("没有获取作品数据。"); return;}
		if (dlg.downSchemeDom.selectedOptions.length < 1) { alert("没有选中方案"); return; }
		var scheme = dlg.schemes[dlg.downSchemeDom.selectedIndex];
		dlg.log("正在生成文本信息");
		try {
			var page_count = illust.page_count;
			if (illust.type == "ugoira" && illust.ugoira_metadata) //动图
			{
				page_count = illust.ugoira_metadata.frames.length;
			}
			var outArr = []; //输出内容
			for (var pi = 0; pi < page_count; pi++) {
				if (returnLogicValue(scheme.downfilter, null, illust, pi) || new RegExp(limitingFilenamePattern, "ig").exec(illust.filename)) {
					//跳过此次输出
					continue;
				}else{
					outArr.push(showMask(scheme.textout, scheme.masklist, null, illust, pi));
				}
			}
			var outTxt = outArr.join("");
			dlg.textoutTextarea.value = outTxt;
			dlg.textoutTextarea.classList.remove("display-none");
			dlg.log("文本信息输出成功");
		} catch (error) {
			console.log(error)
		}
	}
	//开始下载按钮
	dlg.startdownload = function() {
			dlg.textoutTextarea.classList.add("display-none");
			if (dlg.downSchemeDom.selectedOptions.length < 1) { alert("没有选中方案"); return; }
			var scheme = dlg.schemes[dlg.downSchemeDom.selectedIndex];

			var termwiseType = parseInt(getValueDefault("pubd-termwiseType", 2));
			if (termwiseType == 0)
				dlg.log("开始按图片逐项发送,⏳请耐心等待。");
			else if (termwiseType == 1 || termwiseType == 2)
				dlg.log("一次性发送整个作品,⏳请耐心等待。");
			else
			{
				alert("错误:未知的逐项模式" + termwiseType);
				console.error("PUBD:错误:未知的逐项模式:", termwiseType);
				return;
			}

			var aria2 = new Aria2(scheme.rpcurl); //生成一个aria2对象
			sendToAria2_illust(aria2, termwiseType, [dlg.work], null, scheme, null, function() {
				aria2 = null;
				dlg.log("😄 当前作品下载信息发送完毕");
			});
		}
	//启动初始化
	dlg.initialise = function(arg) {
		if (arg && arg.id>0) //提供了ID
		{
			if (arg.id != dlg.infoCard.infos["ID"])
			{ //更换新的id
				dlg.infoCard.thumbnail = "";
				dlg.infoCard.infos = {"ID":arg.id}; //初始化窗口id
				dlg.work = null; //重置作品数据
			}
		}else if(!dlg.infoCard.infos["ID"]) //没有ID
		{
			dlg.infoCard.infos = {"ID":parseInt(prompt("没有作品ID,请手动输入。", "ID缺失"))}; //初始化窗口id
		}
		dlg.analyse(dlg.infoCard.infos["ID"], function(){
			if (getValueDefault("pubd-autodownload",false)) { //自动开始
				dlg.log("🅰️自动开始发送");
				dlg.startdownload();
			}
		});
		dlg.reloadSchemes();
	};

	return dlg;
}

//构建导入数据对话框
function buildDlgImportData() {
	var dlg = new Dialog("导入数据", "pubd-import", "pubd-import");
	var dl = dlg.content.appendChild(document.createElement("dl"));

	var dt = dl.appendChild(document.createElement("dt"));
	dt.innerHTML = "导入内容";

	var dd = dl.appendChild(document.createElement("dd"));
	dd.className = "pubd-import-textarea-bar";
	var ipt = dd.appendChild(document.createElement("textarea"));
	ipt.className = "pubd-import-textarea";
	dlg.importTxt = ipt;
	var dd = dl.appendChild(document.createElement("dd"));
	var btn = dd.appendChild(document.createElement("input"));
	btn.type = "button";
	btn.className = "pubd-import-done";
	btn.value = "导入";

	//启动初始化
	dlg.initialise = function(arg) {
		ipt.value = "";
		if (arg)
		{
			btn.onclick = function()
			{//返回文本框的内容
				arg.callback(ipt.value);
				dlg.hide();
			}
		}else
		{
			btn.onclick = function()
			{
				alert("窗口异常启动,未提供回调函数");
			}
		}
	};
	return dlg;
}

//构建多画师下载管理对话框
function buildDlgMultiple() {
	var dlg = new Dialog("多画师下载管理", "pubd-multiple", "pubd-multiple");
	var dl = dlg.content.appendChild(document.createElement("dl"));

	var dt = dl.appendChild(document.createElement("dt"));
	var dd = dl.appendChild(document.createElement("dd"));
	var frm = dd.appendChild(new Frame("导出Pivix账号关注", "pubd-frm-userlist"));
	var dl_input_frm = frm.content.appendChild(document.createElement("dl"));
	var dt = dl_input_frm.appendChild(document.createElement("dt"));
	var dd = dl_input_frm.appendChild(document.createElement("dd"));

	var ipt = dd.appendChild(document.createElement("input"));
	ipt.type = "button";
	ipt.className = "pubd-userlist-inputstar-public";
	ipt.value = "导出公开关注";
	ipt.onclick = function() {
	};

	var ipt = dd.appendChild(document.createElement("input"));
	ipt.type = "button";
	ipt.className = "pubd-userlist-inputstar-public";
	ipt.value = "导出非公开关注";
	ipt.onclick = function() {
	};

/*
	var ipt = dd.appendChild(document.createElement("input"));
	ipt.type = "button";
	ipt.className = "pubd-userlist-backup";
	ipt.value = "备份列表JSON"
	ipt.onclick = function() {
	}

	var ipt = dd.appendChild(document.createElement("input"));
	ipt.type = "button";
	ipt.className = "pubd-userlist-restore";
	ipt.value = "导入备份"
	ipt.onclick = function() {
	}
*/

	var dt = dl.appendChild(document.createElement("dt"));
	dt.innerHTML = "选择收藏列表";
	var dd = dl.appendChild(document.createElement("dd"));
	var slt = dd.appendChild(new Select("pubd-staruserlists"));
	slt.onchange = function() {
		dlg.reloadUserList(this.selectedIndex);
	};
	dlg.userListDom = slt;

	var dd = dl.appendChild(document.createElement("dd"));
	var ipt = dd.appendChild(document.createElement("input"));
	ipt.type = "button";
	ipt.className = "pubd-userlist-new";
	ipt.value = "新建"
	ipt.onclick = function() {
		var schemName = prompt("请输入方案名", "我的方案");
		if (schemName)
		{
			var scheme = new DownScheme(schemName);
			var length = dlg.schemes.push(scheme);
			dlg.downSchemeDom.add(scheme.name, length - 1);
			dlg.downSchemeDom.selectedIndex = length - 1;
			dlg.loadScheme(scheme);
			//dlg.reloadSchemes();
		}
	}

	var ipt = dd.appendChild(document.createElement("input"));
	ipt.type = "button";
	ipt.className = "pubd-userlist-rename";
	ipt.value = "重命名列表"
	ipt.onclick = function() {
	}

	var ipt = dd.appendChild(document.createElement("input"));
	ipt.type = "button";
	ipt.className = "pubd-userlist-remove";
	ipt.value = "删除"
	ipt.onclick = function() {
		if (dlg.downSchemeDom.options.length < 1) { alert("已经没有方案了"); return; }
		if (dlg.downSchemeDom.selectedOptions.length < 1) { alert("没有选中方案"); return; }
		var index = dlg.downSchemeDom.selectedIndex;
		dlg.schemes.splice(index, 1);
		dlg.downSchemeDom.remove(index);
		var index = dlg.downSchemeDom.selectedIndex;
		if (index < 0) dlg.reloadSchemes(); //没有选中的,重置
		else dlg.loadScheme(dlg.schemes[index]);
	}

	var dd = dl.appendChild(document.createElement("dd"));
	var frm = dd.appendChild(new Frame("当前列表", "pubd-frm-userlist"));
	var dl_ul_frm = frm.content.appendChild(document.createElement("dl"));
	var dt = dl_ul_frm.appendChild(document.createElement("dt"));
	var dd = dl_ul_frm.appendChild(document.createElement("dd"));

	var ipt = dd.appendChild(document.createElement("input"));
	ipt.type = "button";
	ipt.className = "pubd-userlist-this-add";
	ipt.value = "添加画师ID"
	ipt.onclick = function() {
	}
	var ipt = dd.appendChild(document.createElement("input"));
	ipt.type = "button";
	ipt.className = "pubd-userlist-this-remove";
	ipt.value = "删除选中画师"
	ipt.onclick = function() {
	}
	var ipt = dd.appendChild(document.createElement("input"));
	ipt.type = "button";
	ipt.className = "pubd-userlist-this-reset-getdata";
	ipt.value = "重置数据获取状态"
	ipt.onclick = function() {
	}
	var ipt = dd.appendChild(document.createElement("input"));
	ipt.type = "button";
	ipt.className = "pubd-userlist-this-reset-downloaded";
	ipt.value = "重置下载状态"
	ipt.onclick = function() {
	}

	var dt = dl_ul_frm.appendChild(document.createElement("dt"));
	dt.innerHTML = "画师列表";
	var ipt = dt.appendChild(document.createElement("input"));
	ipt.type = "button";
	ipt.className = "pubd-userlist-break";
	ipt.value = "中断操作"
	ipt.onclick = function() {
	}
	var dd = dl_ul_frm.appendChild(document.createElement("dd"));
	var dl_ul = dd.appendChild(document.createElement("ul"));
	dlg.ulDom = dl_ul;
	dl_ul.className = "pubd-userlist-ul";

	var dt = dl.appendChild(document.createElement("dt"));
	var dd = dl.appendChild(document.createElement("dd"));
	
	var ipt = dd.appendChild(document.createElement("input"));
	ipt.type = "button";
	ipt.className = "pubd-userlist-this-getdata";
	ipt.value = "获取画师数据"
	ipt.onclick = function() {
	}

	var ipt = dd.appendChild(document.createElement("input"));
	ipt.type = "button";
	ipt.className = "pubd-userlist-textdown";
	ipt.value = "输出文本"
	ipt.onclick = function() {
	}
	var ipt = dd.appendChild(document.createElement("input"));
	ipt.type = "button";
	ipt.className = "pubd-userlist-download";
	ipt.value = "下载列表内画师作品"
	ipt.onclick = function() {
	}

	//启动初始化
	dlg.initialise = function(arg) {
	};
	return dlg;
}

//作品循环递归输出
function sendToAria2_illust(aria2, termwiseType, illusts, userInfo, scheme, downP, callback) {
	if (illusts.length < 1) //做完了
	{
		callback();
		return;
	}
	if (pubd.downbreak)
	{
		GM_notification({text:"已中断向Aria2发送下载信息。但Aria2本身仍未停止下载已添加内容,请手动停止。", title:scriptName, image:scriptIcon});
		pubd.downbreak = false;
		return;
	}
	if (termwiseType == 0) //完全逐项
	{
		var illust = illusts.shift(); //读取首个作品
		sendToAria2_Page(aria2, illust, 0, userInfo, scheme, downP, function() {
			sendToAria2_illust(aria2, termwiseType, illusts, userInfo, scheme, downP, callback); //发送下一个作品
		})
		return; //不再继续执行
	}else if (termwiseType == 1) //部分逐项(每作品合并)
	{
		var illust = illusts.shift(); //读取首个作品
		var page_count = illust.page_count; //作品页数
		if (illust.type == "ugoira" && illust.ugoira_metadata) //修改动图的页数
		{
			page_count = illust.ugoira_metadata.frames.length;
		}
	
		if (new RegExp(limitingFilenamePattern, "ig").exec(illust.filename)) //无权查看的文件
		{
			if (downP) downP.progress.set((downP.current += page_count) / downP.max); //直接加上一个作品所有页数
			sendToAria2_illust(aria2, termwiseType, illusts, userInfo, scheme, downP, callback); //调用自身
			return;
		}
		var aria2_params = [];
		for (page=0;page<page_count;page++)
		{
			if (returnLogicValue(scheme.downfilter, userInfo, illust, page)) {
				//跳过此次下载
				//console.info("符合下载过滤器定义,跳过下载:", illust);
				continue;
			} else {
				var aria2_method = {'methodName':'aria2.addUri','params':[]};
				var url = (scheme.https2http //https替换成http
							? illust.url_without_page.replace(/^https:\/\//igm, "http://")
							: illust.url_without_page)
					+ page + "." + illust.extention;
					aria2_method.params.push([url]); //添加下载链接
				var options = {
					"out": replacePathSafe(showMask(scheme.savepath, scheme.masklist, userInfo, illust, page), 1),
					"referer": Referer,
					"user-agent": UA,
				}
				if (scheme.savedir.length > 0) {
					options.dir = replacePathSafe(showMask(scheme.savedir, scheme.masklist, userInfo, illust, page), 0);
				}
				aria2_method.params.push(options);
				aria2_params.push(aria2_method);
			}
		}
		if (aria2_params.length>0)
		{
			aria2.system.multicall([aria2_params],function(res){
				if (res === false) {
					alert("发送到指定的Aria2失败,请检查到Aria2连接是否正常。");
					return;
				}
				if (downP) downP.progress.set((downP.current += page_count) / downP.max); //直接加上一个作品所有页数
				sendToAria2_illust(aria2, termwiseType, illusts, userInfo, scheme, downP, callback); //调用自身
			});
		}else
		{ //这个作品全部跳过的时候
			if (downP) downP.progress.set((downP.current += page_count) / downP.max); //直接加上一个作品所有页数
			sendToAria2_illust(aria2, termwiseType, illusts, userInfo, scheme, downP, callback); //调用自身
		}
		return;
	}else if(termwiseType == 2) //不逐项,每作者合并
	{
		var aria2_params = [];
		for (var illustIndex = 0; illustIndex < illusts.length; illustIndex++)
		{
			var illust = illusts[illustIndex];
			if (new RegExp(limitingFilenamePattern, "ig").exec(illust.filename)) continue; //无权查看的文件,直接继续

			var page_count = illust.page_count; //作品页数
			if (illust.type == "ugoira" && illust.ugoira_metadata) //修改动图的页数
			{
				page_count = illust.ugoira_metadata.frames.length;
			}
			for (page=0;page<page_count;page++)
			{
				if (returnLogicValue(scheme.downfilter, userInfo, illust, page)) {
					//跳过此次下载
					//console.info("符合下载过滤器定义,跳过下载:", illust);
					continue;
				} else {
					var aria2_method = {'methodName':'aria2.addUri','params':[]};
					var url = (scheme.https2http //https替换成http
								? illust.url_without_page.replace(/^https:\/\//igm, "http://")
								: illust.url_without_page)
						+ page + "." + illust.extention;
						aria2_method.params.push([url]); //添加下载链接
					var options = {
						"out": replacePathSafe(showMask(scheme.savepath, scheme.masklist, userInfo, illust, page), 1),
						"referer": Referer,
						"user-agent": UA,
					}
					if (scheme.savedir.length > 0) {
						options.dir = replacePathSafe(showMask(scheme.savedir, scheme.masklist, userInfo, illust, page), 0);
					}
					aria2_method.params.push(options);
					aria2_params.push(aria2_method);
				}
			}
		}
		if (aria2_params.length>0)
		{
			aria2.system.multicall([aria2_params],function(res){
				if (res === false) {
					alert("发送到指定的Aria2失败,请检查到Aria2连接是否正常。不排除数据过大,可考虑临时使用逐项或半逐项模式。");
					var l= JSON.stringify(aria2_params).length/1024;
					console.error("Aria2接受失败。数据量在未添加token的情况下有" + (
						(l>1024)?
						((l/1024)+"MB"):
						(l+"KB")
					),aria2_params);
					return;
				}
				if (downP) downP.progress.set((downP.current = downP.max) / downP.max); //直接加上所有页数
				sendToAria2_illust(aria2, termwiseType, [], userInfo, scheme, downP, callback); //调用自身
			});
		}else
		{ //这个作品全部跳过的时候
			if (downP) downP.progress.set((downP.current = downP.max) / downP.max); //直接加上所有页数
			sendToAria2_illust(aria2, termwiseType, [], userInfo, scheme, downP, callback); //调用自身
		}
		return;
	}
}
//作品每页循环递归输出
function sendToAria2_Page(aria2, illust, page, userInfo, scheme, downP, callback) {
	if (pubd.downbreak) {
		GM_notification({text:"已中断向Aria2发送下载信息。但Aria2本身仍未停止下载已添加内容,请手动停止。", title:scriptName, image:scriptIcon});
		pubd.downbreak = false;
		return;
	}
	var page_count = illust.page_count;
	if (illust.type == "ugoira" && illust.ugoira_metadata) //动图的帧数当页数
	{
		page_count = illust.ugoira_metadata.frames.length;
	}
	if (new RegExp(limitingFilenamePattern, "ig").exec(illust.filename)) //无法查看的文件,直接把page加到顶
	{
		page = page_count;
		downP.progress.set((downP.current += page_count) / downP.max); //直接加上所有页数
	}
	if (page >= page_count) //本作品页数已经完毕
	{
		callback();
		return;
	}
	var url = (scheme.https2http //https替换成http
		? illust.url_without_page.replace(/^https:\/\//igm, "http://")
		: illust.url_without_page)
		+ page + "." + illust.extention;

	if (returnLogicValue(scheme.downfilter, userInfo, illust, page)) {
		//跳过此次下载
		downP.progress.set(++downP.current / downP.max); //设置进度
		sendToAria2_Page(aria2, illust, ++page, userInfo, scheme, downP, callback); //递归调用自身
		//console.info("符合下载过滤器定义,跳过下载:", illust);
	} else {
		var options = {
			"out": replacePathSafe(showMask(scheme.savepath, scheme.masklist, userInfo, illust, page), 1),
			"referer": Referer,
			"user-agent": UA,
		}

		if (scheme.savedir.length > 0) {
			options.dir = replacePathSafe(showMask(scheme.savedir, scheme.masklist, userInfo, illust, page), 0);
		}
		aria2.addUri(url, options, function(res) {
			if (res === false) {
				alert("发送到指定的Aria2失败,请检查到Aria2连接是否正常。");
				return;
			}
			downP.progress.set(++downP.current / downP.max); //设置进度
			sendToAria2_Page(aria2, illust, ++page, userInfo, scheme, downP, callback); //递归调用自身
		});
	}
}
//返回掩码值
function showMask(oldStr, maskList, user, illust, page) {
	var newStr = oldStr;
	//var pattern = "%{([^}]+)}"; //旧的,简单匹配
	var regPattern = "%{(.*?(?:[^\\\\](?:\\\\{2})+|[^\\\\]))}"; //新的,支持转义符
	var regResult = null;

	//不断循环直到没有掩码
	while ((regResult = new RegExp(regPattern).exec(newStr)) != null) {
		var mskO = regResult[0], //包含括号的原始掩码
			mskN = regResult[1]; //去掉掩码括号
		if (mskN != undefined) {
			//去掉转义符的掩码名
			mskN = (mskN != undefined) ? mskN.replace(/\\{/ig, "{").replace(/\\}/ig, "}").replace(/\\\\/ig, "\\") : null;
			//搜寻自定义掩码
			var cusMasks = maskList.filter(function(mask) { return mask.name == mskN; });
			if (cusMasks.length > 0) { //如果有对应的自定义掩码
				var cusMask = cusMasks[0];
				try {
					if (returnLogicValue(cusMask.logic, user, illust, page)) //mask的逻辑判断
						newStr = newStr.replace(mskO, cusMask.content);
					else
						newStr = newStr.replace(mskO, "");
				} catch (e) {
					console.error(mskO + " 自定义掩码出现了异常情况", e);
				}
			} else { //普通掩码
				try {
					var evTemp = eval(mskN);
					if (evTemp != undefined)
						newStr = newStr.replace(mskO, evTemp.toString());
					else
						newStr = newStr.replace(mskO, "");
				} catch (e) {
					newStr = newStr.replace(mskO, "");
					console.error(mskO + " 掩码出现了异常情况", e);
				}
			}
		}
	}

	return newStr;
}
//返回逻辑值
function returnLogicValue(logic, user, illust, page) {
	try {
		if (logic.length == 0) return false;
		var evTemp = eval("(" + logic + ")");
		return evTemp;
	} catch (e) {
		console.error("下载过滤器出现了异常情况,逻辑内容:","(" + logic + ")", e);
		return false;
	}
}

function replacePathSafe(str, type) //去除Windows下无法作为文件名的字符,目前为了支持Linux暂不替换两种斜杠吧。
{ //keepTree表示是否要保留目录树的字符(\、/和:)
	if (typeof(str) == "undefined")
	{
		return "";
	}
	var nstr = str; //新字符
	nstr = nstr.toString();
	nstr = nstr.replace(/\u0000-\u001F\u007F-\u00A0/ig, ""); //替换所有的控制字符
	var patternStrs = [
		"[\\*\\?\"<>\\|]",                 //只替换路径中完全不能出现的特殊字符
		"[\\*\\?\"<>\\|\\r\\n]",           //上述字符加冒号:,用于非驱动器路径
		"[\\*\\?\"<>\\|\\r\\n\\\\\\/]",    //完全替换所有不能出现的特殊字符,包含斜杠
	];
	if (patternStrs[type] != undefined)
	{
		nstr = nstr.replace(new RegExp(patternStrs[type],"ig"), "_"); //只替换路径中完全不能出现的特殊字符
	}
	return nstr;
}

//开始构建UI
function findInsertPlace(btnStart) {
	var btnStartInsertPlace = document.querySelector("#root>div:nth-of-type(2)>div>div>div>div:nth-of-type(2)>div:nth-of-type(2)") //2018年10月8日 新版用户资料首页
							||document.querySelector("#root>div:nth-of-type(2)>div>div>aside>section") //新版作品页
							//||document.querySelector("#root>div:nth-of-type(5)>div>div>div>div>div>div>div>div") //新版FANBOOK页,但是并不支持收费的东西,所以就隐藏了吧
							||document.querySelector("#root>div:nth-of-type(2)>div>div>div>div:nth-of-type(2)>div") //新版关注页
							||document.querySelector("._user-profile-card") //老版用户资料页
							||document.querySelector(".ui-layout-west aside") //老版作品页
							||document.querySelector(".introduction") //未登录页面
							;
	if (btnStartInsertPlace == undefined)
	{
		console.error("PUBD:未找到开始按钮插入点。");
		return;
	}else
	{
		//第一张作品图像
		var artWorkLink = document.querySelector(artWorkStarCssPath);
		if (artWorkLink) //如果是作品页面,显示下载当前作品按钮
		{
			pubd.menu.downillust.classList.remove("display-none");
			downIllustMenuId = GM_registerMenuCommand("PUBD-下载该作品", function(){
				pubd.dialog.downillust.show(
					(document.body.clientWidth - 500)/2,
					window.pageYOffset+150,
					{id:getQueryString('illust_id',artWorkLink)}
				);
			});
		}else
		{
			pubd.menu.downillust.classList.add("display-none");
			GM_unregisterMenuCommand(downIllustMenuId);
		}
		checkStar(); //检查是否有收藏
		//插入开始操作按钮
		btnStartInsertPlace.appendChild(btnStart);
		console.log("PUBD:网页发生变动,已重新呈现开始按钮。");
		clearInterval(findInsertPlaceHook); //停止循环
	}
}
//主引导程序
function start(touch) {
	if (touch) //手机版
	{ //手机版退出执行
		//alert("PUBD暂不支持手机版");
		clearInterval(findInsertPlaceHook);
		return;
	}

	if (!mdev) GM_addStyle(GM_getResourceText("pubd-style")); //不是开发模式时加载CSS资源

	//载入设置
	pubd.auth = new Auth();
	pubd.auth.loadFromAuth(GM_getValue("pubd-auth"));

	pubd.downSchemes = NewDownSchemeArrayFromJson(getValueDefault("pubd-downschemes",[]));
	//对下载方案的修改添加监听
	GM_addValueChangeListener("pubd-downschemes", function(name, old_value, new_value, remote) {
		pubd.downSchemes = NewDownSchemeArrayFromJson(new_value); //重新读取下载方案(可能被其他页面修改的)
	});
	//快速收藏列表的监听修改
	pubd.fastStarList = getValueDefault("pubd-faststar-list",[]);
	GM_addValueChangeListener("pubd-faststar-list", function(name, old_value, new_value, remote) {
		pubd.fastStarList = new_value;
		checkStar();
		//将来还需要在更改收藏时,就自动刷新所有的其他推荐列表
		//put my code
	});
	//登陆信息的监听修改
	GM_addValueChangeListener("pubd-auth", function(name, old_value, new_value, remote) {
		pubd.auth.loadFromAuth(new_value);
	});

	//预先添加所有视窗,即便没有操作按钮也能通过菜单打开
	var btnDlgInsertPlace = document.body;
	pubd.dialog.config = btnDlgInsertPlace.appendChild(buildDlgConfig());
	pubd.dialog.login = btnDlgInsertPlace.appendChild(buildDlgLogin());
	pubd.dialog.downthis = btnDlgInsertPlace.appendChild(buildDlgDownThis(thisPageUserid));
	pubd.dialog.downillust = btnDlgInsertPlace.appendChild(buildDlgDownIllust(thisPageIllustid));
	pubd.dialog.importdata = btnDlgInsertPlace.appendChild(buildDlgImportData());
	pubd.dialog.multiple = btnDlgInsertPlace.appendChild(buildDlgMultiple());
	
	//添加Tampermonkey扩展菜单内的入口
	GM_registerMenuCommand("PUBD-选项", function(){
		pubd.dialog.config.show(
			(document.body.clientWidth - 400)/2,
			window.pageYOffset+50
		);
	});
	GM_registerMenuCommand("PUBD-下载该画师", function(){
		pubd.dialog.downthis.show(
			(document.body.clientWidth - 440)/2,
			window.pageYOffset+100,
			{id:getCurrentUserId()}
		)
	});

	if (mdev)
	GM_registerMenuCommand("PUBD-导入窗口测试", function(){
		pubd.dialog.importdata.show(
			(document.body.clientWidth - 370)/2,
			window.pageYOffset+200,
			{callback:function(txt){console.log(txt);}}
		);
	});


	//开始操作按钮
	var btnStartBox = document.createElement("div");
	btnStartBox.className = "pubd-btnStartInsertPlace";
	pubd.start = btnStartBox.appendChild(buildbtnStart());
	pubd.menu = btnStartBox.appendChild(buildbtnMenu());

	findInsertPlaceHook = setInterval(function(){
		findInsertPlace(btnStartBox);
	}, 1000);
	var vueRoot = document.querySelector("#root");
	//对于新版P站的SPA结构需要循环寻找插入点,每秒循环
	if (window.MutationObserver && vueRoot) //如果支持MutationObserver,且是vue框架
	{
		function newInsertStart(){
			//不存在开始按钮就重新插入
			if (document.querySelector("#pubd-start") == undefined)
			{
				findInsertPlace(btnStartBox);
			}
		}
		var observerFirstOnce = new MutationObserver(function(mutationsList, observer) {
			//第一次
			if (document.querySelector("#pubd-start") == undefined)
			{ //不存在开始按钮就重新插入
				findInsertPlace(btnStartBox);
				//成功后就结束
				observerFirstOnce.disconnect();
				var observerContinued = new MutationObserver(function(mutationsList, observer) {
					//console.log(mutationsList);
					if (mutationsList.some(mutation=>Array.from(mutation.removedNodes).some(node=>node.contains(btnStartBox))))
					{ //如果被删除的节点里有我们的开始按钮,就重新恢复
						findInsertPlace(btnStartBox);
					}
					//作品页面显示推荐的部分
					const otherWorks = document.querySelector("#root>div:nth-of-type(2)>div>aside:nth-of-type(2)");
					if (otherWorks)
					{ //已发现推荐列表大部位
						if (mutationsList.some(mutation=>otherWorks.contains(mutation.target) && //目标属于推荐部分
							Array.from(mutation.addedNodes).some(node=>node.nodeName == "SECTION" || node.querySelector("section")) //新增node要么是section,要么包括section
						)) //当改变目标为这个aside,并且新增的是section时
						{
							recommendList = otherWorks.querySelector("section>div:nth-of-type(2)>ul");
							//console.log('储存推荐列表节点',recommendList);
						}
					}
					if (recommendList)
					{
						if (mutationsList.some(mutation=>mutation.target==recommendList))
						{ //当改变目标为推荐列表时,并且新增的是section时
							//console.log('推荐列表改变',mutationsList);
							mutationsList.forEach(mutation=>
								mutation.addedNodes.forEach(linode=>{ //这个node是每个新增列表里的li
									const userLink = linode.querySelector("div>div:nth-of-type(2)>div>a");
									const uidRes = /\d+/.exec(userLink.pathname);
									if (uidRes.length)
									{
										const uid = parseInt(uidRes[0],10); //得到这个作品的作者ID
										if (pubd.fastStarList.includes(uid))
										{
											linode.classList.add("pubd-stared"); //添加隐藏用的css
										}
									}
								})
							)
						}
						if (mutationsList.some(mutation=>Array.from(mutation.removedNodes).some(node=>node.contains(recommendList))))
						{ //如果被删除的节点里有推荐列表,重新标空
							recommendList = null;
						}
					}
				});
				observerContinued.observe(vueRoot, {childList: true,subtree:true});
			}
		});
		observerFirstOnce.observe(vueRoot, {childList: true,subtree:true});
	}
}
start(pubd.touch); //开始主程序