Greasy Fork is available in English.

CKTools

A library by CKylinMC combined all usually-used script segments.

Dieses Skript sollte nicht direkt installiert werden. Es handelt sich hier um eine Bibliothek für andere Skripte, welche über folgenden Befehl in den Metadaten eines Skriptes eingebunden wird // @require https://update.greasyfork.org/scripts/429720/1082330/CKTools.js

// ==UserScript==
// @name         CKTools
// @namespace    ckylin-script-lib-combined-tools
// @version      1.6.1
// @match        http://*
// @match        https://*
// @author       CKylinMC
// @license      GPLv3 License
// ==/UserScript==
(function () {
	const VERSION = 1.6;
	if ('CKTools' in window) {
		if (!window.CKTools.ver) console.warn('Unrecognized version of CKTools is already loaded, overwriting...');
		else if (window.CKTools.ver > VERSION) throw new Error("You have newer version CKTools loaded. Aborting loading version " + VERSION);
		else console.warn(`You have older version of CKTools (${window.CKTools.ver}) was loaded, and now upgrading to newer version ${VERSION}.`);
	}
	class CKTools {
		static ver = VERSION
		static get(q, base = document) {
			return base.querySelector(q);
		}
		static getAll(q, base = document) {
			return [...base.querySelectorAll(q)];
		}
		static domHelper(options = {}, compatibleParm2 = {}) {
			let tagName = 'div';
			if (typeof (options) == 'string') {
				/* Migrate from version 1 */
				tagName = options;
				/* Migrate from makeDom */
				if (compatibleParm2.constructor.name == 'Object') options = compatibleParm2;
				else if (compatibleParm2.constructor.name == 'AsyncFunction') options = {
					initAsync: compatibleParm2
				};
				else if (compatibleParm2.constructor.name == 'Function') options = {
					init: compatibleParm2
				};
				else options = {};
			}
			if (options.listeners) {
				/* Migrate from version 1 */
				if (!options.on) options.on = {};
				Object.assign(options.on, options.listeners);
			}
			if (options.classnames) {
				/* Migrate from version 1 */
				if (!options.classList) options.classList = [];
				options.classList.concat(options.classnames);
			}
			if (options.tag) tagName = options.tag;
			let el;
			if (options.from) {
				if (options.from instanceof HTMLElement) {
					el = options.from;
				} else if (typeof (options.from) == "string") {
					els = domHelper(tagName, {
						html: options.from
					});
					if (els.childElementCount === 0) {
						el = document.createElement(tagName);
					} else if (els.childElementCount === 1) {
						el = els.firstElementChild;
					} else {
						el = els;
					}
				}
			} else if (options.query) {
				const query = document.querySelector(options.query);
				if (query) el = query;
				else return null;
			} else el = document.createElement(tagName);
			if (options.id) el.id = options.id;
			if (options.html) el.innerHTML = options.html;
			if (options.text) el.innerText = options.text;
			if (options.attr) {
				for (const ak of Object.keys(options.attr)) {
					el.setAttribute(ak, options.attr[ak]);
				}
			}
			if (options.cssText) el.style.cssText = options.cssText;
			if (options.style) Object.assign(el.style, options.style);
			if (options.css) Object.assign(el.style, options.css);
			if (options.childs) {
				if (options.childs instanceof Array) options.childs.filter(el => !!el).forEach(child => {
					if (child instanceof HTMLElement) el.appendChild(child);
					else if (child.hasOwnProperty('type') && child.hasOwnProperty('content')) {
						switch (child.type) {
							case 'html': {
								arguments.callee('span', {
									from: child.content,
									append: el
								});
							}
							break;
						case 'style': {
							const scoped = child.hasOwnProperty('scoped') && !!child.scoped;
							arguments.callee('style', {
								html: child.content,
								append: el,
								attr: {
									scoped
								}
							});
						}
						break;
						default:
							el.appendChild(arguments.callee(child.type, child.content));
						}
					} else el.appendChild(document.createTextNode(child));
				});
				else if (options.childs instanceof HTMLElement) el.appendChild(options.childs);
				else el.appendChild(document.createTextNode(options.childs));
			}
			if (options.classlist) {
				if (options.classlist instanceof Array) options.classlist.forEach(classname => {
					el.classList.add(classname);
				});
				else el.classList.add(...options.classlist.split(" "));
			}
			if (options.classList) {
				if (options.classList instanceof Array) options.classList.forEach(classname => {
					el.classList.add(classname);
				});
				else el.classList.add(...options.classList.split(" "));
			}
			if (options.on) {
				for (let listenerName of Object.keys(options.on)) {
					el.addEventListener(listenerName, options.on[listenerName]);
				}
			}
			if (options.off) {
				for (let listenerName of Object.keys(options.of)) {
					el.removeEventListener(listenerName, options.off[listenerName]);
				}
			}
			if (options.bind) {
				const serverName = "$bindingserver" + Math.floor(Math.random() * 100000);
				const bindings = CKTools.deepClone(options.bind);
				const unbindProperty = (prop) => bindings[prop] = undefined;
				const unbindAllProperties = () => el[serverName].disconnect();
				el[serverName] = new MutationObserver(mutations => {
					for (const mutation in mutations) {
						if (bindings.hasOwnProperty(mutation.attributeName)) {
							try {
								bindings[mutation.attributeName]({
									target: mutation.target,
									attributeName: mutation.attributeName,
									attributeNamespace: mutation.attributeNamespace,
									oldValue: mutation.oldValue,
									newValue: mutation.target.getAttribute(mutation.attributeName) || undefined,
									unbind: () => unbindProperty(mutation.attributeName),
									stopListen: () => (unbindAllProperties(), el[serverName] = undefined)
								});
							} catch (e) {}
						}
					}
				});
				el.addEventListener('DOMNodeRemoved', () => (unbindAllProperties(), el[serverName] = undefined));
				el[serverName].observe(el, {
					attributes: true,
					attributeOldValue: true
				});
			}
			if (options.append && options.append instanceof HTMLElement) options.append.appendChild(el);
			if (options.insertBefore && insertBefore instanceof HTMLElement) options.insertBefore.parentNode.insertBefore(el, options.insertBefore);
			if (options.insertAfter && insertAfter instanceof HTMLElement) options.insertAfter.parentNode.insertAfter(el, options.insertAfter);
			if (options.init && options.init instanceof Function) options.init(el);
			if (options.initAsync && options.initAsync instanceof Function) {
				return options.initAsync(el).then(() => el);
			}
			return el;
		}
		static makeDom() {
			console.warn('"makeDom" has been deprecated. Redirecting to "domHelper"...');
			return CKTools.domHelper(...arguments);
		}
		static addDom(item) {
			const make = (tag = 'div') => document.createElement(tag);
			const txt = (it = '') => document.createTextNode(it);
			class DOMItem {
				constructor(it = '') {
					this.setItem(it);
				}
				setItem(it = '') {
					if (typeof it === 'string' || it instanceof String) {
						this.el = txt(it);
					} else if (it instanceof HTMLElement) {
						this.el = it;
					} else this.el = txt(it.toString());
					if (!this.target) this.target = document.body;
					this.mode = 'child';
					return this;
				}
				inside(q = document.body) {
					this.mode = 'child';
					if (q instanceof HTMLElement) {
						this.target = q;
					} else if (typeof q === 'string' || q instanceof String) {
						const ql = this.target.querySelector(q);
						if (ql) this.target = ql;
					}
					return this;
				}
				after(a = null) {
					this.mode = 'child-after';
					if (a instanceof HTMLElement) {
						this.after = a;
					} else if (typeof a === 'string' || a instanceof String) {
						const al = this.target.querySelector(a);
						if (al) this.after = al;
					}
					return this;
				}
				before(a = null) {
					this.mode = 'child-before';
					if (a instanceof HTMLElement) {
						this.before = a;
					} else if (typeof a === 'string' || a instanceof String) {
						const al = this.target.querySelector(a);
						if (al) this.before = al;
					}
					return this;
				}
				done() {
					switch (this.mode) {
						case "child": {
							if (this.el && this.target)
								this.target.appendChild(this.el);
						}
						break;
					case "child-before": {
						if (this.el && this.target && this.before)
							this.target.insertBefore(this.el, this.before);
					}
					break;
					case "child-after": {
						if (this.el && this.target && this.after)
							this.target.insertBefore(this.el, this.after.nextSibling);
					}
					break;
					}
				}
			}
			return new DOMItem(item);
		}
		static deepClone(obj) {
			let newObject = {};
			if (Array.isArray(obj)) {
				newObject = [];
				for (let i = 0; i < obj.length; i++) {
					newObject.push(CKTools.deepClone(obj[i]));
				}
				return newObject;
			}
			Object.keys(obj).map(key => {
				if (typeof obj[key] === 'object') {
					newObject[key] = CKTools.deepClone(obj[key]);
				} else {
					newObject[key] = obj[key];
				}
			});
			return newObject;
		}
		static getCookie(name) {
			const value = `; ${document.cookie}`;
			const parts = value.split(`; ${name}=`);
			if (parts.length === 2) return parts.pop().split(';').shift();
		}
		static clearAllCookies() {
			return document.cookie.split(';').forEach(cookie => document.cookie = cookie.replace(/^ +/, '').replace(/=.*/, `=;expires=${new Date(0).toUTCString()};path=/`));
		}
		static getUrlParam(key) {
			return (new URL(location.href)).searchParams.get(key);
		}
		static wait(ms) {
			return new Promise(r => setTimeout(r, ms));
		}
		static async waitForDom(query, domparent = document, maxRetries = 20, gagms = 200) {
			let i = maxRetries;
			while (--i > 0) {
				if (domparent.querySelector(query)) return true;
				await CKTools.wait(gagms);
			}
			return false;
		}
		static async waitForAttribute(q, attr) {
			let i = 50;
			let value;
			while (--i >= 0) {
				if ((attr in q) &&
					q[attr] != null) {
					value = q[attr];
					break;
				}
				await wait(100);
			}
			return value;
		}
		static async waitForPageVisible() {
			if (document.hidden) return true;
			return new Promise(r => {
				const handler = () => {
					r(true);
					document.removeEventListener('visibilitychange', handler);
				};
				document.addEventListener("visibilitychange", handler)
			});
		}
		static clearStyles(className = "injectedStyle") {
			let dom = document.querySelectorAll("style." + className);
			if (dom)[...dom].forEach(e => e.remove());
		}
		static addStyle(s, className = "injectedStyle", mode = "append", injectBase = document.head) {
			switch (mode) {
				default:
				case "append":
					break;
				case "unique":
					if (document.querySelector("style." + className)) return;
					break;
				case "update":
					CKTools.clearStyles(className);
					break;
			}
			let style = document.createElement("style");
			style.classList.add(className);
			style.innerHTML = s;
			injectBase.appendChild(style);
		}
		// stackoverflow
		static debounce(func, timeout = 300) {
			let timer;
			return (...args) => {
				clearTimeout(timer);
				timer = setTimeout(() => {
					func.apply(this, args);
				}, timeout);
			};
		}
		static throttle(callback, limit) {
			var waiting = false;
			return function () {
				if (!waiting) {
					callback.apply(this, arguments);
					waiting = true;
					setTimeout(function () {
						waiting = false;
					}, limit);
				}
			}
		}
		static domContains(selector, text) {
			var elements = document.querySelectorAll(selector);
			return [].filter.call(elements, function (element) {
				return RegExp(text).test(element.textContent);
			});
		}
		static mapReplace(str, map) {
			//reference: https://segmentfault.com/q/1010000023489916 answer-2
			const replace = ({
					str,
					reg,
					replacer
				}) =>
				str.replace(new RegExp(reg, 'g'), replacer);
			return Object.keys(map).reduce((str, reg) => replace({
				str,
				reg,
				replacer: map[reg]
			}), str);
		}
		static padStart(num, count = 2) {
			return (('' + Math.pow(10, count)).substr(1) + num).slice(-1 * Math.max(count, ('' + num).length));
		}
		static fixNum(num, fix = 0) {
			return Math.floor(num * (Math.pow(10, fix))) / (Math.pow(10, fix));
		}
		static random = class {
			static hex() {
				return `#${Math.floor(Math.random() * 0xffffff).toString(16).padEnd(6, "0")}`;
			}
			static shuffleArray(arr) {
				return arr.sort(() => 0.5 - Math.random());
			}
			static num(min, max) {
				return Math.random() * (max - min) + min;
			}
			static fromArray(arr = []) {
				return arr[Math.floor(CKTools.random.num(0, arr.length))];
			}
			static from(...args) {
				return CKTools.random.fromArray(args);
			}
		}
		static is = class {
			static str(s) {
				return (s != null && (typeof s === "string" || s instanceof String));
			}
			static elementInViewport(el) {
				var rect = el.getBoundingClientRect();
				return (
					rect.top >= 0 &&
					rect.left >= 0 &&
					rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
					rect.right <= (window.innerWidth || document.documentElement.clientWidth)
				);
			}
			static asyncFn(fn) {
				return fn.constructor.name === "AsyncFunction";
			}
			static darkMode() {
				return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
			}
		}
		static modal = class {
			static openModal(title = '', content) {
				CKTools.modal.blockWindow();
				let modal = CKTools.get("#CKTOOLS-modal");
				if (!modal) modal = CKTools.modal.initModal();
				modal.setTitle(title);
				modal.setContent(content);
				modal.show();
			}
			static isModalShowing() {
				let modal = CKTools.get("#CKTOOLS-modal");
				if (modal) return modal.classList.contains("show");
				else return false;
			}
			static hideModal() {
				CKTools.modal.blockWindow(false);
				let modal = CKTools.get("#CKTOOLS-modal");
				if (modal) modal.hide();
			}
			static initModal() {
				CKTools.addStyle(`
				#CKTOOLS-modal{
					position: fixed;
					z-index: 99010;
					top: 50%;
					left: 50%;
					width: 300px;
					width: 30vw;
					/*height: 300px;
					height: 50vh;*/
					background: white;
					border-radius: 8px;
					padding: 12px;
					transform: translate(-50%,-50%);
					transition: all .3s;
					box-shadow: 0 2px 8px grey;
				}
				#CKTOOLS-modal.show{
					opacity: 1;
					transform: translate(-50%,-50%) scale(1);
				}
				#CKTOOLS-modal.hide{
					opacity: 0;
					pointer-events: none;
					transform: translate(-50%,-50%) scale(0.9);
				}
				.CKTOOLS-modal-title{
					font-size: large;
				}
				.CKTOOLS-modal-content{
					font-size: medium;
				}
				.CKTOOLS-modal-content>div{
					display: flex;
					margin: 6px 10px;
					flex-wrap: wrap;
					flex-direction: column;
					align-content: space-around;
					justify-content: space-between;
					align-items: center;
				}
				.CKTOOLS-toolbar-btns{
					flex: 1;
					border: none;
					background: #2196f3;
					border-radius: 3px;
					margin: 0 6px;
					padding: 3px;
					color: white;
					box-shadow: 0 2px 3px grey;
					min-width: 60px;
				}
				.CKTOOLS-toolbar-btns:hover{
					filter: brightness(0.85);
				}
				`, "CKTOOLS-modal-css", "unique");
				const modal = document.createElement("div");
				modal.id = "CKTOOLS-modal";
				modal.className = "hide";

				const header = document.createElement("h2");
				header.className = "CKTOOLS-modal-title"
				modal.appendChild(header);

				modal.setTitle = (t = '') => {
					header.innerHTML = t;
				}

				const contents = document.createElement("div");
				contents.className = "CKTOOLS-modal-content";
				modal.appendChild(contents);

				modal.setContent = async (c) => {
					let ct = c;
					if (ct instanceof Function) {
						ct = await ct();
					}
					if (ct instanceof HTMLElement) {
						contents.innerHTML = '';
						contents.appendChild(ct);
						return;
					}
					if (typeof (ct) === "string") {
						contents.innerHTML = ct;
						return;
					}
					console.log("unknown: ", ct);
				}
				modal.addContent = async (c) => {
					let ct = c;
					if (ct instanceof Function) {
						ct = await ct();
					}
					if (ct instanceof HTMLElement) {
						contents.appendChild(ct);
						return;
					}
					if (ct instanceof String) {
						contents.innerHTML += ct;
						return;
					}
					console.log("unknown: ", ct);
				}

				modal.close = CKTools.modal.closeModal;
				modal.open = CKTools.modal.openModal;
				modal.show = () => {
					modal.className = "show";
				}
				modal.hide = () => {
					modal.className = "hide";
				}

				document.body.appendChild(modal);
				return modal;
			}
			static closeModal() {
				CKTools.modal.blockWindow(false);
				let modal = CKTools.get("#CKTOOLS-modal");
				if (modal) modal.remove();
			}
			static async alertModal(title = "", content = "", okbtn = null) {
				if (CKTools.modal.isModalShowing()) {
					CKTools.modal.hideModal();
					await CKTools.wait(200);
				}
				CKTools.modal.openModal(title, await CKTools.domHelper("div", async container => {
					container.appendChild(await CKTools.domHelper("div", tip => {
						tip.innerHTML = content;
					}))
					if (okbtn !== null)
						container.appendChild(await CKTools.domHelper("div", async btns => {
							btns.style.display = "flex";
							btns.appendChild(await CKTools.domHelper("button", btn => {
								btn.className = "CKTOOLS-toolbar-btns";
								btn.innerHTML = okbtn;
								btn.onclick = e => CKTools.modal.hideModal();
							}))
						}))
				}))
				await CKTools.wait(300);
			}
			static blockWindow(block = true) {
				CKTools.addStyle(`
				#CKTOOLS-blockWindow{
					z-index: 99005;
					display: block;
					background: #00000080;
					opacity: 0;
					transition: all .3s;
					position: fixed;
					left: 0;
					top: 0;
					width: 100vw;
					height: 100vh;
				}
				#CKTOOLS-blockWindow.hide{
					pointer-events: none;
					opacity: 0;
				}
				#CKTOOLS-blockWindow.show{
					opacity: 1;
				}
				`, "CKTOOLS-blockWindow-css", "unique");
				let dom = CKTools.get("#CKTOOLS-blockWindow");
				if (!dom) {
					dom = document.createElement("div");
					dom.id = "CKTOOLS-blockWindow";
					dom.className = "hide";
					document.body.appendChild(dom);
				}
				if (block) {
					dom.className = "show";
				} else {
					dom.className = "hide";
				}
			}
		}
		static bili = class {
			static getCSRFToken() {
				return CKTools.getCookie("bili_jct");
			}
			static async playerReady() {
				let i = 50;
				while (--i >= 0) {
					await CKTools.wait(100);
					if (!('player' in window)) continue;
					if (!('isInitialized' in window.player)) continue;
					if (!window.player.isInitialized()) continue;
				}
				if (i < 0) return false;
				await CKTools.waitForPageVisible();
				while (1) {
					await CKTools.wait(200);
					if (document.querySelector(".bilibili-player-video-control-wrap, .bpx-player-control-wrap")) return true;
				}
			}
			static getTotalTime() {
				return waitForAttribute(CKTools.get('video, bwp-video'), 'duration')||unsafeWindow.player?.getDuration();
			}
			static getCurrentTime() {
				return CKTools.get('video, bwp-video').currentTime||unsafeWindow.player?.getCurrentTime();
			}
			static setTime(t) {
				return window.player.seek(t);
			}
			static play() {
				return window.player.play();
			}
			static pause() {
				return window.player.pause();
			}
			static getInfoByBvid(bvid) {
				return fetch('https://api.bilibili.com/x/web-interface/view?bvid=' + bvid).then(raw => raw.json());
			}
			static getInfoByAid(aid) {
				return fetch('https://api.bilibili.com/x/web-interface/view?aid=' + aid).then(raw => raw.json());
			}
		}
		static EventEmitter = class {
			handlers = {};
			on(name, func) {
				if (!(func instanceof Function)) throw "Param must be func!";
				if (!(name in this.handlers)) {
					this.handlers[name] = [];
				}
				this.handlers[name].push(func);
			}
			off(name, func) {
				if (!(func instanceof Function)) throw "Param must be func!";
				if (name in this.handlers) {
					for (let i = 0; i < this.handlers[name].length; i++) {
						if (this.handlers[name][i] === func) {
							this.handlers[name].splice(i, 1);
							i--;
						}
					}
				}
			}
			clean(name) {
				if (name in this.handlers) {
					this.handlers[name] = [];
				}
			}
			emit(name, ...args) {
				if (name in this.handlers) {
					for (let func of this.handlers[name]) {
						try {
							func(...args);
						} catch (e) {
							console.error('ERROR:', e);
						}
					}
				}
			}
		}
		static HoldClick = class {
			dom;
			emitter = new CKTools.EventEmitter;
			downTime = 0;
			holdingTime = 250;
			mouseDown = false;

			constructor(dom, holdingTime = 250) {
				this.bind(dom);
				this.holdingTime = holdingTime;
			}

			bind(dom) {
				if (this.dom) {
					this.unregListeners();
				}
				if (dom instanceof HTMLElement) {
					this.dom = dom;
					this.initListener();
				}
			}

			onclick(func) {
				this.emitter.on("click", func);
				return this;
			}

			onhold(func) {
				this.emitter.on("hold", func);
				return this;
			}

			onup(func) {
				this.emitter.on("up", func);
				return this;
			}

			offclick(func) {
				this.emitter.off("click", func);
				return this;
			}

			offhold(func) {
				this.emitter.off("hold", func);
				return this;
			}

			offup(func) {
				this.emitter.off("up", func);
				return this;
			}

			resetCallback(name = "all") {
				const allEv = ["click", "hold", "up"];
				if (name === "all") {
					allEv.forEach(e => this.emitter.clean(e));
				} else if (allEv.includes(name)) {
					this.emitter.clean(name);
				}
			}

			unregListeners() {
				this.dom.removeEventListener("mouseup", this.handleMouseUp.bind(this));
				this.dom.removeEventListener("mousedown", this.handleMouseDown.bind(this));
				this.dom.removeEventListener("mouseout", this.handleMouseOut.bind(this));
			}

			uninstall() {
				this.resetCallback();
				this.unregListeners();
			}

			handleMouseDown(e) {
				if (e.button !== 0 && e.button !== 1) return;
				e.preventDefault();
				this.mouseDown = true;
				this.downTime = (new Date()).getTime();
				setTimeout(() => {
					if (this.mouseDown) {
						console.log(this);
						this.mouseDown = false;
						this.downTime = 0;
						this.emitter.emit("hold", e);
					}
				}, this.holdingTime)
			}

			handleMouseUp(e) {
				if (e.button !== 0 && e.button !== 1) return;
				e.preventDefault();
				if (this.mouseDown) {
					this.mouseDown = false;
					const currTime = (new Date).getTime();
					if ((currTime - this.downTime) >= this.holdingTime) {
						this.emitter.emit("hold", e);
					} else {
						this.emitter.emit("click", e);
					}
					this.downTime = 0;
				}
				this.emitter.emit("up", e);
			}

			handleMouseOut(e) {
				e.preventDefault();
				if (this.mouseDown) {
					this.mouseDown = false;
					this.downTime = 0;
					this.emitter.emit("hold", e);
				}
			}

			initListener() {
				this.dom.addEventListener("mouseup", this.handleMouseUp.bind(this))
				this.dom.addEventListener("mousedown", this.handleMouseDown.bind(this))
				this.dom.addEventListener("mouseout", this.handleMouseOut.bind(this))
			}
		}
		static dragger = class {
			static defaultHandler(val) {
				return console.log("DRAG:", val);
			}
			static async waitForDragger(waitStatus = true) {
				while (CKTools.dragger.dragging !== waitStatus) await CKTools.wait(10);
				return CKTools.dragger;
			}
			static async regHandler(func) {
				if (!(func instanceof Function)) throw "Param must be a func!";
				await CKTools.dragger.waitForDragger(false);
				CKTools.dragger.handler = func;
				return CKTools.dragger;
			}
			static handler() {}
			static dragging = false;
			static initialDragData = {
				x: 0,
				y: 0
			}
			static lastDragData = {
				x: 0,
				y: 0
			}
			static startDrag(e) {
				if (CKTools.dragger.dragging) return;
				CKTools.dragger.dragging = true;
				console.log(CKTools.dragger.initialDragData);
				CKTools.dragger.initialDragData.x = e.screenX;
				CKTools.dragger.initialDragData.y = e.screenY;
				CKTools.dragger.lastDragData.x = e.screenX;
				CKTools.dragger.lastDragData.y = e.screenY;
				document.body.addEventListener("mouseup", CKTools.dragger.stopDrag);
				document.body.addEventListener("mousemove", CKTools.dragger.handleDrag);
				console.info("DRAG:", "Start Drag");
				return CKTools.dragger;
			}
			static handleDrag(e) {
				const currPos = {
					x: e.screenX,
					y: e.screenY
				};
				const initPos = CKTools.dragger.initialDragData;
				const delta = {
					x: initPos.x - currPos.x,
					y: initPos.y - currPos.y
				}
				const lastdelta = {
					x: CKTools.dragger.lastDragData.x - currPos.x,
					y: CKTools.dragger.lastDragData.y - currPos.y
				}
				CKTools.dragger.lastDragData = currPos;
				CKTools.dragger.handler(delta, lastdelta);
			}
			static stopDrag() {
				document.body.removeEventListener("mouseup", CKTools.dragger.stopDrag);
				document.body.removeEventListener("mousemove", CKTools.dragger.handleDrag);
				CKTools.dragger.handler = CKTools.dragger.defaultHandler;
				console.info("DRAG:", "Stop Drag");
				CKTools.dragger.dragging = false;
				return CKTools.dragger;
			}
		}
		static GUID = class {
			static S4() {
				return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
			}
			static get() {
				let S4 = CKTools.GUID.S4;
				return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
			}
			static getShort() {
				let S4 = CKTools.GUID.S4;
				return (S4() + S4() + S4() + S4());
			}
		}
	}
	window.CKTools = CKTools;
})();