PixivのリンクをTweetに添えて

Tweetの画像の下に(プロフィール欄にあれば)その人のPixivのリンクを表示します。

// ==UserScript==
// @name			PixivのリンクをTweetに添えて
// @name:ja			PixivのリンクをTweetに添えて
// @name:en			Show me your Pixiv.
// @version			1145141919810.2.6
// @description			Tweetの画像の下に(プロフィール欄にあれば)その人のPixivのリンクを表示します。
// @description:ja			Tweetの画像の下に(プロフィール欄にあれば)その人のPixivのリンクを表示します。
// @description:en			Display Pixiv link below the Tweet image.
// @author			ゆにてぃー
// @match			https://twitter.com/*
// @match			https://mobile.twitter.com/*
// @match			https://x.com/*
// @match			https://X.com/*
// @connect			api.twitter.com
// @connect			api.fanbox.cc
// @connect			skeb.jp
// @connect			fantia.jp
// @connect			booth.pm
// @connect			linktr.ee
// @connect			profcard.info
// @connect			lit.link
// @connect			potofu.me
// @connect			creatorlink.net
// @connect			lab.syncer.jp
// @connect			carrd.co
// @connect			sketch.pixiv.net
// @connect			tumblr.com
// @connect			html.co.jp
// @connect			twpf.jp
// @icon			data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAJkSURBVHgB7VZBbtpQEH3zIW0WVYuXVaH4Bs0NSk4AOUFhEarskhMknIDsqkKlcIT0BNAT1D1B3ZJK3dmVuirwp/MhVmzAxiagKBJv9+ePZ97M/JkxsMMODwzChlD84FWQp3MxeCDHAhiumB+MJrr1+8Ryw3p/9+H4DctfIPCq49Xlw8Kv99YlMuB19885gy/i7llziwGfFFWJyR02XzSCuwiBUse7BlFVaz5LS8KQVkRXaXRJsqImfDjKSZBNyzEyFWFKVJ4KFbWLElUao6KbSk8i9TXgTPaorxTskPwOxa7/9baGt4zg8oQbNyfWYJlRU0/KUx9ZwNwYNq1ecFRzl18QpW0bB0Ks//KjV1uwlbuLJA3GxEdh5wb5yGEPl3qMd2xecYQHKnlFlVLX95kxYCFKGg5IlU2a0uLpCM68LEJA+sJ/Dm6Jy3aMjQIRakRUm+UuvfOp/X34iQSejeFo0Hdx4optG5uFH/R+GHNvANcm3VtwLs+Lvy2TRwhIOnrYHhysIuDKcCDwGbYAjglOzQt+HssElF6dvoNNOZeuCSbfSgIGMjILMo4/ExZf7TqghNLmlwm1gpSC2tmaLAZMvWGz0Iu7XpqBm2NrQNN5cD+Y5ZOTdZyok3RZMusZOJUN+QZrQFb0oQkG6xIIYHe8A03Unx/Ryd6jS2ctAsbxmFRVynGKlM5na5ePVkUe0p+h9MmraS2zXqYgmSWjOPtElHbLTVB3Q79gqQlMScxqXpeav0UWiGMmXKSNOpZAAPvKs/U/1MRoxRxl+5WD+psUy2D5IdmRVoWjnqDnLlkyO+zwaPAf1zXwZL751PUAAAAASUVORK5CYII=
// @grant			GM_xmlhttpRequest
// @license			MIT
// @namespace			https://greasyfork.org/ja/users/1023652
// ==/UserScript==
(function() {
    'use strict';
	const desktop_selector = {'tweet_field': 'article[data-testid="tweet"]','media_field': '.r-9aw3ui.r-1s2bzr4','profile_field': '[data-testid="UserProfileHeader_Items"]'};
	const mobile_selector = {'tweet_field': 'article[data-testid="tweet"]','media_field': '.r-9aw3ui.r-a1ub67 > .r-9aw3ui','profile_field': '[data-testid="UserProfileHeader_Items"]'};
	const deny_name = /^(home|explore|notifications|messages|i|settings|tos|privacy|compose|search)$/;
	var env_selector;
	function isMobileDevice(){
		const userAgent = navigator.userAgent || navigator.vendor || window.opera;
		return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent);
	}
	if(isMobileDevice()){
		env_selector = mobile_selector;
	}else{
		env_selector = desktop_selector;
	}
	const is_debug = false;
	let currentUrl = document.location.href;
	locationChange();
	var already_acquisition_arr = replace_null_to_something(JSON.parse(localStorage.getItem('user_pixvi_link_collection')),{});
	let updating = false;
	window.addEventListener("scroll", update);
	init();
	wait_load_Element_and_do_function(env_selector.profile_field,show_pixiv_link_in_profile);
	function write_content(target_node,screen_name,additional_linefeed = ''){
		if(target_node === null) return;
		if(get_only_particular_key_value(already_acquisition_arr,`${screen_name}.pixiv_url`) && !target_node.querySelector('.display_pixiv_link')){
			var new_content = document.createElement("span");
			new_content.innerHTML = `${additional_linefeed}<a class="display_pixiv_link" href="${already_acquisition_arr[screen_name].pixiv_url}" target="_blank" rel="noopener">Pixiv🔗</a>`;
			target_node.appendChild(new_content);
			return `${screen_name}のメディア欄に書き込みました。`;
		}
		return `${screen_name}のPixivのURLはすでに書き込まれています。`
	}
	function show_pixiv_link_in_tweet(target){
		var todo_promise_list = [];
		document.querySelectorAll(target).forEach((target_node, index)=> {
			const screen_name = target_node.querySelector('[data-testid="User-Name"]>div>div>a').href.split("/").pop();
			if(! get_only_particular_key_value(already_acquisition_arr,`${screen_name}.pixiv_url`)) return;
			todo_promise_list[index] = new Promise(async function(resolve){
				resolve(write_content(target_node.querySelector(env_selector.media_field),screen_name));
			});
		});
		Promise.allSettled(todo_promise_list).then(results => {
			results.forEach(result => {
				if(result.status === 'fulfilled'){
					debug_log(`${result.value}`);
				}else{
					debug_log(`Failure: ${result.reason}`);
				}
			});
		}).catch(error => debug_log(`Error: ${error}`));
	}
	function show_pixiv_link_in_profile(){
		var profile_field = document.querySelector(env_selector.profile_field);
		if(profile_field .querySelector('.display_pixiv_link') !== null){
			profile_field .querySelector('.display_pixiv_link').parentNode.remove();
		}
		var screen_name = currentUrl.split('/')[3];
		setTimeout(() => write_content(profile_field,screen_name,'<br>'),800);
	}

	function findTarget(target){
		var todo_promise_list = [];
		document.querySelectorAll(`${target}:not([is_pixiv_link_check="true"])`).forEach((target_node, index)=> {
			//なんども取得しないように。
			target_node.setAttribute("is_pixiv_link_check","true");
			//TwitterのID(@の後に見えるやつ)を取得。
			const screen_name = target_node.querySelector('[data-testid="User-Name"]>div>div>a').href.split("/").pop();
			if(already_acquisition_arr[screen_name]?.pixiv_url === undefined || (get_only_particular_key_value(already_acquisition_arr,`${screen_name}.Create_date`,0) + 604800000) <= new Date().getTime()){
				todo_promise_list[screen_name] = new Promise(async function(resolve){
					if(target_node.querySelector(env_selector.media_field) === null){
						resolve(`${screen_name}: 画像なし`);
					}else{
						const end_stat = await find_pixiv_link(screen_name);
						if(end_stat == "Too Many Requests"){
							console.log("API limit.");
						}else if(end_stat === false || end_stat === undefined){
							already_acquisition_arr[screen_name] = {"pixiv_url": null,"Create_date": new Date().getTime()};
							resolve(`${screen_name}: Pixivリンクなし`);
						}else{
							//httpをhttpsにする。
							already_acquisition_arr[screen_name] = {"pixiv_url": end_stat.replace(/^https?/,'https'),"Create_date": new Date().getTime()};
							//console.log(JSON.stringify(already_acquisition_arr))
							resolve(`${screen_name}: ${end_stat}`);
						}
					}
				});
			}
		});
		//連想配列だとうまくいかないので普通の配列に戻す。
		todo_promise_list = Object.values(todo_promise_list);
		Promise.allSettled(todo_promise_list).then(results => {
			results.forEach(result => {
				if(result.status === 'fulfilled'){
					debug_log(`${result.value}`);
				}else{
					debug_log(`Failure: ${result.reason}`);
				}
			});
		}).catch(error => debug_log(`Error: ${error}`)).then(() => {
			show_pixiv_link_in_tweet(env_selector.tweet_field);
			localStorage.setItem('user_pixvi_link_collection', JSON.stringify(already_acquisition_arr));
		});
	}
	async function find_pixiv_link(screen_name){
		if(screen_name.match(deny_name)) return undefined;
		const Pixiv_url_regex = /^https?:\/\/(((www|touch)\.)?pixiv\.(net\/([a-z]{2}\/)?((member(_illust)?\.php\?id\=|(users|u)\/)[0-9]*)|me\/.*))/;
		return new Promise(async function(resolve){
			const request_object = new requestObject_twitter_1_1(screen_name);
			const Twitter_author_data = await request(request_object);
			if(Twitter_author_data.statusText == "Too Many Requests") return resolve("Too Many Requests");
			const urls_in_description = get_only_particular_key_value(Twitter_author_data,'response.entities.description.urls.url',[]);
			const urls_in_description_expanded = get_only_particular_key_value(Twitter_author_data,'response.entities.description.urls.expanded_url',[]);
			const urls_in_url_place = get_only_particular_key_value(Twitter_author_data,'response.entities.url.urls.url',[]);
			const urls_in_url_place_expanded = get_only_particular_key_value(Twitter_author_data,'response.entities.url.urls.expanded_url',[]);
			var urls = urls_in_description.concat(urls_in_description_expanded,urls_in_url_place,urls_in_url_place_expanded).filter(item => !/^https?:\/\/t\.co\//.test(item));
			var Pixiv_url;
			if(urls.length > 0){
				Pixiv_url = await find_pixiv_link_from_profile_urls(urls);
				if(Pixiv_url === undefined || Pixiv_url === null || Pixiv_url === false){
					urls = await expand_shortening_link(urls);
					Pixiv_url = await find_pixiv_link_from_profile_urls(urls);
					return resolve(Pixiv_url);
				}else{
					return resolve(Pixiv_url);
				}
			}
			return resolve(false);
		});
		async function expand_shortening_link(urls_in_profile){
			return new Promise(async function(resolve){
				var return_urls = [];
				if(urls_in_profile.length == 0 || urls_in_profile.length === null || urls_in_profile.length === undefined) return ;
				var promise_list = [];
				urls_in_profile.forEach(target=>{
					switch(true){
						case /^https?:\/\/bit\.ly\/[\w]{1,9}$/.test(target):
							promise_list.push(request(new requestObject('https://lab.syncer.jp/api/32/' + target)));
							break;
						default:
							break;
					}
				});
				await Promise.allSettled(promise_list).then(results => {
					 const res_tmp = get_only_particular_key_value(results, 'value.response', undefined);
					 var tmp;
					 for(let i=0;i<res_tmp.length;i++){
					 	tmp = JSON.parse(res_tmp[i]).pop();
						tmp = tmp.pop();
					 	return_urls[i] = tmp[0];
					 }
				});
				resolve(return_urls);
			});
		}
		async function find_pixiv_link_from_profile_urls(urls_in_profile){
			const Fanbox_url_regex = /^https?:(\/\/www\.pixiv\.net\/fanbox\/creator\/[0-9]*|\/\/.*\.fanbox\.cc\/?)/;
			return new Promise(async function(resolve,reject){
				var tmp_pixiv_url;
				tmp_pixiv_url = findMatch_from_array(urls_in_profile,Pixiv_url_regex,true);
				if (tmp_pixiv_url !== undefined) return resolve(tmp_pixiv_url);
				if(findMatch_from_array(urls_in_profile,Fanbox_url_regex) !== undefined){
					tmp_pixiv_url = await when_fanbox(findMatch_from_array(urls_in_profile,Fanbox_url_regex,true));
					if (Pixiv_url_regex.test(tmp_pixiv_url)){
						console.log(`「${screen_name}」のPixivのURLをFanboxから発見!`)
						return resolve(tmp_pixiv_url);
					}
				}else{
					var get_url_promise_list = [];
					urls_in_profile.forEach(target=>{
						switch(true){
							case /^https?:\/\/((skeb\.jp\/\@.*)|(fantia\.jp\/(fanclubs\/[0-9])?.*)|(.*\.booth\.pm)|(.*linktr\.ee)|(.*profcard\.info)|(.*lit\.link)|(potofu\.me)|(.*\.carrd\.co)|(.*\.tumblr\.com$)|(html\.co\.jp)|(twpf\.jp))\/?/.test(target):
								get_url_promise_list.push(new Promise(
									async function(resolve,reject){
										try{
											return resolve(await when_general(target));
										}catch(error){
											return reject(error);
										}
									}
								));
								break;
							case /^https?:\/\/.*\.creatorlink\.net(\/.*)?/.test(target):
								get_url_promise_list.push(new Promise(
									async function(resolve,reject){
										try{
											return resolve(await when_general(`${target.match(/^https?:\/\/.*\.creatorlink\.net/)[0]}\/Contact`));
										}catch(error){
											return reject(error);
											}
									}
								));
								break;
							case /^https?:\/\/sketch\.pixiv\.net\//.test(target):
								get_url_promise_list.push(new Promise(
									async function(resolve,reject){
										try{
											return resolve(await when_pixiv_sketch(target));
										}catch(error){
											return reject(error);
										}
									}
								));
								break;
							default:
								break;
						}
					});
					if(get_url_promise_list.length > 0){
						await Promise.any(get_url_promise_list).then((value) => {tmp_pixiv_url = value}).catch(() => {tmp_pixiv_url = undefined});
						if(!Pixiv_url_regex.test(tmp_pixiv_url)) return resolve(undefined);
						return resolve(tmp_pixiv_url.replace(/^https?/,'https').replace(/(\/|\\)$/,''));
					}
				}
				return resolve(false);
				async function when_general(target_url){
					return new Promise(async function(resolve,reject){
						const response_data = await request(new requestObject(target_url.replace(/^https?/,"https")));
						var response_data_urls = response_data.response.split(/\"|\<|\>/).filter(function(data_str){return data_str.match(/^https?:(\/\/(((www|touch)\.)?pixiv\.(net\/([a-z]{2}\/)?((member(_illust)?\.php\?id\=|(users|u|fanbox\/creator)\/)[0-9].*)|me\/.*))|.*\.fanbox\.cc\/?)$/)});
						if(response_data_urls.find(function(element){return element.match(Pixiv_url_regex)}) !== undefined){
							console.log(`「${screen_name}」のPixivのURLを ${target_url.split("/")[2]} から発見!`);
							return resolve(response_data_urls.find(function(element){return element.match(Pixiv_url_regex)}));
						}else if(response_data_urls.find(function(element){return element.match(Fanbox_url_regex)}) !== undefined){
							return resolve(when_fanbox(response_data_urls.find(function(element){return element.match(Fanbox_url_regex)})));
						}else{
							return reject(undefined);
						}
					});
				}
				async function when_pixiv_sketch(target_url){
						return new Promise(async function(resolve,reject){
							const response_data = await request(new requestObject(target_url));
							var User_id = response_data.response.split(',').filter(function(data_str){return data_str.match(/\\"pixiv_user_id\\":\\"[0-9]*\\"/)});
							if(User_id){
								console.log(`「${screen_name}」のPixivのURLを pixiv sketchから発見!`);
								return resolve("https://www.pixiv.net/users/" + User_id[0].split(/\"|\\/)[6]);
							}else{
								return reject(undefined);
							}
						});
					}
				async function when_fanbox(fanbox_url){
					if(fanbox_url.match(/^https?:\/\/www\.pixiv\.net\/fanbox\/creator\/[0-9]*/)){
						return fanbox_url.replace('fanbox/creator', 'users');
					}else{
						const fanbox_response = await request(new requestObject_fanbox(`https://api.fanbox.cc/creator.get?creatorId=${fanbox_url.replace(/(https?:\/\/|\.fanbox.*)/g,'')}`,fanbox_url.replace(/^http:/, 'https:').replace(/\/$/, '')));
						if(fanbox_response.status == "404") return undefined;
						tmp_pixiv_url = findMatch_from_array(fanbox_response.response.body.profileLinks,Pixiv_url_regex,true);
						if(tmp_pixiv_url !== undefined){
							return tmp_pixiv_url;
						}else{
							return `https://www.pixiv.net/users/${fanbox_response.response.body.user.userId}`;
						}
					}
				}
			}
		)}
	}
	async function when_location_change(screen_name){
		if(!screen_name.match(deny_name)){
			const end_stat = await find_pixiv_link(screen_name);
			if(end_stat == "Too Many Requests"){
				console.log("API limit.");
			}else if(end_stat === false || end_stat === undefined){
				already_acquisition_arr[screen_name] = {"pixiv_url": null,"Create_date": new Date().getTime()};
			}else{
				already_acquisition_arr[screen_name] = {"pixiv_url":end_stat,"Create_date": new Date().getTime()};
				wait_load_Element_and_do_function(env_selector.profile_field,show_pixiv_link_in_profile);
			}
			localStorage.setItem('user_pixvi_link_collection', JSON.stringify(already_acquisition_arr));
		}
	}
	function debug_log(str_ = "debug"){
		if(is_debug === true){
			console.log(str_);
		}
	}
	function init() {
		findTarget(env_selector.tweet_field);
		show_pixiv_link_in_tweet(env_selector.tweet_field);
	}
	function update() {
		if(updating) return;
		updating = true;
		init();
		setTimeout(() => {updating = false;}, 1000);
	}

	function locationChange() {
		const observer = new MutationObserver(mutations => {
			mutations.forEach(() => {
				if(currentUrl !== document.location.href){
					currentUrl = document.location.href;
					init();
					wait_load_Element_and_do_function(env_selector.profile_field,show_pixiv_link_in_profile);
					when_location_change(currentUrl.split("/")[3]);
				}
			});
		});
		const target = document.getElementById("react-root");
		const config = {childList: true,subtree: true};
		observer.observe(target, config);
	}
	function GetCookie(name){
		let arr, reg = new RegExp("(^| )" + name + "=([^;]*)(;|$)");
		if(arr = document.cookie.match(reg)){
			return decodeURIComponent(arr[2]);
    	}else{
        	return null;
		}
	}

	function findMatch_from_array(arr, regex, is_strict = false){
		for(let i = 0; i < arr.length; i++){
			if(regex.test(arr[i])){
				if(is_strict === true){
					return arr[i].match(regex)[0];
				}else{
					return arr[i];
				}
			}
		}
		return undefined;
	}
	function get_only_particular_key_value(object, path, defaultValue = undefined){
		var isArray = Array.isArray;
		if(object == null || typeof object != 'object') return defaultValue;
		return (isArray(object)) ? object.map(createProcessFunction(path)) : createProcessFunction(path)(object);
		function createProcessFunction(path){
			if(typeof path == 'string') path = path.split('.');
			if(!isArray(path)) path = [path];
			return function(object){
				var index = 0,
					length = path.length;
				while(index < length){
					const key = toString_(path[index++]);
					if(object === undefined){
						return defaultValue;
					}
					// 配列に対する処理
					if(isArray(object)){
						object = object.map(item => item[key]);
					}else{
						object = object[key];
					}
				}
				return (index && index == length) ? object : void 0;
			};
		}
		function toString_(value){
			if(value == null) return '';
			if(typeof value == 'string') return value;
			if(isArray(value)) return value.map(toString) + '';
			var result = value + '';
			return '0' == result && 1 / value == -(1 / 0) ? '-0' : result;
		}
	}

	function wait_load_Element_and_do_function(Element_Name,func,func_argument){
		const MAX_RETRY_COUNT = 5;
		var retry_counter = 0;
		var set_interval_id = setInterval(find_target_element, 500);
		function find_target_element(){
			retry_counter++;
			if(retry_counter > MAX_RETRY_COUNT) {
			clearInterval(set_interval_id);
                return;
			}
			var target_elements = document.querySelectorAll(Element_Name);
			if(target_elements.length > 0){
				if(typeof(set_interval_id) != 'undefined'){
					clearInterval(set_interval_id);
					func(func_argument,target_elements);
				}else{
					return target_elements;
				}
			}
		}
		find_target_element();
	}
	function replace_null_to_something(input_character,replace_character = " "){
		if(input_character === null || input_character === undefined || input_character === ""){
			return replace_character;
		}else{
			return input_character;
		}
	}
	async function request(object, timeout = 60000){
		return new Promise((resolve, reject) => {
			try{
				GM_xmlhttpRequest({
					method: object.method,
					url: object.url,
					headers: object.headers,
					responseType: object.respType,
					data: object.body,
					anonymous: object.anonymous,
					timeout: timeout,
					onload: function(responseDetails){
						//console.log(responseDetails);
						return resolve(responseDetails);
					},
					ontimeout: function(responseDetails){
						reject(`[request]time out:\nresponse ${responseDetails}`)
					},
					onerror: function(responseDetails){
						reject(`[request]error:\nresponse ${responseDetails}`)
					}
				});
			}catch(err){
				//console.log(err)
			}
		});
	}
	class requestObject_twitter{
		constructor(ID) {
			this.method = 'GET';
			this.respType = 'json';
			this.url = `https://api.twitter.com/graphql/rePnxwe9LZ51nQ7Sn_xN_A/UserByScreenName?variables=%7B%22screen_name%22%3A%22${ID}%22%2C%22withSafetyModeUserFields%22%3Afalse%2C%22withSuperFollowsUserFields%22%3Afalse%7D&features=%7B%22responsive_web_twitter_blue_verified_badge_is_enabled%22%3Afalse%2C%22responsive_web_graphql_exclude_directive_enabled%22%3Afalse%2C%22verified_phone_label_enabled%22%3Afalse%2C%22responsive_web_graphql_skip_user_profile_image_extensions_enabled%22%3Afalse%2C%22responsive_web_graphql_timeline_navigation_enabled%22%3Afalse%7D`;
			this.body = null;
			this.headers = {
				"Content-Type": "application/json",
				'User-agent': navigator.userAgent || navigator.vendor || window.opera,
				'accept': '*/*',
				'Referer': "https://twitter.com/",
				'Host': 'api.twitter.com',
				'authorization': `Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA`,
				'x-csrf-token': GetCookie("ct0"),
			};
			this.package = null;
			this.anonymous = false;
			this.screen_name = ID;
		}
	}
	class requestObject_twitter_1_1{
		constructor(ID) {
			this.method = 'GET';
			this.respType = 'json';
			this.url = `https://api.twitter.com/1.1/users/show.json?screen_name=${ID}`;
			this.body = null;
			this.headers = {
				"Content-Type": "application/json",
				'User-agent': navigator.userAgent || navigator.vendor || window.opera,
				'accept': '*/*',
				'Referer': "https://twitter.com/",
				'Host': 'api.twitter.com',
				'authorization': `Bearer AAAAAAAAAAAAAAAAAAAAANRILgAAAAAAnNwIzUejRCOuH5E6I8xnZz4puTs%3D1Zv7ttfk8LF81IUq16cHjhLTvJu4FA33AGWWjCpTnA`,
				'x-csrf-token': GetCookie("ct0"),
			};
			this.package = null;
			this.anonymous = false;
			this.screen_name = ID;
		}
	}
	class requestObject_fanbox{
		constructor(URL,fanbox_URL){
			this.method = 'GET';
			this.respType = 'json';
			this.url = `${URL}`;
			this.body = null;
			this.headers = {
				'User-agent': navigator.userAgent || navigator.vendor || window.opera,
				'origin': fanbox_URL,
				'Host': 'api.fanbox.cc',
			};
		}
	}
	class requestObject{
		constructor(URL,addtional_cookie = undefined){
			this.method = 'GET';
			this.respType = '';
			this.url = `${URL}`;
			this.headers = {
				"Content-Type": "text/html,application/xhtml+xml,application/xml",
				'User-agent': navigator.userAgent || navigator.vendor || window.opera,
				'accept': '*/*',
				'Referer': URL,
				"Sec-Fetch-Mode": "navigate",
				'cookie': `${addtional_cookie}`
			};
			this.package = null;
		}
	}
	when_location_change(currentUrl.split("/")[3]);
})();