Includes : Persist BETA

Persist Function

目前為 2014-12-24 提交的版本,檢視 最新版本

此腳本不應該直接安裝,它是一個供其他腳本使用的函式庫。欲使用本函式庫,請在腳本 metadata 寫上: // @require https://update.gf.qytechs.cn/scripts/7144/29375/Includes%20%3A%20Persist%20BETA.js

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name           Includes : Persist BETA
// @namespace      http://gm.wesley.eti.br
// @description    Persist Function
// @author         w35l3y
// @email          [email protected]
// @copyright      2013+, w35l3y (http://gm.wesley.eti.br)
// @license        GNU GPL
// @homepage       http://gm.wesley.eti.br
// @version        1.0.1.0
// @language       en
// @include        nowhere
// @exclude        *
// ==/UserScript==

/**************************************************************************

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.

**************************************************************************/

var Persist = function (s) {
	var service = Persist.services[s];

	this.request = function (obj) {
		var k,
		xthis = this,
		params = function (def) {
			var list = {};

			this.add = function (vars) {
				if (vars) {
					if (vars instanceof unsafeWindow.HTMLCollection) {
						vars = Array.prototype.slice.apply(vars);
					}

					if (typeof vars == "object") {
						for (var k in vars) {
							var e = vars[k];

							if (typeof e == "object" && (!/^(?:radio|checkbox)$/i.test(e.type) || e.checked)) {
								var n = e.name || k;

								if (e.checked && /^checkbox$/i.test(e.type)) {
									if (n in list) {
										list[n].push(e.value);
									} else {
										list[n] = [e.value];
									}
								} else if ("value" in e) {
									list[n] = e.value;
								} else {
									list[n] = e;
								}
							} else {
								list[k] = e;
							}
						}
					} else {
						list = vars;
					}
				}
			};
		
			this.toString = function() {
				if (typeof list == "object") {
					var data = "";
					for (var key in list) {
						if (list[key] instanceof Array) {
							var keyarr = key.replace(/^\s+|\s+$/g, "");
							if (!/\[\w*\]$/.test(key)) keyarr += "[]";

							for (var k in list[key]) {
								var v = list[key][k];
								data += "&" + encodeURIComponent(keyarr) + "=" + encodeURIComponent(v);
							}
						} else {
							data += "&" + encodeURIComponent(key) + "=" + encodeURIComponent(list[key]);
						}
					}

					return data.substr(1);
				} else {
					return list;
				}
			};
		},
		data = new params();

		data.add(obj.pdata);
		data.add(obj.adata);
		data.add(obj.data);
		data.add(obj.odata);
	
		if (typeof obj.onload != "function") {
			obj.onload = function (obj) {
				console.log(["request.load", obj]);
			};
		}

		if (typeof obj.onerror != "function") {
			obj.onerror = function (obj) {
				console.log(["request.error", obj]);
			};
		}

		var update = function (obj, e) {
			obj.response = {
				raw	: function () {return e;},
				text	: function () {return e.responseText;},
				json	: function () {
					try {
						return JSON.parse(e.responseText);
					} catch (x) {
						return eval("(" + e.responseText + ")");
					}
				},
				doc	: function () {
					try {
						return new DOMParser().parseFromString(e.responseText, /^Content-Type: ([\w/]+)$/mi.test(e.responseHeaders) && RegExp.$1 || "text/html");
					} catch (x) {
						var doc = document.implementation.createHTMLDocument("");
						doc.documentElement.innerHTML = e.responseText;

						return doc;
					}
				},
			};
			obj.value = e.responseText;
		},
		params = {
			url		: obj.url,
			method	: obj.method,
			onload	: function (e) {
				update(obj, e);

				obj[(/^2/.test(e.status)?"onload":"onerror")].apply(xthis, [obj]);
			},
			onerror	: function (e) {
				update(obj, e);

				obj.onerror.apply(xthis, [obj]);
			}
		},
		sdata = data.toString();

		if (/^post$/i.test(obj.method)) {
			var p = {
				headers	: {
					"Content-Type"	: "application/x-www-form-urlencoded"
				},
				data	: sdata,
			};

			console.log(sdata);

			for (k in p) {
				params[k] = p[k];
			}
		} else if (sdata) {
			params.url += "?" + sdata;

		}
	
		if (typeof obj.headers == "object")
		for (k in obj.headers) {
			params.headers[k] = obj.headers[k];
		}

		return GM_xmlhttpRequest(params);
	};

	for (var k in service) {
		this[k] = service[k];
	}
};

Persist.services	= {
	LOCAL		: {
		write	: function (obj) {
			var label_keys = "persist_keys-" + obj.service,
			label_data = "persist_data-" + obj.service,
			mapping = JSON.parse(GM_getValue(label_keys, "{}")),
			data = JSON.parse(GM_getValue(label_data, "[]")),
			key = obj.key;

			if (key in mapping) {
				key = mapping[obj.key];
			}

			if (key in data) {
				var tv = typeof obj.value,
				td = typeof data[key];

				if ((td != tv) && (td == "object" || tv == "object")) {
					throw ["Incompatible types ", td, tv].toString();
				} else {
					switch (td) {
						case "string":
							if (-1 == obj.mode) {	// prepend
								data[key] = obj.value + data[key];
							} else if (1 == obj.mode) {	// append
								data[key] += obj.value;
							} else {	// overwrite (default)
								data[key] = obj.value;
							}
							break;
						case "boolean":
							if (-1 == obj.mode) {	// unused
								throw ["Reserved action"].toString();
							} else if (1 == obj.mode) {	// toggle
								data[key] = !data[key];
							} else {	// overwrite (default)
								data[key] = !!value;
							}
							break;
						case "number":
							var value = Number(obj.value);

							if (-1 == obj.mode) {	// subtract
								data[key] -= value;
							} else if (1 == obj.mode) {	// add
								data[key] += value;
							} else {	// overwrite (default)
								data[key] = value;
							}
							break;
						case "object":
							if (-1 == obj.mode) {	// prepend
								for (var k in data[key]) {
									obj.value[k] = data[key][k];
								}
								data[key] = obj.value;
							} else if (1 == obj.mode) {	// append
								for (var k in obj.value) {
									data[key][k] = obj.value[k];
								}
							} else {	// overwrite (default)
								data[key] = obj.value;
							}
							break;
						default:
							throw ["Unsupported type " + td, data[key], key].toString();
					}

					obj.value = data[key];
				}
			} else {
				var tkey = data.push(obj.value);
				
				if (--tkey != key) {
					if ("key" in obj) {
						if (key in mapping) {
							console.log(["Wrong mapping... ", tkey, key]);
						}

						mapping[key] = tkey;
						obj.key = key;

						GM_setValue(label_keys, JSON.stringify(mapping));
					} else {
						obj.key = tkey;
					}
				}
			}

			GM_setValue(label_data, JSON.stringify(data));

			if (typeof obj.onload != "function") {
				obj.onload = function (obj) {
					console.log(["write.load", obj]);
				};
			}

			return obj.onload.apply(this, [obj]);
		},
		delete	: function (obj) {
			throw ["Not implemented"].toString();
		},
		read	: function (obj) {
			var mapping = JSON.parse(GM_getValue("persist_keys-" + obj.service, "{}")),
			key = obj.key;

			if (key in mapping) {
				key = mapping[key];
			}

			obj.value = JSON.parse(GM_getValue("persist_data-" + obj.service, "[]"))[key];

			if (typeof obj.onload != "function") {
				obj.onload = function (obj) {
					console.log(["read.load", obj]);
				};
			}

			return obj.onload.apply(this, [obj]);
		},
	},
	PASTEBIN	: {
		write	: function (obj) {
			var execute = function (x) {
				var onload = x.onload,
				value = x.value,
				xthis = this,
				p = {
					url		: "http://pastebin.com/api/api_post.php",
					method	: "post",
					adata		: JSON.parse(GM_getValue("pastebin_adata", JSON.stringify({
//						api_dev_key			: "",	// required
//						api_user_key		: "",
					}))),
					pdata		: {
						api_paste_format		: "text",
						api_paste_private		: "1",
						api_paste_expire_date	: "N",
						api_paste_code		: x.value,	// required
					},
					odata		: {
						api_paste_key		: obj.key,
						api_option			: "paste"
					},
					onload	: function (y) {
						if (/^https?:\/\//i.test(y.value) && /\w+$/.test(y.value)) {
							var key = x.key;
							x.key = RegExp["$&"];

							x.onload = onload;
							x.value = value;

							x.onload.apply(xthis, [x]);

							if (x.read) {
								/* It is implemented that way because currently there isn't an EDIT option via API */
								y.key = key;
								y.onload = function (z) {
									console.log(["delete.load", z]);
								};
								y.onerror = function (z) {
									console.log(["delete.error", z]);
								};

								xthis.delete.apply(xthis, [y]);
							}
						} else {
							x.onerror.apply(xthis, [x]);
						}
					}
				};
				
				for (var i in p) {
					x[i] = p[i];
				}
				
				return this.request(x);
			};

			if (1 == Math.abs(obj.mode)) {	// prepend or append
				var value = obj.value,
				onload = obj.onload,
				xthis = this;

				obj.onload = function (x) {
					obj.onload = onload;

					if (-1 == obj.mode) {	// prepend
						obj.value = value + obj.value;
					} else {	// append
						obj.value += value;
					}
					
					return execute.apply(xthis, [obj]);
				};

				return this.read.apply(this, [obj]);
			} else {
				return execute.apply(this, [obj]);
			}
		},
		read	: function (obj) {
			if ("key" in obj) {
				var onload = obj.onload,
				xthis = this,
				p = {
					read		: false,
					url		: "http://pastebin.com/download.php",
					method	: "get",
					adata		: {
						i	: obj.key
					},
					onload	: function (x) {
						obj.onload = onload;

						if (x.response.raw().finalUrl.indexOf(x.url)) {
							obj.value = "";

							obj[typeof obj.onwarn == "function"?"onwarn":"onload"].apply(this, [obj]);
						} else {
							obj.read = true;

							obj.onload.apply(xthis, [obj]);
						}
					}
				};
				for (var i in p) {
					obj[i] = p[i];
				}

				return this.request(obj);
			} else {
				obj.value = "";
				obj.read = false;

				obj[typeof obj.onwarn == "function"?"onwarn":"onload"].apply(this, [obj]);
			}
		},
		delete	: function (obj) {
			var onload = obj.onload,
			value = obj.value,
			xthis = this,
			p = {
				url		: "http://pastebin.com/api/api_post.php",
				method	: "post",
				adata		: JSON.parse(GM_getValue("pastebin_adata", JSON.stringify({
//					api_dev_key			: "",	// required
//					api_user_key		: "",
				}))),
				pdata		: {},
				odata		: {
					api_paste_key		: obj.key,
					api_option			: "delete"
				},
				onload	: function (x) {
					if (/^Paste Removed$/i.test(x.value)) {
						obj.onload = onload;
						obj.value = value;

						obj.onload.apply(xthis, [obj]);
					} else {
						obj.onerror.apply(xthis, [obj]);
					}
				}
			};
		
			for (var i in p) {
				obj[i] = p[i];
			}
		
			return this.request(obj);
		},
	},
	PASTEBIN2	: {
		write	: function (obj) {
			var ovalue = obj.value,
			xthis = this,
			execute = function (x) {
				var onload = x.onload,
				nvalue = x.value,
				p = {
					method	: "post",
					adata	: JSON.parse(GM_getValue("pastebin2_adata", JSON.stringify({
//						paste_private		: "1",
					}))),
					pdata	: {
						paste_format		: "1",
						paste_private		: "2",
						paste_expire_date	: "N",
						paste_code			: x.value,	// required
					},
					odata	: {
						submit			: "Submit",
						submit_hidden	: "submit_hidden",
						item_key		: obj.key,
						post_key		: obj.key,
					},
					onload	: function (y) {
						var doc = y.response.doc(),
						url = y.response.raw().finalUrl;

						if (/warning\.php\?p=(\d+)/i.test(url)) {
							x.code = RegExp.$1 - 1;
							x.value = [
								"You have reached your limit of [10] pastes per 24 hours.",
								"You have reached your limit of [20] pastes per 24 hours.",
								"You have reached your limit of [250] pastes per 24 hours.",
							][x.code] || xpath("string(id('content_left')/div[2])", doc) || "Unknown error (WARN " + x.code + ")";

							x.onerror.apply(xthis, [x]);
						} else if ((/^https?:/i.test(url)) && (/\/(\w+)$/.test(url))) {
							x.key = RegExp.$1;

							if (xpath("string(.//text()[contains(., 'Pastebin.com is under heavy load right now')])", doc)) {
								x.onerror.apply(xthis, [x]);
							} else {
								if (xpath("id('siimage')", doc)[0]) {
									x.value = nvalue;
									GM_log(ovalue);
									alert("A new window will be opened. You must fill the captcha correctly, otherwise you will lose your data and a new paste will be created next time.");
									GM_openInTab(url);
								} else {
									var code = xpath("id('paste_code')", doc)[0];
									x.value = code && code.textContent || "";
								}
								x.onload = onload;
								x.onload.apply(xthis, [x]);
							}
						} else {
							x.value = [
								"You have exceeded the maximum file size of [500] kilobytes per paste.",
								"You cannot create an empty paste.",
								"You have reached the maximum number of [25] unlisted pastes.",
								"You have reached the maximum number of [10] private pastes.",
							][/index\.php\?e=(\d+)/.test(url) && (x.code = RegExp.$1 - 1)] || xpath("string(id('notice'))", doc) || "Unknown error (ERROR " + x.code + ")";

							x.onerror.apply(xthis, [x]);
						}
					}
				};
				
				for (var i in p) {
					x[i] = p[i];
				}

				return this.request(x);
			};

			if (1 == Math.abs(obj.mode)) {	// prepend or append
				var onload = obj.onload;

				if ("" != ovalue) {
					obj.onload = function (x) {
						obj.onload = onload;

						if (-1 == obj.mode) {	// prepend
							obj.value = ovalue + obj.value;
						} else {	// append
							obj.value += ovalue;
						}
					
						return execute.apply(xthis, [obj]);
					};
				}

				return xthis.read.apply(xthis, [obj]);
			} else {
				return execute.apply(xthis, [obj]);
			}
		},
		read	: function (obj) {
			var xthis = this;

			if ("key" in obj && obj.key) {
				var onload = obj.onload,
				p = {
					read		: false,
					url		: "http://pastebin.com/download.php",
					method	: "get",
					adata		: {
						i	: obj.key
					},
					onload	: function (x) {
						var url = x.response.raw().finalUrl;

						x.onload = onload;

						if (url.indexOf(x.url)) {	// Unknown Paste ID
							x.value = "";
							x.url = "http://pastebin.com/post.php";
							x.key = "";

							x[typeof x.onwarn == "function"?"onwarn":"onload"].apply(this, [x]);
						} else if (~x.value.indexOf("this is a private paste. If this is your private paste")) {	// may occur false positive
							x.value = "This is a private paste (#"+x.key+"). You must login to Pastebin first.";
							x.onerror.apply(xthis, [x]);
						} else if (~x.value.indexOf("Pastebin.com is under heavy load right now")) {
							x.value = "Pastebin.com is under heavy load right now.";
							x.onerror.apply(xthis, [x]);
						} else {
							x.read = true;
							x.url = "http://pastebin.com/edit.php";

							x.onload.apply(xthis, [x]);
						}
					}
				};
				for (var i in p) {
					obj[i] = p[i];
				}

				return this.request(obj);
			} else {
				obj.read = false;
				obj.url = "http://pastebin.com/post.php";
				obj.key = "";

				this.request({
					url		: "http://pastebin.com/",
					method	: "get",
					onload	: function (c) {
						if (xpath("boolean(id('header_bottom')//a[contains(@href, '/logout')])", c.response.doc())) {
							obj.value = "";

							obj[typeof obj.onwarn == "function"?"onwarn":"onload"].apply(xthis, [obj]);
						} else {
							obj.value = "You must login to Pastebin first.";

							obj.onerror.apply(xthis, [obj]);
						}
					},
					onerror	: function (c) {
						c.value = xpath("string(id('content_left')/div[1])", c.response.doc());

						obj.onerror(c);
					},
				});
			}
		},
		delete	: function (obj) {
			var onload = obj.onload,
			value = obj.value,
			xthis = this,
			p = {
				url		: "http://pastebin.com/delete.php",
				method	: "get",
				adata	: {},
				pdata	: {},
				odata	: {
					i	: obj.key,
					r	: "/" + obj.key
				},
				onload	: function (x) {
					if (/^Paste Removed$/i.test(x.value)) {
						obj.onload = onload;
						obj.value = value;

						obj.onload.apply(xthis, [obj]);
					} else {
						obj.onerror.apply(xthis, [obj]);
					}
				}
			};
		
			for (var i in p) {
				obj[i] = p[i];
			}
		
			return this.request(obj);
		}
	}
};

Persist.write = function (obj) {
	var p = new Persist(obj.service);

	return p.write.apply(p, [obj]);
};

Persist.delete = function (obj) {
	var p = new Persist(obj.service);

	return p.delete.apply(p, [obj]);
};

Persist.read = function (obj) {
	var p = new Persist(obj.service);

	return p.read.apply(p, [obj]);
};